define(["@jupyter-widgets/base"], function(__WEBPACK_EXTERNAL_MODULE_2__) { return /******/ (function(modules) { // webpackBootstrap
/******/ 	// The module cache
/******/ 	var installedModules = {};
/******/
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/
/******/ 		// Check if module is in cache
/******/ 		if(installedModules[moduleId]) {
/******/ 			return installedModules[moduleId].exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = installedModules[moduleId] = {
/******/ 			i: moduleId,
/******/ 			l: false,
/******/ 			exports: {}
/******/ 		};
/******/
/******/ 		// Execute the module function
/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ 		// Flag the module as loaded
/******/ 		module.l = true;
/******/
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/
/******/
/******/ 	// expose the modules object (__webpack_modules__)
/******/ 	__webpack_require__.m = modules;
/******/
/******/ 	// expose the module cache
/******/ 	__webpack_require__.c = installedModules;
/******/
/******/ 	// define getter function for harmony exports
/******/ 	__webpack_require__.d = function(exports, name, getter) {
/******/ 		if(!__webpack_require__.o(exports, name)) {
/******/ 			Object.defineProperty(exports, name, {
/******/ 				configurable: false,
/******/ 				enumerable: true,
/******/ 				get: getter
/******/ 			});
/******/ 		}
/******/ 	};
/******/
/******/ 	// getDefaultExport function for compatibility with non-harmony modules
/******/ 	__webpack_require__.n = function(module) {
/******/ 		var getter = module && module.__esModule ?
/******/ 			function getDefault() { return module['default']; } :
/******/ 			function getModuleExports() { return module; };
/******/ 		__webpack_require__.d(getter, 'a', getter);
/******/ 		return getter;
/******/ 	};
/******/
/******/ 	// Object.prototype.hasOwnProperty.call
/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ 	// __webpack_public_path__
/******/ 	__webpack_require__.p = "";
/******/
/******/ 	// Load entry module and return exports
/******/ 	return __webpack_require__(__webpack_require__.s = 7);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {

/* WEBPACK VAR INJECTION */(function(global, module) {var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;//     Underscore.js 1.9.1
//     http://underscorejs.org
//     (c) 2009-2018 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
//     Underscore may be freely distributed under the MIT license.

(function() {

  // Baseline setup
  // --------------

  // Establish the root object, `window` (`self`) in the browser, `global`
  // on the server, or `this` in some virtual machines. We use `self`
  // instead of `window` for `WebWorker` support.
  var root = typeof self == 'object' && self.self === self && self ||
            typeof global == 'object' && global.global === global && global ||
            this ||
            {};

  // Save the previous value of the `_` variable.
  var previousUnderscore = root._;

  // Save bytes in the minified (but not gzipped) version:
  var ArrayProto = Array.prototype, ObjProto = Object.prototype;
  var SymbolProto = typeof Symbol !== 'undefined' ? Symbol.prototype : null;

  // Create quick reference variables for speed access to core prototypes.
  var push = ArrayProto.push,
      slice = ArrayProto.slice,
      toString = ObjProto.toString,
      hasOwnProperty = ObjProto.hasOwnProperty;

  // All **ECMAScript 5** native function implementations that we hope to use
  // are declared here.
  var nativeIsArray = Array.isArray,
      nativeKeys = Object.keys,
      nativeCreate = Object.create;

  // Naked function reference for surrogate-prototype-swapping.
  var Ctor = function(){};

  // Create a safe reference to the Underscore object for use below.
  var _ = function(obj) {
    if (obj instanceof _) return obj;
    if (!(this instanceof _)) return new _(obj);
    this._wrapped = obj;
  };

  // Export the Underscore object for **Node.js**, with
  // backwards-compatibility for their old module API. If we're in
  // the browser, add `_` as a global object.
  // (`nodeType` is checked to ensure that `module`
  // and `exports` are not HTML elements.)
  if (typeof exports != 'undefined' && !exports.nodeType) {
    if (typeof module != 'undefined' && !module.nodeType && module.exports) {
      exports = module.exports = _;
    }
    exports._ = _;
  } else {
    root._ = _;
  }

  // Current version.
  _.VERSION = '1.9.1';

  // Internal function that returns an efficient (for current engines) version
  // of the passed-in callback, to be repeatedly applied in other Underscore
  // functions.
  var optimizeCb = function(func, context, argCount) {
    if (context === void 0) return func;
    switch (argCount == null ? 3 : argCount) {
      case 1: return function(value) {
        return func.call(context, value);
      };
      // The 2-argument case is omitted because we’re not using it.
      case 3: return function(value, index, collection) {
        return func.call(context, value, index, collection);
      };
      case 4: return function(accumulator, value, index, collection) {
        return func.call(context, accumulator, value, index, collection);
      };
    }
    return function() {
      return func.apply(context, arguments);
    };
  };

  var builtinIteratee;

  // An internal function to generate callbacks that can be applied to each
  // element in a collection, returning the desired result — either `identity`,
  // an arbitrary callback, a property matcher, or a property accessor.
  var cb = function(value, context, argCount) {
    if (_.iteratee !== builtinIteratee) return _.iteratee(value, context);
    if (value == null) return _.identity;
    if (_.isFunction(value)) return optimizeCb(value, context, argCount);
    if (_.isObject(value) && !_.isArray(value)) return _.matcher(value);
    return _.property(value);
  };

  // External wrapper for our callback generator. Users may customize
  // `_.iteratee` if they want additional predicate/iteratee shorthand styles.
  // This abstraction hides the internal-only argCount argument.
  _.iteratee = builtinIteratee = function(value, context) {
    return cb(value, context, Infinity);
  };

  // Some functions take a variable number of arguments, or a few expected
  // arguments at the beginning and then a variable number of values to operate
  // on. This helper accumulates all remaining arguments past the function’s
  // argument length (or an explicit `startIndex`), into an array that becomes
  // the last argument. Similar to ES6’s "rest parameter".
  var restArguments = function(func, startIndex) {
    startIndex = startIndex == null ? func.length - 1 : +startIndex;
    return function() {
      var length = Math.max(arguments.length - startIndex, 0),
          rest = Array(length),
          index = 0;
      for (; index < length; index++) {
        rest[index] = arguments[index + startIndex];
      }
      switch (startIndex) {
        case 0: return func.call(this, rest);
        case 1: return func.call(this, arguments[0], rest);
        case 2: return func.call(this, arguments[0], arguments[1], rest);
      }
      var args = Array(startIndex + 1);
      for (index = 0; index < startIndex; index++) {
        args[index] = arguments[index];
      }
      args[startIndex] = rest;
      return func.apply(this, args);
    };
  };

  // An internal function for creating a new object that inherits from another.
  var baseCreate = function(prototype) {
    if (!_.isObject(prototype)) return {};
    if (nativeCreate) return nativeCreate(prototype);
    Ctor.prototype = prototype;
    var result = new Ctor;
    Ctor.prototype = null;
    return result;
  };

  var shallowProperty = function(key) {
    return function(obj) {
      return obj == null ? void 0 : obj[key];
    };
  };

  var has = function(obj, path) {
    return obj != null && hasOwnProperty.call(obj, path);
  }

  var deepGet = function(obj, path) {
    var length = path.length;
    for (var i = 0; i < length; i++) {
      if (obj == null) return void 0;
      obj = obj[path[i]];
    }
    return length ? obj : void 0;
  };

  // Helper for collection methods to determine whether a collection
  // should be iterated as an array or as an object.
  // Related: http://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength
  // Avoids a very nasty iOS 8 JIT bug on ARM-64. #2094
  var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
  var getLength = shallowProperty('length');
  var isArrayLike = function(collection) {
    var length = getLength(collection);
    return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
  };

  // Collection Functions
  // --------------------

  // The cornerstone, an `each` implementation, aka `forEach`.
  // Handles raw objects in addition to array-likes. Treats all
  // sparse array-likes as if they were dense.
  _.each = _.forEach = function(obj, iteratee, context) {
    iteratee = optimizeCb(iteratee, context);
    var i, length;
    if (isArrayLike(obj)) {
      for (i = 0, length = obj.length; i < length; i++) {
        iteratee(obj[i], i, obj);
      }
    } else {
      var keys = _.keys(obj);
      for (i = 0, length = keys.length; i < length; i++) {
        iteratee(obj[keys[i]], keys[i], obj);
      }
    }
    return obj;
  };

  // Return the results of applying the iteratee to each element.
  _.map = _.collect = function(obj, iteratee, context) {
    iteratee = cb(iteratee, context);
    var keys = !isArrayLike(obj) && _.keys(obj),
        length = (keys || obj).length,
        results = Array(length);
    for (var index = 0; index < length; index++) {
      var currentKey = keys ? keys[index] : index;
      results[index] = iteratee(obj[currentKey], currentKey, obj);
    }
    return results;
  };

  // Create a reducing function iterating left or right.
  var createReduce = function(dir) {
    // Wrap code that reassigns argument variables in a separate function than
    // the one that accesses `arguments.length` to avoid a perf hit. (#1991)
    var reducer = function(obj, iteratee, memo, initial) {
      var keys = !isArrayLike(obj) && _.keys(obj),
          length = (keys || obj).length,
          index = dir > 0 ? 0 : length - 1;
      if (!initial) {
        memo = obj[keys ? keys[index] : index];
        index += dir;
      }
      for (; index >= 0 && index < length; index += dir) {
        var currentKey = keys ? keys[index] : index;
        memo = iteratee(memo, obj[currentKey], currentKey, obj);
      }
      return memo;
    };

    return function(obj, iteratee, memo, context) {
      var initial = arguments.length >= 3;
      return reducer(obj, optimizeCb(iteratee, context, 4), memo, initial);
    };
  };

  // **Reduce** builds up a single result from a list of values, aka `inject`,
  // or `foldl`.
  _.reduce = _.foldl = _.inject = createReduce(1);

  // The right-associative version of reduce, also known as `foldr`.
  _.reduceRight = _.foldr = createReduce(-1);

  // Return the first value which passes a truth test. Aliased as `detect`.
  _.find = _.detect = function(obj, predicate, context) {
    var keyFinder = isArrayLike(obj) ? _.findIndex : _.findKey;
    var key = keyFinder(obj, predicate, context);
    if (key !== void 0 && key !== -1) return obj[key];
  };

  // Return all the elements that pass a truth test.
  // Aliased as `select`.
  _.filter = _.select = function(obj, predicate, context) {
    var results = [];
    predicate = cb(predicate, context);
    _.each(obj, function(value, index, list) {
      if (predicate(value, index, list)) results.push(value);
    });
    return results;
  };

  // Return all the elements for which a truth test fails.
  _.reject = function(obj, predicate, context) {
    return _.filter(obj, _.negate(cb(predicate)), context);
  };

  // Determine whether all of the elements match a truth test.
  // Aliased as `all`.
  _.every = _.all = function(obj, predicate, context) {
    predicate = cb(predicate, context);
    var keys = !isArrayLike(obj) && _.keys(obj),
        length = (keys || obj).length;
    for (var index = 0; index < length; index++) {
      var currentKey = keys ? keys[index] : index;
      if (!predicate(obj[currentKey], currentKey, obj)) return false;
    }
    return true;
  };

  // Determine if at least one element in the object matches a truth test.
  // Aliased as `any`.
  _.some = _.any = function(obj, predicate, context) {
    predicate = cb(predicate, context);
    var keys = !isArrayLike(obj) && _.keys(obj),
        length = (keys || obj).length;
    for (var index = 0; index < length; index++) {
      var currentKey = keys ? keys[index] : index;
      if (predicate(obj[currentKey], currentKey, obj)) return true;
    }
    return false;
  };

  // Determine if the array or object contains a given item (using `===`).
  // Aliased as `includes` and `include`.
  _.contains = _.includes = _.include = function(obj, item, fromIndex, guard) {
    if (!isArrayLike(obj)) obj = _.values(obj);
    if (typeof fromIndex != 'number' || guard) fromIndex = 0;
    return _.indexOf(obj, item, fromIndex) >= 0;
  };

  // Invoke a method (with arguments) on every item in a collection.
  _.invoke = restArguments(function(obj, path, args) {
    var contextPath, func;
    if (_.isFunction(path)) {
      func = path;
    } else if (_.isArray(path)) {
      contextPath = path.slice(0, -1);
      path = path[path.length - 1];
    }
    return _.map(obj, function(context) {
      var method = func;
      if (!method) {
        if (contextPath && contextPath.length) {
          context = deepGet(context, contextPath);
        }
        if (context == null) return void 0;
        method = context[path];
      }
      return method == null ? method : method.apply(context, args);
    });
  });

  // Convenience version of a common use case of `map`: fetching a property.
  _.pluck = function(obj, key) {
    return _.map(obj, _.property(key));
  };

  // Convenience version of a common use case of `filter`: selecting only objects
  // containing specific `key:value` pairs.
  _.where = function(obj, attrs) {
    return _.filter(obj, _.matcher(attrs));
  };

  // Convenience version of a common use case of `find`: getting the first object
  // containing specific `key:value` pairs.
  _.findWhere = function(obj, attrs) {
    return _.find(obj, _.matcher(attrs));
  };

  // Return the maximum element (or element-based computation).
  _.max = function(obj, iteratee, context) {
    var result = -Infinity, lastComputed = -Infinity,
        value, computed;
    if (iteratee == null || typeof iteratee == 'number' && typeof obj[0] != 'object' && obj != null) {
      obj = isArrayLike(obj) ? obj : _.values(obj);
      for (var i = 0, length = obj.length; i < length; i++) {
        value = obj[i];
        if (value != null && value > result) {
          result = value;
        }
      }
    } else {
      iteratee = cb(iteratee, context);
      _.each(obj, function(v, index, list) {
        computed = iteratee(v, index, list);
        if (computed > lastComputed || computed === -Infinity && result === -Infinity) {
          result = v;
          lastComputed = computed;
        }
      });
    }
    return result;
  };

  // Return the minimum element (or element-based computation).
  _.min = function(obj, iteratee, context) {
    var result = Infinity, lastComputed = Infinity,
        value, computed;
    if (iteratee == null || typeof iteratee == 'number' && typeof obj[0] != 'object' && obj != null) {
      obj = isArrayLike(obj) ? obj : _.values(obj);
      for (var i = 0, length = obj.length; i < length; i++) {
        value = obj[i];
        if (value != null && value < result) {
          result = value;
        }
      }
    } else {
      iteratee = cb(iteratee, context);
      _.each(obj, function(v, index, list) {
        computed = iteratee(v, index, list);
        if (computed < lastComputed || computed === Infinity && result === Infinity) {
          result = v;
          lastComputed = computed;
        }
      });
    }
    return result;
  };

  // Shuffle a collection.
  _.shuffle = function(obj) {
    return _.sample(obj, Infinity);
  };

  // Sample **n** random values from a collection using the modern version of the
  // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle).
  // If **n** is not specified, returns a single random element.
  // The internal `guard` argument allows it to work with `map`.
  _.sample = function(obj, n, guard) {
    if (n == null || guard) {
      if (!isArrayLike(obj)) obj = _.values(obj);
      return obj[_.random(obj.length - 1)];
    }
    var sample = isArrayLike(obj) ? _.clone(obj) : _.values(obj);
    var length = getLength(sample);
    n = Math.max(Math.min(n, length), 0);
    var last = length - 1;
    for (var index = 0; index < n; index++) {
      var rand = _.random(index, last);
      var temp = sample[index];
      sample[index] = sample[rand];
      sample[rand] = temp;
    }
    return sample.slice(0, n);
  };

  // Sort the object's values by a criterion produced by an iteratee.
  _.sortBy = function(obj, iteratee, context) {
    var index = 0;
    iteratee = cb(iteratee, context);
    return _.pluck(_.map(obj, function(value, key, list) {
      return {
        value: value,
        index: index++,
        criteria: iteratee(value, key, list)
      };
    }).sort(function(left, right) {
      var a = left.criteria;
      var b = right.criteria;
      if (a !== b) {
        if (a > b || a === void 0) return 1;
        if (a < b || b === void 0) return -1;
      }
      return left.index - right.index;
    }), 'value');
  };

  // An internal function used for aggregate "group by" operations.
  var group = function(behavior, partition) {
    return function(obj, iteratee, context) {
      var result = partition ? [[], []] : {};
      iteratee = cb(iteratee, context);
      _.each(obj, function(value, index) {
        var key = iteratee(value, index, obj);
        behavior(result, value, key);
      });
      return result;
    };
  };

  // Groups the object's values by a criterion. Pass either a string attribute
  // to group by, or a function that returns the criterion.
  _.groupBy = group(function(result, value, key) {
    if (has(result, key)) result[key].push(value); else result[key] = [value];
  });

  // Indexes the object's values by a criterion, similar to `groupBy`, but for
  // when you know that your index values will be unique.
  _.indexBy = group(function(result, value, key) {
    result[key] = value;
  });

  // Counts instances of an object that group by a certain criterion. Pass
  // either a string attribute to count by, or a function that returns the
  // criterion.
  _.countBy = group(function(result, value, key) {
    if (has(result, key)) result[key]++; else result[key] = 1;
  });

  var reStrSymbol = /[^\ud800-\udfff]|[\ud800-\udbff][\udc00-\udfff]|[\ud800-\udfff]/g;
  // Safely create a real, live array from anything iterable.
  _.toArray = function(obj) {
    if (!obj) return [];
    if (_.isArray(obj)) return slice.call(obj);
    if (_.isString(obj)) {
      // Keep surrogate pair characters together
      return obj.match(reStrSymbol);
    }
    if (isArrayLike(obj)) return _.map(obj, _.identity);
    return _.values(obj);
  };

  // Return the number of elements in an object.
  _.size = function(obj) {
    if (obj == null) return 0;
    return isArrayLike(obj) ? obj.length : _.keys(obj).length;
  };

  // Split a collection into two arrays: one whose elements all satisfy the given
  // predicate, and one whose elements all do not satisfy the predicate.
  _.partition = group(function(result, value, pass) {
    result[pass ? 0 : 1].push(value);
  }, true);

  // Array Functions
  // ---------------

  // Get the first element of an array. Passing **n** will return the first N
  // values in the array. Aliased as `head` and `take`. The **guard** check
  // allows it to work with `_.map`.
  _.first = _.head = _.take = function(array, n, guard) {
    if (array == null || array.length < 1) return n == null ? void 0 : [];
    if (n == null || guard) return array[0];
    return _.initial(array, array.length - n);
  };

  // Returns everything but the last entry of the array. Especially useful on
  // the arguments object. Passing **n** will return all the values in
  // the array, excluding the last N.
  _.initial = function(array, n, guard) {
    return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n)));
  };

  // Get the last element of an array. Passing **n** will return the last N
  // values in the array.
  _.last = function(array, n, guard) {
    if (array == null || array.length < 1) return n == null ? void 0 : [];
    if (n == null || guard) return array[array.length - 1];
    return _.rest(array, Math.max(0, array.length - n));
  };

  // Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
  // Especially useful on the arguments object. Passing an **n** will return
  // the rest N values in the array.
  _.rest = _.tail = _.drop = function(array, n, guard) {
    return slice.call(array, n == null || guard ? 1 : n);
  };

  // Trim out all falsy values from an array.
  _.compact = function(array) {
    return _.filter(array, Boolean);
  };

  // Internal implementation of a recursive `flatten` function.
  var flatten = function(input, shallow, strict, output) {
    output = output || [];
    var idx = output.length;
    for (var i = 0, length = getLength(input); i < length; i++) {
      var value = input[i];
      if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) {
        // Flatten current level of array or arguments object.
        if (shallow) {
          var j = 0, len = value.length;
          while (j < len) output[idx++] = value[j++];
        } else {
          flatten(value, shallow, strict, output);
          idx = output.length;
        }
      } else if (!strict) {
        output[idx++] = value;
      }
    }
    return output;
  };

  // Flatten out an array, either recursively (by default), or just one level.
  _.flatten = function(array, shallow) {
    return flatten(array, shallow, false);
  };

  // Return a version of the array that does not contain the specified value(s).
  _.without = restArguments(function(array, otherArrays) {
    return _.difference(array, otherArrays);
  });

  // Produce a duplicate-free version of the array. If the array has already
  // been sorted, you have the option of using a faster algorithm.
  // The faster algorithm will not work with an iteratee if the iteratee
  // is not a one-to-one function, so providing an iteratee will disable
  // the faster algorithm.
  // Aliased as `unique`.
  _.uniq = _.unique = function(array, isSorted, iteratee, context) {
    if (!_.isBoolean(isSorted)) {
      context = iteratee;
      iteratee = isSorted;
      isSorted = false;
    }
    if (iteratee != null) iteratee = cb(iteratee, context);
    var result = [];
    var seen = [];
    for (var i = 0, length = getLength(array); i < length; i++) {
      var value = array[i],
          computed = iteratee ? iteratee(value, i, array) : value;
      if (isSorted && !iteratee) {
        if (!i || seen !== computed) result.push(value);
        seen = computed;
      } else if (iteratee) {
        if (!_.contains(seen, computed)) {
          seen.push(computed);
          result.push(value);
        }
      } else if (!_.contains(result, value)) {
        result.push(value);
      }
    }
    return result;
  };

  // Produce an array that contains the union: each distinct element from all of
  // the passed-in arrays.
  _.union = restArguments(function(arrays) {
    return _.uniq(flatten(arrays, true, true));
  });

  // Produce an array that contains every item shared between all the
  // passed-in arrays.
  _.intersection = function(array) {
    var result = [];
    var argsLength = arguments.length;
    for (var i = 0, length = getLength(array); i < length; i++) {
      var item = array[i];
      if (_.contains(result, item)) continue;
      var j;
      for (j = 1; j < argsLength; j++) {
        if (!_.contains(arguments[j], item)) break;
      }
      if (j === argsLength) result.push(item);
    }
    return result;
  };

  // Take the difference between one array and a number of other arrays.
  // Only the elements present in just the first array will remain.
  _.difference = restArguments(function(array, rest) {
    rest = flatten(rest, true, true);
    return _.filter(array, function(value){
      return !_.contains(rest, value);
    });
  });

  // Complement of _.zip. Unzip accepts an array of arrays and groups
  // each array's elements on shared indices.
  _.unzip = function(array) {
    var length = array && _.max(array, getLength).length || 0;
    var result = Array(length);

    for (var index = 0; index < length; index++) {
      result[index] = _.pluck(array, index);
    }
    return result;
  };

  // Zip together multiple lists into a single array -- elements that share
  // an index go together.
  _.zip = restArguments(_.unzip);

  // Converts lists into objects. Pass either a single array of `[key, value]`
  // pairs, or two parallel arrays of the same length -- one of keys, and one of
  // the corresponding values. Passing by pairs is the reverse of _.pairs.
  _.object = function(list, values) {
    var result = {};
    for (var i = 0, length = getLength(list); i < length; i++) {
      if (values) {
        result[list[i]] = values[i];
      } else {
        result[list[i][0]] = list[i][1];
      }
    }
    return result;
  };

  // Generator function to create the findIndex and findLastIndex functions.
  var createPredicateIndexFinder = function(dir) {
    return function(array, predicate, context) {
      predicate = cb(predicate, context);
      var length = getLength(array);
      var index = dir > 0 ? 0 : length - 1;
      for (; index >= 0 && index < length; index += dir) {
        if (predicate(array[index], index, array)) return index;
      }
      return -1;
    };
  };

  // Returns the first index on an array-like that passes a predicate test.
  _.findIndex = createPredicateIndexFinder(1);
  _.findLastIndex = createPredicateIndexFinder(-1);

  // Use a comparator function to figure out the smallest index at which
  // an object should be inserted so as to maintain order. Uses binary search.
  _.sortedIndex = function(array, obj, iteratee, context) {
    iteratee = cb(iteratee, context, 1);
    var value = iteratee(obj);
    var low = 0, high = getLength(array);
    while (low < high) {
      var mid = Math.floor((low + high) / 2);
      if (iteratee(array[mid]) < value) low = mid + 1; else high = mid;
    }
    return low;
  };

  // Generator function to create the indexOf and lastIndexOf functions.
  var createIndexFinder = function(dir, predicateFind, sortedIndex) {
    return function(array, item, idx) {
      var i = 0, length = getLength(array);
      if (typeof idx == 'number') {
        if (dir > 0) {
          i = idx >= 0 ? idx : Math.max(idx + length, i);
        } else {
          length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1;
        }
      } else if (sortedIndex && idx && length) {
        idx = sortedIndex(array, item);
        return array[idx] === item ? idx : -1;
      }
      if (item !== item) {
        idx = predicateFind(slice.call(array, i, length), _.isNaN);
        return idx >= 0 ? idx + i : -1;
      }
      for (idx = dir > 0 ? i : length - 1; idx >= 0 && idx < length; idx += dir) {
        if (array[idx] === item) return idx;
      }
      return -1;
    };
  };

  // Return the position of the first occurrence of an item in an array,
  // or -1 if the item is not included in the array.
  // If the array is large and already in sort order, pass `true`
  // for **isSorted** to use binary search.
  _.indexOf = createIndexFinder(1, _.findIndex, _.sortedIndex);
  _.lastIndexOf = createIndexFinder(-1, _.findLastIndex);

  // Generate an integer Array containing an arithmetic progression. A port of
  // the native Python `range()` function. See
  // [the Python documentation](http://docs.python.org/library/functions.html#range).
  _.range = function(start, stop, step) {
    if (stop == null) {
      stop = start || 0;
      start = 0;
    }
    if (!step) {
      step = stop < start ? -1 : 1;
    }

    var length = Math.max(Math.ceil((stop - start) / step), 0);
    var range = Array(length);

    for (var idx = 0; idx < length; idx++, start += step) {
      range[idx] = start;
    }

    return range;
  };

  // Chunk a single array into multiple arrays, each containing `count` or fewer
  // items.
  _.chunk = function(array, count) {
    if (count == null || count < 1) return [];
    var result = [];
    var i = 0, length = array.length;
    while (i < length) {
      result.push(slice.call(array, i, i += count));
    }
    return result;
  };

  // Function (ahem) Functions
  // ------------------

  // Determines whether to execute a function as a constructor
  // or a normal function with the provided arguments.
  var executeBound = function(sourceFunc, boundFunc, context, callingContext, args) {
    if (!(callingContext instanceof boundFunc)) return sourceFunc.apply(context, args);
    var self = baseCreate(sourceFunc.prototype);
    var result = sourceFunc.apply(self, args);
    if (_.isObject(result)) return result;
    return self;
  };

  // Create a function bound to a given object (assigning `this`, and arguments,
  // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
  // available.
  _.bind = restArguments(function(func, context, args) {
    if (!_.isFunction(func)) throw new TypeError('Bind must be called on a function');
    var bound = restArguments(function(callArgs) {
      return executeBound(func, bound, context, this, args.concat(callArgs));
    });
    return bound;
  });

  // Partially apply a function by creating a version that has had some of its
  // arguments pre-filled, without changing its dynamic `this` context. _ acts
  // as a placeholder by default, allowing any combination of arguments to be
  // pre-filled. Set `_.partial.placeholder` for a custom placeholder argument.
  _.partial = restArguments(function(func, boundArgs) {
    var placeholder = _.partial.placeholder;
    var bound = function() {
      var position = 0, length = boundArgs.length;
      var args = Array(length);
      for (var i = 0; i < length; i++) {
        args[i] = boundArgs[i] === placeholder ? arguments[position++] : boundArgs[i];
      }
      while (position < arguments.length) args.push(arguments[position++]);
      return executeBound(func, bound, this, this, args);
    };
    return bound;
  });

  _.partial.placeholder = _;

  // Bind a number of an object's methods to that object. Remaining arguments
  // are the method names to be bound. Useful for ensuring that all callbacks
  // defined on an object belong to it.
  _.bindAll = restArguments(function(obj, keys) {
    keys = flatten(keys, false, false);
    var index = keys.length;
    if (index < 1) throw new Error('bindAll must be passed function names');
    while (index--) {
      var key = keys[index];
      obj[key] = _.bind(obj[key], obj);
    }
  });

  // Memoize an expensive function by storing its results.
  _.memoize = function(func, hasher) {
    var memoize = function(key) {
      var cache = memoize.cache;
      var address = '' + (hasher ? hasher.apply(this, arguments) : key);
      if (!has(cache, address)) cache[address] = func.apply(this, arguments);
      return cache[address];
    };
    memoize.cache = {};
    return memoize;
  };

  // Delays a function for the given number of milliseconds, and then calls
  // it with the arguments supplied.
  _.delay = restArguments(function(func, wait, args) {
    return setTimeout(function() {
      return func.apply(null, args);
    }, wait);
  });

  // Defers a function, scheduling it to run after the current call stack has
  // cleared.
  _.defer = _.partial(_.delay, _, 1);

  // Returns a function, that, when invoked, will only be triggered at most once
  // during a given window of time. Normally, the throttled function will run
  // as much as it can, without ever going more than once per `wait` duration;
  // but if you'd like to disable the execution on the leading edge, pass
  // `{leading: false}`. To disable execution on the trailing edge, ditto.
  _.throttle = function(func, wait, options) {
    var timeout, context, args, result;
    var previous = 0;
    if (!options) options = {};

    var later = function() {
      previous = options.leading === false ? 0 : _.now();
      timeout = null;
      result = func.apply(context, args);
      if (!timeout) context = args = null;
    };

    var throttled = function() {
      var now = _.now();
      if (!previous && options.leading === false) previous = now;
      var remaining = wait - (now - previous);
      context = this;
      args = arguments;
      if (remaining <= 0 || remaining > wait) {
        if (timeout) {
          clearTimeout(timeout);
          timeout = null;
        }
        previous = now;
        result = func.apply(context, args);
        if (!timeout) context = args = null;
      } else if (!timeout && options.trailing !== false) {
        timeout = setTimeout(later, remaining);
      }
      return result;
    };

    throttled.cancel = function() {
      clearTimeout(timeout);
      previous = 0;
      timeout = context = args = null;
    };

    return throttled;
  };

  // Returns a function, that, as long as it continues to be invoked, will not
  // be triggered. The function will be called after it stops being called for
  // N milliseconds. If `immediate` is passed, trigger the function on the
  // leading edge, instead of the trailing.
  _.debounce = function(func, wait, immediate) {
    var timeout, result;

    var later = function(context, args) {
      timeout = null;
      if (args) result = func.apply(context, args);
    };

    var debounced = restArguments(function(args) {
      if (timeout) clearTimeout(timeout);
      if (immediate) {
        var callNow = !timeout;
        timeout = setTimeout(later, wait);
        if (callNow) result = func.apply(this, args);
      } else {
        timeout = _.delay(later, wait, this, args);
      }

      return result;
    });

    debounced.cancel = function() {
      clearTimeout(timeout);
      timeout = null;
    };

    return debounced;
  };

  // Returns the first function passed as an argument to the second,
  // allowing you to adjust arguments, run code before and after, and
  // conditionally execute the original function.
  _.wrap = function(func, wrapper) {
    return _.partial(wrapper, func);
  };

  // Returns a negated version of the passed-in predicate.
  _.negate = function(predicate) {
    return function() {
      return !predicate.apply(this, arguments);
    };
  };

  // Returns a function that is the composition of a list of functions, each
  // consuming the return value of the function that follows.
  _.compose = function() {
    var args = arguments;
    var start = args.length - 1;
    return function() {
      var i = start;
      var result = args[start].apply(this, arguments);
      while (i--) result = args[i].call(this, result);
      return result;
    };
  };

  // Returns a function that will only be executed on and after the Nth call.
  _.after = function(times, func) {
    return function() {
      if (--times < 1) {
        return func.apply(this, arguments);
      }
    };
  };

  // Returns a function that will only be executed up to (but not including) the Nth call.
  _.before = function(times, func) {
    var memo;
    return function() {
      if (--times > 0) {
        memo = func.apply(this, arguments);
      }
      if (times <= 1) func = null;
      return memo;
    };
  };

  // Returns a function that will be executed at most one time, no matter how
  // often you call it. Useful for lazy initialization.
  _.once = _.partial(_.before, 2);

  _.restArguments = restArguments;

  // Object Functions
  // ----------------

  // Keys in IE < 9 that won't be iterated by `for key in ...` and thus missed.
  var hasEnumBug = !{toString: null}.propertyIsEnumerable('toString');
  var nonEnumerableProps = ['valueOf', 'isPrototypeOf', 'toString',
    'propertyIsEnumerable', 'hasOwnProperty', 'toLocaleString'];

  var collectNonEnumProps = function(obj, keys) {
    var nonEnumIdx = nonEnumerableProps.length;
    var constructor = obj.constructor;
    var proto = _.isFunction(constructor) && constructor.prototype || ObjProto;

    // Constructor is a special case.
    var prop = 'constructor';
    if (has(obj, prop) && !_.contains(keys, prop)) keys.push(prop);

    while (nonEnumIdx--) {
      prop = nonEnumerableProps[nonEnumIdx];
      if (prop in obj && obj[prop] !== proto[prop] && !_.contains(keys, prop)) {
        keys.push(prop);
      }
    }
  };

  // Retrieve the names of an object's own properties.
  // Delegates to **ECMAScript 5**'s native `Object.keys`.
  _.keys = function(obj) {
    if (!_.isObject(obj)) return [];
    if (nativeKeys) return nativeKeys(obj);
    var keys = [];
    for (var key in obj) if (has(obj, key)) keys.push(key);
    // Ahem, IE < 9.
    if (hasEnumBug) collectNonEnumProps(obj, keys);
    return keys;
  };

  // Retrieve all the property names of an object.
  _.allKeys = function(obj) {
    if (!_.isObject(obj)) return [];
    var keys = [];
    for (var key in obj) keys.push(key);
    // Ahem, IE < 9.
    if (hasEnumBug) collectNonEnumProps(obj, keys);
    return keys;
  };

  // Retrieve the values of an object's properties.
  _.values = function(obj) {
    var keys = _.keys(obj);
    var length = keys.length;
    var values = Array(length);
    for (var i = 0; i < length; i++) {
      values[i] = obj[keys[i]];
    }
    return values;
  };

  // Returns the results of applying the iteratee to each element of the object.
  // In contrast to _.map it returns an object.
  _.mapObject = function(obj, iteratee, context) {
    iteratee = cb(iteratee, context);
    var keys = _.keys(obj),
        length = keys.length,
        results = {};
    for (var index = 0; index < length; index++) {
      var currentKey = keys[index];
      results[currentKey] = iteratee(obj[currentKey], currentKey, obj);
    }
    return results;
  };

  // Convert an object into a list of `[key, value]` pairs.
  // The opposite of _.object.
  _.pairs = function(obj) {
    var keys = _.keys(obj);
    var length = keys.length;
    var pairs = Array(length);
    for (var i = 0; i < length; i++) {
      pairs[i] = [keys[i], obj[keys[i]]];
    }
    return pairs;
  };

  // Invert the keys and values of an object. The values must be serializable.
  _.invert = function(obj) {
    var result = {};
    var keys = _.keys(obj);
    for (var i = 0, length = keys.length; i < length; i++) {
      result[obj[keys[i]]] = keys[i];
    }
    return result;
  };

  // Return a sorted list of the function names available on the object.
  // Aliased as `methods`.
  _.functions = _.methods = function(obj) {
    var names = [];
    for (var key in obj) {
      if (_.isFunction(obj[key])) names.push(key);
    }
    return names.sort();
  };

  // An internal function for creating assigner functions.
  var createAssigner = function(keysFunc, defaults) {
    return function(obj) {
      var length = arguments.length;
      if (defaults) obj = Object(obj);
      if (length < 2 || obj == null) return obj;
      for (var index = 1; index < length; index++) {
        var source = arguments[index],
            keys = keysFunc(source),
            l = keys.length;
        for (var i = 0; i < l; i++) {
          var key = keys[i];
          if (!defaults || obj[key] === void 0) obj[key] = source[key];
        }
      }
      return obj;
    };
  };

  // Extend a given object with all the properties in passed-in object(s).
  _.extend = createAssigner(_.allKeys);

  // Assigns a given object with all the own properties in the passed-in object(s).
  // (https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)
  _.extendOwn = _.assign = createAssigner(_.keys);

  // Returns the first key on an object that passes a predicate test.
  _.findKey = function(obj, predicate, context) {
    predicate = cb(predicate, context);
    var keys = _.keys(obj), key;
    for (var i = 0, length = keys.length; i < length; i++) {
      key = keys[i];
      if (predicate(obj[key], key, obj)) return key;
    }
  };

  // Internal pick helper function to determine if `obj` has key `key`.
  var keyInObj = function(value, key, obj) {
    return key in obj;
  };

  // Return a copy of the object only containing the whitelisted properties.
  _.pick = restArguments(function(obj, keys) {
    var result = {}, iteratee = keys[0];
    if (obj == null) return result;
    if (_.isFunction(iteratee)) {
      if (keys.length > 1) iteratee = optimizeCb(iteratee, keys[1]);
      keys = _.allKeys(obj);
    } else {
      iteratee = keyInObj;
      keys = flatten(keys, false, false);
      obj = Object(obj);
    }
    for (var i = 0, length = keys.length; i < length; i++) {
      var key = keys[i];
      var value = obj[key];
      if (iteratee(value, key, obj)) result[key] = value;
    }
    return result;
  });

  // Return a copy of the object without the blacklisted properties.
  _.omit = restArguments(function(obj, keys) {
    var iteratee = keys[0], context;
    if (_.isFunction(iteratee)) {
      iteratee = _.negate(iteratee);
      if (keys.length > 1) context = keys[1];
    } else {
      keys = _.map(flatten(keys, false, false), String);
      iteratee = function(value, key) {
        return !_.contains(keys, key);
      };
    }
    return _.pick(obj, iteratee, context);
  });

  // Fill in a given object with default properties.
  _.defaults = createAssigner(_.allKeys, true);

  // Creates an object that inherits from the given prototype object.
  // If additional properties are provided then they will be added to the
  // created object.
  _.create = function(prototype, props) {
    var result = baseCreate(prototype);
    if (props) _.extendOwn(result, props);
    return result;
  };

  // Create a (shallow-cloned) duplicate of an object.
  _.clone = function(obj) {
    if (!_.isObject(obj)) return obj;
    return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
  };

  // Invokes interceptor with the obj, and then returns obj.
  // The primary purpose of this method is to "tap into" a method chain, in
  // order to perform operations on intermediate results within the chain.
  _.tap = function(obj, interceptor) {
    interceptor(obj);
    return obj;
  };

  // Returns whether an object has a given set of `key:value` pairs.
  _.isMatch = function(object, attrs) {
    var keys = _.keys(attrs), length = keys.length;
    if (object == null) return !length;
    var obj = Object(object);
    for (var i = 0; i < length; i++) {
      var key = keys[i];
      if (attrs[key] !== obj[key] || !(key in obj)) return false;
    }
    return true;
  };


  // Internal recursive comparison function for `isEqual`.
  var eq, deepEq;
  eq = function(a, b, aStack, bStack) {
    // Identical objects are equal. `0 === -0`, but they aren't identical.
    // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
    if (a === b) return a !== 0 || 1 / a === 1 / b;
    // `null` or `undefined` only equal to itself (strict comparison).
    if (a == null || b == null) return false;
    // `NaN`s are equivalent, but non-reflexive.
    if (a !== a) return b !== b;
    // Exhaust primitive checks
    var type = typeof a;
    if (type !== 'function' && type !== 'object' && typeof b != 'object') return false;
    return deepEq(a, b, aStack, bStack);
  };

  // Internal recursive comparison function for `isEqual`.
  deepEq = function(a, b, aStack, bStack) {
    // Unwrap any wrapped objects.
    if (a instanceof _) a = a._wrapped;
    if (b instanceof _) b = b._wrapped;
    // Compare `[[Class]]` names.
    var className = toString.call(a);
    if (className !== toString.call(b)) return false;
    switch (className) {
      // Strings, numbers, regular expressions, dates, and booleans are compared by value.
      case '[object RegExp]':
      // RegExps are coerced to strings for comparison (Note: '' + /a/i === '/a/i')
      case '[object String]':
        // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
        // equivalent to `new String("5")`.
        return '' + a === '' + b;
      case '[object Number]':
        // `NaN`s are equivalent, but non-reflexive.
        // Object(NaN) is equivalent to NaN.
        if (+a !== +a) return +b !== +b;
        // An `egal` comparison is performed for other numeric values.
        return +a === 0 ? 1 / +a === 1 / b : +a === +b;
      case '[object Date]':
      case '[object Boolean]':
        // Coerce dates and booleans to numeric primitive values. Dates are compared by their
        // millisecond representations. Note that invalid dates with millisecond representations
        // of `NaN` are not equivalent.
        return +a === +b;
      case '[object Symbol]':
        return SymbolProto.valueOf.call(a) === SymbolProto.valueOf.call(b);
    }

    var areArrays = className === '[object Array]';
    if (!areArrays) {
      if (typeof a != 'object' || typeof b != 'object') return false;

      // Objects with different constructors are not equivalent, but `Object`s or `Array`s
      // from different frames are.
      var aCtor = a.constructor, bCtor = b.constructor;
      if (aCtor !== bCtor && !(_.isFunction(aCtor) && aCtor instanceof aCtor &&
                               _.isFunction(bCtor) && bCtor instanceof bCtor)
                          && ('constructor' in a && 'constructor' in b)) {
        return false;
      }
    }
    // Assume equality for cyclic structures. The algorithm for detecting cyclic
    // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.

    // Initializing stack of traversed objects.
    // It's done here since we only need them for objects and arrays comparison.
    aStack = aStack || [];
    bStack = bStack || [];
    var length = aStack.length;
    while (length--) {
      // Linear search. Performance is inversely proportional to the number of
      // unique nested structures.
      if (aStack[length] === a) return bStack[length] === b;
    }

    // Add the first object to the stack of traversed objects.
    aStack.push(a);
    bStack.push(b);

    // Recursively compare objects and arrays.
    if (areArrays) {
      // Compare array lengths to determine if a deep comparison is necessary.
      length = a.length;
      if (length !== b.length) return false;
      // Deep compare the contents, ignoring non-numeric properties.
      while (length--) {
        if (!eq(a[length], b[length], aStack, bStack)) return false;
      }
    } else {
      // Deep compare objects.
      var keys = _.keys(a), key;
      length = keys.length;
      // Ensure that both objects contain the same number of properties before comparing deep equality.
      if (_.keys(b).length !== length) return false;
      while (length--) {
        // Deep compare each member
        key = keys[length];
        if (!(has(b, key) && eq(a[key], b[key], aStack, bStack))) return false;
      }
    }
    // Remove the first object from the stack of traversed objects.
    aStack.pop();
    bStack.pop();
    return true;
  };

  // Perform a deep comparison to check if two objects are equal.
  _.isEqual = function(a, b) {
    return eq(a, b);
  };

  // Is a given array, string, or object empty?
  // An "empty" object has no enumerable own-properties.
  _.isEmpty = function(obj) {
    if (obj == null) return true;
    if (isArrayLike(obj) && (_.isArray(obj) || _.isString(obj) || _.isArguments(obj))) return obj.length === 0;
    return _.keys(obj).length === 0;
  };

  // Is a given value a DOM element?
  _.isElement = function(obj) {
    return !!(obj && obj.nodeType === 1);
  };

  // Is a given value an array?
  // Delegates to ECMA5's native Array.isArray
  _.isArray = nativeIsArray || function(obj) {
    return toString.call(obj) === '[object Array]';
  };

  // Is a given variable an object?
  _.isObject = function(obj) {
    var type = typeof obj;
    return type === 'function' || type === 'object' && !!obj;
  };

  // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp, isError, isMap, isWeakMap, isSet, isWeakSet.
  _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error', 'Symbol', 'Map', 'WeakMap', 'Set', 'WeakSet'], function(name) {
    _['is' + name] = function(obj) {
      return toString.call(obj) === '[object ' + name + ']';
    };
  });

  // Define a fallback version of the method in browsers (ahem, IE < 9), where
  // there isn't any inspectable "Arguments" type.
  if (!_.isArguments(arguments)) {
    _.isArguments = function(obj) {
      return has(obj, 'callee');
    };
  }

  // Optimize `isFunction` if appropriate. Work around some typeof bugs in old v8,
  // IE 11 (#1621), Safari 8 (#1929), and PhantomJS (#2236).
  var nodelist = root.document && root.document.childNodes;
  if (typeof /./ != 'function' && typeof Int8Array != 'object' && typeof nodelist != 'function') {
    _.isFunction = function(obj) {
      return typeof obj == 'function' || false;
    };
  }

  // Is a given object a finite number?
  _.isFinite = function(obj) {
    return !_.isSymbol(obj) && isFinite(obj) && !isNaN(parseFloat(obj));
  };

  // Is the given value `NaN`?
  _.isNaN = function(obj) {
    return _.isNumber(obj) && isNaN(obj);
  };

  // Is a given value a boolean?
  _.isBoolean = function(obj) {
    return obj === true || obj === false || toString.call(obj) === '[object Boolean]';
  };

  // Is a given value equal to null?
  _.isNull = function(obj) {
    return obj === null;
  };

  // Is a given variable undefined?
  _.isUndefined = function(obj) {
    return obj === void 0;
  };

  // Shortcut function for checking if an object has a given property directly
  // on itself (in other words, not on a prototype).
  _.has = function(obj, path) {
    if (!_.isArray(path)) {
      return has(obj, path);
    }
    var length = path.length;
    for (var i = 0; i < length; i++) {
      var key = path[i];
      if (obj == null || !hasOwnProperty.call(obj, key)) {
        return false;
      }
      obj = obj[key];
    }
    return !!length;
  };

  // Utility Functions
  // -----------------

  // Run Underscore.js in *noConflict* mode, returning the `_` variable to its
  // previous owner. Returns a reference to the Underscore object.
  _.noConflict = function() {
    root._ = previousUnderscore;
    return this;
  };

  // Keep the identity function around for default iteratees.
  _.identity = function(value) {
    return value;
  };

  // Predicate-generating functions. Often useful outside of Underscore.
  _.constant = function(value) {
    return function() {
      return value;
    };
  };

  _.noop = function(){};

  // Creates a function that, when passed an object, will traverse that object’s
  // properties down the given `path`, specified as an array of keys or indexes.
  _.property = function(path) {
    if (!_.isArray(path)) {
      return shallowProperty(path);
    }
    return function(obj) {
      return deepGet(obj, path);
    };
  };

  // Generates a function for a given object that returns a given property.
  _.propertyOf = function(obj) {
    if (obj == null) {
      return function(){};
    }
    return function(path) {
      return !_.isArray(path) ? obj[path] : deepGet(obj, path);
    };
  };

  // Returns a predicate for checking whether an object has a given set of
  // `key:value` pairs.
  _.matcher = _.matches = function(attrs) {
    attrs = _.extendOwn({}, attrs);
    return function(obj) {
      return _.isMatch(obj, attrs);
    };
  };

  // Run a function **n** times.
  _.times = function(n, iteratee, context) {
    var accum = Array(Math.max(0, n));
    iteratee = optimizeCb(iteratee, context, 1);
    for (var i = 0; i < n; i++) accum[i] = iteratee(i);
    return accum;
  };

  // Return a random integer between min and max (inclusive).
  _.random = function(min, max) {
    if (max == null) {
      max = min;
      min = 0;
    }
    return min + Math.floor(Math.random() * (max - min + 1));
  };

  // A (possibly faster) way to get the current timestamp as an integer.
  _.now = Date.now || function() {
    return new Date().getTime();
  };

  // List of HTML entities for escaping.
  var escapeMap = {
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;',
    '"': '&quot;',
    "'": '&#x27;',
    '`': '&#x60;'
  };
  var unescapeMap = _.invert(escapeMap);

  // Functions for escaping and unescaping strings to/from HTML interpolation.
  var createEscaper = function(map) {
    var escaper = function(match) {
      return map[match];
    };
    // Regexes for identifying a key that needs to be escaped.
    var source = '(?:' + _.keys(map).join('|') + ')';
    var testRegexp = RegExp(source);
    var replaceRegexp = RegExp(source, 'g');
    return function(string) {
      string = string == null ? '' : '' + string;
      return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string;
    };
  };
  _.escape = createEscaper(escapeMap);
  _.unescape = createEscaper(unescapeMap);

  // Traverses the children of `obj` along `path`. If a child is a function, it
  // is invoked with its parent as context. Returns the value of the final
  // child, or `fallback` if any child is undefined.
  _.result = function(obj, path, fallback) {
    if (!_.isArray(path)) path = [path];
    var length = path.length;
    if (!length) {
      return _.isFunction(fallback) ? fallback.call(obj) : fallback;
    }
    for (var i = 0; i < length; i++) {
      var prop = obj == null ? void 0 : obj[path[i]];
      if (prop === void 0) {
        prop = fallback;
        i = length; // Ensure we don't continue iterating.
      }
      obj = _.isFunction(prop) ? prop.call(obj) : prop;
    }
    return obj;
  };

  // Generate a unique integer id (unique within the entire client session).
  // Useful for temporary DOM ids.
  var idCounter = 0;
  _.uniqueId = function(prefix) {
    var id = ++idCounter + '';
    return prefix ? prefix + id : id;
  };

  // By default, Underscore uses ERB-style template delimiters, change the
  // following template settings to use alternative delimiters.
  _.templateSettings = {
    evaluate: /<%([\s\S]+?)%>/g,
    interpolate: /<%=([\s\S]+?)%>/g,
    escape: /<%-([\s\S]+?)%>/g
  };

  // When customizing `templateSettings`, if you don't want to define an
  // interpolation, evaluation or escaping regex, we need one that is
  // guaranteed not to match.
  var noMatch = /(.)^/;

  // Certain characters need to be escaped so that they can be put into a
  // string literal.
  var escapes = {
    "'": "'",
    '\\': '\\',
    '\r': 'r',
    '\n': 'n',
    '\u2028': 'u2028',
    '\u2029': 'u2029'
  };

  var escapeRegExp = /\\|'|\r|\n|\u2028|\u2029/g;

  var escapeChar = function(match) {
    return '\\' + escapes[match];
  };

  // JavaScript micro-templating, similar to John Resig's implementation.
  // Underscore templating handles arbitrary delimiters, preserves whitespace,
  // and correctly escapes quotes within interpolated code.
  // NB: `oldSettings` only exists for backwards compatibility.
  _.template = function(text, settings, oldSettings) {
    if (!settings && oldSettings) settings = oldSettings;
    settings = _.defaults({}, settings, _.templateSettings);

    // Combine delimiters into one regular expression via alternation.
    var matcher = RegExp([
      (settings.escape || noMatch).source,
      (settings.interpolate || noMatch).source,
      (settings.evaluate || noMatch).source
    ].join('|') + '|$', 'g');

    // Compile the template source, escaping string literals appropriately.
    var index = 0;
    var source = "__p+='";
    text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
      source += text.slice(index, offset).replace(escapeRegExp, escapeChar);
      index = offset + match.length;

      if (escape) {
        source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
      } else if (interpolate) {
        source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
      } else if (evaluate) {
        source += "';\n" + evaluate + "\n__p+='";
      }

      // Adobe VMs need the match returned to produce the correct offset.
      return match;
    });
    source += "';\n";

    // If a variable is not specified, place data values in local scope.
    if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';

    source = "var __t,__p='',__j=Array.prototype.join," +
      "print=function(){__p+=__j.call(arguments,'');};\n" +
      source + 'return __p;\n';

    var render;
    try {
      render = new Function(settings.variable || 'obj', '_', source);
    } catch (e) {
      e.source = source;
      throw e;
    }

    var template = function(data) {
      return render.call(this, data, _);
    };

    // Provide the compiled source as a convenience for precompilation.
    var argument = settings.variable || 'obj';
    template.source = 'function(' + argument + '){\n' + source + '}';

    return template;
  };

  // Add a "chain" function. Start chaining a wrapped Underscore object.
  _.chain = function(obj) {
    var instance = _(obj);
    instance._chain = true;
    return instance;
  };

  // OOP
  // ---------------
  // If Underscore is called as a function, it returns a wrapped object that
  // can be used OO-style. This wrapper holds altered versions of all the
  // underscore functions. Wrapped objects may be chained.

  // Helper function to continue chaining intermediate results.
  var chainResult = function(instance, obj) {
    return instance._chain ? _(obj).chain() : obj;
  };

  // Add your own custom functions to the Underscore object.
  _.mixin = function(obj) {
    _.each(_.functions(obj), function(name) {
      var func = _[name] = obj[name];
      _.prototype[name] = function() {
        var args = [this._wrapped];
        push.apply(args, arguments);
        return chainResult(this, func.apply(_, args));
      };
    });
    return _;
  };

  // Add all of the Underscore functions to the wrapper object.
  _.mixin(_);

  // Add all mutator Array functions to the wrapper.
  _.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
    var method = ArrayProto[name];
    _.prototype[name] = function() {
      var obj = this._wrapped;
      method.apply(obj, arguments);
      if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0];
      return chainResult(this, obj);
    };
  });

  // Add all accessor Array functions to the wrapper.
  _.each(['concat', 'join', 'slice'], function(name) {
    var method = ArrayProto[name];
    _.prototype[name] = function() {
      return chainResult(this, method.apply(this._wrapped, arguments));
    };
  });

  // Extracts the result from a wrapped and chained object.
  _.prototype.value = function() {
    return this._wrapped;
  };

  // Provide unwrapping proxy for some methods used in engine operations
  // such as arithmetic and JSON stringification.
  _.prototype.valueOf = _.prototype.toJSON = _.prototype.value;

  _.prototype.toString = function() {
    return String(this._wrapped);
  };

  // AMD registration happens at the end for compatibility with AMD loaders
  // that may not enforce next-turn semantics on modules. Even though general
  // practice for AMD registration is to be anonymous, underscore registers
  // as a named module because, like jQuery, it is a base library that is
  // popular enough to be bundled in a third party lib, but not be part of
  // an AMD load request. Those cases could generate an error when an
  // anonymous define() is called outside of a loader request.
  if (true) {
    !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_RESULT__ = (function() {
      return _;
    }).apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
				__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
  }
}());

/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(20), __webpack_require__(21)(module)))

/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {

(function webpackUniversalModuleDefinition(root, factory) {
	if(true)
		module.exports = factory((function webpackLoadOptionalExternalModule() { try { return __webpack_require__(22); } catch(e) {} }()), (function webpackLoadOptionalExternalModule() { try { return __webpack_require__(23); } catch(e) {} }()));
	else if(typeof define === 'function' && define.amd)
		define(["hammerjs", "d3"], factory);
	else if(typeof exports === 'object')
		exports["geo"] = factory((function webpackLoadOptionalExternalModule() { try { return require("hammerjs"); } catch(e) {} }()), (function webpackLoadOptionalExternalModule() { try { return require("d3"); } catch(e) {} }()));
	else
		root["geo"] = factory(root["Hammer"], root["d3"]);
})(typeof self !== 'undefined' ? self : this, function(__WEBPACK_EXTERNAL_MODULE_283__, __WEBPACK_EXTERNAL_MODULE_284__) {
return /******/ (function(modules) { // webpackBootstrap
/******/ 	// The module cache
/******/ 	var installedModules = {};
/******/
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/
/******/ 		// Check if module is in cache
/******/ 		if(installedModules[moduleId]) {
/******/ 			return installedModules[moduleId].exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = installedModules[moduleId] = {
/******/ 			i: moduleId,
/******/ 			l: false,
/******/ 			exports: {}
/******/ 		};
/******/
/******/ 		// Execute the module function
/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ 		// Flag the module as loaded
/******/ 		module.l = true;
/******/
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/
/******/
/******/ 	// expose the modules object (__webpack_modules__)
/******/ 	__webpack_require__.m = modules;
/******/
/******/ 	// expose the module cache
/******/ 	__webpack_require__.c = installedModules;
/******/
/******/ 	// define getter function for harmony exports
/******/ 	__webpack_require__.d = function(exports, name, getter) {
/******/ 		if(!__webpack_require__.o(exports, name)) {
/******/ 			Object.defineProperty(exports, name, {
/******/ 				configurable: false,
/******/ 				enumerable: true,
/******/ 				get: getter
/******/ 			});
/******/ 		}
/******/ 	};
/******/
/******/ 	// getDefaultExport function for compatibility with non-harmony modules
/******/ 	__webpack_require__.n = function(module) {
/******/ 		var getter = module && module.__esModule ?
/******/ 			function getDefault() { return module['default']; } :
/******/ 			function getModuleExports() { return module; };
/******/ 		__webpack_require__.d(getter, 'a', getter);
/******/ 		return getter;
/******/ 	};
/******/
/******/ 	// Object.prototype.hasOwnProperty.call
/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ 	// __webpack_public_path__
/******/ 	__webpack_require__.p = "dist/built";
/******/
/******/ 	// Load entry module and return exports
/******/ 	return __webpack_require__(__webpack_require__.s = 133);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


function newfunc() {
  return function () {};
}

/**
 * Convenient function to define JS inheritance.
 *
 * @param {object} C Child class instance.
 * @param {object} P Parent class instance.
 */
module.exports = function (C, P) {
  var F = newfunc();
  F.prototype = P.prototype;
  C.prototype = new F();
  C.prototype.constructor = C;
};

/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var $ = __webpack_require__(2);
var widgets = {
  dom: {}
};
var layers = {};
var layerDefaultFeatures = {};
var renderers = {};
var features = {};
var featureCapabilities = {};
var fileReaders = {};
var rendererLayerAdjustments = {};
var annotations = {};
var util = {};

/**
 * Register a new file reader type.
 *
 * @alias geo.registerFileReader
 * @param {string} name Name of the reader to register.  If the name already
 *      exists, the class creation function is replaced.
 * @param {function} func Class creation function.
 */
util.registerFileReader = function (name, func) {
  fileReaders[name] = func;
};

/**
 * Create a new file reader.
 *
 * @alias geo.createFileReader
 * @param {string} name Name of the reader to create.
 * @param {object} opts Options for the new reader.
 * @returns {geo.fileReader|null} The new reader or null if no such name is
 *      registered.
 */
util.createFileReader = function (name, opts) {
  if (fileReaders.hasOwnProperty(name)) {
    return fileReaders[name](opts);
  }
  return null;
};

/**
 * Register a new renderer type.
 *
 * @alias geo.registerRenderer
 * @param {string} name Name of the renderer to register.  If the name already
 *      exists, the class creation function is replaced.
 * @param {function} func Class creation function.
 */
util.registerRenderer = function (name, func) {
  renderers[name] = func;
};

/**
 * Create new instance of the renderer.
 *
 * @alias geo.createRenderer
 * @param {string} name Name of the renderer to create.
 * @param {geo.layer} layer The layer associated with the renderer.
 * @param {HTMLCanvasElement} [canvas] A canvas object to share between
 *      renderers.
 * @param {object} options Options for the new renderer.
 * @returns {geo.renderer|null} The new renderer or null if no such name is
 *      registered.
 */
util.createRenderer = function (name, layer, canvas, options) {
  if (renderers.hasOwnProperty(name)) {
    var ren = renderers[name]({ layer: layer, canvas: canvas, options: options });
    ren._init();
    return ren;
  }
  return null;
};

/**
 * Check if the named renderer is supported.  If not, display a warning and get
 * the name of a fallback renderer.  Ideally, we would pass a list of desired
 * features, and, if the renderer is unavailable, this would choose a fallback
 * that would support those features.
 *
 * @alias geo.checkRenderer
 * @param {string|null} name Name of the desired renderer.
 * @param {boolean} noFallback If truthy, don't recommend a fallback.
 * @returns {string|null|false} The name of the renderer that should be used
 *      or false if no valid renderer can be determined.
 */
util.checkRenderer = function (name, noFallback) {
  if (name === null) {
    return name;
  }
  if (renderers.hasOwnProperty(name)) {
    var ren = renderers[name];
    if (!ren.supported || ren.supported()) {
      return name;
    }
    if (!ren.fallback || noFallback) {
      return false;
    }
    var fallback = util.checkRenderer(ren.fallback(), true);
    if (fallback !== false) {
      console.warn(name + ' renderer is unavailable, using ' + fallback + ' renderer instead');
    }
    return fallback;
  }
  return false;
};

/**
 * Check if there is a renderer that is supported and supports a list of
 * features.  If not, display a warning.  This picks the first renderer that
 * supports all of the listed features.
 *
 * @alias geo.rendererForFeatures
 * @param {string[]|undefined} featureList A list of features that will be used
 *      with this renderer.  Features are the basic feature names (e.g.,
 *      `'quad'`), or the feature name followed by a required capability (e.g.,
 *      `'quad.image'`).  If more than one feature or more than one capability
 *      of a feature is required, include each feature and capability
 *      combination in the list (e.g., `['quad.image', 'plane']`).  If no
 *      capability is specified for a feature (or that feature was registered
 *      without a capability object), then the feature will match regardless of
 *      capabilities.
 * @returns {string|null|false} The name of the renderer that should be used
 *      or false if no valid renderer can be determined.
 */
util.rendererForFeatures = function (featureList) {
  var preferredRenderers = ['vgl', 'canvas', 'd3', null];

  var renderer, ridx, feature, fidx, capability, available;
  for (ridx = 0; ridx < preferredRenderers.length; ridx += 1) {
    renderer = preferredRenderers[ridx];
    if (util.checkRenderer(renderer, true) === false) {
      continue;
    }
    if (!featureList) {
      return renderer;
    }
    if (!features[renderer]) {
      continue;
    }
    available = true;
    for (fidx = 0; fidx < featureList.length; fidx += 1) {
      feature = featureList[fidx];
      capability = null;
      if (feature.indexOf('.') >= 0) {
        capability = feature;
        feature = feature.substr(0, feature.indexOf('.'));
      }
      if (features[renderer][feature] === undefined) {
        available = false;
        break;
      }
      if (capability && featureCapabilities[renderer][feature] && !featureCapabilities[renderer][feature][capability]) {
        available = false;
        break;
      }
    }
    if (available) {
      return renderer;
    }
  }
  console.warn('There is no renderer available for the feature list "' + (featureList || []).join(', ') + '".');
  return false;
};

/**
 * Register a new feature type.
 *
 * @alias geo.registerFeature
 * @param {string} category The feature category -- this is the renderer name.
 * @param {string} name The feature name.
 * @param {function} func A function to call to create the feature.
 * @param {object|undefined} capabilities A map of capabilities that this
 *      feature supports.  If the feature is implemented with different
 *      capabilities in multiple categories (renderers), then the feature
 *      should expose a simple dictionary of supported and unsupported
 *      features.  For instance, the quad feature has color quads, image quads,
 *      and image quads that support full transformations.  The capabilities
 *      should be defined in the base feature in a capabilities object so that
 *      they can be referenced by that rather than an explicit string.
 * @returns {object} if this feature replaces an existing one, this was the
 *      feature that was replaced.  In this case, a warning is issued.
 */
util.registerFeature = function (category, name, func, capabilities) {
  if (!(category in features)) {
    features[category] = {};
    featureCapabilities[category] = {};
  }

  var old = features[category][name];
  if (old) {
    console.warn('The ' + category + '.' + name + ' feature is already registered');
  }
  features[category][name] = func;
  featureCapabilities[category][name] = capabilities;
  return old;
};

/**
 * Create new instance of a feature.
 *
 * @alias geo.createFeature
 * @param {string} name Name of the feature to create.
 * @param {geo.layer} layer The layer associated with the feature.
 * @param {geo.renderer} renderer The renderer associated with the feature.
 *      This is usually `layer.renderer()`.
 * @param {object} arg Options for the new feature.
 * @returns {geo.feature|null} The new feature or null if no such name is
 *      registered.
 */
util.createFeature = function (name, layer, renderer, arg) {
  var category = renderer.api(),
      options = { 'layer': layer, 'renderer': renderer };
  if (category in features && name in features[category]) {
    if (arg !== undefined) {
      $.extend(true, options, arg);
    }
    var feature = features[category][name](options);
    if (layer.gcs === undefined) {
      layer.gcs = function () {
        return layer.map().gcs();
      };
    }
    return feature;
  }
  return null;
};

/**
 * Register a layer adjustment.  Layer adjustments are appiled to specified
 * layers when they are created as a method to mixin specific changes,
 * usually based on the renderer used for that layer.
 *
 * @alias geo.registerLayerAdjustment
 * @param {string} category The category for the adjustment; this is commonly
 *      the renderer name.
 * @param {string} name The name of the adjustement.
 * @param {function} func The function to call when the adjustment is used.
 * @returns {object} if this layer adjustment replaces an existing one, this
 *      was the value that was replaced.  In this case, a warning is issued.
 */
util.registerLayerAdjustment = function (category, name, func) {
  if (!(category in rendererLayerAdjustments)) {
    rendererLayerAdjustments[category] = {};
  }

  var old = rendererLayerAdjustments[category][name];
  if (old) {
    console.warn('The ' + category + '.' + name + ' layer adjustment is already registered');
  }
  rendererLayerAdjustments[category][name] = func;
  return old;
};

/**
 * If a layer needs to be adjusted based on the renderer, call the function
 * that adjusts it.
 *
 * @alias geo.adjustLayerForRenderer
 * @param {string} name Name of the layer.
 * @param {object} layer Instantiated layer object.
 */
util.adjustLayerForRenderer = function (name, layer) {
  var rendererName = layer.rendererName();
  if (rendererName) {
    if (rendererLayerAdjustments && rendererLayerAdjustments[rendererName] && rendererLayerAdjustments[rendererName][name]) {
      rendererLayerAdjustments[rendererName][name].apply(layer);
    }
  }
};

/**
 * Register a new layer type.
 *
 * @alias geo.registerLayer
 * @param {string} name Name of the layer to register.  If the name already
 *      exists, the class creation function is replaced.
 * @param {function} func Class creation function.
 * @param {string[]} [defaultFeatures] An optional list of feature capabilities
 *      that are required to use this layer.
 */
util.registerLayer = function (name, func, defaultFeatures) {
  layers[name] = func;
  layerDefaultFeatures[name] = defaultFeatures;
};

/**
 * Create new instance of the layer.
 *
 * @alias geo.createLayer
 * @param {string} name Name of the layer to create.
 * @param {geo.map} map The map class instance that owns the layer.
 * @param {object} arg Options for the new layer.
 * @returns {geo.layer|null} The new layer or null if no such name is
 *      registered.
 */
util.createLayer = function (name, map, arg) {
  // Default renderer is vgl
  console.log('util.createLayer()')
  var options = { map: map },
      layer = null;

  if (name in layers) {
    if (!arg.renderer && !arg.features && layerDefaultFeatures) {
      options.features = layerDefaultFeatures[name];
    }
    if (arg !== undefined) {
      $.extend(true, options, arg);
    }
    layer = layers[name](options);
    layer.layerName = name;
    layer._init();
    return layer;
  } else {
    return null;
  }
};

/**
 * Register a new widget type.
 *
 * @alias geo.registerWidget
 * @param {string} category A category for this widget.  This is usually
 *      `'dom'`.
 * @param {string} name The name of the widget to register.
 * @param {function} func Class creation function.
 * @returns {object} If this widget replaces an existing one, this was the
 *      value that was replaced.  In this case, a warning is issued.
 */
util.registerWidget = function (category, name, func) {
  if (!(category in widgets)) {
    widgets[category] = {};
  }

  var old = widgets[category][name];
  if (old) {
    console.warn('The ' + category + '.' + name + ' widget is already registered');
  }
  widgets[category][name] = func;
  return old;
};

/**
 * Create new instance of a dom widget.
 *
 * @alias geo.createWidget
 * @param {string} name Name of the widget to create.
 * @param {geo.layer} layer The layer associated with the widget.
 * @param {object} arg Options for the new widget.
 * @returns {geo.widget} The new widget.
 */
util.createWidget = function (name, layer, arg) {
  var options = {
    layer: layer
  };

  if (name in widgets.dom) {
    if (arg !== undefined) {
      $.extend(true, options, arg);
    }

    return widgets.dom[name](options);
  }

  throw new Error('Cannot create unknown widget ' + name);
};

/**
 * Register a new annotation type.
 *
 * @alias geo.registerAnnotation
 * @param {string} name The annotation name.
 * @param {function} func A function to call to create the annotation.
 * @param {object|undefined} features A map of features that are used by this
 *      annotation.  Each key is a feature that is used.  If the value is true,
 *      the that feature is always needed.  If a list, then it is the set of
 *      annotation states for which that feature is required.  These can be
 *      used to pick an appropriate renderer when creating an annotation layer.
 * @returns {object} if this annotation replaces an existing one, this was the
 *      value that was replaced.  In this case, a warning is issued.
 */
util.registerAnnotation = function (name, func, features) {
  var old = annotations[name];
  if (old) {
    console.warn('The ' + name + ' annotation is already registered');
  }
  annotations[name] = { func: func, features: features || {} };
  return old;
};

/**
 * Create an annotation based on a registered type.
 *
 * @alias geo.createAnnotation
 * @param {string} name The annotation name
 * @param {object} options The options for the annotation.
 * @returns {object} the new annotation.
 */
util.createAnnotation = function (name, options) {
  if (!annotations[name]) {
    console.warn('The ' + name + ' annotation is not registered');
    return;
  }
  var annotation = annotations[name].func(options);
  return annotation;
};

/**
 * Get a list of registered annotation types.
 *
 * @alias geo.listAnnotations
 * @returns {string[]} A list of registered annotations.
 */
util.listAnnotations = function () {
  return Object.keys(annotations);
};

/**
 * Get a list of required features for a set of annotations.
 *
 * @alias geo.featuresForAnnotations
 * @param {string[]|object|undefined} annotationList A list of annotations that
 *   will be used.  Instead of a list, if this is an object, the keys are the
 *   annotation names, and the values are each a list of modes that will be
 *   used with that annotation.  For example, ['polygon', 'rectangle'] lists
 *   features required to show those annotations in any mode,  whereas
 *   {polygon: [annotationState.done], rectangle: [annotationState.done]} only
 *   lists features that are needed to show the completed annotations.
 * @returns {string[]} a list of features needed for the specified annotations.
 *   There may be duplicates in the list.
 */
util.featuresForAnnotations = function (annotationList) {
  var features = [];

  var annList = Array.isArray(annotationList) ? annotationList : Object.keys(annotationList);
  annList.forEach(function (ann) {
    if (!annotations[ann]) {
      return;
    }
    Object.keys(annotations[ann].features).forEach(function (feature) {
      if (Array.isArray(annotationList) || annotationList[ann] === true || !Array.isArray(annotations[ann].features[feature])) {
        features.push(feature);
      } else {
        annotationList[ann].forEach(function (state) {
          if ($.inArray(state, annotations[ann].features[feature]) >= 0) {
            features.push(feature);
          }
        });
      }
    });
  });
  return features;
};

/**
 * Check if there is a renderer that is supported and supports a list of
 * annotations.  If not, display a warning.  This generates a list of required
 * features, then picks the first renderer that supports all of these features.
 *
 * @alias geo.rendererForAnnotations
 * @param {string[]|object|undefined} annotationList A list of annotations that
 *   will be used with this renderer.  Instead of a list, if this is an object,
 *   the keys are the annotation names, and the values are each a list of modes
 *   that will be used with that annotation.  See featuresForAnnotations for
 *   more details.
 * @returns {string|null|false} the name of the renderer that should be used or
 *   false if no valid renderer can be determined.
 */
util.rendererForAnnotations = function (annotationList) {
  return util.rendererForFeatures(util.featuresForAnnotations(annotationList));
};

/**
 * Expose the various registries so that the can be examined to see what
 * things are registered.
 *
 * @namespace geo.registries
 */
util.registries = {
  annotations: annotations,
  features: features,
  featureCapabilities: featureCapabilities,
  fileReaders: fileReaders,
  layers: layers,
  renderers: renderers
};

module.exports = util;

/***/ }),
/* 2 */
/***/ (function(module, exports, __webpack_require__) {

var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*!
 * jQuery JavaScript Library v3.3.1
 * https://jquery.com/
 *
 * Includes Sizzle.js
 * https://sizzlejs.com/
 *
 * Copyright JS Foundation and other contributors
 * Released under the MIT license
 * https://jquery.org/license
 *
 * Date: 2018-01-20T17:24Z
 */
( function( global, factory ) {

	"use strict";

	if ( typeof module === "object" && typeof module.exports === "object" ) {

		// For CommonJS and CommonJS-like environments where a proper `window`
		// is present, execute the factory and get jQuery.
		// For environments that do not have a `window` with a `document`
		// (such as Node.js), expose a factory as module.exports.
		// This accentuates the need for the creation of a real `window`.
		// e.g. var jQuery = require("jquery")(window);
		// See ticket #14549 for more info.
		module.exports = global.document ?
			factory( global, true ) :
			function( w ) {
				if ( !w.document ) {
					throw new Error( "jQuery requires a window with a document" );
				}
				return factory( w );
			};
	} else {
		factory( global );
	}

// Pass this if window is not defined yet
} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) {

// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1
// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode
// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common
// enough that all such attempts are guarded in a try block.
"use strict";

var arr = [];

var document = window.document;

var getProto = Object.getPrototypeOf;

var slice = arr.slice;

var concat = arr.concat;

var push = arr.push;

var indexOf = arr.indexOf;

var class2type = {};

var toString = class2type.toString;

var hasOwn = class2type.hasOwnProperty;

var fnToString = hasOwn.toString;

var ObjectFunctionString = fnToString.call( Object );

var support = {};

var isFunction = function isFunction( obj ) {

      // Support: Chrome <=57, Firefox <=52
      // In some browsers, typeof returns "function" for HTML <object> elements
      // (i.e., `typeof document.createElement( "object" ) === "function"`).
      // We don't want to classify *any* DOM node as a function.
      return typeof obj === "function" && typeof obj.nodeType !== "number";
  };


var isWindow = function isWindow( obj ) {
		return obj != null && obj === obj.window;
	};




	var preservedScriptAttributes = {
		type: true,
		src: true,
		noModule: true
	};

	function DOMEval( code, doc, node ) {
		doc = doc || document;

		var i,
			script = doc.createElement( "script" );

		script.text = code;
		if ( node ) {
			for ( i in preservedScriptAttributes ) {
				if ( node[ i ] ) {
					script[ i ] = node[ i ];
				}
			}
		}
		doc.head.appendChild( script ).parentNode.removeChild( script );
	}


function toType( obj ) {
	if ( obj == null ) {
		return obj + "";
	}

	// Support: Android <=2.3 only (functionish RegExp)
	return typeof obj === "object" || typeof obj === "function" ?
		class2type[ toString.call( obj ) ] || "object" :
		typeof obj;
}
/* global Symbol */
// Defining this global in .eslintrc.json would create a danger of using the global
// unguarded in another place, it seems safer to define global only for this module



var
	version = "3.3.1",

	// Define a local copy of jQuery
	jQuery = function( selector, context ) {

		// The jQuery object is actually just the init constructor 'enhanced'
		// Need init if jQuery is called (just allow error to be thrown if not included)
		return new jQuery.fn.init( selector, context );
	},

	// Support: Android <=4.0 only
	// Make sure we trim BOM and NBSP
	rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;

jQuery.fn = jQuery.prototype = {

	// The current version of jQuery being used
	jquery: version,

	constructor: jQuery,

	// The default length of a jQuery object is 0
	length: 0,

	toArray: function() {
		return slice.call( this );
	},

	// Get the Nth element in the matched element set OR
	// Get the whole matched element set as a clean array
	get: function( num ) {

		// Return all the elements in a clean array
		if ( num == null ) {
			return slice.call( this );
		}

		// Return just the one element from the set
		return num < 0 ? this[ num + this.length ] : this[ num ];
	},

	// Take an array of elements and push it onto the stack
	// (returning the new matched element set)
	pushStack: function( elems ) {

		// Build a new jQuery matched element set
		var ret = jQuery.merge( this.constructor(), elems );

		// Add the old object onto the stack (as a reference)
		ret.prevObject = this;

		// Return the newly-formed element set
		return ret;
	},

	// Execute a callback for every element in the matched set.
	each: function( callback ) {
		return jQuery.each( this, callback );
	},

	map: function( callback ) {
		return this.pushStack( jQuery.map( this, function( elem, i ) {
			return callback.call( elem, i, elem );
		} ) );
	},

	slice: function() {
		return this.pushStack( slice.apply( this, arguments ) );
	},

	first: function() {
		return this.eq( 0 );
	},

	last: function() {
		return this.eq( -1 );
	},

	eq: function( i ) {
		var len = this.length,
			j = +i + ( i < 0 ? len : 0 );
		return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] );
	},

	end: function() {
		return this.prevObject || this.constructor();
	},

	// For internal use only.
	// Behaves like an Array's method, not like a jQuery method.
	push: push,
	sort: arr.sort,
	splice: arr.splice
};

jQuery.extend = jQuery.fn.extend = function() {
	var options, name, src, copy, copyIsArray, clone,
		target = arguments[ 0 ] || {},
		i = 1,
		length = arguments.length,
		deep = false;

	// Handle a deep copy situation
	if ( typeof target === "boolean" ) {
		deep = target;

		// Skip the boolean and the target
		target = arguments[ i ] || {};
		i++;
	}

	// Handle case when target is a string or something (possible in deep copy)
	if ( typeof target !== "object" && !isFunction( target ) ) {
		target = {};
	}

	// Extend jQuery itself if only one argument is passed
	if ( i === length ) {
		target = this;
		i--;
	}

	for ( ; i < length; i++ ) {

		// Only deal with non-null/undefined values
		if ( ( options = arguments[ i ] ) != null ) {

			// Extend the base object
			for ( name in options ) {
				src = target[ name ];
				copy = options[ name ];

				// Prevent never-ending loop
				if ( target === copy ) {
					continue;
				}

				// Recurse if we're merging plain objects or arrays
				if ( deep && copy && ( jQuery.isPlainObject( copy ) ||
					( copyIsArray = Array.isArray( copy ) ) ) ) {

					if ( copyIsArray ) {
						copyIsArray = false;
						clone = src && Array.isArray( src ) ? src : [];

					} else {
						clone = src && jQuery.isPlainObject( src ) ? src : {};
					}

					// Never move original objects, clone them
					target[ name ] = jQuery.extend( deep, clone, copy );

				// Don't bring in undefined values
				} else if ( copy !== undefined ) {
					target[ name ] = copy;
				}
			}
		}
	}

	// Return the modified object
	return target;
};

jQuery.extend( {

	// Unique for each copy of jQuery on the page
	expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ),

	// Assume jQuery is ready without the ready module
	isReady: true,

	error: function( msg ) {
		throw new Error( msg );
	},

	noop: function() {},

	isPlainObject: function( obj ) {
		var proto, Ctor;

		// Detect obvious negatives
		// Use toString instead of jQuery.type to catch host objects
		if ( !obj || toString.call( obj ) !== "[object Object]" ) {
			return false;
		}

		proto = getProto( obj );

		// Objects with no prototype (e.g., `Object.create( null )`) are plain
		if ( !proto ) {
			return true;
		}

		// Objects with prototype are plain iff they were constructed by a global Object function
		Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor;
		return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString;
	},

	isEmptyObject: function( obj ) {

		/* eslint-disable no-unused-vars */
		// See https://github.com/eslint/eslint/issues/6125
		var name;

		for ( name in obj ) {
			return false;
		}
		return true;
	},

	// Evaluates a script in a global context
	globalEval: function( code ) {
		DOMEval( code );
	},

	each: function( obj, callback ) {
		var length, i = 0;

		if ( isArrayLike( obj ) ) {
			length = obj.length;
			for ( ; i < length; i++ ) {
				if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
					break;
				}
			}
		} else {
			for ( i in obj ) {
				if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
					break;
				}
			}
		}

		return obj;
	},

	// Support: Android <=4.0 only
	trim: function( text ) {
		return text == null ?
			"" :
			( text + "" ).replace( rtrim, "" );
	},

	// results is for internal usage only
	makeArray: function( arr, results ) {
		var ret = results || [];

		if ( arr != null ) {
			if ( isArrayLike( Object( arr ) ) ) {
				jQuery.merge( ret,
					typeof arr === "string" ?
					[ arr ] : arr
				);
			} else {
				push.call( ret, arr );
			}
		}

		return ret;
	},

	inArray: function( elem, arr, i ) {
		return arr == null ? -1 : indexOf.call( arr, elem, i );
	},

	// Support: Android <=4.0 only, PhantomJS 1 only
	// push.apply(_, arraylike) throws on ancient WebKit
	merge: function( first, second ) {
		var len = +second.length,
			j = 0,
			i = first.length;

		for ( ; j < len; j++ ) {
			first[ i++ ] = second[ j ];
		}

		first.length = i;

		return first;
	},

	grep: function( elems, callback, invert ) {
		var callbackInverse,
			matches = [],
			i = 0,
			length = elems.length,
			callbackExpect = !invert;

		// Go through the array, only saving the items
		// that pass the validator function
		for ( ; i < length; i++ ) {
			callbackInverse = !callback( elems[ i ], i );
			if ( callbackInverse !== callbackExpect ) {
				matches.push( elems[ i ] );
			}
		}

		return matches;
	},

	// arg is for internal usage only
	map: function( elems, callback, arg ) {
		var length, value,
			i = 0,
			ret = [];

		// Go through the array, translating each of the items to their new values
		if ( isArrayLike( elems ) ) {
			length = elems.length;
			for ( ; i < length; i++ ) {
				value = callback( elems[ i ], i, arg );

				if ( value != null ) {
					ret.push( value );
				}
			}

		// Go through every key on the object,
		} else {
			for ( i in elems ) {
				value = callback( elems[ i ], i, arg );

				if ( value != null ) {
					ret.push( value );
				}
			}
		}

		// Flatten any nested arrays
		return concat.apply( [], ret );
	},

	// A global GUID counter for objects
	guid: 1,

	// jQuery.support is not used in Core but other projects attach their
	// properties to it so it needs to exist.
	support: support
} );

if ( typeof Symbol === "function" ) {
	jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ];
}

// Populate the class2type map
jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ),
function( i, name ) {
	class2type[ "[object " + name + "]" ] = name.toLowerCase();
} );

function isArrayLike( obj ) {

	// Support: real iOS 8.2 only (not reproducible in simulator)
	// `in` check used to prevent JIT error (gh-2145)
	// hasOwn isn't used here due to false negatives
	// regarding Nodelist length in IE
	var length = !!obj && "length" in obj && obj.length,
		type = toType( obj );

	if ( isFunction( obj ) || isWindow( obj ) ) {
		return false;
	}

	return type === "array" || length === 0 ||
		typeof length === "number" && length > 0 && ( length - 1 ) in obj;
}
var Sizzle =
/*!
 * Sizzle CSS Selector Engine v2.3.3
 * https://sizzlejs.com/
 *
 * Copyright jQuery Foundation and other contributors
 * Released under the MIT license
 * http://jquery.org/license
 *
 * Date: 2016-08-08
 */
(function( window ) {

var i,
	support,
	Expr,
	getText,
	isXML,
	tokenize,
	compile,
	select,
	outermostContext,
	sortInput,
	hasDuplicate,

	// Local document vars
	setDocument,
	document,
	docElem,
	documentIsHTML,
	rbuggyQSA,
	rbuggyMatches,
	matches,
	contains,

	// Instance-specific data
	expando = "sizzle" + 1 * new Date(),
	preferredDoc = window.document,
	dirruns = 0,
	done = 0,
	classCache = createCache(),
	tokenCache = createCache(),
	compilerCache = createCache(),
	sortOrder = function( a, b ) {
		if ( a === b ) {
			hasDuplicate = true;
		}
		return 0;
	},

	// Instance methods
	hasOwn = ({}).hasOwnProperty,
	arr = [],
	pop = arr.pop,
	push_native = arr.push,
	push = arr.push,
	slice = arr.slice,
	// Use a stripped-down indexOf as it's faster than native
	// https://jsperf.com/thor-indexof-vs-for/5
	indexOf = function( list, elem ) {
		var i = 0,
			len = list.length;
		for ( ; i < len; i++ ) {
			if ( list[i] === elem ) {
				return i;
			}
		}
		return -1;
	},

	booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",

	// Regular expressions

	// http://www.w3.org/TR/css3-selectors/#whitespace
	whitespace = "[\\x20\\t\\r\\n\\f]",

	// http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
	identifier = "(?:\\\\.|[\\w-]|[^\0-\\xa0])+",

	// Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors
	attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace +
		// Operator (capture 2)
		"*([*^$|!~]?=)" + whitespace +
		// "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]"
		"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace +
		"*\\]",

	pseudos = ":(" + identifier + ")(?:\\((" +
		// To reduce the number of selectors needing tokenize in the preFilter, prefer arguments:
		// 1. quoted (capture 3; capture 4 or capture 5)
		"('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" +
		// 2. simple (capture 6)
		"((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" +
		// 3. anything else (capture 2)
		".*" +
		")\\)|)",

	// Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
	rwhitespace = new RegExp( whitespace + "+", "g" ),
	rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),

	rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
	rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ),

	rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ),

	rpseudo = new RegExp( pseudos ),
	ridentifier = new RegExp( "^" + identifier + "$" ),

	matchExpr = {
		"ID": new RegExp( "^#(" + identifier + ")" ),
		"CLASS": new RegExp( "^\\.(" + identifier + ")" ),
		"TAG": new RegExp( "^(" + identifier + "|[*])" ),
		"ATTR": new RegExp( "^" + attributes ),
		"PSEUDO": new RegExp( "^" + pseudos ),
		"CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace +
			"*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
			"*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
		"bool": new RegExp( "^(?:" + booleans + ")$", "i" ),
		// For use in libraries implementing .is()
		// We use this for POS matching in `select`
		"needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
			whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
	},

	rinputs = /^(?:input|select|textarea|button)$/i,
	rheader = /^h\d$/i,

	rnative = /^[^{]+\{\s*\[native \w/,

	// Easily-parseable/retrievable ID or TAG or CLASS selectors
	rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,

	rsibling = /[+~]/,

	// CSS escapes
	// http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
	runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ),
	funescape = function( _, escaped, escapedWhitespace ) {
		var high = "0x" + escaped - 0x10000;
		// NaN means non-codepoint
		// Support: Firefox<24
		// Workaround erroneous numeric interpretation of +"0x"
		return high !== high || escapedWhitespace ?
			escaped :
			high < 0 ?
				// BMP codepoint
				String.fromCharCode( high + 0x10000 ) :
				// Supplemental Plane codepoint (surrogate pair)
				String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
	},

	// CSS string/identifier serialization
	// https://drafts.csswg.org/cssom/#common-serializing-idioms
	rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,
	fcssescape = function( ch, asCodePoint ) {
		if ( asCodePoint ) {

			// U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER
			if ( ch === "\0" ) {
				return "\uFFFD";
			}

			// Control characters and (dependent upon position) numbers get escaped as code points
			return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " ";
		}

		// Other potentially-special ASCII characters get backslash-escaped
		return "\\" + ch;
	},

	// Used for iframes
	// See setDocument()
	// Removing the function wrapper causes a "Permission Denied"
	// error in IE
	unloadHandler = function() {
		setDocument();
	},

	disabledAncestor = addCombinator(
		function( elem ) {
			return elem.disabled === true && ("form" in elem || "label" in elem);
		},
		{ dir: "parentNode", next: "legend" }
	);

// Optimize for push.apply( _, NodeList )
try {
	push.apply(
		(arr = slice.call( preferredDoc.childNodes )),
		preferredDoc.childNodes
	);
	// Support: Android<4.0
	// Detect silently failing push.apply
	arr[ preferredDoc.childNodes.length ].nodeType;
} catch ( e ) {
	push = { apply: arr.length ?

		// Leverage slice if possible
		function( target, els ) {
			push_native.apply( target, slice.call(els) );
		} :

		// Support: IE<9
		// Otherwise append directly
		function( target, els ) {
			var j = target.length,
				i = 0;
			// Can't trust NodeList.length
			while ( (target[j++] = els[i++]) ) {}
			target.length = j - 1;
		}
	};
}

function Sizzle( selector, context, results, seed ) {
	var m, i, elem, nid, match, groups, newSelector,
		newContext = context && context.ownerDocument,

		// nodeType defaults to 9, since context defaults to document
		nodeType = context ? context.nodeType : 9;

	results = results || [];

	// Return early from calls with invalid selector or context
	if ( typeof selector !== "string" || !selector ||
		nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) {

		return results;
	}

	// Try to shortcut find operations (as opposed to filters) in HTML documents
	if ( !seed ) {

		if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {
			setDocument( context );
		}
		context = context || document;

		if ( documentIsHTML ) {

			// If the selector is sufficiently simple, try using a "get*By*" DOM method
			// (excepting DocumentFragment context, where the methods don't exist)
			if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) {

				// ID selector
				if ( (m = match[1]) ) {

					// Document context
					if ( nodeType === 9 ) {
						if ( (elem = context.getElementById( m )) ) {

							// Support: IE, Opera, Webkit
							// TODO: identify versions
							// getElementById can match elements by name instead of ID
							if ( elem.id === m ) {
								results.push( elem );
								return results;
							}
						} else {
							return results;
						}

					// Element context
					} else {

						// Support: IE, Opera, Webkit
						// TODO: identify versions
						// getElementById can match elements by name instead of ID
						if ( newContext && (elem = newContext.getElementById( m )) &&
							contains( context, elem ) &&
							elem.id === m ) {

							results.push( elem );
							return results;
						}
					}

				// Type selector
				} else if ( match[2] ) {
					push.apply( results, context.getElementsByTagName( selector ) );
					return results;

				// Class selector
				} else if ( (m = match[3]) && support.getElementsByClassName &&
					context.getElementsByClassName ) {

					push.apply( results, context.getElementsByClassName( m ) );
					return results;
				}
			}

			// Take advantage of querySelectorAll
			if ( support.qsa &&
				!compilerCache[ selector + " " ] &&
				(!rbuggyQSA || !rbuggyQSA.test( selector )) ) {

				if ( nodeType !== 1 ) {
					newContext = context;
					newSelector = selector;

				// qSA looks outside Element context, which is not what we want
				// Thanks to Andrew Dupont for this workaround technique
				// Support: IE <=8
				// Exclude object elements
				} else if ( context.nodeName.toLowerCase() !== "object" ) {

					// Capture the context ID, setting it first if necessary
					if ( (nid = context.getAttribute( "id" )) ) {
						nid = nid.replace( rcssescape, fcssescape );
					} else {
						context.setAttribute( "id", (nid = expando) );
					}

					// Prefix every selector in the list
					groups = tokenize( selector );
					i = groups.length;
					while ( i-- ) {
						groups[i] = "#" + nid + " " + toSelector( groups[i] );
					}
					newSelector = groups.join( "," );

					// Expand context for sibling selectors
					newContext = rsibling.test( selector ) && testContext( context.parentNode ) ||
						context;
				}

				if ( newSelector ) {
					try {
						push.apply( results,
							newContext.querySelectorAll( newSelector )
						);
						return results;
					} catch ( qsaError ) {
					} finally {
						if ( nid === expando ) {
							context.removeAttribute( "id" );
						}
					}
				}
			}
		}
	}

	// All others
	return select( selector.replace( rtrim, "$1" ), context, results, seed );
}

/**
 * Create key-value caches of limited size
 * @returns {function(string, object)} Returns the Object data after storing it on itself with
 *	property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
 *	deleting the oldest entry
 */
function createCache() {
	var keys = [];

	function cache( key, value ) {
		// Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
		if ( keys.push( key + " " ) > Expr.cacheLength ) {
			// Only keep the most recent entries
			delete cache[ keys.shift() ];
		}
		return (cache[ key + " " ] = value);
	}
	return cache;
}

/**
 * Mark a function for special use by Sizzle
 * @param {Function} fn The function to mark
 */
function markFunction( fn ) {
	fn[ expando ] = true;
	return fn;
}

/**
 * Support testing using an element
 * @param {Function} fn Passed the created element and returns a boolean result
 */
function assert( fn ) {
	var el = document.createElement("fieldset");

	try {
		return !!fn( el );
	} catch (e) {
		return false;
	} finally {
		// Remove from its parent by default
		if ( el.parentNode ) {
			el.parentNode.removeChild( el );
		}
		// release memory in IE
		el = null;
	}
}

/**
 * Adds the same handler for all of the specified attrs
 * @param {String} attrs Pipe-separated list of attributes
 * @param {Function} handler The method that will be applied
 */
function addHandle( attrs, handler ) {
	var arr = attrs.split("|"),
		i = arr.length;

	while ( i-- ) {
		Expr.attrHandle[ arr[i] ] = handler;
	}
}

/**
 * Checks document order of two siblings
 * @param {Element} a
 * @param {Element} b
 * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b
 */
function siblingCheck( a, b ) {
	var cur = b && a,
		diff = cur && a.nodeType === 1 && b.nodeType === 1 &&
			a.sourceIndex - b.sourceIndex;

	// Use IE sourceIndex if available on both nodes
	if ( diff ) {
		return diff;
	}

	// Check if b follows a
	if ( cur ) {
		while ( (cur = cur.nextSibling) ) {
			if ( cur === b ) {
				return -1;
			}
		}
	}

	return a ? 1 : -1;
}

/**
 * Returns a function to use in pseudos for input types
 * @param {String} type
 */
function createInputPseudo( type ) {
	return function( elem ) {
		var name = elem.nodeName.toLowerCase();
		return name === "input" && elem.type === type;
	};
}

/**
 * Returns a function to use in pseudos for buttons
 * @param {String} type
 */
function createButtonPseudo( type ) {
	return function( elem ) {
		var name = elem.nodeName.toLowerCase();
		return (name === "input" || name === "button") && elem.type === type;
	};
}

/**
 * Returns a function to use in pseudos for :enabled/:disabled
 * @param {Boolean} disabled true for :disabled; false for :enabled
 */
function createDisabledPseudo( disabled ) {

	// Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable
	return function( elem ) {

		// Only certain elements can match :enabled or :disabled
		// https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled
		// https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled
		if ( "form" in elem ) {

			// Check for inherited disabledness on relevant non-disabled elements:
			// * listed form-associated elements in a disabled fieldset
			//   https://html.spec.whatwg.org/multipage/forms.html#category-listed
			//   https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled
			// * option elements in a disabled optgroup
			//   https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled
			// All such elements have a "form" property.
			if ( elem.parentNode && elem.disabled === false ) {

				// Option elements defer to a parent optgroup if present
				if ( "label" in elem ) {
					if ( "label" in elem.parentNode ) {
						return elem.parentNode.disabled === disabled;
					} else {
						return elem.disabled === disabled;
					}
				}

				// Support: IE 6 - 11
				// Use the isDisabled shortcut property to check for disabled fieldset ancestors
				return elem.isDisabled === disabled ||

					// Where there is no isDisabled, check manually
					/* jshint -W018 */
					elem.isDisabled !== !disabled &&
						disabledAncestor( elem ) === disabled;
			}

			return elem.disabled === disabled;

		// Try to winnow out elements that can't be disabled before trusting the disabled property.
		// Some victims get caught in our net (label, legend, menu, track), but it shouldn't
		// even exist on them, let alone have a boolean value.
		} else if ( "label" in elem ) {
			return elem.disabled === disabled;
		}

		// Remaining elements are neither :enabled nor :disabled
		return false;
	};
}

/**
 * Returns a function to use in pseudos for positionals
 * @param {Function} fn
 */
function createPositionalPseudo( fn ) {
	return markFunction(function( argument ) {
		argument = +argument;
		return markFunction(function( seed, matches ) {
			var j,
				matchIndexes = fn( [], seed.length, argument ),
				i = matchIndexes.length;

			// Match elements found at the specified indexes
			while ( i-- ) {
				if ( seed[ (j = matchIndexes[i]) ] ) {
					seed[j] = !(matches[j] = seed[j]);
				}
			}
		});
	});
}

/**
 * Checks a node for validity as a Sizzle context
 * @param {Element|Object=} context
 * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value
 */
function testContext( context ) {
	return context && typeof context.getElementsByTagName !== "undefined" && context;
}

// Expose support vars for convenience
support = Sizzle.support = {};

/**
 * Detects XML nodes
 * @param {Element|Object} elem An element or a document
 * @returns {Boolean} True iff elem is a non-HTML XML node
 */
isXML = Sizzle.isXML = function( elem ) {
	// documentElement is verified for cases where it doesn't yet exist
	// (such as loading iframes in IE - #4833)
	var documentElement = elem && (elem.ownerDocument || elem).documentElement;
	return documentElement ? documentElement.nodeName !== "HTML" : false;
};

/**
 * Sets document-related variables once based on the current document
 * @param {Element|Object} [doc] An element or document object to use to set the document
 * @returns {Object} Returns the current document
 */
setDocument = Sizzle.setDocument = function( node ) {
	var hasCompare, subWindow,
		doc = node ? node.ownerDocument || node : preferredDoc;

	// Return early if doc is invalid or already selected
	if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
		return document;
	}

	// Update global variables
	document = doc;
	docElem = document.documentElement;
	documentIsHTML = !isXML( document );

	// Support: IE 9-11, Edge
	// Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936)
	if ( preferredDoc !== document &&
		(subWindow = document.defaultView) && subWindow.top !== subWindow ) {

		// Support: IE 11, Edge
		if ( subWindow.addEventListener ) {
			subWindow.addEventListener( "unload", unloadHandler, false );

		// Support: IE 9 - 10 only
		} else if ( subWindow.attachEvent ) {
			subWindow.attachEvent( "onunload", unloadHandler );
		}
	}

	/* Attributes
	---------------------------------------------------------------------- */

	// Support: IE<8
	// Verify that getAttribute really returns attributes and not properties
	// (excepting IE8 booleans)
	support.attributes = assert(function( el ) {
		el.className = "i";
		return !el.getAttribute("className");
	});

	/* getElement(s)By*
	---------------------------------------------------------------------- */

	// Check if getElementsByTagName("*") returns only elements
	support.getElementsByTagName = assert(function( el ) {
		el.appendChild( document.createComment("") );
		return !el.getElementsByTagName("*").length;
	});

	// Support: IE<9
	support.getElementsByClassName = rnative.test( document.getElementsByClassName );

	// Support: IE<10
	// Check if getElementById returns elements by name
	// The broken getElementById methods don't pick up programmatically-set names,
	// so use a roundabout getElementsByName test
	support.getById = assert(function( el ) {
		docElem.appendChild( el ).id = expando;
		return !document.getElementsByName || !document.getElementsByName( expando ).length;
	});

	// ID filter and find
	if ( support.getById ) {
		Expr.filter["ID"] = function( id ) {
			var attrId = id.replace( runescape, funescape );
			return function( elem ) {
				return elem.getAttribute("id") === attrId;
			};
		};
		Expr.find["ID"] = function( id, context ) {
			if ( typeof context.getElementById !== "undefined" && documentIsHTML ) {
				var elem = context.getElementById( id );
				return elem ? [ elem ] : [];
			}
		};
	} else {
		Expr.filter["ID"] =  function( id ) {
			var attrId = id.replace( runescape, funescape );
			return function( elem ) {
				var node = typeof elem.getAttributeNode !== "undefined" &&
					elem.getAttributeNode("id");
				return node && node.value === attrId;
			};
		};

		// Support: IE 6 - 7 only
		// getElementById is not reliable as a find shortcut
		Expr.find["ID"] = function( id, context ) {
			if ( typeof context.getElementById !== "undefined" && documentIsHTML ) {
				var node, i, elems,
					elem = context.getElementById( id );

				if ( elem ) {

					// Verify the id attribute
					node = elem.getAttributeNode("id");
					if ( node && node.value === id ) {
						return [ elem ];
					}

					// Fall back on getElementsByName
					elems = context.getElementsByName( id );
					i = 0;
					while ( (elem = elems[i++]) ) {
						node = elem.getAttributeNode("id");
						if ( node && node.value === id ) {
							return [ elem ];
						}
					}
				}

				return [];
			}
		};
	}

	// Tag
	Expr.find["TAG"] = support.getElementsByTagName ?
		function( tag, context ) {
			if ( typeof context.getElementsByTagName !== "undefined" ) {
				return context.getElementsByTagName( tag );

			// DocumentFragment nodes don't have gEBTN
			} else if ( support.qsa ) {
				return context.querySelectorAll( tag );
			}
		} :

		function( tag, context ) {
			var elem,
				tmp = [],
				i = 0,
				// By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too
				results = context.getElementsByTagName( tag );

			// Filter out possible comments
			if ( tag === "*" ) {
				while ( (elem = results[i++]) ) {
					if ( elem.nodeType === 1 ) {
						tmp.push( elem );
					}
				}

				return tmp;
			}
			return results;
		};

	// Class
	Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) {
		if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) {
			return context.getElementsByClassName( className );
		}
	};

	/* QSA/matchesSelector
	---------------------------------------------------------------------- */

	// QSA and matchesSelector support

	// matchesSelector(:active) reports false when true (IE9/Opera 11.5)
	rbuggyMatches = [];

	// qSa(:focus) reports false when true (Chrome 21)
	// We allow this because of a bug in IE8/9 that throws an error
	// whenever `document.activeElement` is accessed on an iframe
	// So, we allow :focus to pass through QSA all the time to avoid the IE error
	// See https://bugs.jquery.com/ticket/13378
	rbuggyQSA = [];

	if ( (support.qsa = rnative.test( document.querySelectorAll )) ) {
		// Build QSA regex
		// Regex strategy adopted from Diego Perini
		assert(function( el ) {
			// Select is set to empty string on purpose
			// This is to test IE's treatment of not explicitly
			// setting a boolean content attribute,
			// since its presence should be enough
			// https://bugs.jquery.com/ticket/12359
			docElem.appendChild( el ).innerHTML = "<a id='" + expando + "'></a>" +
				"<select id='" + expando + "-\r\\' msallowcapture=''>" +
				"<option selected=''></option></select>";

			// Support: IE8, Opera 11-12.16
			// Nothing should be selected when empty strings follow ^= or $= or *=
			// The test attribute must be unknown in Opera but "safe" for WinRT
			// https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section
			if ( el.querySelectorAll("[msallowcapture^='']").length ) {
				rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" );
			}

			// Support: IE8
			// Boolean attributes and "value" are not treated correctly
			if ( !el.querySelectorAll("[selected]").length ) {
				rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" );
			}

			// Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+
			if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) {
				rbuggyQSA.push("~=");
			}

			// Webkit/Opera - :checked should return selected option elements
			// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
			// IE8 throws error here and will not see later tests
			if ( !el.querySelectorAll(":checked").length ) {
				rbuggyQSA.push(":checked");
			}

			// Support: Safari 8+, iOS 8+
			// https://bugs.webkit.org/show_bug.cgi?id=136851
			// In-page `selector#id sibling-combinator selector` fails
			if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) {
				rbuggyQSA.push(".#.+[+~]");
			}
		});

		assert(function( el ) {
			el.innerHTML = "<a href='' disabled='disabled'></a>" +
				"<select disabled='disabled'><option/></select>";

			// Support: Windows 8 Native Apps
			// The type and name attributes are restricted during .innerHTML assignment
			var input = document.createElement("input");
			input.setAttribute( "type", "hidden" );
			el.appendChild( input ).setAttribute( "name", "D" );

			// Support: IE8
			// Enforce case-sensitivity of name attribute
			if ( el.querySelectorAll("[name=d]").length ) {
				rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" );
			}

			// FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
			// IE8 throws error here and will not see later tests
			if ( el.querySelectorAll(":enabled").length !== 2 ) {
				rbuggyQSA.push( ":enabled", ":disabled" );
			}

			// Support: IE9-11+
			// IE's :disabled selector does not pick up the children of disabled fieldsets
			docElem.appendChild( el ).disabled = true;
			if ( el.querySelectorAll(":disabled").length !== 2 ) {
				rbuggyQSA.push( ":enabled", ":disabled" );
			}

			// Opera 10-11 does not throw on post-comma invalid pseudos
			el.querySelectorAll("*,:x");
			rbuggyQSA.push(",.*:");
		});
	}

	if ( (support.matchesSelector = rnative.test( (matches = docElem.matches ||
		docElem.webkitMatchesSelector ||
		docElem.mozMatchesSelector ||
		docElem.oMatchesSelector ||
		docElem.msMatchesSelector) )) ) {

		assert(function( el ) {
			// Check to see if it's possible to do matchesSelector
			// on a disconnected node (IE 9)
			support.disconnectedMatch = matches.call( el, "*" );

			// This should fail with an exception
			// Gecko does not error, returns false instead
			matches.call( el, "[s!='']:x" );
			rbuggyMatches.push( "!=", pseudos );
		});
	}

	rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") );
	rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") );

	/* Contains
	---------------------------------------------------------------------- */
	hasCompare = rnative.test( docElem.compareDocumentPosition );

	// Element contains another
	// Purposefully self-exclusive
	// As in, an element does not contain itself
	contains = hasCompare || rnative.test( docElem.contains ) ?
		function( a, b ) {
			var adown = a.nodeType === 9 ? a.documentElement : a,
				bup = b && b.parentNode;
			return a === bup || !!( bup && bup.nodeType === 1 && (
				adown.contains ?
					adown.contains( bup ) :
					a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
			));
		} :
		function( a, b ) {
			if ( b ) {
				while ( (b = b.parentNode) ) {
					if ( b === a ) {
						return true;
					}
				}
			}
			return false;
		};

	/* Sorting
	---------------------------------------------------------------------- */

	// Document order sorting
	sortOrder = hasCompare ?
	function( a, b ) {

		// Flag for duplicate removal
		if ( a === b ) {
			hasDuplicate = true;
			return 0;
		}

		// Sort on method existence if only one input has compareDocumentPosition
		var compare = !a.compareDocumentPosition - !b.compareDocumentPosition;
		if ( compare ) {
			return compare;
		}

		// Calculate position if both inputs belong to the same document
		compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ?
			a.compareDocumentPosition( b ) :

			// Otherwise we know they are disconnected
			1;

		// Disconnected nodes
		if ( compare & 1 ||
			(!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) {

			// Choose the first element that is related to our preferred document
			if ( a === document || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) {
				return -1;
			}
			if ( b === document || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) {
				return 1;
			}

			// Maintain original order
			return sortInput ?
				( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :
				0;
		}

		return compare & 4 ? -1 : 1;
	} :
	function( a, b ) {
		// Exit early if the nodes are identical
		if ( a === b ) {
			hasDuplicate = true;
			return 0;
		}

		var cur,
			i = 0,
			aup = a.parentNode,
			bup = b.parentNode,
			ap = [ a ],
			bp = [ b ];

		// Parentless nodes are either documents or disconnected
		if ( !aup || !bup ) {
			return a === document ? -1 :
				b === document ? 1 :
				aup ? -1 :
				bup ? 1 :
				sortInput ?
				( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :
				0;

		// If the nodes are siblings, we can do a quick check
		} else if ( aup === bup ) {
			return siblingCheck( a, b );
		}

		// Otherwise we need full lists of their ancestors for comparison
		cur = a;
		while ( (cur = cur.parentNode) ) {
			ap.unshift( cur );
		}
		cur = b;
		while ( (cur = cur.parentNode) ) {
			bp.unshift( cur );
		}

		// Walk down the tree looking for a discrepancy
		while ( ap[i] === bp[i] ) {
			i++;
		}

		return i ?
			// Do a sibling check if the nodes have a common ancestor
			siblingCheck( ap[i], bp[i] ) :

			// Otherwise nodes in our document sort first
			ap[i] === preferredDoc ? -1 :
			bp[i] === preferredDoc ? 1 :
			0;
	};

	return document;
};

Sizzle.matches = function( expr, elements ) {
	return Sizzle( expr, null, null, elements );
};

Sizzle.matchesSelector = function( elem, expr ) {
	// Set document vars if needed
	if ( ( elem.ownerDocument || elem ) !== document ) {
		setDocument( elem );
	}

	// Make sure that attribute selectors are quoted
	expr = expr.replace( rattributeQuotes, "='$1']" );

	if ( support.matchesSelector && documentIsHTML &&
		!compilerCache[ expr + " " ] &&
		( !rbuggyMatches || !rbuggyMatches.test( expr ) ) &&
		( !rbuggyQSA     || !rbuggyQSA.test( expr ) ) ) {

		try {
			var ret = matches.call( elem, expr );

			// IE 9's matchesSelector returns false on disconnected nodes
			if ( ret || support.disconnectedMatch ||
					// As well, disconnected nodes are said to be in a document
					// fragment in IE 9
					elem.document && elem.document.nodeType !== 11 ) {
				return ret;
			}
		} catch (e) {}
	}

	return Sizzle( expr, document, null, [ elem ] ).length > 0;
};

Sizzle.contains = function( context, elem ) {
	// Set document vars if needed
	if ( ( context.ownerDocument || context ) !== document ) {
		setDocument( context );
	}
	return contains( context, elem );
};

Sizzle.attr = function( elem, name ) {
	// Set document vars if needed
	if ( ( elem.ownerDocument || elem ) !== document ) {
		setDocument( elem );
	}

	var fn = Expr.attrHandle[ name.toLowerCase() ],
		// Don't get fooled by Object.prototype properties (jQuery #13807)
		val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ?
			fn( elem, name, !documentIsHTML ) :
			undefined;

	return val !== undefined ?
		val :
		support.attributes || !documentIsHTML ?
			elem.getAttribute( name ) :
			(val = elem.getAttributeNode(name)) && val.specified ?
				val.value :
				null;
};

Sizzle.escape = function( sel ) {
	return (sel + "").replace( rcssescape, fcssescape );
};

Sizzle.error = function( msg ) {
	throw new Error( "Syntax error, unrecognized expression: " + msg );
};

/**
 * Document sorting and removing duplicates
 * @param {ArrayLike} results
 */
Sizzle.uniqueSort = function( results ) {
	var elem,
		duplicates = [],
		j = 0,
		i = 0;

	// Unless we *know* we can detect duplicates, assume their presence
	hasDuplicate = !support.detectDuplicates;
	sortInput = !support.sortStable && results.slice( 0 );
	results.sort( sortOrder );

	if ( hasDuplicate ) {
		while ( (elem = results[i++]) ) {
			if ( elem === results[ i ] ) {
				j = duplicates.push( i );
			}
		}
		while ( j-- ) {
			results.splice( duplicates[ j ], 1 );
		}
	}

	// Clear input after sorting to release objects
	// See https://github.com/jquery/sizzle/pull/225
	sortInput = null;

	return results;
};

/**
 * Utility function for retrieving the text value of an array of DOM nodes
 * @param {Array|Element} elem
 */
getText = Sizzle.getText = function( elem ) {
	var node,
		ret = "",
		i = 0,
		nodeType = elem.nodeType;

	if ( !nodeType ) {
		// If no nodeType, this is expected to be an array
		while ( (node = elem[i++]) ) {
			// Do not traverse comment nodes
			ret += getText( node );
		}
	} else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
		// Use textContent for elements
		// innerText usage removed for consistency of new lines (jQuery #11153)
		if ( typeof elem.textContent === "string" ) {
			return elem.textContent;
		} else {
			// Traverse its children
			for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
				ret += getText( elem );
			}
		}
	} else if ( nodeType === 3 || nodeType === 4 ) {
		return elem.nodeValue;
	}
	// Do not include comment or processing instruction nodes

	return ret;
};

Expr = Sizzle.selectors = {

	// Can be adjusted by the user
	cacheLength: 50,

	createPseudo: markFunction,

	match: matchExpr,

	attrHandle: {},

	find: {},

	relative: {
		">": { dir: "parentNode", first: true },
		" ": { dir: "parentNode" },
		"+": { dir: "previousSibling", first: true },
		"~": { dir: "previousSibling" }
	},

	preFilter: {
		"ATTR": function( match ) {
			match[1] = match[1].replace( runescape, funescape );

			// Move the given value to match[3] whether quoted or unquoted
			match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape );

			if ( match[2] === "~=" ) {
				match[3] = " " + match[3] + " ";
			}

			return match.slice( 0, 4 );
		},

		"CHILD": function( match ) {
			/* matches from matchExpr["CHILD"]
				1 type (only|nth|...)
				2 what (child|of-type)
				3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
				4 xn-component of xn+y argument ([+-]?\d*n|)
				5 sign of xn-component
				6 x of xn-component
				7 sign of y-component
				8 y of y-component
			*/
			match[1] = match[1].toLowerCase();

			if ( match[1].slice( 0, 3 ) === "nth" ) {
				// nth-* requires argument
				if ( !match[3] ) {
					Sizzle.error( match[0] );
				}

				// numeric x and y parameters for Expr.filter.CHILD
				// remember that false/true cast respectively to 0/1
				match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) );
				match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" );

			// other types prohibit arguments
			} else if ( match[3] ) {
				Sizzle.error( match[0] );
			}

			return match;
		},

		"PSEUDO": function( match ) {
			var excess,
				unquoted = !match[6] && match[2];

			if ( matchExpr["CHILD"].test( match[0] ) ) {
				return null;
			}

			// Accept quoted arguments as-is
			if ( match[3] ) {
				match[2] = match[4] || match[5] || "";

			// Strip excess characters from unquoted arguments
			} else if ( unquoted && rpseudo.test( unquoted ) &&
				// Get excess from tokenize (recursively)
				(excess = tokenize( unquoted, true )) &&
				// advance to the next closing parenthesis
				(excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {

				// excess is a negative index
				match[0] = match[0].slice( 0, excess );
				match[2] = unquoted.slice( 0, excess );
			}

			// Return only captures needed by the pseudo filter method (type and argument)
			return match.slice( 0, 3 );
		}
	},

	filter: {

		"TAG": function( nodeNameSelector ) {
			var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();
			return nodeNameSelector === "*" ?
				function() { return true; } :
				function( elem ) {
					return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
				};
		},

		"CLASS": function( className ) {
			var pattern = classCache[ className + " " ];

			return pattern ||
				(pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
				classCache( className, function( elem ) {
					return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "" );
				});
		},

		"ATTR": function( name, operator, check ) {
			return function( elem ) {
				var result = Sizzle.attr( elem, name );

				if ( result == null ) {
					return operator === "!=";
				}
				if ( !operator ) {
					return true;
				}

				result += "";

				return operator === "=" ? result === check :
					operator === "!=" ? result !== check :
					operator === "^=" ? check && result.indexOf( check ) === 0 :
					operator === "*=" ? check && result.indexOf( check ) > -1 :
					operator === "$=" ? check && result.slice( -check.length ) === check :
					operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 :
					operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
					false;
			};
		},

		"CHILD": function( type, what, argument, first, last ) {
			var simple = type.slice( 0, 3 ) !== "nth",
				forward = type.slice( -4 ) !== "last",
				ofType = what === "of-type";

			return first === 1 && last === 0 ?

				// Shortcut for :nth-*(n)
				function( elem ) {
					return !!elem.parentNode;
				} :

				function( elem, context, xml ) {
					var cache, uniqueCache, outerCache, node, nodeIndex, start,
						dir = simple !== forward ? "nextSibling" : "previousSibling",
						parent = elem.parentNode,
						name = ofType && elem.nodeName.toLowerCase(),
						useCache = !xml && !ofType,
						diff = false;

					if ( parent ) {

						// :(first|last|only)-(child|of-type)
						if ( simple ) {
							while ( dir ) {
								node = elem;
								while ( (node = node[ dir ]) ) {
									if ( ofType ?
										node.nodeName.toLowerCase() === name :
										node.nodeType === 1 ) {

										return false;
									}
								}
								// Reverse direction for :only-* (if we haven't yet done so)
								start = dir = type === "only" && !start && "nextSibling";
							}
							return true;
						}

						start = [ forward ? parent.firstChild : parent.lastChild ];

						// non-xml :nth-child(...) stores cache data on `parent`
						if ( forward && useCache ) {

							// Seek `elem` from a previously-cached index

							// ...in a gzip-friendly way
							node = parent;
							outerCache = node[ expando ] || (node[ expando ] = {});

							// Support: IE <9 only
							// Defend against cloned attroperties (jQuery gh-1709)
							uniqueCache = outerCache[ node.uniqueID ] ||
								(outerCache[ node.uniqueID ] = {});

							cache = uniqueCache[ type ] || [];
							nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];
							diff = nodeIndex && cache[ 2 ];
							node = nodeIndex && parent.childNodes[ nodeIndex ];

							while ( (node = ++nodeIndex && node && node[ dir ] ||

								// Fallback to seeking `elem` from the start
								(diff = nodeIndex = 0) || start.pop()) ) {

								// When found, cache indexes on `parent` and break
								if ( node.nodeType === 1 && ++diff && node === elem ) {
									uniqueCache[ type ] = [ dirruns, nodeIndex, diff ];
									break;
								}
							}

						} else {
							// Use previously-cached element index if available
							if ( useCache ) {
								// ...in a gzip-friendly way
								node = elem;
								outerCache = node[ expando ] || (node[ expando ] = {});

								// Support: IE <9 only
								// Defend against cloned attroperties (jQuery gh-1709)
								uniqueCache = outerCache[ node.uniqueID ] ||
									(outerCache[ node.uniqueID ] = {});

								cache = uniqueCache[ type ] || [];
								nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];
								diff = nodeIndex;
							}

							// xml :nth-child(...)
							// or :nth-last-child(...) or :nth(-last)?-of-type(...)
							if ( diff === false ) {
								// Use the same loop as above to seek `elem` from the start
								while ( (node = ++nodeIndex && node && node[ dir ] ||
									(diff = nodeIndex = 0) || start.pop()) ) {

									if ( ( ofType ?
										node.nodeName.toLowerCase() === name :
										node.nodeType === 1 ) &&
										++diff ) {

										// Cache the index of each encountered element
										if ( useCache ) {
											outerCache = node[ expando ] || (node[ expando ] = {});

											// Support: IE <9 only
											// Defend against cloned attroperties (jQuery gh-1709)
											uniqueCache = outerCache[ node.uniqueID ] ||
												(outerCache[ node.uniqueID ] = {});

											uniqueCache[ type ] = [ dirruns, diff ];
										}

										if ( node === elem ) {
											break;
										}
									}
								}
							}
						}

						// Incorporate the offset, then check against cycle size
						diff -= last;
						return diff === first || ( diff % first === 0 && diff / first >= 0 );
					}
				};
		},

		"PSEUDO": function( pseudo, argument ) {
			// pseudo-class names are case-insensitive
			// http://www.w3.org/TR/selectors/#pseudo-classes
			// Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
			// Remember that setFilters inherits from pseudos
			var args,
				fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
					Sizzle.error( "unsupported pseudo: " + pseudo );

			// The user may use createPseudo to indicate that
			// arguments are needed to create the filter function
			// just as Sizzle does
			if ( fn[ expando ] ) {
				return fn( argument );
			}

			// But maintain support for old signatures
			if ( fn.length > 1 ) {
				args = [ pseudo, pseudo, "", argument ];
				return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
					markFunction(function( seed, matches ) {
						var idx,
							matched = fn( seed, argument ),
							i = matched.length;
						while ( i-- ) {
							idx = indexOf( seed, matched[i] );
							seed[ idx ] = !( matches[ idx ] = matched[i] );
						}
					}) :
					function( elem ) {
						return fn( elem, 0, args );
					};
			}

			return fn;
		}
	},

	pseudos: {
		// Potentially complex pseudos
		"not": markFunction(function( selector ) {
			// Trim the selector passed to compile
			// to avoid treating leading and trailing
			// spaces as combinators
			var input = [],
				results = [],
				matcher = compile( selector.replace( rtrim, "$1" ) );

			return matcher[ expando ] ?
				markFunction(function( seed, matches, context, xml ) {
					var elem,
						unmatched = matcher( seed, null, xml, [] ),
						i = seed.length;

					// Match elements unmatched by `matcher`
					while ( i-- ) {
						if ( (elem = unmatched[i]) ) {
							seed[i] = !(matches[i] = elem);
						}
					}
				}) :
				function( elem, context, xml ) {
					input[0] = elem;
					matcher( input, null, xml, results );
					// Don't keep the element (issue #299)
					input[0] = null;
					return !results.pop();
				};
		}),

		"has": markFunction(function( selector ) {
			return function( elem ) {
				return Sizzle( selector, elem ).length > 0;
			};
		}),

		"contains": markFunction(function( text ) {
			text = text.replace( runescape, funescape );
			return function( elem ) {
				return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
			};
		}),

		// "Whether an element is represented by a :lang() selector
		// is based solely on the element's language value
		// being equal to the identifier C,
		// or beginning with the identifier C immediately followed by "-".
		// The matching of C against the element's language value is performed case-insensitively.
		// The identifier C does not have to be a valid language name."
		// http://www.w3.org/TR/selectors/#lang-pseudo
		"lang": markFunction( function( lang ) {
			// lang value must be a valid identifier
			if ( !ridentifier.test(lang || "") ) {
				Sizzle.error( "unsupported lang: " + lang );
			}
			lang = lang.replace( runescape, funescape ).toLowerCase();
			return function( elem ) {
				var elemLang;
				do {
					if ( (elemLang = documentIsHTML ?
						elem.lang :
						elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) {

						elemLang = elemLang.toLowerCase();
						return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
					}
				} while ( (elem = elem.parentNode) && elem.nodeType === 1 );
				return false;
			};
		}),

		// Miscellaneous
		"target": function( elem ) {
			var hash = window.location && window.location.hash;
			return hash && hash.slice( 1 ) === elem.id;
		},

		"root": function( elem ) {
			return elem === docElem;
		},

		"focus": function( elem ) {
			return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
		},

		// Boolean properties
		"enabled": createDisabledPseudo( false ),
		"disabled": createDisabledPseudo( true ),

		"checked": function( elem ) {
			// In CSS3, :checked should return both checked and selected elements
			// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
			var nodeName = elem.nodeName.toLowerCase();
			return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
		},

		"selected": function( elem ) {
			// Accessing this property makes selected-by-default
			// options in Safari work properly
			if ( elem.parentNode ) {
				elem.parentNode.selectedIndex;
			}

			return elem.selected === true;
		},

		// Contents
		"empty": function( elem ) {
			// http://www.w3.org/TR/selectors/#empty-pseudo
			// :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5),
			//   but not by others (comment: 8; processing instruction: 7; etc.)
			// nodeType < 6 works because attributes (2) do not appear as children
			for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
				if ( elem.nodeType < 6 ) {
					return false;
				}
			}
			return true;
		},

		"parent": function( elem ) {
			return !Expr.pseudos["empty"]( elem );
		},

		// Element/input types
		"header": function( elem ) {
			return rheader.test( elem.nodeName );
		},

		"input": function( elem ) {
			return rinputs.test( elem.nodeName );
		},

		"button": function( elem ) {
			var name = elem.nodeName.toLowerCase();
			return name === "input" && elem.type === "button" || name === "button";
		},

		"text": function( elem ) {
			var attr;
			return elem.nodeName.toLowerCase() === "input" &&
				elem.type === "text" &&

				// Support: IE<8
				// New HTML5 attribute values (e.g., "search") appear with elem.type === "text"
				( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" );
		},

		// Position-in-collection
		"first": createPositionalPseudo(function() {
			return [ 0 ];
		}),

		"last": createPositionalPseudo(function( matchIndexes, length ) {
			return [ length - 1 ];
		}),

		"eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
			return [ argument < 0 ? argument + length : argument ];
		}),

		"even": createPositionalPseudo(function( matchIndexes, length ) {
			var i = 0;
			for ( ; i < length; i += 2 ) {
				matchIndexes.push( i );
			}
			return matchIndexes;
		}),

		"odd": createPositionalPseudo(function( matchIndexes, length ) {
			var i = 1;
			for ( ; i < length; i += 2 ) {
				matchIndexes.push( i );
			}
			return matchIndexes;
		}),

		"lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
			var i = argument < 0 ? argument + length : argument;
			for ( ; --i >= 0; ) {
				matchIndexes.push( i );
			}
			return matchIndexes;
		}),

		"gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
			var i = argument < 0 ? argument + length : argument;
			for ( ; ++i < length; ) {
				matchIndexes.push( i );
			}
			return matchIndexes;
		})
	}
};

Expr.pseudos["nth"] = Expr.pseudos["eq"];

// Add button/input type pseudos
for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
	Expr.pseudos[ i ] = createInputPseudo( i );
}
for ( i in { submit: true, reset: true } ) {
	Expr.pseudos[ i ] = createButtonPseudo( i );
}

// Easy API for creating new setFilters
function setFilters() {}
setFilters.prototype = Expr.filters = Expr.pseudos;
Expr.setFilters = new setFilters();

tokenize = Sizzle.tokenize = function( selector, parseOnly ) {
	var matched, match, tokens, type,
		soFar, groups, preFilters,
		cached = tokenCache[ selector + " " ];

	if ( cached ) {
		return parseOnly ? 0 : cached.slice( 0 );
	}

	soFar = selector;
	groups = [];
	preFilters = Expr.preFilter;

	while ( soFar ) {

		// Comma and first run
		if ( !matched || (match = rcomma.exec( soFar )) ) {
			if ( match ) {
				// Don't consume trailing commas as valid
				soFar = soFar.slice( match[0].length ) || soFar;
			}
			groups.push( (tokens = []) );
		}

		matched = false;

		// Combinators
		if ( (match = rcombinators.exec( soFar )) ) {
			matched = match.shift();
			tokens.push({
				value: matched,
				// Cast descendant combinators to space
				type: match[0].replace( rtrim, " " )
			});
			soFar = soFar.slice( matched.length );
		}

		// Filters
		for ( type in Expr.filter ) {
			if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
				(match = preFilters[ type ]( match ))) ) {
				matched = match.shift();
				tokens.push({
					value: matched,
					type: type,
					matches: match
				});
				soFar = soFar.slice( matched.length );
			}
		}

		if ( !matched ) {
			break;
		}
	}

	// Return the length of the invalid excess
	// if we're just parsing
	// Otherwise, throw an error or return tokens
	return parseOnly ?
		soFar.length :
		soFar ?
			Sizzle.error( selector ) :
			// Cache the tokens
			tokenCache( selector, groups ).slice( 0 );
};

function toSelector( tokens ) {
	var i = 0,
		len = tokens.length,
		selector = "";
	for ( ; i < len; i++ ) {
		selector += tokens[i].value;
	}
	return selector;
}

function addCombinator( matcher, combinator, base ) {
	var dir = combinator.dir,
		skip = combinator.next,
		key = skip || dir,
		checkNonElements = base && key === "parentNode",
		doneName = done++;

	return combinator.first ?
		// Check against closest ancestor/preceding element
		function( elem, context, xml ) {
			while ( (elem = elem[ dir ]) ) {
				if ( elem.nodeType === 1 || checkNonElements ) {
					return matcher( elem, context, xml );
				}
			}
			return false;
		} :

		// Check against all ancestor/preceding elements
		function( elem, context, xml ) {
			var oldCache, uniqueCache, outerCache,
				newCache = [ dirruns, doneName ];

			// We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching
			if ( xml ) {
				while ( (elem = elem[ dir ]) ) {
					if ( elem.nodeType === 1 || checkNonElements ) {
						if ( matcher( elem, context, xml ) ) {
							return true;
						}
					}
				}
			} else {
				while ( (elem = elem[ dir ]) ) {
					if ( elem.nodeType === 1 || checkNonElements ) {
						outerCache = elem[ expando ] || (elem[ expando ] = {});

						// Support: IE <9 only
						// Defend against cloned attroperties (jQuery gh-1709)
						uniqueCache = outerCache[ elem.uniqueID ] || (outerCache[ elem.uniqueID ] = {});

						if ( skip && skip === elem.nodeName.toLowerCase() ) {
							elem = elem[ dir ] || elem;
						} else if ( (oldCache = uniqueCache[ key ]) &&
							oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) {

							// Assign to newCache so results back-propagate to previous elements
							return (newCache[ 2 ] = oldCache[ 2 ]);
						} else {
							// Reuse newcache so results back-propagate to previous elements
							uniqueCache[ key ] = newCache;

							// A match means we're done; a fail means we have to keep checking
							if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) {
								return true;
							}
						}
					}
				}
			}
			return false;
		};
}

function elementMatcher( matchers ) {
	return matchers.length > 1 ?
		function( elem, context, xml ) {
			var i = matchers.length;
			while ( i-- ) {
				if ( !matchers[i]( elem, context, xml ) ) {
					return false;
				}
			}
			return true;
		} :
		matchers[0];
}

function multipleContexts( selector, contexts, results ) {
	var i = 0,
		len = contexts.length;
	for ( ; i < len; i++ ) {
		Sizzle( selector, contexts[i], results );
	}
	return results;
}

function condense( unmatched, map, filter, context, xml ) {
	var elem,
		newUnmatched = [],
		i = 0,
		len = unmatched.length,
		mapped = map != null;

	for ( ; i < len; i++ ) {
		if ( (elem = unmatched[i]) ) {
			if ( !filter || filter( elem, context, xml ) ) {
				newUnmatched.push( elem );
				if ( mapped ) {
					map.push( i );
				}
			}
		}
	}

	return newUnmatched;
}

function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
	if ( postFilter && !postFilter[ expando ] ) {
		postFilter = setMatcher( postFilter );
	}
	if ( postFinder && !postFinder[ expando ] ) {
		postFinder = setMatcher( postFinder, postSelector );
	}
	return markFunction(function( seed, results, context, xml ) {
		var temp, i, elem,
			preMap = [],
			postMap = [],
			preexisting = results.length,

			// Get initial elements from seed or context
			elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),

			// Prefilter to get matcher input, preserving a map for seed-results synchronization
			matcherIn = preFilter && ( seed || !selector ) ?
				condense( elems, preMap, preFilter, context, xml ) :
				elems,

			matcherOut = matcher ?
				// If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
				postFinder || ( seed ? preFilter : preexisting || postFilter ) ?

					// ...intermediate processing is necessary
					[] :

					// ...otherwise use results directly
					results :
				matcherIn;

		// Find primary matches
		if ( matcher ) {
			matcher( matcherIn, matcherOut, context, xml );
		}

		// Apply postFilter
		if ( postFilter ) {
			temp = condense( matcherOut, postMap );
			postFilter( temp, [], context, xml );

			// Un-match failing elements by moving them back to matcherIn
			i = temp.length;
			while ( i-- ) {
				if ( (elem = temp[i]) ) {
					matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
				}
			}
		}

		if ( seed ) {
			if ( postFinder || preFilter ) {
				if ( postFinder ) {
					// Get the final matcherOut by condensing this intermediate into postFinder contexts
					temp = [];
					i = matcherOut.length;
					while ( i-- ) {
						if ( (elem = matcherOut[i]) ) {
							// Restore matcherIn since elem is not yet a final match
							temp.push( (matcherIn[i] = elem) );
						}
					}
					postFinder( null, (matcherOut = []), temp, xml );
				}

				// Move matched elements from seed to results to keep them synchronized
				i = matcherOut.length;
				while ( i-- ) {
					if ( (elem = matcherOut[i]) &&
						(temp = postFinder ? indexOf( seed, elem ) : preMap[i]) > -1 ) {

						seed[temp] = !(results[temp] = elem);
					}
				}
			}

		// Add elements to results, through postFinder if defined
		} else {
			matcherOut = condense(
				matcherOut === results ?
					matcherOut.splice( preexisting, matcherOut.length ) :
					matcherOut
			);
			if ( postFinder ) {
				postFinder( null, results, matcherOut, xml );
			} else {
				push.apply( results, matcherOut );
			}
		}
	});
}

function matcherFromTokens( tokens ) {
	var checkContext, matcher, j,
		len = tokens.length,
		leadingRelative = Expr.relative[ tokens[0].type ],
		implicitRelative = leadingRelative || Expr.relative[" "],
		i = leadingRelative ? 1 : 0,

		// The foundational matcher ensures that elements are reachable from top-level context(s)
		matchContext = addCombinator( function( elem ) {
			return elem === checkContext;
		}, implicitRelative, true ),
		matchAnyContext = addCombinator( function( elem ) {
			return indexOf( checkContext, elem ) > -1;
		}, implicitRelative, true ),
		matchers = [ function( elem, context, xml ) {
			var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
				(checkContext = context).nodeType ?
					matchContext( elem, context, xml ) :
					matchAnyContext( elem, context, xml ) );
			// Avoid hanging onto element (issue #299)
			checkContext = null;
			return ret;
		} ];

	for ( ; i < len; i++ ) {
		if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
			matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];
		} else {
			matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );

			// Return special upon seeing a positional matcher
			if ( matcher[ expando ] ) {
				// Find the next relative operator (if any) for proper handling
				j = ++i;
				for ( ; j < len; j++ ) {
					if ( Expr.relative[ tokens[j].type ] ) {
						break;
					}
				}
				return setMatcher(
					i > 1 && elementMatcher( matchers ),
					i > 1 && toSelector(
						// If the preceding token was a descendant combinator, insert an implicit any-element `*`
						tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" })
					).replace( rtrim, "$1" ),
					matcher,
					i < j && matcherFromTokens( tokens.slice( i, j ) ),
					j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
					j < len && toSelector( tokens )
				);
			}
			matchers.push( matcher );
		}
	}

	return elementMatcher( matchers );
}

function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
	var bySet = setMatchers.length > 0,
		byElement = elementMatchers.length > 0,
		superMatcher = function( seed, context, xml, results, outermost ) {
			var elem, j, matcher,
				matchedCount = 0,
				i = "0",
				unmatched = seed && [],
				setMatched = [],
				contextBackup = outermostContext,
				// We must always have either seed elements or outermost context
				elems = seed || byElement && Expr.find["TAG"]( "*", outermost ),
				// Use integer dirruns iff this is the outermost matcher
				dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1),
				len = elems.length;

			if ( outermost ) {
				outermostContext = context === document || context || outermost;
			}

			// Add elements passing elementMatchers directly to results
			// Support: IE<9, Safari
			// Tolerate NodeList properties (IE: "length"; Safari: <number>) matching elements by id
			for ( ; i !== len && (elem = elems[i]) != null; i++ ) {
				if ( byElement && elem ) {
					j = 0;
					if ( !context && elem.ownerDocument !== document ) {
						setDocument( elem );
						xml = !documentIsHTML;
					}
					while ( (matcher = elementMatchers[j++]) ) {
						if ( matcher( elem, context || document, xml) ) {
							results.push( elem );
							break;
						}
					}
					if ( outermost ) {
						dirruns = dirrunsUnique;
					}
				}

				// Track unmatched elements for set filters
				if ( bySet ) {
					// They will have gone through all possible matchers
					if ( (elem = !matcher && elem) ) {
						matchedCount--;
					}

					// Lengthen the array for every element, matched or not
					if ( seed ) {
						unmatched.push( elem );
					}
				}
			}

			// `i` is now the count of elements visited above, and adding it to `matchedCount`
			// makes the latter nonnegative.
			matchedCount += i;

			// Apply set filters to unmatched elements
			// NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount`
			// equals `i`), unless we didn't visit _any_ elements in the above loop because we have
			// no element matchers and no seed.
			// Incrementing an initially-string "0" `i` allows `i` to remain a string only in that
			// case, which will result in a "00" `matchedCount` that differs from `i` but is also
			// numerically zero.
			if ( bySet && i !== matchedCount ) {
				j = 0;
				while ( (matcher = setMatchers[j++]) ) {
					matcher( unmatched, setMatched, context, xml );
				}

				if ( seed ) {
					// Reintegrate element matches to eliminate the need for sorting
					if ( matchedCount > 0 ) {
						while ( i-- ) {
							if ( !(unmatched[i] || setMatched[i]) ) {
								setMatched[i] = pop.call( results );
							}
						}
					}

					// Discard index placeholder values to get only actual matches
					setMatched = condense( setMatched );
				}

				// Add matches to results
				push.apply( results, setMatched );

				// Seedless set matches succeeding multiple successful matchers stipulate sorting
				if ( outermost && !seed && setMatched.length > 0 &&
					( matchedCount + setMatchers.length ) > 1 ) {

					Sizzle.uniqueSort( results );
				}
			}

			// Override manipulation of globals by nested matchers
			if ( outermost ) {
				dirruns = dirrunsUnique;
				outermostContext = contextBackup;
			}

			return unmatched;
		};

	return bySet ?
		markFunction( superMatcher ) :
		superMatcher;
}

compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) {
	var i,
		setMatchers = [],
		elementMatchers = [],
		cached = compilerCache[ selector + " " ];

	if ( !cached ) {
		// Generate a function of recursive functions that can be used to check each element
		if ( !match ) {
			match = tokenize( selector );
		}
		i = match.length;
		while ( i-- ) {
			cached = matcherFromTokens( match[i] );
			if ( cached[ expando ] ) {
				setMatchers.push( cached );
			} else {
				elementMatchers.push( cached );
			}
		}

		// Cache the compiled function
		cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );

		// Save selector and tokenization
		cached.selector = selector;
	}
	return cached;
};

/**
 * A low-level selection function that works with Sizzle's compiled
 *  selector functions
 * @param {String|Function} selector A selector or a pre-compiled
 *  selector function built with Sizzle.compile
 * @param {Element} context
 * @param {Array} [results]
 * @param {Array} [seed] A set of elements to match against
 */
select = Sizzle.select = function( selector, context, results, seed ) {
	var i, tokens, token, type, find,
		compiled = typeof selector === "function" && selector,
		match = !seed && tokenize( (selector = compiled.selector || selector) );

	results = results || [];

	// Try to minimize operations if there is only one selector in the list and no seed
	// (the latter of which guarantees us context)
	if ( match.length === 1 ) {

		// Reduce context if the leading compound selector is an ID
		tokens = match[0] = match[0].slice( 0 );
		if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
				context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[1].type ] ) {

			context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0];
			if ( !context ) {
				return results;

			// Precompiled matchers will still verify ancestry, so step up a level
			} else if ( compiled ) {
				context = context.parentNode;
			}

			selector = selector.slice( tokens.shift().value.length );
		}

		// Fetch a seed set for right-to-left matching
		i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length;
		while ( i-- ) {
			token = tokens[i];

			// Abort if we hit a combinator
			if ( Expr.relative[ (type = token.type) ] ) {
				break;
			}
			if ( (find = Expr.find[ type ]) ) {
				// Search, expanding context for leading sibling combinators
				if ( (seed = find(
					token.matches[0].replace( runescape, funescape ),
					rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context
				)) ) {

					// If seed is empty or no tokens remain, we can return early
					tokens.splice( i, 1 );
					selector = seed.length && toSelector( tokens );
					if ( !selector ) {
						push.apply( results, seed );
						return results;
					}

					break;
				}
			}
		}
	}

	// Compile and execute a filtering function if one is not provided
	// Provide `match` to avoid retokenization if we modified the selector above
	( compiled || compile( selector, match ) )(
		seed,
		context,
		!documentIsHTML,
		results,
		!context || rsibling.test( selector ) && testContext( context.parentNode ) || context
	);
	return results;
};

// One-time assignments

// Sort stability
support.sortStable = expando.split("").sort( sortOrder ).join("") === expando;

// Support: Chrome 14-35+
// Always assume duplicates if they aren't passed to the comparison function
support.detectDuplicates = !!hasDuplicate;

// Initialize against the default document
setDocument();

// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27)
// Detached nodes confoundingly follow *each other*
support.sortDetached = assert(function( el ) {
	// Should return 1, but returns 4 (following)
	return el.compareDocumentPosition( document.createElement("fieldset") ) & 1;
});

// Support: IE<8
// Prevent attribute/property "interpolation"
// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
if ( !assert(function( el ) {
	el.innerHTML = "<a href='#'></a>";
	return el.firstChild.getAttribute("href") === "#" ;
}) ) {
	addHandle( "type|href|height|width", function( elem, name, isXML ) {
		if ( !isXML ) {
			return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 );
		}
	});
}

// Support: IE<9
// Use defaultValue in place of getAttribute("value")
if ( !support.attributes || !assert(function( el ) {
	el.innerHTML = "<input/>";
	el.firstChild.setAttribute( "value", "" );
	return el.firstChild.getAttribute( "value" ) === "";
}) ) {
	addHandle( "value", function( elem, name, isXML ) {
		if ( !isXML && elem.nodeName.toLowerCase() === "input" ) {
			return elem.defaultValue;
		}
	});
}

// Support: IE<9
// Use getAttributeNode to fetch booleans when getAttribute lies
if ( !assert(function( el ) {
	return el.getAttribute("disabled") == null;
}) ) {
	addHandle( booleans, function( elem, name, isXML ) {
		var val;
		if ( !isXML ) {
			return elem[ name ] === true ? name.toLowerCase() :
					(val = elem.getAttributeNode( name )) && val.specified ?
					val.value :
				null;
		}
	});
}

return Sizzle;

})( window );



jQuery.find = Sizzle;
jQuery.expr = Sizzle.selectors;

// Deprecated
jQuery.expr[ ":" ] = jQuery.expr.pseudos;
jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort;
jQuery.text = Sizzle.getText;
jQuery.isXMLDoc = Sizzle.isXML;
jQuery.contains = Sizzle.contains;
jQuery.escapeSelector = Sizzle.escape;




var dir = function( elem, dir, until ) {
	var matched = [],
		truncate = until !== undefined;

	while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) {
		if ( elem.nodeType === 1 ) {
			if ( truncate && jQuery( elem ).is( until ) ) {
				break;
			}
			matched.push( elem );
		}
	}
	return matched;
};


var siblings = function( n, elem ) {
	var matched = [];

	for ( ; n; n = n.nextSibling ) {
		if ( n.nodeType === 1 && n !== elem ) {
			matched.push( n );
		}
	}

	return matched;
};


var rneedsContext = jQuery.expr.match.needsContext;



function nodeName( elem, name ) {

  return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();

};
var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i );



// Implement the identical functionality for filter and not
function winnow( elements, qualifier, not ) {
	if ( isFunction( qualifier ) ) {
		return jQuery.grep( elements, function( elem, i ) {
			return !!qualifier.call( elem, i, elem ) !== not;
		} );
	}

	// Single element
	if ( qualifier.nodeType ) {
		return jQuery.grep( elements, function( elem ) {
			return ( elem === qualifier ) !== not;
		} );
	}

	// Arraylike of elements (jQuery, arguments, Array)
	if ( typeof qualifier !== "string" ) {
		return jQuery.grep( elements, function( elem ) {
			return ( indexOf.call( qualifier, elem ) > -1 ) !== not;
		} );
	}

	// Filtered directly for both simple and complex selectors
	return jQuery.filter( qualifier, elements, not );
}

jQuery.filter = function( expr, elems, not ) {
	var elem = elems[ 0 ];

	if ( not ) {
		expr = ":not(" + expr + ")";
	}

	if ( elems.length === 1 && elem.nodeType === 1 ) {
		return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [];
	}

	return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {
		return elem.nodeType === 1;
	} ) );
};

jQuery.fn.extend( {
	find: function( selector ) {
		var i, ret,
			len = this.length,
			self = this;

		if ( typeof selector !== "string" ) {
			return this.pushStack( jQuery( selector ).filter( function() {
				for ( i = 0; i < len; i++ ) {
					if ( jQuery.contains( self[ i ], this ) ) {
						return true;
					}
				}
			} ) );
		}

		ret = this.pushStack( [] );

		for ( i = 0; i < len; i++ ) {
			jQuery.find( selector, self[ i ], ret );
		}

		return len > 1 ? jQuery.uniqueSort( ret ) : ret;
	},
	filter: function( selector ) {
		return this.pushStack( winnow( this, selector || [], false ) );
	},
	not: function( selector ) {
		return this.pushStack( winnow( this, selector || [], true ) );
	},
	is: function( selector ) {
		return !!winnow(
			this,

			// If this is a positional/relative selector, check membership in the returned set
			// so $("p:first").is("p:last") won't return true for a doc with two "p".
			typeof selector === "string" && rneedsContext.test( selector ) ?
				jQuery( selector ) :
				selector || [],
			false
		).length;
	}
} );


// Initialize a jQuery object


// A central reference to the root jQuery(document)
var rootjQuery,

	// A simple way to check for HTML strings
	// Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
	// Strict HTML recognition (#11290: must start with <)
	// Shortcut simple #id case for speed
	rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,

	init = jQuery.fn.init = function( selector, context, root ) {
		var match, elem;

		// HANDLE: $(""), $(null), $(undefined), $(false)
		if ( !selector ) {
			return this;
		}

		// Method init() accepts an alternate rootjQuery
		// so migrate can support jQuery.sub (gh-2101)
		root = root || rootjQuery;

		// Handle HTML strings
		if ( typeof selector === "string" ) {
			if ( selector[ 0 ] === "<" &&
				selector[ selector.length - 1 ] === ">" &&
				selector.length >= 3 ) {

				// Assume that strings that start and end with <> are HTML and skip the regex check
				match = [ null, selector, null ];

			} else {
				match = rquickExpr.exec( selector );
			}

			// Match html or make sure no context is specified for #id
			if ( match && ( match[ 1 ] || !context ) ) {

				// HANDLE: $(html) -> $(array)
				if ( match[ 1 ] ) {
					context = context instanceof jQuery ? context[ 0 ] : context;

					// Option to run scripts is true for back-compat
					// Intentionally let the error be thrown if parseHTML is not present
					jQuery.merge( this, jQuery.parseHTML(
						match[ 1 ],
						context && context.nodeType ? context.ownerDocument || context : document,
						true
					) );

					// HANDLE: $(html, props)
					if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) {
						for ( match in context ) {

							// Properties of context are called as methods if possible
							if ( isFunction( this[ match ] ) ) {
								this[ match ]( context[ match ] );

							// ...and otherwise set as attributes
							} else {
								this.attr( match, context[ match ] );
							}
						}
					}

					return this;

				// HANDLE: $(#id)
				} else {
					elem = document.getElementById( match[ 2 ] );

					if ( elem ) {

						// Inject the element directly into the jQuery object
						this[ 0 ] = elem;
						this.length = 1;
					}
					return this;
				}

			// HANDLE: $(expr, $(...))
			} else if ( !context || context.jquery ) {
				return ( context || root ).find( selector );

			// HANDLE: $(expr, context)
			// (which is just equivalent to: $(context).find(expr)
			} else {
				return this.constructor( context ).find( selector );
			}

		// HANDLE: $(DOMElement)
		} else if ( selector.nodeType ) {
			this[ 0 ] = selector;
			this.length = 1;
			return this;

		// HANDLE: $(function)
		// Shortcut for document ready
		} else if ( isFunction( selector ) ) {
			return root.ready !== undefined ?
				root.ready( selector ) :

				// Execute immediately if ready is not present
				selector( jQuery );
		}

		return jQuery.makeArray( selector, this );
	};

// Give the init function the jQuery prototype for later instantiation
init.prototype = jQuery.fn;

// Initialize central reference
rootjQuery = jQuery( document );


var rparentsprev = /^(?:parents|prev(?:Until|All))/,

	// Methods guaranteed to produce a unique set when starting from a unique set
	guaranteedUnique = {
		children: true,
		contents: true,
		next: true,
		prev: true
	};

jQuery.fn.extend( {
	has: function( target ) {
		var targets = jQuery( target, this ),
			l = targets.length;

		return this.filter( function() {
			var i = 0;
			for ( ; i < l; i++ ) {
				if ( jQuery.contains( this, targets[ i ] ) ) {
					return true;
				}
			}
		} );
	},

	closest: function( selectors, context ) {
		var cur,
			i = 0,
			l = this.length,
			matched = [],
			targets = typeof selectors !== "string" && jQuery( selectors );

		// Positional selectors never match, since there's no _selection_ context
		if ( !rneedsContext.test( selectors ) ) {
			for ( ; i < l; i++ ) {
				for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) {

					// Always skip document fragments
					if ( cur.nodeType < 11 && ( targets ?
						targets.index( cur ) > -1 :

						// Don't pass non-elements to Sizzle
						cur.nodeType === 1 &&
							jQuery.find.matchesSelector( cur, selectors ) ) ) {

						matched.push( cur );
						break;
					}
				}
			}
		}

		return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched );
	},

	// Determine the position of an element within the set
	index: function( elem ) {

		// No argument, return index in parent
		if ( !elem ) {
			return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1;
		}

		// Index in selector
		if ( typeof elem === "string" ) {
			return indexOf.call( jQuery( elem ), this[ 0 ] );
		}

		// Locate the position of the desired element
		return indexOf.call( this,

			// If it receives a jQuery object, the first element is used
			elem.jquery ? elem[ 0 ] : elem
		);
	},

	add: function( selector, context ) {
		return this.pushStack(
			jQuery.uniqueSort(
				jQuery.merge( this.get(), jQuery( selector, context ) )
			)
		);
	},

	addBack: function( selector ) {
		return this.add( selector == null ?
			this.prevObject : this.prevObject.filter( selector )
		);
	}
} );

function sibling( cur, dir ) {
	while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {}
	return cur;
}

jQuery.each( {
	parent: function( elem ) {
		var parent = elem.parentNode;
		return parent && parent.nodeType !== 11 ? parent : null;
	},
	parents: function( elem ) {
		return dir( elem, "parentNode" );
	},
	parentsUntil: function( elem, i, until ) {
		return dir( elem, "parentNode", until );
	},
	next: function( elem ) {
		return sibling( elem, "nextSibling" );
	},
	prev: function( elem ) {
		return sibling( elem, "previousSibling" );
	},
	nextAll: function( elem ) {
		return dir( elem, "nextSibling" );
	},
	prevAll: function( elem ) {
		return dir( elem, "previousSibling" );
	},
	nextUntil: function( elem, i, until ) {
		return dir( elem, "nextSibling", until );
	},
	prevUntil: function( elem, i, until ) {
		return dir( elem, "previousSibling", until );
	},
	siblings: function( elem ) {
		return siblings( ( elem.parentNode || {} ).firstChild, elem );
	},
	children: function( elem ) {
		return siblings( elem.firstChild );
	},
	contents: function( elem ) {
        if ( nodeName( elem, "iframe" ) ) {
            return elem.contentDocument;
        }

        // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only
        // Treat the template element as a regular one in browsers that
        // don't support it.
        if ( nodeName( elem, "template" ) ) {
            elem = elem.content || elem;
        }

        return jQuery.merge( [], elem.childNodes );
	}
}, function( name, fn ) {
	jQuery.fn[ name ] = function( until, selector ) {
		var matched = jQuery.map( this, fn, until );

		if ( name.slice( -5 ) !== "Until" ) {
			selector = until;
		}

		if ( selector && typeof selector === "string" ) {
			matched = jQuery.filter( selector, matched );
		}

		if ( this.length > 1 ) {

			// Remove duplicates
			if ( !guaranteedUnique[ name ] ) {
				jQuery.uniqueSort( matched );
			}

			// Reverse order for parents* and prev-derivatives
			if ( rparentsprev.test( name ) ) {
				matched.reverse();
			}
		}

		return this.pushStack( matched );
	};
} );
var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g );



// Convert String-formatted options into Object-formatted ones
function createOptions( options ) {
	var object = {};
	jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) {
		object[ flag ] = true;
	} );
	return object;
}

/*
 * Create a callback list using the following parameters:
 *
 *	options: an optional list of space-separated options that will change how
 *			the callback list behaves or a more traditional option object
 *
 * By default a callback list will act like an event callback list and can be
 * "fired" multiple times.
 *
 * Possible options:
 *
 *	once:			will ensure the callback list can only be fired once (like a Deferred)
 *
 *	memory:			will keep track of previous values and will call any callback added
 *					after the list has been fired right away with the latest "memorized"
 *					values (like a Deferred)
 *
 *	unique:			will ensure a callback can only be added once (no duplicate in the list)
 *
 *	stopOnFalse:	interrupt callings when a callback returns false
 *
 */
jQuery.Callbacks = function( options ) {

	// Convert options from String-formatted to Object-formatted if needed
	// (we check in cache first)
	options = typeof options === "string" ?
		createOptions( options ) :
		jQuery.extend( {}, options );

	var // Flag to know if list is currently firing
		firing,

		// Last fire value for non-forgettable lists
		memory,

		// Flag to know if list was already fired
		fired,

		// Flag to prevent firing
		locked,

		// Actual callback list
		list = [],

		// Queue of execution data for repeatable lists
		queue = [],

		// Index of currently firing callback (modified by add/remove as needed)
		firingIndex = -1,

		// Fire callbacks
		fire = function() {

			// Enforce single-firing
			locked = locked || options.once;

			// Execute callbacks for all pending executions,
			// respecting firingIndex overrides and runtime changes
			fired = firing = true;
			for ( ; queue.length; firingIndex = -1 ) {
				memory = queue.shift();
				while ( ++firingIndex < list.length ) {

					// Run callback and check for early termination
					if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false &&
						options.stopOnFalse ) {

						// Jump to end and forget the data so .add doesn't re-fire
						firingIndex = list.length;
						memory = false;
					}
				}
			}

			// Forget the data if we're done with it
			if ( !options.memory ) {
				memory = false;
			}

			firing = false;

			// Clean up if we're done firing for good
			if ( locked ) {

				// Keep an empty list if we have data for future add calls
				if ( memory ) {
					list = [];

				// Otherwise, this object is spent
				} else {
					list = "";
				}
			}
		},

		// Actual Callbacks object
		self = {

			// Add a callback or a collection of callbacks to the list
			add: function() {
				if ( list ) {

					// If we have memory from a past run, we should fire after adding
					if ( memory && !firing ) {
						firingIndex = list.length - 1;
						queue.push( memory );
					}

					( function add( args ) {
						jQuery.each( args, function( _, arg ) {
							if ( isFunction( arg ) ) {
								if ( !options.unique || !self.has( arg ) ) {
									list.push( arg );
								}
							} else if ( arg && arg.length && toType( arg ) !== "string" ) {

								// Inspect recursively
								add( arg );
							}
						} );
					} )( arguments );

					if ( memory && !firing ) {
						fire();
					}
				}
				return this;
			},

			// Remove a callback from the list
			remove: function() {
				jQuery.each( arguments, function( _, arg ) {
					var index;
					while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
						list.splice( index, 1 );

						// Handle firing indexes
						if ( index <= firingIndex ) {
							firingIndex--;
						}
					}
				} );
				return this;
			},

			// Check if a given callback is in the list.
			// If no argument is given, return whether or not list has callbacks attached.
			has: function( fn ) {
				return fn ?
					jQuery.inArray( fn, list ) > -1 :
					list.length > 0;
			},

			// Remove all callbacks from the list
			empty: function() {
				if ( list ) {
					list = [];
				}
				return this;
			},

			// Disable .fire and .add
			// Abort any current/pending executions
			// Clear all callbacks and values
			disable: function() {
				locked = queue = [];
				list = memory = "";
				return this;
			},
			disabled: function() {
				return !list;
			},

			// Disable .fire
			// Also disable .add unless we have memory (since it would have no effect)
			// Abort any pending executions
			lock: function() {
				locked = queue = [];
				if ( !memory && !firing ) {
					list = memory = "";
				}
				return this;
			},
			locked: function() {
				return !!locked;
			},

			// Call all callbacks with the given context and arguments
			fireWith: function( context, args ) {
				if ( !locked ) {
					args = args || [];
					args = [ context, args.slice ? args.slice() : args ];
					queue.push( args );
					if ( !firing ) {
						fire();
					}
				}
				return this;
			},

			// Call all the callbacks with the given arguments
			fire: function() {
				self.fireWith( this, arguments );
				return this;
			},

			// To know if the callbacks have already been called at least once
			fired: function() {
				return !!fired;
			}
		};

	return self;
};


function Identity( v ) {
	return v;
}
function Thrower( ex ) {
	throw ex;
}

function adoptValue( value, resolve, reject, noValue ) {
	var method;

	try {

		// Check for promise aspect first to privilege synchronous behavior
		if ( value && isFunction( ( method = value.promise ) ) ) {
			method.call( value ).done( resolve ).fail( reject );

		// Other thenables
		} else if ( value && isFunction( ( method = value.then ) ) ) {
			method.call( value, resolve, reject );

		// Other non-thenables
		} else {

			// Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer:
			// * false: [ value ].slice( 0 ) => resolve( value )
			// * true: [ value ].slice( 1 ) => resolve()
			resolve.apply( undefined, [ value ].slice( noValue ) );
		}

	// For Promises/A+, convert exceptions into rejections
	// Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in
	// Deferred#then to conditionally suppress rejection.
	} catch ( value ) {

		// Support: Android 4.0 only
		// Strict mode functions invoked without .call/.apply get global-object context
		reject.apply( undefined, [ value ] );
	}
}

jQuery.extend( {

	Deferred: function( func ) {
		var tuples = [

				// action, add listener, callbacks,
				// ... .then handlers, argument index, [final state]
				[ "notify", "progress", jQuery.Callbacks( "memory" ),
					jQuery.Callbacks( "memory" ), 2 ],
				[ "resolve", "done", jQuery.Callbacks( "once memory" ),
					jQuery.Callbacks( "once memory" ), 0, "resolved" ],
				[ "reject", "fail", jQuery.Callbacks( "once memory" ),
					jQuery.Callbacks( "once memory" ), 1, "rejected" ]
			],
			state = "pending",
			promise = {
				state: function() {
					return state;
				},
				always: function() {
					deferred.done( arguments ).fail( arguments );
					return this;
				},
				"catch": function( fn ) {
					return promise.then( null, fn );
				},

				// Keep pipe for back-compat
				pipe: function( /* fnDone, fnFail, fnProgress */ ) {
					var fns = arguments;

					return jQuery.Deferred( function( newDefer ) {
						jQuery.each( tuples, function( i, tuple ) {

							// Map tuples (progress, done, fail) to arguments (done, fail, progress)
							var fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ];

							// deferred.progress(function() { bind to newDefer or newDefer.notify })
							// deferred.done(function() { bind to newDefer or newDefer.resolve })
							// deferred.fail(function() { bind to newDefer or newDefer.reject })
							deferred[ tuple[ 1 ] ]( function() {
								var returned = fn && fn.apply( this, arguments );
								if ( returned && isFunction( returned.promise ) ) {
									returned.promise()
										.progress( newDefer.notify )
										.done( newDefer.resolve )
										.fail( newDefer.reject );
								} else {
									newDefer[ tuple[ 0 ] + "With" ](
										this,
										fn ? [ returned ] : arguments
									);
								}
							} );
						} );
						fns = null;
					} ).promise();
				},
				then: function( onFulfilled, onRejected, onProgress ) {
					var maxDepth = 0;
					function resolve( depth, deferred, handler, special ) {
						return function() {
							var that = this,
								args = arguments,
								mightThrow = function() {
									var returned, then;

									// Support: Promises/A+ section 2.3.3.3.3
									// https://promisesaplus.com/#point-59
									// Ignore double-resolution attempts
									if ( depth < maxDepth ) {
										return;
									}

									returned = handler.apply( that, args );

									// Support: Promises/A+ section 2.3.1
									// https://promisesaplus.com/#point-48
									if ( returned === deferred.promise() ) {
										throw new TypeError( "Thenable self-resolution" );
									}

									// Support: Promises/A+ sections 2.3.3.1, 3.5
									// https://promisesaplus.com/#point-54
									// https://promisesaplus.com/#point-75
									// Retrieve `then` only once
									then = returned &&

										// Support: Promises/A+ section 2.3.4
										// https://promisesaplus.com/#point-64
										// Only check objects and functions for thenability
										( typeof returned === "object" ||
											typeof returned === "function" ) &&
										returned.then;

									// Handle a returned thenable
									if ( isFunction( then ) ) {

										// Special processors (notify) just wait for resolution
										if ( special ) {
											then.call(
												returned,
												resolve( maxDepth, deferred, Identity, special ),
												resolve( maxDepth, deferred, Thrower, special )
											);

										// Normal processors (resolve) also hook into progress
										} else {

											// ...and disregard older resolution values
											maxDepth++;

											then.call(
												returned,
												resolve( maxDepth, deferred, Identity, special ),
												resolve( maxDepth, deferred, Thrower, special ),
												resolve( maxDepth, deferred, Identity,
													deferred.notifyWith )
											);
										}

									// Handle all other returned values
									} else {

										// Only substitute handlers pass on context
										// and multiple values (non-spec behavior)
										if ( handler !== Identity ) {
											that = undefined;
											args = [ returned ];
										}

										// Process the value(s)
										// Default process is resolve
										( special || deferred.resolveWith )( that, args );
									}
								},

								// Only normal processors (resolve) catch and reject exceptions
								process = special ?
									mightThrow :
									function() {
										try {
											mightThrow();
										} catch ( e ) {

											if ( jQuery.Deferred.exceptionHook ) {
												jQuery.Deferred.exceptionHook( e,
													process.stackTrace );
											}

											// Support: Promises/A+ section 2.3.3.3.4.1
											// https://promisesaplus.com/#point-61
											// Ignore post-resolution exceptions
											if ( depth + 1 >= maxDepth ) {

												// Only substitute handlers pass on context
												// and multiple values (non-spec behavior)
												if ( handler !== Thrower ) {
													that = undefined;
													args = [ e ];
												}

												deferred.rejectWith( that, args );
											}
										}
									};

							// Support: Promises/A+ section 2.3.3.3.1
							// https://promisesaplus.com/#point-57
							// Re-resolve promises immediately to dodge false rejection from
							// subsequent errors
							if ( depth ) {
								process();
							} else {

								// Call an optional hook to record the stack, in case of exception
								// since it's otherwise lost when execution goes async
								if ( jQuery.Deferred.getStackHook ) {
									process.stackTrace = jQuery.Deferred.getStackHook();
								}
								window.setTimeout( process );
							}
						};
					}

					return jQuery.Deferred( function( newDefer ) {

						// progress_handlers.add( ... )
						tuples[ 0 ][ 3 ].add(
							resolve(
								0,
								newDefer,
								isFunction( onProgress ) ?
									onProgress :
									Identity,
								newDefer.notifyWith
							)
						);

						// fulfilled_handlers.add( ... )
						tuples[ 1 ][ 3 ].add(
							resolve(
								0,
								newDefer,
								isFunction( onFulfilled ) ?
									onFulfilled :
									Identity
							)
						);

						// rejected_handlers.add( ... )
						tuples[ 2 ][ 3 ].add(
							resolve(
								0,
								newDefer,
								isFunction( onRejected ) ?
									onRejected :
									Thrower
							)
						);
					} ).promise();
				},

				// Get a promise for this deferred
				// If obj is provided, the promise aspect is added to the object
				promise: function( obj ) {
					return obj != null ? jQuery.extend( obj, promise ) : promise;
				}
			},
			deferred = {};

		// Add list-specific methods
		jQuery.each( tuples, function( i, tuple ) {
			var list = tuple[ 2 ],
				stateString = tuple[ 5 ];

			// promise.progress = list.add
			// promise.done = list.add
			// promise.fail = list.add
			promise[ tuple[ 1 ] ] = list.add;

			// Handle state
			if ( stateString ) {
				list.add(
					function() {

						// state = "resolved" (i.e., fulfilled)
						// state = "rejected"
						state = stateString;
					},

					// rejected_callbacks.disable
					// fulfilled_callbacks.disable
					tuples[ 3 - i ][ 2 ].disable,

					// rejected_handlers.disable
					// fulfilled_handlers.disable
					tuples[ 3 - i ][ 3 ].disable,

					// progress_callbacks.lock
					tuples[ 0 ][ 2 ].lock,

					// progress_handlers.lock
					tuples[ 0 ][ 3 ].lock
				);
			}

			// progress_handlers.fire
			// fulfilled_handlers.fire
			// rejected_handlers.fire
			list.add( tuple[ 3 ].fire );

			// deferred.notify = function() { deferred.notifyWith(...) }
			// deferred.resolve = function() { deferred.resolveWith(...) }
			// deferred.reject = function() { deferred.rejectWith(...) }
			deferred[ tuple[ 0 ] ] = function() {
				deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments );
				return this;
			};

			// deferred.notifyWith = list.fireWith
			// deferred.resolveWith = list.fireWith
			// deferred.rejectWith = list.fireWith
			deferred[ tuple[ 0 ] + "With" ] = list.fireWith;
		} );

		// Make the deferred a promise
		promise.promise( deferred );

		// Call given func if any
		if ( func ) {
			func.call( deferred, deferred );
		}

		// All done!
		return deferred;
	},

	// Deferred helper
	when: function( singleValue ) {
		var

			// count of uncompleted subordinates
			remaining = arguments.length,

			// count of unprocessed arguments
			i = remaining,

			// subordinate fulfillment data
			resolveContexts = Array( i ),
			resolveValues = slice.call( arguments ),

			// the master Deferred
			master = jQuery.Deferred(),

			// subordinate callback factory
			updateFunc = function( i ) {
				return function( value ) {
					resolveContexts[ i ] = this;
					resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;
					if ( !( --remaining ) ) {
						master.resolveWith( resolveContexts, resolveValues );
					}
				};
			};

		// Single- and empty arguments are adopted like Promise.resolve
		if ( remaining <= 1 ) {
			adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject,
				!remaining );

			// Use .then() to unwrap secondary thenables (cf. gh-3000)
			if ( master.state() === "pending" ||
				isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) {

				return master.then();
			}
		}

		// Multiple arguments are aggregated like Promise.all array elements
		while ( i-- ) {
			adoptValue( resolveValues[ i ], updateFunc( i ), master.reject );
		}

		return master.promise();
	}
} );


// These usually indicate a programmer mistake during development,
// warn about them ASAP rather than swallowing them by default.
var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;

jQuery.Deferred.exceptionHook = function( error, stack ) {

	// Support: IE 8 - 9 only
	// Console exists when dev tools are open, which can happen at any time
	if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) {
		window.console.warn( "jQuery.Deferred exception: " + error.message, error.stack, stack );
	}
};




jQuery.readyException = function( error ) {
	window.setTimeout( function() {
		throw error;
	} );
};




// The deferred used on DOM ready
var readyList = jQuery.Deferred();

jQuery.fn.ready = function( fn ) {

	readyList
		.then( fn )

		// Wrap jQuery.readyException in a function so that the lookup
		// happens at the time of error handling instead of callback
		// registration.
		.catch( function( error ) {
			jQuery.readyException( error );
		} );

	return this;
};

jQuery.extend( {

	// Is the DOM ready to be used? Set to true once it occurs.
	isReady: false,

	// A counter to track how many items to wait for before
	// the ready event fires. See #6781
	readyWait: 1,

	// Handle when the DOM is ready
	ready: function( wait ) {

		// Abort if there are pending holds or we're already ready
		if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
			return;
		}

		// Remember that the DOM is ready
		jQuery.isReady = true;

		// If a normal DOM Ready event fired, decrement, and wait if need be
		if ( wait !== true && --jQuery.readyWait > 0 ) {
			return;
		}

		// If there are functions bound, to execute
		readyList.resolveWith( document, [ jQuery ] );
	}
} );

jQuery.ready.then = readyList.then;

// The ready event handler and self cleanup method
function completed() {
	document.removeEventListener( "DOMContentLoaded", completed );
	window.removeEventListener( "load", completed );
	jQuery.ready();
}

// Catch cases where $(document).ready() is called
// after the browser event has already occurred.
// Support: IE <=9 - 10 only
// Older IE sometimes signals "interactive" too soon
if ( document.readyState === "complete" ||
	( document.readyState !== "loading" && !document.documentElement.doScroll ) ) {

	// Handle it asynchronously to allow scripts the opportunity to delay ready
	window.setTimeout( jQuery.ready );

} else {

	// Use the handy event callback
	document.addEventListener( "DOMContentLoaded", completed );

	// A fallback to window.onload, that will always work
	window.addEventListener( "load", completed );
}




// Multifunctional method to get and set values of a collection
// The value/s can optionally be executed if it's a function
var access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
	var i = 0,
		len = elems.length,
		bulk = key == null;

	// Sets many values
	if ( toType( key ) === "object" ) {
		chainable = true;
		for ( i in key ) {
			access( elems, fn, i, key[ i ], true, emptyGet, raw );
		}

	// Sets one value
	} else if ( value !== undefined ) {
		chainable = true;

		if ( !isFunction( value ) ) {
			raw = true;
		}

		if ( bulk ) {

			// Bulk operations run against the entire set
			if ( raw ) {
				fn.call( elems, value );
				fn = null;

			// ...except when executing function values
			} else {
				bulk = fn;
				fn = function( elem, key, value ) {
					return bulk.call( jQuery( elem ), value );
				};
			}
		}

		if ( fn ) {
			for ( ; i < len; i++ ) {
				fn(
					elems[ i ], key, raw ?
					value :
					value.call( elems[ i ], i, fn( elems[ i ], key ) )
				);
			}
		}
	}

	if ( chainable ) {
		return elems;
	}

	// Gets
	if ( bulk ) {
		return fn.call( elems );
	}

	return len ? fn( elems[ 0 ], key ) : emptyGet;
};


// Matches dashed string for camelizing
var rmsPrefix = /^-ms-/,
	rdashAlpha = /-([a-z])/g;

// Used by camelCase as callback to replace()
function fcamelCase( all, letter ) {
	return letter.toUpperCase();
}

// Convert dashed to camelCase; used by the css and data modules
// Support: IE <=9 - 11, Edge 12 - 15
// Microsoft forgot to hump their vendor prefix (#9572)
function camelCase( string ) {
	return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
}
var acceptData = function( owner ) {

	// Accepts only:
	//  - Node
	//    - Node.ELEMENT_NODE
	//    - Node.DOCUMENT_NODE
	//  - Object
	//    - Any
	return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType );
};




function Data() {
	this.expando = jQuery.expando + Data.uid++;
}

Data.uid = 1;

Data.prototype = {

	cache: function( owner ) {

		// Check if the owner object already has a cache
		var value = owner[ this.expando ];

		// If not, create one
		if ( !value ) {
			value = {};

			// We can accept data for non-element nodes in modern browsers,
			// but we should not, see #8335.
			// Always return an empty object.
			if ( acceptData( owner ) ) {

				// If it is a node unlikely to be stringify-ed or looped over
				// use plain assignment
				if ( owner.nodeType ) {
					owner[ this.expando ] = value;

				// Otherwise secure it in a non-enumerable property
				// configurable must be true to allow the property to be
				// deleted when data is removed
				} else {
					Object.defineProperty( owner, this.expando, {
						value: value,
						configurable: true
					} );
				}
			}
		}

		return value;
	},
	set: function( owner, data, value ) {
		var prop,
			cache = this.cache( owner );

		// Handle: [ owner, key, value ] args
		// Always use camelCase key (gh-2257)
		if ( typeof data === "string" ) {
			cache[ camelCase( data ) ] = value;

		// Handle: [ owner, { properties } ] args
		} else {

			// Copy the properties one-by-one to the cache object
			for ( prop in data ) {
				cache[ camelCase( prop ) ] = data[ prop ];
			}
		}
		return cache;
	},
	get: function( owner, key ) {
		return key === undefined ?
			this.cache( owner ) :

			// Always use camelCase key (gh-2257)
			owner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ];
	},
	access: function( owner, key, value ) {

		// In cases where either:
		//
		//   1. No key was specified
		//   2. A string key was specified, but no value provided
		//
		// Take the "read" path and allow the get method to determine
		// which value to return, respectively either:
		//
		//   1. The entire cache object
		//   2. The data stored at the key
		//
		if ( key === undefined ||
				( ( key && typeof key === "string" ) && value === undefined ) ) {

			return this.get( owner, key );
		}

		// When the key is not a string, or both a key and value
		// are specified, set or extend (existing objects) with either:
		//
		//   1. An object of properties
		//   2. A key and value
		//
		this.set( owner, key, value );

		// Since the "set" path can have two possible entry points
		// return the expected data based on which path was taken[*]
		return value !== undefined ? value : key;
	},
	remove: function( owner, key ) {
		var i,
			cache = owner[ this.expando ];

		if ( cache === undefined ) {
			return;
		}

		if ( key !== undefined ) {

			// Support array or space separated string of keys
			if ( Array.isArray( key ) ) {

				// If key is an array of keys...
				// We always set camelCase keys, so remove that.
				key = key.map( camelCase );
			} else {
				key = camelCase( key );

				// If a key with the spaces exists, use it.
				// Otherwise, create an array by matching non-whitespace
				key = key in cache ?
					[ key ] :
					( key.match( rnothtmlwhite ) || [] );
			}

			i = key.length;

			while ( i-- ) {
				delete cache[ key[ i ] ];
			}
		}

		// Remove the expando if there's no more data
		if ( key === undefined || jQuery.isEmptyObject( cache ) ) {

			// Support: Chrome <=35 - 45
			// Webkit & Blink performance suffers when deleting properties
			// from DOM nodes, so set to undefined instead
			// https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted)
			if ( owner.nodeType ) {
				owner[ this.expando ] = undefined;
			} else {
				delete owner[ this.expando ];
			}
		}
	},
	hasData: function( owner ) {
		var cache = owner[ this.expando ];
		return cache !== undefined && !jQuery.isEmptyObject( cache );
	}
};
var dataPriv = new Data();

var dataUser = new Data();



//	Implementation Summary
//
//	1. Enforce API surface and semantic compatibility with 1.9.x branch
//	2. Improve the module's maintainability by reducing the storage
//		paths to a single mechanism.
//	3. Use the same single mechanism to support "private" and "user" data.
//	4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData)
//	5. Avoid exposing implementation details on user objects (eg. expando properties)
//	6. Provide a clear path for implementation upgrade to WeakMap in 2014

var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,
	rmultiDash = /[A-Z]/g;

function getData( data ) {
	if ( data === "true" ) {
		return true;
	}

	if ( data === "false" ) {
		return false;
	}

	if ( data === "null" ) {
		return null;
	}

	// Only convert to a number if it doesn't change the string
	if ( data === +data + "" ) {
		return +data;
	}

	if ( rbrace.test( data ) ) {
		return JSON.parse( data );
	}

	return data;
}

function dataAttr( elem, key, data ) {
	var name;

	// If nothing was found internally, try to fetch any
	// data from the HTML5 data-* attribute
	if ( data === undefined && elem.nodeType === 1 ) {
		name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase();
		data = elem.getAttribute( name );

		if ( typeof data === "string" ) {
			try {
				data = getData( data );
			} catch ( e ) {}

			// Make sure we set the data so it isn't changed later
			dataUser.set( elem, key, data );
		} else {
			data = undefined;
		}
	}
	return data;
}

jQuery.extend( {
	hasData: function( elem ) {
		return dataUser.hasData( elem ) || dataPriv.hasData( elem );
	},

	data: function( elem, name, data ) {
		return dataUser.access( elem, name, data );
	},

	removeData: function( elem, name ) {
		dataUser.remove( elem, name );
	},

	// TODO: Now that all calls to _data and _removeData have been replaced
	// with direct calls to dataPriv methods, these can be deprecated.
	_data: function( elem, name, data ) {
		return dataPriv.access( elem, name, data );
	},

	_removeData: function( elem, name ) {
		dataPriv.remove( elem, name );
	}
} );

jQuery.fn.extend( {
	data: function( key, value ) {
		var i, name, data,
			elem = this[ 0 ],
			attrs = elem && elem.attributes;

		// Gets all values
		if ( key === undefined ) {
			if ( this.length ) {
				data = dataUser.get( elem );

				if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) {
					i = attrs.length;
					while ( i-- ) {

						// Support: IE 11 only
						// The attrs elements can be null (#14894)
						if ( attrs[ i ] ) {
							name = attrs[ i ].name;
							if ( name.indexOf( "data-" ) === 0 ) {
								name = camelCase( name.slice( 5 ) );
								dataAttr( elem, name, data[ name ] );
							}
						}
					}
					dataPriv.set( elem, "hasDataAttrs", true );
				}
			}

			return data;
		}

		// Sets multiple values
		if ( typeof key === "object" ) {
			return this.each( function() {
				dataUser.set( this, key );
			} );
		}

		return access( this, function( value ) {
			var data;

			// The calling jQuery object (element matches) is not empty
			// (and therefore has an element appears at this[ 0 ]) and the
			// `value` parameter was not undefined. An empty jQuery object
			// will result in `undefined` for elem = this[ 0 ] which will
			// throw an exception if an attempt to read a data cache is made.
			if ( elem && value === undefined ) {

				// Attempt to get data from the cache
				// The key will always be camelCased in Data
				data = dataUser.get( elem, key );
				if ( data !== undefined ) {
					return data;
				}

				// Attempt to "discover" the data in
				// HTML5 custom data-* attrs
				data = dataAttr( elem, key );
				if ( data !== undefined ) {
					return data;
				}

				// We tried really hard, but the data doesn't exist.
				return;
			}

			// Set the data...
			this.each( function() {

				// We always store the camelCased key
				dataUser.set( this, key, value );
			} );
		}, null, value, arguments.length > 1, null, true );
	},

	removeData: function( key ) {
		return this.each( function() {
			dataUser.remove( this, key );
		} );
	}
} );


jQuery.extend( {
	queue: function( elem, type, data ) {
		var queue;

		if ( elem ) {
			type = ( type || "fx" ) + "queue";
			queue = dataPriv.get( elem, type );

			// Speed up dequeue by getting out quickly if this is just a lookup
			if ( data ) {
				if ( !queue || Array.isArray( data ) ) {
					queue = dataPriv.access( elem, type, jQuery.makeArray( data ) );
				} else {
					queue.push( data );
				}
			}
			return queue || [];
		}
	},

	dequeue: function( elem, type ) {
		type = type || "fx";

		var queue = jQuery.queue( elem, type ),
			startLength = queue.length,
			fn = queue.shift(),
			hooks = jQuery._queueHooks( elem, type ),
			next = function() {
				jQuery.dequeue( elem, type );
			};

		// If the fx queue is dequeued, always remove the progress sentinel
		if ( fn === "inprogress" ) {
			fn = queue.shift();
			startLength--;
		}

		if ( fn ) {

			// Add a progress sentinel to prevent the fx queue from being
			// automatically dequeued
			if ( type === "fx" ) {
				queue.unshift( "inprogress" );
			}

			// Clear up the last queue stop function
			delete hooks.stop;
			fn.call( elem, next, hooks );
		}

		if ( !startLength && hooks ) {
			hooks.empty.fire();
		}
	},

	// Not public - generate a queueHooks object, or return the current one
	_queueHooks: function( elem, type ) {
		var key = type + "queueHooks";
		return dataPriv.get( elem, key ) || dataPriv.access( elem, key, {
			empty: jQuery.Callbacks( "once memory" ).add( function() {
				dataPriv.remove( elem, [ type + "queue", key ] );
			} )
		} );
	}
} );

jQuery.fn.extend( {
	queue: function( type, data ) {
		var setter = 2;

		if ( typeof type !== "string" ) {
			data = type;
			type = "fx";
			setter--;
		}

		if ( arguments.length < setter ) {
			return jQuery.queue( this[ 0 ], type );
		}

		return data === undefined ?
			this :
			this.each( function() {
				var queue = jQuery.queue( this, type, data );

				// Ensure a hooks for this queue
				jQuery._queueHooks( this, type );

				if ( type === "fx" && queue[ 0 ] !== "inprogress" ) {
					jQuery.dequeue( this, type );
				}
			} );
	},
	dequeue: function( type ) {
		return this.each( function() {
			jQuery.dequeue( this, type );
		} );
	},
	clearQueue: function( type ) {
		return this.queue( type || "fx", [] );
	},

	// Get a promise resolved when queues of a certain type
	// are emptied (fx is the type by default)
	promise: function( type, obj ) {
		var tmp,
			count = 1,
			defer = jQuery.Deferred(),
			elements = this,
			i = this.length,
			resolve = function() {
				if ( !( --count ) ) {
					defer.resolveWith( elements, [ elements ] );
				}
			};

		if ( typeof type !== "string" ) {
			obj = type;
			type = undefined;
		}
		type = type || "fx";

		while ( i-- ) {
			tmp = dataPriv.get( elements[ i ], type + "queueHooks" );
			if ( tmp && tmp.empty ) {
				count++;
				tmp.empty.add( resolve );
			}
		}
		resolve();
		return defer.promise( obj );
	}
} );
var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source;

var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" );


var cssExpand = [ "Top", "Right", "Bottom", "Left" ];

var isHiddenWithinTree = function( elem, el ) {

		// isHiddenWithinTree might be called from jQuery#filter function;
		// in that case, element will be second argument
		elem = el || elem;

		// Inline style trumps all
		return elem.style.display === "none" ||
			elem.style.display === "" &&

			// Otherwise, check computed style
			// Support: Firefox <=43 - 45
			// Disconnected elements can have computed display: none, so first confirm that elem is
			// in the document.
			jQuery.contains( elem.ownerDocument, elem ) &&

			jQuery.css( elem, "display" ) === "none";
	};

var swap = function( elem, options, callback, args ) {
	var ret, name,
		old = {};

	// Remember the old values, and insert the new ones
	for ( name in options ) {
		old[ name ] = elem.style[ name ];
		elem.style[ name ] = options[ name ];
	}

	ret = callback.apply( elem, args || [] );

	// Revert the old values
	for ( name in options ) {
		elem.style[ name ] = old[ name ];
	}

	return ret;
};




function adjustCSS( elem, prop, valueParts, tween ) {
	var adjusted, scale,
		maxIterations = 20,
		currentValue = tween ?
			function() {
				return tween.cur();
			} :
			function() {
				return jQuery.css( elem, prop, "" );
			},
		initial = currentValue(),
		unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ),

		// Starting value computation is required for potential unit mismatches
		initialInUnit = ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) &&
			rcssNum.exec( jQuery.css( elem, prop ) );

	if ( initialInUnit && initialInUnit[ 3 ] !== unit ) {

		// Support: Firefox <=54
		// Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144)
		initial = initial / 2;

		// Trust units reported by jQuery.css
		unit = unit || initialInUnit[ 3 ];

		// Iteratively approximate from a nonzero starting point
		initialInUnit = +initial || 1;

		while ( maxIterations-- ) {

			// Evaluate and update our best guess (doubling guesses that zero out).
			// Finish if the scale equals or crosses 1 (making the old*new product non-positive).
			jQuery.style( elem, prop, initialInUnit + unit );
			if ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) {
				maxIterations = 0;
			}
			initialInUnit = initialInUnit / scale;

		}

		initialInUnit = initialInUnit * 2;
		jQuery.style( elem, prop, initialInUnit + unit );

		// Make sure we update the tween properties later on
		valueParts = valueParts || [];
	}

	if ( valueParts ) {
		initialInUnit = +initialInUnit || +initial || 0;

		// Apply relative offset (+=/-=) if specified
		adjusted = valueParts[ 1 ] ?
			initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] :
			+valueParts[ 2 ];
		if ( tween ) {
			tween.unit = unit;
			tween.start = initialInUnit;
			tween.end = adjusted;
		}
	}
	return adjusted;
}


var defaultDisplayMap = {};

function getDefaultDisplay( elem ) {
	var temp,
		doc = elem.ownerDocument,
		nodeName = elem.nodeName,
		display = defaultDisplayMap[ nodeName ];

	if ( display ) {
		return display;
	}

	temp = doc.body.appendChild( doc.createElement( nodeName ) );
	display = jQuery.css( temp, "display" );

	temp.parentNode.removeChild( temp );

	if ( display === "none" ) {
		display = "block";
	}
	defaultDisplayMap[ nodeName ] = display;

	return display;
}

function showHide( elements, show ) {
	var display, elem,
		values = [],
		index = 0,
		length = elements.length;

	// Determine new display value for elements that need to change
	for ( ; index < length; index++ ) {
		elem = elements[ index ];
		if ( !elem.style ) {
			continue;
		}

		display = elem.style.display;
		if ( show ) {

			// Since we force visibility upon cascade-hidden elements, an immediate (and slow)
			// check is required in this first loop unless we have a nonempty display value (either
			// inline or about-to-be-restored)
			if ( display === "none" ) {
				values[ index ] = dataPriv.get( elem, "display" ) || null;
				if ( !values[ index ] ) {
					elem.style.display = "";
				}
			}
			if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) {
				values[ index ] = getDefaultDisplay( elem );
			}
		} else {
			if ( display !== "none" ) {
				values[ index ] = "none";

				// Remember what we're overwriting
				dataPriv.set( elem, "display", display );
			}
		}
	}

	// Set the display of the elements in a second loop to avoid constant reflow
	for ( index = 0; index < length; index++ ) {
		if ( values[ index ] != null ) {
			elements[ index ].style.display = values[ index ];
		}
	}

	return elements;
}

jQuery.fn.extend( {
	show: function() {
		return showHide( this, true );
	},
	hide: function() {
		return showHide( this );
	},
	toggle: function( state ) {
		if ( typeof state === "boolean" ) {
			return state ? this.show() : this.hide();
		}

		return this.each( function() {
			if ( isHiddenWithinTree( this ) ) {
				jQuery( this ).show();
			} else {
				jQuery( this ).hide();
			}
		} );
	}
} );
var rcheckableType = ( /^(?:checkbox|radio)$/i );

var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]+)/i );

var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i );



// We have to close these tags to support XHTML (#13200)
var wrapMap = {

	// Support: IE <=9 only
	option: [ 1, "<select multiple='multiple'>", "</select>" ],

	// XHTML parsers do not magically insert elements in the
	// same way that tag soup parsers do. So we cannot shorten
	// this by omitting <tbody> or other required elements.
	thead: [ 1, "<table>", "</table>" ],
	col: [ 2, "<table><colgroup>", "</colgroup></table>" ],
	tr: [ 2, "<table><tbody>", "</tbody></table>" ],
	td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],

	_default: [ 0, "", "" ]
};

// Support: IE <=9 only
wrapMap.optgroup = wrapMap.option;

wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
wrapMap.th = wrapMap.td;


function getAll( context, tag ) {

	// Support: IE <=9 - 11 only
	// Use typeof to avoid zero-argument method invocation on host objects (#15151)
	var ret;

	if ( typeof context.getElementsByTagName !== "undefined" ) {
		ret = context.getElementsByTagName( tag || "*" );

	} else if ( typeof context.querySelectorAll !== "undefined" ) {
		ret = context.querySelectorAll( tag || "*" );

	} else {
		ret = [];
	}

	if ( tag === undefined || tag && nodeName( context, tag ) ) {
		return jQuery.merge( [ context ], ret );
	}

	return ret;
}


// Mark scripts as having already been evaluated
function setGlobalEval( elems, refElements ) {
	var i = 0,
		l = elems.length;

	for ( ; i < l; i++ ) {
		dataPriv.set(
			elems[ i ],
			"globalEval",
			!refElements || dataPriv.get( refElements[ i ], "globalEval" )
		);
	}
}


var rhtml = /<|&#?\w+;/;

function buildFragment( elems, context, scripts, selection, ignored ) {
	var elem, tmp, tag, wrap, contains, j,
		fragment = context.createDocumentFragment(),
		nodes = [],
		i = 0,
		l = elems.length;

	for ( ; i < l; i++ ) {
		elem = elems[ i ];

		if ( elem || elem === 0 ) {

			// Add nodes directly
			if ( toType( elem ) === "object" ) {

				// Support: Android <=4.0 only, PhantomJS 1 only
				// push.apply(_, arraylike) throws on ancient WebKit
				jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem );

			// Convert non-html into a text node
			} else if ( !rhtml.test( elem ) ) {
				nodes.push( context.createTextNode( elem ) );

			// Convert html into DOM nodes
			} else {
				tmp = tmp || fragment.appendChild( context.createElement( "div" ) );

				// Deserialize a standard representation
				tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase();
				wrap = wrapMap[ tag ] || wrapMap._default;
				tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ];

				// Descend through wrappers to the right content
				j = wrap[ 0 ];
				while ( j-- ) {
					tmp = tmp.lastChild;
				}

				// Support: Android <=4.0 only, PhantomJS 1 only
				// push.apply(_, arraylike) throws on ancient WebKit
				jQuery.merge( nodes, tmp.childNodes );

				// Remember the top-level container
				tmp = fragment.firstChild;

				// Ensure the created nodes are orphaned (#12392)
				tmp.textContent = "";
			}
		}
	}

	// Remove wrapper from fragment
	fragment.textContent = "";

	i = 0;
	while ( ( elem = nodes[ i++ ] ) ) {

		// Skip elements already in the context collection (trac-4087)
		if ( selection && jQuery.inArray( elem, selection ) > -1 ) {
			if ( ignored ) {
				ignored.push( elem );
			}
			continue;
		}

		contains = jQuery.contains( elem.ownerDocument, elem );

		// Append to fragment
		tmp = getAll( fragment.appendChild( elem ), "script" );

		// Preserve script evaluation history
		if ( contains ) {
			setGlobalEval( tmp );
		}

		// Capture executables
		if ( scripts ) {
			j = 0;
			while ( ( elem = tmp[ j++ ] ) ) {
				if ( rscriptType.test( elem.type || "" ) ) {
					scripts.push( elem );
				}
			}
		}
	}

	return fragment;
}


( function() {
	var fragment = document.createDocumentFragment(),
		div = fragment.appendChild( document.createElement( "div" ) ),
		input = document.createElement( "input" );

	// Support: Android 4.0 - 4.3 only
	// Check state lost if the name is set (#11217)
	// Support: Windows Web Apps (WWA)
	// `name` and `type` must use .setAttribute for WWA (#14901)
	input.setAttribute( "type", "radio" );
	input.setAttribute( "checked", "checked" );
	input.setAttribute( "name", "t" );

	div.appendChild( input );

	// Support: Android <=4.1 only
	// Older WebKit doesn't clone checked state correctly in fragments
	support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked;

	// Support: IE <=11 only
	// Make sure textarea (and checkbox) defaultValue is properly cloned
	div.innerHTML = "<textarea>x</textarea>";
	support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue;
} )();
var documentElement = document.documentElement;



var
	rkeyEvent = /^key/,
	rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/,
	rtypenamespace = /^([^.]*)(?:\.(.+)|)/;

function returnTrue() {
	return true;
}

function returnFalse() {
	return false;
}

// Support: IE <=9 only
// See #13393 for more info
function safeActiveElement() {
	try {
		return document.activeElement;
	} catch ( err ) { }
}

function on( elem, types, selector, data, fn, one ) {
	var origFn, type;

	// Types can be a map of types/handlers
	if ( typeof types === "object" ) {

		// ( types-Object, selector, data )
		if ( typeof selector !== "string" ) {

			// ( types-Object, data )
			data = data || selector;
			selector = undefined;
		}
		for ( type in types ) {
			on( elem, type, selector, data, types[ type ], one );
		}
		return elem;
	}

	if ( data == null && fn == null ) {

		// ( types, fn )
		fn = selector;
		data = selector = undefined;
	} else if ( fn == null ) {
		if ( typeof selector === "string" ) {

			// ( types, selector, fn )
			fn = data;
			data = undefined;
		} else {

			// ( types, data, fn )
			fn = data;
			data = selector;
			selector = undefined;
		}
	}
	if ( fn === false ) {
		fn = returnFalse;
	} else if ( !fn ) {
		return elem;
	}

	if ( one === 1 ) {
		origFn = fn;
		fn = function( event ) {

			// Can use an empty set, since event contains the info
			jQuery().off( event );
			return origFn.apply( this, arguments );
		};

		// Use same guid so caller can remove using origFn
		fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
	}
	return elem.each( function() {
		jQuery.event.add( this, types, fn, data, selector );
	} );
}

/*
 * Helper functions for managing events -- not part of the public interface.
 * Props to Dean Edwards' addEvent library for many of the ideas.
 */
jQuery.event = {

	global: {},

	add: function( elem, types, handler, data, selector ) {

		var handleObjIn, eventHandle, tmp,
			events, t, handleObj,
			special, handlers, type, namespaces, origType,
			elemData = dataPriv.get( elem );

		// Don't attach events to noData or text/comment nodes (but allow plain objects)
		if ( !elemData ) {
			return;
		}

		// Caller can pass in an object of custom data in lieu of the handler
		if ( handler.handler ) {
			handleObjIn = handler;
			handler = handleObjIn.handler;
			selector = handleObjIn.selector;
		}

		// Ensure that invalid selectors throw exceptions at attach time
		// Evaluate against documentElement in case elem is a non-element node (e.g., document)
		if ( selector ) {
			jQuery.find.matchesSelector( documentElement, selector );
		}

		// Make sure that the handler has a unique ID, used to find/remove it later
		if ( !handler.guid ) {
			handler.guid = jQuery.guid++;
		}

		// Init the element's event structure and main handler, if this is the first
		if ( !( events = elemData.events ) ) {
			events = elemData.events = {};
		}
		if ( !( eventHandle = elemData.handle ) ) {
			eventHandle = elemData.handle = function( e ) {

				// Discard the second event of a jQuery.event.trigger() and
				// when an event is called after a page has unloaded
				return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ?
					jQuery.event.dispatch.apply( elem, arguments ) : undefined;
			};
		}

		// Handle multiple events separated by a space
		types = ( types || "" ).match( rnothtmlwhite ) || [ "" ];
		t = types.length;
		while ( t-- ) {
			tmp = rtypenamespace.exec( types[ t ] ) || [];
			type = origType = tmp[ 1 ];
			namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort();

			// There *must* be a type, no attaching namespace-only handlers
			if ( !type ) {
				continue;
			}

			// If event changes its type, use the special event handlers for the changed type
			special = jQuery.event.special[ type ] || {};

			// If selector defined, determine special event api type, otherwise given type
			type = ( selector ? special.delegateType : special.bindType ) || type;

			// Update special based on newly reset type
			special = jQuery.event.special[ type ] || {};

			// handleObj is passed to all event handlers
			handleObj = jQuery.extend( {
				type: type,
				origType: origType,
				data: data,
				handler: handler,
				guid: handler.guid,
				selector: selector,
				needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
				namespace: namespaces.join( "." )
			}, handleObjIn );

			// Init the event handler queue if we're the first
			if ( !( handlers = events[ type ] ) ) {
				handlers = events[ type ] = [];
				handlers.delegateCount = 0;

				// Only use addEventListener if the special events handler returns false
				if ( !special.setup ||
					special.setup.call( elem, data, namespaces, eventHandle ) === false ) {

					if ( elem.addEventListener ) {
						elem.addEventListener( type, eventHandle );
					}
				}
			}

			if ( special.add ) {
				special.add.call( elem, handleObj );

				if ( !handleObj.handler.guid ) {
					handleObj.handler.guid = handler.guid;
				}
			}

			// Add to the element's handler list, delegates in front
			if ( selector ) {
				handlers.splice( handlers.delegateCount++, 0, handleObj );
			} else {
				handlers.push( handleObj );
			}

			// Keep track of which events have ever been used, for event optimization
			jQuery.event.global[ type ] = true;
		}

	},

	// Detach an event or set of events from an element
	remove: function( elem, types, handler, selector, mappedTypes ) {

		var j, origCount, tmp,
			events, t, handleObj,
			special, handlers, type, namespaces, origType,
			elemData = dataPriv.hasData( elem ) && dataPriv.get( elem );

		if ( !elemData || !( events = elemData.events ) ) {
			return;
		}

		// Once for each type.namespace in types; type may be omitted
		types = ( types || "" ).match( rnothtmlwhite ) || [ "" ];
		t = types.length;
		while ( t-- ) {
			tmp = rtypenamespace.exec( types[ t ] ) || [];
			type = origType = tmp[ 1 ];
			namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort();

			// Unbind all events (on this namespace, if provided) for the element
			if ( !type ) {
				for ( type in events ) {
					jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
				}
				continue;
			}

			special = jQuery.event.special[ type ] || {};
			type = ( selector ? special.delegateType : special.bindType ) || type;
			handlers = events[ type ] || [];
			tmp = tmp[ 2 ] &&
				new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" );

			// Remove matching events
			origCount = j = handlers.length;
			while ( j-- ) {
				handleObj = handlers[ j ];

				if ( ( mappedTypes || origType === handleObj.origType ) &&
					( !handler || handler.guid === handleObj.guid ) &&
					( !tmp || tmp.test( handleObj.namespace ) ) &&
					( !selector || selector === handleObj.selector ||
						selector === "**" && handleObj.selector ) ) {
					handlers.splice( j, 1 );

					if ( handleObj.selector ) {
						handlers.delegateCount--;
					}
					if ( special.remove ) {
						special.remove.call( elem, handleObj );
					}
				}
			}

			// Remove generic event handler if we removed something and no more handlers exist
			// (avoids potential for endless recursion during removal of special event handlers)
			if ( origCount && !handlers.length ) {
				if ( !special.teardown ||
					special.teardown.call( elem, namespaces, elemData.handle ) === false ) {

					jQuery.removeEvent( elem, type, elemData.handle );
				}

				delete events[ type ];
			}
		}

		// Remove data and the expando if it's no longer used
		if ( jQuery.isEmptyObject( events ) ) {
			dataPriv.remove( elem, "handle events" );
		}
	},

	dispatch: function( nativeEvent ) {

		// Make a writable jQuery.Event from the native event object
		var event = jQuery.event.fix( nativeEvent );

		var i, j, ret, matched, handleObj, handlerQueue,
			args = new Array( arguments.length ),
			handlers = ( dataPriv.get( this, "events" ) || {} )[ event.type ] || [],
			special = jQuery.event.special[ event.type ] || {};

		// Use the fix-ed jQuery.Event rather than the (read-only) native event
		args[ 0 ] = event;

		for ( i = 1; i < arguments.length; i++ ) {
			args[ i ] = arguments[ i ];
		}

		event.delegateTarget = this;

		// Call the preDispatch hook for the mapped type, and let it bail if desired
		if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
			return;
		}

		// Determine handlers
		handlerQueue = jQuery.event.handlers.call( this, event, handlers );

		// Run delegates first; they may want to stop propagation beneath us
		i = 0;
		while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) {
			event.currentTarget = matched.elem;

			j = 0;
			while ( ( handleObj = matched.handlers[ j++ ] ) &&
				!event.isImmediatePropagationStopped() ) {

				// Triggered event must either 1) have no namespace, or 2) have namespace(s)
				// a subset or equal to those in the bound event (both can have no namespace).
				if ( !event.rnamespace || event.rnamespace.test( handleObj.namespace ) ) {

					event.handleObj = handleObj;
					event.data = handleObj.data;

					ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle ||
						handleObj.handler ).apply( matched.elem, args );

					if ( ret !== undefined ) {
						if ( ( event.result = ret ) === false ) {
							event.preventDefault();
							event.stopPropagation();
						}
					}
				}
			}
		}

		// Call the postDispatch hook for the mapped type
		if ( special.postDispatch ) {
			special.postDispatch.call( this, event );
		}

		return event.result;
	},

	handlers: function( event, handlers ) {
		var i, handleObj, sel, matchedHandlers, matchedSelectors,
			handlerQueue = [],
			delegateCount = handlers.delegateCount,
			cur = event.target;

		// Find delegate handlers
		if ( delegateCount &&

			// Support: IE <=9
			// Black-hole SVG <use> instance trees (trac-13180)
			cur.nodeType &&

			// Support: Firefox <=42
			// Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861)
			// https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click
			// Support: IE 11 only
			// ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343)
			!( event.type === "click" && event.button >= 1 ) ) {

			for ( ; cur !== this; cur = cur.parentNode || this ) {

				// Don't check non-elements (#13208)
				// Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
				if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) {
					matchedHandlers = [];
					matchedSelectors = {};
					for ( i = 0; i < delegateCount; i++ ) {
						handleObj = handlers[ i ];

						// Don't conflict with Object.prototype properties (#13203)
						sel = handleObj.selector + " ";

						if ( matchedSelectors[ sel ] === undefined ) {
							matchedSelectors[ sel ] = handleObj.needsContext ?
								jQuery( sel, this ).index( cur ) > -1 :
								jQuery.find( sel, this, null, [ cur ] ).length;
						}
						if ( matchedSelectors[ sel ] ) {
							matchedHandlers.push( handleObj );
						}
					}
					if ( matchedHandlers.length ) {
						handlerQueue.push( { elem: cur, handlers: matchedHandlers } );
					}
				}
			}
		}

		// Add the remaining (directly-bound) handlers
		cur = this;
		if ( delegateCount < handlers.length ) {
			handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } );
		}

		return handlerQueue;
	},

	addProp: function( name, hook ) {
		Object.defineProperty( jQuery.Event.prototype, name, {
			enumerable: true,
			configurable: true,

			get: isFunction( hook ) ?
				function() {
					if ( this.originalEvent ) {
							return hook( this.originalEvent );
					}
				} :
				function() {
					if ( this.originalEvent ) {
							return this.originalEvent[ name ];
					}
				},

			set: function( value ) {
				Object.defineProperty( this, name, {
					enumerable: true,
					configurable: true,
					writable: true,
					value: value
				} );
			}
		} );
	},

	fix: function( originalEvent ) {
		return originalEvent[ jQuery.expando ] ?
			originalEvent :
			new jQuery.Event( originalEvent );
	},

	special: {
		load: {

			// Prevent triggered image.load events from bubbling to window.load
			noBubble: true
		},
		focus: {

			// Fire native event if possible so blur/focus sequence is correct
			trigger: function() {
				if ( this !== safeActiveElement() && this.focus ) {
					this.focus();
					return false;
				}
			},
			delegateType: "focusin"
		},
		blur: {
			trigger: function() {
				if ( this === safeActiveElement() && this.blur ) {
					this.blur();
					return false;
				}
			},
			delegateType: "focusout"
		},
		click: {

			// For checkbox, fire native event so checked state will be right
			trigger: function() {
				if ( this.type === "checkbox" && this.click && nodeName( this, "input" ) ) {
					this.click();
					return false;
				}
			},

			// For cross-browser consistency, don't fire native .click() on links
			_default: function( event ) {
				return nodeName( event.target, "a" );
			}
		},

		beforeunload: {
			postDispatch: function( event ) {

				// Support: Firefox 20+
				// Firefox doesn't alert if the returnValue field is not set.
				if ( event.result !== undefined && event.originalEvent ) {
					event.originalEvent.returnValue = event.result;
				}
			}
		}
	}
};

jQuery.removeEvent = function( elem, type, handle ) {

	// This "if" is needed for plain objects
	if ( elem.removeEventListener ) {
		elem.removeEventListener( type, handle );
	}
};

jQuery.Event = function( src, props ) {

	// Allow instantiation without the 'new' keyword
	if ( !( this instanceof jQuery.Event ) ) {
		return new jQuery.Event( src, props );
	}

	// Event object
	if ( src && src.type ) {
		this.originalEvent = src;
		this.type = src.type;

		// Events bubbling up the document may have been marked as prevented
		// by a handler lower down the tree; reflect the correct value.
		this.isDefaultPrevented = src.defaultPrevented ||
				src.defaultPrevented === undefined &&

				// Support: Android <=2.3 only
				src.returnValue === false ?
			returnTrue :
			returnFalse;

		// Create target properties
		// Support: Safari <=6 - 7 only
		// Target should not be a text node (#504, #13143)
		this.target = ( src.target && src.target.nodeType === 3 ) ?
			src.target.parentNode :
			src.target;

		this.currentTarget = src.currentTarget;
		this.relatedTarget = src.relatedTarget;

	// Event type
	} else {
		this.type = src;
	}

	// Put explicitly provided properties onto the event object
	if ( props ) {
		jQuery.extend( this, props );
	}

	// Create a timestamp if incoming event doesn't have one
	this.timeStamp = src && src.timeStamp || Date.now();

	// Mark it as fixed
	this[ jQuery.expando ] = true;
};

// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
jQuery.Event.prototype = {
	constructor: jQuery.Event,
	isDefaultPrevented: returnFalse,
	isPropagationStopped: returnFalse,
	isImmediatePropagationStopped: returnFalse,
	isSimulated: false,

	preventDefault: function() {
		var e = this.originalEvent;

		this.isDefaultPrevented = returnTrue;

		if ( e && !this.isSimulated ) {
			e.preventDefault();
		}
	},
	stopPropagation: function() {
		var e = this.originalEvent;

		this.isPropagationStopped = returnTrue;

		if ( e && !this.isSimulated ) {
			e.stopPropagation();
		}
	},
	stopImmediatePropagation: function() {
		var e = this.originalEvent;

		this.isImmediatePropagationStopped = returnTrue;

		if ( e && !this.isSimulated ) {
			e.stopImmediatePropagation();
		}

		this.stopPropagation();
	}
};

// Includes all common event props including KeyEvent and MouseEvent specific props
jQuery.each( {
	altKey: true,
	bubbles: true,
	cancelable: true,
	changedTouches: true,
	ctrlKey: true,
	detail: true,
	eventPhase: true,
	metaKey: true,
	pageX: true,
	pageY: true,
	shiftKey: true,
	view: true,
	"char": true,
	charCode: true,
	key: true,
	keyCode: true,
	button: true,
	buttons: true,
	clientX: true,
	clientY: true,
	offsetX: true,
	offsetY: true,
	pointerId: true,
	pointerType: true,
	screenX: true,
	screenY: true,
	targetTouches: true,
	toElement: true,
	touches: true,

	which: function( event ) {
		var button = event.button;

		// Add which for key events
		if ( event.which == null && rkeyEvent.test( event.type ) ) {
			return event.charCode != null ? event.charCode : event.keyCode;
		}

		// Add which for click: 1 === left; 2 === middle; 3 === right
		if ( !event.which && button !== undefined && rmouseEvent.test( event.type ) ) {
			if ( button & 1 ) {
				return 1;
			}

			if ( button & 2 ) {
				return 3;
			}

			if ( button & 4 ) {
				return 2;
			}

			return 0;
		}

		return event.which;
	}
}, jQuery.event.addProp );

// Create mouseenter/leave events using mouseover/out and event-time checks
// so that event delegation works in jQuery.
// Do the same for pointerenter/pointerleave and pointerover/pointerout
//
// Support: Safari 7 only
// Safari sends mouseenter too often; see:
// https://bugs.chromium.org/p/chromium/issues/detail?id=470258
// for the description of the bug (it existed in older Chrome versions as well).
jQuery.each( {
	mouseenter: "mouseover",
	mouseleave: "mouseout",
	pointerenter: "pointerover",
	pointerleave: "pointerout"
}, function( orig, fix ) {
	jQuery.event.special[ orig ] = {
		delegateType: fix,
		bindType: fix,

		handle: function( event ) {
			var ret,
				target = this,
				related = event.relatedTarget,
				handleObj = event.handleObj;

			// For mouseenter/leave call the handler if related is outside the target.
			// NB: No relatedTarget if the mouse left/entered the browser window
			if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) {
				event.type = handleObj.origType;
				ret = handleObj.handler.apply( this, arguments );
				event.type = fix;
			}
			return ret;
		}
	};
} );

jQuery.fn.extend( {

	on: function( types, selector, data, fn ) {
		return on( this, types, selector, data, fn );
	},
	one: function( types, selector, data, fn ) {
		return on( this, types, selector, data, fn, 1 );
	},
	off: function( types, selector, fn ) {
		var handleObj, type;
		if ( types && types.preventDefault && types.handleObj ) {

			// ( event )  dispatched jQuery.Event
			handleObj = types.handleObj;
			jQuery( types.delegateTarget ).off(
				handleObj.namespace ?
					handleObj.origType + "." + handleObj.namespace :
					handleObj.origType,
				handleObj.selector,
				handleObj.handler
			);
			return this;
		}
		if ( typeof types === "object" ) {

			// ( types-object [, selector] )
			for ( type in types ) {
				this.off( type, selector, types[ type ] );
			}
			return this;
		}
		if ( selector === false || typeof selector === "function" ) {

			// ( types [, fn] )
			fn = selector;
			selector = undefined;
		}
		if ( fn === false ) {
			fn = returnFalse;
		}
		return this.each( function() {
			jQuery.event.remove( this, types, fn, selector );
		} );
	}
} );


var

	/* eslint-disable max-len */

	// See https://github.com/eslint/eslint/issues/3229
	rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi,

	/* eslint-enable */

	// Support: IE <=10 - 11, Edge 12 - 13 only
	// In IE/Edge using regex groups here causes severe slowdowns.
	// See https://connect.microsoft.com/IE/feedback/details/1736512/
	rnoInnerhtml = /<script|<style|<link/i,

	// checked="checked" or checked
	rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
	rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;

// Prefer a tbody over its parent table for containing new rows
function manipulationTarget( elem, content ) {
	if ( nodeName( elem, "table" ) &&
		nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) {

		return jQuery( elem ).children( "tbody" )[ 0 ] || elem;
	}

	return elem;
}

// Replace/restore the type attribute of script elements for safe DOM manipulation
function disableScript( elem ) {
	elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type;
	return elem;
}
function restoreScript( elem ) {
	if ( ( elem.type || "" ).slice( 0, 5 ) === "true/" ) {
		elem.type = elem.type.slice( 5 );
	} else {
		elem.removeAttribute( "type" );
	}

	return elem;
}

function cloneCopyEvent( src, dest ) {
	var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events;

	if ( dest.nodeType !== 1 ) {
		return;
	}

	// 1. Copy private data: events, handlers, etc.
	if ( dataPriv.hasData( src ) ) {
		pdataOld = dataPriv.access( src );
		pdataCur = dataPriv.set( dest, pdataOld );
		events = pdataOld.events;

		if ( events ) {
			delete pdataCur.handle;
			pdataCur.events = {};

			for ( type in events ) {
				for ( i = 0, l = events[ type ].length; i < l; i++ ) {
					jQuery.event.add( dest, type, events[ type ][ i ] );
				}
			}
		}
	}

	// 2. Copy user data
	if ( dataUser.hasData( src ) ) {
		udataOld = dataUser.access( src );
		udataCur = jQuery.extend( {}, udataOld );

		dataUser.set( dest, udataCur );
	}
}

// Fix IE bugs, see support tests
function fixInput( src, dest ) {
	var nodeName = dest.nodeName.toLowerCase();

	// Fails to persist the checked state of a cloned checkbox or radio button.
	if ( nodeName === "input" && rcheckableType.test( src.type ) ) {
		dest.checked = src.checked;

	// Fails to return the selected option to the default selected state when cloning options
	} else if ( nodeName === "input" || nodeName === "textarea" ) {
		dest.defaultValue = src.defaultValue;
	}
}

function domManip( collection, args, callback, ignored ) {

	// Flatten any nested arrays
	args = concat.apply( [], args );

	var fragment, first, scripts, hasScripts, node, doc,
		i = 0,
		l = collection.length,
		iNoClone = l - 1,
		value = args[ 0 ],
		valueIsFunction = isFunction( value );

	// We can't cloneNode fragments that contain checked, in WebKit
	if ( valueIsFunction ||
			( l > 1 && typeof value === "string" &&
				!support.checkClone && rchecked.test( value ) ) ) {
		return collection.each( function( index ) {
			var self = collection.eq( index );
			if ( valueIsFunction ) {
				args[ 0 ] = value.call( this, index, self.html() );
			}
			domManip( self, args, callback, ignored );
		} );
	}

	if ( l ) {
		fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored );
		first = fragment.firstChild;

		if ( fragment.childNodes.length === 1 ) {
			fragment = first;
		}

		// Require either new content or an interest in ignored elements to invoke the callback
		if ( first || ignored ) {
			scripts = jQuery.map( getAll( fragment, "script" ), disableScript );
			hasScripts = scripts.length;

			// Use the original fragment for the last item
			// instead of the first because it can end up
			// being emptied incorrectly in certain situations (#8070).
			for ( ; i < l; i++ ) {
				node = fragment;

				if ( i !== iNoClone ) {
					node = jQuery.clone( node, true, true );

					// Keep references to cloned scripts for later restoration
					if ( hasScripts ) {

						// Support: Android <=4.0 only, PhantomJS 1 only
						// push.apply(_, arraylike) throws on ancient WebKit
						jQuery.merge( scripts, getAll( node, "script" ) );
					}
				}

				callback.call( collection[ i ], node, i );
			}

			if ( hasScripts ) {
				doc = scripts[ scripts.length - 1 ].ownerDocument;

				// Reenable scripts
				jQuery.map( scripts, restoreScript );

				// Evaluate executable scripts on first document insertion
				for ( i = 0; i < hasScripts; i++ ) {
					node = scripts[ i ];
					if ( rscriptType.test( node.type || "" ) &&
						!dataPriv.access( node, "globalEval" ) &&
						jQuery.contains( doc, node ) ) {

						if ( node.src && ( node.type || "" ).toLowerCase()  !== "module" ) {

							// Optional AJAX dependency, but won't run scripts if not present
							if ( jQuery._evalUrl ) {
								jQuery._evalUrl( node.src );
							}
						} else {
							DOMEval( node.textContent.replace( rcleanScript, "" ), doc, node );
						}
					}
				}
			}
		}
	}

	return collection;
}

function remove( elem, selector, keepData ) {
	var node,
		nodes = selector ? jQuery.filter( selector, elem ) : elem,
		i = 0;

	for ( ; ( node = nodes[ i ] ) != null; i++ ) {
		if ( !keepData && node.nodeType === 1 ) {
			jQuery.cleanData( getAll( node ) );
		}

		if ( node.parentNode ) {
			if ( keepData && jQuery.contains( node.ownerDocument, node ) ) {
				setGlobalEval( getAll( node, "script" ) );
			}
			node.parentNode.removeChild( node );
		}
	}

	return elem;
}

jQuery.extend( {
	htmlPrefilter: function( html ) {
		return html.replace( rxhtmlTag, "<$1></$2>" );
	},

	clone: function( elem, dataAndEvents, deepDataAndEvents ) {
		var i, l, srcElements, destElements,
			clone = elem.cloneNode( true ),
			inPage = jQuery.contains( elem.ownerDocument, elem );

		// Fix IE cloning issues
		if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) &&
				!jQuery.isXMLDoc( elem ) ) {

			// We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2
			destElements = getAll( clone );
			srcElements = getAll( elem );

			for ( i = 0, l = srcElements.length; i < l; i++ ) {
				fixInput( srcElements[ i ], destElements[ i ] );
			}
		}

		// Copy the events from the original to the clone
		if ( dataAndEvents ) {
			if ( deepDataAndEvents ) {
				srcElements = srcElements || getAll( elem );
				destElements = destElements || getAll( clone );

				for ( i = 0, l = srcElements.length; i < l; i++ ) {
					cloneCopyEvent( srcElements[ i ], destElements[ i ] );
				}
			} else {
				cloneCopyEvent( elem, clone );
			}
		}

		// Preserve script evaluation history
		destElements = getAll( clone, "script" );
		if ( destElements.length > 0 ) {
			setGlobalEval( destElements, !inPage && getAll( elem, "script" ) );
		}

		// Return the cloned set
		return clone;
	},

	cleanData: function( elems ) {
		var data, elem, type,
			special = jQuery.event.special,
			i = 0;

		for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) {
			if ( acceptData( elem ) ) {
				if ( ( data = elem[ dataPriv.expando ] ) ) {
					if ( data.events ) {
						for ( type in data.events ) {
							if ( special[ type ] ) {
								jQuery.event.remove( elem, type );

							// This is a shortcut to avoid jQuery.event.remove's overhead
							} else {
								jQuery.removeEvent( elem, type, data.handle );
							}
						}
					}

					// Support: Chrome <=35 - 45+
					// Assign undefined instead of using delete, see Data#remove
					elem[ dataPriv.expando ] = undefined;
				}
				if ( elem[ dataUser.expando ] ) {

					// Support: Chrome <=35 - 45+
					// Assign undefined instead of using delete, see Data#remove
					elem[ dataUser.expando ] = undefined;
				}
			}
		}
	}
} );

jQuery.fn.extend( {
	detach: function( selector ) {
		return remove( this, selector, true );
	},

	remove: function( selector ) {
		return remove( this, selector );
	},

	text: function( value ) {
		return access( this, function( value ) {
			return value === undefined ?
				jQuery.text( this ) :
				this.empty().each( function() {
					if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
						this.textContent = value;
					}
				} );
		}, null, value, arguments.length );
	},

	append: function() {
		return domManip( this, arguments, function( elem ) {
			if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
				var target = manipulationTarget( this, elem );
				target.appendChild( elem );
			}
		} );
	},

	prepend: function() {
		return domManip( this, arguments, function( elem ) {
			if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
				var target = manipulationTarget( this, elem );
				target.insertBefore( elem, target.firstChild );
			}
		} );
	},

	before: function() {
		return domManip( this, arguments, function( elem ) {
			if ( this.parentNode ) {
				this.parentNode.insertBefore( elem, this );
			}
		} );
	},

	after: function() {
		return domManip( this, arguments, function( elem ) {
			if ( this.parentNode ) {
				this.parentNode.insertBefore( elem, this.nextSibling );
			}
		} );
	},

	empty: function() {
		var elem,
			i = 0;

		for ( ; ( elem = this[ i ] ) != null; i++ ) {
			if ( elem.nodeType === 1 ) {

				// Prevent memory leaks
				jQuery.cleanData( getAll( elem, false ) );

				// Remove any remaining nodes
				elem.textContent = "";
			}
		}

		return this;
	},

	clone: function( dataAndEvents, deepDataAndEvents ) {
		dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
		deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;

		return this.map( function() {
			return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
		} );
	},

	html: function( value ) {
		return access( this, function( value ) {
			var elem = this[ 0 ] || {},
				i = 0,
				l = this.length;

			if ( value === undefined && elem.nodeType === 1 ) {
				return elem.innerHTML;
			}

			// See if we can take a shortcut and just use innerHTML
			if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
				!wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) {

				value = jQuery.htmlPrefilter( value );

				try {
					for ( ; i < l; i++ ) {
						elem = this[ i ] || {};

						// Remove element nodes and prevent memory leaks
						if ( elem.nodeType === 1 ) {
							jQuery.cleanData( getAll( elem, false ) );
							elem.innerHTML = value;
						}
					}

					elem = 0;

				// If using innerHTML throws an exception, use the fallback method
				} catch ( e ) {}
			}

			if ( elem ) {
				this.empty().append( value );
			}
		}, null, value, arguments.length );
	},

	replaceWith: function() {
		var ignored = [];

		// Make the changes, replacing each non-ignored context element with the new content
		return domManip( this, arguments, function( elem ) {
			var parent = this.parentNode;

			if ( jQuery.inArray( this, ignored ) < 0 ) {
				jQuery.cleanData( getAll( this ) );
				if ( parent ) {
					parent.replaceChild( elem, this );
				}
			}

		// Force callback invocation
		}, ignored );
	}
} );

jQuery.each( {
	appendTo: "append",
	prependTo: "prepend",
	insertBefore: "before",
	insertAfter: "after",
	replaceAll: "replaceWith"
}, function( name, original ) {
	jQuery.fn[ name ] = function( selector ) {
		var elems,
			ret = [],
			insert = jQuery( selector ),
			last = insert.length - 1,
			i = 0;

		for ( ; i <= last; i++ ) {
			elems = i === last ? this : this.clone( true );
			jQuery( insert[ i ] )[ original ]( elems );

			// Support: Android <=4.0 only, PhantomJS 1 only
			// .get() because push.apply(_, arraylike) throws on ancient WebKit
			push.apply( ret, elems.get() );
		}

		return this.pushStack( ret );
	};
} );
var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" );

var getStyles = function( elem ) {

		// Support: IE <=11 only, Firefox <=30 (#15098, #14150)
		// IE throws on elements created in popups
		// FF meanwhile throws on frame elements through "defaultView.getComputedStyle"
		var view = elem.ownerDocument.defaultView;

		if ( !view || !view.opener ) {
			view = window;
		}

		return view.getComputedStyle( elem );
	};

var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" );



( function() {

	// Executing both pixelPosition & boxSizingReliable tests require only one layout
	// so they're executed at the same time to save the second computation.
	function computeStyleTests() {

		// This is a singleton, we need to execute it only once
		if ( !div ) {
			return;
		}

		container.style.cssText = "position:absolute;left:-11111px;width:60px;" +
			"margin-top:1px;padding:0;border:0";
		div.style.cssText =
			"position:relative;display:block;box-sizing:border-box;overflow:scroll;" +
			"margin:auto;border:1px;padding:1px;" +
			"width:60%;top:1%";
		documentElement.appendChild( container ).appendChild( div );

		var divStyle = window.getComputedStyle( div );
		pixelPositionVal = divStyle.top !== "1%";

		// Support: Android 4.0 - 4.3 only, Firefox <=3 - 44
		reliableMarginLeftVal = roundPixelMeasures( divStyle.marginLeft ) === 12;

		// Support: Android 4.0 - 4.3 only, Safari <=9.1 - 10.1, iOS <=7.0 - 9.3
		// Some styles come back with percentage values, even though they shouldn't
		div.style.right = "60%";
		pixelBoxStylesVal = roundPixelMeasures( divStyle.right ) === 36;

		// Support: IE 9 - 11 only
		// Detect misreporting of content dimensions for box-sizing:border-box elements
		boxSizingReliableVal = roundPixelMeasures( divStyle.width ) === 36;

		// Support: IE 9 only
		// Detect overflow:scroll screwiness (gh-3699)
		div.style.position = "absolute";
		scrollboxSizeVal = div.offsetWidth === 36 || "absolute";

		documentElement.removeChild( container );

		// Nullify the div so it wouldn't be stored in the memory and
		// it will also be a sign that checks already performed
		div = null;
	}

	function roundPixelMeasures( measure ) {
		return Math.round( parseFloat( measure ) );
	}

	var pixelPositionVal, boxSizingReliableVal, scrollboxSizeVal, pixelBoxStylesVal,
		reliableMarginLeftVal,
		container = document.createElement( "div" ),
		div = document.createElement( "div" );

	// Finish early in limited (non-browser) environments
	if ( !div.style ) {
		return;
	}

	// Support: IE <=9 - 11 only
	// Style of cloned element affects source element cloned (#8908)
	div.style.backgroundClip = "content-box";
	div.cloneNode( true ).style.backgroundClip = "";
	support.clearCloneStyle = div.style.backgroundClip === "content-box";

	jQuery.extend( support, {
		boxSizingReliable: function() {
			computeStyleTests();
			return boxSizingReliableVal;
		},
		pixelBoxStyles: function() {
			computeStyleTests();
			return pixelBoxStylesVal;
		},
		pixelPosition: function() {
			computeStyleTests();
			return pixelPositionVal;
		},
		reliableMarginLeft: function() {
			computeStyleTests();
			return reliableMarginLeftVal;
		},
		scrollboxSize: function() {
			computeStyleTests();
			return scrollboxSizeVal;
		}
	} );
} )();


function curCSS( elem, name, computed ) {
	var width, minWidth, maxWidth, ret,

		// Support: Firefox 51+
		// Retrieving style before computed somehow
		// fixes an issue with getting wrong values
		// on detached elements
		style = elem.style;

	computed = computed || getStyles( elem );

	// getPropertyValue is needed for:
	//   .css('filter') (IE 9 only, #12537)
	//   .css('--customProperty) (#3144)
	if ( computed ) {
		ret = computed.getPropertyValue( name ) || computed[ name ];

		if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) {
			ret = jQuery.style( elem, name );
		}

		// A tribute to the "awesome hack by Dean Edwards"
		// Android Browser returns percentage for some values,
		// but width seems to be reliably pixels.
		// This is against the CSSOM draft spec:
		// https://drafts.csswg.org/cssom/#resolved-values
		if ( !support.pixelBoxStyles() && rnumnonpx.test( ret ) && rboxStyle.test( name ) ) {

			// Remember the original values
			width = style.width;
			minWidth = style.minWidth;
			maxWidth = style.maxWidth;

			// Put in the new values to get a computed value out
			style.minWidth = style.maxWidth = style.width = ret;
			ret = computed.width;

			// Revert the changed values
			style.width = width;
			style.minWidth = minWidth;
			style.maxWidth = maxWidth;
		}
	}

	return ret !== undefined ?

		// Support: IE <=9 - 11 only
		// IE returns zIndex value as an integer.
		ret + "" :
		ret;
}


function addGetHookIf( conditionFn, hookFn ) {

	// Define the hook, we'll check on the first run if it's really needed.
	return {
		get: function() {
			if ( conditionFn() ) {

				// Hook not needed (or it's not possible to use it due
				// to missing dependency), remove it.
				delete this.get;
				return;
			}

			// Hook needed; redefine it so that the support test is not executed again.
			return ( this.get = hookFn ).apply( this, arguments );
		}
	};
}


var

	// Swappable if display is none or starts with table
	// except "table", "table-cell", or "table-caption"
	// See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
	rdisplayswap = /^(none|table(?!-c[ea]).+)/,
	rcustomProp = /^--/,
	cssShow = { position: "absolute", visibility: "hidden", display: "block" },
	cssNormalTransform = {
		letterSpacing: "0",
		fontWeight: "400"
	},

	cssPrefixes = [ "Webkit", "Moz", "ms" ],
	emptyStyle = document.createElement( "div" ).style;

// Return a css property mapped to a potentially vendor prefixed property
function vendorPropName( name ) {

	// Shortcut for names that are not vendor prefixed
	if ( name in emptyStyle ) {
		return name;
	}

	// Check for vendor prefixed names
	var capName = name[ 0 ].toUpperCase() + name.slice( 1 ),
		i = cssPrefixes.length;

	while ( i-- ) {
		name = cssPrefixes[ i ] + capName;
		if ( name in emptyStyle ) {
			return name;
		}
	}
}

// Return a property mapped along what jQuery.cssProps suggests or to
// a vendor prefixed property.
function finalPropName( name ) {
	var ret = jQuery.cssProps[ name ];
	if ( !ret ) {
		ret = jQuery.cssProps[ name ] = vendorPropName( name ) || name;
	}
	return ret;
}

function setPositiveNumber( elem, value, subtract ) {

	// Any relative (+/-) values have already been
	// normalized at this point
	var matches = rcssNum.exec( value );
	return matches ?

		// Guard against undefined "subtract", e.g., when used as in cssHooks
		Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) :
		value;
}

function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) {
	var i = dimension === "width" ? 1 : 0,
		extra = 0,
		delta = 0;

	// Adjustment may not be necessary
	if ( box === ( isBorderBox ? "border" : "content" ) ) {
		return 0;
	}

	for ( ; i < 4; i += 2 ) {

		// Both box models exclude margin
		if ( box === "margin" ) {
			delta += jQuery.css( elem, box + cssExpand[ i ], true, styles );
		}

		// If we get here with a content-box, we're seeking "padding" or "border" or "margin"
		if ( !isBorderBox ) {

			// Add padding
			delta += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );

			// For "border" or "margin", add border
			if ( box !== "padding" ) {
				delta += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );

			// But still keep track of it otherwise
			} else {
				extra += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
			}

		// If we get here with a border-box (content + padding + border), we're seeking "content" or
		// "padding" or "margin"
		} else {

			// For "content", subtract padding
			if ( box === "content" ) {
				delta -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
			}

			// For "content" or "padding", subtract border
			if ( box !== "margin" ) {
				delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
			}
		}
	}

	// Account for positive content-box scroll gutter when requested by providing computedVal
	if ( !isBorderBox && computedVal >= 0 ) {

		// offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border
		// Assuming integer scroll gutter, subtract the rest and round down
		delta += Math.max( 0, Math.ceil(
			elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] -
			computedVal -
			delta -
			extra -
			0.5
		) );
	}

	return delta;
}

function getWidthOrHeight( elem, dimension, extra ) {

	// Start with computed style
	var styles = getStyles( elem ),
		val = curCSS( elem, dimension, styles ),
		isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
		valueIsBorderBox = isBorderBox;

	// Support: Firefox <=54
	// Return a confounding non-pixel value or feign ignorance, as appropriate.
	if ( rnumnonpx.test( val ) ) {
		if ( !extra ) {
			return val;
		}
		val = "auto";
	}

	// Check for style in case a browser which returns unreliable values
	// for getComputedStyle silently falls back to the reliable elem.style
	valueIsBorderBox = valueIsBorderBox &&
		( support.boxSizingReliable() || val === elem.style[ dimension ] );

	// Fall back to offsetWidth/offsetHeight when value is "auto"
	// This happens for inline elements with no explicit setting (gh-3571)
	// Support: Android <=4.1 - 4.3 only
	// Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602)
	if ( val === "auto" ||
		!parseFloat( val ) && jQuery.css( elem, "display", false, styles ) === "inline" ) {

		val = elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ];

		// offsetWidth/offsetHeight provide border-box values
		valueIsBorderBox = true;
	}

	// Normalize "" and auto
	val = parseFloat( val ) || 0;

	// Adjust for the element's box model
	return ( val +
		boxModelAdjustment(
			elem,
			dimension,
			extra || ( isBorderBox ? "border" : "content" ),
			valueIsBorderBox,
			styles,

			// Provide the current computed size to request scroll gutter calculation (gh-3589)
			val
		)
	) + "px";
}

jQuery.extend( {

	// Add in style property hooks for overriding the default
	// behavior of getting and setting a style property
	cssHooks: {
		opacity: {
			get: function( elem, computed ) {
				if ( computed ) {

					// We should always get a number back from opacity
					var ret = curCSS( elem, "opacity" );
					return ret === "" ? "1" : ret;
				}
			}
		}
	},

	// Don't automatically add "px" to these possibly-unitless properties
	cssNumber: {
		"animationIterationCount": true,
		"columnCount": true,
		"fillOpacity": true,
		"flexGrow": true,
		"flexShrink": true,
		"fontWeight": true,
		"lineHeight": true,
		"opacity": true,
		"order": true,
		"orphans": true,
		"widows": true,
		"zIndex": true,
		"zoom": true
	},

	// Add in properties whose names you wish to fix before
	// setting or getting the value
	cssProps: {},

	// Get and set the style property on a DOM Node
	style: function( elem, name, value, extra ) {

		// Don't set styles on text and comment nodes
		if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
			return;
		}

		// Make sure that we're working with the right name
		var ret, type, hooks,
			origName = camelCase( name ),
			isCustomProp = rcustomProp.test( name ),
			style = elem.style;

		// Make sure that we're working with the right name. We don't
		// want to query the value if it is a CSS custom property
		// since they are user-defined.
		if ( !isCustomProp ) {
			name = finalPropName( origName );
		}

		// Gets hook for the prefixed version, then unprefixed version
		hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];

		// Check if we're setting a value
		if ( value !== undefined ) {
			type = typeof value;

			// Convert "+=" or "-=" to relative numbers (#7345)
			if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) {
				value = adjustCSS( elem, name, ret );

				// Fixes bug #9237
				type = "number";
			}

			// Make sure that null and NaN values aren't set (#7116)
			if ( value == null || value !== value ) {
				return;
			}

			// If a number was passed in, add the unit (except for certain CSS properties)
			if ( type === "number" ) {
				value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" );
			}

			// background-* props affect original clone's values
			if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) {
				style[ name ] = "inherit";
			}

			// If a hook was provided, use that value, otherwise just set the specified value
			if ( !hooks || !( "set" in hooks ) ||
				( value = hooks.set( elem, value, extra ) ) !== undefined ) {

				if ( isCustomProp ) {
					style.setProperty( name, value );
				} else {
					style[ name ] = value;
				}
			}

		} else {

			// If a hook was provided get the non-computed value from there
			if ( hooks && "get" in hooks &&
				( ret = hooks.get( elem, false, extra ) ) !== undefined ) {

				return ret;
			}

			// Otherwise just get the value from the style object
			return style[ name ];
		}
	},

	css: function( elem, name, extra, styles ) {
		var val, num, hooks,
			origName = camelCase( name ),
			isCustomProp = rcustomProp.test( name );

		// Make sure that we're working with the right name. We don't
		// want to modify the value if it is a CSS custom property
		// since they are user-defined.
		if ( !isCustomProp ) {
			name = finalPropName( origName );
		}

		// Try prefixed name followed by the unprefixed name
		hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];

		// If a hook was provided get the computed value from there
		if ( hooks && "get" in hooks ) {
			val = hooks.get( elem, true, extra );
		}

		// Otherwise, if a way to get the computed value exists, use that
		if ( val === undefined ) {
			val = curCSS( elem, name, styles );
		}

		// Convert "normal" to computed value
		if ( val === "normal" && name in cssNormalTransform ) {
			val = cssNormalTransform[ name ];
		}

		// Make numeric if forced or a qualifier was provided and val looks numeric
		if ( extra === "" || extra ) {
			num = parseFloat( val );
			return extra === true || isFinite( num ) ? num || 0 : val;
		}

		return val;
	}
} );

jQuery.each( [ "height", "width" ], function( i, dimension ) {
	jQuery.cssHooks[ dimension ] = {
		get: function( elem, computed, extra ) {
			if ( computed ) {

				// Certain elements can have dimension info if we invisibly show them
				// but it must have a current display style that would benefit
				return rdisplayswap.test( jQuery.css( elem, "display" ) ) &&

					// Support: Safari 8+
					// Table columns in Safari have non-zero offsetWidth & zero
					// getBoundingClientRect().width unless display is changed.
					// Support: IE <=11 only
					// Running getBoundingClientRect on a disconnected node
					// in IE throws an error.
					( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ?
						swap( elem, cssShow, function() {
							return getWidthOrHeight( elem, dimension, extra );
						} ) :
						getWidthOrHeight( elem, dimension, extra );
			}
		},

		set: function( elem, value, extra ) {
			var matches,
				styles = getStyles( elem ),
				isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
				subtract = extra && boxModelAdjustment(
					elem,
					dimension,
					extra,
					isBorderBox,
					styles
				);

			// Account for unreliable border-box dimensions by comparing offset* to computed and
			// faking a content-box to get border and padding (gh-3699)
			if ( isBorderBox && support.scrollboxSize() === styles.position ) {
				subtract -= Math.ceil(
					elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] -
					parseFloat( styles[ dimension ] ) -
					boxModelAdjustment( elem, dimension, "border", false, styles ) -
					0.5
				);
			}

			// Convert to pixels if value adjustment is needed
			if ( subtract && ( matches = rcssNum.exec( value ) ) &&
				( matches[ 3 ] || "px" ) !== "px" ) {

				elem.style[ dimension ] = value;
				value = jQuery.css( elem, dimension );
			}

			return setPositiveNumber( elem, value, subtract );
		}
	};
} );

jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft,
	function( elem, computed ) {
		if ( computed ) {
			return ( parseFloat( curCSS( elem, "marginLeft" ) ) ||
				elem.getBoundingClientRect().left -
					swap( elem, { marginLeft: 0 }, function() {
						return elem.getBoundingClientRect().left;
					} )
				) + "px";
		}
	}
);

// These hooks are used by animate to expand properties
jQuery.each( {
	margin: "",
	padding: "",
	border: "Width"
}, function( prefix, suffix ) {
	jQuery.cssHooks[ prefix + suffix ] = {
		expand: function( value ) {
			var i = 0,
				expanded = {},

				// Assumes a single number if not a string
				parts = typeof value === "string" ? value.split( " " ) : [ value ];

			for ( ; i < 4; i++ ) {
				expanded[ prefix + cssExpand[ i ] + suffix ] =
					parts[ i ] || parts[ i - 2 ] || parts[ 0 ];
			}

			return expanded;
		}
	};

	if ( prefix !== "margin" ) {
		jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;
	}
} );

jQuery.fn.extend( {
	css: function( name, value ) {
		return access( this, function( elem, name, value ) {
			var styles, len,
				map = {},
				i = 0;

			if ( Array.isArray( name ) ) {
				styles = getStyles( elem );
				len = name.length;

				for ( ; i < len; i++ ) {
					map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles );
				}

				return map;
			}

			return value !== undefined ?
				jQuery.style( elem, name, value ) :
				jQuery.css( elem, name );
		}, name, value, arguments.length > 1 );
	}
} );


function Tween( elem, options, prop, end, easing ) {
	return new Tween.prototype.init( elem, options, prop, end, easing );
}
jQuery.Tween = Tween;

Tween.prototype = {
	constructor: Tween,
	init: function( elem, options, prop, end, easing, unit ) {
		this.elem = elem;
		this.prop = prop;
		this.easing = easing || jQuery.easing._default;
		this.options = options;
		this.start = this.now = this.cur();
		this.end = end;
		this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" );
	},
	cur: function() {
		var hooks = Tween.propHooks[ this.prop ];

		return hooks && hooks.get ?
			hooks.get( this ) :
			Tween.propHooks._default.get( this );
	},
	run: function( percent ) {
		var eased,
			hooks = Tween.propHooks[ this.prop ];

		if ( this.options.duration ) {
			this.pos = eased = jQuery.easing[ this.easing ](
				percent, this.options.duration * percent, 0, 1, this.options.duration
			);
		} else {
			this.pos = eased = percent;
		}
		this.now = ( this.end - this.start ) * eased + this.start;

		if ( this.options.step ) {
			this.options.step.call( this.elem, this.now, this );
		}

		if ( hooks && hooks.set ) {
			hooks.set( this );
		} else {
			Tween.propHooks._default.set( this );
		}
		return this;
	}
};

Tween.prototype.init.prototype = Tween.prototype;

Tween.propHooks = {
	_default: {
		get: function( tween ) {
			var result;

			// Use a property on the element directly when it is not a DOM element,
			// or when there is no matching style property that exists.
			if ( tween.elem.nodeType !== 1 ||
				tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) {
				return tween.elem[ tween.prop ];
			}

			// Passing an empty string as a 3rd parameter to .css will automatically
			// attempt a parseFloat and fallback to a string if the parse fails.
			// Simple values such as "10px" are parsed to Float;
			// complex values such as "rotate(1rad)" are returned as-is.
			result = jQuery.css( tween.elem, tween.prop, "" );

			// Empty strings, null, undefined and "auto" are converted to 0.
			return !result || result === "auto" ? 0 : result;
		},
		set: function( tween ) {

			// Use step hook for back compat.
			// Use cssHook if its there.
			// Use .style if available and use plain properties where available.
			if ( jQuery.fx.step[ tween.prop ] ) {
				jQuery.fx.step[ tween.prop ]( tween );
			} else if ( tween.elem.nodeType === 1 &&
				( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null ||
					jQuery.cssHooks[ tween.prop ] ) ) {
				jQuery.style( tween.elem, tween.prop, tween.now + tween.unit );
			} else {
				tween.elem[ tween.prop ] = tween.now;
			}
		}
	}
};

// Support: IE <=9 only
// Panic based approach to setting things on disconnected nodes
Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {
	set: function( tween ) {
		if ( tween.elem.nodeType && tween.elem.parentNode ) {
			tween.elem[ tween.prop ] = tween.now;
		}
	}
};

jQuery.easing = {
	linear: function( p ) {
		return p;
	},
	swing: function( p ) {
		return 0.5 - Math.cos( p * Math.PI ) / 2;
	},
	_default: "swing"
};

jQuery.fx = Tween.prototype.init;

// Back compat <1.8 extension point
jQuery.fx.step = {};




var
	fxNow, inProgress,
	rfxtypes = /^(?:toggle|show|hide)$/,
	rrun = /queueHooks$/;

function schedule() {
	if ( inProgress ) {
		if ( document.hidden === false && window.requestAnimationFrame ) {
			window.requestAnimationFrame( schedule );
		} else {
			window.setTimeout( schedule, jQuery.fx.interval );
		}

		jQuery.fx.tick();
	}
}

// Animations created synchronously will run synchronously
function createFxNow() {
	window.setTimeout( function() {
		fxNow = undefined;
	} );
	return ( fxNow = Date.now() );
}

// Generate parameters to create a standard animation
function genFx( type, includeWidth ) {
	var which,
		i = 0,
		attrs = { height: type };

	// If we include width, step value is 1 to do all cssExpand values,
	// otherwise step value is 2 to skip over Left and Right
	includeWidth = includeWidth ? 1 : 0;
	for ( ; i < 4; i += 2 - includeWidth ) {
		which = cssExpand[ i ];
		attrs[ "margin" + which ] = attrs[ "padding" + which ] = type;
	}

	if ( includeWidth ) {
		attrs.opacity = attrs.width = type;
	}

	return attrs;
}

function createTween( value, prop, animation ) {
	var tween,
		collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ),
		index = 0,
		length = collection.length;
	for ( ; index < length; index++ ) {
		if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) {

			// We're done with this property
			return tween;
		}
	}
}

function defaultPrefilter( elem, props, opts ) {
	var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display,
		isBox = "width" in props || "height" in props,
		anim = this,
		orig = {},
		style = elem.style,
		hidden = elem.nodeType && isHiddenWithinTree( elem ),
		dataShow = dataPriv.get( elem, "fxshow" );

	// Queue-skipping animations hijack the fx hooks
	if ( !opts.queue ) {
		hooks = jQuery._queueHooks( elem, "fx" );
		if ( hooks.unqueued == null ) {
			hooks.unqueued = 0;
			oldfire = hooks.empty.fire;
			hooks.empty.fire = function() {
				if ( !hooks.unqueued ) {
					oldfire();
				}
			};
		}
		hooks.unqueued++;

		anim.always( function() {

			// Ensure the complete handler is called before this completes
			anim.always( function() {
				hooks.unqueued--;
				if ( !jQuery.queue( elem, "fx" ).length ) {
					hooks.empty.fire();
				}
			} );
		} );
	}

	// Detect show/hide animations
	for ( prop in props ) {
		value = props[ prop ];
		if ( rfxtypes.test( value ) ) {
			delete props[ prop ];
			toggle = toggle || value === "toggle";
			if ( value === ( hidden ? "hide" : "show" ) ) {

				// Pretend to be hidden if this is a "show" and
				// there is still data from a stopped show/hide
				if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) {
					hidden = true;

				// Ignore all other no-op show/hide data
				} else {
					continue;
				}
			}
			orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop );
		}
	}

	// Bail out if this is a no-op like .hide().hide()
	propTween = !jQuery.isEmptyObject( props );
	if ( !propTween && jQuery.isEmptyObject( orig ) ) {
		return;
	}

	// Restrict "overflow" and "display" styles during box animations
	if ( isBox && elem.nodeType === 1 ) {

		// Support: IE <=9 - 11, Edge 12 - 15
		// Record all 3 overflow attributes because IE does not infer the shorthand
		// from identically-valued overflowX and overflowY and Edge just mirrors
		// the overflowX value there.
		opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];

		// Identify a display type, preferring old show/hide data over the CSS cascade
		restoreDisplay = dataShow && dataShow.display;
		if ( restoreDisplay == null ) {
			restoreDisplay = dataPriv.get( elem, "display" );
		}
		display = jQuery.css( elem, "display" );
		if ( display === "none" ) {
			if ( restoreDisplay ) {
				display = restoreDisplay;
			} else {

				// Get nonempty value(s) by temporarily forcing visibility
				showHide( [ elem ], true );
				restoreDisplay = elem.style.display || restoreDisplay;
				display = jQuery.css( elem, "display" );
				showHide( [ elem ] );
			}
		}

		// Animate inline elements as inline-block
		if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) {
			if ( jQuery.css( elem, "float" ) === "none" ) {

				// Restore the original display value at the end of pure show/hide animations
				if ( !propTween ) {
					anim.done( function() {
						style.display = restoreDisplay;
					} );
					if ( restoreDisplay == null ) {
						display = style.display;
						restoreDisplay = display === "none" ? "" : display;
					}
				}
				style.display = "inline-block";
			}
		}
	}

	if ( opts.overflow ) {
		style.overflow = "hidden";
		anim.always( function() {
			style.overflow = opts.overflow[ 0 ];
			style.overflowX = opts.overflow[ 1 ];
			style.overflowY = opts.overflow[ 2 ];
		} );
	}

	// Implement show/hide animations
	propTween = false;
	for ( prop in orig ) {

		// General show/hide setup for this element animation
		if ( !propTween ) {
			if ( dataShow ) {
				if ( "hidden" in dataShow ) {
					hidden = dataShow.hidden;
				}
			} else {
				dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } );
			}

			// Store hidden/visible for toggle so `.stop().toggle()` "reverses"
			if ( toggle ) {
				dataShow.hidden = !hidden;
			}

			// Show elements before animating them
			if ( hidden ) {
				showHide( [ elem ], true );
			}

			/* eslint-disable no-loop-func */

			anim.done( function() {

			/* eslint-enable no-loop-func */

				// The final step of a "hide" animation is actually hiding the element
				if ( !hidden ) {
					showHide( [ elem ] );
				}
				dataPriv.remove( elem, "fxshow" );
				for ( prop in orig ) {
					jQuery.style( elem, prop, orig[ prop ] );
				}
			} );
		}

		// Per-property setup
		propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim );
		if ( !( prop in dataShow ) ) {
			dataShow[ prop ] = propTween.start;
			if ( hidden ) {
				propTween.end = propTween.start;
				propTween.start = 0;
			}
		}
	}
}

function propFilter( props, specialEasing ) {
	var index, name, easing, value, hooks;

	// camelCase, specialEasing and expand cssHook pass
	for ( index in props ) {
		name = camelCase( index );
		easing = specialEasing[ name ];
		value = props[ index ];
		if ( Array.isArray( value ) ) {
			easing = value[ 1 ];
			value = props[ index ] = value[ 0 ];
		}

		if ( index !== name ) {
			props[ name ] = value;
			delete props[ index ];
		}

		hooks = jQuery.cssHooks[ name ];
		if ( hooks && "expand" in hooks ) {
			value = hooks.expand( value );
			delete props[ name ];

			// Not quite $.extend, this won't overwrite existing keys.
			// Reusing 'index' because we have the correct "name"
			for ( index in value ) {
				if ( !( index in props ) ) {
					props[ index ] = value[ index ];
					specialEasing[ index ] = easing;
				}
			}
		} else {
			specialEasing[ name ] = easing;
		}
	}
}

function Animation( elem, properties, options ) {
	var result,
		stopped,
		index = 0,
		length = Animation.prefilters.length,
		deferred = jQuery.Deferred().always( function() {

			// Don't match elem in the :animated selector
			delete tick.elem;
		} ),
		tick = function() {
			if ( stopped ) {
				return false;
			}
			var currentTime = fxNow || createFxNow(),
				remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),

				// Support: Android 2.3 only
				// Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497)
				temp = remaining / animation.duration || 0,
				percent = 1 - temp,
				index = 0,
				length = animation.tweens.length;

			for ( ; index < length; index++ ) {
				animation.tweens[ index ].run( percent );
			}

			deferred.notifyWith( elem, [ animation, percent, remaining ] );

			// If there's more to do, yield
			if ( percent < 1 && length ) {
				return remaining;
			}

			// If this was an empty animation, synthesize a final progress notification
			if ( !length ) {
				deferred.notifyWith( elem, [ animation, 1, 0 ] );
			}

			// Resolve the animation and report its conclusion
			deferred.resolveWith( elem, [ animation ] );
			return false;
		},
		animation = deferred.promise( {
			elem: elem,
			props: jQuery.extend( {}, properties ),
			opts: jQuery.extend( true, {
				specialEasing: {},
				easing: jQuery.easing._default
			}, options ),
			originalProperties: properties,
			originalOptions: options,
			startTime: fxNow || createFxNow(),
			duration: options.duration,
			tweens: [],
			createTween: function( prop, end ) {
				var tween = jQuery.Tween( elem, animation.opts, prop, end,
						animation.opts.specialEasing[ prop ] || animation.opts.easing );
				animation.tweens.push( tween );
				return tween;
			},
			stop: function( gotoEnd ) {
				var index = 0,

					// If we are going to the end, we want to run all the tweens
					// otherwise we skip this part
					length = gotoEnd ? animation.tweens.length : 0;
				if ( stopped ) {
					return this;
				}
				stopped = true;
				for ( ; index < length; index++ ) {
					animation.tweens[ index ].run( 1 );
				}

				// Resolve when we played the last frame; otherwise, reject
				if ( gotoEnd ) {
					deferred.notifyWith( elem, [ animation, 1, 0 ] );
					deferred.resolveWith( elem, [ animation, gotoEnd ] );
				} else {
					deferred.rejectWith( elem, [ animation, gotoEnd ] );
				}
				return this;
			}
		} ),
		props = animation.props;

	propFilter( props, animation.opts.specialEasing );

	for ( ; index < length; index++ ) {
		result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts );
		if ( result ) {
			if ( isFunction( result.stop ) ) {
				jQuery._queueHooks( animation.elem, animation.opts.queue ).stop =
					result.stop.bind( result );
			}
			return result;
		}
	}

	jQuery.map( props, createTween, animation );

	if ( isFunction( animation.opts.start ) ) {
		animation.opts.start.call( elem, animation );
	}

	// Attach callbacks from options
	animation
		.progress( animation.opts.progress )
		.done( animation.opts.done, animation.opts.complete )
		.fail( animation.opts.fail )
		.always( animation.opts.always );

	jQuery.fx.timer(
		jQuery.extend( tick, {
			elem: elem,
			anim: animation,
			queue: animation.opts.queue
		} )
	);

	return animation;
}

jQuery.Animation = jQuery.extend( Animation, {

	tweeners: {
		"*": [ function( prop, value ) {
			var tween = this.createTween( prop, value );
			adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween );
			return tween;
		} ]
	},

	tweener: function( props, callback ) {
		if ( isFunction( props ) ) {
			callback = props;
			props = [ "*" ];
		} else {
			props = props.match( rnothtmlwhite );
		}

		var prop,
			index = 0,
			length = props.length;

		for ( ; index < length; index++ ) {
			prop = props[ index ];
			Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || [];
			Animation.tweeners[ prop ].unshift( callback );
		}
	},

	prefilters: [ defaultPrefilter ],

	prefilter: function( callback, prepend ) {
		if ( prepend ) {
			Animation.prefilters.unshift( callback );
		} else {
			Animation.prefilters.push( callback );
		}
	}
} );

jQuery.speed = function( speed, easing, fn ) {
	var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
		complete: fn || !fn && easing ||
			isFunction( speed ) && speed,
		duration: speed,
		easing: fn && easing || easing && !isFunction( easing ) && easing
	};

	// Go to the end state if fx are off
	if ( jQuery.fx.off ) {
		opt.duration = 0;

	} else {
		if ( typeof opt.duration !== "number" ) {
			if ( opt.duration in jQuery.fx.speeds ) {
				opt.duration = jQuery.fx.speeds[ opt.duration ];

			} else {
				opt.duration = jQuery.fx.speeds._default;
			}
		}
	}

	// Normalize opt.queue - true/undefined/null -> "fx"
	if ( opt.queue == null || opt.queue === true ) {
		opt.queue = "fx";
	}

	// Queueing
	opt.old = opt.complete;

	opt.complete = function() {
		if ( isFunction( opt.old ) ) {
			opt.old.call( this );
		}

		if ( opt.queue ) {
			jQuery.dequeue( this, opt.queue );
		}
	};

	return opt;
};

jQuery.fn.extend( {
	fadeTo: function( speed, to, easing, callback ) {

		// Show any hidden elements after setting opacity to 0
		return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show()

			// Animate to the value specified
			.end().animate( { opacity: to }, speed, easing, callback );
	},
	animate: function( prop, speed, easing, callback ) {
		var empty = jQuery.isEmptyObject( prop ),
			optall = jQuery.speed( speed, easing, callback ),
			doAnimation = function() {

				// Operate on a copy of prop so per-property easing won't be lost
				var anim = Animation( this, jQuery.extend( {}, prop ), optall );

				// Empty animations, or finishing resolves immediately
				if ( empty || dataPriv.get( this, "finish" ) ) {
					anim.stop( true );
				}
			};
			doAnimation.finish = doAnimation;

		return empty || optall.queue === false ?
			this.each( doAnimation ) :
			this.queue( optall.queue, doAnimation );
	},
	stop: function( type, clearQueue, gotoEnd ) {
		var stopQueue = function( hooks ) {
			var stop = hooks.stop;
			delete hooks.stop;
			stop( gotoEnd );
		};

		if ( typeof type !== "string" ) {
			gotoEnd = clearQueue;
			clearQueue = type;
			type = undefined;
		}
		if ( clearQueue && type !== false ) {
			this.queue( type || "fx", [] );
		}

		return this.each( function() {
			var dequeue = true,
				index = type != null && type + "queueHooks",
				timers = jQuery.timers,
				data = dataPriv.get( this );

			if ( index ) {
				if ( data[ index ] && data[ index ].stop ) {
					stopQueue( data[ index ] );
				}
			} else {
				for ( index in data ) {
					if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {
						stopQueue( data[ index ] );
					}
				}
			}

			for ( index = timers.length; index--; ) {
				if ( timers[ index ].elem === this &&
					( type == null || timers[ index ].queue === type ) ) {

					timers[ index ].anim.stop( gotoEnd );
					dequeue = false;
					timers.splice( index, 1 );
				}
			}

			// Start the next in the queue if the last step wasn't forced.
			// Timers currently will call their complete callbacks, which
			// will dequeue but only if they were gotoEnd.
			if ( dequeue || !gotoEnd ) {
				jQuery.dequeue( this, type );
			}
		} );
	},
	finish: function( type ) {
		if ( type !== false ) {
			type = type || "fx";
		}
		return this.each( function() {
			var index,
				data = dataPriv.get( this ),
				queue = data[ type + "queue" ],
				hooks = data[ type + "queueHooks" ],
				timers = jQuery.timers,
				length = queue ? queue.length : 0;

			// Enable finishing flag on private data
			data.finish = true;

			// Empty the queue first
			jQuery.queue( this, type, [] );

			if ( hooks && hooks.stop ) {
				hooks.stop.call( this, true );
			}

			// Look for any active animations, and finish them
			for ( index = timers.length; index--; ) {
				if ( timers[ index ].elem === this && timers[ index ].queue === type ) {
					timers[ index ].anim.stop( true );
					timers.splice( index, 1 );
				}
			}

			// Look for any animations in the old queue and finish them
			for ( index = 0; index < length; index++ ) {
				if ( queue[ index ] && queue[ index ].finish ) {
					queue[ index ].finish.call( this );
				}
			}

			// Turn off finishing flag
			delete data.finish;
		} );
	}
} );

jQuery.each( [ "toggle", "show", "hide" ], function( i, name ) {
	var cssFn = jQuery.fn[ name ];
	jQuery.fn[ name ] = function( speed, easing, callback ) {
		return speed == null || typeof speed === "boolean" ?
			cssFn.apply( this, arguments ) :
			this.animate( genFx( name, true ), speed, easing, callback );
	};
} );

// Generate shortcuts for custom animations
jQuery.each( {
	slideDown: genFx( "show" ),
	slideUp: genFx( "hide" ),
	slideToggle: genFx( "toggle" ),
	fadeIn: { opacity: "show" },
	fadeOut: { opacity: "hide" },
	fadeToggle: { opacity: "toggle" }
}, function( name, props ) {
	jQuery.fn[ name ] = function( speed, easing, callback ) {
		return this.animate( props, speed, easing, callback );
	};
} );

jQuery.timers = [];
jQuery.fx.tick = function() {
	var timer,
		i = 0,
		timers = jQuery.timers;

	fxNow = Date.now();

	for ( ; i < timers.length; i++ ) {
		timer = timers[ i ];

		// Run the timer and safely remove it when done (allowing for external removal)
		if ( !timer() && timers[ i ] === timer ) {
			timers.splice( i--, 1 );
		}
	}

	if ( !timers.length ) {
		jQuery.fx.stop();
	}
	fxNow = undefined;
};

jQuery.fx.timer = function( timer ) {
	jQuery.timers.push( timer );
	jQuery.fx.start();
};

jQuery.fx.interval = 13;
jQuery.fx.start = function() {
	if ( inProgress ) {
		return;
	}

	inProgress = true;
	schedule();
};

jQuery.fx.stop = function() {
	inProgress = null;
};

jQuery.fx.speeds = {
	slow: 600,
	fast: 200,

	// Default speed
	_default: 400
};


// Based off of the plugin by Clint Helfers, with permission.
// https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/
jQuery.fn.delay = function( time, type ) {
	time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
	type = type || "fx";

	return this.queue( type, function( next, hooks ) {
		var timeout = window.setTimeout( next, time );
		hooks.stop = function() {
			window.clearTimeout( timeout );
		};
	} );
};


( function() {
	var input = document.createElement( "input" ),
		select = document.createElement( "select" ),
		opt = select.appendChild( document.createElement( "option" ) );

	input.type = "checkbox";

	// Support: Android <=4.3 only
	// Default value for a checkbox should be "on"
	support.checkOn = input.value !== "";

	// Support: IE <=11 only
	// Must access selectedIndex to make default options select
	support.optSelected = opt.selected;

	// Support: IE <=11 only
	// An input loses its value after becoming a radio
	input = document.createElement( "input" );
	input.value = "t";
	input.type = "radio";
	support.radioValue = input.value === "t";
} )();


var boolHook,
	attrHandle = jQuery.expr.attrHandle;

jQuery.fn.extend( {
	attr: function( name, value ) {
		return access( this, jQuery.attr, name, value, arguments.length > 1 );
	},

	removeAttr: function( name ) {
		return this.each( function() {
			jQuery.removeAttr( this, name );
		} );
	}
} );

jQuery.extend( {
	attr: function( elem, name, value ) {
		var ret, hooks,
			nType = elem.nodeType;

		// Don't get/set attributes on text, comment and attribute nodes
		if ( nType === 3 || nType === 8 || nType === 2 ) {
			return;
		}

		// Fallback to prop when attributes are not supported
		if ( typeof elem.getAttribute === "undefined" ) {
			return jQuery.prop( elem, name, value );
		}

		// Attribute hooks are determined by the lowercase version
		// Grab necessary hook if one is defined
		if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
			hooks = jQuery.attrHooks[ name.toLowerCase() ] ||
				( jQuery.expr.match.bool.test( name ) ? boolHook : undefined );
		}

		if ( value !== undefined ) {
			if ( value === null ) {
				jQuery.removeAttr( elem, name );
				return;
			}

			if ( hooks && "set" in hooks &&
				( ret = hooks.set( elem, value, name ) ) !== undefined ) {
				return ret;
			}

			elem.setAttribute( name, value + "" );
			return value;
		}

		if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) {
			return ret;
		}

		ret = jQuery.find.attr( elem, name );

		// Non-existent attributes return null, we normalize to undefined
		return ret == null ? undefined : ret;
	},

	attrHooks: {
		type: {
			set: function( elem, value ) {
				if ( !support.radioValue && value === "radio" &&
					nodeName( elem, "input" ) ) {
					var val = elem.value;
					elem.setAttribute( "type", value );
					if ( val ) {
						elem.value = val;
					}
					return value;
				}
			}
		}
	},

	removeAttr: function( elem, value ) {
		var name,
			i = 0,

			// Attribute names can contain non-HTML whitespace characters
			// https://html.spec.whatwg.org/multipage/syntax.html#attributes-2
			attrNames = value && value.match( rnothtmlwhite );

		if ( attrNames && elem.nodeType === 1 ) {
			while ( ( name = attrNames[ i++ ] ) ) {
				elem.removeAttribute( name );
			}
		}
	}
} );

// Hooks for boolean attributes
boolHook = {
	set: function( elem, value, name ) {
		if ( value === false ) {

			// Remove boolean attributes when set to false
			jQuery.removeAttr( elem, name );
		} else {
			elem.setAttribute( name, name );
		}
		return name;
	}
};

jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) {
	var getter = attrHandle[ name ] || jQuery.find.attr;

	attrHandle[ name ] = function( elem, name, isXML ) {
		var ret, handle,
			lowercaseName = name.toLowerCase();

		if ( !isXML ) {

			// Avoid an infinite loop by temporarily removing this function from the getter
			handle = attrHandle[ lowercaseName ];
			attrHandle[ lowercaseName ] = ret;
			ret = getter( elem, name, isXML ) != null ?
				lowercaseName :
				null;
			attrHandle[ lowercaseName ] = handle;
		}
		return ret;
	};
} );




var rfocusable = /^(?:input|select|textarea|button)$/i,
	rclickable = /^(?:a|area)$/i;

jQuery.fn.extend( {
	prop: function( name, value ) {
		return access( this, jQuery.prop, name, value, arguments.length > 1 );
	},

	removeProp: function( name ) {
		return this.each( function() {
			delete this[ jQuery.propFix[ name ] || name ];
		} );
	}
} );

jQuery.extend( {
	prop: function( elem, name, value ) {
		var ret, hooks,
			nType = elem.nodeType;

		// Don't get/set properties on text, comment and attribute nodes
		if ( nType === 3 || nType === 8 || nType === 2 ) {
			return;
		}

		if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {

			// Fix name and attach hooks
			name = jQuery.propFix[ name ] || name;
			hooks = jQuery.propHooks[ name ];
		}

		if ( value !== undefined ) {
			if ( hooks && "set" in hooks &&
				( ret = hooks.set( elem, value, name ) ) !== undefined ) {
				return ret;
			}

			return ( elem[ name ] = value );
		}

		if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) {
			return ret;
		}

		return elem[ name ];
	},

	propHooks: {
		tabIndex: {
			get: function( elem ) {

				// Support: IE <=9 - 11 only
				// elem.tabIndex doesn't always return the
				// correct value when it hasn't been explicitly set
				// https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
				// Use proper attribute retrieval(#12072)
				var tabindex = jQuery.find.attr( elem, "tabindex" );

				if ( tabindex ) {
					return parseInt( tabindex, 10 );
				}

				if (
					rfocusable.test( elem.nodeName ) ||
					rclickable.test( elem.nodeName ) &&
					elem.href
				) {
					return 0;
				}

				return -1;
			}
		}
	},

	propFix: {
		"for": "htmlFor",
		"class": "className"
	}
} );

// Support: IE <=11 only
// Accessing the selectedIndex property
// forces the browser to respect setting selected
// on the option
// The getter ensures a default option is selected
// when in an optgroup
// eslint rule "no-unused-expressions" is disabled for this code
// since it considers such accessions noop
if ( !support.optSelected ) {
	jQuery.propHooks.selected = {
		get: function( elem ) {

			/* eslint no-unused-expressions: "off" */

			var parent = elem.parentNode;
			if ( parent && parent.parentNode ) {
				parent.parentNode.selectedIndex;
			}
			return null;
		},
		set: function( elem ) {

			/* eslint no-unused-expressions: "off" */

			var parent = elem.parentNode;
			if ( parent ) {
				parent.selectedIndex;

				if ( parent.parentNode ) {
					parent.parentNode.selectedIndex;
				}
			}
		}
	};
}

jQuery.each( [
	"tabIndex",
	"readOnly",
	"maxLength",
	"cellSpacing",
	"cellPadding",
	"rowSpan",
	"colSpan",
	"useMap",
	"frameBorder",
	"contentEditable"
], function() {
	jQuery.propFix[ this.toLowerCase() ] = this;
} );




	// Strip and collapse whitespace according to HTML spec
	// https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace
	function stripAndCollapse( value ) {
		var tokens = value.match( rnothtmlwhite ) || [];
		return tokens.join( " " );
	}


function getClass( elem ) {
	return elem.getAttribute && elem.getAttribute( "class" ) || "";
}

function classesToArray( value ) {
	if ( Array.isArray( value ) ) {
		return value;
	}
	if ( typeof value === "string" ) {
		return value.match( rnothtmlwhite ) || [];
	}
	return [];
}

jQuery.fn.extend( {
	addClass: function( value ) {
		var classes, elem, cur, curValue, clazz, j, finalValue,
			i = 0;

		if ( isFunction( value ) ) {
			return this.each( function( j ) {
				jQuery( this ).addClass( value.call( this, j, getClass( this ) ) );
			} );
		}

		classes = classesToArray( value );

		if ( classes.length ) {
			while ( ( elem = this[ i++ ] ) ) {
				curValue = getClass( elem );
				cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " );

				if ( cur ) {
					j = 0;
					while ( ( clazz = classes[ j++ ] ) ) {
						if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
							cur += clazz + " ";
						}
					}

					// Only assign if different to avoid unneeded rendering.
					finalValue = stripAndCollapse( cur );
					if ( curValue !== finalValue ) {
						elem.setAttribute( "class", finalValue );
					}
				}
			}
		}

		return this;
	},

	removeClass: function( value ) {
		var classes, elem, cur, curValue, clazz, j, finalValue,
			i = 0;

		if ( isFunction( value ) ) {
			return this.each( function( j ) {
				jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) );
			} );
		}

		if ( !arguments.length ) {
			return this.attr( "class", "" );
		}

		classes = classesToArray( value );

		if ( classes.length ) {
			while ( ( elem = this[ i++ ] ) ) {
				curValue = getClass( elem );

				// This expression is here for better compressibility (see addClass)
				cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " );

				if ( cur ) {
					j = 0;
					while ( ( clazz = classes[ j++ ] ) ) {

						// Remove *all* instances
						while ( cur.indexOf( " " + clazz + " " ) > -1 ) {
							cur = cur.replace( " " + clazz + " ", " " );
						}
					}

					// Only assign if different to avoid unneeded rendering.
					finalValue = stripAndCollapse( cur );
					if ( curValue !== finalValue ) {
						elem.setAttribute( "class", finalValue );
					}
				}
			}
		}

		return this;
	},

	toggleClass: function( value, stateVal ) {
		var type = typeof value,
			isValidValue = type === "string" || Array.isArray( value );

		if ( typeof stateVal === "boolean" && isValidValue ) {
			return stateVal ? this.addClass( value ) : this.removeClass( value );
		}

		if ( isFunction( value ) ) {
			return this.each( function( i ) {
				jQuery( this ).toggleClass(
					value.call( this, i, getClass( this ), stateVal ),
					stateVal
				);
			} );
		}

		return this.each( function() {
			var className, i, self, classNames;

			if ( isValidValue ) {

				// Toggle individual class names
				i = 0;
				self = jQuery( this );
				classNames = classesToArray( value );

				while ( ( className = classNames[ i++ ] ) ) {

					// Check each className given, space separated list
					if ( self.hasClass( className ) ) {
						self.removeClass( className );
					} else {
						self.addClass( className );
					}
				}

			// Toggle whole class name
			} else if ( value === undefined || type === "boolean" ) {
				className = getClass( this );
				if ( className ) {

					// Store className if set
					dataPriv.set( this, "__className__", className );
				}

				// If the element has a class name or if we're passed `false`,
				// then remove the whole classname (if there was one, the above saved it).
				// Otherwise bring back whatever was previously saved (if anything),
				// falling back to the empty string if nothing was stored.
				if ( this.setAttribute ) {
					this.setAttribute( "class",
						className || value === false ?
						"" :
						dataPriv.get( this, "__className__" ) || ""
					);
				}
			}
		} );
	},

	hasClass: function( selector ) {
		var className, elem,
			i = 0;

		className = " " + selector + " ";
		while ( ( elem = this[ i++ ] ) ) {
			if ( elem.nodeType === 1 &&
				( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) {
					return true;
			}
		}

		return false;
	}
} );




var rreturn = /\r/g;

jQuery.fn.extend( {
	val: function( value ) {
		var hooks, ret, valueIsFunction,
			elem = this[ 0 ];

		if ( !arguments.length ) {
			if ( elem ) {
				hooks = jQuery.valHooks[ elem.type ] ||
					jQuery.valHooks[ elem.nodeName.toLowerCase() ];

				if ( hooks &&
					"get" in hooks &&
					( ret = hooks.get( elem, "value" ) ) !== undefined
				) {
					return ret;
				}

				ret = elem.value;

				// Handle most common string cases
				if ( typeof ret === "string" ) {
					return ret.replace( rreturn, "" );
				}

				// Handle cases where value is null/undef or number
				return ret == null ? "" : ret;
			}

			return;
		}

		valueIsFunction = isFunction( value );

		return this.each( function( i ) {
			var val;

			if ( this.nodeType !== 1 ) {
				return;
			}

			if ( valueIsFunction ) {
				val = value.call( this, i, jQuery( this ).val() );
			} else {
				val = value;
			}

			// Treat null/undefined as ""; convert numbers to string
			if ( val == null ) {
				val = "";

			} else if ( typeof val === "number" ) {
				val += "";

			} else if ( Array.isArray( val ) ) {
				val = jQuery.map( val, function( value ) {
					return value == null ? "" : value + "";
				} );
			}

			hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];

			// If set returns undefined, fall back to normal setting
			if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) {
				this.value = val;
			}
		} );
	}
} );

jQuery.extend( {
	valHooks: {
		option: {
			get: function( elem ) {

				var val = jQuery.find.attr( elem, "value" );
				return val != null ?
					val :

					// Support: IE <=10 - 11 only
					// option.text throws exceptions (#14686, #14858)
					// Strip and collapse whitespace
					// https://html.spec.whatwg.org/#strip-and-collapse-whitespace
					stripAndCollapse( jQuery.text( elem ) );
			}
		},
		select: {
			get: function( elem ) {
				var value, option, i,
					options = elem.options,
					index = elem.selectedIndex,
					one = elem.type === "select-one",
					values = one ? null : [],
					max = one ? index + 1 : options.length;

				if ( index < 0 ) {
					i = max;

				} else {
					i = one ? index : 0;
				}

				// Loop through all the selected options
				for ( ; i < max; i++ ) {
					option = options[ i ];

					// Support: IE <=9 only
					// IE8-9 doesn't update selected after form reset (#2551)
					if ( ( option.selected || i === index ) &&

							// Don't return options that are disabled or in a disabled optgroup
							!option.disabled &&
							( !option.parentNode.disabled ||
								!nodeName( option.parentNode, "optgroup" ) ) ) {

						// Get the specific value for the option
						value = jQuery( option ).val();

						// We don't need an array for one selects
						if ( one ) {
							return value;
						}

						// Multi-Selects return an array
						values.push( value );
					}
				}

				return values;
			},

			set: function( elem, value ) {
				var optionSet, option,
					options = elem.options,
					values = jQuery.makeArray( value ),
					i = options.length;

				while ( i-- ) {
					option = options[ i ];

					/* eslint-disable no-cond-assign */

					if ( option.selected =
						jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1
					) {
						optionSet = true;
					}

					/* eslint-enable no-cond-assign */
				}

				// Force browsers to behave consistently when non-matching value is set
				if ( !optionSet ) {
					elem.selectedIndex = -1;
				}
				return values;
			}
		}
	}
} );

// Radios and checkboxes getter/setter
jQuery.each( [ "radio", "checkbox" ], function() {
	jQuery.valHooks[ this ] = {
		set: function( elem, value ) {
			if ( Array.isArray( value ) ) {
				return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 );
			}
		}
	};
	if ( !support.checkOn ) {
		jQuery.valHooks[ this ].get = function( elem ) {
			return elem.getAttribute( "value" ) === null ? "on" : elem.value;
		};
	}
} );




// Return jQuery for attributes-only inclusion


support.focusin = "onfocusin" in window;


var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
	stopPropagationCallback = function( e ) {
		e.stopPropagation();
	};

jQuery.extend( jQuery.event, {

	trigger: function( event, data, elem, onlyHandlers ) {

		var i, cur, tmp, bubbleType, ontype, handle, special, lastElement,
			eventPath = [ elem || document ],
			type = hasOwn.call( event, "type" ) ? event.type : event,
			namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : [];

		cur = lastElement = tmp = elem = elem || document;

		// Don't do events on text and comment nodes
		if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
			return;
		}

		// focus/blur morphs to focusin/out; ensure we're not firing them right now
		if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
			return;
		}

		if ( type.indexOf( "." ) > -1 ) {

			// Namespaced trigger; create a regexp to match event type in handle()
			namespaces = type.split( "." );
			type = namespaces.shift();
			namespaces.sort();
		}
		ontype = type.indexOf( ":" ) < 0 && "on" + type;

		// Caller can pass in a jQuery.Event object, Object, or just an event type string
		event = event[ jQuery.expando ] ?
			event :
			new jQuery.Event( type, typeof event === "object" && event );

		// Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)
		event.isTrigger = onlyHandlers ? 2 : 3;
		event.namespace = namespaces.join( "." );
		event.rnamespace = event.namespace ?
			new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) :
			null;

		// Clean up the event in case it is being reused
		event.result = undefined;
		if ( !event.target ) {
			event.target = elem;
		}

		// Clone any incoming data and prepend the event, creating the handler arg list
		data = data == null ?
			[ event ] :
			jQuery.makeArray( data, [ event ] );

		// Allow special events to draw outside the lines
		special = jQuery.event.special[ type ] || {};
		if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {
			return;
		}

		// Determine event propagation path in advance, per W3C events spec (#9951)
		// Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
		if ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) {

			bubbleType = special.delegateType || type;
			if ( !rfocusMorph.test( bubbleType + type ) ) {
				cur = cur.parentNode;
			}
			for ( ; cur; cur = cur.parentNode ) {
				eventPath.push( cur );
				tmp = cur;
			}

			// Only add window if we got to document (e.g., not plain obj or detached DOM)
			if ( tmp === ( elem.ownerDocument || document ) ) {
				eventPath.push( tmp.defaultView || tmp.parentWindow || window );
			}
		}

		// Fire handlers on the event path
		i = 0;
		while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) {
			lastElement = cur;
			event.type = i > 1 ?
				bubbleType :
				special.bindType || type;

			// jQuery handler
			handle = ( dataPriv.get( cur, "events" ) || {} )[ event.type ] &&
				dataPriv.get( cur, "handle" );
			if ( handle ) {
				handle.apply( cur, data );
			}

			// Native handler
			handle = ontype && cur[ ontype ];
			if ( handle && handle.apply && acceptData( cur ) ) {
				event.result = handle.apply( cur, data );
				if ( event.result === false ) {
					event.preventDefault();
				}
			}
		}
		event.type = type;

		// If nobody prevented the default action, do it now
		if ( !onlyHandlers && !event.isDefaultPrevented() ) {

			if ( ( !special._default ||
				special._default.apply( eventPath.pop(), data ) === false ) &&
				acceptData( elem ) ) {

				// Call a native DOM method on the target with the same name as the event.
				// Don't do default actions on window, that's where global variables be (#6170)
				if ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) {

					// Don't re-trigger an onFOO event when we call its FOO() method
					tmp = elem[ ontype ];

					if ( tmp ) {
						elem[ ontype ] = null;
					}

					// Prevent re-triggering of the same event, since we already bubbled it above
					jQuery.event.triggered = type;

					if ( event.isPropagationStopped() ) {
						lastElement.addEventListener( type, stopPropagationCallback );
					}

					elem[ type ]();

					if ( event.isPropagationStopped() ) {
						lastElement.removeEventListener( type, stopPropagationCallback );
					}

					jQuery.event.triggered = undefined;

					if ( tmp ) {
						elem[ ontype ] = tmp;
					}
				}
			}
		}

		return event.result;
	},

	// Piggyback on a donor event to simulate a different one
	// Used only for `focus(in | out)` events
	simulate: function( type, elem, event ) {
		var e = jQuery.extend(
			new jQuery.Event(),
			event,
			{
				type: type,
				isSimulated: true
			}
		);

		jQuery.event.trigger( e, null, elem );
	}

} );

jQuery.fn.extend( {

	trigger: function( type, data ) {
		return this.each( function() {
			jQuery.event.trigger( type, data, this );
		} );
	},
	triggerHandler: function( type, data ) {
		var elem = this[ 0 ];
		if ( elem ) {
			return jQuery.event.trigger( type, data, elem, true );
		}
	}
} );


// Support: Firefox <=44
// Firefox doesn't have focus(in | out) events
// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787
//
// Support: Chrome <=48 - 49, Safari <=9.0 - 9.1
// focus(in | out) events fire after focus & blur events,
// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order
// Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857
if ( !support.focusin ) {
	jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) {

		// Attach a single capturing handler on the document while someone wants focusin/focusout
		var handler = function( event ) {
			jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) );
		};

		jQuery.event.special[ fix ] = {
			setup: function() {
				var doc = this.ownerDocument || this,
					attaches = dataPriv.access( doc, fix );

				if ( !attaches ) {
					doc.addEventListener( orig, handler, true );
				}
				dataPriv.access( doc, fix, ( attaches || 0 ) + 1 );
			},
			teardown: function() {
				var doc = this.ownerDocument || this,
					attaches = dataPriv.access( doc, fix ) - 1;

				if ( !attaches ) {
					doc.removeEventListener( orig, handler, true );
					dataPriv.remove( doc, fix );

				} else {
					dataPriv.access( doc, fix, attaches );
				}
			}
		};
	} );
}
var location = window.location;

var nonce = Date.now();

var rquery = ( /\?/ );



// Cross-browser xml parsing
jQuery.parseXML = function( data ) {
	var xml;
	if ( !data || typeof data !== "string" ) {
		return null;
	}

	// Support: IE 9 - 11 only
	// IE throws on parseFromString with invalid input.
	try {
		xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" );
	} catch ( e ) {
		xml = undefined;
	}

	if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) {
		jQuery.error( "Invalid XML: " + data );
	}
	return xml;
};


var
	rbracket = /\[\]$/,
	rCRLF = /\r?\n/g,
	rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i,
	rsubmittable = /^(?:input|select|textarea|keygen)/i;

function buildParams( prefix, obj, traditional, add ) {
	var name;

	if ( Array.isArray( obj ) ) {

		// Serialize array item.
		jQuery.each( obj, function( i, v ) {
			if ( traditional || rbracket.test( prefix ) ) {

				// Treat each array item as a scalar.
				add( prefix, v );

			} else {

				// Item is non-scalar (array or object), encode its numeric index.
				buildParams(
					prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]",
					v,
					traditional,
					add
				);
			}
		} );

	} else if ( !traditional && toType( obj ) === "object" ) {

		// Serialize object item.
		for ( name in obj ) {
			buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
		}

	} else {

		// Serialize scalar item.
		add( prefix, obj );
	}
}

// Serialize an array of form elements or a set of
// key/values into a query string
jQuery.param = function( a, traditional ) {
	var prefix,
		s = [],
		add = function( key, valueOrFunction ) {

			// If value is a function, invoke it and use its return value
			var value = isFunction( valueOrFunction ) ?
				valueOrFunction() :
				valueOrFunction;

			s[ s.length ] = encodeURIComponent( key ) + "=" +
				encodeURIComponent( value == null ? "" : value );
		};

	// If an array was passed in, assume that it is an array of form elements.
	if ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {

		// Serialize the form elements
		jQuery.each( a, function() {
			add( this.name, this.value );
		} );

	} else {

		// If traditional, encode the "old" way (the way 1.3.2 or older
		// did it), otherwise encode params recursively.
		for ( prefix in a ) {
			buildParams( prefix, a[ prefix ], traditional, add );
		}
	}

	// Return the resulting serialization
	return s.join( "&" );
};

jQuery.fn.extend( {
	serialize: function() {
		return jQuery.param( this.serializeArray() );
	},
	serializeArray: function() {
		return this.map( function() {

			// Can add propHook for "elements" to filter or add form elements
			var elements = jQuery.prop( this, "elements" );
			return elements ? jQuery.makeArray( elements ) : this;
		} )
		.filter( function() {
			var type = this.type;

			// Use .is( ":disabled" ) so that fieldset[disabled] works
			return this.name && !jQuery( this ).is( ":disabled" ) &&
				rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&
				( this.checked || !rcheckableType.test( type ) );
		} )
		.map( function( i, elem ) {
			var val = jQuery( this ).val();

			if ( val == null ) {
				return null;
			}

			if ( Array.isArray( val ) ) {
				return jQuery.map( val, function( val ) {
					return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
				} );
			}

			return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
		} ).get();
	}
} );


var
	r20 = /%20/g,
	rhash = /#.*$/,
	rantiCache = /([?&])_=[^&]*/,
	rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg,

	// #7653, #8125, #8152: local protocol detection
	rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/,
	rnoContent = /^(?:GET|HEAD)$/,
	rprotocol = /^\/\//,

	/* Prefilters
	 * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
	 * 2) These are called:
	 *    - BEFORE asking for a transport
	 *    - AFTER param serialization (s.data is a string if s.processData is true)
	 * 3) key is the dataType
	 * 4) the catchall symbol "*" can be used
	 * 5) execution will start with transport dataType and THEN continue down to "*" if needed
	 */
	prefilters = {},

	/* Transports bindings
	 * 1) key is the dataType
	 * 2) the catchall symbol "*" can be used
	 * 3) selection will start with transport dataType and THEN go to "*" if needed
	 */
	transports = {},

	// Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
	allTypes = "*/".concat( "*" ),

	// Anchor tag for parsing the document origin
	originAnchor = document.createElement( "a" );
	originAnchor.href = location.href;

// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
function addToPrefiltersOrTransports( structure ) {

	// dataTypeExpression is optional and defaults to "*"
	return function( dataTypeExpression, func ) {

		if ( typeof dataTypeExpression !== "string" ) {
			func = dataTypeExpression;
			dataTypeExpression = "*";
		}

		var dataType,
			i = 0,
			dataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || [];

		if ( isFunction( func ) ) {

			// For each dataType in the dataTypeExpression
			while ( ( dataType = dataTypes[ i++ ] ) ) {

				// Prepend if requested
				if ( dataType[ 0 ] === "+" ) {
					dataType = dataType.slice( 1 ) || "*";
					( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func );

				// Otherwise append
				} else {
					( structure[ dataType ] = structure[ dataType ] || [] ).push( func );
				}
			}
		}
	};
}

// Base inspection function for prefilters and transports
function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) {

	var inspected = {},
		seekingTransport = ( structure === transports );

	function inspect( dataType ) {
		var selected;
		inspected[ dataType ] = true;
		jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) {
			var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR );
			if ( typeof dataTypeOrTransport === "string" &&
				!seekingTransport && !inspected[ dataTypeOrTransport ] ) {

				options.dataTypes.unshift( dataTypeOrTransport );
				inspect( dataTypeOrTransport );
				return false;
			} else if ( seekingTransport ) {
				return !( selected = dataTypeOrTransport );
			}
		} );
		return selected;
	}

	return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" );
}

// A special extend for ajax options
// that takes "flat" options (not to be deep extended)
// Fixes #9887
function ajaxExtend( target, src ) {
	var key, deep,
		flatOptions = jQuery.ajaxSettings.flatOptions || {};

	for ( key in src ) {
		if ( src[ key ] !== undefined ) {
			( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ];
		}
	}
	if ( deep ) {
		jQuery.extend( true, target, deep );
	}

	return target;
}

/* Handles responses to an ajax request:
 * - finds the right dataType (mediates between content-type and expected dataType)
 * - returns the corresponding response
 */
function ajaxHandleResponses( s, jqXHR, responses ) {

	var ct, type, finalDataType, firstDataType,
		contents = s.contents,
		dataTypes = s.dataTypes;

	// Remove auto dataType and get content-type in the process
	while ( dataTypes[ 0 ] === "*" ) {
		dataTypes.shift();
		if ( ct === undefined ) {
			ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" );
		}
	}

	// Check if we're dealing with a known content-type
	if ( ct ) {
		for ( type in contents ) {
			if ( contents[ type ] && contents[ type ].test( ct ) ) {
				dataTypes.unshift( type );
				break;
			}
		}
	}

	// Check to see if we have a response for the expected dataType
	if ( dataTypes[ 0 ] in responses ) {
		finalDataType = dataTypes[ 0 ];
	} else {

		// Try convertible dataTypes
		for ( type in responses ) {
			if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) {
				finalDataType = type;
				break;
			}
			if ( !firstDataType ) {
				firstDataType = type;
			}
		}

		// Or just use first one
		finalDataType = finalDataType || firstDataType;
	}

	// If we found a dataType
	// We add the dataType to the list if needed
	// and return the corresponding response
	if ( finalDataType ) {
		if ( finalDataType !== dataTypes[ 0 ] ) {
			dataTypes.unshift( finalDataType );
		}
		return responses[ finalDataType ];
	}
}

/* Chain conversions given the request and the original response
 * Also sets the responseXXX fields on the jqXHR instance
 */
function ajaxConvert( s, response, jqXHR, isSuccess ) {
	var conv2, current, conv, tmp, prev,
		converters = {},

		// Work with a copy of dataTypes in case we need to modify it for conversion
		dataTypes = s.dataTypes.slice();

	// Create converters map with lowercased keys
	if ( dataTypes[ 1 ] ) {
		for ( conv in s.converters ) {
			converters[ conv.toLowerCase() ] = s.converters[ conv ];
		}
	}

	current = dataTypes.shift();

	// Convert to each sequential dataType
	while ( current ) {

		if ( s.responseFields[ current ] ) {
			jqXHR[ s.responseFields[ current ] ] = response;
		}

		// Apply the dataFilter if provided
		if ( !prev && isSuccess && s.dataFilter ) {
			response = s.dataFilter( response, s.dataType );
		}

		prev = current;
		current = dataTypes.shift();

		if ( current ) {

			// There's only work to do if current dataType is non-auto
			if ( current === "*" ) {

				current = prev;

			// Convert response if prev dataType is non-auto and differs from current
			} else if ( prev !== "*" && prev !== current ) {

				// Seek a direct converter
				conv = converters[ prev + " " + current ] || converters[ "* " + current ];

				// If none found, seek a pair
				if ( !conv ) {
					for ( conv2 in converters ) {

						// If conv2 outputs current
						tmp = conv2.split( " " );
						if ( tmp[ 1 ] === current ) {

							// If prev can be converted to accepted input
							conv = converters[ prev + " " + tmp[ 0 ] ] ||
								converters[ "* " + tmp[ 0 ] ];
							if ( conv ) {

								// Condense equivalence converters
								if ( conv === true ) {
									conv = converters[ conv2 ];

								// Otherwise, insert the intermediate dataType
								} else if ( converters[ conv2 ] !== true ) {
									current = tmp[ 0 ];
									dataTypes.unshift( tmp[ 1 ] );
								}
								break;
							}
						}
					}
				}

				// Apply converter (if not an equivalence)
				if ( conv !== true ) {

					// Unless errors are allowed to bubble, catch and return them
					if ( conv && s.throws ) {
						response = conv( response );
					} else {
						try {
							response = conv( response );
						} catch ( e ) {
							return {
								state: "parsererror",
								error: conv ? e : "No conversion from " + prev + " to " + current
							};
						}
					}
				}
			}
		}
	}

	return { state: "success", data: response };
}

jQuery.extend( {

	// Counter for holding the number of active queries
	active: 0,

	// Last-Modified header cache for next request
	lastModified: {},
	etag: {},

	ajaxSettings: {
		url: location.href,
		type: "GET",
		isLocal: rlocalProtocol.test( location.protocol ),
		global: true,
		processData: true,
		async: true,
		contentType: "application/x-www-form-urlencoded; charset=UTF-8",

		/*
		timeout: 0,
		data: null,
		dataType: null,
		username: null,
		password: null,
		cache: null,
		throws: false,
		traditional: false,
		headers: {},
		*/

		accepts: {
			"*": allTypes,
			text: "text/plain",
			html: "text/html",
			xml: "application/xml, text/xml",
			json: "application/json, text/javascript"
		},

		contents: {
			xml: /\bxml\b/,
			html: /\bhtml/,
			json: /\bjson\b/
		},

		responseFields: {
			xml: "responseXML",
			text: "responseText",
			json: "responseJSON"
		},

		// Data converters
		// Keys separate source (or catchall "*") and destination types with a single space
		converters: {

			// Convert anything to text
			"* text": String,

			// Text to html (true = no transformation)
			"text html": true,

			// Evaluate text as a json expression
			"text json": JSON.parse,

			// Parse text as xml
			"text xml": jQuery.parseXML
		},

		// For options that shouldn't be deep extended:
		// you can add your own custom options here if
		// and when you create one that shouldn't be
		// deep extended (see ajaxExtend)
		flatOptions: {
			url: true,
			context: true
		}
	},

	// Creates a full fledged settings object into target
	// with both ajaxSettings and settings fields.
	// If target is omitted, writes into ajaxSettings.
	ajaxSetup: function( target, settings ) {
		return settings ?

			// Building a settings object
			ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) :

			// Extending ajaxSettings
			ajaxExtend( jQuery.ajaxSettings, target );
	},

	ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
	ajaxTransport: addToPrefiltersOrTransports( transports ),

	// Main method
	ajax: function( url, options ) {

		// If url is an object, simulate pre-1.5 signature
		if ( typeof url === "object" ) {
			options = url;
			url = undefined;
		}

		// Force options to be an object
		options = options || {};

		var transport,

			// URL without anti-cache param
			cacheURL,

			// Response headers
			responseHeadersString,
			responseHeaders,

			// timeout handle
			timeoutTimer,

			// Url cleanup var
			urlAnchor,

			// Request state (becomes false upon send and true upon completion)
			completed,

			// To know if global events are to be dispatched
			fireGlobals,

			// Loop variable
			i,

			// uncached part of the url
			uncached,

			// Create the final options object
			s = jQuery.ajaxSetup( {}, options ),

			// Callbacks context
			callbackContext = s.context || s,

			// Context for global events is callbackContext if it is a DOM node or jQuery collection
			globalEventContext = s.context &&
				( callbackContext.nodeType || callbackContext.jquery ) ?
					jQuery( callbackContext ) :
					jQuery.event,

			// Deferreds
			deferred = jQuery.Deferred(),
			completeDeferred = jQuery.Callbacks( "once memory" ),

			// Status-dependent callbacks
			statusCode = s.statusCode || {},

			// Headers (they are sent all at once)
			requestHeaders = {},
			requestHeadersNames = {},

			// Default abort message
			strAbort = "canceled",

			// Fake xhr
			jqXHR = {
				readyState: 0,

				// Builds headers hashtable if needed
				getResponseHeader: function( key ) {
					var match;
					if ( completed ) {
						if ( !responseHeaders ) {
							responseHeaders = {};
							while ( ( match = rheaders.exec( responseHeadersString ) ) ) {
								responseHeaders[ match[ 1 ].toLowerCase() ] = match[ 2 ];
							}
						}
						match = responseHeaders[ key.toLowerCase() ];
					}
					return match == null ? null : match;
				},

				// Raw string
				getAllResponseHeaders: function() {
					return completed ? responseHeadersString : null;
				},

				// Caches the header
				setRequestHeader: function( name, value ) {
					if ( completed == null ) {
						name = requestHeadersNames[ name.toLowerCase() ] =
							requestHeadersNames[ name.toLowerCase() ] || name;
						requestHeaders[ name ] = value;
					}
					return this;
				},

				// Overrides response content-type header
				overrideMimeType: function( type ) {
					if ( completed == null ) {
						s.mimeType = type;
					}
					return this;
				},

				// Status-dependent callbacks
				statusCode: function( map ) {
					var code;
					if ( map ) {
						if ( completed ) {

							// Execute the appropriate callbacks
							jqXHR.always( map[ jqXHR.status ] );
						} else {

							// Lazy-add the new callbacks in a way that preserves old ones
							for ( code in map ) {
								statusCode[ code ] = [ statusCode[ code ], map[ code ] ];
							}
						}
					}
					return this;
				},

				// Cancel the request
				abort: function( statusText ) {
					var finalText = statusText || strAbort;
					if ( transport ) {
						transport.abort( finalText );
					}
					done( 0, finalText );
					return this;
				}
			};

		// Attach deferreds
		deferred.promise( jqXHR );

		// Add protocol if not provided (prefilters might expect it)
		// Handle falsy url in the settings object (#10093: consistency with old signature)
		// We also use the url parameter if available
		s.url = ( ( url || s.url || location.href ) + "" )
			.replace( rprotocol, location.protocol + "//" );

		// Alias method option to type as per ticket #12004
		s.type = options.method || options.type || s.method || s.type;

		// Extract dataTypes list
		s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnothtmlwhite ) || [ "" ];

		// A cross-domain request is in order when the origin doesn't match the current origin.
		if ( s.crossDomain == null ) {
			urlAnchor = document.createElement( "a" );

			// Support: IE <=8 - 11, Edge 12 - 15
			// IE throws exception on accessing the href property if url is malformed,
			// e.g. http://example.com:80x/
			try {
				urlAnchor.href = s.url;

				// Support: IE <=8 - 11 only
				// Anchor's host property isn't correctly set when s.url is relative
				urlAnchor.href = urlAnchor.href;
				s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !==
					urlAnchor.protocol + "//" + urlAnchor.host;
			} catch ( e ) {

				// If there is an error parsing the URL, assume it is crossDomain,
				// it can be rejected by the transport if it is invalid
				s.crossDomain = true;
			}
		}

		// Convert data if not already a string
		if ( s.data && s.processData && typeof s.data !== "string" ) {
			s.data = jQuery.param( s.data, s.traditional );
		}

		// Apply prefilters
		inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );

		// If request was aborted inside a prefilter, stop there
		if ( completed ) {
			return jqXHR;
		}

		// We can fire global events as of now if asked to
		// Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118)
		fireGlobals = jQuery.event && s.global;

		// Watch for a new set of requests
		if ( fireGlobals && jQuery.active++ === 0 ) {
			jQuery.event.trigger( "ajaxStart" );
		}

		// Uppercase the type
		s.type = s.type.toUpperCase();

		// Determine if request has content
		s.hasContent = !rnoContent.test( s.type );

		// Save the URL in case we're toying with the If-Modified-Since
		// and/or If-None-Match header later on
		// Remove hash to simplify url manipulation
		cacheURL = s.url.replace( rhash, "" );

		// More options handling for requests with no content
		if ( !s.hasContent ) {

			// Remember the hash so we can put it back
			uncached = s.url.slice( cacheURL.length );

			// If data is available and should be processed, append data to url
			if ( s.data && ( s.processData || typeof s.data === "string" ) ) {
				cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data;

				// #9682: remove data so that it's not used in an eventual retry
				delete s.data;
			}

			// Add or update anti-cache param if needed
			if ( s.cache === false ) {
				cacheURL = cacheURL.replace( rantiCache, "$1" );
				uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce++ ) + uncached;
			}

			// Put hash and anti-cache on the URL that will be requested (gh-1732)
			s.url = cacheURL + uncached;

		// Change '%20' to '+' if this is encoded form body content (gh-2658)
		} else if ( s.data && s.processData &&
			( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) {
			s.data = s.data.replace( r20, "+" );
		}

		// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
		if ( s.ifModified ) {
			if ( jQuery.lastModified[ cacheURL ] ) {
				jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] );
			}
			if ( jQuery.etag[ cacheURL ] ) {
				jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] );
			}
		}

		// Set the correct header, if data is being sent
		if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
			jqXHR.setRequestHeader( "Content-Type", s.contentType );
		}

		// Set the Accepts header for the server, depending on the dataType
		jqXHR.setRequestHeader(
			"Accept",
			s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ?
				s.accepts[ s.dataTypes[ 0 ] ] +
					( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
				s.accepts[ "*" ]
		);

		// Check for headers option
		for ( i in s.headers ) {
			jqXHR.setRequestHeader( i, s.headers[ i ] );
		}

		// Allow custom headers/mimetypes and early abort
		if ( s.beforeSend &&
			( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) {

			// Abort if not done already and return
			return jqXHR.abort();
		}

		// Aborting is no longer a cancellation
		strAbort = "abort";

		// Install callbacks on deferreds
		completeDeferred.add( s.complete );
		jqXHR.done( s.success );
		jqXHR.fail( s.error );

		// Get transport
		transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );

		// If no transport, we auto-abort
		if ( !transport ) {
			done( -1, "No Transport" );
		} else {
			jqXHR.readyState = 1;

			// Send global event
			if ( fireGlobals ) {
				globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
			}

			// If request was aborted inside ajaxSend, stop there
			if ( completed ) {
				return jqXHR;
			}

			// Timeout
			if ( s.async && s.timeout > 0 ) {
				timeoutTimer = window.setTimeout( function() {
					jqXHR.abort( "timeout" );
				}, s.timeout );
			}

			try {
				completed = false;
				transport.send( requestHeaders, done );
			} catch ( e ) {

				// Rethrow post-completion exceptions
				if ( completed ) {
					throw e;
				}

				// Propagate others as results
				done( -1, e );
			}
		}

		// Callback for when everything is done
		function done( status, nativeStatusText, responses, headers ) {
			var isSuccess, success, error, response, modified,
				statusText = nativeStatusText;

			// Ignore repeat invocations
			if ( completed ) {
				return;
			}

			completed = true;

			// Clear timeout if it exists
			if ( timeoutTimer ) {
				window.clearTimeout( timeoutTimer );
			}

			// Dereference transport for early garbage collection
			// (no matter how long the jqXHR object will be used)
			transport = undefined;

			// Cache response headers
			responseHeadersString = headers || "";

			// Set readyState
			jqXHR.readyState = status > 0 ? 4 : 0;

			// Determine if successful
			isSuccess = status >= 200 && status < 300 || status === 304;

			// Get response data
			if ( responses ) {
				response = ajaxHandleResponses( s, jqXHR, responses );
			}

			// Convert no matter what (that way responseXXX fields are always set)
			response = ajaxConvert( s, response, jqXHR, isSuccess );

			// If successful, handle type chaining
			if ( isSuccess ) {

				// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
				if ( s.ifModified ) {
					modified = jqXHR.getResponseHeader( "Last-Modified" );
					if ( modified ) {
						jQuery.lastModified[ cacheURL ] = modified;
					}
					modified = jqXHR.getResponseHeader( "etag" );
					if ( modified ) {
						jQuery.etag[ cacheURL ] = modified;
					}
				}

				// if no content
				if ( status === 204 || s.type === "HEAD" ) {
					statusText = "nocontent";

				// if not modified
				} else if ( status === 304 ) {
					statusText = "notmodified";

				// If we have data, let's convert it
				} else {
					statusText = response.state;
					success = response.data;
					error = response.error;
					isSuccess = !error;
				}
			} else {

				// Extract error from statusText and normalize for non-aborts
				error = statusText;
				if ( status || !statusText ) {
					statusText = "error";
					if ( status < 0 ) {
						status = 0;
					}
				}
			}

			// Set data for the fake xhr object
			jqXHR.status = status;
			jqXHR.statusText = ( nativeStatusText || statusText ) + "";

			// Success/Error
			if ( isSuccess ) {
				deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
			} else {
				deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
			}

			// Status-dependent callbacks
			jqXHR.statusCode( statusCode );
			statusCode = undefined;

			if ( fireGlobals ) {
				globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError",
					[ jqXHR, s, isSuccess ? success : error ] );
			}

			// Complete
			completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );

			if ( fireGlobals ) {
				globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );

				// Handle the global AJAX counter
				if ( !( --jQuery.active ) ) {
					jQuery.event.trigger( "ajaxStop" );
				}
			}
		}

		return jqXHR;
	},

	getJSON: function( url, data, callback ) {
		return jQuery.get( url, data, callback, "json" );
	},

	getScript: function( url, callback ) {
		return jQuery.get( url, undefined, callback, "script" );
	}
} );

jQuery.each( [ "get", "post" ], function( i, method ) {
	jQuery[ method ] = function( url, data, callback, type ) {

		// Shift arguments if data argument was omitted
		if ( isFunction( data ) ) {
			type = type || callback;
			callback = data;
			data = undefined;
		}

		// The url can be an options object (which then must have .url)
		return jQuery.ajax( jQuery.extend( {
			url: url,
			type: method,
			dataType: type,
			data: data,
			success: callback
		}, jQuery.isPlainObject( url ) && url ) );
	};
} );


jQuery._evalUrl = function( url ) {
	return jQuery.ajax( {
		url: url,

		// Make this explicit, since user can override this through ajaxSetup (#11264)
		type: "GET",
		dataType: "script",
		cache: true,
		async: false,
		global: false,
		"throws": true
	} );
};


jQuery.fn.extend( {
	wrapAll: function( html ) {
		var wrap;

		if ( this[ 0 ] ) {
			if ( isFunction( html ) ) {
				html = html.call( this[ 0 ] );
			}

			// The elements to wrap the target around
			wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true );

			if ( this[ 0 ].parentNode ) {
				wrap.insertBefore( this[ 0 ] );
			}

			wrap.map( function() {
				var elem = this;

				while ( elem.firstElementChild ) {
					elem = elem.firstElementChild;
				}

				return elem;
			} ).append( this );
		}

		return this;
	},

	wrapInner: function( html ) {
		if ( isFunction( html ) ) {
			return this.each( function( i ) {
				jQuery( this ).wrapInner( html.call( this, i ) );
			} );
		}

		return this.each( function() {
			var self = jQuery( this ),
				contents = self.contents();

			if ( contents.length ) {
				contents.wrapAll( html );

			} else {
				self.append( html );
			}
		} );
	},

	wrap: function( html ) {
		var htmlIsFunction = isFunction( html );

		return this.each( function( i ) {
			jQuery( this ).wrapAll( htmlIsFunction ? html.call( this, i ) : html );
		} );
	},

	unwrap: function( selector ) {
		this.parent( selector ).not( "body" ).each( function() {
			jQuery( this ).replaceWith( this.childNodes );
		} );
		return this;
	}
} );


jQuery.expr.pseudos.hidden = function( elem ) {
	return !jQuery.expr.pseudos.visible( elem );
};
jQuery.expr.pseudos.visible = function( elem ) {
	return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length );
};




jQuery.ajaxSettings.xhr = function() {
	try {
		return new window.XMLHttpRequest();
	} catch ( e ) {}
};

var xhrSuccessStatus = {

		// File protocol always yields status code 0, assume 200
		0: 200,

		// Support: IE <=9 only
		// #1450: sometimes IE returns 1223 when it should be 204
		1223: 204
	},
	xhrSupported = jQuery.ajaxSettings.xhr();

support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported );
support.ajax = xhrSupported = !!xhrSupported;

jQuery.ajaxTransport( function( options ) {
	var callback, errorCallback;

	// Cross domain only allowed if supported through XMLHttpRequest
	if ( support.cors || xhrSupported && !options.crossDomain ) {
		return {
			send: function( headers, complete ) {
				var i,
					xhr = options.xhr();

				xhr.open(
					options.type,
					options.url,
					options.async,
					options.username,
					options.password
				);

				// Apply custom fields if provided
				if ( options.xhrFields ) {
					for ( i in options.xhrFields ) {
						xhr[ i ] = options.xhrFields[ i ];
					}
				}

				// Override mime type if needed
				if ( options.mimeType && xhr.overrideMimeType ) {
					xhr.overrideMimeType( options.mimeType );
				}

				// X-Requested-With header
				// For cross-domain requests, seeing as conditions for a preflight are
				// akin to a jigsaw puzzle, we simply never set it to be sure.
				// (it can always be set on a per-request basis or even using ajaxSetup)
				// For same-domain requests, won't change header if already provided.
				if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) {
					headers[ "X-Requested-With" ] = "XMLHttpRequest";
				}

				// Set headers
				for ( i in headers ) {
					xhr.setRequestHeader( i, headers[ i ] );
				}

				// Callback
				callback = function( type ) {
					return function() {
						if ( callback ) {
							callback = errorCallback = xhr.onload =
								xhr.onerror = xhr.onabort = xhr.ontimeout =
									xhr.onreadystatechange = null;

							if ( type === "abort" ) {
								xhr.abort();
							} else if ( type === "error" ) {

								// Support: IE <=9 only
								// On a manual native abort, IE9 throws
								// errors on any property access that is not readyState
								if ( typeof xhr.status !== "number" ) {
									complete( 0, "error" );
								} else {
									complete(

										// File: protocol always yields status 0; see #8605, #14207
										xhr.status,
										xhr.statusText
									);
								}
							} else {
								complete(
									xhrSuccessStatus[ xhr.status ] || xhr.status,
									xhr.statusText,

									// Support: IE <=9 only
									// IE9 has no XHR2 but throws on binary (trac-11426)
									// For XHR2 non-text, let the caller handle it (gh-2498)
									( xhr.responseType || "text" ) !== "text"  ||
									typeof xhr.responseText !== "string" ?
										{ binary: xhr.response } :
										{ text: xhr.responseText },
									xhr.getAllResponseHeaders()
								);
							}
						}
					};
				};

				// Listen to events
				xhr.onload = callback();
				errorCallback = xhr.onerror = xhr.ontimeout = callback( "error" );

				// Support: IE 9 only
				// Use onreadystatechange to replace onabort
				// to handle uncaught aborts
				if ( xhr.onabort !== undefined ) {
					xhr.onabort = errorCallback;
				} else {
					xhr.onreadystatechange = function() {

						// Check readyState before timeout as it changes
						if ( xhr.readyState === 4 ) {

							// Allow onerror to be called first,
							// but that will not handle a native abort
							// Also, save errorCallback to a variable
							// as xhr.onerror cannot be accessed
							window.setTimeout( function() {
								if ( callback ) {
									errorCallback();
								}
							} );
						}
					};
				}

				// Create the abort callback
				callback = callback( "abort" );

				try {

					// Do send the request (this may raise an exception)
					xhr.send( options.hasContent && options.data || null );
				} catch ( e ) {

					// #14683: Only rethrow if this hasn't been notified as an error yet
					if ( callback ) {
						throw e;
					}
				}
			},

			abort: function() {
				if ( callback ) {
					callback();
				}
			}
		};
	}
} );




// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432)
jQuery.ajaxPrefilter( function( s ) {
	if ( s.crossDomain ) {
		s.contents.script = false;
	}
} );

// Install script dataType
jQuery.ajaxSetup( {
	accepts: {
		script: "text/javascript, application/javascript, " +
			"application/ecmascript, application/x-ecmascript"
	},
	contents: {
		script: /\b(?:java|ecma)script\b/
	},
	converters: {
		"text script": function( text ) {
			jQuery.globalEval( text );
			return text;
		}
	}
} );

// Handle cache's special case and crossDomain
jQuery.ajaxPrefilter( "script", function( s ) {
	if ( s.cache === undefined ) {
		s.cache = false;
	}
	if ( s.crossDomain ) {
		s.type = "GET";
	}
} );

// Bind script tag hack transport
jQuery.ajaxTransport( "script", function( s ) {

	// This transport only deals with cross domain requests
	if ( s.crossDomain ) {
		var script, callback;
		return {
			send: function( _, complete ) {
				script = jQuery( "<script>" ).prop( {
					charset: s.scriptCharset,
					src: s.url
				} ).on(
					"load error",
					callback = function( evt ) {
						script.remove();
						callback = null;
						if ( evt ) {
							complete( evt.type === "error" ? 404 : 200, evt.type );
						}
					}
				);

				// Use native DOM manipulation to avoid our domManip AJAX trickery
				document.head.appendChild( script[ 0 ] );
			},
			abort: function() {
				if ( callback ) {
					callback();
				}
			}
		};
	}
} );




var oldCallbacks = [],
	rjsonp = /(=)\?(?=&|$)|\?\?/;

// Default jsonp settings
jQuery.ajaxSetup( {
	jsonp: "callback",
	jsonpCallback: function() {
		var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce++ ) );
		this[ callback ] = true;
		return callback;
	}
} );

// Detect, normalize options and install callbacks for jsonp requests
jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {

	var callbackName, overwritten, responseContainer,
		jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ?
			"url" :
			typeof s.data === "string" &&
				( s.contentType || "" )
					.indexOf( "application/x-www-form-urlencoded" ) === 0 &&
				rjsonp.test( s.data ) && "data"
		);

	// Handle iff the expected data type is "jsonp" or we have a parameter to set
	if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) {

		// Get callback name, remembering preexisting value associated with it
		callbackName = s.jsonpCallback = isFunction( s.jsonpCallback ) ?
			s.jsonpCallback() :
			s.jsonpCallback;

		// Insert callback into url or form data
		if ( jsonProp ) {
			s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName );
		} else if ( s.jsonp !== false ) {
			s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName;
		}

		// Use data converter to retrieve json after script execution
		s.converters[ "script json" ] = function() {
			if ( !responseContainer ) {
				jQuery.error( callbackName + " was not called" );
			}
			return responseContainer[ 0 ];
		};

		// Force json dataType
		s.dataTypes[ 0 ] = "json";

		// Install callback
		overwritten = window[ callbackName ];
		window[ callbackName ] = function() {
			responseContainer = arguments;
		};

		// Clean-up function (fires after converters)
		jqXHR.always( function() {

			// If previous value didn't exist - remove it
			if ( overwritten === undefined ) {
				jQuery( window ).removeProp( callbackName );

			// Otherwise restore preexisting value
			} else {
				window[ callbackName ] = overwritten;
			}

			// Save back as free
			if ( s[ callbackName ] ) {

				// Make sure that re-using the options doesn't screw things around
				s.jsonpCallback = originalSettings.jsonpCallback;

				// Save the callback name for future use
				oldCallbacks.push( callbackName );
			}

			// Call if it was a function and we have a response
			if ( responseContainer && isFunction( overwritten ) ) {
				overwritten( responseContainer[ 0 ] );
			}

			responseContainer = overwritten = undefined;
		} );

		// Delegate to script
		return "script";
	}
} );




// Support: Safari 8 only
// In Safari 8 documents created via document.implementation.createHTMLDocument
// collapse sibling forms: the second one becomes a child of the first one.
// Because of that, this security measure has to be disabled in Safari 8.
// https://bugs.webkit.org/show_bug.cgi?id=137337
support.createHTMLDocument = ( function() {
	var body = document.implementation.createHTMLDocument( "" ).body;
	body.innerHTML = "<form></form><form></form>";
	return body.childNodes.length === 2;
} )();


// Argument "data" should be string of html
// context (optional): If specified, the fragment will be created in this context,
// defaults to document
// keepScripts (optional): If true, will include scripts passed in the html string
jQuery.parseHTML = function( data, context, keepScripts ) {
	if ( typeof data !== "string" ) {
		return [];
	}
	if ( typeof context === "boolean" ) {
		keepScripts = context;
		context = false;
	}

	var base, parsed, scripts;

	if ( !context ) {

		// Stop scripts or inline event handlers from being executed immediately
		// by using document.implementation
		if ( support.createHTMLDocument ) {
			context = document.implementation.createHTMLDocument( "" );

			// Set the base href for the created document
			// so any parsed elements with URLs
			// are based on the document's URL (gh-2965)
			base = context.createElement( "base" );
			base.href = document.location.href;
			context.head.appendChild( base );
		} else {
			context = document;
		}
	}

	parsed = rsingleTag.exec( data );
	scripts = !keepScripts && [];

	// Single tag
	if ( parsed ) {
		return [ context.createElement( parsed[ 1 ] ) ];
	}

	parsed = buildFragment( [ data ], context, scripts );

	if ( scripts && scripts.length ) {
		jQuery( scripts ).remove();
	}

	return jQuery.merge( [], parsed.childNodes );
};


/**
 * Load a url into a page
 */
jQuery.fn.load = function( url, params, callback ) {
	var selector, type, response,
		self = this,
		off = url.indexOf( " " );

	if ( off > -1 ) {
		selector = stripAndCollapse( url.slice( off ) );
		url = url.slice( 0, off );
	}

	// If it's a function
	if ( isFunction( params ) ) {

		// We assume that it's the callback
		callback = params;
		params = undefined;

	// Otherwise, build a param string
	} else if ( params && typeof params === "object" ) {
		type = "POST";
	}

	// If we have elements to modify, make the request
	if ( self.length > 0 ) {
		jQuery.ajax( {
			url: url,

			// If "type" variable is undefined, then "GET" method will be used.
			// Make value of this field explicit since
			// user can override it through ajaxSetup method
			type: type || "GET",
			dataType: "html",
			data: params
		} ).done( function( responseText ) {

			// Save response for use in complete callback
			response = arguments;

			self.html( selector ?

				// If a selector was specified, locate the right elements in a dummy div
				// Exclude scripts to avoid IE 'Permission Denied' errors
				jQuery( "<div>" ).append( jQuery.parseHTML( responseText ) ).find( selector ) :

				// Otherwise use the full result
				responseText );

		// If the request succeeds, this function gets "data", "status", "jqXHR"
		// but they are ignored because response was set above.
		// If it fails, this function gets "jqXHR", "status", "error"
		} ).always( callback && function( jqXHR, status ) {
			self.each( function() {
				callback.apply( this, response || [ jqXHR.responseText, status, jqXHR ] );
			} );
		} );
	}

	return this;
};




// Attach a bunch of functions for handling common AJAX events
jQuery.each( [
	"ajaxStart",
	"ajaxStop",
	"ajaxComplete",
	"ajaxError",
	"ajaxSuccess",
	"ajaxSend"
], function( i, type ) {
	jQuery.fn[ type ] = function( fn ) {
		return this.on( type, fn );
	};
} );




jQuery.expr.pseudos.animated = function( elem ) {
	return jQuery.grep( jQuery.timers, function( fn ) {
		return elem === fn.elem;
	} ).length;
};




jQuery.offset = {
	setOffset: function( elem, options, i ) {
		var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition,
			position = jQuery.css( elem, "position" ),
			curElem = jQuery( elem ),
			props = {};

		// Set position first, in-case top/left are set even on static elem
		if ( position === "static" ) {
			elem.style.position = "relative";
		}

		curOffset = curElem.offset();
		curCSSTop = jQuery.css( elem, "top" );
		curCSSLeft = jQuery.css( elem, "left" );
		calculatePosition = ( position === "absolute" || position === "fixed" ) &&
			( curCSSTop + curCSSLeft ).indexOf( "auto" ) > -1;

		// Need to be able to calculate position if either
		// top or left is auto and position is either absolute or fixed
		if ( calculatePosition ) {
			curPosition = curElem.position();
			curTop = curPosition.top;
			curLeft = curPosition.left;

		} else {
			curTop = parseFloat( curCSSTop ) || 0;
			curLeft = parseFloat( curCSSLeft ) || 0;
		}

		if ( isFunction( options ) ) {

			// Use jQuery.extend here to allow modification of coordinates argument (gh-1848)
			options = options.call( elem, i, jQuery.extend( {}, curOffset ) );
		}

		if ( options.top != null ) {
			props.top = ( options.top - curOffset.top ) + curTop;
		}
		if ( options.left != null ) {
			props.left = ( options.left - curOffset.left ) + curLeft;
		}

		if ( "using" in options ) {
			options.using.call( elem, props );

		} else {
			curElem.css( props );
		}
	}
};

jQuery.fn.extend( {

	// offset() relates an element's border box to the document origin
	offset: function( options ) {

		// Preserve chaining for setter
		if ( arguments.length ) {
			return options === undefined ?
				this :
				this.each( function( i ) {
					jQuery.offset.setOffset( this, options, i );
				} );
		}

		var rect, win,
			elem = this[ 0 ];

		if ( !elem ) {
			return;
		}

		// Return zeros for disconnected and hidden (display: none) elements (gh-2310)
		// Support: IE <=11 only
		// Running getBoundingClientRect on a
		// disconnected node in IE throws an error
		if ( !elem.getClientRects().length ) {
			return { top: 0, left: 0 };
		}

		// Get document-relative position by adding viewport scroll to viewport-relative gBCR
		rect = elem.getBoundingClientRect();
		win = elem.ownerDocument.defaultView;
		return {
			top: rect.top + win.pageYOffset,
			left: rect.left + win.pageXOffset
		};
	},

	// position() relates an element's margin box to its offset parent's padding box
	// This corresponds to the behavior of CSS absolute positioning
	position: function() {
		if ( !this[ 0 ] ) {
			return;
		}

		var offsetParent, offset, doc,
			elem = this[ 0 ],
			parentOffset = { top: 0, left: 0 };

		// position:fixed elements are offset from the viewport, which itself always has zero offset
		if ( jQuery.css( elem, "position" ) === "fixed" ) {

			// Assume position:fixed implies availability of getBoundingClientRect
			offset = elem.getBoundingClientRect();

		} else {
			offset = this.offset();

			// Account for the *real* offset parent, which can be the document or its root element
			// when a statically positioned element is identified
			doc = elem.ownerDocument;
			offsetParent = elem.offsetParent || doc.documentElement;
			while ( offsetParent &&
				( offsetParent === doc.body || offsetParent === doc.documentElement ) &&
				jQuery.css( offsetParent, "position" ) === "static" ) {

				offsetParent = offsetParent.parentNode;
			}
			if ( offsetParent && offsetParent !== elem && offsetParent.nodeType === 1 ) {

				// Incorporate borders into its offset, since they are outside its content origin
				parentOffset = jQuery( offsetParent ).offset();
				parentOffset.top += jQuery.css( offsetParent, "borderTopWidth", true );
				parentOffset.left += jQuery.css( offsetParent, "borderLeftWidth", true );
			}
		}

		// Subtract parent offsets and element margins
		return {
			top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ),
			left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true )
		};
	},

	// This method will return documentElement in the following cases:
	// 1) For the element inside the iframe without offsetParent, this method will return
	//    documentElement of the parent window
	// 2) For the hidden or detached element
	// 3) For body or html element, i.e. in case of the html node - it will return itself
	//
	// but those exceptions were never presented as a real life use-cases
	// and might be considered as more preferable results.
	//
	// This logic, however, is not guaranteed and can change at any point in the future
	offsetParent: function() {
		return this.map( function() {
			var offsetParent = this.offsetParent;

			while ( offsetParent && jQuery.css( offsetParent, "position" ) === "static" ) {
				offsetParent = offsetParent.offsetParent;
			}

			return offsetParent || documentElement;
		} );
	}
} );

// Create scrollLeft and scrollTop methods
jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function( method, prop ) {
	var top = "pageYOffset" === prop;

	jQuery.fn[ method ] = function( val ) {
		return access( this, function( elem, method, val ) {

			// Coalesce documents and windows
			var win;
			if ( isWindow( elem ) ) {
				win = elem;
			} else if ( elem.nodeType === 9 ) {
				win = elem.defaultView;
			}

			if ( val === undefined ) {
				return win ? win[ prop ] : elem[ method ];
			}

			if ( win ) {
				win.scrollTo(
					!top ? val : win.pageXOffset,
					top ? val : win.pageYOffset
				);

			} else {
				elem[ method ] = val;
			}
		}, method, val, arguments.length );
	};
} );

// Support: Safari <=7 - 9.1, Chrome <=37 - 49
// Add the top/left cssHooks using jQuery.fn.position
// Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
// Blink bug: https://bugs.chromium.org/p/chromium/issues/detail?id=589347
// getComputedStyle returns percent when specified for top/left/bottom/right;
// rather than make the css module depend on the offset module, just check for it here
jQuery.each( [ "top", "left" ], function( i, prop ) {
	jQuery.cssHooks[ prop ] = addGetHookIf( support.pixelPosition,
		function( elem, computed ) {
			if ( computed ) {
				computed = curCSS( elem, prop );

				// If curCSS returns percentage, fallback to offset
				return rnumnonpx.test( computed ) ?
					jQuery( elem ).position()[ prop ] + "px" :
					computed;
			}
		}
	);
} );


// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
	jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name },
		function( defaultExtra, funcName ) {

		// Margin is only for outerHeight, outerWidth
		jQuery.fn[ funcName ] = function( margin, value ) {
			var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ),
				extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" );

			return access( this, function( elem, type, value ) {
				var doc;

				if ( isWindow( elem ) ) {

					// $( window ).outerWidth/Height return w/h including scrollbars (gh-1729)
					return funcName.indexOf( "outer" ) === 0 ?
						elem[ "inner" + name ] :
						elem.document.documentElement[ "client" + name ];
				}

				// Get document width or height
				if ( elem.nodeType === 9 ) {
					doc = elem.documentElement;

					// Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height],
					// whichever is greatest
					return Math.max(
						elem.body[ "scroll" + name ], doc[ "scroll" + name ],
						elem.body[ "offset" + name ], doc[ "offset" + name ],
						doc[ "client" + name ]
					);
				}

				return value === undefined ?

					// Get width or height on the element, requesting but not forcing parseFloat
					jQuery.css( elem, type, extra ) :

					// Set width or height on the element
					jQuery.style( elem, type, value, extra );
			}, type, chainable ? margin : undefined, chainable );
		};
	} );
} );


jQuery.each( ( "blur focus focusin focusout resize scroll click dblclick " +
	"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
	"change select submit keydown keypress keyup contextmenu" ).split( " " ),
	function( i, name ) {

	// Handle event binding
	jQuery.fn[ name ] = function( data, fn ) {
		return arguments.length > 0 ?
			this.on( name, null, data, fn ) :
			this.trigger( name );
	};
} );

jQuery.fn.extend( {
	hover: function( fnOver, fnOut ) {
		return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
	}
} );




jQuery.fn.extend( {

	bind: function( types, data, fn ) {
		return this.on( types, null, data, fn );
	},
	unbind: function( types, fn ) {
		return this.off( types, null, fn );
	},

	delegate: function( selector, types, data, fn ) {
		return this.on( types, selector, data, fn );
	},
	undelegate: function( selector, types, fn ) {

		// ( namespace ) or ( selector, types [, fn] )
		return arguments.length === 1 ?
			this.off( selector, "**" ) :
			this.off( types, selector || "**", fn );
	}
} );

// Bind a function to a context, optionally partially applying any
// arguments.
// jQuery.proxy is deprecated to promote standards (specifically Function#bind)
// However, it is not slated for removal any time soon
jQuery.proxy = function( fn, context ) {
	var tmp, args, proxy;

	if ( typeof context === "string" ) {
		tmp = fn[ context ];
		context = fn;
		fn = tmp;
	}

	// Quick check to determine if target is callable, in the spec
	// this throws a TypeError, but we will just return undefined.
	if ( !isFunction( fn ) ) {
		return undefined;
	}

	// Simulated bind
	args = slice.call( arguments, 2 );
	proxy = function() {
		return fn.apply( context || this, args.concat( slice.call( arguments ) ) );
	};

	// Set the guid of unique handler to the same of original handler, so it can be removed
	proxy.guid = fn.guid = fn.guid || jQuery.guid++;

	return proxy;
};

jQuery.holdReady = function( hold ) {
	if ( hold ) {
		jQuery.readyWait++;
	} else {
		jQuery.ready( true );
	}
};
jQuery.isArray = Array.isArray;
jQuery.parseJSON = JSON.parse;
jQuery.nodeName = nodeName;
jQuery.isFunction = isFunction;
jQuery.isWindow = isWindow;
jQuery.camelCase = camelCase;
jQuery.type = toType;

jQuery.now = Date.now;

jQuery.isNumeric = function( obj ) {

	// As of jQuery 3.0, isNumeric is limited to
	// strings and numbers (primitives or objects)
	// that can be coerced to finite numbers (gh-2662)
	var type = jQuery.type( obj );
	return ( type === "number" || type === "string" ) &&

		// parseFloat NaNs numeric-cast false positives ("")
		// ...but misinterprets leading-number strings, particularly hex literals ("0x...")
		// subtraction forces infinities to NaN
		!isNaN( obj - parseFloat( obj ) );
};




// Register as a named AMD module, since jQuery can be concatenated with other
// files that may use define, but not via a proper concatenation script that
// understands anonymous AMD modules. A named AMD is safest and most robust
// way to register. Lowercase jquery is used because AMD module names are
// derived from file names, and jQuery is normally delivered in a lowercase
// file name. Do this after creating the global so that if an AMD module wants
// to call noConflict to hide this version of jQuery, it will work.

// Note that for maximum portability, libraries that are not jQuery should
// declare themselves as anonymous modules, and avoid setting a global if an
// AMD loader is present. jQuery is a special case. For more information, see
// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon

if ( true ) {
	!(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_RESULT__ = (function() {
		return jQuery;
	}).apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
				__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
}




var

	// Map over jQuery in case of overwrite
	_jQuery = window.jQuery,

	// Map over the $ in case of overwrite
	_$ = window.$;

jQuery.noConflict = function( deep ) {
	if ( window.$ === jQuery ) {
		window.$ = _$;
	}

	if ( deep && window.jQuery === jQuery ) {
		window.jQuery = _jQuery;
	}

	return jQuery;
};

// Expose jQuery and $ identifiers, even in AMD
// (#7102#comment:10, https://github.com/jquery/jquery/pull/557)
// and CommonJS for browser emulators (#13566)
if ( !noGlobal ) {
	window.jQuery = window.$ = jQuery;
}




return jQuery;
} );


/***/ }),
/* 3 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };

var $ = __webpack_require__(2);
var proj4 = __webpack_require__(58);
proj4 = proj4.__esModule ? proj4.default : proj4;

var throttle = __webpack_require__(172);
var mockVGL = __webpack_require__(173);
var DistanceGrid = __webpack_require__(114);
var ClusterGroup = __webpack_require__(115);

var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';

var svgForeignObject = '<svg xmlns="http://www.w3.org/2000/svg">' + '<foreignObject width="100%" height="100%">' + '</foreignObject>' + '</svg>';

var m_timingData = {},
    m_timingKeepRecent = 200,
    m_threshold = 15,
    m_originalRequestAnimationFrame,
    m_htmlToImageSupport;

/**
 * @typedef {object} geo.util.cssColorConversionRecord
 * @property {string} name The name of the color conversion.
 * @property {RegEx} regex A regex that, if it matches the color string, will
 *      cause the process function to be invoked.
 * @property {function} process A function that takes (`color`, `match`) with
 *      the original color string and the results of matching the regex using
 *      the regex's `exec` function.  It outputs a {@link geo.geoColorObject}
 *      color object or the original color string if there is still a parsing
 *      failure.
 */

/**
 * Takes a variable number of arguments and returns the first numeric value
 * it finds.
 *
 * @param {...*} var_args Any number of arguments.
 * @returns {number} The first numeric argument, or `undefined` if there are no
 *      numeric arguments.
 * @private
 */
function setNumeric() {
  var i;
  for (i = 0; i < arguments.length; i += 1) {
    if (isFinite(arguments[i])) {
      return arguments[i];
    }
  }
}

/**
 * Contains utility classes and methods used by geojs.
 * @namespace geo.util
 */
var util = module.exports = {
  DistanceGrid: DistanceGrid,
  ClusterGroup: ClusterGroup,

  /**
   * Check if a point is inside of a polygon.
   * Algorithm description:
   *   http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
   * The point and polygon must be in the same coordinate system.
   *
   * @param {geo.point2D} point The test point.
   * @param {geo.point2D[]} outer The outer boundary of the polygon.
   * @param {Array.<geo.point2D[]>} [inner] A list of inner boundaries
   *    (holes).
   * @param {object} [range] If specified, this is the extent of the outer
   *    polygon and is used for early detection.
   * @param {geo.point2D} range.min The minimum value of coordinates in
   *    the outer polygon.
   * @param {geo.point2D} range.max The maximum value of coordinates in
   *    the outer polygon.
   * @returns {boolean} `true` if the point is inside or on the border of the
   *    polygon.
   * @memberof geo.util
   */
  pointInPolygon: function pointInPolygon(point, outer, inner, range) {
    var inside = false,
        n = outer.length,
        i,
        j;

    if (range && range.min && range.max) {
      if (point.x < range.min.x || point.y < range.min.y || point.x > range.max.x || point.y > range.max.y) {
        return;
      }
    }

    if (n < 3) {
      // we need 3 coordinates for this to make sense
      return false;
    }

    for (i = 0, j = n - 1; i < n; j = i, i += 1) {
      if (outer[i].y > point.y !== outer[j].y > point.y && point.x < (outer[j].x - outer[i].x) * (point.y - outer[i].y) / (outer[j].y - outer[i].y) + outer[i].x) {
        inside = !inside;
      }
    }

    if (inner && inside) {
      (inner || []).forEach(function (hole) {
        inside = inside && !util.pointInPolygon(point, hole);
      });
    }

    return inside;
  },

  /**
   * Return a point in the basis of the triangle.  If the point is located on
   * a vertex of the triangle, it will be at `vert0`: (0, 0), `vert1`:
   * (1, 0), `vert2`: (0, 1).  If it is within the triangle, its coordinates
   * will be 0 <= x <= 1, 0 <= y <= 1, x + y <= 1.  The point and vertices
   * must be in the same coordinate system.
   *
   * @param {geo.point2D} point The point to convert.
   * @param {geo.point2D} vert0 Vertex 0 of the triangle.
   * @param {geo.point2D} vert1 Vertex 1 (x direction) of the triangle.
   * @param {geo.point2D} vert2 Vertex 2 (y direction) of the triangle.
   * @returns {geo.point2D} The point in the triangle basis, or `undefined`
   *    if the triangle is degenerate.
   * @memberof geo.util
   */
  pointTo2DTriangleBasis: function pointTo2DTriangleBasis(point, vert0, vert1, vert2) {
    var a = vert1.x - vert0.x,
        b = vert2.x - vert0.x,
        c = vert1.y - vert0.y,
        d = vert2.y - vert0.y,
        x = point.x - vert0.x,
        y = point.y - vert0.y,
        det = a * d - b * c;
    if (det) {
      return { x: (x * d - y * b) / det, y: (x * -c + y * a) / det };
    }
  },

  /**
   * Check if an object an HTML Image element that is fully loaded.
   *
   * @param {object} img An object that might be an HTML Image element.
   * @param {boolean} [allowFailedImage] If `true`, an image element that has
   *     a source and has failed to load is also considered 'ready' in the
   *     sense that it isn't expected to change to a better state.
   * @returns {boolean} `true` if this is an image that is ready.
   * @memberof geo.util
   */
  isReadyImage: function isReadyImage(img, allowFailedImage) {
    if (img instanceof Image && img.complete && img.src) {
      if (img.naturalWidth && img.naturalHeight || allowFailedImage) {
        return true;
      }
    }
    return false;
  },

  /**
   * Check if an object an HTMLVideoElement element that is loaded.
   *
   * @param {object} vid An object that might be an HTMLVideoElement.
   * @param {boolean} [allowFailedVideo] If `true`, an viedo element that has
   *     a source and has failed to load is also considered 'ready' in the
   *     sense that it isn't expected to change to a better state.
   * @returns {boolean} `true` if this is a video that is ready.
   * @memberof geo.util
   */
  isReadyVideo: function isReadyVideo(vid, allowFailedVideo) {
    if (vid instanceof HTMLVideoElement && vid.src && vid.HAVE_CURRENT_DATA !== undefined) {
      if (vid.videoWidth && vid.videoHeight && vid.readyState >= vid.HAVE_CURRENT_DATA || allowFailedVideo && vid.error) {
        return true;
      }
    }
    return false;
  },

  /**
   * Test if an object is a function.
   *
   * @param {object} f An object that might be a function.
   * @returns {boolean} `true` if the object is a function.
   * @memberof geo.util
   */
  isFunction: function isFunction(f) {
    return typeof f === 'function';
  },

  /**
   * Return a function.  If the supplied object is a function, return it.
   * Otherwise, return a function that returns the argument.
   *
   * @param {object} f An object that might be a function.
   * @returns {function} A function.  Either `f` or a function that returns
   *    `f`.
   * @memberof geo.util
   */
  ensureFunction: function ensureFunction(f) {
    if (util.isFunction(f)) {
      return f;
    } else {
      return function () {
        return f;
      };
    }
  },

  /**
   * Check if a value coerces to a number that is finite, not a NaN, and not
   * `null`, `false`, or the empty string.
   *
   * @param {object} val The value to check.
   * @returns {boolean} True if `val` is a non-null, non-false, finite number.
   */
  isNonNullFinite: function isNonNullFinite(val) {
    return isFinite(val) && val !== null && val !== false && val !== '';
  },

  /**
   * Return a random string of length n || 8.  The string consists of
   * mixed-case ASCII alphanumerics.
   *
   * @param {number} [n=8] The length of the string to return.
   * @returns {string} A string of random characters.
   * @memberof geo.util
   */
  randomString: function randomString(n) {
    var s, i, r;
    n = n || 8;
    s = '';
    for (i = 0; i < n; i += 1) {
      r = Math.floor(Math.random() * chars.length);
      s += chars.substring(r, r + 1);
    }
    return s;
  },

  /**
   * Convert a color to a standard rgb object.  Allowed inputs:
   *   - rgb object with optional 'a' (alpha) value.
   *   - css color name
   *   - #rrggbb, #rrggbbaa, #rgb, #rgba hexadecimal colors
   *   - rgb(), rgba(), hsl(), and hsla() css colors
   *   - transparent
   * The output object always contains r, g, b on a scale of [0-1].  If an
   * alpha value is specified, the output will also contain an 'a' value on a
   * scale of [0-1].  Objects already in rgb format are not checked to make
   * sure that all parameters are in the range of [0-1], but string inputs
   * are so validated.
   *
   * @param {geo.geoColor} color Any valid color input.
   * @returns {geo.geoColorObject} An rgb color object, possibly with an 'a'
   *    value.  If the input cannot be converted to a valid color, the input
   *    value is returned.
   * @memberof geo.util
   */
  convertColor: function convertColor(color) {
    if (color === undefined || color === null || color.r !== undefined && color.g !== undefined && color.b !== undefined) {
      return color;
    }
    var opacity;
    if (typeof color === 'string') {
      var lowerColor = color.toLowerCase();
      if (util.cssColors.hasOwnProperty(lowerColor)) {
        color = util.cssColors[lowerColor];
      } else if (color.charAt(0) === '#') {
        if (color.length === 4 || color.length === 5) {
          /* interpret values of the form #rgb as #rrggbb and #rgba as
           * #rrggbbaa */
          if (color.length === 5) {
            opacity = parseInt(color.slice(4), 16) / 0xf;
          }
          color = parseInt(color.slice(1, 4), 16);
          color = (color & 0xf00) * 0x1100 + (color & 0xf0) * 0x110 + (color & 0xf) * 0x11;
        } else if (color.length === 7 || color.length === 9) {
          if (color.length === 9) {
            opacity = parseInt(color.slice(7), 16) / 0xff;
          }
          color = parseInt(color.slice(1, 7), 16);
        }
      } else if (lowerColor === 'transparent') {
        opacity = color = 0;
      } else if (lowerColor.indexOf('(') >= 0) {
        for (var idx = 0; idx < util.cssColorConversions.length; idx += 1) {
          var match = util.cssColorConversions[idx].regex.exec(lowerColor);
          if (match) {
            return util.cssColorConversions[idx].process(lowerColor, match);
          }
        }
      }
    }
    if (isFinite(color)) {
      color = {
        r: ((color & 0xff0000) >> 16) / 255,
        g: ((color & 0xff00) >> 8) / 255,
        b: (color & 0xff) / 255
      };
    }
    if (opacity !== undefined && color && color.r !== undefined) {
      color.a = opacity;
    }
    return color;
  },

  /**
   * Convert a color (possibly with opacity) and an optional opacity value to
   * a color object that always has opacity.  The opacity is guaranteed to be
   * within [0-1].  A valid color object is always returned.
   *
   * @param {geo.geoColor} [color] Any valid color input.  If an invalid value
   *    or no value is supplied, the `defaultColor` is used.
   * @param {number} [opacity=1] A value from [0-1].  This is multipled with
   *    the opacity from `color`.
   * @param {geo.geoColorObject} [defaultColor={r: 0, g: 0, b: 0}] The color
   *    to use if an invalid color is supplied.
   * @returns {geo.geoColorObject} An rgba color object.
   * @memberof geo.util
   */
  convertColorAndOpacity: function convertColorAndOpacity(color, opacity, defaultColor) {
    color = util.convertColor(color);
    if (!color || color.r === undefined || color.g === undefined || color.b === undefined) {
      color = util.convertColor(defaultColor || { r: 0, g: 0, b: 0 });
    }
    if (!color || color.r === undefined || color.g === undefined || color.b === undefined) {
      color = { r: 0, g: 0, b: 0 };
    }
    color = {
      r: isFinite(color.r) && color.r >= 0 ? color.r <= 1 ? +color.r : 1 : 0,
      g: isFinite(color.g) && color.g >= 0 ? color.g <= 1 ? +color.g : 1 : 0,
      b: isFinite(color.b) && color.b >= 0 ? color.b <= 1 ? +color.b : 1 : 0,
      a: util.isNonNullFinite(color.a) && color.a >= 0 && color.a < 1 ? +color.a : 1
    };
    if (util.isNonNullFinite(opacity) && opacity < 1) {
      color.a = opacity <= 0 ? 0 : color.a * opacity;
    }
    return color;
  },

  /**
   * Convert a color to a six or eight digit hex value prefixed with #.
   *
   * @param {geo.geoColorObject} color The color object to convert.
   * @param {boolean} [allowAlpha] If truthy and `color` has a defined `a`
   *    value, include the alpha channel in the output.  If `'needed'`, only
   *    include the alpha channel if it is set and not 1.
   * @returns {string} A color string.
   * @memberof geo.util
   */
  convertColorToHex: function convertColorToHex(color, allowAlpha) {
    var rgb = util.convertColor(color),
        value;
    if (!rgb.r && !rgb.g && !rgb.b) {
      value = '#000000';
    } else {
      value = '#' + ((1 << 24) + (Math.round(rgb.r * 255) << 16) + (Math.round(rgb.g * 255) << 8) + Math.round(rgb.b * 255)).toString(16).slice(1);
    }
    if (rgb.a !== undefined && allowAlpha && (rgb.a < 1 || allowAlpha !== 'needed')) {
      value += (256 + Math.round(rgb.a * 255)).toString(16).slice(1);
    }
    return value;
  },
  /**
   * Convert a color to a css rgba() value.
   *
   * @param {geo.geoColorObject} color The color object to convert.
   * @returns {string} A color string.
   * @memberof geo.util
   */
  convertColorToRGBA: function convertColorToRGBA(color) {
    var rgb = util.convertColor(color);
    if (!rgb) {
      rgb = { r: 0, g: 0, b: 0 };
    }
    if (!util.isNonNullFinite(rgb.a) || rgb.a > 1) {
      rgb.a = 1;
    }
    return 'rgba(' + Math.round(rgb.r * 255) + ', ' + Math.round(rgb.g * 255) + ', ' + Math.round(rgb.b * 255) + ', ' + +(+rgb.a).toFixed(5) + ')';
  },

  /**
   * Normalize a coordinate object into {@link geo.geoPosition} form.  The
   * input can be a 2 or 3 element array or an object with a variety of
   * properties.
   *
   * @param {object|array} p The point to convert.
   * @returns {geo.geoPosition} The point as an object with `x`, `y`, and `z`
   *    properties.
   * @memberof geo.util
   */
  normalizeCoordinates: function normalizeCoordinates(p) {
    p = p || {};
    if (Array.isArray(p)) {
      return {
        x: p[0],
        y: p[1],
        z: p[2] || 0
      };
    }
    return {
      x: setNumeric(p.x, p.longitude, p.lng, p.lon, 0),
      y: setNumeric(p.y, p.latitude, p.lat, 0),
      z: setNumeric(p.z, p.elevation, p.elev, p.height, 0)
    };
  },

  /**
   * Create an integer array contains elements from one integer to another
   * integer.
   *
   * @param {number} start The start integer.
   * @param {number} end The end integer.
   * @param {number} [step=1] The step.
   * @returns {number[]} An array of integers.
   */
  range: function range(start, end, step) {
    step = step || 1;
    var results = [];
    for (var i = start; i <= end; i += step) {
      results.push(i);
    }
    return results;
  },

  /**
   * Compare two arrays and return if their contents are equal.
   * @param {array} a1 First array to compare.
   * @param {array} a2 Second array to compare.
   * @returns {boolean} `true` if the contents of the arrays are equal.
   * @memberof geo.util
   */
  compareArrays: function compareArrays(a1, a2) {
    return a1.length === a2.length && a1.every(function (el, idx) {
      return el === a2[idx];
    });
  },

  /**
   * Create a `vec3` that is always an array.  This should only be used if it
   * will not be used in a WebGL context.  Plain arrays usually use 64-bit
   * float values, whereas `vec3` defaults to 32-bit floats.
   *
   * @returns {array} Zeroed-out vec3 compatible array.
   * @memberof geo.util
   */
  vec3AsArray: function vec3AsArray() {
    return [0, 0, 0];
  },

  /**
   * Create a `mat3` that is always an array.  This should only be used if it
   * will not be used in a WebGL context.  Plain arrays usually use 64-bit
   * float values, whereas `mat3` defaults to 32-bit floats.
   *
   * @returns {array} Identity `mat3` compatible array.
   * @memberof geo.util
   */
  mat3AsArray: function mat3AsArray() {
    return [1, 0, 0, 0, 1, 0, 0, 0, 1];
  },

  /**
   * Create a `mat4` that is always an array.  This should only be used if it
   * will not be used in a WebGL context.  Plain arrays usually use 64-bit
   * float values, whereas `mat4` defaults to 32-bit floats.
   *
   * @returns {array} Identity `mat4` compatible array.
   * @memberof geo.util
   */
  mat4AsArray: function mat4AsArray() {
    return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
  },

  /**
   * Get a buffer for a vgl geometry source.  If a buffer already exists and
   * is the correct size, return it.  Otherwise, allocate a new buffer; any
   * data in an old buffer is discarded.
   *
   * @param {vgl.geometryData} geom The geometry to reference and modify.
   * @param {string} srcName The name of the source.
   * @param {number} len The number of elements for the array.
   * @returns {Float32Array} A buffer for the named source.
   * @memberof geo.util
   */
  getGeomBuffer: function getGeomBuffer(geom, srcName, len) {
    var src = geom.sourceByName(srcName),
        data;

    data = src.data();
    if (data instanceof Float32Array && data.length === len) {
      return data;
    }
    data = new Float32Array(len);
    src.setData(data);
    return data;
  },

  /**
   * Ensure that the input and modifiers properties of all actions are
   * objects, not plain strings.
   *
   * @param {geo.actionRecord[]} actions An array of actions to adjust as
   *    needed.
   * @memberof geo.util
   */
  adjustActions: function adjustActions(actions) {
    var action, i;
    for (i = 0; i < actions.length; i += 1) {
      action = actions[i];
      if ($.type(action.input) === 'string') {
        var actionEvents = {};
        actionEvents[action.input] = true;
        action.input = actionEvents;
      }
      if (!action.modifiers) {
        action.modifiers = {};
      }
      if ($.type(action.modifiers) === 'string') {
        var actionModifiers = {};
        actionModifiers[action.modifiers] = true;
        action.modifiers = actionModifiers;
      }
    }
  },

  /**
   * Add an action to the list of handled actions.
   *
   * @param {geo.actionRecord[]} actions An array of actions to adjust as
   *    needed.
   * @param {geo.actionRecord} action An object defining the action.  Use
   *    `action`, `name`, and `owner` to make this entry distinct if it will
   *    need to be removed later.
   * @param {boolean} toEnd The action is added at the beginning of the
   *    actions list unless `toEnd` is `true`.  Earlier actions prevent later
   *    actions with the similar input and modifiers.
   * @memberof geo.util
   */
  addAction: function addAction(actions, action, toEnd) {
    if (toEnd) {
      actions.push(action);
    } else {
      actions.unshift(action);
    }
    util.adjustActions(actions);
  },

  /**
   * Check if an action is in the actions list.  An action matches if the
   * `action`, `name`, and `owner` match.  A `null` or `undefined` value will
   * match all actions.  If using a {@link geo.actionRecord} object, this is
   * the same as passing (`action.action`, `action.name`, `action.owner`).
   *
   * @param {geo.actionRecord[]} actions An array of actions to search.
   * @param {geo.actionRecord|string} action Either an action object or the
   *    name of an action.
   * @param {string} [name] Optional name associated with the action.
   * @param {string} [owner] Optional owner associated with the action.
   * @returns {geo.actionRecord?} The first matching action or `null`.
   * @memberof geo.util
   */
  hasAction: function hasAction(actions, action, name, owner) {
    if (action && action.action) {
      name = action.name;
      owner = action.owner;
      action = action.action;
    }
    for (var i = 0; i < actions.length; i += 1) {
      if ((!action || actions[i].action === action) && (!name || actions[i].name === name) && (!owner || actions[i].owner === owner)) {
        return actions[i];
      }
    }
    return null;
  },

  /**
   * Remove all matching actions.  Actions are matched as with `hasAction`.
   *
   * @param {geo.actionRecord[]} actions An array of actions to adjust as
   *    needed.
   * @param {geo.actionRecord|string} action Either an action object or the
   *    name of an action.
   * @param {string} [name] Optional name associated with the action.
   * @param {string} [owner] Optional owner associated with the action.
   * @returns {number} The number of actions that were removed.
   * @memberof geo.util
   */
  removeAction: function removeAction(actions, action, name, owner) {
    var found,
        removed = 0;

    do {
      found = util.hasAction(actions, action, name, owner);
      if (found) {
        actions.splice($.inArray(found, actions), 1);
        removed += 1;
      }
    } while (found);
    return removed;
  },

  /**
   * Determine if the current inputs and modifiers match a known action.
   *
   * @param {object} inputs Aan object where each input that is currently
   *    active is truthy.  Common inputs are `left`, `right`, `middle`,
   *    `wheel`, `pan`, `rotate`.
   * @param {object} modifiers An object where each currently applied
   *    modifier is truthy.  Common modifiers are `shift`, `ctrl`, `alt`,
   *    `meta`.
   * @param {geo.actionRecord[]} actions A list of actions to compare to the
   *    inputs and modifiers.  The first action that matches will be
   *    returned.
   * @returns {geo.actionRecord} A matching action or `undefined`.
   * @memberof geo.util
   */
  actionMatch: function actionMatch(inputs, modifiers, actions) {
    var matched;

    /* actions must have already been processed by adjustActions */
    if (actions.some(function (action) {
      for (var input in action.input) {
        if (action.input.hasOwnProperty(input)) {
          if (action.input[input] === false && inputs[input] || action.input[input] && !inputs[input]) {
            return false;
          }
        }
      }
      for (var modifier in action.modifiers) {
        if (action.modifiers.hasOwnProperty(modifier)) {
          if (action.modifiers[modifier] === false && modifiers[modifier] || action.modifiers[modifier] && !modifiers[modifier]) {
            return false;
          }
        }
      }
      matched = action;
      return true;
    })) {
      return matched;
    }
  },

  /**
   * Return recommended defaults for map parameters and osm or tile layer
   * paramaters where the expected intent is to use the map in pixel
   * coordinates (upper left is (0, 0), lower right is (`width`, `height`).
   *
   * @example <caption>The returned objects can be modified or
   *    extended.</caption>
   * var results = pixelCoordinateParams('#map', 10000, 9000);
   * var map = geo.map($.extend(results.map, {clampZoom: false}));
   * map.createLayer('osm', results.layer);
   *
   * @param {string} [node] DOM selector for the map container.
   * @param {number} width Width of the whole map contents in pixels.
   * @param {number} height Height of the whole map contents in pixels.
   * @param {number} [tileWidth] If an osm or tile layer is going to be used,
   *    the width of a tile.
   * @param {number} [tileHeight] If an osm or tile layer is going to be used,
   *    the height of a tile.
   * @returns {object} An object with `map` and `layer` properties.  `map` is
   *    an object that can be passed to {@link geo.map}, and `layer` is an
   *    object that can be passed to `map.createLayer`.
   * @memberof geo.util
   */
  pixelCoordinateParams: function pixelCoordinateParams(node, width, height, tileWidth, tileHeight) {
    var mapW, mapH, tiled;
    if (node) {
      node = $(node);
      mapW = node.innerWidth();
      mapH = node.innerHeight();
    }
    tileWidth = tileWidth || width;
    tileHeight = tileHeight || height;
    tiled = tileWidth !== width || tileHeight !== height;
    var minLevel = Math.min(0, Math.floor(Math.log(Math.min((mapW || tileWidth) / tileWidth, (mapH || tileHeight) / tileHeight)) / Math.log(2))),
        maxLevel = Math.ceil(Math.log(Math.max(width / tileWidth, height / tileHeight)) / Math.log(2));
    var mapParams = {
      node: node,
      ingcs: '+proj=longlat +axis=esu',
      gcs: '+proj=longlat +axis=enu',
      maxBounds: { left: 0, top: 0, right: width, bottom: height },
      unitsPerPixel: Math.pow(2, maxLevel),
      center: { x: width / 2, y: height / 2 },
      min: minLevel,
      max: maxLevel,
      zoom: minLevel,
      clampBoundsX: true,
      clampBoundsY: true,
      clampZoom: true
    };
    var layerParams = {
      maxLevel: maxLevel,
      wrapX: false,
      wrapY: false,
      tileOffset: function tileOffset() {
        return { x: 0, y: 0 };
      },
      attribution: '',
      tileWidth: tileWidth,
      tileHeight: tileHeight,
      tileRounding: Math.ceil,
      tilesAtZoom: tiled ? function (level) {
        var scale = Math.pow(2, maxLevel - level);
        return {
          x: Math.ceil(width / tileWidth / scale),
          y: Math.ceil(height / tileHeight / scale)
        };
      } : undefined,
      tilesMaxBounds: tiled ? function (level) {
        var scale = Math.pow(2, maxLevel - level);
        return {
          x: Math.floor(width / scale),
          y: Math.floor(height / scale)
        };
      } : undefined
    };
    return { map: mapParams, layer: layerParams };
  },

  /**
   * Return the coordinate associated with the center of the perimeter formed
   * from a list of points.  This averages all of the vertices in the perimeter
   * weighted by the line length on either side of each point.  Functionally,
   * this is the same as the average of all the points of the lines of the
   * perimeter.
   *
   * @param {geo.geoPosition[]} coor An array of coordinates.
   * @returns {geo.geoPosition|undefined} The position for the center, or
   *    `undefined` if no such position exists.
   */
  centerFromPerimeter: function centerFromPerimeter(coor) {
    var position, p0, p1, w, sumw, i;
    if (!coor || !coor.length) {
      return;
    }
    if (coor.length === 1) {
      return { x: coor[0].x, y: coor[0].y };
    }
    position = { x: 0, y: 0 };
    sumw = 0;
    p0 = coor[coor.length - 1];
    for (i = 0; i < coor.length; i += 1) {
      p1 = p0;
      p0 = coor[i];
      w = Math.sqrt(Math.pow(p1.x - p0.x, 2) + Math.pow(p1.y - p0.y, 2));
      position.x += (p0.x + p1.x) * w;
      position.y += (p0.y + p1.y) * w;
      sumw += 2 * w;
    }
    position.x /= sumw;
    position.y /= sumw;
    // return a copy of p0 if all points are the same
    return sumw ? position : { x: p0.x, y: p0.y };
  },

  /**
   * Get the square of the Euclidean 2D distance between two points.
   *
   * @param {geo.geoPosition} pt1 The first point.
   * @param {geo.geoPosition} pt2 The second point.
   * @returns {number} The distance squared.
   */
  distance2dSquared: function distance2dSquared(pt1, pt2) {
    var dx = pt1.x - pt2.x,
        dy = pt1.y - pt2.y;
    return dx * dx + dy * dy;
  },

  /**
   * Get the square of the Euclidean 2D distance between a point and a line
   * segment.
   *
   * @param {geo.geoPosition} pt The point.
   * @param {geo.geoPosition} line1 One end of the line.
   * @param {geo.geoPosition} line2 The other end of the line.
   * @returns {number} The distance squared.
   */
  distance2dToLineSquared: function distance2dToLineSquared(pt, line1, line2) {
    var dx = line2.x - line1.x,
        dy = line2.y - line1.y,

    // we could get the line length from the distance2dSquared function,
    // but since we need dx and dy in this function, it is faster to just
    // compute it here.
    lengthSquared = dx * dx + dy * dy,
        t = 0;
    if (lengthSquared) {
      t = ((pt.x - line1.x) * dx + (pt.y - line1.y) * dy) / lengthSquared;
      t = Math.max(0, Math.min(1, t));
    }
    return util.distance2dSquared(pt, {
      x: line1.x + t * dx,
      y: line1.y + t * dy
    });
  },

  /**
   * Get twice the signed area of a 2d triangle.
   *
   * @param {geo.geoPosition} pt1 A vertex.
   * @param {geo.geoPosition} pt2 A vertex.
   * @param {geo.geoPosition} pt3 A vertex.
   * @returns {number} Twice the signed area.
   */
  triangleTwiceSignedArea2d: function triangleTwiceSignedArea2d(pt1, pt2, pt3) {
    return (pt2.y - pt1.y) * (pt3.x - pt2.x) - (pt2.x - pt1.x) * (pt3.y - pt2.y);
  },

  /**
   * Determine if two line segments cross.  They are not considered crossing if
   * they share a vertex.  They are crossing if either of one segment's
   * vertices are colinear with the other segment.
   *
   * @param {geo.geoPosition} seg1pt1 One endpoint of the first segment.
   * @param {geo.geoPosition} seg1pt2 The other endpoint of the first segment.
   * @param {geo.geoPosition} seg2pt1 One endpoint of the second segment.
   * @param {geo.geoPosition} seg2pt2 The other endpoint of the second segment.
   * @returns {boolean} True if the segments cross.
   */
  crossedLineSegments2d: function crossedLineSegments2d(seg1pt1, seg1pt2, seg2pt1, seg2pt2) {
    /* If the segments don't have any overlap in x or y, they can't cross */
    if (seg1pt1.x > seg2pt1.x && seg1pt1.x > seg2pt2.x && seg1pt2.x > seg2pt1.x && seg1pt2.x > seg2pt2.x || seg1pt1.x < seg2pt1.x && seg1pt1.x < seg2pt2.x && seg1pt2.x < seg2pt1.x && seg1pt2.x < seg2pt2.x || seg1pt1.y > seg2pt1.y && seg1pt1.y > seg2pt2.y && seg1pt2.y > seg2pt1.y && seg1pt2.y > seg2pt2.y || seg1pt1.y < seg2pt1.y && seg1pt1.y < seg2pt2.y && seg1pt2.y < seg2pt1.y && seg1pt2.y < seg2pt2.y) {
      return false;
    }
    /* If any vertex is in common, it is not considered crossing */
    if (seg1pt1.x === seg2pt1.x && seg1pt1.y === seg2pt1.y || seg1pt1.x === seg2pt2.x && seg1pt1.y === seg2pt2.y || seg1pt2.x === seg2pt1.x && seg1pt2.y === seg2pt1.y || seg1pt2.x === seg2pt2.x && seg1pt2.y === seg2pt2.y) {
      return false;
    }
    /* If the lines cross, the signed area of the triangles formed between one
     * segment and the other's vertices will have different signs.  By using
     * > 0, colinear points are crossing. */
    if (util.triangleTwiceSignedArea2d(seg1pt1, seg1pt2, seg2pt1) * util.triangleTwiceSignedArea2d(seg1pt1, seg1pt2, seg2pt2) > 0 || util.triangleTwiceSignedArea2d(seg2pt1, seg2pt2, seg1pt1) * util.triangleTwiceSignedArea2d(seg2pt1, seg2pt2, seg1pt2) > 0) {
      return false;
    }
    return true;
  },

  /**
   * Check if a line segment crosses any segment from a list of lines.  The
   * segment is considered crossing it it touches a line segment, unless that
   * line segment shares a vertex with the segment.
   *
   * @param {geo.geoPosition} pt1 One end of the line segment.
   * @param {geo.geoPosition} pt2 The other end of the line segment.
   * @param {Array.<geo.geoPosition[]>} lineList A list of open lines.  Each
   *    line is a list of vertices.  The line segment is checked against each
   *    segment of each line in this list.
   * @returns {boolean} True if the segment crosses any line segment.
   */
  segmentCrossesLineList2d: function segmentCrossesLineList2d(pt1, pt2, lineList) {
    var result = lineList.some(function (line) {
      return line.some(function (linePt, idx) {
        if (idx) {
          return util.crossedLineSegments2d(pt1, pt2, line[idx - 1], linePt);
        }
      });
    });
    return result;
  },

  /**
   * Remove vertices from a chain of 2d line segments so that it is simpler but
   * is close to the original overall shape within some tolerance limit.  This
   * is the Ramer–Douglas–Peucker algorithm.  The first and last points will
   * always remain the same for open lines.  For closed lines (polygons), this
   * picks an point that likely to be significant and then reduces it, possibly
   * returning a single point.
   *
   * @param {geo.geoPosition[]} pts A list of points forming the line or
   *    polygon.
   * @param {number} tolerance The maximum variation allowed.  A value of zero
   *    will only remove perfectly colinear points.
   * @param {boolean} [closed] If true, this is a polygon rather than an open
   *    line.  In this case, it is possible to get back a single point.
   * @param {Array.<geo.geoPosition[]>?} [noCrossLines] A falsy value to allow
   *    the resultant line to cross itself, an empty array (`[]`) to prevent
   *    self-crossing, or an array of line segments to prevent self-crossing
   *    and disallow crossing any line segment in the list.  Each entry in the
   *    list is an open line (with one segment less than the number of
   *    vertices).  If self-crossing is prohibited, the resultant point set
   *    might not be as simplified as it could be.
   * @returns {geo.geoPosition[]} The new point set.
   */
  rdpLineSimplify: function rdpLineSimplify(pts, tolerance, closed, noCrossLines) {
    if (pts.length <= 2 || tolerance < 0) {
      return pts;
    }
    var i,
        distSq,
        maxDistSq = -1,
        index,
        toleranceSq = tolerance * tolerance;
    if (closed) {
      /* If this is closed, find the point that is furthest from the first
       * point.  ideally, one would find a point that is guaranteed to be on
       * the diameter of the convex hull, but doing so is an O(n^2) operation,
       * whereas this is sufficient and only O(n).  The chosen point is
       * duplicated at the start and end of the chain. */
      for (i = 1; i < pts.length; i += 1) {
        distSq = util.distance2dSquared(pts[0], pts[i]);
        if (distSq > maxDistSq) {
          maxDistSq = distSq;
          index = i;
        }
      }
      /* Points could be on any side of the start point, so if all points are
       * within 1/2 of the tolerance of the start point, we know all points are
       * within the tolerance of each other and therefore this polygon or
       * closed line can be simplified to a point. */
      if (maxDistSq * 4 <= toleranceSq) {
        return pts.slice(index, index + 1);
      }
      pts = pts.slice(index).concat(pts.slice(0, index + 1));
      pts = util.rdpLineSimplify(pts, tolerance, false, noCrossLines);
      /* Removed the duplicated first point */
      pts.splice(pts.length - 1);
      return pts;
    }
    for (i = 1; i < pts.length - 1; i += 1) {
      distSq = util.distance2dToLineSquared(pts[i], pts[0], pts[pts.length - 1]);
      if (distSq > maxDistSq) {
        maxDistSq = distSq;
        index = i;
      }
    }
    /* We can collapse this to a single line if it is within the tolerance and
     * we are either allowed to self-cross or it does not self-cross the rest
     * of the line. */
    if (maxDistSq <= toleranceSq && (!noCrossLines || !util.segmentCrossesLineList2d(pts[0], pts[pts.length - 1], noCrossLines))) {
      return [pts[0], pts[pts.length - 1]];
    }
    var left = pts.slice(0, index + 1),
        right = pts.slice(index),
        leftSide = util.rdpLineSimplify(left, tolerance, false, noCrossLines ? noCrossLines.concat([right]) : null),
        rightSide = util.rdpLineSimplify(right, tolerance, false, noCrossLines ? noCrossLines.concat([left]) : null);
    return leftSide.slice(0, leftSide.length - 1).concat(rightSide);
  },

  /**
   * Given an array, return the minimum and maximum values within the array.
   * If a numeric value is specified for one or the other, return that instead.
   *
   * @param {number[]} values An array of numeric values.
   * @param {number} [min] If specified, use this instead of calculating the
   *    minimum.
   * @param {number} [max] If specified, use this instead of calculating the
   *    maximum.
   * @param {boolean} [limit=false] If truthy, if `min` is specified, the
   *    returned `min` will be the larger of the specified value and the
   *    computed value, and if `max` is specified, the returned value will
   *    be the smaller of the specified value and the computed value.
   * @returns {object} An object with `min` and `max`, both numbers.  If the
   *    array is empty, `undefined` may be returned for the `min` and `max`.
   */
  getMinMaxValues: function getMinMaxValues(values, min, max, limit) {
    if (values.length && (limit || !$.isNumeric(min) || !$.isNumeric(max))) {
      var minValue = values[0],
          maxValue = values[0],
          value,
          i;
      for (i = values.length - 1; i > 0; i -= 1) {
        value = values[i];
        if (value < minValue) {
          minValue = value;
        }
        if (value > maxValue) {
          maxValue = value;
        }
      }
      if (!$.isNumeric(min) || limit && minValue > min) {
        min = minValue;
      }
      if (!$.isNumeric(max) || limit && maxValue < max) {
        max = maxValue;
      }
    }
    return { min: min, max: max };
  },

  /**
   * Given a value in radians, return a value wrapped to the range [-PI, PI).
   *
   * @param {number} value A value in radians.
   * @returns {number} The wrapped value.
   */
  wrapAngle: function wrapAngle(value) {
    /* Module will only ensure that this is between [-2 PI, 2 PI). */
    value = value % (Math.PI * 2);
    if (value < -Math.PI) {
      value += Math.PI * 2;
    } else if (value >= Math.PI) {
      value -= Math.PI * 2;
    }
    return value;
  },

  /**
   * Escape any character in a string that has a code point >= 127.
   *
   * @param {string} text The string to escape.
   * @returns {string} The escaped string.
   * @memberof geo.util
   */
  escapeUnicodeHTML: function escapeUnicodeHTML(text) {
    return text.replace(/./g, function (k) {
      var code = k.charCodeAt();
      if (code < 127) {
        return k;
      }
      return '&#' + code.toString(10) + ';';
    });
  },

  /**
   * Check svg image and html img tags.  If the source is set, load images
   * explicitly and convert them to local data:image references.
   *
   * @param {jQuery.selector} elem A jQuery selector or element set that may
   *    contain images.
   * @returns {jQuery.Deferred[]} A list of deferred objects that resolve
   *    when images are dereferenced.
   * @memberof geo.util
   */
  dereferenceElements: function dereferenceElements(elem) {
    var deferList = [];

    $('img,image', elem).each(function () {
      var src = $(this);
      var key = src.is('image') ? 'href' : 'src';
      if (src.attr(key)) {
        var img = new Image();
        if (src.attr(key).substr(0, 4) === 'http' || src[0].crossOrigin) {
          img.crossOrigin = src[0].crossOrigin || 'anonymous';
        }
        var defer = $.Deferred();
        img.onload = function () {
          var cvs = document.createElement('canvas');
          cvs.width = img.naturalWidth;
          cvs.height = img.naturalHeight;
          cvs.getContext('2d').drawImage(img, 0, 0);
          src.attr(key, cvs.toDataURL('image/png'));
          if (src.attr(key).substr(0, 10) !== 'data:image') {
            src.remove();
          }
          defer.resolve();
        };
        img.onerror = function () {
          src.remove();
          defer.resolve();
        };
        img.src = src.attr(key);
        deferList.push(defer);
      }
    });
    return deferList;
  },

  dereferenceCssUrlsRegex: /url\(["']?(http[^)"']+|[^:)"']+)["']?\)/g,

  /**
   * Check css text.  Any url(http[s]...) references are dereferenced and
   * stored as local base64 urls.
   *
   * @param {string} css The css to parse for urls.
   * @param {jQuery.selector|DOMElement} styleElem The element that receivs
   *    the css text after dereferencing or the DOM element that has style
   *    that will be updated.
   * @param {jQuery.Deferred} styleDefer A Deferred to resolve once
   *    dereferencing is complete.
   * @param {string} [styleKey] If unset, styleElem is a header element.  If
   *    set, styleElem is a DOM element and the named style will be updated.
   * @param {string} [baseUrl] If present, this is the base for relative urls.
   * @memberof geo.util
   */
  dereferenceCssUrls: function dereferenceCssUrls(css, styleElem, styleDefer, styleKey, baseUrl) {
    var deferList = [],
        results = [];

    /* Remove comments to avoid dereferencing commented out sections.
     * To match across lines, use [^\0] rather than . */
    css = css.replace(/\/\*[^\0]*?\*\//g, '');
    /* reduce whitespace to make the css shorter */
    css = css.replace(/\r/g, '\n').replace(/\s+\n/g, '\n').replace(/\n\s+/g, '\n').replace(/\n\n+/g, '\n');
    if (baseUrl) {
      var match = /(^[^?#]*)\/[^?#/]*([?#]|$)/g.exec(baseUrl);
      baseUrl = match && match[1] ? match[1] + '/' : null;
    }
    css.replace(util.dereferenceCssUrlsRegex, function (match, url) {
      var idx = deferList.length,
          defer = $.Deferred(),
          xhr = new XMLHttpRequest();
      deferList.push(defer);
      results.push('');

      if (/^[^/:][^:]*(\/|$)/g.exec(url) && baseUrl) {
        url = baseUrl + url;
      }
      xhr.open('GET', url, true);
      xhr.responseType = 'arraybuffer';
      xhr.onload = function () {
        if (this.status === 200) {
          var response = new Uint8Array(this.response),
              data = new Array(response.length),
              i;
          for (i = 0; i < response.length; i += 1) {
            data[i] = String.fromCharCode(response[i]);
          }
          data = data.join('');
          results[idx] = 'url(data:' + xhr.getResponseHeader('content-type') + ';base64,' + btoa(data) + ')';
        }
        // resolve regardless of what response we get
        defer.resolve();
      };
      // if this fails, resolve anyway
      xhr.onerror = defer.resolve;
      xhr.send();
      return match;
    });
    $.when.apply($, deferList).then(function () {
      var idx = 0;
      css = css.replace(util.dereferenceCssUrlsRegex, function (match, url) {
        idx += 1;
        return results[idx - 1];
      });
      if (styleKey === undefined) {
        styleElem.text(css);
      } else {
        styleElem.style[styleKey] = css;
      }
      styleDefer.resolve();
    });
  },

  /**
   * Check if the current browser supports covnerting html to an image via an
   * svg foreignObject and canvas.  If this has not been checked before, it
   * returns a Deferred that resolves to a boolean (never rejects).  If the
   * check has been done before, it returns a boolean.
   *
   * @returns {boolean|jQuery.Deferred}
   */
  htmlToImageSupported: function htmlToImageSupported() {
    if (m_htmlToImageSupport === undefined) {
      var defer = $.Deferred();
      var svg = $(svgForeignObject);
      svg.attr({
        width: '10px',
        height: '10px',
        'text-rendering': 'optimizeLegibility'
      });
      $('foreignObject', svg).append('<div/>');
      var img = new Image();
      img.onload = img.onerror = function () {
        var canvas = document.createElement('canvas');
        canvas.width = 10;
        canvas.height = 10;
        var context = canvas.getContext('2d');
        context.drawImage(img, 0, 0);
        try {
          canvas.toDataURL();
          m_htmlToImageSupport = true;
        } catch (err) {
          console.warn('This browser does not support converting HTML to an image via ' + 'SVG foreignObject.  Some functionality will be limited.', err);
          m_htmlToImageSupport = false;
        }
        defer.resolve(m_htmlToImageSupport);
      };
      img.src = 'data:image/svg+xml;base64,' + btoa(util.escapeUnicodeHTML(new XMLSerializer().serializeToString(svg[0])));
      return defer;
    }
    return m_htmlToImageSupport;
  },

  /**
   * Convert an html element to an image.  This attempts to localize any
   * images within the element.  If there are other external references, the
   * image may not work due to security considerations.
   *
   * @param {jQuery.selector} elem Either a jquery selector or an HTML
   *    element.  This may contain multiple elements.  The direct parent and
   *    grandparent of the element are used for class information.
   * @param {number} [parents] Number of layers up to travel to get class
   *    information.
   * @returns {jQuery.Deferred} A jquery deferred object which receives an
   *    HTML Image element when resolved.
   * @memberof geo.util
   */
  htmlToImage: function htmlToImage(elem, parents) {
    var defer = $.Deferred(),
        deferList = [util.htmlToImageSupported()],
        container;

    var parent = $(elem);
    elem = $(elem).clone();
    while (parents && parents > 0) {
      parent = parent.parent();
      if (parent.is('div')) {
        /* Create a containing div with the parent's class and id (so css
         * will be used), but override size and background. */
        container = $('<div>').attr({
          'class': parent.attr('class'),
          id: parent.attr('id')
        }).css({
          width: '100%',
          height: '100%',
          background: 'none',
          margin: 0
        });
        container.append(elem);
        elem = container;
      }
      parents -= 1;
    }
    // canvas elements won't render properly here.
    $('canvas', elem).remove();
    /* Walk through all of the children of elem and check if any explicitly set
     * css property needs to be dereferenced. */
    $('*', elem).addBack().each(function () {
      var style = this.style;
      for (var idx = 0; idx < style.length; idx += 1) {
        var key = this.style[idx];
        if (this.style[key].match(util.dereferenceCssUrlsRegex)) {
          var styleDefer = $.Deferred();
          util.dereferenceCssUrls(this.style[key], this, styleDefer, key);
          deferList.push(styleDefer);
        }
      }
    });
    container = $('<div xmlns="http://www.w3.org/1999/xhtml">');
    container.css({
      width: parent.width() + 'px',
      height: parent.height() + 'px'
    });
    container.append($('<head>'));
    var body = $('<body>');
    container.append(body);
    /* We must specify the new body as having no background, or we'll clobber
     * other layers. */
    body.css({
      width: parent.width() + 'px',
      height: parent.height() + 'px',
      background: 'none',
      margin: 0
    });
    body.append(elem);
    deferList = deferList.concat(util.dereferenceElements(elem));
    /* Get styles and links in order, as order matters in css */
    $('style,link[rel="stylesheet"]').each(function () {
      var styleElem = $('<style type="text/css">'),
          styleDefer = $.Deferred();
      if ($(this).is('style')) {
        var css = $(this).text();
        util.dereferenceCssUrls(css, styleElem, styleDefer);
      } else {
        var href = $(this).attr('href');
        $.get(href).done(function (css) {
          util.dereferenceCssUrls(css, styleElem, styleDefer, undefined, href);
        }).fail(function (xhr, status, err) {
          console.warn('Failed to dereference ' + href, status, err);
          styleElem.remove();
          styleDefer.resolve();
        });
      }
      deferList.push(styleDefer);
      $('head', container).append(styleElem);
    });

    $.when.apply($, deferList).then(function () {
      var svg = $(svgForeignObject);
      svg.attr({
        width: parent.width() + 'px',
        height: parent.height() + 'px',
        // Adding this via the attr call works in Firefox headless, whereas if
        // it is part of the svgForeignObject string, it does not.
        'text-rendering': 'optimizeLegibility'
      });
      $('foreignObject', svg).append(container);

      var img = new Image();
      if (!util.htmlToImageSupported()) {
        defer.resolve(img);
      } else {
        img.onload = function () {
          defer.resolve(img);
        };
        img.onerror = function () {
          console.warn('Failed to render html to image');
          defer.reject();
        };
        // Firefox requires the HTML to be base64 encoded.  Chrome doesn't, but
        // doing so does no harm.
        img.src = 'data:image/svg+xml;base64,' + btoa(util.escapeUnicodeHTML(new XMLSerializer().serializeToString(svg[0])));
      }
    });
    return defer;
  },

  /**
   * Report on one or all of the tracked timings.
   *
   * @param {string} [name] A name to report on, or `undefined` to report all.
   * @returns {object} An object with timing information, or an object with
   *    properties for all tracked timings, each of which contains timing
   *    information.
   * @memberof geo.util
   */
  timeReport: function timeReport(name) {
    $.each(m_timingData, function (key, item) {
      /* calculate the standard deviation of each item. */
      if (item.count) {
        item.stddev = Math.sqrt(Math.abs((item.sum2 - item.sum * item.sum / item.count) / item.count));
        item.average = item.sum / item.count;
      } else {
        item.stddev = 0;
        item.average = 0;
      }
    });
    if (name) {
      return m_timingData[name];
    }
    return m_timingData;
  },

  /**
   * Note the start time of a function (or any other section of code).  This
   * should be paired with `timeFunctionStop`, which will collect statistics on
   * the amount of time spent in a function.
   *
   * @param {string} name Name to use for tracking the timing.
   * @param {boolean} reset If `true`, clear old tracking data for this named
   *    tracker.
   * @memberof geo.util
   */
  timeFunctionStart: function timeFunctionStart(name, reset) {
    if (!m_timingData[name] || reset) {
      m_timingData[name] = {
        count: 0, sum: 0, sum2: 0, max: 0, recent: []
      };
    }
    m_timingData[name].start = window.performance.now();
  },

  /**
   * Note the stop time of a function (or any other section of code).  This
   * should be paired with `timeFunctionStart`.
   *
   * @param {string} name Name to use for tracking the timing.
   * @memberof geo.util
   */
  timeFunctionStop: function timeFunctionStop(name) {
    if (!m_timingData[name] || !m_timingData[name].start) {
      return;
    }
    var duration = window.performance.now() - m_timingData[name].start;
    m_timingData[name].start = null;
    m_timingData[name].sum += duration;
    m_timingData[name].sum2 += duration * duration;
    m_timingData[name].count += 1;
    m_timingData[name].max = Math.max(m_timingData[name].max, duration);
    m_timingData[name].recent.push(duration);
    if (m_timingData[name].recent.length > m_timingKeepRecent) {
      m_timingData[name].recent.splice(0, m_timingData[name].recent.length - m_timingKeepRecent);
    }
  },

  /**
   * Start or stop tracking the time spent in `requestAnimationFrame`.  If
   * tracked, the results can be fetched via
   * `timeFunctionReport('requestAnimationFrame')`.
   *
   * @param {boolean} [stop] Falsy to start tracking, truthy to start tracking.
   * @param {boolean} [reset] If truthy, reset the statistics.
   * @param {number} [threshold=15] If present, set the threshold in
   *    milliseconds used in tracking slow callbacks.
   * @param {number} [keep=200] If present, set the number of recent frame
   *    times to track.
   * @memberof geo.util
   */
  timeRequestAnimationFrame: function timeRequestAnimationFrame(stop, reset, threshold, keep) {
    if (!m_timingData.requestAnimationFrame || reset) {
      m_timingData.requestAnimationFrame = {
        count: 0,
        sum: 0,
        sum2: 0,
        max: 0,
        above_threshold: 0,
        recent: [],
        recentsub: []
      };
    }
    if (threshold) {
      m_threshold = threshold;
    }
    if (keep) {
      m_timingKeepRecent = keep;
    }
    if (stop && m_originalRequestAnimationFrame) {
      window.requestAnimationFrame = m_originalRequestAnimationFrame;
      m_originalRequestAnimationFrame = null;
    } else if (!stop && !m_originalRequestAnimationFrame) {
      m_originalRequestAnimationFrame = window.requestAnimationFrame;
      window.requestAnimationFrame = function (callback) {
        return m_originalRequestAnimationFrame.call(window, function (timestamp) {
          var track = m_timingData.requestAnimationFrame,
              recent;
          /* Some environments have unsynchronized performance and time
           * counters.  The nowDelta factor compensates for this.  For
           * instance, our test enviornment has performance.now() values on
           * the order of ~3000 and timestamps approximating epoch. */
          if (track.timestamp !== timestamp) {
            track.nowDelta = window.performance.now() - timestamp;
            if (Math.abs(track.nowDelta) < 1000) {
              track.nowDelta = 0;
            }
            track.timestamp = timestamp;
            track.subcalls = track.subcalls || 0;
            track.start = {
              sum: track.sum,
              sum2: track.sum2,
              count: track.count,
              max: track.max,
              above_threshold: track.above_threshold
            };
            track.recent.push([0]);
            track.recentsub.push([]);
            if (track.recent.length > m_timingKeepRecent) {
              track.recent.splice(0, track.recent.length - m_timingKeepRecent);
              track.recentsub.splice(0, track.recentsub.length - m_timingKeepRecent);
            }
          }
          track.subcalls += 1;
          callback.apply(this, arguments);
          var duration = window.performance.now() - timestamp;
          duration -= track.nowDelta;
          track.sum = track.start.sum + duration;
          track.sum2 = track.start.sum2 + duration * duration;
          track.count = track.start.count + 1;
          track.max = Math.max(track.max, duration);
          track.above_threshold = track.start.above_threshold + (duration >= m_threshold ? 1 : 0);
          track.recent[track.recent.length - 1] = duration;
          recent = track.recentsub[track.recent.length - 1];
          recent.push({
            total_duration: duration,
            duration: duration - (recent.length ? recent[recent.length - 1].total_duration : 0),
            callback: callback.name || callback
          });
        });
      };
    }
  },

  /**
   * Test if an item is an object.  This uses typeof not instanceof, since
   * instanceof will return false for some things that we expect to be objects.
   *
   * @param {*} value The item to test.
   * @returns {boolean} True if the tested item is an object.
   */
  isObject: function isObject(value) {
    var type = typeof value === 'undefined' ? 'undefined' : _typeof(value);
    return value !== null && value !== undefined && (type === 'object' || type === 'function');
  },

  ///////////////////////////////////////////////////////////////////////////
  /*
   * Utility member properties.
   */
  ///////////////////////////////////////////////////////////////////////////

  /**
   * Radius of the earth in meters, from the equatorial radius of SRID 4326.
   * @memberof geo.util
   */
  radiusEarth: proj4.WGS84.a,

  /**
   * A regular expression string that will parse a number (integer or floating
   * point) for CSS properties.
   * @memberof geo.util
   */
  cssNumberRegex: '[+-]?(?:\\d+\\.?\\d*|\\.\\d+)(?:[eE][+-]?\\d+)?',

  /**
   * A dictionary of conversion factors for angular CSS measurements.
   * @memberof geo.util
   */
  cssAngleUnitsBase: { deg: 360, grad: 400, rad: 2 * Math.PI, turn: 1 },

  /**
   * The predefined CSS colors.  See
   * {@link https://drafts.csswg.org/css-color}.
   *
   * @memberof geo.util
   */
  cssColors: {
    aliceblue: 0xf0f8ff,
    antiquewhite: 0xfaebd7,
    aqua: 0x00ffff,
    aquamarine: 0x7fffd4,
    azure: 0xf0ffff,
    beige: 0xf5f5dc,
    bisque: 0xffe4c4,
    black: 0x000000,
    blanchedalmond: 0xffebcd,
    blue: 0x0000ff,
    blueviolet: 0x8a2be2,
    brown: 0xa52a2a,
    burlywood: 0xdeb887,
    cadetblue: 0x5f9ea0,
    chartreuse: 0x7fff00,
    chocolate: 0xd2691e,
    coral: 0xff7f50,
    cornflowerblue: 0x6495ed,
    cornsilk: 0xfff8dc,
    crimson: 0xdc143c,
    cyan: 0x00ffff,
    darkblue: 0x00008b,
    darkcyan: 0x008b8b,
    darkgoldenrod: 0xb8860b,
    darkgray: 0xa9a9a9,
    darkgreen: 0x006400,
    darkgrey: 0xa9a9a9,
    darkkhaki: 0xbdb76b,
    darkmagenta: 0x8b008b,
    darkolivegreen: 0x556b2f,
    darkorange: 0xff8c00,
    darkorchid: 0x9932cc,
    darkred: 0x8b0000,
    darksalmon: 0xe9967a,
    darkseagreen: 0x8fbc8f,
    darkslateblue: 0x483d8b,
    darkslategray: 0x2f4f4f,
    darkslategrey: 0x2f4f4f,
    darkturquoise: 0x00ced1,
    darkviolet: 0x9400d3,
    deeppink: 0xff1493,
    deepskyblue: 0x00bfff,
    dimgray: 0x696969,
    dimgrey: 0x696969,
    dodgerblue: 0x1e90ff,
    firebrick: 0xb22222,
    floralwhite: 0xfffaf0,
    forestgreen: 0x228b22,
    fuchsia: 0xff00ff,
    gainsboro: 0xdcdcdc,
    ghostwhite: 0xf8f8ff,
    gold: 0xffd700,
    goldenrod: 0xdaa520,
    gray: 0x808080,
    green: 0x008000,
    greenyellow: 0xadff2f,
    grey: 0x808080,
    honeydew: 0xf0fff0,
    hotpink: 0xff69b4,
    indianred: 0xcd5c5c,
    indigo: 0x4b0082,
    ivory: 0xfffff0,
    khaki: 0xf0e68c,
    lavender: 0xe6e6fa,
    lavenderblush: 0xfff0f5,
    lawngreen: 0x7cfc00,
    lemonchiffon: 0xfffacd,
    lightblue: 0xadd8e6,
    lightcoral: 0xf08080,
    lightcyan: 0xe0ffff,
    lightgoldenrodyellow: 0xfafad2,
    lightgray: 0xd3d3d3,
    lightgreen: 0x90ee90,
    lightgrey: 0xd3d3d3,
    lightpink: 0xffb6c1,
    lightsalmon: 0xffa07a,
    lightseagreen: 0x20b2aa,
    lightskyblue: 0x87cefa,
    lightslategray: 0x778899,
    lightslategrey: 0x778899,
    lightsteelblue: 0xb0c4de,
    lightyellow: 0xffffe0,
    lime: 0x00ff00,
    limegreen: 0x32cd32,
    linen: 0xfaf0e6,
    magenta: 0xff00ff,
    maroon: 0x800000,
    mediumaquamarine: 0x66cdaa,
    mediumblue: 0x0000cd,
    mediumorchid: 0xba55d3,
    mediumpurple: 0x9370db,
    mediumseagreen: 0x3cb371,
    mediumslateblue: 0x7b68ee,
    mediumspringgreen: 0x00fa9a,
    mediumturquoise: 0x48d1cc,
    mediumvioletred: 0xc71585,
    midnightblue: 0x191970,
    mintcream: 0xf5fffa,
    mistyrose: 0xffe4e1,
    moccasin: 0xffe4b5,
    navajowhite: 0xffdead,
    navy: 0x000080,
    oldlace: 0xfdf5e6,
    olive: 0x808000,
    olivedrab: 0x6b8e23,
    orange: 0xffa500,
    orangered: 0xff4500,
    orchid: 0xda70d6,
    palegoldenrod: 0xeee8aa,
    palegreen: 0x98fb98,
    paleturquoise: 0xafeeee,
    palevioletred: 0xdb7093,
    papayawhip: 0xffefd5,
    peachpuff: 0xffdab9,
    peru: 0xcd853f,
    pink: 0xffc0cb,
    plum: 0xdda0dd,
    powderblue: 0xb0e0e6,
    purple: 0x800080,
    rebeccapurple: 0x663399,
    red: 0xff0000,
    rosybrown: 0xbc8f8f,
    royalblue: 0x4169e1,
    saddlebrown: 0x8b4513,
    salmon: 0xfa8072,
    sandybrown: 0xf4a460,
    seagreen: 0x2e8b57,
    seashell: 0xfff5ee,
    sienna: 0xa0522d,
    silver: 0xc0c0c0,
    skyblue: 0x87ceeb,
    slateblue: 0x6a5acd,
    slategray: 0x708090,
    slategrey: 0x708090,
    snow: 0xfffafa,
    springgreen: 0x00ff7f,
    steelblue: 0x4682b4,
    tan: 0xd2b48c,
    teal: 0x008080,
    thistle: 0xd8bfd8,
    tomato: 0xff6347,
    turquoise: 0x40e0d0,
    violet: 0xee82ee,
    wheat: 0xf5deb3,
    white: 0xffffff,
    whitesmoke: 0xf5f5f5,
    yellow: 0xffff00,
    yellowgreen: 0x9acd32
  }
};

///////////////////////////////////////////////////////////////////////////
/*
 * Utility member properties that need to refer to util functions and
 * properties.
 */
///////////////////////////////////////////////////////////////////////////

/**
 * A list of regex and processing functions for color conversions to rgb
 * objects.  Each entry is a {@link geo.util.cssColorConversionRecord}.  In
 * general, these conversions are somewhat more forgiving than the css
 * specification (see {@link https://drafts.csswg.org/css-color/}) in that
 * percentages may be mixed with numbers, and that floating point values are
 * accepted for all numbers.  Commas are optional.  As per the latest draft
 * standard, `rgb` and `rgba` are aliases of each other, as are `hsl` and
 * `hsla`.
 * @name cssColorConversions
 * @property cssColorConversions
 * @memberof geo.util
 */
util.cssColorConversions = [{
  name: 'rgb',
  regex: new RegExp('^\\s*rgba?' + '\\(\\s*(' + util.cssNumberRegex + ')\\s*(%?)\\s*' + ',?\\s*(' + util.cssNumberRegex + ')\\s*(%?)\\s*' + ',?\\s*(' + util.cssNumberRegex + ')\\s*(%?)\\s*' + '([/,]?\\s*(' + util.cssNumberRegex + ')\\s*(%?)\\s*)?' + '\\)\\s*$'),
  process: function process(color, match) {
    color = {
      r: Math.min(1, Math.max(0, +match[1] / (match[2] ? 100 : 255))),
      g: Math.min(1, Math.max(0, +match[3] / (match[4] ? 100 : 255))),
      b: Math.min(1, Math.max(0, +match[5] / (match[6] ? 100 : 255)))
    };
    if (match[7]) {
      color.a = Math.min(1, Math.max(0, +match[8] / (match[9] ? 100 : 1)));
    }
    return color;
  }
}, {
  name: 'hsl',
  regex: new RegExp('^\\s*hsla?' + '\\(\\s*(' + util.cssNumberRegex + ')\\s*(deg|grad|rad|turn)?\\s*' + ',?\\s*(' + util.cssNumberRegex + ')\\s*%\\s*' + ',?\\s*(' + util.cssNumberRegex + ')\\s*%\\s*' + '([/,]?\\s*(' + util.cssNumberRegex + ')\\s*(%?)\\s*)?' + '\\)\\s*$'),
  process: function process(color, match) {
    /* Conversion from https://www.w3.org/TR/2011/REC-css3-color-20110607
     */
    var hue_to_rgb = function hue_to_rgb(m1, m2, h) {
      h = h - Math.floor(h);
      if (h * 6 < 1) {
        return m1 + (m2 - m1) * h * 6;
      }
      if (h * 6 < 3) {
        return m2;
      }
      if (h * 6 < 4) {
        return m1 + (m2 - m1) * (2 / 3 - h) * 6;
      }
      return m1;
    };

    var h = +match[1] / (util.cssAngleUnitsBase[match[2]] || 360),
        s = Math.min(1, Math.max(0, +match[3] / 100)),
        l = Math.min(1, Math.max(0, +match[4] / 100)),
        m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s,
        m1 = l * 2 - m2;
    color = {
      r: hue_to_rgb(m1, m2, h + 1 / 3),
      g: hue_to_rgb(m1, m2, h),
      b: hue_to_rgb(m1, m2, h - 1 / 3)
    };
    if (match[5]) {
      color.a = Math.min(1, Math.max(0, +match[6] / (match[7] ? 100 : 1)));
    }
    return color;
  }
}];

/* Add additional utilities to the main object. */
$.extend(util, throttle, mockVGL);

/***/ }),
/* 4 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "e", function() { return PJD_3PARAM; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "f", function() { return PJD_7PARAM; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "h", function() { return PJD_WGS84; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "g", function() { return PJD_NODATUM; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "l", function() { return SEC_TO_RAD; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "d", function() { return HALF_PI; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "m", function() { return SIXTH; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "j", function() { return RA4; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "k", function() { return RA6; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "b", function() { return EPSLN; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return D2R; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "i", function() { return R2D; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "c", function() { return FORTPI; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "o", function() { return TWO_PI; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "n", function() { return SPI; });
var PJD_3PARAM = 1;
var PJD_7PARAM = 2;
var PJD_WGS84 = 4; // WGS84 or equivalent
var PJD_NODATUM = 5; // WGS84 or equivalent
var SEC_TO_RAD = 4.84813681109535993589914102357e-6;
var HALF_PI = Math.PI/2;
// ellipoid pj_set_ell.c
var SIXTH = 0.1666666666666666667;
/* 1/6 */
var RA4 = 0.04722222222222222222;
/* 17/360 */
var RA6 = 0.02215608465608465608;
var EPSLN = 1.0e-10;
// you'd think you could use Number.EPSILON above but that makes
// Mollweide get into an infinate loop.

var D2R = 0.01745329251994329577;
var R2D = 57.29577951308232088;
var FORTPI = Math.PI/4;
var TWO_PI = Math.PI * 2;
// SPI is slightly greater than Math.PI, so values that exceed the -180..180
// degree range by a tiny amount don't get wrapped. This prevents points that
// have drifted from their original location along the 180th meridian (due to
// floating point error) from changing their sign.
var SPI = 3.14159265359;


/***/ }),
/* 5 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__constants_values__ = __webpack_require__(4);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__sign__ = __webpack_require__(20);




/* harmony default export */ __webpack_exports__["a"] = (function(x) {
  return (Math.abs(x) <= __WEBPACK_IMPORTED_MODULE_0__constants_values__["n" /* SPI */]) ? x : (x - (Object(__WEBPACK_IMPORTED_MODULE_1__sign__["a" /* default */])(x) * __WEBPACK_IMPORTED_MODULE_0__constants_values__["o" /* TWO_PI */]));
});


/***/ }),
/* 6 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/**
 * Common object containing all event types that are provided by the GeoJS
 * API.  Each property contained here is a valid target for event handling
 * via {@link geo.object#geoOn}.  The event object provided to handlers is
 * different for each event type.  Each handler is generally called with the
 * `this` context being the class that caused the event.
 *
 * @namespace
 * @alias geo.event
 * @type {geo.event.base}
 *
 * @example
 * map.geoOn(geo.event.layerAdd, function (event) {
 *   // event is an object with type: {@link geo.event.layerAdd}
 * });
 *
 */
var geo_event = {};

/**
 * All events are sent an object that are an extension of this type.
 *
 * @typedef geo.event.base
 * @type {object}
 * @property {string} event The event type that was triggered.
 * @property {object} geo A universal event object for controlling propagation.
 */

/*
 * Event types
 */

/**
 * Triggered when a layer is added to the map.
 *
 * @event geo.event.layerAdd
 * @type {geo.event.base}
 * @property {geo.map} target The current map.
 * @property {geo.layer} layer The new layer that was added.
 */
geo_event.layerAdd = 'geo_layerAdd';

/**
 * Triggered when a layer is removed from the map.
 *
 * @event geo.event.layerRemove
 * @type {geo.event.base}
 * @property {geo.map} target The current map.
 * @property {geo.layer} layer The old layer that was removed.
 */
geo_event.layerRemove = 'geo_layerRemove';

/**
 * Triggered when the map's zoom level is changed.
 *
 * @event geo.event.zoom
 * @type {geo.event.base}
 * @property {number} zoomLevel New zoom level.
 * @property {geo.screenPosition} screenPosition The screen position of the
 *      mouse pointer.
 */
geo_event.zoom = 'geo_zoom';

/**
 * Triggered when the map is rotated around the current map center (pointing
 * downward so that positive angles are clockwise rotations).
 *
 * @event geo.event.rotate
 * @type {geo.event.base}
 * @property {number} rotation The angle of the rotation in radians.  This is
 *      the map's complete rotation, not a delta.
 * @property {geo.screenPosition} screenPosition The screen position of the
 *      mouse pointer.
 */
geo_event.rotate = 'geo_rotate';

/**
 * Triggered when the map is panned either by user interaction or map
 * transition.
 *
 * @event geo.event.pan
 * @type {geo.event.base}
 * @property {object} screenDelta The number of pixels of the pan.
 * @property {number} screenDelta.x Horizontal pan distance in pixels.
 * @property {number} screenDelta.y Vertical pan distance in pixels.
 */
geo_event.pan = 'geo_pan';

/**
 * Triggered when the map's canvas is resized.
 *
 * @event geo.event.resize
 * @type {geo.event.base}
 * @property {geo.map} target The map that was resized.
 * @property {number} width The new width in pixels.
 * @property {number} height The new height in pixels.
 */
geo_event.resize = 'geo_resize';

/**
 * Triggered on every call to {@link geo.map#draw} before the map is rendered.
 *
 * @event geo.event.draw
 * @type {geo.event.base}
 * @property {geo.map} target The current map.
 */
geo_event.draw = 'geo_draw';

/**
 * Triggered on every call to {@link geo.map#draw} after the map is rendered.
 *
 * @event geo.event.drawEnd
 * @type {geo.event.base}
 * @property {geo.map} target The current map.
 */
geo_event.drawEnd = 'geo_drawEnd';

/**
 * Triggered on every `mousemove` over the map's DOM element unless a click
 * might occur.  The event object extends {@link geo.mouseState}.
 *
 * @event geo.event.mousemove
 * @type {(geo.event.base|geo.mouseState)}
 */
geo_event.mousemove = 'geo_mousemove';

/**
 * Triggered on `mouseup` events that happen soon enough and close enough to a
 * `mousedown` event.  The event object extends {@link geo.mouseState}.
 *
 * @event geo.event.mouseclick
 * @type {(geo.event.base|geo.mouseState)}
 * @property {geo.mouseButtons} buttonsDown The buttons that were down at the
 *      start of the click action.
 */
geo_event.mouseclick = 'geo_mouseclick';

/**
 * Triggered on every `mousemove` during a brushing selection.
 * The event object extends {@link geo.brushSelection}.
 *
 * @event geo.event.brush
 * @type {(geo.event.base|geo.brushSelection)}
 */
geo_event.brush = 'geo_brush';

/**
 * Triggered after a brush selection ends.
 * The event object extends {@link geo.brushSelection}.
 *
 * @event geo.event.brushend
 * @type {(geo.event.base|geo.brushSelection)}
 */
geo_event.brushend = 'geo_brushend';

/**
 * Triggered when a brush selection starts.
 * The event object extends {@link geo.brushSelection}.
 *
 * @event geo.event.brushstart
 * @type {(geo.event.base|geo.brushSelection)}
 */
geo_event.brushstart = 'geo_brushstart';

/**
 * Triggered when brushing results in a selection.
 * The event object extends {@link geo.brushSelection}.
 *
 * @event geo.event.select
 * @type {(geo.event.base|geo.brushSelection)}
 */
geo_event.select = 'geo_select';

/**
 * Triggered when brushing results in a zoom selection.
 * The event object extends {@link geo.brushSelection}.
 *
 * @event geo.event.zoomselect
 * @type {(geo.event.base|geo.brushSelection)}
 */
geo_event.zoomselect = 'geo_zoomselect';

/**
 * Triggered when brushing results in a zoom-out selection.
 * The event object extends {@link geo.brushSelection}.
 *
 * @event geo.event.unzoomselect
 * @type {(geo.event.base|geo.brushSelection)}
 */

geo_event.unzoomselect = 'geo_unzoomselect';

/**
 * Triggered when an action is initiated with mouse down.
 *
 * @event geo.event.actiondown
 * @type {geo.event.base}
 * @property {geo.actionState} state The action state.
 * @property {geo.mouseState} mouse The mouse state.
 * @property {jQuery.Event} event The triggering jQuery event.
 */
geo_event.actiondown = 'geo_actiondown';

/**
 * Triggered when an action is being processed during mouse movement.
 *
 * @event geo.event.actionmove
 * @type {geo.event.base}
 * @property {geo.actionState} state The action state.
 * @property {geo.mouseState} mouse The mouse state.
 * @property {jQuery.Event} event The triggering event.
 */
geo_event.actionmove = 'geo_actionmove';

/**
 * Triggered when an action is ended with a mouse up.
 *
 * @event geo.event.actionup
 * @type {geo.event.base}
 * @property {geo.actionState} state The action state.
 * @property {geo.mouseState} mouse The mouse state.
 * @property {jQuery.Event} event The triggering event.
 */
geo_event.actionup = 'geo_actionup';

/**
 * Triggered when an action results in a selection.
 *
 * @event geo.event.actionselection
 * @type {geo.event.base}
 * @property {geo.actionState} state The action state.
 * @property {geo.mouseState} mouse The mouse state.
 * @property {jQuery.Event} event The triggering event.
 * @property {geo.screenPosition} lowerLeft Lower left of selection in screen
 *      coordinates.
 * @property {geo.screenPosition} upperRight Upper right of selection in screen
 *      coordinates.
 */
geo_event.actionselection = 'geo_actionselection';

/**
 * Triggered when an action is triggered with a mouse wheel event.
 *
 * @event geo.event.actionwheel
 * @type {geo.event.base}
 * @property {geo.actionState} state The action state.
 * @property {geo.mouseState} mouse The mouse state.
 * @property {jQuery.Event} event The triggering event.
 */
geo_event.actionwheel = 'geo_actionwheel';

/**
 * Triggered when an action is triggered via the keyboard.
 *
 * @event geo.event.keyaction
 * @type {geo.event.base}
 * @property {object} move The movement that would happen if the action is
 *      passed through.
 * @property {number} [move.zoomDelta] A change in the zoom level.
 * @property {number} [move.zoom] A new zoom level.
 * @property {number} [move.rotationDelta] A change in the rotation in radians.
 * @property {number} [move.rotation] A new absolute rotation in radians.
 * @property {number} [move.panX] A horizontal shift in display pixels.
 * @property {number} [move.panY] A vertical shift in display pixels.
 * @property {boolean} [move.cancel] Set to `true` to cancel the entire
 *      movement.
 * @property {string} action Action based on key
 * @property {number} factor Factor based on metakeys [0-2].  0 means a small
 *      movement is preferred, 1 a medium movement, and 2 a large movement.
 * @property {jQuery.Event} event The triggering event
 */
geo_event.keyaction = 'geo_keyaction';

/**
 * Triggered before a map navigation animation begins.  Set
 * `event.geo.cancelAnimation` to cancel the animation of the navigation.  This
 * will cause the map to navigate to the target location immediately.  Set
 * `event.geo.cancelNavigation` to cancel the navigation completely.  The
 * transition options can be modified in place.
 *
 * @event geo.event.transitionstart
 * @type {geo.event.base}
 * @property {geo.geoPosition} center The target center.
 * @property {number} zoom The target zoom level.
 * @property {number} duration The duration of the transition in milliseconds.
 * @property {function} ease The easing function.
 */
geo_event.transitionstart = 'geo_transitionstart';

/**
 * Triggered after a map navigation animation ends.
 *
 * @event geo.event.transitionend
 * @type {geo.event.base}
 * @property {geo.geoPosition} center The target center.
 * @property {number} zoom The target zoom level.
 * @property {number} duration The duration of the transition in milliseconds.
 * @property {function} ease The easing function.
 */
geo_event.transitionend = 'geo_transitionend';

/**
 * Triggered if a map navigation animation is canceled.
 *
 * @event geo.event.transitioncancel
 * @type {geo.event.base}
 * @property {geo.geoPosition} center The target center.
 * @property {number} zoom The target zoom level.
 * @property {number} duration The duration of the transition in milliseconds.
 * @property {function} ease The easing function.
 */
geo_event.transitioncancel = 'geo_transitioncancel';

/**
 * Triggered when the parallel projection mode is changes.
 *
 * @event geo.event.parallelprojection
 * @type {geo.event.base}
 * @property {boolean} paralellProjection `true` if parallel projection is
 *      turned on.
 */
geo_event.parallelprojection = 'geo_parallelprojection';

/**
 * This event object provides mouse/keyboard events that can be handled
 * by the features.  This provides a similar interface as core events,
 * but with different names so the events don't interfere.  Subclasses
 * can override this to provide custom events.
 *
 * These events will only be triggered on features which were instantiated
 * with the option 'selectionAPI'.
 * @namespace geo.event.feature
 */
geo_event.feature = {
  /**
   * The event is the feature version of {@link geo.event.mousemove}.  It is
   * fired for each data component of a feature below the mouse when the mouse
   * moves.
   *
   * @event geo.event.feature.mousemove
   * @type {geo.event.base}
   * @property {object} data The feature data the mouse is over.
   * @property {number} index The index of the feature data the mouse is over.
   * @property {object} extra Extra information about the feature and mouse
   *    location.
   * @property {geo.mouseState} mouse The mouse state.
   * @property {number} eventID a monotonically increasing event number.  All
   *    features that the mouse moves over simultaneously will have the same
   *    `eventID`.
   * @property {boolean} top True if this is the topmost data element.
   */
  mousemove: 'geo_feature_mousemove',
  /**
   * This event is fired for each data component of a feature when the mouse is
   * above it.
   *
   * @event geo.event.feature.mouseover
   * @type {geo.event.base}
   * @property {object} data The feature data the mouse is over.
   * @property {number} index The index of the feature data the mouse is over.
   * @property {object} extra Extra information about the feature and mouse
   *    location.
   * @property {geo.mouseState} mouse The mouse state.
   * @property {number} eventID a monotonically increasing event number.  All
   *    features that the mouse goes over simultaneously will have the same
   *    `eventID`.
   * @property {boolean} top True if this is the topmost data element.
   */
  mouseover: 'geo_feature_mouseover',
  /**
   * This event is fired when the mouse changes either which feature components
   * or the order of the feature components that it is over.
   *
   * @event geo.event.feature.mouseover_order
   * @type {geo.event.base}
   * @property {geo.feature} feature The feature.
   * @property {geo.mouseState} mouse The mouse state.
   * @proeprty {geo.feature.searchResult} over A list of feature components
   *    that the mouse is over.
   * @proeprty {number[]} The indices of the data components that the mouse
   *    was over before this event.
   */
  mouseover_order: 'geo_feature_mouseover_order',
  /**
   * The event is the feature version of {@link geo.event.mouseout}.a
   * This event is fired for each data component of a feature when the mouse is
   * no longer above it.
   *
   * @event geo.event.feature.mouseout
   * @type {geo.event.base}
   * @property {object} data The feature data the mouse is over.
   * @property {number} index The index of the feature data the mouse is over.
   * @property {object} extra Extra information about the feature and mouse
   *    location.
   * @property {geo.mouseState} mouse The mouse state.
   * @property {number} eventID a monotonically increasing event number.  All
   *    features that the mouse goes over simultaneously will have the same
   *    `eventID`.
   * @property {boolean} top True if this is the topmost data element.
   */
  mouseout: 'geo_feature_mouseout',
  /**
   * This event is fired when mouse is over a new topmost data component of a
   * feature.
   *
   * @event geo.event.feature.mouseon
   * @type {geo.event.base}
   * @property {object} data The feature data the mouse is on.
   * @property {number} index The index of the feature data the mouse is on.
   * @property {geo.mouseState} mouse The mouse state.
   */
  mouseon: 'geo_feature_mouseon',
  /**
   * This event is fired when mouse is no longer has the same topmost data
   * component of a feature.
   *
   * @event geo.event.feature.mouseoff
   * @type {geo.event.base}
   * @property {object} data The feature data the mouse is off.
   * @property {number} index The index of the feature data the mouse is off.
   * @property {geo.mouseState} mouse The mouse state.
   */
  mouseoff: 'geo_feature_mouseoff',
  /**
   * The event is the feature version of {@link geo.event.mouseclick}.
   *
   * @event geo.event.feature.mouseclick
   * @type {geo.event.base}
   * @property {object} data The feature data the mouse is off.
   * @property {number} index The index of the feature data the mouse is off.
   * @property {object} extra Extra information about the feature and mouse
   *    location.
   * @property {geo.mouseState} mouse The mouse state.
   * @property {number} eventID a monotonically increasing event number.  All
   *    features that the mouse clicks simultaneously will have the same
   *    `eventID`.
   * @property {boolean} top True if this is the topmost data element.
   */
  mouseclick: 'geo_feature_mouseclick',
  /**
   * The event contains the `feature`, the `mouse` record, and `over`, the
   * record of data elements that are unrder the mouse.
   *
   * @event geo.event.feature.mouseclick_order
   * @type {geo.event.base}
   * @property {geo.feature} feature The feature that was clicked.
   * @property {geo.mouseState} mouse The mouse state.
   * @proeprty {geo.feature.searchResult} over A list of feature components
   *    that the mouse is over.
   */
  mouseclick_order: 'geo_feature_mouseclick_order',
  /**
   * This event is fired for each data component of a feature under a brush
   * that has just finished its selection.
   *
   * @event geo.event.feature.brushend
   * @type {geo.event.base}
   * @property {object} data The feature data the mouse is over.
   * @property {number} index The index of the feature data the mouse is over.
   * @property {geo.mouseState} mouse The mouse state.
   * @property {geo.brushSelection} brush The current brush selection.
   * @property {number} eventID a monotonically increasing event number.  All
   *    features that the mouse goes over simultaneously will have the same
   *    `eventID`.
   * @property {boolean} top True if this is the topmost data element.
   */
  brushend: 'geo_feature_brushend',
  /**
   * This event is fired for each data component of a feature under an active
   * brush.
   *
   * @event geo.event.feature.brush
   * @type {geo.event.base}
   * @property {object} data The feature data the mouse is over.
   * @property {number} index The index of the feature data the mouse is over.
   * @property {geo.mouseState} mouse The mouse state.
   * @property {geo.brushSelection} brush The current brush selection.
   * @property {number} eventID a monotonically increasing event number.  All
   *    features that the mouse goes over simultaneously will have the same
   *    `eventID`.
   * @property {boolean} top True if this is the topmost data element.
   */
  brush: 'geo_feature_brush'
};

/**
 * These events are triggered by the pixelmap feature.
 * @namespace geo.event.pixelmap
 */
geo_event.pixelmap = {
  /**
   * Report that an image associated with a pixel map has been prepared and
   * rendered once.
   *
   * @event geo.event.pixelmap.prepared
   * @type {geo.event.base}
   * @property {geo.pixelmapFeature} pixelmap The pixelmap object that was
   *    prepared.
   */
  prepared: 'geo_pixelmap_prepared'
};

/**
 * These events are triggered by the map screenshot feature.
 * @namespace geo.event.screenshot
 */
geo_event.screenshot = {
  /**
   * Triggered when a screenshot has been completed.
   *
   * @event geo.event.screenshot.ready
   * @type {geo.event.base}
   * @property {HTMLCanvasElement} canvas The canvas used to take the
   *    screenshot.
   * @property {string|HTMLCanvasElement} screenshot The screenshot as a
   *    dataURL string or the canvas, depending on the screenshot request.
   */
  ready: 'geo_screenshot_ready'
};

/**
 * These events are triggered by the camera when it's internal state is
 * mutated.
 * @namespace geo.event.camera
 */
geo_event.camera = {};

/**
 * Triggered after a general view matrix change (any change in the visible
 * bounds).  This is equivalent to the union of pan and zoom.
 *
 * @event geo.event.camera.view
 * @type {geo.event.base}
 * @property {geo.camera} camera The camera instance.
 */
geo_event.camera.view = 'geo_camera_view';

/**
 * Triggered after a projection change.
 *
 * @event geo.event.camera.projection
 * @type {geo.event.base}
 * @property {geo.camera} camera The camera instance.
 * @property {string} type The projection type, either `'perspective'` or
 *      `'parallel'`.
 */
geo_event.camera.projection = 'geo_camera_projection';

/**
 * Triggered after a viewport change.
 *
 * @event geo.event.camera.viewport
 * @type {geo.event.base}
 * @property {geo.camera} camera The camera instance.
 * @property {geo.screenSize} viewport The new viewport size.
 */
geo_event.camera.viewport = 'geo_camera_viewport';

/**
 * These events are triggered by the annotation layer.
 * @namespace geo.event.annotation
 */
geo_event.annotation = {};

/**
 * Triggered when an annotation has been added.
 *
 * @event geo.event.annotation.add
 * @type {geo.event.base}
 * @property {geo.annotation} annotation The annotation that was added.
 */
geo_event.annotation.add = 'geo_annotation_add';

/**
 * Triggered when an annotation is about to be added.
 *
 * @event geo.event.annotation.add_before
 * @type {geo.event.base}
 * @property {geo.annotation} annotation The annotation that will be added.
 */
geo_event.annotation.add_before = 'geo_annotation_add_before';

/**
 * Triggered when an annotation has been altered.  This is currently only
 * triggered when updating existing annotations via the geojson function.
 *
 * @event geo.event.annotation.update
 * @type {geo.event.base}
 * @property {geo.annotation} annotation The annotation that was altered.
 */
geo_event.annotation.update = 'geo_annotation_update';

/**
 * Triggered when an annotation's coordinates have been updated.
 *
 * @event geo.event.annotation.coordinates
 * @type {geo.event.base}
 * @property {geo.annotation} annotation The annotation that was altered.
 */
geo_event.annotation.coordinates = 'geo_annotation_coordinates';

/**
 * Triggered when an annotation's edit handle is selected or released.
 *
 * @event geo.event.annotation.select_edit_handle
 * @type {geo.event.base}
 * @property {geo.annotation} annotation The annotation that has an edit handle
 *   selected or unselected.
 * @property {object} handle Information on the edit handle.
 * @property {boolean} enable Truthy if the handle was enabled, falsy if
 *   disabled.
 */
geo_event.annotation.select_edit_handle = 'geo_annotation_select_edit_handle';

/**
 * Triggered when an action is performed on an annotation's edit handle.
 *
 * @event geo.event.annotation.edit_action
 * @type {geo.event.base}
 * @property {geo.annotation} annotation The annotation that has an edit handle
 *   selected or unselected.
 * @property {object} handle Information on the edit handle.
 * @property {boolean} action The edit action, typically one of
 *  {@link geo.event.actiondown}, {@link geo.event.actionmove},
 *  {@link geo.event.actionup}.
 */
geo_event.annotation.edit_action = 'geo_annotation_edit_action';

/**
 * Triggered when an annotation has been removed.
 *
 * @event geo.event.annotation.remove
 * @type {geo.event.base}
 * @property {geo.annotation} annotation The annotation that was removed.
 */
geo_event.annotation.remove = 'geo_annotation_remove';

/**
 * Triggered when an annotation's state changes.
 *
 * @event geo.event.annotation.state
 * @type {geo.event.base}
 * @property {geo.annotation} annotation The annotation that changed.
 */
geo_event.annotation.state = 'geo_annotation_state';

/**
 * Triggered when the annotation mode is changed.
 *
 * @event geo.event.annotation.mode
 * @type {geo.event.base}
 * @property {string?} mode The new annotation mode.  This is one of the values
 *      from {@link geo.annotation.state}.
 * @property {string?} oldMode The annotation mode before this change.  This is
 *      one of the values from {@link geo.annotation.state}.
 */
geo_event.annotation.mode = 'geo_annotation_mode';

module.exports = geo_event;

/***/ }),
/* 7 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var proj4 = __webpack_require__(58);
proj4 = proj4.__esModule ? proj4.default : proj4;
/* Ensure all projections in proj4 are included. */
var projections = __webpack_require__(166);
projections.keys().forEach(function (key) {
  proj4.Proj.projections.add(projections(key));
});
var util = __webpack_require__(3);

var transformCache = {};
/* Up to maxTransformCacheSize squared might be cached.  When the maximum cache
 * size is reached, the cache is completely emptied.  Since we probably won't
 * be rapidly switching between a large number of transforms, this is adequate
 * simple behavior. */
var maxTransformCacheSize = 10;

/**
 * This purpose of this class is to provide a generic interface for computing
 * coordinate transformationss.  The interface is taken from the proj4js,
 * which also provides the geospatial projection implementation.  The
 * interface is intentionally simple to allow for custom, non-geospatial use
 * cases. For further details, see http://proj4js.org/
 *
 * The default transforms lat/long coordinates into web mercator
 * for use with standard tile sets.
 *
 * This class is intended to be extended in the future to support 2.5 and 3
 * dimensional transformations.  The forward/inverse methods take optional
 * z values that are ignored in current mapping context, but will in the
 * future perform more general 3D transformations.
 *
 * @class
 * @alias geo.transform
 * @param {object} options Constructor options
 * @param {string} options.source A proj4 string for the source projection
 * @param {string} options.target A proj4 string for the target projection
 * @returns {geo.transform}
 */
var transform = function transform(options) {
  'use strict';

  if (!(this instanceof transform)) {
    options = options || {};
    if (!(options.source in transformCache)) {
      if (Object.size(transformCache) >= maxTransformCacheSize) {
        transformCache = {};
      }
      transformCache[options.source] = {};
    }
    if (!(options.target in transformCache[options.source])) {
      if (Object.size(transformCache[options.source]) >= maxTransformCacheSize) {
        transformCache[options.source] = {};
      }
      transformCache[options.source][options.target] = new transform(options);
    }
    return transformCache[options.source][options.target];
  }

  var m_this = this,
      m_proj,
      // The raw proj4js object
  m_source,
      // The source projection
  m_target; // The target projection

  /**
   * Generate the internal proj4 object.
   * @private
   */
  function generate_proj4() {
    m_proj = new proj4(m_this.source(), m_this.target());
  }

  /**
   * Get/Set the source projection.
   *
   * @param {string} [arg] The new source projection.  If `undefined`, return
   *    the current source projection.
   * @returns {string|this} The current source projection if it was queried,
   *    otherwise the current transform object.
   */
  this.source = function (arg) {
    if (arg === undefined) {
      return m_source || 'EPSG:4326';
    }
    m_source = arg;
    generate_proj4();
    return m_this;
  };

  /**
   * Get/Set the target projection.
   *
   * @param {string} [arg] The new target projection.  If `undefined`, return
   *    the current target projection.
   * @returns {string|this} The current target projection if it was queried,
   *    otherwise the current transform object.
   */
  this.target = function (arg) {
    if (arg === undefined) {
      return m_target || 'EPSG:3857';
    }
    m_target = arg;
    generate_proj4();
    return m_this;
  };

  /**
   * Perform a forward transformation (source -> target).
   * @protected
   *
   * @param {geo.geoPosition} point The point in source coordinates.
   * @returns {geo.geoPosition} A point object in the target coordinates.
   */
  this._forward = function (point) {
    var pt = m_proj.forward(point);
    pt.z = point.z || 0;
    return pt;
  };

  /**
   * Perform an inverse transformation (target -> source).
   * @protected
   *
   * @param {geo.geoPosition} point The point in target coordinates.
   * @returns {geo.geoPosition} A point object in the source coordinates.
   */
  this._inverse = function (point) {
    var pt = m_proj.inverse(point);
    pt.z = point.z || 0;
    return pt;
  };

  /**
   * Perform a forward transformation (source -> target) in place.
   * @protected
   *
   * @param {geo.geoPosition|geo.geoPosition[]} point The point coordinates
   *    or array of points in source coordinates.
   * @returns {geo.geoPosition|geo.geoPosition[]} A point object or array in
   *    the target coordinates.
   */
  this.forward = function (point) {
    if (Array.isArray(point)) {
      return point.map(m_this._forward);
    }
    return m_this._forward(point);
  };

  /**
   * Perform an inverse transformation (target -> source) in place.
   * @protected
   *
   * @param {geo.geoPosition|geo.geoPosition[]} point The point coordinates
   *    or array of points in target coordinates.
   * @returns {geo.geoPosition|geo.geoPosition[]} A point object or array in
   *    the source coordinates.
   */
  this.inverse = function (point) {
    if (Array.isArray(point)) {
      return point.map(m_this._inverse);
    }
    return m_this._inverse(point);
  };

  // Set defaults given by the constructor
  options = options || {};
  try {
    this.source(options.source);
  } catch (err) {
    console.error('Can\'t use transform source: ' + options.source);
    this.source('EPSG:4326');
  }
  try {
    this.target(options.target);
  } catch (err) {
    console.error('Can\'t use transform target: ' + options.target);
    this.target('EPSG:3857');
  }

  return this;
};

/**
 * Contains a reference to `proj4.defs`.  The functions serves two
 * purposes.
 *
 *   1. It is a key value mapping of all loaded projection definitions
 *   2. It is a function that will add additional definitions.
 *
 * See:
 *   http://proj4js.org/
 */
transform.defs = proj4.defs;

/**
 * Look up a projection definition from epsg.io.
 * For the moment, we only handle `EPSG` codes.
 *
 * @param {string} projection A projection alias (e.g. EPSG:4326)
 * @returns {promise} Resolves with the proj4 definition
 */
transform.lookup = function (projection) {
  var $ = __webpack_require__(2);
  var code,
      defer = $.Deferred(),
      parts;

  if (proj4.defs.hasOwnProperty(projection)) {
    return defer.resolve(proj4.defs[projection]);
  }

  parts = projection.split(':');
  if (parts.length !== 2 || parts[0].toUpperCase() !== 'EPSG') {
    return defer.reject('Invalid projection code').promise();
  }
  code = parts[1];

  return $.ajax({
    url: 'http://epsg.io/?q=' + code + '&format=json'
  }).done(function (data) {
    var result = (data.results || [])[0];
    if (!result || !result.proj4) {
      return defer.reject(data).promise();
    }

    proj4.defs(projection, result.proj4);
    return $.when(proj4.defs[projection]);
  });
};

/**
 * Transform an array of coordinates from one projection into another.  The
 * transformation may occur in place (modifying the input coordinate array),
 * depending on the input format.  The coordinates can be an object with x,
 * y, and (optionally z) or an array of 2 or 3 values, or an array of either
 * of those, or a single flat array with 2 or 3 components per coordinate.
 * Arrays are always modified in place.  Individual point objects are not
 * altered; new point objects are returned unless no transform is needed.
 *
 * @param {string} srcPrj The source projection.
 * @param {string} tgtPrj The destination projection.
 * @param {geoPosition|geoPosition[]|number[]} coordinates An array of
 *      coordinate objects.  These may be in object or array form, or a flat
 *      array.
 * @param {number} numberOfComponents For flat arrays, either 2 or 3.
 * @returns {geoPosition|geoPosition[]|number[]} The transformed coordinates.
 */
transform.transformCoordinates = function (srcPrj, tgtPrj, coordinates, numberOfComponents) {
  'use strict';

  if (srcPrj === tgtPrj) {
    return coordinates;
  }

  var trans = transform({ source: srcPrj, target: tgtPrj }),
      output;
  if (util.isObject(coordinates) && 'x' in coordinates && 'y' in coordinates) {
    output = trans.forward({ x: +coordinates.x, y: +coordinates.y, z: +coordinates.z || 0 });
    if ('z' in coordinates) {
      return output;
    }
    return { x: output.x, y: output.y };
  }
  if (Array.isArray(coordinates) && coordinates.length === 1 && util.isObject(coordinates[0]) && 'x' in coordinates[0] && 'y' in coordinates[0]) {
    output = trans.forward({ x: +coordinates[0].x, y: +coordinates[0].y, z: +coordinates[0].z || 0 });
    if ('z' in coordinates[0]) {
      return [output];
    }
    return [{ x: output.x, y: output.y }];
  }
  return transform.transformCoordinatesArray(trans, coordinates, numberOfComponents);
};

/**
 * Transform an array of coordinates from one projection into another.  The
 * transformation may occur in place (modifying the input coordinate array),
 * depending on the input format.  The coordinates can be an array of 2 or 3
 * values, or an array of either of those, or a single flat array with 2 or 3
 * components per coordinate.  The array is modified in place.
 *
 * @param {transform} trans The transformation object.
 * @param {geoPosition[]|number[]} coordinates An array of coordinate
 *      objects or a flat array.
 * @param {number} numberOfComponents For flat arrays, either 2 or 3.
 * @returns {geoPosition[]|number[]} The transformed coordinates
 */
transform.transformCoordinatesArray = function (trans, coordinates, numberOfComponents) {
  var i, count, offset, xAcc, yAcc, zAcc, writer, output, projPoint;

  // Default Z accessor
  zAcc = function zAcc() {
    return 0.0;
  };

  // Helper methods
  function handleArrayCoordinates() {
    if (Array.isArray(coordinates[0])) {
      if (coordinates[0].length === 2) {
        xAcc = function xAcc(index) {
          return +coordinates[index][0];
        };
        yAcc = function yAcc(index) {
          return +coordinates[index][1];
        };
        writer = function writer(index, x, y) {
          output[index] = [x, y];
        };
      } else if (coordinates[0].length === 3) {
        xAcc = function xAcc(index) {
          return +coordinates[index][0];
        };
        yAcc = function yAcc(index) {
          return +coordinates[index][1];
        };
        zAcc = function zAcc(index) {
          return +coordinates[index][2];
        };
        writer = function writer(index, x, y, z) {
          output[index] = [x, y, z];
        };
      } else {
        throw new Error('Invalid coordinates. Requires two or three components per array');
      }
    } else {
      if (coordinates.length === 2) {
        offset = 2;

        xAcc = function xAcc(index) {
          return +coordinates[index * offset];
        };
        yAcc = function yAcc(index) {
          return +coordinates[index * offset + 1];
        };
        writer = function writer(index, x, y) {
          output[index] = x;
          output[index + 1] = y;
        };
      } else if (coordinates.length === 3) {
        offset = 3;

        xAcc = function xAcc(index) {
          return +coordinates[index * offset];
        };
        yAcc = function yAcc(index) {
          return +coordinates[index * offset + 1];
        };
        zAcc = function zAcc(index) {
          return +coordinates[index * offset + 2];
        };
        writer = function writer(index, x, y, z) {
          output[index] = x;
          output[index + 1] = y;
          output[index + 2] = z;
        };
      } else if (numberOfComponents) {
        if (numberOfComponents === 2 || numberOfComponents === 3) {
          offset = numberOfComponents;

          xAcc = function xAcc(index) {
            return +coordinates[index];
          };
          yAcc = function yAcc(index) {
            return +coordinates[index + 1];
          };
          if (numberOfComponents === 2) {
            writer = function writer(index, x, y) {
              output[index] = x;
              output[index + 1] = y;
            };
          } else {
            zAcc = function zAcc(index) {
              return +coordinates[index + 2];
            };
            writer = function writer(index, x, y, z) {
              output[index] = x;
              output[index + 1] = y;
              output[index + 2] = z;
            };
          }
        } else {
          throw new Error('Number of components should be two or three');
        }
      } else {
        throw new Error('Invalid coordinates');
      }
    }
  }

  // Helper methods
  function handleObjectCoordinates() {
    if (coordinates[0] && 'x' in coordinates[0] && 'y' in coordinates[0]) {
      xAcc = function xAcc(index) {
        return +coordinates[index].x;
      };
      yAcc = function yAcc(index) {
        return +coordinates[index].y;
      };

      if ('z' in coordinates[0]) {
        zAcc = function zAcc(index) {
          return +coordinates[index].z;
        };
        writer = function writer(index, x, y, z) {
          output[i] = { x: x, y: y, z: z };
        };
      } else {
        writer = function writer(index, x, y) {
          output[index] = { x: x, y: y };
        };
      }
    } else {
      throw new Error('Invalid coordinates');
    }
  }

  if (Array.isArray(coordinates)) {
    output = [];
    output.length = coordinates.length;
    count = coordinates.length;

    if (!coordinates.length) {
      return output;
    }
    if (Array.isArray(coordinates[0]) || util.isObject(coordinates[0])) {
      offset = 1;

      if (Array.isArray(coordinates[0])) {
        handleArrayCoordinates();
      } else if (util.isObject(coordinates[0])) {
        handleObjectCoordinates();
      }
    } else {
      handleArrayCoordinates();
    }
  } else {
    throw new Error('Coordinates are not valid');
  }

  for (i = 0; i < count; i += offset) {
    projPoint = trans.forward({ x: xAcc(i), y: yAcc(i), z: zAcc(i) });
    writer(i, projPoint.x, projPoint.y, projPoint.z);
  }
  return output;
};

/**
 * Apply an affine transformation consisting of a translation then a scaling
 * to the given coordinate array.  Note, the transformation occurs in place
 * so the input coordinate object are mutated.
 *
 * @param {object} def
 * @param {geo.geoPosition} def.origin The transformed origin
 * @param {object} def.scale The transformed scale factor.  This is an object
 *  with `x`, `y`, and `z` parameters.
 * @param {geo.geoPosition[]} coords An array of coordinate objects.
 * @returns {geo.geoPosition[]} The transformed coordinates.
 */
transform.affineForward = function (def, coords) {
  'use strict';

  var i,
      origin = def.origin,
      scale = def.scale || { x: 1, y: 1, z: 1 };
  for (i = 0; i < coords.length; i += 1) {
    coords[i].x = (coords[i].x - origin.x) * scale.x;
    coords[i].y = (coords[i].y - origin.y) * scale.y;
    coords[i].z = ((coords[i].z || 0) - (origin.z || 0)) * scale.z;
  }
  return coords;
};

/**
 * Apply an inverse affine transformation which is the inverse to
 * {@link geo.transform.affineForward}.  Note, the transformation occurs in
 * place so the input coordinate object are mutated.
 *
 * @param {object} def
 * @param {geo.geoPosition} def.origin The transformed origin
 * @param {object} def.scale The transformed scale factor.  This is an object
 *  with `x`, `y`, and `z` parameters.
 * @param {geo.geoPosition[]} coords An array of coordinate objects.
 * @returns {geo.geoPosition[]} The transformed coordinates.
 */
transform.affineInverse = function (def, coords) {
  'use strict';

  var i,
      origin = def.origin,
      scale = def.scale || { x: 1, y: 1, z: 1 };
  for (i = 0; i < coords.length; i += 1) {
    coords[i].x = coords[i].x / scale.x + origin.x;
    coords[i].y = coords[i].y / scale.y + origin.y;
    coords[i].z = (coords[i].z || 0) / scale.z + (origin.z || 0);
  }
  return coords;
};

/**
 * Compute the distance on the surface on a sphere.  The sphere is the major
 * radius of a specified ellipsoid.  Altitude is ignored.
 *
 * @param {geo.geoPosition} pt1 The first point.
 * @param {geo.geoPosition} pt2 The second point.
 * @param {string|geo.transform} [gcs] `undefined` to use the same gcs as the
 *    ellipsoid, otherwise the gcs of the points.
 * @param {string|geo.transform} [baseGcs='EPSG:4326'] the gcs of the
 *    ellipsoid.
 * @param {object} [ellipsoid=proj4.WGS84] An object with at least `a` and one
 *    of `b`, `f`, or `rf` (1 / `f`) -- this works with  proj4 ellipsoid
 *    definitions.
 * @param {number} [maxIterations=100] Maximum number of iterations to use
 *    to test convergence.
 * @returns {number} The distance in meters (or whatever units the ellipsoid
 *    was specified in.
 */
transform.sphericalDistance = function (pt1, pt2, gcs, baseGcs, ellipsoid) {
  baseGcs = baseGcs || 'EPSG:4326';
  ellipsoid = ellipsoid || proj4.WGS84;
  gcs = gcs || baseGcs;
  if (gcs !== baseGcs) {
    var pts = transform.transformCoordinates(gcs, baseGcs, [pt1, pt2]);
    pt1 = pts[0];
    pt2 = pts[1];
  }
  // baseGcs must be in degrees or this will be wrong
  var phi1 = pt1.y * Math.PI / 180,
      phi2 = pt2.y * Math.PI / 180,
      lambda = (pt2.x - pt1.x) * Math.PI / 180,
      sinphi1 = Math.sin(phi1),
      cosphi1 = Math.cos(phi1),
      sinphi2 = Math.sin(phi2),
      cosphi2 = Math.cos(phi2);
  var sigma = Math.atan2(Math.pow(Math.pow(cosphi2 * Math.sin(lambda), 2) + Math.pow(cosphi1 * sinphi2 - sinphi1 * cosphi2 * Math.cos(lambda), 2), 0.5), sinphi1 * sinphi2 + cosphi1 * cosphi2 * Math.cos(lambda));
  return ellipsoid.a * sigma;
};

/**
 * Compute the Vincenty distance on the surface on an ellipsoid.  Altitude is
 * ignored.
 *
 * @param {geo.geoPosition} pt1 The first point.
 * @param {geo.geoPosition} pt2 The second point.
 * @param {string|geo.transform} [gcs] `undefined` to use the same gcs as the
 *    ellipsoid, otherwise the gcs of the points.
 * @param {string|geo.transform} [baseGcs='EPSG:4326'] the gcs of the
 *    ellipsoid.
 * @param {object} [ellipsoid=proj4.WGS84] An object with at least `a` and one
 *    of `b`, `f`, or `rf` (1 / `f`) -- this works with  proj4 ellipsoid
 *    definitions.
 * @param {number} [maxIterations=100] Maximum number of iterations to use
 *    to test convergence.
 * @returns {object} An object with `distance` in meters (or whatever units the
 *    ellipsoid was specified in), `alpha1` and `alpha2`, the azimuths at the
 *    two points in radians.  The result may be `undefined` if the formula
 *    fails to converge, which can happen near antipodal points.
 */
transform.vincentyDistance = function (pt1, pt2, gcs, baseGcs, ellipsoid, maxIterations) {
  baseGcs = baseGcs || 'EPSG:4326';
  ellipsoid = ellipsoid || proj4.WGS84;
  maxIterations = maxIterations || 100;
  gcs = gcs || baseGcs;
  if (gcs !== baseGcs) {
    var pts = transform.transformCoordinates(gcs, baseGcs, [pt1, pt2]);
    pt1 = pts[0];
    pt2 = pts[1];
  }
  var a = ellipsoid.a,
      b = ellipsoid.b || ellipsoid.a * (1.0 - (ellipsoid.f || 1.0 / ellipsoid.rf)),
      f = ellipsoid.f || (ellipsoid.rf ? 1.0 / ellipsoid.rf : 1.0 - b / a),

  // baseGcs must be in degrees or this will be wrong
  phi1 = pt1.y * Math.PI / 180,
      phi2 = pt2.y * Math.PI / 180,
      L = ((pt2.x - pt1.x) % 360 + 360) % 360 * Math.PI / 180,
      U1 = Math.atan((1 - f) * Math.tan(phi1)),
      // reduced latitude
  U2 = Math.atan((1 - f) * Math.tan(phi2)),
      sinU1 = Math.sin(U1),
      cosU1 = Math.cos(U1),
      sinU2 = Math.sin(U2),
      cosU2 = Math.cos(U2),
      lambda = L,
      lastLambda = L + Math.PI * 2,
      sinSigma,
      cosSigma,
      sigma,
      sinAlpha,
      cos2alpha,
      cos2sigmasubm,
      C,
      u2,
      A,
      B,
      deltaSigma,
      iter;
  if (phi1 === phi2 && !L) {
    return {
      distance: 0,
      alpha1: 0,
      alpha2: 0
    };
  }
  for (iter = maxIterations; iter > 0 && Math.abs(lambda - lastLambda) > 1e-12; iter -= 1) {
    sinSigma = Math.pow(Math.pow(cosU2 * Math.sin(lambda), 2) + Math.pow(cosU1 * sinU2 - sinU1 * cosU2 * Math.cos(lambda), 2), 0.5);
    cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * Math.cos(lambda);
    sigma = Math.atan2(sinSigma, cosSigma);
    sinAlpha = cosU1 * cosU2 * Math.sin(lambda) / sinSigma;
    cos2alpha = 1 - Math.pow(sinAlpha, 2);
    // cos2alpha is zero only when phi1 and phi2 are nearly zero.  In this
    // case, sinU1 and sinU2 are nearly zero and the the second term can be
    // dropped
    cos2sigmasubm = cosSigma - (cos2alpha ? 2 * sinU1 * sinU2 / cos2alpha : 0);
    C = f / 16 * cos2alpha * (4 + f * (4 - 3 * cos2alpha));
    lastLambda = lambda;
    lambda = L + (1 - C) * f * sinAlpha * (sigma + C * sinSigma * (cos2sigmasubm + C * cosSigma * (-1 + 2 * Math.pow(cos2sigmasubm, 2))));
  }
  if (!iter) {
    // failure to converge
    return;
  }
  u2 = cos2alpha * (a * a - b * b) / (b * b);
  A = 1 + u2 / 16384 * (4096 + u2 * (-768 + u2 * (320 - 175 * u2)));
  B = u2 / 1024 * (256 + u2 * (-128 + u2 * (74 - 47 * u2)));
  deltaSigma = B * sinSigma * (cos2sigmasubm + B / 4 * (cosSigma * (-1 + 2 * Math.pow(cos2sigmasubm, 2)) - B / 6 * cos2sigmasubm * (-3 + 4 * sinSigma * sinSigma) * (-3 + 4 * Math.pow(cos2sigmasubm, 2))));
  return {
    distance: b * A * (sigma - deltaSigma),
    alpha1: Math.atan2(cosU2 * Math.sin(lambda), cosU1 * sinU2 - sinU1 * cosU2 * Math.cos(lambda)),
    alpha2: Math.atan2(cosU1 * Math.sin(lambda), -sinU1 * cosU2 + cosU1 * sinU2 * Math.cos(lambda))
  };
};

/* Expose proj4 to make it easier to debug */
transform.proj4 = proj4;

module.exports = transform;

/***/ }),
/* 8 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };

var $ = __webpack_require__(2);
var inherit = __webpack_require__(0);
var sceneObject = __webpack_require__(13);
var timestamp = __webpack_require__(10);
var geo_event = __webpack_require__(6);

/**
 * General specification for features.
 *
 * @typedef {object} geo.feature.spec
 * @property {geo.layer} [layer] the parent layer associated with the feature.
 * @property {boolean} [selectionAPI=false] If truthy, enable selection events
 *   on the feature.  Selection events are those in {@link geo.event.feature}.
 *   They can be bound via a call like
 *   ```
 *   feature.geoOn(geo.event.feature.mousemove, function (evt) {
 *     // do something with the feature
 *   });
 *   ```
 *   where the handler is passed a {@link geo.event.feature} object.
 * @property {boolean} [visible=true] If truthy, show the feature.  If falsy,
 *   hide the feature and do not allow interaction with it.
 * @property {string} [gcs] The interface gcs for this feature.  If `undefined`
 *   or `null`, this uses the layer's interface gcs.  This is a string used by
 *   {@link geo.transform}.
 * @property {number} [bin=null] The bin number is used to determine the order
 *   of multiple features on the same layer.  It has no effect except on the
 *   vgl renderer.  A negative value hides the feature without stopping
 *   interaction with it.  Otherwise, more features with higher bin numbers are
 *   drawn above those with lower bin numbers.  If two features have the same
 *   bin number, their order relative to one another is indeterminate and may
 *   be unstable.  A value of `null` will use the current position of the
 *   feature within its parent's list of children as the bin number.
 * @property {geo.renderer?} [renderer] A reference to the renderer used for
 *   the feature.
 * @property {geo.feature.styleSpec} [style] An object that contains style
 *   values for the feature.
 */

/**
 * Style specification for a feature.
 *
 * @typedef {object} geo.feature.styleSpec
 * @property {number|function} [opacity=1] The opacity for the whole feature on
 *    a scale of 0 to 1.
 */

/**
 * @typedef {geo.feature.spec} geo.feature.createSpec
 * @property {string} type A supported feature type.
 * @property {object[]} [data=[]] An array of arbitrary objects used to
 *   construct the feature.  These objects (and their associated indices in the
 *   array) will be passed back to style and attribute accessors provided by
 *   the user.
 */

/**
 * @typedef {geo.event} geo.feature.event
 * @property {number} index The index of the feature within the data array.
 * @property {object} data The data element associated with the indexed
 *   feature.
 * @property {geo.mouseState} mouse The mouse information during the event.
 * @property {object} [extra] Additional information about the feature.  This
 *   is sometimes used to identify a subsection of the feature.
 * @property {number} [eventID] A monotonically increasing number identifying
 *   this feature event loop.  This is provided on
 *   {@link geo.event.feature.mousemove}, {@link geo.event.feature.mouseclick},
 *   {@link geo.event.feature.mouseover}, {@link geo.event.feature.mouseout},
 *   {@link geo.event.feature.brush}, and {@link geo.event.feature.brushend}
 *   events, since each of those can trigger multiple events for one mouse
 *   action (all events triggered by the same mouse action will have the same
 *   `eventID`).
 * @property {boolean} [top] `true` if this is the top-most feature that the
 *   mouse is over.  Only the top-most feature gets
 *   {@link geo.event.feature.mouseon} events, whereas multiple features can
 *   get other events.
 */

/**
 * @typedef {object} geo.feature.searchResult
 * @property {object[]} found A list of elements from the data array that were
 *   found by the search.
 * @property {number[]} index A list of the indices of the elements that were
 *   found by the search.
 * @property {object[]} [extra] A list of additional information per found
 *   element.  The information is passed to events without change.
 */

/**
 * Create a new instance of class feature.
 *
 * @class
 * @alias geo.feature
 * @extends geo.sceneObject
 * @param {geo.feature.spec} [arg] A feature specification.
 * @returns {geo.feature}
 */
var feature = function feature(arg) {
  'use strict';

  if (!(this instanceof feature)) {
    return new feature(arg);
  }
  sceneObject.call(this);

  var util = __webpack_require__(3);

  /**
   * @private
   */
  arg = arg || {};

  var m_this = this,
      s_exit = this._exit,
      m_ready,
      m_selectionAPI = arg.selectionAPI === undefined ? false : !!arg.selectionAPI,
      m_style = {},
      m_layer = arg.layer === undefined ? null : arg.layer,
      m_gcs = arg.gcs,
      m_visible = arg.visible === undefined ? true : arg.visible,
      m_bin = arg.bin === undefined ? null : arg.bin,
      m_renderer = arg.renderer === undefined ? null : arg.renderer,
      m_dataTime = timestamp(),
      m_buildTime = timestamp(),
      m_updateTime = timestamp(),
      m_dependentFeatures = [],
      m_selectedFeatures = [];

  // subclasses can add keys to this for styles that apply to subcomponents of
  // data items, such as individual vertices on lines or polygons.
  this._subfeatureStyles = {};

  /**
   * @property {boolean} ready `true` if this feature has been initialized,
   *    `false` if it was destroyed, `undefined` if it was created but not
   *    initialized.
   * @name geo.feature#ready
   */
  Object.defineProperty(this, 'ready', {
    get: function get() {
      return m_ready;
    }
  });

  /**
   * Private method to bind mouse handlers on the map element.  This does
   * nothing if the selectionAPI is turned off.  Otherwise, it first unbinds
   * any existing handlers and then binds handlers.
   */
  this._bindMouseHandlers = function () {
    // Don't bind handlers for improved performance on features that don't
    // require it.
    if (!m_this.selectionAPI()) {
      return;
    }

    // First unbind to be sure that the handlers aren't bound twice.
    m_this._unbindMouseHandlers();

    m_this.geoOn(geo_event.mousemove, m_this._handleMousemove);
    m_this.geoOn(geo_event.mouseclick, m_this._handleMouseclick);
    m_this.geoOn(geo_event.brushend, m_this._handleBrushend);
    m_this.geoOn(geo_event.brush, m_this._handleBrush);
  };

  /**
   * Private method to unbind mouse handlers on the map element.
   */
  this._unbindMouseHandlers = function () {
    m_this.geoOff(geo_event.mousemove, m_this._handleMousemove);
    m_this.geoOff(geo_event.mouseclick, m_this._handleMouseclick);
    m_this.geoOff(geo_event.brushend, m_this._handleBrushend);
    m_this.geoOff(geo_event.brush, m_this._handleBrush);
  };

  /**
   * Search for features containing the given point.  This should be defined in
   * relevant subclasses.
   *
   * @param {geo.geoPosition} geo Coordinate in interface gcs.
   * @returns {geo.feature.searchResult} An object with a list of features and
   *    feature indices that are located at the specified point.
   */
  this.pointSearch = function (geo) {
    // base class method does nothing
    return {
      index: [],
      found: []
    };
  };

  /**
   * Search for features contained within a rectangilar region.  This should be
   * defined in relevant subclasses.
   *
   * @param {geo.geoPosition} lowerLeft Lower-left corner in gcs coordinates.
   * @param {geo.geoPosition} upperRight Upper-right corner in gcs coordinates.
   * @param {object} [opts] Additional search options.
   * @param {boolean} [opts.partial=false] If truthy, include features that are
   *    partially in the box, otherwise only include features that are fully
   *    within the region.
   * @returns {number[]} A list of features indices that are in the box region.
   */
  this.boxSearch = function (lowerLeft, upperRight, opts) {
    // base class method does nothing
    return [];
  };

  /**
   * Private mousemove handler.  This uses `pointSearch` to determine which
   * features the mouse is over, then fires appropriate events.
   *
   * @fires geo.event.feature.mouseover_order
   * @fires geo.event.feature.mouseover
   * @fires geo.event.feature.mouseout
   * @fires geo.event.feature.mousemove
   * @fires geo.event.feature.mouseoff
   * @fires geo.event.feature.mouseon
   */
  this._handleMousemove = function () {
    var mouse = m_this.layer().map().interactor().mouse(),
        data = m_this.data(),
        over = m_this.pointSearch(mouse.geo),
        newFeatures = [],
        oldFeatures = [],
        lastTop = -1,
        top = -1,
        extra;

    // exit if we have no old or new found entries
    if (!m_selectedFeatures.length && !over.index.length) {
      return;
    }

    extra = over.extra || {};

    // if we are over more than one item, trigger an event that is allowed to
    // reorder the values in evt.over.index.  Event handlers don't have to
    // maintain evt.over.found.  Handlers should not modify evt.over.extra or
    // evt.previous.
    if (over.index.length > 1) {
      m_this.geoTrigger(geo_event.feature.mouseover_order, {
        feature: m_this,
        mouse: mouse,
        previous: m_selectedFeatures,
        over: over
      });
    }

    // Get the index of the element that was previously on top
    if (m_selectedFeatures.length) {
      lastTop = m_selectedFeatures[m_selectedFeatures.length - 1];
    }

    // There are probably faster ways of doing this:
    newFeatures = over.index.filter(function (i) {
      return m_selectedFeatures.indexOf(i) < 0;
    });
    oldFeatures = m_selectedFeatures.filter(function (i) {
      return over.index.indexOf(i) < 0;
    });

    feature.eventID += 1;
    // Fire events for mouse in first.
    newFeatures.forEach(function (i, idx) {
      m_this.geoTrigger(geo_event.feature.mouseover, {
        data: data[i],
        index: i,
        extra: extra[i],
        mouse: mouse,
        eventID: feature.eventID,
        top: idx === newFeatures.length - 1
      }, true);
    });

    feature.eventID += 1;
    // Fire events for mouse out next
    oldFeatures.forEach(function (i, idx) {
      m_this.geoTrigger(geo_event.feature.mouseout, {
        data: data[i],
        index: i,
        mouse: mouse,
        eventID: feature.eventID,
        top: idx === oldFeatures.length - 1
      }, true);
    });

    feature.eventID += 1;
    // Fire events for mouse move last
    over.index.forEach(function (i, idx) {
      m_this.geoTrigger(geo_event.feature.mousemove, {
        data: data[i],
        index: i,
        extra: extra[i],
        mouse: mouse,
        eventID: feature.eventID,
        top: idx === over.index.length - 1
      }, true);
    });

    // Replace the selected features array
    m_selectedFeatures = over.index;

    // Get the index of the element that is now on top
    if (m_selectedFeatures.length) {
      top = m_selectedFeatures[m_selectedFeatures.length - 1];
    }

    if (lastTop !== top) {
      // The element on top changed so we need to fire mouseon/mouseoff
      if (lastTop !== -1) {
        m_this.geoTrigger(geo_event.feature.mouseoff, {
          data: data[lastTop],
          index: lastTop,
          mouse: mouse
        }, true);
      }

      if (top !== -1) {
        m_this.geoTrigger(geo_event.feature.mouseon, {
          data: data[top],
          index: top,
          extra: extra[top],
          mouse: mouse
        }, true);
      }
    }
  };

  /**
   * Clear our tracked selected features.
   *
   * @returns {this}
   */
  this._clearSelectedFeatures = function () {
    m_selectedFeatures = [];
    return m_this;
  };

  /**
   * Private mouseclick handler.  This uses `pointSearch` to determine which
   * features the mouse is over, then fires a click event for each such
   * feature.
   *
   * @param {geo.event} evt The event that triggered this handler.
   * @fires geo.event.feature.mouseclick_order
   * @fires geo.event.feature.mouseclick
   */
  this._handleMouseclick = function (evt) {
    var mouse = m_this.layer().map().interactor().mouse(),
        data = m_this.data(),
        over = m_this.pointSearch(mouse.geo),
        extra = over.extra || {};

    // if we are over more than one item, trigger an event that is allowed to
    // reorder the values in evt.over.index.  Event handlers don't have to
    // maintain evt.over.found.  Handlers should not modify evt.over.extra.
    if (over.index.length > 1) {
      m_this.geoTrigger(geo_event.feature.mouseclick_order, {
        feature: m_this,
        mouse: mouse,
        over: over
      });
    }
    mouse.buttonsDown = evt.buttonsDown;
    feature.eventID += 1;
    over.index.forEach(function (i, idx) {
      m_this.geoTrigger(geo_event.feature.mouseclick, {
        data: data[i],
        index: i,
        extra: extra[i],
        mouse: mouse,
        eventID: feature.eventID,
        top: idx === over.index.length - 1
      }, true);
    });
  };

  /**
   * Private brush handler.  This uses `boxSearch` to determine which features
   * the brush includes, then fires appropriate events.
   *
   * @param {geo.brushSelection} brush The current brush selection.
   * @fires geo.event.feature.brush
   */
  this._handleBrush = function (brush) {
    var idx = m_this.boxSearch(brush.gcs.lowerLeft, brush.gcs.upperRight),
        data = m_this.data();

    feature.eventID += 1;
    idx.forEach(function (i, idx) {
      m_this.geoTrigger(geo_event.feature.brush, {
        data: data[i],
        index: i,
        mouse: brush.mouse,
        brush: brush,
        eventID: feature.eventID,
        top: idx === idx.length - 1
      }, true);
    });
  };

  /**
   * Private brushend handler.  This uses `boxSearch` to determine which
   * features the brush includes, then fires appropriate events.
   *
   * @param {geo.brushSelection} brush The current brush selection.
   * @fires geo.event.feature.brushend
   */
  this._handleBrushend = function (brush) {
    var idx = m_this.boxSearch(brush.gcs.lowerLeft, brush.gcs.upperRight),
        data = m_this.data();

    feature.eventID += 1;
    idx.forEach(function (i, idx) {
      m_this.geoTrigger(geo_event.feature.brushend, {
        data: data[i],
        index: i,
        mouse: brush.mouse,
        brush: brush,
        eventID: feature.eventID,
        top: idx === idx.length - 1
      }, true);
    });
  };

  /**
   * Get/Set style used by the feature.  Styles can be constant values or
   * functions.  If a function, the style is typically called with parameters
   * such as `(dataElement, dataIndex)` or, if the specific style of a feature
   * has a subfeature style, with `(subfeatureElement, subfeatureIndex,
   * dataElement, dataIndex)`.
   *
   * See the <a href="#.styleSpec">style specification
   * <code>styleSpec</code></a> for available styles.
   *
   * @param {string|object} [arg1] If `undefined`, return the current style
   *    object.  If a string and `arg2` is undefined, return the style
   *    associated with the specified key.  If a string and `arg2` is defined,
   *    set the named style to the specified value.  Otherwise, extend the
   *    current style with the values in the specified object.
   * @param {*} [arg2] If `arg1` is a string, the new value for that style.
   * @returns {object|this} Either the entire style object, the value of a
   *    specific style, or the current class instance.
   */
  this.style = function (arg1, arg2) {
    if (arg1 === undefined) {
      return m_style;
    } else if (typeof arg1 === 'string' && arg2 === undefined) {
      return m_style[arg1];
    } else if (arg2 === undefined) {
      m_style = $.extend({}, m_style, arg1);
      m_this.modified();
      return m_this;
    } else {
      m_style[arg1] = arg2;
      m_this.modified();
      return m_this;
    }
  };

  /**
   * A uniform getter that always returns a function even for constant styles.
   * This can also return all defined styles as functions in a single object.
   *
   * @function style_DOT_get
   * @memberof geo.feature
   * @instance
   * @param {string} [key] If defined, return a function for the named style.
   *    Otherwise, return an object with a function for all defined styles.
   * @returns {function|object} Either a function for the named style or an
   *    object with functions for all defined styles.
   */
  this.style.get = function (key) {
    var out;
    if (key === undefined) {
      var all = {},
          k;
      for (k in m_style) {
        if (m_style.hasOwnProperty(k)) {
          all[k] = m_this.style.get(k);
        }
      }
      return all;
    }
    if (key.toLowerCase().match(/color$/)) {
      if (util.isFunction(m_style[key])) {
        out = function out() {
          return util.convertColor(m_style[key].apply(m_this, arguments));
        };
      } else {
        // if the color is not a function, only convert it once
        out = util.ensureFunction(util.convertColor(m_style[key]));
      }
    } else {
      out = util.ensureFunction(m_style[key]);
    }
    return out;
  };

  /**
   * Set style(s) from array(s).  For each style, the array should have one
   * value per data item.  The values are not converted or validated.  Color
   * values should be {@link geo.geoColorObject}s.  If invalid values are given
   * the behavior is undefined.
   *   For some feature styles, if the first entry of an array is itself an
   * array, then each entry of the array is expected to be an array, and values
   * are used from these subarrays.  This allows a style to apply, for
   * instance, per vertex of a data item rather than per data item.
   *
   * @param {string|object} keyOrObject Either the name of a single style or
   *    an object where the keys are the names of styles and the values are
   *    each arrays.
   * @param {array} styleArray If keyOrObject is a string, an array of values
   *    for the style.  If keyOrObject is an object, this parameter is ignored.
   * @param {boolean} [refresh=false] `true` to redraw the feature when it has
   *    been updated.  If an object with styles is passed, the redraw is only
   *    done once.
   * @returns {this} The feature instance.
   */
  this.updateStyleFromArray = function (keyOrObject, styleArray, refresh) {
    if (typeof keyOrObject !== 'string') {
      $.each(keyOrObject, function (key, value) {
        m_this.updateStyleFromArray(key, value);
      });
    } else {
      /* colors are always expected to be objects with r, g, b values, so for
       * any color, make sure we don't have undefined entries. */
      var fallback;
      if (keyOrObject.toLowerCase().match(/color$/)) {
        fallback = { r: 0, g: 0, b: 0 };
      }
      if (!Array.isArray(styleArray)) {
        return m_this;
      }
      if (m_this._subfeatureStyles[keyOrObject]) {
        if (styleArray.length && Array.isArray(styleArray[0])) {
          m_this.style(keyOrObject, function (v, j, d, i) {
            var val = (styleArray[i] || [])[j];
            return val !== undefined ? val : fallback;
          });
        } else {
          m_this.style(keyOrObject, function (v, j, d, i) {
            var val = styleArray[i];
            return val !== undefined ? val : fallback;
          });
        }
      } else {
        m_this.style(keyOrObject, function (d, i) {
          var val = styleArray[i];
          return val !== undefined ? val : fallback;
        });
      }
    }
    if (refresh && m_this.visible()) {
      m_this.draw();
    }
    return m_this;
  };

  /**
   * Get the layer referenced by the feature.
   *
   * @returns {geo.layer} The layer associated with the feature.
   */
  this.layer = function () {
    return m_layer;
  };

  /**
   * Get the renderer used by the feature.
   *
   * @returns {geo.renderer} The renderer used to render the feature.
   */
  this.renderer = function () {
    return m_renderer;
  };

  /**
   * Get/Set the projection of the feature.
   *
   * @param {string?} [val] If `undefined`, return the current gcs.  If
   *    `null`, use the map's interface gcs.  Otherwise, set a new value for
   *    the gcs.
   * @returns {string|this} A string used by {@link geo.transform}.  If the
   *    map interface gcs is in use, that value will be returned.  If the gcs
   *    is set, return the current class instance.
   */
  this.gcs = function (val) {
    if (val === undefined) {
      if ((m_gcs === undefined || m_gcs === null) && m_layer) {
        return m_layer.map().ingcs();
      }
      return m_gcs;
    } else {
      m_gcs = val;
      m_this.modified();
      return m_this;
    }
  };

  /**
   * Convert from the feature's gcs coordinates to display coordinates.
   *
   * @param {geo.geoPosition} c The input coordinate to convert.
   * @returns {geo.screenPosition} Display space coordinates.
   */
  this.featureGcsToDisplay = function (c) {
    var map = m_layer.map();
    c = map.gcsToWorld(c, m_this.gcs());
    c = map.worldToDisplay(c);
    if (m_renderer.baseToLocal) {
      c = m_renderer.baseToLocal(c);
    }
    return c;
  };

  /**
   * Get/Set the visibility of the feature.
   *
   * @param {boolean} [val] A boolean to change the visibility, or `undefined`
   *    to return the visibility.
   * @param {boolean} [direct] If `true`, when getting the visibility,
   *    disregard the visibility of the parent layer, and when setting, refresh
   *    the state regardless of whether it has changed or not.  Otherwise, the
   *    functional visibility is returned, where both the feature and the layer
   *    must be visible for a `true` result.
   * @returns {boolean|this} Either the visibility (if getting) or the feature
   *    (if setting).
   */
  this.visible = function (val, direct) {
    if (val === undefined) {
      if (!direct && m_layer && m_layer.visible && !m_layer.visible()) {
        return false;
      }
      return m_visible;
    }
    if (m_visible !== val || direct) {
      m_visible = val;
      m_this.modified();
      if (m_layer && m_layer.visible && !m_layer.visible()) {
        val = false;
      }
      // bind or unbind mouse handlers on visibility change
      if (val) {
        m_this._bindMouseHandlers();
      } else {
        m_this._unbindMouseHandlers();
      }
      for (var i = 0; i < m_dependentFeatures.length; i += 1) {
        m_dependentFeatures[i].visible(m_visible, direct);
      }
    }
    return m_this;
  };

  /**
   * Get/Set a list of dependent features.  Dependent features have their
   * visibility changed at the same time as the feature.
   *
   * @param {geo.feature[]} [arg] If specified, the new list of dependent
   *    features.  Otherwise, return the current list of dependent features.
   * @returns {geo.feature[]|this} The current list of dependent features or
   *    a reference to `this`.
   */
  this.dependentFeatures = function (arg) {
    if (arg === undefined) {
      return m_dependentFeatures.slice();
    }
    m_dependentFeatures = arg.slice();
    return m_this;
  };

  /**
   * Get/Set bin of the feature.  The bin number is used to determine the order
   * of multiple features on the same layer.  It has no effect except on the
   * vgl renderer.  A negative value hides the feature without stopping
   * interaction with it.  Otherwise, features with higher bin numbers are
   * drawn above those with lower bin numbers.  If two features have the same
   * bin number, their order relative to one another is indeterminate and may
   * be unstable.
   *
   * @param {number} [val] The new bin number.  If `undefined`, return the
   *    current bin number.  If `null`, the bin is dynamically computed based
   *    on order within the parent.  If children are nested, this may not be
   *    what is desired.
   * @returns {number|this} The current bin number or a reference to `this`.
   */
  this.bin = function (val) {
    if (val === undefined) {
      if (m_bin === null) {
        var parent = m_this.parent(),
            idx = parent ? parent.children().indexOf(m_this) : -1;
        return idx >= 0 ? idx : 0;
      }
      return m_bin;
    } else {
      m_bin = val;
      m_this.modified();
      return m_this;
    }
  };

  /**
   * Get/Set timestamp of data change.
   *
   * @param {geo.timestamp} [val] The new data timestamp object or `undefined`
   *    to get the current data timestamp object.
   * @returns {geo.timestamp|this}
   */
  this.dataTime = function (val) {
    if (val === undefined) {
      return m_dataTime;
    } else {
      m_dataTime = val;
      m_this.modified();
      return m_this;
    }
  };

  /**
   * Get/Set timestamp of last time a build happened.
   *
   * @param {geo.timestamp} [val] The new build timestamp object or `undefined`
   *    to get the current build timestamp object.
   * @returns {geo.timestamp|this}
   */
  this.buildTime = function (val) {
    if (val === undefined) {
      return m_buildTime;
    } else {
      m_buildTime = val;
      m_this.modified();
      return m_this;
    }
  };

  /**
   * Get/Set timestamp of last time an update happened.
   *
   * @param {geo.timestamp} [val] The new update timestamp object or
   *    `undefined` to get the current update timestamp object.
   * @returns {geo.timestamp|this}
   */
  this.updateTime = function (val) {
    if (val === undefined) {
      return m_updateTime;
    } else {
      m_updateTime = val;
      m_this.modified();
      return m_this;
    }
  };

  /**
   * Get/Set the data array for the feature.  This is equivalent to getting or
   * setting the `data` style, except that setting the data array via this
   * method updates the data timestamp, whereas setting it via the style does
   * not.
   *
   * @param {array} [data] A new data array or `undefined` to return the
   *    existing array.
   * @returns {array|this}
   */
  this.data = function (data) {
    if (data === undefined) {
      return m_this.style('data') || [];
    } else {
      m_this.style('data', data);
      m_this.dataTime().modified();
      m_this.modified();
      return m_this;
    }
  };

  /**
   * Get/Set if the selection API is enabled for this feature.
   *
   * @param {boolean} [arg] `undefined` to return the selectionAPI state, or a
   *    boolean to change the state.
   * @param {boolean} [direct] If `true`, when getting the selectionAPI state,
   *    disregard the state of the parent layer, and when setting, refresh the
   *    state regardless of whether it has changed or not.
   * @returns {boolean|this} Either the selectionAPI state (if getting) or the
   *    feature (if setting).
   */
  this.selectionAPI = function (arg, direct) {
    if (arg === undefined) {
      if (!direct && m_layer && m_layer.selectionAPI && !m_layer.selectionAPI()) {
        return false;
      }
      return m_selectionAPI;
    }
    arg = !!arg;
    if (arg !== m_selectionAPI || direct) {
      m_selectionAPI = arg;
      m_this._unbindMouseHandlers();
      m_this._bindMouseHandlers();
    }
    return m_this;
  };

  /**
   * If the selectionAPI is on, then setting
   * `this.geoOn(geo.event.feature.mouseover_order, this.mouseOverOrderHighestIndex)`
   * will make it so that the mouseon events prefer the highest index feature.
   *
   * @param {geo.event} evt The event; this should be triggered from
   *    {@link geo.event.feature.mouseover_order}.
   */
  this.mouseOverOrderHighestIndex = function (evt) {
    // sort the found indices.  The last one is the one "on top".
    evt.over.index.sort();
    // this isn't necessary, but ensures that other event handlers have
    // consistent information
    var data = evt.feature.data();
    evt.over.index.forEach(function (di, idx) {
      evt.over.found[idx] = data[di];
    });
  };

  /**
   * Initialize the class instance.  Derived classes should implement this.
   *
   * @param {geo.feature.spec} arg The feature specification.
   */
  this._init = function (arg) {
    if (!m_layer) {
      throw new Error('Feature requires a valid layer');
    }
    m_style = $.extend({}, { 'opacity': 1.0 }, arg.style === undefined ? {} : arg.style);
    m_this._bindMouseHandlers();
    m_ready = true;
  };

  /**
   * Build.
   *
   * Derived classes should implement this.
   */
  this._build = function () {};

  /**
   * Update.
   *
   * Derived classes should implement this.
   */
  this._update = function () {};

  /**
   * Destroy.  Unbind mouse handlers, clear internal variables, and call the
   * parent destroy method.
   *
   * Derived classes should implement this.
   */
  this._exit = function () {
    m_this._unbindMouseHandlers();
    m_selectedFeatures = [];
    m_style = {};
    s_exit();
    m_ready = false;
  };

  this._init(arg);
  return this;
};

/**
 * The most recent {@link geo.feature.event} triggered.
 * @type {number}
 */
feature.eventID = 0;

/**
 * Create a feature.  This defines a general interface; see individual feature
 * types for specific details.
 *
 * @param {geo.layer} layer The layer to add the feature to.
 * @param {geo.feature.spec} spec The feature specification.  At least the
 *      `type` must be specified.
 * @returns {geo.feature|null} The created feature or `null` for a failure.
 */
feature.create = function (layer, spec) {
  'use strict';

  // Check arguments

  if (!(layer instanceof __webpack_require__(24))) {
    console.warn('Invalid layer');
    return null;
  }
  if ((typeof spec === 'undefined' ? 'undefined' : _typeof(spec)) !== 'object') {
    console.warn('Invalid spec');
    return null;
  }
  var type = spec.type;
  var feature = layer.createFeature(type, spec);
  if (!feature) {
    console.warn('Could not create feature type "' + type + '"');
    return null;
  }

  spec.data = spec.data || [];
  return feature.style(spec);
};

inherit(feature, sceneObject);
module.exports = feature;

/***/ }),
/* 9 */
/***/ (function(module, exports, __webpack_require__) {

/* WEBPACK VAR INJECTION */(function(global) {module.exports = global["vgl"] = __webpack_require__(175);
/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(174)))

/***/ }),
/* 10 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var vgl = __webpack_require__(9);
var inherit = __webpack_require__(0);

/**
 * Create a new instance of class timestamp.
 *
 * @class
 * @alias geo.timestamp
 * @extends vgl.timestamp
 * @returns {geo.timestamp}
 */
var timestamp = function timestamp() {
  'use strict';

  if (!(this instanceof timestamp)) {
    return new timestamp();
  }
  vgl.timestamp.call(this);
};

inherit(timestamp, vgl.timestamp);
module.exports = timestamp;

/***/ }),
/* 11 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var inherit = __webpack_require__(0);
var registerRenderer = __webpack_require__(1).registerRenderer;
var renderer = __webpack_require__(22);

/**
 * Create a new instance of class d3Renderer.
 *
 * @class
 * @alias geo.d3.renderer
 * @extends geo.renderer
 * @param {object} arg Options for the renderer.
 * @param {geo.layer} [arg.layer] Layer associated with the renderer.
 * @param {HTMLElement} [arg.canvas] Canvas element associated with the
 *   renderer.
 * @param {boolean} [arg.widget=false] Set to `true` if this is a stand-alone
 *   widget.  If it is not a widget, svg elements are wrapped in a parent
 *   group.
 * @param {HTMLElement} [arg.d3Parent] If specified, the parent for any
 *   rendered objects; otherwise the renderer's layer's main node is used.
 * @returns {geo.d3.d3Renderer}
 */
var d3Renderer = function d3Renderer(arg) {
  'use strict';

  var d3 = d3Renderer.d3;
  var object = __webpack_require__(14);
  var util = __webpack_require__(3);
  var geo_event = __webpack_require__(6);
  var d3Rescale = __webpack_require__(119);

  if (!(this instanceof d3Renderer)) {
    return new d3Renderer(arg);
  }
  renderer.call(this, arg);

  var s_exit = this._exit;

  object.call(this, arg);

  arg = arg || {};

  var m_this = this,
      m_sticky = null,
      m_features = {},
      m_corners = null,
      m_width = null,
      m_height = null,
      m_diagonal = null,
      m_scale = 1,
      m_transform = { dx: 0, dy: 0, rx: 0, ry: 0, rotation: 0 },
      m_renderIds = {},
      m_removeIds = {},
      m_svg = null,
      m_defs = null;

  /**
   * Set attributes to a d3 selection.
   * @private
   * @param {d3Selector} select The d3 selector with the elements to change.
   * @param {object} attrs A map of attributes to set on the elements.
   */
  function setAttrs(select, attrs) {
    var key;
    for (key in attrs) {
      if (attrs.hasOwnProperty(key)) {
        select.attr(key, attrs[key]);
      }
    }
  }

  /**
   * Meta functions for converting from geojs styles to d3.
   * @private
   * @param {object|function} f The style value or function to convert.
   * @param {function} [g] An optional function that returns a boolean; if it
   *    returns false, the style is set to `'none'`.
   * @returns {function} A function for converting styles.
   */
  this._convertColor = function (f, g) {
    f = util.ensureFunction(f);
    g = g || function () {
      return true;
    };
    return function () {
      var c = 'none';
      if (g.apply(m_this, arguments)) {
        c = f.apply(m_this, arguments);
        if (c.hasOwnProperty('r') && c.hasOwnProperty('g') && c.hasOwnProperty('b')) {
          c = d3.rgb(255 * c.r, 255 * c.g, 255 * c.b);
        }
      }
      return c;
    };
  };

  /**
   * Return a function for converting a size in pixels to an appropriate
   * d3 scale.
   * @private
   * @param {object|function} f The style value or function to convert.
   * @returns {function} A function for converting scale.
   */
  this._convertScale = function (f) {
    f = util.ensureFunction(f);
    return function () {
      return f.apply(m_this, arguments) / m_scale;
    };
  };

  /**
   * Set styles to a d3 selection. Ignores unknown style keys.
   * @private
   * @param {d3Selector} select The d3 selector with the elements to change.
   * @param {object} styles Style object associated with a feature.
   */
  function setStyles(select, styles) {
    var key, k, f;
    /**
     * Check if the fill parameter is truthy.
     *
     * @returns {null|'none'} `null` to fill the element, `'none'` to skip
     *  filling it.
     */
    function fillFunc() {
      if (styles.fill.apply(m_this, arguments)) {
        return null;
      } else {
        return 'none';
      }
    }
    /**
     * Check if the stroke parameter is truthy.
     *
     * @returns {null|'none'} `null` to fill the element, `'none'` to skip
     *  filling it.
     */
    function strokeFunc() {
      if (styles.stroke.apply(m_this, arguments)) {
        return null;
      } else {
        return 'none';
      }
    }
    for (key in styles) {
      if (styles.hasOwnProperty(key)) {
        f = null;
        k = null;
        if (key === 'strokeColor') {
          k = 'stroke';
          f = m_this._convertColor(styles[key], styles.stroke);
        } else if (key === 'stroke' && styles[key] && !styles.hasOwnProperty('strokeColor')) {
          k = 'stroke';
          f = strokeFunc;
        } else if (key === 'strokeWidth') {
          k = 'stroke-width';
          f = m_this._convertScale(styles[key]);
        } else if (key === 'strokeOpacity') {
          k = 'stroke-opacity';
          f = styles[key];
        } else if (key === 'fillColor') {
          k = 'fill';
          f = m_this._convertColor(styles[key], styles.fill);
        } else if (key === 'fill' && !styles.hasOwnProperty('fillColor')) {
          k = 'fill';
          f = fillFunc;
        } else if (key === 'fillOpacity') {
          k = 'fill-opacity';
          f = styles[key];
        } else if (key === 'lineCap') {
          k = 'stroke-linecap';
          f = styles[key];
        } else if (key === 'lineJoin') {
          k = 'stroke-linejoin';
          f = styles[key];
        } else if (key === 'miterLimit') {
          k = 'stroke-miterlimit';
          f = styles[key];
        }
        if (k) {
          select.style(k, f);
        }
      }
    }
  }

  /**
   * Get the svg group element associated with this renderer instance, or of a
   * group within the render instance.
   *
   * @private
   * @param {string} [parentId] Optional parent ID name.
   * @returns {d3Selector} Selector with the d3 group.
   */
  function getGroup(parentId) {
    if (parentId) {
      return m_svg.select('.group-' + parentId);
    }
    return m_svg.select('.group-' + m_this._d3id());
  }

  /**
   * Set the initial lat-lon coordinates of the map view.
   * @private
   */
  function initCorners() {
    var layer = m_this.layer(),
        map = layer.map(),
        width = map.size().width,
        height = map.size().height;

    m_width = width;
    m_height = height;
    if (!m_width || !m_height) {
      throw new Error('Map layer has size 0');
    }
    m_diagonal = Math.pow(width * width + height * height, 0.5);
    m_corners = {
      upperLeft: map.displayToGcs({ x: 0, y: 0 }, null),
      lowerRight: map.displayToGcs({ x: width, y: height }, null),
      center: map.displayToGcs({ x: width / 2, y: height / 2 }, null)
    };
  }

  /**
   * Set the translation, scale, and zoom for the current view.
   * @private
   */
  this._setTransform = function () {
    if (!m_corners) {
      initCorners();
    }

    if (!m_sticky) {
      return;
    }

    var layer = m_this.layer();

    var map = layer.map(),
        upperLeft = map.gcsToDisplay(m_corners.upperLeft, null),
        lowerRight = map.gcsToDisplay(m_corners.lowerRight, null),
        center = map.gcsToDisplay(m_corners.center, null),
        group = getGroup(),
        dx,
        dy,
        scale,
        rotation,
        rx,
        ry;

    scale = Math.sqrt(Math.pow(lowerRight.y - upperLeft.y, 2) + Math.pow(lowerRight.x - upperLeft.x, 2)) / m_diagonal;
    // calculate the translation
    rotation = map.rotation();
    rx = -m_width / 2;
    ry = -m_height / 2;
    dx = scale * rx + center.x;
    dy = scale * ry + center.y;

    // set the group transform property
    if (!rotation) {
      dx = Math.round(dx);
      dy = Math.round(dy);
    }
    var transform = 'matrix(' + [scale, 0, 0, scale, dx, dy].join() + ')';
    if (rotation) {
      transform += ' rotate(' + [rotation * 180 / Math.PI, -rx, -ry].join() + ')';
    }
    group.attr('transform', transform);

    // set internal variables
    m_scale = scale;
    m_transform.dx = dx;
    m_transform.dy = dy;
    m_transform.rx = rx;
    m_transform.ry = ry;
    m_transform.rotation = rotation;
  };

  /**
   * Convert from screen pixel coordinates to the local coordinate system
   * in the SVG group element taking into account the transform.
   * @private
   * @param {geo.screenPosition} pt The coordinates to convert.
   * @returns {geo.geoPosition} The converted coordinates.
   */
  this.baseToLocal = function (pt) {
    pt = {
      x: (pt.x - m_transform.dx) / m_scale,
      y: (pt.y - m_transform.dy) / m_scale
    };
    if (m_transform.rotation) {
      var sinr = Math.sin(-m_transform.rotation),
          cosr = Math.cos(-m_transform.rotation);
      var x = pt.x + m_transform.rx,
          y = pt.y + m_transform.ry;
      pt = {
        x: x * cosr - y * sinr - m_transform.rx,
        y: x * sinr + y * cosr - m_transform.ry
      };
    }
    return pt;
  };

  /**
   * Convert from the local coordinate system in the SVG group element
   * to screen pixel coordinates.
   * @private
   * @param {geo.geoPosition} pt The coordinates to convert.
   * @returns {geo.screenPosition} The converted coordinates.
   */
  this.localToBase = function (pt) {
    if (m_transform.rotation) {
      var sinr = Math.sin(m_transform.rotation),
          cosr = Math.cos(m_transform.rotation);
      var x = pt.x + m_transform.rx,
          y = pt.y + m_transform.ry;
      pt = {
        x: x * cosr - y * sinr - m_transform.rx,
        y: x * sinr + y * cosr - m_transform.ry
      };
    }
    pt = {
      x: pt.x * m_scale + m_transform.dx,
      y: pt.y * m_scale + m_transform.dy
    };
    return pt;
  };

  /**
   * Initialize.
   *
   * @param {object} arg The options used to create the renderer.
   * @param {boolean} [arg.widget=false] Set to `true` if this is a stand-alone
   *   widget.  If it is not a widget, svg elements are wrapped in a parent
   *   group.
   * @param {HTMLElement} [arg.d3Parent] If specified, the parent for any
   *   rendered objects; otherwise the renderer's layer's main node is used.
   * @returns {this}
   */
  this._init = function (arg) {
    if (!m_this.canvas()) {
      var canvas;
      arg.widget = arg.widget || false;

      if ('d3Parent' in arg) {
        m_svg = d3.select(arg.d3Parent).append('svg');
      } else {
        m_svg = d3.select(m_this.layer().node().get(0)).append('svg');
      }
      m_svg.attr('display', 'block');

      // create a global svg definitions element
      m_defs = m_svg.append('defs');

      var shadow = m_defs.append('filter').attr('id', 'geo-highlight').attr('x', '-100%').attr('y', '-100%').attr('width', '300%').attr('height', '300%');
      shadow.append('feMorphology').attr('operator', 'dilate').attr('radius', 2).attr('in', 'SourceAlpha').attr('result', 'dilateOut');
      shadow.append('feGaussianBlur').attr('stdDeviation', 5).attr('in', 'dilateOut').attr('result', 'blurOut');
      shadow.append('feColorMatrix').attr('type', 'matrix').attr('values', '-1 0 0 0 1  0 -1 0 0 1  0 0 -1 0 1  0 0 0 1 0').attr('in', 'blurOut').attr('result', 'invertOut');
      shadow.append('feBlend').attr('in', 'SourceGraphic').attr('in2', 'invertOut').attr('mode', 'normal');

      if (!arg.widget) {
        canvas = m_svg.append('g');
      }

      shadow = m_defs.append('filter').attr('id', 'geo-blur').attr('x', '-100%').attr('y', '-100%').attr('width', '300%').attr('height', '300%');

      shadow.append('feGaussianBlur').attr('stdDeviation', 20).attr('in', 'SourceGraphic');

      m_sticky = m_this.layer().sticky();
      m_svg.attr('class', m_this._d3id());
      m_svg.attr('width', m_this.layer().node().width());
      m_svg.attr('height', m_this.layer().node().height());

      if (!arg.widget) {
        canvas.attr('class', 'group-' + m_this._d3id());

        m_this.canvas(canvas);
      } else {
        m_this.canvas(m_svg);
      }
    }
    m_this._setTransform();
    return m_this;
  };

  /**
   * Get API used by the renderer.
   *
   * @returns {string} 'd3'.
   */
  this.api = function () {
    return 'd3';
  };

  /**
   * Return the current scaling factor to build features that shouldn't
   * change size during zooms.  For example:
   *
   *  selection.append('circle')
   *    .attr('r', r0 / renderer.scaleFactor());
   *
   * This will create a circle element with radius r0 independent of the
   * current zoom level.
   *
   * @returns {number} The current scale factor.
   */
  this.scaleFactor = function () {
    return m_scale;
  };

  /**
   * Handle resize event.
   *
   * @param {number} x Ignored.
   * @param {number} y Ignored.
   * @param {number} w New width in pixels.
   * @param {number} h New height in pixels.
   * @returns {this}
   * @fires geo.event.d3.rescale
   */
  this._resize = function (x, y, w, h) {
    if (!m_corners) {
      initCorners();
    }
    m_svg.attr('width', w);
    m_svg.attr('height', h);
    m_this._setTransform();
    m_this.layer().geoTrigger(d3Rescale, { scale: m_scale }, true);
    return m_this;
  };

  /**
   * Exit.
   */
  this._exit = function () {
    m_features = {};
    m_this.canvas().remove();
    m_svg.remove();
    m_svg = undefined;
    m_defs.remove();
    m_defs = undefined;
    m_renderIds = {};
    m_removeIds = {};
    s_exit();
  };

  /**
   * Get the definitions DOM element for the layer.
   * @protected
   * @returns {HTMLElement} The definitions DOM element.
   */
  this._definitions = function () {
    return m_defs;
  };

  /**
   * Create a new feature element from an object that describes the feature
   * attributes.  To be called from feature classes only.
   *
   * @param {object} arg Options for the features.
   * @param {string} arg.id A unique string identifying the feature.
   * @param {array} arg.data Array of data objects used in a d3 data method.
   * @param {function} [aeg.dataIndex] A function that returns a unique id for
   *    each data element.  This is passed to the data access function.
   * @param {object} arg.style An object with style values or functions.
   * @param {object} arg.attributes An object containing element attributes.
   *    The keys are the attribute names, and the values are either constants
   *    or functions that get passed a data element and a data index.
   * @param {string[]} arg.classes An array of classes to add to the elements.
   * @param {string} arg.append The element type as used in d3 append methods.
   *    This is something like `'path'`, `'circle'`, or `'line'`.
   * @param {boolean} [arg.onlyRenderNew] If truthy, features only get
   *    attributes and styles set when new.  If falsy, features always have
   *    attributes and styles updated.
   * @param {boolean} [arg.sortByZ] If truthy, sort features by the `d.zIndex`.
   * @param {string} [parentId] If set, the group ID of the parent element.
   * @returns {this}
   */
  this._drawFeatures = function (arg) {
    m_features[arg.id] = {
      data: arg.data,
      index: arg.dataIndex,
      style: arg.style,
      visible: arg.visible,
      attributes: arg.attributes,
      classes: arg.classes,
      append: arg.append,
      onlyRenderNew: arg.onlyRenderNew,
      sortByZ: arg.sortByZ,
      parentId: arg.parentId
    };
    return m_this.__render(arg.id, arg.parentId);
  };

  /**
   * Updates a feature by performing a d3 data join.  If no input id is
   * provided then this method will update all features.
   *
   * @param {string} [id] The id of the feature to update.  `undefined` to
   *    update all features.
   * @param {string} [parentId] The parent of the feature(s).  If not
   *    specified, features are rendered on the next animation frame.
   * @returns {this}
   */
  this.__render = function (id, parentId) {
    var key;
    if (id === undefined) {
      for (key in m_features) {
        if (m_features.hasOwnProperty(key)) {
          m_this.__render(key);
        }
      }
      return m_this;
    }
    if (parentId) {
      m_this._renderFeature(id, parentId);
    } else {
      m_renderIds[id] = true;
      m_this.layer().map().scheduleAnimationFrame(m_this._renderFrame);
    }
    return m_this;
  };

  /**
   * Render all features that are marked as needing an update.  This should
   * only be called duration an animation frame.
   */
  this._renderFrame = function () {
    var id;
    for (id in m_removeIds) {
      m_this.select(id).remove();
      m_defs.selectAll('.' + id).remove();
    }
    m_removeIds = {};
    var ids = m_renderIds;
    m_renderIds = {};
    for (id in ids) {
      if (ids.hasOwnProperty(id)) {
        m_this._renderFeature(id);
      }
    }
  };

  /**
   * Render a single feature.
   *
   * @param {string} id The id of the feature to update.
   * @param {string} [parentId] The parent of the feature.  This is used to
   *    select the feature.
   * @returns {this}
   */
  this._renderFeature = function (id, parentId) {
    if (!m_features[id]) {
      return m_this;
    }
    var data = m_features[id].data,
        index = m_features[id].index,
        style = m_features[id].style,
        visible = m_features[id].visible,
        attributes = m_features[id].attributes,
        classes = m_features[id].classes,
        append = m_features[id].append,
        selection = m_this.select(id, parentId).data(data, index),
        entries,
        rendersel;
    entries = selection.enter().append(append);
    selection.exit().remove();
    rendersel = m_features[id].onlyRenderNew ? entries : selection;
    setAttrs(rendersel, attributes);
    rendersel.attr('class', classes.concat([id]).join(' '));
    setStyles(rendersel, style);
    if (visible) {
      rendersel.style('visibility', visible() ? 'visible' : 'hidden');
    }
    if (entries.size() && m_features[id].sortByZ) {
      selection.sort(function (a, b) {
        return (a.zIndex || 0) - (b.zIndex || 0);
      });
    }
    return m_this;
  };

  /**
   * Returns a d3 selection for the given feature id.
   *
   * @param {string} id The id of the feature to select.
   * @param {string} [parentId] The parent of the feature.  This is used to
   *    determine the feature's group.
   * @returns {d3Selector}
   */
  this.select = function (id, parentId) {
    return getGroup(parentId).selectAll('.' + id);
  };

  /**
   * Removes a feature from the layer.
   *
   * @param {string} id The id of the feature to remove.
   * @returns {this}
   */
  this._removeFeature = function (id) {
    m_removeIds[id] = true;
    m_this.layer().map().scheduleAnimationFrame(m_this._renderFrame);
    delete m_features[id];
    if (m_renderIds[id]) {
      delete m_renderIds[id];
    }
    return m_this;
  };

  /**
   * Override draw method to do nothing.
   */
  this.draw = function () {};

  // connect to pan event
  this.layer().geoOn(geo_event.pan, m_this._setTransform);

  // connect to rotate event
  this.layer().geoOn(geo_event.rotate, m_this._setTransform);

  // connect to zoom event
  this.layer().geoOn(geo_event.zoom, function () {
    m_this._setTransform();
    m_this.__render();
    m_this.layer().geoTrigger(d3Rescale, { scale: m_scale }, true);
  });

  this.layer().geoOn(geo_event.resize, function (event) {
    m_this._resize(event.x, event.y, event.width, event.height);
  });

  this._init(arg);
  return this;
};

inherit(d3Renderer, renderer);

registerRenderer('d3', d3Renderer);

/* Code for checking if the renderer is supported */

/**
 * Report if the d3 renderer is supported.  This is just a check if d3 is
 * available.
 *
 * @returns {boolean} true if available.
 */
d3Renderer.supported = function () {
  delete d3Renderer.d3;
  // webpack expects optional dependencies to be wrapped in a try-catch
  try {
    d3Renderer.d3 = __webpack_require__(284);
  } catch (_error) {}
  return d3Renderer.d3 !== undefined;
};

/**
 * If the d3 renderer is not supported, supply the name of a renderer that
 * should be used instead.  This asks for the null renderer.
 *
 * @returns {null} `null` for the null renderer.
 */
d3Renderer.fallback = function () {
  return null;
};

d3Renderer.supported(); // cache reference to d3 if it is available

module.exports = d3Renderer;

/***/ }),
/* 12 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var vgl = __webpack_require__(9);
var inherit = __webpack_require__(0);

/**
 * Create a new instance of class object.
 *
 * @class
 * @alias geo.object
 * @extends vgl.object
 * @returns {geo.object}
 */
var object = function object() {
  'use strict';

  if (!(this instanceof object)) {
    return new object();
  }

  var util = __webpack_require__(3);

  var m_this = this,
      m_eventHandlers = {},
      m_idleHandlers = [],
      m_promiseCount = 0;

  /**
   * Bind a handler that will be called one time when all internal promises are
   * resolved.  If there are no outstanding promises, this is invoked
   * synchronously.
   *
   * @param {function} handler A function taking no arguments.
   * @returns {this}
   */
  this.onIdle = function (handler) {
    if (m_promiseCount) {
      m_idleHandlers.push(handler);
    } else {
      handler();
    }
    return m_this;
  };

  /**
   * Getter for the idle state.  Read only.
   *
   * @property {boolean} idle `true` if the object is idle (`onIdle` would call
   *    a handler immediately).
   * @name geo.object#idle
   */
  Object.defineProperty(this, 'idle', {
    get: function get() {
      return !m_promiseCount;
    },
    configurable: true
  });

  /**
   * Add a new promise object preventing idle event handlers from being called
   * until it is resolved.
   *
   * @param {Promise} promise A promise object.
   * @returns {this}
   */
  this.addPromise = function (promise) {
    // called on any resolution of the promise
    function onDone() {
      m_promiseCount -= 1;
      if (!m_promiseCount) {
        m_idleHandlers.splice(0, m_idleHandlers.length).forEach(function (handler) {
          handler();
        });
      }
    }
    m_promiseCount += 1;
    if (promise.always) {
      promise.always(onDone);
    } else {
      promise.then(onDone, onDone);
    }
    return m_this;
  };

  /**
   * Bind an event handler to this object.
   *
   * @param {string} event An event from {@link geo.event} or a user-defined
   *   value.
   * @param {function} handler A function that is called when `event` is
   *   triggered.  The function is passed a {@link geo.event} object.
   * @returns {this}
   */
  this.geoOn = function (event, handler) {
    if (Array.isArray(event)) {
      event.forEach(function (e) {
        m_this.geoOn(e, handler);
      });
      return m_this;
    }
    if (!util.isFunction(handler)) {
      console.warn('Handler for ' + event + ' is not a function', handler, m_this);
      return m_this;
    }
    if (!m_eventHandlers.hasOwnProperty(event)) {
      m_eventHandlers[event] = [];
    }
    m_eventHandlers[event].push(handler);
    return m_this;
  };

  /**
   * Report if an event handler is bound to this object.
   *
   * @param {string|string[]} event An event or list of events to check.
   * @param {function} [handler] A function that might be bound.  If
   *   `undefined`, this will report `true` if there is any handler for the
   *   specified event.
   * @returns {boolean} true if any of the specified events are bound to the
   *   specified handler.
   */
  this.geoIsOn = function (event, handler) {
    if (Array.isArray(event)) {
      return event.some(function (e) {
        return m_this.geoIsOn(e, handler);
      });
    }
    return (m_eventHandlers[event] || []).some(function (h) {
      return h === handler || handler === undefined;
    });
  };

  /**
   * Trigger an event (or events) on this object and call all handlers.
   *
   * @param {string|string[]} event An event or list of events from
   *   {@link geo.event} or defined by the user.
   * @param {object} [args] Additional information to add to the
   *   {@link geo.event} object passed to the handlers.
   * @returns {this}
   */
  this.geoTrigger = function (event, args) {

    // if we have an array of events, recall with single events
    if (Array.isArray(event)) {
      event.forEach(function (e) {
        m_this.geoTrigger(e, args);
      });
      return m_this;
    }

    // append the event type to the argument object
    args = args || {};
    args.event = event;

    if (m_eventHandlers.hasOwnProperty(event)) {
      m_eventHandlers[event].forEach(function (handler) {
        try {
          handler.call(m_this, args);
        } catch (err) {
          console.warn('Event handler for ' + event + ' threw an error', err);
        }
      });
    }

    return m_this;
  };

  /**
   * Remove handlers from one event or an array of events.  If no event is
   * provided all handlers will be removed.
   *
   * @param {string|string[]} [event] An event or a list of events from
   *   {@link geo.event} or defined by the user, or `undefined` to remove all
   *   events (in which case `arg` is ignored).
   * @param {(function|function[])?} [arg] A function or array of functions to
   *   remove from the events or a falsy value to remove all handlers from the
   *   events.
   * @returns {this}
   */
  this.geoOff = function (event, arg) {
    if (event === undefined) {
      m_eventHandlers = {};
      m_idleHandlers = [];
      m_promiseCount = 0;
    }
    if (Array.isArray(event)) {
      event.forEach(function (e) {
        m_this.geoOff(e, arg);
      });
      return m_this;
    }
    if (!arg) {
      m_eventHandlers[event] = [];
    } else if (Array.isArray(arg)) {
      arg.forEach(function (handler) {
        m_this.geoOff(event, handler);
      });
      return m_this;
    }
    if (m_eventHandlers.hasOwnProperty(event)) {
      m_eventHandlers[event] = m_eventHandlers[event].filter(function (f) {
        return f !== arg;
      });
    }
    return m_this;
  };

  /**
   * Free all resources and destroy the object.
   */
  this._exit = function () {
    m_this.geoOff();
  };

  vgl.object.call(this);

  return this;
};

inherit(object, vgl.object);
module.exports = object;

/***/ }),
/* 13 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var inherit = __webpack_require__(0);
var object = __webpack_require__(12);

/**
 * Create a new instance of class sceneObject, which extends the object's
 * event handling with a tree-based event propagation.
 *
 * @class
 * @alias geo.sceneObject
 * @extends geo.object
 * @param {object} arg Options for the object.
 * @returns {geo.sceneObject}
 */
var sceneObject = function sceneObject(arg) {
  'use strict';

  if (!(this instanceof sceneObject)) {
    return new sceneObject();
  }
  object.call(this, arg);

  var m_this = this,
      m_parent = null,
      m_children = [],
      s_exit = this._exit,
      s_trigger = this.geoTrigger,
      s_addPromise = this.addPromise,
      s_onIdle = this.onIdle;

  /**
   * Override object.addPromise to propagate up the scene tree.
   *
   * @param {Promise} promise A promise object.
   * @returns {this}
   */
  this.addPromise = function (promise) {
    if (m_parent) {
      m_parent.addPromise(promise);
    } else {
      s_addPromise(promise);
    }
    return this;
  };

  /**
   * Override object.onIdle to propagate up the scene tree.
   *
   * @param {function} handler A function taking no arguments.
   * @returns {this}
   */
  this.onIdle = function (handler) {
    if (m_parent) {
      m_parent.onIdle(handler);
    } else {
      s_onIdle(handler);
    }
    return this;
  };

  /**
   * Get/set parent of the object.
   *
   * @param {geo.sceneObject} [arg] The new parant or `undefined` to get the
   *    current parent.
   * @returns {this|geo.sceneObject}
   */
  this.parent = function (arg) {
    if (arg === undefined) {
      return m_parent;
    }
    m_parent = arg;
    return m_this;
  };

  /**
   * Add a child (or an array of children) to the object.
   *
   * @param {geo.object|geo.object[]} child A child object or array of child
   *    objects.
   * @returns {this}
   */
  this.addChild = function (child) {
    if (Array.isArray(child)) {
      child.forEach(m_this.addChild);
      return m_this;
    }
    child.parent(m_this);
    m_children.push(child);
    return m_this;
  };

  /**
   * Remove a child (or array of children) from the object.
   *
   * @param {geo.object|geo.object[]} child A child object or array of child
   *    objects.
   * @returns {this}
   */
  this.removeChild = function (child) {
    if (Array.isArray(child)) {
      child.forEach(m_this.removeChild);
      return m_this;
    }
    m_children = m_children.filter(function (c) {
      return c !== child;
    });
    return m_this;
  };

  /**
   * Get an array of the child objects.
   *
   * @returns {geo.object[]} A copy of the array of child objects.
   */
  this.children = function () {
    return m_children.slice();
  };

  /**
   * Force redraw of a scene object, to be implemented by subclasses.
   * Base class just calls draw of child objects.
   *
   * @param {object} arg Options to pass to the child draw functions.
   * @returns {this}
   */
  this.draw = function (arg) {
    m_this.children().forEach(function (child) {
      child.draw(arg);
    });
    return m_this;
  };

  /**
   * Trigger an event (or events) on this object and call all handlers.
   *
   * @param {string} event The event to trigger.
   * @param {object} args Arbitrary argument to pass to the handler.
   * @param {boolean} [childrenOnly] If truthy, only propagate down the tree.
   * @returns {this}
   */
  this.geoTrigger = function (event, args, childrenOnly) {

    var geoArgs;

    args = args || {};
    geoArgs = args.geo || {};
    args.geo = geoArgs;

    // stop propagation if requested by the handler
    if (geoArgs.stopPropagation) {
      return m_this;
    }

    // If the event was not triggered by the parent, just propagate up the tree
    if (!childrenOnly && m_parent && geoArgs._triggeredBy !== m_parent) {
      geoArgs._triggeredBy = m_this;
      m_parent.geoTrigger(event, args);
      return m_this;
    }

    // call the object's own handlers
    s_trigger.call(m_this, event, args);

    // stop propagation if requested by the handler
    if (geoArgs.stopPropagation) {
      return m_this;
    }

    // trigger the event on the children
    m_children.forEach(function (child) {
      if (child.geoTrigger) {
        geoArgs._triggeredBy = m_this;
        child.geoTrigger(event, args);
      }
    });

    return m_this;
  };

  /**
   * Free all resources and destroy the object.
   */
  this._exit = function () {
    m_children = [];
    m_parent = null;
    s_exit();
  };

  return this;
};

inherit(sceneObject, object);
module.exports = sceneObject;

/***/ }),
/* 14 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var inherit = __webpack_require__(0);
var sceneObject = __webpack_require__(13);

/**
 * D3 specific subclass of object which adds an id property for d3 selections
 * on groups of objects by class id.
 *
 * @class
 * @alias geo.d3.object
 * @extends geo.sceneObject
 * @param {object} arg Options for the object.
 * @returns {geo.d3.object}
 */
var d3_object = function d3_object(arg) {
  'use strict';

  var object = __webpack_require__(12);
  var uniqueID = __webpack_require__(52);

  // this is used to extend other geojs classes, so only generate
  // a new object when that is not the case... like if this === window
  if (!(this instanceof object)) {
    return new d3_object(arg);
  }
  sceneObject.call(this);

  var m_id = 'd3-' + uniqueID(),
      s_exit = this._exit,
      m_this = this,
      s_draw = this.draw;

  this._d3id = function () {
    return m_id;
  };

  /**
   * Returns a d3 selection for the feature elements.
   *
   * @returns {d3.selector} A d3 selector of the features in this object.
   */
  this.select = function () {
    return m_this.renderer().select(m_this._d3id());
  };

  /**
   * Redraw the object.
   *
   * @returns {this}
   */
  this.draw = function () {
    if (m_this.ready) {
      m_this._update();
      s_draw();
    }
    return m_this;
  };

  /**
   * Removes the element from the svg and the renderer.
   */
  this._exit = function () {
    m_this.renderer()._removeFeature(m_this._d3id());
    s_exit();
  };

  return this;
};

inherit(d3_object, sceneObject);
module.exports = d3_object;

/***/ }),
/* 15 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony default export */ __webpack_exports__["a"] = (function(eccent, sinphi, cosphi) {
  var con = eccent * sinphi;
  return cosphi / (Math.sqrt(1 - con * con));
});

/***/ }),
/* 16 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony default export */ __webpack_exports__["a"] = (function(x) {
  if (Math.abs(x) > 1) {
    x = (x > 1) ? 1 : -1;
  }
  return Math.asin(x);
});

/***/ }),
/* 17 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var $ = __webpack_require__(2);
var inherit = __webpack_require__(0);
var feature = __webpack_require__(8);

/**
 * Quad feature specification.
 *
 * @typedef {geo.feature.spec} geo.quadFeature.spec
 * @property {geo.geoColor|function} [color] Color for quads without images.
 *   Default is white (`{r: 1, g: 1, b: 1}`).
 * @property {number|function} [opacity=1] Opacity for the quads.
 * @property {number|function} [depth=0] Default z-coordinate for positions
 *   that don't explicitly specify one.
 * @property {boolean|function} [drawOnAsyncResourceLoaded=true] Redraw quads
 *   when images or videos are loaded after initial render.
 * @property {Image|string|function} [image] Image for each data item.  If
 *   falsy and `video` is also falsy, the quad is a solid color.  Default is
 *   (data).image.
 * @property {HTMLVideoElement|string|function} [video] Video for each data
 *   item.  If falsy and `image` is also falsy, the quad is a solid color.
 *   Default is (data).video.
 * @property {boolean|function} [delayRenderWhenSeeking=true] If any video has a
 *   truthy value and is seeking, delaying rendering the entire feature.  This
 *   prevents blinking when seeking a playing video, but may cause stuttering
 *   when there are multiple videos.
 * @property {geo.geoColor|function} [previewColor=null] If specified, a color
 *   to show on image and video quads while waiting for the image or video to
 *   load.
 * @property {Image|string|function} [previewImage=null] If specified, an image
 *   to show on image quads while waiting for the quad-specific image to load.
 *   This will only be shown if it (the preview image) is already loaded.
 * @property {object|function} [position] Position of the quad.  Default is
 *   (data).  The position is an object which specifies the corners of the
 *   quad: ll, lr, ur, ul.  At least two opposite corners must be specified.
 *   The corners do not have to physically correspond to the order specified,
 *   but rather correspond to that part of an image or video (if there is one).
 *   If a corner is unspecified, it will use the x coordinate from one adjacent
 *   corner, the y coordinate from the other adjacent corner, and the average
 *   z value of those two corners.  For instance, if ul is unspecified, it is
 *   {x: ll.x, y: ur.y}.  Note that each quad is rendered as a pair of
 *   triangles: (ll, lr, ul) and (ur, ul, lr).  Nothing special is done for
 *   quads that are not convex or quads that have substantially different
 *   transformations for those two triangles.
 * @property {boolean} [cacheQuads=true] If truthy, a set of internal
 *   information is stored on each data item in the _cachedQuad attribute.  If
 *   this is falsy, the data item is not altered.  If the data (positions,
 *   opacity, etc.) of individual quads will change, set this to `false` or
 *   call `cacheUpdate` on the data item or for all data.
 */

/**
 * Create a new instance of class quadFeature.
 *
 * @class
 * @alias geo.quadFeature
 * @param {geo.quadFeature.spec} arg Options object.
 * @extends geo.feature
 * @returns {geo.quadFeature}
 */
var quadFeature = function quadFeature(arg) {
  'use strict';

  var transform = __webpack_require__(7);
  var util = __webpack_require__(3);

  if (!(this instanceof quadFeature)) {
    return new quadFeature(arg);
  }
  arg = arg || {};
  feature.call(this, arg);

  /**
   * @private
   */
  var m_this = this,
      s_init = this._init,
      m_cacheQuads,
      m_nextQuadId = 0,
      m_images = [],
      m_videos = [],
      m_quads;

  /**
   * Track a list of object->object mappings.  The mappings are kept in a list.
   * This marks all known mappings as unused.  If they are not marked as used
   * before `_objectListEnd` is called, that function will remove them.
   *
   * @param {array} list The list of mappings.
   */
  this._objectListStart = function (list) {
    $.each(list, function (idx, item) {
      item.used = false;
    });
  };

  /**
   * Get the value from a list of object->object mappings.  If the key object
   * is not present, return `undefined`.  If found, the entry is marked as
   * being in use.
   *
   * @param {array} list The list of mappings.
   * @param {object} entry The key to search for.
   * @returns {object} The associated object or undefined.
   */
  this._objectListGet = function (list, entry) {
    for (var i = 0; i < list.length; i += 1) {
      if (list[i].entry === entry) {
        list[i].used = true;
        return list[i].value;
      }
    }
    return undefined;
  };

  /**
   * Add a new object to a list of object->object mappings.  The key object
   * should not exist, or this will create a duplicate.  The new entry is
   * marked as being in use.
   *
   * @param {array} list The list of mappings.
   * @param {object} entry The key to add.
   * @param {object} value The value to store with the entry.
   */
  this._objectListAdd = function (list, entry, value) {
    list.push({ entry: entry, value: value, used: true });
  };

  /**
   * Remove all unused entries from a list of object->object mappings.
   *
   * @param {array} list The list of mappings.
   */
  this._objectListEnd = function (list) {
    for (var i = list.length - 1; i >= 0; i -= 1) {
      if (!list[i].used) {
        list.splice(i, 1);
      }
    }
  };

  /**
   * Point search method for selection api.  Returns markers containing the
   * given point.
   *
   * @param {geo.geoPosition} coordinate Coordinate in input gcs to check if it
   *    is located in any quad in map interface gcs.
   * @returns {object} An object with `index`: a list of quad indices, and
   *    `found`: a list of quads that contain the specified coordinate.
   */
  this.pointSearch = function (coordinate) {
    var found = [],
        indices = [],
        extra = {},
        poly1 = [{}, {}, {}, {}],
        poly2 = [{}, {}, {}, {}],
        order1 = [0, 1, 2, 0],
        order2 = [1, 2, 3, 1],
        data = m_this.data(),
        map = m_this.layer().map(),
        i,
        coordbasis;
    coordinate = transform.transformCoordinates(map.ingcs(), map.gcs(), coordinate);
    if (!m_quads) {
      this._generateQuads();
    }
    $.each([m_quads.clrQuads, m_quads.imgQuads, m_quads.vidQuads], function (idx, quadList) {
      quadList.forEach(function (quad, idx) {
        for (i = 0; i < order1.length; i += 1) {
          poly1[i].x = quad.pos[order1[i] * 3];
          poly1[i].y = quad.pos[order1[i] * 3 + 1];
          poly1[i].z = quad.pos[order1[i] * 3 + 2];
          poly2[i].x = quad.pos[order2[i] * 3];
          poly2[i].y = quad.pos[order2[i] * 3 + 1];
          poly2[i].z = quad.pos[order2[i] * 3 + 2];
        }
        if (util.pointInPolygon(coordinate, poly1) || util.pointInPolygon(coordinate, poly2)) {
          indices.push(quad.idx);
          found.push(data[quad.idx]);
          /* If a point is in the quad (based on pointInPolygon, above), check
           * where in the quad it is located.  We want to output coordinates
           * where the upper-left is (0, 0) and the lower-right is (1, 1). */
          coordbasis = util.pointTo2DTriangleBasis(coordinate, poly1[0], poly1[1], poly1[2]);
          if (!coordbasis || coordbasis.x + coordbasis.y > 1) {
            coordbasis = util.pointTo2DTriangleBasis(coordinate, poly2[2], poly2[1], poly2[0]);
            if (coordbasis) {
              /* In the second triangle, (0, 0) is upper-right, (1, 0) is
               * upper-left, and (0, 1) is lower-right.  Invert x to get to
               * the desired output coordinates. */
              coordbasis.x = 1 - coordbasis.x;
            }
          } else {
            /* In the first triangle, (0, 0) is lower-left, (1, 0) is lower-
             * right, and (0, 1) is upper-left.  Invert y to get to the
             * desired output coordinates. */
            coordbasis.y = 1 - coordbasis.y;
          }
          if (coordbasis) {
            extra[quad.idx] = { basis: coordbasis };
          }
        }
      });
    });
    return {
      index: indices,
      found: found,
      extra: extra
    };
  };

  /**
   * Get/Set position.
   *
   * @memberof geo.quadFeature
   * @param {object|Function} [val] Object or function that returns the
   *    position of each quad.  `undefined` to get the current position value.
   * @returns {geo.quadFeature}
   */
  this.position = function (val) {
    if (val === undefined) {
      return m_this.style('position');
    } else {
      m_this.style('position', util.ensureFunction(val));
      m_this.dataTime().modified();
      m_this.modified();
    }
    return m_this;
  };

  /**
   * Given a data item and its index, fetch its position and ensure we have
   * complete information for the quad.  This generates missing corners and z
   * values.
   *
   * @param {function} posFunc A function to call to get the position of a data
   *   item.  It is passed (d, i).
   * @param {function} depthFunc A function to call to get the z-value of a
   *   data item.  It is passed (d, i).
   * @param {object} d A data item.  Used to fetch position and possibly depth.
   * @param {number} i The index within the data.  Used to fetch position and
   *   possibly depth.
   * @returns {object|undefined} Either an object with all four corners, or
   *   `undefined` if no such object can be generated.  The coordinates have
   *   been converted to map coordinates.
   */
  this._positionToQuad = function (posFunc, depthFunc, d, i) {
    var initPos = posFunc.call(m_this, d, i);
    if ((!initPos.ll || !initPos.ur) && (!initPos.ul || !initPos.lr)) {
      return;
    }
    var gcs = m_this.gcs(),
        map_gcs = m_this.layer().map().gcs(),
        pos = {};
    $.each(['ll', 'lr', 'ul', 'ur'], function (idx, key) {
      if (initPos[key] !== undefined) {
        pos[key] = {};
        if (initPos[key].x === undefined) {
          pos[key] = [initPos[key][0], initPos[key][1], initPos[key][2]];
        } else {
          pos[key] = [initPos[key].x, initPos[key].y, initPos[key].z];
        }
        if (pos[key][2] === undefined) {
          pos[key][2] = depthFunc.call(m_this, d, i);
        }
        if (gcs !== map_gcs && gcs !== false) {
          pos[key] = transform.transformCoordinates(gcs, map_gcs, pos[key]);
        }
      }
    });
    pos.ll = pos.ll || [pos.ul[0], pos.lr[1], (pos.ul[2] + pos.lr[2]) / 2];
    pos.lr = pos.lr || [pos.ur[0], pos.ll[1], (pos.ur[2] + pos.ll[2]) / 2];
    pos.ur = pos.ur || [pos.lr[0], pos.ul[1], (pos.lr[2] + pos.ul[2]) / 2];
    pos.ul = pos.ul || [pos.ll[0], pos.ur[1], (pos.ll[2] + pos.ur[2]) / 2];
    return pos;
  };

  /**
   * Renderers can subclass this when needed.
   *
   * This is called when a video qaud may have changed play state.
   * @param {object} quad The quad record that triggered this.
   * @param {jQuery.Event} [evt] The event that triggered this.
   */
  this._checkQuadUpdate = function (quad, evt) {};

  /**
   * Convert the current data set to a set of 3 arrays: quads that are a solid
   * color, quads that have an image, and quads that have a video.  All quads
   * are objects with pos (a 12 value array containing 4 three-dimensional
   * position coordinates), and opacity.  Color quads also have a color.  Image
   * quads may have an image element if the image is loaded.  If it isn't, this
   * element will be missing.  For preview images, the image quad will have a
   * reference to the preview element that may later be removed.  If a preview
   * color is used, the quad will be in both lists, but may be removed from the
   * color quad list once the image is loaded.  Video quads may have a video
   * element if the video is loaded.
   *
   * The value for origin is one of an ll corner from one of the quads with the
   * smallest sum of diagonals.  The assumption is that, if using the origin to
   * improve precision, the smallest quads are the ones most in need of this
   * benefit.
   *
   * @returns {object} An object with `clrQuads`, `imgQuads`, and `vidQuads`,
   *   each of which is an array; and `origin`, which is a triplet that is
   *   guaranteed to be one of the quads' corners for a quad with the smallest
   *   sum of diagonal lengths.
   */
  this._generateQuads = function () {
    var posFunc = m_this.position(),
        imgFunc = m_this.style.get('image'),
        vidFunc = m_this.style.get('video'),
        delayFunc = m_this.style.get('delayRenderWhenSeeking'),
        colorFunc = m_this.style.get('color'),
        depthFunc = m_this.style.get('depth'),
        opacityFunc = m_this.style.get('opacity'),
        loadedFunc = m_this.style.get('drawOnAsyncResourceLoaded'),
        previewColorFunc = m_this.style.get('previewColor'),
        previewImageFunc = m_this.style.get('previewImage'),
        data = m_this.data(),
        clrQuads = [],
        imgQuads = [],
        vidQuads = [],
        origin = [0, 0, 0],
        origindiag2,
        diag2;
    /* Keep track of images that we are using.  This prevents creating
     * additional Image elements for repeated urls. */
    m_this._objectListStart(m_images);
    m_this._objectListStart(m_videos);
    $.each(data, function (i, d) {
      if (d._cachedQuad) {
        diag2 = d._cachedQuad.diag2;
        if (origindiag2 === undefined || d._cachedQuad.diag2 && d._cachedQuad.diag2 < origindiag2) {
          origin = d._cachedQuad.ll;
          origindiag2 = d._cachedQuad.diag2;
        }
        if (d._cachedQuad.clrquad) {
          clrQuads.push(d._cachedQuad.clrquad);
        }
        if (d._cachedQuad.imgquad) {
          if (d._cachedQuad.imageEntry) {
            m_this._objectListGet(m_images, d._cachedQuad.imageEntry);
          }
          imgQuads.push(d._cachedQuad.imgquad);
        }
        if (d._cachedQuad.vidquad) {
          if (d._cachedQuad.videoEntry) {
            m_this._objectListGet(m_videos, d._cachedQuad.videoEntry);
          }
          vidQuads.push(d._cachedQuad.vidquad);
        }
        return;
      }
      var quad,
          reload,
          image,
          video,
          prev_onload,
          prev_onerror,
          defer,
          pos,
          img,
          vid,
          opacity,
          previewColor,
          previewImage,
          quadinfo = {};

      pos = m_this._positionToQuad(posFunc, depthFunc, d, i);
      opacity = opacityFunc.call(m_this, d, i);
      if (pos === undefined || !opacity || opacity < 0) {
        return;
      }
      diag2 = Math.pow(pos.ll[0] - pos.ur[0], 2) + Math.pow(pos.ll[1] - pos.ur[1], 2) + Math.pow(pos.ll[2] - pos.ur[0], 2) + Math.pow(pos.lr[0] - pos.ur[0], 2) + Math.pow(pos.lr[1] - pos.ur[1], 2) + Math.pow(pos.lr[2] - pos.ur[0], 2);
      quadinfo.diag2 = diag2;
      quadinfo.ll = pos.ll;
      if (origindiag2 === undefined || diag2 && diag2 < origindiag2) {
        origin = pos.ll;
        origindiag2 = diag2;
      }
      pos = [pos.ll[0], pos.ll[1], pos.ll[2], pos.lr[0], pos.lr[1], pos.lr[2], pos.ul[0], pos.ul[1], pos.ul[2], pos.ur[0], pos.ur[1], pos.ur[2]];
      quad = {
        idx: i,
        pos: pos,
        opacity: opacity
      };
      if (d.reference) {
        quad.reference = d.reference;
      }
      if (d.crop) {
        quad.crop = d.crop;
      }
      img = imgFunc.call(m_this, d, i);
      vid = img ? null : vidFunc.call(m_this, d, i);
      if (img) {
        quadinfo.imageEntry = img;
        /* Handle image quads */
        image = m_this._objectListGet(m_images, img);
        if (image === undefined) {
          if (img instanceof Image || img instanceof HTMLCanvasElement) {
            image = img;
          } else {
            image = new Image();
            image.src = img;
          }
          m_this._objectListAdd(m_images, img, image);
        }
        if (util.isReadyImage(image) || image instanceof HTMLCanvasElement) {
          quad.image = image;
        } else {
          previewColor = undefined;
          previewImage = previewImageFunc.call(m_this, d, i);
          if (previewImage && util.isReadyImage(previewImage)) {
            quad.image = previewImage;
          } else {
            previewColor = previewColorFunc.call(m_this, d, i);
            quad.color = util.convertColor(previewColor);
            if (quad.color && quad.color.r !== undefined && quad.color.g !== undefined && quad.color.b !== undefined) {
              clrQuads.push(quad);
              quadinfo.clrquad = quad;
            } else {
              previewColor = undefined;
            }
          }
          reload = loadedFunc.call(m_this, d, i);
          if (reload) {
            // add a promise to the layer if this image might complete
            defer = util.isReadyImage(image, true) ? null : $.Deferred();
            prev_onload = image.onload;
            image.onload = function () {
              if (previewColor !== undefined) {
                if ($.inArray(quad, clrQuads) >= 0) {
                  clrQuads.splice($.inArray(quad, clrQuads), 1);
                }
                delete quadinfo.clrquad;
              }
              quad.image = image;
              m_this.dataTime().modified();
              m_this.modified();
              m_this._update();
              m_this.layer().draw();
              if (defer) {
                defer.resolve();
              }
              if (prev_onload) {
                return prev_onload.apply(this, arguments);
              }
            };
            prev_onerror = image.onerror;
            image.onerror = function () {
              if (defer) {
                defer.reject();
              }
              if (prev_onerror) {
                return prev_onerror.apply(this, arguments);
              }
            };
            if (defer) {
              m_this.layer().addPromise(defer.promise());
            }
          } else if (previewColor === undefined && !quad.image) {
            /* the image isn't ready and we don't want to reload, so don't add
             * it to the list of image quads */
            return;
          }
        }
        imgQuads.push(quad);
        quadinfo.imgquad = quad;
      } else if (vid) {
        /* Handle video quads */
        quadinfo.videoEntry = vid;
        video = m_this._objectListGet(m_videos, vid);
        if (video === undefined) {
          if (vid instanceof HTMLVideoElement) {
            video = vid;
          } else {
            video = document.createElement('video');
            video.src = vid;
          }
          m_this._objectListAdd(m_videos, vid, video);
          /* monitor some media events that may indicate a change of play state
           * or seeking */
          $(video).off('.geojsvideo').on('seeking.geojsvideo canplay.geojsvideo pause.geojsvideo playing.geojsvideo', function (evt) {
            m_this._checkQuadUpdate(quad, evt);
          });
        }
        quad.delayRenderWhenSeeking = delayFunc.call(m_this, d, i);
        if (quad.delayRenderWhenSeeking === undefined) {
          quad.delayRenderWhenSeeking = true;
        }
        if (util.isReadyVideo(video)) {
          quad.video = video;
        } else {
          previewColor = previewColorFunc.call(m_this, d, i);
          quad.color = util.convertColor(previewColor);
          if (quad.color && quad.color.r !== undefined && quad.color.g !== undefined && quad.color.b !== undefined) {
            clrQuads.push(quad);
            quadinfo.clrquad = quad;
          } else {
            previewColor = undefined;
          }
          reload = loadedFunc.call(m_this, d, i);
          if (reload) {
            // add a promise to the layer if this video might load
            defer = util.isReadyVideo(video, true) ? null : $.Deferred();
            prev_onload = video.onloadeddata;
            video.onloadeddata = function () {
              if (previewColor !== undefined) {
                if ($.inArray(quad, clrQuads) >= 0) {
                  clrQuads.splice($.inArray(quad, clrQuads), 1);
                }
                delete quadinfo.clrquad;
              }
              quad.video = video;
              m_this.dataTime().modified();
              m_this.modified();
              m_this._update();
              m_this.layer().draw();
              if (defer) {
                defer.resolve();
              }
              if (prev_onload) {
                return prev_onload.apply(this, arguments);
              }
            };
            prev_onerror = video.onerror;
            video.onerror = function () {
              if (defer) {
                defer.reject();
              }
              if (prev_onerror) {
                return prev_onerror.apply(this, arguments);
              }
            };
            if (defer) {
              m_this.layer().addPromise(defer.promise());
            }
          } else if (previewColor === undefined && !quad.video) {
            /* the video isn't ready and we don't want to reload, so don't add
             * it to the list of video quads */
            return;
          }
        }
        vidQuads.push(quad);
        quadinfo.vidquad = quad;
      } else {
        /* Handle color quads */
        quad.color = util.convertColor(colorFunc.call(m_this, d, i));
        if (!quad.color || quad.color.r === undefined || quad.color.g === undefined || quad.color.b === undefined) {
          /* if we can't resolve the color, don't make a quad */
          return;
        }
        clrQuads.push(quad);
        quadinfo.clrquad = quad;
      }
      if (quadinfo.clrquad) {
        m_nextQuadId += 1;
        quadinfo.clrquad.quadId = m_nextQuadId;
      }
      if (quadinfo.imgquad) {
        m_nextQuadId += 1;
        quadinfo.imgquad.quadId = m_nextQuadId;
      }
      if (quadinfo.vidquad) {
        m_nextQuadId += 1;
        quadinfo.vidquad.quadId = m_nextQuadId;
      }
      if (m_cacheQuads !== false) {
        d._cachedQuad = quadinfo;
      }
    });
    m_this._objectListEnd(m_images);
    m_this._objectListEnd(m_videos);
    m_quads = {
      clrQuads: clrQuads,
      imgQuads: imgQuads,
      vidQuads: vidQuads,
      origin: origin
    };
    return m_quads;
  };

  /**
   * If the data has changed and caching has been used, update one or all data
   * items by clearing their caches and updating the modified flag.
   *
   * @param {number|object} [indexOrData] If not specified, clear all quad
   *    caches.  If a number, clear that index-numbered entry from the data
   *    array.  Otherwise, clear the matching entry in the data array.
   * @returns {this}
   */
  this.cacheUpdate = function (indexOrData) {
    if (indexOrData === undefined || indexOrData === null) {
      $.each(m_this.data(), function (idx, entry) {
        if (entry._cachedQuad) {
          delete entry._cachedQuad;
        }
      });
    } else {
      if (isFinite(indexOrData)) {
        indexOrData = m_this.data()[indexOrData];
      }
      if (indexOrData._cachedQuad) {
        delete indexOrData._cachedQuad;
      }
    }
    m_this.modified();
    return m_this;
  };

  /**
   * Get the HTML video element associated with a data item.
   *
   * @param {number|object} indexOrData If a number, use that entry in the data
   *    array, otherwise this must be a value in the data array.  If caching is
   *    used, this is much more efficient.
   * @returns {HTMLVideoElement|null}
   */
  this.video = function (indexOrData) {
    var video, index;

    if (isFinite(indexOrData)) {
      indexOrData = m_this.data()[indexOrData];
    }
    if (indexOrData._cachedQuad) {
      video = (indexOrData._cachedQuad.vidquad || {}).video;
    } else {
      if (!m_quads) {
        m_this._generateQuads();
      }
      index = m_this.data().indexOf(indexOrData);
      if (index >= 0) {
        /* If we don't cache the quad, we don't maintain a direct link between
         * a data element and the video (partly because videos could be shared
         * between multiple quads).  Instead, the video will be in the
         * last-used object list with a reference to the video value of the
         * data entry. */
        video = m_this._objectListGet(m_videos, m_this.style.get('video')(indexOrData, index));
      }
    }
    if (video instanceof HTMLVideoElement) {
      return video;
    }
    return null;
  };

  /**
   * Initialize.
   *
   * @param {geo.quadFeature.spec} arg Options for the feature.
   */
  this._init = function (arg) {
    arg = arg || {};
    s_init.call(m_this, arg);

    m_cacheQuads = arg.cacheQuads !== false;

    var style = $.extend({}, {
      color: { r: 1.0, g: 1, b: 1 },
      opacity: 1,
      depth: 0,
      drawOnAsyncResourceLoaded: true,
      previewColor: null,
      previewImage: null,
      image: function image(d) {
        return d.image;
      },
      video: function video(d) {
        return d.video;
      },
      position: function position(d) {
        return d;
      }
    }, arg.style === undefined ? {} : arg.style);

    if (arg.position !== undefined) {
      style.position = util.ensureFunction(arg.position);
    }
    m_this.style(style);
    m_this.dataTime().modified();
  };

  return m_this;
};

/**
 * Create a quadFeature from an object.
 *
 * @see {@link geo.feature.create}
 * @param {geo.layer} layer The layer to add the feature to.
 * @param {geo.quadFeature.spec} spec The object specification.
 * @returns {geo.quadFeature|null}
 */
quadFeature.create = function (layer, spec) {
  'use strict';

  spec = spec || {};
  spec.type = 'quad';
  return feature.create(layer, spec);
};

quadFeature.capabilities = {
  /* support for solid-colored quads */
  color: 'quad.color',
  /* support for parallelogram image quads */
  image: 'quad.image',
  /* support for cropping quad images */
  imageCrop: 'quad.imageCrop',
  /* support for fixed-scale quad images */
  imageFixedScale: 'quad.imageFixedScale',
  /* support for arbitrary quad images */
  imageFull: 'quad.imageFull',
  /* support for canvas elements as content in image quads */
  canvas: 'quad.canvas',
  /* support for parallelogram video quads */
  video: 'quad.video'
};

inherit(quadFeature, feature);
module.exports = quadFeature;

/***/ }),
/* 18 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/**
 * VGL specific subclass of object which rerenders when the object is drawn.
 *
 * @class
 * @alias geo.gl.object
 * @extends geo.sceneObject
 * @param {object} arg Options for the object.
 * @returns {geo.gl.object}
 */
var gl_object = function gl_object(arg) {
  'use strict';

  var object = __webpack_require__(12);

  // this is used to extend other geojs classes, so only generate
  // a new object when that is not the case... like if this === window
  if (!(this instanceof object)) {
    return new gl_object(arg);
  }

  var m_this = this,
      s_draw = this.draw;

  /**
   * Redraw the object.
   *
   * @returns {this}
   */
  this.draw = function () {
    if (m_this.ready) {
      m_this._update({ mayDelay: true });
      m_this.renderer()._render();
      s_draw();
    }
    return m_this;
  };

  return this;
};

module.exports = gl_object;

/***/ }),
/* 19 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var inherit = __webpack_require__(0);
var sceneObject = __webpack_require__(13);

/**
 * Canvas specific subclass of object which rerenders when the object is drawn.
 * @class
 * @alias geo.canvas.object
 * @extends geo.sceneObject
 * @param {object} arg Options for the object.
 * @returns {geo.canvas.object}
 */
var canvas_object = function canvas_object(arg) {
  'use strict';

  var object = __webpack_require__(12);

  // this is used to extend other geojs classes, so only generate
  // a new object when that is not the case... like if this === window
  if (!(this instanceof object)) {
    return new canvas_object(arg);
  }
  sceneObject.call(this);

  var m_this = this,
      s_draw = this.draw,
      m_canvasProperties = {};

  /**
   * This must be overridden by any feature that needs to render.
   */
  this._renderOnCanvas = function () {};

  /**
   * If this returns true, the render will be skipped and tried again on the
   * next animation frame.
   *
   * @returns {boolean} Truthy to delay rendering.
   */
  this._delayRender = function () {
    return false;
  };

  /**
   * Check if a property has already been set on a canvas's context.  If so,
   * don't set it again.  Some browsers are much slower if the properties are
   * set, even if no change is made.
   *
   * @param {CanvasRenderingContext2D} [context] The canvas context to modify.
   *    If `undefined`, clear the internal property buffer.
   * @param {string} [key] The property to set on the canvas.
   * @param {object} [value] The value for the property.
   * @returns {this} The current object.
   */
  this._canvasProperty = function (context, key, value) {
    if (!context || !key) {
      m_canvasProperties = {};
      return m_this;
    }
    if (m_canvasProperties[key] !== value) {
      m_canvasProperties[key] = value;
      context[key] = value;
    }
    return m_this;
  };

  /**
   * Redraw the object.
   *
   * @returns {this} The current object.
   */
  this.draw = function () {
    if (m_this.ready) {
      m_this._update();
      m_this.renderer()._render();
      s_draw();
    }
    return m_this;
  };

  return this;
};

inherit(canvas_object, sceneObject);
module.exports = canvas_object;

/***/ }),
/* 20 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony default export */ __webpack_exports__["a"] = (function(x) {
  return x<0 ? -1 : 1;
});

/***/ }),
/* 21 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__constants_values__ = __webpack_require__(4);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__sign__ = __webpack_require__(20);



/* harmony default export */ __webpack_exports__["a"] = (function(x) {
  return (Math.abs(x) < __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */]) ? x : (x - (Object(__WEBPACK_IMPORTED_MODULE_1__sign__["a" /* default */])(x) * Math.PI));
});


/***/ }),
/* 22 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var inherit = __webpack_require__(0);
var object = __webpack_require__(12);

/**
 * Create a new instance of class renderer.
 *
 * @class
 * @alias geo.renderer
 * @extends geo.object
 * @param {object} arg Options for the renderer.
 * @param {geo.layer} [arg.layer] Layer associated with the renderer.
 * @param {HTMLElement} [arg.canvas] Canvas element associated with the
 *   renderer.
 * @returns {geo.renderer}
 */
var renderer = function renderer(arg) {
  'use strict';

  if (!(this instanceof renderer)) {
    return new renderer(arg);
  }
  object.call(this);

  arg = arg || {};
  var m_this = this,
      m_layer = arg.layer === undefined ? null : arg.layer,
      m_canvas = arg.canvas === undefined ? null : arg.canvas,
      m_initialized = false;

  /**
   * Get layer of the renderer.
   *
   * @returns {geo.layer}
   */
  this.layer = function () {
    return m_layer;
  };

  /**
   * Get/set canvas for the renderer.
   *
   * @param {HTMLElement} [val] If `undefined`, return the current canvas
   *    element, otherwise set the canvas element and mark the renderer as
   *    modified.
   * @returns {HTMLElement|this} The current canvas element or the renderer
   *    instance.
   */
  this.canvas = function (val) {
    if (val === undefined) {
      return m_canvas;
    }
    m_canvas = val;
    m_this.modified();
    return m_this;
  };

  /**
   * Get the map associated with the renderer's layer.
   *
   * @returns {geo.map|null} The map associated with the renderer's layer or
   *    `null` if there is no layer.
   */
  this.map = function () {
    if (m_layer) {
      return m_layer.map();
    } else {
      return null;
    }
  };

  /**
   * Get/set if renderer has been initialized.
   *
   * @param {boolean} [val] If `undefined` return the initialization state,
   *    otherwise set it.
   * @returns {boolean|this} The initialization state or this renderer
   *    instance.
   */
  this.initialized = function (val) {
    if (val === undefined) {
      return m_initialized;
    }
    m_initialized = val;
    return m_this;
  };

  /**
   * Get render API used by the renderer.
   *
   * This must be subclassed, returning a string describing the renderer
   * interface.
   */
  this.api = function () {
    throw new Error('Should be implemented by derived classes');
  };

  /**
   * Initialize.
   *
   * @returns {this}
   */
  this._init = function () {
    return m_this;
  };

  /**
   * Handle resize event.
   *
   * @param {number} x Ignored.
   * @param {number} y Ignored.
   * @param {number} w New width in pixels.
   * @param {number} h New height in pixels.
   * @returns {this}
   */
  this._resize = function (x, y, w, h) {
    return m_this;
  };

  /**
   * Render.
   *
   * @returns {this}
   */
  this._render = function () {
    return m_this;
  };

  return this;
};

inherit(renderer, object);
module.exports = renderer;

/***/ }),
/* 23 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var inherit = __webpack_require__(0);
var feature = __webpack_require__(8);
var timestamp = __webpack_require__(10);
var transform = __webpack_require__(7);
var util = __webpack_require__(3);

/**
 * Line feature specification.
 *
 * @typedef {geo.feature.spec} geo.lineFeature.spec
 * @property {geo.geoPosition|function} [position] Position of the data.
 *   Default is (data).
 * @property {object|function} [line] Lines from the data.  Default is (data).
 *   Typically, the data is an array of lines, each of which is an array of
 *   points.  Only lines that have at least two points are rendered.  The
 *   position function is called for each point as `position(linePoint,
 *   pointIndex, lineEntry, lineEntryIndex)`.
 * @property {geo.lineFeature.styleSpec} [style] Style object with default
 *   style options.
 */

/**
 * Style specification for a line feature.
 *
 * @typedef {geo.feature.styleSpec} geo.lineFeature.styleSpec
 * @extends geo.feature.styleSpec
 * @property {geo.geoColor|function} [strokeColor] Color to stroke each line.
 *   The color can vary by point.
 * @property {number|function} [strokeOpacity] Opacity for each line stroke.
 *   The opacity can vary by point.  Opacity is on a [0-1] scale.
 * @property {number|function} [strokeWidth] The weight of the line stroke in
 *   pixels.  The width can vary by point.
 * @property {number|function} [strokeOffset] This is a value from -1 (left) to
 *   1 (right), with 0 being centered.  This can vary by point.
 * @property {string|function} [lineCap='butt'] One of 'butt', 'square', or
 *   'round'.  This can vary by point.
 * @property {string|function} [lineJoin='miter'] One of 'miter', 'bevel',
 *   'round', or 'miter-clip'.  This can vary by point.
 * @property {boolean|function} [closed=false] If true and the renderer
 *   supports it, connect the first and last points of a line if the line has
 *   more than two points.  This applies per line (if a function, it is called
 *   with `(lineEntry, lineEntryIndex)`.
 * @property {number|function} [miterLimit=10] For lines of more than two
 *   segments that are mitered, if the miter length exceeds the `strokeWidth`
 *   divided by the sine of half the angle between segments, then a bevel join
 *   is used instead.  This is a single value that applies to all lines.  If a
 *   function, it is called with `(data)`.
 * @property {number|function} [antialiasing] Antialiasing distance in pixels.
 *   Values must be non-negative.  A value greater than 1 will produce a
 *   visible gradient.  This is a single value that applies to all lines.
 * @property {string|function} [debug] If 'debug', render lines in debug mode.
 *   This is a single value that applies to all lines.
 */

/**
 * Create a new instance of class lineFeature.
 *
 * @class
 * @alias geo.lineFeature
 * @extends geo.feature
 * @param {geo.lineFeature.spec} arg
 * @returns {geo.lineFeature}
 */
var lineFeature = function lineFeature(arg) {
  'use strict';

  if (!(this instanceof lineFeature)) {
    return new lineFeature(arg);
  }

  var $ = __webpack_require__(2);

  arg = arg || {};
  feature.call(this, arg);

  /**
   * @private
   */
  var m_this = this,
      s_init = this._init,
      m_pointSearchTime = timestamp(),
      m_pointSearchInfo;

  this.featureType = 'line';
  this._subfeatureStyles = {
    lineCap: true,
    lineJoin: true,
    strokeColor: true,
    strokeOffset: true,
    strokeOpacity: true,
    strokeWidth: true
  };

  /**
   * Get/set line accessor.
   *
   * @param {object|function} [val] If not specified, return the current line
   *    accessor.  If specified, use this for the line accessor and return
   *    `this`.  If a function is given, the function is passed `(dataElement,
   *    dataIndex)` and returns an array of vertex elements.
   * @returns {object|function|this} The current line accessor or this feature.
   */
  this.line = function (val) {
    if (val === undefined) {
      return m_this.style('line');
    } else {
      m_this.style('line', val);
      m_this.dataTime().modified();
      m_this.modified();
    }
    return m_this;
  };

  /**
   * Get/Set position accessor.
   *
   * @param {geo.geoPosition|function} [val] If not specified, return the
   *    current position accessor.  If specified, use this for the position
   *    accessor and return `this`.  If a function is given, this is called
   *    with `(vertexElement, vertexIndex, dataElement, dataIndex)`.
   * @returns {geo.geoPosition|function|this} The current position or this
   *    feature.
   */
  this.position = function (val) {
    if (val === undefined) {
      return m_this.style('position');
    } else {
      m_this.style('position', val);
      m_this.dataTime().modified();
      m_this.modified();
    }
    return m_this;
  };

  /**
   * Cache information needed for point searches.  The point search
   * information record is an array with one entry per line, each entry of
   * which is an array with one entry per line segment.  These each contain
   * an object with the end coordinates (`u`, `v`) of the segment in map gcs
   * coordinates and the square of the maximum half-width that needs to be
   * considered for the line (`r2`).
   *
   * @returns {object} The point search information record.
   */
  this._updatePointSearchInfo = function () {
    if (m_pointSearchTime.getMTime() >= m_this.dataTime().getMTime() && m_pointSearchTime.getMTime() >= m_this.getMTime()) {
      return m_pointSearchInfo;
    }
    m_pointSearchTime.modified();
    m_pointSearchInfo = [];
    var data = m_this.data(),
        line = m_this.line(),
        widthFunc = m_this.style.get('strokeWidth'),
        posFunc = m_this.position(),
        closedFunc = m_this.style.get('closed'),
        gcs = m_this.gcs(),
        mapgcs = m_this.layer().map().gcs();

    data.forEach(function (d, index) {
      var closed = closedFunc(d, index),
          last,
          lastr2,
          first,
          record = [];

      line(d, index).forEach(function (current, j) {
        var p = posFunc(current, j, d, index);
        if (gcs !== mapgcs) {
          p = transform.transformCoordinates(gcs, mapgcs, p);
        }
        var r = Math.ceil(widthFunc(current, j, d, index) / 2) + 2;
        var r2 = r * r;
        if (last) {
          record.push({ u: p, v: last, r2: lastr2 > r2 ? lastr2 : r2 });
        }
        last = p;
        lastr2 = r2;
        if (!first && closed) {
          first = { p: p, r2: r2 };
        }
      });
      if (closed && first && (last.x !== first.p.x || last.y !== first.p.y)) {
        record.push({ u: last, v: first.p, r2: lastr2 > first.r2 ? lastr2 : first.r2 });
      }
      m_pointSearchInfo.push(record);
    });
    return m_pointSearchInfo;
  };

  /**
   * Returns an array of datum indices that contain the given point.  This is a
   * slow implementation with runtime order of the number of vertices.  A point
   * is considered on a line segment if it is close to the line or either end
   * point.  Closeness is based on the maximum width of the line segement, and
   * is `ceil(maxwidth / 2) + 2` pixels.  This means that corner extensions
   * due to mitering may be outside of the selection area and that variable-
   * width lines will have a greater selection region than their visual size at
   * the narrow end.
   *
   * @param {geo.geoPosition} p point to search for in map interface gcs.
   * @returns {object} An object with `index`: a list of line indices, and
   *    `found`: a list of quads that contain the specified coordinate.
   */
  this.pointSearch = function (p) {
    var data = m_this.data(),
        indices = [],
        found = [];
    if (!data || !data.length || !m_this.layer()) {
      return {
        found: found,
        index: indices
      };
    }
    this._updatePointSearchInfo();
    var map = m_this.layer().map(),
        scale = map.unitsPerPixel(map.zoom()),
        scale2 = scale * scale,
        pt = transform.transformCoordinates(map.ingcs(), map.gcs(), p),
        i,
        j,
        record;

    for (i = 0; i < m_pointSearchInfo.length; i += 1) {
      record = m_pointSearchInfo[i];
      for (j = 0; j < record.length; j += 1) {
        if (util.distance2dToLineSquared(pt, record[j].u, record[j].v) <= record[j].r2 * scale2) {
          found.push(data[i]);
          indices.push(i);
          break;
        }
      }
    }
    return {
      found: found,
      index: indices
    };
  };

  /**
   * Search for lines contained within a rectangilar region.
   *
   * @param {geo.geoPosition} lowerLeft Lower-left corner in gcs coordinates.
   * @param {geo.geoPosition} upperRight Upper-right corner in gcs coordinates.
   * @param {object} [opts] Additional search options.
   * @param {boolean} [opts.partial=false] If truthy, include lines that are
   *    partially in the box, otherwise only include lines that are fully
   *    within the region.
   * @returns {number[]} A list of line indices that are in the box region.
   */
  this.boxSearch = function (lowerLeft, upperRight, opts) {
    var pos = m_this.position(),
        idx = [],
        line = m_this.line();

    opts = opts || {};
    opts.partial = opts.partial || false;
    if (opts.partial) {
      throw new Error('Unimplemented query method.');
    }

    m_this.data().forEach(function (d, i) {
      var inside = true;
      line(d, i).forEach(function (e, j) {
        if (!inside) {
          return;
        }
        var p = pos(e, j, d, i);
        if (!(p.x >= lowerLeft.x && p.x <= upperRight.x && p.y >= lowerLeft.y && p.y <= upperRight.y)) {
          inside = false;
        }
      });
      if (inside) {
        idx.push(i);
      }
    });
    return idx;
  };

  /**
   * Take a set of data, reduce the number of vertices per line using the
   * Ramer–Douglas–Peucker algorithm, and use the result as the new data.
   * This changes the instance's data, the position accessor, and the line
   * accessor.
   *
   * @param {array} data A new data array.
   * @param {number} [tolerance] The maximum variation allowed in map.gcs
   *    units.  A value of zero will only remove perfectly colinear points.  If
   *    not specified, this is set to a half display pixel at the map's current
   *    zoom level.
   * @param {function} [posFunc=this.style.get('position')] The function to
   *    get the position of each vertex.
   * @param {function} [lineFunc=this.style.get('line')] The function to get
   *    each line.
   * @returns {this}
   */
  this.rdpSimplifyData = function (data, tolerance, posFunc, lineFunc) {
    data = data || m_this.data();
    posFunc = posFunc || m_this.style.get('position');
    lineFunc = lineFunc || m_this.style.get('line');
    var map = m_this.layer().map(),
        mapgcs = map.gcs(),
        featuregcs = m_this.gcs(),
        closedFunc = m_this.style.get('closed');
    if (tolerance === undefined) {
      tolerance = map.unitsPerPixel(map.zoom()) * 0.5;
    }

    /* transform the coordinates to the map gcs */
    data = data.map(function (d, idx) {
      var lineItem = lineFunc(d, idx),
          pts = transform.transformCoordinates(featuregcs, mapgcs, lineItem.map(function (ld, lidx) {
        return posFunc(ld, lidx, d, idx);
      })),
          elem = util.rdpLineSimplify(pts, tolerance, closedFunc(d, idx), []);
      if (elem.length < 2 || elem.length === 2 && util.distance2dSquared(elem[0], elem[1]) < tolerance * tolerance) {
        elem = [];
      }
      elem = transform.transformCoordinates(mapgcs, featuregcs, elem);
      /* Copy element properties, as they might be used by styles */
      for (var key in d) {
        if (d.hasOwnProperty(key) && !(Array.isArray(d) && key >= 0 && key < d.length)) {
          elem[key] = d[key];
        }
      }
      return elem;
    });

    /* Set the reduced lines as the data and use simple accessors. */
    m_this.style('position', function (d) {
      return d;
    });
    m_this.style('line', function (d) {
      return d;
    });
    m_this.data(data);
    return m_this;
  };

  /**
   * Initialize.
   *
   * @param {geo.lineFeature.spec} arg The feature specification.
   * @returns {this}
   */
  this._init = function (arg) {
    arg = arg || {};
    s_init.call(m_this, arg);

    var defaultStyle = $.extend({}, {
      strokeWidth: 1.0,
      // Default to gold color for lines
      strokeColor: { r: 1.0, g: 0.8431372549, b: 0.0 },
      strokeStyle: 'solid',
      strokeOpacity: 1.0,
      // Values of 2 and above appear smoothest.
      antialiasing: 2.0,
      closed: false,
      line: function line(d) {
        return d;
      },
      position: function position(d) {
        return d;
      }
    }, arg.style === undefined ? {} : arg.style);

    if (arg.line !== undefined) {
      defaultStyle.line = arg.line;
    }

    if (arg.position !== undefined) {
      defaultStyle.position = arg.position;
    }

    m_this.style(defaultStyle);

    m_this.dataTime().modified();
    return m_this;
  };

  this._init(arg);
  return this;
};

/**
 * Create a lineFeature.
 *
 * @see {@link geo.feature.create}
 * @param {geo.layer} layer The layer to add the feature to
 * @param {geo.lineFeature.spec} spec The feature specification
 * @returns {geo.lineFeature|null} The created feature or `null` for failure.
 */
lineFeature.create = function (layer, spec) {
  'use strict';

  spec = spec || {};
  spec.type = 'line';
  return feature.create(layer, spec);
};

lineFeature.capabilities = {
  /* core feature name -- support in any manner */
  feature: 'line',
  /* support for solid-colored, constant-width lines */
  basic: 'line.basic',
  /* support for lines that vary in width and color */
  multicolor: 'line.multicolor'
};

inherit(lineFeature, feature);
module.exports = lineFeature;

/***/ }),
/* 24 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var inherit = __webpack_require__(0);
var sceneObject = __webpack_require__(13);
var feature = __webpack_require__(8);
var checkRenderer = __webpack_require__(1).checkRenderer;
var rendererForFeatures = __webpack_require__(1).rendererForFeatures;
var rendererForAnnotations = __webpack_require__(1).rendererForAnnotations;

/**
 * Create a new layer.
 *
 * @class
 * @alias geo.layer
 * @extends geo.sceneObject
 * @param {object} [arg] Options for the new layer.
 * @param {number} [arg.id] The id of the layer.  Defaults to a increasing
 *   sequence.
 * @param {geo.map} [arg.map=null] Parent map of the layer.
 * @param {string|geo.renderer} [arg.renderer] Renderer to associate with the
 *   layer.  If not specified, either `arg.annotations` or `arg.features` can
 *   be used to determine the renderer.
 * @param {string[]|object} [arg.annotations] A list of annotations that will
 *   be used on this layer, used to select a renderer.  Instead of a list, if
 *   this is an object, the keys are the annotation names, and the values are
 *   each a list of modes that will be used with that annotation.  See
 *   `featuresForAnnotations` more details.  This is ignored if `arg.renderer`
 *   is specified.
 * @param {string[]} [arg.features] A list of features that will be used on
 *   this layer, used to select a renderer.  Features are the basic feature
 *   names (e.g., `'quad'`), or the feature name followed by a required
 *   capability (e.g., `'quad.image'`).  This is ignored if `arg.renderer` or
 *   `arg.annotations` is specified.
 * @param {boolean} [arg.active=true] Truthy if the layer has the `active` css
 *   class and may receive native mouse events.
 * @param {string} [arg.attribution] An attribution string to display.
 * @param {number} [arg.opacity=1] The layer opacity on a scale of [0-1].
 * @param {string} [arg.name=''] A name for the layer for user convenience.
 * @param {boolean} [arg.selectionAPI=true] Truthy if the layer can generate
 *   selection and other interaction events.
 * @param {boolean} [arg.sticky=true] Truthy if the layer should navigate with
 *   the map.
 * @param {boolean} [arg.visible=true] Truthy if the layer is visible.
 * @param {number} [arg.zIndex] The z-index to assign to the layer (defaults
 *   to the index of the layer inside the map).
 * @returns {geo.layer}
 */
var layer = function layer(arg) {
  'use strict';

  if (!(this instanceof layer)) {
    return new layer(arg);
  }
  arg = arg || {};
  sceneObject.call(this, arg);

  var $ = __webpack_require__(2);
  var timestamp = __webpack_require__(10);
  var createRenderer = __webpack_require__(1).createRenderer;
  var newLayerId = __webpack_require__(3).newLayerId;
  var geo_event = __webpack_require__(6);
  var camera = __webpack_require__(49);

  /**
   * @private
   */
  var m_this = this,
      s_exit = this._exit,
      m_id = arg.id === undefined ? layer.newLayerId() : arg.id,
      m_name = arg.name === undefined ? '' : arg.name,
      m_map = arg.map === undefined ? null : arg.map,
      m_node = null,
      m_canvas = null,
      m_renderer = null,
      m_initialized = false,
      m_rendererName = arg.renderer !== undefined ? arg.renderer : arg.annotations ? rendererForAnnotations(arg.annotations) : rendererForFeatures(arg.features),
      m_dataTime = timestamp(),
      m_updateTime = timestamp(),
      m_sticky = arg.sticky === undefined ? true : arg.sticky,
      m_active = arg.active === undefined ? true : arg.active,
      m_opacity = arg.opacity === undefined ? 1 : arg.opacity,
      m_attribution = arg.attribution || null,
      m_visible = arg.visible === undefined ? true : arg.visible,
      m_selectionAPI = arg.selectionAPI === undefined ? true : arg.selectionAPI,
      m_zIndex;

  m_rendererName = checkRenderer(m_rendererName);

  if (!m_map) {
    throw new Error('Layers must be initialized on a map.');
  }

  /**
   * Get a list of sibling layers.  If no parent has been assigned to this
   * layer, assume that the map will be the parent.  This gets all of the
   * parent's children that are layer instances.
   *
   * @returns {geo.layer[]} A list of sibling layers.
   */
  function _siblingLayers() {
    return (m_this.parent() || m_this.map()).children().filter(function (child) {
      return child instanceof layer;
    });
  }

  /**
   * Get the name of the renderer.
   *
   * @returns {string}
   */
  this.rendererName = function () {
    return m_rendererName;
  };

  /**
   * Get or set the z-index of the layer.  The z-index controls the display
   * order of the layers in much the same way as the CSS z-index property.
   *
   * @param {number} [zIndex] The new z-index, or undefined to return the
   *    current z-index.
   * @param {boolean} [allowDuplicate] When setting the z index, if this is
   *    truthy, allow other layers to have the same z-index.  Otherwise,
   *    ensure that other layers have distinct z-indices from this one.
   * @returns {number|this}
   */
  this.zIndex = function (zIndex, allowDuplicate) {
    if (zIndex === undefined) {
      return m_zIndex;
    }
    if (!allowDuplicate) {
      // if any extant layer has the same index, then we move all of those
      // layers up.  We do this in reverse order since, if two layers above
      // this one share a z-index, they will resolve to the layer insert order.
      _siblingLayers().reverse().forEach(function (child) {
        if (child !== this && child.zIndex() === zIndex) {
          child.zIndex(zIndex + 1);
        }
      });
    }
    m_zIndex = zIndex;
    m_node.css('z-index', m_zIndex);
    console.log(`Set zIndex to ${m_zIndex}`);
    return m_this;
  };

  /**
   * Bring the layer above the given number of layers.  This will rotate the
   * current z-indices for this and the next `n` layers.
   *
   * @param {number} [n=1] The number of positions to move.
   * @returns {this}
   */
  this.moveUp = function (n) {
    var order,
        i,
        me = null,
        tmp,
        sign;

    // set the default
    if (n === undefined) {
      n = 1;
    }

    // set the sort direction that controls if we are moving up
    // or down the z-index
    sign = 1;
    if (n < 0) {
      sign = -1;
      n = -n;
    }

    // get a sorted list of layers
    order = _siblingLayers().sort(function (a, b) {
      return sign * (a.zIndex() - b.zIndex());
    });

    for (i = 0; i < order.length; i += 1) {
      if (me === null) {
        // loop until we get to the current layer
        if (order[i] === m_this) {
          me = i;
        }
      } else if (i - me <= n) {
        // swap the next n layers
        tmp = m_this.zIndex();
        m_this.zIndex(order[i].zIndex(), true);
        order[i].zIndex(tmp, true);
      } else {
        // all the swaps are done now
        break;
      }
    }
    return m_this;
  };

  /**
   * Bring the layer below the given number of layers.  This will rotate the
   * current z-indices for this and the previous `n` layers.
   *
   * @param {number} [n=1] The number of positions to move.
   * @returns {this}
   */
  this.moveDown = function (n) {
    if (n === undefined) {
      n = 1;
    }
    return m_this.moveUp(-n);
  };

  /**
   * Bring the layer to the top of the map layers.
   *
   * @returns {this}
   */
  this.moveToTop = function () {
    return m_this.moveUp(_siblingLayers().length - 1);
  };

  /**
   * Bring the layer to the bottom of the map layers.
   *
   * @returns {this}
   */
  this.moveToBottom = function () {
    return m_this.moveDown(_siblingLayers().length - 1);
  };

  /**
   * Get whether or not the layer is sticky (navigates with the map).
   *
   * @returns {boolean}
   */
  this.sticky = function () {
    return m_sticky;
  };

  /**
   * Get/Set whether or not the layer is active.  An active layer will receive
   * native mouse when the layer is on top.  Non-active layers will never
   * receive native mouse events.
   *
   * @param {boolean} [arg] If specified, the new `active` value.
   * @returns {boolean|object}
   */
  this.active = function (arg) {
    if (arg === undefined) {
      return m_active;
    }
    if (m_active !== arg) {
      m_active = arg;
      m_node.toggleClass('active', m_active);
    }
    return this;
  };

  /**
   * Get root node of the layer.
   *
   * @returns {div}
   */
  this.node = function () {
    return m_node;
  };

  /**
   * Get/Set id of the layer.
   *
   * @param {string} [val] If specified, the new id of the layer.
   * @returns {string|this}
   */
  this.id = function (val) {
    if (val === undefined) {
      return m_id;
    }
    m_id = newLayerId();
    m_this.modified();
    return m_this;
  };

  /**
   * Get/Set name of the layer.
   *
   * @param {string} [val] If specified, the new name of the layer.
   * @returns {string|this}
   */
  this.name = function (val) {
    if (val === undefined) {
      return m_name;
    }
    m_name = val;
    m_this.modified();
    return m_this;
  };

  /**
   * Get the map associated with this layer.
   *
   * @returns {geo.map} The map associated with the layer.
   */
  this.map = function () {
    return m_map;
  };

  /**
   * Get renderer for the layer.
   *
   * @returns {geo.renderer} The renderer associated with the layer or `null`
   *    if there is no renderer.
   */
  this.renderer = function () {
    return m_renderer;
  };

  /**
   * Get canvas of the layer.
   *
   * @returns {HTMLCanvasElement} The canvas element associated with the
   *    layer.
   */
  this.canvas = function () {
    return m_canvas;
  };

  /**
   * Return last time data got changed.
   *
   * @returns {geo.timestamp} The data time.
   */
  this.dataTime = function () {
    return m_dataTime;
  };

  /**
   * Return the modified time for the last update that did something.
   *
   * @returns {geo.timestamp} The update time.
   */
  this.updateTime = function () {
    return m_updateTime;
  };

  /**
   * Get/Set if the layer has been initialized.
   *
   * @param {boolean} [val] If specified, update the intialized value.
   *    Otherwise, return it.
   * @returns {boolean|this} Either the initialized value or this.
   */
  this.initialized = function (val) {
    if (val !== undefined) {
      m_initialized = val;
      return m_this;
    }
    return m_initialized;
  };

  /**
   * Transform coordinates from world coordinates into a local coordinate
   * system specific to the underlying renderer.  This method is exposed
   * to allow direct access the rendering context, but otherwise should
   * not be called directly.  The default implementation is the identity
   * operator.
   *
   * @param {geo.geoPosition} input World coordinates.
   * @returns {geo.geoPosition} Renderer coordinates.
   */
  this.toLocal = function (input) {
    if (m_this._toLocalMatrix) {
      camera.applyTransform(m_this._toLocalMatrix, input);
    }
    return input;
  };

  /**
   * Transform coordinates from a local coordinate system to world coordinates.
   *
   * @param {geo.geoPosition} input Renderer coordinates.
   * @returns {geo.geoPosition} World coordinates.
   */
  this.fromLocal = function (input) {
    if (m_this._fromLocalMatrix) {
      camera.applyTransform(m_this._fromLocalMatrix, input);
    }
    return input;
  };

  /**
   * Get or set the attribution html content that will displayed with the
   * layer.  By default, nothing will be displayed.  Note, this content
   * is **not** html escaped, so care should be taken when renderering
   * user provided content.
   *
   * @param {string?} arg An html fragment
   * @returns {string|this} Chainable as a setter
   */
  this.attribution = function (arg) {
    if (arg !== undefined) {
      m_attribution = arg;
      m_this.map().updateAttribution();
      return m_this;
    }
    return m_attribution;
  };

  /**
   * Get/Set visibility of the layer.
   *
   * @param {boolean} [val] If specified, change the visibility.  Otherwise,
   *    get it.
   * @returns {boolean|this} either the visibility (if getting) or the layer
   *    (if setting).
   */
  this.visible = function (val) {
    if (val === undefined) {
      return m_visible;
    }
    if (m_visible !== val) {
      m_visible = val;
      m_node.css('display', m_visible ? '' : 'none');
      m_this.modified();
    }
    return m_this;
  };

  /**
   * Get/Set selectionAPI of the layer.
   *
   * @param {boolean} [val] If specified, set the selectionAPI state, otherwise
   *    return it.
   * @returns {boolean|this} Either the selectionAPI state or the layer.
   */
  this.selectionAPI = function (val) {
    if (val === undefined) {
      return m_selectionAPI;
    }
    if (m_selectionAPI !== val) {
      m_selectionAPI = val;
    }
    return m_this;
  };

  /**
   * Init layer.
   *
   * @param {boolean} noEvents If a subclass of this intends to bind the
   *    resize, pan, and zoom events itself, set this flag to true to avoid
   *    binding them here.
   * @returns {this}
   */
  this._init = function (noEvents) {
    if (m_initialized) {
      return m_this;
    }

    m_map.node().append(m_node);

    /* Pass along the arguments, but not the map reference */
    var options = $.extend({}, arg);
    delete options.map;

    if (m_rendererName === null) {
      // if given a "null" renderer, then pass the map element as the
      // canvas
      m_renderer = null;
      m_canvas = m_node;
    } else if (m_canvas) {
      // Share context if we have valid one
      m_renderer = createRenderer(m_rendererName, m_this, m_canvas, options);
    } else {
      m_renderer = createRenderer(m_rendererName, m_this, undefined, options);
      m_canvas = m_renderer.canvas();
    }

    m_node.toggleClass('active', m_this.active());

    m_initialized = true;

    if (!noEvents) {
      // Bind events to handlers
      m_this.geoOn(geo_event.resize, function (event) {
        m_this._update({ event: event });
      });

      m_this.geoOn(geo_event.pan, function (event) {
        m_this._update({ event: event });
      });

      m_this.geoOn(geo_event.rotate, function (event) {
        m_this._update({ event: event });
      });

      m_this.geoOn(geo_event.zoom, function (event) {
        m_this._update({ event: event });
      });
    }

    return m_this;
  };

  /**
   * Clean up resources.
   */
  this._exit = function () {
    m_this.geoOff();
    if (m_renderer) {
      m_renderer._exit();
    }
    m_node.off();
    m_node.remove();
    arg = {};
    m_canvas = null;
    m_renderer = null;
    s_exit();
  };

  /**
   * Update layer.
   *
   * This is a stub that should be subclasses.
   */
  this._update = function () {};

  /**
   * Return the width of the layer in pixels.
   *
   * @returns {number} The width of the parent map in pixels.
   */
  this.width = function () {
    return m_this.map().size().width;
  };

  /**
   * Return the height of the layer in pixels.
   *
   * @returns {number} The height of the parent map in pixels.
   */
  this.height = function () {
    return m_this.map().size().height;
  };

  /**
   * Get or set the current layer opacity.  The opacity is in the range [0-1].
   *
   * @param {number} [opac] If specified, set the opacity.  Otherwise, return
   *    the opacity.
   * @returns {number|this} The current opacity or the current layer.
   */
  this.opacity = function (opac) {
    if (opac !== undefined) {
      m_opacity = opac;
      m_node.css('opacity', m_opacity);
      return m_this;
    }
    return m_opacity;
  };

  // Create top level div for the layer
  m_node = $(document.createElement('div'));
  m_node.addClass('geojs-layer');
  m_node.attr('id', m_name);
  m_this.opacity(m_opacity);

  // set the z-index (this prevents duplication)
  if (arg.zIndex === undefined) {
    var maxZ = -1;
    _siblingLayers().forEach(function (child) {
      if (child.zIndex() !== undefined) {
        maxZ = Math.max(maxZ, child.zIndex());
      }
    });
    arg.zIndex = maxZ + 1;
  }
  m_this.zIndex(arg.zIndex);

  return m_this;
};

/**
 * Gets a new id number for a layer.
 * @protected
 * @instance
 * @returns {number}
 */
layer.newLayerId = function () {
  'use strict';

  var currentId = 1;
  return function () {
    var id = currentId;
    currentId += 1;
    return id;
  };
}();

/**
 * General object specification for feature types.
 * @typedef geo.layer.spec
 * @type {object}
 * @property {string} [type='feature'] For feature compatibility with more than
 *    one kind of creatable layer
 * @property {object[]} [data=[]] The default data array to apply to each
 *    feature if none exists
 * @property {string} [renderer='vgl'] The renderer to use
 * @property {geo.feature.spec[]} [features=[]] Features to add to the layer.
 */

/**
 * Create a layer from an object.  Any errors in the creation
 * of the layer will result in returning null.
 * @param {geo.map} map The map to add the layer to
 * @param {geo.layer.spec} spec The object specification
 * @returns {geo.layer|null}
 */
layer.create = function (map, spec) {
  'use strict';

  spec = spec || {};

  // add osmLayer later
  spec.type = 'feature';
  if (spec.type !== 'feature') {
    console.warn('Unsupported layer type');
    return null;
  }

  spec.renderer = spec.renderer || 'vgl';
  spec.renderer = checkRenderer(spec.renderer);

  if (!spec.renderer) {
    console.warn('Invalid renderer');
    return null;
  }

  var layer = map.createLayer(spec.type, spec);
  if (!layer) {
    console.warn('Unable to create a layer');
    return null;
  }

  // probably move this down to featureLayer eventually
  spec.features.forEach(function (f) {
    f.data = f.data || spec.data;
    f.feature = feature.create(layer, f);
  });

  return layer;
};

inherit(layer, sceneObject);
module.exports = layer;

/***/ }),
/* 25 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var inherit = __webpack_require__(0);
var feature = __webpack_require__(8);

/**
 * Object specification for a text feature.
 *
 * @typedef {geo.feature.spec} geo.textFeature.spec
 * @property {geo.geoPosition[]|function} [position] The position of each data
 *   element.  Defaults to the `x`, `y`, and `z` properties of the data
 *   element.
 * @property {string[]|function} [text] The text of each data element.
 *   Defaults to the `text` property of the data element.
 * @property {geo.textFeature.styleSpec} [style] The style to apply to each
 *   data element.
 */

/**
 * Style specification for a text feature.
 *
 * @typedef {geo.feature.styleSpec} geo.textFeature.styleSpec
 * @extends geo.feature.styleSpec
 * @property {boolean|function} [visible=true] If falsy, don't show this data
 *    element.
 * @property {string|function} [font] A css font specification.  This is of the
 *    form `[style] [variant] [weight] [stretch] size[/line-height] family`.
 *    Individual font styles override this value if a style is specified in
 *    each.  See the individual font styles for details.
 * @property {string|function} [fontStyle='normal'] The font style.  One of
 *    `normal`, `italic`, or `oblique`.
 * @property {string|function} [fontVariant='normal'] The font variant.  This
 *    can have values such as `small-caps` or `slashed-zero`.
 * @property {string|function} [fontWeight='normal'] The font weight.  This may
 *    be a numeric value where 400 is normal and 700 is bold, or a string such
 *    as `bold` or `lighter`.
 * @property {string|function} [fontStretch='normal'] The font stretch, such as
 *    `condensed`.
 * @property {string|function} [fontSize='medium'] The font size.
 * @property {string|function} [lineHeight='normal'] The font line height.
 * @property {string|function} [fontFamily] The font family.
 * @property {string|function} [textAlign='center'] The horizontal text
 *    alignment.  One of `start`, `end`, `left`, `right`, or `center`.
 * @property {string|function} [textBaseline='middle'] The vertical text
 *    alignment.  One of `top`, `hanging`, `middle`, `alphabetic`,
 *    `ideographic`, or `bottom`.
 * @property {geo.geoColor|function} [color='black'] Text color.  May include
 *    opacity.
 * @property {number|function} [textOpacity=1] The opacity of the text.  If the
 *    color includes opacity, this is combined with that value.
 * @property {number|function} [rotation=0] Text rotation in radians.
 * @property {boolean|function} [rotateWithMap=false] If truthy, rotate the
 *    text when the map rotates.  Otherwise, the text is always in the same
 *    orientation.
 * @property {number|function} [textScaled] If defined, the text is scaled when
 *    the map zoomes and this is the basis zoom for the fontSize.
 * @property {geo.screenPosition|function} [offset] Offset from the default
 *    position for the text.  This is applied before rotation.
 * @property {geo.geoColor|function} [shadowColor='black'] Text shadow color.
 *    May include opacity.
 * @property {geo.screenPosition|function} [shadowOffset] Offset for a text
 *    shadow.  This is applied before rotation.
 * @property {number|null|function} [shadowBlur] If not null, add a text shadow
 *    with this much blur.
 * @property {boolean|function} [shadowRotate=false] If truthy, rotate the
 *    shadow offset based on the text rotation (the `shadowOffset` is the
 *    offset if the text has a 0 rotation).
 * @property {geo.geoColor|function} [textStrokeColor='transparent'] Text
 *    stroke color.  May include opacity.
 * @property {geo.geoColor|function} [textStrokeWidth=0] Text stroke width in
 *    pixels.
 * @property {number|function} [renderThreshold] If this is a positive number,
 *    text elements may not be rendered if their base position (before offset
 *    and font effects are applied) is more than this distance in pixels
 *    outside of the current viewport.  If it is known that such text elements
 *    cannot affect the current viewport, setting this can speed up rendering.
 *    This is computed once for the whole feature.
 */

/**
 * Create a new instance of class textFeature.
 *
 * @class
 * @alias geo.textFeature
 * @extends geo.feature
 *
 * @param {geo.textFeature.spec} [arg] Options for the feature.
 * @returns {geo.textFeature} The created feature.
 */
var textFeature = function textFeature(arg) {
  'use strict';

  if (!(this instanceof textFeature)) {
    return new textFeature(arg);
  }
  arg = arg || {};
  feature.call(this, arg);

  var $ = __webpack_require__(2);

  /**
   * @private
   */
  var m_this = this,
      s_init = this._init;

  this.featureType = 'text';

  /**
   * Get/Set position.
   *
   * @param {array|function} [val] If `undefined`, return the current position
   *    setting.  Otherwise, modify the current position setting.
   * @returns {array|function|this} The current position or this feature.
   */
  this.position = function (val) {
    if (val === undefined) {
      return m_this.style('position');
    } else if (val !== m_this.style('position')) {
      m_this.style('position', val);
      m_this.dataTime().modified();
      m_this.modified();
    }
    return m_this;
  };

  /**
   * Get/Set text.
   *
   * @param {array|function} [val] If `undefined`, return the current text
   *    setting.  Otherwise, modify the current text setting.
   * @returns {array|function|this} The current text or this feature.
   */
  this.text = function (val) {
    if (val === undefined) {
      return m_this.style('text');
    } else if (val !== m_this.style('text')) {
      m_this.style('text', val);
      m_this.dataTime().modified();
      m_this.modified();
    }
    return m_this;
  };

  /**
   * Initialize.
   *
   * @param {geo.textFeature.spec} [arg] The feature specification.
   */
  this._init = function (arg) {
    arg = arg || {};
    s_init.call(m_this, arg);

    var style = $.extend({}, {
      font: 'bold 16px sans-serif',
      textAlign: 'center',
      textBaseline: 'middle',
      color: { r: 0, g: 0, b: 0 },
      rotation: 0, /* in radians */
      rotateWithMap: false,
      textScaled: false,
      position: function position(d) {
        return d;
      },
      text: function text(d) {
        return d.text;
      }
    }, arg.style === undefined ? {} : arg.style);

    if (arg.position !== undefined) {
      style.position = arg.position;
    }
    if (arg.text !== undefined) {
      style.text = arg.text;
    }

    m_this.style(style);
    if (style.position) {
      m_this.position(style.position);
    }
    if (style.text) {
      m_this.text(style.text);
    }
    m_this.dataTime().modified();
  };

  this._init(arg);
  return m_this;
};

textFeature.usedStyles = ['visible', 'font', 'fontStyle', 'fontVariant', 'fontWeight', 'fontStretch', 'fontSize', 'lineHeight', 'fontFamily', 'textAlign', 'textBaseline', 'color', 'textOpacity', 'rotation', 'rotateWithMap', 'textScaled', 'offset', 'shadowColor', 'shadowOffset', 'shadowBlur', 'shadowRotate', 'textStrokeColor', 'textStrokeWidth'];

/**
 * Create a textFeature from an object.
 * @see {@link geo.feature.create}
 * @param {geo.layer} layer The layer to add the feature to
 * @param {geo.textFeature.spec} spec The object specification
 * @returns {geo.textFeature|null}
 */
textFeature.create = function (layer, spec) {
  'use strict';

  spec = spec || {};
  spec.type = 'text';
  return feature.create(layer, spec);
};

textFeature.capabilities = {
  /* core feature name -- support in any manner */
  feature: 'text'
};

inherit(textFeature, feature);
module.exports = textFeature;

/***/ }),
/* 26 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony export (immutable) */ __webpack_exports__["a"] = match;
var ignoredChar = /[\s_\-\/\(\)]/g;
function match(obj, key) {
  if (obj[key]) {
    return obj[key];
  }
  var keys = Object.keys(obj);
  var lkey = key.toLowerCase().replace(ignoredChar, '');
  var i = -1;
  var testkey, processedKey;
  while (++i < keys.length) {
    testkey = keys[i];
    processedKey = testkey.toLowerCase().replace(ignoredChar, '');
    if (processedKey === lkey) {
      return obj[testkey];
    }
  }
}


/***/ }),
/* 27 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__constants_values__ = __webpack_require__(4);


/* harmony default export */ __webpack_exports__["a"] = (function(eccent, phi, sinphi) {
  var con = eccent * sinphi;
  var com = 0.5 * eccent;
  con = Math.pow(((1 - con) / (1 + con)), com);
  return (Math.tan(0.5 * (__WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */] - phi)) / con);
});


/***/ }),
/* 28 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__constants_values__ = __webpack_require__(4);


/* harmony default export */ __webpack_exports__["a"] = (function(eccent, ts) {
  var eccnth = 0.5 * eccent;
  var con, dphi;
  var phi = __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */] - 2 * Math.atan(ts);
  for (var i = 0; i <= 15; i++) {
    con = eccent * Math.sin(phi);
    dphi = __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */] - 2 * Math.atan(ts * (Math.pow(((1 - con) / (1 + con)), eccnth))) - phi;
    phi += dphi;
    if (Math.abs(dphi) <= 0.0000000001) {
      return phi;
    }
  }
  //console.log("phi2z has NoConvergence");
  return -9999;
});


/***/ }),
/* 29 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony default export */ __webpack_exports__["a"] = (function(e0, e1, e2, e3, phi) {
  return (e0 * phi - e1 * Math.sin(2 * phi) + e2 * Math.sin(4 * phi) - e3 * Math.sin(6 * phi));
});

/***/ }),
/* 30 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony default export */ __webpack_exports__["a"] = (function(x) {
  return (1 - 0.25 * x * (1 + x / 16 * (3 + 1.25 * x)));
});

/***/ }),
/* 31 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony default export */ __webpack_exports__["a"] = (function(x) {
  return (0.375 * x * (1 + 0.25 * x * (1 + 0.46875 * x)));
});

/***/ }),
/* 32 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony default export */ __webpack_exports__["a"] = (function(x) {
  return (0.05859375 * x * x * (1 + 0.75 * x));
});

/***/ }),
/* 33 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony default export */ __webpack_exports__["a"] = (function(x) {
  return (x * x * x * (35 / 3072));
});

/***/ }),
/* 34 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var inherit = __webpack_require__(0);
var feature = __webpack_require__(8);

/**
 * Object specification for a point feature.
 *
 * @typedef {geo.feature.spec} geo.pointFeature.spec
 * @property {geo.geoPosition|function} [position] Position of the data.
 *   Default is (data).
 * @property {geo.pointFeature.styleSpec} [style] Style object with default
 *   style options.
 * @property {boolean|geo.pointFeature.clusteringSpec} [clustering=false]
 *   Enable point clustering.
 * @property {string} [primitiveShape='sprite'] For the gl renderer, select the
 *   primitive shape.  This is one of `'triangle'`, `'square'`, or `'sprite'`.
 *   `sprite` uses the least memory, `triangle` is fastest if the vertex shader
 *   is the bottleneck, and `square` is fastest if the fragment shader is the
 *   bottleneck.  `sprite` may not work for very large points.
 * @property {boolean} [dynamicDraw=false] For the gl renderer, if this is
 *   truthy, webgl source buffers can be modifies and updated directly.
 */

/**
 * Style specification for a point feature.
 *
 * @typedef {geo.feature.styleSpec} geo.pointFeature.styleSpec
 * @extends geo.feature.styleSpec
 * @property {number|function} [radius=5] Radius of each point in pixels.  This
 *   is the fill radius inside of the stroke.
 * @property {boolean|function} [stroke=true] True to stroke point.
 * @property {geo.geoColor|function} [strokeColor] Color to stroke each point.
 * @property {number|function} [strokeOpacity=1] Opacity for each point's
 *   stroke.  Opacity is on a [0-1] scale.
 * @property {number|function} [strokeWidth=1.25] The weight of the point's
 *   stroke in pixels.
 * @property {boolean|function} [fill=true] True to fill point.
 * @property {geo.geoColor|function} [fillColor] Color to fill each point.
 * @property {number|function} [fillOpacity=1] Opacity for each point.  Opacity
 *   is on a [0-1] scale.
 */

/**
 * Point clustering specification.
 *
 * @typedef {object} geo.pointFeature.clusteringSpec
 * @property {number} [radius=0.05] This is combined with the `width` and
 *   `height` to determine how close points need to be to each other to be
 *   clustered.
 * @property {number} [maxZoom=18] Never cluster above this zoom level.
 * @property {number} [width=256]
 * @property {number} [height=256]
 */

/**
 * Create a new instance of class pointFeature.
 *
 * @class
 * @alias geo.pointFeature
 * @extends geo.feature
 * @param {geo.pointFeature.spec} arg
 * @returns {geo.pointFeature}
 */
var pointFeature = function pointFeature(arg) {
  'use strict';

  if (!(this instanceof pointFeature)) {
    return new pointFeature(arg);
  }
  arg = arg || {};
  feature.call(this, arg);

  var $ = __webpack_require__(2);
  var timestamp = __webpack_require__(10);
  var ClusterGroup = __webpack_require__(115);
  var geo_event = __webpack_require__(6);
  var util = __webpack_require__(3);
  var kdbush = __webpack_require__(278);

  /**
   * @private
   */
  var m_this = this,
      s_init = this._init,
      m_rangeTree = null,
      m_rangeTreeTime = timestamp(),
      s_data = this.data,
      m_maxRadius = 0,
      m_clustering = arg.clustering,
      m_clusterTree = null,
      m_allData = [],
      m_lastZoom = null,
      m_ignoreData = false; // flag to ignore data() calls made locally

  this.featureType = 'point';

  /**
   * Get/Set clustering option.
   *
   * @param {boolean|geo.pointFeature.clusteringSpec} [val] If not specified,
   *   return the current value.  If specified and falsy, turn off clustering.
   *   If `true`, use a default clustering with `radius` set to `0.01`.
   *   Otherwise, turn on clustering with these options.
   * @returns {geo.pointFeature.clusteringSpec|boolean|this}
   */
  this.clustering = function (val) {
    if (val === undefined) {
      return m_clustering;
    }
    if (m_clustering && !val) {
      // Throw out the cluster tree and reset the data
      m_clusterTree = null;
      m_clustering = false;
      s_data(m_allData);
    } else if (val && m_clustering !== val) {
      // Generate the cluster tree
      m_clustering = val;
      m_this._clusterData();
    }
    return m_this;
  };

  /**
   * Generate the clustering tree from positions.  This might be async in the
   * future.
   */
  this._clusterData = function () {
    if (!m_clustering) {
      // clustering is not enabled, so this is a no-op
      return;
    }

    // set clustering options to default if an options argument wasn't supplied
    var opts = m_clustering === true ? { radius: 0.01 } : m_clustering;

    // generate the cluster tree from the raw data
    var position = m_this.position();
    m_clusterTree = new ClusterGroup(opts, m_this.layer().width(), m_this.layer().height());

    m_allData.forEach(function (d, i) {

      // for each point in the data set normalize the coordinate
      // representation and add the point to the cluster treee
      var pt = util.normalizeCoordinates(position(d, i));
      pt.index = i;
      m_clusterTree.addPoint(pt);
    });

    // reset the last zoom state and trigger a redraw at the current zoom level
    m_lastZoom = null;
    m_this._handleZoom(m_this.layer().map().zoom());
  };

  /**
   * Handle zoom events for clustering.  This keeps track of the last
   * clustering level, and only regenerates the displayed points when the
   * zoom level changes.
   *
   * @param {number} zoom The new zoom level.
   */
  this._handleZoom = function (zoom) {
    // get the current zoom level rounded down
    var z = Math.floor(zoom);

    if (!m_clustering || z === m_lastZoom) {
      // short cut when there is nothing to do
      return;
    }

    // store the current zoom level privately
    m_lastZoom = z;

    // get the raw data elements for the points at the current level
    var data = m_clusterTree.points(z).map(function (d) {
      return m_allData[d.index];
    });

    // append the clusters at the current level
    m_clusterTree.clusters(z).forEach(function (d) {
      // mark the datum as a cluster for accessor methods
      d.__cluster = true;

      // store all of the data objects for each point in the cluster as __data
      d.__data = [];
      d.obj.each(function (e) {
        d.__data.push(m_allData[e.index]);
      });
      data.push(d);
    });

    // prevent recomputing the clustering and set the new data array
    m_ignoreData = true;
    m_this.data(data);
  };

  /**
   * Get/Set position.
   *
   * @param {function|geo.geoPosition} [val]  If not specified, return the
   *    position accessor, which is guaranteed to be a function.  If specified,
   *    wrap the value in an function that handles clustering if it is enabled
   *    and set the position accessor to that function.
   * @returns {this|function}
   */
  this.position = function (val) {
    if (val === undefined) {
      return m_this.style('position');
    } else {
      var isFunc = util.isFunction(val);
      m_this.style('position', function (d, i) {
        if (d !== null && d !== undefined && d.__cluster) {
          return d;
        } else if (isFunc) {
          return val(d, i);
        } else {
          return val;
        }
      });
      m_this.dataTime().modified();
      m_this.modified();
    }
    return m_this;
  };

  /**
   * Update the current range tree object.  Should be called whenever the
   * data changes.
   */
  this._updateRangeTree = function () {
    if (m_rangeTreeTime.getMTime() >= m_this.dataTime().getMTime()) {
      return;
    }
    var pts,
        position,
        radius = m_this.style.get('radius'),
        stroke = m_this.style.get('stroke'),
        strokeWidth = m_this.style.get('strokeWidth');

    position = m_this.position();

    m_maxRadius = 0;

    // create an array of positions in geo coordinates
    pts = m_this.data().map(function (d, i) {
      var pt = position(d, i);

      // store the maximum point radius
      m_maxRadius = Math.max(m_maxRadius, radius(d, i) + (stroke(d, i) ? strokeWidth(d, i) : 0));

      return [pt.x, pt.y];
    });

    m_rangeTree = kdbush(pts);
    m_rangeTreeTime.modified();
  };

  /**
   * Returns an array of datum indices that contain the given point.
   * Largely adapted from wigglemaps pointQuerier:
   * https://github.com/dotskapes/wigglemaps/blob/cf5bed3fbfe2c3e48d31799462a80c564be1fb60/src/query/PointQuerier.js
   * This does not take into account clustering.
   *
   * @param {geo.geoPosition} p point to search for in map interface gcs.
   * @returns {object} An object with `index`: a list of point indices, and
   *    `found`: a list of points that contain the specified coordinate.
   */
  this.pointSearch = function (p) {
    var min,
        max,
        data,
        idx = [],
        found = [],
        ifound = [],
        map,
        pt,
        fgcs = m_this.gcs(),
        // this feature's gcs
    corners,
        stroke = m_this.style.get('stroke'),
        strokeWidth = m_this.style.get('strokeWidth'),
        radius = m_this.style.get('radius');

    data = m_this.data();
    if (!data || !data.length) {
      return {
        found: [],
        index: []
      };
    }

    // We need to do this before we find corners, since the max radius is
    // determined then
    m_this._updateRangeTree();

    map = m_this.layer().map();
    pt = map.gcsToDisplay(p);
    // check all corners to make sure we handle rotations
    corners = [map.displayToGcs({ x: pt.x - m_maxRadius, y: pt.y - m_maxRadius }, fgcs), map.displayToGcs({ x: pt.x + m_maxRadius, y: pt.y - m_maxRadius }, fgcs), map.displayToGcs({ x: pt.x - m_maxRadius, y: pt.y + m_maxRadius }, fgcs), map.displayToGcs({ x: pt.x + m_maxRadius, y: pt.y + m_maxRadius }, fgcs)];
    min = {
      x: Math.min(corners[0].x, corners[1].x, corners[2].x, corners[3].x),
      y: Math.min(corners[0].y, corners[1].y, corners[2].y, corners[3].y)
    };
    max = {
      x: Math.max(corners[0].x, corners[1].x, corners[2].x, corners[3].x),
      y: Math.max(corners[0].y, corners[1].y, corners[2].y, corners[3].y)
    };

    // Find points inside the bounding box
    idx = m_rangeTree.range(min.x, min.y, max.x, max.y);

    // Filter by circular region
    idx.forEach(function (i) {
      var d = data[i],
          p = m_this.position()(d, i),
          dx,
          dy,
          rad,
          rad2;

      rad = radius(data[i], i);
      rad += stroke(data[i], i) ? strokeWidth(data[i], i) : 0;
      if (rad) {
        rad2 = rad * rad;
        p = map.gcsToDisplay(p, fgcs);
        dx = p.x - pt.x;
        dy = p.y - pt.y;
        if (dx * dx + dy * dy <= rad2) {
          found.push(d);
          ifound.push(i);
        }
      }
    });

    return {
      found: found,
      index: ifound
    };
  };

  /**
   * Returns an array of datum indices that are contained in the given box.
   * This does not take clustering into account.
   *
   * @param {geo.geoPosition} lowerLeft Lower-left corner in gcs coordinates.
   * @param {geo.geoPosition} upperRight Upper-right corner in gcs coordinates.
   * @returns {number[]} A list of point indices that are in the box region.
   */
  this.boxSearch = function (lowerLeft, upperRight) {
    var pos = m_this.position(),
        idx = [];
    // TODO: use the range tree
    m_this.data().forEach(function (d, i) {
      var p = pos(d, i);
      if (p.x >= lowerLeft.x && p.x <= upperRight.x && p.y >= lowerLeft.y && p.y <= upperRight.y) {
        idx.push(i);
      }
    });
    return idx;
  };

  /**
   * Overloaded data method that updates the internal range tree on write.
   * Get/Set the data array for the feature.
   *
   * @param {array} [data] A new data array or `undefined` to return the
   *    existing array.
   * @returns {array|this}
   */
  this.data = function (data) {
    if (data === undefined) {
      return s_data();
    }
    if (!m_ignoreData) {
      m_allData = data;
    }
    if (m_clustering && !m_ignoreData) {
      m_this._clusterData();
    } else {
      s_data(data);
    }
    m_ignoreData = false;
    return m_this;
  };

  /**
   * Initialize.
   *
   * @param {geo.pointFeature.spec} arg The feature specification.
   * @returns {this}
   */
  this._init = function (arg) {
    arg = arg || {};
    s_init.call(m_this, arg);

    var defaultStyle = $.extend({}, {
      radius: 5.0,
      stroke: true,
      strokeColor: { r: 0.851, g: 0.604, b: 0.0 },
      strokeWidth: 1.25,
      strokeOpacity: 1.0,
      fillColor: { r: 1.0, g: 0.839, b: 0.439 },
      fill: true,
      fillOpacity: 0.8,
      position: function position(d) {
        return d;
      }
    }, arg.style === undefined ? {} : arg.style);

    if (arg.position !== undefined) {
      defaultStyle.position = arg.position;
    }

    m_this.style(defaultStyle);
    if (defaultStyle.position) {
      m_this.position(defaultStyle.position);
    }
    m_this.dataTime().modified();

    // bind to the zoom handler for point clustering
    m_this.geoOn(geo_event.zoom, function (evt) {
      m_this._handleZoom(evt.zoomLevel);
    });
    return m_this;
  };

  return m_this;
};

/**
 * Create a pointFeature from an object.
 * @see {@link geo.feature.create}
 * @param {geo.layer} layer The layer to add the feature to
 * @param {geo.pointFeature.spec} spec The object specification
 * @returns {geo.pointFeature|null}
 */
pointFeature.create = function (layer, spec) {
  'use strict';

  spec = spec || {};
  spec.type = 'point';
  return feature.create(layer, spec);
};

pointFeature.capabilities = {
  /* core feature name -- support in any manner */
  feature: 'point'
};

inherit(pointFeature, feature);
module.exports = pointFeature;

/***/ }),
/* 35 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var inherit = __webpack_require__(0);
var layer = __webpack_require__(24);
var geo_event = __webpack_require__(6);
var registry = __webpack_require__(1);

/**
 * Layer to draw points, lines, and polygons on the map The polydata layer
 * provide mechanisms to create and draw geometrical shapes such as points,
 * lines, and polygons.
 * @class
 * @alias geo.featureLayer
 * @extends geo.layer
 * @param {object} arg Options for the new layer.
 * @returns {geo.featureLayer}
 */
var featureLayer = function featureLayer(arg) {
  'use strict';

  if (!(this instanceof featureLayer)) {
    return new featureLayer(arg);
  }
  layer.call(this, arg);

  /**
   * private
   */
  var m_this = this,
      m_features = [],
      s_init = this._init,
      s_exit = this._exit,
      s_update = this._update,
      s_visible = this.visible,
      s_selectionAPI = this.selectionAPI,
      s_draw = this.draw;

  /**
   * Create a feature by name.
   *
   * @param {string} featureName The name of the feature to create.
   * @param {object} arg Properties for the new feature.
   * @returns {geo.feature} The created feature.
   */
  this.createFeature = function (featureName, arg) {

    var newFeature = registry.createFeature(featureName, m_this, m_this.renderer(), arg);
    if (newFeature) {
      this.addFeature(newFeature);
    } else {
      console.warn('Layer renderer (' + m_this.rendererName() + ') does not support feature type "' + featureName + '"');
    }
    return newFeature;
  };

  /**
   * Add a feature to the layer if it is not already present.
   *
   * @param {object} feature the feature to add.
   * @returns {this}
   */
  this.addFeature = function (feature) {
    /* try to remove the feature first so that we don't have two copies */
    this.removeFeature(feature);
    m_this.addChild(feature);
    m_features.push(feature);
    m_this.dataTime().modified();
    m_this.modified();
    return m_this;
  };

  /**
   * Remove a feature without destroying it.
   *
   * @param {geo.feature} feature The feature to remove.
   * @returns {this}
   */
  this.removeFeature = function (feature) {
    var pos;

    pos = m_features.indexOf(feature);
    if (pos >= 0) {
      m_features.splice(pos, 1);
      m_this.removeChild(feature);
      m_this.dataTime().modified();
      m_this.modified();
    }

    return m_this;
  };

  /**
   * Delete feature.
   *
   * @param {geo.feature} feature The feature to delete.
   * @returns {this}
   */
  this.deleteFeature = function (feature) {

    // call _exit first, as destroying the feature affect other features
    if (feature) {
      if (m_features.indexOf(feature) >= 0) {
        feature._exit();
      }
      this.removeFeature(feature);
    }

    return m_this;
  };

  /**
   * Get/Set drawables.
   *
   * @param {geo.feature[]} val A list of features, or unspecified to return
   *    the current feature list.  If a list is provided, features are added or
   *    removed as needed.
   * @returns {geo.feature[]|this} The current features associated with the
   *    layer or the current layer.
   */
  this.features = function (val) {
    if (val === undefined) {
      return m_features.slice();
    } else {
      // delete existing features that aren't in the new array.  Since features
      // can affect other features during their exit process, make sure each
      // feature still exists as we work through the list.
      var existing = m_features.slice();
      var i;
      for (i = 0; i < existing.length; i += 1) {
        if (val.indexOf(existing[i]) < 0 && m_features.indexOf(existing[i]) >= 0) {
          this.deleteFeature(existing[i]);
        }
      }
      for (i = 0; i < val.length; i += 1) {
        this.addFeature(val[i]);
      }
      return m_this;
    }
  };

  /**
   * Initialize.
   *
   * @returns {this}
   */
  this._init = function () {
    if (m_this.initialized()) {
      return m_this;
    }

    // Call super class init
    s_init.call(m_this, true);

    // Bind events to handlers
    m_this.geoOn(geo_event.resize, function (event) {
      if (m_this.renderer()) {
        m_this.renderer()._resize(event.x, event.y, event.width, event.height);
        m_this._update({ event: event });
        m_this.renderer()._render();
      } else {
        m_this._update({ event: event });
      }
    });

    m_this.geoOn(geo_event.pan, function (event) {
      m_this._update({ event: event });
      if (m_this.renderer()) {
        m_this.renderer()._render();
      }
    });

    m_this.geoOn(geo_event.rotate, function (event) {
      m_this._update({ event: event });
      if (m_this.renderer()) {
        m_this.renderer()._render();
      }
    });

    m_this.geoOn(geo_event.zoom, function (event) {
      m_this._update({ event: event });
      if (m_this.renderer()) {
        m_this.renderer()._render();
      }
    });

    return m_this;
  };

  /**
   * Update layer.
   *
   * @param {object} request A value to pass to the parent class.
   * @returns {this}
   */
  this._update = function (request) {
    var i;

    if (!m_features.length) {
      return m_this;
    }

    // Call base class update
    s_update.call(m_this, request);

    if (m_this.dataTime().getMTime() > m_this.updateTime().getMTime()) {
      for (i = 0; i < m_features.length; i += 1) {
        m_features[i].renderer(m_this.renderer());
      }
    }

    for (i = 0; i < m_features.length; i += 1) {
      m_features[i]._update();
    }

    m_this.updateTime().modified();

    return m_this;
  };

  /**
   * Free all resources.
   */
  this._exit = function () {
    m_this.clear();
    s_exit();
  };

  /**
   * Draw.  If the layer is visible, call the parent class's draw function and
   * the renderer's render function.
   *
   * @returns {this}
   */
  this.draw = function () {
    if (m_this.visible()) {
      // Call sceneObject.draw, which calls draw on all child objects.
      s_draw();

      // Now call render on the renderer. In certain cases it may not do
      // anything if the child objects are drawn on the screen already.
      if (m_this.renderer()) {
        m_this.renderer()._render();
      }
    }
    return m_this;
  };

  /**
   * Get/Set visibility of the layer.
   *
   * @param {boolean} [val] If specified, change the visibility, otherwise
   *    return it.
   * @returns {boolean|this} The current visibility or the layer.
   */
  this.visible = function (val) {
    if (val === undefined) {
      return s_visible();
    }
    if (m_this.visible() !== val) {
      s_visible(val);

      // take a copy of the features; changing visible could mutate them.
      var features = m_features.slice(),
          i;

      for (i = 0; i < features.length; i += 1) {
        features[i].visible(features[i].visible(undefined, true), true);
      }
      if (val) {
        m_this.draw();
      }
    }
    return m_this;
  };

  /**
   * Get/Set selectionAPI of the layer.
   *
   * @param {boolean} [val] If specified change the selectionAPI state of the
   *    layer, otherwise return the current state.
   * @returns {boolean|this} The selectionAPI state or the current layer.
   */
  this.selectionAPI = function (val) {
    if (val === undefined) {
      return s_selectionAPI();
    }
    if (m_this.selectionAPI() !== val) {
      s_selectionAPI(val);

      // take a copy of the features; changing selectionAPI could mutate them.
      var features = m_features.slice(),
          i;

      for (i = 0; i < features.length; i += 1) {
        features[i].selectionAPI(features[i].selectionAPI(undefined, true), true);
      }
    }
    return m_this;
  };

  /**
   * Clear all features in layer.
   *
   * @returns {this}
   */
  this.clear = function () {
    while (m_features.length) {
      m_this.deleteFeature(m_features[0]);
    }
    return m_this;
  };

  return m_this;
};

inherit(featureLayer, layer);
registry.registerLayer('feature', featureLayer);
module.exports = featureLayer;

/***/ }),
/* 36 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var widget = __webpack_require__(56);
var inherit = __webpack_require__(0);
var registerWidget = __webpack_require__(1).registerWidget;

/**
 * Create a new instance of class geo.gui.svgWidget.
 *
 * Due to the nature of d3 creating DOM elements as it inserts them, calls to
 * appendChild don't appear in this widget.
 *
 * The canvas of an svgWidget always refers to the actual svg element.
 * The parentCanvas can refer to another widget's svg element, dom element, or
 * the UI layer's dom element.
 * See {@link geo.gui.widget#parentCanvas}.
 *
 * @class
 * @alias geo.gui.svgWidget
 * @extends geo.gui.widget
 * @param {object} arg
 * @param {geo.widget} [parent] A parent widget for this widget.
 * @returns {geo.gui.svgWidget}
 */
var svgWidget = function svgWidget(arg) {
  'use strict';

  if (!(this instanceof svgWidget)) {
    return new svgWidget(arg);
  }

  widget.call(this, arg);

  var d3Renderer = __webpack_require__(11);

  var m_this = this,
      s_exit = this._exit,
      m_renderer = null;

  /**
   * Initializes SVG Widget.
   *
   * @returns {this}
   */
  this._init = function () {
    var d3Parent;
    if (arg.hasOwnProperty('parent')) {
      arg.parent.addChild(m_this);

      // Tell the renderer there is an SVG element as a parent
      d3Parent = arg.parent.canvas();
    }

    m_this._createCanvas(d3Parent);

    m_this.canvas().addEventListener('mousedown', function (e) {
      e.stopPropagation();
    });

    m_this.reposition();
    return m_this;
  };

  /**
   * Clean up the widget.
   */
  this._exit = function () {
    if (m_renderer) {
      m_renderer._exit();
    }
    s_exit();
  };

  /**
   * Creates the canvas for the svg widget.
   * This directly uses the {@link geo.d3.d3Renderer} as a helper to do all of
   * the heavy lifting.
   *
   * @param {d3Selector} d3Parent The canvas's parent element.
   */
  this._createCanvas = function (d3Parent) {
    var rendererOpts = {
      layer: m_this.layer(),
      widget: true
    };

    if (d3Parent) {
      rendererOpts.d3Parent = d3Parent;
    }

    m_renderer = d3Renderer(rendererOpts);

    // svg widgets manage their own sizes, so make the resize handler a no-op
    m_renderer._resize = function () {};

    m_this.canvas(m_renderer.canvas()[0][0]);
  };

  return this;
};

inherit(svgWidget, widget);

registerWidget('dom', 'svg', svgWidget);
module.exports = svgWidget;

/***/ }),
/* 37 */
/***/ (function(module, exports) {

/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/
// css base code, injected by the css-loader
module.exports = function(useSourceMap) {
	var list = [];

	// return the list of modules as css string
	list.toString = function toString() {
		return this.map(function (item) {
			var content = cssWithMappingToString(item, useSourceMap);
			if(item[2]) {
				return "@media " + item[2] + "{" + content + "}";
			} else {
				return content;
			}
		}).join("");
	};

	// import a list of modules into the list
	list.i = function(modules, mediaQuery) {
		if(typeof modules === "string")
			modules = [[null, modules, ""]];
		var alreadyImportedModules = {};
		for(var i = 0; i < this.length; i++) {
			var id = this[i][0];
			if(typeof id === "number")
				alreadyImportedModules[id] = true;
		}
		for(i = 0; i < modules.length; i++) {
			var item = modules[i];
			// skip already imported module
			// this implementation is not 100% perfect for weird media query combinations
			//  when a module is imported multiple times with different media queries.
			//  I hope this will never occur (Hey this way we have smaller bundles)
			if(typeof item[0] !== "number" || !alreadyImportedModules[item[0]]) {
				if(mediaQuery && !item[2]) {
					item[2] = mediaQuery;
				} else if(mediaQuery) {
					item[2] = "(" + item[2] + ") and (" + mediaQuery + ")";
				}
				list.push(item);
			}
		}
	};
	return list;
};

function cssWithMappingToString(item, useSourceMap) {
	var content = item[1] || '';
	var cssMapping = item[3];
	if (!cssMapping) {
		return content;
	}

	if (useSourceMap && typeof btoa === 'function') {
		var sourceMapping = toComment(cssMapping);
		var sourceURLs = cssMapping.sources.map(function (source) {
			return '/*# sourceURL=' + cssMapping.sourceRoot + source + ' */'
		});

		return [content].concat(sourceURLs).concat([sourceMapping]).join('\n');
	}

	return [content].join('\n');
}

// Adapted from convert-source-map (MIT)
function toComment(sourceMap) {
	// eslint-disable-next-line no-undef
	var base64 = btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap))));
	var data = 'sourceMappingURL=data:application/json;charset=utf-8;base64,' + base64;

	return '/*# ' + data + ' */';
}


/***/ }),
/* 38 */
/***/ (function(module, exports, __webpack_require__) {

/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/

var stylesInDom = {};

var	memoize = function (fn) {
	var memo;

	return function () {
		if (typeof memo === "undefined") memo = fn.apply(this, arguments);
		return memo;
	};
};

var isOldIE = memoize(function () {
	// Test for IE <= 9 as proposed by Browserhacks
	// @see http://browserhacks.com/#hack-e71d8692f65334173fee715c222cb805
	// Tests for existence of standard globals is to allow style-loader
	// to operate correctly into non-standard environments
	// @see https://github.com/webpack-contrib/style-loader/issues/177
	return window && document && document.all && !window.atob;
});

var getTarget = function (target) {
  return document.querySelector(target);
};

var getElement = (function (fn) {
	var memo = {};

	return function(target) {
                // If passing function in options, then use it for resolve "head" element.
                // Useful for Shadow Root style i.e
                // {
                //   insertInto: function () { return document.querySelector("#foo").shadowRoot }
                // }
                if (typeof target === 'function') {
                        return target();
                }
                if (typeof memo[target] === "undefined") {
			var styleTarget = getTarget.call(this, target);
			// Special case to return head of iframe instead of iframe itself
			if (window.HTMLIFrameElement && styleTarget instanceof window.HTMLIFrameElement) {
				try {
					// This will throw an exception if access to iframe is blocked
					// due to cross-origin restrictions
					styleTarget = styleTarget.contentDocument.head;
				} catch(e) {
					styleTarget = null;
				}
			}
			memo[target] = styleTarget;
		}
		return memo[target]
	};
})();

var singleton = null;
var	singletonCounter = 0;
var	stylesInsertedAtTop = [];

var	fixUrls = __webpack_require__(136);

module.exports = function(list, options) {
	if (typeof DEBUG !== "undefined" && DEBUG) {
		if (typeof document !== "object") throw new Error("The style-loader cannot be used in a non-browser environment");
	}

	options = options || {};

	options.attrs = typeof options.attrs === "object" ? options.attrs : {};

	// Force single-tag solution on IE6-9, which has a hard limit on the # of <style>
	// tags it will allow on a page
	if (!options.singleton && typeof options.singleton !== "boolean") options.singleton = isOldIE();

	// By default, add <style> tags to the <head> element
        if (!options.insertInto) options.insertInto = "head";

	// By default, add <style> tags to the bottom of the target
	if (!options.insertAt) options.insertAt = "bottom";

	var styles = listToStyles(list, options);

	addStylesToDom(styles, options);

	return function update (newList) {
		var mayRemove = [];

		for (var i = 0; i < styles.length; i++) {
			var item = styles[i];
			var domStyle = stylesInDom[item.id];

			domStyle.refs--;
			mayRemove.push(domStyle);
		}

		if(newList) {
			var newStyles = listToStyles(newList, options);
			addStylesToDom(newStyles, options);
		}

		for (var i = 0; i < mayRemove.length; i++) {
			var domStyle = mayRemove[i];

			if(domStyle.refs === 0) {
				for (var j = 0; j < domStyle.parts.length; j++) domStyle.parts[j]();

				delete stylesInDom[domStyle.id];
			}
		}
	};
};

function addStylesToDom (styles, options) {
	for (var i = 0; i < styles.length; i++) {
		var item = styles[i];
		var domStyle = stylesInDom[item.id];

		if(domStyle) {
			domStyle.refs++;

			for(var j = 0; j < domStyle.parts.length; j++) {
				domStyle.parts[j](item.parts[j]);
			}

			for(; j < item.parts.length; j++) {
				domStyle.parts.push(addStyle(item.parts[j], options));
			}
		} else {
			var parts = [];

			for(var j = 0; j < item.parts.length; j++) {
				parts.push(addStyle(item.parts[j], options));
			}

			stylesInDom[item.id] = {id: item.id, refs: 1, parts: parts};
		}
	}
}

function listToStyles (list, options) {
	var styles = [];
	var newStyles = {};

	for (var i = 0; i < list.length; i++) {
		var item = list[i];
		var id = options.base ? item[0] + options.base : item[0];
		var css = item[1];
		var media = item[2];
		var sourceMap = item[3];
		var part = {css: css, media: media, sourceMap: sourceMap};

		if(!newStyles[id]) styles.push(newStyles[id] = {id: id, parts: [part]});
		else newStyles[id].parts.push(part);
	}

	return styles;
}

function insertStyleElement (options, style) {
	var target = getElement(options.insertInto)

	if (!target) {
		throw new Error("Couldn't find a style target. This probably means that the value for the 'insertInto' parameter is invalid.");
	}

	var lastStyleElementInsertedAtTop = stylesInsertedAtTop[stylesInsertedAtTop.length - 1];

	if (options.insertAt === "top") {
		if (!lastStyleElementInsertedAtTop) {
			target.insertBefore(style, target.firstChild);
		} else if (lastStyleElementInsertedAtTop.nextSibling) {
			target.insertBefore(style, lastStyleElementInsertedAtTop.nextSibling);
		} else {
			target.appendChild(style);
		}
		stylesInsertedAtTop.push(style);
	} else if (options.insertAt === "bottom") {
		target.appendChild(style);
	} else if (typeof options.insertAt === "object" && options.insertAt.before) {
		var nextSibling = getElement(options.insertInto + " " + options.insertAt.before);
		target.insertBefore(style, nextSibling);
	} else {
		throw new Error("[Style Loader]\n\n Invalid value for parameter 'insertAt' ('options.insertAt') found.\n Must be 'top', 'bottom', or Object.\n (https://github.com/webpack-contrib/style-loader#insertat)\n");
	}
}

function removeStyleElement (style) {
	if (style.parentNode === null) return false;
	style.parentNode.removeChild(style);

	var idx = stylesInsertedAtTop.indexOf(style);
	if(idx >= 0) {
		stylesInsertedAtTop.splice(idx, 1);
	}
}

function createStyleElement (options) {
	var style = document.createElement("style");

	if(options.attrs.type === undefined) {
		options.attrs.type = "text/css";
	}

	addAttrs(style, options.attrs);
	insertStyleElement(options, style);

	return style;
}

function createLinkElement (options) {
	var link = document.createElement("link");

	if(options.attrs.type === undefined) {
		options.attrs.type = "text/css";
	}
	options.attrs.rel = "stylesheet";

	addAttrs(link, options.attrs);
	insertStyleElement(options, link);

	return link;
}

function addAttrs (el, attrs) {
	Object.keys(attrs).forEach(function (key) {
		el.setAttribute(key, attrs[key]);
	});
}

function addStyle (obj, options) {
	var style, update, remove, result;

	// If a transform function was defined, run it on the css
	if (options.transform && obj.css) {
	    result = options.transform(obj.css);

	    if (result) {
	    	// If transform returns a value, use that instead of the original css.
	    	// This allows running runtime transformations on the css.
	    	obj.css = result;
	    } else {
	    	// If the transform function returns a falsy value, don't add this css.
	    	// This allows conditional loading of css
	    	return function() {
	    		// noop
	    	};
	    }
	}

	if (options.singleton) {
		var styleIndex = singletonCounter++;

		style = singleton || (singleton = createStyleElement(options));

		update = applyToSingletonTag.bind(null, style, styleIndex, false);
		remove = applyToSingletonTag.bind(null, style, styleIndex, true);

	} else if (
		obj.sourceMap &&
		typeof URL === "function" &&
		typeof URL.createObjectURL === "function" &&
		typeof URL.revokeObjectURL === "function" &&
		typeof Blob === "function" &&
		typeof btoa === "function"
	) {
		style = createLinkElement(options);
		update = updateLink.bind(null, style, options);
		remove = function () {
			removeStyleElement(style);

			if(style.href) URL.revokeObjectURL(style.href);
		};
	} else {
		style = createStyleElement(options);
		update = applyToTag.bind(null, style);
		remove = function () {
			removeStyleElement(style);
		};
	}

	update(obj);

	return function updateStyle (newObj) {
		if (newObj) {
			if (
				newObj.css === obj.css &&
				newObj.media === obj.media &&
				newObj.sourceMap === obj.sourceMap
			) {
				return;
			}

			update(obj = newObj);
		} else {
			remove();
		}
	};
}

var replaceText = (function () {
	var textStore = [];

	return function (index, replacement) {
		textStore[index] = replacement;

		return textStore.filter(Boolean).join('\n');
	};
})();

function applyToSingletonTag (style, index, remove, obj) {
	var css = remove ? "" : obj.css;

	if (style.styleSheet) {
		style.styleSheet.cssText = replaceText(index, css);
	} else {
		var cssNode = document.createTextNode(css);
		var childNodes = style.childNodes;

		if (childNodes[index]) style.removeChild(childNodes[index]);

		if (childNodes.length) {
			style.insertBefore(cssNode, childNodes[index]);
		} else {
			style.appendChild(cssNode);
		}
	}
}

function applyToTag (style, obj) {
	var css = obj.css;
	var media = obj.media;

	if(media) {
		style.setAttribute("media", media)
	}

	if(style.styleSheet) {
		style.styleSheet.cssText = css;
	} else {
		while(style.firstChild) {
			style.removeChild(style.firstChild);
		}

		style.appendChild(document.createTextNode(css));
	}
}

function updateLink (link, options, obj) {
	var css = obj.css;
	var sourceMap = obj.sourceMap;

	/*
		If convertToAbsoluteUrls isn't defined, but sourcemaps are enabled
		and there is no publicPath defined then lets turn convertToAbsoluteUrls
		on by default.  Otherwise default to the convertToAbsoluteUrls option
		directly
	*/
	var autoFixUrls = options.convertToAbsoluteUrls === undefined && sourceMap;

	if (options.convertToAbsoluteUrls || autoFixUrls) {
		css = fixUrls(css);
	}

	if (sourceMap) {
		// http://stackoverflow.com/a/26603875
		css += "\n/*# sourceMappingURL=data:application/json;base64," + btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))) + " */";
	}

	var blob = new Blob([css], { type: "text/css" });

	var oldSrc = link.href;

	link.href = URL.createObjectURL(blob);

	if(oldSrc) URL.revokeObjectURL(oldSrc);
}


/***/ }),
/* 39 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/**
 * Common object containing all action types that are provided by the GeoJS
 * API.
 *
 * @namespace
 * @alias geo.action
 * @enum {string}
 */
var geo_action = {
  momentum: 'geo_action_momentum',
  pan: 'geo_action_pan',
  rotate: 'geo_action_rotate',
  select: 'geo_action_select',
  unzoomselect: 'geo_action_unzoomselect',
  zoom: 'geo_action_zoom',
  zoomrotate: 'geo_action_zoom_rotate',
  zoomselect: 'geo_action_zoomselect',

  // annotation actions
  annotation_line: 'geo_annotation_line',
  annotation_polygon: 'geo_annotation_polygon',
  annotation_rectangle: 'geo_annotation_rectangle',
  annotation_edit_handle: 'geo_annotation_edit_handle'
};

module.exports = geo_action;

/***/ }),
/* 40 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__parseCode__ = __webpack_require__(138);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__extend__ = __webpack_require__(144);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__projections__ = __webpack_require__(145);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__deriveConstants__ = __webpack_require__(146);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__constants_Datum__ = __webpack_require__(148);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__datum__ = __webpack_require__(149);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_6__match__ = __webpack_require__(26);








function Projection(srsCode,callback) {
  if (!(this instanceof Projection)) {
    return new Projection(srsCode);
  }
  callback = callback || function(error){
    if(error){
      throw error;
    }
  };
  var json = Object(__WEBPACK_IMPORTED_MODULE_0__parseCode__["a" /* default */])(srsCode);
  if(typeof json !== 'object'){
    callback(srsCode);
    return;
  }
  var ourProj = Projection.projections.get(json.projName);
  if(!ourProj){
    callback(srsCode);
    return;
  }
  if (json.datumCode && json.datumCode !== 'none') {
    var datumDef = Object(__WEBPACK_IMPORTED_MODULE_6__match__["a" /* default */])(__WEBPACK_IMPORTED_MODULE_4__constants_Datum__["a" /* default */], json.datumCode);
    if (datumDef) {
      json.datum_params = datumDef.towgs84 ? datumDef.towgs84.split(',') : null;
      json.ellps = datumDef.ellipse;
      json.datumName = datumDef.datumName ? datumDef.datumName : json.datumCode;
    }
  }
  json.k0 = json.k0 || 1.0;
  json.axis = json.axis || 'enu';
  json.ellps = json.ellps || 'wgs84';
  var sphere_ = Object(__WEBPACK_IMPORTED_MODULE_3__deriveConstants__["b" /* sphere */])(json.a, json.b, json.rf, json.ellps, json.sphere);
  var ecc = Object(__WEBPACK_IMPORTED_MODULE_3__deriveConstants__["a" /* eccentricity */])(sphere_.a, sphere_.b, sphere_.rf, json.R_A);
  var datumObj = json.datum || Object(__WEBPACK_IMPORTED_MODULE_5__datum__["a" /* default */])(json.datumCode, json.datum_params, sphere_.a, sphere_.b, ecc.es, ecc.ep2);

  Object(__WEBPACK_IMPORTED_MODULE_1__extend__["a" /* default */])(this, json); // transfer everything over from the projection because we don't know what we'll need
  Object(__WEBPACK_IMPORTED_MODULE_1__extend__["a" /* default */])(this, ourProj); // transfer all the methods from the projection

  // copy the 4 things over we calulated in deriveConstants.sphere
  this.a = sphere_.a;
  this.b = sphere_.b;
  this.rf = sphere_.rf;
  this.sphere = sphere_.sphere;

  // copy the 3 things we calculated in deriveConstants.eccentricity
  this.es = ecc.es;
  this.e = ecc.e;
  this.ep2 = ecc.ep2;

  // add in the datum object
  this.datum = datumObj;

  // init the projection
  this.init();

  // legecy callback from back in the day when it went to spatialreference.org
  callback(null, this);

}
Projection.projections = __WEBPACK_IMPORTED_MODULE_2__projections__["a" /* default */];
Projection.projections.start();
/* harmony default export */ __webpack_exports__["a"] = (Projection);


/***/ }),
/* 41 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony default export */ __webpack_exports__["a"] = (function(phi, sphi, cphi, en) {
  cphi *= sphi;
  sphi *= sphi;
  return (en[0] * phi - cphi * (en[1] + sphi * (en[2] + sphi * (en[3] + sphi * en[4]))));
});

/***/ }),
/* 42 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony export (immutable) */ __webpack_exports__["init"] = init;
/* harmony export (immutable) */ __webpack_exports__["forward"] = forward;
/* harmony export (immutable) */ __webpack_exports__["inverse"] = inverse;
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "names", function() { return names; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__common_sinh__ = __webpack_require__(43);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__common_hypot__ = __webpack_require__(70);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__common_asinhy__ = __webpack_require__(158);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__common_gatg__ = __webpack_require__(160);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__common_clens__ = __webpack_require__(161);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__common_clens_cmplx__ = __webpack_require__(162);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_6__common_adjust_lon__ = __webpack_require__(5);
// Heavily based on this etmerc projection implementation
// https://github.com/mbloch/mapshaper-proj/blob/master/src/projections/etmerc.js









function init() {
  if (this.es === undefined || this.es <= 0) {
    throw new Error('incorrect elliptical usage');
  }

  this.x0 = this.x0 !== undefined ? this.x0 : 0;
  this.y0 = this.y0 !== undefined ? this.y0 : 0;
  this.long0 = this.long0 !== undefined ? this.long0 : 0;
  this.lat0 = this.lat0 !== undefined ? this.lat0 : 0;

  this.cgb = [];
  this.cbg = [];
  this.utg = [];
  this.gtu = [];

  var f = this.es / (1 + Math.sqrt(1 - this.es));
  var n = f / (2 - f);
  var np = n;

  this.cgb[0] = n * (2 + n * (-2 / 3 + n * (-2 + n * (116 / 45 + n * (26 / 45 + n * (-2854 / 675 ))))));
  this.cbg[0] = n * (-2 + n * ( 2 / 3 + n * ( 4 / 3 + n * (-82 / 45 + n * (32 / 45 + n * (4642 / 4725))))));

  np = np * n;
  this.cgb[1] = np * (7 / 3 + n * (-8 / 5 + n * (-227 / 45 + n * (2704 / 315 + n * (2323 / 945)))));
  this.cbg[1] = np * (5 / 3 + n * (-16 / 15 + n * ( -13 / 9 + n * (904 / 315 + n * (-1522 / 945)))));

  np = np * n;
  this.cgb[2] = np * (56 / 15 + n * (-136 / 35 + n * (-1262 / 105 + n * (73814 / 2835))));
  this.cbg[2] = np * (-26 / 15 + n * (34 / 21 + n * (8 / 5 + n * (-12686 / 2835))));

  np = np * n;
  this.cgb[3] = np * (4279 / 630 + n * (-332 / 35 + n * (-399572 / 14175)));
  this.cbg[3] = np * (1237 / 630 + n * (-12 / 5 + n * ( -24832 / 14175)));

  np = np * n;
  this.cgb[4] = np * (4174 / 315 + n * (-144838 / 6237));
  this.cbg[4] = np * (-734 / 315 + n * (109598 / 31185));

  np = np * n;
  this.cgb[5] = np * (601676 / 22275);
  this.cbg[5] = np * (444337 / 155925);

  np = Math.pow(n, 2);
  this.Qn = this.k0 / (1 + n) * (1 + np * (1 / 4 + np * (1 / 64 + np / 256)));

  this.utg[0] = n * (-0.5 + n * ( 2 / 3 + n * (-37 / 96 + n * ( 1 / 360 + n * (81 / 512 + n * (-96199 / 604800))))));
  this.gtu[0] = n * (0.5 + n * (-2 / 3 + n * (5 / 16 + n * (41 / 180 + n * (-127 / 288 + n * (7891 / 37800))))));

  this.utg[1] = np * (-1 / 48 + n * (-1 / 15 + n * (437 / 1440 + n * (-46 / 105 + n * (1118711 / 3870720)))));
  this.gtu[1] = np * (13 / 48 + n * (-3 / 5 + n * (557 / 1440 + n * (281 / 630 + n * (-1983433 / 1935360)))));

  np = np * n;
  this.utg[2] = np * (-17 / 480 + n * (37 / 840 + n * (209 / 4480 + n * (-5569 / 90720 ))));
  this.gtu[2] = np * (61 / 240 + n * (-103 / 140 + n * (15061 / 26880 + n * (167603 / 181440))));

  np = np * n;
  this.utg[3] = np * (-4397 / 161280 + n * (11 / 504 + n * (830251 / 7257600)));
  this.gtu[3] = np * (49561 / 161280 + n * (-179 / 168 + n * (6601661 / 7257600)));

  np = np * n;
  this.utg[4] = np * (-4583 / 161280 + n * (108847 / 3991680));
  this.gtu[4] = np * (34729 / 80640 + n * (-3418889 / 1995840));

  np = np * n;
  this.utg[5] = np * (-20648693 / 638668800);
  this.gtu[5] = np * (212378941 / 319334400);

  var Z = Object(__WEBPACK_IMPORTED_MODULE_3__common_gatg__["a" /* default */])(this.cbg, this.lat0);
  this.Zb = -this.Qn * (Z + Object(__WEBPACK_IMPORTED_MODULE_4__common_clens__["a" /* default */])(this.gtu, 2 * Z));
}

function forward(p) {
  var Ce = Object(__WEBPACK_IMPORTED_MODULE_6__common_adjust_lon__["a" /* default */])(p.x - this.long0);
  var Cn = p.y;

  Cn = Object(__WEBPACK_IMPORTED_MODULE_3__common_gatg__["a" /* default */])(this.cbg, Cn);
  var sin_Cn = Math.sin(Cn);
  var cos_Cn = Math.cos(Cn);
  var sin_Ce = Math.sin(Ce);
  var cos_Ce = Math.cos(Ce);

  Cn = Math.atan2(sin_Cn, cos_Ce * cos_Cn);
  Ce = Math.atan2(sin_Ce * cos_Cn, Object(__WEBPACK_IMPORTED_MODULE_1__common_hypot__["a" /* default */])(sin_Cn, cos_Cn * cos_Ce));
  Ce = Object(__WEBPACK_IMPORTED_MODULE_2__common_asinhy__["a" /* default */])(Math.tan(Ce));

  var tmp = Object(__WEBPACK_IMPORTED_MODULE_5__common_clens_cmplx__["a" /* default */])(this.gtu, 2 * Cn, 2 * Ce);

  Cn = Cn + tmp[0];
  Ce = Ce + tmp[1];

  var x;
  var y;

  if (Math.abs(Ce) <= 2.623395162778) {
    x = this.a * (this.Qn * Ce) + this.x0;
    y = this.a * (this.Qn * Cn + this.Zb) + this.y0;
  }
  else {
    x = Infinity;
    y = Infinity;
  }

  p.x = x;
  p.y = y;

  return p;
}

function inverse(p) {
  var Ce = (p.x - this.x0) * (1 / this.a);
  var Cn = (p.y - this.y0) * (1 / this.a);

  Cn = (Cn - this.Zb) / this.Qn;
  Ce = Ce / this.Qn;

  var lon;
  var lat;

  if (Math.abs(Ce) <= 2.623395162778) {
    var tmp = Object(__WEBPACK_IMPORTED_MODULE_5__common_clens_cmplx__["a" /* default */])(this.utg, 2 * Cn, 2 * Ce);

    Cn = Cn + tmp[0];
    Ce = Ce + tmp[1];
    Ce = Math.atan(Object(__WEBPACK_IMPORTED_MODULE_0__common_sinh__["a" /* default */])(Ce));

    var sin_Cn = Math.sin(Cn);
    var cos_Cn = Math.cos(Cn);
    var sin_Ce = Math.sin(Ce);
    var cos_Ce = Math.cos(Ce);

    Cn = Math.atan2(sin_Cn * cos_Ce, Object(__WEBPACK_IMPORTED_MODULE_1__common_hypot__["a" /* default */])(sin_Ce, cos_Ce * cos_Cn));
    Ce = Math.atan2(sin_Ce, cos_Ce * cos_Cn);

    lon = Object(__WEBPACK_IMPORTED_MODULE_6__common_adjust_lon__["a" /* default */])(Ce + this.long0);
    lat = Object(__WEBPACK_IMPORTED_MODULE_3__common_gatg__["a" /* default */])(this.cgb, Cn);
  }
  else {
    lon = Infinity;
    lat = Infinity;
  }

  p.x = lon;
  p.y = lat;

  return p;
}

var names = ["Extended_Transverse_Mercator", "Extended Transverse Mercator", "etmerc"];
/* harmony default export */ __webpack_exports__["default"] = ({
  init: init,
  forward: forward,
  inverse: inverse,
  names: names
});


/***/ }),
/* 43 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony default export */ __webpack_exports__["a"] = (function(x) {
  var r = Math.exp(x);
  r = (r - 1 / r) / 2;
  return r;
});

/***/ }),
/* 44 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony default export */ __webpack_exports__["a"] = (function(a, e, sinphi) {
  var temp = e * sinphi;
  return a / Math.sqrt(1 - temp * temp);
});

/***/ }),
/* 45 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony default export */ __webpack_exports__["a"] = (function(ml, e0, e1, e2, e3) {
  var phi;
  var dphi;

  phi = ml / e0;
  for (var i = 0; i < 15; i++) {
    dphi = (ml - (e0 * phi - e1 * Math.sin(2 * phi) + e2 * Math.sin(4 * phi) - e3 * Math.sin(6 * phi))) / (e0 - 2 * e1 * Math.cos(2 * phi) + 4 * e2 * Math.cos(4 * phi) - 6 * e3 * Math.cos(6 * phi));
    phi += dphi;
    if (Math.abs(dphi) <= 0.0000000001) {
      return phi;
    }
  }

  //..reportError("IMLFN-CONV:Latitude failed to converge after 15 iterations");
  return NaN;
});

/***/ }),
/* 46 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony default export */ __webpack_exports__["a"] = (function(eccent, sinphi) {
  var con;
  if (eccent > 1.0e-7) {
    con = eccent * sinphi;
    return ((1 - eccent * eccent) * (sinphi / (1 - con * con) - (0.5 / eccent) * Math.log((1 - con) / (1 + con))));
  }
  else {
    return (2 * sinphi);
  }
});

/***/ }),
/* 47 */
/***/ (function(module, exports, __webpack_require__) {

module.exports = {
  create: __webpack_require__(176)
  , clone: __webpack_require__(177)
  , copy: __webpack_require__(178)
  , identity: __webpack_require__(97)
  , transpose: __webpack_require__(179)
  , invert: __webpack_require__(180)
  , adjoint: __webpack_require__(181)
  , determinant: __webpack_require__(182)
  , multiply: __webpack_require__(183)
  , translate: __webpack_require__(184)
  , scale: __webpack_require__(185)
  , rotate: __webpack_require__(186)
  , rotateX: __webpack_require__(187)
  , rotateY: __webpack_require__(188)
  , rotateZ: __webpack_require__(189)
  , fromRotation: __webpack_require__(190)
  , fromRotationTranslation: __webpack_require__(191)
  , fromScaling: __webpack_require__(192)
  , fromTranslation: __webpack_require__(193)
  , fromXRotation: __webpack_require__(194)
  , fromYRotation: __webpack_require__(195)
  , fromZRotation: __webpack_require__(196)
  , fromQuat: __webpack_require__(197)
  , frustum: __webpack_require__(198)
  , perspective: __webpack_require__(199)
  , perspectiveFromFieldOfView: __webpack_require__(200)
  , ortho: __webpack_require__(201)
  , lookAt: __webpack_require__(202)
  , str: __webpack_require__(203)
}


/***/ }),
/* 48 */
/***/ (function(module, exports, __webpack_require__) {

module.exports = {
  EPSILON: __webpack_require__(101)
  , create: __webpack_require__(102)
  , clone: __webpack_require__(227)
  , angle: __webpack_require__(228)
  , fromValues: __webpack_require__(103)
  , copy: __webpack_require__(229)
  , set: __webpack_require__(230)
  , equals: __webpack_require__(231)
  , exactEquals: __webpack_require__(232)
  , add: __webpack_require__(233)
  , subtract: __webpack_require__(106)
  , sub: __webpack_require__(234)
  , multiply: __webpack_require__(107)
  , mul: __webpack_require__(235)
  , divide: __webpack_require__(108)
  , div: __webpack_require__(236)
  , min: __webpack_require__(237)
  , max: __webpack_require__(238)
  , floor: __webpack_require__(239)
  , ceil: __webpack_require__(240)
  , round: __webpack_require__(241)
  , scale: __webpack_require__(242)
  , scaleAndAdd: __webpack_require__(243)
  , distance: __webpack_require__(109)
  , dist: __webpack_require__(244)
  , squaredDistance: __webpack_require__(110)
  , sqrDist: __webpack_require__(245)
  , length: __webpack_require__(111)
  , len: __webpack_require__(246)
  , squaredLength: __webpack_require__(112)
  , sqrLen: __webpack_require__(247)
  , negate: __webpack_require__(248)
  , inverse: __webpack_require__(249)
  , normalize: __webpack_require__(104)
  , dot: __webpack_require__(105)
  , cross: __webpack_require__(250)
  , lerp: __webpack_require__(251)
  , random: __webpack_require__(252)
  , transformMat4: __webpack_require__(253)
  , transformMat3: __webpack_require__(254)
  , transformQuat: __webpack_require__(255)
  , rotateX: __webpack_require__(256)
  , rotateY: __webpack_require__(257)
  , rotateZ: __webpack_require__(258)
  , forEach: __webpack_require__(259)
}


/***/ }),
/* 49 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var inherit = __webpack_require__(0);
var object = __webpack_require__(12);
var util = __webpack_require__(3);
var mat3 = __webpack_require__(116);
var vec3 = __webpack_require__(48);
var mat4 = __webpack_require__(47);
var vec4 = __webpack_require__(98);

/**
 * This class defines the raw interface for a camera.  At a low level, the
 * camera provides a methods for converting between a map's coordinate system
 * to display pixel coordinates.
 *
 * For the moment, all camera transforms are assumed to be expressible as
 * 4x4 matrices.  More general cameras may follow that break this assumption.
 *
 * The interface for the camera is relatively stable for "map-like" views,
 * e.g. when the camera is pointing in the direction [0, 0, -1], and placed
 * above the z=0 plane.  More general view changes and events have not yet
 * been defined.
 *
 * The camera emits the following events when the view changes:
 *
 *   * {@link geo.event.camera.pan} when the camera is translated in the
 *       x/y plane
 *   * {@link geo.event.camera.zoom} when the camera is changed in a way
 *       that modifies the current zoom level
 *   * {@link geo.event.camera.view} when the visible bounds change for
 *       any reason
 *   * {@link geo.event.camera.projection} when the projection type changes
 *   * {@link geo.event.camera.viewport} when the viewport changes
 *
 * By convention, protected methods do not update the internal matrix state,
 * public methods do.  There are a few primary methods that are intended to
 * be used by external classes to mutate the internal state:
 *
 *   * bounds: Set the visible bounds (for initialization and zooming)
 *   * pan: Translate the camera in x/y by an offset (for panning)
 *   * viewFromCenterSizeRotation: set the camera view based on a center
 *        point, boundary size, and rotation angle.
 *
 * @class
 * @alias geo.camera
 * @extends geo.object
 * @param {object?} spec Options argument
 * @param {string} spec.projection One of the supported
 *    {@link geo.camera.projection}.
 * @param {object} spec.viewport The initial camera viewport
 * @param {object} spec.viewport.width
 * @param {object} spec.viewport.height
 * @returns {geo.camera}
 */
var camera = function camera(spec) {
  if (!(this instanceof camera)) {
    return new camera(spec);
  }

  var geo_event = __webpack_require__(6);

  spec = spec || {};
  object.call(this, spec);

  /**
   * The view matrix
   * @protected
   */
  this._view = util.mat4AsArray();

  /**
   * The projection matrix
   * @protected
   */
  this._proj = util.mat4AsArray();

  /**
   * The projection type (one of `this.constructor.projection`)
   * @protected
   */
  this._projection = null;

  /**
   * The transform matrix (view * proj)
   * @protected
   */
  this._transform = util.mat4AsArray();

  /**
   * The inverse transform matrix (view * proj)^-1
   * @protected
   */
  this._inverse = util.mat4AsArray();

  /**
   * Cached bounds object recomputed on demand.
   * @protected
   */
  this._bounds = null;

  /**
   * Cached "display" matrix recomputed on demand.
   * @see {@link geo.camera.display}
   * @protected
   */
  this._display = null;

  /**
   * Cached "world" matrix recomputed on demand.
   * @see {@link geo.camera.world}
   * @protected
   */
  this._world = null;

  /**
   * The viewport parameters size and offset.
   * @property {number} height Viewport height in pixels
   * @property {number} width Viewport width in pixels
   * @protected
   */
  this._viewport = { width: 1, height: 1 };

  /**
   * Set up the projection matrix for the current projection type.
   * @protected
   */
  this._createProj = function () {
    var func = this._projection === 'perspective' ? mat4.frustum : mat4.ortho,
        clipbounds = this._clipbounds[this._projection];
    func(this._proj, clipbounds.left, clipbounds.right, clipbounds.bottom, clipbounds.top, clipbounds.near, clipbounds.far);
  };

  /**
   * Update the internal state of the camera on change to camera
   * parameters.
   * @protected
   * @fires geo.event.camera.view
   */
  this._update = function () {
    this._bounds = null;
    this._display = null;
    this._world = null;
    this._transform = mat4.multiply(util.mat4AsArray(), this._proj, this._view);
    mat4.invert(this._inverse, this._transform);
    this.geoTrigger(geo_event.camera.view, {
      camera: this
    });
  };

  /**
   * Getter/setter for the view matrix.
   * @note copies the matrix value on set.
   * @property {mat4} view The view matrix.
   * @name geo.camera#view
   */
  Object.defineProperty(this, 'view', {
    get: function get() {
      return this._view;
    },
    set: function set(view) {
      mat4.copy(this._view, view);
      this._update();
    }
  });

  /**
   * Getter/setter for the view bounds.
   *
   * @property {object} bounds The view bounds.
   * @property {number} bounds.left
   * @property {number} bounds.top
   * @property {number} bounds.right
   * @property {number} bounds.bottom
   * @name geo.camera#bounds
   */
  Object.defineProperty(this, 'bounds', {
    get: function get() {
      if (this._bounds === null) {
        this._bounds = this._getBounds();
      }
      return this._bounds;
    },
    set: function set(bounds) {
      this._setBounds(bounds);
      this._update();
    }
  });

  /**
   * Getter/setter for the render clipbounds.  Opposite bounds must have
   * different values.  There are independant clipbounds for each projection
   * (parallel and perspective); switching the projection will switch to the
   * clipbounds.  Individual values of the clipbounds can be set either via a
   * command like `camera.clipbounds = {near: 3, far: 1}` or
   * `camera.clipbounds.near = 3`.  In the second example, no check is made to
   * ensure a non-zero volume clipbounds.
   *
   * @property {object} clipbounds The clipbounds for the current projection.
   * @name geo.camera#clipbounds
   */
  Object.defineProperty(this, 'clipbounds', {
    get: function get() {
      return this._clipbounds[this._projection];
    },
    set: function set(bounds) {
      var clipbounds = this._clipbounds[this._projection];
      bounds = {
        left: bounds.left === undefined ? clipbounds.left : bounds.left,
        right: bounds.right === undefined ? clipbounds.right : bounds.right,
        top: bounds.top === undefined ? clipbounds.top : bounds.top,
        bottom: bounds.bottom === undefined ? clipbounds.bottom : bounds.bottom,
        near: bounds.near === undefined ? clipbounds.near : bounds.near,
        far: bounds.far === undefined ? clipbounds.far : bounds.far
      };
      if (bounds.left === bounds.right) {
        throw new Error('Left and right values must be different');
      }
      if (bounds.top === bounds.bottom) {
        throw new Error('Top and bottom values must be different');
      }
      if (bounds.near === bounds.far) {
        throw new Error('Near and far values must be different');
      }
      this._clipbounds[this._projection] = bounds;
      this._createProj();
      this._update();
    }
  });

  /**
   * Getter for the "display" matrix.  This matrix converts from world
   * coordinates into display coordinates.  Read only.
   *
   * @property {mat4} display The display matrix.
   * @name geo.camera#display
   */
  Object.defineProperty(this, 'display', {
    get: function get() {
      if (this._display === null) {
        var b = this._clipbounds[this._projection];
        var mat = util.mat4AsArray();
        mat4.translate(mat, mat, [this.viewport.width / 2, this.viewport.height / 2, 0]);
        mat4.scale(mat, mat, [this.viewport.width / (b.right - b.left), this.viewport.height / (b.bottom - b.top), 1]);
        mat4.translate(mat, mat, [-(b.left + b.right) / 2, -(b.top + b.bottom) / 2, 0]);
        mat4.multiply(mat, mat, this._transform);
        this._display = mat;
      }
      return this._display;
    }
  });

  /**
   * Getter for the "world" matrix.  This matrix converts from display
   * coordinates into world coordinates.  This is the inverse of the "display"
   * matrix.  Read only.
   *
   * @property {mat4} world The world matrix.
   * @name geo.camera#world
   */
  Object.defineProperty(this, 'world', {
    get: function get() {
      if (this._world === null) {
        this._world = mat4.invert(util.mat4AsArray(), this.display);
      }
      return this._world;
    }
  });

  /**
   * Getter/setter for the projection type.
   *
   * @property {string} projection The projection type.  One of `parallel` or
   *    `perspective`.
   * @name geo.camera#projection
   * @fires geo.event.camera.projection
   */
  Object.defineProperty(this, 'projection', {
    get: function get() {
      return this._projection;
    },
    set: function set(type) {
      if (!this.constructor.projection[type]) {
        throw new Error('Unsupported projection type: ' + type);
      }
      if (type !== this._projection) {
        this._projection = type;
        this._createProj();
        this._update();
        this.geoTrigger(geo_event.camera.projection, {
          camera: this,
          projection: type
        });
      }
    }
  });

  /**
   * Getter for the projection matrix.  Read only.
   *
   * @property {mat4} projectionMatrix The projection matrix.
   * @name geo.camera#projectionMatrix
   */
  Object.defineProperty(this, 'projectionMatrix', {
    get: function get() {
      return this._proj;
    }
  });

  /**
   * Getter for the transform matrix.  This is the projection multiplied by the
   * view matrix.  Read only.
   *
   * @property {mat4} transform The transform matrix.
   * @name geo.camera#transform
   */
  Object.defineProperty(this, 'transform', {
    get: function get() {
      return this._transform;
    }
  });

  /**
   * Getter for the inverse transform matrix.  Read only.
   *
   * @property {mat4} inverse The inverse transform matrix.
   * @name geo.camera#inverse
   */
  Object.defineProperty(this, 'inverse', {
    get: function get() {
      return this._inverse;
    }
  });

  /**
   * Getter/setter for the viewport.
   *
   * The viewport consists of a width and height in pixels, plus a left and
   * top offset in pixels.  The offsets are only used to determine if pixel
   * alignment is possible.
   *
   * @property {object} viewport The viewport in pixels.
   * @property {number} viewport.width
   * @property {number} viewport.height
   * @property {number} viewport.top
   * @property {number} viewport.left
   * @name geo.camera#viewport
   * @fires geo.event.camera.viewport
   */
  Object.defineProperty(this, 'viewport', {
    get: function get() {
      return {
        width: this._viewport.width,
        height: this._viewport.height,
        left: this._viewport.left,
        top: this._viewport.top
      };
    },
    set: function set(viewport) {
      if (!(viewport.width > 0 && viewport.height > 0)) {
        throw new Error('Invalid viewport dimensions');
      }
      if (viewport.width === this._viewport.width && viewport.height === this._viewport.height) {
        return;
      }

      // apply scaling to the view matrix to account for the new aspect ratio
      // without changing the apparent zoom level
      if (this._viewport.width && this._viewport.height) {
        this._scale([this._viewport.width / viewport.width, this._viewport.height / viewport.height, 1]);

        // translate by half the difference to keep the center the same
        this._translate([(viewport.width - this._viewport.width) / 2, (viewport.height - this._viewport.height) / 2, 0]);
      }

      this._viewport = {
        width: viewport.width,
        height: viewport.height,
        left: viewport.left,
        top: viewport.top
      };
      this._update();
      this.geoTrigger(geo_event.camera.viewport, {
        camera: this,
        viewport: this.viewport
      });
    }
  });

  /**
   * Reset the view matrix to its initial (identity) state.
   * @protected
   * @returns {this} Chainable.
   */
  this._resetView = function () {
    mat4.identity(this._view);
    return this;
  };

  /**
   * Uses `mat4.translate` to translate the camera by the given vector amount.
   * @protected
   * @param {vec3|Array} offset The camera translation vector.
   * @returns {this} Chainable.
   */
  this._translate = function (offset) {
    mat4.translate(this._view, this._view, offset);
    return this;
  };

  /**
   * Uses `mat4.scale` to scale the camera by the given vector amount.
   * @protected
   * @param {vec3|Array} scale The scaling vector.
   * @returns {this} Chainable.
   */
  this._scale = function (scale) {
    mat4.scale(this._view, this._view, scale);
    return this;
  };

  /**
   * Project a vec4 from world space into clipped space [-1, 1] in place.
   * @protected
   * @param {vec4} point The point in world coordinates (mutated).
   * @returns {vec4} The point in clip space coordinates.
   */
  this._worldToClip4 = function (point) {
    return vec4.transformMat4(point, point, this._transform);
  };

  /**
   * Project a vec4 from clipped space into world space in place.
   * @protected
   * @param {vec4} point The point in clipped coordinates (mutated).
   * @returns {vec4} The point in world space coordinates.
   */
  this._clipToWorld4 = function (point) {
    return vec4.transformMat4(point, point, this._inverse);
  };

  /**
   * Apply the camera's projection transform to the given point.
   * @param {vec4} pt a point in clipped coordinates.
   * @returns {vec4} the point in normalized coordinates.
   */
  this.applyProjection = function (pt) {
    var w;
    if (this._projection === 'perspective') {
      w = 1 / (pt[3] || 1);
      pt[0] = w * pt[0];
      pt[1] = w * pt[1];
      pt[2] = w * pt[2];
      pt[3] = w;
    } else {
      pt[3] = 1;
    }
    return pt;
  };

  /**
   * Unapply the camera's projection transform from the given point.
   * @param {vec4} pt a point in normalized coordinates.
   * @returns {vec4} the point in clipped coordinates.
   */
  this.unapplyProjection = function (pt) {
    var w;
    if (this._projection === 'perspective') {
      w = pt[3] || 1;
      pt[0] = w * pt[0];
      pt[1] = w * pt[1];
      pt[2] = w * pt[2];
      pt[3] = w;
    } else {
      pt[3] = 1;
    }
    return pt;
  };

  /**
   * Project a vector from world space into viewport (display) space.  The
   * resulting vector always has the last component (`w`) equal to 1.
   *
   * @param {vec2|vec3|vec4} point The point in world coordinates.
   * @returns {vec4} The point in display coordinates.
   */
  this.worldToDisplay4 = function (point) {
    point = [point[0], point[1], point[2] || 0, point[3] || 1];
    point = vec4.transformMat4(point, point, this.display);
    if (point[3] && point[3] !== 1) {
      point = [point[0] / point[3], point[1] / point[3], point[2] / point[3], 1];
    }
    return point;
  };

  /**
   * Project a vector from viewport (display) space into world space.  The
   * resulting vector always has the last component (`w`) equal to 1.
   *
   * @param {vec2|vec3|vec4} point The point in display coordinates.
   * @returns {vec4} The point in world space coordinates.
   */
  this.displayToWorld4 = function (point) {
    point = [point[0], point[1], point[2] || 0, point[3] || 1];
    point = vec4.transformMat4(point, point, this.world);
    if (point[3] && point[3] !== 1) {
      point = [point[0] / point[3], point[1] / point[3], point[2] / point[3], 1];
    }
    return point;
  };

  /**
   * Project a 2D point object from world space into viewport space.  `z` is
   * set to `-this.clipbounds.near` to scale with the clip space.
   *
   * @param {object} point The point in world coordinates.
   * @param {number} point.x
   * @param {number} point.y
   * @returns {object} The point in display coordinates.
   */
  this.worldToDisplay = function (point) {
    var b = this._clipbounds[this._projection];
    point = this.worldToDisplay4([point.x, point.y, -b.near]);
    return { x: point[0], y: point[1] };
  };

  /**
   * Project a 2D point object from viewport space into world space.  `z` is
   * set to -1 to scale with the clip space.
   *
   * @param {object} point The point in display coordinates.
   * @param {number} point.x
   * @param {number} point.y
   * @returns {object} The point in world coordinates.
   */
  this.displayToWorld = function (point) {
    point = this.displayToWorld4([point.x, point.y, -1]);
    return { x: point[0], y: point[1] };
  };

  /**
   * Calculate the current bounds in world coordinates from the
   * current view matrix.  This computes a matrix vector multiplication
   * so the result is cached for public facing methods.
   *
   * @protected
   * @returns {object} bounds object.
   */
  this._getBounds = function () {
    var ul,
        ur,
        ll,
        lr,
        bds = {};

    // get corners
    ul = this.displayToWorld({ x: 0, y: 0 });
    ur = this.displayToWorld({ x: this._viewport.width, y: 0 });
    ll = this.displayToWorld({ x: 0, y: this._viewport.height });
    lr = this.displayToWorld({ x: this._viewport.width, y: this._viewport.height });

    bds.left = Math.min(ul.x, ur.x, ll.x, lr.x);
    bds.bottom = Math.min(ul.y, ur.y, ll.y, lr.y);
    bds.right = Math.max(ul.x, ur.x, ll.x, lr.x);
    bds.top = Math.max(ul.y, ur.y, ll.y, lr.y);

    return bds;
  };

  /**
   * Sets the view matrix so that the given world bounds
   * are in view.  To account for the viewport aspect ratio,
   * the resulting bounds may be larger in width or height than
   * the requested bound, but should be centered in the frame.
   *
   * @protected
   * @param {object} bounds
   * @param {number} bounds.left
   * @param {number} bounds.right
   * @param {number} bounds.bottom
   * @param {number} bounds.top
   * @param {number?} bounds.near Currently ignored.
   * @param {number?} bounds.far Currently ignored.
   * @returns {this} Chainable.
   */
  this._setBounds = function (bounds) {
    var size = {
      width: bounds.right - bounds.left,
      height: bounds.top - bounds.bottom
    };
    var center = {
      x: (bounds.left + bounds.right) / 2,
      y: (bounds.bottom + bounds.top) / 2
    };

    this._viewFromCenterSizeRotation(center, size, 0);
    return this;
  };

  /**
   * Sets the view matrix so that the given world center is centered, at
   * least a certain width and height are visible, and a rotation is applied.
   * The resulting bounds may be larger in width or height than the values if
   * the viewport is a different aspect ratio.
   *
   * @protected
   * @param {object} center Center of the view in gcs coordinates.
   * @param {number} center.x
   * @param {number} center.y
   * @param {object} size Minimum size of the view in gcs units.
   * @param {number} size.width
   * @param {number} size.height
   * @param {number} rotation in clockwise radians.  Optional.
   * @returns {this} Chainable.
   */
  this._viewFromCenterSizeRotation = function (center, size, rotation) {
    var translate = util.vec3AsArray(),
        scale = util.vec3AsArray(),
        c_ar,
        v_ar,
        w,
        h;

    // reset view to the identity
    this._resetView();

    w = Math.abs(size.width);
    h = Math.abs(size.height);
    c_ar = w / h;
    v_ar = this._viewport.width / this._viewport.height;

    if (c_ar >= v_ar) {
      // grow camera bounds vertically
      h = w / v_ar;
      scale[0] = 2 / w;
      scale[1] = 2 / h;
    } else {
      // grow bounds horizontally
      w = h * v_ar;
      scale[0] = 2 / w;
      scale[1] = 2 / h;
    }

    scale[2] = 1;
    this._scale(scale);

    if (rotation) {
      this._rotate(rotation);
    }

    // translate to the new center.
    translate[0] = -center.x;
    translate[1] = -center.y;
    translate[2] = 0;

    this._translate(translate);

    return this;
  };

  /**
   * Sets the view matrix so that the given world center is centered, at
   * least a certain width and height are visible, and a rotation is applied.
   * The resulting bounds may be larger in width or height than the values if
   * the viewport is a different aspect ratio.
   *
   * @param {object} center Center of the view in gcs coordinates.
   * @param {number} center.x
   * @param {number} center.y
   * @param {object} size Minimum size of the view in gcs units.
   * @param {number} size.width
   * @param {number} size.height
   * @param {number} rotation in clockwise radians.  Optional.
   * @returns {this} Chainable.
   */
  this.viewFromCenterSizeRotation = function (center, size, rotation) {
    this._viewFromCenterSizeRotation(center, size, rotation);
    this._update();
    return this;
  };

  /**
   * Pans the view matrix by the given amount.
   *
   * @param {object} offset The delta in world space coordinates.
   * @param {number} offset.x
   * @param {number} offset.y
   * @param {number} [offset.z=0]
   * @returns {this} Chainable.
   */
  this.pan = function (offset) {
    if (!offset.x && !offset.y && !offset.z) {
      return;
    }
    this._translate([offset.x, offset.y, offset.z || 0]);
    this._update();
    return this;
  };

  /**
   * Zooms the view matrix by the given amount.
   *
   * @param {number} zoom The zoom scale to apply
   * @returns {this} Chainable.
   */
  this.zoom = function (zoom) {
    if (zoom === 1) {
      return;
    }
    mat4.scale(this._view, this._view, [zoom, zoom, zoom]);
    this._update();
    return this;
  };

  /**
   * Rotate the view matrix by the given amount.
   *
   * @param {number} rotation Counter-clockwise rotation angle in radians.
   * @param {object} center Center of rotation in world space coordinates.
   * @param {vec3} [axis=[0, 0, -1]] axis of rotation.
   * @returns {this} Chainable.
   */
  this._rotate = function (rotation, center, axis) {
    if (!rotation) {
      return;
    }
    axis = axis || [0, 0, -1];
    if (!center) {
      center = [0, 0, 0];
    } else if (center.x !== undefined) {
      center = [center.x || 0, center.y || 0, center.z || 0];
    }
    var invcenter = [-center[0], -center[1], -center[2]];
    mat4.translate(this._view, this._view, center);
    mat4.rotate(this._view, this._view, rotation, axis);
    mat4.translate(this._view, this._view, invcenter);
    return this;
  };

  /**
   * Returns a CSS transform that converts (by default) from world coordinates
   * into display coordinates.  This allows users of this module to position
   * elements using world coordinates directly inside DOM elements.  This
   * expects that the transform-origin is 0 0.
   *
   * @param {string} [transform='display'] The transform to return.  One of
   *   `display` or `world`.
   * @returns {string} The css transform string.
   */
  this.css = function (transform) {
    var m;
    switch ((transform || '').toLowerCase()) {
      case 'display':
      case '':
        m = this.display;
        break;
      case 'world':
        m = this.world;
        break;
      default:
        throw new Error('Unknown transform ' + transform);
    }
    return camera.css(m);
  };

  /**
   * Represent a glmatrix as a pretty-printed string.
   * @param {mat4} mat A 4 x 4 matrix.
   * @param {number} prec The number of decimal places.
   * @returns {string}
   */
  this.ppMatrix = function (mat, prec) {
    var t = mat;
    prec = prec || 2;
    function f(i) {
      var d = t[i],
          s = d.toExponential(prec);
      if (d >= 0) {
        s = ' ' + s;
      }
      return s;
    }
    return [[f(0), f(4), f(8), f(12)].join(' '), [f(1), f(5), f(9), f(13)].join(' '), [f(2), f(6), f(10), f(14)].join(' '), [f(3), f(7), f(11), f(15)].join(' ')].join('\n');
  };

  /**
   * Pretty print the transform matrix.
   * @returns {string} A string representation of the matrix.
   */
  this.toString = function () {
    return this.ppMatrix(this._transform);
  };

  /**
   * Return a debugging string of the current camera state.
   * @returns {string} A string with the camera state.
   */
  this.debug = function () {
    return ['bounds', JSON.stringify(this.bounds), 'view:', this.ppMatrix(this._view), 'projection:', this.ppMatrix(this._proj), 'transform:', this.ppMatrix(this._transform)].join('\n');
  };

  /**
   * Represent the value of the camera as its transform matrix.
   * @returns {mat4} The transform matrix.
   */
  this.valueOf = function () {
    return this._transform;
  };

  this._clipbounds = this.constructor.clipbounds;
  // initialize the view matrix
  this._resetView();

  // set up the projection matrix
  this.projection = spec.projection || 'parallel';

  // initialize the viewport
  if (spec.viewport) {
    this.viewport = spec.viewport;
  }

  // trigger an initial update to set up the camera state
  this._update();

  return this;
};

/**
 * Supported projection types.
 * @enum
 */
camera.projection = {
  perspective: true,
  parallel: true
};

/**
 * Default camera clipping bounds.  Some features and renderers may rely on the
 * far clip value being more positive than the near clip value.
 * @enum
 */
camera.clipbounds = {
  perspective: {
    left: -1,
    right: 1,
    top: 1,
    bottom: -1,
    far: 2000,
    near: 0.01
  },
  parallel: {
    left: -1,
    right: 1,
    top: 1,
    bottom: -1,
    far: -1,
    near: 1
  }
};

/**
 * Output a mat4 as a css transform.  This expects that the transform-origin is
 * 0 0.
 *
 * @variation 2
 * @param {mat4} t A matrix transform.
 * @returns {string} A css transform string.
 */
camera.css = function (t) {
  return 'matrix3d(' + t.map(function (val) {
    /* Format each value with a certain precision, but don't use scientific
     * notation or keep needless trailing zeroes. */
    val = (+val).toPrecision(15);
    if (val.indexOf('e') >= 0) {
      val = (+val).toString();
    } else if (val.indexOf('.') >= 0) {
      val = val.replace(/(\.|)0+$/, '');
    }
    return val;
  }).join(',') + ')';
};

// expose the vector and matrix functions for convenience
camera.vec3 = vec3;
camera.mat3 = mat3;
camera.vec4 = vec4;
camera.mat4 = mat4;

inherit(camera, object);
module.exports = camera;

/***/ }),
/* 50 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var $ = __webpack_require__(2);
var inherit = __webpack_require__(0);
var feature = __webpack_require__(8);
var transform = __webpack_require__(7);

/**
 * Polygon feature specification.
 *
 * @typedef {geo.feature.spec} geo.polygonFeature.spec
 * @property {geo.geoPosition|function} [position] Position of the data.
 *   Default is (data).
 * @property {geo.polygon|function} [polygon] Polygons from the data.  Default
 *   (data).
 * @property {geo.polygonFeature.styleSpec} [style] Style object with default
 *   style options.
 */

/**
 * Style specification for a polygon feature.
 *
 * @typedef {geo.lineFeature.styleSpec} geo.polygonFeature.styleSpec
 * @extends geo.lineFeature.styleSpec
 * @property {boolean|function} [fill=true] True to fill polygon.
 * @property {geo.geoColor|function} [fillColor] Color to fill each polygon.
 *   The color can vary by vertex.
 * @property {number|function} [fillOpacity] Opacity for each polygon.  The
 *   opacity can vary by vertex.  Opacity is on a [0-1] scale.
 * @property {boolean|function} [stroke=false] True to stroke polygon.
 * @property {boolean|function} [uniformPolygon=false] Boolean indicating if
 *   each polygon has a uniform style (uniform fill color, fill opacity, stroke
 *   color, and stroke opacity).  Can vary by polygon.
 * @property {boolean|function} [closed=true] Ignored.  Always `true`.
 */

/**
 * Create a new instance of class polygonFeature.
 *
 * @class
 * @alias geo.polygonFeature
 * @extends geo.feature
 * @param {geo.polygonFeature.spec} arg
 * @returns {geo.polygonFeature}
 */
var polygonFeature = function polygonFeature(arg) {
  'use strict';

  if (!(this instanceof polygonFeature)) {
    return new polygonFeature(arg);
  }
  arg = arg || {};
  feature.call(this, arg);

  var util = __webpack_require__(3);

  /**
   * @private
   */
  var m_this = this,
      m_lineFeature,
      s_init = this._init,
      s_exit = this._exit,
      s_data = this.data,
      s_draw = this.draw,
      s_modified = this.modified,
      s_style = this.style,
      m_coordinates = [];

  this.featureType = 'polygon';
  this._subfeatureStyles = {
    fillColor: true,
    fillOpacity: true,
    lineCap: true,
    lineJoin: true,
    strokeColor: true,
    strokeOffset: true,
    strokeOpacity: true,
    strokeWidth: true
  };

  /**
   * Get/set data.
   *
   * @param {object} [arg] if specified, use this for the data and return the
   *    feature.  If not specified, return the current data.
   * @returns {geo.polygonFeature|object}
   */
  this.data = function (arg) {
    var ret = s_data(arg);
    if (arg !== undefined) {
      m_coordinates = getCoordinates();
      this._checkForStroke();
    }
    return ret;
  };

  /**
   * Get the internal coordinates whenever the data changes.  Also compute the
   * extents of the outside of each polygon for faster checking if points are
   * in the polygon.
   *
   * @private
   * @param {object[]} [data=this.data()] The data to process.
   * @param {function} [posFunc=this.style.get('position')] The function to
   *    get the position of each vertex.
   * @param {function} [polyFunc=this.style.get('polygon')] The function to
   *    get each polygon.
   * @returns {geo.polygonObject[]} An array of polygon positions.  Each has
   *    `outer` and `inner` if it has any coordinates, or is `undefined`.
   */
  function getCoordinates(data, posFunc, polyFunc) {
    data = data || m_this.data();
    posFunc = posFunc || m_this.style.get('position');
    polyFunc = polyFunc || m_this.style.get('polygon');
    var coordinates = data.map(function (d, i) {
      var poly = polyFunc(d, i);
      if (!poly) {
        return;
      }
      var outer, inner, range, coord, j, x, y;

      coord = poly.outer || (Array.isArray(poly) ? poly : []);
      outer = new Array(coord.length);
      for (j = 0; j < coord.length; j += 1) {
        outer[j] = posFunc.call(m_this, coord[j], j, d, i);
        x = outer[j].x || outer[j][0] || 0;
        y = outer[j].y || outer[j][1] || 0;
        if (!j) {
          range = { min: { x: x, y: y }, max: { x: x, y: y } };
        } else {
          if (x < range.min.x) {
            range.min.x = x;
          }
          if (y < range.min.y) {
            range.min.y = y;
          }
          if (x > range.max.x) {
            range.max.x = x;
          }
          if (y > range.max.y) {
            range.max.y = y;
          }
        }
      }
      inner = (poly.inner || []).map(function (hole) {
        coord = hole || [];
        var trans = new Array(coord.length);
        for (j = 0; j < coord.length; j += 1) {
          trans[j] = posFunc.call(m_this, coord[j], j, d, i);
        }
        return trans;
      });
      return {
        outer: outer,
        inner: inner,
        range: range
      };
    });
    return coordinates;
  }

  /**
   * Get the set of normalized polygon coordinates.
   *
   * @returns {geo.polygonObject[]} An array of polygon positions.  Each has
   *    `outer` and `inner` if it has any coordinates, or is `undefined`.
   */
  this.polygonCoordinates = function () {
    return m_coordinates;
  };

  /**
   * Get the style for the stroke of the polygon.  Since polygons can have
   * holes, the number of stroke lines may not be the same as the number of
   * polygons.  If the style for a stroke is a function, this calls the
   * appropriate value for the polygon.  Any style set for a stroke line should
   * be wrapped in this function.
   *
   * @param {(object|function)?} styleValue The polygon's style value used for
   *    the stroke.  This should be m_this.style(<name of style>) and not
   *    m_this.style.get(<name of style>), as the result is more efficient if
   *    the style is not a function.
   * @returns {object|function} A style that can be used for the stroke.
   * @private
   */
  function linePolyStyle(styleValue) {
    if (util.isFunction(styleValue)) {
      return function (d) {
        return styleValue(d[0], d[1], d[2], d[3]);
      };
    } else {
      return styleValue;
    }
  }

  /**
   * Get/set polygon accessor.
   *
   * @param {object|function} [val] If not specified, return the current
   *    polygon accessor.  If specified, use this for the polygon accessor and
   *    return `this`.  If a function is given, the function is passed
   *    `(dataElement, dataIndex)` and returns a {@link geo.polygon}.
   * @returns {object|function|this} The current polygon accessor or this
   *    feature.
   */
  this.polygon = function (val) {
    if (val === undefined) {
      return m_this.style('polygon');
    } else {
      m_this.style('polygon', val);
      m_this.dataTime().modified();
      m_this.modified();
      m_coordinates = getCoordinates();
    }
    return m_this;
  };

  /**
   * Get/Set position accessor.
   *
   * @param {geo.geoPosition|function} [val] If not specified, return the
   *    current position accessor.  If specified, use this for the position
   *    accessor and return `this`.  If a function is given, this is called
   *    with `(vertexElement, vertexIndex, dataElement, dataIndex)`.
   * @returns {geo.geoPosition|this} The current position or this feature.
   */
  this.position = function (val) {
    if (val === undefined) {
      return m_this.style('position');
    } else {
      m_this.style('position', val);
      m_this.dataTime().modified();
      m_this.modified();
      m_coordinates = getCoordinates();
    }
    return m_this;
  };

  /**
   * Point search method for selection api.  Returns markers containing the
   * given point.
   *
   * @param {geo.geoPosition} coordinate point to search for in map interface
   *    gcs.
   * @returns {object} An object with `index`: a list of polygon indices, and
   *    `found`: a list of polygons that contain the specified coordinate.
   */
  this.pointSearch = function (coordinate) {
    var found = [],
        indices = [],
        irecord = {},
        data = m_this.data(),
        map = m_this.layer().map(),
        pt = transform.transformCoordinates(map.ingcs(), m_this.gcs(), coordinate);
    m_coordinates.forEach(function (coord, i) {
      var inside = util.pointInPolygon(pt, coord.outer, coord.inner, coord.range);
      if (inside) {
        indices.push(i);
        irecord[i] = true;
        found.push(data[i]);
      }
    });
    if (m_lineFeature) {
      var lineFound = m_lineFeature.pointSearch(coordinate);
      lineFound.found.forEach(function (lineData) {
        if (lineData.length && lineData[0].length === 4 && !irecord[lineData[0][3]]) {
          indices.push(lineData[0][3]);
          irecord[lineData[0][3]] = true;
          found.push(data[lineData[0][3]]);
        }
      });
    }
    return {
      index: indices,
      found: found
    };
  };

  /**
   * Get/Set style used by the feature.  This calls the super function, then
   * checks if strokes are required.
   *
   * See the <a href="#.styleSpec">style specification
   * <code>styleSpec</code></a> for available styles.
   *
   * @param {string|object} [arg1] If `undefined`, return the current style
   *    object.  If a string and `arg2` is undefined, return the style
   *    associated with the specified key.  If a string and `arg2` is defined,
   *    set the named style to the specified value.  Otherwise, extend the
   *    current style with the values in the specified object.
   * @param {*} [arg2] If `arg1` is a string, the new value for that style.
   * @returns {object|this} Either the entire style object, the value of a
   *    specific style, or the current class instance.
   */
  this.style = function (arg1, arg2) {
    var result = s_style.apply(this, arguments);
    if (arg1 !== undefined && (typeof arg1 !== 'string' || arg2 !== undefined)) {
      this._checkForStroke();
    }
    return result;
  };

  this.style.get = s_style.get;

  /**
   * Get an outer or inner loop of a polygon and return the necessary data to
   * use it for a closed polyline.
   *
   * @param {object} item: the polygon.
   * @param {number} itemIndex: the index of the polygon
   * @param {Array} loop: the inner or outer loop.
   * @returns {Array} the loop with the data necessary to send to the position
   *    function for each vertex.
   */
  this._getLoopData = function (item, itemIndex, loop) {
    var line = [],
        i;

    for (i = 0; i < loop.length; i += 1) {
      line.push([loop[i], i, item, itemIndex]);
    }
    return line;
  };

  /**
   * Check if we need to add a line feature to the layer, and update it as
   * necessary.
   */
  this._checkForStroke = function () {
    if (s_style('stroke') === false) {
      if (m_lineFeature && m_this.layer()) {
        m_this.layer().deleteFeature(m_lineFeature);
        m_lineFeature = null;
        m_this.dependentFeatures([]);
      }
      return;
    }
    if (!m_this.layer()) {
      return;
    }
    if (!m_lineFeature) {
      m_lineFeature = m_this.layer().createFeature('line', {
        selectionAPI: false,
        gcs: m_this.gcs(),
        visible: m_this.visible(undefined, true)
      });
      m_this.dependentFeatures([m_lineFeature]);
    }
    var polyStyle = m_this.style();
    m_lineFeature.style({
      antialiasing: linePolyStyle(polyStyle.antialiasing),
      closed: true,
      lineCap: linePolyStyle(polyStyle.lineCap),
      lineJoin: linePolyStyle(polyStyle.lineJoin),
      miterLimit: linePolyStyle(polyStyle.miterLimit),
      strokeWidth: linePolyStyle(polyStyle.strokeWidth),
      strokeStyle: linePolyStyle(polyStyle.strokeStyle),
      strokeColor: linePolyStyle(polyStyle.strokeColor),
      strokeOffset: linePolyStyle(polyStyle.strokeOffset),
      strokeOpacity: util.isFunction(polyStyle.stroke) || !polyStyle.stroke ? function (d) {
        return m_this.style.get('stroke')(d[2], d[3]) ? m_this.style.get('strokeOpacity')(d[0], d[1], d[2], d[3]) : 0;
      } : linePolyStyle(polyStyle.strokeOpacity)
    });
    var data = this.data(),
        posVal = this.style('position');
    if (data !== m_lineFeature._lastData || posVal !== m_lineFeature._lastPosVal) {
      var lineData = [],
          i,
          polygon,
          loop,
          posFunc = this.style.get('position'),
          polyFunc = this.style.get('polygon');

      for (i = 0; i < data.length; i += 1) {
        polygon = polyFunc(data[i], i);
        if (!polygon) {
          continue;
        }
        loop = polygon.outer || (Array.isArray(polygon) ? polygon : []);
        if (loop.length >= 2) {
          lineData.push(m_this._getLoopData(data[i], i, loop));
          if (polygon.inner) {
            polygon.inner.forEach(function (loop) {
              if (loop.length >= 2) {
                lineData.push(m_this._getLoopData(data[i], i, loop));
              }
            });
          }
        }
      }
      m_lineFeature.position(function (d, i, item, itemIndex) {
        return posFunc(d[0], d[1], d[2], d[3]);
      });
      m_lineFeature.data(lineData);
      m_lineFeature._lastData = data;
      m_lineFeature._lastPosVal = posVal;
    }
  };

  /**
   * Redraw the object.
   *
   * @returns {object} The results of the superclass draw function.
   */
  this.draw = function () {
    var result = s_draw();
    if (m_lineFeature) {
      m_lineFeature.draw();
    }
    return result;
  };

  /**
   * When the feature is marked as modified, mark our sub-feature as
   * modified, too.
   *
   * @returns {object} The results of the superclass modified function.
   */
  this.modified = function () {
    var result = s_modified();
    if (m_lineFeature) {
      m_lineFeature.modified();
    }
    return result;
  };

  /**
   * Take a set of data, reduce the number of vertices per polygon using the
   * Ramer–Douglas–Peucker algorithm, and use the result as the new data.
   * This changes the instance's data, the position accessor, and the polygon
   * accessor.
   *
   * @param {array} data A new data array.
   * @param {number} [tolerance] The maximum variation allowed in map.gcs
   *    units.  A value of zero will only remove perfectly colinear points.  If
   *    not specified, this is set to a half display pixel at the map's current
   *    zoom level.
   * @param {function} [posFunc=this.style.get('position')] The function to
   *    get the position of each vertex.
   * @param {function} [polyFunc=this.style.get('polygon')] The function to
   *    get each polygon.
   * @returns {this}
   */
  this.rdpSimplifyData = function (data, tolerance, posFunc, polyFunc) {
    var map = m_this.layer().map(),
        mapgcs = map.gcs(),
        featuregcs = m_this.gcs(),
        coordinates = getCoordinates(data, posFunc, polyFunc);
    if (tolerance === undefined) {
      tolerance = map.unitsPerPixel(map.zoom()) * 0.5;
    }

    /* transform the coordinates to the map gcs */
    coordinates = coordinates.map(function (poly) {
      return {
        outer: transform.transformCoordinates(featuregcs, mapgcs, poly.outer),
        inner: poly.inner.map(function (hole) {
          return transform.transformCoordinates(featuregcs, mapgcs, hole);
        })
      };
    });
    data = data.map(function (d, idx) {
      var poly = coordinates[idx],
          elem = {};
      /* Copy element properties, as they might be used by styles */
      for (var key in d) {
        if (d.hasOwnProperty(key) && !(Array.isArray(d) && key >= 0 && key < d.length)) {
          elem[key] = d[key];
        }
      }
      if (poly && poly.outer.length >= 3) {
        // discard degenerate holes before anything else
        elem.inner = poly.inner.filter(function (hole) {
          return hole.length >= 3;
        });
        // simplify the outside of the polygon without letting it cross holes
        elem.outer = util.rdpLineSimplify(poly.outer, tolerance, true, elem.inner);
        if (elem.outer.length >= 3) {
          var allButSelf = elem.inner.slice();
          // simplify holes without crossing other holes or the outside
          elem.inner.map(function (hole, idx) {
            allButSelf[idx] = elem.outer;
            var result = util.rdpLineSimplify(hole, tolerance, true, allButSelf);
            allButSelf[idx] = result;
            return result;
          }).filter(function (hole) {
            return hole.length >= 3;
          });
          // transform coordinates back to the feature gcs
          elem.outer = transform.transformCoordinates(mapgcs, featuregcs, elem.outer);
          elem.inner = elem.inner.map(function (hole) {
            return transform.transformCoordinates(mapgcs, featuregcs, hole);
          });
        } else {
          elem.outer = elem.inner = [];
        }
      } else {
        elem.outer = [];
      }
      return elem;
    });

    /* Set the reduced polgons as the data and use simple accessors. */
    m_this.style('position', function (d) {
      return d;
    });
    m_this.style('polygon', function (d) {
      return d;
    });
    m_this.data(data);
    return m_this;
  };

  /**
   * If the selectionAPI is on, then setting
   * `this.geoOn(geo.event.feature.mouseover_order, this.mouseOverOrderClosestBorder)`
   * will make it so that the mouseon events prefer the polygon with the
   * closet border, including hole edges.
   *
   * @param {geo.event} evt The event; this should be triggered from
   *    {@link geo.event.feature.mouseover_order}.
   */
  this.mouseOverOrderClosestBorder = function (evt) {
    var data = evt.feature.data(),
        map = evt.feature.layer().map(),
        pt = transform.transformCoordinates(map.ingcs(), evt.feature.gcs(), evt.mouse.geo),
        coor = evt.feature.polygonCoordinates(),
        dist = {};
    evt.over.index.forEach(function (di, idx) {
      var poly = coor[di],
          mindist;
      poly.outer.forEach(function (line1, pidx) {
        var line2 = poly.outer[(pidx + 1) % poly.outer.length];
        var dist = util.distance2dToLineSquared(pt, line1, line2);
        if (mindist === undefined || dist < mindist) {
          mindist = dist;
        }
      });
      poly.inner.forEach(function (inner) {
        inner.forEach(function (line1, pidx) {
          var line2 = inner[(pidx + 1) % inner.length];
          var dist = util.distance2dToLineSquared(pt, line1, line2);
          if (mindist === undefined || dist < mindist) {
            mindist = dist;
          }
        });
      });
      dist[di] = mindist;
    });
    evt.over.index.sort(function (i1, i2) {
      return dist[i1] - dist[i2];
    }).reverse();
    // this isn't necessary, but ensures that other event handlers have
    // consistent information
    evt.over.index.forEach(function (di, idx) {
      evt.over.found[idx] = data[di];
    });
  };

  /**
   * Destroy.
   */
  this._exit = function () {
    if (m_lineFeature && m_this.layer()) {
      m_this.layer().deleteFeature(m_lineFeature);
      m_lineFeature = null;
      m_this.dependentFeatures([]);
    }
    s_exit();
  };

  /**
   * Initialize.
   *
   * @param {geo.polygonFeature.spec} arg An object with options for the
   *    feature.
   */
  this._init = function (arg) {
    arg = arg || {};
    s_init.call(m_this, arg);

    var style = $.extend({}, {
      // default style
      fill: true,
      fillColor: { r: 0.0, g: 0.5, b: 0.5 },
      fillOpacity: 1.0,
      stroke: false,
      strokeWidth: 1.0,
      strokeStyle: 'solid',
      strokeColor: { r: 0.0, g: 1.0, b: 1.0 },
      strokeOpacity: 1.0,
      polygon: function polygon(d) {
        return d;
      },
      position: function position(d) {
        return d;
      }
    }, arg.style === undefined ? {} : arg.style);

    if (arg.polygon !== undefined) {
      style.polygon = arg.polygon;
    }
    if (arg.position !== undefined) {
      style.position = arg.position;
    }
    m_this.style(style);

    this._checkForStroke();
  };

  /* Don't call _init here -- let subclasses call it */
  return this;
};

/**
 * Create a polygonFeature from an object.
 *
 * @see {@link geo.feature.create}
 * @param {geo.layer} layer The layer to add the feature to
 * @param {geo.polygonFeature.spec} spec The object specification
 * @returns {geo.polygonFeature|null}
 */
polygonFeature.create = function (layer, spec) {
  'use strict';

  spec = spec || {};
  spec.type = 'polygon';
  return feature.create(layer, spec);
};

polygonFeature.capabilities = {
  /* core feature name -- support in any manner */
  feature: 'polygon'
};

inherit(polygonFeature, feature);
module.exports = polygonFeature;

/***/ }),
/* 51 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var inherit = __webpack_require__(0);
var object = __webpack_require__(12);
var util = __webpack_require__(3);
var Mousetrap = __webpack_require__(117);

/**
 * Map Interactor specification.
 *
 * @typedef {object} geo.mapInteractor.spec
 * @property {number} [throttle=30] Mouse events are throttled so that an event
 *      occurs no more often that this number of milliseconds.
 * @property {boolean|number} [discreteZoom=false] If `true`, only allow
 *      discrete (integer) zoom levels and debounce with a 400 ms delay.  If a
 *      positive number, debounce zoom events with the given delay in
 *      milliseconds.
 * @property {geo.actionRecord[]} [actions] The list of available actions.  See
 *      the code for the full default list.
 * @property {object} [click] An object specifying if click events should be
 *      handled.
 * @property {boolean} [click.enabled=true] Truthy to enable click events.
 * @property {object} [click.buttons] An object with button names (`left`,
 *      `right`, `middle`), each of which is a boolean which indicates if that
 *      button triggers a click event.
 * @property {number} [click.duration=0] If a positive number, the mouse up
 *      event must occur within this time in milliseconds of the mouse down
 *      event for it to be considered a click.
 * @property {boolean} [click.cancelOnMove=true] If truthy, don't generate
 *      click events if the mouse moved at all.
 * @property {object} [keyboard] An object describing which keyboard events are
 *      handled.
 * @property {object} [keyboard.actions] An object with different actions that
 *      are trigger by the keyboard.  Each key is the event that is triggered,
 *      with the values a list of keys that trigger the event.  See the code
 *      for the defaults.
 * @property {object} [keyboard.meta] Keyboard events can generate actions of
 *      different magnitudes.  This is an object with keys of `0`, `1`, and
 *      `2`, corresponding to small, medium, and large actions.  Each entry is
 *      an object with keys of the meta keys that are required to be down or
 *      up for that scale action to trigger.  If the value of the meta key is
 *      truthy, it must be down.  If `false`, it must be up.
 * @property {boolean} [keyboard.focusHighlight=true] If truthy, when the map
 *      gains focus, a highlight style is shown around it.  This gives an
 *      indicator that keyboard events will affect the map, but may not be
 *      visuallly desirabel.
 * @property {boolean} [alwaysTouch=false] If true, add touch support even if
 *      the browser doesn't apepar to be touch-aware.
 * @property {number} [wheelScaleX=1] A scale multiplier for horizontal wheel
 *      interactions.
 * @property {number} [wheelScaleY=1] A scale multiplier for vertical wheel
 *      interactions.
 * @property {number} [zoomScale=1] This affects how far the mouse must be
 *      dragged to zoom one level.  Roughly, the mouse must move `zoomScale` *
 *      120 pixels per level.
 * @property {number} [rotateWheelScale=0.105] When the mouse wheel is used for
 *      rotation, this is the number of radians per wheel step.
 * @property {number} [zoomrotateMinimumRotation=0.087] The minimum angle of
 *      rotation in radians before a `geo_action.zoomrotate` action will allow
 *      rotation.  Set to 0 to always include rotation.
 * @property {number} [zoomrotateReverseRotation=0.698] The minimum angle of
 *      rotation (in radians) before the `geo_action.zoomrotate` action will
 *      reverse the rotation direction.  This helps reduce chatter when zooms
 *      and pans are combined with rotations.
 * @property {number} [zoomrotateMinimumZoom=0.05] The minimum zoom factor
 *      change (increasing or desceasing) before the `geo_action.zoomrotate`
 *      action will allow zoom.  Set to 0 to always include zoom.
 * @property {number} [zoomrotateMinimumPan=5] The minimum number of pixels
 *      before the `geo_action.zoomrotate` action will allow panning.  Set to 0
 *      to always include panning.
 * @property {number} [touchPanDelay=50] The touch pan delay prevents a touch
 *      pan event from immediately following a rotate (including zoom) event.
 *      No touch pan event is processed within this number of milliseconds of a
 *      non-pan touch event.
 * @property {object} [momentum] Enable momentum when panning and zooming.
 * @property {boolean} [momentum.enabled=true] Truthy to allow momentum.
 * @property {number} [momentum.maxSpeed=2.5] Maximum animation speed.
 * @property {number} [momentum.minSpeed=0.01] Animations top when they drop
 *      below this speed.
 * @property {number} [momentum.stopTime=250] If the mouse hasn't moved in this
 *      many milliseconds, don't apply momentum.  The movement is a separate
 *      action from the preceding movement.
 * @property {number} [momentum.drag=0.01] Drag coefficient; larger values slow
 *      down faster.
 * @property {string[]} [momentum.actions] A list of actions on which to apply
 *      momentum.  Defaults to pan and zoom.
 * @property {object} [spring] Enable spring clamping to screen edges.
 * @property {boolean} [spring.enabled=true] Truthy to allow edge spring back.
 * @property {number} [spring.springConstant=0.00005] Higher values spring back
 *      faster.
 * @property {object} [zoomAnimation] Enable zoom animation for both discrete
 *      and continuous zoom.
 * @property {boolean} [zoomAnimation.enabled=true] Truthy to allow zoom
 *      animation.
 * @property {number} [zoomAnimation.duration=500] The time it takes for the
 *      final zoom to be reached.
 * @property {function} [zoomAnimation.ease] The easing function for the zoom.
 *      The default is `(2 - t) * t`.
 */

/**
 * The mapInteractor class is responsible for handling raw events from the
 * browser and interpreting them as map navigation interactions.  This class
 * will call the navigation methods on the connected map, which will make
 * modifications to the camera directly.
 *
 * @class
 * @alias geo.mapInteractor
 * @extends geo.object
 * @param {geo.mapInterator.spec} args Interactor specification object.
 * @returns {geo.mapInteractor}
 */
var mapInteractor = function mapInteractor(args) {
  'use strict';

  if (!(this instanceof mapInteractor)) {
    return new mapInteractor(args);
  }
  object.call(this);

  var $ = __webpack_require__(2);
  var geo_event = __webpack_require__(6);
  var geo_action = __webpack_require__(39);
  var throttle = __webpack_require__(3).throttle;
  var debounce = __webpack_require__(3).debounce;
  var actionMatch = __webpack_require__(3).actionMatch;
  var quadFeature = __webpack_require__(17);

  var m_options,
      m_this = this,
      m_mouse,
      m_keyHandler,
      m_boundKeys,
      m_touchHandler,
      m_state,
      m_queue,
      $node,
      m_selectionLayer = null,
      m_selectionQuad,
      m_paused = false,

  // if m_clickMaybe is not false, it contains the x, y, and buttons that
  // were present when the mouse down event occurred.
  m_clickMaybe = false,
      m_clickMaybeTimeout,
      m_callZoom = function m_callZoom() {};

  // Helper method to calculate the speed from a velocity
  function calcSpeed(v) {
    var x = v.x,
        y = v.y;
    return Math.sqrt(x * x + y * y);
  }

  // copy the options object with defaults
  m_options = $.extend(true, {}, {
    throttle: 30,
    discreteZoom: false,

    /* There should only be one action with any specific combination of event
     * and modifiers.  When that event and modifiers occur, the specified
     * action is triggered.  The event and modifiers fields can either be a
     * simple string or an object with multiple entries with each entry set
     * to true, false, or undefined.  If an object, all values that are
     * truthy must match, all values that are false must not match, and all
     * other values that are falsy are ignored.
     *   Available actions:
     * see geo_action list
     *   Available events:
     * left, right, middle, wheel
     *   Available modifiers:
     * shift, ctrl, alt, meta
     *   Useful fields:
     * action: the name of the action.  Multiple events may trigger the same
     *    action.
     * input: the name of the input or an object with input names for keys
     *    and boolean values that indicates the combination of events that
     *    trigger this action.
     * modifiers: the name of a modifier or an object with modifier names for
     *    keys and boolean values that indicates the combination of modifiers
     *    that trigger this action.
     * selectionRectangle: truthy if a selection rectangle should be shown
     *    during the action.  This can be the name of an event that will be
     *    triggered when the selection is complete.
     * name: a string that can be used to reference this action.
     * owner: a string that can be used to reference this action.
     */
    actions: [{
      action: geo_action.pan,
      input: 'left',
      modifiers: { shift: false, ctrl: false },
      owner: 'geo.mapInteractor',
      name: 'button pan'
    }, {
      action: geo_action.zoom,
      input: 'right',
      modifiers: { shift: false, ctrl: false },
      owner: 'geo.mapInteractor',
      name: 'button zoom'
    }, {
      action: geo_action.zoom,
      input: 'wheel',
      modifiers: { shift: false, ctrl: false },
      owner: 'geo.mapInteractor',
      name: 'wheel zoom'
    }, {
      action: geo_action.rotate,
      input: 'left',
      modifiers: { shift: false, ctrl: true },
      owner: 'geo.mapInteractor',
      name: 'button rotate'
    }, {
      action: geo_action.rotate,
      input: 'wheel',
      modifiers: { shift: false, ctrl: true },
      owner: 'geo.mapInteractor',
      name: 'wheel rotate'
    }, {
      action: geo_action.select,
      input: 'left',
      modifiers: { shift: true, ctrl: true },
      selectionRectangle: geo_event.select,
      owner: 'geo.mapInteractor',
      name: 'drag select'
    }, {
      action: geo_action.zoomselect,
      input: 'left',
      modifiers: { shift: true, ctrl: false },
      selectionRectangle: geo_event.zoomselect,
      owner: 'geo.mapInteractor',
      name: 'drag zoom'
    }, {
      action: geo_action.unzoomselect,
      input: 'right',
      modifiers: { shift: true, ctrl: false },
      selectionRectangle: geo_event.unzoomselect,
      owner: 'geo.mapInteractor',
      name: 'drag unzoom'
    }, {
      action: geo_action.pan,
      input: 'pan',
      owner: 'geo.mapInteractor',
      name: 'touch pan'
    }, {
      action: geo_action.zoomrotate,
      input: 'rotate',
      owner: 'geo.mapInteractor',
      name: 'touch zoom and rotate'
    }],

    click: {
      enabled: true,
      buttons: { left: true, right: true, middle: true },
      duration: 0,
      cancelOnMove: true
    },

    keyboard: {
      actions: {
        /* Specific actions can be disabled by removing them from this object
         * or setting an empty list as the key bindings.  Additional actions
         * can be added to the dictionary, each of which gets a list of key
         * bindings.  See Mousetrap documentation for special key names. */
        'zoom.in': ['plus', 'shift+plus', 'shift+ctrl+plus', '=', 'shift+=', 'shift+ctrl+='],
        'zoom.out': ['-', 'shift+-', 'shift+ctrl+-', '_', 'shift+_', 'shift+ctrl+_'],
        'zoom.0': ['1'],
        'zoom.3': ['2'],
        'zoom.6': ['3'],
        'zoom.9': ['4'],
        'zoom.12': ['5'],
        'zoom.15': ['6'],
        'zoom.18': ['7'],
        'pan.left': ['left', 'shift+left', 'shift+ctrl+left'],
        'pan.right': ['right', 'shift+right', 'shift+ctrl+right'],
        'pan.up': ['up', 'shift+up', 'shift+ctrl+up'],
        'pan.down': ['down', 'shift+down', 'shift+ctrl+down'],
        'rotate.ccw': ['<', 'shift+<', 'shift+ctrl+<', '.', 'shift+.', 'shift+ctrl+.'],
        'rotate.cw': ['>', 'shift+>', 'shift+ctrl+>', ',', 'shift+,', 'shift+ctrl+,'],
        'rotate.0': ['0']
      },
      meta: {
        /* the metakeys that are down during a key event determine the
         * magnitude of the action, where 0 is the default small action
         * (1-pixel pan, small zoom, small rotation), 1 is a middle-sized
         * action, and 2 is the largest action.  Metakeys that aren't listed
         * are ignored.  Metakeys include shift, ctrl, alt, and meta (alt is
         * either the alt or option key, and meta is either windows or
         * command). */
        0: { shift: false, ctrl: false },
        1: { shift: true, ctrl: true },
        2: { shift: true, ctrl: false }
      },
      /* if focusHighlight is truthy, then a class is added to the map such
       * that when the map gets focus, it is indicated inside the border of
       * the map -- browsers usually show focus on the outside, which isn't
       * useful if the map is full window.  It might be desirable to change
       * this so it is only present if the focus is reached via the keyboard
       * (which would probably require detecting keyup events). */
      focusHighlight: true
    },
    /* Set alwaysTouch to false to only add touch support on devices that
     * report touch support.  Set to true to add touch support on all
     * devices. */
    alwaysTouch: false,
    wheelScaleX: 1,
    wheelScaleY: 1,
    zoomScale: 1,
    rotateWheelScale: 6 * Math.PI / 180,
    /* The minimum angle of rotation (in radians) before the
     * geo_action.zoomrotate action will allow rotation.  Set to 0 to always
     * include rotation. */
    zoomrotateMinimumRotation: 5.0 * Math.PI / 180,
    /* The minimum angle of rotation (in radians) before the
     * geo_action.zoomrotate action will reverse the rotation direction.
     * This helps reduce chatter when zooms and pans are combined with
     * rotations. */
    zoomrotateReverseRotation: 4.0 * Math.PI / 180,
    /* The minimum zoom factor change (increasing or decreasing) before the
     * geo_action.zoomrotate action will allow zoom.  Set to 0 to always
     * include zoom. */
    zoomrotateMinimumZoom: 0.05,
    /* The minimum number of pixels before the geo_action.zoomrotate action
     * will allow panning.  Set to 0 to always include panning. */
    zoomrotateMinimumPan: 5,
    /* The touch pan delay prevents a touch pan event from immediately
     * following a rotate (including zoom) event.  No touch pan event is
     * processed within this number of milliseconds of a non-pan touch
     * event. */
    touchPanDelay: 50,
    momentum: {
      enabled: true,
      maxSpeed: 2.5,
      minSpeed: 0.01,
      stopTime: 250,
      drag: 0.01,
      actions: [geo_action.pan, geo_action.zoom]
    },
    spring: {
      enabled: false,
      springConstant: 0.00005
    },
    zoomAnimation: {
      enabled: true,
      duration: 500,
      ease: function ease(t) {
        return (2 - t) * t;
      }
    }
  }, args || {});
  /* We don't want to merge the original arrays with arrays passed in the args,
   * so override that as necessary for actions. */
  if (args && args.actions) {
    m_options.actions = $.extend(true, [], args.actions);
  }
  if (args && args.momentum && args.momentum.actions) {
    m_options.momentum.actions = $.extend(true, [], args.momentum.actions);
  }
  if (args && args.keyboard && args.keyboard.actions !== undefined) {
    m_options.keyboard.actions = $.extend(true, {}, args.keyboard.actions);
  }

  // default mouse object
  m_mouse = {
    page: { x: 0, y: 0 }, // mouse position relative to the page
    map: { x: 0, y: 0 }, // mouse position relative to the map
    geo: { x: 0, y: 0 }, // mouse position in map interface gcs
    mapgcs: { x: 0, y: 0 }, // mouse position in map gcs
    // mouse button status
    buttons: {
      left: false,
      right: false,
      middle: false
    },
    // keyboard modifier status
    modifiers: {
      alt: false,
      ctrl: false,
      shift: false,
      meta: false
    },
    // time the event was captured
    time: new Date(),
    // time elapsed since the last mouse event
    deltaTime: 1,
    // pixels/ms
    velocity: {
      x: 0,
      y: 0
    }
  };

  /*
   * The interactor state determines what actions are taken in response to
   * core browser events.
   *
   * i.e.
   *  {
   *    'action': geo_action.pan,    * an ongoing pan event
   *    'origin': {...},       * mouse object at the start of the action
   *    'delta': {x: *, y: *} // mouse movement since action start
   *                           * not including the current event
   *  }
   *
   *  {
   *    'action': geo_action.zoom,   * an ongoing zoom event
   *    ...
   *  }
   *
   *  {
   *    'action': geo_action.rotate,   * an ongoing rotate event
   *    'origin': {...},       * mouse object at the start of the action
   *    'delta': {x: *, y: *} // mouse movement since action start
   *                           * not including the current event
   *  }
   *
   *  {
   *    'acton': geo_action.select,
   *    'origin': {...},
   *    'delta': {x: *, y: *}
   *  }
   *
   *  {
   *    'action': geo_action.momentum,
   *    'origin': {...},
   *    'handler': function () { }, // called in animation loop
   *    'timer': animate loop timer
   *  }
   */
  m_state = {};

  /**
   * Store queued map navigation commands (due to throttling) here
   * {
   *   kind: 'move' | 'wheel',  // what kind of mouse action triggered this
   *   method: function () {},  // the throttled method
   *   scroll: {x: ..., y: ...} // accumulated scroll wheel deltas
   * }
   */
  m_queue = {};

  /**
   * Process keys that we've captured.  Metakeys determine the magnitude of
   * the action.
   *
   * @param {string} action The basic action to take.
   * @param {object} evt The event with metakeys.
   * @param {object} keys Keys used to trigger the event.  `keys.simulated` is
   *    true if artificially triggered.
   * @fires geo.event.keyaction
   */
  this._handleKeys = function (action, evt, keys) {
    if (keys && keys.simulated === true) {
      evt = keys;
    }
    var meta = m_options.keyboard.meta || { 0: {} },
        map = m_this.map(),
        mapSize = map.size(),
        actionBase = action,
        actionValue = '',
        value,
        factor,
        move = {};

    for (value in meta) {
      if (meta.hasOwnProperty(value)) {
        if ((meta[value].shift === undefined || evt.shiftKey === !!meta[value].shift) && (meta[value].ctrl === undefined || evt.ctrlKey === !!meta[value].ctrl) && (meta[value].alt === undefined || evt.altKey === !!meta[value].alt) && (meta[value].meta === undefined || evt.metaKey === !!meta[value].meta)) {
          factor = value;
        }
      }
    }
    if (factor === undefined) {
      /* metakeys don't match, so don't trigger an event. */
      return;
    }

    evt.stopPropagation();
    evt.preventDefault();

    if (action.indexOf('.') >= 0) {
      actionBase = action.substr(0, action.indexOf('.'));
      actionValue = action.substr(action.indexOf('.') + 1);
    }
    switch (actionBase) {
      case 'zoom':
        switch (actionValue) {
          case 'in':
            move.zoomDelta = [0.05, 0.25, 1][factor];
            break;
          case 'out':
            move.zoomDelta = -[0.05, 0.25, 1][factor];
            break;
          default:
            if (!isNaN(parseFloat(actionValue))) {
              move.zoom = parseFloat(actionValue);
            }
            break;
        }
        break;
      case 'pan':
        switch (actionValue) {
          case 'down':
            move.panY = -Math.max(1, [0, 0.05, 0.5][factor] * mapSize.height);
            break;
          case 'left':
            move.panX = Math.max(1, [0, 0.05, 0.5][factor] * mapSize.width);
            break;
          case 'right':
            move.panX = -Math.max(1, [0, 0.05, 0.5][factor] * mapSize.width);
            break;
          case 'up':
            move.panY = Math.max(1, [0, 0.05, 0.5][factor] * mapSize.height);
            break;
        }
        break;
      case 'rotate':
        switch (actionValue) {
          case 'ccw':
            move.rotationDelta = [1, 5, 90][factor] * Math.PI / 180;
            break;
          case 'cw':
            move.rotationDelta = -[1, 5, 90][factor] * Math.PI / 180;
            break;
          default:
            if (!isNaN(parseFloat(actionValue))) {
              move.rotation = parseFloat(actionValue);
            }
            break;
        }
        break;
    }
    map.geoTrigger(geo_event.keyaction, {
      move: move,
      action: action,
      factor: factor,
      event: evt
    });
    if (move.cancel) {
      return;
    }
    if (move.zoom !== undefined) {
      map.zoom(move.zoom);
    } else if (move.zoomDelta) {
      map.zoom(map.zoom() + move.zoomDelta);
    }
    if (move.rotation !== undefined) {
      map.rotation(move.rotation);
    } else if (move.rotationDelta) {
      map.rotation(map.rotation() + move.rotationDelta);
    }
    if (move.panX || move.panY) {
      map.pan({ x: move.panX || 0, y: move.panY || 0 });
    }
  };

  /**
   * Check if this browser has touch support.
   * Copied from https://github.com/hammerjs/touchemulator under the MIT
   * license.
   *
   * @returns {boolean} `true` if there is touch support.
   */
  this.hasTouchSupport = function () {
    return 'ontouchstart' in window || // touch events
    window.Modernizr && window.Modernizr.touch || // modernizr
    (navigator.msMaxTouchPoints || navigator.maxTouchPoints) > 1; // pointer events
  };

  /**
   * Handle touch events.
   *
   * @param {object} evt The touch event.
   */
  this._handleTouch = function (evt) {
    var endIfBound = false;
    if (evt.pointerType === 'mouse' && m_touchHandler.touchSupport) {
      endIfBound = true;
    }
    if (evt.type === 'hammer.input') {
      if (m_touchHandler.lastEventType === 'pan' && evt.pointers.length !== 1) {
        endIfBound = true;
        m_touchHandler.lastEventType = null;
      } else {
        return;
      }
    }
    var evtType = /^(.*)(start|end|move|tap)$/.exec(evt.type);
    if (!evtType || evtType.length !== 3) {
      endIfBound = true;
    }
    if (endIfBound) {
      if (m_state.boundDocumentHandlers && m_touchHandler.lastEvent) {
        m_this._handleMouseUpDocument(m_touchHandler.lastEvent);
      }
      return;
    }
    evt.which = evtType[1];
    var time = new Date().valueOf();
    if (evt.which === 'pan' && m_touchHandler.lastEventType !== 'pan' && time - m_touchHandler.lastTime < m_options.touchPanDelay) {
      return;
    }
    m_touchHandler.lastTime = time;
    m_touchHandler.lastEventType = evt.which;
    m_touchHandler.lastEvent = evt;
    /* convert touch events to have page locations */
    if (evt.pageX === undefined && evt.center !== undefined && evt.center.x !== undefined) {
      evt.pageX = evt.center.x;
      evt.pageY = evt.center.y;
    }
    /* start events should occur *before* the triggering delta.  By using the
     * mouse handlers, we get all of the action properties we expect (and
     * actions can be changed or defined as we see fit). */
    if (evtType[2] === 'start') {
      m_this._handleMouseDown(evt);
      m_this._setClickMaybe(false);
      if (m_state.boundDocumentHandlers) {
        $(document).on('mousemove.geojs', m_this._handleMouseUpDocument);
      }
    }
    /* start and move events both trigger a movement */
    if (evtType[2] === 'start' || evtType[2] === 'move') {
      if (m_state.boundDocumentHandlers) {
        m_this._handleMouseMoveDocument(evt);
      } else {
        m_this._handleMouseMove(evt);
      }
    }
    if (evtType[2] === 'end' || evtType[2] === 'cancel') {
      if (m_state.boundDocumentHandlers) {
        m_this._handleMouseUpDocument(evt);
      } else {
        m_this._handleMouseUp(evt);
      }
      m_touchHandler.lastEvent = null;
    }
    /* tap events are represented as a mouse left button down and up. */
    if (evtType[2] === 'tap' && !m_state.boundDocumentHandlers) {
      evt.which = 1;
      m_touchHandler.lastEventType = null;
      m_this._handleMouseDown(evt);
      m_this._handleMouseUp(evt);
      m_touchHandler.lastEventType = evtType[2];
    }
  };

  /**
   * Retrigger a mouse movement with the current mouse state.
   * @fires geo.event.mousemove
   */
  this.retriggerMouseMove = function () {
    m_this.map().geoTrigger(geo_event.mousemove, m_this.mouse());
  };

  /**
   * Connects events to a map.  If the map is not set, then this does nothing.
   * @returns {this}
   */
  this._connectEvents = function () {
    if (!m_options.map) {
      return m_this;
    }

    // prevent double binding to DOM elements
    m_this._disconnectEvents();

    // store the connected element
    $node = $(m_options.map.node());

    // set methods related to asynchronous event handling
    m_this._handleMouseWheel = throttled_wheel();
    m_callZoom = debounced_zoom();

    // catalog what inputs we are using
    util.adjustActions(m_options.actions);
    var usedInputs = {};
    ['right', 'pan', 'rotate'].forEach(function (input) {
      usedInputs[input] = m_options.actions.some(function (action) {
        return action.input[input];
      });
    });
    // add event handlers
    $node.on('wheel.geojs', m_this._handleMouseWheel);
    $node.on('mousemove.geojs', m_this._handleMouseMove);
    $node.on('mousedown.geojs', m_this._handleMouseDown);
    $node.on('mouseup.geojs', m_this._handleMouseUp);
    // Disable dragging images and such
    $node.on('dragstart', function () {
      return false;
    });
    if (usedInputs.right) {
      $node.on('contextmenu.geojs', function () {
        return false;
      });
    }

    // bind keyboard events
    if (m_options.keyboard && m_options.keyboard.actions) {
      m_keyHandler = Mousetrap($node[0]);
      var bound = [];
      for (var keyAction in m_options.keyboard.actions) {
        if (m_options.keyboard.actions.hasOwnProperty(keyAction)) {
          m_keyHandler.bind(m_options.keyboard.actions[keyAction], function (action) {
            return function (evt, keys) {
              m_this._handleKeys(action, evt, keys);
            };
          }(keyAction));
          bound = bound.concat(m_options.keyboard.actions[keyAction]);
        }
      }
      m_boundKeys = bound;
    }
    $node.toggleClass('highlight-focus', !!(m_boundKeys && m_boundKeys.length && m_options.keyboard.focusHighlight));
    // bind touch events
    if ((m_this.hasTouchSupport() || m_options.alwaysTouch) && (usedInputs.pan || usedInputs.rotate)) {
      // webpack expects optional dependencies to be wrapped in a try-catch
      var Hammer;
      try {
        Hammer = __webpack_require__(283);
      } catch (_error) {}
      if (Hammer !== undefined) {
        var recog = [],
            touchEvents = ['hammer.input'];
        if (usedInputs.rotate) {
          recog.push([Hammer.Rotate, { enable: true }]);
          touchEvents = touchEvents.concat(['rotatestart', 'rotateend', 'rotatemove']);
        }
        if (usedInputs.pan) {
          recog.push([Hammer.Pan, { direction: Hammer.DIRECTION_ALL }]);
          touchEvents = touchEvents.concat(['panstart', 'panend', 'panmove']);
        }
        /* Always handle tap events.  Reject double taps. */
        recog.push([Hammer.Tap, { event: 'doubletap', taps: 2 }]);
        recog.push([Hammer.Tap, { event: 'singletap' }, undefined, ['doubletap']]);
        touchEvents = touchEvents.concat(['singletap']);
        var hammerParams = { recognizers: recog, preventDefault: true };
        m_touchHandler = {
          manager: new Hammer.Manager($node[0], hammerParams),
          touchSupport: m_this.hasTouchSupport(),
          lastTime: 0
        };
        m_touchHandler.manager.get('doubletap').recognizeWith('singletap');
        touchEvents.forEach(function (touchEvent) {
          m_touchHandler.manager.on(touchEvent, m_this._handleTouch);
        });
      }
    }

    return m_this;
  };

  /**
   * Disconnects events to a map.  If the map is not set, then this does
   * nothing.
   * @returns {this}
   */
  this._disconnectEvents = function () {
    if (m_boundKeys) {
      if (m_keyHandler) {
        m_keyHandler.unbind(m_boundKeys);
      }
      m_boundKeys = null;
      m_keyHandler = null;
    }
    if (m_touchHandler) {
      m_touchHandler.manager.destroy();
      m_touchHandler = null;
    }
    if ($node) {
      $node.off('.geojs');
      $node = null;
    }
    m_this._handleMouseWheel = function () {};
    m_callZoom = function m_callZoom() {};

    return m_this;
  };

  /**
   * Sets or gets map for this interactor, adds draw region layer if needed.
   *
   * @param {geo.map} [val] Either a new map object for `undefined` to return
   *    the current map object.
   * @returns {geo.map|this} Either the current map object or the
   *    mapInteractor class instance.
   */
  this.map = function (val) {
    if (val !== undefined) {
      m_options.map = val;
      m_this._connectEvents();
      return m_this;
    }
    return m_options.map;
  };

  /**
   * Gets/sets the options object for the interactor.
   *
   * @param {geo.mapInteractor.spec} opts Options to set.
   * @returns {geo.mapInteractor.spec|this}
   */
  this.options = function (opts) {
    if (opts === undefined) {
      return $.extend({}, m_options);
    }
    $.extend(m_options, opts);

    // reset event handlers for new options
    this._connectEvents();
    return m_this;
  };

  /**
   * Stores the current mouse position from an event.
   *
   * @param {jQuery.Event} evt JQuery event with the mouse position.
   */
  this._getMousePosition = function (evt) {
    var offset = $node.offset(),
        dt,
        t;

    t = new Date().valueOf();
    dt = t - m_mouse.time;
    m_mouse.time = t;
    m_mouse.deltaTime = dt;
    m_mouse.velocity = {
      x: (evt.pageX - m_mouse.page.x) / dt,
      y: (evt.pageY - m_mouse.page.y) / dt
    };
    m_mouse.page = {
      x: evt.pageX,
      y: evt.pageY
    };
    m_mouse.map = {
      x: evt.pageX - offset.left,
      y: evt.pageY - offset.top
    };
    try {
      m_mouse.geo = m_this.map().displayToGcs(m_mouse.map);
      m_mouse.mapgcs = m_this.map().displayToGcs(m_mouse.map, null);
    } catch (e) {
      // catch georeferencing problems and move on
      m_mouse.geo = m_mouse.mapgcs = null;
    }
  };

  /**
   * Stores the current mouse button state in the m_mouse object.
   *
   * @param {jQuery.Event} evt The event that trigger the mouse action.
   */
  this._getMouseButton = function (evt) {
    for (var prop in m_mouse.buttons) {
      if (m_mouse.buttons.hasOwnProperty(prop)) {
        m_mouse.buttons[prop] = false;
      }
    }
    /* If the event buttons are specified, use them in preference to the
     * evt.which for determining which buttons are down.  buttons is a bitfield
     * and therefore can represent more than one button at a time. */
    if (evt.buttons !== undefined) {
      m_mouse.buttons.left = !!(evt.buttons & 1);
      m_mouse.buttons.right = !!(evt.buttons & 2);
      m_mouse.buttons.middle = !!(evt.buttons & 4);
    } else if (evt.type !== 'mouseup') {
      /* If we don't evt.buttons, fall back to which, but not on mouseup. */
      switch (evt.which) {
        case 1:
          m_mouse.buttons.left = true;break;
        case 2:
          m_mouse.buttons.middle = true;break;
        case 3:
          m_mouse.buttons.right = true;break;
      }
    }
    /* When handling touch events, evt.which can be a string, in which case
     * handle a "button" with that name -- a non-integer string will not
     * evaluate as being between 1 and 3. */
    if (evt.which && !(evt.which >= 1 && evt.which <= 3)) {
      m_mouse.buttons[evt.which] = true;
    }
  };

  /**
   * Stores the current keyboard modifiers from an event in the m_mouse
   * object.
   *
   * @param {jQuery.Event} evt JQuery event with the keyboard modifiers.
   */
  this._getMouseModifiers = function (evt) {
    m_mouse.modifiers.alt = evt.altKey;
    m_mouse.modifiers.ctrl = evt.ctrlKey;
    m_mouse.modifiers.meta = evt.metaKey;
    m_mouse.modifiers.shift = evt.shiftKey;
  };

  /**
   * Compute a selection information object.
   *
   * @private
   * @returns {geo.brushSelection}
   */
  this._getSelection = function () {
    var origin = m_state.origin,
        mouse = m_this.mouse(),
        map = m_this.map(),
        display = {},
        gcs = {};

    // TODO: clamp to map bounds
    // Get the display coordinates
    display.upperLeft = {
      x: Math.min(origin.map.x, mouse.map.x),
      y: Math.min(origin.map.y, mouse.map.y)
    };

    display.lowerRight = {
      x: Math.max(origin.map.x, mouse.map.x),
      y: Math.max(origin.map.y, mouse.map.y)
    };

    display.upperRight = {
      x: display.lowerRight.x,
      y: display.upperLeft.y
    };

    display.lowerLeft = {
      x: display.upperLeft.x,
      y: display.lowerRight.y
    };

    // Get the gcs coordinates
    gcs.upperLeft = map.displayToGcs(display.upperLeft, null);
    gcs.lowerRight = map.displayToGcs(display.lowerRight, null);
    gcs.upperRight = map.displayToGcs(display.upperRight, null);
    gcs.lowerLeft = map.displayToGcs(display.lowerLeft, null);

    m_selectionQuad.data([{
      ul: gcs.upperLeft,
      ur: gcs.upperRight,
      ll: gcs.lowerLeft,
      lr: gcs.lowerRight
    }]);
    m_selectionQuad.draw();

    return {
      display: display,
      gcs: gcs,
      mouse: mouse,
      origin: $.extend({}, m_state.origin)
    };
  };

  /**
   * Immediately cancel an ongoing action.
   *
   * @param {string?} action The action type, if `null` cancel any action.
   * @param {boolean} [keepQueue] If truthy, keep the queue event if an action
   *    is canceled.
   * @returns {boolean} Set if an action was canceled.
   */
  this.cancel = function (action, keepQueue) {
    var out;
    if (!action) {
      out = !!m_state.action;
    } else {
      out = m_state.action === action;
    }
    if (out) {
      // cancel any queued interaction events
      if (!keepQueue) {
        m_queue = {};
      }
      clearState();
    }
    return out;
  };

  /**
   * Set the value of whether a click is possible.  Cancel any outstanding
   * timer for this process.
   *
   * @param {false|object} value If `false`, there is no chance of a future
   *    click.  If an object with coordinates and the mouse button state,
   *    a click is possible if the same button is released nearby.
   */
  this._setClickMaybe = function (value) {
    m_clickMaybe = value;
    if (m_clickMaybeTimeout) {
      window.clearTimeout(m_clickMaybeTimeout);
      m_clickMaybeTimeout = null;
    }
  };

  /**
   * Handle event when a mouse button is pressed.
   *
   * @param {jQuery.Event} evt The event that triggered this.
   * @fires geo.event.brushstart
   * @fires geo.event.actiondown
   */
  this._handleMouseDown = function (evt) {
    var action, actionRecord;

    if (m_paused) {
      return;
    }
    /* In some scenarios, we get both a tap event and then, somewhat later, a
     * set of mousedown/mouseup events.  Ignore the spurious down/up set if we
     * just handled a tap. */
    if (m_touchHandler && m_touchHandler.lastEventType === 'tap' && new Date().valueOf() - m_touchHandler.lastTime < 1000) {
      return;
    }

    m_this._getMousePosition(evt);
    m_this._getMouseButton(evt);
    m_this._getMouseModifiers(evt);

    if (m_options.click.enabled && (!m_mouse.buttons.left || m_options.click.buttons.left) && (!m_mouse.buttons.right || m_options.click.buttons.right) && (!m_mouse.buttons.middle || m_options.click.buttons.middle)) {
      m_this._setClickMaybe({
        x: m_mouse.page.x,
        y: m_mouse.page.y,
        buttons: $.extend({}, m_mouse.buttons)
      });
      if (m_options.click.duration > 0) {
        m_clickMaybeTimeout = window.setTimeout(function () {
          m_clickMaybe = false;
          m_clickMaybeTimeout = null;
        }, m_options.click.duration);
      }
    }
    actionRecord = actionMatch(m_mouse.buttons, m_mouse.modifiers, m_options.actions);
    action = (actionRecord || {}).action;

    var map = m_this.map();
    // cancel transitions and momentum on click
    map.transitionCancel('_handleMouseDown' + (action ? '.' + action : ''));
    m_this.cancel(geo_action.momentum);

    m_mouse.velocity = {
      x: 0,
      y: 0
    };

    if (action) {
      // cancel any ongoing interaction queue
      m_queue = {
        kind: 'move'
      };

      // store the state object
      m_state = {
        action: action,
        actionRecord: actionRecord,
        origin: $.extend(true, {}, m_mouse),
        initialZoom: map.zoom(),
        initialRotation: map.rotation(),
        initialEventRotation: evt.rotation,
        delta: { x: 0, y: 0 }
      };

      if (actionRecord.selectionRectangle) {
        // Make sure the old selection layer is gone.
        if (m_selectionLayer) {
          m_selectionLayer.clear();
          map.deleteLayer(m_selectionLayer);
          m_selectionLayer = null;
        }
        m_selectionLayer = map.createLayer('feature', { features: [quadFeature.capabilities.color] });
        m_selectionQuad = m_selectionLayer.createFeature('quad', { gcs: map.gcs() });
        m_selectionQuad.style({
          opacity: 0.25,
          color: { r: 0.3, g: 0.3, b: 0.3 }
        });
        map.geoTrigger(geo_event.brushstart, m_this._getSelection());
      }
      map.geoTrigger(geo_event.actiondown, {
        state: m_this.state(), mouse: m_this.mouse(), event: evt });

      // bind temporary handlers to document
      if (m_options.throttle > 0) {
        $(document).on('mousemove.geojs', throttle(m_options.throttle, m_this._handleMouseMoveDocument));
      } else {
        $(document).on('mousemove.geojs', m_this._handleMouseMoveDocument);
      }
      $(document).on('mouseup.geojs', m_this._handleMouseUpDocument);
      m_state.boundDocumentHandlers = true;
    }
  };

  /**
   * Handle mouse move event.
   *
   * @param {jQuery.Event} evt The event that triggered this.
   * @fires geo.event.mousemove
   */
  this._handleMouseMove = function (evt) {
    if (m_paused) {
      return;
    }

    if (m_state.boundDocumentHandlers) {
      // If currently performing a navigation action, the mouse
      // coordinates will be captured by the document handler.
      return;
    }

    if (m_options.click.cancelOnMove && m_clickMaybe) {
      m_this._setClickMaybe(false);
    }

    m_this._getMousePosition(evt);
    m_this._getMouseButton(evt);
    m_this._getMouseModifiers(evt);

    if (m_clickMaybe) {
      return;
    }

    m_this.map().geoTrigger(geo_event.mousemove, m_this.mouse());
  };

  /**
   * Handle the zoomrotate action.
   *
   * @param {object} evt The mouse event that triggered this.
   */
  this._handleZoomrotate = function (evt) {
    /* Only zoom if we have once exceeded the initial zoom threshold. */
    var deltaZoom = Math.log2(evt.scale);
    if (!m_state.zoomrotateAllowZoom && deltaZoom && Math.abs(deltaZoom) >= Math.log2(1 + m_options.zoomrotateMinimumZoom)) {
      if (m_options.zoomrotateMinimumZoom) {
        m_state.initialZoom -= deltaZoom;
      }
      m_state.zoomrotateAllowZoom = true;
    }
    if (m_state.zoomrotateAllowZoom && deltaZoom) {
      var zoom = m_state.initialZoom + deltaZoom;
      m_this.map().zoom(zoom, m_state.origin);
    }
    /* Only rotate if we have once exceeded the initial rotation threshold.  The
     * first time this happens (if the threshold is greater than zero), set the
     * start of rotation to the current position, so that there is no sudden
     * jump. */
    var deltaTheta = (evt.rotation - m_state.initialEventRotation) * Math.PI / 180;
    if (!m_state.zoomrotateAllowRotation && deltaTheta && Math.abs(deltaTheta) >= m_options.zoomrotateMinimumRotation) {
      if (m_options.zoomrotateMinimumRotation) {
        m_state.initialEventRotation = evt.rotation;
        deltaTheta = 0;
      }
      m_state.zoomrotateAllowRotation = true;
    }
    if (m_state.zoomrotateAllowRotation) {
      var theta = m_state.initialRotation + deltaTheta;
      /* Compute the delta in the range of [-PI, PI). */
      deltaTheta = util.wrapAngle(theta - m_this.map().rotation());
      /* If we reverse direction, don't rotate until some threshold is
       * exceeded.  This helps prevent rotation bouncing while panning. */
      if (deltaTheta && (deltaTheta * (m_state.lastRotationDelta || 0) >= 0 || Math.abs(deltaTheta) >= m_options.zoomrotateReverseRotation)) {
        m_this.map().rotation(theta, m_state.origin);
        m_state.lastRotationDelta = deltaTheta;
      }
    }
    /* Only pan if we have once exceed the initial pan threshold. */
    var panOrigin = m_state.origin.page;
    if (m_state.initialEventGeo) {
      var offset = $node.offset();
      panOrigin = m_this.map().gcsToDisplay(m_state.initialEventGeo);
      panOrigin.x += offset.left;
      panOrigin.y += offset.top;
    }
    var x = evt.pageX,
        deltaX = x - panOrigin.x,
        y = evt.pageY,
        deltaY = y - panOrigin.y,
        deltaPan2 = deltaX * deltaX + deltaY * deltaY;
    if (!m_state.zoomrotateAllowPan && deltaPan2 && deltaPan2 >= m_options.zoomrotateMinimumPan * m_options.zoomrotateMinimumPan) {
      if (m_options.zoomrotateMinimumPan) {
        deltaX = deltaY = 0;
        m_state.initialEventGeo = m_this.mouse().geo;
      } else {
        m_state.initialEventGeo = m_state.origin.geo;
      }
      m_state.zoomrotateAllowPan = true;
    }
    if (m_state.zoomrotateAllowPan && (deltaX || deltaY)) {
      m_this.map().pan({ x: deltaX, y: deltaY });
    }
  };

  /**
   * Handle mouse move event on the document (temporary bindings).
   *
   * @param {jQuery.Event} evt The event that triggered this.
   * @fires geo.event.brush
   * @fires geo.event.actionmove
   */
  this._handleMouseMoveDocument = function (evt) {
    var dx, dy, selectionObj;

    // If the map has been disconnected, we do nothing.
    if (!m_this.map()) {
      return;
    }

    if (m_paused || m_queue.kind !== 'move') {
      return;
    }

    m_this._getMousePosition(evt);
    m_this._getMouseButton(evt);
    m_this._getMouseModifiers(evt);

    /* Only cancel possible clicks on move if we actually moved */
    if (m_options.click.cancelOnMove && (m_clickMaybe.x === undefined || m_mouse.page.x !== m_clickMaybe.x || m_mouse.page.y !== m_clickMaybe.y)) {
      m_this._setClickMaybe(false);
    }
    if (m_clickMaybe) {
      return;
    }

    if (!m_state.action) {
      // This shouldn't happen
      console.log('WARNING: Invalid state in mapInteractor.');
      return;
    }

    // calculate the delta from the origin point to avoid
    // accumulation of floating point errors
    dx = m_mouse.map.x - m_state.origin.map.x - m_state.delta.x;
    dy = m_mouse.map.y - m_state.origin.map.y - m_state.delta.y;
    m_state.delta.x += dx;
    m_state.delta.y += dy;

    if (m_state.action === geo_action.pan) {
      m_this.map().pan({ x: dx, y: dy }, undefined, 'limited');
    } else if (m_state.action === geo_action.zoom) {
      m_callZoom(-dy * m_options.zoomScale / 120, m_state);
    } else if (m_state.action === geo_action.rotate) {
      var cx, cy;
      if (m_state.origin.rotation === undefined) {
        cx = m_state.origin.map.x - m_this.map().size().width / 2;
        cy = m_state.origin.map.y - m_this.map().size().height / 2;
        m_state.origin.rotation = m_this.map().rotation() - Math.atan2(cy, cx);
      }
      cx = m_mouse.map.x - m_this.map().size().width / 2;
      cy = m_mouse.map.y - m_this.map().size().height / 2;
      m_this.map().rotation(m_state.origin.rotation + Math.atan2(cy, cx));
    } else if (m_state.action === geo_action.zoomrotate) {
      m_this._handleZoomrotate(evt);
    } else if (m_state.actionRecord.selectionRectangle) {
      // Get the bounds of the current selection
      selectionObj = m_this._getSelection();
      m_this.map().geoTrigger(geo_event.brush, selectionObj);
    }
    m_this.map().geoTrigger(geo_event.actionmove, {
      state: m_this.state(), mouse: m_this.mouse(), event: evt });

    // Prevent default to stop text selection in particular
    evt.preventDefault();
  };

  /**
   * Clear the action state, but remember if we have bound document handlers.
   * @private
   */
  function clearState() {
    m_state = { boundDocumentHandlers: m_state.boundDocumentHandlers };
  }

  /**
   * Use interactor options to modify the mouse velocity by momentum
   * or spring equations depending on the current map state.
   * @private
   * @param {object} v Current velocity in pixels / millisecond.
   * @param {number} deltaT The time delta.
   * @returns {object} New velocity.
   */
  function modifyVelocity(v, deltaT) {
    deltaT = deltaT <= 0 ? 30 : deltaT;
    var sf = springForce();
    var speed = calcSpeed(v);
    var vx = v.x / speed;
    var vy = v.y / speed;

    speed = speed * Math.exp(-m_options.momentum.drag * deltaT);

    // |force| + |velocity| < c <- stopping condition
    if (calcSpeed(sf) * deltaT + speed < m_options.momentum.minSpeed) {
      return null;
    }

    if (speed > 0) {
      vx = vx * speed;
      vy = vy * speed;
    } else {
      vx = 0;
      vy = 0;
    }

    return {
      x: vx - sf.x * deltaT,
      y: vy - sf.y * deltaT
    };
  }

  /**
   * Get the spring force for the current map bounds.
   * @private
   * @returns {object} The spring force.
   */
  function springForce() {
    var xplus, // force to the right
    xminus, // force to the left
    yplus, // force to the top
    yminus; // force to the bottom

    if (!m_options.spring.enabled) {
      return { x: 0, y: 0 };
    }
    // get screen coordinates of corners
    var maxBounds = m_this.map().maxBounds(undefined, null);
    var ul = m_this.map().gcsToDisplay({
      x: maxBounds.left,
      y: maxBounds.top
    }, null);
    var lr = m_this.map().gcsToDisplay({
      x: maxBounds.right,
      y: maxBounds.bottom
    }, null);

    var c = m_options.spring.springConstant;
    // Arg... map needs to expose the canvas size
    var width = m_this.map().node().width();
    var height = m_this.map().node().height();

    xplus = c * Math.max(0, ul.x);
    xminus = c * Math.max(0, width - lr.x);
    yplus = c * Math.max(0, ul.y) / 2;
    yminus = c * Math.max(0, height - lr.y) / 2;

    return {
      x: xplus - xminus,
      y: yplus - yminus
    };
  }

  /**
   * Based on the screen coordinates of a selection, zoom or unzoom and
   * recenter.
   *
   * @private
   * @param {string} action Either `geo_action.zoomselect` or
   *    `geo_action.unzoomselect`.
   * @param {object} lowerLeft The x and y coordinates of the lower left corner
   *    of the zoom rectangle.
   * @param {object} upperRight The x and y coordinates of the upper right
   *    corner of the zoom rectangle.
   */
  this._zoomFromSelection = function (action, lowerLeft, upperRight) {
    if (action !== geo_action.zoomselect && action !== geo_action.unzoomselect) {
      return;
    }
    if (lowerLeft.x === upperRight.x || lowerLeft.y === upperRight.y) {
      return;
    }
    var zoom,
        center,
        map = m_this.map(),
        mapsize = map.size();
    /* To arbitrarily handle rotation and projection, we center the map at the
     * central coordinate of the selection and set the zoom level such that the
     * four corners are just barely on the map.  When unzooming (zooming out),
     * we ensure that the previous view is centered in the selection but use
     * the maximal size for the zoom factor. */
    var scaling = {
      x: Math.abs((upperRight.x - lowerLeft.x) / mapsize.width),
      y: Math.abs((upperRight.y - lowerLeft.y) / mapsize.height)
    };
    if (action === geo_action.zoomselect) {
      center = map.displayToGcs({
        x: (lowerLeft.x + upperRight.x) / 2,
        y: (lowerLeft.y + upperRight.y) / 2
      }, null);
      zoom = map.zoom() - Math.log2(Math.max(scaling.x, scaling.y));
    } else {
      /* unzoom */
      /* To make the existing visible map entirely within the selection
       * rectangle, this would be changed to Math.min instead of Math.max of
       * the scaling factors.  This felt wrong, though. */
      zoom = map.zoom() + Math.log2(Math.max(scaling.x, scaling.y));
      /* Record the current center.  Later, this is panned to the center of the
       * selection rectangle. */
      center = map.center(undefined, null);
    }
    /* When discrete zoom is enable, always round down.  We have to do this
     * explicitly, as otherwise we may zoom too far and the selection will not
     * be completely visible. */
    if (map.discreteZoom()) {
      zoom = Math.floor(zoom);
    }
    map.zoom(zoom);
    if (action === geo_action.zoomselect) {
      map.center(center, null);
    } else {
      var newcenter = map.gcsToDisplay(center, null);
      map.pan({
        x: (lowerLeft.x + upperRight.x) / 2 - newcenter.x,
        y: (lowerLeft.y + upperRight.y) / 2 - newcenter.y
      });
    }
  };

  /**
   * Handle event when a mouse button is unpressed on the document.
   * Removes temporary bindings.
   *
   * @param {jQuery.Event} evt The event that triggered this.
   * @fires geo.event.brushend
   * @fires geo.event.actionselection
   * @fires geo.event.actionup
   * @fires geo.event.select
   * @fires geo.event.zoomselect
   * @fires geo.event.unzoomselect
   */
  this._handleMouseUpDocument = function (evt) {
    var selectionObj, oldAction;

    if (m_paused) {
      return;
    }

    // cancel queued interactions
    m_queue = {};

    m_this._setClickMaybe(false);
    m_this._getMouseButton(evt);
    m_this._getMouseModifiers(evt);

    // unbind temporary handlers on document
    $(document).off('.geojs');
    m_state.boundDocumentHandlers = false;

    if (m_mouse.buttons.right) {
      evt.preventDefault();
    }

    if (m_state.actionRecord && m_state.actionRecord.selectionRectangle) {
      m_this._getMousePosition(evt);
      selectionObj = m_this._getSelection();

      m_selectionLayer.clear();
      m_this.map().deleteLayer(m_selectionLayer);
      m_selectionLayer = null;
      m_selectionQuad = null;

      m_this.map().geoTrigger(geo_event.brushend, selectionObj);
      if (m_state.actionRecord.selectionRectangle !== true) {
        m_this.map().geoTrigger(m_state.actionRecord.selectionRectangle, selectionObj);
      }
      m_this._zoomFromSelection(m_state.action, selectionObj.display.lowerLeft, selectionObj.display.upperRight);
      m_this.map().geoTrigger(geo_event.actionselection, {
        state: m_this.state(),
        mouse: m_this.mouse(),
        event: evt,
        lowerLeft: selectionObj.display.lowerLeft,
        upperRight: selectionObj.display.upperRight });
    }
    m_this.map().geoTrigger(geo_event.actionup, {
      state: m_this.state(), mouse: m_this.mouse(), event: evt });

    // reset the interactor state
    oldAction = m_state.action;
    clearState();

    // if momentum is enabled, start the action here
    if (m_options.momentum.enabled && $.inArray(oldAction, m_options.momentum.actions) >= 0) {
      var t = new Date().valueOf();
      var dt = t - m_mouse.time + m_mouse.deltaTime;
      if (t - m_mouse.time < m_options.momentum.stopTime) {
        m_mouse.velocity.x *= m_mouse.deltaTime / dt;
        m_mouse.velocity.y *= m_mouse.deltaTime / dt;
        m_mouse.deltaTime = dt;
      } else {
        m_mouse.velocity.x = m_mouse.velocity.y = 0;
      }
      m_this.springBack(true, oldAction);
    }
  };

  /**
   * Handle event when a mouse button is unpressed.
   *
   * @param {jQuery.Event} evt The event that triggered this.
   */
  this._handleMouseUp = function (evt) {
    if (m_paused) {
      return;
    }

    m_this._getMouseButton(evt);

    if (m_clickMaybe) {
      m_this._handleMouseClick(evt);
    }
  };

  /**
   * Handle event when a mouse click is detected.  A mouse click is a simulated
   * event that occurs when the time between a mouse down and mouse up
   * is less than the configured duration and (optionally) if no mousemove
   * events were triggered in the interim.
   *
   * @param {jQuery.Event} evt The event that triggered this.
   * @fires geo.event.mouseup
   */
  this._handleMouseClick = function (evt) {

    /* Cancel a selection if it is occurring */
    if (m_state.actionRecord && m_state.actionRecord.selectionRectangle) {
      m_selectionLayer.clear();
      m_this.map().deleteLayer(m_selectionLayer);
      m_selectionLayer = null;
      m_selectionQuad = null;
      m_state.action = m_state.actionRecord = null;
    }
    m_this._getMouseButton(evt);
    m_this._getMouseModifiers(evt);

    // cancel any ongoing pan action
    m_this.cancel(geo_action.pan);

    // unbind temporary handlers on document
    $(document).off('.geojs');
    m_state.boundDocumentHandlers = false;
    // add information about the button state to the event information
    var details = m_this.mouse();
    details.buttonsDown = m_clickMaybe.buttons;

    // reset click detector variable
    m_this._setClickMaybe(false);
    // fire a click event
    m_this.map().geoTrigger(geo_event.mouseclick, details);
  };

  /**
   * Private wrapper around the map zoom method that is debounced to support
   * discrete zoom interactions.
   *
   * @returns {function} A function to handle zooms that debounces such
   *    events.  This function is passed the zoom level and the mouse state.
   */
  function debounced_zoom() {
    var deltaZ = 0,
        delay = 400,
        origin,
        startZoom,
        targetZoom;

    function accum(dz, org) {
      var map = m_this.map(),
          zoom;

      origin = $.extend(true, {}, org);
      deltaZ += dz;
      if (targetZoom === undefined) {
        startZoom = targetZoom = map.zoom();
      }
      targetZoom += dz;

      // Respond to debounced events when they add up to a change in the
      // discrete zoom level.
      if (map && Math.abs(deltaZ) >= 1 && m_options.discreteZoom && !m_options.zoomAnimation.enabled) {

        zoom = Math.round(deltaZ + map.zoom());

        // delta is what is left over from the zoom delta after the new zoom
        // value
        deltaZ = deltaZ + map.zoom() - zoom;

        map.zoom(zoom, origin);
      }
    }

    function apply() {
      var map = m_this.map(),
          zoom;
      if (map) {
        if (m_options.zoomAnimation.enabled) {
          zoom = targetZoom;
          if (m_options.discreteZoom) {
            zoom = Math.round(zoom);
            if (zoom === startZoom && targetZoom !== startZoom) {
              zoom = startZoom + (targetZoom > startZoom ? 1 : -1);
            }
          }
          map.transitionCancel('debounced_zoom.' + geo_action.zoom);
          map.transition({
            zoom: zoom,
            zoomOrigin: origin,
            duration: m_options.zoomAnimation.duration,
            ease: m_options.zoomAnimation.ease,
            done: function done(status) {
              status = status || {};
              var zoomRE = new RegExp('\\.' + geo_action.zoom + '$');
              if (!status.next && (!status.cancel || ('' + status.source).search(zoomRE) < 0)) {
                targetZoom = undefined;
              }
              /* If we were animating the zoom, if the zoom is continuous, just
               * stop where we are.  If using discrete zoom, we need to make
               * sure we end up discrete.  However, we don't want to do that if
               * the next action is further zooming. */
              if (m_options.discreteZoom && status.cancel && status.transition && status.transition.end && ('' + status.source).search(zoomRE) < 0) {
                map.zoom(status.transition.end.zoom, status.transition.end.zoomOrigin);
              }
            }
          });
        } else {
          zoom = deltaZ + map.zoom();
          if (m_options.discreteZoom) {
            // round off the zoom to an integer and throw away the rest
            zoom = Math.round(zoom);
          }
          map.zoom(zoom, origin);
        }
      }
      deltaZ = 0;
    }

    if (m_options.discreteZoom !== true && m_options.discreteZoom > 0) {
      delay = m_options.discreteZoom;
    }
    if ((m_options.discreteZoom === true || m_options.discreteZoom > 0) && !m_options.zoomAnimation.enabled) {
      return debounce(delay, false, apply, accum);
    } else {
      return function (dz, org) {
        if (!dz && targetZoom === undefined) {
          return;
        }
        accum(dz, org);
        apply(dz, org);
      };
    }
  }

  /**
   * Attaches wrapped methods for accumulating fast mouse wheel events and
   * throttling map interactions.
   * @private
   *
   * @returns {function} A function that takes a jQuery.Event for mouse wheel
   *    actions.
   * @fires geo.event.actionwheel
   */
  function throttled_wheel() {
    var my_queue = {};

    function accum(evt) {
      var dx, dy;

      if (m_paused) {
        return;
      }

      if (my_queue !== m_queue) {
        my_queue = {
          kind: 'wheel',
          scroll: { x: 0, y: 0 }
        };
        m_queue = my_queue;
      }

      evt.preventDefault();

      // try to normalize deltas using the wheel event standard:
      //   https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent
      evt.deltaFactor = 1;
      if (evt.originalEvent.deltaMode === 1) {
        // DOM_DELTA_LINE -- estimate line height
        evt.deltaFactor = 40;
      } else if (evt.originalEvent.deltaMode === 2) {
        // DOM_DELTA_PAGE -- get window height
        evt.deltaFactor = $(window).height();
      }

      // prevent NaN's on legacy browsers
      dx = evt.originalEvent.deltaX || 0;
      dy = evt.originalEvent.deltaY || 0;

      // scale according to the options
      dx = dx * m_options.wheelScaleX * evt.deltaFactor / 120;
      dy = dy * m_options.wheelScaleY * evt.deltaFactor / 120;

      my_queue.scroll.x += dx;
      my_queue.scroll.y += dy;
    }

    function wheel(evt) {
      var zoomFactor, action, actionRecord;

      // If the current queue doesn't match the queue passed in as an argument,
      // assume it was cancelled and do nothing.
      if (my_queue !== m_queue) {
        return;
      }

      // perform the map navigation event
      m_this._getMouseModifiers(evt);

      actionRecord = actionMatch({ wheel: true }, m_mouse.modifiers, m_options.actions);
      action = (actionRecord || {}).action;

      if (action) {
        // if we were moving because of momentum or a transition, cancel it and
        // recompute where the mouse action is occurring.
        var recompute = m_this.map().transitionCancel('wheel.' + action);
        recompute |= m_this.cancel(geo_action.momentum, true);
        if (recompute) {
          m_mouse.geo = m_this.map().displayToGcs(m_mouse.map);
          m_mouse.mapgcs = m_this.map().displayToGcs(m_mouse.map, null);
        }
        switch (action) {
          case geo_action.pan:
            m_this.map().pan({
              x: m_queue.scroll.x,
              y: m_queue.scroll.y
            }, undefined, 'limited');
            break;
          case geo_action.zoom:
            zoomFactor = -m_queue.scroll.y;
            m_callZoom(zoomFactor, m_mouse);
            break;
          case geo_action.rotate:
            m_this.map().rotation(m_this.map().rotation() + m_queue.scroll.y * m_options.rotateWheelScale, m_mouse);
            break;
        }
        m_this.map().geoTrigger(geo_event.actionwheel, {
          state: m_this.state(), mouse: m_this.mouse(), event: evt });
      }

      // reset the queue
      m_queue = {};
    }

    if (m_options.throttle > 0) {
      return throttle(m_options.throttle, false, wheel, accum);
    } else {
      return function (evt) {
        accum(evt);
        wheel(evt);
      };
    }
  }

  /**
   * Handle mouse wheel event.  (Defined inside _connectEvents).
   */
  this._handleMouseWheel = function () {};

  /**
   * Start up a spring back action when the map bounds are out of range.
   * Not to be user callable.
   * @protected
   *
   * @param {boolean} initialVelocity Truthy if the mouse's current velocity
   *    should be used as the initial velocity.  False to assume no initial
   *    velocity.
   * @param {object} origAction The original action that started the spring
   *    back.  If this was a zoom action, the spring back includes zoom;
   *    otherwise it only includes panning.
   */
  this.springBack = function (initialVelocity, origAction) {
    if (m_state.action === geo_action.momentum) {
      return;
    }
    if (!initialVelocity) {
      m_mouse.velocity = {
        x: 0,
        y: 0
      };
    }
    m_state.origAction = origAction;
    m_state.action = geo_action.momentum;
    m_state.origin = m_this.mouse();
    m_state.momentum = m_this.mouse();
    m_state.start = new Date();
    m_state.handler = function () {
      var v, s, last, dt;

      if (m_state.action !== geo_action.momentum || !m_this.map() || m_this.map().transition()) {
        // cancel if a new action was performed
        return;
      }
      // Not sure the correct way to do this.  We need the delta t for the
      // next time step...  Maybe use a better interpolator and the time
      // parameter from requestAnimationFrame.
      dt = Math.min(m_state.momentum.deltaTime, 30);

      last = m_state.start.valueOf();
      m_state.start = new Date();

      v = modifyVelocity(m_state.momentum.velocity, m_state.start - last);

      // stop panning when the speed is below the threshold
      if (!v) {
        clearState();
        return;
      }

      s = calcSpeed(v);
      if (s > m_options.momentum.maxSpeed) {
        s = m_options.momentum.maxSpeed / s;
        v.x = v.x * s;
        v.y = v.y * s;
      }

      if (!isFinite(v.x) || !isFinite(v.y)) {
        v.x = 0;
        v.y = 0;
      }
      m_state.momentum.velocity.x = v.x;
      m_state.momentum.velocity.y = v.y;

      switch (m_state.origAction) {
        case geo_action.zoom:
          var dy = m_state.momentum.velocity.y * dt;
          m_callZoom(-dy * m_options.zoomScale / 120, m_state);
          break;
        default:
          m_this.map().pan({
            x: m_state.momentum.velocity.x * dt,
            y: m_state.momentum.velocity.y * dt
          }, undefined, 'limited');
          break;
      }

      if (m_state.handler) {
        m_this.map().scheduleAnimationFrame(m_state.handler);
      }
    };
    if (m_state.handler) {
      m_this.map().scheduleAnimationFrame(m_state.handler);
    }
  };

  /**
   * Public method that unbinds all events.
   */
  this.destroy = function () {
    m_this._disconnectEvents();
    if (m_this.map()) {
      $(m_this.map().node()).removeClass('highlight-focus');
    }
    m_this.map(null);
  };

  /**
   * Get current mouse information.
   *
   * @returns {object} The current mouse state.
   */
  this.mouse = function () {
    return $.extend(true, {}, m_mouse);
  };

  /**
   * Get/set current keyboard information.
   *
   * @param {object} [newValue] Either a object with new options for the
   *    keyboard actions or `undefined` to get the current options.
   * @returns {object|this} Either the current keyboard options or this
   *    mapInteractor class instance.
   */
  this.keyboard = function (newValue) {
    if (newValue === undefined) {
      return $.extend(true, {}, m_options.keyboard || {});
    }
    return m_this.options({ keyboard: newValue });
  };

  /**
   * Get the current interactor state.
   *
   * @returns {geo.interactorState}
   */
  this.state = function () {
    return $.extend(true, {}, m_state);
  };

  /**
   * Get or set the pause state of the interactor, which ignores all native
   * mouse and keyboard events.
   *
   * @param {boolean} [value] The pause state to set or undefined to return
   *    the current state.
   * @returns {boolean|this} The current pause state or this class instance.
   */
  this.pause = function (value) {
    if (value === undefined) {
      return m_paused;
    }
    m_paused = !!value;
    return m_this;
  };

  /**
   * Add an action to the list of handled actions.
   *
   * @param {object} action An object defining the action.  This must have
   *    action and input properties, and may have modifiers, name, and owner.
   *    Use action, name, and owner to make this entry distinct if it will need
   *    to be removed later.
   * @param {boolean} [toEnd] The action is added at the beginning of the
   *    actions list unless truthy.  Earlier actions prevent later actions with
   *    similar input and modifiers.
   */
  this.addAction = function (action, toEnd) {
    if (!action || !action.action || !action.input) {
      return;
    }
    util.addAction(m_options.actions, action, toEnd);
    if (m_options.map && m_options.actions.some(function (action) {
      return action.input.right;
    })) {
      $node.off('contextmenu.geojs');
      $node.on('contextmenu.geojs', function () {
        return false;
      });
    }
  };

  /**
   * Check if an action is in the actions list.  An action matches if the
   * action, name, and owner match.  A null or undefined value will match all
   * actions.  If using an action object, this is the same as passing
   * (action.action, action.name, action.owner).
   *
   * @param {object|string} action Either an action object or the name of an
   *    action.
   * @param {string} name Optional name associated with the action.
   * @param {string} owner Optional owner associated with the action.
   * @returns {object|null} The first matching action or null.
   */
  this.hasAction = function (action, name, owner) {
    return util.hasAction(m_options.actions, action, name, owner);
  };

  /**
   * Remove all matching actions.  Actions are matched as with hasAction.
   *
   * @param {object|string} action Either an action object or the name of an
   *    action.
   * @param {string} name Optional name associated with the action.
   * @param {string} owner Optional owner associated with the action.
   * @returns {number} The number of actions that were removed.
   */
  this.removeAction = function (action, name, owner) {
    var removed = util.removeAction(m_options.actions, action, name, owner);
    if (m_options.map && !m_options.actions.some(function (action) {
      return action.input.right;
    })) {
      $node.off('contextmenu.geojs');
    }
    return removed;
  };

  /**
   * Simulate a DOM mouse event on connected map.  Not all options are required
   * for every event type.
   *
   * @param {string} type Event type `mousemove`, `mousedown`, `mouseup`, etc.
   *    `keyboard` is treated separately from other types.
   * @param {object} options Options for the simulated event.
   * @param {boolean} [options.shift] A boolean if a `keyboard` event has the
   *    shift key down or explicitly up.
   * @param {boolean} [options.shiftKey] Alternate name to `shift`.
   * @param {boolean} [options.ctrl] A boolean if a `keyboard` event has the
   *    control key down or explicitly up.
   * @param {boolean} [options.ctrlKey] Alternate name to `ctrl`.
   * @param {boolean} [options.alt] A boolean if a `keyboard` event has the
   *    alt key down or explicitly up.
   * @param {boolean} [options.altKey] Alternate name to `alt`.
   * @param {boolean} [options.meta] A boolean if a `keyboard` event has the
   *    meta key down or explicitly up.
   * @param {boolean} [options.metaKey] Alternate name to `meta`.
   * @param {string} [options.keys] A Mousetrap-style key sequence string for
   *    `keyboard` events.
   * @param {geo.screenPosition} [options.page] The position of the event on
   *    the window.  Overridden by `map`.
   * @param {geo.screenPosition} [options.map] The position of the event on the
   *    map in pixels relative to the map's div.
   * @param {geo.screenPosition} [options.center] The position of a touch
   *    event center relative to the window.
   * @param {string} [button] One of `left`, `middle`, or `right` for mouse
   *    events.
   * @param {string} [modifiers] A space-separated list of metakeys that are
   *    down on mouse events.
   * @param {geo.screenPosition} [wheelDelta] The amount the wheel moved in
   *    both directions for wheel events.  One step is often 20 units of
   *    vement.
   * @param {number} [wheelMode] The wheel delta mode.  See
   *    https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent .
   * @param {boolean} [touch] `truthy` if this is a touch event.
   * @param {number} [rotation] Touch event rotation in degrees.
   * @param {number} [scale] Touch event scale.  Initial events should have a
   *    scale of 1; subsequent events should increase or decrease this to
   *    simulate spread and pinch actions.
   * @param {number[]} [pointers] A list of pointer numbers involved in a
   *    touch event.  Pointers are number from one up, so `[1]` is the first
   *    touch point, `[1, 2]` are two touch points, and `[2]` is when the first
   *    point was released but the second is still touching.
   * @param {string} [pointerType] `mouse` if this is a mouse action rather
   *    than a touch action.
   * @returns {mapInteractor}
   */
  this.simulateEvent = function (type, options) {
    var evt, page, offset, which, buttons;

    if (!m_this.map()) {
      return m_this;
    }

    if (type === 'keyboard' && m_keyHandler) {
      /* Mousetrap passes through the keys we send, but not an event object,
       * so we construct an artificial event object as the keys, and use that.
       */
      var keys = {
        shiftKey: options.shift || options.shiftKey || false,
        ctrlKey: options.ctrl || options.ctrlKey || false,
        altKey: options.alt || options.altKey || false,
        metaKey: options.meta || options.metaKey || false,
        toString: function toString() {
          return options.keys;
        },
        stopPropagation: function stopPropagation() {},
        preventDefault: function preventDefault() {},
        simulated: true
      };
      m_keyHandler.trigger(keys);
      return;
    }

    page = options.page || {};

    if (options.map) {
      offset = $node.offset();
      page.x = options.map.x + offset.left;
      page.y = options.map.y + offset.top;
    }

    if (options.button === 'left') {
      which = 1;
      buttons = 1;
    } else if (options.button === 'right') {
      which = 3;
      buttons = 2;
    } else if (options.button === 'middle') {
      which = 2;
      buttons = 4;
    }

    options.modifiers = options.modifiers || [];
    options.wheelDelta = options.wheelDelta || {};

    evt = $.Event(type, {
      pageX: page.x,
      pageY: page.y,
      which: which,
      buttons: buttons,
      altKey: options.modifiers.indexOf('alt') >= 0,
      ctrlKey: options.modifiers.indexOf('ctrl') >= 0,
      metaKey: options.modifiers.indexOf('meta') >= 0,
      shiftKey: options.modifiers.indexOf('shift') >= 0,
      center: options.center,
      rotation: options.touch ? options.rotation || 0 : options.rotation,
      scale: options.touch ? options.scale || 1 : options.scale,
      pointers: options.pointers,
      pointerType: options.pointerType,

      originalEvent: {
        deltaX: options.wheelDelta.x,
        deltaY: options.wheelDelta.y,
        deltaMode: options.wheelMode,
        preventDefault: function preventDefault() {},
        stopPropagation: function stopPropagation() {},
        stopImmediatePropagation: function stopImmediatePropagation() {}
      }
    });
    if (options.touch && m_touchHandler) {
      m_this._handleTouch(evt);
    } else {
      $node.trigger(evt);
    }
    if (type.indexOf('.geojs') >= 0) {
      $(document).trigger(evt);
    }
  };
  this._connectEvents();
  return this;
};

inherit(mapInteractor, object);
module.exports = mapInteractor;

/***/ }),
/* 52 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz',
    strLength = 8;

/**
 * Get a random string to use as a div ID.
 *
 * @alias geo.d3.uniqueID
 * @returns {string} A random ID string.
 */
var uniqueID = function uniqueID() {
  var strArray = [],
      i;
  strArray.length = strLength;
  for (i = 0; i < strLength; i += 1) {
    strArray[i] = chars.charAt(Math.floor(Math.random() * chars.length));
  }
  return strArray.join('');
};

module.exports = uniqueID;

/***/ }),
/* 53 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var inherit = __webpack_require__(0);
var feature = __webpack_require__(8);

/**
 * Mesh feature specification.
 *
 * @typedef {geo.feature.spec} geo.meshFeature.spec
 * @property {object[]} [data=[]] An array of arbitrary objects used to
 *    construct the feature.
 * @property {geo.feature.styleSpec} [style] An object that contains style
 *    values for the feature.
 * @property {geo.meshFeature.meshSpec} [mesh] The mesh specification for the
 *    feature.
 */

/**
 * A mesh formed by a set of triangular or square elements or a
 * squarely-connected grid that is of rectangular extent.  The gird can be
 * regularly spaced or have arbitrary position coordinates for each node.  All
 * of these properties can be functions, which get passed `data`.
 *
 * @typedef {object} geo.meshFeature.meshSpec
 * @property {number[]|array.<number[]>} [elements] If specified, a list of
 *      indices into the data array that form elements.  If this is an array of
 *      arrays, each subarray must have at least either 3 values for triangular
 *      elements or 4 values for square elements.  If this is a single array,
 *      the data indices are of all elements are packed one after another and
 *      the `elementPacking` property is used to determine element shape.  If
 *      this `null` or `undefined`, a rectangular grid of squares is used based
 *      on `gridWidth` and `gridHeight` or an implicit version of those
 *      parameters.
 * @property {string} [elementPacking='auto'] If `elements` is provided as a
 *      single array, this determines the shape of the elements.  It is one of
 *      `auto`, `triangle`, or `square`.  `triangle` uses triplets of values to
 *      define elements.  `square` uses quads of values.  `auto` is identical
 *      to `triangle` unless `elements`'s length is a multiple of 4 and *not* a
 *      multiple of 3, in which case it is the same as `square`.
 * @property {number} [gridWidth] The number of data columns in the grid.  If
 *      this is not specified and `gridHeight` is given, this is the number of
 *      data elements divided by `gridHeight`.  If neither `gridWidth` nor
 *      `gridHeight` are specified, the squareroot of the number of data
 *      elements is used.  If both are specified, some data could be unused.
 *      Ignored if `elements` is used.
 * @property {number} [gridHeight] The number of data rows in the grid.  If
 *      this is not specified and `gridWidth` is given, this is the number of
 *      data elements divided by `gridWidth`.  If neither `gridWidth` not
 *      `gridHeight` are specified, the squareroot of the number of data
 *      elements is used.  If both are specified, some data could be unused.
 *      Ignored if `elements` is used.
 * @property {number} [x0] The x coordinate of the 0th data point.  If `null`
 *      or `undefined`, the coordinate is taken from the `position` style.
 *      Ignored if `elements` is used.
 * @property {number} [y0] The y coordinate of the 0th data point.  If `null`
 *      or `undefined`, the coordinate is taken from the `position` style.
 *      Ignored if `elements` is used.
 * @property {number} [dx] The distance in the x direction between the 0th and
 *      1st data point.  This may be positive or negative.  If 0, `null`, or
 *      `undefined`, the coordinate is taken from the `position` style.
 *      Ignored if `elements` is used.
 * @property {number} [dy] The distance in the y direction between the 0th and
 *      `gridWidth`th data point.  This may be positive or negative.  If 0,
 *      `null`, or `undefined`, the coordinate is taken from the `position`
 *      style.  Ignored if `elements` is used.
 * @property {boolean} [wrapLongitude=true] If truthy and `position` is not
 *      used (`elements` is not used and `x0`, `y0`, `dx`, and `dy` are all set
 *      appropriately), assume the x coordinates are longitude and should be
 *      adjusted to be within -180 to 180.  If the data spans 180 degrees, the
 *      points or squares that straddle the meridian will be duplicated to
 *      ensure that the map is covered from -180 to 180 as appropriate.  Set
 *      this to `false` if using a non-longitude x coordinate.
 */

/**
 * A set of elements and their associated positions for a mesh.
 *
 * @typedef {object} geo.meshFeature.meshInfo
 * @property {string} shape One of `'triangle'` or `'square'`.  If `square`,
 *      each pair of elements is one square.  These elements have vertices v00,
 *      v01, v02, v10, v11, v12.  The square is formed via v00-v01-v10-v11-v00.
 *      For `triangle`, each element stands alone.
 * @property {number[]} elements A packed array of indices into the `pos` array
 *      defining the elements.  Each sequential three values forms a triangle.
 * @property {number[]} index An array that references which data index is
 *      associated with each vertex.
 * @property {number[]} pos A packed array of coordinates in the interface gcs
 *      for the vertices.  This is in the order x0, y0, z0, x1, y1, z2, x2, ...
 * @property {number} numberVertices the number of vertices in the mesh.
 * @property {number} numberElementsthe number of elements (squares or
 *      triangles) in the mesh.
 * @property {number} verticesPerElement 3 for triangles, 6 for squares.
 */

/**
 * Create a new instance of class meshFeature.  This should be the parent of a
 * more useable feature class.
 *
 * @class
 * @alias geo.meshFeature
 * @extends geo.feature
 *
 * @param {geo.meshFeature.spec} arg
 * @returns {this}
 */
var meshFeature = function meshFeature(arg) {
  'use strict';

  if (!(this instanceof meshFeature)) {
    return new meshFeature(arg);
  }

  var $ = __webpack_require__(2);
  var util = __webpack_require__(3);

  arg = arg || {};
  feature.call(this, arg);

  var m_this = this,
      s_init = this._init,
      m_mesh = {};

  /**
   * Get/Set mesh accessor.
   *
   * @param {string|geo.meshFeature.meshSpec} [specOrProperty] If `undefined`,
   *    return the current mesh specification.  If a string is specified,
   *    either get or set the named mesh property.  If an object is given, set
   *    or update the specification with the specified parameters.
   * @param {object} [value] If `specOrProperty` is a string, set that property
   *    to `value`.  If `undefined`, return the current value of the named
   *    property.
   * @returns {geo.meshFeature.meshSpec|object|this} The current mesh
   *    specification, the value of a named mesh property, or this mesh object.
   */
  this.mesh = function (specOrProperty, value) {
    if (specOrProperty === undefined) {
      return m_mesh;
    }
    if (typeof specOrProperty === 'string' && value === undefined) {
      return m_mesh[specOrProperty];
    }
    if (value === undefined) {
      var mesh = $.extend({}, {
        gridWidth: function gridWidth() {
          if (specOrProperty.gridHeight) {
            return Math.floor(m_this.data().length / specOrProperty.gridHeight);
          }
          return Math.floor(Math.sqrt(m_this.data().length));
        },
        gridHeight: function gridHeight() {
          if (specOrProperty.gridWidth) {
            return Math.floor(m_this.data().length / specOrProperty.gridWidth);
          }
          return Math.floor(Math.sqrt(m_this.data().length));
        }
      }, m_mesh, specOrProperty);
      m_mesh = mesh;
    } else {
      m_mesh[specOrProperty] = value;
    }
    m_this.modified();
    return m_this;
  };

  /**
   * A uniform getter that always returns a function even for constant values.
   * If undefined input, return all the mesh values as an object.
   *
   * @function mesh_DOT_get
   * @memberof geo.meshFeature
   * @instance
   * @param {string|undefined} key The name of the mesh key or `undefined` to
   *    return an object with all keys as functions.
   * @returns {object|function} A function related to the key, or an object
   *    with all mesh keys, each of which is a function.
   */
  this.mesh.get = function (key) {
    if (key === undefined) {
      var all = {},
          k;
      for (k in m_mesh) {
        if (m_mesh.hasOwnProperty(k)) {
          all[k] = m_this.mesh.get(k);
        }
      }
      return all;
    }
    return util.ensureFunction(m_mesh[key]);
  };

  /**
   * Get/Set position accessor.  This is identical to getting or setting the
   * `position` style.
   *
   * @param {array|function} [val] If specified, set the position style.  If
   *    `undefined`, return the current value.
   * @returns {array|function|this} Either the position style or this.
   */
  this.position = function (val) {
    if (val === undefined) {
      return m_this.style('position');
    } else {
      m_this.style('position', val);
      m_this.dataTime().modified();
      m_this.modified();
    }
    return m_this;
  };

  /**
   * Create a set of vertices and elements from a mesh specification.
   *
   * This currently takes a set of `vertexValueFuncs` to detemrine which
   * vertices are used (shown) and values at those vertices.  It could be
   * extended with `elementValueFuncs`.
   *
   * @param {object} [vertexValueFuncs] A dictionary where the keys are the
   *    names of properties to include in the results and the values are
   *    functions that are evaluated at each vertex with the arguments
   *    `(data[idx], idx, position)`.  If a key is named `used`, then its
   *    function is passed `(data[idx], idx)` and if it returns a falsy value
   *    for a data point, the vertex associated with that data point is removed
   *    from the resultant mesh.
   * @returns {geo.meshFeature.meshInfo} An object with the mesh information.
   */
  this._createMesh = function (vertexValueFuncs) {
    vertexValueFuncs = vertexValueFuncs || {};
    var i,
        i3,
        j,
        k,
        idx,
        numPts,
        usedPts,
        usePos,
        item,
        key,
        data = m_this.data(),
        posFunc = m_this.position(),
        posVal,
        elements = m_this.mesh.get('elements')(data),
        elementPacking = m_this.mesh.get('elementPacking')(data),
        gridW = m_this.mesh.get('gridWidth')(data),
        gridH = m_this.mesh.get('gridHeight')(data),
        wrapLongitude = m_this.mesh.get('wrapLongitude')(data),
        x0 = m_this.mesh.get('x0')(data),
        y0 = m_this.mesh.get('y0')(data),
        dx = m_this.mesh.get('dx')(data),
        dy = m_this.mesh.get('dy')(data),
        calcX,
        skipColumn,
        x,
        origI,
        /* used for wrapping */
    gridWorig = gridW,
        /* can be different when wrapping */
    result = {
      shape: 'square',
      elements: []
    };
    /* If we are using a grid, calculate the elements and positions. */
    if (!elements) {
      if (gridW * gridH > data.length) {
        gridH = Math.floor(data.length / gridW);
      }
      /* If we are not using the position values (we are using x0, y0, dx, dy),
       * and wrapLongitude is turned on, and the position spans 180 degrees,
       * duplicate one or two columns of points at opposite ends of the map. */
      usePos = elements || x0 === null || x0 === undefined || y0 === null || y0 === undefined || !dx || !dy;
      wrapLongitude = !!(wrapLongitude === undefined || wrapLongitude);
      if (!usePos && wrapLongitude && (x0 < -180 || x0 > 180 || x0 + dx * (gridW - 1) < -180 || x0 + dx * (gridW - 1) > 180) && dx > -180 && dx < 180 && dx * (gridW - 1) < 360 + 1e-4) {
        calcX = [];
        for (i = 0; i < gridW; i += 1) {
          x = x0 + i * dx;
          while (x < -180) {
            x += 360;
          }
          while (x > 180) {
            x -= 360;
          }
          if (i && Math.abs(x - calcX[calcX.length - 1]) > 180) {
            if (x > calcX[calcX.length - 1]) {
              calcX.push(x - 360);
              calcX.push(calcX[calcX.length - 2] + 360);
            } else {
              calcX.push(x + 360);
              calcX.push(calcX[calcX.length - 2] - 360);
            }
            skipColumn = i;
          }
          calcX.push(x);
        }
        gridW += 2;
        if (Math.abs(Math.abs(gridWorig * dx) - 360) < 0.01) {
          gridW += 1;
          x = x0 + gridWorig * dx;
          while (x < -180) {
            x += 360;
          }
          while (x > 180) {
            x -= 360;
          }
          calcX.push(x);
        }
      }
      /* Calculate the value for point */
      numPts = gridW * gridH;
      if (skipColumn !== undefined) {
        result.index = new Array(numPts);
        for (i = 0; i < numPts; i += 1) {
          j = Math.floor(i / gridW);
          origI = i - j * gridW;
          origI += origI > skipColumn ? -2 : 0;
          if (origI >= gridWorig) {
            origI -= gridWorig;
          }
          origI += j * gridWorig;
          result.index[i] = origI;
        }
      }
      /* Create triangles */
      for (j = idx = 0; j < gridH - 1; j += 1, idx += 1) {
        for (i = 0; i < gridW - 1; i += 1, idx += 1) {
          if (i !== skipColumn) {
            result.elements.push(idx);
            result.elements.push(idx + 1);
            result.elements.push(idx + gridW);
            result.elements.push(idx + gridW + 1);
            result.elements.push(idx + gridW);
            result.elements.push(idx + 1);
          }
        }
      }
    } else {
      /* use defined elements */
      var hasSubArray = elements.length && Array.isArray(elements[0]);
      if (elementPacking === 'square' || elementPacking !== 'triangle' && (!hasSubArray && !(elements.length % 4) && elements.length % 3 || hasSubArray && elements[0].length === 4)) {
        result.shape = 'square';
        if (hasSubArray) {
          for (i = 0; i < elements.length; i += 1) {
            result.elements.push(elements[i][0]);
            result.elements.push(elements[i][1]);
            result.elements.push(elements[i][3]);
            result.elements.push(elements[i][2]);
            result.elements.push(elements[i][3]);
            result.elements.push(elements[i][1]);
          }
        } else {
          for (i = 0; i < elements.length - 3; i += 4) {
            result.elements.push(elements[i]);
            result.elements.push(elements[i + 1]);
            result.elements.push(elements[i + 3]);
            result.elements.push(elements[i + 2]);
            result.elements.push(elements[i + 3]);
            result.elements.push(elements[i + 1]);
          }
        }
      } else {
        result.shape = 'triangle';
        if (hasSubArray) {
          for (i = 0; i < elements.length; i += 1) {
            result.elements.push(elements[i][0]);
            result.elements.push(elements[i][1]);
            result.elements.push(elements[i][2]);
          }
        } else {
          result.elements = elements.slice(0, elements.length - elements.length % 3);
        }
      }
      numPts = data.length;
      usePos = true;
    }
    result.verticesPerElement = result.shape === 'triangle' ? 3 : 6;
    /* If we have a `vertexValueFuncs.used` function, remove any unused
     * vertices.  Then, remove any elements that have a vertex that can't be
     * used.  This could leave vertices that are unused by any element, but
     * removing those is expensive so it is not done. */
    if (vertexValueFuncs.used) {
      for (i = 0; i < numPts; i += 1) {
        idx = result.index ? result.index[i] : i;
        if (!vertexValueFuncs.used(data[idx], idx)) {
          break;
        }
      }
      if (i !== numPts) {
        usedPts = i;
        var remap = new Array(numPts),
            vpe = result.verticesPerElement;
        for (j = 0; j < usedPts; j += 1) {
          remap[j] = j;
        }
        remap[usedPts] = -1;
        if (!result.index) {
          result.index = new Array(data.length);
          for (j = 0; j < data.length; j += 1) {
            result.index[j] = j;
          }
        }
        for (i = usedPts + 1; i < numPts; i += 1) {
          idx = result.index[i];
          if (vertexValueFuncs.used(data[idx], idx)) {
            remap[i] = usedPts;
            result.index[usedPts] = result.index[i];
            usedPts += 1;
          } else {
            remap[i] = -1;
          }
        }
        result.index.splice(usedPts);
        for (i = k = 0; i < result.elements.length; i += vpe) {
          for (j = 0; j < vpe; j += 1) {
            if (remap[result.elements[i + j]] < 0) {
              break;
            }
            result.elements[k + j] = remap[result.elements[i + j]];
          }
          if (j === vpe) {
            k += vpe;
          }
        }
        result.elements.splice(k);
        numPts = usedPts;
      }
    }
    /* Get point locations and store them in a packed array */
    result.pos = new Array(numPts * 3);
    for (key in vertexValueFuncs) {
      if (key !== 'used' && vertexValueFuncs.hasOwnProperty(key)) {
        result[key] = new Array(numPts);
      }
    }
    for (i = i3 = 0; i < numPts; i += 1, i3 += 3) {
      idx = result.index ? result.index[i] : i;
      item = data[idx];
      if (usePos) {
        posVal = posFunc(item, idx);
        result.pos[i3] = posVal.x;
        result.pos[i3 + 1] = posVal.y;
        result.pos[i3 + 2] = posVal.z || 0;
      } else {
        if (skipColumn === undefined) {
          result.pos[i3] = x0 + dx * (idx % gridW);
        } else {
          result.pos[i3] = calcX[i % gridW];
        }
        result.pos[i3 + 1] = y0 + dy * Math.floor(idx / gridW);
        result.pos[i3 + 2] = 0;
        posVal = { x: result.pos[i3], y: result.pos[i3 + 1], z: result.pos[i3 + 2] };
      }
      for (key in vertexValueFuncs) {
        if (key !== 'used' && vertexValueFuncs.hasOwnProperty(key)) {
          result[key][i] = vertexValueFuncs[key](item, idx, posVal);
        }
      }
    }
    result.numVertices = numPts;
    result.numElements = result.elements.length / result.verticesPerElement;
    return result;
  };

  /**
   * Initialize.
   *
   * @param {geo.meshFeature.spec} arg The mesh feature specification.
   */
  this._init = function (arg) {
    s_init.call(m_this, arg);

    /* Initialize from arguments */
    arg = arg || {};
    var style = $.extend({}, {
      position: function position(d) {
        /* We could construct a new object and return
         *  {x: d.x, y: d.y, z: d.z};
         * but that isn't necessary. */
        return d;
      }
    }, arg.style || {});

    this.mesh(arg.mesh || {});
    this.style(style);
  };

  return this;
};

inherit(meshFeature, feature);

module.exports = meshFeature;

/***/ }),
/* 54 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var $ = __webpack_require__(2);

/**
 * This class defines the raw interface for a "tile" on a map.  A tile is
 * defined as a rectangular section of a map.  The base implementation
 * is independent of the actual content of the tile, but assumes that
 * the content is loaded asynchronously via a url.  The tile object
 * has a promise-like interface.
 * @example
 * tile.then(function (data) {...}).catch(function (data) {...});
 *
 * @class
 * @alias geo.tile
 * @param {object} spec The tile specification.
 *
 * @param {object} spec.index The global position of the tile.
 * @param {number} spec.index.x The x-coordinate (usually the column number).
 * @param {number} spec.index.y The y-coordinate (usually the row number).
 *
 * @param {object} spec.size The size of each tile.
 * @param {number} spec.size.x Width (usually in pixels).
 * @param {number} spec.size.y Height (usually in pixels).
 *
 * @param {object|string} spec.url A url or jQuery ajax config object.
 *
 * @param {object?} spec.overlap The size of overlap with neighboring tiles.
 * @param {number} [spec.overlap.x=0]
 * @param {number} [spec.overlap.y=0]
 * @returns {geo.tile}
 */
var tile = function tile(spec) {
  if (!(this instanceof tile)) {
    return new tile(spec);
  }

  this._index = spec.index;
  this._size = spec.size;
  this._overlap = spec.overlap || { x: 0, y: 0 };
  this._wrap = spec.wrap || { x: 1, y: 1 };
  this._url = spec.url;
  this._fetched = false;
  this._queue = spec.queue || null;

  /**
   * Return the index coordinates.  Read only.
   *
   * @property {object} index The tile index.
   * @property {number} index.x The tile x index.
   * @property {number} index.y The tile y index.
   * @name geo.tile#index
   */
  Object.defineProperty(this, 'index', {
    get: function get() {
      return this._index;
    }
  });

  /**
   * Return the tile size.  Read only.
   *
   * @property {object} size The tile size.
   * @property {number} size.x The tile width.
   * @property {number} size.y The tile height.
   * @name geo.tile#size
   */
  Object.defineProperty(this, 'size', {
    get: function get() {
      return this._size;
    }
  });

  /**
   * Return the tile overlap.  Read only.
   *
   * @property {object} overlap The tile overlap.
   * @property {number} overlap.x The tile x overlap.
   * @property {number} overlap.y The tile y overlap.
   * @name geo.tile#overlap
   */
  Object.defineProperty(this, 'overlap', {
    get: function get() {
      return this._overlap;
    }
  });

  /**
   * Initiate the ajax request and add a promise interface to the tile
   * object.  This method exists to allow derived classes the ability to
   * override how the tile is obtained.  For example, imageTile uses an
   * Image element rather than $.get.
   *
   * @returns {this}
   */
  this.fetch = function () {
    if (!this._fetched) {
      $.get(this._url).done(function () {
        this._fetched = true;
      }.bind(this)).promise(this);
    }
    return this;
  };

  /**
   * Return whether this tile has been fetched already.
   *
   * @returns {boolean} True if the tile has been fetched.
   */
  this.fetched = function () {
    return this._fetched;
  };

  /**
   * Add a method to be called with the data when the ajax request is
   * successfully resolved.
   *
   * @param {function?} onSuccess The success handler.
   * @param {function?} onFailure The failure handler.
   * @returns {this}
   */
  this.then = function (onSuccess, onFailure) {
    // both fetch and _queueAdd can replace the current then method
    if (!this.fetched() && this._queue && this._queue.add && (!this.state || this.state() === 'pending')) {
      this._queue.add(this, this.fetch);
    } else {
      this.fetch();
    }
    // Call then on the new promise
    if (this.done && this.fail) {
      this.done(onSuccess).fail(onFailure);
    } else {
      this.then(onSuccess, onFailure);
    }
    return this;
  };

  /**
   * Add a method to be called with the data when the ajax fails.
   *
   * @param {function} method The rejection handler.
   * @returns {this}
   */
  this.catch = function (method) {
    this.then(undefined, method);
    return this;
  };

  /**
   * Return a unique string representation of the given tile useable as a
   * hash key.  Possibly extend later to include url information to make
   * caches aware of the tile source.
   *
   * @returns {string}
   */
  this.toString = function () {
    return [this._index.level || 0, this._index.y, this._index.x].join('_');
  };

  /**
   * Return the bounds of the tile given an index offset and a translation.
   *
   * @param {object} index The tile index containing (0, 0).
   * @param {object} shift The coordinates of (0, 0) inside the tile.
   * @returns {object} An object with `left`, `top`, `right`, `bottom`.
   */
  this.bounds = function (index, shift) {
    var left, right, bottom, top;
    left = this.size.x * (this.index.x - index.x) - this.overlap.x - shift.x;
    right = left + this.size.x + this.overlap.x * 2;
    top = this.size.y * (this.index.y - index.y) - this.overlap.y - shift.y;
    bottom = top + this.size.y + this.overlap.y * 2;
    return {
      left: left,
      right: right,
      bottom: bottom,
      top: top
    };
  };

  /**
   * Computes the global coordinates of the bottom edge.
   * @property {number} bottom The global coordinates of the bottom edge.
   * @name geo.tile#bottom
   */
  Object.defineProperty(this, 'bottom', {
    get: function get() {
      return this.size.y * (this.index.y + 1) + this.overlap.y;
    }
  });

  /**
   * Computes the global coordinates of the top edge.
   * @property {number} top The global coordinates of the top edge.
   * @name geo.tile#top
   */
  Object.defineProperty(this, 'top', {
    get: function get() {
      return this.size.y * this.index.y - this.overlap.y;
    }
  });

  /**
   * Computes the global coordinates of the left edge.
   * @property {number} left The global coordinates of the left edge.
   * @name geo.tile#left
   */
  Object.defineProperty(this, 'left', {
    get: function get() {
      return this.size.x * this.index.x - this.overlap.x;
    }
  });

  /**
   * Computes the global coordinates of the right edge.
   * @property {number} right The global coordinates of the right edge.
   * @name geo.tile#right
   */
  Object.defineProperty(this, 'right', {
    get: function get() {
      return this.size.x * (this.index.x + 1) + this.overlap.x;
    }
  });

  /**
   * Set the opacity of the tile to 0 and gradually fade in over the given
   * number of milliseconds.  This is just a delay.
   *
   * @param {number} duration The duration of the animation in ms.
   * @returns {this}
   */
  this.fadeIn = function (duration) {
    $.noop(duration);
    return this;
  };
};

module.exports = tile;

/***/ }),
/* 55 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var inherit = __webpack_require__(0);
var meshFeature = __webpack_require__(53);
var registry = __webpack_require__(1);
var util = __webpack_require__(3);

/**
 * Isoline feature specification.
 *
 * @typedef {geo.feature.spec} geo.isolineFeature.spec
 * @property {object[]} [data=[]] An array of arbitrary objects used to
 *    construct the feature.
 * @property {geo.isolineFeature.styleSpec} [style] An object that contains
 *    style values for the feature.
 * @property {geo.isolineFeature.isolineSpec} [isoline] The isoline
 *    specification for the feature.
 */

/**
 * Style specification for an isoline feature.  Extends
 * {@link geo.lineFeasture.styleSpec} and {@link geo.textFeasture.styleSpec}.
 *
 * @typedef {geo.feature.styleSpec} geo.isolineFeature.styleSpec
 * @extends geo.feature.styleSpec
 * @extends geo.textFeature.styleSpec
 * @extends geo.lineFeature.styleSpec
 * @property {geo.geoPosition|function} [position=data] The position of each
 *    data element.  This defaults to just using `x`, `y`, and `z` properties
 *    of the data element itself.  The position is in the feature's gcs
 *    coordinates.
 * @property {number|function} [value=data.z] The value of each data element.
 *    This defaults to the `z` property of the data elements.  If the value of
 *    a grid point is `null` or `undefined`, the point and elements that use
 *    that point won't be included in the results.
 * @property {geo.geoColor|function} [strokeColor='black'] Color to stroke each
 *    line.
 * @property {number|function} [strokeWidth] The weight of the line stroke in
 *    pixels.  This defaults to the line value's level + 0.5.
 * @property {boolean|function} [rotateWithMap=true] Rotate label text when the
 *    map rotates.
 * @property {number|function} [rotation] Text rotation in radians.  This
 *    defaults to the label oriented so that top of the text is toward the
 *    higher value.  There is a utility function that can be used for common
 *    rotation preferences.  See {@link geo.isolineFeature#rotationFunction}.
 *    For instance, `rotation=geo.isolineFeature.rotationFunction('map')`.
 * @property {string|function} [fontSize='12px'] The font size.
 * @property {geo.geoColor|function} [textStrokeColor='white'] Text
 *    stroke color.  This adds contrast between the label and the isoline.
 * @property {geo.geoColor|function} [textStrokeWidth=2] Text stroke width in
 *    pixels.
 */

/**
 * Isoline specification.  All of these properties can be functions, which get
 * passed the {@link geo.meshFeature.meshInfo} object.
 *
 * @typedef {geo.meshFeature.meshSpec} geo.isolineFeature.isolineSpec
 * @property {number} [min] Minimum isoline value.  If unspecified, taken from
 *    the computed minimum of the `value` style.
 * @property {number} [max] Maximum isoline value.  If unspecified, taken from
 *    the computed maximum of the `value` style.
 * @property {number} [count=15] Approximate number of isolines shown through
 *    the value range.  Used if `spacing` or `values` is not specified.
 * @property {boolean} [autofit=true] If `count` is used to determine the
 *    isolines, and this is truthy, the isoline values will be round numbers.
 *    If falsy, they will include the exact minimum and maximum values.
 * @property {number} [spacing] Distance in value units between isolines.
 *    Used if specified and `values` is not specified.
 * @property {number[]|geo.isolineFeature.valueEntry[]} [values] An array of
 *    explicit values for isolines.
 * @property {number[]} [levels=[5, 5]] If `values` is not used to explicitly
 *    set isoline levels, this determines the spacing of levels which can be
 *    used to style lines distinctly.  Most isolines will be level 0.  If
 *    `levels` is an array of [`n0`, `n1`, ...], every `n0`th line will be
 *    level 1, every `n0 * n1`th line will be level 2, etc.
 * @property {boolean|function} [label] Truthy if a label should be shown for a
 *    isoline value.  If a function, this is called with
 *    `(geo.isolineFeature.valueEntry, index)`.  This defaults to
 *    `valueEntry.level >= 1`.
 * @property {string|function} [labelText] Text for a label.  If a function,
 *    this is called with `(geo.isolineFeature.valueEntry, index)`.  This
 *    defaults to `valueEntry.value`.
 * @property {number|function} [labelSpacing=200] Minimum distance between
 *    labels on an isoline in screen pixels.  If a function, this is called
 *    with `(geo.isolineFeature.valueEntry, index)`.
 * @property {number|function} [labelOffset=0] Offset for labels along an
 *    isoline relative to where they would be placed by default on a scale of
 *    [-0.5, 0.5].  +/- 1 would move the text to the next repeated occurance of
 *    the label.  If a function, this is called with
 *    `(geo.isolineFeature.valueEntry, index)`.
 * @property {number|function} [labelViewport=10000] If the main position of a
 *    label would be further than this many pixels from the current viewport,
 *    don't create it.  This prevents creating an excessive number of labels
 *    when zoomed in, but requires regenerating labels occasionally when
 *    panning.  If <= 0, all labels are generated regardless of location.
 * @property {boolean|function} [labelAutoUpdate=true] If truthy, when the map
 *    is panned (including zoom, rotation, etc.), periodically regenerate
 *    labels.  This uses an internal function that has a threshold based on a
 *    fixed change in zoom, size, and other parameters.  Set `labelAutoUpdate`
 *    to `false` and handle the {@link geo.event.pan} elsewhere.
 */

/**
 * Isoline value entry.
 *
 * @typedef {object} geo.isolineFeature.valueEntry
 * @property {number} value The value of the isoline.
 * @property {number} level The level of the isoline.
 * @property {number} [position] An index of the position of the isoline.  For
 *   evenly spaced or autofit values, this is the value modulo the spacing.
 *   Otherwise, this is the index position within the list of values.  This is
 *   computed when calculating isolines.
 * @property {string} [label] The label to display on this value.  This is
 *   computed from the `label` and `labelText` styles when calculating
 *   isolines.
 */

/**
 * Computed isoline information.
 *
 * @typedef {object} geo.isolineFeature.isolineInfo
 * @property {geo.isolineFeature.valueEntry[]} values The values used to
 *    produce the isolines.
 * @property {geo.meshFeature.meshInfo} mesh The normalized mesh.
 * @property {array[]} lines An array of arrays.  Each entry is a list of
 *    vertices that also have a `value` property with the appropriate entry in
 *    `values`.  If the line should show a label, it will  also have a `label`
 *    property with the text of the label.
 * @property {boolean} hasLabels `true` if there are any lines that have
 *    labels that need to be shown if there is enough resolution.
 */

/* This includes both the marching triangles and marching squares conditions.
 * The triangle pattern has three values, where 0 is less below the threshold
 * and 1 is above it.  The square pattern has four values in the order
 * ul-ur-ll-lr.  For each line a pattern produces, the line is created with a
 * low and high vertex from each of two edges.  Additionally, the create line
 * is such that the low value is outside of a clockwise winding.
 *
 * Performance note: Initially this table used string keys (e.g., '0001'), but
 * the string lookup was vastly slower than an integer lookup.
 */
var patternLineTable = {
  /* triangles with one high vertex */
  17 /* 001 */: [{ l0: 1, h0: 2, l1: 0, h1: 2 }],
  18 /* 010 */: [{ l0: 0, h0: 1, l1: 2, h1: 1 }],
  20 /* 100 */: [{ l0: 2, h0: 0, l1: 1, h1: 0 }],
  /* triangles with one low vertex */
  22 /* 110 */: [{ l0: 2, h0: 0, l1: 2, h1: 1 }],
  21 /* 101 */: [{ l0: 1, h0: 2, l1: 1, h1: 0 }],
  19 /* 011 */: [{ l0: 0, h0: 1, l1: 0, h1: 2 }],
  /* squares with one high vertex */
  1 /* 0001 */: [{ l0: 2, h0: 3, l1: 1, h1: 3 }],
  2 /* 0010 */: [{ l0: 0, h0: 2, l1: 3, h1: 2 }],
  4 /* 0100 */: [{ l0: 3, h0: 1, l1: 0, h1: 1 }],
  8 /* 1000 */: [{ l0: 1, h0: 0, l1: 2, h1: 0 }],
  /* squares with one low vertex */
  14 /* 1110 */: [{ l0: 3, h0: 1, l1: 3, h1: 2 }],
  13 /* 1101 */: [{ l0: 2, h0: 3, l1: 2, h1: 0 }],
  11 /* 1011 */: [{ l0: 1, h0: 0, l1: 1, h1: 3 }],
  7 /* 0111 */: [{ l0: 0, h0: 2, l1: 0, h1: 1 }],
  /* squares with two low vertices sharing a side */
  3 /* 0011 */: [{ l0: 0, h0: 2, l1: 1, h1: 3 }],
  10 /* 1010 */: [{ l0: 1, h0: 0, l1: 3, h1: 2 }],
  12 /* 1100 */: [{ l0: 3, h0: 1, l1: 2, h1: 0 }],
  5 /* 0101 */: [{ l0: 2, h0: 3, l1: 0, h1: 1 }],
  /* squares with two low vertices on opposite corners.  These could generate
   * a different pair of lines each. */
  6 /* 0110 */: [{ l0: 0, h0: 2, l1: 0, h1: 1 }, { l0: 3, h0: 1, l1: 3, h1: 2 }],
  9 /* 1001 */: [{ l0: 1, h0: 0, l1: 1, h1: 3 }, { l0: 2, h0: 3, l1: 2, h1: 0 }]
};

/**
 * Create a new instance of class isolineFeature.
 *
 * @class
 * @alias geo.isolineFeature
 * @extends geo.meshFeature
 *
 * @borrows geo.isolineFeature#mesh as geo.isolineFeature#contour
 * @borrows geo.isolineFeature#mesh as geo.isolineFeature#isoline
 *
 * @param {geo.isolineFeature.spec} arg
 * @returns {geo.isolineFeature}
 */
var isolineFeature = function isolineFeature(arg) {
  'use strict';

  if (!(this instanceof isolineFeature)) {
    return new isolineFeature(arg);
  }

  var $ = __webpack_require__(2);
  var transform = __webpack_require__(7);
  var geo_event = __webpack_require__(6);
  var textFeature = __webpack_require__(25);

  arg = arg || {};
  meshFeature.call(this, arg);

  /**
   * @private
   */
  var m_this = this,
      m_isolines,
      m_lastLabelPositions,
      m_lineFeature,
      m_labelLayer,
      m_labelFeature,
      s_draw = this.draw,
      s_exit = this._exit,
      s_init = this._init,
      s_modified = this.modified,
      s_update = this._update;

  this.contour = m_this.mesh;
  this.isoline = m_this.mesh;

  /**
   * Create a set of isolines.  This is a set of lines that could be used for a
   * line feature and to inform a text feature.
   *
   * @returns {geo.isolineFeature.isolineInfo} An object with the isoline
   *    information.
   */
  this._createIsolines = function () {
    var valueFunc = m_this.style.get('value'),
        usedFunc = m_this.style('used') !== undefined ? m_this.style.get('used') : function (d, i) {
      return util.isNonNullFinite(valueFunc(d, i));
    },
        values,
        hasLabels = false,
        lines = [];
    var mesh = this._createMesh({
      used: usedFunc,
      value: valueFunc
    });
    values = this._getValueList(mesh);
    if (!values.length) {
      return {};
    }
    values.forEach(function (value) {
      var valueLines = m_this._isolinesForValue(mesh, value);
      if (valueLines.length) {
        lines = lines.concat(valueLines);
        hasLabels = hasLabels || !!value.label;
      }
    });
    /* We may want to rdpSimplify the result to remove very small segments, but
     * if we do, it must NOT change the winding direction. */
    return {
      lines: lines,
      mesh: mesh,
      values: values,
      hasLabels: hasLabels
    };
  };

  /**
   * Generate an array of values for which isolines will be generated.
   *
   * @param {geo.meshFeature.meshInfo} mesh The normalized mesh.
   * @returns {geo.isolineFeature.valueEntry[]} The values in ascending order.
   */
  this._getValueList = function (mesh) {
    var isoline = m_this.isoline,
        values = isoline.get('values')(mesh),
        spacing = isoline.get('spacing')(mesh),
        count = isoline.get('count')(mesh),
        autofit = isoline.get('autofit')(mesh),
        levels = isoline.get('levels')(mesh),
        minmax,
        delta,
        step,
        steppow,
        steplog10,
        fixedDigits,
        i;
    if (!mesh.numVertices || !mesh.numElements) {
      return [];
    }
    minmax = util.getMinMaxValues(mesh.value, isoline.get('min')(mesh), isoline.get('max')(mesh), true);
    mesh.minValue = minmax.min;
    mesh.maxValue = minmax.max;
    delta = mesh.maxValue - mesh.minValue;
    if (delta <= 0) {
      return [];
    }
    /* Determine values for which we need to generate isolines. */
    if (Array.isArray(values)) {
      /* if the caller specified values, use them.  Each can either be a number
       * or an object with `value` and optionally `level`.  If it doesn't have
       * level, the position is just the index in the array. */
      values = values.map(function (val, idx) {
        return {
          value: val.value !== undefined ? val.value : val,
          position: idx,
          level: val.level
        };
      });
      /* Remove any values that are outside of the data range. */
      values = values.filter(function (val) {
        return val.value >= mesh.minValue && val.value <= mesh.maxValue;
      });
    } else if (!spacing && !autofit) {
      /* If no values or spacing are specified and autofit is falsy, then
       * use uniform spacing across the value range.  The max and min won't
       * produce contours (since they are exact values), so there range is
       * divided into `count + 1` sections to get `count` visible lines. */
      values = Array(count);
      for (i = 0; i < count; i += 1) {
        values[i] = {
          value: mesh.minValue + delta * (i + 1) / (count + 1),
          position: i + 1
        };
      }
    } else {
      if (!spacing) {
        /* If no spacing is specfied, then this has a count with autofit.
         * Generate at least 2/3rds as many lines as the count, but it could be
         * 5/2 of that when adjusted to "nice values" (so between 2/3 and 5/3
         * of the specified count). */
        step = delta / (count * 2 / 3);
        steplog10 = Math.floor(Math.log10(step));
        fixedDigits = Math.max(0, -steplog10);
        steppow = Math.pow(10, steplog10);
        step /= steppow; // will now be in range [1, 10)
        step = step >= 5 ? 5 : step >= 2 ? 2 : 1; // now 1, 2, or 5
        spacing = step * steppow;
      }
      /* Generate the values based on a spacing.  The `position` is used for
       * figuring out level further on and is based so that 0 will be the
       * maximum level. */
      values = [];
      for (i = Math.ceil(mesh.minValue / spacing); i <= Math.floor(mesh.maxValue / spacing); i += 1) {
        values.push({ value: i * spacing, position: i, fixedDigits: fixedDigits });
      }
    }
    /* Mark levels for each value.  These are intended for styling.  All values
     * will have a `value` and `position` attribute at this point. */
    if (levels.length) {
      values.forEach(function (val, idx) {
        if (val.level === undefined) {
          val.level = 0;
          for (var i = 0, basis = levels[0]; i < levels.length && !(val.position % basis); i += 1, basis *= levels[i]) {
            val.level = i + 1;
          }
        }
        if (isoline.get('label')(val, val.position)) {
          var label = isoline.get('labelText')(val, val.position);
          if (label === undefined) {
            if (val.fixedDigits !== undefined) {
              label = '' + parseFloat(val.value.toFixed(val.fixedDigits));
            } else {
              label = '' + val.value;
            }
          }
          if (label) {
            val.label = label;
          }
        }
      });
    }
    return values;
  };

  /**
   * Add a new segment to a list of chains.  Each chain is a list of vertices,
   * each of which is an array of two values with the low/high mesh vertices
   * for that chain vertex.  There are then three possibilities:  (a) The
   * segment forms a new chain that doesn't attch to an existing chain.  (b)
   * One endpoint of the segment matches the endpoint of an existing chain, and
   * it gets added to that chain.  (c) Both endpoints of the segment match
   * endpoints of two different chains, and those two chains are combined via
   * the segment.  A chain may represent a loop, in which case its two
   * endpoints will match.  This function does not join the loop.
   *
   * @param {array} chains An array of existing chains.
   * @param {array} first One endpoint of the new segment.  This is an array of
   *    two numbers defining the mesh vertices used for the endpoint.
   * @param {array} last The second endpoint of the new segment.
   * @returns {array} The modified chains array.
   */
  this._addSegment = function (chains, first, last) {
    var chain = [first, last],
        idx = chains.length,
        i,
        iter,
        check,
        checkFirst,
        checkLast,
        combine;
    /* Add the segment as a new chain by itself. */
    chains.push(chain);
    for (iter = 0; iter < 2; iter += 1) {
      /* Check if the new chain can attach to an existing chain */
      for (i = idx - 1; i >= 0; i -= 1) {
        check = chains[i];
        checkFirst = check[0];
        checkLast = check[check.length - 1];
        /* The segment can be inserted at the start of this chain */
        if (last[0] === checkFirst[0] && last[1] === checkFirst[1]) {
          combine = chain.concat(check.slice(1));
          /* The segment can be inserted at the end of this chain */
        } else if (first[0] === checkLast[0] && first[1] === checkLast[1]) {
          combine = check.concat(chain.slice(1));
          /* These two conditions should never be required, as we generate
           * segments with a consistent winding direction.
          } else if (first[0] === checkFirst[0] && first[1] === checkFirst[1]) {
            combine = chain.slice(1).reverse().concat(check);
          } else if (last[0] === checkLast[0] && last[1] === checkLast[1]) {
            combine = check.concat(chain.slice(0, chain.length - 1).reverse());
           */
          /* The segment doesn't match this chain, so keep scanning chains */
        } else {
          continue;
        }
        /* The segment matched and `combine` contains the chain it has been
         * merged with. */
        chains.splice(idx, 1);
        chains[i] = chain = combine;
        idx = i;
        first = chain[0];
        last = chain[chain.length - 1];
        break;
      }
      /* If we didn't combine the new chain to any existing chains, then don't
       * check if the other end also joins an existing chain. */
      if (i < 0) {
        break;
      }
    }
    return chains;
  };

  /**
   * Given a vertex of the form [low vertex index, high vertex index], compute
   * the coordinates of the vertex.
   *
   * @param {geo.meshFeature.meshInfo} mesh The normalized mesh.
   * @param {geo.isolineFeature.valueEntry} value The value for which to
   *    generate the vertex.
   * @param {number[]} vertex The low vertex index and high vertex index.
   * @returns {geo.geoPosition} The calculated coordinate.
   */
  this._chainVertex = function (mesh, value, vertex) {
    var v0 = vertex[0],
        v1 = vertex[1],
        v03 = v0 * 3,
        v13 = v1 * 3,
        f = (value.value - mesh.value[v0]) / (mesh.value[v1] - mesh.value[v0]),
        g = 1 - f;
    return {
      x: mesh.pos[v03] * g + mesh.pos[v13] * f,
      y: mesh.pos[v03 + 1] * g + mesh.pos[v13 + 1] * f,
      z: mesh.pos[v03 + 2] * g + mesh.pos[v13 + 2] * f
    };
  };

  /**
   * Generate the lines for associated with a particular value.  This performs
   * either marching triangles or marching squares.
   *
   * @param {geo.meshFeature.meshInfo} mesh The normalized mesh.
   * @param {geo.isolineFeature.valueEntry} value The value for which to
   *    generate the isolines.
   * @returns {geo.isolineFeature.line[]} An array of lines.
   */
  this._isolinesForValue = function (mesh, value) {
    var val = value.value,
        lowhigh = Array(mesh.value.length),
        chains = [],
        i,
        v,
        pattern,
        lines;
    /* Determine if each vertex is above or below the value.  It is faster to
     * use a for loop than map since it avoids function calls. */
    for (i = lowhigh.length - 1; i >= 0; i -= 1) {
      lowhigh[i] = mesh.value[i] <= val ? 0 : 1;
    }
    var vpe = mesh.verticesPerElement,
        square = mesh.shape === 'square',
        elem = mesh.elements,
        elemLen = elem.length;
    for (v = 0; v < elemLen; v += vpe) {
      if (square) {
        pattern = lowhigh[elem[v]] * 8 + lowhigh[elem[v + 1]] * 4 + lowhigh[elem[v + 2]] * 2 + lowhigh[elem[v + 3]];
        if (pattern === 0 || pattern === 15) {
          continue;
        }
      } else {
        pattern = 16 + lowhigh[elem[v]] * 4 + lowhigh[elem[v + 1]] * 2 + lowhigh[elem[v + 2]];
        if (pattern === 16 || pattern === 23) {
          continue;
        }
      }
      patternLineTable[pattern].forEach(function (lineEntry) {
        chains = m_this._addSegment(chains, [elem[v + lineEntry.l0], elem[v + lineEntry.h0]], [elem[v + lineEntry.l1], elem[v + lineEntry.h1]]);
      });
    }
    /* convert chains to lines */
    lines = chains.map(function (chain) {
      var line = [];
      chain.forEach(function (vertex) {
        var v = m_this._chainVertex(mesh, value, vertex);
        if (!line.length || v.x !== line[line.length - 1].x || v.y !== line[line.length - 1].y) {
          line.push(v);
        }
      });
      line.closed = line[0].x === line[line.length - 1].x && line[0].y === line[line.length - 1].y;
      /* Add value, level, position, and label information to the line. */
      line.value = value.value;
      line.level = value.level;
      line.position = value.position;
      line.label = value.label;
      return line;
    }).filter(function (line) {
      return line.length > 1;
    });
    return lines;
  };

  /**
   * When the feature is marked as modified, mark our sub-feature as
   * modified, too.
   *
   * @returns {object} The results of the superclass modified function.
   */
  this.modified = function () {
    var result = s_modified();
    if (m_lineFeature) {
      m_lineFeature.modified();
    }
    if (m_labelFeature) {
      m_labelFeature.modified();
    }
    return result;
  };

  /**
   * Compute the positions for labels on each line.  This can be called to
   * recompute label positions without needign to recompute isolines, for
   * instance when the zoom level changes.  Label positions are computed in the
   * map gcs coordinates, not interface gcs coordinates, since the interface
   * gcs may not be linear with the display space.
   *
   * @returns {this}
   */
  this.labelPositions = function () {
    if (m_this.dataTime().getMTime() >= m_this.buildTime().getMTime()) {
      m_this._build();
    }
    m_lastLabelPositions = null;
    if (!m_labelFeature) {
      return m_this;
    }
    if (!m_isolines || !m_isolines.hasLabels || !m_isolines.lines || !m_isolines.lines.length) {
      m_labelFeature.data([]);
      return m_this;
    }
    var isoline = m_this.isoline,
        spacingFunc = isoline.get('labelSpacing'),
        offsetFunc = isoline.get('labelOffset'),
        labelViewport = isoline.get('labelViewport')(m_isolines.mesh),
        gcs = m_this.gcs(),
        map = m_this.layer().map(),
        mapgcs = map.gcs(),
        mapRotation = map.rotation(),
        mapSize = map.size(),
        labelData = [],
        maxSpacing = 0;
    m_isolines.lines.forEach(function (line, idx) {
      if (!line.label) {
        return;
      }
      var spacing = spacingFunc(line.value, line.value.position),
          offset = offsetFunc(line.value, line.value.position) || 0,
          dispCoor = map.gcsToDisplay(line, gcs),
          totalDistance = 0,
          dist,
          count,
          localSpacing,
          next,
          lineDistance,
          i,
          i2,
          f,
          g,
          pos,
          mapCoor;
      if (spacing <= 0 || isNaN(spacing)) {
        return;
      }
      maxSpacing = Math.max(spacing, maxSpacing);
      /* make offset in the range of [0, 1) with the default at 0.5 */
      offset = offset + 0.5 - Math.floor(offset + 0.5);
      dist = dispCoor.map(function (pt1, coorIdx) {
        if (!line.closed && coorIdx + 1 === dispCoor.length) {
          return 0;
        }
        var val = Math.sqrt(util.distance2dSquared(pt1, dispCoor[coorIdx + 1 < dispCoor.length ? coorIdx + 1 : 0]));
        totalDistance += val;
        return val;
      });
      count = Math.floor(totalDistance / spacing);
      if (!count) {
        return;
      }
      /* If we have any labels, compute map coordinates of the line and use
       * those for interpolating label positions */
      mapCoor = transform.transformCoordinates(gcs, mapgcs, line);
      localSpacing = totalDistance / count;
      next = localSpacing * offset;
      lineDistance = 0;
      for (i = 0; i < dispCoor.length; i += 1) {
        while (lineDistance + dist[i] >= next) {
          i2 = i + 1 === dispCoor.length ? 0 : i + 1;
          f = (next - lineDistance) / dist[i];
          g = 1 - f;
          next += localSpacing;
          if (labelViewport > 0) {
            pos = {
              x: dispCoor[i].x * g + dispCoor[i2].x * f,
              y: dispCoor[i].y * g + dispCoor[i2].y * f
            };
            if (pos.x < -labelViewport || pos.x > mapSize.width + labelViewport || pos.y < -labelViewport || pos.y > mapSize.height + labelViewport) {
              continue;
            }
          }
          labelData.push({
            x: mapCoor[i].x * g + mapCoor[i2].x * f,
            y: mapCoor[i].y * g + mapCoor[i2].y * f,
            z: mapCoor[i].z * g + mapCoor[i2].z * f,
            line: line,
            rotation: Math.atan2(dispCoor[i].y - dispCoor[i2].y, dispCoor[i].x - dispCoor[i2].x) - mapRotation
          });
        }
        lineDistance += dist[i];
      }
    });
    m_labelFeature.gcs(mapgcs);
    m_labelFeature.data(labelData);
    m_labelFeature.style('renderThreshold', maxSpacing * 2);
    m_lastLabelPositions = {
      zoom: map.zoom(),
      center: map.center(),
      rotation: mapRotation,
      size: mapSize,
      labelViewport: labelViewport,
      maxSpacing: maxSpacing,
      labelAutoUpdate: isoline.get('labelAutoUpdate')(m_isolines.mesh)
    };
    return m_this;
  };

  /**
   * Get the last map position that was used for generating labels.
   *
   * @returns {object} An object with the map `zoom` and `center` and the
   *    `labelViewport` used in generating labels.  The object may have no
   *    properties if there are no labels.
   */
  this.lastLabelPositions = function () {
    return $.extend({}, m_lastLabelPositions);
  };

  /**
   * On a pan event, if labels exist and are set to autoupdate, recalculate
   * their positions and redraw them as needed.  Labels are redrawn if the
   * zoom level changes by at least 2 levels, or the map's center is moved
   * enough that there is a chance that the viewport is nearing the extent of
   * the generated labels.  The viewport calculation is conservative, as the
   * map could be rotated, changed size, or have other modifications.
   *
   * @returns {exit}
   */
  this._updateLabelPositions = function () {
    var last = m_lastLabelPositions;
    if (!last || !last.labelAutoUpdate) {
      return m_this;
    }
    var map = m_this.layer().map(),
        zoom = map.zoom(),
        mapSize = map.size(),
        update = !!(Math.abs(zoom - last.zoom) >= 2);
    if (!update && last.labelViewport > 0) {
      /* Distance in scaled pixels between the map's current center and the
       * center when the labels were computed. */
      var lastDelta = Math.sqrt(util.distance2dSquared(map.gcsToDisplay(map.center()), map.gcsToDisplay(last.center))) * Math.pow(2, last.zoom - zoom);
      /* Half the viewport, less twice the maxSpacing, less any expansion of
       * the map. */
      var threshold = last.labelViewport / 2 - last.maxSpacing * 2 - Math.max(mapSize.width - last.size.width, mapSize.height - last.size.height, 0);
      update = update || lastDelta >= threshold;
    }
    if (update) {
      m_this.labelPositions().draw();
    }
    return m_this;
  };

  /**
   * Build.  Generate the isolines.  Create a line feature if necessary and
   * update it.
   *
   * @returns {this}
   */
  this._build = function () {
    m_isolines = m_this._createIsolines();
    if (m_isolines && m_isolines.lines && m_isolines.lines.length) {
      if (!m_lineFeature) {
        m_lineFeature = m_this.layer().createFeature('line', {
          selectionAPI: false,
          gcs: m_this.gcs(),
          visible: m_this.visible(undefined, true),
          style: {
            closed: function closed(d) {
              return d.closed;
            }
          }
        });
        m_this.dependentFeatures([m_lineFeature]);
      }
      var style = m_this.style();
      m_lineFeature.data(m_isolines.lines).style({
        antialiasing: style.antialiasing,
        lineCap: style.lineCap,
        lineJoin: style.lineJoin,
        miterLimit: style.miterLimit,
        strokeWidth: style.strokeWidth,
        strokeStyle: style.strokeStyle,
        strokeColor: style.strokeColor,
        strokeOffset: style.strokeOffset,
        strokeOpacity: style.strokeOpacity
      });
      if (m_isolines.hasLabels) {
        if (!m_labelFeature) {
          if (!(registry.registries.features[m_this.layer().rendererName()] || {}).text) {
            var renderer = registry.rendererForFeatures(['text']);
            m_labelLayer = registry.createLayer('feature', m_this.layer().map(), { renderer: renderer });
            m_this.layer().addChild(m_labelLayer);
            m_this.layer().node().append(m_labelLayer.node());
          }
          m_labelFeature = (m_labelLayer || m_this.layer()).createFeature('text', {
            selectionAPI: false,
            gcs: m_this.gcs(),
            visible: m_this.visible(undefined, true),
            style: {
              text: function text(d) {
                return d.line.label;
              }
            }
          }).geoOn(geo_event.pan, m_this._updateLabelPositions);
        }
        textFeature.usedStyles.forEach(function (styleName) {
          if (styleName !== 'visible') {
            m_labelFeature.style(styleName, style[styleName]);
          }
        });
        m_this.dependentFeatures([m_lineFeature, m_labelFeature]);
      }
    } else if (m_lineFeature) {
      m_lineFeature.data([]);
    }
    m_this.buildTime().modified();
    /* Update label positions after setting the build time.  The labelPositions
     * method will build if necessary, and this prevents it from looping. */
    m_this.labelPositions();
    return m_this;
  };

  /**
   * Update.  Rebuild if necessary.
   *
   * @returns {this}
   */
  this._update = function () {
    s_update.call(m_this);

    if (m_this.dataTime().getMTime() >= m_this.buildTime().getMTime() || m_this.updateTime().getMTime() <= m_this.getMTime()) {
      m_this._build();
    }
    m_this.updateTime().modified();
    return m_this;
  };

  /**
   * Redraw the object.
   *
   * @returns {object} The results of the superclass draw function.
   */
  this.draw = function () {
    var result = s_draw();
    if (m_lineFeature) {
      m_lineFeature.draw();
    }
    if (m_labelFeature) {
      m_labelFeature.draw();
    }
    return result;
  };

  /**
   * Destroy.
   */
  this._exit = function () {
    if (m_labelFeature) {
      if (m_labelLayer || m_this.layer()) {
        (m_labelLayer || m_this.layer()).deleteFeature(m_labelFeature);
      }
      if (m_labelLayer && m_this.layer()) {
        m_this.layer().removeChild(m_labelLayer);
      }
    }
    if (m_lineFeature && m_this.layer()) {
      m_this.layer().deleteFeature(m_lineFeature);
    }
    m_labelFeature = null;
    m_labelLayer = null;
    m_lineFeature = null;
    m_this.dependentFeatures([]);

    s_exit();
  };

  /**
   * Initialize.
   *
   * @param {geo.isolineFeature.spec} arg The isoline feature specification.
   */
  this._init = function (arg) {
    arg = arg || {};
    s_init.call(m_this, arg);

    var defaultStyle = $.extend({}, {
      opacity: 1.0,
      value: function value(d, i) {
        return m_this.position()(d, i).z;
      },
      rotateWithMap: true,
      rotation: isolineFeature.rotationFunction(),
      strokeWidth: function strokeWidth(v, vi, d, di) {
        return d.level + 0.5;
      },
      strokeColor: { r: 0, g: 0, b: 0 },
      textStrokeColor: { r: 1, g: 1, b: 1, a: 0.75 },
      textStrokeWidth: 2,
      fontSize: '12px'
    }, arg.style === undefined ? {} : arg.style);

    m_this.style(defaultStyle);

    m_this.isoline($.extend({}, {
      count: 15,
      autofit: true,
      levels: [5, 5],
      label: function label(value) {
        return value.level >= 1;
      },
      labelSpacing: 200,
      labelViewport: 10000,
      labelAutoUpdate: true
    }, arg.mesh || {}, arg.contour || {}, arg.isoline || {}));

    if (arg.mesh || arg.contour || arg.isoline) {
      m_this.dataTime().modified();
    }
  };

  return this;
};

/**
 * Return a function that will rotate text labels in a specified orientation.
 * The results of this are intended to be used as the value of the `rotation`
 * style.
 *
 * @param {string} [mode='higher'] The rotation mode.  `higher` orients the top
 *   of the text to high values.  `lower` orients the top of the text to lower
 *   values.  `map` orients the top of the text so it is aligned to the isoline
 *   and biased toward the top of the map.  `screen` orients the top of the
 *   text so it is aligned to the isoline and biased toward the top of the
 *   display screen.
 * @param {geo.map} [map] The parent map.  Required for `screen` mode.
 * @returns {function} A function for the rotation style.
 */
isolineFeature.rotationFunction = function (mode, map) {
  var functionList = {
    'higher': function higher(d) {
      return d.rotation;
    },
    'lower': function lower(d) {
      return d.rotation + Math.PI;
    },
    'map': function map(d) {
      var r = d.rotation,
          rt = util.wrapAngle(r, true);
      if (rt > Math.PI / 2 || rt < -Math.PI / 2) {
        r += Math.PI;
      }
      return r;
    },
    'screen': function screen(d) {
      var r = d.rotation,
          rt = util.wrapAngle(r + map.rotation(), true);
      if (rt > Math.PI / 2 || rt < -Math.PI / 2) {
        r += Math.PI;
      }
      return r;
    }
  };
  return functionList[mode] || functionList.higher;
};

inherit(isolineFeature, meshFeature);
module.exports = isolineFeature;

/***/ }),
/* 56 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var inherit = __webpack_require__(0);
var sceneObject = __webpack_require__(13);
var $ = __webpack_require__(2);

/**
 * @typedef {object} geo.gui.widget.position
 * @property {string|number} [top] The position to the top of the container.
 *   A string css position or a number in pixels.
 * @property {string|number} [right] The position to the right of the
 *   container.  A string css position or a number in pixels.
 * @property {string|number} [bottom] The position to the bottom of the
 *   container.  A string css position or a number in pixels.
 * @property {string|number} [left] The position to the left of the container.
 * @property {string|number} [top] The position to the top of the container.
 * @property {*} [...] Additional css properties that affect position are
 *   allowed.  See the css specification for details.
 */

/**
 * Create a new instance of class widget.
 *
 * @class
 * @alias geo.gui.widget
 * @param {object} [arg] Options for the widget.
 * @param {geo.layer} [arg.layer] Layer associated with the widget.
 * @param {geo.gui.widget.position} [arg.position] Location of the widget.
 * @param {geo.gui.widget} [arg.parent] Optional parent widget.
 * @extends {geo.sceneObject}
 * @returns {geo.gui.widget}
 */
var widget = function widget(arg) {
  'use strict';

  if (!(this instanceof widget)) {
    return new widget(arg);
  }
  arg = arg || {};
  sceneObject.call(this, arg);

  var geo_event = __webpack_require__(6);

  var m_this = this,
      s_exit = this._exit,
      m_layer = arg.layer,
      m_canvas = null,
      m_position = arg.position === undefined ? { left: 0, top: 0 } : arg.position;

  if (arg.parent !== undefined && !(arg.parent instanceof widget)) {
    throw new Error('Parent must be of type geo.gui.widget');
  } else if (arg.parent) {
    m_this.parent(arg.parent);
  }

  /**
   * Initialize the widget.
   *
   * @returns {this}
   */
  this._init = function () {
    m_this.modified();
    return m_this;
  };

  /**
   * Clean up the widget.
   */
  this._exit = function () {
    m_this.children().forEach(function (child) {
      m_this.removeChild(child);
      child._exit();
    });

    m_this.layer().geoOff(geo_event.pan, m_this.repositionEvent);
    if (m_this.parentCanvas().removeChild && m_this.canvas()) {
      try {
        m_this.parentCanvas().removeChild(m_this.canvas());
      } catch (err) {
        // fail gracefully if the canvas is not a child of the parentCanvas
      }
    }
    s_exit();
  };

  /**
   * Return the layer associated with this widget.
   *
   * @returns {geo.layer}
   */
  this.layer = function () {
    return m_layer || m_this.parent() && m_this.parent().layer();
  };

  /**
   * Create the canvas this widget will operate on.
   */
  this._createCanvas = function () {
    throw new Error('Must be defined in derived classes');
  };

  /**
   * Get/Set the canvas for the widget.
   *
   * @param {HTMLElement} [val] If specified, set the canvas, otherwise get
   *    the canvas.
   * @returns {HTMLElement|this} If getting the canvas, return the current
   *    value; otherwise, return this widget.
   */
  this.canvas = function (val) {
    if (val === undefined) {
      return m_canvas;
    }
    m_canvas = val;
    return m_this;
  };

  /**
   * Appends the canvas to the parent canvas.
   * The widget determines how to append itself to a parent, the parent can
   * either be another widget, or the UI Layer.
   */
  this._appendCanvasToParent = function () {
    m_this.parentCanvas().appendChild(m_this.canvas());
  };

  /**
   * Get the parent canvas (top level widgets define their layer as their
   * parent canvas).
   *
   * @returns {HTMLElement} The canvas of the widget's parent.
   */
  this.parentCanvas = function () {
    if (!m_this.parent()) {
      return m_this.layer().canvas();
    }
    return m_this.parent().canvas();
  };

  /**
   * Get or set the CSS positioning that a widget should be placed at.
   *
   * @param {geo.gui.widget.position} [pos] If unspecified, return the current
   *    position.  Otherwise, set the current position.
   * @param {boolean} [actualValue] If getting the position, if this is truthy,
   *    always return the stored value, not a value adjusted for display.
   * @returns {geo.gui.widget.position|this} Either the position or the widget
   *    instance.  If this is the position and `actualValue` is falsy,
   *    positions that specify an explicit `x` and `y` parameter will be
   *    converted to a value that can be used by the display css.
   */
  this.position = function (pos, actualValue) {
    if (pos !== undefined) {
      this.layer().geoOff(geo_event.pan, m_this.repositionEvent);
      var clearPosition = {};
      for (var attr in m_position) {
        if (m_position.hasOwnProperty(attr)) {
          clearPosition[attr] = null;
        }
      }
      m_position = pos;
      if (m_position.hasOwnProperty('x') && m_position.hasOwnProperty('y')) {
        this.layer().geoOn(geo_event.pan, m_this.repositionEvent);
      }
      this.reposition($.extend(clearPosition, m_this.position()));
      return this;
    }
    if (m_position.hasOwnProperty('x') && m_position.hasOwnProperty('y') && !actualValue) {
      var position = m_this.layer().map().gcsToDisplay(m_position);

      return {
        left: position.x,
        top: position.y,
        right: null,
        bottom: null
      };
    }

    return m_position;
  };

  /**
   * Repositions a widget.
   *
   * @param {geo.gui.widget.position} [position] The new position for the
   *    widget.  `undefined` uses the stored position value.
   * @returns {this}
   */
  this.reposition = function (position) {
    position = position || m_this.position();
    if (m_this.canvas() && m_this.canvas().style) {
      m_this.canvas().style.position = 'absolute';

      for (var cssAttr in position) {
        if (position.hasOwnProperty(cssAttr)) {
          // if the property is a number, add px to it, otherwise set it to the
          // specified value.  Setting a property to null clears it.  Setting to
          // undefined doesn't alter it.
          if (/^\s*(-|\+)?(\d+(\.\d*)?|\d*\.\d+)([eE](-|\+)?\d+)?\s*$/.test(position[cssAttr])) {
            // this ensures that the number is a float with no more than 3
            // decimal places (Chrome does this automatically, but doing so
            // explicitly makes testing more consistent).  It will be an
            // integer when possible.
            m_this.canvas().style[cssAttr] = parseFloat(parseFloat(position[cssAttr]).toFixed(3)) + 'px';
          } else {
            m_this.canvas().style[cssAttr] = position[cssAttr];
          }
        }
      }
    }
    return m_this;
  };

  /**
   * If the position is based on map coordinates, this gets called when the
   * map is panned to resposition the widget.
   *
   * @returns {this}
   */
  this.repositionEvent = function () {
    return m_this.reposition();
  };

  /**
   * Report if the top left of widget (or its current x, y position) is within
   * the viewport.
   *
   * @returns {boolean} True if the widget is within the viewport.
   */
  this.isInViewport = function () {
    var position = m_this.position();
    var layer = m_this.layer();

    return position.left >= 0 && position.top >= 0 && position.left <= layer.width() && position.top <= layer.height();
  };

  if (m_position.hasOwnProperty('x') && m_position.hasOwnProperty('y')) {
    this.layer().geoOn(geo_event.pan, m_this.repositionEvent);
  }
};
inherit(widget, sceneObject);
module.exports = widget;

/***/ }),
/* 57 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var $ = __webpack_require__(2);
var inherit = __webpack_require__(0);
var geo_event = __webpack_require__(6);
var geo_action = __webpack_require__(39);
var transform = __webpack_require__(7);
var util = __webpack_require__(3);
var registerAnnotation = __webpack_require__(1).registerAnnotation;
var lineFeature = __webpack_require__(23);
var pointFeature = __webpack_require__(34);
var polygonFeature = __webpack_require__(50);
var textFeature = __webpack_require__(25);

var annotationId = 0;

/**
 * @alias geo.annotation.state
 * @enum {string}
 */
var annotationState = {
  create: 'create',
  done: 'done',
  highlight: 'highlight',
  edit: 'edit'
};

var annotationActionOwner = 'annotationAction';

/**
 * These styles are applied to edit handles, and can be overridden by
 * individual annotations.
 *
 * @alias geo.annotation.defaultEditHandleStyle
 * @type {object}
 * @default
 */
var defaultEditHandleStyle = {
  fill: true,
  fillColor: function fillColor(d) {
    return d.selected ? { r: 0, g: 1, b: 1 } : { r: 0.3, g: 0.3, b: 0.3 };
  },
  fillOpacity: function fillOpacity(d) {
    return d.selected ? 0.5 : 0.25;
  },
  radius: function radius(d) {
    return d.type === 'edge' || d.type === 'rotate' ? 8 : 10;
  },
  scaled: false,
  stroke: true,
  strokeColor: { r: 0, g: 0, b: 1 },
  strokeOpacity: 1,
  strokeWidth: function strokeWidth(d) {
    return d.type === 'edge' || d.type === 'rotate' ? 2 : 3;
  },
  rotateHandleOffset: 24, // should be roughly twice radius + strokeWidth
  rotateHandleRotation: -Math.PI / 4,
  resizeHandleOffset: 48, // should be roughly twice radius + strokeWidth + rotateHandleOffset
  resizeHandleRotation: -Math.PI / 4,
  // handles may be a function to dynamically generate the results
  handles: { // if `false`, the handle won't be created for editing
    vertex: true,
    edge: true,
    center: true,
    rotate: true,
    resize: true
  }
};
var editHandleFeatureLevel = 3;

/**
 * General annotation specification.
 *
 * @typedef {object} geo.annotation.spec
 * @property {string} [name] A name for the annotation.  This defaults to the
 *    type with a unique ID suffixed to it.
 * @property {geo.annotationLayer} [layer] A reference to the controlling
 *    layer.  This is used for coordinate transforms.
 * @property {string} [state] Initial annotation state.  One of the
 *    {@link geo.annotation.state} values.
 * @property {boolean|string[]} [showLabel=true] `true` to show the annotation
 *    label on annotations in done or edit states.  Alternately, a list of
 *    states in which to show the label.  Falsy to not show the label.
 */

/**
 * Base annotation class.
 *
 * @class
 * @alias geo.annotation
 * @param {string} type The type of annotation.  These should be registered
 *    with {@link geo.registerAnnotation} and can be listed with
 *    {@link geo.listAnnotations}.
 * @param {geo.annotation.spec?} [args] Options for the annotation.
 * @returns {geo.annotation}
 */
var annotation = function annotation(type, args) {
  'use strict';

  if (!(this instanceof annotation)) {
    return new annotation(type, args);
  }

  var m_this = this,
      m_options = $.extend({}, { showLabel: true }, args || {}),
      m_id = m_options.annotationId;
  delete m_options.annotationId;
  if (m_id === undefined || m_options.layer && m_options.layer.annotationById(m_id)) {
    annotationId += 1;
    if (m_id !== undefined) {
      console.warn('Annotation id ' + m_id + ' is in use; using ' + annotationId + ' instead.');
    }
    m_id = annotationId;
  } else {
    if (m_id > annotationId) {
      annotationId = m_id;
    }
  }
  var m_name = m_options.name || type.charAt(0).toUpperCase() + type.substr(1) + ' ' + m_id,
      m_label = m_options.label || null,
      m_description = m_options.description || undefined,
      m_type = type,
      m_layer = m_options.layer,

  /* one of annotationState.* */
  m_state = m_options.state || annotationState.done;
  delete m_options.state;
  delete m_options.layer;
  delete m_options.name;
  delete m_options.label;
  delete m_options.description;

  /**
   * Clean up any resources that the annotation is using.
   */
  this._exit = function () {};

  /**
   * Get a unique annotation id.
   *
   * @returns {number} The annotation id.
   */
  this.id = function () {
    return m_id;
  };

  /**
   * Get or set the name of this annotation.
   *
   * @param {string|undefined} arg If `undefined`, return the name, otherwise
   *    change it.  When setting the name, the value is trimmed of
   *    whitespace.  The name will not be changed to an empty string.
   * @returns {this|string} The current name or this annotation.
   */
  this.name = function (arg) {
    if (arg === undefined) {
      return m_name;
    }
    if (arg !== null && ('' + arg).trim()) {
      arg = ('' + arg).trim();
      if (arg !== m_name) {
        m_name = arg;
        m_this.modified();
      }
    }
    return m_this;
  };

  /**
   * Get or set the label of this annotation.
   *
   * @param {string|null|undefined} arg If `undefined`, return the label,
   *    otherwise change it.  `null` to clear the label.
   * @param {boolean} noFallback If not truthy and the label is `null`, return
   *    the name, otherwise return the actual value for label.
   * @returns {this|string} The current label or this annotation.
   */
  this.label = function (arg, noFallback) {
    if (arg === undefined) {
      return m_label === null && !noFallback ? m_name : m_label;
    }
    if (arg !== m_label) {
      m_label = arg;
      m_this.modified();
    }
    return m_this;
  };

  /**
   * Return the coordinate associated with the label.
   *
   * @returns {geo.geoPosition|undefined} The map gcs position for the label,
   *    or `undefined` if no such position exists.
   */
  this._labelPosition = function () {
    return util.centerFromPerimeter(m_this._coordinates());
  };

  /**
   * Return the coordinate associated with the rotation handle for the
   * annotation.
   *
   * @param {number} [offset] An additional offset from cetner to apply to the
   *    handle.
   * @param {number} [rotation] An additional rotation to apply to the handle.
   * @returns {geo.geoPosition|undefined} The map gcs position for the handle,
   *    or `undefined` if no such position exists.
   */
  this._rotateHandlePosition = function (offset, rotation) {
    var map = m_this.layer().map(),
        coor = m_this._coordinates(),
        center = util.centerFromPerimeter(m_this._coordinates()),
        dispCenter = center ? map.gcsToDisplay(center, null) : undefined,
        i,
        pos,
        maxr2 = 0,
        r;
    if (!center) {
      return;
    }
    offset = offset || 0;
    rotation = rotation || 0;
    for (i = 0; i < coor.length; i += 1) {
      pos = map.gcsToDisplay(coor[i], null);
      maxr2 = Math.max(maxr2, Math.pow(pos.x - dispCenter.x, 2) + Math.pow(pos.y - dispCenter.y, 2));
    }
    r = Math.sqrt(maxr2) + offset;
    pos = map.displayToGcs({
      x: dispCenter.x + r * Math.cos(rotation),
      y: dispCenter.y - r * Math.sin(rotation) }, null);
    return pos;
  };

  /**
   * If the label should be shown, get a record of the label that can be used
   * in a {@link geo.textFeature}.
   *
   * @returns {geo.annotationLayer.labelRecord|undefined} A label record, or
   *    `undefined` if it should not be shown.
   */
  this.labelRecord = function () {
    var show = m_this.options('showLabel');
    if (!show) {
      return;
    }
    var state = m_this.state();
    if (show === true && state === annotationState.create || show !== true && show.indexOf(state) < 0) {
      return;
    }
    var style = m_this.labelStyle();
    var labelRecord = {
      text: m_this.label(),
      position: m_this._labelPosition()
    };
    if (!labelRecord.position) {
      return;
    }
    if (style) {
      labelRecord.style = style;
    }
    return labelRecord;
  };

  /**
   * Get or set the description of this annotation.
   *
   * @param {string|undefined} arg If `undefined`, return the description,
   *    otherwise change it.
   * @returns {this|string} The current description or this annotation.
   */
  this.description = function (arg) {
    if (arg === undefined) {
      return m_description;
    }
    if (arg !== m_description) {
      m_description = arg;
      m_this.modified();
    }
    return m_this;
  };

  /**
   * Get or set the annotation layer associated with this annotation.
   *
   * @param {geo.annotationLayer|undefined} arg if undefined, return the layer,
   *    otherwise change it.
   * @returns {this|geo.annotationLayer} the current layer or this annotation.
   */
  this.layer = function (arg) {
    if (arg === undefined) {
      return m_layer;
    }
    m_layer = arg;
    return m_this;
  };

  /**
   * Get or set the state of this annotation.
   *
   * @param {string|undefined} arg If `undefined`, return the state,
   *    otherwise change it.  This should be one of the
   *    {@link geo.annotation.state} values.
   * @returns {this|string} The current state or this annotation.
   * @fires geo.event.annotation.state
   */
  this.state = function (arg) {
    if (arg === undefined) {
      return m_state;
    }
    if (m_state !== arg) {
      m_state = arg;
      if (m_this.layer()) {
        m_this.layer().geoTrigger(geo_event.annotation.state, {
          annotation: this
        });
      }
    }
    return m_this;
  };

  /**
   * Return actions needed for the specified state of this annotation.
   *
   * @param {string} [state] The state to return actions for.  Defaults to
   *    the current state.
   * @returns {geo.actionRecord[]} A list of actions.
   */
  this.actions = function (state) {
    if (!state) {
      state = m_this.state();
    }
    switch (state) {
      case annotationState.edit:
        return [{
          action: geo_action.annotation_edit_handle,
          name: 'annotation edit',
          owner: annotationActionOwner,
          input: 'left'
        }, {
          action: geo_action.annotation_edit_handle,
          name: 'annotation edit',
          owner: annotationActionOwner,
          input: 'pan'
        }];
      default:
        return [];
    }
  };

  /**
   * Process any non-edit actions for this annotation.
   *
   * @param {geo.event} evt The action event.
   * @returns {boolean|string} `true` to update the annotation, `'done'` if the
   *    annotation was completed (changed from create to done state),
   *    `'remove'` if the annotation should be removed, falsy to not update
   *    anything.
   */
  this.processAction = function () {
    return undefined;
  };

  /**
   * Process any edit actions for this annotation.
   *
   * @param {geo.event} evt The action event.
   * @returns {boolean} `true` to update the annotation, falsy to not update
   *    anything.
   */
  this.processEditAction = function (evt) {
    if (!evt || !m_this._editHandle || !m_this._editHandle.handle) {
      return;
    }
    switch (m_this._editHandle.handle.type) {
      case 'vertex':
        return m_this._processEditActionVertex(evt);
      case 'edge':
        return m_this._processEditActionEdge(evt);
      case 'center':
        return m_this._processEditActionCenter(evt);
      case 'rotate':
        return m_this._processEditActionRotate(evt);
      case 'resize':
        return m_this._processEditActionResize(evt);
    }
  };

  /**
   * When an edit handle is selected or deselected (for instance, by moving the
   * mouse on or off of it), mark if it is selected and record the current
   * coordinates.
   *
   * @param {object} handle The data for the edit handle.
   * @param {boolean} enable True to enable the handle, false to disable.
   * @returns {this}
   * @fires geo.event.annotation.select_edit_handle
   */
  this.selectEditHandle = function (handle, enable) {
    if (enable && m_this._editHandle && m_this._editHandle.handle && m_this._editHandle.handle.selected) {
      m_this._editHandle.handle.selected = false;
    }
    handle.selected = enable;
    var amountRotated = (m_this._editHandle || {}).amountRotated || 0;
    m_this._editHandle = {
      handle: handle,
      startCoordinates: m_this._coordinates().slice(),
      center: util.centerFromPerimeter(m_this._coordinates()),
      rotatePosition: m_this._rotateHandlePosition(handle.style.rotateHandleOffset, handle.style.rotateHandleRotation + amountRotated),
      startAmountRotated: amountRotated,
      amountRotated: amountRotated,
      resizePosition: m_this._rotateHandlePosition(handle.style.resizeHandleOffset, handle.style.resizeHandleRotation)
    };
    if (m_this.layer()) {
      m_this.layer().geoTrigger(geo_event.annotation.select_edit_handle, {
        annotation: m_this,
        handle: m_this._editHandle,
        enable: enable
      });
    }
    return m_this;
  };

  /**
   * Set or get options.
   *
   * @param {string|object} [arg1] If `undefined`, return the options object.
   *    If a string, either set or return the option of that name.  If an
   *    object, update the options with the object's values.
   * @param {object} [arg2] If `arg1` is a string and this is defined, set
   *    the option to this value.
   * @returns {object|this} If options are set, return the annotation,
   *    otherwise return the requested option or the set of options.
   * @fires geo.event.annotation.coordinates
   */
  this.options = function (arg1, arg2) {
    if (arg1 === undefined) {
      return m_options;
    }
    if (typeof arg1 === 'string' && arg2 === undefined) {
      return m_options[arg1];
    }
    var coordinatesSet;
    if (arg2 === undefined) {
      coordinatesSet = arg1[m_this._coordinateOption] !== undefined;
      m_options = $.extend(true, m_options, arg1);
      /* For style objects, re-extend them without recursion.  This allows
       * setting colors without an opacity field, for instance. */
      ['style', 'createStyle', 'editStyle', 'editHandleStyle', 'labelStyle', 'highlightStyle'].forEach(function (key) {
        if (arg1[key] !== undefined) {
          $.extend(m_options[key], arg1[key]);
        }
      });
    } else {
      coordinatesSet = arg1 === m_this._coordinateOption;
      m_options[arg1] = arg2;
    }
    if (m_options.coordinates) {
      var coor = m_options.coordinates;
      delete m_options.coordinates;
      m_this._coordinates(coor);
    }
    if (m_options.name !== undefined) {
      var name = m_options.name;
      delete m_options.name;
      m_this.name(name);
    }
    if (m_options.label !== undefined) {
      var label = m_options.label;
      delete m_options.label;
      m_this.label(label);
    }
    if (m_options.description !== undefined) {
      var description = m_options.description;
      delete m_options.description;
      m_this.description(description);
    }
    m_this.modified();
    if (coordinatesSet && m_this.layer()) {
      m_this.layer().geoTrigger(geo_event.annotation.coordinates, {
        annotation: m_this
      });
    }
    return this;
  };

  /**
   * Set or get style.
   *
   * @param {string|object} [arg1] If `undefined`, return the current style
   *    object.  If a string and `arg2` is undefined, return the style
   *    associated with the specified key.  If a string and `arg2` is defined,
   *    set the named style to the specified value.  Otherwise, extend the
   *    current style with the values in the specified object.
   * @param {*} [arg2] If `arg1` is a string, the new value for that style.
   * @param {string} [styleType='style'] The name of the style type, such as
   *    `createStyle`, `editStyle`, `editHandleStyle`, `labelStyle`, or
   *    `highlightStyle`.
   * @returns {object|this} Either the entire style object, the value of a
   *    specific style, or the current class instance.
   */
  this.style = function (arg1, arg2, styleType) {
    styleType = styleType || 'style';
    if (arg1 === undefined) {
      return m_options[styleType];
    }
    if (typeof arg1 === 'string' && arg2 === undefined) {
      return (m_options[styleType] || {})[arg1];
    }
    if (m_options[styleType] === undefined) {
      m_options[styleType] = {};
    }
    if (arg2 === undefined) {
      m_options[styleType] = $.extend(true, m_options[styleType], arg1);
    } else {
      m_options[styleType][arg1] = arg2;
    }
    m_this.modified();
    return m_this;
  };

  /**
   * Calls {@link geo.annotation#style} with `styleType='createStyle'`.
   * @function createStyle
   * @memberof geo.annotation
   * @instance
   */
  /**
   * Calls {@link geo.annotation#style} with `styleType='editStyle'`.
   * @function editStyle
   * @memberof geo.annotation
   * @instance
   */
  /**
   * Calls {@link geo.annotation#style} with `styleType='editHandleStyle'`.
   * @function editHandleStyle
   * @memberof geo.annotation
   * @instance
   */
  /**
   * Calls {@link geo.annotation#style} with `styleType='labelStyle'`.
   * @function labelStyle
   * @memberof geo.annotation
   * @instance
   */
  /**
   * Calls {@link geo.annotation#style} with `styleType='highlightHandleStyle'`.
   * @function highlightHandleStyle
   * @memberof geo.annotation
   * @instance
   */
  ['createStyle', 'editStyle', 'editHandleStyle', 'labelStyle', 'highlightStyle'].forEach(function (styleType) {
    m_this[styleType] = function (arg1, arg2) {
      return m_this.style(arg1, arg2, styleType);
    };
  });

  /**
   * Return the style dictionary for a particular state.
   * @param {string} [state] The state to return styles for.  Defaults to the
   *    current state.
   * @returns {object} The style object for the state.  If there is no such
   *    style defined, the default style is used.
   */
  this.styleForState = function (state) {
    state = state || m_this.state();
    /* for some states, fall back to the general style if they don't specify a
     * value explicitly. */
    if (state === annotationState.edit || state === annotationState.highlight) {
      return $.extend({}, m_options.style, m_options[state + 'Style']);
    }
    if (state === annotationState.create) {
      return $.extend({}, m_options.style, m_options.editStyle, m_options[state + 'Style']);
    }
    return m_options[state + 'Style'] || m_options.style || {};
  };

  /**
   * Get the type of this annotation.
   *
   * @returns {string} The annotation type.
   */
  this.type = function () {
    return m_type;
  };

  /**
   * Get a list of renderable features for this annotation.  The list index is
   * functionally a z-index for the feature.  Each entry is a dictionary with
   * the key as the feature name (such as `line`, `quad`, or `polygon`), and
   * the value a dictionary of values to pass to the feature constructor, such
   * as `style` and `coordinates`.
   *
   * @returns {array} An array of features.
   */
  this.features = function () {
    return [];
  };

  /**
   * Handle a mouse click on this annotation.  If the event is processed,
   * evt.handled should be set to `true` to prevent further processing.
   *
   * @param {geo.event} evt The mouse click event.
   * @returns {boolean|string} `true` to update the annotation, `'done'` if
   *    the annotation was completed (changed from create to done state),
   *    `'remove'` if the annotation should be removed, falsy to not update
   *    anything.
   */
  this.mouseClick = function (evt) {
    return undefined;
  };

  /**
   * Handle a mouse click on this annotation when in edit mode.  If the event
   * is processed, evt.handled should be set to `true` to prevent further
   * processing.
   *
   * @param {geo.event} evt The mouse click event.
   * @returns {boolean|string} `true` to update the annotation, `'done'` if
   *    the annotation was completed (changed from create to done state),
   *    `'remove'` if the annotation should be removed, falsy to not update
   *    anything.
   */
  this.mouseClickEdit = function (evt) {
    return undefined;
  };

  /**
   * Handle a mouse move on this annotation.
   *
   * @param {geo.event} evt The mouse move event.
   * @returns {boolean} Truthy to update the annotation, falsy to not
   *    update anything.
   */
  this.mouseMove = function (evt) {
    return undefined;
  };

  /**
   * Get or set coordinates associated with this annotation in the map gcs
   * coordinate system.
   *
   * @param {geo.geoPosition[]} [coordinates] An optional array of coordinates
   *  to set.
   * @returns {geo.geoPosition[]} The current array of coordinates.
   */
  this._coordinates = function (coordinates) {
    return [];
  };

  this._coordinateOption = 'vertices';

  /**
   * Get coordinates associated with this annotation.
   *
   * @param {string|geo.transform|null} [gcs] `undefined` to use the interface
   *    gcs, `null` to use the map gcs, or any other transform.
   * @returns {geo.geoPosition[]} An array of coordinates.
   */
  this.coordinates = function (gcs) {
    var coord = m_this._coordinates() || [];
    if (m_this.layer()) {
      var map = m_this.layer().map();
      gcs = gcs === null ? map.gcs() : gcs === undefined ? map.ingcs() : gcs;
      if (gcs !== map.gcs()) {
        coord = transform.transformCoordinates(map.gcs(), gcs, coord);
      }
    }
    return coord;
  };

  /**
   * Mark this annotation as modified.  This just marks the parent layer as
   * modified.
   *
   * @returns {this} The annotation.
   */
  this.modified = function () {
    if (m_this.layer()) {
      m_this.layer().modified();
    }
    return m_this;
  };

  /**
   * Draw this annotation.  This just updates and draws the parent layer.
   *
   * @returns {this} The annotation.
   */
  this.draw = function () {
    if (m_this.layer()) {
      m_this.layer()._update();
      m_this.layer().draw();
    }
    return m_this;
  };

  /**
   * Return a list of styles that should be preserved in a geojson
   * representation of the annotation.
   *
   * @returns {string[]} A list of style names to store.
   */
  this._geojsonStyles = function () {
    return ['closed', 'fill', 'fillColor', 'fillOpacity', 'lineCap', 'lineJoin', 'radius', 'stroke', 'strokeColor', 'strokeOffset', 'strokeOpacity', 'strokeWidth'];
  };

  /**
   * Return the coordinates to be stored in a geojson geometry object.
   *
   * @param {string|geo.transform|null} [gcs] `undefined` to use the interface
   *    gcs, `null` to use the map gcs, or any other transform.
   * @returns {array} An array of flattened coordinates in the interface gcs
   *    coordinate system.  `undefined` if this annotation is incomplete.
   */
  this._geojsonCoordinates = function (gcs) {
    return [];
  };

  /**
   * Return the geometry type that is used to store this annotation in geojson.
   *
   * @returns {string} A geojson geometry type.
   */
  this._geojsonGeometryType = function () {
    return '';
  };

  /**
   * Return the annotation as a geojson object.
   *
   * @param {string|geo.transform|null} [gcs] `undefined` to use the interface
   *    gcs, `null` to use the map gcs, or any other transform.
   * @param {boolean} [includeCrs] If truthy, include the coordinate system.
   * @returns {object} The annotation as a geojson object, or `undefined` if it
   *    should not be represented (for instance, while it is being created).
   */
  this.geojson = function (gcs, includeCrs) {
    var coor = m_this._geojsonCoordinates(gcs),
        geotype = m_this._geojsonGeometryType(),
        styles = m_this._geojsonStyles(),
        objStyle = m_this.options('style') || {},
        objLabelStyle = m_this.labelStyle() || {},
        i,
        key,
        value;
    if (!coor || !coor.length || !geotype) {
      return;
    }
    var obj = {
      type: 'Feature',
      geometry: {
        type: geotype,
        coordinates: coor
      },
      properties: {
        annotationType: m_type,
        name: m_this.name(),
        annotationId: m_this.id()
      }
    };
    if (m_label) {
      obj.properties.label = m_label;
    }
    if (m_description) {
      obj.properties.description = m_description;
    }
    if (m_this.options('showLabel') === false) {
      obj.properties.showLabel = m_this.options('showLabel');
    }
    for (i = 0; i < styles.length; i += 1) {
      key = styles[i];
      value = util.ensureFunction(objStyle[key])();
      if (value !== undefined) {
        if (key.toLowerCase().match(/color$/)) {
          value = util.convertColorToHex(value, 'needed');
        }
        obj.properties[key] = value;
      }
    }
    for (i = 0; i < textFeature.usedStyles.length; i += 1) {
      key = textFeature.usedStyles[i];
      value = util.ensureFunction(objLabelStyle[key])();
      if (value !== undefined) {
        if (key.toLowerCase().match(/color$/)) {
          value = util.convertColorToHex(value, 'needed');
        }
        obj.properties['label' + key.charAt(0).toUpperCase() + key.slice(1)] = value;
      }
    }
    if (includeCrs) {
      var map = m_this.layer().map();
      gcs = gcs === null ? map.gcs() : gcs === undefined ? map.ingcs() : gcs;
      obj.crs = {
        type: 'name',
        properties: {
          type: 'proj4',
          name: gcs
        }
      };
    }
    return obj;
  };

  /**
   * Add edit handles to the feature list.
   *
   * @param {array} features The array of features to modify.
   * @param {geo.geoPosition[]} vertices An array of vertices in map gcs
   *    coordinates.
   * @param {object} [opts] If specified, the keys are the types of the
   *    handles.  This matches the `editHandleStyle.handle` object.  Any type
   *    that is set to `false` in either `opts` or `editHandleStyle.handle`
   *    will prevent those handles from being created.
   * @param {boolean} [isOpen=false] If true, no edge handle will be created
   *    between the last and first vertices.
   */
  this._addEditHandles = function (features, vertices, opts, isOpen) {
    var editPoints,
        style = $.extend({}, defaultEditHandleStyle, m_this.editHandleStyle()),
        handles = util.ensureFunction(style.handles)() || {},
        selected = m_this._editHandle && m_this._editHandle.handle && m_this._editHandle.handle.selected ? m_this._editHandle.handle : undefined;
    /* opts specify which handles are allowed.  They must be allowed by the
     * original opts object and by the editHandleStyle.handle object. */
    opts = $.extend({}, opts);
    Object.keys(handles).forEach(function (key) {
      if (handles[key] === false) {
        opts[key] = false;
      }
    });
    if (!features[editHandleFeatureLevel]) {
      features[editHandleFeatureLevel] = { point: [] };
    }
    editPoints = features[editHandleFeatureLevel].point;
    vertices.forEach(function (pt, idx) {
      if (opts.vertex !== false) {
        editPoints.push($.extend({}, pt, { type: 'vertex', index: idx, style: style, editHandle: true }));
      }
      if (opts.edge !== false && idx !== vertices.length - 1 && (pt.x !== vertices[idx + 1].x || pt.y !== vertices[idx + 1].y)) {
        editPoints.push($.extend({
          x: (pt.x + vertices[idx + 1].x) / 2,
          y: (pt.y + vertices[idx + 1].y) / 2
        }, { type: 'edge', index: idx, style: style, editHandle: true }));
      }
      if (opts.edge !== false && !isOpen && idx === vertices.length - 1 && (pt.x !== vertices[0].x || pt.y !== vertices[0].y)) {
        editPoints.push($.extend({
          x: (pt.x + vertices[0].x) / 2,
          y: (pt.y + vertices[0].y) / 2
        }, { type: 'edge', index: idx, style: style, editHandle: true }));
      }
    });
    if (opts.center !== false) {
      editPoints.push($.extend({}, util.centerFromPerimeter(m_this._coordinates()), { type: 'center', style: style, editHandle: true }));
    }
    if (opts.rotate !== false) {
      editPoints.push($.extend(m_this._rotateHandlePosition(style.rotateHandleOffset, style.rotateHandleRotation + (selected && selected.type === 'rotate' ? m_this._editHandle.amountRotated : 0)), { type: 'rotate', style: style, editHandle: true }));
      if (m_this._editHandle && (!selected || selected.type !== 'rotate')) {
        m_this._editHandle.amountRotated = 0;
      }
    }
    if (opts.resize !== false) {
      editPoints.push($.extend(m_this._rotateHandlePosition(style.resizeHandleOffset, style.resizeHandleRotation), { type: 'resize', style: style, editHandle: true }));
    }
    if (selected) {
      editPoints.forEach(function (pt) {
        if (pt.type === selected.type && pt.index === selected.index) {
          pt.selected = true;
        }
      });
    }
  };

  /**
   * Process the edit center action for a general annotation.
   *
   * @param {geo.event} evt The action event.
   * @returns {boolean|string} `true` to update the annotation, falsy to not
   *    update anything.
   */
  this._processEditActionCenter = function (evt) {
    var start = m_this._editHandle.startCoordinates,
        delta = {
      x: evt.mouse.mapgcs.x - evt.state.origin.mapgcs.x,
      y: evt.mouse.mapgcs.y - evt.state.origin.mapgcs.y
    },
        curPts = m_this._coordinates();
    var pts = start.map(function (elem) {
      return { x: elem.x + delta.x, y: elem.y + delta.y };
    });
    if (pts[0].x !== curPts[0].x || pts[0].y !== curPts[0].y) {
      m_this._coordinates(pts);
      return true;
    }
    return false;
  };

  /**
   * Process the edit rotate action for a general annotation.
   *
   * @param {geo.event} evt The action event.
   * @returns {boolean|string} `true` to update the annotation, falsy to not
   *    update anything.
   */
  this._processEditActionRotate = function (evt) {
    var handle = m_this._editHandle,
        start = handle.startCoordinates,
        delta = {
      x: evt.mouse.mapgcs.x - evt.state.origin.mapgcs.x,
      y: evt.mouse.mapgcs.y - evt.state.origin.mapgcs.y
    },
        ang1 = Math.atan2(handle.rotatePosition.y - handle.center.y, handle.rotatePosition.x - handle.center.x),
        ang2 = Math.atan2(handle.rotatePosition.y + delta.y - handle.center.y, handle.rotatePosition.x + delta.x - handle.center.x),
        ang = ang2 - ang1,
        curPts = m_this._coordinates();
    var pts = start.map(function (elem) {
      var delta = { x: elem.x - handle.center.x, y: elem.y - handle.center.y };
      return {
        x: delta.x * Math.cos(ang) - delta.y * Math.sin(ang) + handle.center.x,
        y: delta.x * Math.sin(ang) + delta.y * Math.cos(ang) + handle.center.y
      };
    });
    if (pts[0].x !== curPts[0].x || pts[0].y !== curPts[0].y) {
      m_this._coordinates(pts);
      handle.amountRotated = handle.startAmountRotated + ang;
      return true;
    }
    return false;
  };

  /**
   * Process the edit resize action for a general annotation.
   *
   * @param {geo.event} evt The action event.
   * @returns {boolean|string} `true` to update the annotation, falsy to not
   *    update anything.
   */
  this._processEditActionResize = function (evt) {
    var handle = m_this._editHandle,
        start = handle.startCoordinates,
        delta = {
      x: evt.mouse.mapgcs.x - evt.state.origin.mapgcs.x,
      y: evt.mouse.mapgcs.y - evt.state.origin.mapgcs.y
    },
        map = m_this.layer().map(),
        p0 = map.gcsToDisplay(handle.center, null),
        p1 = map.gcsToDisplay(handle.resizePosition, null),
        p2 = map.gcsToDisplay({
      x: handle.resizePosition.x + delta.x,
      y: handle.resizePosition.y + delta.y
    }, null),
        d01 = Math.pow(Math.pow(p1.y - p0.y, 2) + Math.pow(p1.x - p0.x, 2), 0.5) - handle.handle.style.resizeHandleOffset,
        d02 = Math.pow(Math.pow(p2.y - p0.y, 2) + Math.pow(p2.x - p0.x, 2), 0.5) - handle.handle.style.resizeHandleOffset,
        curPts = m_this._coordinates();
    if (d02 && d01) {
      var scale = d02 / d01;
      var pts = start.map(function (elem) {
        return {
          x: (elem.x - handle.center.x) * scale + handle.center.x,
          y: (elem.y - handle.center.y) * scale + handle.center.y
        };
      });
      if (pts[0].x !== curPts[0].x || pts[0].y !== curPts[0].y) {
        m_this._coordinates(pts);
        return true;
      }
    }
    return false;
  };

  /**
   * Process the edit edge action for a general annotation.
   *
   * @param {geo.event} evt The action event.
   * @returns {boolean|string} `true` to update the annotation, falsy to not
   *    update anything.
   */
  this._processEditActionEdge = function (evt) {
    var handle = m_this._editHandle,
        index = handle.handle.index,
        curPts = m_this._coordinates();
    curPts.splice(index + 1, 0, { x: handle.handle.x, y: handle.handle.y });
    handle.handle.type = 'vertex';
    handle.handle.index += 1;
    handle.startCoordinates = curPts.slice();
    m_this.modified();
    return true;
  };

  /**
   * Process the edit vertex action for a general annotation.
   *
   * @param {geo.event} evt The action event.
   * @param {boolean} [canClose] if True, this annotation has a closed style
   *    that indicates if the first and last vertices are joined.  If falsy, is
   *    allowed to be changed to true.
   * @returns {boolean|string} `true` to update the annotation, `false` to
   *    prevent closure, any other falsy to not update anything.
   */
  this._processEditActionVertex = function (evt, canClose) {
    var handle = m_this._editHandle,
        index = handle.handle.index,
        start = handle.startCoordinates,
        curPts = m_this._coordinates(),
        origLen = curPts.length,
        origPt = curPts[index],
        delta = {
      x: evt.mouse.mapgcs.x - evt.state.origin.mapgcs.x,
      y: evt.mouse.mapgcs.y - evt.state.origin.mapgcs.y
    },
        layer = m_this.layer(),
        aPP = layer.options('adjacentPointProximity'),
        near,
        atEnd;

    curPts[index] = {
      x: start[index].x + delta.x,
      y: start[index].y + delta.y
    };
    if (layer.displayDistance(curPts[index], null, start[index], null) <= aPP) {
      /* if we haven't moved at least aPP from where the vertex started, don't
       * allow it to be merged into another vertex.  This prevents small scale
       * edits from collapsing immediately. */
    } else if (layer.displayDistance(curPts[index], null, curPts[(index + 1) % curPts.length], null) <= aPP) {
      near = (index + 1) % curPts.length;
    } else if (layer.displayDistance(curPts[index], null, curPts[(index + curPts.length - 1) % curPts.length], null) <= aPP) {
      near = (index + curPts.length - 1) % curPts.length;
    }
    atEnd = near === 0 && index === curPts.length - 1 || near === curPts.length - 1 && index === 0;
    if (canClose === false && atEnd) {
      near = undefined;
    }
    if (near !== undefined && curPts.length > (canClose || m_this.options('style').closed ? 3 : 2)) {
      curPts[index] = { x: curPts[near].x, y: curPts[near].y };
      if (evt.event === geo_event.actionup) {
        if (canClose && atEnd) {
          m_this.options('style').closed = true;
        }
        curPts.splice(index, 1);
      }
    }
    if (curPts.length === origLen && curPts[index].x === origPt.x && curPts[index].y === origPt.y) {
      return false;
    }
    m_this._coordinates(curPts);
    return true;
  };
};

/**
 * Rectangle annotation specification.  Extends {@link geo.annotation.spec}.
 *
 * @typedef {object} geo.rectangleAnnotation.spec
 * @extends geo.annotation.spec
 * @property {geo.geoPosition[]} [corners] A list of four corners in map gcs
 *    coordinates.  These must be in order around the perimeter of the
 *    rectangle (in either direction).
 * @property {geo.geoPosition[]} [coordinates] An alternate name for `corners`.
 * @property {geo.polygonFeature.styleSpec} [style] The style to apply to a
 *    finished rectangle.  This uses styles for {@link geo.polygonFeature}.
 * @property {geo.polygonFeature.styleSpec} [editStyle] The style to apply to a
 *    rectangle in edit mode.
 */

/**
 * Rectangle annotation class.
 *
 * Rectangles are always rendered as polygons.  This could be changed -- if no
 * stroke is specified, the quad feature would be sufficient and work on more
 * renderers.
 *
 * @class
 * @alias geo.rectangleAnnotation
 * @extends geo.annotation
 *
 * @param {geo.rectangleAnnotation.spec?} [args] Options for the annotation.
 */
var rectangleAnnotation = function rectangleAnnotation(args) {
  'use strict';

  if (!(this instanceof rectangleAnnotation)) {
    return new rectangleAnnotation(args);
  }

  args = $.extend(true, {}, {
    style: {
      fill: true,
      fillColor: { r: 0, g: 1, b: 0 },
      fillOpacity: 0.25,
      polygon: function polygon(d) {
        return d.polygon;
      },
      stroke: true,
      strokeColor: { r: 0, g: 0, b: 0 },
      strokeOpacity: 1,
      strokeWidth: 3,
      uniformPolygon: true
    },
    highlightStyle: {
      fillColor: { r: 0, g: 1, b: 1 },
      fillOpacity: 0.5,
      strokeWidth: 5
    },
    createStyle: {
      fillColor: { r: 0.3, g: 0.3, b: 0.3 },
      fillOpacity: 0.25,
      strokeColor: { r: 0, g: 0, b: 1 }
    }
  }, args || {});
  args.corners = args.corners || args.coordinates || [];
  delete args.coordinates;
  annotation.call(this, 'rectangle', args);

  var m_this = this,
      s_actions = this.actions,
      s_processEditAction = this.processEditAction;

  /**
   * Return actions needed for the specified state of this annotation.
   *
   * @param {string} [state] The state to return actions for.  Defaults to
   *    the current state.
   * @returns {geo.actionRecord[]} A list of actions.
   */
  this.actions = function (state) {
    if (!state) {
      state = m_this.state();
    }
    switch (state) {
      case annotationState.create:
        return [{
          action: geo_action.annotation_rectangle,
          name: 'rectangle create',
          owner: annotationActionOwner,
          input: 'left',
          modifiers: { shift: false, ctrl: false },
          selectionRectangle: true
        }];
      default:
        return s_actions.apply(m_this, arguments);
    }
  };

  /**
   * Process any actions for this annotation.
   *
   * @param {geo.event} evt The action event.
   * @returns {boolean|string} `true` to update the annotation, `'done'` if the
   *    annotation was completed (changed from create to done state),
   *    `'remove'` if the annotation should be removed, falsy to not update
   *    anything.
   */
  this.processAction = function (evt) {
    var layer = m_this.layer();
    if (m_this.state() !== annotationState.create || !layer || evt.event !== geo_event.actionselection || evt.state.action !== geo_action.annotation_rectangle) {
      return;
    }
    var map = layer.map(),
        corners = [
    /* Keep in map gcs, not interface gcs to avoid wrapping issues */
    map.displayToGcs({ x: evt.lowerLeft.x, y: evt.lowerLeft.y }, null), map.displayToGcs({ x: evt.lowerLeft.x, y: evt.upperRight.y }, null), map.displayToGcs({ x: evt.upperRight.x, y: evt.upperRight.y }, null), map.displayToGcs({ x: evt.upperRight.x, y: evt.lowerLeft.y }, null)];
    /* Don't keep rectangles that have nearly zero area in display pixels */
    if (layer.displayDistance(corners[0], null, corners[1], null) * layer.displayDistance(corners[0], null, corners[3], null) < 0.01) {
      return 'remove';
    }
    m_this.options('corners', corners);
    m_this.state(annotationState.done);
    return 'done';
  };

  /**
   * Get a list of renderable features for this annotation.
   *
   * @returns {array} An array of features.
   */
  this.features = function () {
    var opt = m_this.options(),
        state = m_this.state(),
        features;
    switch (state) {
      case annotationState.create:
        features = [];
        if (opt.corners && opt.corners.length >= 4) {
          features = [{
            polygon: {
              polygon: opt.corners,
              style: m_this.styleForState(state)
            }
          }];
        }
        break;
      default:
        features = [{
          polygon: {
            polygon: opt.corners,
            style: m_this.styleForState(state)
          }
        }];
        if (state === annotationState.edit) {
          m_this._addEditHandles(features, opt.corners);
        }
        break;
    }
    return features;
  };

  /**
   * Get and optionally set coordinates associated with this annotation in the
   * map gcs coordinate system.
   *
   * @param {geo.geoPosition[]} [coordinates] An optional array of coordinates
   *  to set.
   * @returns {geo.geoPosition[]} The current array of coordinates.
   */
  this._coordinates = function (coordinates) {
    if (coordinates && coordinates.length >= 4) {
      m_this.options('corners', coordinates.slice(0, 4));
      /* Should we ensure that the four points form a rectangle in the current
       * projection, though this might not be rectangular in another gcs? */
    }
    return m_this.options('corners');
  };

  this._coordinateOption = 'corners';

  /**
   * Return the coordinates to be stored in a geojson geometry object.
   *
   * @param {string|geo.transform|null} [gcs] `undefined` to use the interface
   *    gcs, `null` to use the map gcs, or any other transform.
   * @returns {array} An array of flattened coordinates in the interface gcs
   *    coordinate system.  `undefined` if this annotation is incomplete.
   */
  this._geojsonCoordinates = function (gcs) {
    var src = m_this.coordinates(gcs);
    if (!src || m_this.state() === annotationState.create || src.length < 4) {
      return;
    }
    var coor = [];
    for (var i = 0; i < 4; i += 1) {
      coor.push([src[i].x, src[i].y]);
    }
    coor.push([src[0].x, src[0].y]);
    return [coor];
  };

  /**
   * Return the geometry type that is used to store this annotation in geojson.
   *
   * @returns {string} A geojson geometry type.
   */
  this._geojsonGeometryType = function () {
    return 'Polygon';
  };

  /**
   * Return a list of styles that should be preserved in a geojson
   * representation of the annotation.
   *
   * @returns {string[]} A list of style names to store.
   */
  this._geojsonStyles = function () {
    return ['fill', 'fillColor', 'fillOpacity', 'lineCap', 'lineJoin', 'stroke', 'strokeColor', 'strokeOffset', 'strokeOpacity', 'strokeWidth'];
  };

  /**
   * Set three corners based on an initial corner and a mouse event.
   *
   * @param {geo.geoPosition} corners An array of four corners to update.
   * @param {geo.event} evt The mouse move event.
   */
  this._setCornersFromMouse = function (corners, evt) {
    var map = m_this.layer().map(),
        c0 = map.gcsToDisplay({ x: corners[0].x, y: corners[0].y }, null),
        c2 = map.gcsToDisplay(evt.mapgcs, null),
        c1 = { x: c2.x, y: c0.y },
        c3 = { x: c0.x, y: c2.y };
    corners[2] = $.extend({}, evt.mapgcs);
    corners[1] = map.displayToGcs(c1, null);
    corners[3] = map.displayToGcs(c3, null);
  };

  /**
   * Handle a mouse move on this annotation.
   *
   * @param {geo.event} evt The mouse move event.
   * @returns {boolean} Truthy to update the annotation, falsy to not
   *    update anything.
   */
  this.mouseMove = function (evt) {
    if (m_this.state() !== annotationState.create) {
      return;
    }
    var corners = m_this.options('corners');
    if (corners.length) {
      m_this._setCornersFromMouse(corners, evt);
      return true;
    }
  };

  /**
   * Handle a mouse click on this annotation.  If the event is processed,
   * evt.handled should be set to `true` to prevent further processing.
   *
   * @param {geo.event} evt The mouse click event.
   * @returns {boolean|string} `true` to update the annotation, `'done'` if
   *    the annotation was completed (changed from create to done state),
   *    `'remove'` if the annotation should be removed, falsy to not update
   *    anything.
   */
  this.mouseClick = function (evt) {
    var layer = m_this.layer();
    if (m_this.state() !== annotationState.create || !layer) {
      return;
    }
    if (!evt.buttonsDown.left && !evt.buttonsDown.right) {
      return;
    }
    var corners = m_this.options('corners');
    if (evt.buttonsDown.right && !corners.length) {
      return;
    }
    evt.handled = true;
    if (corners.length) {
      m_this._setCornersFromMouse(corners, evt);
      /* Don't keep rectangles that have nearly zero area in display pixels */
      if (layer.displayDistance(corners[0], null, corners[1], null) * layer.displayDistance(corners[0], null, corners[3], null) < 0.01) {
        return 'remove';
      }
      m_this.state(annotationState.done);
      return 'done';
    }
    if (evt.buttonsDown.left) {
      corners.push($.extend({}, evt.mapgcs));
      corners.push($.extend({}, evt.mapgcs));
      corners.push($.extend({}, evt.mapgcs));
      corners.push($.extend({}, evt.mapgcs));
      return true;
    }
  };

  /**
   * Process any edit actions for this annotation.
   *
   * @param {geo.event} evt The action event.
   * @returns {boolean|string} `true` to update the annotation, falsy to not
   *    update anything.
   */
  this.processEditAction = function (evt) {
    var start = m_this._editHandle.startCoordinates,
        delta = {
      x: evt.mouse.mapgcs.x - evt.state.origin.mapgcs.x,
      y: evt.mouse.mapgcs.y - evt.state.origin.mapgcs.y
    },
        type = m_this._editHandle.handle.type,
        index = m_this._editHandle.handle.index,
        ang = [Math.atan2(start[1].y - start[0].y, start[1].x - start[0].x), Math.atan2(start[2].y - start[1].y, start[2].x - start[1].x), Math.atan2(start[3].y - start[2].y, start[3].x - start[2].x), Math.atan2(start[0].y - start[3].y, start[0].x - start[3].x)],
        corners,
        delta1,
        delta2,
        ang1,
        ang2;
    // an angle can be zero because it is horizontal or undefined.  If opposite
    // angles are both zero, this is a degenerate rectangle (a line or a point)
    if (!ang[0] && !ang[1] && !ang[2] && !ang[3]) {
      ang[1] = Math.PI / 2;
      ang[2] = Math.PI;
      ang[3] = -Math.PI / 2;
    }
    if (!ang[0] && !ang[2]) {
      ang[0] = ang[1] - Math.PI / 2;
      ang[2] = ang[1] + Math.PI / 2;
    }
    if (!ang[1] && !ang[3]) {
      ang[1] = ang[2] - Math.PI / 2;
      ang[3] = ang[2] + Math.PI / 2;
    }
    switch (type) {
      case 'vertex':
        corners = start.map(function (elem) {
          return { x: elem.x, y: elem.y };
        });
        ang1 = ang[(index + 1) % 4];
        delta1 = {
          x: (delta.x * Math.cos(ang1) + delta.y * Math.sin(ang1)) * Math.cos(ang1),
          y: (delta.y * Math.sin(ang1) + delta.x * Math.cos(ang1)) * Math.sin(ang1)
        };
        ang2 = ang[index];
        delta2 = {
          x: (delta.x * Math.cos(ang2) + delta.y * Math.sin(ang2)) * Math.cos(ang2),
          y: (delta.y * Math.sin(ang2) + delta.x * Math.cos(ang2)) * Math.sin(ang2)
        };
        corners[index].x += delta.x;
        corners[index].y += delta.y;
        corners[(index + 1) % 4].x += delta1.x;
        corners[(index + 1) % 4].y += delta1.y;
        corners[(index + 3) % 4].x += delta2.x;
        corners[(index + 3) % 4].y += delta2.y;
        m_this.options('corners', corners);
        return true;
      case 'edge':
        corners = start.map(function (elem) {
          return { x: elem.x, y: elem.y };
        });
        ang1 = ang[(index + 1) % 4];
        delta = {
          x: (delta.x * Math.cos(ang1) + delta.y * Math.sin(ang1)) * Math.cos(ang1),
          y: (delta.y * Math.sin(ang1) + delta.x * Math.cos(ang1)) * Math.sin(ang1)
        };
        corners[index].x += delta.x;
        corners[index].y += delta.y;
        corners[(index + 1) % 4].x += delta.x;
        corners[(index + 1) % 4].y += delta.y;
        m_this.options('corners', corners);
        return true;
    }
    return s_processEditAction.apply(m_this, arguments);
  };
};
inherit(rectangleAnnotation, annotation);

var rectangleRequiredFeatures = {};
rectangleRequiredFeatures[polygonFeature.capabilities.feature] = true;
registerAnnotation('rectangle', rectangleAnnotation, rectangleRequiredFeatures);

/**
 * Polygon annotation specification.  Extends {@link geo.annotation.spec}.
 *
 * @typedef {object} geo.polygonAnnotation.spec
 * @extends geo.annotation.spec
 * @property {geo.geoPosition[]} [vertices] A list of vertices in map gcs
 *    coordinates.  These must be in order around the perimeter of the polygon
 *    (in either direction).
 * @property {geo.geoPosition[]} [coordinates] An alternate name for
 *    `vertices`.
 * @property {geo.polygonFeature.styleSpec} [style] The style to apply to a
 *    finished polygon.  This uses styles for {@link geo.polygonFeature}.
 * @property {geo.polygonFeature.styleSpec} [editStyle] The style to apply to ai
 *    polygon in edit mode.
 */

/**
 * Polygon annotation class
 *
 * When complete, polygons are rendered as polygons.  During creation they are
 * rendered as lines and polygons.
 *
 * @class
 * @alias geo.polygonAnnotation
 * @extends geo.annotation
 *
 * @param {geo.polygonAnnotation.spec?} [args] Options for the annotation.
 */
var polygonAnnotation = function polygonAnnotation(args) {
  'use strict';

  if (!(this instanceof polygonAnnotation)) {
    return new polygonAnnotation(args);
  }

  args = $.extend(true, {}, {
    style: {
      fill: true,
      fillColor: { r: 0, g: 1, b: 0 },
      fillOpacity: 0.25,
      polygon: function polygon(d) {
        return d.polygon;
      },
      stroke: true,
      strokeColor: { r: 0, g: 0, b: 0 },
      strokeOpacity: 1,
      strokeWidth: 3,
      uniformPolygon: true
    },
    highlightStyle: {
      fillColor: { r: 0, g: 1, b: 1 },
      fillOpacity: 0.5,
      strokeWidth: 5
    },
    createStyle: {
      closed: false,
      fillColor: { r: 0.3, g: 0.3, b: 0.3 },
      fillOpacity: 0.25,
      line: function line(d) {
        /* Return an array that has the same number of items as we have
         * vertices. */
        return Array.apply(null, Array(m_this.options('vertices').length)).map(function () {
          return d;
        });
      },
      position: function position(d, i) {
        return m_this.options('vertices')[i];
      },
      stroke: false,
      strokeColor: { r: 0, g: 0, b: 1 }
    }
  }, args || {});
  args.vertices = args.vertices || args.coordinates || [];
  delete args.coordinates;
  annotation.call(this, 'polygon', args);

  var m_this = this;

  /**
   * Get a list of renderable features for this annotation.  When the polygon
   * is done, this is just a single polygon.  During creation this can be a
   * polygon and line at z-levels 1 and 2.
   *
   * @returns {array} An array of features.
   */
  this.features = function () {
    var opt = m_this.options(),
        state = m_this.state(),
        style = m_this.styleForState(state),
        features;
    switch (state) {
      case annotationState.create:
        features = [];
        if (opt.vertices && opt.vertices.length >= 3) {
          features[1] = {
            polygon: {
              polygon: opt.vertices,
              style: style
            }
          };
        }
        if (opt.vertices && opt.vertices.length >= 2) {
          features[2] = {
            line: {
              line: opt.vertices,
              style: style
            }
          };
        }
        break;
      default:
        features = [{
          polygon: {
            polygon: opt.vertices,
            style: style
          }
        }];
        if (state === annotationState.edit) {
          m_this._addEditHandles(features, opt.vertices);
        }
        break;
    }
    return features;
  };

  /**
   * Get and optionally set coordinates associated with this annotation in the
   * map gcs coordinate system.
   *
   * @param {geo.geoPosition[]} [coordinates] An optional array of coordinates
   *  to set.
   * @returns {geo.geoPosition[]} The current array of coordinates.
   */
  this._coordinates = function (coordinates) {
    if (coordinates) {
      m_this.options('vertices', coordinates);
    }
    return m_this.options('vertices');
  };

  /**
   * Handle a mouse move on this annotation.
   *
   * @param {geo.event} evt The mouse move event.
   * @returns {boolean} Truthy to update the annotation, falsy to not
   *    update anything.
   */
  this.mouseMove = function (evt) {
    if (m_this.state() !== annotationState.create) {
      return;
    }
    var vertices = m_this.options('vertices');
    if (vertices.length) {
      vertices[vertices.length - 1] = evt.mapgcs;
      return true;
    }
  };

  /**
   * Handle a mouse click on this annotation.  If the event is processed,
   * evt.handled should be set to `true` to prevent further processing.
   *
   * @param {geo.event} evt The mouse click event.
   * @returns {boolean|string} `true` to update the annotation, `'done'` if
   *    the annotation was completed (changed from create to done state),
   *    `'remove'` if the annotation should be removed, falsy to not update
   *    anything.
   */
  this.mouseClick = function (evt) {
    var layer = m_this.layer();
    if (m_this.state() !== annotationState.create || !layer) {
      return;
    }
    var end = !!evt.buttonsDown.right,
        skip;
    if (!evt.buttonsDown.left && !evt.buttonsDown.right) {
      return;
    }
    var vertices = m_this.options('vertices');
    if (evt.buttonsDown.right && !vertices.length) {
      return;
    }
    evt.handled = true;
    if (evt.buttonsDown.left) {
      if (vertices.length) {
        if (vertices.length >= 2 && layer.displayDistance(vertices[vertices.length - 2], null, evt.map, 'display') <= layer.options('adjacentPointProximity')) {
          skip = true;
          if (m_this._lastClick && evt.time - m_this._lastClick < layer.options('dblClickTime')) {
            end = true;
          }
        } else if (vertices.length >= 2 && layer.displayDistance(vertices[0], null, evt.map, 'display') <= layer.options('finalPointProximity')) {
          end = true;
        } else {
          vertices[vertices.length - 1] = evt.mapgcs;
        }
      } else {
        vertices.push(evt.mapgcs);
      }
      if (!end && !skip) {
        vertices.push(evt.mapgcs);
      }
      m_this._lastClick = evt.time;
    }
    if (end) {
      if (vertices.length < 4) {
        return 'remove';
      }
      vertices.pop();
      m_this.state(annotationState.done);
      return 'done';
    }
    return end || !skip;
  };

  /**
   * Return the coordinates to be stored in a geojson geometry object.
   *
   * @param {string|geo.transform|null} [gcs] `undefined` to use the interface
   *    gcs, `null` to use the map gcs, or any other transform.
   * @returns {array} An array of flattened coordinates in the interface gcs
   *    coordinate system.  `undefined` if this annotation is incomplete.
   */
  this._geojsonCoordinates = function (gcs) {
    var src = m_this.coordinates(gcs);
    if (!src || src.length < 3 || m_this.state() === annotationState.create) {
      return;
    }
    var coor = [];
    for (var i = 0; i < src.length; i += 1) {
      coor.push([src[i].x, src[i].y]);
    }
    coor.push([src[0].x, src[0].y]);
    return [coor];
  };

  /**
   * Return the geometry type that is used to store this annotation in geojson.
   *
   * @returns {string} A geojson geometry type.
   */
  this._geojsonGeometryType = function () {
    return 'Polygon';
  };

  /**
   * Return a list of styles that should be preserved in a geojson
   * representation of the annotation.
   *
   * @returns {string[]} A list of style names to store.
   */
  this._geojsonStyles = function () {
    return ['fill', 'fillColor', 'fillOpacity', 'lineCap', 'lineJoin', 'stroke', 'strokeColor', 'strokeOffset', 'strokeOpacity', 'strokeWidth'];
  };
};
inherit(polygonAnnotation, annotation);

var polygonRequiredFeatures = {};
polygonRequiredFeatures[polygonFeature.capabilities.feature] = true;
polygonRequiredFeatures[lineFeature.capabilities.basic] = [annotationState.create];
registerAnnotation('polygon', polygonAnnotation, polygonRequiredFeatures);

/**
 * Line annotation specification.  Extends {@link geo.annotation.spec}.
 *
 * @typedef {object} geo.lineAnnotation.spec
 * @extends geo.annotation.spec
 * @property {geo.geoPosition[]} [vertices] A list of vertices in map gcs
 *    coordinates.
 * @property {geo.geoPosition[]} [coordinates] An alternate name for
 *    `vertices`.
 * @property {geo.lineFeature.styleSpec} [style] The style to apply to a
 *    finished line.  This uses styles for {@link geo.lineFeature}.
 * @property {geo.lineFeature.styleSpec} [editStyle] The style to apply to a
 *    line in edit mode.
 */

/**
 * Line annotation class.
 *
 * @class
 * @alias geo.lineAnnotation
 * @extends geo.annotation
 *
 * @param {geo.lineAnnotation.spec?} [args] Options for the annotation.
 */
var lineAnnotation = function lineAnnotation(args) {
  'use strict';

  if (!(this instanceof lineAnnotation)) {
    return new lineAnnotation(args);
  }

  args = $.extend(true, {}, {
    style: {
      line: function line(d) {
        /* Return an array that has the same number of items as we have
         * vertices. */
        return Array.apply(null, Array(m_this.options('vertices').length)).map(function () {
          return d;
        });
      },
      position: function position(d, i) {
        return m_this.options('vertices')[i];
      },
      strokeColor: { r: 0, g: 0, b: 0 },
      strokeOpacity: 1,
      strokeWidth: 3,
      closed: false,
      lineCap: 'butt',
      lineJoin: 'miter'
    },
    highlightStyle: {
      strokeWidth: 5
    },
    createStyle: {
      line: function line(d) {
        /* Return an array that has the same number of items as we have
         * vertices. */
        return Array.apply(null, Array(m_this.options('vertices').length)).map(function () {
          return d;
        });
      },
      position: function position(d, i) {
        return m_this.options('vertices')[i];
      },
      strokeColor: { r: 0, g: 0, b: 1 },
      strokeOpacity: 1,
      strokeWidth: 3,
      closed: false,
      lineCap: 'butt',
      lineJoin: 'miter'
    }
  }, args || {});
  args.vertices = args.vertices || args.coordinates || [];
  delete args.coordinates;
  annotation.call(this, 'line', args);

  var m_this = this,
      s_actions = this.actions,
      s_processEditAction = this.processEditAction;

  /**
   * Get a list of renderable features for this annotation.
   *
   * @returns {array} An array of features.
   */
  this.features = function () {
    var opt = m_this.options(),
        state = m_this.state(),
        features;
    switch (state) {
      case annotationState.create:
        features = [{
          line: {
            line: opt.vertices,
            style: m_this.styleForState(state)
          }
        }];
        break;
      default:
        features = [{
          line: {
            line: opt.vertices,
            style: m_this.styleForState(state)
          }
        }];
        if (state === annotationState.edit) {
          m_this._addEditHandles(features, opt.vertices, undefined, !m_this.style('closed'));
        }
        break;
    }
    return features;
  };

  /**
   * Get and optionally set coordinates associated with this annotation in the
   * map gcs coordinate system.
   *
   * @param {geo.geoPosition[]} [coordinates] An optional array of coordinates
   *  to set.
   * @returns {geo.geoPosition[]} The current array of coordinates.
   */
  this._coordinates = function (coordinates) {
    if (coordinates) {
      m_this.options('vertices', coordinates);
    }
    return m_this.options('vertices');
  };

  /**
   * Handle a mouse move on this annotation.
   *
   * @param {geo.event} evt The mouse move event.
   * @returns {boolean} Truthy to update the annotation, falsy to not
   *    update anything.
   */
  this.mouseMove = function (evt) {
    if (m_this.state() !== annotationState.create) {
      return;
    }
    var vertices = m_this.options('vertices');
    if (vertices.length) {
      vertices[vertices.length - 1] = evt.mapgcs;
      return true;
    }
  };

  /**
   * Handle a mouse click on this annotation.  If the event is processed,
   * evt.handled should be set to `true` to prevent further processing.
   *
   * @param {geo.event} evt The mouse click event.
   * @returns {boolean|string} `true` to update the annotation, `'done'` if
   *    the annotation was completed (changed from create to done state),
   *    `'remove'` if the annotation should be removed, falsy to not update
   *    anything.
   */
  this.mouseClick = function (evt) {
    var layer = m_this.layer();
    if (m_this.state() !== annotationState.create || !layer) {
      return;
    }
    var end = !!evt.buttonsDown.right,
        skip;
    if (!evt.buttonsDown.left && !evt.buttonsDown.right) {
      return;
    }
    var vertices = m_this.options('vertices');
    if (evt.buttonsDown.right && !vertices.length) {
      return;
    }
    evt.handled = true;
    if (evt.buttonsDown.left) {
      if (vertices.length) {
        if (vertices.length >= 2 && layer.displayDistance(vertices[vertices.length - 2], null, evt.map, 'display') <= layer.options('adjacentPointProximity')) {
          skip = true;
          if (m_this._lastClick && evt.time - m_this._lastClick < layer.options('dblClickTime')) {
            end = true;
          }
        } else if (vertices.length >= 2 && layer.displayDistance(vertices[0], null, evt.map, 'display') <= layer.options('finalPointProximity')) {
          end = 'close';
        } else {
          vertices[vertices.length - 1] = evt.mapgcs;
        }
      } else {
        vertices.push(evt.mapgcs);
      }
      if (!end && !skip) {
        vertices.push(evt.mapgcs);
      }
      m_this._lastClick = evt.time;
      m_this._lastClickVertexCount = vertices.length;
    }
    if (end) {
      if (vertices.length < 3) {
        return 'remove';
      }
      vertices.pop();
      m_this.options('style').closed = end === 'close';
      m_this.state(annotationState.done);
      return 'done';
    }
    return end || !skip;
  };

  /**
   * Return actions needed for the specified state of this annotation.
   *
   * @param {string} [state] The state to return actions for.  Defaults to
   *    the current state.
   * @returns {geo.actionRecord[]} A list of actions.
   */
  this.actions = function (state) {
    if (!state) {
      state = m_this.state();
    }
    switch (state) {
      case annotationState.create:
        return [{
          action: geo_action.annotation_line,
          name: 'line create',
          owner: annotationActionOwner,
          input: 'left',
          modifiers: { shift: false, ctrl: false }
        }, {
          action: geo_action.annotation_line,
          name: 'line create',
          owner: annotationActionOwner,
          input: 'pan'
        }];
      default:
        return s_actions.apply(m_this, arguments);
    }
  };

  /**
   * Process any actions for this annotation.
   *
   * @param {geo.event} evt The action event.
   * @returns {boolean|string} `true` to update the annotation, `'done'` if the
   *    annotation was completed (changed from create to done state),
   *    `'remove'` if the annotation should be removed, falsy to not update
   *    anything.
   */
  this.processAction = function (evt) {
    var layer = m_this.layer();
    if (m_this.state() !== annotationState.create || !layer || evt.state.action !== geo_action.annotation_line) {
      return;
    }
    var cpp = layer.options('continuousPointProximity');
    var cpc = layer.options('continuousPointColinearity');
    if (cpp || cpp === 0) {
      var vertices = m_this.options('vertices');
      if (!vertices.length) {
        vertices.push(evt.mouse.mapgcs);
        vertices.push(evt.mouse.mapgcs);
        return true;
      }
      var dist = layer.displayDistance(vertices[vertices.length - 2], null, evt.mouse.map, 'display');
      if (dist && dist > cpp) {
        // combine nearly colinear points
        if (vertices.length >= (m_this._lastClickVertexCount || 1) + 3) {
          var d01 = layer.displayDistance(vertices[vertices.length - 3], null, vertices[vertices.length - 2], null),
              d12 = dist,
              d02 = layer.displayDistance(vertices[vertices.length - 3], null, evt.mouse.map, 'display');
          if (d01 && d02) {
            var costheta = (d02 * d02 - d01 * d01 - d12 * d12) / (2 * d01 * d12);
            if (costheta > Math.cos(cpc)) {
              vertices.pop();
            }
          }
        }
        vertices[vertices.length - 1] = evt.mouse.mapgcs;
        vertices.push(evt.mouse.mapgcs);
        return true;
      }
    }
  };

  /**
   * Return the coordinates to be stored in a geojson geometry object.
   *
   * @param {string|geo.transform|null} [gcs] `undefined` to use the interface
   *    gcs, `null` to use the map gcs, or any other transform.
   * @returns {array} An array of flattened coordinates in the interface gcs
   *    coordinate system.  `undefined` if this annotation is incomplete.
   */
  this._geojsonCoordinates = function (gcs) {
    var src = m_this.coordinates(gcs);
    if (!src || src.length < 2 || m_this.state() === annotationState.create) {
      return;
    }
    var coor = [];
    for (var i = 0; i < src.length; i += 1) {
      coor.push([src[i].x, src[i].y]);
    }
    return coor;
  };

  /**
   * Return the geometry type that is used to store this annotation in geojson.
   *
   * @returns {string} A geojson geometry type.
   */
  this._geojsonGeometryType = function () {
    return 'LineString';
  };

  /**
   * Return a list of styles that should be preserved in a geojson
   * representation of the annotation.
   *
   * @returns {string[]} A list of style names to store.
   */
  this._geojsonStyles = function () {
    return ['closed', 'lineCap', 'lineJoin', 'strokeColor', 'strokeOffset', 'strokeOpacity', 'strokeWidth'];
  };

  /**
   * Process any edit actions for this annotation.
   *
   * @param {geo.event} evt The action event.
   * @returns {boolean|string} `true` to update the annotation, falsy to not
   *    update anything.
   */
  this.processEditAction = function (evt) {
    switch (m_this._editHandle.handle.type) {
      case 'vertex':
        return m_this._processEditActionVertex(evt, true);
    }
    return s_processEditAction.apply(m_this, arguments);
  };

  /**
   * Handle a mouse click on this annotation when in edit mode.  If the event
   * is processed, evt.handled should be set to `true` to prevent further
   * processing.
   *
   * @param {geo.event} evt The mouse click event.
   * @returns {boolean|string} `true` to update the annotation, `'done'` if
   *    the annotation was completed (changed from create to done state),
   *    `'remove'` if the annotation should be removed, falsy to not update
   *    anything.
   */
  this.mouseClickEdit = function (evt) {
    // if we get a left double click on an edge on a closed line, break the
    // line at that edge
    var layer = m_this.layer(),
        handle = m_this._editHandle,
        split;
    // ensure we are in edit mode and this is a left click
    if (m_this.state() !== annotationState.edit || !layer || !evt.buttonsDown.left) {
      return;
    }
    // ensure this is an edge on a closed line
    if (!handle || !handle.handle.selected || handle.handle.type !== 'edge' || !m_this.options('style').closed) {
      return;
    }
    evt.handled = true;
    if (m_this._lastClick && evt.time - m_this._lastClick < layer.options('dblClickTime')) {
      split = true;
    }
    m_this._lastClick = evt.time;
    if (split) {
      var index = handle.handle.index,
          curPts = m_this._coordinates(),
          pts = curPts.slice(index + 1).concat(curPts.slice(0, index + 1));
      m_this._coordinates(pts);
      m_this.options('style').closed = false;
      handle.handle.index = undefined;
      return true;
    }
  };
};
inherit(lineAnnotation, annotation);

var lineRequiredFeatures = {};
lineRequiredFeatures[lineFeature.capabilities.basic] = [annotationState.create];
registerAnnotation('line', lineAnnotation, lineRequiredFeatures);

/**
 * Point annotation specification.  Extends {@link geo.annotation.spec}.
 *
 * @typedef {object} geo.pointAnnotation.spec
 * @extends geo.annotation.spec
 * @property {geo.geoPosition} [position] A coordinate in map gcs coordinates.
 * @property {geo.geoPosition[]} [coordinates] An array with one coordinate to
 *    use in place of `position`.
 * @property {geo.pointFeature.styleSpec} [style] The style to apply to a
 *    finished point.  This uses styles for {@link geo.pointFeature}.
 * @property {boolean|number} [style.scaled=false] If `false`, the point is not
 *    scaled with zoom level.  If `true`, the radius is based on the zoom level
 *    at first instantiation.  If a number, the radius is used at the `scaled`
 *    zoom level.
 * @property {geo.pointFeature.styleSpec} [editStyle] The style to apply to a
 *    point in edit mode.
 */

/**
 * Point annotation class.
 *
 * @class
 * @alias geo.pointAnnotation
 * @extends geo.annotation
 *
 * @param {geo.pointAnnotation.spec?} [args] Options for the annotation.
 */
var pointAnnotation = function pointAnnotation(args) {
  'use strict';

  if (!(this instanceof pointAnnotation)) {
    return new pointAnnotation(args);
  }

  args = $.extend(true, {}, {
    style: {
      fill: true,
      fillColor: { r: 0, g: 1, b: 0 },
      fillOpacity: 0.25,
      radius: 10,
      scaled: false,
      stroke: true,
      strokeColor: { r: 0, g: 0, b: 0 },
      strokeOpacity: 1,
      strokeWidth: 3
    },
    createStyle: {
      fillColor: { r: 0.3, g: 0.3, b: 0.3 },
      fillOpacity: 0.25,
      strokeColor: { r: 0, g: 0, b: 1 }
    },
    highlightStyle: {
      fillColor: { r: 0, g: 1, b: 1 },
      fillOpacity: 0.5,
      strokeWidth: 5
    }
  }, args || {});
  args.position = args.position || (args.coordinates ? args.coordinates[0] : undefined);
  delete args.coordinates;
  annotation.call(this, 'point', args);

  var m_this = this;

  /**
   * Get a list of renderable features for this annotation.
   *
   * @returns {array} An array of features.
   */
  this.features = function () {
    var opt = m_this.options(),
        state = m_this.state(),
        features,
        style,
        scaleOnZoom;
    switch (state) {
      case annotationState.create:
        features = [];
        break;
      default:
        style = m_this.styleForState(state);
        if (opt.style.scaled || opt.style.scaled === 0) {
          if (opt.style.scaled === true) {
            opt.style.scaled = m_this.layer().map().zoom();
          }
          style = $.extend({}, style, {
            radius: function radius() {
              var radius = opt.style.radius,
                  zoom = m_this.layer().map().zoom();
              if (util.isFunction(radius)) {
                radius = radius.apply(m_this, arguments);
              }
              radius *= Math.pow(2, zoom - opt.style.scaled);
              return radius;
            }
          });
          scaleOnZoom = true;
        }
        features = [{
          point: {
            x: opt.position.x,
            y: opt.position.y,
            style: style,
            scaleOnZoom: scaleOnZoom
          }
        }];
        if (state === annotationState.edit) {
          m_this._addEditHandles(features, [opt.position], { edge: false, center: false, resize: false, rotate: false });
        }
        break;
    }
    return features;
  };

  /**
   * Get and optionally set coordinates associated with this annotation in the
   * map gcs coordinate system.
   *
   * @param {geo.geoPosition[]} [coordinates] An optional array of coordinates
   *  to set.
   * @returns {geo.geoPosition[]} The current array of coordinates.
   */
  this._coordinates = function (coordinates) {
    if (coordinates && coordinates.length >= 1) {
      m_this.options('position', coordinates[0]);
    }
    if (m_this.state() === annotationState.create) {
      return [];
    }
    return [m_this.options('position')];
  };

  this._coordinateOption = 'position';

  /**
   * Handle a mouse click on this annotation.  If the event is processed,
   * evt.handled should be set to `true` to prevent further processing.
   *
   * @param {geo.event} evt The mouse click event.
   * @returns {boolean|string} `true` to update the annotation, `'done'` if
   *    the annotation was completed (changed from create to done state),
   *    `'remove'` if the annotation should be removed, falsy to not update
   *    anything.
   */
  this.mouseClick = function (evt) {
    if (m_this.state() !== annotationState.create) {
      return;
    }
    if (!evt.buttonsDown.left) {
      return;
    }
    evt.handled = true;
    m_this.options('position', evt.mapgcs);
    m_this.state(annotationState.done);
    return 'done';
  };

  /**
   * Return a list of styles that should be preserved in a geojson
   * representation of the annotation.
   *
   * @returns {string[]} A list of style names to store.
   */
  this._geojsonStyles = function () {
    return ['fill', 'fillColor', 'fillOpacity', 'radius', 'scaled', 'stroke', 'strokeColor', 'strokeOpacity', 'strokeWidth'];
  };

  /**
   * Return the coordinates to be stored in a geojson geometry object.
   *
   * @param {string|geo.transform|null} [gcs] `undefined` to use the interface
   *    gcs, `null` to use the map gcs, or any other transform.
   * @returns {array} An array of flattened coordinates in the interface gcs
   *    coordinate system.  `undefined` if this annotation is incomplete.
   */
  this._geojsonCoordinates = function (gcs) {
    var src = m_this.coordinates(gcs);
    if (!src || m_this.state() === annotationState.create || src.length < 1 || src[0] === undefined) {
      return;
    }
    return [src[0].x, src[0].y];
  };

  /**
   * Return the geometry type that is used to store this annotation in geojson.
   *
   * @returns {string} A geojson geometry type.
   */
  this._geojsonGeometryType = function () {
    return 'Point';
  };
};
inherit(pointAnnotation, annotation);

var pointRequiredFeatures = {};
pointRequiredFeatures[pointFeature.capabilities.feature] = true;
registerAnnotation('point', pointAnnotation, pointRequiredFeatures);

module.exports = {
  state: annotationState,
  actionOwner: annotationActionOwner,
  annotation: annotation,
  lineAnnotation: lineAnnotation,
  pointAnnotation: pointAnnotation,
  polygonAnnotation: polygonAnnotation,
  rectangleAnnotation: rectangleAnnotation,
  _editHandleFeatureLevel: editHandleFeatureLevel,
  defaultEditHandleStyle: defaultEditHandleStyle
};

/***/ }),
/* 58 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__core__ = __webpack_require__(137);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__Proj__ = __webpack_require__(40);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__Point__ = __webpack_require__(154);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__common_toPoint__ = __webpack_require__(65);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__defs__ = __webpack_require__(59);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__transform__ = __webpack_require__(64);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_6_mgrs__ = __webpack_require__(66);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_7__version__ = __webpack_require__(155);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_8__projs__ = __webpack_require__(157);










__WEBPACK_IMPORTED_MODULE_0__core__["a" /* default */].defaultDatum = 'WGS84'; //default datum
__WEBPACK_IMPORTED_MODULE_0__core__["a" /* default */].Proj = __WEBPACK_IMPORTED_MODULE_1__Proj__["a" /* default */];
__WEBPACK_IMPORTED_MODULE_0__core__["a" /* default */].WGS84 = new __WEBPACK_IMPORTED_MODULE_0__core__["a" /* default */].Proj('WGS84');
__WEBPACK_IMPORTED_MODULE_0__core__["a" /* default */].Point = __WEBPACK_IMPORTED_MODULE_2__Point__["a" /* default */];
__WEBPACK_IMPORTED_MODULE_0__core__["a" /* default */].toPoint = __WEBPACK_IMPORTED_MODULE_3__common_toPoint__["a" /* default */];
__WEBPACK_IMPORTED_MODULE_0__core__["a" /* default */].defs = __WEBPACK_IMPORTED_MODULE_4__defs__["a" /* default */];
__WEBPACK_IMPORTED_MODULE_0__core__["a" /* default */].transform = __WEBPACK_IMPORTED_MODULE_5__transform__["a" /* default */];
__WEBPACK_IMPORTED_MODULE_0__core__["a" /* default */].mgrs = __WEBPACK_IMPORTED_MODULE_6_mgrs__["a" /* default */];
__WEBPACK_IMPORTED_MODULE_0__core__["a" /* default */].version = __WEBPACK_IMPORTED_MODULE_7__version__["a" /* default */];
Object(__WEBPACK_IMPORTED_MODULE_8__projs__["a" /* default */])(__WEBPACK_IMPORTED_MODULE_0__core__["a" /* default */]);
/* harmony default export */ __webpack_exports__["default"] = (__WEBPACK_IMPORTED_MODULE_0__core__["a" /* default */]);


/***/ }),
/* 59 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__global__ = __webpack_require__(139);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__projString__ = __webpack_require__(60);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2_wkt_parser__ = __webpack_require__(61);




function defs(name) {
  /*global console*/
  var that = this;
  if (arguments.length === 2) {
    var def = arguments[1];
    if (typeof def === 'string') {
      if (def.charAt(0) === '+') {
        defs[name] = Object(__WEBPACK_IMPORTED_MODULE_1__projString__["a" /* default */])(arguments[1]);
      }
      else {
        defs[name] = Object(__WEBPACK_IMPORTED_MODULE_2_wkt_parser__["a" /* default */])(arguments[1]);
      }
    } else {
      defs[name] = def;
    }
  }
  else if (arguments.length === 1) {
    if (Array.isArray(name)) {
      return name.map(function(v) {
        if (Array.isArray(v)) {
          defs.apply(that, v);
        }
        else {
          defs(v);
        }
      });
    }
    else if (typeof name === 'string') {
      if (name in defs) {
        return defs[name];
      }
    }
    else if ('EPSG' in name) {
      defs['EPSG:' + name.EPSG] = name;
    }
    else if ('ESRI' in name) {
      defs['ESRI:' + name.ESRI] = name;
    }
    else if ('IAU2000' in name) {
      defs['IAU2000:' + name.IAU2000] = name;
    }
    else {
      console.log(name);
    }
    return;
  }


}
Object(__WEBPACK_IMPORTED_MODULE_0__global__["a" /* default */])(defs);
/* harmony default export */ __webpack_exports__["a"] = (defs);


/***/ }),
/* 60 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__constants_values__ = __webpack_require__(4);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__constants_PrimeMeridian__ = __webpack_require__(140);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__constants_units__ = __webpack_require__(141);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__match__ = __webpack_require__(26);





/* harmony default export */ __webpack_exports__["a"] = (function(defData) {
  var self = {};
  var paramObj = defData.split('+').map(function(v) {
    return v.trim();
  }).filter(function(a) {
    return a;
  }).reduce(function(p, a) {
    var split = a.split('=');
    split.push(true);
    p[split[0].toLowerCase()] = split[1];
    return p;
  }, {});
  var paramName, paramVal, paramOutname;
  var params = {
    proj: 'projName',
    datum: 'datumCode',
    rf: function(v) {
      self.rf = parseFloat(v);
    },
    lat_0: function(v) {
      self.lat0 = v * __WEBPACK_IMPORTED_MODULE_0__constants_values__["a" /* D2R */];
    },
    lat_1: function(v) {
      self.lat1 = v * __WEBPACK_IMPORTED_MODULE_0__constants_values__["a" /* D2R */];
    },
    lat_2: function(v) {
      self.lat2 = v * __WEBPACK_IMPORTED_MODULE_0__constants_values__["a" /* D2R */];
    },
    lat_ts: function(v) {
      self.lat_ts = v * __WEBPACK_IMPORTED_MODULE_0__constants_values__["a" /* D2R */];
    },
    lon_0: function(v) {
      self.long0 = v * __WEBPACK_IMPORTED_MODULE_0__constants_values__["a" /* D2R */];
    },
    lon_1: function(v) {
      self.long1 = v * __WEBPACK_IMPORTED_MODULE_0__constants_values__["a" /* D2R */];
    },
    lon_2: function(v) {
      self.long2 = v * __WEBPACK_IMPORTED_MODULE_0__constants_values__["a" /* D2R */];
    },
    alpha: function(v) {
      self.alpha = parseFloat(v) * __WEBPACK_IMPORTED_MODULE_0__constants_values__["a" /* D2R */];
    },
    lonc: function(v) {
      self.longc = v * __WEBPACK_IMPORTED_MODULE_0__constants_values__["a" /* D2R */];
    },
    x_0: function(v) {
      self.x0 = parseFloat(v);
    },
    y_0: function(v) {
      self.y0 = parseFloat(v);
    },
    k_0: function(v) {
      self.k0 = parseFloat(v);
    },
    k: function(v) {
      self.k0 = parseFloat(v);
    },
    a: function(v) {
      self.a = parseFloat(v);
    },
    b: function(v) {
      self.b = parseFloat(v);
    },
    r_a: function() {
      self.R_A = true;
    },
    zone: function(v) {
      self.zone = parseInt(v, 10);
    },
    south: function() {
      self.utmSouth = true;
    },
    towgs84: function(v) {
      self.datum_params = v.split(",").map(function(a) {
        return parseFloat(a);
      });
    },
    to_meter: function(v) {
      self.to_meter = parseFloat(v);
    },
    units: function(v) {
      self.units = v;
      var unit = Object(__WEBPACK_IMPORTED_MODULE_3__match__["a" /* default */])(__WEBPACK_IMPORTED_MODULE_2__constants_units__["a" /* default */], v);
      if (unit) {
        self.to_meter = unit.to_meter;
      }
    },
    from_greenwich: function(v) {
      self.from_greenwich = v * __WEBPACK_IMPORTED_MODULE_0__constants_values__["a" /* D2R */];
    },
    pm: function(v) {
      var pm = Object(__WEBPACK_IMPORTED_MODULE_3__match__["a" /* default */])(__WEBPACK_IMPORTED_MODULE_1__constants_PrimeMeridian__["a" /* default */], v);
      self.from_greenwich = (pm ? pm : parseFloat(v)) * __WEBPACK_IMPORTED_MODULE_0__constants_values__["a" /* D2R */];
    },
    nadgrids: function(v) {
      if (v === '@null') {
        self.datumCode = 'none';
      }
      else {
        self.nadgrids = v;
      }
    },
    axis: function(v) {
      var legalAxis = "ewnsud";
      if (v.length === 3 && legalAxis.indexOf(v.substr(0, 1)) !== -1 && legalAxis.indexOf(v.substr(1, 1)) !== -1 && legalAxis.indexOf(v.substr(2, 1)) !== -1) {
        self.axis = v;
      }
    }
  };
  for (paramName in paramObj) {
    paramVal = paramObj[paramName];
    if (paramName in params) {
      paramOutname = params[paramName];
      if (typeof paramOutname === 'function') {
        paramOutname(paramVal);
      }
      else {
        self[paramOutname] = paramVal;
      }
    }
    else {
      self[paramName] = paramVal;
    }
  }
  if(typeof self.datumCode === 'string' && self.datumCode !== "WGS84"){
    self.datumCode = self.datumCode.toLowerCase();
  }
  return self;
});


/***/ }),
/* 61 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__parser__ = __webpack_require__(142);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__process__ = __webpack_require__(143);
var D2R = 0.01745329251994329577;





function rename(obj, params) {
  var outName = params[0];
  var inName = params[1];
  if (!(outName in obj) && (inName in obj)) {
    obj[outName] = obj[inName];
    if (params.length === 3) {
      obj[outName] = params[2](obj[outName]);
    }
  }
}

function d2r(input) {
  return input * D2R;
}

function cleanWKT(wkt) {
  if (wkt.type === 'GEOGCS') {
    wkt.projName = 'longlat';
  } else if (wkt.type === 'LOCAL_CS') {
    wkt.projName = 'identity';
    wkt.local = true;
  } else {
    if (typeof wkt.PROJECTION === 'object') {
      wkt.projName = Object.keys(wkt.PROJECTION)[0];
    } else {
      wkt.projName = wkt.PROJECTION;
    }
  }
  if (wkt.UNIT) {
    wkt.units = wkt.UNIT.name.toLowerCase();
    if (wkt.units === 'metre') {
      wkt.units = 'meter';
    }
    if (wkt.UNIT.convert) {
      if (wkt.type === 'GEOGCS') {
        if (wkt.DATUM && wkt.DATUM.SPHEROID) {
          wkt.to_meter = wkt.UNIT.convert*wkt.DATUM.SPHEROID.a;
        }
      } else {
        wkt.to_meter = wkt.UNIT.convert, 10;
      }
    }
  }
  var geogcs = wkt.GEOGCS;
  if (wkt.type === 'GEOGCS') {
    geogcs = wkt;
  }
  if (geogcs) {
    //if(wkt.GEOGCS.PRIMEM&&wkt.GEOGCS.PRIMEM.convert){
    //  wkt.from_greenwich=wkt.GEOGCS.PRIMEM.convert*D2R;
    //}
    if (geogcs.DATUM) {
      wkt.datumCode = geogcs.DATUM.name.toLowerCase();
    } else {
      wkt.datumCode = geogcs.name.toLowerCase();
    }
    if (wkt.datumCode.slice(0, 2) === 'd_') {
      wkt.datumCode = wkt.datumCode.slice(2);
    }
    if (wkt.datumCode === 'new_zealand_geodetic_datum_1949' || wkt.datumCode === 'new_zealand_1949') {
      wkt.datumCode = 'nzgd49';
    }
    if (wkt.datumCode === 'wgs_1984') {
      if (wkt.PROJECTION === 'Mercator_Auxiliary_Sphere') {
        wkt.sphere = true;
      }
      wkt.datumCode = 'wgs84';
    }
    if (wkt.datumCode.slice(-6) === '_ferro') {
      wkt.datumCode = wkt.datumCode.slice(0, - 6);
    }
    if (wkt.datumCode.slice(-8) === '_jakarta') {
      wkt.datumCode = wkt.datumCode.slice(0, - 8);
    }
    if (~wkt.datumCode.indexOf('belge')) {
      wkt.datumCode = 'rnb72';
    }
    if (geogcs.DATUM && geogcs.DATUM.SPHEROID) {
      wkt.ellps = geogcs.DATUM.SPHEROID.name.replace('_19', '').replace(/[Cc]larke\_18/, 'clrk');
      if (wkt.ellps.toLowerCase().slice(0, 13) === 'international') {
        wkt.ellps = 'intl';
      }

      wkt.a = geogcs.DATUM.SPHEROID.a;
      wkt.rf = parseFloat(geogcs.DATUM.SPHEROID.rf, 10);
    }

    if (geogcs.DATUM && geogcs.DATUM.TOWGS84) {
      wkt.datum_params = geogcs.DATUM.TOWGS84;
    }
    if (~wkt.datumCode.indexOf('osgb_1936')) {
      wkt.datumCode = 'osgb36';
    }
    if (~wkt.datumCode.indexOf('osni_1952')) {
      wkt.datumCode = 'osni52';
    }
    if (~wkt.datumCode.indexOf('tm65')
      || ~wkt.datumCode.indexOf('geodetic_datum_of_1965')) {
      wkt.datumCode = 'ire65';
    }
    if (wkt.datumCode === 'ch1903+') {
      wkt.datumCode = 'ch1903';
    }
    if (~wkt.datumCode.indexOf('israel')) {
      wkt.datumCode = 'isr93';
    }
  }
  if (wkt.b && !isFinite(wkt.b)) {
    wkt.b = wkt.a;
  }

  function toMeter(input) {
    var ratio = wkt.to_meter || 1;
    return input * ratio;
  }
  var renamer = function(a) {
    return rename(wkt, a);
  };
  var list = [
    ['standard_parallel_1', 'Standard_Parallel_1'],
    ['standard_parallel_2', 'Standard_Parallel_2'],
    ['false_easting', 'False_Easting'],
    ['false_northing', 'False_Northing'],
    ['central_meridian', 'Central_Meridian'],
    ['latitude_of_origin', 'Latitude_Of_Origin'],
    ['latitude_of_origin', 'Central_Parallel'],
    ['scale_factor', 'Scale_Factor'],
    ['k0', 'scale_factor'],
    ['latitude_of_center', 'Latitude_Of_Center'],
    ['latitude_of_center', 'Latitude_of_center'],
    ['lat0', 'latitude_of_center', d2r],
    ['longitude_of_center', 'Longitude_Of_Center'],
    ['longitude_of_center', 'Longitude_of_center'],
    ['longc', 'longitude_of_center', d2r],
    ['x0', 'false_easting', toMeter],
    ['y0', 'false_northing', toMeter],
    ['long0', 'central_meridian', d2r],
    ['lat0', 'latitude_of_origin', d2r],
    ['lat0', 'standard_parallel_1', d2r],
    ['lat1', 'standard_parallel_1', d2r],
    ['lat2', 'standard_parallel_2', d2r],
    ['azimuth', 'Azimuth'],
    ['alpha', 'azimuth', d2r],
    ['srsCode', 'name']
  ];
  list.forEach(renamer);
  if (!wkt.long0 && wkt.longc && (wkt.projName === 'Albers_Conic_Equal_Area' || wkt.projName === 'Lambert_Azimuthal_Equal_Area')) {
    wkt.long0 = wkt.longc;
  }
  if (!wkt.lat_ts && wkt.lat1 && (wkt.projName === 'Stereographic_South_Pole' || wkt.projName === 'Polar Stereographic (variant B)')) {
    wkt.lat0 = d2r(wkt.lat1 > 0 ? 90 : -90);
    wkt.lat_ts = wkt.lat1;
  }
}
/* harmony default export */ __webpack_exports__["a"] = (function(wkt) {
  var lisp = Object(__WEBPACK_IMPORTED_MODULE_0__parser__["a" /* default */])(wkt);
  var type = lisp.shift();
  var name = lisp.shift();
  lisp.unshift(['name', name]);
  lisp.unshift(['type', type]);
  var obj = {};
  Object(__WEBPACK_IMPORTED_MODULE_1__process__["a" /* sExpr */])(lisp, obj);
  cleanWKT(obj);
  return obj;
});


/***/ }),
/* 62 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony export (immutable) */ __webpack_exports__["init"] = init;
/* harmony export (immutable) */ __webpack_exports__["forward"] = forward;
/* harmony export (immutable) */ __webpack_exports__["inverse"] = inverse;
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "names", function() { return names; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__common_msfnz__ = __webpack_require__(15);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__common_adjust_lon__ = __webpack_require__(5);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__common_tsfnz__ = __webpack_require__(27);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__common_phi2z__ = __webpack_require__(28);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__constants_values__ = __webpack_require__(4);






function init() {
  var con = this.b / this.a;
  this.es = 1 - con * con;
  if(!('x0' in this)){
    this.x0 = 0;
  }
  if(!('y0' in this)){
    this.y0 = 0;
  }
  this.e = Math.sqrt(this.es);
  if (this.lat_ts) {
    if (this.sphere) {
      this.k0 = Math.cos(this.lat_ts);
    }
    else {
      this.k0 = Object(__WEBPACK_IMPORTED_MODULE_0__common_msfnz__["a" /* default */])(this.e, Math.sin(this.lat_ts), Math.cos(this.lat_ts));
    }
  }
  else {
    if (!this.k0) {
      if (this.k) {
        this.k0 = this.k;
      }
      else {
        this.k0 = 1;
      }
    }
  }
}

/* Mercator forward equations--mapping lat,long to x,y
  --------------------------------------------------*/

function forward(p) {
  var lon = p.x;
  var lat = p.y;
  // convert to radians
  if (lat * __WEBPACK_IMPORTED_MODULE_4__constants_values__["i" /* R2D */] > 90 && lat * __WEBPACK_IMPORTED_MODULE_4__constants_values__["i" /* R2D */] < -90 && lon * __WEBPACK_IMPORTED_MODULE_4__constants_values__["i" /* R2D */] > 180 && lon * __WEBPACK_IMPORTED_MODULE_4__constants_values__["i" /* R2D */] < -180) {
    return null;
  }

  var x, y;
  if (Math.abs(Math.abs(lat) - __WEBPACK_IMPORTED_MODULE_4__constants_values__["d" /* HALF_PI */]) <= __WEBPACK_IMPORTED_MODULE_4__constants_values__["b" /* EPSLN */]) {
    return null;
  }
  else {
    if (this.sphere) {
      x = this.x0 + this.a * this.k0 * Object(__WEBPACK_IMPORTED_MODULE_1__common_adjust_lon__["a" /* default */])(lon - this.long0);
      y = this.y0 + this.a * this.k0 * Math.log(Math.tan(__WEBPACK_IMPORTED_MODULE_4__constants_values__["c" /* FORTPI */] + 0.5 * lat));
    }
    else {
      var sinphi = Math.sin(lat);
      var ts = Object(__WEBPACK_IMPORTED_MODULE_2__common_tsfnz__["a" /* default */])(this.e, lat, sinphi);
      x = this.x0 + this.a * this.k0 * Object(__WEBPACK_IMPORTED_MODULE_1__common_adjust_lon__["a" /* default */])(lon - this.long0);
      y = this.y0 - this.a * this.k0 * Math.log(ts);
    }
    p.x = x;
    p.y = y;
    return p;
  }
}

/* Mercator inverse equations--mapping x,y to lat/long
  --------------------------------------------------*/
function inverse(p) {

  var x = p.x - this.x0;
  var y = p.y - this.y0;
  var lon, lat;

  if (this.sphere) {
    lat = __WEBPACK_IMPORTED_MODULE_4__constants_values__["d" /* HALF_PI */] - 2 * Math.atan(Math.exp(-y / (this.a * this.k0)));
  }
  else {
    var ts = Math.exp(-y / (this.a * this.k0));
    lat = Object(__WEBPACK_IMPORTED_MODULE_3__common_phi2z__["a" /* default */])(this.e, ts);
    if (lat === -9999) {
      return null;
    }
  }
  lon = Object(__WEBPACK_IMPORTED_MODULE_1__common_adjust_lon__["a" /* default */])(this.long0 + x / (this.a * this.k0));

  p.x = lon;
  p.y = lat;
  return p;
}

var names = ["Mercator", "Popular Visualisation Pseudo Mercator", "Mercator_1SP", "Mercator_Auxiliary_Sphere", "merc"];
/* harmony default export */ __webpack_exports__["default"] = ({
  init: init,
  forward: forward,
  inverse: inverse,
  names: names
});


/***/ }),
/* 63 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony export (immutable) */ __webpack_exports__["init"] = init;
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "forward", function() { return identity; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "inverse", function() { return identity; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "names", function() { return names; });
function init() {
  //no-op for longlat
}

function identity(pt) {
  return pt;
}


var names = ["longlat", "identity"];
/* harmony default export */ __webpack_exports__["default"] = ({
  init: init,
  forward: identity,
  inverse: identity,
  names: names
});


/***/ }),
/* 64 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony export (immutable) */ __webpack_exports__["a"] = transform;
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__constants_values__ = __webpack_require__(4);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__datum_transform__ = __webpack_require__(150);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__adjust_axis__ = __webpack_require__(152);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__Proj__ = __webpack_require__(40);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__common_toPoint__ = __webpack_require__(65);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__checkSanity__ = __webpack_require__(153);







function checkNotWGS(source, dest) {
  return ((source.datum.datum_type === __WEBPACK_IMPORTED_MODULE_0__constants_values__["e" /* PJD_3PARAM */] || source.datum.datum_type === __WEBPACK_IMPORTED_MODULE_0__constants_values__["f" /* PJD_7PARAM */]) && dest.datumCode !== 'WGS84') || ((dest.datum.datum_type === __WEBPACK_IMPORTED_MODULE_0__constants_values__["e" /* PJD_3PARAM */] || dest.datum.datum_type === __WEBPACK_IMPORTED_MODULE_0__constants_values__["f" /* PJD_7PARAM */]) && source.datumCode !== 'WGS84');
}

function transform(source, dest, point) {
  var wgs84;
  if (Array.isArray(point)) {
    point = Object(__WEBPACK_IMPORTED_MODULE_4__common_toPoint__["a" /* default */])(point);
  }
  Object(__WEBPACK_IMPORTED_MODULE_5__checkSanity__["a" /* default */])(point);
  // Workaround for datum shifts towgs84, if either source or destination projection is not wgs84
  if (source.datum && dest.datum && checkNotWGS(source, dest)) {
    wgs84 = new __WEBPACK_IMPORTED_MODULE_3__Proj__["a" /* default */]('WGS84');
    point = transform(source, wgs84, point);
    source = wgs84;
  }
  // DGR, 2010/11/12
  if (source.axis !== 'enu') {
    point = Object(__WEBPACK_IMPORTED_MODULE_2__adjust_axis__["a" /* default */])(source, false, point);
  }
  // Transform source points to long/lat, if they aren't already.
  if (source.projName === 'longlat') {
    point = {
      x: point.x * __WEBPACK_IMPORTED_MODULE_0__constants_values__["a" /* D2R */],
      y: point.y * __WEBPACK_IMPORTED_MODULE_0__constants_values__["a" /* D2R */]
    };
  }
  else {
    if (source.to_meter) {
      point = {
        x: point.x * source.to_meter,
        y: point.y * source.to_meter
      };
    }
    point = source.inverse(point); // Convert Cartesian to longlat
  }
  // Adjust for the prime meridian if necessary
  if (source.from_greenwich) {
    point.x += source.from_greenwich;
  }

  // Convert datums if needed, and if possible.
  point = Object(__WEBPACK_IMPORTED_MODULE_1__datum_transform__["a" /* default */])(source.datum, dest.datum, point);

  // Adjust for the prime meridian if necessary
  if (dest.from_greenwich) {
    point = {
      x: point.x - dest.from_greenwich,
      y: point.y
    };
  }

  if (dest.projName === 'longlat') {
    // convert radians to decimal degrees
    point = {
      x: point.x * __WEBPACK_IMPORTED_MODULE_0__constants_values__["i" /* R2D */],
      y: point.y * __WEBPACK_IMPORTED_MODULE_0__constants_values__["i" /* R2D */]
    };
  } else { // else project
    point = dest.forward(point);
    if (dest.to_meter) {
      point = {
        x: point.x / dest.to_meter,
        y: point.y / dest.to_meter
      };
    }
  }

  // DGR, 2010/11/12
  if (dest.axis !== 'enu') {
    return Object(__WEBPACK_IMPORTED_MODULE_2__adjust_axis__["a" /* default */])(dest, true, point);
  }

  return point;
}


/***/ }),
/* 65 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony default export */ __webpack_exports__["a"] = (function (array){
  var out = {
    x: array[0],
    y: array[1]
  };
  if (array.length>2) {
    out.z = array[2];
  }
  if (array.length>3) {
    out.m = array[3];
  }
  return out;
});

/***/ }),
/* 66 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony export (immutable) */ __webpack_exports__["b"] = forward;
/* unused harmony export inverse */
/* harmony export (immutable) */ __webpack_exports__["c"] = toPoint;



/**
 * UTM zones are grouped, and assigned to one of a group of 6
 * sets.
 *
 * {int} @private
 */
var NUM_100K_SETS = 6;

/**
 * The column letters (for easting) of the lower left value, per
 * set.
 *
 * {string} @private
 */
var SET_ORIGIN_COLUMN_LETTERS = 'AJSAJS';

/**
 * The row letters (for northing) of the lower left value, per
 * set.
 *
 * {string} @private
 */
var SET_ORIGIN_ROW_LETTERS = 'AFAFAF';

var A = 65; // A
var I = 73; // I
var O = 79; // O
var V = 86; // V
var Z = 90; // Z
/* harmony default export */ __webpack_exports__["a"] = ({
  forward: forward,
  inverse: inverse,
  toPoint: toPoint
});
/**
 * Conversion of lat/lon to MGRS.
 *
 * @param {object} ll Object literal with lat and lon properties on a
 *     WGS84 ellipsoid.
 * @param {int} accuracy Accuracy in digits (5 for 1 m, 4 for 10 m, 3 for
 *      100 m, 2 for 1000 m or 1 for 10000 m). Optional, default is 5.
 * @return {string} the MGRS string for the given location and accuracy.
 */
function forward(ll, accuracy) {
  accuracy = accuracy || 5; // default accuracy 1m
  return encode(LLtoUTM({
    lat: ll[1],
    lon: ll[0]
  }), accuracy);
};

/**
 * Conversion of MGRS to lat/lon.
 *
 * @param {string} mgrs MGRS string.
 * @return {array} An array with left (longitude), bottom (latitude), right
 *     (longitude) and top (latitude) values in WGS84, representing the
 *     bounding box for the provided MGRS reference.
 */
function inverse(mgrs) {
  var bbox = UTMtoLL(decode(mgrs.toUpperCase()));
  if (bbox.lat && bbox.lon) {
    return [bbox.lon, bbox.lat, bbox.lon, bbox.lat];
  }
  return [bbox.left, bbox.bottom, bbox.right, bbox.top];
};

function toPoint(mgrs) {
  var bbox = UTMtoLL(decode(mgrs.toUpperCase()));
  if (bbox.lat && bbox.lon) {
    return [bbox.lon, bbox.lat];
  }
  return [(bbox.left + bbox.right) / 2, (bbox.top + bbox.bottom) / 2];
};
/**
 * Conversion from degrees to radians.
 *
 * @private
 * @param {number} deg the angle in degrees.
 * @return {number} the angle in radians.
 */
function degToRad(deg) {
  return (deg * (Math.PI / 180.0));
}

/**
 * Conversion from radians to degrees.
 *
 * @private
 * @param {number} rad the angle in radians.
 * @return {number} the angle in degrees.
 */
function radToDeg(rad) {
  return (180.0 * (rad / Math.PI));
}

/**
 * Converts a set of Longitude and Latitude co-ordinates to UTM
 * using the WGS84 ellipsoid.
 *
 * @private
 * @param {object} ll Object literal with lat and lon properties
 *     representing the WGS84 coordinate to be converted.
 * @return {object} Object literal containing the UTM value with easting,
 *     northing, zoneNumber and zoneLetter properties, and an optional
 *     accuracy property in digits. Returns null if the conversion failed.
 */
function LLtoUTM(ll) {
  var Lat = ll.lat;
  var Long = ll.lon;
  var a = 6378137.0; //ellip.radius;
  var eccSquared = 0.00669438; //ellip.eccsq;
  var k0 = 0.9996;
  var LongOrigin;
  var eccPrimeSquared;
  var N, T, C, A, M;
  var LatRad = degToRad(Lat);
  var LongRad = degToRad(Long);
  var LongOriginRad;
  var ZoneNumber;
  // (int)
  ZoneNumber = Math.floor((Long + 180) / 6) + 1;

  //Make sure the longitude 180.00 is in Zone 60
  if (Long === 180) {
    ZoneNumber = 60;
  }

  // Special zone for Norway
  if (Lat >= 56.0 && Lat < 64.0 && Long >= 3.0 && Long < 12.0) {
    ZoneNumber = 32;
  }

  // Special zones for Svalbard
  if (Lat >= 72.0 && Lat < 84.0) {
    if (Long >= 0.0 && Long < 9.0) {
      ZoneNumber = 31;
    }
    else if (Long >= 9.0 && Long < 21.0) {
      ZoneNumber = 33;
    }
    else if (Long >= 21.0 && Long < 33.0) {
      ZoneNumber = 35;
    }
    else if (Long >= 33.0 && Long < 42.0) {
      ZoneNumber = 37;
    }
  }

  LongOrigin = (ZoneNumber - 1) * 6 - 180 + 3; //+3 puts origin
  // in middle of
  // zone
  LongOriginRad = degToRad(LongOrigin);

  eccPrimeSquared = (eccSquared) / (1 - eccSquared);

  N = a / Math.sqrt(1 - eccSquared * Math.sin(LatRad) * Math.sin(LatRad));
  T = Math.tan(LatRad) * Math.tan(LatRad);
  C = eccPrimeSquared * Math.cos(LatRad) * Math.cos(LatRad);
  A = Math.cos(LatRad) * (LongRad - LongOriginRad);

  M = a * ((1 - eccSquared / 4 - 3 * eccSquared * eccSquared / 64 - 5 * eccSquared * eccSquared * eccSquared / 256) * LatRad - (3 * eccSquared / 8 + 3 * eccSquared * eccSquared / 32 + 45 * eccSquared * eccSquared * eccSquared / 1024) * Math.sin(2 * LatRad) + (15 * eccSquared * eccSquared / 256 + 45 * eccSquared * eccSquared * eccSquared / 1024) * Math.sin(4 * LatRad) - (35 * eccSquared * eccSquared * eccSquared / 3072) * Math.sin(6 * LatRad));

  var UTMEasting = (k0 * N * (A + (1 - T + C) * A * A * A / 6.0 + (5 - 18 * T + T * T + 72 * C - 58 * eccPrimeSquared) * A * A * A * A * A / 120.0) + 500000.0);

  var UTMNorthing = (k0 * (M + N * Math.tan(LatRad) * (A * A / 2 + (5 - T + 9 * C + 4 * C * C) * A * A * A * A / 24.0 + (61 - 58 * T + T * T + 600 * C - 330 * eccPrimeSquared) * A * A * A * A * A * A / 720.0)));
  if (Lat < 0.0) {
    UTMNorthing += 10000000.0; //10000000 meter offset for
    // southern hemisphere
  }

  return {
    northing: Math.round(UTMNorthing),
    easting: Math.round(UTMEasting),
    zoneNumber: ZoneNumber,
    zoneLetter: getLetterDesignator(Lat)
  };
}

/**
 * Converts UTM coords to lat/long, using the WGS84 ellipsoid. This is a convenience
 * class where the Zone can be specified as a single string eg."60N" which
 * is then broken down into the ZoneNumber and ZoneLetter.
 *
 * @private
 * @param {object} utm An object literal with northing, easting, zoneNumber
 *     and zoneLetter properties. If an optional accuracy property is
 *     provided (in meters), a bounding box will be returned instead of
 *     latitude and longitude.
 * @return {object} An object literal containing either lat and lon values
 *     (if no accuracy was provided), or top, right, bottom and left values
 *     for the bounding box calculated according to the provided accuracy.
 *     Returns null if the conversion failed.
 */
function UTMtoLL(utm) {

  var UTMNorthing = utm.northing;
  var UTMEasting = utm.easting;
  var zoneLetter = utm.zoneLetter;
  var zoneNumber = utm.zoneNumber;
  // check the ZoneNummber is valid
  if (zoneNumber < 0 || zoneNumber > 60) {
    return null;
  }

  var k0 = 0.9996;
  var a = 6378137.0; //ellip.radius;
  var eccSquared = 0.00669438; //ellip.eccsq;
  var eccPrimeSquared;
  var e1 = (1 - Math.sqrt(1 - eccSquared)) / (1 + Math.sqrt(1 - eccSquared));
  var N1, T1, C1, R1, D, M;
  var LongOrigin;
  var mu, phi1Rad;

  // remove 500,000 meter offset for longitude
  var x = UTMEasting - 500000.0;
  var y = UTMNorthing;

  // We must know somehow if we are in the Northern or Southern
  // hemisphere, this is the only time we use the letter So even
  // if the Zone letter isn't exactly correct it should indicate
  // the hemisphere correctly
  if (zoneLetter < 'N') {
    y -= 10000000.0; // remove 10,000,000 meter offset used
    // for southern hemisphere
  }

  // There are 60 zones with zone 1 being at West -180 to -174
  LongOrigin = (zoneNumber - 1) * 6 - 180 + 3; // +3 puts origin
  // in middle of
  // zone

  eccPrimeSquared = (eccSquared) / (1 - eccSquared);

  M = y / k0;
  mu = M / (a * (1 - eccSquared / 4 - 3 * eccSquared * eccSquared / 64 - 5 * eccSquared * eccSquared * eccSquared / 256));

  phi1Rad = mu + (3 * e1 / 2 - 27 * e1 * e1 * e1 / 32) * Math.sin(2 * mu) + (21 * e1 * e1 / 16 - 55 * e1 * e1 * e1 * e1 / 32) * Math.sin(4 * mu) + (151 * e1 * e1 * e1 / 96) * Math.sin(6 * mu);
  // double phi1 = ProjMath.radToDeg(phi1Rad);

  N1 = a / Math.sqrt(1 - eccSquared * Math.sin(phi1Rad) * Math.sin(phi1Rad));
  T1 = Math.tan(phi1Rad) * Math.tan(phi1Rad);
  C1 = eccPrimeSquared * Math.cos(phi1Rad) * Math.cos(phi1Rad);
  R1 = a * (1 - eccSquared) / Math.pow(1 - eccSquared * Math.sin(phi1Rad) * Math.sin(phi1Rad), 1.5);
  D = x / (N1 * k0);

  var lat = phi1Rad - (N1 * Math.tan(phi1Rad) / R1) * (D * D / 2 - (5 + 3 * T1 + 10 * C1 - 4 * C1 * C1 - 9 * eccPrimeSquared) * D * D * D * D / 24 + (61 + 90 * T1 + 298 * C1 + 45 * T1 * T1 - 252 * eccPrimeSquared - 3 * C1 * C1) * D * D * D * D * D * D / 720);
  lat = radToDeg(lat);

  var lon = (D - (1 + 2 * T1 + C1) * D * D * D / 6 + (5 - 2 * C1 + 28 * T1 - 3 * C1 * C1 + 8 * eccPrimeSquared + 24 * T1 * T1) * D * D * D * D * D / 120) / Math.cos(phi1Rad);
  lon = LongOrigin + radToDeg(lon);

  var result;
  if (utm.accuracy) {
    var topRight = UTMtoLL({
      northing: utm.northing + utm.accuracy,
      easting: utm.easting + utm.accuracy,
      zoneLetter: utm.zoneLetter,
      zoneNumber: utm.zoneNumber
    });
    result = {
      top: topRight.lat,
      right: topRight.lon,
      bottom: lat,
      left: lon
    };
  }
  else {
    result = {
      lat: lat,
      lon: lon
    };
  }
  return result;
}

/**
 * Calculates the MGRS letter designator for the given latitude.
 *
 * @private
 * @param {number} lat The latitude in WGS84 to get the letter designator
 *     for.
 * @return {char} The letter designator.
 */
function getLetterDesignator(lat) {
  //This is here as an error flag to show that the Latitude is
  //outside MGRS limits
  var LetterDesignator = 'Z';

  if ((84 >= lat) && (lat >= 72)) {
    LetterDesignator = 'X';
  }
  else if ((72 > lat) && (lat >= 64)) {
    LetterDesignator = 'W';
  }
  else if ((64 > lat) && (lat >= 56)) {
    LetterDesignator = 'V';
  }
  else if ((56 > lat) && (lat >= 48)) {
    LetterDesignator = 'U';
  }
  else if ((48 > lat) && (lat >= 40)) {
    LetterDesignator = 'T';
  }
  else if ((40 > lat) && (lat >= 32)) {
    LetterDesignator = 'S';
  }
  else if ((32 > lat) && (lat >= 24)) {
    LetterDesignator = 'R';
  }
  else if ((24 > lat) && (lat >= 16)) {
    LetterDesignator = 'Q';
  }
  else if ((16 > lat) && (lat >= 8)) {
    LetterDesignator = 'P';
  }
  else if ((8 > lat) && (lat >= 0)) {
    LetterDesignator = 'N';
  }
  else if ((0 > lat) && (lat >= -8)) {
    LetterDesignator = 'M';
  }
  else if ((-8 > lat) && (lat >= -16)) {
    LetterDesignator = 'L';
  }
  else if ((-16 > lat) && (lat >= -24)) {
    LetterDesignator = 'K';
  }
  else if ((-24 > lat) && (lat >= -32)) {
    LetterDesignator = 'J';
  }
  else if ((-32 > lat) && (lat >= -40)) {
    LetterDesignator = 'H';
  }
  else if ((-40 > lat) && (lat >= -48)) {
    LetterDesignator = 'G';
  }
  else if ((-48 > lat) && (lat >= -56)) {
    LetterDesignator = 'F';
  }
  else if ((-56 > lat) && (lat >= -64)) {
    LetterDesignator = 'E';
  }
  else if ((-64 > lat) && (lat >= -72)) {
    LetterDesignator = 'D';
  }
  else if ((-72 > lat) && (lat >= -80)) {
    LetterDesignator = 'C';
  }
  return LetterDesignator;
}

/**
 * Encodes a UTM location as MGRS string.
 *
 * @private
 * @param {object} utm An object literal with easting, northing,
 *     zoneLetter, zoneNumber
 * @param {number} accuracy Accuracy in digits (1-5).
 * @return {string} MGRS string for the given UTM location.
 */
function encode(utm, accuracy) {
  // prepend with leading zeroes
  var seasting = "00000" + utm.easting,
    snorthing = "00000" + utm.northing;

  return utm.zoneNumber + utm.zoneLetter + get100kID(utm.easting, utm.northing, utm.zoneNumber) + seasting.substr(seasting.length - 5, accuracy) + snorthing.substr(snorthing.length - 5, accuracy);
}

/**
 * Get the two letter 100k designator for a given UTM easting,
 * northing and zone number value.
 *
 * @private
 * @param {number} easting
 * @param {number} northing
 * @param {number} zoneNumber
 * @return the two letter 100k designator for the given UTM location.
 */
function get100kID(easting, northing, zoneNumber) {
  var setParm = get100kSetForZone(zoneNumber);
  var setColumn = Math.floor(easting / 100000);
  var setRow = Math.floor(northing / 100000) % 20;
  return getLetter100kID(setColumn, setRow, setParm);
}

/**
 * Given a UTM zone number, figure out the MGRS 100K set it is in.
 *
 * @private
 * @param {number} i An UTM zone number.
 * @return {number} the 100k set the UTM zone is in.
 */
function get100kSetForZone(i) {
  var setParm = i % NUM_100K_SETS;
  if (setParm === 0) {
    setParm = NUM_100K_SETS;
  }

  return setParm;
}

/**
 * Get the two-letter MGRS 100k designator given information
 * translated from the UTM northing, easting and zone number.
 *
 * @private
 * @param {number} column the column index as it relates to the MGRS
 *        100k set spreadsheet, created from the UTM easting.
 *        Values are 1-8.
 * @param {number} row the row index as it relates to the MGRS 100k set
 *        spreadsheet, created from the UTM northing value. Values
 *        are from 0-19.
 * @param {number} parm the set block, as it relates to the MGRS 100k set
 *        spreadsheet, created from the UTM zone. Values are from
 *        1-60.
 * @return two letter MGRS 100k code.
 */
function getLetter100kID(column, row, parm) {
  // colOrigin and rowOrigin are the letters at the origin of the set
  var index = parm - 1;
  var colOrigin = SET_ORIGIN_COLUMN_LETTERS.charCodeAt(index);
  var rowOrigin = SET_ORIGIN_ROW_LETTERS.charCodeAt(index);

  // colInt and rowInt are the letters to build to return
  var colInt = colOrigin + column - 1;
  var rowInt = rowOrigin + row;
  var rollover = false;

  if (colInt > Z) {
    colInt = colInt - Z + A - 1;
    rollover = true;
  }

  if (colInt === I || (colOrigin < I && colInt > I) || ((colInt > I || colOrigin < I) && rollover)) {
    colInt++;
  }

  if (colInt === O || (colOrigin < O && colInt > O) || ((colInt > O || colOrigin < O) && rollover)) {
    colInt++;

    if (colInt === I) {
      colInt++;
    }
  }

  if (colInt > Z) {
    colInt = colInt - Z + A - 1;
  }

  if (rowInt > V) {
    rowInt = rowInt - V + A - 1;
    rollover = true;
  }
  else {
    rollover = false;
  }

  if (((rowInt === I) || ((rowOrigin < I) && (rowInt > I))) || (((rowInt > I) || (rowOrigin < I)) && rollover)) {
    rowInt++;
  }

  if (((rowInt === O) || ((rowOrigin < O) && (rowInt > O))) || (((rowInt > O) || (rowOrigin < O)) && rollover)) {
    rowInt++;

    if (rowInt === I) {
      rowInt++;
    }
  }

  if (rowInt > V) {
    rowInt = rowInt - V + A - 1;
  }

  var twoLetter = String.fromCharCode(colInt) + String.fromCharCode(rowInt);
  return twoLetter;
}

/**
 * Decode the UTM parameters from a MGRS string.
 *
 * @private
 * @param {string} mgrsString an UPPERCASE coordinate string is expected.
 * @return {object} An object literal with easting, northing, zoneLetter,
 *     zoneNumber and accuracy (in meters) properties.
 */
function decode(mgrsString) {

  if (mgrsString && mgrsString.length === 0) {
    throw ("MGRSPoint coverting from nothing");
  }

  var length = mgrsString.length;

  var hunK = null;
  var sb = "";
  var testChar;
  var i = 0;

  // get Zone number
  while (!(/[A-Z]/).test(testChar = mgrsString.charAt(i))) {
    if (i >= 2) {
      throw ("MGRSPoint bad conversion from: " + mgrsString);
    }
    sb += testChar;
    i++;
  }

  var zoneNumber = parseInt(sb, 10);

  if (i === 0 || i + 3 > length) {
    // A good MGRS string has to be 4-5 digits long,
    // ##AAA/#AAA at least.
    throw ("MGRSPoint bad conversion from: " + mgrsString);
  }

  var zoneLetter = mgrsString.charAt(i++);

  // Should we check the zone letter here? Why not.
  if (zoneLetter <= 'A' || zoneLetter === 'B' || zoneLetter === 'Y' || zoneLetter >= 'Z' || zoneLetter === 'I' || zoneLetter === 'O') {
    throw ("MGRSPoint zone letter " + zoneLetter + " not handled: " + mgrsString);
  }

  hunK = mgrsString.substring(i, i += 2);

  var set = get100kSetForZone(zoneNumber);

  var east100k = getEastingFromChar(hunK.charAt(0), set);
  var north100k = getNorthingFromChar(hunK.charAt(1), set);

  // We have a bug where the northing may be 2000000 too low.
  // How
  // do we know when to roll over?

  while (north100k < getMinNorthing(zoneLetter)) {
    north100k += 2000000;
  }

  // calculate the char index for easting/northing separator
  var remainder = length - i;

  if (remainder % 2 !== 0) {
    throw ("MGRSPoint has to have an even number \nof digits after the zone letter and two 100km letters - front \nhalf for easting meters, second half for \nnorthing meters" + mgrsString);
  }

  var sep = remainder / 2;

  var sepEasting = 0.0;
  var sepNorthing = 0.0;
  var accuracyBonus, sepEastingString, sepNorthingString, easting, northing;
  if (sep > 0) {
    accuracyBonus = 100000.0 / Math.pow(10, sep);
    sepEastingString = mgrsString.substring(i, i + sep);
    sepEasting = parseFloat(sepEastingString) * accuracyBonus;
    sepNorthingString = mgrsString.substring(i + sep);
    sepNorthing = parseFloat(sepNorthingString) * accuracyBonus;
  }

  easting = sepEasting + east100k;
  northing = sepNorthing + north100k;

  return {
    easting: easting,
    northing: northing,
    zoneLetter: zoneLetter,
    zoneNumber: zoneNumber,
    accuracy: accuracyBonus
  };
}

/**
 * Given the first letter from a two-letter MGRS 100k zone, and given the
 * MGRS table set for the zone number, figure out the easting value that
 * should be added to the other, secondary easting value.
 *
 * @private
 * @param {char} e The first letter from a two-letter MGRS 100´k zone.
 * @param {number} set The MGRS table set for the zone number.
 * @return {number} The easting value for the given letter and set.
 */
function getEastingFromChar(e, set) {
  // colOrigin is the letter at the origin of the set for the
  // column
  var curCol = SET_ORIGIN_COLUMN_LETTERS.charCodeAt(set - 1);
  var eastingValue = 100000.0;
  var rewindMarker = false;

  while (curCol !== e.charCodeAt(0)) {
    curCol++;
    if (curCol === I) {
      curCol++;
    }
    if (curCol === O) {
      curCol++;
    }
    if (curCol > Z) {
      if (rewindMarker) {
        throw ("Bad character: " + e);
      }
      curCol = A;
      rewindMarker = true;
    }
    eastingValue += 100000.0;
  }

  return eastingValue;
}

/**
 * Given the second letter from a two-letter MGRS 100k zone, and given the
 * MGRS table set for the zone number, figure out the northing value that
 * should be added to the other, secondary northing value. You have to
 * remember that Northings are determined from the equator, and the vertical
 * cycle of letters mean a 2000000 additional northing meters. This happens
 * approx. every 18 degrees of latitude. This method does *NOT* count any
 * additional northings. You have to figure out how many 2000000 meters need
 * to be added for the zone letter of the MGRS coordinate.
 *
 * @private
 * @param {char} n Second letter of the MGRS 100k zone
 * @param {number} set The MGRS table set number, which is dependent on the
 *     UTM zone number.
 * @return {number} The northing value for the given letter and set.
 */
function getNorthingFromChar(n, set) {

  if (n > 'V') {
    throw ("MGRSPoint given invalid Northing " + n);
  }

  // rowOrigin is the letter at the origin of the set for the
  // column
  var curRow = SET_ORIGIN_ROW_LETTERS.charCodeAt(set - 1);
  var northingValue = 0.0;
  var rewindMarker = false;

  while (curRow !== n.charCodeAt(0)) {
    curRow++;
    if (curRow === I) {
      curRow++;
    }
    if (curRow === O) {
      curRow++;
    }
    // fixing a bug making whole application hang in this loop
    // when 'n' is a wrong character
    if (curRow > V) {
      if (rewindMarker) { // making sure that this loop ends
        throw ("Bad character: " + n);
      }
      curRow = A;
      rewindMarker = true;
    }
    northingValue += 100000.0;
  }

  return northingValue;
}

/**
 * The function getMinNorthing returns the minimum northing value of a MGRS
 * zone.
 *
 * Ported from Geotrans' c Lattitude_Band_Value structure table.
 *
 * @private
 * @param {char} zoneLetter The MGRS zone to get the min northing for.
 * @return {number}
 */
function getMinNorthing(zoneLetter) {
  var northing;
  switch (zoneLetter) {
  case 'C':
    northing = 1100000.0;
    break;
  case 'D':
    northing = 2000000.0;
    break;
  case 'E':
    northing = 2800000.0;
    break;
  case 'F':
    northing = 3700000.0;
    break;
  case 'G':
    northing = 4600000.0;
    break;
  case 'H':
    northing = 5500000.0;
    break;
  case 'J':
    northing = 6400000.0;
    break;
  case 'K':
    northing = 7300000.0;
    break;
  case 'L':
    northing = 8200000.0;
    break;
  case 'M':
    northing = 9100000.0;
    break;
  case 'N':
    northing = 0.0;
    break;
  case 'P':
    northing = 800000.0;
    break;
  case 'Q':
    northing = 1700000.0;
    break;
  case 'R':
    northing = 2600000.0;
    break;
  case 'S':
    northing = 3500000.0;
    break;
  case 'T':
    northing = 4400000.0;
    break;
  case 'U':
    northing = 5300000.0;
    break;
  case 'V':
    northing = 6200000.0;
    break;
  case 'W':
    northing = 7000000.0;
    break;
  case 'X':
    northing = 7900000.0;
    break;
  default:
    northing = -1.0;
  }
  if (northing >= 0.0) {
    return northing;
  }
  else {
    throw ("Invalid zone letter: " + zoneLetter);
  }

}


/***/ }),
/* 67 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony export (immutable) */ __webpack_exports__["init"] = init;
/* harmony export (immutable) */ __webpack_exports__["forward"] = forward;
/* harmony export (immutable) */ __webpack_exports__["inverse"] = inverse;
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "names", function() { return names; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__common_pj_enfn__ = __webpack_require__(68);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__common_pj_mlfn__ = __webpack_require__(41);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__common_pj_inv_mlfn__ = __webpack_require__(69);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__common_adjust_lon__ = __webpack_require__(5);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__constants_values__ = __webpack_require__(4);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__common_sign__ = __webpack_require__(20);
// Heavily based on this tmerc projection implementation
// https://github.com/mbloch/mapshaper-proj/blob/master/src/projections/tmerc.js









function init() {
  this.x0 = this.x0 !== undefined ? this.x0 : 0;
  this.y0 = this.y0 !== undefined ? this.y0 : 0;
  this.long0 = this.long0 !== undefined ? this.long0 : 0;
  this.lat0 = this.lat0 !== undefined ? this.lat0 : 0;

  if (this.es) {
    this.en = Object(__WEBPACK_IMPORTED_MODULE_0__common_pj_enfn__["a" /* default */])(this.es);
    this.ml0 = Object(__WEBPACK_IMPORTED_MODULE_1__common_pj_mlfn__["a" /* default */])(this.lat0, Math.sin(this.lat0), Math.cos(this.lat0), this.en);
  }
}

/**
    Transverse Mercator Forward  - long/lat to x/y
    long/lat in radians
  */
function forward(p) {
  var lon = p.x;
  var lat = p.y;

  var delta_lon = Object(__WEBPACK_IMPORTED_MODULE_3__common_adjust_lon__["a" /* default */])(lon - this.long0);
  var con;
  var x, y;
  var sin_phi = Math.sin(lat);
  var cos_phi = Math.cos(lat);

  if (!this.es) {
    var b = cos_phi * Math.sin(delta_lon);

    if ((Math.abs(Math.abs(b) - 1)) < __WEBPACK_IMPORTED_MODULE_4__constants_values__["b" /* EPSLN */]) {
      return (93);
    }
    else {
      x = 0.5 * this.a * this.k0 * Math.log((1 + b) / (1 - b)) + this.x0;
      y = cos_phi * Math.cos(delta_lon) / Math.sqrt(1 - Math.pow(b, 2));
      b = Math.abs(y);

      if (b >= 1) {
        if ((b - 1) > __WEBPACK_IMPORTED_MODULE_4__constants_values__["b" /* EPSLN */]) {
          return (93);
        }
        else {
          y = 0;
        }
      }
      else {
        y = Math.acos(y);
      }

      if (lat < 0) {
        y = -y;
      }

      y = this.a * this.k0 * (y - this.lat0) + this.y0;
    }
  }
  else {
    var al = cos_phi * delta_lon;
    var als = Math.pow(al, 2);
    var c = this.ep2 * Math.pow(cos_phi, 2);
    var cs = Math.pow(c, 2);
    var tq = Math.abs(cos_phi) > __WEBPACK_IMPORTED_MODULE_4__constants_values__["b" /* EPSLN */] ? Math.tan(lat) : 0;
    var t = Math.pow(tq, 2);
    var ts = Math.pow(t, 2);
    con = 1 - this.es * Math.pow(sin_phi, 2);
    al = al / Math.sqrt(con);
    var ml = Object(__WEBPACK_IMPORTED_MODULE_1__common_pj_mlfn__["a" /* default */])(lat, sin_phi, cos_phi, this.en);

    x = this.a * (this.k0 * al * (1 +
      als / 6 * (1 - t + c +
      als / 20 * (5 - 18 * t + ts + 14 * c - 58 * t * c +
      als / 42 * (61 + 179 * ts - ts * t - 479 * t))))) +
      this.x0;

    y = this.a * (this.k0 * (ml - this.ml0 +
      sin_phi * delta_lon * al / 2 * (1 +
      als / 12 * (5 - t + 9 * c + 4 * cs +
      als / 30 * (61 + ts - 58 * t + 270 * c - 330 * t * c +
      als / 56 * (1385 + 543 * ts - ts * t - 3111 * t)))))) +
      this.y0;
  }

  p.x = x;
  p.y = y;

  return p;
}

/**
    Transverse Mercator Inverse  -  x/y to long/lat
  */
function inverse(p) {
  var con, phi;
  var lat, lon;
  var x = (p.x - this.x0) * (1 / this.a);
  var y = (p.y - this.y0) * (1 / this.a);

  if (!this.es) {
    var f = Math.exp(x / this.k0);
    var g = 0.5 * (f - 1 / f);
    var temp = this.lat0 + y / this.k0;
    var h = Math.cos(temp);
    con = Math.sqrt((1 - Math.pow(h, 2)) / (1 + Math.pow(g, 2)));
    lat = Math.asin(con);

    if (y < 0) {
      lat = -lat;
    }

    if ((g === 0) && (h === 0)) {
      lon = 0;
    }
    else {
      lon = Object(__WEBPACK_IMPORTED_MODULE_3__common_adjust_lon__["a" /* default */])(Math.atan2(g, h) + this.long0);
    }
  }
  else { // ellipsoidal form
    con = this.ml0 + y / this.k0;
    phi = Object(__WEBPACK_IMPORTED_MODULE_2__common_pj_inv_mlfn__["a" /* default */])(con, this.es, this.en);

    if (Math.abs(phi) < __WEBPACK_IMPORTED_MODULE_4__constants_values__["d" /* HALF_PI */]) {
      var sin_phi = Math.sin(phi);
      var cos_phi = Math.cos(phi);
      var tan_phi = Math.abs(cos_phi) > __WEBPACK_IMPORTED_MODULE_4__constants_values__["b" /* EPSLN */] ? Math.tan(phi) : 0;
      var c = this.ep2 * Math.pow(cos_phi, 2);
      var cs = Math.pow(c, 2);
      var t = Math.pow(tan_phi, 2);
      var ts = Math.pow(t, 2);
      con = 1 - this.es * Math.pow(sin_phi, 2);
      var d = x * Math.sqrt(con) / this.k0;
      var ds = Math.pow(d, 2);
      con = con * tan_phi;

      lat = phi - (con * ds / (1 - this.es)) * 0.5 * (1 -
        ds / 12 * (5 + 3 * t - 9 * c * t + c - 4 * cs -
        ds / 30 * (61 + 90 * t - 252 * c * t + 45 * ts + 46 * c -
        ds / 56 * (1385 + 3633 * t + 4095 * ts + 1574 * ts * t))));

      lon = Object(__WEBPACK_IMPORTED_MODULE_3__common_adjust_lon__["a" /* default */])(this.long0 + (d * (1 -
        ds / 6 * (1 + 2 * t + c -
        ds / 20 * (5 + 28 * t + 24 * ts + 8 * c * t + 6 * c -
        ds / 42 * (61 + 662 * t + 1320 * ts + 720 * ts * t)))) / cos_phi));
    }
    else {
      lat = __WEBPACK_IMPORTED_MODULE_4__constants_values__["d" /* HALF_PI */] * Object(__WEBPACK_IMPORTED_MODULE_5__common_sign__["a" /* default */])(y);
      lon = 0;
    }
  }

  p.x = lon;
  p.y = lat;

  return p;
}

var names = ["Transverse_Mercator", "Transverse Mercator", "tmerc"];
/* harmony default export */ __webpack_exports__["default"] = ({
  init: init,
  forward: forward,
  inverse: inverse,
  names: names
});


/***/ }),
/* 68 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
var C00 = 1;
var C02 = 0.25;
var C04 = 0.046875;
var C06 = 0.01953125;
var C08 = 0.01068115234375;
var C22 = 0.75;
var C44 = 0.46875;
var C46 = 0.01302083333333333333;
var C48 = 0.00712076822916666666;
var C66 = 0.36458333333333333333;
var C68 = 0.00569661458333333333;
var C88 = 0.3076171875;

/* harmony default export */ __webpack_exports__["a"] = (function(es) {
  var en = [];
  en[0] = C00 - es * (C02 + es * (C04 + es * (C06 + es * C08)));
  en[1] = es * (C22 - es * (C04 + es * (C06 + es * C08)));
  var t = es * es;
  en[2] = t * (C44 - es * (C46 + es * C48));
  t *= es;
  en[3] = t * (C66 - es * C68);
  en[4] = t * es * C88;
  return en;
});

/***/ }),
/* 69 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__pj_mlfn__ = __webpack_require__(41);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__constants_values__ = __webpack_require__(4);



var MAX_ITER = 20;

/* harmony default export */ __webpack_exports__["a"] = (function(arg, es, en) {
  var k = 1 / (1 - es);
  var phi = arg;
  for (var i = MAX_ITER; i; --i) { /* rarely goes over 2 iterations */
    var s = Math.sin(phi);
    var t = 1 - es * s * s;
    //t = this.pj_mlfn(phi, s, Math.cos(phi), en) - arg;
    //phi -= t * (t * Math.sqrt(t)) * k;
    t = (Object(__WEBPACK_IMPORTED_MODULE_0__pj_mlfn__["a" /* default */])(phi, s, Math.cos(phi), en) - arg) * (t * Math.sqrt(t)) * k;
    phi -= t;
    if (Math.abs(t) < __WEBPACK_IMPORTED_MODULE_1__constants_values__["b" /* EPSLN */]) {
      return phi;
    }
  }
  //..reportError("cass:pj_inv_mlfn: Convergence error");
  return phi;
});


/***/ }),
/* 70 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony default export */ __webpack_exports__["a"] = (function(x, y) {
  x = Math.abs(x);
  y = Math.abs(y);
  var a = Math.max(x, y);
  var b = Math.min(x, y) / (a ? a : 1);

  return a * Math.sqrt(1 + Math.pow(b, 2));
});


/***/ }),
/* 71 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony default export */ __webpack_exports__["a"] = (function(x) {
  var r = Math.exp(x);
  r = (r + 1 / r) / 2;
  return r;
});

/***/ }),
/* 72 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "dependsOn", function() { return dependsOn; });
/* harmony export (immutable) */ __webpack_exports__["init"] = init;
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "names", function() { return names; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__common_adjust_zone__ = __webpack_require__(163);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__etmerc__ = __webpack_require__(42);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__constants_values__ = __webpack_require__(4);


var dependsOn = 'etmerc';



function init() {
  var zone = Object(__WEBPACK_IMPORTED_MODULE_0__common_adjust_zone__["a" /* default */])(this.zone, this.long0);
  if (zone === undefined) {
    throw new Error('unknown utm zone');
  }
  this.lat0 = 0;
  this.long0 =  ((6 * Math.abs(zone)) - 183) * __WEBPACK_IMPORTED_MODULE_2__constants_values__["a" /* D2R */];
  this.x0 = 500000;
  this.y0 = this.utmSouth ? 10000000 : 0;
  this.k0 = 0.9996;

  __WEBPACK_IMPORTED_MODULE_1__etmerc__["default"].init.apply(this);
  this.forward = __WEBPACK_IMPORTED_MODULE_1__etmerc__["default"].forward;
  this.inverse = __WEBPACK_IMPORTED_MODULE_1__etmerc__["default"].inverse;
}

var names = ["Universal Transverse Mercator System", "utm"];
/* harmony default export */ __webpack_exports__["default"] = ({
  init: init,
  names: names,
  dependsOn: dependsOn
});


/***/ }),
/* 73 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony export (immutable) */ __webpack_exports__["init"] = init;
/* harmony export (immutable) */ __webpack_exports__["forward"] = forward;
/* harmony export (immutable) */ __webpack_exports__["inverse"] = inverse;
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "names", function() { return names; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__gauss__ = __webpack_require__(74);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__common_adjust_lon__ = __webpack_require__(5);



function init() {
  __WEBPACK_IMPORTED_MODULE_0__gauss__["default"].init.apply(this);
  if (!this.rc) {
    return;
  }
  this.sinc0 = Math.sin(this.phic0);
  this.cosc0 = Math.cos(this.phic0);
  this.R2 = 2 * this.rc;
  if (!this.title) {
    this.title = "Oblique Stereographic Alternative";
  }
}

function forward(p) {
  var sinc, cosc, cosl, k;
  p.x = Object(__WEBPACK_IMPORTED_MODULE_1__common_adjust_lon__["a" /* default */])(p.x - this.long0);
  __WEBPACK_IMPORTED_MODULE_0__gauss__["default"].forward.apply(this, [p]);
  sinc = Math.sin(p.y);
  cosc = Math.cos(p.y);
  cosl = Math.cos(p.x);
  k = this.k0 * this.R2 / (1 + this.sinc0 * sinc + this.cosc0 * cosc * cosl);
  p.x = k * cosc * Math.sin(p.x);
  p.y = k * (this.cosc0 * sinc - this.sinc0 * cosc * cosl);
  p.x = this.a * p.x + this.x0;
  p.y = this.a * p.y + this.y0;
  return p;
}

function inverse(p) {
  var sinc, cosc, lon, lat, rho;
  p.x = (p.x - this.x0) / this.a;
  p.y = (p.y - this.y0) / this.a;

  p.x /= this.k0;
  p.y /= this.k0;
  if ((rho = Math.sqrt(p.x * p.x + p.y * p.y))) {
    var c = 2 * Math.atan2(rho, this.R2);
    sinc = Math.sin(c);
    cosc = Math.cos(c);
    lat = Math.asin(cosc * this.sinc0 + p.y * sinc * this.cosc0 / rho);
    lon = Math.atan2(p.x * sinc, rho * this.cosc0 * cosc - p.y * this.sinc0 * sinc);
  }
  else {
    lat = this.phic0;
    lon = 0;
  }

  p.x = lon;
  p.y = lat;
  __WEBPACK_IMPORTED_MODULE_0__gauss__["default"].inverse.apply(this, [p]);
  p.x = Object(__WEBPACK_IMPORTED_MODULE_1__common_adjust_lon__["a" /* default */])(p.x + this.long0);
  return p;
}

var names = ["Stereographic_North_Pole", "Oblique_Stereographic", "Polar_Stereographic", "sterea","Oblique Stereographic Alternative","Double_Stereographic"];
/* harmony default export */ __webpack_exports__["default"] = ({
  init: init,
  forward: forward,
  inverse: inverse,
  names: names
});


/***/ }),
/* 74 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony export (immutable) */ __webpack_exports__["init"] = init;
/* harmony export (immutable) */ __webpack_exports__["forward"] = forward;
/* harmony export (immutable) */ __webpack_exports__["inverse"] = inverse;
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "names", function() { return names; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__common_srat__ = __webpack_require__(164);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__constants_values__ = __webpack_require__(4);

var MAX_ITER = 20;


function init() {
  var sphi = Math.sin(this.lat0);
  var cphi = Math.cos(this.lat0);
  cphi *= cphi;
  this.rc = Math.sqrt(1 - this.es) / (1 - this.es * sphi * sphi);
  this.C = Math.sqrt(1 + this.es * cphi * cphi / (1 - this.es));
  this.phic0 = Math.asin(sphi / this.C);
  this.ratexp = 0.5 * this.C * this.e;
  this.K = Math.tan(0.5 * this.phic0 + __WEBPACK_IMPORTED_MODULE_1__constants_values__["c" /* FORTPI */]) / (Math.pow(Math.tan(0.5 * this.lat0 + __WEBPACK_IMPORTED_MODULE_1__constants_values__["c" /* FORTPI */]), this.C) * Object(__WEBPACK_IMPORTED_MODULE_0__common_srat__["a" /* default */])(this.e * sphi, this.ratexp));
}

function forward(p) {
  var lon = p.x;
  var lat = p.y;

  p.y = 2 * Math.atan(this.K * Math.pow(Math.tan(0.5 * lat + __WEBPACK_IMPORTED_MODULE_1__constants_values__["c" /* FORTPI */]), this.C) * Object(__WEBPACK_IMPORTED_MODULE_0__common_srat__["a" /* default */])(this.e * Math.sin(lat), this.ratexp)) - __WEBPACK_IMPORTED_MODULE_1__constants_values__["d" /* HALF_PI */];
  p.x = this.C * lon;
  return p;
}

function inverse(p) {
  var DEL_TOL = 1e-14;
  var lon = p.x / this.C;
  var lat = p.y;
  var num = Math.pow(Math.tan(0.5 * lat + __WEBPACK_IMPORTED_MODULE_1__constants_values__["c" /* FORTPI */]) / this.K, 1 / this.C);
  for (var i = MAX_ITER; i > 0; --i) {
    lat = 2 * Math.atan(num * Object(__WEBPACK_IMPORTED_MODULE_0__common_srat__["a" /* default */])(this.e * Math.sin(p.y), - 0.5 * this.e)) - __WEBPACK_IMPORTED_MODULE_1__constants_values__["d" /* HALF_PI */];
    if (Math.abs(lat - p.y) < DEL_TOL) {
      break;
    }
    p.y = lat;
  }
  /* convergence failed */
  if (!i) {
    return null;
  }
  p.x = lon;
  p.y = lat;
  return p;
}

var names = ["gauss"];
/* harmony default export */ __webpack_exports__["default"] = ({
  init: init,
  forward: forward,
  inverse: inverse,
  names: names
});


/***/ }),
/* 75 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony export (immutable) */ __webpack_exports__["ssfn_"] = ssfn_;
/* harmony export (immutable) */ __webpack_exports__["init"] = init;
/* harmony export (immutable) */ __webpack_exports__["forward"] = forward;
/* harmony export (immutable) */ __webpack_exports__["inverse"] = inverse;
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "names", function() { return names; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__constants_values__ = __webpack_require__(4);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__common_sign__ = __webpack_require__(20);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__common_msfnz__ = __webpack_require__(15);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__common_tsfnz__ = __webpack_require__(27);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__common_phi2z__ = __webpack_require__(28);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__common_adjust_lon__ = __webpack_require__(5);








function ssfn_(phit, sinphi, eccen) {
  sinphi *= eccen;
  return (Math.tan(0.5 * (__WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */] + phit)) * Math.pow((1 - sinphi) / (1 + sinphi), 0.5 * eccen));
}

function init() {
  this.coslat0 = Math.cos(this.lat0);
  this.sinlat0 = Math.sin(this.lat0);
  if (this.sphere) {
    if (this.k0 === 1 && !isNaN(this.lat_ts) && Math.abs(this.coslat0) <= __WEBPACK_IMPORTED_MODULE_0__constants_values__["b" /* EPSLN */]) {
      this.k0 = 0.5 * (1 + Object(__WEBPACK_IMPORTED_MODULE_1__common_sign__["a" /* default */])(this.lat0) * Math.sin(this.lat_ts));
    }
  }
  else {
    if (Math.abs(this.coslat0) <= __WEBPACK_IMPORTED_MODULE_0__constants_values__["b" /* EPSLN */]) {
      if (this.lat0 > 0) {
        //North pole
        //trace('stere:north pole');
        this.con = 1;
      }
      else {
        //South pole
        //trace('stere:south pole');
        this.con = -1;
      }
    }
    this.cons = Math.sqrt(Math.pow(1 + this.e, 1 + this.e) * Math.pow(1 - this.e, 1 - this.e));
    if (this.k0 === 1 && !isNaN(this.lat_ts) && Math.abs(this.coslat0) <= __WEBPACK_IMPORTED_MODULE_0__constants_values__["b" /* EPSLN */]) {
      this.k0 = 0.5 * this.cons * Object(__WEBPACK_IMPORTED_MODULE_2__common_msfnz__["a" /* default */])(this.e, Math.sin(this.lat_ts), Math.cos(this.lat_ts)) / Object(__WEBPACK_IMPORTED_MODULE_3__common_tsfnz__["a" /* default */])(this.e, this.con * this.lat_ts, this.con * Math.sin(this.lat_ts));
    }
    this.ms1 = Object(__WEBPACK_IMPORTED_MODULE_2__common_msfnz__["a" /* default */])(this.e, this.sinlat0, this.coslat0);
    this.X0 = 2 * Math.atan(this.ssfn_(this.lat0, this.sinlat0, this.e)) - __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */];
    this.cosX0 = Math.cos(this.X0);
    this.sinX0 = Math.sin(this.X0);
  }
}

// Stereographic forward equations--mapping lat,long to x,y
function forward(p) {
  var lon = p.x;
  var lat = p.y;
  var sinlat = Math.sin(lat);
  var coslat = Math.cos(lat);
  var A, X, sinX, cosX, ts, rh;
  var dlon = Object(__WEBPACK_IMPORTED_MODULE_5__common_adjust_lon__["a" /* default */])(lon - this.long0);

  if (Math.abs(Math.abs(lon - this.long0) - Math.PI) <= __WEBPACK_IMPORTED_MODULE_0__constants_values__["b" /* EPSLN */] && Math.abs(lat + this.lat0) <= __WEBPACK_IMPORTED_MODULE_0__constants_values__["b" /* EPSLN */]) {
    //case of the origine point
    //trace('stere:this is the origin point');
    p.x = NaN;
    p.y = NaN;
    return p;
  }
  if (this.sphere) {
    //trace('stere:sphere case');
    A = 2 * this.k0 / (1 + this.sinlat0 * sinlat + this.coslat0 * coslat * Math.cos(dlon));
    p.x = this.a * A * coslat * Math.sin(dlon) + this.x0;
    p.y = this.a * A * (this.coslat0 * sinlat - this.sinlat0 * coslat * Math.cos(dlon)) + this.y0;
    return p;
  }
  else {
    X = 2 * Math.atan(this.ssfn_(lat, sinlat, this.e)) - __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */];
    cosX = Math.cos(X);
    sinX = Math.sin(X);
    if (Math.abs(this.coslat0) <= __WEBPACK_IMPORTED_MODULE_0__constants_values__["b" /* EPSLN */]) {
      ts = Object(__WEBPACK_IMPORTED_MODULE_3__common_tsfnz__["a" /* default */])(this.e, lat * this.con, this.con * sinlat);
      rh = 2 * this.a * this.k0 * ts / this.cons;
      p.x = this.x0 + rh * Math.sin(lon - this.long0);
      p.y = this.y0 - this.con * rh * Math.cos(lon - this.long0);
      //trace(p.toString());
      return p;
    }
    else if (Math.abs(this.sinlat0) < __WEBPACK_IMPORTED_MODULE_0__constants_values__["b" /* EPSLN */]) {
      //Eq
      //trace('stere:equateur');
      A = 2 * this.a * this.k0 / (1 + cosX * Math.cos(dlon));
      p.y = A * sinX;
    }
    else {
      //other case
      //trace('stere:normal case');
      A = 2 * this.a * this.k0 * this.ms1 / (this.cosX0 * (1 + this.sinX0 * sinX + this.cosX0 * cosX * Math.cos(dlon)));
      p.y = A * (this.cosX0 * sinX - this.sinX0 * cosX * Math.cos(dlon)) + this.y0;
    }
    p.x = A * cosX * Math.sin(dlon) + this.x0;
  }
  //trace(p.toString());
  return p;
}

//* Stereographic inverse equations--mapping x,y to lat/long
function inverse(p) {
  p.x -= this.x0;
  p.y -= this.y0;
  var lon, lat, ts, ce, Chi;
  var rh = Math.sqrt(p.x * p.x + p.y * p.y);
  if (this.sphere) {
    var c = 2 * Math.atan(rh / (2 * this.a * this.k0));
    lon = this.long0;
    lat = this.lat0;
    if (rh <= __WEBPACK_IMPORTED_MODULE_0__constants_values__["b" /* EPSLN */]) {
      p.x = lon;
      p.y = lat;
      return p;
    }
    lat = Math.asin(Math.cos(c) * this.sinlat0 + p.y * Math.sin(c) * this.coslat0 / rh);
    if (Math.abs(this.coslat0) < __WEBPACK_IMPORTED_MODULE_0__constants_values__["b" /* EPSLN */]) {
      if (this.lat0 > 0) {
        lon = Object(__WEBPACK_IMPORTED_MODULE_5__common_adjust_lon__["a" /* default */])(this.long0 + Math.atan2(p.x, - 1 * p.y));
      }
      else {
        lon = Object(__WEBPACK_IMPORTED_MODULE_5__common_adjust_lon__["a" /* default */])(this.long0 + Math.atan2(p.x, p.y));
      }
    }
    else {
      lon = Object(__WEBPACK_IMPORTED_MODULE_5__common_adjust_lon__["a" /* default */])(this.long0 + Math.atan2(p.x * Math.sin(c), rh * this.coslat0 * Math.cos(c) - p.y * this.sinlat0 * Math.sin(c)));
    }
    p.x = lon;
    p.y = lat;
    return p;
  }
  else {
    if (Math.abs(this.coslat0) <= __WEBPACK_IMPORTED_MODULE_0__constants_values__["b" /* EPSLN */]) {
      if (rh <= __WEBPACK_IMPORTED_MODULE_0__constants_values__["b" /* EPSLN */]) {
        lat = this.lat0;
        lon = this.long0;
        p.x = lon;
        p.y = lat;
        //trace(p.toString());
        return p;
      }
      p.x *= this.con;
      p.y *= this.con;
      ts = rh * this.cons / (2 * this.a * this.k0);
      lat = this.con * Object(__WEBPACK_IMPORTED_MODULE_4__common_phi2z__["a" /* default */])(this.e, ts);
      lon = this.con * Object(__WEBPACK_IMPORTED_MODULE_5__common_adjust_lon__["a" /* default */])(this.con * this.long0 + Math.atan2(p.x, - 1 * p.y));
    }
    else {
      ce = 2 * Math.atan(rh * this.cosX0 / (2 * this.a * this.k0 * this.ms1));
      lon = this.long0;
      if (rh <= __WEBPACK_IMPORTED_MODULE_0__constants_values__["b" /* EPSLN */]) {
        Chi = this.X0;
      }
      else {
        Chi = Math.asin(Math.cos(ce) * this.sinX0 + p.y * Math.sin(ce) * this.cosX0 / rh);
        lon = Object(__WEBPACK_IMPORTED_MODULE_5__common_adjust_lon__["a" /* default */])(this.long0 + Math.atan2(p.x * Math.sin(ce), rh * this.cosX0 * Math.cos(ce) - p.y * this.sinX0 * Math.sin(ce)));
      }
      lat = -1 * Object(__WEBPACK_IMPORTED_MODULE_4__common_phi2z__["a" /* default */])(this.e, Math.tan(0.5 * (__WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */] + Chi)));
    }
  }
  p.x = lon;
  p.y = lat;

  //trace(p.toString());
  return p;

}

var names = ["stere", "Stereographic_South_Pole", "Polar Stereographic (variant B)"];
/* harmony default export */ __webpack_exports__["default"] = ({
  init: init,
  forward: forward,
  inverse: inverse,
  names: names,
  ssfn_: ssfn_
});


/***/ }),
/* 76 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony export (immutable) */ __webpack_exports__["init"] = init;
/* harmony export (immutable) */ __webpack_exports__["forward"] = forward;
/* harmony export (immutable) */ __webpack_exports__["inverse"] = inverse;
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "names", function() { return names; });
/*
  references:
    Formules et constantes pour le Calcul pour la
    projection cylindrique conforme à axe oblique et pour la transformation entre
    des systèmes de référence.
    http://www.swisstopo.admin.ch/internet/swisstopo/fr/home/topics/survey/sys/refsys/switzerland.parsysrelated1.31216.downloadList.77004.DownloadFile.tmp/swissprojectionfr.pdf
  */

function init() {
  var phy0 = this.lat0;
  this.lambda0 = this.long0;
  var sinPhy0 = Math.sin(phy0);
  var semiMajorAxis = this.a;
  var invF = this.rf;
  var flattening = 1 / invF;
  var e2 = 2 * flattening - Math.pow(flattening, 2);
  var e = this.e = Math.sqrt(e2);
  this.R = this.k0 * semiMajorAxis * Math.sqrt(1 - e2) / (1 - e2 * Math.pow(sinPhy0, 2));
  this.alpha = Math.sqrt(1 + e2 / (1 - e2) * Math.pow(Math.cos(phy0), 4));
  this.b0 = Math.asin(sinPhy0 / this.alpha);
  var k1 = Math.log(Math.tan(Math.PI / 4 + this.b0 / 2));
  var k2 = Math.log(Math.tan(Math.PI / 4 + phy0 / 2));
  var k3 = Math.log((1 + e * sinPhy0) / (1 - e * sinPhy0));
  this.K = k1 - this.alpha * k2 + this.alpha * e / 2 * k3;
}

function forward(p) {
  var Sa1 = Math.log(Math.tan(Math.PI / 4 - p.y / 2));
  var Sa2 = this.e / 2 * Math.log((1 + this.e * Math.sin(p.y)) / (1 - this.e * Math.sin(p.y)));
  var S = -this.alpha * (Sa1 + Sa2) + this.K;

  // spheric latitude
  var b = 2 * (Math.atan(Math.exp(S)) - Math.PI / 4);

  // spheric longitude
  var I = this.alpha * (p.x - this.lambda0);

  // psoeudo equatorial rotation
  var rotI = Math.atan(Math.sin(I) / (Math.sin(this.b0) * Math.tan(b) + Math.cos(this.b0) * Math.cos(I)));

  var rotB = Math.asin(Math.cos(this.b0) * Math.sin(b) - Math.sin(this.b0) * Math.cos(b) * Math.cos(I));

  p.y = this.R / 2 * Math.log((1 + Math.sin(rotB)) / (1 - Math.sin(rotB))) + this.y0;
  p.x = this.R * rotI + this.x0;
  return p;
}

function inverse(p) {
  var Y = p.x - this.x0;
  var X = p.y - this.y0;

  var rotI = Y / this.R;
  var rotB = 2 * (Math.atan(Math.exp(X / this.R)) - Math.PI / 4);

  var b = Math.asin(Math.cos(this.b0) * Math.sin(rotB) + Math.sin(this.b0) * Math.cos(rotB) * Math.cos(rotI));
  var I = Math.atan(Math.sin(rotI) / (Math.cos(this.b0) * Math.cos(rotI) - Math.sin(this.b0) * Math.tan(rotB)));

  var lambda = this.lambda0 + I / this.alpha;

  var S = 0;
  var phy = b;
  var prevPhy = -1000;
  var iteration = 0;
  while (Math.abs(phy - prevPhy) > 0.0000001) {
    if (++iteration > 20) {
      //...reportError("omercFwdInfinity");
      return;
    }
    //S = Math.log(Math.tan(Math.PI / 4 + phy / 2));
    S = 1 / this.alpha * (Math.log(Math.tan(Math.PI / 4 + b / 2)) - this.K) + this.e * Math.log(Math.tan(Math.PI / 4 + Math.asin(this.e * Math.sin(phy)) / 2));
    prevPhy = phy;
    phy = 2 * Math.atan(Math.exp(S)) - Math.PI / 2;
  }

  p.x = lambda;
  p.y = phy;
  return p;
}

var names = ["somerc"];
/* harmony default export */ __webpack_exports__["default"] = ({
  init: init,
  forward: forward,
  inverse: inverse,
  names: names
});


/***/ }),
/* 77 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony export (immutable) */ __webpack_exports__["init"] = init;
/* harmony export (immutable) */ __webpack_exports__["forward"] = forward;
/* harmony export (immutable) */ __webpack_exports__["inverse"] = inverse;
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "names", function() { return names; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__common_tsfnz__ = __webpack_require__(27);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__common_adjust_lon__ = __webpack_require__(5);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__common_phi2z__ = __webpack_require__(28);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__constants_values__ = __webpack_require__(4);





/* Initialize the Oblique Mercator  projection
    ------------------------------------------*/
function init() {
  this.no_off = this.no_off || false;
  this.no_rot = this.no_rot || false;

  if (isNaN(this.k0)) {
    this.k0 = 1;
  }
  var sinlat = Math.sin(this.lat0);
  var coslat = Math.cos(this.lat0);
  var con = this.e * sinlat;

  this.bl = Math.sqrt(1 + this.es / (1 - this.es) * Math.pow(coslat, 4));
  this.al = this.a * this.bl * this.k0 * Math.sqrt(1 - this.es) / (1 - con * con);
  var t0 = Object(__WEBPACK_IMPORTED_MODULE_0__common_tsfnz__["a" /* default */])(this.e, this.lat0, sinlat);
  var dl = this.bl / coslat * Math.sqrt((1 - this.es) / (1 - con * con));
  if (dl * dl < 1) {
    dl = 1;
  }
  var fl;
  var gl;
  if (!isNaN(this.longc)) {
    //Central point and azimuth method

    if (this.lat0 >= 0) {
      fl = dl + Math.sqrt(dl * dl - 1);
    }
    else {
      fl = dl - Math.sqrt(dl * dl - 1);
    }
    this.el = fl * Math.pow(t0, this.bl);
    gl = 0.5 * (fl - 1 / fl);
    this.gamma0 = Math.asin(Math.sin(this.alpha) / dl);
    this.long0 = this.longc - Math.asin(gl * Math.tan(this.gamma0)) / this.bl;

  }
  else {
    //2 points method
    var t1 = Object(__WEBPACK_IMPORTED_MODULE_0__common_tsfnz__["a" /* default */])(this.e, this.lat1, Math.sin(this.lat1));
    var t2 = Object(__WEBPACK_IMPORTED_MODULE_0__common_tsfnz__["a" /* default */])(this.e, this.lat2, Math.sin(this.lat2));
    if (this.lat0 >= 0) {
      this.el = (dl + Math.sqrt(dl * dl - 1)) * Math.pow(t0, this.bl);
    }
    else {
      this.el = (dl - Math.sqrt(dl * dl - 1)) * Math.pow(t0, this.bl);
    }
    var hl = Math.pow(t1, this.bl);
    var ll = Math.pow(t2, this.bl);
    fl = this.el / hl;
    gl = 0.5 * (fl - 1 / fl);
    var jl = (this.el * this.el - ll * hl) / (this.el * this.el + ll * hl);
    var pl = (ll - hl) / (ll + hl);
    var dlon12 = Object(__WEBPACK_IMPORTED_MODULE_1__common_adjust_lon__["a" /* default */])(this.long1 - this.long2);
    this.long0 = 0.5 * (this.long1 + this.long2) - Math.atan(jl * Math.tan(0.5 * this.bl * (dlon12)) / pl) / this.bl;
    this.long0 = Object(__WEBPACK_IMPORTED_MODULE_1__common_adjust_lon__["a" /* default */])(this.long0);
    var dlon10 = Object(__WEBPACK_IMPORTED_MODULE_1__common_adjust_lon__["a" /* default */])(this.long1 - this.long0);
    this.gamma0 = Math.atan(Math.sin(this.bl * (dlon10)) / gl);
    this.alpha = Math.asin(dl * Math.sin(this.gamma0));
  }

  if (this.no_off) {
    this.uc = 0;
  }
  else {
    if (this.lat0 >= 0) {
      this.uc = this.al / this.bl * Math.atan2(Math.sqrt(dl * dl - 1), Math.cos(this.alpha));
    }
    else {
      this.uc = -1 * this.al / this.bl * Math.atan2(Math.sqrt(dl * dl - 1), Math.cos(this.alpha));
    }
  }

}

/* Oblique Mercator forward equations--mapping lat,long to x,y
    ----------------------------------------------------------*/
function forward(p) {
  var lon = p.x;
  var lat = p.y;
  var dlon = Object(__WEBPACK_IMPORTED_MODULE_1__common_adjust_lon__["a" /* default */])(lon - this.long0);
  var us, vs;
  var con;
  if (Math.abs(Math.abs(lat) - __WEBPACK_IMPORTED_MODULE_3__constants_values__["d" /* HALF_PI */]) <= __WEBPACK_IMPORTED_MODULE_3__constants_values__["b" /* EPSLN */]) {
    if (lat > 0) {
      con = -1;
    }
    else {
      con = 1;
    }
    vs = this.al / this.bl * Math.log(Math.tan(__WEBPACK_IMPORTED_MODULE_3__constants_values__["c" /* FORTPI */] + con * this.gamma0 * 0.5));
    us = -1 * con * __WEBPACK_IMPORTED_MODULE_3__constants_values__["d" /* HALF_PI */] * this.al / this.bl;
  }
  else {
    var t = Object(__WEBPACK_IMPORTED_MODULE_0__common_tsfnz__["a" /* default */])(this.e, lat, Math.sin(lat));
    var ql = this.el / Math.pow(t, this.bl);
    var sl = 0.5 * (ql - 1 / ql);
    var tl = 0.5 * (ql + 1 / ql);
    var vl = Math.sin(this.bl * (dlon));
    var ul = (sl * Math.sin(this.gamma0) - vl * Math.cos(this.gamma0)) / tl;
    if (Math.abs(Math.abs(ul) - 1) <= __WEBPACK_IMPORTED_MODULE_3__constants_values__["b" /* EPSLN */]) {
      vs = Number.POSITIVE_INFINITY;
    }
    else {
      vs = 0.5 * this.al * Math.log((1 - ul) / (1 + ul)) / this.bl;
    }
    if (Math.abs(Math.cos(this.bl * (dlon))) <= __WEBPACK_IMPORTED_MODULE_3__constants_values__["b" /* EPSLN */]) {
      us = this.al * this.bl * (dlon);
    }
    else {
      us = this.al * Math.atan2(sl * Math.cos(this.gamma0) + vl * Math.sin(this.gamma0), Math.cos(this.bl * dlon)) / this.bl;
    }
  }

  if (this.no_rot) {
    p.x = this.x0 + us;
    p.y = this.y0 + vs;
  }
  else {

    us -= this.uc;
    p.x = this.x0 + vs * Math.cos(this.alpha) + us * Math.sin(this.alpha);
    p.y = this.y0 + us * Math.cos(this.alpha) - vs * Math.sin(this.alpha);
  }
  return p;
}

function inverse(p) {
  var us, vs;
  if (this.no_rot) {
    vs = p.y - this.y0;
    us = p.x - this.x0;
  }
  else {
    vs = (p.x - this.x0) * Math.cos(this.alpha) - (p.y - this.y0) * Math.sin(this.alpha);
    us = (p.y - this.y0) * Math.cos(this.alpha) + (p.x - this.x0) * Math.sin(this.alpha);
    us += this.uc;
  }
  var qp = Math.exp(-1 * this.bl * vs / this.al);
  var sp = 0.5 * (qp - 1 / qp);
  var tp = 0.5 * (qp + 1 / qp);
  var vp = Math.sin(this.bl * us / this.al);
  var up = (vp * Math.cos(this.gamma0) + sp * Math.sin(this.gamma0)) / tp;
  var ts = Math.pow(this.el / Math.sqrt((1 + up) / (1 - up)), 1 / this.bl);
  if (Math.abs(up - 1) < __WEBPACK_IMPORTED_MODULE_3__constants_values__["b" /* EPSLN */]) {
    p.x = this.long0;
    p.y = __WEBPACK_IMPORTED_MODULE_3__constants_values__["d" /* HALF_PI */];
  }
  else if (Math.abs(up + 1) < __WEBPACK_IMPORTED_MODULE_3__constants_values__["b" /* EPSLN */]) {
    p.x = this.long0;
    p.y = -1 * __WEBPACK_IMPORTED_MODULE_3__constants_values__["d" /* HALF_PI */];
  }
  else {
    p.y = Object(__WEBPACK_IMPORTED_MODULE_2__common_phi2z__["a" /* default */])(this.e, ts);
    p.x = Object(__WEBPACK_IMPORTED_MODULE_1__common_adjust_lon__["a" /* default */])(this.long0 - Math.atan2(sp * Math.cos(this.gamma0) - vp * Math.sin(this.gamma0), Math.cos(this.bl * us / this.al)) / this.bl);
  }
  return p;
}

var names = ["Hotine_Oblique_Mercator", "Hotine Oblique Mercator", "Hotine_Oblique_Mercator_Azimuth_Natural_Origin", "Hotine_Oblique_Mercator_Azimuth_Center", "omerc"];
/* harmony default export */ __webpack_exports__["default"] = ({
  init: init,
  forward: forward,
  inverse: inverse,
  names: names
});


/***/ }),
/* 78 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony export (immutable) */ __webpack_exports__["init"] = init;
/* harmony export (immutable) */ __webpack_exports__["forward"] = forward;
/* harmony export (immutable) */ __webpack_exports__["inverse"] = inverse;
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "names", function() { return names; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__common_msfnz__ = __webpack_require__(15);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__common_tsfnz__ = __webpack_require__(27);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__common_sign__ = __webpack_require__(20);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__common_adjust_lon__ = __webpack_require__(5);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__common_phi2z__ = __webpack_require__(28);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__constants_values__ = __webpack_require__(4);






function init() {

  // array of:  r_maj,r_min,lat1,lat2,c_lon,c_lat,false_east,false_north
  //double c_lat;                   /* center latitude                      */
  //double c_lon;                   /* center longitude                     */
  //double lat1;                    /* first standard parallel              */
  //double lat2;                    /* second standard parallel             */
  //double r_maj;                   /* major axis                           */
  //double r_min;                   /* minor axis                           */
  //double false_east;              /* x offset in meters                   */
  //double false_north;             /* y offset in meters                   */

  if (!this.lat2) {
    this.lat2 = this.lat1;
  } //if lat2 is not defined
  if (!this.k0) {
    this.k0 = 1;
  }
  this.x0 = this.x0 || 0;
  this.y0 = this.y0 || 0;
  // Standard Parallels cannot be equal and on opposite sides of the equator
  if (Math.abs(this.lat1 + this.lat2) < __WEBPACK_IMPORTED_MODULE_5__constants_values__["b" /* EPSLN */]) {
    return;
  }

  var temp = this.b / this.a;
  this.e = Math.sqrt(1 - temp * temp);

  var sin1 = Math.sin(this.lat1);
  var cos1 = Math.cos(this.lat1);
  var ms1 = Object(__WEBPACK_IMPORTED_MODULE_0__common_msfnz__["a" /* default */])(this.e, sin1, cos1);
  var ts1 = Object(__WEBPACK_IMPORTED_MODULE_1__common_tsfnz__["a" /* default */])(this.e, this.lat1, sin1);

  var sin2 = Math.sin(this.lat2);
  var cos2 = Math.cos(this.lat2);
  var ms2 = Object(__WEBPACK_IMPORTED_MODULE_0__common_msfnz__["a" /* default */])(this.e, sin2, cos2);
  var ts2 = Object(__WEBPACK_IMPORTED_MODULE_1__common_tsfnz__["a" /* default */])(this.e, this.lat2, sin2);

  var ts0 = Object(__WEBPACK_IMPORTED_MODULE_1__common_tsfnz__["a" /* default */])(this.e, this.lat0, Math.sin(this.lat0));

  if (Math.abs(this.lat1 - this.lat2) > __WEBPACK_IMPORTED_MODULE_5__constants_values__["b" /* EPSLN */]) {
    this.ns = Math.log(ms1 / ms2) / Math.log(ts1 / ts2);
  }
  else {
    this.ns = sin1;
  }
  if (isNaN(this.ns)) {
    this.ns = sin1;
  }
  this.f0 = ms1 / (this.ns * Math.pow(ts1, this.ns));
  this.rh = this.a * this.f0 * Math.pow(ts0, this.ns);
  if (!this.title) {
    this.title = "Lambert Conformal Conic";
  }
}

// Lambert Conformal conic forward equations--mapping lat,long to x,y
// -----------------------------------------------------------------
function forward(p) {

  var lon = p.x;
  var lat = p.y;

  // singular cases :
  if (Math.abs(2 * Math.abs(lat) - Math.PI) <= __WEBPACK_IMPORTED_MODULE_5__constants_values__["b" /* EPSLN */]) {
    lat = Object(__WEBPACK_IMPORTED_MODULE_2__common_sign__["a" /* default */])(lat) * (__WEBPACK_IMPORTED_MODULE_5__constants_values__["d" /* HALF_PI */] - 2 * __WEBPACK_IMPORTED_MODULE_5__constants_values__["b" /* EPSLN */]);
  }

  var con = Math.abs(Math.abs(lat) - __WEBPACK_IMPORTED_MODULE_5__constants_values__["d" /* HALF_PI */]);
  var ts, rh1;
  if (con > __WEBPACK_IMPORTED_MODULE_5__constants_values__["b" /* EPSLN */]) {
    ts = Object(__WEBPACK_IMPORTED_MODULE_1__common_tsfnz__["a" /* default */])(this.e, lat, Math.sin(lat));
    rh1 = this.a * this.f0 * Math.pow(ts, this.ns);
  }
  else {
    con = lat * this.ns;
    if (con <= 0) {
      return null;
    }
    rh1 = 0;
  }
  var theta = this.ns * Object(__WEBPACK_IMPORTED_MODULE_3__common_adjust_lon__["a" /* default */])(lon - this.long0);
  p.x = this.k0 * (rh1 * Math.sin(theta)) + this.x0;
  p.y = this.k0 * (this.rh - rh1 * Math.cos(theta)) + this.y0;

  return p;
}

// Lambert Conformal Conic inverse equations--mapping x,y to lat/long
// -----------------------------------------------------------------
function inverse(p) {

  var rh1, con, ts;
  var lat, lon;
  var x = (p.x - this.x0) / this.k0;
  var y = (this.rh - (p.y - this.y0) / this.k0);
  if (this.ns > 0) {
    rh1 = Math.sqrt(x * x + y * y);
    con = 1;
  }
  else {
    rh1 = -Math.sqrt(x * x + y * y);
    con = -1;
  }
  var theta = 0;
  if (rh1 !== 0) {
    theta = Math.atan2((con * x), (con * y));
  }
  if ((rh1 !== 0) || (this.ns > 0)) {
    con = 1 / this.ns;
    ts = Math.pow((rh1 / (this.a * this.f0)), con);
    lat = Object(__WEBPACK_IMPORTED_MODULE_4__common_phi2z__["a" /* default */])(this.e, ts);
    if (lat === -9999) {
      return null;
    }
  }
  else {
    lat = -__WEBPACK_IMPORTED_MODULE_5__constants_values__["d" /* HALF_PI */];
  }
  lon = Object(__WEBPACK_IMPORTED_MODULE_3__common_adjust_lon__["a" /* default */])(theta / this.ns + this.long0);

  p.x = lon;
  p.y = lat;
  return p;
}

var names = ["Lambert Tangential Conformal Conic Projection", "Lambert_Conformal_Conic", "Lambert_Conformal_Conic_2SP", "lcc"];
/* harmony default export */ __webpack_exports__["default"] = ({
  init: init,
  forward: forward,
  inverse: inverse,
  names: names
});


/***/ }),
/* 79 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony export (immutable) */ __webpack_exports__["init"] = init;
/* harmony export (immutable) */ __webpack_exports__["forward"] = forward;
/* harmony export (immutable) */ __webpack_exports__["inverse"] = inverse;
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "names", function() { return names; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__common_adjust_lon__ = __webpack_require__(5);


function init() {
  this.a = 6377397.155;
  this.es = 0.006674372230614;
  this.e = Math.sqrt(this.es);
  if (!this.lat0) {
    this.lat0 = 0.863937979737193;
  }
  if (!this.long0) {
    this.long0 = 0.7417649320975901 - 0.308341501185665;
  }
  /* if scale not set default to 0.9999 */
  if (!this.k0) {
    this.k0 = 0.9999;
  }
  this.s45 = 0.785398163397448; /* 45 */
  this.s90 = 2 * this.s45;
  this.fi0 = this.lat0;
  this.e2 = this.es;
  this.e = Math.sqrt(this.e2);
  this.alfa = Math.sqrt(1 + (this.e2 * Math.pow(Math.cos(this.fi0), 4)) / (1 - this.e2));
  this.uq = 1.04216856380474;
  this.u0 = Math.asin(Math.sin(this.fi0) / this.alfa);
  this.g = Math.pow((1 + this.e * Math.sin(this.fi0)) / (1 - this.e * Math.sin(this.fi0)), this.alfa * this.e / 2);
  this.k = Math.tan(this.u0 / 2 + this.s45) / Math.pow(Math.tan(this.fi0 / 2 + this.s45), this.alfa) * this.g;
  this.k1 = this.k0;
  this.n0 = this.a * Math.sqrt(1 - this.e2) / (1 - this.e2 * Math.pow(Math.sin(this.fi0), 2));
  this.s0 = 1.37008346281555;
  this.n = Math.sin(this.s0);
  this.ro0 = this.k1 * this.n0 / Math.tan(this.s0);
  this.ad = this.s90 - this.uq;
}

/* ellipsoid */
/* calculate xy from lat/lon */
/* Constants, identical to inverse transform function */
function forward(p) {
  var gfi, u, deltav, s, d, eps, ro;
  var lon = p.x;
  var lat = p.y;
  var delta_lon = Object(__WEBPACK_IMPORTED_MODULE_0__common_adjust_lon__["a" /* default */])(lon - this.long0);
  /* Transformation */
  gfi = Math.pow(((1 + this.e * Math.sin(lat)) / (1 - this.e * Math.sin(lat))), (this.alfa * this.e / 2));
  u = 2 * (Math.atan(this.k * Math.pow(Math.tan(lat / 2 + this.s45), this.alfa) / gfi) - this.s45);
  deltav = -delta_lon * this.alfa;
  s = Math.asin(Math.cos(this.ad) * Math.sin(u) + Math.sin(this.ad) * Math.cos(u) * Math.cos(deltav));
  d = Math.asin(Math.cos(u) * Math.sin(deltav) / Math.cos(s));
  eps = this.n * d;
  ro = this.ro0 * Math.pow(Math.tan(this.s0 / 2 + this.s45), this.n) / Math.pow(Math.tan(s / 2 + this.s45), this.n);
  p.y = ro * Math.cos(eps) / 1;
  p.x = ro * Math.sin(eps) / 1;

  if (!this.czech) {
    p.y *= -1;
    p.x *= -1;
  }
  return (p);
}

/* calculate lat/lon from xy */
function inverse(p) {
  var u, deltav, s, d, eps, ro, fi1;
  var ok;

  /* Transformation */
  /* revert y, x*/
  var tmp = p.x;
  p.x = p.y;
  p.y = tmp;
  if (!this.czech) {
    p.y *= -1;
    p.x *= -1;
  }
  ro = Math.sqrt(p.x * p.x + p.y * p.y);
  eps = Math.atan2(p.y, p.x);
  d = eps / Math.sin(this.s0);
  s = 2 * (Math.atan(Math.pow(this.ro0 / ro, 1 / this.n) * Math.tan(this.s0 / 2 + this.s45)) - this.s45);
  u = Math.asin(Math.cos(this.ad) * Math.sin(s) - Math.sin(this.ad) * Math.cos(s) * Math.cos(d));
  deltav = Math.asin(Math.cos(s) * Math.sin(d) / Math.cos(u));
  p.x = this.long0 - deltav / this.alfa;
  fi1 = u;
  ok = 0;
  var iter = 0;
  do {
    p.y = 2 * (Math.atan(Math.pow(this.k, - 1 / this.alfa) * Math.pow(Math.tan(u / 2 + this.s45), 1 / this.alfa) * Math.pow((1 + this.e * Math.sin(fi1)) / (1 - this.e * Math.sin(fi1)), this.e / 2)) - this.s45);
    if (Math.abs(fi1 - p.y) < 0.0000000001) {
      ok = 1;
    }
    fi1 = p.y;
    iter += 1;
  } while (ok === 0 && iter < 15);
  if (iter >= 15) {
    return null;
  }

  return (p);
}

var names = ["Krovak", "krovak"];
/* harmony default export */ __webpack_exports__["default"] = ({
  init: init,
  forward: forward,
  inverse: inverse,
  names: names
});


/***/ }),
/* 80 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony export (immutable) */ __webpack_exports__["init"] = init;
/* harmony export (immutable) */ __webpack_exports__["forward"] = forward;
/* harmony export (immutable) */ __webpack_exports__["inverse"] = inverse;
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "names", function() { return names; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__common_mlfn__ = __webpack_require__(29);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__common_e0fn__ = __webpack_require__(30);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__common_e1fn__ = __webpack_require__(31);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__common_e2fn__ = __webpack_require__(32);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__common_e3fn__ = __webpack_require__(33);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__common_gN__ = __webpack_require__(44);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_6__common_adjust_lon__ = __webpack_require__(5);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_7__common_adjust_lat__ = __webpack_require__(21);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_8__common_imlfn__ = __webpack_require__(45);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_9__constants_values__ = __webpack_require__(4);











function init() {
  if (!this.sphere) {
    this.e0 = Object(__WEBPACK_IMPORTED_MODULE_1__common_e0fn__["a" /* default */])(this.es);
    this.e1 = Object(__WEBPACK_IMPORTED_MODULE_2__common_e1fn__["a" /* default */])(this.es);
    this.e2 = Object(__WEBPACK_IMPORTED_MODULE_3__common_e2fn__["a" /* default */])(this.es);
    this.e3 = Object(__WEBPACK_IMPORTED_MODULE_4__common_e3fn__["a" /* default */])(this.es);
    this.ml0 = this.a * Object(__WEBPACK_IMPORTED_MODULE_0__common_mlfn__["a" /* default */])(this.e0, this.e1, this.e2, this.e3, this.lat0);
  }
}

/* Cassini forward equations--mapping lat,long to x,y
  -----------------------------------------------------------------------*/
function forward(p) {

  /* Forward equations
      -----------------*/
  var x, y;
  var lam = p.x;
  var phi = p.y;
  lam = Object(__WEBPACK_IMPORTED_MODULE_6__common_adjust_lon__["a" /* default */])(lam - this.long0);

  if (this.sphere) {
    x = this.a * Math.asin(Math.cos(phi) * Math.sin(lam));
    y = this.a * (Math.atan2(Math.tan(phi), Math.cos(lam)) - this.lat0);
  }
  else {
    //ellipsoid
    var sinphi = Math.sin(phi);
    var cosphi = Math.cos(phi);
    var nl = Object(__WEBPACK_IMPORTED_MODULE_5__common_gN__["a" /* default */])(this.a, this.e, sinphi);
    var tl = Math.tan(phi) * Math.tan(phi);
    var al = lam * Math.cos(phi);
    var asq = al * al;
    var cl = this.es * cosphi * cosphi / (1 - this.es);
    var ml = this.a * Object(__WEBPACK_IMPORTED_MODULE_0__common_mlfn__["a" /* default */])(this.e0, this.e1, this.e2, this.e3, phi);

    x = nl * al * (1 - asq * tl * (1 / 6 - (8 - tl + 8 * cl) * asq / 120));
    y = ml - this.ml0 + nl * sinphi / cosphi * asq * (0.5 + (5 - tl + 6 * cl) * asq / 24);


  }

  p.x = x + this.x0;
  p.y = y + this.y0;
  return p;
}

/* Inverse equations
  -----------------*/
function inverse(p) {
  p.x -= this.x0;
  p.y -= this.y0;
  var x = p.x / this.a;
  var y = p.y / this.a;
  var phi, lam;

  if (this.sphere) {
    var dd = y + this.lat0;
    phi = Math.asin(Math.sin(dd) * Math.cos(x));
    lam = Math.atan2(Math.tan(x), Math.cos(dd));
  }
  else {
    /* ellipsoid */
    var ml1 = this.ml0 / this.a + y;
    var phi1 = Object(__WEBPACK_IMPORTED_MODULE_8__common_imlfn__["a" /* default */])(ml1, this.e0, this.e1, this.e2, this.e3);
    if (Math.abs(Math.abs(phi1) - __WEBPACK_IMPORTED_MODULE_9__constants_values__["d" /* HALF_PI */]) <= __WEBPACK_IMPORTED_MODULE_9__constants_values__["b" /* EPSLN */]) {
      p.x = this.long0;
      p.y = __WEBPACK_IMPORTED_MODULE_9__constants_values__["d" /* HALF_PI */];
      if (y < 0) {
        p.y *= -1;
      }
      return p;
    }
    var nl1 = Object(__WEBPACK_IMPORTED_MODULE_5__common_gN__["a" /* default */])(this.a, this.e, Math.sin(phi1));

    var rl1 = nl1 * nl1 * nl1 / this.a / this.a * (1 - this.es);
    var tl1 = Math.pow(Math.tan(phi1), 2);
    var dl = x * this.a / nl1;
    var dsq = dl * dl;
    phi = phi1 - nl1 * Math.tan(phi1) / rl1 * dl * dl * (0.5 - (1 + 3 * tl1) * dl * dl / 24);
    lam = dl * (1 - dsq * (tl1 / 3 + (1 + 3 * tl1) * tl1 * dsq / 15)) / Math.cos(phi1);

  }

  p.x = Object(__WEBPACK_IMPORTED_MODULE_6__common_adjust_lon__["a" /* default */])(lam + this.long0);
  p.y = Object(__WEBPACK_IMPORTED_MODULE_7__common_adjust_lat__["a" /* default */])(phi);
  return p;

}

var names = ["Cassini", "Cassini_Soldner", "cass"];
/* harmony default export */ __webpack_exports__["default"] = ({
  init: init,
  forward: forward,
  inverse: inverse,
  names: names
});


/***/ }),
/* 81 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "S_POLE", function() { return S_POLE; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "N_POLE", function() { return N_POLE; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "EQUIT", function() { return EQUIT; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "OBLIQ", function() { return OBLIQ; });
/* harmony export (immutable) */ __webpack_exports__["init"] = init;
/* harmony export (immutable) */ __webpack_exports__["forward"] = forward;
/* harmony export (immutable) */ __webpack_exports__["inverse"] = inverse;
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "names", function() { return names; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__constants_values__ = __webpack_require__(4);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__common_qsfnz__ = __webpack_require__(46);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__common_adjust_lon__ = __webpack_require__(5);






/*
  reference
    "New Equal-Area Map Projections for Noncircular Regions", John P. Snyder,
    The American Cartographer, Vol 15, No. 4, October 1988, pp. 341-355.
  */

var S_POLE = 1;

var N_POLE = 2;
var EQUIT = 3;
var OBLIQ = 4;

/* Initialize the Lambert Azimuthal Equal Area projection
  ------------------------------------------------------*/
function init() {
  var t = Math.abs(this.lat0);
  if (Math.abs(t - __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */]) < __WEBPACK_IMPORTED_MODULE_0__constants_values__["b" /* EPSLN */]) {
    this.mode = this.lat0 < 0 ? this.S_POLE : this.N_POLE;
  }
  else if (Math.abs(t) < __WEBPACK_IMPORTED_MODULE_0__constants_values__["b" /* EPSLN */]) {
    this.mode = this.EQUIT;
  }
  else {
    this.mode = this.OBLIQ;
  }
  if (this.es > 0) {
    var sinphi;

    this.qp = Object(__WEBPACK_IMPORTED_MODULE_1__common_qsfnz__["a" /* default */])(this.e, 1);
    this.mmf = 0.5 / (1 - this.es);
    this.apa = authset(this.es);
    switch (this.mode) {
    case this.N_POLE:
      this.dd = 1;
      break;
    case this.S_POLE:
      this.dd = 1;
      break;
    case this.EQUIT:
      this.rq = Math.sqrt(0.5 * this.qp);
      this.dd = 1 / this.rq;
      this.xmf = 1;
      this.ymf = 0.5 * this.qp;
      break;
    case this.OBLIQ:
      this.rq = Math.sqrt(0.5 * this.qp);
      sinphi = Math.sin(this.lat0);
      this.sinb1 = Object(__WEBPACK_IMPORTED_MODULE_1__common_qsfnz__["a" /* default */])(this.e, sinphi) / this.qp;
      this.cosb1 = Math.sqrt(1 - this.sinb1 * this.sinb1);
      this.dd = Math.cos(this.lat0) / (Math.sqrt(1 - this.es * sinphi * sinphi) * this.rq * this.cosb1);
      this.ymf = (this.xmf = this.rq) / this.dd;
      this.xmf *= this.dd;
      break;
    }
  }
  else {
    if (this.mode === this.OBLIQ) {
      this.sinph0 = Math.sin(this.lat0);
      this.cosph0 = Math.cos(this.lat0);
    }
  }
}

/* Lambert Azimuthal Equal Area forward equations--mapping lat,long to x,y
  -----------------------------------------------------------------------*/
function forward(p) {

  /* Forward equations
      -----------------*/
  var x, y, coslam, sinlam, sinphi, q, sinb, cosb, b, cosphi;
  var lam = p.x;
  var phi = p.y;

  lam = Object(__WEBPACK_IMPORTED_MODULE_2__common_adjust_lon__["a" /* default */])(lam - this.long0);
  if (this.sphere) {
    sinphi = Math.sin(phi);
    cosphi = Math.cos(phi);
    coslam = Math.cos(lam);
    if (this.mode === this.OBLIQ || this.mode === this.EQUIT) {
      y = (this.mode === this.EQUIT) ? 1 + cosphi * coslam : 1 + this.sinph0 * sinphi + this.cosph0 * cosphi * coslam;
      if (y <= __WEBPACK_IMPORTED_MODULE_0__constants_values__["b" /* EPSLN */]) {
        return null;
      }
      y = Math.sqrt(2 / y);
      x = y * cosphi * Math.sin(lam);
      y *= (this.mode === this.EQUIT) ? sinphi : this.cosph0 * sinphi - this.sinph0 * cosphi * coslam;
    }
    else if (this.mode === this.N_POLE || this.mode === this.S_POLE) {
      if (this.mode === this.N_POLE) {
        coslam = -coslam;
      }
      if (Math.abs(phi + this.phi0) < __WEBPACK_IMPORTED_MODULE_0__constants_values__["b" /* EPSLN */]) {
        return null;
      }
      y = __WEBPACK_IMPORTED_MODULE_0__constants_values__["c" /* FORTPI */] - phi * 0.5;
      y = 2 * ((this.mode === this.S_POLE) ? Math.cos(y) : Math.sin(y));
      x = y * Math.sin(lam);
      y *= coslam;
    }
  }
  else {
    sinb = 0;
    cosb = 0;
    b = 0;
    coslam = Math.cos(lam);
    sinlam = Math.sin(lam);
    sinphi = Math.sin(phi);
    q = Object(__WEBPACK_IMPORTED_MODULE_1__common_qsfnz__["a" /* default */])(this.e, sinphi);
    if (this.mode === this.OBLIQ || this.mode === this.EQUIT) {
      sinb = q / this.qp;
      cosb = Math.sqrt(1 - sinb * sinb);
    }
    switch (this.mode) {
    case this.OBLIQ:
      b = 1 + this.sinb1 * sinb + this.cosb1 * cosb * coslam;
      break;
    case this.EQUIT:
      b = 1 + cosb * coslam;
      break;
    case this.N_POLE:
      b = __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */] + phi;
      q = this.qp - q;
      break;
    case this.S_POLE:
      b = phi - __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */];
      q = this.qp + q;
      break;
    }
    if (Math.abs(b) < __WEBPACK_IMPORTED_MODULE_0__constants_values__["b" /* EPSLN */]) {
      return null;
    }
    switch (this.mode) {
    case this.OBLIQ:
    case this.EQUIT:
      b = Math.sqrt(2 / b);
      if (this.mode === this.OBLIQ) {
        y = this.ymf * b * (this.cosb1 * sinb - this.sinb1 * cosb * coslam);
      }
      else {
        y = (b = Math.sqrt(2 / (1 + cosb * coslam))) * sinb * this.ymf;
      }
      x = this.xmf * b * cosb * sinlam;
      break;
    case this.N_POLE:
    case this.S_POLE:
      if (q >= 0) {
        x = (b = Math.sqrt(q)) * sinlam;
        y = coslam * ((this.mode === this.S_POLE) ? b : -b);
      }
      else {
        x = y = 0;
      }
      break;
    }
  }

  p.x = this.a * x + this.x0;
  p.y = this.a * y + this.y0;
  return p;
}

/* Inverse equations
  -----------------*/
function inverse(p) {
  p.x -= this.x0;
  p.y -= this.y0;
  var x = p.x / this.a;
  var y = p.y / this.a;
  var lam, phi, cCe, sCe, q, rho, ab;
  if (this.sphere) {
    var cosz = 0,
      rh, sinz = 0;

    rh = Math.sqrt(x * x + y * y);
    phi = rh * 0.5;
    if (phi > 1) {
      return null;
    }
    phi = 2 * Math.asin(phi);
    if (this.mode === this.OBLIQ || this.mode === this.EQUIT) {
      sinz = Math.sin(phi);
      cosz = Math.cos(phi);
    }
    switch (this.mode) {
    case this.EQUIT:
      phi = (Math.abs(rh) <= __WEBPACK_IMPORTED_MODULE_0__constants_values__["b" /* EPSLN */]) ? 0 : Math.asin(y * sinz / rh);
      x *= sinz;
      y = cosz * rh;
      break;
    case this.OBLIQ:
      phi = (Math.abs(rh) <= __WEBPACK_IMPORTED_MODULE_0__constants_values__["b" /* EPSLN */]) ? this.phi0 : Math.asin(cosz * this.sinph0 + y * sinz * this.cosph0 / rh);
      x *= sinz * this.cosph0;
      y = (cosz - Math.sin(phi) * this.sinph0) * rh;
      break;
    case this.N_POLE:
      y = -y;
      phi = __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */] - phi;
      break;
    case this.S_POLE:
      phi -= __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */];
      break;
    }
    lam = (y === 0 && (this.mode === this.EQUIT || this.mode === this.OBLIQ)) ? 0 : Math.atan2(x, y);
  }
  else {
    ab = 0;
    if (this.mode === this.OBLIQ || this.mode === this.EQUIT) {
      x /= this.dd;
      y *= this.dd;
      rho = Math.sqrt(x * x + y * y);
      if (rho < __WEBPACK_IMPORTED_MODULE_0__constants_values__["b" /* EPSLN */]) {
        p.x = 0;
        p.y = this.phi0;
        return p;
      }
      sCe = 2 * Math.asin(0.5 * rho / this.rq);
      cCe = Math.cos(sCe);
      x *= (sCe = Math.sin(sCe));
      if (this.mode === this.OBLIQ) {
        ab = cCe * this.sinb1 + y * sCe * this.cosb1 / rho;
        q = this.qp * ab;
        y = rho * this.cosb1 * cCe - y * this.sinb1 * sCe;
      }
      else {
        ab = y * sCe / rho;
        q = this.qp * ab;
        y = rho * cCe;
      }
    }
    else if (this.mode === this.N_POLE || this.mode === this.S_POLE) {
      if (this.mode === this.N_POLE) {
        y = -y;
      }
      q = (x * x + y * y);
      if (!q) {
        p.x = 0;
        p.y = this.phi0;
        return p;
      }
      ab = 1 - q / this.qp;
      if (this.mode === this.S_POLE) {
        ab = -ab;
      }
    }
    lam = Math.atan2(x, y);
    phi = authlat(Math.asin(ab), this.apa);
  }

  p.x = Object(__WEBPACK_IMPORTED_MODULE_2__common_adjust_lon__["a" /* default */])(this.long0 + lam);
  p.y = phi;
  return p;
}

/* determine latitude from authalic latitude */
var P00 = 0.33333333333333333333;

var P01 = 0.17222222222222222222;
var P02 = 0.10257936507936507936;
var P10 = 0.06388888888888888888;
var P11 = 0.06640211640211640211;
var P20 = 0.01641501294219154443;

function authset(es) {
  var t;
  var APA = [];
  APA[0] = es * P00;
  t = es * es;
  APA[0] += t * P01;
  APA[1] = t * P10;
  t *= es;
  APA[0] += t * P02;
  APA[1] += t * P11;
  APA[2] = t * P20;
  return APA;
}

function authlat(beta, APA) {
  var t = beta + beta;
  return (beta + APA[0] * Math.sin(t) + APA[1] * Math.sin(t + t) + APA[2] * Math.sin(t + t + t));
}

var names = ["Lambert Azimuthal Equal Area", "Lambert_Azimuthal_Equal_Area", "laea"];
/* harmony default export */ __webpack_exports__["default"] = ({
  init: init,
  forward: forward,
  inverse: inverse,
  names: names,
  S_POLE: S_POLE,
  N_POLE: N_POLE,
  EQUIT: EQUIT,
  OBLIQ: OBLIQ
});


/***/ }),
/* 82 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony export (immutable) */ __webpack_exports__["init"] = init;
/* harmony export (immutable) */ __webpack_exports__["forward"] = forward;
/* harmony export (immutable) */ __webpack_exports__["inverse"] = inverse;
/* harmony export (immutable) */ __webpack_exports__["phi1z"] = phi1z;
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "names", function() { return names; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__common_msfnz__ = __webpack_require__(15);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__common_qsfnz__ = __webpack_require__(46);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__common_adjust_lon__ = __webpack_require__(5);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__common_asinz__ = __webpack_require__(16);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__constants_values__ = __webpack_require__(4);






function init() {

  if (Math.abs(this.lat1 + this.lat2) < __WEBPACK_IMPORTED_MODULE_4__constants_values__["b" /* EPSLN */]) {
    return;
  }
  this.temp = this.b / this.a;
  this.es = 1 - Math.pow(this.temp, 2);
  this.e3 = Math.sqrt(this.es);

  this.sin_po = Math.sin(this.lat1);
  this.cos_po = Math.cos(this.lat1);
  this.t1 = this.sin_po;
  this.con = this.sin_po;
  this.ms1 = Object(__WEBPACK_IMPORTED_MODULE_0__common_msfnz__["a" /* default */])(this.e3, this.sin_po, this.cos_po);
  this.qs1 = Object(__WEBPACK_IMPORTED_MODULE_1__common_qsfnz__["a" /* default */])(this.e3, this.sin_po, this.cos_po);

  this.sin_po = Math.sin(this.lat2);
  this.cos_po = Math.cos(this.lat2);
  this.t2 = this.sin_po;
  this.ms2 = Object(__WEBPACK_IMPORTED_MODULE_0__common_msfnz__["a" /* default */])(this.e3, this.sin_po, this.cos_po);
  this.qs2 = Object(__WEBPACK_IMPORTED_MODULE_1__common_qsfnz__["a" /* default */])(this.e3, this.sin_po, this.cos_po);

  this.sin_po = Math.sin(this.lat0);
  this.cos_po = Math.cos(this.lat0);
  this.t3 = this.sin_po;
  this.qs0 = Object(__WEBPACK_IMPORTED_MODULE_1__common_qsfnz__["a" /* default */])(this.e3, this.sin_po, this.cos_po);

  if (Math.abs(this.lat1 - this.lat2) > __WEBPACK_IMPORTED_MODULE_4__constants_values__["b" /* EPSLN */]) {
    this.ns0 = (this.ms1 * this.ms1 - this.ms2 * this.ms2) / (this.qs2 - this.qs1);
  }
  else {
    this.ns0 = this.con;
  }
  this.c = this.ms1 * this.ms1 + this.ns0 * this.qs1;
  this.rh = this.a * Math.sqrt(this.c - this.ns0 * this.qs0) / this.ns0;
}

/* Albers Conical Equal Area forward equations--mapping lat,long to x,y
  -------------------------------------------------------------------*/
function forward(p) {

  var lon = p.x;
  var lat = p.y;

  this.sin_phi = Math.sin(lat);
  this.cos_phi = Math.cos(lat);

  var qs = Object(__WEBPACK_IMPORTED_MODULE_1__common_qsfnz__["a" /* default */])(this.e3, this.sin_phi, this.cos_phi);
  var rh1 = this.a * Math.sqrt(this.c - this.ns0 * qs) / this.ns0;
  var theta = this.ns0 * Object(__WEBPACK_IMPORTED_MODULE_2__common_adjust_lon__["a" /* default */])(lon - this.long0);
  var x = rh1 * Math.sin(theta) + this.x0;
  var y = this.rh - rh1 * Math.cos(theta) + this.y0;

  p.x = x;
  p.y = y;
  return p;
}

function inverse(p) {
  var rh1, qs, con, theta, lon, lat;

  p.x -= this.x0;
  p.y = this.rh - p.y + this.y0;
  if (this.ns0 >= 0) {
    rh1 = Math.sqrt(p.x * p.x + p.y * p.y);
    con = 1;
  }
  else {
    rh1 = -Math.sqrt(p.x * p.x + p.y * p.y);
    con = -1;
  }
  theta = 0;
  if (rh1 !== 0) {
    theta = Math.atan2(con * p.x, con * p.y);
  }
  con = rh1 * this.ns0 / this.a;
  if (this.sphere) {
    lat = Math.asin((this.c - con * con) / (2 * this.ns0));
  }
  else {
    qs = (this.c - con * con) / this.ns0;
    lat = this.phi1z(this.e3, qs);
  }

  lon = Object(__WEBPACK_IMPORTED_MODULE_2__common_adjust_lon__["a" /* default */])(theta / this.ns0 + this.long0);
  p.x = lon;
  p.y = lat;
  return p;
}

/* Function to compute phi1, the latitude for the inverse of the
   Albers Conical Equal-Area projection.
-------------------------------------------*/
function phi1z(eccent, qs) {
  var sinphi, cosphi, con, com, dphi;
  var phi = Object(__WEBPACK_IMPORTED_MODULE_3__common_asinz__["a" /* default */])(0.5 * qs);
  if (eccent < __WEBPACK_IMPORTED_MODULE_4__constants_values__["b" /* EPSLN */]) {
    return phi;
  }

  var eccnts = eccent * eccent;
  for (var i = 1; i <= 25; i++) {
    sinphi = Math.sin(phi);
    cosphi = Math.cos(phi);
    con = eccent * sinphi;
    com = 1 - con * con;
    dphi = 0.5 * com * com / cosphi * (qs / (1 - eccnts) - sinphi / com + 0.5 / eccent * Math.log((1 - con) / (1 + con)));
    phi = phi + dphi;
    if (Math.abs(dphi) <= 1e-7) {
      return phi;
    }
  }
  return null;
}

var names = ["Albers_Conic_Equal_Area", "Albers", "aea"];
/* harmony default export */ __webpack_exports__["default"] = ({
  init: init,
  forward: forward,
  inverse: inverse,
  names: names,
  phi1z: phi1z
});


/***/ }),
/* 83 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony export (immutable) */ __webpack_exports__["init"] = init;
/* harmony export (immutable) */ __webpack_exports__["forward"] = forward;
/* harmony export (immutable) */ __webpack_exports__["inverse"] = inverse;
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "names", function() { return names; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__common_adjust_lon__ = __webpack_require__(5);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__common_asinz__ = __webpack_require__(16);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__constants_values__ = __webpack_require__(4);




/*
  reference:
    Wolfram Mathworld "Gnomonic Projection"
    http://mathworld.wolfram.com/GnomonicProjection.html
    Accessed: 12th November 2009
  */
function init() {

  /* Place parameters in static storage for common use
      -------------------------------------------------*/
  this.sin_p14 = Math.sin(this.lat0);
  this.cos_p14 = Math.cos(this.lat0);
  // Approximation for projecting points to the horizon (infinity)
  this.infinity_dist = 1000 * this.a;
  this.rc = 1;
}

/* Gnomonic forward equations--mapping lat,long to x,y
    ---------------------------------------------------*/
function forward(p) {
  var sinphi, cosphi; /* sin and cos value        */
  var dlon; /* delta longitude value      */
  var coslon; /* cos of longitude        */
  var ksp; /* scale factor          */
  var g;
  var x, y;
  var lon = p.x;
  var lat = p.y;
  /* Forward equations
      -----------------*/
  dlon = Object(__WEBPACK_IMPORTED_MODULE_0__common_adjust_lon__["a" /* default */])(lon - this.long0);

  sinphi = Math.sin(lat);
  cosphi = Math.cos(lat);

  coslon = Math.cos(dlon);
  g = this.sin_p14 * sinphi + this.cos_p14 * cosphi * coslon;
  ksp = 1;
  if ((g > 0) || (Math.abs(g) <= __WEBPACK_IMPORTED_MODULE_2__constants_values__["b" /* EPSLN */])) {
    x = this.x0 + this.a * ksp * cosphi * Math.sin(dlon) / g;
    y = this.y0 + this.a * ksp * (this.cos_p14 * sinphi - this.sin_p14 * cosphi * coslon) / g;
  }
  else {

    // Point is in the opposing hemisphere and is unprojectable
    // We still need to return a reasonable point, so we project
    // to infinity, on a bearing
    // equivalent to the northern hemisphere equivalent
    // This is a reasonable approximation for short shapes and lines that
    // straddle the horizon.

    x = this.x0 + this.infinity_dist * cosphi * Math.sin(dlon);
    y = this.y0 + this.infinity_dist * (this.cos_p14 * sinphi - this.sin_p14 * cosphi * coslon);

  }
  p.x = x;
  p.y = y;
  return p;
}

function inverse(p) {
  var rh; /* Rho */
  var sinc, cosc;
  var c;
  var lon, lat;

  /* Inverse equations
      -----------------*/
  p.x = (p.x - this.x0) / this.a;
  p.y = (p.y - this.y0) / this.a;

  p.x /= this.k0;
  p.y /= this.k0;

  if ((rh = Math.sqrt(p.x * p.x + p.y * p.y))) {
    c = Math.atan2(rh, this.rc);
    sinc = Math.sin(c);
    cosc = Math.cos(c);

    lat = Object(__WEBPACK_IMPORTED_MODULE_1__common_asinz__["a" /* default */])(cosc * this.sin_p14 + (p.y * sinc * this.cos_p14) / rh);
    lon = Math.atan2(p.x * sinc, rh * this.cos_p14 * cosc - p.y * this.sin_p14 * sinc);
    lon = Object(__WEBPACK_IMPORTED_MODULE_0__common_adjust_lon__["a" /* default */])(this.long0 + lon);
  }
  else {
    lat = this.phic0;
    lon = 0;
  }

  p.x = lon;
  p.y = lat;
  return p;
}

var names = ["gnom"];
/* harmony default export */ __webpack_exports__["default"] = ({
  init: init,
  forward: forward,
  inverse: inverse,
  names: names
});


/***/ }),
/* 84 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony export (immutable) */ __webpack_exports__["init"] = init;
/* harmony export (immutable) */ __webpack_exports__["forward"] = forward;
/* harmony export (immutable) */ __webpack_exports__["inverse"] = inverse;
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "names", function() { return names; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__common_adjust_lon__ = __webpack_require__(5);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__common_qsfnz__ = __webpack_require__(46);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__common_msfnz__ = __webpack_require__(15);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__common_iqsfnz__ = __webpack_require__(165);





/*
  reference:
    "Cartographic Projection Procedures for the UNIX Environment-
    A User's Manual" by Gerald I. Evenden,
    USGS Open File Report 90-284and Release 4 Interim Reports (2003)
*/
function init() {
  //no-op
  if (!this.sphere) {
    this.k0 = Object(__WEBPACK_IMPORTED_MODULE_2__common_msfnz__["a" /* default */])(this.e, Math.sin(this.lat_ts), Math.cos(this.lat_ts));
  }
}

/* Cylindrical Equal Area forward equations--mapping lat,long to x,y
    ------------------------------------------------------------*/
function forward(p) {
  var lon = p.x;
  var lat = p.y;
  var x, y;
  /* Forward equations
      -----------------*/
  var dlon = Object(__WEBPACK_IMPORTED_MODULE_0__common_adjust_lon__["a" /* default */])(lon - this.long0);
  if (this.sphere) {
    x = this.x0 + this.a * dlon * Math.cos(this.lat_ts);
    y = this.y0 + this.a * Math.sin(lat) / Math.cos(this.lat_ts);
  }
  else {
    var qs = Object(__WEBPACK_IMPORTED_MODULE_1__common_qsfnz__["a" /* default */])(this.e, Math.sin(lat));
    x = this.x0 + this.a * this.k0 * dlon;
    y = this.y0 + this.a * qs * 0.5 / this.k0;
  }

  p.x = x;
  p.y = y;
  return p;
}

/* Cylindrical Equal Area inverse equations--mapping x,y to lat/long
    ------------------------------------------------------------*/
function inverse(p) {
  p.x -= this.x0;
  p.y -= this.y0;
  var lon, lat;

  if (this.sphere) {
    lon = Object(__WEBPACK_IMPORTED_MODULE_0__common_adjust_lon__["a" /* default */])(this.long0 + (p.x / this.a) / Math.cos(this.lat_ts));
    lat = Math.asin((p.y / this.a) * Math.cos(this.lat_ts));
  }
  else {
    lat = Object(__WEBPACK_IMPORTED_MODULE_3__common_iqsfnz__["a" /* default */])(this.e, 2 * p.y * this.k0 / this.a);
    lon = Object(__WEBPACK_IMPORTED_MODULE_0__common_adjust_lon__["a" /* default */])(this.long0 + p.x / (this.a * this.k0));
  }

  p.x = lon;
  p.y = lat;
  return p;
}

var names = ["cea"];
/* harmony default export */ __webpack_exports__["default"] = ({
  init: init,
  forward: forward,
  inverse: inverse,
  names: names
});


/***/ }),
/* 85 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony export (immutable) */ __webpack_exports__["init"] = init;
/* harmony export (immutable) */ __webpack_exports__["forward"] = forward;
/* harmony export (immutable) */ __webpack_exports__["inverse"] = inverse;
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "names", function() { return names; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__common_adjust_lon__ = __webpack_require__(5);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__common_adjust_lat__ = __webpack_require__(21);



function init() {

  this.x0 = this.x0 || 0;
  this.y0 = this.y0 || 0;
  this.lat0 = this.lat0 || 0;
  this.long0 = this.long0 || 0;
  this.lat_ts = this.lat_ts || 0;
  this.title = this.title || "Equidistant Cylindrical (Plate Carre)";

  this.rc = Math.cos(this.lat_ts);
}

// forward equations--mapping lat,long to x,y
// -----------------------------------------------------------------
function forward(p) {

  var lon = p.x;
  var lat = p.y;

  var dlon = Object(__WEBPACK_IMPORTED_MODULE_0__common_adjust_lon__["a" /* default */])(lon - this.long0);
  var dlat = Object(__WEBPACK_IMPORTED_MODULE_1__common_adjust_lat__["a" /* default */])(lat - this.lat0);
  p.x = this.x0 + (this.a * dlon * this.rc);
  p.y = this.y0 + (this.a * dlat);
  return p;
}

// inverse equations--mapping x,y to lat/long
// -----------------------------------------------------------------
function inverse(p) {

  var x = p.x;
  var y = p.y;

  p.x = Object(__WEBPACK_IMPORTED_MODULE_0__common_adjust_lon__["a" /* default */])(this.long0 + ((x - this.x0) / (this.a * this.rc)));
  p.y = Object(__WEBPACK_IMPORTED_MODULE_1__common_adjust_lat__["a" /* default */])(this.lat0 + ((y - this.y0) / (this.a)));
  return p;
}

var names = ["Equirectangular", "Equidistant_Cylindrical", "eqc"];
/* harmony default export */ __webpack_exports__["default"] = ({
  init: init,
  forward: forward,
  inverse: inverse,
  names: names
});


/***/ }),
/* 86 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony export (immutable) */ __webpack_exports__["init"] = init;
/* harmony export (immutable) */ __webpack_exports__["forward"] = forward;
/* harmony export (immutable) */ __webpack_exports__["inverse"] = inverse;
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "names", function() { return names; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__common_e0fn__ = __webpack_require__(30);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__common_e1fn__ = __webpack_require__(31);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__common_e2fn__ = __webpack_require__(32);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__common_e3fn__ = __webpack_require__(33);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__common_adjust_lon__ = __webpack_require__(5);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__common_adjust_lat__ = __webpack_require__(21);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_6__common_mlfn__ = __webpack_require__(29);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_7__constants_values__ = __webpack_require__(4);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_8__common_gN__ = __webpack_require__(44);










var MAX_ITER = 20;

function init() {
  /* Place parameters in static storage for common use
      -------------------------------------------------*/
  this.temp = this.b / this.a;
  this.es = 1 - Math.pow(this.temp, 2); // devait etre dans tmerc.js mais n y est pas donc je commente sinon retour de valeurs nulles
  this.e = Math.sqrt(this.es);
  this.e0 = Object(__WEBPACK_IMPORTED_MODULE_0__common_e0fn__["a" /* default */])(this.es);
  this.e1 = Object(__WEBPACK_IMPORTED_MODULE_1__common_e1fn__["a" /* default */])(this.es);
  this.e2 = Object(__WEBPACK_IMPORTED_MODULE_2__common_e2fn__["a" /* default */])(this.es);
  this.e3 = Object(__WEBPACK_IMPORTED_MODULE_3__common_e3fn__["a" /* default */])(this.es);
  this.ml0 = this.a * Object(__WEBPACK_IMPORTED_MODULE_6__common_mlfn__["a" /* default */])(this.e0, this.e1, this.e2, this.e3, this.lat0); //si que des zeros le calcul ne se fait pas
}

/* Polyconic forward equations--mapping lat,long to x,y
    ---------------------------------------------------*/
function forward(p) {
  var lon = p.x;
  var lat = p.y;
  var x, y, el;
  var dlon = Object(__WEBPACK_IMPORTED_MODULE_4__common_adjust_lon__["a" /* default */])(lon - this.long0);
  el = dlon * Math.sin(lat);
  if (this.sphere) {
    if (Math.abs(lat) <= __WEBPACK_IMPORTED_MODULE_7__constants_values__["b" /* EPSLN */]) {
      x = this.a * dlon;
      y = -1 * this.a * this.lat0;
    }
    else {
      x = this.a * Math.sin(el) / Math.tan(lat);
      y = this.a * (Object(__WEBPACK_IMPORTED_MODULE_5__common_adjust_lat__["a" /* default */])(lat - this.lat0) + (1 - Math.cos(el)) / Math.tan(lat));
    }
  }
  else {
    if (Math.abs(lat) <= __WEBPACK_IMPORTED_MODULE_7__constants_values__["b" /* EPSLN */]) {
      x = this.a * dlon;
      y = -1 * this.ml0;
    }
    else {
      var nl = Object(__WEBPACK_IMPORTED_MODULE_8__common_gN__["a" /* default */])(this.a, this.e, Math.sin(lat)) / Math.tan(lat);
      x = nl * Math.sin(el);
      y = this.a * Object(__WEBPACK_IMPORTED_MODULE_6__common_mlfn__["a" /* default */])(this.e0, this.e1, this.e2, this.e3, lat) - this.ml0 + nl * (1 - Math.cos(el));
    }

  }
  p.x = x + this.x0;
  p.y = y + this.y0;
  return p;
}

/* Inverse equations
  -----------------*/
function inverse(p) {
  var lon, lat, x, y, i;
  var al, bl;
  var phi, dphi;
  x = p.x - this.x0;
  y = p.y - this.y0;

  if (this.sphere) {
    if (Math.abs(y + this.a * this.lat0) <= __WEBPACK_IMPORTED_MODULE_7__constants_values__["b" /* EPSLN */]) {
      lon = Object(__WEBPACK_IMPORTED_MODULE_4__common_adjust_lon__["a" /* default */])(x / this.a + this.long0);
      lat = 0;
    }
    else {
      al = this.lat0 + y / this.a;
      bl = x * x / this.a / this.a + al * al;
      phi = al;
      var tanphi;
      for (i = MAX_ITER; i; --i) {
        tanphi = Math.tan(phi);
        dphi = -1 * (al * (phi * tanphi + 1) - phi - 0.5 * (phi * phi + bl) * tanphi) / ((phi - al) / tanphi - 1);
        phi += dphi;
        if (Math.abs(dphi) <= __WEBPACK_IMPORTED_MODULE_7__constants_values__["b" /* EPSLN */]) {
          lat = phi;
          break;
        }
      }
      lon = Object(__WEBPACK_IMPORTED_MODULE_4__common_adjust_lon__["a" /* default */])(this.long0 + (Math.asin(x * Math.tan(phi) / this.a)) / Math.sin(lat));
    }
  }
  else {
    if (Math.abs(y + this.ml0) <= __WEBPACK_IMPORTED_MODULE_7__constants_values__["b" /* EPSLN */]) {
      lat = 0;
      lon = Object(__WEBPACK_IMPORTED_MODULE_4__common_adjust_lon__["a" /* default */])(this.long0 + x / this.a);
    }
    else {

      al = (this.ml0 + y) / this.a;
      bl = x * x / this.a / this.a + al * al;
      phi = al;
      var cl, mln, mlnp, ma;
      var con;
      for (i = MAX_ITER; i; --i) {
        con = this.e * Math.sin(phi);
        cl = Math.sqrt(1 - con * con) * Math.tan(phi);
        mln = this.a * Object(__WEBPACK_IMPORTED_MODULE_6__common_mlfn__["a" /* default */])(this.e0, this.e1, this.e2, this.e3, phi);
        mlnp = this.e0 - 2 * this.e1 * Math.cos(2 * phi) + 4 * this.e2 * Math.cos(4 * phi) - 6 * this.e3 * Math.cos(6 * phi);
        ma = mln / this.a;
        dphi = (al * (cl * ma + 1) - ma - 0.5 * cl * (ma * ma + bl)) / (this.es * Math.sin(2 * phi) * (ma * ma + bl - 2 * al * ma) / (4 * cl) + (al - ma) * (cl * mlnp - 2 / Math.sin(2 * phi)) - mlnp);
        phi -= dphi;
        if (Math.abs(dphi) <= __WEBPACK_IMPORTED_MODULE_7__constants_values__["b" /* EPSLN */]) {
          lat = phi;
          break;
        }
      }

      //lat=phi4z(this.e,this.e0,this.e1,this.e2,this.e3,al,bl,0,0);
      cl = Math.sqrt(1 - this.es * Math.pow(Math.sin(lat), 2)) * Math.tan(lat);
      lon = Object(__WEBPACK_IMPORTED_MODULE_4__common_adjust_lon__["a" /* default */])(this.long0 + Math.asin(x * cl / this.a) / Math.sin(lat));
    }
  }

  p.x = lon;
  p.y = lat;
  return p;
}

var names = ["Polyconic", "poly"];
/* harmony default export */ __webpack_exports__["default"] = ({
  init: init,
  forward: forward,
  inverse: inverse,
  names: names
});


/***/ }),
/* 87 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "iterations", function() { return iterations; });
/* harmony export (immutable) */ __webpack_exports__["init"] = init;
/* harmony export (immutable) */ __webpack_exports__["forward"] = forward;
/* harmony export (immutable) */ __webpack_exports__["inverse"] = inverse;
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "names", function() { return names; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__constants_values__ = __webpack_require__(4);


/*
  reference
    Department of Land and Survey Technical Circular 1973/32
      http://www.linz.govt.nz/docs/miscellaneous/nz-map-definition.pdf
    OSG Technical Report 4.1
      http://www.linz.govt.nz/docs/miscellaneous/nzmg.pdf
  */

/**
 * iterations: Number of iterations to refine inverse transform.
 *     0 -> km accuracy
 *     1 -> m accuracy -- suitable for most mapping applications
 *     2 -> mm accuracy
 */
var iterations = 1;

function init() {
  this.A = [];
  this.A[1] = 0.6399175073;
  this.A[2] = -0.1358797613;
  this.A[3] = 0.063294409;
  this.A[4] = -0.02526853;
  this.A[5] = 0.0117879;
  this.A[6] = -0.0055161;
  this.A[7] = 0.0026906;
  this.A[8] = -0.001333;
  this.A[9] = 0.00067;
  this.A[10] = -0.00034;

  this.B_re = [];
  this.B_im = [];
  this.B_re[1] = 0.7557853228;
  this.B_im[1] = 0;
  this.B_re[2] = 0.249204646;
  this.B_im[2] = 0.003371507;
  this.B_re[3] = -0.001541739;
  this.B_im[3] = 0.041058560;
  this.B_re[4] = -0.10162907;
  this.B_im[4] = 0.01727609;
  this.B_re[5] = -0.26623489;
  this.B_im[5] = -0.36249218;
  this.B_re[6] = -0.6870983;
  this.B_im[6] = -1.1651967;

  this.C_re = [];
  this.C_im = [];
  this.C_re[1] = 1.3231270439;
  this.C_im[1] = 0;
  this.C_re[2] = -0.577245789;
  this.C_im[2] = -0.007809598;
  this.C_re[3] = 0.508307513;
  this.C_im[3] = -0.112208952;
  this.C_re[4] = -0.15094762;
  this.C_im[4] = 0.18200602;
  this.C_re[5] = 1.01418179;
  this.C_im[5] = 1.64497696;
  this.C_re[6] = 1.9660549;
  this.C_im[6] = 2.5127645;

  this.D = [];
  this.D[1] = 1.5627014243;
  this.D[2] = 0.5185406398;
  this.D[3] = -0.03333098;
  this.D[4] = -0.1052906;
  this.D[5] = -0.0368594;
  this.D[6] = 0.007317;
  this.D[7] = 0.01220;
  this.D[8] = 0.00394;
  this.D[9] = -0.0013;
}

/**
    New Zealand Map Grid Forward  - long/lat to x/y
    long/lat in radians
  */
function forward(p) {
  var n;
  var lon = p.x;
  var lat = p.y;

  var delta_lat = lat - this.lat0;
  var delta_lon = lon - this.long0;

  // 1. Calculate d_phi and d_psi    ...                          // and d_lambda
  // For this algorithm, delta_latitude is in seconds of arc x 10-5, so we need to scale to those units. Longitude is radians.
  var d_phi = delta_lat / __WEBPACK_IMPORTED_MODULE_0__constants_values__["l" /* SEC_TO_RAD */] * 1E-5;
  var d_lambda = delta_lon;
  var d_phi_n = 1; // d_phi^0

  var d_psi = 0;
  for (n = 1; n <= 10; n++) {
    d_phi_n = d_phi_n * d_phi;
    d_psi = d_psi + this.A[n] * d_phi_n;
  }

  // 2. Calculate theta
  var th_re = d_psi;
  var th_im = d_lambda;

  // 3. Calculate z
  var th_n_re = 1;
  var th_n_im = 0; // theta^0
  var th_n_re1;
  var th_n_im1;

  var z_re = 0;
  var z_im = 0;
  for (n = 1; n <= 6; n++) {
    th_n_re1 = th_n_re * th_re - th_n_im * th_im;
    th_n_im1 = th_n_im * th_re + th_n_re * th_im;
    th_n_re = th_n_re1;
    th_n_im = th_n_im1;
    z_re = z_re + this.B_re[n] * th_n_re - this.B_im[n] * th_n_im;
    z_im = z_im + this.B_im[n] * th_n_re + this.B_re[n] * th_n_im;
  }

  // 4. Calculate easting and northing
  p.x = (z_im * this.a) + this.x0;
  p.y = (z_re * this.a) + this.y0;

  return p;
}

/**
    New Zealand Map Grid Inverse  -  x/y to long/lat
  */
function inverse(p) {
  var n;
  var x = p.x;
  var y = p.y;

  var delta_x = x - this.x0;
  var delta_y = y - this.y0;

  // 1. Calculate z
  var z_re = delta_y / this.a;
  var z_im = delta_x / this.a;

  // 2a. Calculate theta - first approximation gives km accuracy
  var z_n_re = 1;
  var z_n_im = 0; // z^0
  var z_n_re1;
  var z_n_im1;

  var th_re = 0;
  var th_im = 0;
  for (n = 1; n <= 6; n++) {
    z_n_re1 = z_n_re * z_re - z_n_im * z_im;
    z_n_im1 = z_n_im * z_re + z_n_re * z_im;
    z_n_re = z_n_re1;
    z_n_im = z_n_im1;
    th_re = th_re + this.C_re[n] * z_n_re - this.C_im[n] * z_n_im;
    th_im = th_im + this.C_im[n] * z_n_re + this.C_re[n] * z_n_im;
  }

  // 2b. Iterate to refine the accuracy of the calculation
  //        0 iterations gives km accuracy
  //        1 iteration gives m accuracy -- good enough for most mapping applications
  //        2 iterations bives mm accuracy
  for (var i = 0; i < this.iterations; i++) {
    var th_n_re = th_re;
    var th_n_im = th_im;
    var th_n_re1;
    var th_n_im1;

    var num_re = z_re;
    var num_im = z_im;
    for (n = 2; n <= 6; n++) {
      th_n_re1 = th_n_re * th_re - th_n_im * th_im;
      th_n_im1 = th_n_im * th_re + th_n_re * th_im;
      th_n_re = th_n_re1;
      th_n_im = th_n_im1;
      num_re = num_re + (n - 1) * (this.B_re[n] * th_n_re - this.B_im[n] * th_n_im);
      num_im = num_im + (n - 1) * (this.B_im[n] * th_n_re + this.B_re[n] * th_n_im);
    }

    th_n_re = 1;
    th_n_im = 0;
    var den_re = this.B_re[1];
    var den_im = this.B_im[1];
    for (n = 2; n <= 6; n++) {
      th_n_re1 = th_n_re * th_re - th_n_im * th_im;
      th_n_im1 = th_n_im * th_re + th_n_re * th_im;
      th_n_re = th_n_re1;
      th_n_im = th_n_im1;
      den_re = den_re + n * (this.B_re[n] * th_n_re - this.B_im[n] * th_n_im);
      den_im = den_im + n * (this.B_im[n] * th_n_re + this.B_re[n] * th_n_im);
    }

    // Complex division
    var den2 = den_re * den_re + den_im * den_im;
    th_re = (num_re * den_re + num_im * den_im) / den2;
    th_im = (num_im * den_re - num_re * den_im) / den2;
  }

  // 3. Calculate d_phi              ...                                    // and d_lambda
  var d_psi = th_re;
  var d_lambda = th_im;
  var d_psi_n = 1; // d_psi^0

  var d_phi = 0;
  for (n = 1; n <= 9; n++) {
    d_psi_n = d_psi_n * d_psi;
    d_phi = d_phi + this.D[n] * d_psi_n;
  }

  // 4. Calculate latitude and longitude
  // d_phi is calcuated in second of arc * 10^-5, so we need to scale back to radians. d_lambda is in radians.
  var lat = this.lat0 + (d_phi * __WEBPACK_IMPORTED_MODULE_0__constants_values__["l" /* SEC_TO_RAD */] * 1E5);
  var lon = this.long0 + d_lambda;

  p.x = lon;
  p.y = lat;

  return p;
}

var names = ["New_Zealand_Map_Grid", "nzmg"];
/* harmony default export */ __webpack_exports__["default"] = ({
  init: init,
  forward: forward,
  inverse: inverse,
  names: names
});


/***/ }),
/* 88 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony export (immutable) */ __webpack_exports__["init"] = init;
/* harmony export (immutable) */ __webpack_exports__["forward"] = forward;
/* harmony export (immutable) */ __webpack_exports__["inverse"] = inverse;
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "names", function() { return names; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__common_adjust_lon__ = __webpack_require__(5);


/*
  reference
    "New Equal-Area Map Projections for Noncircular Regions", John P. Snyder,
    The American Cartographer, Vol 15, No. 4, October 1988, pp. 341-355.
  */


/* Initialize the Miller Cylindrical projection
  -------------------------------------------*/
function init() {
  //no-op
}

/* Miller Cylindrical forward equations--mapping lat,long to x,y
    ------------------------------------------------------------*/
function forward(p) {
  var lon = p.x;
  var lat = p.y;
  /* Forward equations
      -----------------*/
  var dlon = Object(__WEBPACK_IMPORTED_MODULE_0__common_adjust_lon__["a" /* default */])(lon - this.long0);
  var x = this.x0 + this.a * dlon;
  var y = this.y0 + this.a * Math.log(Math.tan((Math.PI / 4) + (lat / 2.5))) * 1.25;

  p.x = x;
  p.y = y;
  return p;
}

/* Miller Cylindrical inverse equations--mapping x,y to lat/long
    ------------------------------------------------------------*/
function inverse(p) {
  p.x -= this.x0;
  p.y -= this.y0;

  var lon = Object(__WEBPACK_IMPORTED_MODULE_0__common_adjust_lon__["a" /* default */])(this.long0 + p.x / this.a);
  var lat = 2.5 * (Math.atan(Math.exp(0.8 * p.y / this.a)) - Math.PI / 4);

  p.x = lon;
  p.y = lat;
  return p;
}

var names = ["Miller_Cylindrical", "mill"];
/* harmony default export */ __webpack_exports__["default"] = ({
  init: init,
  forward: forward,
  inverse: inverse,
  names: names
});


/***/ }),
/* 89 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony export (immutable) */ __webpack_exports__["init"] = init;
/* harmony export (immutable) */ __webpack_exports__["forward"] = forward;
/* harmony export (immutable) */ __webpack_exports__["inverse"] = inverse;
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "names", function() { return names; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__common_adjust_lon__ = __webpack_require__(5);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__common_adjust_lat__ = __webpack_require__(21);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__common_pj_enfn__ = __webpack_require__(68);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__common_pj_mlfn__ = __webpack_require__(41);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__common_pj_inv_mlfn__ = __webpack_require__(69);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__constants_values__ = __webpack_require__(4);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_6__common_asinz__ = __webpack_require__(16);



var MAX_ITER = 20;







function init() {
  /* Place parameters in static storage for common use
    -------------------------------------------------*/


  if (!this.sphere) {
    this.en = Object(__WEBPACK_IMPORTED_MODULE_2__common_pj_enfn__["a" /* default */])(this.es);
  }
  else {
    this.n = 1;
    this.m = 0;
    this.es = 0;
    this.C_y = Math.sqrt((this.m + 1) / this.n);
    this.C_x = this.C_y / (this.m + 1);
  }

}

/* Sinusoidal forward equations--mapping lat,long to x,y
  -----------------------------------------------------*/
function forward(p) {
  var x, y;
  var lon = p.x;
  var lat = p.y;
  /* Forward equations
    -----------------*/
  lon = Object(__WEBPACK_IMPORTED_MODULE_0__common_adjust_lon__["a" /* default */])(lon - this.long0);

  if (this.sphere) {
    if (!this.m) {
      lat = this.n !== 1 ? Math.asin(this.n * Math.sin(lat)) : lat;
    }
    else {
      var k = this.n * Math.sin(lat);
      for (var i = MAX_ITER; i; --i) {
        var V = (this.m * lat + Math.sin(lat) - k) / (this.m + Math.cos(lat));
        lat -= V;
        if (Math.abs(V) < __WEBPACK_IMPORTED_MODULE_5__constants_values__["b" /* EPSLN */]) {
          break;
        }
      }
    }
    x = this.a * this.C_x * lon * (this.m + Math.cos(lat));
    y = this.a * this.C_y * lat;

  }
  else {

    var s = Math.sin(lat);
    var c = Math.cos(lat);
    y = this.a * Object(__WEBPACK_IMPORTED_MODULE_3__common_pj_mlfn__["a" /* default */])(lat, s, c, this.en);
    x = this.a * lon * c / Math.sqrt(1 - this.es * s * s);
  }

  p.x = x;
  p.y = y;
  return p;
}

function inverse(p) {
  var lat, temp, lon, s;

  p.x -= this.x0;
  lon = p.x / this.a;
  p.y -= this.y0;
  lat = p.y / this.a;

  if (this.sphere) {
    lat /= this.C_y;
    lon = lon / (this.C_x * (this.m + Math.cos(lat)));
    if (this.m) {
      lat = Object(__WEBPACK_IMPORTED_MODULE_6__common_asinz__["a" /* default */])((this.m * lat + Math.sin(lat)) / this.n);
    }
    else if (this.n !== 1) {
      lat = Object(__WEBPACK_IMPORTED_MODULE_6__common_asinz__["a" /* default */])(Math.sin(lat) / this.n);
    }
    lon = Object(__WEBPACK_IMPORTED_MODULE_0__common_adjust_lon__["a" /* default */])(lon + this.long0);
    lat = Object(__WEBPACK_IMPORTED_MODULE_1__common_adjust_lat__["a" /* default */])(lat);
  }
  else {
    lat = Object(__WEBPACK_IMPORTED_MODULE_4__common_pj_inv_mlfn__["a" /* default */])(p.y / this.a, this.es, this.en);
    s = Math.abs(lat);
    if (s < __WEBPACK_IMPORTED_MODULE_5__constants_values__["d" /* HALF_PI */]) {
      s = Math.sin(lat);
      temp = this.long0 + p.x * Math.sqrt(1 - this.es * s * s) / (this.a * Math.cos(lat));
      //temp = this.long0 + p.x / (this.a * Math.cos(lat));
      lon = Object(__WEBPACK_IMPORTED_MODULE_0__common_adjust_lon__["a" /* default */])(temp);
    }
    else if ((s - __WEBPACK_IMPORTED_MODULE_5__constants_values__["b" /* EPSLN */]) < __WEBPACK_IMPORTED_MODULE_5__constants_values__["d" /* HALF_PI */]) {
      lon = this.long0;
    }
  }
  p.x = lon;
  p.y = lat;
  return p;
}

var names = ["Sinusoidal", "sinu"];
/* harmony default export */ __webpack_exports__["default"] = ({
  init: init,
  forward: forward,
  inverse: inverse,
  names: names
});


/***/ }),
/* 90 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony export (immutable) */ __webpack_exports__["init"] = init;
/* harmony export (immutable) */ __webpack_exports__["forward"] = forward;
/* harmony export (immutable) */ __webpack_exports__["inverse"] = inverse;
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "names", function() { return names; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__common_adjust_lon__ = __webpack_require__(5);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__constants_values__ = __webpack_require__(4);

function init() {}

/* Mollweide forward equations--mapping lat,long to x,y
    ----------------------------------------------------*/
function forward(p) {

  /* Forward equations
      -----------------*/
  var lon = p.x;
  var lat = p.y;

  var delta_lon = Object(__WEBPACK_IMPORTED_MODULE_0__common_adjust_lon__["a" /* default */])(lon - this.long0);
  var theta = lat;
  var con = Math.PI * Math.sin(lat);

  /* Iterate using the Newton-Raphson method to find theta
      -----------------------------------------------------*/
  while (true) {
    var delta_theta = -(theta + Math.sin(theta) - con) / (1 + Math.cos(theta));
    theta += delta_theta;
    if (Math.abs(delta_theta) < __WEBPACK_IMPORTED_MODULE_1__constants_values__["b" /* EPSLN */]) {
      break;
    }
  }
  theta /= 2;

  /* If the latitude is 90 deg, force the x coordinate to be "0 + false easting"
       this is done here because of precision problems with "cos(theta)"
       --------------------------------------------------------------------------*/
  if (Math.PI / 2 - Math.abs(lat) < __WEBPACK_IMPORTED_MODULE_1__constants_values__["b" /* EPSLN */]) {
    delta_lon = 0;
  }
  var x = 0.900316316158 * this.a * delta_lon * Math.cos(theta) + this.x0;
  var y = 1.4142135623731 * this.a * Math.sin(theta) + this.y0;

  p.x = x;
  p.y = y;
  return p;
}

function inverse(p) {
  var theta;
  var arg;

  /* Inverse equations
      -----------------*/
  p.x -= this.x0;
  p.y -= this.y0;
  arg = p.y / (1.4142135623731 * this.a);

  /* Because of division by zero problems, 'arg' can not be 1.  Therefore
       a number very close to one is used instead.
       -------------------------------------------------------------------*/
  if (Math.abs(arg) > 0.999999999999) {
    arg = 0.999999999999;
  }
  theta = Math.asin(arg);
  var lon = Object(__WEBPACK_IMPORTED_MODULE_0__common_adjust_lon__["a" /* default */])(this.long0 + (p.x / (0.900316316158 * this.a * Math.cos(theta))));
  if (lon < (-Math.PI)) {
    lon = -Math.PI;
  }
  if (lon > Math.PI) {
    lon = Math.PI;
  }
  arg = (2 * theta + Math.sin(2 * theta)) / Math.PI;
  if (Math.abs(arg) > 1) {
    arg = 1;
  }
  var lat = Math.asin(arg);

  p.x = lon;
  p.y = lat;
  return p;
}

var names = ["Mollweide", "moll"];
/* harmony default export */ __webpack_exports__["default"] = ({
  init: init,
  forward: forward,
  inverse: inverse,
  names: names
});


/***/ }),
/* 91 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony export (immutable) */ __webpack_exports__["init"] = init;
/* harmony export (immutable) */ __webpack_exports__["forward"] = forward;
/* harmony export (immutable) */ __webpack_exports__["inverse"] = inverse;
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "names", function() { return names; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__common_e0fn__ = __webpack_require__(30);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__common_e1fn__ = __webpack_require__(31);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__common_e2fn__ = __webpack_require__(32);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__common_e3fn__ = __webpack_require__(33);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__common_msfnz__ = __webpack_require__(15);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__common_mlfn__ = __webpack_require__(29);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_6__common_adjust_lon__ = __webpack_require__(5);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_7__common_adjust_lat__ = __webpack_require__(21);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_8__common_imlfn__ = __webpack_require__(45);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_9__constants_values__ = __webpack_require__(4);











function init() {

  /* Place parameters in static storage for common use
      -------------------------------------------------*/
  // Standard Parallels cannot be equal and on opposite sides of the equator
  if (Math.abs(this.lat1 + this.lat2) < __WEBPACK_IMPORTED_MODULE_9__constants_values__["b" /* EPSLN */]) {
    return;
  }
  this.lat2 = this.lat2 || this.lat1;
  this.temp = this.b / this.a;
  this.es = 1 - Math.pow(this.temp, 2);
  this.e = Math.sqrt(this.es);
  this.e0 = Object(__WEBPACK_IMPORTED_MODULE_0__common_e0fn__["a" /* default */])(this.es);
  this.e1 = Object(__WEBPACK_IMPORTED_MODULE_1__common_e1fn__["a" /* default */])(this.es);
  this.e2 = Object(__WEBPACK_IMPORTED_MODULE_2__common_e2fn__["a" /* default */])(this.es);
  this.e3 = Object(__WEBPACK_IMPORTED_MODULE_3__common_e3fn__["a" /* default */])(this.es);

  this.sinphi = Math.sin(this.lat1);
  this.cosphi = Math.cos(this.lat1);

  this.ms1 = Object(__WEBPACK_IMPORTED_MODULE_4__common_msfnz__["a" /* default */])(this.e, this.sinphi, this.cosphi);
  this.ml1 = Object(__WEBPACK_IMPORTED_MODULE_5__common_mlfn__["a" /* default */])(this.e0, this.e1, this.e2, this.e3, this.lat1);

  if (Math.abs(this.lat1 - this.lat2) < __WEBPACK_IMPORTED_MODULE_9__constants_values__["b" /* EPSLN */]) {
    this.ns = this.sinphi;
  }
  else {
    this.sinphi = Math.sin(this.lat2);
    this.cosphi = Math.cos(this.lat2);
    this.ms2 = Object(__WEBPACK_IMPORTED_MODULE_4__common_msfnz__["a" /* default */])(this.e, this.sinphi, this.cosphi);
    this.ml2 = Object(__WEBPACK_IMPORTED_MODULE_5__common_mlfn__["a" /* default */])(this.e0, this.e1, this.e2, this.e3, this.lat2);
    this.ns = (this.ms1 - this.ms2) / (this.ml2 - this.ml1);
  }
  this.g = this.ml1 + this.ms1 / this.ns;
  this.ml0 = Object(__WEBPACK_IMPORTED_MODULE_5__common_mlfn__["a" /* default */])(this.e0, this.e1, this.e2, this.e3, this.lat0);
  this.rh = this.a * (this.g - this.ml0);
}

/* Equidistant Conic forward equations--mapping lat,long to x,y
  -----------------------------------------------------------*/
function forward(p) {
  var lon = p.x;
  var lat = p.y;
  var rh1;

  /* Forward equations
      -----------------*/
  if (this.sphere) {
    rh1 = this.a * (this.g - lat);
  }
  else {
    var ml = Object(__WEBPACK_IMPORTED_MODULE_5__common_mlfn__["a" /* default */])(this.e0, this.e1, this.e2, this.e3, lat);
    rh1 = this.a * (this.g - ml);
  }
  var theta = this.ns * Object(__WEBPACK_IMPORTED_MODULE_6__common_adjust_lon__["a" /* default */])(lon - this.long0);
  var x = this.x0 + rh1 * Math.sin(theta);
  var y = this.y0 + this.rh - rh1 * Math.cos(theta);
  p.x = x;
  p.y = y;
  return p;
}

/* Inverse equations
  -----------------*/
function inverse(p) {
  p.x -= this.x0;
  p.y = this.rh - p.y + this.y0;
  var con, rh1, lat, lon;
  if (this.ns >= 0) {
    rh1 = Math.sqrt(p.x * p.x + p.y * p.y);
    con = 1;
  }
  else {
    rh1 = -Math.sqrt(p.x * p.x + p.y * p.y);
    con = -1;
  }
  var theta = 0;
  if (rh1 !== 0) {
    theta = Math.atan2(con * p.x, con * p.y);
  }

  if (this.sphere) {
    lon = Object(__WEBPACK_IMPORTED_MODULE_6__common_adjust_lon__["a" /* default */])(this.long0 + theta / this.ns);
    lat = Object(__WEBPACK_IMPORTED_MODULE_7__common_adjust_lat__["a" /* default */])(this.g - rh1 / this.a);
    p.x = lon;
    p.y = lat;
    return p;
  }
  else {
    var ml = this.g - rh1 / this.a;
    lat = Object(__WEBPACK_IMPORTED_MODULE_8__common_imlfn__["a" /* default */])(ml, this.e0, this.e1, this.e2, this.e3);
    lon = Object(__WEBPACK_IMPORTED_MODULE_6__common_adjust_lon__["a" /* default */])(this.long0 + theta / this.ns);
    p.x = lon;
    p.y = lat;
    return p;
  }

}

var names = ["Equidistant_Conic", "eqdc"];
/* harmony default export */ __webpack_exports__["default"] = ({
  init: init,
  forward: forward,
  inverse: inverse,
  names: names
});


/***/ }),
/* 92 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony export (immutable) */ __webpack_exports__["init"] = init;
/* harmony export (immutable) */ __webpack_exports__["forward"] = forward;
/* harmony export (immutable) */ __webpack_exports__["inverse"] = inverse;
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "names", function() { return names; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__common_adjust_lon__ = __webpack_require__(5);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__constants_values__ = __webpack_require__(4);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__common_asinz__ = __webpack_require__(16);






/* Initialize the Van Der Grinten projection
  ----------------------------------------*/
function init() {
  //this.R = 6370997; //Radius of earth
  this.R = this.a;
}

function forward(p) {

  var lon = p.x;
  var lat = p.y;

  /* Forward equations
    -----------------*/
  var dlon = Object(__WEBPACK_IMPORTED_MODULE_0__common_adjust_lon__["a" /* default */])(lon - this.long0);
  var x, y;

  if (Math.abs(lat) <= __WEBPACK_IMPORTED_MODULE_1__constants_values__["b" /* EPSLN */]) {
    x = this.x0 + this.R * dlon;
    y = this.y0;
  }
  var theta = Object(__WEBPACK_IMPORTED_MODULE_2__common_asinz__["a" /* default */])(2 * Math.abs(lat / Math.PI));
  if ((Math.abs(dlon) <= __WEBPACK_IMPORTED_MODULE_1__constants_values__["b" /* EPSLN */]) || (Math.abs(Math.abs(lat) - __WEBPACK_IMPORTED_MODULE_1__constants_values__["d" /* HALF_PI */]) <= __WEBPACK_IMPORTED_MODULE_1__constants_values__["b" /* EPSLN */])) {
    x = this.x0;
    if (lat >= 0) {
      y = this.y0 + Math.PI * this.R * Math.tan(0.5 * theta);
    }
    else {
      y = this.y0 + Math.PI * this.R * -Math.tan(0.5 * theta);
    }
    //  return(OK);
  }
  var al = 0.5 * Math.abs((Math.PI / dlon) - (dlon / Math.PI));
  var asq = al * al;
  var sinth = Math.sin(theta);
  var costh = Math.cos(theta);

  var g = costh / (sinth + costh - 1);
  var gsq = g * g;
  var m = g * (2 / sinth - 1);
  var msq = m * m;
  var con = Math.PI * this.R * (al * (g - msq) + Math.sqrt(asq * (g - msq) * (g - msq) - (msq + asq) * (gsq - msq))) / (msq + asq);
  if (dlon < 0) {
    con = -con;
  }
  x = this.x0 + con;
  //con = Math.abs(con / (Math.PI * this.R));
  var q = asq + g;
  con = Math.PI * this.R * (m * q - al * Math.sqrt((msq + asq) * (asq + 1) - q * q)) / (msq + asq);
  if (lat >= 0) {
    //y = this.y0 + Math.PI * this.R * Math.sqrt(1 - con * con - 2 * al * con);
    y = this.y0 + con;
  }
  else {
    //y = this.y0 - Math.PI * this.R * Math.sqrt(1 - con * con - 2 * al * con);
    y = this.y0 - con;
  }
  p.x = x;
  p.y = y;
  return p;
}

/* Van Der Grinten inverse equations--mapping x,y to lat/long
  ---------------------------------------------------------*/
function inverse(p) {
  var lon, lat;
  var xx, yy, xys, c1, c2, c3;
  var a1;
  var m1;
  var con;
  var th1;
  var d;

  /* inverse equations
    -----------------*/
  p.x -= this.x0;
  p.y -= this.y0;
  con = Math.PI * this.R;
  xx = p.x / con;
  yy = p.y / con;
  xys = xx * xx + yy * yy;
  c1 = -Math.abs(yy) * (1 + xys);
  c2 = c1 - 2 * yy * yy + xx * xx;
  c3 = -2 * c1 + 1 + 2 * yy * yy + xys * xys;
  d = yy * yy / c3 + (2 * c2 * c2 * c2 / c3 / c3 / c3 - 9 * c1 * c2 / c3 / c3) / 27;
  a1 = (c1 - c2 * c2 / 3 / c3) / c3;
  m1 = 2 * Math.sqrt(-a1 / 3);
  con = ((3 * d) / a1) / m1;
  if (Math.abs(con) > 1) {
    if (con >= 0) {
      con = 1;
    }
    else {
      con = -1;
    }
  }
  th1 = Math.acos(con) / 3;
  if (p.y >= 0) {
    lat = (-m1 * Math.cos(th1 + Math.PI / 3) - c2 / 3 / c3) * Math.PI;
  }
  else {
    lat = -(-m1 * Math.cos(th1 + Math.PI / 3) - c2 / 3 / c3) * Math.PI;
  }

  if (Math.abs(xx) < __WEBPACK_IMPORTED_MODULE_1__constants_values__["b" /* EPSLN */]) {
    lon = this.long0;
  }
  else {
    lon = Object(__WEBPACK_IMPORTED_MODULE_0__common_adjust_lon__["a" /* default */])(this.long0 + Math.PI * (xys - 1 + Math.sqrt(1 + 2 * (xx * xx - yy * yy) + xys * xys)) / 2 / xx);
  }

  p.x = lon;
  p.y = lat;
  return p;
}

var names = ["Van_der_Grinten_I", "VanDerGrinten", "vandg"];
/* harmony default export */ __webpack_exports__["default"] = ({
  init: init,
  forward: forward,
  inverse: inverse,
  names: names
});


/***/ }),
/* 93 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony export (immutable) */ __webpack_exports__["init"] = init;
/* harmony export (immutable) */ __webpack_exports__["forward"] = forward;
/* harmony export (immutable) */ __webpack_exports__["inverse"] = inverse;
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "names", function() { return names; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__common_adjust_lon__ = __webpack_require__(5);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__constants_values__ = __webpack_require__(4);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__common_mlfn__ = __webpack_require__(29);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__common_e0fn__ = __webpack_require__(30);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__common_e1fn__ = __webpack_require__(31);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__common_e2fn__ = __webpack_require__(32);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_6__common_e3fn__ = __webpack_require__(33);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_7__common_gN__ = __webpack_require__(44);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_8__common_asinz__ = __webpack_require__(16);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_9__common_imlfn__ = __webpack_require__(45);














function init() {
  this.sin_p12 = Math.sin(this.lat0);
  this.cos_p12 = Math.cos(this.lat0);
}

function forward(p) {
  var lon = p.x;
  var lat = p.y;
  var sinphi = Math.sin(p.y);
  var cosphi = Math.cos(p.y);
  var dlon = Object(__WEBPACK_IMPORTED_MODULE_0__common_adjust_lon__["a" /* default */])(lon - this.long0);
  var e0, e1, e2, e3, Mlp, Ml, tanphi, Nl1, Nl, psi, Az, G, H, GH, Hs, c, kp, cos_c, s, s2, s3, s4, s5;
  if (this.sphere) {
    if (Math.abs(this.sin_p12 - 1) <= __WEBPACK_IMPORTED_MODULE_1__constants_values__["b" /* EPSLN */]) {
      //North Pole case
      p.x = this.x0 + this.a * (__WEBPACK_IMPORTED_MODULE_1__constants_values__["d" /* HALF_PI */] - lat) * Math.sin(dlon);
      p.y = this.y0 - this.a * (__WEBPACK_IMPORTED_MODULE_1__constants_values__["d" /* HALF_PI */] - lat) * Math.cos(dlon);
      return p;
    }
    else if (Math.abs(this.sin_p12 + 1) <= __WEBPACK_IMPORTED_MODULE_1__constants_values__["b" /* EPSLN */]) {
      //South Pole case
      p.x = this.x0 + this.a * (__WEBPACK_IMPORTED_MODULE_1__constants_values__["d" /* HALF_PI */] + lat) * Math.sin(dlon);
      p.y = this.y0 + this.a * (__WEBPACK_IMPORTED_MODULE_1__constants_values__["d" /* HALF_PI */] + lat) * Math.cos(dlon);
      return p;
    }
    else {
      //default case
      cos_c = this.sin_p12 * sinphi + this.cos_p12 * cosphi * Math.cos(dlon);
      c = Math.acos(cos_c);
      kp = c / Math.sin(c);
      p.x = this.x0 + this.a * kp * cosphi * Math.sin(dlon);
      p.y = this.y0 + this.a * kp * (this.cos_p12 * sinphi - this.sin_p12 * cosphi * Math.cos(dlon));
      return p;
    }
  }
  else {
    e0 = Object(__WEBPACK_IMPORTED_MODULE_3__common_e0fn__["a" /* default */])(this.es);
    e1 = Object(__WEBPACK_IMPORTED_MODULE_4__common_e1fn__["a" /* default */])(this.es);
    e2 = Object(__WEBPACK_IMPORTED_MODULE_5__common_e2fn__["a" /* default */])(this.es);
    e3 = Object(__WEBPACK_IMPORTED_MODULE_6__common_e3fn__["a" /* default */])(this.es);
    if (Math.abs(this.sin_p12 - 1) <= __WEBPACK_IMPORTED_MODULE_1__constants_values__["b" /* EPSLN */]) {
      //North Pole case
      Mlp = this.a * Object(__WEBPACK_IMPORTED_MODULE_2__common_mlfn__["a" /* default */])(e0, e1, e2, e3, __WEBPACK_IMPORTED_MODULE_1__constants_values__["d" /* HALF_PI */]);
      Ml = this.a * Object(__WEBPACK_IMPORTED_MODULE_2__common_mlfn__["a" /* default */])(e0, e1, e2, e3, lat);
      p.x = this.x0 + (Mlp - Ml) * Math.sin(dlon);
      p.y = this.y0 - (Mlp - Ml) * Math.cos(dlon);
      return p;
    }
    else if (Math.abs(this.sin_p12 + 1) <= __WEBPACK_IMPORTED_MODULE_1__constants_values__["b" /* EPSLN */]) {
      //South Pole case
      Mlp = this.a * Object(__WEBPACK_IMPORTED_MODULE_2__common_mlfn__["a" /* default */])(e0, e1, e2, e3, __WEBPACK_IMPORTED_MODULE_1__constants_values__["d" /* HALF_PI */]);
      Ml = this.a * Object(__WEBPACK_IMPORTED_MODULE_2__common_mlfn__["a" /* default */])(e0, e1, e2, e3, lat);
      p.x = this.x0 + (Mlp + Ml) * Math.sin(dlon);
      p.y = this.y0 + (Mlp + Ml) * Math.cos(dlon);
      return p;
    }
    else {
      //Default case
      tanphi = sinphi / cosphi;
      Nl1 = Object(__WEBPACK_IMPORTED_MODULE_7__common_gN__["a" /* default */])(this.a, this.e, this.sin_p12);
      Nl = Object(__WEBPACK_IMPORTED_MODULE_7__common_gN__["a" /* default */])(this.a, this.e, sinphi);
      psi = Math.atan((1 - this.es) * tanphi + this.es * Nl1 * this.sin_p12 / (Nl * cosphi));
      Az = Math.atan2(Math.sin(dlon), this.cos_p12 * Math.tan(psi) - this.sin_p12 * Math.cos(dlon));
      if (Az === 0) {
        s = Math.asin(this.cos_p12 * Math.sin(psi) - this.sin_p12 * Math.cos(psi));
      }
      else if (Math.abs(Math.abs(Az) - Math.PI) <= __WEBPACK_IMPORTED_MODULE_1__constants_values__["b" /* EPSLN */]) {
        s = -Math.asin(this.cos_p12 * Math.sin(psi) - this.sin_p12 * Math.cos(psi));
      }
      else {
        s = Math.asin(Math.sin(dlon) * Math.cos(psi) / Math.sin(Az));
      }
      G = this.e * this.sin_p12 / Math.sqrt(1 - this.es);
      H = this.e * this.cos_p12 * Math.cos(Az) / Math.sqrt(1 - this.es);
      GH = G * H;
      Hs = H * H;
      s2 = s * s;
      s3 = s2 * s;
      s4 = s3 * s;
      s5 = s4 * s;
      c = Nl1 * s * (1 - s2 * Hs * (1 - Hs) / 6 + s3 / 8 * GH * (1 - 2 * Hs) + s4 / 120 * (Hs * (4 - 7 * Hs) - 3 * G * G * (1 - 7 * Hs)) - s5 / 48 * GH);
      p.x = this.x0 + c * Math.sin(Az);
      p.y = this.y0 + c * Math.cos(Az);
      return p;
    }
  }


}

function inverse(p) {
  p.x -= this.x0;
  p.y -= this.y0;
  var rh, z, sinz, cosz, lon, lat, con, e0, e1, e2, e3, Mlp, M, N1, psi, Az, cosAz, tmp, A, B, D, Ee, F;
  if (this.sphere) {
    rh = Math.sqrt(p.x * p.x + p.y * p.y);
    if (rh > (2 * __WEBPACK_IMPORTED_MODULE_1__constants_values__["d" /* HALF_PI */] * this.a)) {
      return;
    }
    z = rh / this.a;

    sinz = Math.sin(z);
    cosz = Math.cos(z);

    lon = this.long0;
    if (Math.abs(rh) <= __WEBPACK_IMPORTED_MODULE_1__constants_values__["b" /* EPSLN */]) {
      lat = this.lat0;
    }
    else {
      lat = Object(__WEBPACK_IMPORTED_MODULE_8__common_asinz__["a" /* default */])(cosz * this.sin_p12 + (p.y * sinz * this.cos_p12) / rh);
      con = Math.abs(this.lat0) - __WEBPACK_IMPORTED_MODULE_1__constants_values__["d" /* HALF_PI */];
      if (Math.abs(con) <= __WEBPACK_IMPORTED_MODULE_1__constants_values__["b" /* EPSLN */]) {
        if (this.lat0 >= 0) {
          lon = Object(__WEBPACK_IMPORTED_MODULE_0__common_adjust_lon__["a" /* default */])(this.long0 + Math.atan2(p.x, - p.y));
        }
        else {
          lon = Object(__WEBPACK_IMPORTED_MODULE_0__common_adjust_lon__["a" /* default */])(this.long0 - Math.atan2(-p.x, p.y));
        }
      }
      else {
        /*con = cosz - this.sin_p12 * Math.sin(lat);
        if ((Math.abs(con) < EPSLN) && (Math.abs(p.x) < EPSLN)) {
          //no-op, just keep the lon value as is
        } else {
          var temp = Math.atan2((p.x * sinz * this.cos_p12), (con * rh));
          lon = adjust_lon(this.long0 + Math.atan2((p.x * sinz * this.cos_p12), (con * rh)));
        }*/
        lon = Object(__WEBPACK_IMPORTED_MODULE_0__common_adjust_lon__["a" /* default */])(this.long0 + Math.atan2(p.x * sinz, rh * this.cos_p12 * cosz - p.y * this.sin_p12 * sinz));
      }
    }

    p.x = lon;
    p.y = lat;
    return p;
  }
  else {
    e0 = Object(__WEBPACK_IMPORTED_MODULE_3__common_e0fn__["a" /* default */])(this.es);
    e1 = Object(__WEBPACK_IMPORTED_MODULE_4__common_e1fn__["a" /* default */])(this.es);
    e2 = Object(__WEBPACK_IMPORTED_MODULE_5__common_e2fn__["a" /* default */])(this.es);
    e3 = Object(__WEBPACK_IMPORTED_MODULE_6__common_e3fn__["a" /* default */])(this.es);
    if (Math.abs(this.sin_p12 - 1) <= __WEBPACK_IMPORTED_MODULE_1__constants_values__["b" /* EPSLN */]) {
      //North pole case
      Mlp = this.a * Object(__WEBPACK_IMPORTED_MODULE_2__common_mlfn__["a" /* default */])(e0, e1, e2, e3, __WEBPACK_IMPORTED_MODULE_1__constants_values__["d" /* HALF_PI */]);
      rh = Math.sqrt(p.x * p.x + p.y * p.y);
      M = Mlp - rh;
      lat = Object(__WEBPACK_IMPORTED_MODULE_9__common_imlfn__["a" /* default */])(M / this.a, e0, e1, e2, e3);
      lon = Object(__WEBPACK_IMPORTED_MODULE_0__common_adjust_lon__["a" /* default */])(this.long0 + Math.atan2(p.x, - 1 * p.y));
      p.x = lon;
      p.y = lat;
      return p;
    }
    else if (Math.abs(this.sin_p12 + 1) <= __WEBPACK_IMPORTED_MODULE_1__constants_values__["b" /* EPSLN */]) {
      //South pole case
      Mlp = this.a * Object(__WEBPACK_IMPORTED_MODULE_2__common_mlfn__["a" /* default */])(e0, e1, e2, e3, __WEBPACK_IMPORTED_MODULE_1__constants_values__["d" /* HALF_PI */]);
      rh = Math.sqrt(p.x * p.x + p.y * p.y);
      M = rh - Mlp;

      lat = Object(__WEBPACK_IMPORTED_MODULE_9__common_imlfn__["a" /* default */])(M / this.a, e0, e1, e2, e3);
      lon = Object(__WEBPACK_IMPORTED_MODULE_0__common_adjust_lon__["a" /* default */])(this.long0 + Math.atan2(p.x, p.y));
      p.x = lon;
      p.y = lat;
      return p;
    }
    else {
      //default case
      rh = Math.sqrt(p.x * p.x + p.y * p.y);
      Az = Math.atan2(p.x, p.y);
      N1 = Object(__WEBPACK_IMPORTED_MODULE_7__common_gN__["a" /* default */])(this.a, this.e, this.sin_p12);
      cosAz = Math.cos(Az);
      tmp = this.e * this.cos_p12 * cosAz;
      A = -tmp * tmp / (1 - this.es);
      B = 3 * this.es * (1 - A) * this.sin_p12 * this.cos_p12 * cosAz / (1 - this.es);
      D = rh / N1;
      Ee = D - A * (1 + A) * Math.pow(D, 3) / 6 - B * (1 + 3 * A) * Math.pow(D, 4) / 24;
      F = 1 - A * Ee * Ee / 2 - D * Ee * Ee * Ee / 6;
      psi = Math.asin(this.sin_p12 * Math.cos(Ee) + this.cos_p12 * Math.sin(Ee) * cosAz);
      lon = Object(__WEBPACK_IMPORTED_MODULE_0__common_adjust_lon__["a" /* default */])(this.long0 + Math.asin(Math.sin(Az) * Math.sin(Ee) / Math.cos(psi)));
      lat = Math.atan((1 - this.es * F * this.sin_p12 / Math.sin(psi)) * Math.tan(psi) / (1 - this.es));
      p.x = lon;
      p.y = lat;
      return p;
    }
  }

}

var names = ["Azimuthal_Equidistant", "aeqd"];
/* harmony default export */ __webpack_exports__["default"] = ({
  init: init,
  forward: forward,
  inverse: inverse,
  names: names
});


/***/ }),
/* 94 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony export (immutable) */ __webpack_exports__["init"] = init;
/* harmony export (immutable) */ __webpack_exports__["forward"] = forward;
/* harmony export (immutable) */ __webpack_exports__["inverse"] = inverse;
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "names", function() { return names; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__common_adjust_lon__ = __webpack_require__(5);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__common_asinz__ = __webpack_require__(16);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__constants_values__ = __webpack_require__(4);




function init() {
  //double temp;      /* temporary variable    */

  /* Place parameters in static storage for common use
      -------------------------------------------------*/
  this.sin_p14 = Math.sin(this.lat0);
  this.cos_p14 = Math.cos(this.lat0);
}

/* Orthographic forward equations--mapping lat,long to x,y
    ---------------------------------------------------*/
function forward(p) {
  var sinphi, cosphi; /* sin and cos value        */
  var dlon; /* delta longitude value      */
  var coslon; /* cos of longitude        */
  var ksp; /* scale factor          */
  var g, x, y;
  var lon = p.x;
  var lat = p.y;
  /* Forward equations
      -----------------*/
  dlon = Object(__WEBPACK_IMPORTED_MODULE_0__common_adjust_lon__["a" /* default */])(lon - this.long0);

  sinphi = Math.sin(lat);
  cosphi = Math.cos(lat);

  coslon = Math.cos(dlon);
  g = this.sin_p14 * sinphi + this.cos_p14 * cosphi * coslon;
  ksp = 1;
  if ((g > 0) || (Math.abs(g) <= __WEBPACK_IMPORTED_MODULE_2__constants_values__["b" /* EPSLN */])) {
    x = this.a * ksp * cosphi * Math.sin(dlon);
    y = this.y0 + this.a * ksp * (this.cos_p14 * sinphi - this.sin_p14 * cosphi * coslon);
  }
  p.x = x;
  p.y = y;
  return p;
}

function inverse(p) {
  var rh; /* height above ellipsoid      */
  var z; /* angle          */
  var sinz, cosz; /* sin of z and cos of z      */
  var con;
  var lon, lat;
  /* Inverse equations
      -----------------*/
  p.x -= this.x0;
  p.y -= this.y0;
  rh = Math.sqrt(p.x * p.x + p.y * p.y);
  z = Object(__WEBPACK_IMPORTED_MODULE_1__common_asinz__["a" /* default */])(rh / this.a);

  sinz = Math.sin(z);
  cosz = Math.cos(z);

  lon = this.long0;
  if (Math.abs(rh) <= __WEBPACK_IMPORTED_MODULE_2__constants_values__["b" /* EPSLN */]) {
    lat = this.lat0;
    p.x = lon;
    p.y = lat;
    return p;
  }
  lat = Object(__WEBPACK_IMPORTED_MODULE_1__common_asinz__["a" /* default */])(cosz * this.sin_p14 + (p.y * sinz * this.cos_p14) / rh);
  con = Math.abs(this.lat0) - __WEBPACK_IMPORTED_MODULE_2__constants_values__["d" /* HALF_PI */];
  if (Math.abs(con) <= __WEBPACK_IMPORTED_MODULE_2__constants_values__["b" /* EPSLN */]) {
    if (this.lat0 >= 0) {
      lon = Object(__WEBPACK_IMPORTED_MODULE_0__common_adjust_lon__["a" /* default */])(this.long0 + Math.atan2(p.x, - p.y));
    }
    else {
      lon = Object(__WEBPACK_IMPORTED_MODULE_0__common_adjust_lon__["a" /* default */])(this.long0 - Math.atan2(-p.x, p.y));
    }
    p.x = lon;
    p.y = lat;
    return p;
  }
  lon = Object(__WEBPACK_IMPORTED_MODULE_0__common_adjust_lon__["a" /* default */])(this.long0 + Math.atan2((p.x * sinz), rh * this.cos_p14 * cosz - p.y * this.sin_p14 * sinz));
  p.x = lon;
  p.y = lat;
  return p;
}

var names = ["ortho"];
/* harmony default export */ __webpack_exports__["default"] = ({
  init: init,
  forward: forward,
  inverse: inverse,
  names: names
});


/***/ }),
/* 95 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony export (immutable) */ __webpack_exports__["init"] = init;
/* harmony export (immutable) */ __webpack_exports__["forward"] = forward;
/* harmony export (immutable) */ __webpack_exports__["inverse"] = inverse;
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "names", function() { return names; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__constants_values__ = __webpack_require__(4);
// QSC projection rewritten from the original PROJ4
// https://github.com/OSGeo/proj.4/blob/master/src/PJ_qsc.c



/* constants */
var FACE_ENUM = {
    FRONT: 1,
    RIGHT: 2,
    BACK: 3,
    LEFT: 4,
    TOP: 5,
    BOTTOM: 6
};

var AREA_ENUM = {
    AREA_0: 1,
    AREA_1: 2,
    AREA_2: 3,
    AREA_3: 4
};

function init() {

  this.x0 = this.x0 || 0;
  this.y0 = this.y0 || 0;
  this.lat0 = this.lat0 || 0;
  this.long0 = this.long0 || 0;
  this.lat_ts = this.lat_ts || 0;
  this.title = this.title || "Quadrilateralized Spherical Cube";

  /* Determine the cube face from the center of projection. */
  if (this.lat0 >= __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */] - __WEBPACK_IMPORTED_MODULE_0__constants_values__["c" /* FORTPI */] / 2.0) {
    this.face = FACE_ENUM.TOP;
  } else if (this.lat0 <= -(__WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */] - __WEBPACK_IMPORTED_MODULE_0__constants_values__["c" /* FORTPI */] / 2.0)) {
    this.face = FACE_ENUM.BOTTOM;
  } else if (Math.abs(this.long0) <= __WEBPACK_IMPORTED_MODULE_0__constants_values__["c" /* FORTPI */]) {
    this.face = FACE_ENUM.FRONT;
  } else if (Math.abs(this.long0) <= __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */] + __WEBPACK_IMPORTED_MODULE_0__constants_values__["c" /* FORTPI */]) {
    this.face = this.long0 > 0.0 ? FACE_ENUM.RIGHT : FACE_ENUM.LEFT;
  } else {
    this.face = FACE_ENUM.BACK;
  }

  /* Fill in useful values for the ellipsoid <-> sphere shift
   * described in [LK12]. */
  if (this.es !== 0) {
    this.one_minus_f = 1 - (this.a - this.b) / this.a;
    this.one_minus_f_squared = this.one_minus_f * this.one_minus_f;
  }
}

// QSC forward equations--mapping lat,long to x,y
// -----------------------------------------------------------------
function forward(p) {
  var xy = {x: 0, y: 0};
  var lat, lon;
  var theta, phi;
  var t, mu;
  /* nu; */
  var area = {value: 0};

  // move lon according to projection's lon
  p.x -= this.long0;

  /* Convert the geodetic latitude to a geocentric latitude.
   * This corresponds to the shift from the ellipsoid to the sphere
   * described in [LK12]. */
  if (this.es !== 0) {//if (P->es != 0) {
    lat = Math.atan(this.one_minus_f_squared * Math.tan(p.y));
  } else {
    lat = p.y;
  }

  /* Convert the input lat, lon into theta, phi as used by QSC.
   * This depends on the cube face and the area on it.
   * For the top and bottom face, we can compute theta and phi
   * directly from phi, lam. For the other faces, we must use
   * unit sphere cartesian coordinates as an intermediate step. */
  lon = p.x; //lon = lp.lam;
  if (this.face === FACE_ENUM.TOP) {
    phi = __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */] - lat;
    if (lon >= __WEBPACK_IMPORTED_MODULE_0__constants_values__["c" /* FORTPI */] && lon <= __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */] + __WEBPACK_IMPORTED_MODULE_0__constants_values__["c" /* FORTPI */]) {
      area.value = AREA_ENUM.AREA_0;
      theta = lon - __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */];
    } else if (lon > __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */] + __WEBPACK_IMPORTED_MODULE_0__constants_values__["c" /* FORTPI */] || lon <= -(__WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */] + __WEBPACK_IMPORTED_MODULE_0__constants_values__["c" /* FORTPI */])) {
      area.value = AREA_ENUM.AREA_1;
      theta = (lon > 0.0 ? lon - __WEBPACK_IMPORTED_MODULE_0__constants_values__["n" /* SPI */] : lon + __WEBPACK_IMPORTED_MODULE_0__constants_values__["n" /* SPI */]);
    } else if (lon > -(__WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */] + __WEBPACK_IMPORTED_MODULE_0__constants_values__["c" /* FORTPI */]) && lon <= -__WEBPACK_IMPORTED_MODULE_0__constants_values__["c" /* FORTPI */]) {
      area.value = AREA_ENUM.AREA_2;
      theta = lon + __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */];
    } else {
      area.value = AREA_ENUM.AREA_3;
      theta = lon;
    }
  } else if (this.face === FACE_ENUM.BOTTOM) {
    phi = __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */] + lat;
    if (lon >= __WEBPACK_IMPORTED_MODULE_0__constants_values__["c" /* FORTPI */] && lon <= __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */] + __WEBPACK_IMPORTED_MODULE_0__constants_values__["c" /* FORTPI */]) {
      area.value = AREA_ENUM.AREA_0;
      theta = -lon + __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */];
    } else if (lon < __WEBPACK_IMPORTED_MODULE_0__constants_values__["c" /* FORTPI */] && lon >= -__WEBPACK_IMPORTED_MODULE_0__constants_values__["c" /* FORTPI */]) {
      area.value = AREA_ENUM.AREA_1;
      theta = -lon;
    } else if (lon < -__WEBPACK_IMPORTED_MODULE_0__constants_values__["c" /* FORTPI */] && lon >= -(__WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */] + __WEBPACK_IMPORTED_MODULE_0__constants_values__["c" /* FORTPI */])) {
      area.value = AREA_ENUM.AREA_2;
      theta = -lon - __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */];
    } else {
      area.value = AREA_ENUM.AREA_3;
      theta = (lon > 0.0 ? -lon + __WEBPACK_IMPORTED_MODULE_0__constants_values__["n" /* SPI */] : -lon - __WEBPACK_IMPORTED_MODULE_0__constants_values__["n" /* SPI */]);
    }
  } else {
    var q, r, s;
    var sinlat, coslat;
    var sinlon, coslon;

    if (this.face === FACE_ENUM.RIGHT) {
      lon = qsc_shift_lon_origin(lon, +__WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */]);
    } else if (this.face === FACE_ENUM.BACK) {
      lon = qsc_shift_lon_origin(lon, +__WEBPACK_IMPORTED_MODULE_0__constants_values__["n" /* SPI */]);
    } else if (this.face === FACE_ENUM.LEFT) {
      lon = qsc_shift_lon_origin(lon, -__WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */]);
    }
    sinlat = Math.sin(lat);
    coslat = Math.cos(lat);
    sinlon = Math.sin(lon);
    coslon = Math.cos(lon);
    q = coslat * coslon;
    r = coslat * sinlon;
    s = sinlat;

    if (this.face === FACE_ENUM.FRONT) {
      phi = Math.acos(q);
      theta = qsc_fwd_equat_face_theta(phi, s, r, area);
    } else if (this.face === FACE_ENUM.RIGHT) {
      phi = Math.acos(r);
      theta = qsc_fwd_equat_face_theta(phi, s, -q, area);
    } else if (this.face === FACE_ENUM.BACK) {
      phi = Math.acos(-q);
      theta = qsc_fwd_equat_face_theta(phi, s, -r, area);
    } else if (this.face === FACE_ENUM.LEFT) {
      phi = Math.acos(-r);
      theta = qsc_fwd_equat_face_theta(phi, s, q, area);
    } else {
      /* Impossible */
      phi = theta = 0;
      area.value = AREA_ENUM.AREA_0;
    }
  }

  /* Compute mu and nu for the area of definition.
   * For mu, see Eq. (3-21) in [OL76], but note the typos:
   * compare with Eq. (3-14). For nu, see Eq. (3-38). */
  mu = Math.atan((12 / __WEBPACK_IMPORTED_MODULE_0__constants_values__["n" /* SPI */]) * (theta + Math.acos(Math.sin(theta) * Math.cos(__WEBPACK_IMPORTED_MODULE_0__constants_values__["c" /* FORTPI */])) - __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */]));
  t = Math.sqrt((1 - Math.cos(phi)) / (Math.cos(mu) * Math.cos(mu)) / (1 - Math.cos(Math.atan(1 / Math.cos(theta)))));

  /* Apply the result to the real area. */
  if (area.value === AREA_ENUM.AREA_1) {
    mu += __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */];
  } else if (area.value === AREA_ENUM.AREA_2) {
    mu += __WEBPACK_IMPORTED_MODULE_0__constants_values__["n" /* SPI */];
  } else if (area.value === AREA_ENUM.AREA_3) {
    mu += 1.5 * __WEBPACK_IMPORTED_MODULE_0__constants_values__["n" /* SPI */];
  }

  /* Now compute x, y from mu and nu */
  xy.x = t * Math.cos(mu);
  xy.y = t * Math.sin(mu);
  xy.x = xy.x * this.a + this.x0;
  xy.y = xy.y * this.a + this.y0;

  p.x = xy.x;
  p.y = xy.y;
  return p;
}

// QSC inverse equations--mapping x,y to lat/long
// -----------------------------------------------------------------
function inverse(p) {
  var lp = {lam: 0, phi: 0};
  var mu, nu, cosmu, tannu;
  var tantheta, theta, cosphi, phi;
  var t;
  var area = {value: 0};

  /* de-offset */
  p.x = (p.x - this.x0) / this.a;
  p.y = (p.y - this.y0) / this.a;

  /* Convert the input x, y to the mu and nu angles as used by QSC.
   * This depends on the area of the cube face. */
  nu = Math.atan(Math.sqrt(p.x * p.x + p.y * p.y));
  mu = Math.atan2(p.y, p.x);
  if (p.x >= 0.0 && p.x >= Math.abs(p.y)) {
    area.value = AREA_ENUM.AREA_0;
  } else if (p.y >= 0.0 && p.y >= Math.abs(p.x)) {
    area.value = AREA_ENUM.AREA_1;
    mu -= __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */];
  } else if (p.x < 0.0 && -p.x >= Math.abs(p.y)) {
    area.value = AREA_ENUM.AREA_2;
    mu = (mu < 0.0 ? mu + __WEBPACK_IMPORTED_MODULE_0__constants_values__["n" /* SPI */] : mu - __WEBPACK_IMPORTED_MODULE_0__constants_values__["n" /* SPI */]);
  } else {
    area.value = AREA_ENUM.AREA_3;
    mu += __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */];
  }

  /* Compute phi and theta for the area of definition.
   * The inverse projection is not described in the original paper, but some
   * good hints can be found here (as of 2011-12-14):
   * http://fits.gsfc.nasa.gov/fitsbits/saf.93/saf.9302
   * (search for "Message-Id: <9302181759.AA25477 at fits.cv.nrao.edu>") */
  t = (__WEBPACK_IMPORTED_MODULE_0__constants_values__["n" /* SPI */] / 12) * Math.tan(mu);
  tantheta = Math.sin(t) / (Math.cos(t) - (1 / Math.sqrt(2)));
  theta = Math.atan(tantheta);
  cosmu = Math.cos(mu);
  tannu = Math.tan(nu);
  cosphi = 1 - cosmu * cosmu * tannu * tannu * (1 - Math.cos(Math.atan(1 / Math.cos(theta))));
  if (cosphi < -1) {
    cosphi = -1;
  } else if (cosphi > +1) {
    cosphi = +1;
  }

  /* Apply the result to the real area on the cube face.
   * For the top and bottom face, we can compute phi and lam directly.
   * For the other faces, we must use unit sphere cartesian coordinates
   * as an intermediate step. */
  if (this.face === FACE_ENUM.TOP) {
    phi = Math.acos(cosphi);
    lp.phi = __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */] - phi;
    if (area.value === AREA_ENUM.AREA_0) {
      lp.lam = theta + __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */];
    } else if (area.value === AREA_ENUM.AREA_1) {
      lp.lam = (theta < 0.0 ? theta + __WEBPACK_IMPORTED_MODULE_0__constants_values__["n" /* SPI */] : theta - __WEBPACK_IMPORTED_MODULE_0__constants_values__["n" /* SPI */]);
    } else if (area.value === AREA_ENUM.AREA_2) {
      lp.lam = theta - __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */];
    } else /* area.value == AREA_ENUM.AREA_3 */ {
      lp.lam = theta;
    }
  } else if (this.face === FACE_ENUM.BOTTOM) {
    phi = Math.acos(cosphi);
    lp.phi = phi - __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */];
    if (area.value === AREA_ENUM.AREA_0) {
      lp.lam = -theta + __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */];
    } else if (area.value === AREA_ENUM.AREA_1) {
      lp.lam = -theta;
    } else if (area.value === AREA_ENUM.AREA_2) {
      lp.lam = -theta - __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */];
    } else /* area.value == AREA_ENUM.AREA_3 */ {
      lp.lam = (theta < 0.0 ? -theta - __WEBPACK_IMPORTED_MODULE_0__constants_values__["n" /* SPI */] : -theta + __WEBPACK_IMPORTED_MODULE_0__constants_values__["n" /* SPI */]);
    }
  } else {
    /* Compute phi and lam via cartesian unit sphere coordinates. */
    var q, r, s;
    q = cosphi;
    t = q * q;
    if (t >= 1) {
      s = 0;
    } else {
      s = Math.sqrt(1 - t) * Math.sin(theta);
    }
    t += s * s;
    if (t >= 1) {
      r = 0;
    } else {
      r = Math.sqrt(1 - t);
    }
    /* Rotate q,r,s into the correct area. */
    if (area.value === AREA_ENUM.AREA_1) {
      t = r;
      r = -s;
      s = t;
    } else if (area.value === AREA_ENUM.AREA_2) {
      r = -r;
      s = -s;
    } else if (area.value === AREA_ENUM.AREA_3) {
      t = r;
      r = s;
      s = -t;
    }
    /* Rotate q,r,s into the correct cube face. */
    if (this.face === FACE_ENUM.RIGHT) {
      t = q;
      q = -r;
      r = t;
    } else if (this.face === FACE_ENUM.BACK) {
      q = -q;
      r = -r;
    } else if (this.face === FACE_ENUM.LEFT) {
      t = q;
      q = r;
      r = -t;
    }
    /* Now compute phi and lam from the unit sphere coordinates. */
    lp.phi = Math.acos(-s) - __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */];
    lp.lam = Math.atan2(r, q);
    if (this.face === FACE_ENUM.RIGHT) {
      lp.lam = qsc_shift_lon_origin(lp.lam, -__WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */]);
    } else if (this.face === FACE_ENUM.BACK) {
      lp.lam = qsc_shift_lon_origin(lp.lam, -__WEBPACK_IMPORTED_MODULE_0__constants_values__["n" /* SPI */]);
    } else if (this.face === FACE_ENUM.LEFT) {
      lp.lam = qsc_shift_lon_origin(lp.lam, +__WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */]);
    }
  }

  /* Apply the shift from the sphere to the ellipsoid as described
   * in [LK12]. */
  if (this.es !== 0) {
    var invert_sign;
    var tanphi, xa;
    invert_sign = (lp.phi < 0 ? 1 : 0);
    tanphi = Math.tan(lp.phi);
    xa = this.b / Math.sqrt(tanphi * tanphi + this.one_minus_f_squared);
    lp.phi = Math.atan(Math.sqrt(this.a * this.a - xa * xa) / (this.one_minus_f * xa));
    if (invert_sign) {
      lp.phi = -lp.phi;
    }
  }

  lp.lam += this.long0;
  p.x = lp.lam;
  p.y = lp.phi;
  return p;
}

/* Helper function for forward projection: compute the theta angle
 * and determine the area number. */
function qsc_fwd_equat_face_theta(phi, y, x, area) {
  var theta;
  if (phi < __WEBPACK_IMPORTED_MODULE_0__constants_values__["b" /* EPSLN */]) {
    area.value = AREA_ENUM.AREA_0;
    theta = 0.0;
  } else {
    theta = Math.atan2(y, x);
    if (Math.abs(theta) <= __WEBPACK_IMPORTED_MODULE_0__constants_values__["c" /* FORTPI */]) {
      area.value = AREA_ENUM.AREA_0;
    } else if (theta > __WEBPACK_IMPORTED_MODULE_0__constants_values__["c" /* FORTPI */] && theta <= __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */] + __WEBPACK_IMPORTED_MODULE_0__constants_values__["c" /* FORTPI */]) {
      area.value = AREA_ENUM.AREA_1;
      theta -= __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */];
    } else if (theta > __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */] + __WEBPACK_IMPORTED_MODULE_0__constants_values__["c" /* FORTPI */] || theta <= -(__WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */] + __WEBPACK_IMPORTED_MODULE_0__constants_values__["c" /* FORTPI */])) {
      area.value = AREA_ENUM.AREA_2;
      theta = (theta >= 0.0 ? theta - __WEBPACK_IMPORTED_MODULE_0__constants_values__["n" /* SPI */] : theta + __WEBPACK_IMPORTED_MODULE_0__constants_values__["n" /* SPI */]);
    } else {
      area.value = AREA_ENUM.AREA_3;
      theta += __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */];
    }
  }
  return theta;
}

/* Helper function: shift the longitude. */
function qsc_shift_lon_origin(lon, offset) {
  var slon = lon + offset;
  if (slon < -__WEBPACK_IMPORTED_MODULE_0__constants_values__["n" /* SPI */]) {
    slon += __WEBPACK_IMPORTED_MODULE_0__constants_values__["o" /* TWO_PI */];
  } else if (slon > +__WEBPACK_IMPORTED_MODULE_0__constants_values__["n" /* SPI */]) {
    slon -= __WEBPACK_IMPORTED_MODULE_0__constants_values__["o" /* TWO_PI */];
  }
  return slon;
}

var names = ["Quadrilateralized Spherical Cube", "Quadrilateralized_Spherical_Cube", "qsc"];
/* harmony default export */ __webpack_exports__["default"] = ({
  init: init,
  forward: forward,
  inverse: inverse,
  names: names
});



/***/ }),
/* 96 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony export (immutable) */ __webpack_exports__["init"] = init;
/* harmony export (immutable) */ __webpack_exports__["forward"] = forward;
/* harmony export (immutable) */ __webpack_exports__["inverse"] = inverse;
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "names", function() { return names; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__constants_values__ = __webpack_require__(4);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__common_adjust_lon__ = __webpack_require__(5);
// Robinson projection
// Based on https://github.com/OSGeo/proj.4/blob/master/src/PJ_robin.c
// Polynomial coeficients from http://article.gmane.org/gmane.comp.gis.proj-4.devel/6039




var COEFS_X = [
    [1.0000, 2.2199e-17, -7.15515e-05, 3.1103e-06],
    [0.9986, -0.000482243, -2.4897e-05, -1.3309e-06],
    [0.9954, -0.00083103, -4.48605e-05, -9.86701e-07],
    [0.9900, -0.00135364, -5.9661e-05, 3.6777e-06],
    [0.9822, -0.00167442, -4.49547e-06, -5.72411e-06],
    [0.9730, -0.00214868, -9.03571e-05, 1.8736e-08],
    [0.9600, -0.00305085, -9.00761e-05, 1.64917e-06],
    [0.9427, -0.00382792, -6.53386e-05, -2.6154e-06],
    [0.9216, -0.00467746, -0.00010457, 4.81243e-06],
    [0.8962, -0.00536223, -3.23831e-05, -5.43432e-06],
    [0.8679, -0.00609363, -0.000113898, 3.32484e-06],
    [0.8350, -0.00698325, -6.40253e-05, 9.34959e-07],
    [0.7986, -0.00755338, -5.00009e-05, 9.35324e-07],
    [0.7597, -0.00798324, -3.5971e-05, -2.27626e-06],
    [0.7186, -0.00851367, -7.01149e-05, -8.6303e-06],
    [0.6732, -0.00986209, -0.000199569, 1.91974e-05],
    [0.6213, -0.010418, 8.83923e-05, 6.24051e-06],
    [0.5722, -0.00906601, 0.000182, 6.24051e-06],
    [0.5322, -0.00677797, 0.000275608, 6.24051e-06]
];

var COEFS_Y = [
    [-5.20417e-18, 0.0124, 1.21431e-18, -8.45284e-11],
    [0.0620, 0.0124, -1.26793e-09, 4.22642e-10],
    [0.1240, 0.0124, 5.07171e-09, -1.60604e-09],
    [0.1860, 0.0123999, -1.90189e-08, 6.00152e-09],
    [0.2480, 0.0124002, 7.10039e-08, -2.24e-08],
    [0.3100, 0.0123992, -2.64997e-07, 8.35986e-08],
    [0.3720, 0.0124029, 9.88983e-07, -3.11994e-07],
    [0.4340, 0.0123893, -3.69093e-06, -4.35621e-07],
    [0.4958, 0.0123198, -1.02252e-05, -3.45523e-07],
    [0.5571, 0.0121916, -1.54081e-05, -5.82288e-07],
    [0.6176, 0.0119938, -2.41424e-05, -5.25327e-07],
    [0.6769, 0.011713, -3.20223e-05, -5.16405e-07],
    [0.7346, 0.0113541, -3.97684e-05, -6.09052e-07],
    [0.7903, 0.0109107, -4.89042e-05, -1.04739e-06],
    [0.8435, 0.0103431, -6.4615e-05, -1.40374e-09],
    [0.8936, 0.00969686, -6.4636e-05, -8.547e-06],
    [0.9394, 0.00840947, -0.000192841, -4.2106e-06],
    [0.9761, 0.00616527, -0.000256, -4.2106e-06],
    [1.0000, 0.00328947, -0.000319159, -4.2106e-06]
];

var FXC = 0.8487;
var FYC = 1.3523;
var C1 = __WEBPACK_IMPORTED_MODULE_0__constants_values__["i" /* R2D */]/5; // rad to 5-degree interval
var RC1 = 1/C1;
var NODES = 18;

var poly3_val = function(coefs, x) {
    return coefs[0] + x * (coefs[1] + x * (coefs[2] + x * coefs[3]));
};

var poly3_der = function(coefs, x) {
    return coefs[1] + x * (2 * coefs[2] + x * 3 * coefs[3]);
};

function newton_rapshon(f_df, start, max_err, iters) {
    var x = start;
    for (; iters; --iters) {
        var upd = f_df(x);
        x -= upd;
        if (Math.abs(upd) < max_err) {
            break;
        }
    }
    return x;
}

function init() {
    this.x0 = this.x0 || 0;
    this.y0 = this.y0 || 0;
    this.long0 = this.long0 || 0;
    this.es = 0;
    this.title = this.title || "Robinson";
}

function forward(ll) {
    var lon = Object(__WEBPACK_IMPORTED_MODULE_1__common_adjust_lon__["a" /* default */])(ll.x - this.long0);

    var dphi = Math.abs(ll.y);
    var i = Math.floor(dphi * C1);
    if (i < 0) {
        i = 0;
    } else if (i >= NODES) {
        i = NODES - 1;
    }
    dphi = __WEBPACK_IMPORTED_MODULE_0__constants_values__["i" /* R2D */] * (dphi - RC1 * i);
    var xy = {
        x: poly3_val(COEFS_X[i], dphi) * lon,
        y: poly3_val(COEFS_Y[i], dphi)
    };
    if (ll.y < 0) {
        xy.y = -xy.y;
    }

    xy.x = xy.x * this.a * FXC + this.x0;
    xy.y = xy.y * this.a * FYC + this.y0;
    return xy;
}

function inverse(xy) {
    var ll = {
        x: (xy.x - this.x0) / (this.a * FXC),
        y: Math.abs(xy.y - this.y0) / (this.a * FYC)
    };

    if (ll.y >= 1) { // pathologic case
        ll.x /= COEFS_X[NODES][0];
        ll.y = xy.y < 0 ? -__WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */] : __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */];
    } else {
        // find table interval
        var i = Math.floor(ll.y * NODES);
        if (i < 0) {
            i = 0;
        } else if (i >= NODES) {
            i = NODES - 1;
        }
        for (;;) {
            if (COEFS_Y[i][0] > ll.y) {
                --i;
            } else if (COEFS_Y[i+1][0] <= ll.y) {
                ++i;
            } else {
                break;
            }
        }
        // linear interpolation in 5 degree interval
        var coefs = COEFS_Y[i];
        var t = 5 * (ll.y - coefs[0]) / (COEFS_Y[i+1][0] - coefs[0]);
        // find t so that poly3_val(coefs, t) = ll.y
        t = newton_rapshon(function(x) {
            return (poly3_val(coefs, x) - ll.y) / poly3_der(coefs, x);
        }, t, __WEBPACK_IMPORTED_MODULE_0__constants_values__["b" /* EPSLN */], 100);

        ll.x /= poly3_val(COEFS_X[i], t);
        ll.y = (5 * i + t) * __WEBPACK_IMPORTED_MODULE_0__constants_values__["a" /* D2R */];
        if (xy.y < 0) {
            ll.y = -ll.y;
        }
    }

    ll.x = Object(__WEBPACK_IMPORTED_MODULE_1__common_adjust_lon__["a" /* default */])(ll.x + this.long0);
    return ll;
}

var names = ["Robinson", "robin"];
/* harmony default export */ __webpack_exports__["default"] = ({
  init: init,
  forward: forward,
  inverse: inverse,
  names: names
});


/***/ }),
/* 97 */
/***/ (function(module, exports) {

module.exports = identity;

/**
 * Set a mat4 to the identity matrix
 *
 * @param {mat4} out the receiving matrix
 * @returns {mat4} out
 */
function identity(out) {
    out[0] = 1;
    out[1] = 0;
    out[2] = 0;
    out[3] = 0;
    out[4] = 0;
    out[5] = 1;
    out[6] = 0;
    out[7] = 0;
    out[8] = 0;
    out[9] = 0;
    out[10] = 1;
    out[11] = 0;
    out[12] = 0;
    out[13] = 0;
    out[14] = 0;
    out[15] = 1;
    return out;
};

/***/ }),
/* 98 */
/***/ (function(module, exports, __webpack_require__) {

module.exports = {
  create: __webpack_require__(204),
  clone: __webpack_require__(205),
  fromValues: __webpack_require__(206),
  copy: __webpack_require__(207),
  set: __webpack_require__(208),
  add: __webpack_require__(209),
  subtract: __webpack_require__(210),
  multiply: __webpack_require__(211),
  divide: __webpack_require__(212),
  min: __webpack_require__(213),
  max: __webpack_require__(214),
  scale: __webpack_require__(99),
  scaleAndAdd: __webpack_require__(215),
  distance: __webpack_require__(216),
  squaredDistance: __webpack_require__(217),
  length: __webpack_require__(218),
  squaredLength: __webpack_require__(219),
  negate: __webpack_require__(220),
  inverse: __webpack_require__(221),
  normalize: __webpack_require__(100),
  dot: __webpack_require__(222),
  lerp: __webpack_require__(223),
  random: __webpack_require__(224),
  transformMat4: __webpack_require__(225),
  transformQuat: __webpack_require__(226)
}


/***/ }),
/* 99 */
/***/ (function(module, exports) {

module.exports = scale

/**
 * Scales a vec4 by a scalar number
 *
 * @param {vec4} out the receiving vector
 * @param {vec4} a the vector to scale
 * @param {Number} b amount to scale the vector by
 * @returns {vec4} out
 */
function scale (out, a, b) {
  out[0] = a[0] * b
  out[1] = a[1] * b
  out[2] = a[2] * b
  out[3] = a[3] * b
  return out
}


/***/ }),
/* 100 */
/***/ (function(module, exports) {

module.exports = normalize

/**
 * Normalize a vec4
 *
 * @param {vec4} out the receiving vector
 * @param {vec4} a vector to normalize
 * @returns {vec4} out
 */
function normalize (out, a) {
  var x = a[0],
    y = a[1],
    z = a[2],
    w = a[3]
  var len = x * x + y * y + z * z + w * w
  if (len > 0) {
    len = 1 / Math.sqrt(len)
    out[0] = x * len
    out[1] = y * len
    out[2] = z * len
    out[3] = w * len
  }
  return out
}


/***/ }),
/* 101 */
/***/ (function(module, exports) {

module.exports = 0.000001


/***/ }),
/* 102 */
/***/ (function(module, exports) {

module.exports = create;

/**
 * Creates a new, empty vec3
 *
 * @returns {vec3} a new 3D vector
 */
function create() {
    var out = new Float32Array(3)
    out[0] = 0
    out[1] = 0
    out[2] = 0
    return out
}

/***/ }),
/* 103 */
/***/ (function(module, exports) {

module.exports = fromValues;

/**
 * Creates a new vec3 initialized with the given values
 *
 * @param {Number} x X component
 * @param {Number} y Y component
 * @param {Number} z Z component
 * @returns {vec3} a new 3D vector
 */
function fromValues(x, y, z) {
    var out = new Float32Array(3)
    out[0] = x
    out[1] = y
    out[2] = z
    return out
}

/***/ }),
/* 104 */
/***/ (function(module, exports) {

module.exports = normalize;

/**
 * Normalize a vec3
 *
 * @param {vec3} out the receiving vector
 * @param {vec3} a vector to normalize
 * @returns {vec3} out
 */
function normalize(out, a) {
    var x = a[0],
        y = a[1],
        z = a[2]
    var len = x*x + y*y + z*z
    if (len > 0) {
        //TODO: evaluate use of glm_invsqrt here?
        len = 1 / Math.sqrt(len)
        out[0] = a[0] * len
        out[1] = a[1] * len
        out[2] = a[2] * len
    }
    return out
}

/***/ }),
/* 105 */
/***/ (function(module, exports) {

module.exports = dot;

/**
 * Calculates the dot product of two vec3's
 *
 * @param {vec3} a the first operand
 * @param {vec3} b the second operand
 * @returns {Number} dot product of a and b
 */
function dot(a, b) {
    return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]
}

/***/ }),
/* 106 */
/***/ (function(module, exports) {

module.exports = subtract;

/**
 * Subtracts vector b from vector a
 *
 * @param {vec3} out the receiving vector
 * @param {vec3} a the first operand
 * @param {vec3} b the second operand
 * @returns {vec3} out
 */
function subtract(out, a, b) {
    out[0] = a[0] - b[0]
    out[1] = a[1] - b[1]
    out[2] = a[2] - b[2]
    return out
}

/***/ }),
/* 107 */
/***/ (function(module, exports) {

module.exports = multiply;

/**
 * Multiplies two vec3's
 *
 * @param {vec3} out the receiving vector
 * @param {vec3} a the first operand
 * @param {vec3} b the second operand
 * @returns {vec3} out
 */
function multiply(out, a, b) {
    out[0] = a[0] * b[0]
    out[1] = a[1] * b[1]
    out[2] = a[2] * b[2]
    return out
}

/***/ }),
/* 108 */
/***/ (function(module, exports) {

module.exports = divide;

/**
 * Divides two vec3's
 *
 * @param {vec3} out the receiving vector
 * @param {vec3} a the first operand
 * @param {vec3} b the second operand
 * @returns {vec3} out
 */
function divide(out, a, b) {
    out[0] = a[0] / b[0]
    out[1] = a[1] / b[1]
    out[2] = a[2] / b[2]
    return out
}

/***/ }),
/* 109 */
/***/ (function(module, exports) {

module.exports = distance;

/**
 * Calculates the euclidian distance between two vec3's
 *
 * @param {vec3} a the first operand
 * @param {vec3} b the second operand
 * @returns {Number} distance between a and b
 */
function distance(a, b) {
    var x = b[0] - a[0],
        y = b[1] - a[1],
        z = b[2] - a[2]
    return Math.sqrt(x*x + y*y + z*z)
}

/***/ }),
/* 110 */
/***/ (function(module, exports) {

module.exports = squaredDistance;

/**
 * Calculates the squared euclidian distance between two vec3's
 *
 * @param {vec3} a the first operand
 * @param {vec3} b the second operand
 * @returns {Number} squared distance between a and b
 */
function squaredDistance(a, b) {
    var x = b[0] - a[0],
        y = b[1] - a[1],
        z = b[2] - a[2]
    return x*x + y*y + z*z
}

/***/ }),
/* 111 */
/***/ (function(module, exports) {

module.exports = length;

/**
 * Calculates the length of a vec3
 *
 * @param {vec3} a vector to calculate length of
 * @returns {Number} length of a
 */
function length(a) {
    var x = a[0],
        y = a[1],
        z = a[2]
    return Math.sqrt(x*x + y*y + z*z)
}

/***/ }),
/* 112 */
/***/ (function(module, exports) {

module.exports = squaredLength;

/**
 * Calculates the squared length of a vec3
 *
 * @param {vec3} a vector to calculate squared length of
 * @returns {Number} squared length of a
 */
function squaredLength(a) {
    var x = a[0],
        y = a[1],
        z = a[2]
    return x*x + y*y + z*z
}

/***/ }),
/* 113 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var inherit = __webpack_require__(0);
var registerRenderer = __webpack_require__(1).registerRenderer;
var renderer = __webpack_require__(22);

/**
 * Create a new instance of class vglRenderer.
 *
 * @class
 * @alias geo.gl.vglRenderer
 * @extends geo.renderer
 * @param {object} arg Options for the renderer.
 * @param {geo.layer} [arg.layer] Layer associated with the renderer.
 * @param {HTMLElement} [arg.canvas] Canvas element associated with the
 *   renderer.
 * @param {object} [arg.options] Additional options for the vgl renderer.
 * @returns {geo.gl.vglRenderer}
 */
var vglRenderer = function vglRenderer(arg) {
  'use strict';

  if (!(this instanceof vglRenderer)) {
    return new vglRenderer(arg);
  }
  arg = arg || {};
  renderer.call(this, arg);

  var $ = __webpack_require__(2);
  var vgl = __webpack_require__(9);
  var mat4 = __webpack_require__(47);
  var util = __webpack_require__(3);
  var geo_event = __webpack_require__(6);

  var m_this = this,
      m_contextRenderer = null,
      m_viewer = null,
      m_width = 0,
      m_height = 0,
      m_lastZoom,
      m_updateCamera = false,
      s_init = this._init,
      s_exit = this._exit;

  // TODO: Move this API to the base class
  /**
   * Return width of the renderer.
   *
   * @returns {number} The width of the current canvas.
   */
  this.width = function () {
    return m_width;
  };

  /**
   * Return height of the renderer.
   *
   * @returns {number} The height of the current canvas.
   */
  this.height = function () {
    return m_height;
  };

  /**
   * Get context specific renderer.
   *
   * @returns {object} The vgl context renderer.
   */
  this.contextRenderer = function () {
    return m_contextRenderer;
  };

  /**
   * Get API used by the renderer.
   *
   * @returns {string} `vgl`.
   */
  this.api = function () {
    return 'vgl';
  };

  /**
   * Initialize.
   *
   * @returns {this}
   */
  this._init = function () {
    if (m_this.initialized()) {
      return m_this;
    }

    s_init.call(m_this);

    var canvas = $(document.createElement('canvas'));
    canvas.addClass('webgl-canvas');
    $(m_this.layer().node().get(0)).append(canvas);

    if (window.overrideContextAttributes) {
      var elem = canvas.get(0);
      var getContext = elem.getContext;
      elem.getContext = function (contextType, contextAttributes) {
        contextAttributes = contextAttributes || {};
        if (window.overrideContextAttributes) {
          for (var key in window.overrideContextAttributes) {
            if (window.overrideContextAttributes.hasOwnProperty(key)) {
              contextAttributes[key] = window.overrideContextAttributes[key];
            }
          }
        }
        return getContext.call(elem, contextType, contextAttributes);
      };
    }

    m_viewer = vgl.viewer(canvas.get(0), arg.options);
    m_viewer.init();
    m_contextRenderer = m_viewer.renderWindow().activeRenderer();
    m_contextRenderer.setResetScene(false);

    if (m_viewer.renderWindow().renderers().length > 0) {
      m_contextRenderer.setLayer(m_viewer.renderWindow().renderers().length);
    }
    m_this.canvas(canvas);
    /* Initialize the size of the renderer */
    var map = m_this.layer().map(),
        mapSize = map.size();
    m_this._resize(0, 0, mapSize.width, mapSize.height);

    return m_this;
  };

  /**
   * Handle resize event.
   *
   * @param {number} x The left coordinate.
   * @param {number} y The top coordinate.
   * @param {number} w The width in pixels.
   * @param {number} h The height in pixels.
   * @returns {this}
   */
  this._resize = function (x, y, w, h) {
    var renderWindow = m_viewer.renderWindow();

    if (x !== renderWindow.windowPosition[0] || y !== renderWindow.windowPosition[1] || w !== m_width || h !== m_height) {
      m_width = w;
      m_height = h;
      m_this.canvas().attr('width', w);
      m_this.canvas().attr('height', h);
      renderWindow.positionAndResize(x, y, w, h);

      m_updateCamera = true;
      m_this._render();
    }
    return m_this;
  };

  /**
   * Render.  This actually schedules rendering for the next animation frame.
   *
   * @returns {this}
   */
  this._render = function () {
    /* If we are already scheduled to render, don't schedule again.  Rather,
     * mark that we should render after other animation frame requests occur.
     * It would be nice if we could just reschedule the call by removing and
     * readding the animation frame request, but this doesn't work for if the
     * reschedule occurs during another animation frame callback (it then waits
     * until a subsequent frame). */
    m_this.layer().map().scheduleAnimationFrame(this._renderFrame, true);
    return m_this;
  };

  /**
   * This clears the render timer and actually renders.
   */
  this._renderFrame = function () {
    if (m_viewer) {
      if (m_updateCamera) {
        m_updateCamera = false;
        m_this._updateRendererCamera();
      }
      m_viewer.render();
    }
  };

  /**
   * Get the GL context for this renderer.
   *
   * @returns {WebGLRenderingContext} The current context.  If unavailable,
   *    falls back to the vgl generic context.
   */
  this._glContext = function () {
    if (m_viewer && m_viewer.renderWindow()) {
      return m_viewer.renderWindow().context();
    }
    return vgl.GL;
  };

  /**
   * Exit.
   */
  this._exit = function () {
    m_this.layer().map().scheduleAnimationFrame(this._renderFrame, 'remove');
    m_this.canvas().remove();
    if (m_viewer) {
      var renderState = new vgl.renderState();
      renderState.m_renderer = m_viewer;
      renderState.m_context = this._glContext();
      m_viewer.exit(renderState);
      if (this._glContext() !== vgl.GL && this._glContext().getExtension('WEBGL_lose_context') && this._glContext().getExtension('WEBGL_lose_context').loseContext) {
        this._glContext().getExtension('WEBGL_lose_context').loseContext();
      }
    }
    // make sure we clear shaders associated with the generate context, too
    vgl.clearCachedShaders(vgl.GL);
    m_viewer = null;
    s_exit();
  };

  /**
   * Update the vgl renderer's camera based on the map's camera class.
   */
  this._updateRendererCamera = function () {
    var renderWindow = m_viewer.renderWindow(),
        map = m_this.layer().map(),
        camera = map.camera(),
        rotation = map.rotation() || 0,
        view = camera.view,
        proj = camera.projectionMatrix;
    if (proj[15]) {
      /* In the parallel projection, we want the clipbounds [near, far] to map
       * to [0, 1].  The ortho matrix scales to [-1, 1]. */
      proj = mat4.copy(util.mat4AsArray(), proj);
      proj = mat4.scale(proj, proj, [1, 1, -0.5]);
      proj = mat4.translate(proj, proj, [0, 0, camera.clipbounds.far]);
    } else {
      /* This rescales the perspective projection to work with most gl
       * features.  It doesn't work with all clipbounds, and will probably need
       * to be refactored when we have tiltable maps. */
      var near = camera.clipbounds.near,
          far = camera.clipbounds.far;
      proj = mat4.copy(util.mat4AsArray(), proj);
      proj = mat4.scale(proj, proj, [1 / near, 1 / near, -1 / far]);
    }
    /* Check if the rotation is a multiple of 90 */
    var basis = Math.PI / 2,
        angle = rotation % basis,
        // move to range (-pi/2, pi/2)
    ortho = Math.min(Math.abs(angle), Math.abs(angle - basis)) < 0.00001;
    renderWindow.renderers().forEach(function (renderer) {
      var cam = renderer.camera();
      if (util.compareArrays(view, cam.viewMatrix()) && util.compareArrays(proj, cam.projectionMatrix()) && m_lastZoom === map.zoom()) {
        return;
      }
      m_lastZoom = map.zoom();
      cam.setViewMatrix(view, true);
      cam.setProjectionMatrix(proj);
      var viewport = camera.viewport;
      /* Test if we should align texels.  We won't if the projection matrix
       * is not simple, if there is a rotation that isn't a multiple of 90
       * degrees, if the viewport is not at an integer location, or if the zoom
       * level is not close to an integer.
       *   Note that the test for the viewport is strict (val % 1 is non-zero
       * if the value is not an integer), as, in general, the alignment is only
       * non-integral if a percent offset or calculation was used in css
       * somewhere.  The test for zoom level always has some allowance for
       * precision, as it is often the result of repeated computations. */
      if (proj[1] || proj[2] || proj[3] || proj[4] || proj[6] || proj[7] || proj[8] || proj[9] || proj[11] || proj[15] !== 1 || !ortho || viewport.left && viewport.left % 1 || viewport.top && viewport.top % 1 || parseFloat(m_lastZoom.toFixed(6)) !== parseFloat(m_lastZoom.toFixed(0))) {
        /* Don't align texels */
        cam.viewAlignment = function () {
          return null;
        };
      } else {
        /* Set information for texel alignment.  The rounding factors should
         * probably be divided by window.devicePixelRatio. */
        cam.viewAlignment = function () {
          var align = {
            roundx: 2.0 / viewport.width,
            roundy: 2.0 / viewport.height
          };
          align.dx = viewport.width % 2 ? align.roundx * 0.5 : 0;
          align.dy = viewport.height % 2 ? align.roundy * 0.5 : 0;
          return align;
        };
      }
    });
  };

  // Connect to pan event.  This is sufficient, as all zooms and rotations also
  // produce a pan
  m_this.layer().geoOn(geo_event.pan, function (evt) {
    void evt;
    m_updateCamera = true;
  });

  // Connect to parallelprojection event
  m_this.layer().geoOn(geo_event.parallelprojection, function (evt) {
    var vglRenderer = m_this.contextRenderer(),
        camera,
        layer = m_this.layer();

    if (evt.geo && evt.geo._triggeredBy !== layer) {
      if (!vglRenderer || !vglRenderer.camera()) {
        console.log('Parallel projection event triggered on unconnected VGL ' + 'renderer.');
        return;
      }
      camera = vglRenderer.camera();
      camera.setEnableParallelProjection(evt.parallelProjection);
      m_updateCamera = true;
    }
  });

  return this;
};

inherit(vglRenderer, renderer);

registerRenderer('vgl', vglRenderer);

/* Code for checking if the renderer is supported */

var checkedWebGL;

/**
 * Report if the vgl renderer is supported.  This is just a check if webGL is
 * supported and available.
 *
 * @returns {boolean} true if available.
 */
vglRenderer.supported = function () {
  if (checkedWebGL === undefined) {
    /* This is extracted from what Modernizr uses. */
    var canvas, ctx, exts; // eslint-disable-line no-unused-vars
    try {
      canvas = document.createElement('canvas');
      ctx = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
      exts = ctx.getSupportedExtensions();
      checkedWebGL = true;
    } catch (e) {
      console.warn('No webGL support');
      checkedWebGL = false;
    }
    canvas = undefined;
    ctx = undefined;
    exts = undefined;
  }
  return checkedWebGL;
};

/**
 * If the vgl renderer is not supported, supply the name of a renderer that
 * should be used instead.  This asks for the null renderer.
 *
 * @returns {null} null for the null renderer.
 */
vglRenderer.fallback = function () {
  return null;
};

module.exports = vglRenderer;

/***/ }),
/* 114 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/*
markercluster plugin:

Copyright 2012 David Leaver

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Leaflet utilities:

Copyright (c) 2010-2015, Vladimir Agafonkin
Copyright (c) 2010-2011, CloudMade
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:

   1. Redistributions of source code must retain the above copyright notice, this list of
      conditions and the following disclaimer.

   2. Redistributions in binary form must reproduce the above copyright notice, this list
      of conditions and the following disclaimer in the documentation and/or other
      materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/*
 * Code taken from https://github.com/Leaflet/Leaflet.markercluster
 * to support faster hierarchical clustering of features.
 * @copyright 2012, David Leaver
 */

var $ = __webpack_require__(2);
var L = {};
L.Util = {
    // return unique ID of an object
    stamp: function stamp(obj) {
        obj._leaflet_id = obj._leaflet_id || ++L.Util.lastId;
        return obj._leaflet_id;
    },
    lastId: 0
};

/**
 * @class
 * @alias geo.util.DistanceGrid
 */
var DistanceGrid = function DistanceGrid(cellSize) {
    this._cellSize = cellSize;
    this._sqCellSize = cellSize * cellSize;
    this._grid = {};
    this._objectPoint = {};
};

DistanceGrid.prototype = {

    addObject: function addObject(obj, point) {
        var x = this._getCoord(point.x),
            y = this._getCoord(point.y),
            grid = this._grid,
            row = grid[y] = grid[y] || {},
            cell = row[x] = row[x] || [],
            stamp = L.Util.stamp(obj);

        point.obj = obj;
        this._objectPoint[stamp] = point;

        cell.push(obj);
    },

    updateObject: function updateObject(obj, point) {
        this.removeObject(obj);
        this.addObject(obj, point);
    },

    //Returns true if the object was found
    removeObject: function removeObject(obj, point) {
        var x = this._getCoord(point.x),
            y = this._getCoord(point.y),
            grid = this._grid,
            row = grid[y] = grid[y] || {},
            cell = row[x] = row[x] || [],
            i,
            len;

        delete this._objectPoint[L.Util.stamp(obj)];

        for (i = 0, len = cell.length; i < len; i++) {
            if (cell[i] === obj) {

                cell.splice(i, 1);

                if (len === 1) {
                    delete row[x];
                }

                return true;
            }
        }
    },

    eachObject: function eachObject(fn, context) {
        var i,
            j,
            k,
            len,
            row,
            cell,
            removed,
            grid = this._grid;

        for (i in grid) {
            row = grid[i];

            for (j in row) {
                cell = row[j];

                for (k = 0, len = cell.length; k < len; k++) {
                    removed = fn.call(context, cell[k]);
                    if (removed) {
                        k--;
                        len--;
                    }
                }
            }
        }
    },

    getNearObject: function getNearObject(point) {
        var x = this._getCoord(point.x),
            y = this._getCoord(point.y),
            i,
            j,
            k,
            row,
            cell,
            len,
            obj,
            dist,
            objectPoint = this._objectPoint,
            closestDistSq = this._sqCellSize,
            closest = null;

        for (i = y - 1; i <= y + 1; i++) {
            row = this._grid[i];
            if (row) {

                for (j = x - 1; j <= x + 1; j++) {
                    cell = row[j];
                    if (cell) {

                        for (k = 0, len = cell.length; k < len; k++) {
                            obj = cell[k];
                            dist = this._sqDist(objectPoint[L.Util.stamp(obj)], point);
                            if (dist < closestDistSq) {
                                closestDistSq = dist;
                                closest = obj;
                            }
                        }
                    }
                }
            }
        }
        return closest;
    },

    /* return the point coordinates contained in the structure */
    contents: function contents() {
        return $.map(this._objectPoint, function (val) {
            return val;
        });
    },

    _getCoord: function _getCoord(x) {
        return Math.floor(x / this._cellSize);
    },

    _sqDist: function _sqDist(p, p2) {
        var dx = p2.x - p.x,
            dy = p2.y - p.y;
        return dx * dx + dy * dy;
    }
};

module.exports = DistanceGrid;

/***/ }),
/* 115 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/*
 * Using methods adapted from leaflet to cluster an array of positions
 * hierarchically given an array of length scales (zoom levels).
 */

var $ = __webpack_require__(2);
var vgl = __webpack_require__(9);

/**
 * This class manages a group of nearby points that are clustered as a
 * single object for display purposes.  The class constructor is private
 * and only meant to be created by the ClusterGroup object.
 *
 * This is a tree-like data structure.  Each node in the tree is a
 * cluster containing child clusters and unclustered points.
 *
 * @class
 * @alias geo.util.ClusterTree
 *
 * @param {geo.util.ClusterGroup} group The source cluster group
 * @param {number} zoom The zoom level of the current node
 * @param {object[]} children An array of ClusterTrees or point objects
 */
function ClusterTree(group, zoom, children) {
  this._group = group;
  this._zoom = zoom;
  this._points = []; // Unclustered points
  this._clusters = []; // Child clusters
  this._count = 0; // Total number of points
  this._parent = null;
  this._coord = null; // The cached coordinates
  var that = this;

  // add the children provided in the constructor call
  (children || []).forEach(function (c) {
    that._add(c);
  });
}

/**
 * Add a point or cluster as a child to the current cluster.
 * @param {object} pt A ClusterTree or point object
 * @private
 */
ClusterTree.prototype._add = function (pt) {
  var inc = 1;

  if (pt instanceof ClusterTree) {
    // add a child cluster
    this._clusters.push(pt);
    inc = pt._count;
  } else {
    this._points.push(pt);
  }
  pt._parent = this;

  // increment the counter
  this._increment(inc);
};

/**
 * Increment the child counter for this and the parent.
 * @param {number} inc The value to increment by
 * @private
 */
ClusterTree.prototype._increment = function (inc) {
  this._coord = null;
  this._count += inc;
  if (this._parent) {
    this._parent._increment(inc);
  }
};

/**
 * Return the total number of child points contained in the cluster.
 * @returns {number} Total points contained
 */
ClusterTree.prototype.count = function () {
  return this._count;
};

/**
 * Recursively call a function on all points contained in the cluster.
 * Calls the function with `this` as the current ClusterTree object, and
 * arguments to arguments the point object and the zoom level:
 * `func.call(this, point, zoom)`.
 *
 * @param {function} func The function to call.
 */
ClusterTree.prototype.each = function (func) {
  var i;
  for (i = 0; i < this._points.length; i += 1) {
    func.call(this, this._points[i], this._zoom);
  }
  for (i = 0; i < this._clusters.length; i += 1) {
    this._clusters[i].each.call(this._clusters[i], func);
  }
};

/**
 * Get the coordinates of the cluster (the mean position of all the points
 * contained).  This is lazily calculated and cached.
 *
 * @returns {geo.geoPosition} The 2-d coordinates of the center.
 */
ClusterTree.prototype.coords = function () {
  var i,
      center = { x: 0, y: 0 };
  if (this._coord) {
    return this._coord;
  }
  // first add up the points at the node
  for (i = 0; i < this._points.length; i += 1) {
    center.x += this._points[i].x;
    center.y += this._points[i].y;
  }

  // add up the contribution from the clusters
  for (i = 0; i < this._clusters.length; i += 1) {
    center.x += this._clusters[i].coords().x * this._clusters[i].count();
    center.y += this._clusters[i].coords().y * this._clusters[i].count();
  }

  return {
    x: center.x / this.count(),
    y: center.y / this.count()
  };
};

/**
 * This class manages clustering of an array of positions hierarchically.
 * The algorithm and code was adapted from the Leaflet marker cluster
 * plugin by David Leaver: https://github.com/Leaflet/Leaflet.markercluster .
 *
 * @class
 * @alias geo.util.ClusterGroup
 * @param {object} opts An options object
 * @param {number} width The width of the window; used for scaling.
 * @param {number} height The height of the window; used for scaling.
 * @param {number} maxZoom The maximimum zoom level to calculate.
 * @param {number} radius Proportional to the clustering radius in pixels.
 */
function C(opts, width, height) {

  var DistanceGrid = __webpack_require__(114);

  // store the options
  this._opts = $.extend({
    maxZoom: 18,
    radius: 0.05
  }, opts);
  this._opts.width = this._opts.width || width || 256;
  this._opts.height = this._opts.height || height || 256;

  // generate the initial datastructures
  this._clusters = {}; // clusters at each zoom level
  this._points = {}; // unclustered points at each zoom level

  var zoom, scl;
  for (zoom = this._opts.maxZoom; zoom >= 0; zoom -= 1) {
    scl = this._scaleAtLevel(zoom, this._opts.width, this._opts.height);
    this._clusters[zoom] = new DistanceGrid(scl);
    this._points[zoom] = new DistanceGrid(scl);
  }
  this._topClusterLevel = new ClusterTree(this, -1);
}

/**
 * Returns a characteristic distance scale at a particular zoom level.  This
 * scale is used to control the clustering radius.  When the renderer supports
 * it, this call should be replaced by a calculation involving the view port
 * size in point coordinates at a particular zoom level.
 * @private
 * @param {number} zoom The zoom level for the scale.
 * @param {number} width The viewport width.
 * @param {number} height The viewport height.
 * @returns {number} The scale for clustering the feature coordinates.
 */
C.prototype._scaleAtLevel = function (zoom, width, height) {
  return vgl.zoomToHeight(zoom, width, height) / 2 * this._opts.radius;
};

/**
 * Add a position to the cluster group.
 * @protected
 * @param {geo.geoPosition} point A point to add to the cluster.
 */
C.prototype.addPoint = function (point) {
  var zoom, closest, parent, newCluster, lastParent, z;
  /*
   * start at the maximum zoom level and search for nearby
   *
   * 1.  existing clusters
   * 2.  unclustered points
   *
   * otherwise add the point as a new unclustered point
   */
  for (zoom = this._opts.maxZoom; zoom >= 0; zoom -= 1) {

    // find near cluster
    closest = this._clusters[zoom].getNearObject(point);
    if (closest) {
      // add the point to the cluster and return
      closest._add(point);
      return;
    }

    // find near point
    closest = this._points[zoom].getNearObject(point);
    if (closest) {
      parent = closest._parent;
      if (parent) {
        // remove the point from the parent
        for (z = parent._points.length - 1; z >= 0; z -= 1) {
          if (parent._points[z] === closest) {
            parent._points.splice(z, 1);
            parent._increment(-1);
            break;
          }
        }
      }

      if (!parent) {
        $.noop();
      }
      // create a new cluster with these two points
      newCluster = new ClusterTree(this, zoom, [closest, point]);
      this._clusters[zoom].addObject(newCluster, newCluster.coords());

      // create intermediate parent clusters that don't exist
      lastParent = newCluster;
      for (z = zoom - 1; z > parent._zoom; z -= 1) {
        lastParent = new ClusterTree(this, z, [lastParent]);
        this._clusters[z].addObject(lastParent, lastParent.coords());
      }
      parent._add(lastParent);

      // remove closest from this zoom level and any above (replace with newCluster)
      for (z = zoom; z >= 0; z -= 1) {
        if (!this._points[z].removeObject(closest, closest)) {
          break;
        }
      }

      return;
    }

    // add an unclustered point
    this._points[zoom].addObject(point, point);
  }

  // otherwise add to the top
  this._topClusterLevel._add(point);
};

/**
 * Return the unclustered points contained at a given zoom level.
 * @param {number} zoom The zoom level.
 * @returns {object[]} The array of unclustered points.
 */
C.prototype.points = function (zoom) {
  zoom = Math.min(Math.max(Math.floor(zoom), 0), this._opts.maxZoom - 1);
  return this._points[Math.floor(zoom)].contents();
};

/**
 * Return the clusters contained at a given zoom level.
 * @param {number} zoom The zoom level.
 * @returns {geo.util.ClusterTree[]} The array of clusters.
 */
C.prototype.clusters = function (zoom) {
  zoom = Math.min(Math.max(Math.floor(zoom), 0), this._opts.maxZoom - 1);
  return this._clusters[Math.floor(zoom)].contents();
};

module.exports = C;

/***/ }),
/* 116 */
/***/ (function(module, exports, __webpack_require__) {

module.exports = {
  adjoint: __webpack_require__(260)
  , clone: __webpack_require__(261)
  , copy: __webpack_require__(262)
  , create: __webpack_require__(263)
  , determinant: __webpack_require__(264)
  , frob: __webpack_require__(265)
  , fromMat2: __webpack_require__(266)
  , fromMat4: __webpack_require__(267)
  , fromQuat: __webpack_require__(268)
  , identity: __webpack_require__(269)
  , invert: __webpack_require__(270)
  , multiply: __webpack_require__(271)
  , normalFromMat4: __webpack_require__(272)
  , rotate: __webpack_require__(273)
  , scale: __webpack_require__(274)
  , str: __webpack_require__(275)
  , translate: __webpack_require__(276)
  , transpose: __webpack_require__(277)
}


/***/ }),
/* 117 */
/***/ (function(module, exports, __webpack_require__) {

var __WEBPACK_AMD_DEFINE_RESULT__;/*global define:false */
/**
 * Copyright 2012-2017 Craig Campbell
 *
 * 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.
 *
 * Mousetrap is a simple keyboard shortcut library for Javascript with
 * no external dependencies
 *
 * @version 1.6.2
 * @url craig.is/killing/mice
 */
(function(window, document, undefined) {

    // Check if mousetrap is used inside browser, if not, return
    if (!window) {
        return;
    }

    /**
     * mapping of special keycodes to their corresponding keys
     *
     * everything in this dictionary cannot use keypress events
     * so it has to be here to map to the correct keycodes for
     * keyup/keydown events
     *
     * @type {Object}
     */
    var _MAP = {
        8: 'backspace',
        9: 'tab',
        13: 'enter',
        16: 'shift',
        17: 'ctrl',
        18: 'alt',
        20: 'capslock',
        27: 'esc',
        32: 'space',
        33: 'pageup',
        34: 'pagedown',
        35: 'end',
        36: 'home',
        37: 'left',
        38: 'up',
        39: 'right',
        40: 'down',
        45: 'ins',
        46: 'del',
        91: 'meta',
        93: 'meta',
        224: 'meta'
    };

    /**
     * mapping for special characters so they can support
     *
     * this dictionary is only used incase you want to bind a
     * keyup or keydown event to one of these keys
     *
     * @type {Object}
     */
    var _KEYCODE_MAP = {
        106: '*',
        107: '+',
        109: '-',
        110: '.',
        111 : '/',
        186: ';',
        187: '=',
        188: ',',
        189: '-',
        190: '.',
        191: '/',
        192: '`',
        219: '[',
        220: '\\',
        221: ']',
        222: '\''
    };

    /**
     * this is a mapping of keys that require shift on a US keypad
     * back to the non shift equivelents
     *
     * this is so you can use keyup events with these keys
     *
     * note that this will only work reliably on US keyboards
     *
     * @type {Object}
     */
    var _SHIFT_MAP = {
        '~': '`',
        '!': '1',
        '@': '2',
        '#': '3',
        '$': '4',
        '%': '5',
        '^': '6',
        '&': '7',
        '*': '8',
        '(': '9',
        ')': '0',
        '_': '-',
        '+': '=',
        ':': ';',
        '\"': '\'',
        '<': ',',
        '>': '.',
        '?': '/',
        '|': '\\'
    };

    /**
     * this is a list of special strings you can use to map
     * to modifier keys when you specify your keyboard shortcuts
     *
     * @type {Object}
     */
    var _SPECIAL_ALIASES = {
        'option': 'alt',
        'command': 'meta',
        'return': 'enter',
        'escape': 'esc',
        'plus': '+',
        'mod': /Mac|iPod|iPhone|iPad/.test(navigator.platform) ? 'meta' : 'ctrl'
    };

    /**
     * variable to store the flipped version of _MAP from above
     * needed to check if we should use keypress or not when no action
     * is specified
     *
     * @type {Object|undefined}
     */
    var _REVERSE_MAP;

    /**
     * loop through the f keys, f1 to f19 and add them to the map
     * programatically
     */
    for (var i = 1; i < 20; ++i) {
        _MAP[111 + i] = 'f' + i;
    }

    /**
     * loop through to map numbers on the numeric keypad
     */
    for (i = 0; i <= 9; ++i) {

        // This needs to use a string cause otherwise since 0 is falsey
        // mousetrap will never fire for numpad 0 pressed as part of a keydown
        // event.
        //
        // @see https://github.com/ccampbell/mousetrap/pull/258
        _MAP[i + 96] = i.toString();
    }

    /**
     * cross browser add event method
     *
     * @param {Element|HTMLDocument} object
     * @param {string} type
     * @param {Function} callback
     * @returns void
     */
    function _addEvent(object, type, callback) {
        if (object.addEventListener) {
            object.addEventListener(type, callback, false);
            return;
        }

        object.attachEvent('on' + type, callback);
    }

    /**
     * takes the event and returns the key character
     *
     * @param {Event} e
     * @return {string}
     */
    function _characterFromEvent(e) {

        // for keypress events we should return the character as is
        if (e.type == 'keypress') {
            var character = String.fromCharCode(e.which);

            // if the shift key is not pressed then it is safe to assume
            // that we want the character to be lowercase.  this means if
            // you accidentally have caps lock on then your key bindings
            // will continue to work
            //
            // the only side effect that might not be desired is if you
            // bind something like 'A' cause you want to trigger an
            // event when capital A is pressed caps lock will no longer
            // trigger the event.  shift+a will though.
            if (!e.shiftKey) {
                character = character.toLowerCase();
            }

            return character;
        }

        // for non keypress events the special maps are needed
        if (_MAP[e.which]) {
            return _MAP[e.which];
        }

        if (_KEYCODE_MAP[e.which]) {
            return _KEYCODE_MAP[e.which];
        }

        // if it is not in the special map

        // with keydown and keyup events the character seems to always
        // come in as an uppercase character whether you are pressing shift
        // or not.  we should make sure it is always lowercase for comparisons
        return String.fromCharCode(e.which).toLowerCase();
    }

    /**
     * checks if two arrays are equal
     *
     * @param {Array} modifiers1
     * @param {Array} modifiers2
     * @returns {boolean}
     */
    function _modifiersMatch(modifiers1, modifiers2) {
        return modifiers1.sort().join(',') === modifiers2.sort().join(',');
    }

    /**
     * takes a key event and figures out what the modifiers are
     *
     * @param {Event} e
     * @returns {Array}
     */
    function _eventModifiers(e) {
        var modifiers = [];

        if (e.shiftKey) {
            modifiers.push('shift');
        }

        if (e.altKey) {
            modifiers.push('alt');
        }

        if (e.ctrlKey) {
            modifiers.push('ctrl');
        }

        if (e.metaKey) {
            modifiers.push('meta');
        }

        return modifiers;
    }

    /**
     * prevents default for this event
     *
     * @param {Event} e
     * @returns void
     */
    function _preventDefault(e) {
        if (e.preventDefault) {
            e.preventDefault();
            return;
        }

        e.returnValue = false;
    }

    /**
     * stops propogation for this event
     *
     * @param {Event} e
     * @returns void
     */
    function _stopPropagation(e) {
        if (e.stopPropagation) {
            e.stopPropagation();
            return;
        }

        e.cancelBubble = true;
    }

    /**
     * determines if the keycode specified is a modifier key or not
     *
     * @param {string} key
     * @returns {boolean}
     */
    function _isModifier(key) {
        return key == 'shift' || key == 'ctrl' || key == 'alt' || key == 'meta';
    }

    /**
     * reverses the map lookup so that we can look for specific keys
     * to see what can and can't use keypress
     *
     * @return {Object}
     */
    function _getReverseMap() {
        if (!_REVERSE_MAP) {
            _REVERSE_MAP = {};
            for (var key in _MAP) {

                // pull out the numeric keypad from here cause keypress should
                // be able to detect the keys from the character
                if (key > 95 && key < 112) {
                    continue;
                }

                if (_MAP.hasOwnProperty(key)) {
                    _REVERSE_MAP[_MAP[key]] = key;
                }
            }
        }
        return _REVERSE_MAP;
    }

    /**
     * picks the best action based on the key combination
     *
     * @param {string} key - character for key
     * @param {Array} modifiers
     * @param {string=} action passed in
     */
    function _pickBestAction(key, modifiers, action) {

        // if no action was picked in we should try to pick the one
        // that we think would work best for this key
        if (!action) {
            action = _getReverseMap()[key] ? 'keydown' : 'keypress';
        }

        // modifier keys don't work as expected with keypress,
        // switch to keydown
        if (action == 'keypress' && modifiers.length) {
            action = 'keydown';
        }

        return action;
    }

    /**
     * Converts from a string key combination to an array
     *
     * @param  {string} combination like "command+shift+l"
     * @return {Array}
     */
    function _keysFromString(combination) {
        if (combination === '+') {
            return ['+'];
        }

        combination = combination.replace(/\+{2}/g, '+plus');
        return combination.split('+');
    }

    /**
     * Gets info for a specific key combination
     *
     * @param  {string} combination key combination ("command+s" or "a" or "*")
     * @param  {string=} action
     * @returns {Object}
     */
    function _getKeyInfo(combination, action) {
        var keys;
        var key;
        var i;
        var modifiers = [];

        // take the keys from this pattern and figure out what the actual
        // pattern is all about
        keys = _keysFromString(combination);

        for (i = 0; i < keys.length; ++i) {
            key = keys[i];

            // normalize key names
            if (_SPECIAL_ALIASES[key]) {
                key = _SPECIAL_ALIASES[key];
            }

            // if this is not a keypress event then we should
            // be smart about using shift keys
            // this will only work for US keyboards however
            if (action && action != 'keypress' && _SHIFT_MAP[key]) {
                key = _SHIFT_MAP[key];
                modifiers.push('shift');
            }

            // if this key is a modifier then add it to the list of modifiers
            if (_isModifier(key)) {
                modifiers.push(key);
            }
        }

        // depending on what the key combination is
        // we will try to pick the best event for it
        action = _pickBestAction(key, modifiers, action);

        return {
            key: key,
            modifiers: modifiers,
            action: action
        };
    }

    function _belongsTo(element, ancestor) {
        if (element === null || element === document) {
            return false;
        }

        if (element === ancestor) {
            return true;
        }

        return _belongsTo(element.parentNode, ancestor);
    }

    function Mousetrap(targetElement) {
        var self = this;

        targetElement = targetElement || document;

        if (!(self instanceof Mousetrap)) {
            return new Mousetrap(targetElement);
        }

        /**
         * element to attach key events to
         *
         * @type {Element}
         */
        self.target = targetElement;

        /**
         * a list of all the callbacks setup via Mousetrap.bind()
         *
         * @type {Object}
         */
        self._callbacks = {};

        /**
         * direct map of string combinations to callbacks used for trigger()
         *
         * @type {Object}
         */
        self._directMap = {};

        /**
         * keeps track of what level each sequence is at since multiple
         * sequences can start out with the same sequence
         *
         * @type {Object}
         */
        var _sequenceLevels = {};

        /**
         * variable to store the setTimeout call
         *
         * @type {null|number}
         */
        var _resetTimer;

        /**
         * temporary state where we will ignore the next keyup
         *
         * @type {boolean|string}
         */
        var _ignoreNextKeyup = false;

        /**
         * temporary state where we will ignore the next keypress
         *
         * @type {boolean}
         */
        var _ignoreNextKeypress = false;

        /**
         * are we currently inside of a sequence?
         * type of action ("keyup" or "keydown" or "keypress") or false
         *
         * @type {boolean|string}
         */
        var _nextExpectedAction = false;

        /**
         * resets all sequence counters except for the ones passed in
         *
         * @param {Object} doNotReset
         * @returns void
         */
        function _resetSequences(doNotReset) {
            doNotReset = doNotReset || {};

            var activeSequences = false,
                key;

            for (key in _sequenceLevels) {
                if (doNotReset[key]) {
                    activeSequences = true;
                    continue;
                }
                _sequenceLevels[key] = 0;
            }

            if (!activeSequences) {
                _nextExpectedAction = false;
            }
        }

        /**
         * finds all callbacks that match based on the keycode, modifiers,
         * and action
         *
         * @param {string} character
         * @param {Array} modifiers
         * @param {Event|Object} e
         * @param {string=} sequenceName - name of the sequence we are looking for
         * @param {string=} combination
         * @param {number=} level
         * @returns {Array}
         */
        function _getMatches(character, modifiers, e, sequenceName, combination, level) {
            var i;
            var callback;
            var matches = [];
            var action = e.type;

            // if there are no events related to this keycode
            if (!self._callbacks[character]) {
                return [];
            }

            // if a modifier key is coming up on its own we should allow it
            if (action == 'keyup' && _isModifier(character)) {
                modifiers = [character];
            }

            // loop through all callbacks for the key that was pressed
            // and see if any of them match
            for (i = 0; i < self._callbacks[character].length; ++i) {
                callback = self._callbacks[character][i];

                // if a sequence name is not specified, but this is a sequence at
                // the wrong level then move onto the next match
                if (!sequenceName && callback.seq && _sequenceLevels[callback.seq] != callback.level) {
                    continue;
                }

                // if the action we are looking for doesn't match the action we got
                // then we should keep going
                if (action != callback.action) {
                    continue;
                }

                // if this is a keypress event and the meta key and control key
                // are not pressed that means that we need to only look at the
                // character, otherwise check the modifiers as well
                //
                // chrome will not fire a keypress if meta or control is down
                // safari will fire a keypress if meta or meta+shift is down
                // firefox will fire a keypress if meta or control is down
                if ((action == 'keypress' && !e.metaKey && !e.ctrlKey) || _modifiersMatch(modifiers, callback.modifiers)) {

                    // when you bind a combination or sequence a second time it
                    // should overwrite the first one.  if a sequenceName or
                    // combination is specified in this call it does just that
                    //
                    // @todo make deleting its own method?
                    var deleteCombo = !sequenceName && callback.combo == combination;
                    var deleteSequence = sequenceName && callback.seq == sequenceName && callback.level == level;
                    if (deleteCombo || deleteSequence) {
                        self._callbacks[character].splice(i, 1);
                    }

                    matches.push(callback);
                }
            }

            return matches;
        }

        /**
         * actually calls the callback function
         *
         * if your callback function returns false this will use the jquery
         * convention - prevent default and stop propogation on the event
         *
         * @param {Function} callback
         * @param {Event} e
         * @returns void
         */
        function _fireCallback(callback, e, combo, sequence) {

            // if this event should not happen stop here
            if (self.stopCallback(e, e.target || e.srcElement, combo, sequence)) {
                return;
            }

            if (callback(e, combo) === false) {
                _preventDefault(e);
                _stopPropagation(e);
            }
        }

        /**
         * handles a character key event
         *
         * @param {string} character
         * @param {Array} modifiers
         * @param {Event} e
         * @returns void
         */
        self._handleKey = function(character, modifiers, e) {
            var callbacks = _getMatches(character, modifiers, e);
            var i;
            var doNotReset = {};
            var maxLevel = 0;
            var processedSequenceCallback = false;

            // Calculate the maxLevel for sequences so we can only execute the longest callback sequence
            for (i = 0; i < callbacks.length; ++i) {
                if (callbacks[i].seq) {
                    maxLevel = Math.max(maxLevel, callbacks[i].level);
                }
            }

            // loop through matching callbacks for this key event
            for (i = 0; i < callbacks.length; ++i) {

                // fire for all sequence callbacks
                // this is because if for example you have multiple sequences
                // bound such as "g i" and "g t" they both need to fire the
                // callback for matching g cause otherwise you can only ever
                // match the first one
                if (callbacks[i].seq) {

                    // only fire callbacks for the maxLevel to prevent
                    // subsequences from also firing
                    //
                    // for example 'a option b' should not cause 'option b' to fire
                    // even though 'option b' is part of the other sequence
                    //
                    // any sequences that do not match here will be discarded
                    // below by the _resetSequences call
                    if (callbacks[i].level != maxLevel) {
                        continue;
                    }

                    processedSequenceCallback = true;

                    // keep a list of which sequences were matches for later
                    doNotReset[callbacks[i].seq] = 1;
                    _fireCallback(callbacks[i].callback, e, callbacks[i].combo, callbacks[i].seq);
                    continue;
                }

                // if there were no sequence matches but we are still here
                // that means this is a regular match so we should fire that
                if (!processedSequenceCallback) {
                    _fireCallback(callbacks[i].callback, e, callbacks[i].combo);
                }
            }

            // if the key you pressed matches the type of sequence without
            // being a modifier (ie "keyup" or "keypress") then we should
            // reset all sequences that were not matched by this event
            //
            // this is so, for example, if you have the sequence "h a t" and you
            // type "h e a r t" it does not match.  in this case the "e" will
            // cause the sequence to reset
            //
            // modifier keys are ignored because you can have a sequence
            // that contains modifiers such as "enter ctrl+space" and in most
            // cases the modifier key will be pressed before the next key
            //
            // also if you have a sequence such as "ctrl+b a" then pressing the
            // "b" key will trigger a "keypress" and a "keydown"
            //
            // the "keydown" is expected when there is a modifier, but the
            // "keypress" ends up matching the _nextExpectedAction since it occurs
            // after and that causes the sequence to reset
            //
            // we ignore keypresses in a sequence that directly follow a keydown
            // for the same character
            var ignoreThisKeypress = e.type == 'keypress' && _ignoreNextKeypress;
            if (e.type == _nextExpectedAction && !_isModifier(character) && !ignoreThisKeypress) {
                _resetSequences(doNotReset);
            }

            _ignoreNextKeypress = processedSequenceCallback && e.type == 'keydown';
        };

        /**
         * handles a keydown event
         *
         * @param {Event} e
         * @returns void
         */
        function _handleKeyEvent(e) {

            // normalize e.which for key events
            // @see http://stackoverflow.com/questions/4285627/javascript-keycode-vs-charcode-utter-confusion
            if (typeof e.which !== 'number') {
                e.which = e.keyCode;
            }

            var character = _characterFromEvent(e);

            // no character found then stop
            if (!character) {
                return;
            }

            // need to use === for the character check because the character can be 0
            if (e.type == 'keyup' && _ignoreNextKeyup === character) {
                _ignoreNextKeyup = false;
                return;
            }

            self.handleKey(character, _eventModifiers(e), e);
        }

        /**
         * called to set a 1 second timeout on the specified sequence
         *
         * this is so after each key press in the sequence you have 1 second
         * to press the next key before you have to start over
         *
         * @returns void
         */
        function _resetSequenceTimer() {
            clearTimeout(_resetTimer);
            _resetTimer = setTimeout(_resetSequences, 1000);
        }

        /**
         * binds a key sequence to an event
         *
         * @param {string} combo - combo specified in bind call
         * @param {Array} keys
         * @param {Function} callback
         * @param {string=} action
         * @returns void
         */
        function _bindSequence(combo, keys, callback, action) {

            // start off by adding a sequence level record for this combination
            // and setting the level to 0
            _sequenceLevels[combo] = 0;

            /**
             * callback to increase the sequence level for this sequence and reset
             * all other sequences that were active
             *
             * @param {string} nextAction
             * @returns {Function}
             */
            function _increaseSequence(nextAction) {
                return function() {
                    _nextExpectedAction = nextAction;
                    ++_sequenceLevels[combo];
                    _resetSequenceTimer();
                };
            }

            /**
             * wraps the specified callback inside of another function in order
             * to reset all sequence counters as soon as this sequence is done
             *
             * @param {Event} e
             * @returns void
             */
            function _callbackAndReset(e) {
                _fireCallback(callback, e, combo);

                // we should ignore the next key up if the action is key down
                // or keypress.  this is so if you finish a sequence and
                // release the key the final key will not trigger a keyup
                if (action !== 'keyup') {
                    _ignoreNextKeyup = _characterFromEvent(e);
                }

                // weird race condition if a sequence ends with the key
                // another sequence begins with
                setTimeout(_resetSequences, 10);
            }

            // loop through keys one at a time and bind the appropriate callback
            // function.  for any key leading up to the final one it should
            // increase the sequence. after the final, it should reset all sequences
            //
            // if an action is specified in the original bind call then that will
            // be used throughout.  otherwise we will pass the action that the
            // next key in the sequence should match.  this allows a sequence
            // to mix and match keypress and keydown events depending on which
            // ones are better suited to the key provided
            for (var i = 0; i < keys.length; ++i) {
                var isFinal = i + 1 === keys.length;
                var wrappedCallback = isFinal ? _callbackAndReset : _increaseSequence(action || _getKeyInfo(keys[i + 1]).action);
                _bindSingle(keys[i], wrappedCallback, action, combo, i);
            }
        }

        /**
         * binds a single keyboard combination
         *
         * @param {string} combination
         * @param {Function} callback
         * @param {string=} action
         * @param {string=} sequenceName - name of sequence if part of sequence
         * @param {number=} level - what part of the sequence the command is
         * @returns void
         */
        function _bindSingle(combination, callback, action, sequenceName, level) {

            // store a direct mapped reference for use with Mousetrap.trigger
            self._directMap[combination + ':' + action] = callback;

            // make sure multiple spaces in a row become a single space
            combination = combination.replace(/\s+/g, ' ');

            var sequence = combination.split(' ');
            var info;

            // if this pattern is a sequence of keys then run through this method
            // to reprocess each pattern one key at a time
            if (sequence.length > 1) {
                _bindSequence(combination, sequence, callback, action);
                return;
            }

            info = _getKeyInfo(combination, action);

            // make sure to initialize array if this is the first time
            // a callback is added for this key
            self._callbacks[info.key] = self._callbacks[info.key] || [];

            // remove an existing match if there is one
            _getMatches(info.key, info.modifiers, {type: info.action}, sequenceName, combination, level);

            // add this call back to the array
            // if it is a sequence put it at the beginning
            // if not put it at the end
            //
            // this is important because the way these are processed expects
            // the sequence ones to come first
            self._callbacks[info.key][sequenceName ? 'unshift' : 'push']({
                callback: callback,
                modifiers: info.modifiers,
                action: info.action,
                seq: sequenceName,
                level: level,
                combo: combination
            });
        }

        /**
         * binds multiple combinations to the same callback
         *
         * @param {Array} combinations
         * @param {Function} callback
         * @param {string|undefined} action
         * @returns void
         */
        self._bindMultiple = function(combinations, callback, action) {
            for (var i = 0; i < combinations.length; ++i) {
                _bindSingle(combinations[i], callback, action);
            }
        };

        // start!
        _addEvent(targetElement, 'keypress', _handleKeyEvent);
        _addEvent(targetElement, 'keydown', _handleKeyEvent);
        _addEvent(targetElement, 'keyup', _handleKeyEvent);
    }

    /**
     * binds an event to mousetrap
     *
     * can be a single key, a combination of keys separated with +,
     * an array of keys, or a sequence of keys separated by spaces
     *
     * be sure to list the modifier keys first to make sure that the
     * correct key ends up getting bound (the last key in the pattern)
     *
     * @param {string|Array} keys
     * @param {Function} callback
     * @param {string=} action - 'keypress', 'keydown', or 'keyup'
     * @returns void
     */
    Mousetrap.prototype.bind = function(keys, callback, action) {
        var self = this;
        keys = keys instanceof Array ? keys : [keys];
        self._bindMultiple.call(self, keys, callback, action);
        return self;
    };

    /**
     * unbinds an event to mousetrap
     *
     * the unbinding sets the callback function of the specified key combo
     * to an empty function and deletes the corresponding key in the
     * _directMap dict.
     *
     * TODO: actually remove this from the _callbacks dictionary instead
     * of binding an empty function
     *
     * the keycombo+action has to be exactly the same as
     * it was defined in the bind method
     *
     * @param {string|Array} keys
     * @param {string} action
     * @returns void
     */
    Mousetrap.prototype.unbind = function(keys, action) {
        var self = this;
        return self.bind.call(self, keys, function() {}, action);
    };

    /**
     * triggers an event that has already been bound
     *
     * @param {string} keys
     * @param {string=} action
     * @returns void
     */
    Mousetrap.prototype.trigger = function(keys, action) {
        var self = this;
        if (self._directMap[keys + ':' + action]) {
            self._directMap[keys + ':' + action]({}, keys);
        }
        return self;
    };

    /**
     * resets the library back to its initial state.  this is useful
     * if you want to clear out the current keyboard shortcuts and bind
     * new ones - for example if you switch to another page
     *
     * @returns void
     */
    Mousetrap.prototype.reset = function() {
        var self = this;
        self._callbacks = {};
        self._directMap = {};
        return self;
    };

    /**
     * should we stop this event before firing off callbacks
     *
     * @param {Event} e
     * @param {Element} element
     * @return {boolean}
     */
    Mousetrap.prototype.stopCallback = function(e, element) {
        var self = this;

        // if the element has the class "mousetrap" then no need to stop
        if ((' ' + element.className + ' ').indexOf(' mousetrap ') > -1) {
            return false;
        }

        if (_belongsTo(element, self.target)) {
            return false;
        }

        // stop for input, select, and textarea
        return element.tagName == 'INPUT' || element.tagName == 'SELECT' || element.tagName == 'TEXTAREA' || element.isContentEditable;
    };

    /**
     * exposes _handleKey publicly so it can be overwritten by extensions
     */
    Mousetrap.prototype.handleKey = function() {
        var self = this;
        return self._handleKey.apply(self, arguments);
    };

    /**
     * allow custom key mappings
     */
    Mousetrap.addKeycodes = function(object) {
        for (var key in object) {
            if (object.hasOwnProperty(key)) {
                _MAP[key] = object[key];
            }
        }
        _REVERSE_MAP = null;
    };

    /**
     * Init the global mousetrap functions
     *
     * This method is needed to allow the global mousetrap functions to work
     * now that mousetrap is a constructor function.
     */
    Mousetrap.init = function() {
        var documentMousetrap = Mousetrap(document);
        for (var method in documentMousetrap) {
            if (method.charAt(0) !== '_') {
                Mousetrap[method] = (function(method) {
                    return function() {
                        return documentMousetrap[method].apply(documentMousetrap, arguments);
                    };
                } (method));
            }
        }
    };

    Mousetrap.init();

    // expose mousetrap to the global object
    window.Mousetrap = Mousetrap;

    // expose as a common js module
    if (typeof module !== 'undefined' && module.exports) {
        module.exports = Mousetrap;
    }

    // expose mousetrap as an AMD module
    if (true) {
        !(__WEBPACK_AMD_DEFINE_RESULT__ = (function() {
            return Mousetrap;
        }).call(exports, __webpack_require__, exports, module),
				__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
    }
}) (typeof window !== 'undefined' ? window : null, typeof  window !== 'undefined' ? document : null);


/***/ }),
/* 118 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var inherit = __webpack_require__(0);
var feature = __webpack_require__(8);

/**
 * Create a new instance of class choroplethFeature
 *
 * @class
 * @alias geo.choroplethFeature
 * @param {Object} arg Options object
 * @extends geo.feature
 * @param {Array} [colorRange] Color lookup table for
 *   choroplethFeature. Default is 9-step color table.
 * @param {Function} [scale] A scale converts a input domain into the
 *   the colorRange. Default is d3.scale.quantize.
 * @param {Object} [accessor] A accessor defines three functions; Retrieve
 *   geoId from geometry feature and scalarId and scalarValue from scalarData. By
 *   default geoId uses GEO_ID key in the geometry feature property,
 *   scalarId uses id key and scalarValue uses value from a object within
 *   the scalar array.
 * @param {Object|Function} [scalar] A scalar is a array of objects with keys id
 *   that maps scalar value to the geometry and value of the scalar.
 *   Multiple values mapped to the same id are aggregated by the aggregator.
 * @param {Object|Function} [choropleth] Defines accessor for choropleth. It
 *   provides an API to replace or add attributes to the choropleth.
 * @param {Object|Function} [choropleth.get] A uniform getter that
 *   always returns a function even for constant values.
 *   If undefined input, return all the choropleth values as an object.
 * @returns {geo.choroplethFeature}
 */
var choroplethFeature = function choroplethFeature(arg) {
  'use strict';

  if (!(this instanceof choroplethFeature)) {
    return new choroplethFeature(arg);
  }
  arg = arg || {};
  feature.call(this, arg);

  var $ = __webpack_require__(2);
  var ensureFunction = __webpack_require__(3).ensureFunction;

  /**
   * @private
   */
  var d3 = __webpack_require__(11).d3,
      m_this = this,
      s_init = this._init,
      m_choropleth = $.extend({}, {
    colorRange: [{ r: 0.07514311, g: 0.468049805, b: 1 }, { r: 0.468487184, g: 0.588057293, b: 1 }, { r: 0.656658579, g: 0.707001303, b: 1 }, { r: 0.821573924, g: 0.837809045, b: 1 }, { r: 0.943467973, g: 0.943498599, b: 0.943398095 }, { r: 1, g: 0.788626485, b: 0.750707739 }, { r: 1, g: 0.6289553, b: 0.568237474 }, { r: 1, g: 0.472800903, b: 0.404551679 }, { r: 0.916482116, g: 0.236630659, b: 0.209939162 }],
    scale: d3.scale.quantize(),
    accessors: {
      //accessor for ID on geodata feature
      geoId: function geoId(geoFeature) {
        return geoFeature.properties.GEO_ID;
      },
      //accessor for ID on scalar element
      scalarId: function scalarId(scalarElement) {
        return scalarElement.id;
      },
      //accessor for value on scalar element
      scalarValue: function scalarValue(scalarElement) {
        return scalarElement.value;
      }
    }
  }, arg.choropleth);

  /**
   * Get/Set choropleth scalar data
   *
   * @memberof geo.choroplethFeature
   * @param {Array} [data] Scalar data is an array of objects in which each
   *   object provides an id and a value for that id. The id uniquely identifies
   *   the geometry this scalar is associated with.
   * @param {Function} [aggregator] The aggregator aggregates the scalar in case
   *   there are multiple values are found with the same id. The default
   *   is d3.mean.
   * @returns {geo.feature.choropleth}
   */
  this.scalar = function (data, aggregator) {
    var scalarId, scalarValue;

    if (data === undefined) {
      return m_this.choropleth.get('scalar')() || [];
    } else {
      scalarId = m_this.choropleth.get('accessors')().scalarId;
      scalarValue = m_this.choropleth.get('accessors')().scalarValue;
      m_choropleth.scalar = data;
      m_choropleth.scalarAggregator = aggregator || d3.mean;
      // we make internal dictionary from array for faster lookup
      // when matching geojson features to scalar values,
      // note that we also allow for multiple scalar elements
      // for the same geo feature
      m_choropleth.scalar._dictionary = data.reduce(function (accumeDictionary, scalarElement) {
        var id, value;

        id = scalarId(scalarElement);
        value = scalarValue(scalarElement);

        accumeDictionary[id] = accumeDictionary[id] ? accumeDictionary[id].push(value) : [value];

        return accumeDictionary;
      }, {});
      m_this.dataTime().modified();
    }
    return m_this;
  };

  /**
   * Get/Set choropleth accessor
   *
   * @memberof geo.choroplethFeature
   * @param {Object|String} [arg1] The first argument is the key for a
   *   attributes of the choropleth. If the argument is a string and the second
   *   argument is undefined, the value of the key is returned. If the arg1 is
   *   an object and the second argument is undefined, choropleth attributes
   *   are extended by that object. If arg1 is an object and arg2 is defined,
   *   a new key-value pair is then added to the choropleth as an attribute.
   * @param {Object|String} [arg2] arg2 defines the value of the key (arg1).
   * @returns {geo.feature.choropleth}
   */
  this.choropleth = function (arg1, arg2) {
    var choropleth;

    if (arg1 === undefined) {
      return m_choropleth;
    }
    if (typeof arg1 === 'string' && arg2 === undefined) {
      return m_choropleth[arg1];
    }
    if (arg2 === undefined) {
      choropleth = $.extend({}, m_choropleth, arg1);
      m_choropleth = choropleth;
    } else {
      m_choropleth[arg1] = arg2; //if you pass in accessor for prop
    }
    m_this.modified();
    return m_this;
  };

  /**
   * Get/Set choropleth getter
   *
   * @memberof geo.choroplethFeature
   * A uniform getter that always returns a function even for constant values.
   * If undefined input, return all the choropleth values as an object.
   *
   * @function choropleth_DOT_get
   * @memberof geo.choroplethFeature
   * @instance
   * @param {string|undefined} key defines one of the attributes of a
   *  choropleth.
   * @return {function}
   */
  this.choropleth.get = function (key) {
    var all = {},
        k;
    if (key === undefined) {
      for (k in m_choropleth) {
        if (m_choropleth.hasOwnProperty(k)) {
          all[k] = m_this.choropleth.get(k);
        }
      }
      return all;
    }
    return ensureFunction(m_choropleth[key]);
  };

  /**
   * A method that adds a polygon feature to the current layer.
   *
   * @param {array} coordinateArray
   * @param {geo.color} fillColor
   * @return {geo.feature}
   */
  this._addPolygonFeature = function (feature, fillColor) {
    var newFeature = m_this.layer().createFeature('polygon', {});

    if (feature.geometry.type === 'Polygon') {
      newFeature.data([{
        type: 'Polygon',
        coordinates: feature.geometry.coordinates
      }]);
    } else if (feature.geometry.type === 'MultiPolygon') {
      newFeature.data(feature.geometry.coordinates.map(function (coordinateMap) {
        return {
          type: 'Polygon',
          coordinates: coordinateMap
        };
      }));
    }

    newFeature.polygon(function (d) {
      return {
        'outer': d.coordinates[0],
        'inner': d.coordinates[1] // undefined but ok
      };
    }).position(function (d) {
      return {
        x: d[0],
        y: d[1]
      };
    }).style({
      'fillColor': fillColor
    });

    return newFeature;
  };

  /**
   * A method that adds polygons from a given feature to the current layer.
   *
   * @param {} geoJsonFeature
   * @param geo.color
   * @return [{geo.feature}]
   */
  this._featureToPolygons = function (feature, fillValue) {
    return m_this._addPolygonFeature(feature, fillValue);
  };

  /**
   * A method that sets a choropleth scale's domain and range.
   *
   * @param {undefined | function({})} valueAccessor
   * @return {geo.feature.choropleth}
   */
  this._generateScale = function (valueAccessor) {
    var extent = d3.extent(m_this.scalar(), valueAccessor || undefined);

    m_this.choropleth().scale.domain(extent).range(m_this.choropleth().colorRange);

    return m_this;
  };

  /**
   * Generate scale for choropleth.data(), make polygons from features.
   * @returns: [ [geo.feature.polygon, ...] , ... ]
   */
  this.createChoropleth = function () {
    var choropleth = m_this.choropleth,
        data = m_this.data(),
        scalars = m_this.scalar(),
        valueFunc = choropleth.get('accessors')().scalarValue,
        getFeatureId = choropleth.get('accessors')().geoId;

    m_this._generateScale(valueFunc);

    return data.map(function (feature) {
      var id = getFeatureId(feature);
      var valueArray = scalars._dictionary[id];
      var accumulatedScalarValue = choropleth().scalarAggregator(valueArray);
      // take average of this array of values
      // which allows for non-bijective correspondence
      // between geo data and scalar data
      var fillColor = m_this.choropleth().scale(accumulatedScalarValue);

      return m_this._featureToPolygons(feature, fillColor);
    });
  };

  /**
   * Initialize
   */
  this._init = function (arg) {
    s_init.call(m_this, arg);

    if (m_choropleth) {
      m_this.dataTime().modified();
    }
  };

  this._init(arg);
  return this;
};

inherit(choroplethFeature, feature);
module.exports = choroplethFeature;

/***/ }),
/* 119 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/**
 * @namespace geo.event.d3
 */

/**
 * @event geo.event.d3.rescale
 * @type {geo.event.base}
 * @property {number} scale The scale as a ratio of the original render size.
 */
module.exports = 'geo_d3_rescale';

/***/ }),
/* 120 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var inherit = __webpack_require__(0);
var meshFeature = __webpack_require__(53);

/**
 * Contour feature specification.
 *
 * @typedef {geo.feature.spec} geo.contourFeature.spec
 * @property {object[]} [data=[]] An array of arbitrary objects used to
 *    construct the feature.
 * @property {geo.contourFeature.styleSpec} [style] An object that contains
 *    style values for the feature.
 * @property {geo.contourFeature.contourSpec} [contour] The contour
 *    specification for the feature.
 */

/**
 * Style specification for a contour feature.
 *
 * @typedef {geo.feature.styleSpec} geo.contourFeature.styleSpec
 * @extends geo.feature.styleSpec
 * @property {geo.geoPosition|function} [position=data] The position of each
 *    data element.  This defaults to just using `x`, `y`, and `z` properties
 *    of the data element itself.  The position is in the feature's gcs
 *    coordinates.
 * @property {number|function} [value=data.z] The value of each data element.
 *    This defaults to the `z` property of the data elements.  If the value of
 *    a grid point is `null` or `undefined`, the point and elements that use
 *    that point won't be included in the results.
 */

/**
 * Contour specification.  All of these properties can be functions, which get
 * passed the {@link geo.meshFeature.meshInfo} object.
 *
 * @typedef {geo.meshFeature.meshSpec} geo.contourFeature.contourSpec
 * @property {number} [min] Minimum contour value.  If unspecified, taken from
 *    the computed minimum of the `value` style.
 * @property {number} [max] Maximum contour value.  If unspecified, taken from
 *    the computed maximum of the `value` style.
 * @property {geo.geoColor} [minColor='black'] Color used for any value below
 *    the minimum.
 * @property {number} [minOpacity=0] Opacity used for any value below the
 *    minimum.
 * @property {geo.geoColor} [maxColor='black'] Color used for any value above
 *    the maximum.
 * @property {number} [maxOpacity=0] Opacity used for any value above the
 *    maximum.
 * @property {boolean} [stepped] If falsy but not `undefined`, smooth
 *    transitions between colors.
 * @property {geo.geoColor[]} [colorRange=<color table>] An array of colors
 *    used to show the range of values.  The default is a 9-step color table.
 * @property {number[]} [opacityRange] An array of opacities used to show the
 *    range of values.  If unspecified, the opacity is 1.  If this is a shorter
 *    list than the `colorRange`, an opacity of 1 is used for the entries near
 *    the end of the color range.
 * @property {number[]} [rangeValues] An array used to map values to the
 *    `colorRange`.  By default, values are spaced linearly.  If specified, the
 *    entries must be increasing weakly monotonic, and there must be one more
 *    entry then the length of `colorRange`.
 */

/**
 * Computed contour information.
 *
 * @typedef {geo.meshFeature.meshInfo} geo.contourFeature.contourInfo
 * @property {number[]} value An array of values that have been normalized to a
 *    range of [0, steps].  There is one value per vertex.
 * @property {number[]} opacity An array of opacities per vertex.
 * @property {number} minValue the minimum value used for the contour.  If
 *    `rangeValues` was specified, this is the first entry of that array.
 * @property {number} maxValue the maximum value used for the contour.  If
 *    `rangeValues` was specified, this is the last entry of that array.
 * @property {number} factor If linear value scaling is used, this is the
 *    number of color values divided by the difference between the maximum and
 *    minimum values.  It is ignored if non-linear value scaling is used.
 * @property {geo.geoColorObject} minColor The color used for values below
 *    minValue.  Includes opacity.
 * @property {geo.geoColorObject} maxColor The color used for values above
 *    maxValue.  Includes opacity.
 * @property {geo.geoColorObject[]} colorMap The specified `colorRange` and
 *    `opacityRange` converted into objects that include opacity.
 */

/**
 * Create a new instance of class contourFeature.
 *
 * @class
 * @alias geo.contourFeature
 * @extends geo.meshFeature
 *
 * @borrows geo.contourFeature#mesh as geo.contourFeature#contour
 *
 * @param {geo.contourFeature.spec} arg
 * @returns {geo.contourFeature}
 */
var contourFeature = function contourFeature(arg) {
  'use strict';

  if (!(this instanceof contourFeature)) {
    return new contourFeature(arg);
  }

  var $ = __webpack_require__(2);
  var util = __webpack_require__(3);

  arg = arg || {};
  meshFeature.call(this, arg);

  /**
   * @private
   */
  var m_this = this,
      s_init = this._init;

  /**
   * Create a set of vertices, values at the vertices, and opacities at the
   * vertices.  Create a set of triangles of indices into the vertex array.
   * Create a color and opacity map corresponding to the values.
   *
   * @returns {geo.contourFeature.contourInfo} An object with the contour
   *    information.
   */
  this._createContours = function () {
    var contour = m_this.contour,
        valueFunc = m_this.style.get('value'),
        usedFunc = m_this.style('used') !== undefined ? m_this.style.get('used') : function (d, i) {
      return util.isNonNullFinite(valueFunc(d, i));
    },
        minmax,
        val,
        range,
        i,
        k;
    var result = this._createMesh({
      used: usedFunc,
      opacity: m_this.style.get('opacity'),
      value: valueFunc
    });
    if (!result.numVertices || !result.numElements) {
      return result;
    }
    var stepped = contour.get('stepped')(result),
        opacityRange = contour.get('opacityRange')(result),
        rangeValues = contour.get('rangeValues')(result);
    result.stepped = stepped === undefined || stepped ? true : false;
    /* Create the min/max colors and the color array */
    result.colorMap = [];
    result.minColor = $.extend({ a: contour.get('minOpacity')(result) || 0 }, util.convertColor(contour.get('minColor')(result)));
    result.maxColor = $.extend({ a: contour.get('maxOpacity')(result) || 0 }, util.convertColor(contour.get('maxColor')(result)));
    contour.get('colorRange')(result).forEach(function (clr, idx) {
      result.colorMap.push($.extend({
        a: opacityRange && opacityRange[idx] !== undefined ? opacityRange[idx] : 1
      }, util.convertColor(clr)));
    });
    /* Get min and max values */
    minmax = util.getMinMaxValues(result.value, contour.get('min')(result), contour.get('max')(result));
    result.minValue = minmax.min;
    result.maxValue = minmax.max;
    if (!rangeValues || rangeValues.length !== result.colorMap.length + 1) {
      rangeValues = null;
    }
    if (rangeValues) {
      /* ensure increasing monotonicity */
      for (k = 1; k < rangeValues.length; k += 1) {
        if (rangeValues[k - 1] > rangeValues[k]) {
          rangeValues = null;
          break;
        }
      }
    }
    if (rangeValues) {
      result.minValue = rangeValues[0];
      result.maxValue = rangeValues[rangeValues.length - 1];
    }
    range = result.maxValue - result.minValue;
    if (!range) {
      result.colorMap = result.colorMap.slice(0, 1);
      range = 1;
      rangeValues = null;
    }
    result.rangeValues = rangeValues;
    result.factor = result.colorMap.length / range;
    /* Scale values */
    for (i = 0; i < result.numVertices; i += 1) {
      val = result.value[i];
      if (rangeValues && val >= result.minValue && val <= result.maxValue) {
        for (k = 1; k < rangeValues.length; k += 1) {
          if (val <= rangeValues[k]) {
            result.value[i] = k - 1 + (val - rangeValues[k - 1]) / (rangeValues[k] - rangeValues[k - 1]);
            break;
          }
        }
      } else {
        result.value[i] = (val - result.minValue) * result.factor;
      }
    }
    return result;
  };

  this.contour = m_this.mesh;

  /**
   * Initialize.
   *
   * @param {geo.contourFeature.spec} arg The contour feature specification.
   */
  this._init = function (arg) {
    s_init.call(m_this, arg);

    var defaultStyle = $.extend({}, {
      opacity: 1.0,
      value: function value(d, i) {
        return m_this.position()(d, i).z;
      }
    }, arg.style === undefined ? {} : arg.style);

    m_this.style(defaultStyle);

    m_this.contour($.extend({}, {
      minColor: 'black',
      minOpacity: 0,
      maxColor: 'black',
      maxOpacity: 0,
      /* 9-step based on paraview bwr colortable */
      colorRange: [{ r: 0.07514311, g: 0.468049805, b: 1 }, { r: 0.468487184, g: 0.588057293, b: 1 }, { r: 0.656658579, g: 0.707001303, b: 1 }, { r: 0.821573924, g: 0.837809045, b: 1 }, { r: 0.943467973, g: 0.943498599, b: 0.943398095 }, { r: 1, g: 0.788626485, b: 0.750707739 }, { r: 1, g: 0.6289553, b: 0.568237474 }, { r: 1, g: 0.472800903, b: 0.404551679 }, { r: 0.916482116, g: 0.236630659, b: 0.209939162 }]
    }, arg.mesh || {}, arg.contour || {}));

    if (arg.mesh || arg.contour) {
      m_this.dataTime().modified();
    }
  };

  this._init(arg);
  return this;
};

inherit(contourFeature, meshFeature);
module.exports = contourFeature;

/* Example:

layer.createFeature('contour', {
})
.data(<array with w x h elements>)
.position(function (d) {
  return { x: <longitude>, y: <latitude>, z: <altitude>};
})
.style({
  opacity: function (d) {
    return <opacity of grid point>;
  },
  value: function (d) {            // defaults to position().z
    return <contour value>;
  }
})
.contour({
  gridWidth: <width of grid>,
  gridHeight: <height of grid>,
  x0: <the x coordinate of the 0th point in the value array>,
  y0: <the y coordinate of the 0th point in the value array>,
  dx: <the distance in the x direction between the 0th and 1st point in the
    value array>,
  dy: <the distance in the y direction between the 0th and (gridWidth)th point
    in the value array>,
  wrapLongitude: <boolean (default true).  If true, AND the position array is
    not used, assume the x coordinates is longitude and should be adjusted to
    be within -180 to 180.  If the data spans 180 degrees, the points or
    squares will be duplicated to ensure that the map is covered from -180 to
    180 as appropriate.  Set this to false if using a non longitude x
    coordinate.  This is ignored if the position array is used.>,
  min: <optional minimum contour value, otherwise taken from style.value>,
  max: <optional maximum contour value, otherwise taken from style.value>,
  minColor: <color for any value below the minimum>,
  minOpacity: <opacity for any value below the minimum>,
  maxColor: <color for any value above the maximum>,
  maxOpacity: <opacity for any value above the maximum>,
  stepped: <boolean (default true).  If false, smooth transitions between
    colors>,
  colorRange: [<array of colors used for the contour>],
  opacityRange: [<optional array of opacities used for the contour, expected to
    be the same length as colorRange>],
  rangeValues: [<if specified, instead of spacing the colors linearly, use this
    spacing.  Must be increasing monotonic and one value longer than the length
    of colorRange>]
})
 */

/***/ }),
/* 121 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var $ = __webpack_require__(2);

/**
 * This class implements a queue for Deferred objects.  Whenever one of the
 * objects in the queue completes (resolved or rejected), another item in the
 * queue is processed.  The number of concurrently processing items can be
 * adjusted.  At this time (2015-12-29) most major browsers support 6
 * concurrent requests from any given server, so, when using the queue for
 * tile images, thie number of concurrent requests should be 6 * (number of
 * subdomains serving tiles).
 *
 * @class
 * @alias geo.fetchQueue
 *
 * @param {object} [options] A configuration object for the queue.
 * @param {number} [options.size=6] The maximum number of concurrent deferred
 *    objects.
 * @param {number} [options.track=600] The number of objects that are tracked
 *    that trigger checking if any of them have been abandoned.  The fetch
 *    queue can grow to the greater of this size and the number of items that
 *    are still needed.  Setting this to a low number will increase
 *    processing time, to a high number can increase memory.  Ideally, it
 *    should reflect the number of items that are kept in memory elsewhere.
 *    If needed is null, this is ignored.
 * @param {function} [options.needed=null] If set, this function is passed a
 *    Deferred object and must return a truthy value if the object is still
 *    needed.
 */
var fetchQueue = function fetchQueue(options) {
  if (!(this instanceof fetchQueue)) {
    return new fetchQueue(options);
  }

  options = options || {};
  this._size = options.size || 6;
  this._track = options.track || 600;
  this._needed = options.needed || null;
  this._batch = false;

  var m_this = this,
      m_next_batch = 1;

  /**
   * Get/set the maximum concurrent deferred object size.
   * @property {number} size The maximum number of deferred objects.
   * @name geo.fetchQueue#size
   */
  Object.defineProperty(this, 'size', {
    get: function get() {
      return this._size;
    },
    set: function set(n) {
      this._size = n;
      this.next_item();
    }
  });

  /**
   * Get the current queue size.  Read only.
   * @property {number} length The current queue size.
   * @name geo.fetchQueue#length
   */
  Object.defineProperty(this, 'length', {
    get: function get() {
      return this._queue.length;
    }
  });

  /**
   * Get the current number of processing items.  Read only.
   * @property {number} processing The current number of processing items.
   * @name geo.fetchQueue#processing
   */
  Object.defineProperty(this, 'processing', {
    get: function get() {
      return this._processing;
    }
  });

  /**
   * Remove all items from the queue.
   *
   * @returns {this}
   */
  this.clear = function () {
    this._queue = [];
    this._processing = 0;
    return this;
  };

  /**
   * Add a Deferred object to the queue.
   *
   * @param {jQuery.Deferred} defer Deferred object to add to the queue.
   * @param {function} callback A function to call when the item's turn is
   *  granted.
   * @param {boolean} atEnd If falsy, add the item to the front of the queue
   *  if batching is turned off or at the end of the current batch if it is
   *  turned on.  If truthy, always add the item to the end of the queue.
   * @returns {jQuery.Deferred} The deferred object that was passed to the
   *  function.
   */
  this.add = function (defer, callback, atEnd) {
    if (defer.__fetchQueue) {
      var pos = $.inArray(defer, this._queue);
      if (pos >= 0) {
        this._queue.splice(pos, 1);
        this._addToQueue(defer, atEnd);
        return defer;
      }
    }
    var wait = $.Deferred();
    var process = $.Deferred();
    wait.done(function () {
      $.when(callback.call(defer)).always(process.resolve);
    }).fail(process.resolve);
    defer.__fetchQueue = wait;
    this._addToQueue(defer, atEnd);
    $.when(wait, process).always(function () {
      if (m_this._processing > 0) {
        m_this._processing -= 1;
      }
      m_this.next_item();
    }).promise(defer);
    m_this.next_item();
    return defer;
  };

  /**
   * Add an item to the queue.  If batches are being used, add it at after
   * other items in the same batch.
   *
   * @param {jQuery.Deferred} defer Deferred object to add to the queue.
   * @param {boolean} atEnd If falsy, add the item to the front of the queue
   *  if batching is turned off or at the end of the current batch if it is
   *  turned on.  If truthy, always add the item to the end of the queue.
   */
  this._addToQueue = function (defer, atEnd) {
    defer.__fetchQueue._batch = this._batch;
    if (atEnd) {
      this._queue.push(defer);
    } else if (!this._batch) {
      this._queue.unshift(defer);
    } else {
      for (var i = 0; i < this._queue.length; i += 1) {
        if (this._queue[i].__fetchQueue._batch !== this._batch) {
          break;
        }
      }
      this._queue.splice(i, 0, defer);
    }
  };

  /**
   * Get the position of a deferred object in the queue.
   *
   * @param {jQuery.Deferred} defer Deferred object to get the position of.
   * @returns {number} -1 if not in the queue, or the position in the queue.
   */
  this.get = function (defer) {
    return $.inArray(defer, this._queue);
  };

  /**
   * Remove a Deferred object from the queue.
   *
   * @param {jQuery.Deferred} defer Deferred object to add to the queue.
   * @returns {boolean} `true` if the object was removed.
   */
  this.remove = function (defer) {
    var pos = $.inArray(defer, this._queue);
    if (pos >= 0) {
      this._queue.splice(pos, 1);
      return true;
    }
    return false;
  };

  /**
   * Start a new batch or clear using batches.
   *
   * @param {boolean} start Truthy too start a new batch, falsy to turn off
   *   using batches.  `undefined` to return the current state of batches.
   * @returns {number|boolean|this} `false` if batches are turned off, the
   *   batch number if turned on, or `this` if setting the batch.
   */
  this.batch = function (start) {
    if (start === undefined) {
      return this._batch;
    }
    if (!start) {
      this._batch = false;
    } else {
      this._batch = m_next_batch;
      m_next_batch += 1;
    }
    return this;
  };

  /**
   * Check if any items are queued and if the processing allotment is not
   * full.  If so, process more items.
   */
  this.next_item = function () {
    if (m_this._innextitem) {
      return;
    }
    m_this._innextitem = true;
    /* if the queue is greater than the track size, check each item to see
     * if it is still needed. */
    if (m_this._queue.length > m_this._track && this._needed) {
      for (var i = m_this._queue.length - 1; i >= 0; i -= 1) {
        if (!m_this._needed(m_this._queue[i])) {
          var discard = m_this._queue.splice(i, 1)[0];
          m_this._processing += 1;
          discard.__fetchQueue.reject();
          delete discard.__fetchQueue;
        }
      }
    }
    while (m_this._processing < m_this._size && m_this._queue.length) {
      var defer = m_this._queue.shift();
      if (defer.__fetchQueue) {
        m_this._processing += 1;
        var needed = m_this._needed ? m_this._needed(defer) : true;
        if (needed) {
          defer.__fetchQueue.resolve();
        } else {
          defer.__fetchQueue.reject();
        }
        delete defer.__fetchQueue;
      }
    }
    m_this._innextitem = false;
  };

  this.clear();
  return this;
};

module.exports = fetchQueue;

/***/ }),
/* 122 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var inherit = __webpack_require__(0);
var featureLayer = __webpack_require__(35);
var object = __webpack_require__(12);

/**
 * Object specification for a fileReader.
 *
 * @typedef {object} geo.fileReader.spec
 * @property {geo.featureLayer} layer The target feature layer.
 */

/**
 * Create a new instance of class fileReader.
 *
 * @class
 * @alias geo.fileReader
 * @extends geo.object
 * @param {geo.fileReader.spec} arg
 * @returns {geo.fileReader}
 */
var fileReader = function fileReader(arg) {
  'use strict';

  if (!(this instanceof fileReader)) {
    return new fileReader(arg);
  }
  object.call(this);

  /**
   * @private
   */
  arg = arg || {};

  if (!(arg.layer instanceof featureLayer)) {
    throw new Error('fileReader must be given a feature layer');
  }

  var m_layer = arg.layer;

  /**
   * Get the feature layer attached to the reader.
   *
   * @returns {geo.featureLayer} The layer associated with the reader.
   */
  this.layer = function () {
    return m_layer;
  };

  /**
   * Tells the caller if it can handle the given file by returning a boolean.
   *
   * @param {File|Blob|string|object} file This is either a `File` object, a
   *    `Blob` object, a string representation of a file, or an object
   *    representing data from a file.
   * @returns {boolean} `true` if this reader can read a file.
   */
  this.canRead = function (file) {
    return false;
  };

  /**
   * Reads the file and optionally calls a function when finished.  The `done`
   * function is called with a value that is truthy if the read was a success.
   * Depending on the specific reader, this value may be an object with details
   * of the read operation.
   *
   * @param {File|Blob|string|object} file This is either a `File` object, a
   *    `Blob` object, a string representation of a file, or an object
   *    representing data from a file.
   * @param {function} [done] An optional callback function when the read is
   *    complete.  This is called with `false` on error or the object that was
   *    read and parsed by the reader.
   * @param {function} [progress] A function which is passed `ProgressEvent`
   *    information from a `FileReader`.  This includes `loaded` and `total`
   *    each with a number of bytes.
   * @returns {Promise} A `Promise` that resolves with object parsed by the
   *    reader or is rejected if the reader fails.
   */
  this.read = function (file, done, progress) {
    var promise = new Promise(function (resolve, reject) {
      if (done) {
        done(false);
      }
      throw new Error('The default file reader always fails');
    });
    this.addPromise(promise);
    return promise;
  };

  /**
   * Return a `FileReader` with handlers attached.
   *
   * @param {function} done A callback that receives either the string read
   *    from the file or a `DOMException` with an error.
   * @param {function} [progress] A function which is passed `ProgressEvent`
   *    information from a `FileReader`.  This includes `loaded` and `total`
   *    each with a number of bytes.
   * @returns {FileReader} The `FileReader` with done and progress handles
   *    attached to it.
   */
  function newFileReader(done, progress) {
    var reader = new FileReader();
    if (progress) {
      reader.onprogress = progress;
    }
    reader.onloadend = function () {
      done(reader.error || reader.result);
    };
    return reader;
  }

  /**
   * Read a file object as a string.  Calls `done` with the string content
   * when finished or an error object if unsuccessful.
   *
   * @param {File|Blob} file A `File` or `Blob` object to read.
   * @param {function} done A callback that receives either the string read
   *    from the file or a `DOMException` with an error.
   * @param {function} [progress] A function which is passed `ProgressEvent`
   *    information from a `FileReader`.  This includes `loaded` and `total`
   *    each with a number of bytes.
   */
  this._getString = function (file, done, progress) {
    var reader = newFileReader(done, progress);
    reader.readAsText(file);
  };

  return this;
};

inherit(fileReader, object);
module.exports = fileReader;

/***/ }),
/* 123 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var inherit = __webpack_require__(0);
var feature = __webpack_require__(8);

/**
 * Create a new instance of class graphFeature
 *
 * @class
 * @alias geo.graphFeature
 * @extends geo.feature
 * @returns {geo.graphFeature}
 */
var graphFeature = function graphFeature(arg) {
  'use strict';

  if (!(this instanceof graphFeature)) {
    return new graphFeature(arg);
  }
  arg = arg || {};
  feature.call(this, arg);

  var $ = __webpack_require__(2);
  var util = __webpack_require__(3);
  var registry = __webpack_require__(1);

  /**
   * @private
   */
  var m_this = this,
      s_draw = this.draw,
      s_style = this.style,
      m_nodes = null,
      m_points = null,
      m_children = function m_children(d) {
    return d.children;
  },
      m_links = [],
      s_init = this._init,
      s_exit = this._exit;

  /**
   * Initialize
   */
  this._init = function (arg) {
    s_init.call(m_this, arg);

    var defaultStyle = $.extend(true, {}, {
      nodes: {
        radius: 5.0,
        fill: true,
        fillColor: { r: 1.0, g: 0.0, b: 0.0 },
        strokeColor: { r: 0, g: 0, b: 0 }
      },
      links: {
        strokeColor: { r: 0.0, g: 0.0, b: 0.0 }
      },
      linkType: 'path' /* 'path' || 'line' */
    }, arg.style === undefined ? {} : arg.style);

    m_this.style(defaultStyle);
    m_this.nodes(function (d) {
      return d;
    });
  };

  /**
   * Call child _build methods
   */
  this._build = function () {
    m_this.children().forEach(function (child) {
      child._build();
    });
  };

  /**
   * Call child _update methods
   */
  this._update = function () {
    m_this.children().forEach(function (child) {
      child._update();
    });
  };

  /**
   * Custom _exit method to remove all sub-features
   */
  this._exit = function () {
    m_this.data([]);
    m_links.forEach(function (link) {
      link._exit();
      m_this.removeChild(link);
    });
    m_links = [];
    m_points._exit();
    m_this.removeChild(m_points);
    s_exit();
    return m_this;
  };

  /**
   * Get/Set style
   */
  this.style = function (arg, arg2) {
    var out = s_style.call(m_this, arg, arg2);
    if (out !== m_this) {
      return out;
    }
    // set styles for sub-features
    m_points.style(arg.nodes);
    m_links.forEach(function (l) {
      l.style(arg.links);
    });
    return m_this;
  };

  /**
   * Get/Set links accessor.
   */
  this.links = function (arg) {
    if (arg === undefined) {
      return m_children;
    }

    m_children = util.ensureFunction(arg);
    return m_this;
  };

  /**
   * Get/Set nodes
   */
  this.nodes = function (val) {
    if (val === undefined) {
      return m_nodes;
    }
    m_nodes = val;
    m_this.modified();
    return m_this;
  };

  /**
   * Get internal node feature
   */
  this.nodeFeature = function () {
    return m_points;
  };

  /**
   * Get internal link features
   */
  this.linkFeatures = function () {
    return m_links;
  };

  /**
   * Build the feature for drawing
   */
  this.draw = function () {

    var layer = m_this.layer(),
        data = m_this.data(),
        nLinks = 0,
        style;

    // get the feature style object
    style = m_this.style();

    // Bind data to the point nodes
    m_points.data(data);
    m_points.style(style.nodes);

    // get links from node connections
    data.forEach(function (source) {
      (source.children || []).forEach(function (target) {
        var link;
        nLinks += 1;
        if (m_links.length < nLinks) {
          link = registry.createFeature(style.linkType, layer, layer.renderer()).style(style.links);
          m_this.addChild(link);
          m_links.push(link);
        }
        m_links[nLinks - 1].data([source, target]);
      });
    });

    m_links.splice(nLinks, m_links.length - nLinks).forEach(function (l) {
      l._exit();
      m_this.removeChild(l);
    });

    s_draw();
    return m_this;
  };

  m_points = registry.createFeature('point', this.layer(), this.layer().renderer());
  m_this.addChild(m_points);

  if (arg.nodes) {
    this.nodes(arg.nodes);
  }

  this._init(arg);
  return this;
};

inherit(graphFeature, feature);
module.exports = graphFeature;

/***/ }),
/* 124 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var $ = __webpack_require__(2);
var inherit = __webpack_require__(0);
var feature = __webpack_require__(8);
var transform = __webpack_require__(7);

/**
 * Heatmap feature specification.
 *
 * @typedef {geo.feature.spec} geo.heatmapFeature.spec
 * @property {geo.geoPosition|function} [position] Position of the data.
 *   Default is (data).
 * @param {function} [intensity] Scalar value of each data point.  The scalar
 *   value must be a positive real number and is used to compute the weight
 *   for each data point.
 * @param {number} [maxIntensity=null] Maximum intensity of the data.  Maximum
 *   intensity must be a positive real number and is used to normalize all
 *   intensities within a dataset.  If `null`, it is computed.
 * @param {number} [minIntensity=null] Minimum intensity of the data.  Minimum
 *   intensity must be a positive real number and is used to normalize all
 *   intensities within a dataset.  If `null`, it is computed.
 * @param {number} [updateDelay=1000] Delay in milliseconds after a zoom,
 *   rotate, or pan event before recomputing the heatmap.
 * @param {boolean|number|'auto'} [binned='auto'] If `true` or a number,
 *   spatially bin data as part of producing the heatmap.  If falsy, each
 *   datapoint stands on its own.  If `'auto'`, bin data if there are more data
 *   points than there would be bins.  Using `true` or `auto` uses bins that
 *   are `max(Math.floor((radius + blurRadius) / 8), 3)`.
 * @param {object} [style.color] An object where the keys are numbers from
 *   [0-1] and the values are {@link geo.geoColor}.  This is used to transform
 *   normalized intensity.
 * @param {number} [style.radius=10] Radius of a point in pixels.
 * @param {number} [style.blurRadius=10] Blur radius for each point in pixels.
 * @param {boolean} [style.gaussian=true] If truthy, appoximate a gaussian
 *   distribution for each point using a multi-segment linear radial
 *   appoximation.  The total weight of the gaussian area is approximately the
 *   `9/16 r^2`.  The sum of `radius + blurRadius` is used as the radius for
 *   the gaussian distribution.
 */

/**
 * Create a new instance of class heatmapFeature.
 *
 * @class
 * @alias geo.heatmapFeature
 * @param {geo.heatmapFeature.spec} arg
 * @extends geo.feature
 * @returns {geo.heatmapFeature}
 */
var heatmapFeature = function heatmapFeature(arg) {
  'use strict';

  if (!(this instanceof heatmapFeature)) {
    return new heatmapFeature(arg);
  }
  arg = arg || {};
  feature.call(this, arg);

  /**
   * @private
   */
  var m_this = this,
      m_position,
      m_intensity,
      m_maxIntensity,
      m_minIntensity,
      m_updateDelay,
      m_binned,
      m_gcsPosition,
      s_init = this._init;

  m_position = arg.position || function (d) {
    return d;
  };
  m_intensity = arg.intensity || function (d) {
    return 1;
  };
  m_maxIntensity = arg.maxIntensity !== undefined ? arg.maxIntensity : null;
  m_minIntensity = arg.minIntensity !== undefined ? arg.minIntensity : null;
  m_binned = arg.binned !== undefined ? arg.binned : 'auto';
  m_updateDelay = arg.updateDelay ? parseInt(arg.updateDelay, 10) : 1000;

  /**
   * Get/Set maxIntensity.
   *
   * @param {number|null} [val] If not specified, return the current value.
   *    If a number, use this as the maximum intensity.  If `null`, compute
   *    the maximum intensity.
   * @returns {number|null|this}
   */
  this.maxIntensity = function (val) {
    if (val === undefined) {
      return m_maxIntensity;
    } else {
      m_maxIntensity = val;
      m_this.dataTime().modified();
      m_this.modified();
    }
    return m_this;
  };

  /**
   * Get/Set minIntensity.
   *
   * @param {number|null} [val] If not specified, return the current value.
   *    If a number, use this as the minimum intensity.  If `null`, compute
   *    the minimum intensity.
   * @returns {number|null|this}
   */
  this.minIntensity = function (val) {
    if (val === undefined) {
      return m_minIntensity;
    } else {
      m_minIntensity = val;
      m_this.dataTime().modified();
      m_this.modified();
    }
    return m_this;
  };

  /**
   * Get/Set updateDelay.
   *
   * @param {number} [val] If not specified, return the current update delay.
   *    If specified, this is the delay in milliseconds after a zoom, rotate,
   *    or pan event before recomputing the heatmap.
   * @returns {number|this}
   */
  this.updateDelay = function (val) {
    if (val === undefined) {
      return m_updateDelay;
    } else {
      m_updateDelay = parseInt(val, 10);
    }
    return m_this;
  };

  /**
   * Get/Set binned value.
   *
   * @param {boolean|number|'auto'} [val] If not specified, return the current
   *    binned value.  If `true` or a number, spatially bin data as part of
   *    producing the heatmap.  If falsy, each datapoint stands on its own.
   *    If `'auto'`, bin data if there are more data points than there would be
   *    bins.  Using `true` or `auto` uses bins that are
   *    `max(Math.floor((radius + blurRadius) / 8), 3)`.
   * @returns {boolean|number|'auto'|this}
   */
  this.binned = function (val) {
    if (val === undefined) {
      return m_binned;
    } else {
      if (val === 'true') {
        val = true;
      } else if (val === 'false') {
        val = false;
      } else if (val !== 'auto' && val !== true && val !== false) {
        val = parseInt(val, 10);
        if (val <= 0 || isNaN(val)) {
          val = false;
        }
      }
      m_binned = val;
      m_this.dataTime().modified();
      m_this.modified();
    }
    return m_this;
  };

  /**
   * Get/Set position accessor.
   *
   * @param {geo.geoPosition|function} [val] If not specified, return the
   *    current position accessor.  If specified, use this for the position
   *    accessor and return `this`.  If a function is given, this is called
   *    with `(dataElement, dataIndex)`.
   * @returns {geo.geoPosition|function|this} The current position or this
   *    feature.
   */
  this.position = function (val) {
    if (val === undefined) {
      return m_position;
    } else {
      m_position = val;
      m_this.dataTime().modified();
      m_this.modified();
    }
    return m_this;
  };

  /**
   * Get pre-computed gcs position accessor.
   *
   * @returns {geo.heatmap}
   */
  this.gcsPosition = function () {
    this._update();
    return m_gcsPosition;
  };

  /**
   * Get/Set intensity.
   *
   * @param {function} [val] If not specified, the current intensity accessor.
   *    Otherwise, a function that returns the intensity of each data point.
   * @returns {function|this}
   */
  this.intensity = function (val) {
    if (val === undefined) {
      return m_intensity;
    } else {
      m_intensity = val;
      m_this.dataTime().modified();
      m_this.modified();
    }
    return m_this;
  };

  /**
   * Initialize.
   *
   * @param {geo.heatmapFeature.spec} arg
   */
  this._init = function (arg) {
    s_init.call(m_this, arg);

    var defaultStyle = $.extend({}, {
      radius: 10,
      blurRadius: 10,
      gaussian: true,
      color: {
        0: { r: 0, g: 0, b: 0.0, a: 0.0 },
        0.25: { r: 0, g: 0, b: 1, a: 0.5 },
        0.5: { r: 0, g: 1, b: 1, a: 0.6 },
        0.75: { r: 1, g: 1, b: 0, a: 0.7 },
        1: { r: 1, g: 0, b: 0, a: 0.8 } }
    }, arg.style === undefined ? {} : arg.style);

    m_this.style(defaultStyle);

    if (m_position) {
      m_this.dataTime().modified();
    }
  };

  /**
   * Build the fetaure.
   *
   * @returns {this}
   */
  this._build = function () {
    var data = m_this.data(),
        intensity = null,
        position = [],
        setMax = m_maxIntensity === null || m_maxIntensity === undefined,
        setMin = m_minIntensity === null || m_minIntensity === undefined;

    data.forEach(function (d, i) {
      position.push(m_this.position()(d, i));
      if (setMax || setMin) {
        intensity = m_this.intensity()(d, i);
        if (m_maxIntensity === null || m_maxIntensity === undefined) {
          m_maxIntensity = intensity;
        }
        if (m_minIntensity === null || m_minIntensity === undefined) {
          m_minIntensity = intensity;
        }
        if (setMax && intensity > m_maxIntensity) {
          m_maxIntensity = intensity;
        }
        if (setMin && intensity < m_minIntensity) {
          m_minIntensity = intensity;
        }
      }
    });
    if (setMin && setMax && m_minIntensity === m_maxIntensity) {
      m_minIntensity -= 1;
    }
    m_gcsPosition = transform.transformCoordinates(m_this.gcs(), m_this.layer().map().gcs(), position);

    m_this.buildTime().modified();
    return m_this;
  };

  this._init(arg);
  return this;
};

inherit(heatmapFeature, feature);
module.exports = heatmapFeature;

/***/ }),
/* 125 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var inherit = __webpack_require__(0);
var tile = __webpack_require__(54);

/**
 * This class defines a tile that is part of a standard "image pyramid", such
 * as an open street map tile set.  Every tile is uniquely indexed by a row,
 * column, and zoom level.  The number of rows/columns at zoom level z is
 * `2^z`, the number of pixels per tile is configurable.
 *
 * By default, this class assumes that images are fetched from the url, but
 * subclasses may define additional rendering steps to produce the images
 * before passing them off to the handlers.
 *
 * @class
 * @alias geo.imageTile
 * @extends geo.tile
 * @param {object} spec The tile specification.
 *
 * @param {object} spec.index The global position of the tile.
 * @param {number} spec.index.x The x-coordinate (usually the column number).
 * @param {number} spec.index.y The y-coordinate (usually the row number).
 * @param {number} spec.index.level The zoom level.
 *
 * @param {object?} spec.size The size of each tile.
 * @param {number} [spec.size.x=256] Width in pixels.
 * @param {number} [spec.size.y=256] Height in pixels.
 *
 * @param {string} spec.url A url to the image.
 * @param {string} [spec.crossDomain='anonymous'] Image CORS attribute.
 *
 * @param {object} spec.overlap The size of overlap with neighboring tiles.
 * @param {number} [spec.overlap.x=0]
 * @param {number} [spec.overlap.y=0]
 */
var imageTile = function imageTile(spec) {
  if (!(this instanceof imageTile)) {
    return new imageTile(spec);
  }

  var $ = __webpack_require__(2);

  spec.size = spec.size || { x: 256, y: 256 };
  this._image = null;

  this._cors = spec.crossDomain || spec.crossDomain === null ? spec.crossDomain : 'anonymous';

  // Call superclass constructor
  tile.call(this, spec);

  /**
   * Read only accessor to the Image object used by the tile.  Note, this
   * method does not guarantee that the image data is available.  Use the
   * promise interface to add asynchronous handlers.
   *
   * @property {Image} image The image object used by the tile.
   * @name geo.imageTile#image
   */
  Object.defineProperty(this, 'image', {
    get: function get() {
      return this._image;
    }
  });

  /**
   * Initiate the image request.
   *
   * @returns {this}
   */
  this.fetch = function () {
    var defer;
    if (!this._image) {
      this._image = new Image(this.size.x, this.size.y);
      // Only set the crossOrigin parameter if this is going across origins.
      if (this._cors && this._url.indexOf(':') >= 0 && this._url.indexOf('/') === this._url.indexOf(':') + 1) {
        this._image.crossOrigin = this._cors;
      }
      defer = $.Deferred();
      this._image.onload = defer.resolve;
      this._image.onerror = defer.reject;
      this._image.src = this._url;

      // attach a promise interface to `this`
      defer.done(function () {
        this._fetched = true;
      }.bind(this)).promise(this);
    }
    return this;
  };

  /**
   * Set the opacity of the tile to 0 and gradually fade in over the given
   * number of milliseconds.  This will also resolve the embedded promise
   * interface.
   *
   * @param {number} duration The duration of the animation in ms.
   * @returns {this}
   */
  this.fadeIn = function (duration) {
    var promise = this.fetch(),
        defer = $.Deferred();
    $(this._image).css('display', 'none');
    promise.done(function () {
      $(this._image).fadeIn(duration, function () {
        defer.resolve();
      });
    }.bind(this));
    return defer.promise(this);
  };

  return this;
};

inherit(imageTile, tile);

module.exports = imageTile;

/***/ }),
/* 126 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var inherit = __webpack_require__(0);
var registerLayer = __webpack_require__(1).registerLayer;
var layer = __webpack_require__(24);

/**
 * Create a new instance of class uiLayer.
 *
 * @class
 * @alias geo.gui.uiLayer
 * @extends {geo.layer}
 * @param {object} [arg] Options for the layer.
 * @returns {geo.gui.uiLayer}
 */
var uiLayer = function uiLayer(arg) {
  'use strict';

  var createWidget = __webpack_require__(1).createWidget;

  // The widget stays fixed on the screen.
  arg.renderer = 'dom';
  arg.sticky = false;

  if (!(this instanceof uiLayer)) {
    return new uiLayer(arg);
  }
  layer.call(this, arg);

  var m_this = this,
      s_exit = this._exit;

  /**
   * Create a new ui control.
   *
   * @param {string} widgetName The name of the widget.
   * @param {object} arg Options for the widget.
   * @param {geo.object} [arg.parent] A parent object for the widget.
   * @returns {geo.gui.widget} The new widget.
   */
  this.createWidget = function (widgetName, arg) {
    var newWidget = createWidget(widgetName, m_this, arg);

    // We only want top level widgets to be a child of the uiLayer
    if (!(arg && 'parent' in arg)) {
      m_this.addChild(newWidget);
    }

    newWidget._init(arg);
    m_this.modified();
    return newWidget;
  };

  /**
   * Delete a ui control.
   *
   * @param {geo.gui.widget} widget The widget to remove.
   * @returns {this}
   */
  this.deleteWidget = function (widget) {
    widget._exit();
    m_this.removeChild(widget);
    m_this.modified();
    return m_this;
  };

  /**
   * Free memory and destroy the layer.
   */
  this._exit = function () {
    m_this.children().forEach(function (child) {
      m_this.deleteWidget(child);
    });
    s_exit();
  };
};

inherit(uiLayer, layer);

registerLayer('ui', uiLayer);
module.exports = uiLayer;

/***/ }),
/* 127 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var inherit = __webpack_require__(0);
var featureLayer = __webpack_require__(35);

/**
 * Standard modulo operator where the output is in [0, b) for all inputs.
 * @private
 * @param {number} a Any finite number.
 * @param {number} b A positive number.
 * @returns {number} The positive version of `a % b`.
 */
function modulo(a, b) {
  return (a % b + b) % b;
}

/**
 * Pick a subdomain from a list of subdomains based on a the tile location.
 *
 * @param {number} x The x tile coordinate.
 * @param {number} y The y tile coordinate.
 * @param {number} z The tile layer.
 * @param {string[]} subdomains The list of known subdomains.
 * @returns {string} A subdomain based on the location.
 */
function m_getTileSubdomain(x, y, z, subdomains) {
  return subdomains[modulo(x + y + z, subdomains.length)];
}

/**
 * Returns an OSM tile server formatting function from a standard format
 * string. Replaces `{s}`, `{z}`, `{x}`, and `{y}`.  These may be any case
 * and may be prefixed with `$` (e.g., `${X}` is the same as `{x}`).  The
 * subdomain can be specifed by a string of characters, listed as a range,
 * or as a comma-separated list (e.g., `{s:abc}`, `{a-c}`, `{a,b,c}` are
 * all equivalent.  The comma-separated list can have subdimains that are of
 * any length; the string and range both use one-character subdomains.
 *
 * @param {string} base The tile format string
 * @returns {function} A conversion function.
 * @private.
 */
function m_tileUrlFromTemplate(base) {
  var xPattern = new RegExp(/\$?\{[xX]\}/),
      yPattern = new RegExp(/\$?\{[yY]\}/),
      zPattern = new RegExp(/\$?\{[zZ]\}/),
      sPattern = new RegExp(/\$?\{(s|S|[sS]:[^{}]+|[^-{}]-[^-{}]|([^,{}]+,)+[^,{}]+)\}/);
  var url = base.replace(sPattern, '{s}').replace(xPattern, '{x}').replace(yPattern, '{y}').replace(zPattern, '{z}');
  var urlSubdomains;
  var sMatch = base.match(sPattern);
  if (sMatch) {
    if (sMatch[2]) {
      urlSubdomains = sMatch[1].split(',');
    } else if (sMatch[1][1] === ':') {
      urlSubdomains = sMatch[1].substr(2).split('');
    } else if (sMatch[1][1] === '-') {
      urlSubdomains = [];
      var start = sMatch[1].charCodeAt(0),
          end = sMatch[1].charCodeAt(2);
      for (var i = Math.min(start, end); i <= Math.max(start, end); i += 1) {
        urlSubdomains.push(String.fromCharCode(i));
      }
    }
  }

  return function (x, y, z, subdomains) {
    return url.replace('{s}', m_getTileSubdomain(x, y, z, urlSubdomains || subdomains)).replace('{x}', x).replace('{y}', y).replace('{z}', z);
  };
}

/**
 * This method defines a tileLayer, an abstract class defining a layer
 * divided into tiles of arbitrary data.  Notably, this class provides the
 * core functionality of {@link geo.osmLayer}, but hooks exist to render
 * tiles more generically.  When multiple zoom levels are present in a given
 * dataset, this class assumes that the space occupied by tile `(i, j)` at
 * level `z` is covered by a 2x2 grid of tiles at zoom level `z + 1`:
 * ```
 *   (2i, 2j),     (2i, 2j + 1)
 *   (2i + 1, 2j), (2i + 1, 2j + 1)
 * ```
 * The higher level tile set should represent a 2x increase in resolution.
 *
 * @class
 * @alias geo.tileLayer
 * @extends geo.featureLayer
 * @param {object?} options
 * @param {number} [options.minLevel=0] The minimum zoom level available.
 * @param {number} [options.maxLevel=18] The maximum zoom level available.
 * @param {object} [options.tileOverlap] Pixel overlap between tiles.
 * @param {number} [options.tileOverlap.x] Horizontal overlap.
 * @param {number} [options.tileOverlap.y] Vertical overlap.
 * @param {number} [options.tileWidth=256] The tile width without overlap.
 * @param {number} [options.tileHeight=256] The tile height without overlap.
 * @param {function} [options.tilesAtZoom=null] A function that is given a
 *    zoom level and returns `{x: (num), y: (num)}` with the number of tiles
 *    at that zoom level.
 * @param {number} [options.cacheSize=400] The maximum number of tiles to
 *    cache.  The default is 200 if keepLower is false.
 * @param {boolean} [options.keepLower=true] When truthy, keep lower zoom
 *    level tiles when showing high zoom level tiles.  This uses more memory
 *    but results in smoother transitions.
 * @param {boolean} [options.wrapX=true] Wrap in the x-direction.
 * @param {boolean} [options.wrapY=false] Wrap in the y-direction.
 * @param {string|function} [options.url=null] A function taking the current
 *   tile indices `(x, y, level, subdomains)` and returning a URL or jquery
 *   ajax config to be passed to the {geo.tile} constructor.  Example:
 *   ```
 *   (x, y, z, subdomains) => "http://example.com/z/y/x.png"
 *   ```
 *   If this is a string, a template url with {x}, {y}, {z}, and {s} as
 *   template variables.  {s} picks one of the subdomains parameter and may
 *   contain a comma-separated list of subdomains.
 * @param {string|list} [options.subdomain="abc"] Subdomains to use in
 *   template url strings.  If a string, this is converted to a list before
 *   being passed to a url function.
 * @param {string} [options.baseUrl=null]  If defined, use the old-style base
 *   url instead of the options.url parameter.  This is functionally the same
 *   as using a url of `baseUrl/{z}/{x}/{y}.(options.imageFormat || png)`.
 *   If the specified string does not end in a slash, one is added.
 * @param {string} [options.imageFormat='png'] This is only used if a
 *   `baseUrl` is specified, in which case it determines the image name
 *   extension used in the url.
 * @param {number} [options.animationDuration=0] The number of milliseconds
 *   for the tile loading animation to occur.  Only some renderers support
 *   this.
 * @param {string} [options.attribution] An attribution to display with the
 *   layer (accepts HTML).
 * @param {function} [options.tileRounding=Math.round] This function
 *   determines which tiles will be loaded when the map is at a non-integer
 *   zoom.  For example, `Math.floor`, will use tile level 2 when the map is
 *   at zoom 2.9.
 * @param {function} [options.tileOffset] This function takes a zoom level
 *   argument and returns, in units of pixels, the coordinates of the point
 *   (0, 0) at the given zoom level relative to the bottom left corner of the
 *   domain.
 * @param {function} [options.tilesMaxBounds=null] This function takes a zoom
 *   level argument and returns an object with `x` and `y` in pixels which is
 *   used to crop the last row and column of tiles.  Note that if tiles wrap,
 *   only complete tiles in the wrapping direction(s) are supported, and this
 *   max bounds will probably not behave properly.
 * @param {boolean} [options.topDown=false]  True if the gcs is top-down,
 *   false if bottom-up (the ingcs does not matter, only the gcs coordinate
 *   system).  When falsy, this inverts the gcs y-coordinate when calculating
 *   local coordinates.
 * @returns {geo.tileLayer}
 */
var tileLayer = function tileLayer(options) {
  'use strict';

  if (!(this instanceof tileLayer)) {
    return new tileLayer(options);
  }
  featureLayer.call(this, options);

  var $ = __webpack_require__(2);
  var geo_event = __webpack_require__(6);
  var transform = __webpack_require__(7);
  var tileCache = __webpack_require__(128);
  var fetchQueue = __webpack_require__(121);
  var adjustLayerForRenderer = __webpack_require__(1).adjustLayerForRenderer;
  var Tile = __webpack_require__(54);

  options = $.extend(true, {}, this.constructor.defaults, options || {});
  if (!options.cacheSize) {
    // this size should be sufficient for a 4k display
    options.cacheSize = options.keepLower ? 600 : 200;
  }
  if ($.type(options.subdomains) === 'string') {
    options.subdomains = options.subdomains.split('');
  }
  /* We used to call the url option baseUrl.  If a baseUrl is specified, use
   * it instead of url, interpretting it as before. */
  if (options.baseUrl) {
    var url = options.baseUrl;
    if (url && url.charAt(url.length - 1) !== '/') {
      url += '/';
    }
    options.url = url + '{z}/{x}/{y}.' + (options.imageFormat || 'png');
  }
  /* Save the original url so that we can return it if asked */
  options.originalUrl = options.url;
  if ($.type(options.url) === 'string') {
    options.url = m_tileUrlFromTemplate(options.url);
  }

  var s_init = this._init,
      s_exit = this._exit,
      s_visible = this.visible,
      m_lastTileSet = [],
      m_maxBounds = [],
      m_exited;

  // Space tile levels this far apart in z-buffer space.  This value doesn't
  // have any visual effect in parallel projections, but will slightly skew
  // perspective projections.
  this._levelZIncrement = 1e-5;

  // copy the options into a private variable
  this._options = $.extend(true, {}, options);

  // set the layer attribution text
  this.attribution(options.attribution);

  // initialize the object that keeps track of actively drawn tiles
  this._activeTiles = {};

  // initialize the object that stores active tile regions in a
  // tree-like structure providing quick queries to determine
  // if tiles are completely obscured or not.
  this._tileTree = {};

  // initialize the in memory tile cache
  this._cache = tileCache({ size: options.cacheSize });

  // initialize the tile fetch queue
  this._queue = fetchQueue({
    // this should probably be 6 * subdomains.length if subdomains are used
    size: 6,
    // if track is the same as the cache size, then neither processing time
    // nor memory will be wasted.  Larger values will use more memory,
    // smaller values will do needless computations.
    track: options.cacheSize,
    needed: function (tile) {
      return tile === this.cache.get(tile.toString(), true);
    }.bind(this)
  });

  var m_tileOffsetValues = {};

  /**
   * Readonly accessor to the options object.
   * @property {object} options A copy of the options object.
   * @name geo.tileLayer#options
   */
  Object.defineProperty(this, 'options', { get: function get() {
      return $.extend({}, this._options);
    } });

  /**
   * Readonly accessor to the tile cache object.
   * @property {geo.tileCache} cache The tile cache object.
   * @name geo.tileLayer#cache
   */
  Object.defineProperty(this, 'cache', { get: function get() {
      return this._cache;
    } });

  /**
   * Readonly accessor to the active tile mapping.  This is an object
   * containing all currently drawn tiles (hash(tile) => tile).
   *
   * @property {object} activeTiles The keys are `hash(tile)` and the values
   *    are tiles.
   * @name geo.tileLayer#activeTiles
   */
  Object.defineProperty(this, 'activeTiles', { get: function get() {
      return $.extend({}, this._activeTiles); // copy on output
    } });

  /**
   * The number of tiles at the given zoom level.  The default implementation
   * just returns `Math.pow(2, z)`.
   *
   * @param {number} level A zoom level.
   * @returns {object} The number of tiles in each axis in the form
   *     `{x: nx, y: ny}`.
   */
  this.tilesAtZoom = function (level) {
    if (this._options.tilesAtZoom) {
      return this._options.tilesAtZoom.call(this, level);
    }
    var s = Math.pow(2, level);
    return { x: s, y: s };
  };

  /**
   * The maximum tile bounds at the given zoom level, or null if no special
   * tile bounds.
   *
   * @param {number} level A zoom level.
   * @returns {object} The maximum tile bounds in pixels for the specified
   *    level, or null if none specified (`{x: width, y: height}`).
   */
  this.tilesMaxBounds = function (level) {
    if (this._options.tilesMaxBounds) {
      return this._options.tilesMaxBounds.call(this, level);
    }
    return null;
  };

  /**
   * Get the crop values for a tile based on the tilesMaxBounds function.
   * Returns undefined if the tile should not be cropped.
   *
   * @param {object} tile The tile to compute crop values for.
   * @returns {object} Either `undefined` or an object with `x` and `y` values
   *      which is the size in pixels for the tile.
   */
  this.tileCropFromBounds = function (tile) {
    if (!this._options.tilesMaxBounds) {
      return;
    }
    var level = tile.index.level,
        bounds = this._tileBounds(tile);
    if (m_maxBounds[level] === undefined) {
      m_maxBounds[level] = this.tilesMaxBounds(level) || null;
    }
    if (m_maxBounds[level] && (bounds.right > m_maxBounds[level].x || bounds.bottom > m_maxBounds[level].y)) {
      return {
        x: Math.max(0, Math.min(m_maxBounds[level].x, bounds.right) - bounds.left),
        y: Math.max(0, Math.min(m_maxBounds[level].y, bounds.bottom) - bounds.top)
      };
    }
  };

  /**
   * Returns `true` if the given tile index is valid:
   * - min level <= level <= max level
   * - 0 <= x <= 2^level - 1
   * - 0 <= y <= 2^level - 1
   * If the layer wraps, the x and y values may be allowed to extend beyond
   * these values.
   *
   * @param {object} index The tile index.
   * @param {number} index.x
   * @param {number} index.y
   * @param {number} index.level
   * @returns {boolean}
   */
  this.isValid = function (index) {
    if (!(this._options.minLevel <= index.level && index.level <= this._options.maxLevel)) {
      return false;
    }
    if (!(this._options.wrapX || 0 <= index.x && index.x <= this.tilesAtZoom(index.level).x - 1)) {
      return false;
    }
    if (!(this._options.wrapY || 0 <= index.y && index.y <= this.tilesAtZoom(index.level).y - 1)) {
      return false;
    }
    return true;
  };

  /**
   * Returns the current origin tile and offset at the given zoom level.
   * This is intended to be cached in the future to optimize coordinate
   * transformations.
   *
   * @protected
   * @param {number} level The target zoom level.
   * @returns {object} The origin and offset in the form
   *   `{index: {x, y}, offset: {x, y}}`.
   */
  this._origin = function (level) {
    var origin = this.toLevel(this.toLocal(this.map().origin()), level),
        o = this._options,
        index,
        offset;

    // get the tile index
    index = {
      x: Math.floor(origin.x / o.tileWidth),
      y: Math.floor(origin.y / o.tileHeight)
    };

    // get the offset inside the tile (in pixels)
    // This computation should contain the only numerically unstable
    // subtraction in this class.  All other methods will assume
    // coordinates are given relative to the map origin.
    offset = {
      x: origin.x - o.tileWidth * index.x,
      y: origin.y - o.tileHeight * index.y
    };
    return { index: index, offset: offset };
  };

  /**
   * Returns a tile's bounds in its level coordinates.
   *
   * @param {geo.tile} tile The tile to check.
   * @returns {object} The tile's bounds with `left`, `top`, `right`,
   *   `bottom`.
   */
  this._tileBounds = function (tile) {
    var origin = this._origin(tile.index.level);
    return tile.bounds(origin.index, origin.offset);
  };

  /**
   * Returns the tile indices at the given point.
   *
   * @param {object} point The coordinates in pixels relative to the map
   *   origin.
   * @param {number} point.x
   * @param {number} point.y
   * @param {number} level The target zoom level.
   * @returns {object} The tile indices.  This has `x` and `y` properties.
   */
  this.tileAtPoint = function (point, level) {
    var o = this._origin(level);
    var map = this.map();
    point = this.displayToLevel(map.gcsToDisplay(point, null), level);
    if (isNaN(point.x)) {
      point.x = 0;
    }
    if (isNaN(point.y)) {
      point.y = 0;
    }
    var to = this._tileOffset(level);
    if (to) {
      point.x += to.x;
      point.y += to.y;
    }
    var tile = {
      x: Math.floor(o.index.x + (o.offset.x + point.x) / this._options.tileWidth),
      y: Math.floor(o.index.y + (o.offset.y + point.y) / this._options.tileHeight)
    };
    return tile;
  };

  /**
   * Returns a tile's bounds in a gcs.
   *
   * @param {object|tile} indexOrTile Either a tile or an object with
   *    {x, y, level}` specifying a tile.
   * @param {string|geo.transform|null} [gcs] `undefined` to use the
   *    interface gcs, `null` to use the map gcs, or any other transform.
   * @returns {object} The tile bounds in the specified gcs.
   */
  this.gcsTileBounds = function (indexOrTile, gcs) {
    var tile = indexOrTile.index ? indexOrTile : Tile({
      index: indexOrTile,
      size: { x: this._options.tileWidth, y: this._options.tileHeight },
      url: ''
    });
    var to = this._tileOffset(tile.index.level),
        bounds = tile.bounds({ x: 0, y: 0 }, to),
        map = this.map(),
        unit = map.unitsPerPixel(tile.index.level);
    var coord = [{
      x: bounds.left * unit, y: this._topDown() * bounds.top * unit
    }, {
      x: bounds.right * unit, y: this._topDown() * bounds.bottom * unit
    }];
    gcs = gcs === null ? map.gcs() : gcs === undefined ? map.ingcs() : gcs;
    if (gcs !== map.gcs()) {
      coord = transform.transformCoordinates(map.gcs(), gcs, coord);
    }
    return {
      left: coord[0].x,
      top: coord[0].y,
      right: coord[1].x,
      bottom: coord[1].y
    };
  };

  /**
   * Returns an instantiated tile object with the given indices.  This
   * method always returns a new tile object.  Use `_getTileCached`
   * to use the caching layer.
   *
   * @param {object} index The tile index.
   * @param {number} index.x
   * @param {number} index.y
   * @param {number} index.level
   * @param {object} source The tile index used for constructing the url.
   * @param {number} source.x
   * @param {number} source.y
   * @param {number} source.level
   * @returns {geo.tile}
   */
  this._getTile = function (index, source) {
    var urlParams = source || index;
    return Tile({
      index: index,
      size: { x: this._options.tileWidth, y: this._options.tileHeight },
      queue: this._queue,
      url: this._options.url.call(this, urlParams.x, urlParams.y, urlParams.level || 0, this._options.subdomains)
    });
  };

  /**
   * Returns an instantiated tile object with the given indices.  This
   * method is similar to `_getTile`, but checks the cache before
   * generating a new tile.
   *
   * @param {object} index The tile index.
   * @param {number} index.x
   * @param {number} index.y
   * @param {number} index.level
   * @param {object} source The tile index used for constructing the url.
   * @param {number} source.x
   * @param {number} source.y
   * @param {number} source.level
   * @param {boolean} delayPurge If true, don't purge tiles from the cache.
   * @returns {geo.tile}
   */
  this._getTileCached = function (index, source, delayPurge) {
    var tile = this.cache.get(this._tileHash(index));
    if (tile === null) {
      tile = this._getTile(index, source);
      this.cache.add(tile, this.remove.bind(this), delayPurge);
    }
    return tile;
  };

  /**
   * Returns a string representation of the tile at the given index.
   *
   * Note: This method **must** return the same string as:
   * ```
   *   tile({index: index}).toString();
   * ```
   * This method is used as a hashing function for the caching layer.
   *
   * @param {object} index The tile index
   * @param {number} index.x
   * @param {number} index.y
   * @param {number} index.level
   * @returns {string}
   */
  this._tileHash = function (index) {
    return [index.level || 0, index.y, index.x].join('_');
  };

  /**
   * Returns the optimal starting and ending tile indices (inclusive)
   * necessary to fill the given viewport.
   *
   * @param {number} level The zoom level
   * @param {geo.geoBounds} bounds The map bounds in world coordinates.
   * @returns {object} The tile range with a `start`  and `end` record, each
   *      with `x` and `y` tile indices.
   */
  this._getTileRange = function (level, bounds) {
    var corners = [this.tileAtPoint({ x: bounds.left, y: bounds.top }, level), this.tileAtPoint({ x: bounds.right, y: bounds.top }, level), this.tileAtPoint({ x: bounds.left, y: bounds.bottom }, level), this.tileAtPoint({ x: bounds.right, y: bounds.bottom }, level)];
    return {
      start: {
        x: Math.min(corners[0].x, corners[1].x, corners[2].x, corners[3].x),
        y: Math.min(corners[0].y, corners[1].y, corners[2].y, corners[3].y)
      },
      end: {
        x: Math.max(corners[0].x, corners[1].x, corners[2].x, corners[3].x),
        y: Math.max(corners[0].y, corners[1].y, corners[2].y, corners[3].y)
      }
    };
  };

  /**
   * Returns a list of tiles necessary to fill the screen at the given
   * zoom level, center point, and viewport size.  The list is optionally
   * ordered by loading priority (center tiles first).
   *
   * @protected
   * @param {number} maxLevel The zoom level
   * @param {geo.geoBounds} bounds The map bounds
   * @param {boolean} sorted Return a sorted list
   * @param {boolean} onlyIfChanged If the set of tiles have not changed
   *     (even if their desired order has), return undefined instead of an
   *     array of tiles.
   * @returns {geo.tile[]} An array of tile objects
   */
  this._getTiles = function (maxLevel, bounds, sorted, onlyIfChanged) {
    var i,
        j,
        tiles = [],
        index,
        nTilesLevel,
        start,
        end,
        indexRange,
        source,
        center,
        changed = false,
        old,
        level,
        minLevel = this._options.keepLower ? this._options.minLevel : maxLevel;
    if (maxLevel < minLevel) {
      maxLevel = minLevel;
    }

    /* Generate a list of the tiles that we want to create.  This is done
     * before sorting, because we want to actually generate the tiles in
     * the sort order. */
    for (level = minLevel; level <= maxLevel; level += 1) {
      // get the tile range to fetch
      indexRange = this._getTileRange(level, bounds);
      start = indexRange.start;
      end = indexRange.end;
      // total number of tiles existing at this level
      nTilesLevel = this.tilesAtZoom(level);

      if (!this._options.wrapX) {
        start.x = Math.min(Math.max(start.x, 0), nTilesLevel.x - 1);
        end.x = Math.min(Math.max(end.x, 0), nTilesLevel.x - 1);
        if (level === minLevel && this._options.keepLower) {
          start.x = 0;
          end.x = nTilesLevel.x - 1;
        }
      }
      if (!this._options.wrapY) {
        start.y = Math.min(Math.max(start.y, 0), nTilesLevel.y - 1);
        end.y = Math.min(Math.max(end.y, 0), nTilesLevel.y - 1);
        if (level === minLevel && this._options.keepLower) {
          start.y = 0;
          end.y = nTilesLevel.y - 1;
        }
      }
      /* If we are reprojecting tiles, we need a check to not use all levels
       * if the number of tiles is excessive. */
      if (this._options.gcs && this._options.gcs !== this.map().gcs() && level !== minLevel && (end.x + 1 - start.x) * (end.y + 1 - start.y) > this.map().size().width * this.map().size().height / this._options.tileWidth / this._options.tileHeight * 16) {
        break;
      }

      // loop over the tile range
      for (i = start.x; i <= end.x; i += 1) {
        for (j = start.y; j <= end.y; j += 1) {
          index = { level: level, x: i, y: j };
          source = { level: level, x: i, y: j };
          if (this._options.wrapX) {
            source.x = modulo(source.x, nTilesLevel.x);
          }
          if (this._options.wrapY) {
            source.y = modulo(source.y, nTilesLevel.y);
          }
          if (this.isValid(source)) {
            if (onlyIfChanged && tiles.length < m_lastTileSet.length) {
              old = m_lastTileSet[tiles.length];
              changed = changed || index.level !== old.level || index.x !== old.x || index.y !== old.y;
            }
            tiles.push({ index: index, source: source });
          }
        }
      }
    }

    if (onlyIfChanged) {
      if (!changed && tiles.length === m_lastTileSet.length) {
        return;
      }
      m_lastTileSet.splice(0, m_lastTileSet.length);
      $.each(tiles, function (idx, tile) {
        m_lastTileSet.push(tile.index);
      });
    }

    if (sorted) {
      center = {
        x: (start.x + end.x) / 2,
        y: (start.y + end.y) / 2,
        level: maxLevel,
        bottomLevel: maxLevel
      };
      var numTiles = Math.max(end.x - start.x, end.y - start.y) + 1;
      for (; numTiles >= 1; numTiles /= 2) {
        center.bottomLevel -= 1;
      }
      tiles.sort(this._loadMetric(center));
      /* If we are using a fetch queue, start a new batch */
      if (this._queue) {
        this._queue.batch(true);
      }
    }
    if (this.cache.size < tiles.length) {
      console.log('Increasing cache size to ' + tiles.length);
      this.cache.size = tiles.length;
    }
    /* Actually get the tiles. */
    for (i = 0; i < tiles.length; i += 1) {
      tiles[i] = this._getTileCached(tiles[i].index, tiles[i].source, true);
    }
    this.cache.purge(this.remove.bind(this));
    return tiles;
  };

  /**
   * Prefetches tiles up to a given zoom level around a given bounding box.
   *
   * @param {number} level The zoom level.
   * @param {geo.geoBounds} bounds The map bounds.
   * @returns {jQuery.Deferred} resolves when all of the tiles are fetched.
   */
  this.prefetch = function (level, bounds) {
    var tiles;
    tiles = this._getTiles(level, bounds, true);
    return $.when.apply($, tiles.map(function (tile) {
      return tile.fetch();
    }));
  };

  /**
   * This method returns a metric that determines tile loading order.  The
   * default implementation prioritizes tiles that are closer to the center,
   * or at a lower zoom level.
   *
   * @protected
   * @param {object} center The center tile.
   * @param {number} center.x
   * @param {number} center.y
   * @returns {function} A function accepted by `Array.prototype.sort`.
   */
  this._loadMetric = function (center) {
    return function (a, b) {
      var a0, b0, dx, dy, cx, cy, scale;

      a = a.index || a;
      b = b.index || b;
      // shortcut if zoom level differs
      if (a.level !== b.level) {
        if (center.bottomLevel && a.level >= center.bottomLevel !== b.level >= center.bottomLevel) {
          return a.level >= center.bottomLevel ? -1 : 1;
        }
        return a.level - b.level;
      }

      /* compute the center coordinates relative to a.level.  Since we really
       * care about the center of the tiles, use an offset */
      scale = Math.pow(2, a.level - center.level);
      cx = (center.x + 0.5) * scale - 0.5;
      cy = (center.y + 0.5) * scale - 0.5;

      // calculate distances to the center squared
      dx = a.x - cx;
      dy = a.y - cy;
      a0 = dx * dx + dy * dy;

      dx = b.x - cx;
      dy = b.y - cy;
      b0 = dx * dx + dy * dy;

      // return negative if a < b, or positive if a > b
      return a0 - b0;
    };
  };

  /**
   * Convert a coordinate from pixel coordinates at the given zoom
   * level to world coordinates.
   *
   * @param {object} coord
   * @param {number} coord.x The offset in pixels (level 0) from the left
   *      edge.
   * @param {number} coord.y The offset in pixels (level 0) from the bottom
   *      edge.
   * @param {number} level The zoom level of the source coordinates.
   * @returns {object} World coordinates with `x` and `y`.
   */
  this.fromLevel = function (coord, level) {
    var s = Math.pow(2, -level);
    return {
      x: coord.x * s,
      y: coord.y * s
    };
  };

  /**
   * Convert a coordinate from layer coordinates to pixel coordinates at the
   * given zoom level.
   *
   * @param {object} coord
   * @param {number} coord.x The offset in pixels (level 0) from the left
   *      edge.
   * @param {number} coord.y The offset in pixels (level 0) from the bottom
   *      edge.
   * @param {number} level The zoom level of the new coordinates.
   * @returns {object} The pixel coordinates with `x` and `y`.
   */
  this.toLevel = function (coord, level) {
    var s = Math.pow(2, level);
    return {
      x: coord.x * s,
      y: coord.y * s
    };
  };

  /**
   * Draw the given tile on the active canvas
   *.
   * @param {geo.tile} tile The tile to draw.
   */
  this.drawTile = function (tile) {
    var hash = tile.toString();

    if (this._activeTiles.hasOwnProperty(hash)) {
      // the tile is already drawn, move it to the top
      this._moveToTop(tile);
    } else {
      // pass to the rendering implementation
      this._drawTile(tile);
    }

    // add the tile to the active cache
    this._activeTiles[hash] = tile;
  };

  /**
   * Render the tile on the canvas.  This implementation draws the tiles
   * directly on the DOM using <img> tags.  Derived classes should override
   * this method to draw the tile on a renderer specific context.
   *
   * @protected
   * @param {geo.tile} tile The tile to draw.
   */
  this._drawTile = function (tile) {
    // Make sure this method is not called when there is
    // a renderer attached.
    if (this.renderer() !== null) {
      throw new Error('This draw method is not valid on renderer managed layers.');
    }

    // get the layer node
    var level = tile.index.level,
        div = $(this._getSubLayer(level)),
        bounds = this._tileBounds(tile),
        duration = this._options.animationDuration,
        container = $('<div class="geo-tile-container"/>').attr('tile-reference', tile.toString()),
        crop;

    // apply a transform to place the image correctly
    container.append(tile.image);
    container.css({
      left: bounds.left - parseInt(div.attr('offsetx') || 0, 10) + 'px',
      top: bounds.top - parseInt(div.attr('offsety') || 0, 10) + 'px'
    });

    crop = this.tileCropFromBounds(tile);
    if (crop) {
      container.addClass('crop').css({
        width: crop.x + 'px',
        height: crop.y + 'px'
      });
    }

    // apply fade in animation
    if (duration > 0) {
      tile.fadeIn(duration);
    }

    // append the image element
    div.append(container);

    // add an error handler
    tile.catch(function () {
      // May want to do something special here later
      console.warn('Could not load tile at ' + tile.toString());
      this._remove(tile);
    }.bind(this));
  };

  /**
   * Remove the given tile from the canvas and the active cache.
   *
   * @param {geo.tile|string} tile The tile (or hash) to remove.
   * @returns {geo.tile} The tile removed from the active layer.
   */
  this.remove = function (tile) {
    var hash = tile.toString();
    var value = this._activeTiles[hash];

    if (value instanceof Tile) {
      this._remove(value);
    }

    delete this._activeTiles[hash];
    return value;
  };

  /**
   * Remove the given tile from the canvas.  This implementation just
   * finds and removes the <img> element created for the tile.
   *
   * @param {geo.tile|string} tile The tile object to remove.
   */
  this._remove = function (tile) {
    if (tile.image) {
      if (tile.image.parentElement) {
        $(tile.image.parentElement).remove();
      } else {
        /* This shouldn't happen, but sometimes does.  Originally it happened
         * when a tile was removed from the cache before it was finished
         * being used; there is still some much rarer condition that can
         * cause it.  Log that it happened until we can figure out how to fix
         * the issue. */
        console.log('No parent element to remove ' + tile.toString(), tile);
      }
      $(tile.image).remove();
    }
  };

  /**
   * Move the given tile to the top on the canvas.
   *
   * @param {geo.tile} tile The tile object to move.
   */
  this._moveToTop = function (tile) {
    $.noop(tile);
  };

  /**
   * Query the attached map for the current bounds and return them as pixels
   *      at the current zoom level.
   *
   * @returns {object} Bounds object with `left`, `right`, `top`, `bottom`,
   *      `scale`, and `level` keys.
   */
  this._getViewBounds = function () {
    var map = this.map(),
        mapZoom = map.zoom(),
        zoom = this._options.tileRounding(mapZoom),
        scale = Math.pow(2, mapZoom - zoom),
        size = map.size();
    var ul = this.displayToLevel({ x: 0, y: 0 }),
        ur = this.displayToLevel({ x: size.width, y: 0 }),
        ll = this.displayToLevel({ x: 0, y: size.height }),
        lr = this.displayToLevel({ x: size.width, y: size.height });
    return {
      level: zoom,
      scale: scale,
      left: Math.min(ul.x, ur.x, ll.x, lr.x),
      right: Math.max(ul.x, ur.x, ll.x, lr.x),
      top: Math.min(ul.y, ur.y, ll.y, lr.y),
      bottom: Math.max(ul.y, ur.y, ll.y, lr.y)
    };
  };

  /**
   * Remove all inactive tiles from the display.  An inactive tile is one
   * that is no longer visible either because it was panned out of the active
   * view or the zoom has changed.
   *
   * @protected
   * @param {number} zoom Tiles in bounds at this zoom level will be kept.
   * @param {boolean} doneLoading If true, allow purging additional tiles.
   * @param {geo.geoBounds} bounds View bounds.  If not specified, this is
   *   obtained from _getViewBounds().
   * @returns {this}
   */
  this._purge = function (zoom, doneLoading, bounds) {
    var tile, hash;

    // Don't purge tiles in an active update
    if (this._updating) {
      return this;
    }

    // get the view bounds
    if (!bounds) {
      bounds = this._getViewBounds();
    }

    for (hash in this._activeTiles) {

      tile = this._activeTiles[hash];
      if (this._canPurge(tile, bounds, zoom, doneLoading)) {
        this.remove(tile);
      }
    }
    return this;
  };

  /**
   * Remove all active tiles from the canvas.
   *
   * @returns {geo.tile[]} The array of tiles removed.
   */
  this.clear = function () {
    var tiles = [],
        tile;

    // ignoring the warning here because this is a privately
    // controlled object with simple keys
    for (tile in this._activeTiles) {
      tiles.push(this.remove(tile));
    }

    // clear out the tile coverage tree
    this._tileTree = {};

    m_lastTileSet = [];

    return tiles;
  };

  /**
   * Reset the layer to the initial state, clearing the canvas and resetting
   * the tile cache.
   *
   * @returns {this}
   */
  this.reset = function () {
    this.clear();
    this._cache.clear();
    return this;
  };

  /**
   * Compute local coordinates from the given world coordinates.  The tile
   * layer uses units of pixels relative to the world space coordinate
   * origin.
   *
   * @param {object} pt A point in world space coordinates with `x` and `y`.
   * @param {number|undefined} zoom If unspecified, use the map zoom.
   * @returns {object} Local coordinates with `x` and `y`.
   */
  this.toLocal = function (pt, zoom) {
    var map = this.map(),
        unit = map.unitsPerPixel(zoom === undefined ? map.zoom() : zoom);
    return {
      x: pt.x / unit,
      y: this._topDown() * pt.y / unit
    };
  };

  /**
   * Compute world coordinates from the given local coordinates.  The tile
   * layer uses units of pixels relative to the world space coordinate
   * origin.
   *
   * @param {object} pt A point in world space coordinates with `x` and `y`.
   * @param {number|undefined} zoom If unspecified, use the map zoom.
   * @returns {object} Local coordinates with `x` and `y`.
   */
  this.fromLocal = function (pt, zoom) {
    // these need to always use the *layer* unitsPerPixel, or possibly
    // convert tile space using a transform
    var map = this.map(),
        unit = map.unitsPerPixel(zoom === undefined ? map.zoom() : zoom);
    return {
      x: pt.x * unit,
      y: this._topDown() * pt.y * unit
    };
  };

  /**
   * Return a factor for inverting the y units as appropriate.
   *
   * @returns {number} Either 1 to not invert y, or -1 to invert it.
   */
  this._topDown = function () {
    return this._options.topDown ? 1 : -1;
  };

  /**
   * Return the DOM element containing a level specific layer.  This will
   * create the element if it doesn't already exist.
   *
   * @param {number} level The zoom level of the layer to fetch.
   * @returns {DOM} The layer's DOM element.
   */
  this._getSubLayer = function (level) {
    if (!this.canvas()) {
      return;
    }
    var node = this.canvas().find('div[data-tile-layer=' + level.toFixed() + ']').get(0);
    if (!node) {
      node = $('<div class=geo-tile-layer data-tile-layer="' + level.toFixed() + '"/>').get(0);
      this.canvas().append(node);
    }
    return node;
  };

  /**
   * Set sublayer transforms to align them with the given zoom level.
   *
   * @param {number} level The target zoom level.
   * @param {geo.geoBounds} view The view bounds.  The top and left are used
   *      to adjust the offset of tile layers.
   * @returns {object} The `x` and `y` offsets for the current level.
   */
  this._updateSubLayers = function (level, view) {
    var canvas = this.canvas(),
        lastlevel = parseInt(canvas.attr('lastlevel'), 10),
        lastx = parseInt(canvas.attr('lastoffsetx') || 0, 10),
        lasty = parseInt(canvas.attr('lastoffsety') || 0, 10);
    if (lastlevel === level && Math.abs(lastx - view.left) < 65536 && Math.abs(lasty - view.top) < 65536) {
      return { x: lastx, y: lasty };
    }
    var map = this.map(),
        to = this._tileOffset(level),
        x = parseInt((view.left + view.right - map.size().width) / 2 + to.x, 10),
        y = parseInt((view.top + view.bottom - map.size().height) / 2 + to.y, 10);
    canvas.find('.geo-tile-layer').each(function (idx, el) {
      var $el = $(el),
          layer = parseInt($el.data('tileLayer'), 10);
      $el.css('transform', 'scale(' + Math.pow(2, level - layer) + ')');
      var layerx = parseInt(x / Math.pow(2, level - layer), 10),
          layery = parseInt(y / Math.pow(2, level - layer), 10),
          dx = layerx - parseInt($el.attr('offsetx') || 0, 10),
          dy = layery - parseInt($el.attr('offsety') || 0, 10);
      $el.attr({ offsetx: layerx, offsety: layery });
      $el.find('.geo-tile-container').each(function (tileidx, tileel) {
        $(tileel).css({
          left: parseInt($(tileel).css('left'), 10) - dx + 'px',
          top: parseInt($(tileel).css('top'), 10) - dy + 'px'
        });
      });
    });
    canvas.attr({ lastoffsetx: x, lastoffsety: y, lastlevel: level });
    return { x: x, y: y };
  };

  /**
   * Update the view according to the map/camera.
   *
   * @param {geo.event} evt The event that triggered the change.  Zoom and
   *      rotate events do nothing, since they are always followed by a pan
   *      event which will cause appropriate action.
   * @returns {this}
   */
  this._update = function (evt) {
    /* Ignore zoom and rotate events, as they are ALWAYS followed by a pan
     * event */
    if (evt && evt.event && (evt.event.event === geo_event.zoom || evt.event.event === geo_event.rotate)) {
      return this;
    }
    if (!this.visible()) {
      return this;
    }
    var map = this.map(),
        bounds = map.bounds(undefined, null),
        mapZoom = map.zoom(),
        zoom = this._options.tileRounding(mapZoom),
        tiles;
    if (this._updateSubLayers) {
      var view = this._getViewBounds();
      // Update the transform for the local layer coordinates
      var offset = this._updateSubLayers(zoom, view) || { x: 0, y: 0 };

      var to = this._tileOffset(zoom);
      if (this.renderer() === null) {
        var scale = Math.pow(2, mapZoom - zoom),
            rotation = map.rotation(),
            rx = -to.x + -(view.left + view.right) / 2 + offset.x,
            ry = -to.y + -(view.bottom + view.top) / 2 + offset.y,
            dx = rx + map.size().width / 2,
            dy = ry + map.size().height / 2;

        this.canvas().css({
          'transform-origin': '' + -rx + 'px ' + -ry + 'px'
        });
        var transform = 'translate(' + dx + 'px' + ',' + dy + 'px' + ')' + 'scale(' + scale + ')';
        if (rotation) {
          transform += 'rotate(' + rotation * 180 / Math.PI + 'deg)';
        }
        this.canvas().css('transform', transform);
      }
      /* Set some attributes that can be used by non-css based viewers.  This
       * doesn't include the map center, as that may need to be handled
       * differently from the view center. */
      this.canvas().attr({
        scale: Math.pow(2, mapZoom - zoom),
        dx: -to.x + -(view.left + view.right) / 2,
        dy: -to.y + -(view.bottom + view.top) / 2,
        offsetx: offset.x,
        offsety: offset.y,
        rotation: map.rotation()
      });
    }

    tiles = this._getTiles(zoom, bounds, true, true);

    if (tiles === undefined) {
      return this;
    }

    // reset the tile coverage tree
    this._tileTree = {};

    tiles.forEach(function (tile) {
      if (tile.fetched()) {
        /* if we have already fetched the tile, we know we can just draw it,
         * as the bounds won't have changed since the call to _getTiles. */
        this.drawTile(tile);

        // mark the tile as covered
        this._setTileTree(tile);
      } else {
        if (!tile._queued) {
          tile.then(function () {
            if (m_exited) {
              /* If we have disconnected the renderer, do nothing.  This
               * happens when the layer is being deleted. */
              return;
            }
            if (tile !== this.cache.get(tile.toString())) {
              /* If the tile has fallen out of the cache, don't draw it -- it
               * is untracked.  This may be an indication that a larger cache
               * should have been used. */
              return;
            }
            /* Check if a tile is still desired.  Don't draw it if it
             * isn't. */
            var mapZoom = map.zoom(),
                zoom = this._options.tileRounding(mapZoom),
                view = this._getViewBounds();
            if (this._canPurge(tile, view, zoom)) {
              this.remove(tile);
              return;
            }

            this.drawTile(tile);

            // mark the tile as covered
            this._setTileTree(tile);
          }.bind(this));

          this.addPromise(tile);
          tile._queued = true;
        } else {
          /* If we are using a fetch queue, tell the queue so this tile can
           * be reprioritized. */
          var pos = this._queue ? this._queue.get(tile) : -1;
          if (pos >= 0) {
            this._queue.add(tile);
          }
        }
      }
    }.bind(this));
    // purge all old tiles when the new tiles are loaded (successfully or not)
    $.when.apply($, tiles).done( // called on success and failure
    function () {
      var map = this.map(),
          mapZoom = map.zoom(),
          zoom = this._options.tileRounding(mapZoom);
      this._purge(zoom, true);
    }.bind(this));
    return this;
  };

  /**
   * Set a value in the tile tree object indicating that the given area of
   * the canvas is covered by the tile.
   *
   * @protected
   * @param {geo.tile} tile The tile to add.
   */
  this._setTileTree = function (tile) {
    if (this._options.keepLower) {
      return;
    }
    var index = tile.index;
    this._tileTree[index.level] = this._tileTree[index.level] || {};
    this._tileTree[index.level][index.x] = this._tileTree[index.level][index.x] || {};
    this._tileTree[index.level][index.x][index.y] = tile;
  };

  /**
   * Get a value in the tile tree object if it exists or return `null`.
   * @protected
   * @param {object} index A tile index object
   * @param {object} index.level
   * @param {object} index.x
   * @param {object} index.y
   * @returns {geo.tile|null}
   */
  this._getTileTree = function (index) {
    return ((this._tileTree[index.level] || {})[index.x] || {})[index.y] || null;
  };

  /**
   * Returns true if the tile is completely covered by other tiles on the
   * canvas.  Currently this method only checks layers +/- 1 away from
   * `tile`.  If the zoom level is allowed to change by 2 or more in a single
   * update step, this method will need to be refactored to make a more
   * robust check.  Returns an array of tiles covering it or null if any
   * part of the tile is exposed.
   *
   * @protected
   * @param {geo.tile} tile The tile to check.
   * @returns {geo.tile[]|null}
   */
  this._isCovered = function (tile) {
    var level = tile.index.level,
        x = tile.index.x,
        y = tile.index.y,
        tiles = [];

    // Check one level up
    tiles = this._getTileTree({
      level: level - 1,
      x: Math.floor(x / 2),
      y: Math.floor(y / 2)
    });
    if (tiles) {
      return [tiles];
    }

    // Check one level down
    tiles = [this._getTileTree({
      level: level + 1,
      x: 2 * x,
      y: 2 * y
    }), this._getTileTree({
      level: level + 1,
      x: 2 * x + 1,
      y: 2 * y
    }), this._getTileTree({
      level: level + 1,
      x: 2 * x,
      y: 2 * y + 1
    }), this._getTileTree({
      level: level + 1,
      x: 2 * x + 1,
      y: 2 * y + 1
    })];
    if (tiles.every(function (t) {
      return t !== null;
    })) {
      return tiles;
    }

    return null;
  };

  /**
   * Returns true if the provided tile is outside of the current view bounds
   * and can be removed from the canvas.
   * @protected
   * @param {geo.tile} tile The tile to check.
   * @param {geo.geoBounds} bounds The view bounds.
   * @returns {boolean}
   */
  this._outOfBounds = function (tile, bounds) {
    /* We may want to add an (n) tile edge buffer so we appear more
     * responsive */
    var to = this._tileOffset(tile.index.level);
    var scale = 1;
    if (tile.index.level !== bounds.level) {
      scale = Math.pow(2, (bounds.level || 0) - (tile.index.level || 0));
    }
    return (tile.bottom - to.y) * scale < bounds.top || (tile.left - to.x) * scale > bounds.right || (tile.top - to.y) * scale > bounds.bottom || (tile.right - to.x) * scale < bounds.left;
  };

  /**
   * Returns true if the provided tile can be purged from the canvas.  This
   * method will return `true` if the tile is completely covered by one or
   * more other tiles or it is outside of the active view bounds.  This
   * method returns the logical and of `_isCovered` and `_outOfBounds`.
   * @protected
   * @param {geo.tile} tile The tile to check.
   * @param {geo.geoBounds} [bounds] The view bounds (if unspecified, assume
   *      global bounds)
   * @param {number} bounds.level The zoom level the bounds are given as.
   * @param {number} zoom Keep in bound tile at this zoom level.
   * @param {boolean} doneLoading If true, allow purging additional tiles.
   * @returns {boolean}
   */
  this._canPurge = function (tile, bounds, zoom, doneLoading) {
    if (this._options.keepLower) {
      zoom = zoom || 0;
      if (zoom < tile.index.level && tile.index.level !== this._options.minLevel) {
        return true;
      }
      if (tile.index.level === this._options.minLevel && !this._options.wrapX && !this._options.wrapY) {
        return false;
      }
    } else {
      /* For tile layers that should only keep one layer, if loading is
       * finished, purge all but the current layer.  This is important for
       * semi-transparanet layers. */
      if ((doneLoading || this._isCovered(tile)) && zoom !== tile.index.level) {
        return true;
      }
    }
    if (bounds) {
      return this._outOfBounds(tile, bounds);
    }
    return false;
  };

  /**
   * Convert display pixel coordinates (where (0,0) is the upper left) to
   * layer pixel coordinates (typically (0,0) is the center of the map and
   * the upper-left has the most negative values).
   * By default, this is done at the current base zoom level.
   *
   * @param {object} [pt] The point to convert with `x` and `y`.  If
   *      `undefined`, use the center of the display.
   * @param {number} [zoom] If specified, the zoom level to use.
   * @returns {object} The point in level coordinates with `x` and `y`.
   */
  this.displayToLevel = function (pt, zoom) {
    var map = this.map(),
        mapzoom = map.zoom(),
        roundzoom = this._options.tileRounding(mapzoom),
        unit = map.unitsPerPixel(zoom === undefined ? roundzoom : zoom),
        gcsPt;
    if (pt === undefined) {
      var size = map.size();
      pt = { x: size.width / 2, y: size.height / 2 };
    }
    /* displayToGcs can fail under certain projections.  If this happens,
     * just return the origin. */
    try {
      gcsPt = map.displayToGcs(pt, this._options.gcs || null);
    } catch (err) {
      gcsPt = { x: 0, y: 0 };
    }
    /* Reverse the y coordinate, since we expect the gcs coordinate system
     * to be right-handed and the level coordinate system to be
     * left-handed. */
    var lvlPt = { x: gcsPt.x / unit, y: this._topDown() * gcsPt.y / unit };
    return lvlPt;
  };

  /**
   * Get or set the tile url string or function.  If changed, load the new
   * tiles.
   *
   * @param {string|function} [url] The new tile url.
   * @returns {string|function|this}
   */
  this.url = function (url) {
    if (url === undefined) {
      return this._options.originalUrl;
    }
    if (url === this._options.originalUrl) {
      return this;
    }
    this._options.originalUrl = url;
    if ($.type(url) === 'string') {
      url = m_tileUrlFromTemplate(url);
    }
    this._options.url = url;
    this.reset();
    this.map().draw();
    return this;
  };

  /**
   * Get or set the subdomains used for templating.
   *
   * @param {string|list} [subdomains] A comma-separated list, a string of
   *      single character subdomains, or a list.
   * @returns {string|list|this}
   */
  this.subdomains = function (subdomains) {
    if (subdomains === undefined) {
      return this._options.subdomains;
    }
    if (subdomains) {
      if ($.type(subdomains) === 'string') {
        if (subdomains.indexOf(',') >= 0) {
          subdomains = subdomains.split(',');
        } else {
          subdomains = subdomains.split('');
        }
      }
      this._options.subdomains = subdomains;
      this.reset();
      this.map().draw();
    }
    return this;
  };

  /**
   * Return a value from the tileOffset function, caching it for different
   * levels.
   *
   * @param {number} level The level to pass to the tileOffset function.
   * @returns {object} A tile offset object with `x` and `y` properties.
   */
  this._tileOffset = function (level) {
    if (m_tileOffsetValues[level] === undefined) {
      m_tileOffsetValues[level] = this._options.tileOffset(level);
    }
    return m_tileOffsetValues[level];
  };

  /**
   * Get/Set visibility of the layer.
   *
   * @param {boolean|undefined} val If unspecified, return the visibility,
   *    otherwise set it.
   * @returns {boolean|this} Either the visibility (if getting) or the layer
   *    (if setting).
   */
  this.visible = function (val) {
    if (val === undefined) {
      return s_visible();
    }
    if (this.visible() !== val) {
      s_visible(val);

      if (val) {
        this._update();
      }
    }
    return this;
  };

  /**
   * Initialize after the layer is added to the map.
   *
   * @returns {this}
   */
  this._init = function () {
    var sublayer;

    // call super method
    s_init.apply(this, arguments);

    if (this.renderer() === null) {
      // Initialize sublayers in the correct order
      for (sublayer = 0; sublayer <= this._options.maxLevel; sublayer += 1) {
        this._getSubLayer(sublayer);
      }
    }
    return this;
  };

  /**
   * Clean up the layer.
   *
   * @returns {this}
   */
  this._exit = function () {
    this.reset();
    // call super method
    s_exit.apply(this, arguments);
    m_exited = true;
    return this;
  };

  adjustLayerForRenderer('tile', this);

  return this;
};

/**
 * This object contains the default options used to initialize the tileLayer.
 */
tileLayer.defaults = {
  minLevel: 0,
  maxLevel: 18,
  tileOverlap: { x: 0, y: 0 },
  tileWidth: 256,
  tileHeight: 256,
  wrapX: true,
  wrapY: false,
  url: null,
  subdomains: 'abc',
  tileOffset: function tileOffset(level) {
    return { x: 0, y: 0 };
  },
  tilesMaxBounds: null,
  topDown: false,
  keepLower: true,
  // cacheSize: 400,  // set depending on keepLower
  tileRounding: Math.round,
  attribution: '',
  animationDuration: 0
};

inherit(tileLayer, featureLayer);

module.exports = tileLayer;

/***/ }),
/* 128 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/**
 * This class implements a simple cache for tile objects.  Each tile is
 * stored in cache object keyed by a configurable hashing function.  Another
 * array keeps track of last access times for each tile to purge old tiles
 * once the maximum cache size is reached.
 *
 * @class
 * @alias geo.tileCache
 *
 * @param {object} [options] A configuration object for the cache.
 * @param {number} [options.size=64] The maximum number of tiles to store.
 */
var tileCache = function tileCache(options) {
  if (!(this instanceof tileCache)) {
    return new tileCache(options);
  }
  options = options || {};
  this._size = options.size || 64;

  /**
   * Get/set the maximum cache size.
   * @property {number} size The maximum cache size.
   * @name geo.tileCache#size
   */
  Object.defineProperty(this, 'size', {
    get: function get() {
      return this._size;
    },
    set: function set(n) {
      while (this._atime.length > n) {
        this.remove(this._atime[this._atime.length - 1]);
      }
      this._size = n;
    }
  });

  /**
   * Get the current cache size.  Read only.
   * @property {number} length The current cache size.
   * @name geo.tileCache#length
   */
  Object.defineProperty(this, 'length', {
    get: function get() {
      return this._atime.length;
    }
  });

  /**
   * Get the position of the tile in the access queue.
   *
   * @param {string} hash The tile's hash value.
   * @returns {number} The position in the queue or -1.
   */
  this._access = function (hash) {
    return this._atime.indexOf(hash);
  };

  /**
   * Remove a tile from the cache.
   *
   * @param {string|geo.tile} tile The tile or its hash.
   * @returns {booliean} `true` if a tile was removed.
   */
  this.remove = function (tile) {
    var hash = typeof tile === 'string' ? tile : tile.toString();

    // if the tile is not in the cache
    if (!(hash in this._cache)) {
      return false;
    }

    // Remove the tile from the access queue
    this._atime.splice(this._access(hash), 1);

    // Remove the tile from the cache
    delete this._cache[hash];
    return true;
  };

  /**
   * Remove all tiles from the cache.
   *
   * @returns {this}
   */
  this.clear = function () {
    this._cache = {}; // The hash -> tile mapping
    this._atime = []; // The access queue (the hashes are stored)
    return this;
  };

  /**
   * Get a tile from the cache if it exists, otherwise return `null`.  This
   * method also moves the tile to the front of the access queue.
   *
   * @param {string|geo.tile} hash The tile or the tile hash value.
   * @param {boolean} [noMove] If truthy, don't move the tile to the front of
   *   the access queue.
   * @returns {geo.tile|null}
   */
  this.get = function (hash, noMove) {
    hash = typeof hash === 'string' ? hash : hash.toString();
    if (!(hash in this._cache)) {
      return null;
    }

    if (!noMove) {
      this._atime.splice(this._access(hash), 1);
      this._atime.unshift(hash);
    }
    return this._cache[hash];
  };

  /**
   * Add a tile to the cache.
   *
   * @param {geo.tile} tile The tile to add.
   * @param {function} removeFunc If specified and tiles must be purged from
   *      the cache, call this function on each tile before purging.
   * @param {boolean} noPurge if true, don't purge tiles.
   */
  this.add = function (tile, removeFunc, noPurge) {
    // remove any existing tiles with the same hash
    this.remove(tile);
    var hash = tile.toString();

    // add the tile
    this._cache[hash] = tile;
    this._atime.unshift(hash);

    if (!noPurge) {
      this.purge(removeFunc);
    }
  };

  /**
   * Purge tiles from the cache if it is full.
   *
   * @param {function} removeFunc If specified and tiles must be purged from
   *      the cache, call this function on each tile before purging.
   */
  this.purge = function (removeFunc) {
    var hash;
    while (this._atime.length > this.size) {
      hash = this._atime.pop();
      var tile = this._cache[hash];
      if (removeFunc) {
        removeFunc(tile);
      }
      delete this._cache[hash];
    }
  };

  this.clear();
  return this;
};

module.exports = tileCache;

/***/ }),
/* 129 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var $ = __webpack_require__(2);
var inherit = __webpack_require__(0);
var feature = __webpack_require__(8);

/**
 * Create a new instance of class pathFeature
 *
 * @class
 * @alias geo.pathFeature
 * @extends geo.feature
 * @returns {geo.pathFeature}
 */
var pathFeature = function pathFeature(arg) {
  'use strict';

  if (!(this instanceof pathFeature)) {
    return new pathFeature(arg);
  }
  arg = arg || {};
  feature.call(this, arg);

  /**
   * @private
   */
  var m_this = this,
      m_position = arg.position === undefined ? [] : arg.position,
      s_init = this._init;

  /**
   * Get/Set positions
   *
   * @returns {geo.pathFeature}
   */
  this.position = function (val) {
    if (val === undefined) {
      return m_position;
    }
    // Copy incoming array of positions
    m_position = val;
    m_this.dataTime().modified();
    m_this.modified();
    return m_this;
  };

  /**
   * Initialize
   */
  this._init = function (arg) {
    s_init.call(m_this, arg);

    var defaultStyle = $.extend({}, {
      'strokeWidth': function strokeWidth() {
        return 1;
      },
      'strokeColor': function strokeColor() {
        return { r: 1.0, g: 1.0, b: 1.0 };
      }
    }, arg.style === undefined ? {} : arg.style);

    m_this.style(defaultStyle);

    if (m_position) {
      m_this.dataTime().modified();
    }
  };

  this._init(arg);
  return this;
};

inherit(pathFeature, feature);
module.exports = pathFeature;

/***/ }),
/* 130 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var $ = __webpack_require__(2);
var inherit = __webpack_require__(0);
var feature = __webpack_require__(8);
var geo_event = __webpack_require__(6);
var util = __webpack_require__(3);

/**
 * Create a new instance of class imagemapFeature
 *
 * @class
 * @alias geo.pixelmapFeature
 * @param {Object} arg Options object
 * @extends geo.feature
 * @param {Object|Function|HTMLImageElement} [url] URL of a pixel map or an
 *   HTML Image element.  The rgb data is interpretted as an index of the form
 *   0xbbggrr.  The alpha channel is ignored.
 * @param {Object|Function} [color] The color that should be used for each data
 *   element.  Data elements correspond to the indices in the pixel map.  If an
 *   index is larger than the number of data elements, it will be transparent.
 *   If there is more data than there are indices, it is ignored.
 * @param {Object|Function} [position] Position of the image.  Default is
 *   (data).  The position is an Object which specifies the corners of the
 *   quad: ll, lr, ur, ul.  At least two opposite corners must be specified.
 *   The corners do not have to physically correspond to the order specified,
 *   but rather correspond to that part of the image map.  If a corner is
 *   unspecified, it will use the x coordinate from one adjacent corner, the y
 *   coordinate from the other adjacent corner, and the average z value of
 *   those two corners.  For instance, if ul is unspecified, it is
 *   {x: ll.x, y: ur.y}.  Note that each quad is rendered as a pair of
 *   triangles: (ll, lr, ul) and (ur, ul, lr).  Nothing special is done for
 *   quads that are not convex or quads that have substantially different
 *   transformations for those two triangles.
 * @returns {geo.pixelmapFeature}
 */

var pixelmapFeature = function pixelmapFeature(arg) {
  'use strict';

  if (!(this instanceof pixelmapFeature)) {
    return new pixelmapFeature(arg);
  }
  arg = arg || {};
  feature.call(this, arg);

  /**
   * @private
   */
  var m_this = this,
      m_quadFeature,
      m_srcImage,
      m_info,
      s_update = this._update,
      s_init = this._init,
      s_exit = this._exit;

  /**
   * Get/Set position accessor
   *
   * @returns {geo.pixelmap}
   */
  this.position = function (val) {
    if (val === undefined) {
      return m_this.style('position');
    } else if (val !== m_this.style('position')) {
      m_this.style('position', val);
      m_this.dataTime().modified();
      m_this.modified();
    }
    return m_this;
  };

  /**
   * Get/Set url accessor
   *
   * @returns {geo.pixelmap}
   */
  this.url = function (val) {
    if (val === undefined) {
      return m_this.style('url');
    } else if (val !== m_this.style('url')) {
      m_srcImage = m_info = undefined;
      m_this.style('url', val);
      m_this.dataTime().modified();
      m_this.modified();
    }
    return m_this;
  };

  /**
   * Get the maximum index value from the pixelmap.  This is a value present in
   * the pixelmap.
   *
   * @returns {geo.pixelmap}
   */
  this.maxIndex = function () {
    if (m_info) {
      /* This isn't just m_info.mappedColors.length - 1, since there
       * may be more data than actual indices. */
      if (m_info.maxIndex === undefined) {
        m_info.maxIndex = 0;
        for (var idx in m_info.mappedColors) {
          if (m_info.mappedColors.hasOwnProperty(idx)) {
            m_info.maxIndex = Math.max(m_info.maxIndex, idx);
          }
        }
      }
      return m_info.maxIndex;
    }
  };

  /**
   * Get/Set color accessor
   *
   * @returns {geo.pixelmap}
   */
  this.color = function (val) {
    if (val === undefined) {
      return m_this.style('color');
    } else if (val !== m_this.style('color')) {
      m_this.style('color', val);
      m_this.dataTime().modified();
      m_this.modified();
    }
    return m_this;
  };

  /**
   * If the specified coordinates are in the rendered quad, use the basis
   * information from the quad to determine the pixelmap index value so that it
   * can be included in the found results.
   *
   * @param {geo.geoPosition} coordinate point to search for in map interface
   *    gcs.
   */
  this.pointSearch = function (coordinate) {
    if (m_quadFeature && m_info) {
      var result = m_quadFeature.pointSearch(coordinate);
      if (result.index.length === 1 && result.extra && result.extra[result.index[0]].basis) {
        var basis = result.extra[result.index[0]].basis,
            x,
            y,
            idx;
        x = Math.floor(basis.x * m_info.width);
        y = Math.floor(basis.y * m_info.height);
        if (x >= 0 && x < m_info.width && y >= 0 && y < m_info.height) {
          idx = m_info.indices[y * m_info.width + x];
          result = {
            index: [idx],
            found: [m_this.data()[idx]]
          };
          return result;
        }
      }
    }
    return { index: [], found: [] };
  };

  /**
   * Build
   */
  this._build = function () {
    /* Set the build time at the start of the call.  A build can result in
     * drawing a quad, which can trigger a full layer update, which in tern
     * checks if this feature is built.  Setting the build time avoid calling
     * this a second time. */
    m_this.buildTime().modified();
    if (!m_srcImage) {
      var src = this.style.get('url')();
      if (util.isReadyImage(src)) {
        /* we have an already loaded image, so we can just use it. */
        m_srcImage = src;
        this._computePixelmap();
      } else if (src) {
        var defer = $.Deferred(),
            prev_onload,
            prev_onerror;
        if (src instanceof Image) {
          /* we have an unloaded image.  Hook to the load and error callbacks
           * so that when it is loaded we can use it. */
          m_srcImage = src;
          prev_onload = src.onload;
          prev_onerror = src.onerror;
        } else {
          /* we were given a url, so construct a new image */
          m_srcImage = new Image();
          // Only set the crossOrigin parameter if this is going across origins.
          if (src.indexOf(':') >= 0 && src.indexOf('/') === src.indexOf(':') + 1) {
            m_srcImage.crossOrigin = this.style.get('crossDomain')() || 'anonymous';
          }
        }
        m_srcImage.onload = function () {
          if (prev_onload) {
            prev_onload.apply(this, arguments);
          }
          /* Only use this image if our pixelmap hasn't changed since we
           * attached our handler */
          if (m_this.style.get('url')() === src) {
            m_info = undefined;
            m_this._computePixelmap();
          }
          defer.resolve();
        };
        m_srcImage.onerror = function () {
          if (prev_onerror) {
            prev_onerror.apply(this, arguments);
          }
          defer.reject();
        };
        defer.promise(this);
        this.layer().addPromise(this);
        if (!(src instanceof Image)) {
          m_srcImage.src = src;
        }
      }
    } else if (m_info) {
      this._computePixelmap();
    }
    return m_this;
  };

  /**
   * Compute information for this pixelmap image.  It is wasterful to call this
   * if the pixelmap has already been prepared (it is invalidated by a change
   * in the image).
   */
  this._preparePixelmap = function () {
    var i, idx, pixelData;

    if (!util.isReadyImage(m_srcImage)) {
      return;
    }
    m_info = {
      width: m_srcImage.naturalWidth,
      height: m_srcImage.naturalHeight,
      canvas: document.createElement('canvas'),
      updateIdx: {}
    };

    m_info.canvas.width = m_info.width;
    m_info.canvas.height = m_info.height;
    m_info.context = m_info.canvas.getContext('2d');

    m_info.context.drawImage(m_srcImage, 0, 0);
    m_info.imageData = m_info.context.getImageData(0, 0, m_info.canvas.width, m_info.canvas.height);
    pixelData = m_info.imageData.data;
    m_info.indices = new Array(pixelData.length / 4);
    m_info.area = pixelData.length / 4;

    m_info.mappedColors = {};
    for (i = 0; i < pixelData.length; i += 4) {
      idx = pixelData[i] + (pixelData[i + 1] << 8) + (pixelData[i + 2] << 16);
      m_info.indices[i / 4] = idx;
      if (!m_info.mappedColors[idx]) {
        m_info.mappedColors[idx] = { first: i / 4 };
      }
      m_info.mappedColors[idx].last = i / 4;
    }
    return m_info;
  };

  /**
   * Given the loaded pixelmap image, create a canvas the size of the image.
   * Compute a color for each distinct index and recolor the canvas based on
   * thise colors, then draw the resultant image as a quad.
   *
   * @fires geo.event.pixelmap.prepared
   */
  this._computePixelmap = function () {
    var data = m_this.data() || [],
        colorFunc = m_this.style.get('color'),
        i,
        idx,
        lastidx,
        color,
        pixelData,
        indices,
        mappedColors,
        updateFirst,
        updateLast = -1,
        update,
        prepared;

    if (!m_info) {
      if (!m_this._preparePixelmap()) {
        return;
      }
      prepared = true;
    }
    mappedColors = m_info.mappedColors;
    updateFirst = m_info.area;
    for (idx in mappedColors) {
      if (mappedColors.hasOwnProperty(idx)) {
        color = colorFunc(data[idx], +idx) || {};
        color = [(color.r || 0) * 255, (color.g || 0) * 255, (color.b || 0) * 255, color.a === undefined ? 255 : color.a * 255];
        mappedColors[idx].update = !mappedColors[idx].color || mappedColors[idx].color[0] !== color[0] || mappedColors[idx].color[1] !== color[1] || mappedColors[idx].color[2] !== color[2] || mappedColors[idx].color[3] !== color[3];
        if (mappedColors[idx].update) {
          mappedColors[idx].color = color;
          updateFirst = Math.min(mappedColors[idx].first, updateFirst);
          updateLast = Math.max(mappedColors[idx].last, updateLast);
        }
      }
    }
    /* If nothing was updated, we are done */
    if (updateFirst >= updateLast) {
      return;
    }
    /* Update only the extent that has changed */
    pixelData = m_info.imageData.data;
    indices = m_info.indices;
    for (i = updateFirst; i <= updateLast; i += 1) {
      idx = indices[i];
      if (idx !== lastidx) {
        lastidx = idx;
        color = mappedColors[idx].color;
        update = mappedColors[idx].update;
      }
      if (update) {
        pixelData[i * 4] = color[0];
        pixelData[i * 4 + 1] = color[1];
        pixelData[i * 4 + 2] = color[2];
        pixelData[i * 4 + 3] = color[3];
      }
    }
    /* Place the updated area into the canvas */
    m_info.context.putImageData(m_info.imageData, 0, 0, 0, Math.floor(updateFirst / m_info.width), m_info.width, Math.ceil((updateLast + 1) / m_info.width));

    /* If we haven't made a quad feature, make one now.  The quad feature needs
     * to have the canvas capability. */
    if (!m_quadFeature) {
      m_quadFeature = m_this.layer().createFeature('quad', {
        selectionAPI: false,
        gcs: m_this.gcs(),
        visible: m_this.visible(undefined, true)
      });
      m_this.dependentFeatures([m_quadFeature]);
      m_quadFeature.style({
        image: m_info.canvas,
        position: m_this.style.get('position') }).data([{}]).draw();
    }
    /* If we prepared the pixelmap and rendered it, send a prepared event */
    if (prepared) {
      m_this.geoTrigger(geo_event.pixelmap.prepared, {
        pixelmap: m_this
      });
    }
  };

  /**
   * Update
   */
  this._update = function () {
    s_update.call(m_this);
    if (m_this.buildTime().getMTime() <= m_this.dataTime().getMTime() || m_this.updateTime().getMTime() < m_this.getMTime()) {
      m_this._build();
    }

    m_this.updateTime().modified();
    return m_this;
  };

  /**
   * Destroy
   * @memberof geo.pixelmapFeature
   */
  this._exit = function (abc) {
    if (m_quadFeature && m_this.layer()) {
      m_this.layer().deleteFeature(m_quadFeature);
      m_quadFeature = null;
      m_this.dependentFeatures([]);
    }
    s_exit();
  };

  /**
   * Initialize
   */
  this._init = function (arg) {
    arg = arg || {};
    s_init.call(m_this, arg);

    var style = $.extend({}, {
      color: function color(d, idx) {
        return {
          r: (idx & 0xFF) / 255,
          g: (idx >> 8 & 0xFF) / 255,
          b: (idx >> 16 & 0xFF) / 255,
          a: 1
        };
      },
      position: function position(d) {
        return d;
      }
    }, arg.style === undefined ? {} : arg.style);
    if (arg.position !== undefined) {
      style.position = arg.position;
    }
    if (arg.url !== undefined) {
      style.url = arg.url;
    }
    if (arg.color !== undefined) {
      style.color = arg.color;
    }
    m_this.style(style);
    m_this.dataTime().modified();
  };

  return this;
};

/**
 * Create a pixelmapFeature from an object.
 *
 * @see {@link geo.feature.create}
 * @param {geo.layer} layer The layer to add the feature to
 * @param {geo.pixelmapFeature.spec} spec The object specification
 * @returns {geo.pixelmapFeature|null}
 */
pixelmapFeature.create = function (layer, spec) {
  'use strict';

  spec = spec || {};
  spec.type = 'pixelmap';
  return feature.create(layer, spec);
};

pixelmapFeature.capabilities = {
  /* core feature name -- support in any manner */
  feature: 'pixelmap'
};

inherit(pixelmapFeature, feature);
module.exports = pixelmapFeature;

/***/ }),
/* 131 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var inherit = __webpack_require__(0);
var feature = __webpack_require__(8);

/**
 * Create a new instance of class vectorFeature
 *
 * @class
 * @alias geo.vectorFeature
 * @extends geo.feature
 * @returns {geo.vectorFeature}
 */
var vectorFeature = function vectorFeature(arg) {
  'use strict';

  if (!(this instanceof vectorFeature)) {
    return new vectorFeature(arg);
  }

  var $ = __webpack_require__(2);

  arg = arg || {};
  feature.call(this, arg);

  /**
   * @private
   */
  var m_this = this,
      s_init = this._init,
      s_style = this.style;

  /**
   * Get or set the accessor for the origin of the vector.  This is the point
   * that the vector base resides at.  Defaults to (0, 0, 0).
   * @param {geo.accessor|geo.geoPosition} [accessor] The origin accessor
   */
  this.origin = function (val) {
    if (val === undefined) {
      return s_style('origin');
    } else {
      s_style('origin', val);
      m_this.dataTime().modified();
      m_this.modified();
    }
    return m_this;
  };

  /**
   * Get or set the accessor for the displacement (coordinates) of the vector.
   * @param {geo.accessor|geo.geoPosition} [accessor] The accessor
   */
  this.delta = function (val) {
    if (val === undefined) {
      return s_style('delta');
    } else {
      s_style('delta', val);
      m_this.dataTime().modified();
      m_this.modified();
    }
    return m_this;
  };

  /**
   * Initialize
   * @protected
   */
  this._init = function (arg) {
    s_init.call(m_this, arg);

    var defaultStyle = $.extend({}, {
      strokeColor: 'black',
      strokeWidth: 2.0,
      strokeOpacity: 1.0,
      originStyle: 'none',
      endStyle: 'arrow',
      origin: { x: 0, y: 0, z: 0 },
      delta: function delta(d) {
        return d;
      },
      scale: null // size scaling factor (null -> renderer decides)
    }, arg.style === undefined ? {} : arg.style);

    if (arg.origin !== undefined) {
      defaultStyle.origin = arg.origin;
    }

    m_this.style(defaultStyle);
    m_this.dataTime().modified();
  };
};

inherit(vectorFeature, feature);
module.exports = vectorFeature;

/***/ }),
/* 132 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var widget = __webpack_require__(56);
var inherit = __webpack_require__(0);
var registerWidget = __webpack_require__(1).registerWidget;

/**
 * Create a new instance of class domWidget.
 *
 * @class
 * @alias geo.gui.domWidget
 * @extends geo.gui.widget
 * @param {object} arg
 * @param {geo.widget} [parent] A parent widget for this widget.
 * @param {string} [el='div'] The type of DOM element to create.
 * @returns {geo.domWidget}
 */
var domWidget = function domWidget(arg) {
  'use strict';

  if (!(this instanceof domWidget)) {
    return new domWidget(arg);
  }

  widget.call(this, arg);

  var m_this = this,
      m_default_canvas = 'div';

  /**
   * Initializes DOM Widget.
   * Sets the canvas for the widget, does parent/child relationship management,
   * appends it to it's parent and handles any positioning logic.
   *
   * @returns {this}
   */
  this._init = function () {
    if (arg.hasOwnProperty('parent')) {
      arg.parent.addChild(m_this);
    }

    m_this._createCanvas();
    m_this._appendCanvasToParent();

    m_this.canvas().addEventListener('mousedown', function (e) {
      e.stopPropagation();
    });

    m_this.reposition();
    return m_this;
  };

  /**
   * Creates the widget canvas.  This is a DOM element (`arg.el` or a div).
   */
  this._createCanvas = function () {
    m_this.canvas(document.createElement(arg.el || m_default_canvas));
  };

  return this;
};

inherit(domWidget, widget);

registerWidget('dom', 'dom', domWidget);
module.exports = domWidget;

/***/ }),
/* 133 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


// License headers that will be preserved in distributed bundles.
/**
 * GeoJS
 * @copyright 2013-2018, Kitware, Inc.
 * @license Apache-2.0
 * @namespace geo
 */
/*
 * Bundled with the following libraries:
 *
 * vgl
 * @copyright 2014-2016, Kitware, Inc.
 * @license Apache-2.0
 *
 * Proj4js
 * @copyright 2014, Mike Adair, Richard Greenwood, Didier Richard, Stephen Irons, Olivier Terral and Calvin Metcalf
 * @license MIT
 *
 * gl-matrix
 * @copyright 2015, Brandon Jones, Colin MacKenzie IV
 * @license MIT
 *
 * JQuery
 * @copyright jQuery Foundation and other contributors
 * @license MIT
 *
 * earcut
 * @copyright 2016, Mapbox
 * @license ISC
 *
 * kdbush
 * @copyright 2017, Vladimir Agafonkin
 * @license ISC
 */

var $ = __webpack_require__(2);

__webpack_require__(134);

module.exports = $.extend({
  annotation: __webpack_require__(57),
  annotationLayer: __webpack_require__(282),
  camera: __webpack_require__(49),
  choroplethFeature: __webpack_require__(118),
  contourFeature: __webpack_require__(120),
  domRenderer: __webpack_require__(285),
  event: __webpack_require__(6),
  feature: __webpack_require__(8),
  featureLayer: __webpack_require__(35),
  fetchQueue: __webpack_require__(121),
  fileReader: __webpack_require__(122),
  geo_action: __webpack_require__(39),
  graphFeature: __webpack_require__(123),
  heatmapFeature: __webpack_require__(124),
  imageTile: __webpack_require__(125),
  isolineFeature: __webpack_require__(55),
  geojsonReader: __webpack_require__(286),
  layer: __webpack_require__(24),
  lineFeature: __webpack_require__(23),
  map: __webpack_require__(287),
  mapInteractor: __webpack_require__(51),
  meshFeature: __webpack_require__(53),
  object: __webpack_require__(12),
  osmLayer: __webpack_require__(288),
  pathFeature: __webpack_require__(129),
  pointFeature: __webpack_require__(34),
  polygonFeature: __webpack_require__(50),
  quadFeature: __webpack_require__(17),
  pixelmapFeature: __webpack_require__(130),
  renderer: __webpack_require__(22),
  sceneObject: __webpack_require__(13),
  textFeature: __webpack_require__(25),
  tile: __webpack_require__(54),
  tileCache: __webpack_require__(128),
  tileLayer: __webpack_require__(127),
  timestamp: __webpack_require__(10),
  transform: __webpack_require__(7),
  typedef: __webpack_require__(289),
  vectorFeature: __webpack_require__(131),
  inherit: __webpack_require__(0),
  version: __webpack_require__(290),
  sha: __webpack_require__(291),

  util: __webpack_require__(3),
  jQuery: $,
  d3: __webpack_require__(292),
  gl: __webpack_require__(300),
  canvas: __webpack_require__(310),
  gui: __webpack_require__(319)
}, __webpack_require__(1));

if (window && !window.$) {
  window.$ = $;
}
if (window && !window.jQuery) {
  window.jQuery = $;
}

/***/ }),
/* 134 */
/***/ (function(module, exports, __webpack_require__) {


var content = __webpack_require__(135);

if(typeof content === 'string') content = [[module.i, content, '']];

var transform;
var insertInto;



var options = {"hmr":true}

options.transform = transform
options.insertInto = undefined;

var update = __webpack_require__(38)(content, options);

if(content.locals) module.exports = content.locals;

if(false) {
	module.hot.accept("!!../node_modules/css-loader/index.js!../node_modules/stylus-loader/index.js!./main.styl", function() {
		var newContent = require("!!../node_modules/css-loader/index.js!../node_modules/stylus-loader/index.js!./main.styl");

		if(typeof newContent === 'string') newContent = [[module.id, newContent, '']];

		var locals = (function(a, b) {
			var key, idx = 0;

			for(key in a) {
				if(!b || a[key] !== b[key]) return false;
				idx++;
			}

			for(key in b) idx--;

			return idx === 0;
		}(content.locals, newContent.locals));

		if(!locals) throw new Error('Aborting CSS HMR due to changed css-modules locals.');

		update(newContent);
	});

	module.hot.dispose(function() { update(); });
}

/***/ }),
/* 135 */
/***/ (function(module, exports, __webpack_require__) {

exports = module.exports = __webpack_require__(37)(false);
// imports


// module
exports.push([module.i, ".geojs-map {\n  position: relative;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  -ms-user-select: none;\n  user-select: none;\n}\n.geojs-map .geo-attribution {\n  position: absolute;\n  right: 0;\n  bottom: 0;\n  padding-right: 5px;\n  cursor: auto;\n  font: 11px/1.5 \"Helvetica Neue\", Arial, Helvetica, sans-serif;\n  z-index: 1001;\n  background: rgba(255,255,255,0.7);\n  clear: both;\n  display: block;\n  pointer-events: auto;\n}\n.geojs-map .geo-attribution .geo-attribution-layer {\n  padding-left: 5px;\n}\n.geojs-map .canvas-canvas {\n  display: block;\n  -webkit-transform-origin: 0px 0px;\n  -moz-transform-origin: 0px 0px;\n  -o-transform-origin: 0px 0px;\n  -ms-transform-origin: 0px 0px;\n  transform-origin: 0px 0px;\n}\n.geojs-map .webgl-canvas {\n  display: block;\n}\n.geojs-map .geojs-layer {\n  position: absolute;\n  width: 100%;\n  height: 100%;\n  left: 0;\n  top: 0;\n  pointer-events: none;\n}\n.geojs-map .geojs-layer.active > * {\n  pointer-events: auto;\n}\n.geojs-map .geo-tile-layer {\n  -webkit-transform-origin: 0px 0px;\n  -moz-transform-origin: 0px 0px;\n  -o-transform-origin: 0px 0px;\n  -ms-transform-origin: 0px 0px;\n  transform-origin: 0px 0px;\n  line-height: 0;\n  font-size: 0;\n}\n.geojs-map.annotation-input {\n  cursor: crosshair;\n}\n.geojs-map.highlight-focus:after {\n  content: \"\";\n  display: block;\n  position: absolute;\n  -webkit-box-sizing: border-box;\n  -moz-box-sizing: border-box;\n  box-sizing: border-box;\n  left: 0px;\n  top: 0px;\n  right: 0px;\n  bottom: 0px;\n  border: 3px solid Highlight;\n  opacity: 1;\n  -ms-filter: none;\n  filter: none;\n  -webkit-transition: opacity 0s;\n  -moz-transition: opacity 0s;\n  -o-transition: opacity 0s;\n  -ms-transition: opacity 0s;\n  transition: opacity 0s;\n  visibility: hidden;\n}\n.geojs-map.highlight-focus:focus:after {\n  visibility: visible;\n  -webkit-transition: opacity 2.5s ease-in;\n  -moz-transition: opacity 2.5s ease-in;\n  -o-transition: opacity 2.5s ease-in;\n  -ms-transition: opacity 2.5s ease-in;\n  transition: opacity 2.5s ease-in;\n  opacity: 0;\n  -ms-filter: \"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)\";\n  filter: alpha(opacity=0);\n}\n.geo-tile-container {\n  position: absolute;\n}\n.geo-tile-container.crop {\n  overflow: hidden;\n}\n", ""]);

// exports


/***/ }),
/* 136 */
/***/ (function(module, exports) {


/**
 * When source maps are enabled, `style-loader` uses a link element with a data-uri to
 * embed the css on the page. This breaks all relative urls because now they are relative to a
 * bundle instead of the current page.
 *
 * One solution is to only use full urls, but that may be impossible.
 *
 * Instead, this function "fixes" the relative urls to be absolute according to the current page location.
 *
 * A rudimentary test suite is located at `test/fixUrls.js` and can be run via the `npm test` command.
 *
 */

module.exports = function (css) {
  // get current location
  var location = typeof window !== "undefined" && window.location;

  if (!location) {
    throw new Error("fixUrls requires window.location");
  }

	// blank or null?
	if (!css || typeof css !== "string") {
	  return css;
  }

  var baseUrl = location.protocol + "//" + location.host;
  var currentDir = baseUrl + location.pathname.replace(/\/[^\/]*$/, "/");

	// convert each url(...)
	/*
	This regular expression is just a way to recursively match brackets within
	a string.

	 /url\s*\(  = Match on the word "url" with any whitespace after it and then a parens
	   (  = Start a capturing group
	     (?:  = Start a non-capturing group
	         [^)(]  = Match anything that isn't a parentheses
	         |  = OR
	         \(  = Match a start parentheses
	             (?:  = Start another non-capturing groups
	                 [^)(]+  = Match anything that isn't a parentheses
	                 |  = OR
	                 \(  = Match a start parentheses
	                     [^)(]*  = Match anything that isn't a parentheses
	                 \)  = Match a end parentheses
	             )  = End Group
              *\) = Match anything and then a close parens
          )  = Close non-capturing group
          *  = Match anything
       )  = Close capturing group
	 \)  = Match a close parens

	 /gi  = Get all matches, not the first.  Be case insensitive.
	 */
	var fixedCss = css.replace(/url\s*\(((?:[^)(]|\((?:[^)(]+|\([^)(]*\))*\))*)\)/gi, function(fullMatch, origUrl) {
		// strip quotes (if they exist)
		var unquotedOrigUrl = origUrl
			.trim()
			.replace(/^"(.*)"$/, function(o, $1){ return $1; })
			.replace(/^'(.*)'$/, function(o, $1){ return $1; });

		// already a full url? no change
		if (/^(#|data:|http:\/\/|https:\/\/|file:\/\/\/|\s*$)/i.test(unquotedOrigUrl)) {
		  return fullMatch;
		}

		// convert the url to a full url
		var newUrl;

		if (unquotedOrigUrl.indexOf("//") === 0) {
		  	//TODO: should we add protocol?
			newUrl = unquotedOrigUrl;
		} else if (unquotedOrigUrl.indexOf("/") === 0) {
			// path should be relative to the base url
			newUrl = baseUrl + unquotedOrigUrl; // already starts with '/'
		} else {
			// path should be relative to current directory
			newUrl = currentDir + unquotedOrigUrl.replace(/^\.\//, ""); // Strip leading './'
		}

		// send back the fixed url(...)
		return "url(" + JSON.stringify(newUrl) + ")";
	});

	// send back the fixed css
	return fixedCss;
};


/***/ }),
/* 137 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__Proj__ = __webpack_require__(40);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__transform__ = __webpack_require__(64);


var wgs84 = Object(__WEBPACK_IMPORTED_MODULE_0__Proj__["a" /* default */])('WGS84');

function transformer(from, to, coords) {
  var transformedArray, out, keys;
  if (Array.isArray(coords)) {
    transformedArray = Object(__WEBPACK_IMPORTED_MODULE_1__transform__["a" /* default */])(from, to, coords);
    if (coords.length === 3) {
      return [transformedArray.x, transformedArray.y, transformedArray.z];
    }
    else {
      return [transformedArray.x, transformedArray.y];
    }
  }
  else {
    out = Object(__WEBPACK_IMPORTED_MODULE_1__transform__["a" /* default */])(from, to, coords);
    keys = Object.keys(coords);
    if (keys.length === 2) {
      return out;
    }
    keys.forEach(function (key) {
      if (key === 'x' || key === 'y') {
        return;
      }
      out[key] = coords[key];
    });
    return out;
  }
}

function checkProj(item) {
  if (item instanceof __WEBPACK_IMPORTED_MODULE_0__Proj__["a" /* default */]) {
    return item;
  }
  if (item.oProj) {
    return item.oProj;
  }
  return Object(__WEBPACK_IMPORTED_MODULE_0__Proj__["a" /* default */])(item);
}
function proj4(fromProj, toProj, coord) {
  fromProj = checkProj(fromProj);
  var single = false;
  var obj;
  if (typeof toProj === 'undefined') {
    toProj = fromProj;
    fromProj = wgs84;
    single = true;
  }
  else if (typeof toProj.x !== 'undefined' || Array.isArray(toProj)) {
    coord = toProj;
    toProj = fromProj;
    fromProj = wgs84;
    single = true;
  }
  toProj = checkProj(toProj);
  if (coord) {
    return transformer(fromProj, toProj, coord);
  }
  else {
    obj = {
      forward: function(coords) {
        return transformer(fromProj, toProj, coords);
      },
      inverse: function(coords) {
        return transformer(toProj, fromProj, coords);
      }
    };
    if (single) {
      obj.oProj = toProj;
    }
    return obj;
  }
}
/* harmony default export */ __webpack_exports__["a"] = (proj4);


/***/ }),
/* 138 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__defs__ = __webpack_require__(59);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_wkt_parser__ = __webpack_require__(61);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__projString__ = __webpack_require__(60);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__match__ = __webpack_require__(26);




function testObj(code){
  return typeof code === 'string';
}
function testDef(code){
  return code in __WEBPACK_IMPORTED_MODULE_0__defs__["a" /* default */];
}
 var codeWords = ['PROJECTEDCRS', 'PROJCRS', 'GEOGCS','GEOCCS','PROJCS','LOCAL_CS', 'GEODCRS', 'GEODETICCRS', 'GEODETICDATUM', 'ENGCRS', 'ENGINEERINGCRS'];
function testWKT(code){
  return codeWords.some(function (word) {
    return code.indexOf(word) > -1;
  });
}
var codes = ['3857', '900913', '3785', '102113'];
function checkMercator(item) {
  var auth = Object(__WEBPACK_IMPORTED_MODULE_3__match__["a" /* default */])(item, 'authority');
  if (!auth) {
    return;
  }
  var code = Object(__WEBPACK_IMPORTED_MODULE_3__match__["a" /* default */])(auth, 'epsg');
  return code && codes.indexOf(code) > -1;
}
function checkProjStr(item) {
  var ext = Object(__WEBPACK_IMPORTED_MODULE_3__match__["a" /* default */])(item, 'extension');
  if (!ext) {
    return;
  }
  return Object(__WEBPACK_IMPORTED_MODULE_3__match__["a" /* default */])(ext, 'proj4');
}
function testProj(code){
  return code[0] === '+';
}
function parse(code){
  if (testObj(code)) {
    //check to see if this is a WKT string
    if (testDef(code)) {
      return __WEBPACK_IMPORTED_MODULE_0__defs__["a" /* default */][code];
    }
    if (testWKT(code)) {
      var out = Object(__WEBPACK_IMPORTED_MODULE_1_wkt_parser__["a" /* default */])(code);
      // test of spetial case, due to this being a very common and often malformed
      if (checkMercator(out)) {
        return __WEBPACK_IMPORTED_MODULE_0__defs__["a" /* default */]['EPSG:3857'];
      }
      var maybeProjStr = checkProjStr(out);
      if (maybeProjStr) {
        return Object(__WEBPACK_IMPORTED_MODULE_2__projString__["a" /* default */])(maybeProjStr);
      }
      return out;
    }
    if (testProj(code)) {
      return Object(__WEBPACK_IMPORTED_MODULE_2__projString__["a" /* default */])(code);
    }
  }else{
    return code;
  }
}

/* harmony default export */ __webpack_exports__["a"] = (parse);


/***/ }),
/* 139 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony default export */ __webpack_exports__["a"] = (function(defs) {
  defs('EPSG:4326', "+title=WGS 84 (long/lat) +proj=longlat +ellps=WGS84 +datum=WGS84 +units=degrees");
  defs('EPSG:4269', "+title=NAD83 (long/lat) +proj=longlat +a=6378137.0 +b=6356752.31414036 +ellps=GRS80 +datum=NAD83 +units=degrees");
  defs('EPSG:3857', "+title=WGS 84 / Pseudo-Mercator +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +no_defs");

  defs.WGS84 = defs['EPSG:4326'];
  defs['EPSG:3785'] = defs['EPSG:3857']; // maintain backward compat, official code is 3857
  defs.GOOGLE = defs['EPSG:3857'];
  defs['EPSG:900913'] = defs['EPSG:3857'];
  defs['EPSG:102113'] = defs['EPSG:3857'];
});


/***/ }),
/* 140 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return exports; });
var exports = {};


exports.greenwich = 0.0; //"0dE",
exports.lisbon = -9.131906111111; //"9d07'54.862\"W",
exports.paris = 2.337229166667; //"2d20'14.025\"E",
exports.bogota = -74.080916666667; //"74d04'51.3\"W",
exports.madrid = -3.687938888889; //"3d41'16.58\"W",
exports.rome = 12.452333333333; //"12d27'8.4\"E",
exports.bern = 7.439583333333; //"7d26'22.5\"E",
exports.jakarta = 106.807719444444; //"106d48'27.79\"E",
exports.ferro = -17.666666666667; //"17d40'W",
exports.brussels = 4.367975; //"4d22'4.71\"E",
exports.stockholm = 18.058277777778; //"18d3'29.8\"E",
exports.athens = 23.7163375; //"23d42'58.815\"E",
exports.oslo = 10.722916666667; //"10d43'22.5\"E"


/***/ }),
/* 141 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony default export */ __webpack_exports__["a"] = ({
  ft: {to_meter: 0.3048},
  'us-ft': {to_meter: 1200 / 3937}
});


/***/ }),
/* 142 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony default export */ __webpack_exports__["a"] = (parseString);

var NEUTRAL = 1;
var KEYWORD = 2;
var NUMBER = 3;
var QUOTED = 4;
var AFTERQUOTE = 5;
var ENDED = -1;
var whitespace = /\s/;
var latin = /[A-Za-z]/;
var keyword = /[A-Za-z84]/;
var endThings = /[,\]]/;
var digets = /[\d\.E\-\+]/;
// const ignoredChar = /[\s_\-\/\(\)]/g;
function Parser(text) {
  if (typeof text !== 'string') {
    throw new Error('not a string');
  }
  this.text = text.trim();
  this.level = 0;
  this.place = 0;
  this.root = null;
  this.stack = [];
  this.currentObject = null;
  this.state = NEUTRAL;
}
Parser.prototype.readCharicter = function() {
  var char = this.text[this.place++];
  if (this.state !== QUOTED) {
    while (whitespace.test(char)) {
      if (this.place >= this.text.length) {
        return;
      }
      char = this.text[this.place++];
    }
  }
  switch (this.state) {
    case NEUTRAL:
      return this.neutral(char);
    case KEYWORD:
      return this.keyword(char)
    case QUOTED:
      return this.quoted(char);
    case AFTERQUOTE:
      return this.afterquote(char);
    case NUMBER:
      return this.number(char);
    case ENDED:
      return;
  }
};
Parser.prototype.afterquote = function(char) {
  if (char === '"') {
    this.word += '"';
    this.state = QUOTED;
    return;
  }
  if (endThings.test(char)) {
    this.word = this.word.trim();
    this.afterItem(char);
    return;
  }
  throw new Error('havn\'t handled "' +char + '" in afterquote yet, index ' + this.place);
};
Parser.prototype.afterItem = function(char) {
  if (char === ',') {
    if (this.word !== null) {
      this.currentObject.push(this.word);
    }
    this.word = null;
    this.state = NEUTRAL;
    return;
  }
  if (char === ']') {
    this.level--;
    if (this.word !== null) {
      this.currentObject.push(this.word);
      this.word = null;
    }
    this.state = NEUTRAL;
    this.currentObject = this.stack.pop();
    if (!this.currentObject) {
      this.state = ENDED;
    }

    return;
  }
};
Parser.prototype.number = function(char) {
  if (digets.test(char)) {
    this.word += char;
    return;
  }
  if (endThings.test(char)) {
    this.word = parseFloat(this.word);
    this.afterItem(char);
    return;
  }
  throw new Error('havn\'t handled "' +char + '" in number yet, index ' + this.place);
};
Parser.prototype.quoted = function(char) {
  if (char === '"') {
    this.state = AFTERQUOTE;
    return;
  }
  this.word += char;
  return;
};
Parser.prototype.keyword = function(char) {
  if (keyword.test(char)) {
    this.word += char;
    return;
  }
  if (char === '[') {
    var newObjects = [];
    newObjects.push(this.word);
    this.level++;
    if (this.root === null) {
      this.root = newObjects;
    } else {
      this.currentObject.push(newObjects);
    }
    this.stack.push(this.currentObject);
    this.currentObject = newObjects;
    this.state = NEUTRAL;
    return;
  }
  if (endThings.test(char)) {
    this.afterItem(char);
    return;
  }
  throw new Error('havn\'t handled "' +char + '" in keyword yet, index ' + this.place);
};
Parser.prototype.neutral = function(char) {
  if (latin.test(char)) {
    this.word = char;
    this.state = KEYWORD;
    return;
  }
  if (char === '"') {
    this.word = '';
    this.state = QUOTED;
    return;
  }
  if (digets.test(char)) {
    this.word = char;
    this.state = NUMBER;
    return;
  }
  if (endThings.test(char)) {
    this.afterItem(char);
    return;
  }
  throw new Error('havn\'t handled "' +char + '" in neutral yet, index ' + this.place);
};
Parser.prototype.output = function() {
  while (this.place < this.text.length) {
    this.readCharicter();
  }
  if (this.state === ENDED) {
    return this.root;
  }
  throw new Error('unable to parse string "' +this.text + '". State is ' + this.state);
};

function parseString(txt) {
  var parser = new Parser(txt);
  return parser.output();
}


/***/ }),
/* 143 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony export (immutable) */ __webpack_exports__["a"] = sExpr;


function mapit(obj, key, value) {
  if (Array.isArray(key)) {
    value.unshift(key);
    key = null;
  }
  var thing = key ? {} : obj;

  var out = value.reduce(function(newObj, item) {
    sExpr(item, newObj);
    return newObj
  }, thing);
  if (key) {
    obj[key] = out;
  }
}

function sExpr(v, obj) {
  if (!Array.isArray(v)) {
    obj[v] = true;
    return;
  }
  var key = v.shift();
  if (key === 'PARAMETER') {
    key = v.shift();
  }
  if (v.length === 1) {
    if (Array.isArray(v[0])) {
      obj[key] = {};
      sExpr(v[0], obj[key]);
      return;
    }
    obj[key] = v[0];
    return;
  }
  if (!v.length) {
    obj[key] = true;
    return;
  }
  if (key === 'TOWGS84') {
    obj[key] = v;
    return;
  }
  if (!Array.isArray(key)) {
    obj[key] = {};
  }

  var i;
  switch (key) {
    case 'UNIT':
    case 'PRIMEM':
    case 'VERT_DATUM':
      obj[key] = {
        name: v[0].toLowerCase(),
        convert: v[1]
      };
      if (v.length === 3) {
        sExpr(v[2], obj[key]);
      }
      return;
    case 'SPHEROID':
    case 'ELLIPSOID':
      obj[key] = {
        name: v[0],
        a: v[1],
        rf: v[2]
      };
      if (v.length === 4) {
        sExpr(v[3], obj[key]);
      }
      return;
    case 'PROJECTEDCRS':
    case 'PROJCRS':
    case 'GEOGCS':
    case 'GEOCCS':
    case 'PROJCS':
    case 'LOCAL_CS':
    case 'GEODCRS':
    case 'GEODETICCRS':
    case 'GEODETICDATUM':
    case 'EDATUM':
    case 'ENGINEERINGDATUM':
    case 'VERT_CS':
    case 'VERTCRS':
    case 'VERTICALCRS':
    case 'COMPD_CS':
    case 'COMPOUNDCRS':
    case 'ENGINEERINGCRS':
    case 'ENGCRS':
    case 'FITTED_CS':
    case 'LOCAL_DATUM':
    case 'DATUM':
      v[0] = ['name', v[0]];
      mapit(obj, key, v);
      return;
    default:
      i = -1;
      while (++i < v.length) {
        if (!Array.isArray(v[i])) {
          return sExpr(v, obj[key]);
        }
      }
      return mapit(obj, key, v);
  }
}


/***/ }),
/* 144 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony default export */ __webpack_exports__["a"] = (function(destination, source) {
  destination = destination || {};
  var value, property;
  if (!source) {
    return destination;
  }
  for (property in source) {
    value = source[property];
    if (value !== undefined) {
      destination[property] = value;
    }
  }
  return destination;
});


/***/ }),
/* 145 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* unused harmony export add */
/* unused harmony export get */
/* unused harmony export start */
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__projections_merc__ = __webpack_require__(62);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__projections_longlat__ = __webpack_require__(63);


var projs = [__WEBPACK_IMPORTED_MODULE_0__projections_merc__["default"], __WEBPACK_IMPORTED_MODULE_1__projections_longlat__["default"]];
var names = {};
var projStore = [];

function add(proj, i) {
  var len = projStore.length;
  if (!proj.names) {
    console.log(i);
    return true;
  }
  projStore[len] = proj;
  proj.names.forEach(function(n) {
    names[n.toLowerCase()] = len;
  });
  return this;
}



function get(name) {
  if (!name) {
    return false;
  }
  var n = name.toLowerCase();
  if (typeof names[n] !== 'undefined' && projStore[names[n]]) {
    return projStore[names[n]];
  }
}

function start() {
  projs.forEach(add);
}
/* harmony default export */ __webpack_exports__["a"] = ({
  start: start,
  add: add,
  get: get
});


/***/ }),
/* 146 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony export (immutable) */ __webpack_exports__["a"] = eccentricity;
/* harmony export (immutable) */ __webpack_exports__["b"] = sphere;
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__constants_values__ = __webpack_require__(4);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__constants_Ellipsoid__ = __webpack_require__(147);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__match__ = __webpack_require__(26);




function eccentricity(a, b, rf, R_A) {
  var a2 = a * a; // used in geocentric
  var b2 = b * b; // used in geocentric
  var es = (a2 - b2) / a2; // e ^ 2
  var e = 0;
  if (R_A) {
    a *= 1 - es * (__WEBPACK_IMPORTED_MODULE_0__constants_values__["m" /* SIXTH */] + es * (__WEBPACK_IMPORTED_MODULE_0__constants_values__["j" /* RA4 */] + es * __WEBPACK_IMPORTED_MODULE_0__constants_values__["k" /* RA6 */]));
    a2 = a * a;
    es = 0;
  } else {
    e = Math.sqrt(es); // eccentricity
  }
  var ep2 = (a2 - b2) / b2; // used in geocentric
  return {
    es: es,
    e: e,
    ep2: ep2
  };
}
function sphere(a, b, rf, ellps, sphere) {
  if (!a) { // do we have an ellipsoid?
    var ellipse = Object(__WEBPACK_IMPORTED_MODULE_2__match__["a" /* default */])(__WEBPACK_IMPORTED_MODULE_1__constants_Ellipsoid__["b" /* default */], ellps);
    if (!ellipse) {
      ellipse = __WEBPACK_IMPORTED_MODULE_1__constants_Ellipsoid__["a" /* WGS84 */];
    }
    a = ellipse.a;
    b = ellipse.b;
    rf = ellipse.rf;
  }

  if (rf && !b) {
    b = (1.0 - 1.0 / rf) * a;
  }
  if (rf === 0 || Math.abs(a - b) < __WEBPACK_IMPORTED_MODULE_0__constants_values__["b" /* EPSLN */]) {
    sphere = true;
    b = a;
  }
  return {
    a: a,
    b: b,
    rf: rf,
    sphere: sphere
  };
}


/***/ }),
/* 147 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "b", function() { return exports; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return WGS84; });
var exports = {};

exports.MERIT = {
  a: 6378137.0,
  rf: 298.257,
  ellipseName: "MERIT 1983"
};

exports.SGS85 = {
  a: 6378136.0,
  rf: 298.257,
  ellipseName: "Soviet Geodetic System 85"
};

exports.GRS80 = {
  a: 6378137.0,
  rf: 298.257222101,
  ellipseName: "GRS 1980(IUGG, 1980)"
};

exports.IAU76 = {
  a: 6378140.0,
  rf: 298.257,
  ellipseName: "IAU 1976"
};

exports.airy = {
  a: 6377563.396,
  b: 6356256.910,
  ellipseName: "Airy 1830"
};

exports.APL4 = {
  a: 6378137,
  rf: 298.25,
  ellipseName: "Appl. Physics. 1965"
};

exports.NWL9D = {
  a: 6378145.0,
  rf: 298.25,
  ellipseName: "Naval Weapons Lab., 1965"
};

exports.mod_airy = {
  a: 6377340.189,
  b: 6356034.446,
  ellipseName: "Modified Airy"
};

exports.andrae = {
  a: 6377104.43,
  rf: 300.0,
  ellipseName: "Andrae 1876 (Den., Iclnd.)"
};

exports.aust_SA = {
  a: 6378160.0,
  rf: 298.25,
  ellipseName: "Australian Natl & S. Amer. 1969"
};

exports.GRS67 = {
  a: 6378160.0,
  rf: 298.2471674270,
  ellipseName: "GRS 67(IUGG 1967)"
};

exports.bessel = {
  a: 6377397.155,
  rf: 299.1528128,
  ellipseName: "Bessel 1841"
};

exports.bess_nam = {
  a: 6377483.865,
  rf: 299.1528128,
  ellipseName: "Bessel 1841 (Namibia)"
};

exports.clrk66 = {
  a: 6378206.4,
  b: 6356583.8,
  ellipseName: "Clarke 1866"
};

exports.clrk80 = {
  a: 6378249.145,
  rf: 293.4663,
  ellipseName: "Clarke 1880 mod."
};

exports.clrk58 = {
  a: 6378293.645208759,
  rf: 294.2606763692654,
  ellipseName: "Clarke 1858"
};

exports.CPM = {
  a: 6375738.7,
  rf: 334.29,
  ellipseName: "Comm. des Poids et Mesures 1799"
};

exports.delmbr = {
  a: 6376428.0,
  rf: 311.5,
  ellipseName: "Delambre 1810 (Belgium)"
};

exports.engelis = {
  a: 6378136.05,
  rf: 298.2566,
  ellipseName: "Engelis 1985"
};

exports.evrst30 = {
  a: 6377276.345,
  rf: 300.8017,
  ellipseName: "Everest 1830"
};

exports.evrst48 = {
  a: 6377304.063,
  rf: 300.8017,
  ellipseName: "Everest 1948"
};

exports.evrst56 = {
  a: 6377301.243,
  rf: 300.8017,
  ellipseName: "Everest 1956"
};

exports.evrst69 = {
  a: 6377295.664,
  rf: 300.8017,
  ellipseName: "Everest 1969"
};

exports.evrstSS = {
  a: 6377298.556,
  rf: 300.8017,
  ellipseName: "Everest (Sabah & Sarawak)"
};

exports.fschr60 = {
  a: 6378166.0,
  rf: 298.3,
  ellipseName: "Fischer (Mercury Datum) 1960"
};

exports.fschr60m = {
  a: 6378155.0,
  rf: 298.3,
  ellipseName: "Fischer 1960"
};

exports.fschr68 = {
  a: 6378150.0,
  rf: 298.3,
  ellipseName: "Fischer 1968"
};

exports.helmert = {
  a: 6378200.0,
  rf: 298.3,
  ellipseName: "Helmert 1906"
};

exports.hough = {
  a: 6378270.0,
  rf: 297.0,
  ellipseName: "Hough"
};

exports.intl = {
  a: 6378388.0,
  rf: 297.0,
  ellipseName: "International 1909 (Hayford)"
};

exports.kaula = {
  a: 6378163.0,
  rf: 298.24,
  ellipseName: "Kaula 1961"
};

exports.lerch = {
  a: 6378139.0,
  rf: 298.257,
  ellipseName: "Lerch 1979"
};

exports.mprts = {
  a: 6397300.0,
  rf: 191.0,
  ellipseName: "Maupertius 1738"
};

exports.new_intl = {
  a: 6378157.5,
  b: 6356772.2,
  ellipseName: "New International 1967"
};

exports.plessis = {
  a: 6376523.0,
  rf: 6355863.0,
  ellipseName: "Plessis 1817 (France)"
};

exports.krass = {
  a: 6378245.0,
  rf: 298.3,
  ellipseName: "Krassovsky, 1942"
};

exports.SEasia = {
  a: 6378155.0,
  b: 6356773.3205,
  ellipseName: "Southeast Asia"
};

exports.walbeck = {
  a: 6376896.0,
  b: 6355834.8467,
  ellipseName: "Walbeck"
};

exports.WGS60 = {
  a: 6378165.0,
  rf: 298.3,
  ellipseName: "WGS 60"
};

exports.WGS66 = {
  a: 6378145.0,
  rf: 298.25,
  ellipseName: "WGS 66"
};

exports.WGS7 = {
  a: 6378135.0,
  rf: 298.26,
  ellipseName: "WGS 72"
};

var WGS84 = exports.WGS84 = {
  a: 6378137.0,
  rf: 298.257223563,
  ellipseName: "WGS 84"
};

exports.sphere = {
  a: 6370997.0,
  b: 6370997.0,
  ellipseName: "Normal Sphere (r=6370997)"
};


/***/ }),
/* 148 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return exports; });
var exports = {};

exports.wgs84 = {
  towgs84: "0,0,0",
  ellipse: "WGS84",
  datumName: "WGS84"
};

exports.ch1903 = {
  towgs84: "674.374,15.056,405.346",
  ellipse: "bessel",
  datumName: "swiss"
};

exports.ggrs87 = {
  towgs84: "-199.87,74.79,246.62",
  ellipse: "GRS80",
  datumName: "Greek_Geodetic_Reference_System_1987"
};

exports.nad83 = {
  towgs84: "0,0,0",
  ellipse: "GRS80",
  datumName: "North_American_Datum_1983"
};

exports.nad27 = {
  nadgrids: "@conus,@alaska,@ntv2_0.gsb,@ntv1_can.dat",
  ellipse: "clrk66",
  datumName: "North_American_Datum_1927"
};

exports.potsdam = {
  towgs84: "606.0,23.0,413.0",
  ellipse: "bessel",
  datumName: "Potsdam Rauenberg 1950 DHDN"
};

exports.carthage = {
  towgs84: "-263.0,6.0,431.0",
  ellipse: "clark80",
  datumName: "Carthage 1934 Tunisia"
};

exports.hermannskogel = {
  towgs84: "653.0,-212.0,449.0",
  ellipse: "bessel",
  datumName: "Hermannskogel"
};

exports.osni52 = {
  towgs84: "482.530,-130.596,564.557,-1.042,-0.214,-0.631,8.15",
  ellipse: "airy",
  datumName: "Irish National"
};

exports.ire65 = {
  towgs84: "482.530,-130.596,564.557,-1.042,-0.214,-0.631,8.15",
  ellipse: "mod_airy",
  datumName: "Ireland 1965"
};

exports.rassadiran = {
  towgs84: "-133.63,-157.5,-158.62",
  ellipse: "intl",
  datumName: "Rassadiran"
};

exports.nzgd49 = {
  towgs84: "59.47,-5.04,187.44,0.47,-0.1,1.024,-4.5993",
  ellipse: "intl",
  datumName: "New Zealand Geodetic Datum 1949"
};

exports.osgb36 = {
  towgs84: "446.448,-125.157,542.060,0.1502,0.2470,0.8421,-20.4894",
  ellipse: "airy",
  datumName: "Airy 1830"
};

exports.s_jtsk = {
  towgs84: "589,76,480",
  ellipse: 'bessel',
  datumName: 'S-JTSK (Ferro)'
};

exports.beduaram = {
  towgs84: '-106,-87,188',
  ellipse: 'clrk80',
  datumName: 'Beduaram'
};

exports.gunung_segara = {
  towgs84: '-403,684,41',
  ellipse: 'bessel',
  datumName: 'Gunung Segara Jakarta'
};

exports.rnb72 = {
  towgs84: "106.869,-52.2978,103.724,-0.33657,0.456955,-1.84218,1",
  ellipse: "intl",
  datumName: "Reseau National Belge 1972"
};


/***/ }),
/* 149 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__constants_values__ = __webpack_require__(4);


function datum(datumCode, datum_params, a, b, es, ep2) {
  var out = {};

  if (datumCode === undefined || datumCode === 'none') {
    out.datum_type = __WEBPACK_IMPORTED_MODULE_0__constants_values__["g" /* PJD_NODATUM */];
  } else {
    out.datum_type = __WEBPACK_IMPORTED_MODULE_0__constants_values__["h" /* PJD_WGS84 */];
  }

  if (datum_params) {
    out.datum_params = datum_params.map(parseFloat);
    if (out.datum_params[0] !== 0 || out.datum_params[1] !== 0 || out.datum_params[2] !== 0) {
      out.datum_type = __WEBPACK_IMPORTED_MODULE_0__constants_values__["e" /* PJD_3PARAM */];
    }
    if (out.datum_params.length > 3) {
      if (out.datum_params[3] !== 0 || out.datum_params[4] !== 0 || out.datum_params[5] !== 0 || out.datum_params[6] !== 0) {
        out.datum_type = __WEBPACK_IMPORTED_MODULE_0__constants_values__["f" /* PJD_7PARAM */];
        out.datum_params[3] *= __WEBPACK_IMPORTED_MODULE_0__constants_values__["l" /* SEC_TO_RAD */];
        out.datum_params[4] *= __WEBPACK_IMPORTED_MODULE_0__constants_values__["l" /* SEC_TO_RAD */];
        out.datum_params[5] *= __WEBPACK_IMPORTED_MODULE_0__constants_values__["l" /* SEC_TO_RAD */];
        out.datum_params[6] = (out.datum_params[6] / 1000000.0) + 1.0;
      }
    }
  }

  out.a = a; //datum object also uses these values
  out.b = b;
  out.es = es;
  out.ep2 = ep2;
  return out;
}

/* harmony default export */ __webpack_exports__["a"] = (datum);


/***/ }),
/* 150 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__constants_values__ = __webpack_require__(4);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__datumUtils__ = __webpack_require__(151);



function checkParams(type) {
  return (type === __WEBPACK_IMPORTED_MODULE_0__constants_values__["e" /* PJD_3PARAM */] || type === __WEBPACK_IMPORTED_MODULE_0__constants_values__["f" /* PJD_7PARAM */]);
}

/* harmony default export */ __webpack_exports__["a"] = (function(source, dest, point) {
  // Short cut if the datums are identical.
  if (Object(__WEBPACK_IMPORTED_MODULE_1__datumUtils__["a" /* compareDatums */])(source, dest)) {
    return point; // in this case, zero is sucess,
    // whereas cs_compare_datums returns 1 to indicate TRUE
    // confusing, should fix this
  }

  // Explicitly skip datum transform by setting 'datum=none' as parameter for either source or dest
  if (source.datum_type === __WEBPACK_IMPORTED_MODULE_0__constants_values__["g" /* PJD_NODATUM */] || dest.datum_type === __WEBPACK_IMPORTED_MODULE_0__constants_values__["g" /* PJD_NODATUM */]) {
    return point;
  }

  // If this datum requires grid shifts, then apply it to geodetic coordinates.

  // Do we need to go through geocentric coordinates?
  if (source.es === dest.es && source.a === dest.a && !checkParams(source.datum_type) &&  !checkParams(dest.datum_type)) {
    return point;
  }

  // Convert to geocentric coordinates.
  point = Object(__WEBPACK_IMPORTED_MODULE_1__datumUtils__["e" /* geodeticToGeocentric */])(point, source.es, source.a);
  // Convert between datums
  if (checkParams(source.datum_type)) {
    point = Object(__WEBPACK_IMPORTED_MODULE_1__datumUtils__["d" /* geocentricToWgs84 */])(point, source.datum_type, source.datum_params);
  }
  if (checkParams(dest.datum_type)) {
    point = Object(__WEBPACK_IMPORTED_MODULE_1__datumUtils__["b" /* geocentricFromWgs84 */])(point, dest.datum_type, dest.datum_params);
  }
  return Object(__WEBPACK_IMPORTED_MODULE_1__datumUtils__["c" /* geocentricToGeodetic */])(point, dest.es, dest.a, dest.b);

});


/***/ }),
/* 151 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony export (immutable) */ __webpack_exports__["a"] = compareDatums;
/* harmony export (immutable) */ __webpack_exports__["e"] = geodeticToGeocentric;
/* harmony export (immutable) */ __webpack_exports__["c"] = geocentricToGeodetic;
/* harmony export (immutable) */ __webpack_exports__["d"] = geocentricToWgs84;
/* harmony export (immutable) */ __webpack_exports__["b"] = geocentricFromWgs84;
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__constants_values__ = __webpack_require__(4);


function compareDatums(source, dest) {
  if (source.datum_type !== dest.datum_type) {
    return false; // false, datums are not equal
  } else if (source.a !== dest.a || Math.abs(source.es - dest.es) > 0.000000000050) {
    // the tolerance for es is to ensure that GRS80 and WGS84
    // are considered identical
    return false;
  } else if (source.datum_type === __WEBPACK_IMPORTED_MODULE_0__constants_values__["e" /* PJD_3PARAM */]) {
    return (source.datum_params[0] === dest.datum_params[0] && source.datum_params[1] === dest.datum_params[1] && source.datum_params[2] === dest.datum_params[2]);
  } else if (source.datum_type === __WEBPACK_IMPORTED_MODULE_0__constants_values__["f" /* PJD_7PARAM */]) {
    return (source.datum_params[0] === dest.datum_params[0] && source.datum_params[1] === dest.datum_params[1] && source.datum_params[2] === dest.datum_params[2] && source.datum_params[3] === dest.datum_params[3] && source.datum_params[4] === dest.datum_params[4] && source.datum_params[5] === dest.datum_params[5] && source.datum_params[6] === dest.datum_params[6]);
  } else {
    return true; // datums are equal
  }
} // cs_compare_datums()

/*
 * The function Convert_Geodetic_To_Geocentric converts geodetic coordinates
 * (latitude, longitude, and height) to geocentric coordinates (X, Y, Z),
 * according to the current ellipsoid parameters.
 *
 *    Latitude  : Geodetic latitude in radians                     (input)
 *    Longitude : Geodetic longitude in radians                    (input)
 *    Height    : Geodetic height, in meters                       (input)
 *    X         : Calculated Geocentric X coordinate, in meters    (output)
 *    Y         : Calculated Geocentric Y coordinate, in meters    (output)
 *    Z         : Calculated Geocentric Z coordinate, in meters    (output)
 *
 */
function geodeticToGeocentric(p, es, a) {
  var Longitude = p.x;
  var Latitude = p.y;
  var Height = p.z ? p.z : 0; //Z value not always supplied

  var Rn; /*  Earth radius at location  */
  var Sin_Lat; /*  Math.sin(Latitude)  */
  var Sin2_Lat; /*  Square of Math.sin(Latitude)  */
  var Cos_Lat; /*  Math.cos(Latitude)  */

  /*
   ** Don't blow up if Latitude is just a little out of the value
   ** range as it may just be a rounding issue.  Also removed longitude
   ** test, it should be wrapped by Math.cos() and Math.sin().  NFW for PROJ.4, Sep/2001.
   */
  if (Latitude < -__WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */] && Latitude > -1.001 * __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */]) {
    Latitude = -__WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */];
  } else if (Latitude > __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */] && Latitude < 1.001 * __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */]) {
    Latitude = __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */];
  } else if (Latitude < -__WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */]) {
    /* Latitude out of range */
    //..reportError('geocent:lat out of range:' + Latitude);
    return { x: -Infinity, y: -Infinity, z: p.z };
  } else if (Latitude > __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */]) {
    /* Latitude out of range */
    return { x: Infinity, y: Infinity, z: p.z };
  }

  if (Longitude > Math.PI) {
    Longitude -= (2 * Math.PI);
  }
  Sin_Lat = Math.sin(Latitude);
  Cos_Lat = Math.cos(Latitude);
  Sin2_Lat = Sin_Lat * Sin_Lat;
  Rn = a / (Math.sqrt(1.0e0 - es * Sin2_Lat));
  return {
    x: (Rn + Height) * Cos_Lat * Math.cos(Longitude),
    y: (Rn + Height) * Cos_Lat * Math.sin(Longitude),
    z: ((Rn * (1 - es)) + Height) * Sin_Lat
  };
} // cs_geodetic_to_geocentric()

function geocentricToGeodetic(p, es, a, b) {
  /* local defintions and variables */
  /* end-criterium of loop, accuracy of sin(Latitude) */
  var genau = 1e-12;
  var genau2 = (genau * genau);
  var maxiter = 30;

  var P; /* distance between semi-minor axis and location */
  var RR; /* distance between center and location */
  var CT; /* sin of geocentric latitude */
  var ST; /* cos of geocentric latitude */
  var RX;
  var RK;
  var RN; /* Earth radius at location */
  var CPHI0; /* cos of start or old geodetic latitude in iterations */
  var SPHI0; /* sin of start or old geodetic latitude in iterations */
  var CPHI; /* cos of searched geodetic latitude */
  var SPHI; /* sin of searched geodetic latitude */
  var SDPHI; /* end-criterium: addition-theorem of sin(Latitude(iter)-Latitude(iter-1)) */
  var iter; /* # of continous iteration, max. 30 is always enough (s.a.) */

  var X = p.x;
  var Y = p.y;
  var Z = p.z ? p.z : 0.0; //Z value not always supplied
  var Longitude;
  var Latitude;
  var Height;

  P = Math.sqrt(X * X + Y * Y);
  RR = Math.sqrt(X * X + Y * Y + Z * Z);

  /*      special cases for latitude and longitude */
  if (P / a < genau) {

    /*  special case, if P=0. (X=0., Y=0.) */
    Longitude = 0.0;

    /*  if (X,Y,Z)=(0.,0.,0.) then Height becomes semi-minor axis
     *  of ellipsoid (=center of mass), Latitude becomes PI/2 */
    if (RR / a < genau) {
      Latitude = __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */];
      Height = -b;
      return {
        x: p.x,
        y: p.y,
        z: p.z
      };
    }
  } else {
    /*  ellipsoidal (geodetic) longitude
     *  interval: -PI < Longitude <= +PI */
    Longitude = Math.atan2(Y, X);
  }

  /* --------------------------------------------------------------
   * Following iterative algorithm was developped by
   * "Institut for Erdmessung", University of Hannover, July 1988.
   * Internet: www.ife.uni-hannover.de
   * Iterative computation of CPHI,SPHI and Height.
   * Iteration of CPHI and SPHI to 10**-12 radian resp.
   * 2*10**-7 arcsec.
   * --------------------------------------------------------------
   */
  CT = Z / RR;
  ST = P / RR;
  RX = 1.0 / Math.sqrt(1.0 - es * (2.0 - es) * ST * ST);
  CPHI0 = ST * (1.0 - es) * RX;
  SPHI0 = CT * RX;
  iter = 0;

  /* loop to find sin(Latitude) resp. Latitude
   * until |sin(Latitude(iter)-Latitude(iter-1))| < genau */
  do {
    iter++;
    RN = a / Math.sqrt(1.0 - es * SPHI0 * SPHI0);

    /*  ellipsoidal (geodetic) height */
    Height = P * CPHI0 + Z * SPHI0 - RN * (1.0 - es * SPHI0 * SPHI0);

    RK = es * RN / (RN + Height);
    RX = 1.0 / Math.sqrt(1.0 - RK * (2.0 - RK) * ST * ST);
    CPHI = ST * (1.0 - RK) * RX;
    SPHI = CT * RX;
    SDPHI = SPHI * CPHI0 - CPHI * SPHI0;
    CPHI0 = CPHI;
    SPHI0 = SPHI;
  }
  while (SDPHI * SDPHI > genau2 && iter < maxiter);

  /*      ellipsoidal (geodetic) latitude */
  Latitude = Math.atan(SPHI / Math.abs(CPHI));
  return {
    x: Longitude,
    y: Latitude,
    z: Height
  };
} // cs_geocentric_to_geodetic()

/****************************************************************/
// pj_geocentic_to_wgs84( p )
//  p = point to transform in geocentric coordinates (x,y,z)


/** point object, nothing fancy, just allows values to be
    passed back and forth by reference rather than by value.
    Other point classes may be used as long as they have
    x and y properties, which will get modified in the transform method.
*/
function geocentricToWgs84(p, datum_type, datum_params) {

  if (datum_type === __WEBPACK_IMPORTED_MODULE_0__constants_values__["e" /* PJD_3PARAM */]) {
    // if( x[io] === HUGE_VAL )
    //    continue;
    return {
      x: p.x + datum_params[0],
      y: p.y + datum_params[1],
      z: p.z + datum_params[2],
    };
  } else if (datum_type === __WEBPACK_IMPORTED_MODULE_0__constants_values__["f" /* PJD_7PARAM */]) {
    var Dx_BF = datum_params[0];
    var Dy_BF = datum_params[1];
    var Dz_BF = datum_params[2];
    var Rx_BF = datum_params[3];
    var Ry_BF = datum_params[4];
    var Rz_BF = datum_params[5];
    var M_BF = datum_params[6];
    // if( x[io] === HUGE_VAL )
    //    continue;
    return {
      x: M_BF * (p.x - Rz_BF * p.y + Ry_BF * p.z) + Dx_BF,
      y: M_BF * (Rz_BF * p.x + p.y - Rx_BF * p.z) + Dy_BF,
      z: M_BF * (-Ry_BF * p.x + Rx_BF * p.y + p.z) + Dz_BF
    };
  }
} // cs_geocentric_to_wgs84

/****************************************************************/
// pj_geocentic_from_wgs84()
//  coordinate system definition,
//  point to transform in geocentric coordinates (x,y,z)
function geocentricFromWgs84(p, datum_type, datum_params) {

  if (datum_type === __WEBPACK_IMPORTED_MODULE_0__constants_values__["e" /* PJD_3PARAM */]) {
    //if( x[io] === HUGE_VAL )
    //    continue;
    return {
      x: p.x - datum_params[0],
      y: p.y - datum_params[1],
      z: p.z - datum_params[2],
    };

  } else if (datum_type === __WEBPACK_IMPORTED_MODULE_0__constants_values__["f" /* PJD_7PARAM */]) {
    var Dx_BF = datum_params[0];
    var Dy_BF = datum_params[1];
    var Dz_BF = datum_params[2];
    var Rx_BF = datum_params[3];
    var Ry_BF = datum_params[4];
    var Rz_BF = datum_params[5];
    var M_BF = datum_params[6];
    var x_tmp = (p.x - Dx_BF) / M_BF;
    var y_tmp = (p.y - Dy_BF) / M_BF;
    var z_tmp = (p.z - Dz_BF) / M_BF;
    //if( x[io] === HUGE_VAL )
    //    continue;

    return {
      x: x_tmp + Rz_BF * y_tmp - Ry_BF * z_tmp,
      y: -Rz_BF * x_tmp + y_tmp + Rx_BF * z_tmp,
      z: Ry_BF * x_tmp - Rx_BF * y_tmp + z_tmp
    };
  } //cs_geocentric_from_wgs84()
}


/***/ }),
/* 152 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony default export */ __webpack_exports__["a"] = (function(crs, denorm, point) {
  var xin = point.x,
    yin = point.y,
    zin = point.z || 0.0;
  var v, t, i;
  var out = {};
  for (i = 0; i < 3; i++) {
    if (denorm && i === 2 && point.z === undefined) {
      continue;
    }
    if (i === 0) {
      v = xin;
      t = 'x';
    }
    else if (i === 1) {
      v = yin;
      t = 'y';
    }
    else {
      v = zin;
      t = 'z';
    }
    switch (crs.axis[i]) {
    case 'e':
      out[t] = v;
      break;
    case 'w':
      out[t] = -v;
      break;
    case 'n':
      out[t] = v;
      break;
    case 's':
      out[t] = -v;
      break;
    case 'u':
      if (point[t] !== undefined) {
        out.z = v;
      }
      break;
    case 'd':
      if (point[t] !== undefined) {
        out.z = -v;
      }
      break;
    default:
      //console.log("ERROR: unknow axis ("+crs.axis[i]+") - check definition of "+crs.projName);
      return null;
    }
  }
  return out;
});


/***/ }),
/* 153 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony default export */ __webpack_exports__["a"] = (function (point) {
  checkCoord(point.x);
  checkCoord(point.y);
});
function checkCoord(num) {
  if (typeof Number.isFinite === 'function') {
    if (Number.isFinite(num)) {
      return;
    }
    throw new TypeError('coordinates must be finite numbers');
  }
  if (typeof num !== 'number' || num !== num || !isFinite(num)) {
    throw new TypeError('coordinates must be finite numbers');
  }
}


/***/ }),
/* 154 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_mgrs__ = __webpack_require__(66);


function Point(x, y, z) {
  if (!(this instanceof Point)) {
    return new Point(x, y, z);
  }
  if (Array.isArray(x)) {
    this.x = x[0];
    this.y = x[1];
    this.z = x[2] || 0.0;
  } else if(typeof x === 'object') {
    this.x = x.x;
    this.y = x.y;
    this.z = x.z || 0.0;
  } else if (typeof x === 'string' && typeof y === 'undefined') {
    var coords = x.split(',');
    this.x = parseFloat(coords[0], 10);
    this.y = parseFloat(coords[1], 10);
    this.z = parseFloat(coords[2], 10) || 0.0;
  } else {
    this.x = x;
    this.y = y;
    this.z = z || 0.0;
  }
  console.warn('proj4.Point will be removed in version 3, use proj4.toPoint');
}

Point.fromMGRS = function(mgrsStr) {
  return new Point(Object(__WEBPACK_IMPORTED_MODULE_0_mgrs__["c" /* toPoint */])(mgrsStr));
};
Point.prototype.toMGRS = function(accuracy) {
  return Object(__WEBPACK_IMPORTED_MODULE_0_mgrs__["b" /* forward */])([this.x, this.y], accuracy);
};
/* harmony default export */ __webpack_exports__["a"] = (Point);


/***/ }),
/* 155 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__package_json__ = __webpack_require__(156);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__package_json___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0__package_json__);
/* harmony reexport (binding) */ if(__webpack_require__.o(__WEBPACK_IMPORTED_MODULE_0__package_json__, "version")) __webpack_require__.d(__webpack_exports__, "a", function() { return __WEBPACK_IMPORTED_MODULE_0__package_json__["version"]; });



/***/ }),
/* 156 */
/***/ (function(module, exports) {

module.exports = {"_args":[["proj4@2.5.0","."]],"_from":"proj4@2.5.0","_id":"proj4@2.5.0","_inBundle":false,"_integrity":"sha512-XZTRT7OPdLzgvtTqL8DG2cEj8lYdovztOwiwpwRSYayOty5Ipf3H68dh/fiL+HKDEyetmQSMhkkMGiJoyziz3w==","_location":"/proj4","_phantomChildren":{},"_requested":{"type":"version","registry":true,"raw":"proj4@2.5.0","name":"proj4","escapedName":"proj4","rawSpec":"2.5.0","saveSpec":null,"fetchSpec":"2.5.0"},"_requiredBy":["/"],"_resolved":"https://registry.npmjs.org/proj4/-/proj4-2.5.0.tgz","_spec":"2.5.0","_where":".","author":"","bugs":{"url":"https://github.com/proj4js/proj4js/issues"},"contributors":[{"name":"Mike Adair","email":"madair@dmsolutions.ca"},{"name":"Richard Greenwood","email":"rich@greenwoodmap.com"},{"name":"Calvin Metcalf","email":"calvin.metcalf@gmail.com"},{"name":"Richard Marsden","url":"http://www.winwaed.com"},{"name":"T. Mittan"},{"name":"D. Steinwand"},{"name":"S. Nelson"}],"dependencies":{"mgrs":"1.0.0","wkt-parser":"^1.2.0"},"description":"Proj4js is a JavaScript library to transform point coordinates from one coordinate system to another, including datum transformations.","devDependencies":{"chai":"~4.1.2","curl-amd":"github:cujojs/curl","grunt":"^1.0.1","grunt-cli":"~1.2.0","grunt-contrib-connect":"~1.0.2","grunt-contrib-jshint":"~1.1.0","grunt-contrib-uglify":"~3.1.0","grunt-mocha-phantomjs":"~4.0.0","grunt-rollup":"^6.0.0","istanbul":"~0.4.5","mocha":"~4.0.0","rollup":"^0.50.0","rollup-plugin-json":"^2.3.0","rollup-plugin-node-resolve":"^3.0.0","tin":"~0.5.0"},"directories":{"test":"test","doc":"docs"},"homepage":"https://github.com/proj4js/proj4js#readme","license":"MIT","main":"dist/proj4-src.js","module":"lib/index.js","name":"proj4","repository":{"type":"git","url":"git://github.com/proj4js/proj4js.git"},"scripts":{"build":"grunt","build:tmerc":"grunt build:tmerc","test":"npm run build && istanbul test _mocha test/test.js"},"version":"2.5.0"}

/***/ }),
/* 157 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__lib_projections_tmerc__ = __webpack_require__(67);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__lib_projections_etmerc__ = __webpack_require__(42);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__lib_projections_utm__ = __webpack_require__(72);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__lib_projections_sterea__ = __webpack_require__(73);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__lib_projections_stere__ = __webpack_require__(75);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__lib_projections_somerc__ = __webpack_require__(76);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_6__lib_projections_omerc__ = __webpack_require__(77);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_7__lib_projections_lcc__ = __webpack_require__(78);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_8__lib_projections_krovak__ = __webpack_require__(79);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_9__lib_projections_cass__ = __webpack_require__(80);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_10__lib_projections_laea__ = __webpack_require__(81);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_11__lib_projections_aea__ = __webpack_require__(82);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_12__lib_projections_gnom__ = __webpack_require__(83);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_13__lib_projections_cea__ = __webpack_require__(84);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_14__lib_projections_eqc__ = __webpack_require__(85);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_15__lib_projections_poly__ = __webpack_require__(86);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_16__lib_projections_nzmg__ = __webpack_require__(87);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_17__lib_projections_mill__ = __webpack_require__(88);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_18__lib_projections_sinu__ = __webpack_require__(89);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_19__lib_projections_moll__ = __webpack_require__(90);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_20__lib_projections_eqdc__ = __webpack_require__(91);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_21__lib_projections_vandg__ = __webpack_require__(92);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_22__lib_projections_aeqd__ = __webpack_require__(93);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_23__lib_projections_ortho__ = __webpack_require__(94);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_24__lib_projections_qsc__ = __webpack_require__(95);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_25__lib_projections_robin__ = __webpack_require__(96);


























/* harmony default export */ __webpack_exports__["a"] = (function(proj4){
  proj4.Proj.projections.add(__WEBPACK_IMPORTED_MODULE_0__lib_projections_tmerc__["default"]);
  proj4.Proj.projections.add(__WEBPACK_IMPORTED_MODULE_1__lib_projections_etmerc__["default"]);
  proj4.Proj.projections.add(__WEBPACK_IMPORTED_MODULE_2__lib_projections_utm__["default"]);
  proj4.Proj.projections.add(__WEBPACK_IMPORTED_MODULE_3__lib_projections_sterea__["default"]);
  proj4.Proj.projections.add(__WEBPACK_IMPORTED_MODULE_4__lib_projections_stere__["default"]);
  proj4.Proj.projections.add(__WEBPACK_IMPORTED_MODULE_5__lib_projections_somerc__["default"]);
  proj4.Proj.projections.add(__WEBPACK_IMPORTED_MODULE_6__lib_projections_omerc__["default"]);
  proj4.Proj.projections.add(__WEBPACK_IMPORTED_MODULE_7__lib_projections_lcc__["default"]);
  proj4.Proj.projections.add(__WEBPACK_IMPORTED_MODULE_8__lib_projections_krovak__["default"]);
  proj4.Proj.projections.add(__WEBPACK_IMPORTED_MODULE_9__lib_projections_cass__["default"]);
  proj4.Proj.projections.add(__WEBPACK_IMPORTED_MODULE_10__lib_projections_laea__["default"]);
  proj4.Proj.projections.add(__WEBPACK_IMPORTED_MODULE_11__lib_projections_aea__["default"]);
  proj4.Proj.projections.add(__WEBPACK_IMPORTED_MODULE_12__lib_projections_gnom__["default"]);
  proj4.Proj.projections.add(__WEBPACK_IMPORTED_MODULE_13__lib_projections_cea__["default"]);
  proj4.Proj.projections.add(__WEBPACK_IMPORTED_MODULE_14__lib_projections_eqc__["default"]);
  proj4.Proj.projections.add(__WEBPACK_IMPORTED_MODULE_15__lib_projections_poly__["default"]);
  proj4.Proj.projections.add(__WEBPACK_IMPORTED_MODULE_16__lib_projections_nzmg__["default"]);
  proj4.Proj.projections.add(__WEBPACK_IMPORTED_MODULE_17__lib_projections_mill__["default"]);
  proj4.Proj.projections.add(__WEBPACK_IMPORTED_MODULE_18__lib_projections_sinu__["default"]);
  proj4.Proj.projections.add(__WEBPACK_IMPORTED_MODULE_19__lib_projections_moll__["default"]);
  proj4.Proj.projections.add(__WEBPACK_IMPORTED_MODULE_20__lib_projections_eqdc__["default"]);
  proj4.Proj.projections.add(__WEBPACK_IMPORTED_MODULE_21__lib_projections_vandg__["default"]);
  proj4.Proj.projections.add(__WEBPACK_IMPORTED_MODULE_22__lib_projections_aeqd__["default"]);
  proj4.Proj.projections.add(__WEBPACK_IMPORTED_MODULE_23__lib_projections_ortho__["default"]);
  proj4.Proj.projections.add(__WEBPACK_IMPORTED_MODULE_24__lib_projections_qsc__["default"]);
  proj4.Proj.projections.add(__WEBPACK_IMPORTED_MODULE_25__lib_projections_robin__["default"]);
});

/***/ }),
/* 158 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__hypot__ = __webpack_require__(70);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__log1py__ = __webpack_require__(159);



/* harmony default export */ __webpack_exports__["a"] = (function(x) {
  var y = Math.abs(x);
  y = Object(__WEBPACK_IMPORTED_MODULE_1__log1py__["a" /* default */])(y * (1 + y / (Object(__WEBPACK_IMPORTED_MODULE_0__hypot__["a" /* default */])(1, y) + 1)));

  return x < 0 ? -y : y;
});


/***/ }),
/* 159 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony default export */ __webpack_exports__["a"] = (function(x) {
  var y = 1 + x;
  var z = y - 1;

  return z === 0 ? x : x * Math.log(y) / z;
});


/***/ }),
/* 160 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony default export */ __webpack_exports__["a"] = (function(pp, B) {
  var cos_2B = 2 * Math.cos(2 * B);
  var i = pp.length - 1;
  var h1 = pp[i];
  var h2 = 0;
  var h;

  while (--i >= 0) {
    h = -h2 + cos_2B * h1 + pp[i];
    h2 = h1;
    h1 = h;
  }

  return (B + h * Math.sin(2 * B));
});


/***/ }),
/* 161 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony default export */ __webpack_exports__["a"] = (function(pp, arg_r) {
  var r = 2 * Math.cos(arg_r);
  var i = pp.length - 1;
  var hr1 = pp[i];
  var hr2 = 0;
  var hr;

  while (--i >= 0) {
    hr = -hr2 + r * hr1 + pp[i];
    hr2 = hr1;
    hr1 = hr;
  }

  return Math.sin(arg_r) * hr;
});


/***/ }),
/* 162 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__sinh__ = __webpack_require__(43);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__cosh__ = __webpack_require__(71);



/* harmony default export */ __webpack_exports__["a"] = (function(pp, arg_r, arg_i) {
  var sin_arg_r = Math.sin(arg_r);
  var cos_arg_r = Math.cos(arg_r);
  var sinh_arg_i = Object(__WEBPACK_IMPORTED_MODULE_0__sinh__["a" /* default */])(arg_i);
  var cosh_arg_i = Object(__WEBPACK_IMPORTED_MODULE_1__cosh__["a" /* default */])(arg_i);
  var r = 2 * cos_arg_r * cosh_arg_i;
  var i = -2 * sin_arg_r * sinh_arg_i;
  var j = pp.length - 1;
  var hr = pp[j];
  var hi1 = 0;
  var hr1 = 0;
  var hi = 0;
  var hr2;
  var hi2;

  while (--j >= 0) {
    hr2 = hr1;
    hi2 = hi1;
    hr1 = hr;
    hi1 = hi;
    hr = -hr2 + r * hr1 - i * hi1 + pp[j];
    hi = -hi2 + i * hr1 + r * hi1;
  }

  r = sin_arg_r * cosh_arg_i;
  i = cos_arg_r * sinh_arg_i;

  return [r * hr - i * hi, r * hi + i * hr];
});


/***/ }),
/* 163 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__adjust_lon__ = __webpack_require__(5);


/* harmony default export */ __webpack_exports__["a"] = (function(zone, lon) {
  if (zone === undefined) {
    zone = Math.floor((Object(__WEBPACK_IMPORTED_MODULE_0__adjust_lon__["a" /* default */])(lon) + Math.PI) * 30 / Math.PI) + 1;

    if (zone < 0) {
      return 0;
    } else if (zone > 60) {
      return 60;
    }
  }
  return zone;
});


/***/ }),
/* 164 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony default export */ __webpack_exports__["a"] = (function(esinp, exp) {
  return (Math.pow((1 - esinp) / (1 + esinp), exp));
});

/***/ }),
/* 165 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__constants_values__ = __webpack_require__(4);


/* harmony default export */ __webpack_exports__["a"] = (function(eccent, q) {
  var temp = 1 - (1 - eccent * eccent) / (2 * eccent) * Math.log((1 - eccent) / (1 + eccent));
  if (Math.abs(Math.abs(q) - temp) < 1.0E-6) {
    if (q < 0) {
      return (-1 * __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */]);
    }
    else {
      return __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */];
    }
  }
  //var phi = 0.5* q/(1-eccent*eccent);
  var phi = Math.asin(0.5 * q);
  var dphi;
  var sin_phi;
  var cos_phi;
  var con;
  for (var i = 0; i < 30; i++) {
    sin_phi = Math.sin(phi);
    cos_phi = Math.cos(phi);
    con = eccent * sin_phi;
    dphi = Math.pow(1 - con * con, 2) / (2 * cos_phi) * (q / (1 - eccent * eccent) - sin_phi / (1 - con * con) + 0.5 / eccent * Math.log((1 - con) / (1 + con)));
    phi += dphi;
    if (Math.abs(dphi) <= 0.0000000001) {
      return phi;
    }
  }

  //console.log("IQSFN-CONV:Latitude failed to converge after 30 iterations");
  return NaN;
});


/***/ }),
/* 166 */
/***/ (function(module, exports, __webpack_require__) {

var map = {
	"./aea.js": 82,
	"./aeqd.js": 93,
	"./cass.js": 80,
	"./cea.js": 84,
	"./eqc.js": 85,
	"./eqdc.js": 91,
	"./equi.js": 167,
	"./etmerc.js": 42,
	"./gauss.js": 74,
	"./gnom.js": 83,
	"./gstmerc.js": 168,
	"./krovak.js": 79,
	"./laea.js": 81,
	"./lcc.js": 78,
	"./longlat.js": 63,
	"./merc.js": 62,
	"./mill.js": 88,
	"./moll.js": 90,
	"./nzmg.js": 87,
	"./omerc.js": 77,
	"./ortho.js": 94,
	"./poly.js": 86,
	"./qsc.js": 95,
	"./robin.js": 96,
	"./sinu.js": 89,
	"./somerc.js": 76,
	"./stere.js": 75,
	"./sterea.js": 73,
	"./tmerc.js": 67,
	"./utm.js": 72,
	"./vandg.js": 92
};
function webpackContext(req) {
	return __webpack_require__(webpackContextResolve(req));
};
function webpackContextResolve(req) {
	var id = map[req];
	if(!(id + 1)) // check for number or string
		throw new Error("Cannot find module '" + req + "'.");
	return id;
};
webpackContext.keys = function webpackContextKeys() {
	return Object.keys(map);
};
webpackContext.resolve = webpackContextResolve;
module.exports = webpackContext;
webpackContext.id = 166;

/***/ }),
/* 167 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony export (immutable) */ __webpack_exports__["init"] = init;
/* harmony export (immutable) */ __webpack_exports__["forward"] = forward;
/* harmony export (immutable) */ __webpack_exports__["inverse"] = inverse;
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "names", function() { return names; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__common_adjust_lon__ = __webpack_require__(5);


function init() {
  this.x0 = this.x0 || 0;
  this.y0 = this.y0 || 0;
  this.lat0 = this.lat0 || 0;
  this.long0 = this.long0 || 0;
  ///this.t2;
}

/* Equirectangular forward equations--mapping lat,long to x,y
  ---------------------------------------------------------*/
function forward(p) {

  var lon = p.x;
  var lat = p.y;

  var dlon = Object(__WEBPACK_IMPORTED_MODULE_0__common_adjust_lon__["a" /* default */])(lon - this.long0);
  var x = this.x0 + this.a * dlon * Math.cos(this.lat0);
  var y = this.y0 + this.a * lat;

  this.t1 = x;
  this.t2 = Math.cos(this.lat0);
  p.x = x;
  p.y = y;
  return p;
}

/* Equirectangular inverse equations--mapping x,y to lat/long
  ---------------------------------------------------------*/
function inverse(p) {

  p.x -= this.x0;
  p.y -= this.y0;
  var lat = p.y / this.a;

  var lon = Object(__WEBPACK_IMPORTED_MODULE_0__common_adjust_lon__["a" /* default */])(this.long0 + p.x / (this.a * Math.cos(this.lat0)));
  p.x = lon;
  p.y = lat;
}

var names = ["equi"];
/* harmony default export */ __webpack_exports__["default"] = ({
  init: init,
  forward: forward,
  inverse: inverse,
  names: names
});


/***/ }),
/* 168 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony export (immutable) */ __webpack_exports__["init"] = init;
/* harmony export (immutable) */ __webpack_exports__["forward"] = forward;
/* harmony export (immutable) */ __webpack_exports__["inverse"] = inverse;
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "names", function() { return names; });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__common_latiso__ = __webpack_require__(169);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__common_sinh__ = __webpack_require__(43);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__common_cosh__ = __webpack_require__(71);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__common_invlatiso__ = __webpack_require__(170);





function init() {

  // array of:  a, b, lon0, lat0, k0, x0, y0
  var temp = this.b / this.a;
  this.e = Math.sqrt(1 - temp * temp);
  this.lc = this.long0;
  this.rs = Math.sqrt(1 + this.e * this.e * Math.pow(Math.cos(this.lat0), 4) / (1 - this.e * this.e));
  var sinz = Math.sin(this.lat0);
  var pc = Math.asin(sinz / this.rs);
  var sinzpc = Math.sin(pc);
  this.cp = Object(__WEBPACK_IMPORTED_MODULE_0__common_latiso__["a" /* default */])(0, pc, sinzpc) - this.rs * Object(__WEBPACK_IMPORTED_MODULE_0__common_latiso__["a" /* default */])(this.e, this.lat0, sinz);
  this.n2 = this.k0 * this.a * Math.sqrt(1 - this.e * this.e) / (1 - this.e * this.e * sinz * sinz);
  this.xs = this.x0;
  this.ys = this.y0 - this.n2 * pc;

  if (!this.title) {
    this.title = "Gauss Schreiber transverse mercator";
  }
}

// forward equations--mapping lat,long to x,y
// -----------------------------------------------------------------
function forward(p) {

  var lon = p.x;
  var lat = p.y;

  var L = this.rs * (lon - this.lc);
  var Ls = this.cp + (this.rs * Object(__WEBPACK_IMPORTED_MODULE_0__common_latiso__["a" /* default */])(this.e, lat, Math.sin(lat)));
  var lat1 = Math.asin(Math.sin(L) / Object(__WEBPACK_IMPORTED_MODULE_2__common_cosh__["a" /* default */])(Ls));
  var Ls1 = Object(__WEBPACK_IMPORTED_MODULE_0__common_latiso__["a" /* default */])(0, lat1, Math.sin(lat1));
  p.x = this.xs + (this.n2 * Ls1);
  p.y = this.ys + (this.n2 * Math.atan(Object(__WEBPACK_IMPORTED_MODULE_1__common_sinh__["a" /* default */])(Ls) / Math.cos(L)));
  return p;
}

// inverse equations--mapping x,y to lat/long
// -----------------------------------------------------------------
function inverse(p) {

  var x = p.x;
  var y = p.y;

  var L = Math.atan(Object(__WEBPACK_IMPORTED_MODULE_1__common_sinh__["a" /* default */])((x - this.xs) / this.n2) / Math.cos((y - this.ys) / this.n2));
  var lat1 = Math.asin(Math.sin((y - this.ys) / this.n2) / Object(__WEBPACK_IMPORTED_MODULE_2__common_cosh__["a" /* default */])((x - this.xs) / this.n2));
  var LC = Object(__WEBPACK_IMPORTED_MODULE_0__common_latiso__["a" /* default */])(0, lat1, Math.sin(lat1));
  p.x = this.lc + L / this.rs;
  p.y = Object(__WEBPACK_IMPORTED_MODULE_3__common_invlatiso__["a" /* default */])(this.e, (LC - this.cp) / this.rs);
  return p;
}

var names = ["gstmerg"];
/* harmony default export */ __webpack_exports__["default"] = ({
  init: init,
  forward: forward,
  inverse: inverse,
  names: names
});


/***/ }),
/* 169 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__constants_values__ = __webpack_require__(4);


/* harmony default export */ __webpack_exports__["a"] = (function(eccent, phi, sinphi) {
  if (Math.abs(phi) > __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */]) {
    return Number.NaN;
  }
  if (phi === __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */]) {
    return Number.POSITIVE_INFINITY;
  }
  if (phi === -1 * __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */]) {
    return Number.NEGATIVE_INFINITY;
  }

  var con = eccent * sinphi;
  return Math.log(Math.tan((__WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */] + phi) / 2)) + eccent * Math.log((1 - con) / (1 + con)) / 2;
});


/***/ }),
/* 170 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__fL__ = __webpack_require__(171);


/* harmony default export */ __webpack_exports__["a"] = (function(eccent, ts) {
  var phi = Object(__WEBPACK_IMPORTED_MODULE_0__fL__["a" /* default */])(1, ts);
  var Iphi = 0;
  var con = 0;
  do {
    Iphi = phi;
    con = eccent * Math.sin(Iphi);
    phi = Object(__WEBPACK_IMPORTED_MODULE_0__fL__["a" /* default */])(Math.exp(eccent * Math.log((1 + con) / (1 - con)) / 2), ts);
  } while (Math.abs(phi - Iphi) > 1.0e-12);
  return phi;
});

/***/ }),
/* 171 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__constants_values__ = __webpack_require__(4);


/* harmony default export */ __webpack_exports__["a"] = (function(x, L) {
  return 2 * Math.atan(x * Math.exp(L)) - __WEBPACK_IMPORTED_MODULE_0__constants_values__["d" /* HALF_PI */];
});


/***/ }),
/* 172 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/**
 * Based on the following jquery throttle / debounce plugin:
 *
 * jQuery throttle / debounce - v1.1 - 3/7/2010
 * http://benalman.com/projects/jquery-throttle-debounce-plugin/
 *
 * @copyright 2010 "Cowboy" Ben Alman
 * Dual licensed under the MIT and GPL licenses.
 * http://benalman.com/about/license/
 *
 * The implementation included here is modified to support a callback
 * method that can accumulate values between actual invocations of
 * the throttled method.
 */

/**
 * Throttle execution of a function. Especially useful for rate limiting
 * execution of handlers on events like resize and scroll. If you want to
 * rate-limit execution of a function to a single time see
 * {@link geo.util.debounce}.
 *
 * In this visualization, | is a throttled-function call and X is the actual
 * callback execution:
 *
 * ```
 * Throttled with `no_trailing` specified as false or unspecified:
 * ||||||||||||||||||||||||| (pause) |||||||||||||||||||||||||
 * X    X    X    X    X    X        X    X    X    X    X    X
 *
 * Throttled with `no_trailing` specified as true:
 * ||||||||||||||||||||||||| (pause) |||||||||||||||||||||||||
 * X    X    X    X    X             X    X    X    X    X
 * ```
 *
 * This is also used to handle debouncing a function.
 *
 * @alias geo.util.throttle
 * @param {number} delay A zero-or-greater delay in milliseconds. For event
 *    callbacks, values around 100 or 250 (or even higher) are most useful.
 * @param {boolean} [no_trailing=false] If no_trailing is
 *    true, callback will only execute every `delay` milliseconds while the
 *    throttled-function is being called. If no_trailing is false or
 *    unspecified, callback will be executed one final time after the last
 *    throttled-function call. (After the throttled-function has not been
 *    called for `delay` milliseconds, the internal counter is reset)
 * @param {function} callback A function to be executed after `delay`
 *    milliseconds. The `this` context and all arguments are passed through,
 *    as-is, to `callback` when the throttled-function is executed.
 * @param {function} [accumulator] A function to be executed (synchronously)
 *    during **each** call to the wrapped function.  Typically, this
 *    this method is used to accumulate values that the callback uses
 *    when it finally executes.
 * @param {boolean} [debounce_mode] See the `at_begin` parameter of the
 *    {@link geo.util.debounce} function.
 * @returns {function} The throttled version of `callback`.
 *
 * @example
 * var throttled = geo.util.throttle( delay, [ no_trailing, ] callback );
 * $('selector').bind( 'someevent', throttled );
 * $('selector').unbind( 'someevent', throttled );
 */
var throttle = function throttle(delay, no_trailing, callback, accumulator, debounce_mode) {
  // After wrapper has stopped being called, this timeout ensures that
  // `callback` is executed at the proper times in `throttle` and `end`
  // debounce modes.
  var timeout_id,


  // Keep track of the last time `callback` was executed.
  last_exec = 0;

  // `no_trailing` defaults to falsy.
  if (typeof no_trailing !== 'boolean') {
    debounce_mode = accumulator;
    accumulator = callback;
    callback = no_trailing;
    no_trailing = undefined;
  }

  // accumulator defaults to no-op
  if (typeof accumulator !== 'function') {
    debounce_mode = accumulator;
    accumulator = function accumulator() {};
  }

  // The `wrapper` function encapsulates all of the throttling / debouncing
  // functionality and when executed will limit the rate at which `callback`
  // is executed.
  function wrapper() {
    var that = this,
        elapsed = +new Date() - last_exec,
        args = arguments;

    // Execute `callback` and update the `last_exec` timestamp.
    function exec() {
      last_exec = +new Date();
      callback.apply(that, args);
    }

    // If `debounce_mode` is true (at_begin) this is used to clear the flag
    // to allow future `callback` executions.
    function clear() {
      timeout_id = undefined;
    }

    // always call the accumulator first
    accumulator.apply(that, args);

    if (debounce_mode && !timeout_id) {
      // Since `wrapper` is being called for the first time and
      // `debounce_mode` is true (at_begin), execute `callback`.
      exec();
    }

    // Clear any existing timeout.
    void (timeout_id && clearTimeout(timeout_id));

    if (debounce_mode === undefined && elapsed > delay) {
      // In throttle mode, if `delay` time has been exceeded, execute
      // `callback`.
      exec();
    } else if (no_trailing !== true) {
      /*
       * In trailing throttle mode, since `delay` time has not been
       * exceeded, schedule `callback` to execute `delay` ms after most
       * recent execution.
       *
       * If `debounce_mode` is true (at_begin), schedule `clear` to execute
       * after `delay` ms.
       *
       * If `debounce_mode` is false (at end), schedule `callback` to
       * execute after `delay` ms.
       */
      timeout_id = setTimeout(debounce_mode ? clear : exec, debounce_mode === undefined ? delay - elapsed : delay);
    }
  }

  // Return the wrapper function.
  return wrapper;
};

/**
 * Debounce execution of a function. Debouncing, unlike throttling,
 * guarantees that a function is only executed a single time, either at the
 * very beginning of a series of calls, or at the very end. If you want to
 * simply rate-limit execution of a function, see the <jQuery.throttle>
 * method.
 *
 * In this visualization, | is a debounced-function call and X is the actual
 * callback execution:
 *
 * ::
 *
 *   Debounced with `at_begin` specified as false or unspecified:
 *   ||||||||||||||||||||||||| (pause) |||||||||||||||||||||||||
 *                            X                                 X
 *
 *   Debounced with `at_begin` specified as true:
 *   ||||||||||||||||||||||||| (pause) |||||||||||||||||||||||||
 *   X                                 X
 *
 * The bulk of the work is handled by the {@link geo.util.throttle} function.
 *
 * @method geo.util.debounce
 * @param {number} delay A zero-or-greater delay in milliseconds. For event
 *    callbacks, values around 100 or 250 (or even higher) are most useful.
 * @param {boolean} [at_begin=false] If at_begin is false or
 *    unspecified, callback will only be executed `delay` milliseconds after
 *    the last debounced-function call. If at_begin is true, callback will be
 *    executed only at the first debounced-function call. (After the
 *    throttled-function has not been called for `delay` milliseconds, the
 *    internal counter is reset)
 * @param {function} callback A function to be executed after delay milliseconds.
 *    The `this` context and all arguments are passed through, as-is, to
 *    `callback` when the debounced-function is executed.
 * @param {function} [accumulator] A function to be executed (synchronously)
 *    during **each** call to the wrapped function.  Typically, this
 *    this method is used to accumulate values that the callback uses
 *    when it finally executes.
 *
 * @returns {function} A new, debounced, function.
 *
 * @example
 * var debounced = geo.util.debounce( delay, [ at_begin, ] callback );
 * $('selector').bind( 'someevent', debounced );
 * $('selector').unbind( 'someevent', debounced );
 *
 */
var debounce = function debounce(delay, at_begin, callback, accumulator) {
  if (typeof at_begin !== 'boolean') {
    accumulator = callback;
    callback = at_begin;
    at_begin = false;
  }
  accumulator = accumulator || function () {};
  return throttle(delay, false, callback, accumulator, !!at_begin);
};

module.exports = {
  throttle: throttle,
  debounce: debounce
};

/***/ }),
/* 173 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* eslint-disable camelcase */
/* eslint-disable underscore/prefer-constant */

var $ = __webpack_require__(2);
var vgl = __webpack_require__(9);
var vglRenderer = __webpack_require__(113);

var _renderWindow, _supported;

module.exports = {};

/**
 * Replace vgl.renderer with a mocked version for testing in a non-webGL state.
 * Use restoreVGLRenderer to unmock.  Call vgl.mockCounts() to get the number
 * of times different webGL functions have been called.
 *
 * @param {boolean} [supported=true] If false, then the vgl renderer will
 *      indicate that this is an unsupported browser environment.
 * @alias geo.util.mockVGLRenderer
 */
module.exports.mockVGLRenderer = function mockVGLRenderer(supported) {
  'use strict';

  var vgl = __webpack_require__(9);

  if (supported === undefined) {
    supported = true;
  }

  if (vgl._mocked) {
    throw new Error('VGL renderer already mocked');
  }

  var mockCounts = {};
  var count = function count(name) {
    mockCounts[name] = (mockCounts[name] || 0) + 1;
  };
  var noop = function noop(name) {
    return function () {
      count(name);
    };
  };
  var _id = 0,
      incID = function incID(name) {
    return function () {
      count(name);
      _id += 1;
      return _id;
    };
  };
  /* The context largely does nothing. */
  var default_context = {
    activeTexture: noop('activeTexture'),
    attachShader: noop('attachShader'),
    bindAttribLocation: noop('bindAttribLocation'),
    bindBuffer: noop('bindBuffer'),
    bindFramebuffer: noop('bindFramebuffer'),
    bindTexture: noop('bindTexture'),
    blendFuncSeparate: noop('blendFuncSeparate'),
    bufferData: noop('bufferData'),
    bufferSubData: noop('bufferSubData'),
    checkFramebufferStatus: function checkFramebufferStatus(key) {
      count('checkFramebufferStatus');
      if (key === vgl.GL.FRAMEBUFFER) {
        return vgl.GL.FRAMEBUFFER_COMPLETE;
      }
    },
    clear: noop('clear'),
    clearColor: noop('clearColor'),
    clearDepth: noop('clearDepth'),
    compileShader: noop('compileShader'),
    createBuffer: incID('createBuffer'),
    createFramebuffer: noop('createFramebuffer'),
    createProgram: incID('createProgram'),
    createShader: incID('createShader'),
    createTexture: incID('createTexture'),
    deleteBuffer: noop('deleteBuffer'),
    deleteProgram: noop('deleteProgram'),
    deleteShader: noop('deleteShader'),
    deleteTexture: noop('deleteTexture'),
    depthFunc: noop('depthFunc'),
    disable: noop('disable'),
    disableVertexAttribArray: noop('disableVertexAttribArray'),
    drawArrays: noop('drawArrays'),
    enable: noop('enable'),
    enableVertexAttribArray: noop('enableVertexAttribArray'),
    finish: noop('finish'),
    getExtension: incID('getExtension'),
    getParameter: function getParameter(key) {
      count('getParameter');
      if (key === vgl.GL.DEPTH_BITS) {
        return 16;
      }
    },
    getProgramParameter: function getProgramParameter(id, key) {
      count('getProgramParameter');
      if (key === vgl.GL.LINK_STATUS) {
        return true;
      }
    },
    getShaderInfoLog: function getShaderInfoLog() {
      count('getShaderInfoLog');
      return 'log';
    },
    getShaderParameter: function getShaderParameter(id, key) {
      count('getShaderParameter');
      if (key === vgl.GL.COMPILE_STATUS) {
        return true;
      }
    },
    getUniformLocation: incID('getUniformLocation'),
    isEnabled: function isEnabled(key) {
      count('isEnabled');
      if (key === vgl.GL.BLEND) {
        return true;
      }
    },
    linkProgram: noop('linkProgram'),
    pixelStorei: noop('pixelStorei'),
    shaderSource: noop('shaderSource'),
    texImage2D: noop('texImage2D'),
    texParameteri: noop('texParameteri'),
    uniform1iv: noop('uniform1iv'),
    uniform1fv: noop('uniform1fv'),
    uniform2fv: noop('uniform2fv'),
    uniform3fv: noop('uniform3fv'),
    uniform4fv: noop('uniform4fv'),
    uniformMatrix3fv: noop('uniformMatrix3fv'),
    uniformMatrix4fv: noop('uniformMatrix4fv'),
    useProgram: noop('useProgram'),
    vertexAttribPointer: noop('vertexAttribPointer'),
    vertexAttrib3fv: noop('vertexAttrib3fv'),
    viewport: noop('viewport')
  };

  _renderWindow = vgl.renderWindow;
  var mockedRenderWindow = function mockedRenderWindow() {
    /* Temporarily put back the original definition of renderWindow so that the
     * class instance will be instantiated correctly. */
    vgl.renderWindow = _renderWindow;
    var m_this = new vgl.renderWindow(),
        m_context;
    vgl.renderWindow = mockedRenderWindow;

    m_this._setup = function () {
      var i,
          renderers = m_this.renderers(),
          wsize = m_this.windowSize(),
          wpos = m_this.windowPosition();

      m_context = $.extend({}, default_context);

      for (i = 0; i < renderers.length; i += 1) {
        if (renderers[i].width() > wsize[0] || renderers[i].width() === 0 || renderers[i].height() > wsize[1] || renderers[i].height() === 0) {
          renderers[i].resize(wpos[0], wpos[1], wsize[0], wsize[1]);
        }
      }
      return true;
    };
    m_this.context = function () {
      return m_context;
    };
    return m_this;
  };
  vgl.renderWindow = mockedRenderWindow;

  _supported = vglRenderer.supported;
  vglRenderer.supported = function () {
    return !!supported;
  };

  vgl._mocked = true;
  vgl.mockCounts = function () {
    return mockCounts;
  };
};

/**
 * Unmock the vgl renderer.
 * @alias geo.util.restoreVGLRenderer
 */
module.exports.restoreVGLRenderer = function () {
  if (vgl._mocked) {
    vgl.renderWindow = _renderWindow;
    vglRenderer.supported = _supported;
    delete vgl._mocked;
    delete vgl.mockCounts;
  }
};

/***/ }),
/* 174 */
/***/ (function(module, exports) {

var g;

// This works in non-strict mode
g = (function() {
	return this;
})();

try {
	// This works if eval is allowed (see CSP)
	g = g || Function("return this")() || (1,eval)("this");
} catch(e) {
	// This works if the window reference is available
	if(typeof window === "object")
		g = window;
}

// g can still be undefined, but nothing to do about it...
// We return undefined, instead of nothing here, so it's
// easier to handle this case. if(!global) { ...}

module.exports = g;


/***/ }),
/* 175 */
/***/ (function(module, exports, __webpack_require__) {

var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*** IMPORTS FROM imports-loader ***/
var mat4 = __webpack_require__(47);
var vec4 = __webpack_require__(98);
var vec3 = __webpack_require__(48);
var $ = __webpack_require__(2);

(function (root, factory) {
  if (true) {
    // AMD. Register as an anonymous module unless amdModuleId is set
    !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_RESULT__ = (function () {
      return (root['vgl'] = factory());
    }).apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
				__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
  } else if (typeof exports === 'object') {
    // Node. Does not work with strict CommonJS, but
    // only CommonJS-like environments that support module.exports,
    // like Node.
    module.exports = factory();
  } else {
    root['vgl'] = factory();
  }
}(this, function () {

//////////////////////////////////////////////////////////////////////////////
/**
 * @module vgl
 */

/* exported vgl, inherit */
//////////////////////////////////////////////////////////////////////////////

if (typeof ogs === 'undefined') {
  var ogs = {};
}

//////////////////////////////////////////////////////////////////////////////
/**
 * Create namespace for the given name
 *
 * @param ns_string
 * @returns {*|{}}
 */
//////////////////////////////////////////////////////////////////////////////
ogs.namespace = function (ns_string) {
  'use strict';

  var parts = ns_string.split('.'), parent = ogs, i;

  // strip redundant leading global
  if (parts[0] === 'ogs') {
    parts = parts.slice(1);
  }
  for (i = 0; i < parts.length; i += 1) {
    // create a property if it doesn't exist
    if (typeof parent[parts[i]] === 'undefined') {
      parent[parts[i]] = {};
    }
    parent = parent[parts[i]];
  }
  return parent;
};

/** vgl namespace */
var vgl = ogs.namespace('gl');
window.vgl = vgl;

//////////////////////////////////////////////////////////////////////////////
/**
 * Convenient function to define JS inheritance
 */
//////////////////////////////////////////////////////////////////////////////
vgl.inherit = function (C, P) {
  'use strict';

  var F = inherit.func();
  F.prototype = P.prototype;
  C.prototype = new F();
  C.prototype.constructor = C;
};
vgl.inherit.func = function () {
  'use strict';
  return function () {};
};

window.inherit = vgl.inherit;

//////////////////////////////////////////////////////////////////////////////
/**
 * Convenient function to get size of an object
 *
 * @param obj
 * @returns {number} *
 */
//////////////////////////////////////////////////////////////////////////////
Object.size = function (obj) {
  'use strict';

  var size = 0, key;
  for (key in obj) {
    if (obj.hasOwnProperty(key)) {
      size += 1;
    }
  }
  return size;
};

/* Polyfill for Math.log2 */
if (!Math.log2) {
  Math.log2 = function (val) {
    return Math.log(val) / Math.log(2);
  };
}

vgl.version = '0.3.10';

//////////////////////////////////////////////////////////////////////////////
/**
 * @module vgl
 */

/*global vgl*/
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
/**
 * Wrap GL enums. Currently to get values of the enums we need to create
 * or access the context.
 *
 * Using enums from here:
 * https://github.com/toji/dart-gl-enums/blob/master/lib/gl_enums.dart
 *
 * @class
 */
//////////////////////////////////////////////////////////////////////////////
vgl.GL = {
  ACTIVE_ATTRIBUTES : 0x8B89,
  ACTIVE_TEXTURE : 0x84E0,
  ACTIVE_UNIFORMS : 0x8B86,
  ALIASED_LINE_WIDTH_RANGE : 0x846E,
  ALIASED_POINT_SIZE_RANGE : 0x846D,
  ALPHA : 0x1906,
  ALPHA_BITS : 0x0D55,
  ALWAYS : 0x0207,
  ARRAY_BUFFER : 0x8892,
  ARRAY_BUFFER_BINDING : 0x8894,
  ATTACHED_SHADERS : 0x8B85,
  BACK : 0x0405,
  BLEND : 0x0BE2,
  BLEND_COLOR : 0x8005,
  BLEND_DST_ALPHA : 0x80CA,
  BLEND_DST_RGB : 0x80C8,
  BLEND_EQUATION : 0x8009,
  BLEND_EQUATION_ALPHA : 0x883D,
  BLEND_EQUATION_RGB : 0x8009,
  BLEND_SRC_ALPHA : 0x80CB,
  BLEND_SRC_RGB : 0x80C9,
  BLUE_BITS : 0x0D54,
  BOOL : 0x8B56,
  BOOL_VEC2 : 0x8B57,
  BOOL_VEC3 : 0x8B58,
  BOOL_VEC4 : 0x8B59,
  BROWSER_DEFAULT_WEBGL : 0x9244,
  BUFFER_SIZE : 0x8764,
  BUFFER_USAGE : 0x8765,
  BYTE : 0x1400,
  CCW : 0x0901,
  CLAMP_TO_EDGE : 0x812F,
  COLOR_ATTACHMENT0 : 0x8CE0,
  COLOR_BUFFER_BIT : 0x00004000,
  COLOR_CLEAR_VALUE : 0x0C22,
  COLOR_WRITEMASK : 0x0C23,
  COMPILE_STATUS : 0x8B81,
  COMPRESSED_TEXTURE_FORMATS : 0x86A3,
  CONSTANT_ALPHA : 0x8003,
  CONSTANT_COLOR : 0x8001,
  CONTEXT_LOST_WEBGL : 0x9242,
  CULL_FACE : 0x0B44,
  CULL_FACE_MODE : 0x0B45,
  CURRENT_PROGRAM : 0x8B8D,
  CURRENT_VERTEX_ATTRIB : 0x8626,
  CW : 0x0900,
  DECR : 0x1E03,
  DECR_WRAP : 0x8508,
  DELETE_STATUS : 0x8B80,
  DEPTH_ATTACHMENT : 0x8D00,
  DEPTH_BITS : 0x0D56,
  DEPTH_BUFFER_BIT : 0x00000100,
  DEPTH_CLEAR_VALUE : 0x0B73,
  DEPTH_COMPONENT : 0x1902,
  DEPTH_COMPONENT16 : 0x81A5,
  DEPTH_FUNC : 0x0B74,
  DEPTH_RANGE : 0x0B70,
  DEPTH_STENCIL : 0x84F9,
  DEPTH_STENCIL_ATTACHMENT : 0x821A,
  DEPTH_TEST : 0x0B71,
  DEPTH_WRITEMASK : 0x0B72,
  DITHER : 0x0BD0,
  DONT_CARE : 0x1100,
  DST_ALPHA : 0x0304,
  DST_COLOR : 0x0306,
  DYNAMIC_DRAW : 0x88E8,
  ELEMENT_ARRAY_BUFFER : 0x8893,
  ELEMENT_ARRAY_BUFFER_BINDING : 0x8895,
  EQUAL : 0x0202,
  FASTEST : 0x1101,
  FLOAT : 0x1406,
  FLOAT_MAT2 : 0x8B5A,
  FLOAT_MAT3 : 0x8B5B,
  FLOAT_MAT4 : 0x8B5C,
  FLOAT_VEC2 : 0x8B50,
  FLOAT_VEC3 : 0x8B51,
  FLOAT_VEC4 : 0x8B52,
  FRAGMENT_SHADER : 0x8B30,
  FRAMEBUFFER : 0x8D40,
  FRAMEBUFFER_ATTACHMENT_OBJECT_NAME : 0x8CD1,
  FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE : 0x8CD0,
  FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE : 0x8CD3,
  FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL : 0x8CD2,
  FRAMEBUFFER_BINDING : 0x8CA6,
  FRAMEBUFFER_COMPLETE : 0x8CD5,
  FRAMEBUFFER_INCOMPLETE_ATTACHMENT : 0x8CD6,
  FRAMEBUFFER_INCOMPLETE_DIMENSIONS : 0x8CD9,
  FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT : 0x8CD7,
  FRAMEBUFFER_UNSUPPORTED : 0x8CDD,
  FRONT : 0x0404,
  FRONT_AND_BACK : 0x0408,
  FRONT_FACE : 0x0B46,
  FUNC_ADD : 0x8006,
  FUNC_REVERSE_SUBTRACT : 0x800B,
  FUNC_SUBTRACT : 0x800A,
  GENERATE_MIPMAP_HINT : 0x8192,
  GEQUAL : 0x0206,
  GREATER : 0x0204,
  GREEN_BITS : 0x0D53,
  HIGH_FLOAT : 0x8DF2,
  HIGH_INT : 0x8DF5,
  INCR : 0x1E02,
  INCR_WRAP : 0x8507,
  INT : 0x1404,
  INT_VEC2 : 0x8B53,
  INT_VEC3 : 0x8B54,
  INT_VEC4 : 0x8B55,
  INVALID_ENUM : 0x0500,
  INVALID_FRAMEBUFFER_OPERATION : 0x0506,
  INVALID_OPERATION : 0x0502,
  INVALID_VALUE : 0x0501,
  INVERT : 0x150A,
  KEEP : 0x1E00,
  LEQUAL : 0x0203,
  LESS : 0x0201,
  LINEAR : 0x2601,
  LINEAR_MIPMAP_LINEAR : 0x2703,
  LINEAR_MIPMAP_NEAREST : 0x2701,
  LINES : 0x0001,
  LINE_LOOP : 0x0002,
  LINE_STRIP : 0x0003,
  LINE_WIDTH : 0x0B21,
  LINK_STATUS : 0x8B82,
  LOW_FLOAT : 0x8DF0,
  LOW_INT : 0x8DF3,
  LUMINANCE : 0x1909,
  LUMINANCE_ALPHA : 0x190A,
  MAX_COMBINED_TEXTURE_IMAGE_UNITS : 0x8B4D,
  MAX_CUBE_MAP_TEXTURE_SIZE : 0x851C,
  MAX_FRAGMENT_UNIFORM_VECTORS : 0x8DFD,
  MAX_RENDERBUFFER_SIZE : 0x84E8,
  MAX_TEXTURE_IMAGE_UNITS : 0x8872,
  MAX_TEXTURE_SIZE : 0x0D33,
  MAX_VARYING_VECTORS : 0x8DFC,
  MAX_VERTEX_ATTRIBS : 0x8869,
  MAX_VERTEX_TEXTURE_IMAGE_UNITS : 0x8B4C,
  MAX_VERTEX_UNIFORM_VECTORS : 0x8DFB,
  MAX_VIEWPORT_DIMS : 0x0D3A,
  MEDIUM_FLOAT : 0x8DF1,
  MEDIUM_INT : 0x8DF4,
  MIRRORED_REPEAT : 0x8370,
  NEAREST : 0x2600,
  NEAREST_MIPMAP_LINEAR : 0x2702,
  NEAREST_MIPMAP_NEAREST : 0x2700,
  NEVER : 0x0200,
  NICEST : 0x1102,
  NONE : 0,
  NOTEQUAL : 0x0205,
  NO_ERROR : 0,
  ONE : 1,
  ONE_MINUS_CONSTANT_ALPHA : 0x8004,
  ONE_MINUS_CONSTANT_COLOR : 0x8002,
  ONE_MINUS_DST_ALPHA : 0x0305,
  ONE_MINUS_DST_COLOR : 0x0307,
  ONE_MINUS_SRC_ALPHA : 0x0303,
  ONE_MINUS_SRC_COLOR : 0x0301,
  OUT_OF_MEMORY : 0x0505,
  PACK_ALIGNMENT : 0x0D05,
  POINTS : 0x0000,
  POLYGON_OFFSET_FACTOR : 0x8038,
  POLYGON_OFFSET_FILL : 0x8037,
  POLYGON_OFFSET_UNITS : 0x2A00,
  RED_BITS : 0x0D52,
  RENDERBUFFER : 0x8D41,
  RENDERBUFFER_ALPHA_SIZE : 0x8D53,
  RENDERBUFFER_BINDING : 0x8CA7,
  RENDERBUFFER_BLUE_SIZE : 0x8D52,
  RENDERBUFFER_DEPTH_SIZE : 0x8D54,
  RENDERBUFFER_GREEN_SIZE : 0x8D51,
  RENDERBUFFER_HEIGHT : 0x8D43,
  RENDERBUFFER_INTERNAL_FORMAT : 0x8D44,
  RENDERBUFFER_RED_SIZE : 0x8D50,
  RENDERBUFFER_STENCIL_SIZE : 0x8D55,
  RENDERBUFFER_WIDTH : 0x8D42,
  RENDERER : 0x1F01,
  REPEAT : 0x2901,
  REPLACE : 0x1E01,
  RGB : 0x1907,
  RGB565 : 0x8D62,
  RGB5_A1 : 0x8057,
  RGBA : 0x1908,
  RGBA4 : 0x8056,
  SAMPLER_2D : 0x8B5E,
  SAMPLER_CUBE : 0x8B60,
  SAMPLES : 0x80A9,
  SAMPLE_ALPHA_TO_COVERAGE : 0x809E,
  SAMPLE_BUFFERS : 0x80A8,
  SAMPLE_COVERAGE : 0x80A0,
  SAMPLE_COVERAGE_INVERT : 0x80AB,
  SAMPLE_COVERAGE_VALUE : 0x80AA,
  SCISSOR_BOX : 0x0C10,
  SCISSOR_TEST : 0x0C11,
  SHADER_TYPE : 0x8B4F,
  SHADING_LANGUAGE_VERSION : 0x8B8C,
  SHORT : 0x1402,
  SRC_ALPHA : 0x0302,
  SRC_ALPHA_SATURATE : 0x0308,
  SRC_COLOR : 0x0300,
  STATIC_DRAW : 0x88E4,
  STENCIL_ATTACHMENT : 0x8D20,
  STENCIL_BACK_FAIL : 0x8801,
  STENCIL_BACK_FUNC : 0x8800,
  STENCIL_BACK_PASS_DEPTH_FAIL : 0x8802,
  STENCIL_BACK_PASS_DEPTH_PASS : 0x8803,
  STENCIL_BACK_REF : 0x8CA3,
  STENCIL_BACK_VALUE_MASK : 0x8CA4,
  STENCIL_BACK_WRITEMASK : 0x8CA5,
  STENCIL_BITS : 0x0D57,
  STENCIL_BUFFER_BIT : 0x00000400,
  STENCIL_CLEAR_VALUE : 0x0B91,
  STENCIL_FAIL : 0x0B94,
  STENCIL_FUNC : 0x0B92,
  STENCIL_INDEX : 0x1901,
  STENCIL_INDEX8 : 0x8D48,
  STENCIL_PASS_DEPTH_FAIL : 0x0B95,
  STENCIL_PASS_DEPTH_PASS : 0x0B96,
  STENCIL_REF : 0x0B97,
  STENCIL_TEST : 0x0B90,
  STENCIL_VALUE_MASK : 0x0B93,
  STENCIL_WRITEMASK : 0x0B98,
  STREAM_DRAW : 0x88E0,
  SUBPIXEL_BITS : 0x0D50,
  TEXTURE : 0x1702,
  TEXTURE0 : 0x84C0,
  TEXTURE1 : 0x84C1,
  TEXTURE10 : 0x84CA,
  TEXTURE11 : 0x84CB,
  TEXTURE12 : 0x84CC,
  TEXTURE13 : 0x84CD,
  TEXTURE14 : 0x84CE,
  TEXTURE15 : 0x84CF,
  TEXTURE16 : 0x84D0,
  TEXTURE17 : 0x84D1,
  TEXTURE18 : 0x84D2,
  TEXTURE19 : 0x84D3,
  TEXTURE2 : 0x84C2,
  TEXTURE20 : 0x84D4,
  TEXTURE21 : 0x84D5,
  TEXTURE22 : 0x84D6,
  TEXTURE23 : 0x84D7,
  TEXTURE24 : 0x84D8,
  TEXTURE25 : 0x84D9,
  TEXTURE26 : 0x84DA,
  TEXTURE27 : 0x84DB,
  TEXTURE28 : 0x84DC,
  TEXTURE29 : 0x84DD,
  TEXTURE3 : 0x84C3,
  TEXTURE30 : 0x84DE,
  TEXTURE31 : 0x84DF,
  TEXTURE4 : 0x84C4,
  TEXTURE5 : 0x84C5,
  TEXTURE6 : 0x84C6,
  TEXTURE7 : 0x84C7,
  TEXTURE8 : 0x84C8,
  TEXTURE9 : 0x84C9,
  TEXTURE_2D : 0x0DE1,
  TEXTURE_BINDING_2D : 0x8069,
  TEXTURE_BINDING_CUBE_MAP : 0x8514,
  TEXTURE_CUBE_MAP : 0x8513,
  TEXTURE_CUBE_MAP_NEGATIVE_X : 0x8516,
  TEXTURE_CUBE_MAP_NEGATIVE_Y : 0x8518,
  TEXTURE_CUBE_MAP_NEGATIVE_Z : 0x851A,
  TEXTURE_CUBE_MAP_POSITIVE_X : 0x8515,
  TEXTURE_CUBE_MAP_POSITIVE_Y : 0x8517,
  TEXTURE_CUBE_MAP_POSITIVE_Z : 0x8519,
  TEXTURE_MAG_FILTER : 0x2800,
  TEXTURE_MIN_FILTER : 0x2801,
  TEXTURE_WRAP_S : 0x2802,
  TEXTURE_WRAP_T : 0x2803,
  TRIANGLES : 0x0004,
  TRIANGLE_FAN : 0x0006,
  TRIANGLE_STRIP : 0x0005,
  UNPACK_ALIGNMENT : 0x0CF5,
  UNPACK_COLORSPACE_CONVERSION_WEBGL : 0x9243,
  UNPACK_FLIP_Y_WEBGL : 0x9240,
  UNPACK_PREMULTIPLY_ALPHA_WEBGL : 0x9241,
  UNSIGNED_BYTE : 0x1401,
  UNSIGNED_INT : 0x1405,
  UNSIGNED_SHORT : 0x1403,
  UNSIGNED_SHORT_4_4_4_4 : 0x8033,
  UNSIGNED_SHORT_5_5_5_1 : 0x8034,
  UNSIGNED_SHORT_5_6_5 : 0x8363,
  VALIDATE_STATUS : 0x8B83,
  VENDOR : 0x1F00,
  VERSION : 0x1F02,
  VERTEX_ATTRIB_ARRAY_BUFFER_BINDING : 0x889F,
  VERTEX_ATTRIB_ARRAY_ENABLED : 0x8622,
  VERTEX_ATTRIB_ARRAY_NORMALIZED : 0x886A,
  VERTEX_ATTRIB_ARRAY_POINTER : 0x8645,
  VERTEX_ATTRIB_ARRAY_SIZE : 0x8623,
  VERTEX_ATTRIB_ARRAY_STRIDE : 0x8624,
  VERTEX_ATTRIB_ARRAY_TYPE : 0x8625,
  VERTEX_SHADER : 0x8B31,
  VIEWPORT : 0x0BA2,
  ZERO : 0
};

//////////////////////////////////////////////////////////////////////////////
/**
 * @module vgl
 */

/*global vgl*/
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class timestamp
 *
 * @class
 * @returns {vgl.timestamp}
 */
//////////////////////////////////////////////////////////////////////////////
var m_globalModifiedTime = 0;

vgl.timestamp = function () {
  'use strict';

  if (!(this instanceof vgl.timestamp)) {
    return new vgl.timestamp();
  }

  var m_modifiedTime = 0;

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Update modified time
   */
  /////////////////////////////////////////////////////////////////////////////
  this.modified = function () {
    m_globalModifiedTime += 1;
    m_modifiedTime = m_globalModifiedTime;
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Get modified time
   *
   * @returns {number}
   */
  /////////////////////////////////////////////////////////////////////////////
  this.getMTime = function () {
    return m_modifiedTime;
  };
};

//////////////////////////////////////////////////////////////////////////////
/**
 * @module vgl
 */

/*global vgl*/
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class object
 *
 * @class
 * @returns {vgl.object}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.object = function () {
  'use strict';

  if (!(this instanceof vgl.object)) {
    return new vgl.object();
  }

  /** @private */
  var m_modifiedTime = vgl.timestamp();
  m_modifiedTime.modified();

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Mark the object modified
   */
  ////////////////////////////////////////////////////////////////////////////
  this.modified = function () {
    m_modifiedTime.modified();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return modified time of the object
   *
   * @returns {*}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.getMTime = function () {
    return m_modifiedTime.getMTime();
  };

  return this;
};

//////////////////////////////////////////////////////////////////////////////
/**
 * @module vgl
 */

/*global vgl, inherit*/
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class event
 *
 * @class event
 * @returns {vgl.event}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.event = function () {
  'use strict';

  if (!(this instanceof vgl.event)) {
    return new vgl.event();
  }
  vgl.object.call(this);

  return this;
};

inherit(vgl.event, vgl.object);

//////////////////////////////////////////////////////////////////////////////
/**
 *  types
 */
//////////////////////////////////////////////////////////////////////////////
vgl.event.keyPress = 'vgl.event.keyPress';
vgl.event.mousePress = 'vgl.event.mousePress';
vgl.event.mouseRelease = 'vgl.event.mouseRelease';
vgl.event.contextMenu = 'vgl.event.contextMenu';
vgl.event.configure = 'vgl.event.configure';
vgl.event.enable = 'vgl.event.enable';
vgl.event.mouseWheel = 'vgl.event.mouseWheel';
vgl.event.keyRelease = 'vgl.event.keyRelease';
vgl.event.middleButtonPress = 'vgl.event.middleButtonPress';
vgl.event.startInteraction = 'vgl.event.startInteraction';
vgl.event.enter = 'vgl.event.enter';
vgl.event.rightButtonPress = 'vgl.event.rightButtonPress';
vgl.event.middleButtonRelease = 'vgl.event.middleButtonRelease';
vgl.event.char = 'vgl.event.char';
vgl.event.disable = 'vgl.event.disable';
vgl.event.endInteraction = 'vgl.event.endInteraction';
vgl.event.mouseMove = 'vgl.event.mouseMove';
vgl.event.mouseOut = 'vgl.event.mouseOut';
vgl.event.expose = 'vgl.event.expose';
vgl.event.timer = 'vgl.event.timer';
vgl.event.leftButtonPress = 'vgl.event.leftButtonPress';
vgl.event.leave = 'vgl.event.leave';
vgl.event.rightButtonRelease = 'vgl.event.rightButtonRelease';
vgl.event.leftButtonRelease = 'vgl.event.leftButtonRelease';
vgl.event.click = 'vgl.event.click';
vgl.event.dblClick = 'vgl.event.dblClick';

//////////////////////////////////////////////////////////////////////////////
/**
 * @module vgl
 */

/*global vgl, inherit*/
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class boundingObject
 *
 * @class
 * @return {vgl.boundingObject}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.boundingObject = function () {
  'use strict';

  if (!(this instanceof vgl.boundingObject)) {
    return new vgl.boundingObject();
  }
  vgl.object.call(this);

  /** @private */
  var m_bounds = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
      m_computeBoundsTimestamp = vgl.timestamp(),
      m_boundsDirtyTimestamp = vgl.timestamp();

  m_computeBoundsTimestamp.modified();
  m_boundsDirtyTimestamp.modified();

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get current bounds of the object
   */
  ////////////////////////////////////////////////////////////////////////////
  this.bounds = function () {
    return m_bounds;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Check if bounds are valid
   */
  ////////////////////////////////////////////////////////////////////////////
  this.hasValidBounds = function (bounds) {
    if (bounds[0] === Number.MAX_VALUE ||
        bounds[1] === -Number.MAX_VALUE ||
        bounds[2] === Number.MAX_VALUE ||
        bounds[3] === -Number.MAX_VALUE ||
        bounds[4] === Number.MAX_VALUE ||
        bounds[5] === -Number.MAX_VALUE) {
      return false;
    }

    return true;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set current bounds of the object
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setBounds = function (minX, maxX, minY, maxY, minZ, maxZ) {
    if (!this.hasValidBounds([minX, maxX, minY, maxY, minZ, maxZ])) {
      return;
    }

    m_bounds[0] = minX;
    m_bounds[1] = maxX;
    m_bounds[2] = minY;
    m_bounds[3] = maxY;
    m_bounds[4] = minZ;
    m_bounds[5] = maxZ;

    this.modified();
    m_computeBoundsTimestamp.modified();

    return true;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Reset bounds to default values
   */
  ////////////////////////////////////////////////////////////////////////////
  this.resetBounds = function () {
    m_bounds[0] = Number.MAX_VALUE;
    m_bounds[1] = -Number.MAX_VALUE;
    m_bounds[2] = Number.MAX_VALUE;
    m_bounds[3] = -Number.MAX_VALUE;
    m_bounds[4] = Number.MAX_VALUE;
    m_bounds[5] = -Number.MAX_VALUE;

    this.modified();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Compute bounds of the object
   *
   * Should be implemented by the concrete class
   */
  ////////////////////////////////////////////////////////////////////////////
  this.computeBounds = function () {
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return bounds computation modification time
   *
   * @returns {vgl.timestamp}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.computeBoundsTimestamp = function () {
    return m_computeBoundsTimestamp;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return bounds dirty timestamp
   *
   * @returns {vgl.timestamp}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.boundsDirtyTimestamp = function () {
    return m_boundsDirtyTimestamp;
  };

  this.resetBounds();

  return this;
};

vgl.boundingObject.ReferenceFrame = {
  'Relative' : 0,
  'Absolute' : 1
};

inherit(vgl.boundingObject, vgl.object);

//////////////////////////////////////////////////////////////////////////////
/**
 * @module vgl
 */

/*global vgl, inherit*/
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class node
 *
 * @class
 * @returns {vgl.node}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.node = function () {
  'use strict';

  if (!(this instanceof vgl.node)) {
    return new vgl.node();
  }
  vgl.boundingObject.call(this);

  /** @private */
  var m_parent = null,
      m_material = null,
      m_visible = true,
      m_overlay = false;

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Accept visitor for scene traversal
   */
  ////////////////////////////////////////////////////////////////////////////
  this.accept = function (visitor) {
    visitor.visit(this);
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return active material used by the node
   */
  ////////////////////////////////////////////////////////////////////////////
  this.material = function () {
    return m_material;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set material to be used the node
   *
   * @param material
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setMaterial = function (material) {
    if (material !== m_material) {
      m_material = material;
      this.modified();
      return true;
    }

    return false;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Check if the node is visible or node
   *
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.visible = function () {
    return m_visible;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Turn ON/OFF visibility of the node
   *
   * @param flag
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setVisible = function (flag) {
    if (flag !== m_visible) {
      m_visible = flag;
      this.modified();
      return true;
    }

    return false;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return current parent of the node
   *
   * @returns {null}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.parent = function () {
    return m_parent;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set parent of the node
   *
   * @param parent
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setParent = function (parent) {
    if (parent !== m_parent) {
      if (m_parent !== null) {
        m_parent.removeChild(this);
      }
      m_parent = parent;
      this.modified();
      return true;
    }

    return false;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Check if the node is an overlay node
   *
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.overlay = function () {
    return m_overlay;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set if the node is an overlay node or not
   *
   * @param flag
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setOverlay = function (flag) {
    if (m_overlay !== flag) {
      m_overlay = flag;
      this.modified();
      return true;
    }

    return false;
  };

  ////////////////////////////////////////////////////////////////////////////
  /*
   * Traverse parent and their parent and so on
   */
  ////////////////////////////////////////////////////////////////////////////
  this.ascend = function (visitor) {
    visitor = visitor; /* unused parameter */
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Traverse children
   */
  ////////////////////////////////////////////////////////////////////////////
  this.traverse = function (visitor) {
    visitor = visitor; /* unused parameter */
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Mark that the bounds are modified
   *
   */
  ////////////////////////////////////////////////////////////////////////////
  this.boundsModified = function () {
    // @todo Implement this
    this.boundsDirtyTimestamp().modified();

    if (m_parent !== null) {
      m_parent.boundsModified();
    }
  };

  return this;
};

inherit(vgl.node, vgl.boundingObject);

//////////////////////////////////////////////////////////////////////////////
/**
 * @module vgl
 */

/*global vgl, inherit*/
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class groupNode
 *
 * @class
 * @returns {vgl.groupNode}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.groupNode = function () {
  'use strict';

  if (!(this instanceof vgl.groupNode)) {
    return new vgl.groupNode();
  }
  vgl.node.call(this);

  var m_children = [];

  // Reference to base class methods
  this.b_setVisible = this.setVisible;

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Turn on / off visibility
   *
   * @param flag
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setVisible = function (flag) {
    var i;

    if (this.b_setVisible(flag) !== true) {
      return false;
    }

    for (i = 0; i < m_children.length; i += 1) {
      m_children[i].setVisible(flag);
    }

    return true;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Make the incoming node as child of the group node
   *
   * @param childNode
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.addChild = function (childNode) {
    if (childNode instanceof vgl.node) {
      if (m_children.indexOf(childNode) === -1) {
        childNode.setParent(this);
        m_children.push(childNode);
        this.boundsDirtyTimestamp().modified();
        return true;
      }
      return false;
    }

    return false;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Remove parent-child relationship between the group and incoming node
   *
   * @param childNode
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.removeChild = function (childNode) {
    if (childNode.parent() === this) {
      var index = m_children.indexOf(childNode);
      if (index >= 0) {
        m_children.splice(index, 1);
        childNode.setParent(null);
        this.boundsDirtyTimestamp().modified();
        return true;
      }
    }
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Remove parent-child relationship between child nodes and the group node
   */
  ////////////////////////////////////////////////////////////////////////////
  this.removeChildren = function () {
    while (m_children.length) {
      this.removeChild(m_children[0]);
    }

    this.modified();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return children of this group node
   *
   * @returns {Array}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.children = function () {
    return m_children;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return true if this group node has node as a child, false otherwise.
   *
   * @param node
   * @returns {bool}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.hasChild = function (node) {
    var i = 0, child = false;

    for (i = 0; i < m_children.length; i += 1) {
      if (m_children[i] === node) {
        child = true;
        break;
      }
    }

    return child;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Accept a visitor and traverse the scene tree
   *
   * @param visitor
   */
  ////////////////////////////////////////////////////////////////////////////
  this.accept = function (visitor) {
    visitor.visit(this);
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Traverse the scene
   *
   * @param visitor
   */
  ////////////////////////////////////////////////////////////////////////////
  this.traverse = function (visitor) {
    switch (visitor.type()) {
      case visitor.UpdateVisitor:
        this.traverseChildrenAndUpdateBounds(visitor);
        break;
      case visitor.CullVisitor:
        this.traverseChildren(visitor);
        break;
      default:
        break;
    }
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Traverse all of the children and update the bounds for each
   *
   * @param visitor
   */
  ////////////////////////////////////////////////////////////////////////////
  this.traverseChildrenAndUpdateBounds = function (visitor) {
    var i;

    if (this.parent() && this.boundsDirtyTimestamp().getMTime() >
      this.computeBoundsTimestamp().getMTime()) {
      // Flag parents bounds dirty.
      this.parent().boundsDirtyTimestamp().modified();
    }

    this.computeBounds();

    if (visitor.mode() === visitor.TraverseAllChildren) {
      for (i = 0; i < m_children.length; i += 1) {
        m_children[i].accept(visitor);
        this.updateBounds(m_children[i]);
      }
    }

    this.computeBoundsTimestamp().modified();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Traverse children of the group node
   *
   * @param visitor
   */
  ////////////////////////////////////////////////////////////////////////////
  this.traverseChildren = function (visitor) {
    var i;

    if (visitor.mode() === visitor.TraverseAllChildren) {
      for (i = 0; i < m_children.length; i += 1) {
        m_children[i].accept(visitor);
      }
    }
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Compute bounds for the group node
   */
  ////////////////////////////////////////////////////////////////////////////
  this.computeBounds = function () {
    var i = 0;

    if (this.computeBoundsTimestamp().getMTime() >
        this.boundsDirtyTimestamp().getMTime()) {
      return;
    }

    for (i = 0; i < m_children.length; i += 1) {
      this.updateBounds(m_children[i]);
    }
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Update bounds for the group node
   *
   * This method is used internally to update bounds of the group node by
   * traversing each of its child.
   *
   * @param child
   */
  ////////////////////////////////////////////////////////////////////////////
  this.updateBounds = function (child) {
    // FIXME: This check should not be required and possibly is incorrect
    if (child.overlay()) {
      return;
    }

    // Make sure that child bounds are up to date
    child.computeBounds();

    var bounds = this.bounds(),
        childBounds = child.bounds(),
        istep = 0,
        jstep = 0,
        i;

    for (i = 0; i < 3; i += 1) {
      istep = i * 2;
      jstep = i * 2 + 1;
      if (childBounds[istep] < bounds[istep]) {
        bounds[istep] = childBounds[istep];
      }
      if (childBounds[jstep] > bounds[jstep]) {
        bounds[jstep] = childBounds[jstep];
      }
    }

    this.setBounds(bounds[0], bounds[1], bounds[2], bounds[3],
                   bounds[4], bounds[5]);
  };

  return this;
};

inherit(vgl.groupNode, vgl.node);

//////////////////////////////////////////////////////////////////////////////
/**
 * @module vgl
 */

/*global vgl, vec3, mat4, inherit*/
//////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class actor
 *
 * @class
 * @returns {vgl.actor}
 */
////////////////////////////////////////////////////////////////////////////
vgl.actor = function () {
  'use strict';

  if (!(this instanceof vgl.actor)) {
    return new vgl.actor();
  }
  vgl.node.call(this);

  /** @private */
  var m_this = this,
      m_transformMatrix = mat4.create(),
      m_referenceFrame = vgl.boundingObject.ReferenceFrame.Relative,
      m_mapper = null;

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get transformation matrix used by the actor
   *
   * @returns {mat4}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.matrix = function () {
    return m_transformMatrix;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set transformation matrix for the actor
   *
   * @param {mat4} 4X4 transformation matrix
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setMatrix = function (tmatrix) {
    if (tmatrix !== m_transformMatrix) {
      m_transformMatrix = tmatrix;
      m_this.modified();
    }
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get reference frame for the transformations
   *
   * @returns {String} Possible values are Absolute or Relative
   */
  ////////////////////////////////////////////////////////////////////////////
  this.referenceFrame = function () {
    return m_referenceFrame;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set reference frame for the transformations
   *
   * @param {vgl.boundingObject.ReferenceFrame}
   * referenceFrame Possible values are (Absolute | Relative)
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setReferenceFrame = function (referenceFrame) {
    if (referenceFrame !== m_referenceFrame) {
      m_referenceFrame = referenceFrame;
      m_this.modified();
      return true;
    }
    return false;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return mapper where actor gets it behavior and data
   *
   * @returns {vgl.mapper}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.mapper = function () {
    return m_mapper;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Connect an actor to its data source
   *
   * @param {vgl.mapper}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setMapper = function (mapper) {
    if (mapper !== m_mapper) {
      m_mapper = mapper;
      m_this.boundsModified();
    }
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * @todo
   */
  ////////////////////////////////////////////////////////////////////////////
  this.accept = function (visitor) {
    visitor = visitor; /* ignore this parameter */
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * @todo
   */
  ////////////////////////////////////////////////////////////////////////////
  this.ascend = function (visitor) {
    visitor = visitor; /* ignore this parameter */
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Compute object space to world space matrix
   * @todo
   */
  ////////////////////////////////////////////////////////////////////////////
  this.computeLocalToWorldMatrix = function (matrix, visitor) {
    matrix = matrix; /* ignore this parameter */
    visitor = visitor; /* ignore this parameter */
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Compute world space to object space matrix
   * @todo
   */
  ////////////////////////////////////////////////////////////////////////////
  this.computeWorldToLocalMatrix = function (matrix, visitor) {
    matrix = matrix; /* ignore this parameter */
    visitor = visitor; /* ignore this parameter */
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Compute actor bounds
   */
  ////////////////////////////////////////////////////////////////////////////
  this.computeBounds = function () {
    if (m_mapper === null || m_mapper === undefined) {
      m_this.resetBounds();
      return;
    }

    var computeBoundsTimestamp = m_this.computeBoundsTimestamp(),
        mapperBounds, minPt, maxPt, newBounds;

    if (m_this.boundsDirtyTimestamp().getMTime() > computeBoundsTimestamp.getMTime() ||
      m_mapper.boundsDirtyTimestamp().getMTime() > computeBoundsTimestamp.getMTime()) {

      m_mapper.computeBounds();
      mapperBounds = m_mapper.bounds();

      minPt = [mapperBounds[0], mapperBounds[2], mapperBounds[4]];
      maxPt = [mapperBounds[1], mapperBounds[3], mapperBounds[5]];

      vec3.transformMat4(minPt, minPt, m_transformMatrix);
      vec3.transformMat4(maxPt, maxPt, m_transformMatrix);

      newBounds = [
        minPt[0] > maxPt[0] ? maxPt[0] : minPt[0],
        minPt[0] > maxPt[0] ? minPt[0] : maxPt[0],
        minPt[1] > maxPt[1] ? maxPt[1] : minPt[1],
        minPt[1] > maxPt[1] ? minPt[1] : maxPt[1],
        minPt[2] > maxPt[2] ? maxPt[2] : minPt[2],
        minPt[2] > maxPt[2] ? minPt[2] : maxPt[2]
      ];

      m_this.setBounds(newBounds[0], newBounds[1],
                     newBounds[2], newBounds[3],
                     newBounds[4], newBounds[5]);

      computeBoundsTimestamp.modified();
    }
  };

  return m_this;
};

inherit(vgl.actor, vgl.node);

//////////////////////////////////////////////////////////////////////////////
/**
 * @module vgl
 */

/*global vgl*/
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
/**
 * Freeze javascript object
 *
 * @param obj
 */
//////////////////////////////////////////////////////////////////////////////
vgl.freezeObject = function (obj) {
  'use strict';

  /**
   * Freezes an object, using Object.freeze if available, otherwise returns
   * the object unchanged.  This function should be used in setup code to prevent
   * errors from completely halting JavaScript execution in legacy browsers.
   *
   * @exports freezeObject
   */
  var freezedObject = Object.freeze ? Object.freeze(obj) : undefined;
  if (typeof freezedObject === 'undefined') {
    return obj;
  }

  return freezedObject;
};

//////////////////////////////////////////////////////////////////////////////
/**
 * @module vgl
 */

/*global vgl*/
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
/**
 * Returns the first parameter if not undefined,
 * otherwise the second parameter.
 *
 * @class
 * @returns {vgl.defaultValue}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.defaultValue = function (a, b) {
  'use strict';

  if (typeof a !== 'undefined') {
    return a;
  }
  return b;
};

vgl.defaultValue.EMPTY_OBJECT = vgl.freezeObject({});

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class graphicsObject
 *
 * @class
 * @param type
 * @returns {vgl.graphicsObject}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.graphicsObject = function (type) {
  'use strict';

  type = type; /* unused parameter */
  if (!(this instanceof vgl.graphicsObject)) {
    return new vgl.graphicsObject();
  }
  vgl.object.call(this);

  var m_this = this;

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Setup (initialize) the object
   *
   * @param renderState
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this._setup = function (renderState) {
    renderState = renderState; /* unused parameter */
    return false;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Remove any resources acquired before deletion
   *
   * @param renderState
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this._cleanup = function (renderState) {
    renderState = renderState; /* unused parameter */
    return false;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Bind and activate
   *
   * @param renderState
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.bind = function (renderState) {
    renderState = renderState; /* unused parameter */
    return false;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Undo bind and deactivate
   *
   * @param renderState
   * @returns {boolean}
   *
   * TODO: Change it to unbind (simple)
   */
  ////////////////////////////////////////////////////////////////////////////
  this.undoBind = function (renderState) {
    renderState = renderState; /* unused parameter */
    return false;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Render the object
   */
  ////////////////////////////////////////////////////////////////////////////
  this.render = function (renderState) {
    renderState = renderState; /* unused parameter */
    return false;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Remove the object and release its graphics resources
   */
  ////////////////////////////////////////////////////////////////////////////
  this.remove = function (renderState) {
    m_this._cleanup(renderState);
  };

  return m_this;
};

inherit(vgl.graphicsObject, vgl.object);

//////////////////////////////////////////////////////////////////////////////
/**
 * @module vgl
 */

/*global vgl, Uint16Array*/
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of geojson reader
 *
 * This contains code that reads a geoJSON file and produces rendering
 * primitives from it.
 *
 * @class
 * @returns {vgl.geojsonReader}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.geojsonReader = function () {
  'use strict';

  if (!(this instanceof vgl.geojsonReader)) {
    return new vgl.geojsonReader();
  }

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Read scalars
   *
   * @param coordinates
   * @param geom
   * @param size_estimate
   * @param idx
   */
  ////////////////////////////////////////////////////////////////////////////
  this.readScalars = function (coordinates, geom, size_estimate, idx) {
    var array = null,
        s = null,
        r = null,
        g = null,
        b = null;

    if (this.m_scalarFormat === 'values' && coordinates.length === 4) {
      s = coordinates[3];
      array = geom.sourceData(vgl.vertexAttributeKeys.Scalar);

      if (!array) {
        array = new vgl.sourceDataSf();
        if (this.m_scalarRange) {
          array.setScalarRange(this.m_scalarRange[0], this.m_scalarRange[1]);
        }
        if (size_estimate !== undefined) {
          //array.length = size_estimate; //no, slow on Safari
          array.data().length = size_estimate;
        }
        geom.addSource(array);
      }
      if (size_estimate === undefined) {
        array.pushBack(s);
      } else {
        array.insertAt(idx, s);
      }
    } else if (this.m_scalarFormat === 'rgb' && coordinates.length === 6) {
      array = geom.sourceData(vgl.vertexAttributeKeys.Color);
      if (!array) {
        array = new vgl.sourceDataC3fv();
        if (size_estimate !== undefined) {
          array.length = size_estimate * 3;
        }
        geom.addSource(array);
      }
      r = coordinates[3];
      g = coordinates[4];
      b = coordinates[5];
      if (size_estimate === undefined) {
        array.pushBack([r, g, b]);
      } else {
        array.insertAt(idx, [r, g, b]);
      }
    }
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Read point data
   *
   * @param coordinates
   * @returns {vgl.geometryData}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.readPoint = function (coordinates) {
    var geom = new vgl.geometryData(),
        vglpoints = new vgl.points(),
        vglcoords = new vgl.sourceDataP3fv(),
        indices = new Uint16Array(1),
        x = null,
        y = null,
        z = null,
        i = null;

    geom.addSource(vglcoords);
    for (i = 0; i < 1; i += 1) {
      indices[i] = i;

      x = coordinates[0];
      y = coordinates[1];
      z = 0.0;
      if (coordinates.length > 2) {
        z = coordinates[2];
      }

      //console.log('read ' + x + ',' + y + ',' + z);
      vglcoords.pushBack([x, y, z]);

      //attributes
      this.readScalars(coordinates, geom);
    }

    vglpoints.setIndices(indices);
    geom.addPrimitive(vglpoints);
    geom.setName('aPoint');
    return geom;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Read multipoint data
   *
   * @param coordinates
   * @returns {vgl.geometryData}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.readMultiPoint = function (coordinates) {
    var geom = new vgl.geometryData(),
        vglpoints = new vgl.points(),
        vglcoords = new vgl.sourceDataP3fv(),
        indices = new Uint16Array(coordinates.length),
        pntcnt = 0,
        estpntcnt = coordinates.length,
        x = null,
        y = null,
        z = null,
        i;

    //preallocate with size estimate
    vglcoords.data().length = estpntcnt * 3; //x,y,z

    for (i = 0; i < coordinates.length; i += 1) {
      indices[i] = i;
      x = coordinates[i][0];
      y = coordinates[i][1];
      z = 0.0;
      if (coordinates[i].length > 2) {
        z = coordinates[i][2];
      }

      //console.log('read ' + x + ',' + y + ',' + z);
      vglcoords.insertAt(pntcnt, [x, y, z]);

      //attributes
      this.readScalars(coordinates[i], geom, estpntcnt, pntcnt);

      pntcnt += 1;
    }

    vglpoints.setIndices(indices);
    geom.addPrimitive(vglpoints);
    geom.addSource(vglcoords);
    geom.setName('manyPoints');
    return geom;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Read line string data
   *
   * @param coordinates
   * @returns {vgl.geometryData}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.readLineString = function (coordinates) {
    var geom = new vgl.geometryData(),
        vglline = new vgl.lineStrip(),
        vglcoords = new vgl.sourceDataP3fv(),
        indices = [],
        i = null,
        x = null,
        y = null,
        z = null;

    vglline.setIndicesPerPrimitive(coordinates.length);

    for (i = 0; i < coordinates.length; i += 1) {
      indices.push(i);
      x = coordinates[i][0];
      y = coordinates[i][1];
      z = 0.0;
      if (coordinates[i].length > 2) {
        z = coordinates[i][2];
      }

      //console.log('read ' + x + ',' + y + ',' + z);
      vglcoords.pushBack([x, y, z]);

      //attributes
      this.readScalars(coordinates[i], geom);
    }

    vglline.setIndices(indices);
    geom.addPrimitive(vglline);
    geom.addSource(vglcoords);
    geom.setName('aLineString');
    return geom;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Read multi line string
   *
   * @param coordinates
   * @returns {vgl.geometryData}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.readMultiLineString = function (coordinates) {
    var geom = new vgl.geometryData(),
        vglcoords = new vgl.sourceDataP3fv(),
        pntcnt = 0,
        //lines should be at least 2 verts long, underest OK
        estpntcnt = coordinates.length * 2,
        i = null,
        j = null,
        x = null,
        y = null,
        z = null,
        indices = null,
        vglline = null,
        thisLineLength = null;

    // Preallocate with size estimate
    vglcoords.data().length = estpntcnt * 3; //x,y,z

    for (j = 0; j < coordinates.length; j += 1) {
      indices = [];
      //console.log('getting line ' + j);
      vglline = new vgl.lineStrip();
      thisLineLength = coordinates[j].length;
      vglline.setIndicesPerPrimitive(thisLineLength);
      for (i = 0; i < thisLineLength; i += 1) {
        indices.push(pntcnt);
        x = coordinates[j][i][0];
        y = coordinates[j][i][1];
        z = 0.0;
        if (coordinates[j][i].length > 2) {
          z = coordinates[j][i][2];
        }

        //console.log('read ' + x + ',' + y + ',' + z);
        vglcoords.insertAt(pntcnt, [x, y, z]);

        //attributes
        this.readScalars(coordinates[j][i], geom, estpntcnt * 2, pntcnt);

        pntcnt += 1;
      }

      vglline.setIndices(indices);
      geom.addPrimitive(vglline);
    }

    geom.setName('aMultiLineString');
    geom.addSource(vglcoords);
    return geom;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Read polygon data
   *
   * @param coordinates
   * @returns {vgl.geometryData}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.readPolygon = function (coordinates) {
    //TODO: ignoring holes given in coordinates[1...]
    //TODO: ignoring convex
    //TODO: implement ear clipping in VGL instead of this to handle both
    var geom = new vgl.geometryData(),
        vglcoords = new vgl.sourceDataP3fv(),
        x = null,
        y = null,
        z = null,
        thisPolyLength = coordinates[0].length,
        vl = 1,
        i = null,
        indices = null,
        vgltriangle = null;

    for (i = 0; i < thisPolyLength; i += 1) {
      x = coordinates[0][i][0];
      y = coordinates[0][i][1];
      z = 0.0;
      if (coordinates[0][i].length > 2) {
        z = coordinates[0][i][2];
      }

      //console.log('read ' + x + ',' + y + ',' + z);
      vglcoords.pushBack([x, y, z]);

      //attributes
      this.readScalars(coordinates[0][i], geom);

      if (i > 1) {
        //console.log('Cutting new triangle 0,'+ vl+ ','+ i);
        indices = new Uint16Array([0, vl, i]);
        vgltriangle = new vgl.triangles();
        vgltriangle.setIndices(indices);
        geom.addPrimitive(vgltriangle);
        vl = i;
      }
    }

    geom.setName('POLY');
    geom.addSource(vglcoords);
    return geom;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Read multi polygon data
   *
   * @param coordinates
   * @returns {vgl.geometryData}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.readMultiPolygon = function (coordinates) {
    var geom = new vgl.geometryData(),
        vglcoords = new vgl.sourceDataP3fv(),
        ccount = 0,
        numPolys = coordinates.length,
        pntcnt = 0,
        estpntcnt = numPolys * 3, // assume triangles, underest is fine
        vgltriangle = new vgl.triangles(),
        indexes = [],
        i = null,
        j = null,
        x = null,
        y = null,
        z = null,
        thisPolyLength = null,
        vf = null,
        vl = null,
        flip = null,
        flipped = false,
        tcount = 0;

    //var time1 = new Date().getTime()
    //var a = 0;
    //var b = 0;
    //var c = 0;
    //var d = 0;

    //preallocate with size estimate
    vglcoords.data().length = numPolys * 3; //x,y,z
    for (j = 0; j < numPolys; j += 1) {
      //console.log('getting poly ' + j);

      thisPolyLength = coordinates[j][0].length;
      vf = ccount;
      vl = ccount + 1;
      flip = [false, false, false];
      for (i = 0; i < thisPolyLength; i += 1) {
        //var timea = new Date().getTime()

        x = coordinates[j][0][i][0];
        y = coordinates[j][0][i][1];
        z = 0.0;
        if (coordinates[j][0][i].length > 2) {
          z = coordinates[j][0][i][2];
        }
        flipped = false;
        if (x > 180) {
          flipped = true;
          x = x - 360;
        }
        if (i === 0) {
          flip[0] = flipped;
        } else {
          flip[1 + (i - 1) % 2] = flipped;
        }
        //var timeb = new Date().getTime();
        //console.log('read ' + x + ',' + y + ',' + z);

        vglcoords.insertAt(pntcnt, [x, y, z]);
        //var timec = new Date().getTime();

        //attributes
        this.readScalars(coordinates[j][0][i], geom, estpntcnt, pntcnt);
        pntcnt += 1;
        //var timed = new Date().getTime()

        if (i > 1) {
          //if (vl < 50) {
          //console.log('Cutting new triangle ' + tcount + ':' + vf + ',' +
          //            vl + ',' + ccount);
          //console.log(indexes);
          //}
          if (flip[0] === flip[1] && flip[1] === flip[2]) {
            //indexes = indexes.concat([vf,vl,ccount]); //no, very slow in Safari
            indexes[tcount * 3 + 0] = vf;
            indexes[tcount * 3 + 1] = vl;
            indexes[tcount * 3 + 2] = ccount;
            tcount += 1;
          }
          //else {
          //  //TODO: duplicate triangles that straddle boundary on either side
          //}

          vl = ccount;
        }
        ccount += 1;
        //var timee = new Date().getTime()
        //a = a + (timeb-timea)
        //b = b + (timec-timeb)
        //c = c + (timed-timec)
        //d = d + (timee-timed)
      }
    }
    vgltriangle.setIndices(indexes);
    geom.addPrimitive(vgltriangle);

    //console.log('NUMPOLYS ' + pntcnt);
    //console.log('RMP: ', a, ',', b, ',', c, ',', d)
    //var time2 = new Date().getTime()

    geom.setName('aMultiPoly');
    geom.addSource(vglcoords);
    //var time3 = new Date().getTime()
    //console.log('RMP: ', time2-time1, ',', time3-time2)

    return geom;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * @param object
   * @returns {*}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.readGJObjectInt = function (object) {
    if (!object.hasOwnProperty('type')) {
      //console.log('uh oh, not a geojson object');
      return null;
    }

    //look for properties type annotation
    if (object.properties &&
        object.properties.ScalarFormat &&
        object.properties.ScalarFormat === 'values') {
      this.m_scalarFormat = 'values';
      if (object.properties.ScalarRange) {
        this.m_scalarRange = object.properties.ScalarRange;
      }
    }
    if (object.properties &&
        object.properties.ScalarFormat &&
        object.properties.ScalarFormat === 'rgb') {
      this.m_scalarFormat = 'rgb';
    }

    //TODO: ignoring 'crs' and 'bbox' and misc meta data on all of these,
    //best to handle as references into original probably
    var ret,
        type = object.type,
        next = null,
        nextset = null,
        i = null;

    switch (type) {
      case 'Point':
        //console.log('parsed Point');
        ret = this.readPoint(object.coordinates);
        break;
      case 'MultiPoint':
        //console.log('parsed MultiPoint');
        ret = this.readMultiPoint(object.coordinates);
        break;
      case 'LineString':
        //console.log('parsed LineString');
        ret = this.readLineString(object.coordinates);
        break;
      case 'MultiLineString':
        //console.log('parsed MultiLineString');
        ret = this.readMultiLineString(object.coordinates);
        break;
      case 'Polygon':
        //console.log('parsed Polygon');
        ret = this.readPolygon(object.coordinates);
        break;
      case 'MultiPolygon':
        //console.log('parsed MultiPolygon');
        ret = this.readMultiPolygon(object.coordinates);
        break;
      case 'GeometryCollection':
        //console.log('parsed GeometryCollection');
        nextset = [];
        for (i = 0; i < object.geometries.length; i += 1) {
          next = this.readGJObject(object.geometries[i]);
          nextset.push(next);
        }
        ret = nextset;
        break;
      case 'Feature':
        //console.log('parsed Feature');
        next = this.readGJObject(object.geometry);
        ret = next;
        break;
      case 'FeatureCollection':
        //console.log('parsed FeatureCollection');
        nextset = [];
        for (i = 0; i < object.features.length; i += 1) {
          next = this.readGJObject(object.features[i]);
          nextset.push(next);
        }
        ret = nextset;
        break;
      default:
        console.log('Don\'t understand type ' + type);
        ret = null;
        break;
    }
    return ret;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * @param object
   * @returns {*}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.readGJObject = function (object) {
    //var time1, time2;
    var ret;
    //time1 = new Date().getTime()
    ret = this.readGJObjectInt(object);
    //time2 = new Date().getTime()
    //console.log('ELAPSED: ', time2-time1)
    return ret;
  };

  /**
   * Linearize geometries
   *
   * @param geoms
   * @param geom
   */
  this.linearizeGeoms = function (geoms, geom) {
    var i = null;

    if (Object.prototype.toString.call(geom) === '[object Array]') {
      for (i = 0; i < geom.length; i += 1) {
        this.linearizeGeoms(geoms, geom[i]);
      }
    } else {
      geoms.push(geom);
    }
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Read geometries from geojson object
   *
   * @param object
   * @returns {Array}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.readGeomObject = function (object) {
    var geom,
        geoms = [];

    geom = this.readGJObject(object);
    this.linearizeGeoms(geoms, geom);
    return geoms;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Given a buffer get rendering primitives
   *
   * @param buffer
   * @returns {*}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.getPrimitives = function (buffer) {
    //console.log('Parsing geoJSON');
    if (!buffer) {
      return [];
    }

    var obj = JSON.parse(buffer),
        geom = this.readGJObject(obj),
        geoms = [];

    this.m_scalarFormat = 'none';
    this.m_scalarRange = null;

    this.linearizeGeoms(geoms, geom);

    return { 'geoms': geoms,
             'scalarFormat': this.m_scalarFormat,
             'scalarRange': this.m_scalarRange };
  };

  return this;
};

//////////////////////////////////////////////////////////////////////////////
/**
 * @module vgl
 */

/*global vgl*/
//////////////////////////////////////////////////////////////////////////////

vgl.data = function () {
  'use strict';

  if (!(this instanceof vgl.data)) {
    return new vgl.data();
  }

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return data type. Should be implemented by the derived class
   */
  ////////////////////////////////////////////////////////////////////////////
  this.type = function () {
  };
};

vgl.data.raster = 0;
vgl.data.point = 1;
vgl.data.lineString = 2;
vgl.data.polygon = 3;
vgl.data.geometry = 10;

//////////////////////////////////////////////////////////////////////////////
/**
 * @module vgl
 */

/*global vgl, Uint16Array, inherit*/
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class primitive
 *
 * @class
 * @return {vgl.primitive}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.primitive = function () {
  'use strict';

  if (!(this instanceof vgl.primitive)) {
    return new vgl.primitive();
  }

  /** @private */
  var m_indicesPerPrimitive = 0,
      m_primitiveType = 0,
      m_indicesValueType = 0,
      m_indices = null;

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get indices of the primitive
   *
   * @returns {null}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.indices = function () {
    return m_indices;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Create indices array for the primitive
   * @param type
   */
  ////////////////////////////////////////////////////////////////////////////
  this.createIndices = function (type) {
    void type;
    // TODO Check for the type
    m_indices = new Uint16Array();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return the number of indices
   */
  ////////////////////////////////////////////////////////////////////////////
  this.numberOfIndices = function () {
    return m_indices.length;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return size of indices in bytes
   */
  ////////////////////////////////////////////////////////////////////////////
  this.sizeInBytes = function () {
    return m_indices.length * Uint16Array.BYTES_PER_ELEMENT;
  };

  ////////////////////////////////////////////////////////////////////////////
  /*
   * Return primitive type g
   */
  ////////////////////////////////////////////////////////////////////////////
  this.primitiveType = function () {
    return m_primitiveType;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set primitive type
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setPrimitiveType = function (type) {
    m_primitiveType = type;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return count of indices that form a primitives
   */
  ////////////////////////////////////////////////////////////////////////////
  this.indicesPerPrimitive = function () {
    return m_indicesPerPrimitive;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set count of indices that form a primitive
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setIndicesPerPrimitive = function (count) {
    m_indicesPerPrimitive = count;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return indices value type
   */
  ////////////////////////////////////////////////////////////////////////////
  this.indicesValueType = function () {
    return m_indicesValueType;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set indices value type
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setIndicesValueType = function (type) {
    m_indicesValueType = type;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set indices from a array
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setIndices = function (indicesArray) {
    // TODO Check for the type
    m_indices = new Uint16Array(indicesArray);
  };

  return this;
};

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class triangleStrip
 *
 * @returns {vgl.triangleStrip}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.triangleStrip = function () {
  'use strict';

  if (!(this instanceof vgl.triangleStrip)) {
    return new vgl.triangleStrip();
  }

  vgl.primitive.call(this);

  this.setPrimitiveType(vgl.GL.TRIANGLE_STRIP);
  this.setIndicesValueType(vgl.GL.UNSIGNED_SHORT);
  this.setIndicesPerPrimitive(3);

  return this;
};

inherit(vgl.triangleStrip, vgl.primitive);

////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class triangles
 *
 * @returns {vgl.triangles}
 */
////////////////////////////////////////////////////////////////////////////
vgl.triangles = function () {
  'use strict';

  if (!(this instanceof vgl.triangles)) {
    return new vgl.triangles();
  }
  vgl.primitive.call(this);

  this.setPrimitiveType(vgl.GL.TRIANGLES);
  this.setIndicesValueType(vgl.GL.UNSIGNED_SHORT);
  this.setIndicesPerPrimitive(3);

  return this;
};

inherit(vgl.triangles, vgl.primitive);

//////////////////////////////////////////////////////////////////////////////
/**
 * create a instance of lines primitive type
 *
 * @returns {vgl.lines}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.lines = function () {
  'use strict';

  if (!(this instanceof vgl.lines)) {
    return new vgl.lines();
  }
  vgl.primitive.call(this);

  this.setPrimitiveType(vgl.GL.LINES);
  this.setIndicesValueType(vgl.GL.UNSIGNED_SHORT);
  this.setIndicesPerPrimitive(2);

  return this;
};
inherit(vgl.lines, vgl.primitive);

//////////////////////////////////////////////////////////////////////////////
/**
 * create a instance of line strip primitive type
 *
 * @returns {vgl.lineStrip}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.lineStrip = function () {
  'use strict';

  if (!(this instanceof vgl.lineStrip)) {
    return new vgl.lineStrip();
  }
  vgl.primitive.call(this);

  this.setPrimitiveType(vgl.GL.LINE_STRIP);
  this.setIndicesValueType(vgl.GL.UNSIGNED_SHORT);
  this.setIndicesPerPrimitive(2);

  return this;
};
inherit(vgl.lineStrip, vgl.primitive);

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class points
 *
 * @returns {vgl.points}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.points = function () {
  'use strict';

  if (!(this instanceof vgl.points)) {
    return new vgl.points();
  }
  vgl.primitive.call(this);

  this.setPrimitiveType(vgl.GL.POINTS);
  this.setIndicesValueType(vgl.GL.UNSIGNED_SHORT);
  this.setIndicesPerPrimitive(1);

  return this;
};

inherit(vgl.points, vgl.primitive);

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class vertexDataP3f
 *
 * @returns {vgl.vertexDataP3f}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.vertexDataP3f = function () {
  'use strict';

  if (!(this instanceof vgl.vertexDataP3f)) {
    return new vgl.vertexDataP3f();
  }

  /** @private */
  this.m_position = [];

  return this;
};

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class vertexDataP3N3f
 *
 * @class
 * @returns {vgl.vertexDataP3N3f}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.vertexDataP3N3f = function () {
  'use strict';

  if (!(this instanceof vgl.vertexDataP3N3f)) {
    return new vgl.vertexDataP3N3f();
  }

  this.m_position = [];
  this.m_normal = [];

  return this;
};

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class vertexDataP3T3f
 *
 * @class
 * @returns {vgl.vertexDataP3T3f}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.vertexDataP3T3f = function () {
  'use strict';

  if (!(this instanceof vgl.vertexDataP3T3f)) {
    return new vgl.vertexDataP3T3f();
  }

  this.m_position = [];
  this.m_texCoordinate = [];

  return this;
};

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class sourceData
 * @class
 * @returns {vgl.sourceData}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.sourceData = function (arg) {
  'use strict';

  if (!(this instanceof vgl.sourceData)) {
    return new vgl.sourceData(arg);
  }

  arg = arg || {};
  var m_attributesMap = {},
      m_data = [],
      m_name = arg.name || 'Source ' + new Date().toISOString(),

      ////////////////////////////////////////////////////////////////////////
      /**
       * Attribute data for the source
       */
      ////////////////////////////////////////////////////////////////////////
      vglAttributeData = function () {
        // Number of components per group
        // Type of data type (GL_FLOAT etc)
        this.m_numberOfComponents = 0;
        // Size of data type
        this.m_dataType = 0;
        this.m_dataTypeSize = 0;
        // Specifies whether fixed-point data values should be normalized
        // (true) or converted directly as fixed-point values (false)
        // when they are accessed.
        this.m_normalized = false;
        // Strides for each attribute.
        this.m_stride = 0;
        // Offset
        this.m_offset = 0;
      };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return raw data for this source
   *
   * @returns {Array or Float32Array}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.data = function () {
    return m_data;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return raw data for this source
   *
   * @returns {Array or Float32Array}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.getData = function () {
    return this.data();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * If the raw data is not a Float32Array, convert it to one.  Then, return
   * raw data for this source
   *
   * @returns {Float32Array}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.dataToFloat32Array = function () {
    if (!(m_data instanceof Float32Array)) {
      m_data = new Float32Array(m_data);
    }
    return m_data;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set data for this source
   *
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setData = function (data) {
    if (!(data instanceof Array) && !(data instanceof Float32Array)) {
      console.log('[error] Requires array');
      return;
    }
    if (data instanceof Float32Array) {
      m_data = data;
    } else {
      m_data = data.slice(0);
    }
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Add new attribute data to the source
   */
  ////////////////////////////////////////////////////////////////////////////
  this.addAttribute = function (key, dataType, sizeOfDataType, offset, stride,
                               noOfComponents, normalized) {

    if (!m_attributesMap.hasOwnProperty(key)) {
      /* jshint newcap: false */
      //jscs:disable requireCapitalizedConstructors
      var newAttr = new vglAttributeData();
      //jscs:enable requireCapitalizedConstructors
      /* jshint newcap: true */
      newAttr.m_dataType = dataType;
      newAttr.m_dataTypeSize = sizeOfDataType;
      newAttr.m_offset = offset;
      newAttr.m_stride = stride;
      newAttr.m_numberOfComponents = noOfComponents;
      newAttr.m_normalized = normalized;
      m_attributesMap[key] = newAttr;
    }
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return size of the source data
   */
  ////////////////////////////////////////////////////////////////////////////
  this.sizeOfArray = function () {
    return Object.size(m_data);
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return length of array
   */
  ////////////////////////////////////////////////////////////////////////////
  this.lengthOfArray = function () {
    return m_data.length;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return size of the source data in bytes
   */
  ////////////////////////////////////////////////////////////////////////////
  /*
    * TODO: code below is probably wrong.
    *   Example:
    *            format P3N3f
    *            m_data = [ 1, 2, 3, 4, 5, 6 ]; // contains one vertex,
    *                                    // one normal, m_data.length == 6
    *
    *       The inner loop computes:
    *             sizeInBytes += 3 * 4; // for position
    *             sizeInBytes += 3 * 4; // for normal
    *
    *        Then sizeInBytes *= 6; // m_data.length == 6
    *        which gives sizeInBytes == 144 bytes when it should have been 4*6 = 24
    */
  this.sizeInBytes = function () {
    var sizeInBytes = 0,
        keys = this.keys(), i;

    for (i = 0; i < keys.length; i += 1) {
      sizeInBytes += this.attributeNumberOfComponents(keys[i]) *
                     this.sizeOfAttributeDataType(keys[i]);
    }

    sizeInBytes *= this.sizeOfArray();

    return sizeInBytes;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Check if there is attribute exists of a given key type
   */
  ////////////////////////////////////////////////////////////////////////////
  this.hasKey = function (key) {
    return m_attributesMap.hasOwnProperty(key);
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return keys of all attributes
   */
  ////////////////////////////////////////////////////////////////////////////
  this.keys = function () {
    return Object.keys(m_attributesMap);
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return number of attributes of source data
   */
  ////////////////////////////////////////////////////////////////////////////
  this.numberOfAttributes = function () {
    return Object.size(m_attributesMap);
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return number of components of the attribute data
   */
  ////////////////////////////////////////////////////////////////////////////
  this.attributeNumberOfComponents = function (key) {
    if (m_attributesMap.hasOwnProperty(key)) {
      return m_attributesMap[key].m_numberOfComponents;
    }

    return 0;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return if the attribute data is normalized
   */
  ////////////////////////////////////////////////////////////////////////////
  this.normalized = function (key) {
    if (m_attributesMap.hasOwnProperty(key)) {
      return m_attributesMap[key].m_normalized;
    }

    return false;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return size of the attribute data type
   */
  ////////////////////////////////////////////////////////////////////////////
  this.sizeOfAttributeDataType = function (key) {
    if (m_attributesMap.hasOwnProperty(key)) {
      return m_attributesMap[key].m_dataTypeSize;
    }

    return 0;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return attribute data type
   */
  ////////////////////////////////////////////////////////////////////////////
  this.attributeDataType = function (key) {
    if (m_attributesMap.hasOwnProperty(key)) {
      return m_attributesMap[key].m_dataType;
    }

    return undefined;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return attribute offset
   */
  ////////////////////////////////////////////////////////////////////////////
  this.attributeOffset = function (key) {
    if (m_attributesMap.hasOwnProperty(key)) {
      return m_attributesMap[key].m_offset;
    }

    return 0;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return attribute stride
   */
  ////////////////////////////////////////////////////////////////////////////
  this.attributeStride = function (key) {
    if (m_attributesMap.hasOwnProperty(key)) {
      return m_attributesMap[key].m_stride;
    }

    return 0;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Virtual function to insert new vertex data at the end
   */
  ////////////////////////////////////////////////////////////////////////////
  this.pushBack = function (vertexData) {
    void vertexData;
    // Should be implemented by the base class
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Insert new data block to the raw data
   */
  ////////////////////////////////////////////////////////////////////////////
  this.insert = function (data) {
    var i;

    //m_data = m_data.concat(data); //no, slow on Safari
    /* If we will are given a Float32Array and don't have any other data, use
     * it directly. */
    if (!m_data.length && data.length && data instanceof Float32Array) {
      m_data = data;
      return;
    }
    /* If our internal array is immutable and we will need to change it, create
     * a regular mutable array from it. */
    if (!m_data.slice && (m_data.length || !data.slice)) {
      m_data = Array.prototype.slice.call(m_data);
    }
    if (!data.length) {
      /* data is a singular value, so append it to our array */
      m_data[m_data.length] = data;
    } else {
      /* We don't have any data currently, so it is faster to copy the data
       * using slice. */
      if (!m_data.length && data.slice) {
        m_data = data.slice(0);
      } else {
        for (i = 0; i < data.length; i += 1) {
          m_data[m_data.length] = data[i];
        }
      }
    }
  };

  this.insertAt = function (index, data) {
    var i;

    if (!data.length) {
      m_data[index] = data;
    } else {
      for (i = 0; i < data.length; i += 1) {
        m_data[index * data.length + i] = data[i];
      }
    }
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return name of the source data
   */
  ////////////////////////////////////////////////////////////////////////////
  this.name = function () {
    return m_name;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set name of the source data
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setName = function (name) {
    m_name = name;
  };

  return this;
};

vgl.sourceDataAnyfv = function (size, key, arg) {
  'use strict';
  if (!(this instanceof vgl.sourceDataAnyfv)) {
    return new vgl.sourceDataAnyfv(size, key, arg);
  }

  vgl.sourceData.call(this, arg);
  this.addAttribute(key, vgl.GL.FLOAT,
                    4, 0, size * 4, size, false);

  this.pushBack = function (value) {
    this.insert(value);
  };

  return this;
};
inherit(vgl.sourceDataAnyfv, vgl.sourceData);

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class sourceDataP3T3f
 *
 * @returns {vgl.sourceDataP3T3f}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.sourceDataP3T3f = function (arg) {
  'use strict';

  if (!(this instanceof vgl.sourceDataP3T3f)) {
    return new vgl.sourceDataP3T3f(arg);
  }
  vgl.sourceData.call(this, arg);

  this.addAttribute(vgl.vertexAttributeKeys.Position, vgl.GL.FLOAT, 4, 0, 6 * 4, 3,
                    false);
  this.addAttribute(vgl.vertexAttributeKeys.TextureCoordinate, vgl.GL.FLOAT, 4, 12,
                    6 * 4, 3, false);

  this.pushBack = function (value) {
    this.insert(value.m_position);
    this.insert(value.m_texCoordinate);
  };

  return this;
};

inherit(vgl.sourceDataP3T3f, vgl.sourceData);

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class sourceDataP3N3f
 *
 * @returns {vgl.sourceDataP3N3f}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.sourceDataP3N3f = function (arg) {
  'use strict';

  if (!(this instanceof vgl.sourceDataP3N3f)) {
    return new vgl.sourceDataP3N3f(arg);
  }

  vgl.sourceData.call(this, arg);

  this.addAttribute(vgl.vertexAttributeKeys.Position, vgl.GL.FLOAT, 4, 0, 6 * 4, 3,
                    false);
  this.addAttribute(vgl.vertexAttributeKeys.Normal, vgl.GL.FLOAT, 4, 12, 6 * 4, 3,
                    false);

  this.pushBack = function (value) {
    this.insert(value.m_position);
    this.insert(value.m_normal);
  };

  return this;
};

inherit(vgl.sourceDataP3N3f, vgl.sourceData);

/////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class sourceDataP3fv
 *
 * @returns {vgl.sourceDataP3fv}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.sourceDataP3fv = function (arg) {
  'use strict';

  if (!(this instanceof vgl.sourceDataP3fv)) {
    return new vgl.sourceDataP3fv(arg);
  }

  vgl.sourceData.call(this, arg);

  this.addAttribute(vgl.vertexAttributeKeys.Position, vgl.GL.FLOAT, 4, 0, 3 * 4, 3,
                    false);

  this.pushBack = function (value) {
    this.insert(value);
  };

  return this;
};

inherit(vgl.sourceDataP3fv, vgl.sourceData);

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class sourceDataT2fv
 *
 * @returns {vgl.sourceDataT2fv}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.sourceDataT2fv = function (arg) {
  'use strict';

  if (!(this instanceof vgl.sourceDataT2fv)) {
    return new vgl.sourceDataT2fv(arg);
  }

  vgl.sourceData.call(this, arg);

  this.addAttribute(vgl.vertexAttributeKeys.TextureCoordinate, vgl.GL.FLOAT, 4, 0,
                    2 * 4, 2, false);

  this.pushBack = function (value) {
    this.insert(value);
  };

  return this;
};

inherit(vgl.sourceDataT2fv, vgl.sourceData);

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class sourceDataC3fv
 *
 * @returns {vgl.sourceDataC3fv}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.sourceDataC3fv = function (arg) {
  'use strict';

  if (!(this instanceof vgl.sourceDataC3fv)) {
    return new vgl.sourceDataC3fv(arg);
  }

  vgl.sourceData.call(this, arg);

  this.addAttribute(vgl.vertexAttributeKeys.Color, vgl.GL.FLOAT, 4, 0, 3 * 4, 3, false);

  this.pushBack = function (value) {
    this.insert(value);
  };

  return this;
};

inherit(vgl.sourceDataC3fv, vgl.sourceData);

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class sourceDataSf meant to hold scalar float values
 *
 * @class
 * @returns {vgl.sourceDataSf}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.sourceDataSf = function (arg) {
  'use strict';

  if (!(this instanceof vgl.sourceDataSf)) {
    return new vgl.sourceDataSf(arg);
  }

  var m_min = null,
      m_max = null,
      m_fixedmin = null,
      m_fixedmax = null;

  vgl.sourceData.call(this, arg);

  this.addAttribute(vgl.vertexAttributeKeys.Scalar, vgl.GL.FLOAT, 4, 0, 4, 1, false);

  this.pushBack = function (value) {
    if (m_max === null || value > m_max) {
      m_max = value;
    }
    if (m_min === null || value < m_min) {
      m_min = value;
    }
    //this.insert(value); //no, slow on Safari
    this.data()[this.data().length] = value;
  };

  this.insertAt = function (index, value) {
    if (m_max === null || value > m_max) {
      m_max = value;
    }
    if (m_min === null || value < m_min) {
      m_min = value;
    }
    //call superclass ??
    //vgl.sourceData.insertAt.call(this, index, value);
    this.data()[index] = value;
  };

  this.scalarRange = function () {
    if (m_fixedmin === null || m_fixedmax === null) {
      return [m_min, m_max];
    }

    return [m_fixedmin, m_fixedmax];
  };

  this.setScalarRange = function (min, max) {
    m_fixedmin = min;
    m_fixedmax = max;
  };

  return this;
};

inherit(vgl.sourceDataSf, vgl.sourceData);

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class sourceDataDf meant to hold data float values
 *
 * This source array is the best way to pass a array of floats to the shader
 * that has one entry for each of the vertices.
 *
 * @class
 * @returns {vgl.sourceDataDf}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.sourceDataDf = function (arg) {
  'use strict';

  if (!(this instanceof vgl.sourceDataDf)) {
    return new vgl.sourceDataDf(arg);
  }

  vgl.sourceData.call(this, arg);

  this.addAttribute(vgl.vertexAttributeKeys.Scalar, vgl.GL.FLOAT,
                    4, 0, 4, 1, false);

  this.pushBack = function (value) {
    this.data()[this.data().length] = value;
  };

  this.insertAt = function (index, value) {
    this.data()[index] = value;
  };

  return this;
};

inherit(vgl.sourceDataDf, vgl.sourceData);

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class geometryData
 *
 * @class
 * @returns {vgl.geometryData}
 */
/////////////////////////////////////////////////////////////////////////////
vgl.geometryData = function () {
  'use strict';

  if (!(this instanceof vgl.geometryData)) {
    return new vgl.geometryData();
  }
  vgl.data.call(this);

  /** @private */
  var m_name = '',
      m_primitives = [],
      m_sources = [],
      m_bounds = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
      m_computeBoundsTimestamp = vgl.timestamp(),
      m_boundsDirtyTimestamp = vgl.timestamp();

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return type
   */
  ////////////////////////////////////////////////////////////////////////////
  this.type = function () {
    return vgl.data.geometry;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return ID of the geometry data
   */
  ////////////////////////////////////////////////////////////////////////////
  this.name = function () {
    return m_name;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set name of the geometry data
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setName = function (name) {
    m_name = name;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Add new source
   */
  ////////////////////////////////////////////////////////////////////////////
  this.addSource = function (source, sourceName) {
    // @todo Check if the incoming source has duplicate keys

    if (sourceName !== undefined) {
      source.setName(sourceName);
    }
    // NOTE This might not work on IE8 or lower
    if (m_sources.indexOf(source) === -1) {
      m_sources.push(source);

      if (source.hasKey(vgl.vertexAttributeKeys.Position)) {
        m_boundsDirtyTimestamp.modified();
      }
      return true;
    }

    return false;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return source for a given index. Returns 0 if not found.
   */
  ////////////////////////////////////////////////////////////////////////////
  this.source = function (index) {
    if (index < m_sources.length) {
      return m_sources[index];
    }

    return 0;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return source with a specified name.  Returns 0 if not found.
   */
  ////////////////////////////////////////////////////////////////////////////
  this.sourceByName = function (sourceName) {
    for (var i = 0; i < m_sources.length; i += 1) {
      if (m_sources[i].name() === sourceName) {
        return m_sources[i];
      }
    }
    return 0;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return number of sources
   */
  ////////////////////////////////////////////////////////////////////////////
  this.numberOfSources = function () {
    return m_sources.length;
  };

  /**
   * Return source data given a key
   */
  this.sourceData = function (key) {
    var i;

    for (i = 0; i < m_sources.length; i += 1) {
      if (m_sources[i].hasKey(key)) {
        return m_sources[i];
      }
    }

    return null;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Add new primitive
   */
  ////////////////////////////////////////////////////////////////////////////
  this.addPrimitive = function (primitive) {
    m_primitives.push(primitive);
    return true;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return primitive for a given index. Returns null if not found.
   */
  ////////////////////////////////////////////////////////////////////////////
  this.primitive = function (index) {
    if (index < m_primitives.length) {
      return m_primitives[index];
    }

    return null;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return number of primitives
   */
  ////////////////////////////////////////////////////////////////////////////
  this.numberOfPrimitives = function () {
    return m_primitives.length;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return bounds [minX, maxX, minY, maxY, minZ, maxZ]
   */
  ////////////////////////////////////////////////////////////////////////////
  this.bounds = function () {
    if (m_boundsDirtyTimestamp.getMTime() > m_computeBoundsTimestamp.getMTime()) {
      this.computeBounds();
    }
    return m_bounds;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Check if bounds are dirty or mark them as such.
   *
   * @param dirty: true to set bounds as dirty.
   * Return true if bounds are dirty.
   */
  ////////////////////////////////////////////////////////////////////////////
  this.boundsDirty = function (dirty) {
    if (dirty) {
      m_boundsDirtyTimestamp.modified();
    }
    return m_boundsDirtyTimestamp.getMTime() > m_computeBoundsTimestamp.getMTime();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Reset bounds
   */
  ////////////////////////////////////////////////////////////////////////////
  this.resetBounds = function () {
    m_bounds[0] = 0.0;
    m_bounds[1] = 0.0;
    m_bounds[2] = 0.0;
    m_bounds[3] = 0.0;
    m_bounds[4] = 0.0;
    m_bounds[5] = 0.0;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set bounds
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setBounds = function (minX, maxX, minY, maxY, minZ, maxZ) {
    m_bounds[0] = minX;
    m_bounds[1] = maxX;
    m_bounds[2] = minY;
    m_bounds[3] = maxY;
    m_bounds[4] = minZ;
    m_bounds[5] = maxZ;

    m_computeBoundsTimestamp.modified();

    return true;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Compute bounds
   */
  ////////////////////////////////////////////////////////////////////////////
  this.computeBounds = function () {
    if (m_boundsDirtyTimestamp.getMTime() > m_computeBoundsTimestamp.getMTime()) {
      var attr = vgl.vertexAttributeKeys.Position,
          sourceData = this.sourceData(attr),
          data = sourceData.data(),
          numberOfComponents = sourceData.attributeNumberOfComponents(attr),
          stride = sourceData.attributeStride(attr),
          offset = sourceData.attributeOffset(attr),
          sizeOfDataType = sourceData.sizeOfAttributeDataType(attr),
          count = data.length,
          j, ib, jb, maxv, minv,
          value = null,
          vertexIndex;

      // We advance by index, not by byte
      stride /= sizeOfDataType;
      offset /= sizeOfDataType;

      this.resetBounds();

      for (j = 0; j < numberOfComponents; j += 1) {
        ib = j * 2;
        jb = j * 2 + 1;
        if (count) {
          maxv = minv = m_bounds[jb] = data[offset + j];
        } else {
          maxv = minv = 0;
        }
        for (vertexIndex = offset + stride + j; vertexIndex < count;
             vertexIndex += stride) {
          value = data[vertexIndex];
          if (value > maxv) {
            maxv = value;
          }
          if (value < minv) {
            minv = value;
          }
        }
        m_bounds[ib] = minv; m_bounds[jb] = maxv;
      }

      m_computeBoundsTimestamp.modified();
    }
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Returns the vertex closest to a given position
   */
  ////////////////////////////////////////////////////////////////////////////
  this.findClosestVertex = function (point) {
    var attr = vgl.vertexAttributeKeys.Position,
        sourceData = this.sourceData(attr),
        sizeOfDataType = sourceData.sizeOfAttributeDataType(attr),
        numberOfComponents = sourceData.attributeNumberOfComponents(attr),
        data = sourceData.data(),
        stride = sourceData.attributeStride(attr) / sizeOfDataType,
        offset = sourceData.attributeOffset(attr) / sizeOfDataType,
        minDist = Number.MAX_VALUE,
        minIndex = null,
        vi, vPos, dx, dy, dz, dist, i;

    // assume positions are always triplets
    if (numberOfComponents !== 3) {
      console.log('[warning] Find closest vertex assumes three' +
        'component vertex ');
    }

    if (!point.z) {
      point = {x: point.x, y: point.y, z: 0};
    }

    for (vi = offset, i = 0; vi < data.length; vi += stride, i += 1) {
      vPos = [data[vi],
              data[vi + 1],
              data[vi + 2]];

      dx = vPos[0] - point.x;
      dy = vPos[1] - point.y;
      dz = vPos[2] - point.z;
      dist = Math.sqrt(dx * dx + dy * dy + dz * dz);
      if (dist < minDist) {
        minDist = dist;
        minIndex = i;
      }
    }
    return minIndex;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Returns the requested vertex position
   */
  ////////////////////////////////////////////////////////////////////////////
  this.getPosition = function (index) {
    var attr = vgl.vertexAttributeKeys.Position,
        sourceData = this.sourceData(attr),
        sizeOfDataType = sourceData.sizeOfAttributeDataType(attr),
        numberOfComponents = sourceData.attributeNumberOfComponents(attr),
        data = sourceData.data(),
        stride = sourceData.attributeStride(attr) / sizeOfDataType,
        offset = sourceData.attributeOffset(attr) / sizeOfDataType;

    // assume positions are always triplets
    if (numberOfComponents !== 3) {
      console.log('[warning] getPosition assumes three component data');
    }

    return [data[offset + index * stride],
            data[offset + index * stride + 1],
            data[offset + index * stride + 2]];
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Returns the scalar corresponding to a given vertex index
   */
  ////////////////////////////////////////////////////////////////////////////
  this.getScalar = function (index) {
    var attr = vgl.vertexAttributeKeys.Scalar,
        sourceData = this.sourceData(attr),
        sizeOfDataType, data, stride, offset;

    if (!sourceData) {
      return null;
    }

    sizeOfDataType = sourceData.sizeOfAttributeDataType(attr);
    data = sourceData.data();
    stride = sourceData.attributeStride(attr) / sizeOfDataType;
    offset = sourceData.attributeOffset(attr) / sizeOfDataType;

    if (index * stride + offset >= data.length) {
      console.log('access out of bounds in getScalar');
    }

    return data[index * stride + offset];
  };

  return this;
};

inherit(vgl.geometryData, vgl.data);

//////////////////////////////////////////////////////////////////////////////
/**
 * @module vgl
 */

/*global vgl, Float32Array, inherit*/
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class mapper
 *
 * @class
 * @returns {vgl.mapper}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.mapper = function (arg) {
  'use strict';

  if (!(this instanceof vgl.mapper)) {
    return new vgl.mapper(arg);
  }
  vgl.boundingObject.call(this);

  /** @private */
  arg = arg || {};

  var m_color = [0.0, 1.0, 1.0],
      m_geomData = null,
      m_buffers = [],
      m_bufferVertexAttributeMap = {},
      m_dynamicDraw = arg.dynamicDraw === undefined ? false : arg.dynamicDraw,
      m_glCompileTimestamp = vgl.timestamp(),
      m_context = null,
      m_this = this;

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Delete cached VBO if any
   */
  ////////////////////////////////////////////////////////////////////////////
  this.deleteVertexBufferObjects = function (renderState) {
    var i;
    var context = m_context;
    if (renderState) {
      context = renderState.m_context;
    }
    if (context) {
      for (i = 0; i < m_buffers.length; i += 1) {
        context.deleteBuffer(m_buffers[i]);
      }
    }
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Create new VBO for all its geometryData sources and primitives
   *
   * @private
   */
  ////////////////////////////////////////////////////////////////////////////
  function createVertexBufferObjects(renderState) {
    if (m_geomData) {
      if (renderState) {
        m_context = renderState.m_context;
      }
      var numberOfSources = m_geomData.numberOfSources(),
          i, j, k, bufferId = null, keys, ks, numberOfPrimitives, data;

      for (i = 0; i < numberOfSources; i += 1) {
        bufferId = m_context.createBuffer();
        m_context.bindBuffer(vgl.GL.ARRAY_BUFFER, bufferId);
        data = m_geomData.source(i).data();
        if (!(data instanceof Float32Array)) {
          data = new Float32Array(data);
        }
        m_context.bufferData(vgl.GL.ARRAY_BUFFER, data,
                      m_dynamicDraw ? vgl.GL.DYNAMIC_DRAW :
                      vgl.GL.STATIC_DRAW);

        keys = m_geomData.source(i).keys();
        ks = [];

        for (j = 0; j < keys.length; j += 1) {
          ks.push(keys[j]);
        }

        m_bufferVertexAttributeMap[i] = ks;
        m_buffers[i] = bufferId;
      }

      numberOfPrimitives = m_geomData.numberOfPrimitives();
      for (k = 0; k < numberOfPrimitives; k += 1) {
        bufferId = m_context.createBuffer();
        m_context.bindBuffer(vgl.GL.ARRAY_BUFFER, bufferId);
        m_context.bufferData(vgl.GL.ARRAY_BUFFER,
          m_geomData.primitive(k).indices(), vgl.GL.STATIC_DRAW);
        m_buffers[i] = bufferId;
        i += 1;
      }

      m_glCompileTimestamp.modified();
    }
  }

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Clear cache related to buffers
   *
   * @private
   */
  ////////////////////////////////////////////////////////////////////////////
  function cleanUpDrawObjects(renderState) {
    void renderState;
    m_bufferVertexAttributeMap = {};
    m_buffers = [];
  }

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Setup draw objects; Delete old ones and create new ones
   *
   * @private
   */
  ////////////////////////////////////////////////////////////////////////////
  function setupDrawObjects(renderState) {
    // Delete buffer objects from past if any.
    m_this.deleteVertexBufferObjects(renderState);

    // Clear any cache related to buffers
    cleanUpDrawObjects(renderState);

    // Now construct the new ones.
    createVertexBufferObjects(renderState);
  }

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Compute bounds of the data
   */
  ////////////////////////////////////////////////////////////////////////////
  this.computeBounds = function () {
    if (m_geomData === null || typeof m_geomData === 'undefined') {
      this.resetBounds();
      return;
    }

    var computeBoundsTimestamp = this.computeBoundsTimestamp(),
        boundsDirtyTimestamp = this.boundsDirtyTimestamp(),
        geomBounds = null;

    if (boundsDirtyTimestamp.getMTime() > computeBoundsTimestamp.getMTime()) {
      geomBounds = m_geomData.bounds();

      this.setBounds(geomBounds[0], geomBounds[1], geomBounds[2],
        geomBounds[3], geomBounds[4], geomBounds[5]);

      computeBoundsTimestamp.modified();
    }
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get solid color of the geometry
   */
  ////////////////////////////////////////////////////////////////////////////
  this.color = function () {
    return m_color;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set solid color of the geometry. Default is teal [1.0, 1.0, 1.0]
   *
   * @param r Red component of the color [0.0 - 1.0]
   * @param g Green component of the color [0.0 - 1.0]
   * @param b Blue component of the color [0.0 - 1.0]
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setColor = function (r, g, b) {
    m_color[0] = r;
    m_color[1] = g;
    m_color[2] = b;

    this.modified();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return stored geometry data if any
   */
  ////////////////////////////////////////////////////////////////////////////
  this.geometryData = function () {
    return m_geomData;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Connect mapper to its geometry data
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setGeometryData = function (geom) {
    if (m_geomData !== geom) {
      m_geomData = geom;

      this.modified();
      this.boundsDirtyTimestamp().modified();
    }
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Update the buffer used for a named source.
   *
   * @param {String} sourceName The name of the source to update.
   * @param {Object[] or Float32Array} values The values to use for the source.
   *    If not specified, use the source's own buffer.
   */
  ////////////////////////////////////////////////////////////////////////////
  this.updateSourceBuffer = function (sourceName, values, renderState) {
    if (renderState) {
      m_context = renderState.m_context;
    }
    if (!m_context) {
      return false;
    }
    var bufferIndex = -1;
    for (var i = 0; i < m_geomData.numberOfSources(); i += 1) {
      if (m_geomData.source(i).name() === sourceName) {
        bufferIndex = i;
        break;
      }
    }
    if (bufferIndex < 0 || bufferIndex >= m_buffers.length) {
      return false;
    }
    if (!values) {
      values = m_geomData.source(i).dataToFloat32Array();
    }
    m_context.bindBuffer(vgl.GL.ARRAY_BUFFER, m_buffers[bufferIndex]);
    if (values instanceof Float32Array) {
      m_context.bufferSubData(vgl.GL.ARRAY_BUFFER, 0, values);
    } else {
      m_context.bufferSubData(vgl.GL.ARRAY_BUFFER, 0,
                              new Float32Array(values));
    }
    return true;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get the buffer used for a named source.  If the current buffer isn't a
   * Float32Array, it is converted to one.  This array can then be modified
   * directly, after which updateSourceBuffer can be called to update the
   * GL array.
   *
   * @param {String} sourceName The name of the source to update.
   * @returns {Float32Array} An array used for this source.
   */
  ////////////////////////////////////////////////////////////////////////////
  this.getSourceBuffer = function (sourceName) {
    var source = m_geomData.sourceByName(sourceName);
    if (!source) {
      return new Float32Array();
    }
    return source.dataToFloat32Array();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Render the mapper
   *
   * @param {object} renderState: the current renderering state object.
   * @param {boolean} noUndoBindVertexData: if true, do not unbind vertex data.
   *    This may be desirable if the render function is subclassed.
   */
  ////////////////////////////////////////////////////////////////////////////
  this.render = function (renderState, noUndoBindVertexData) {
    if (this.getMTime() > m_glCompileTimestamp.getMTime() ||
        renderState.m_contextChanged) {
      setupDrawObjects(renderState);
    }
    m_context = renderState.m_context;

    // Fixed vertex color
    m_context.vertexAttrib3fv(vgl.vertexAttributeKeys.Color, this.color());

    // TODO Use renderState
    var bufferIndex = 0,
        j = 0, i, noOfPrimitives = null, primitive = null;

    for (i in m_bufferVertexAttributeMap) {
      if (m_bufferVertexAttributeMap.hasOwnProperty(i)) {
        m_context.bindBuffer(vgl.GL.ARRAY_BUFFER,
                             m_buffers[bufferIndex]);
        for (j = 0; j < m_bufferVertexAttributeMap[i].length; j += 1) {
          renderState.m_material
              .bindVertexData(renderState, m_bufferVertexAttributeMap[i][j]);
        }
        bufferIndex += 1;
      }
    }

    noOfPrimitives = m_geomData.numberOfPrimitives();
    for (j = 0; j < noOfPrimitives; j += 1, bufferIndex += 1) {
      primitive = m_geomData.primitive(j);
      if (!primitive.numberOfIndices()) {
        continue;
      }
      m_context.bindBuffer(vgl.GL.ARRAY_BUFFER, m_buffers[bufferIndex]);
      switch (primitive.primitiveType()) {
        case vgl.GL.POINTS:
          m_context.drawArrays(vgl.GL.POINTS, 0, primitive.numberOfIndices());
          break;
        case vgl.GL.LINES:
          m_context.drawArrays(vgl.GL.LINES, 0, primitive.numberOfIndices());
          break;
        case vgl.GL.LINE_STRIP:
          m_context.drawArrays(vgl.GL.LINE_STRIP, 0, primitive.numberOfIndices());
          break;
        case vgl.GL.TRIANGLES:
          m_context.drawArrays(vgl.GL.TRIANGLES, 0, primitive.numberOfIndices());
          break;
        case vgl.GL.TRIANGLE_STRIP:
          m_context.drawArrays(vgl.GL.TRIANGLE_STRIP, 0, primitive.numberOfIndices());
          break;
      }
      m_context.bindBuffer(vgl.GL.ARRAY_BUFFER, null);
    }

    /* If we are rendering multiple features in the same context, we must
     * unbind the vertex data to make sure the next feature has a known state.
     * This is optional.
     */
    if (!noUndoBindVertexData) {
      this.undoBindVertexData(renderState);
    }
  };

  /**
   * Unbind the vertex data,
   */
  this.undoBindVertexData = function (renderState) {
    var i, j;

    for (i in m_bufferVertexAttributeMap) {
      if (m_bufferVertexAttributeMap.hasOwnProperty(i)) {
        for (j = 0; j < m_bufferVertexAttributeMap[i].length; j += 1) {
          renderState.m_material
              .undoBindVertexData(renderState, m_bufferVertexAttributeMap[i][j]);
        }
      }
    }
  };

  return this;
};

inherit(vgl.mapper, vgl.boundingObject);

//////////////////////////////////////////////////////////////////////////////
/**
 * @module vgl
 */

/*global vgl, inherit*/
//////////////////////////////////////////////////////////////////////////////

vgl.groupMapper = function () {
  'use strict';

  if (!(this instanceof vgl.groupMapper)) {
    return new vgl.groupMapper();
  }
  vgl.mapper.call(this);

  /** @private */
  var m_createMappersTimestamp = vgl.timestamp(),
      m_mappers = [],
      m_geomDataArray = [];

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return stored geometry data if any
   *
   * @param index optional
   */
  ////////////////////////////////////////////////////////////////////////////
  this.geometryData = function (index) {
    if (index !== undefined && index < m_geomDataArray.length) {
      return m_geomDataArray[index];
    }

    if (m_geomDataArray.length > 0) {
      return m_geomDataArray[0];
    }

    return null;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Connect mapper to its geometry data
   *
   * @param geom {vgl.geomData}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setGeometryData = function (geom) {
    if (m_geomDataArray.length === 1) {
      if (m_geomDataArray[0] === geom) {
        return;
      }
    }
    m_geomDataArray = [];
    m_geomDataArray.push(geom);
    this.modified();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return stored geometry data array if any
   */
  ////////////////////////////////////////////////////////////////////////////
  this.geometryDataArray = function () {
    return m_geomDataArray;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Connect mapper to its geometry data
   *
   * @param geoms {Array}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setGeometryDataArray = function (geoms) {
    if (geoms instanceof Array) {
      if (m_geomDataArray !== geoms) {
        m_geomDataArray = [];
        m_geomDataArray = geoms;
        this.modified();
        return true;
      }
    } else {
      console.log('[error] Requies array of geometry data');
    }

    return false;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Compute bounds of the data
   */
  ////////////////////////////////////////////////////////////////////////////
  this.computeBounds = function () {
    if (m_geomDataArray === null ||
        m_geomDataArray === undefined) {
      this.resetBounds();
      return;
    }

    var computeBoundsTimestamp = this.computeBoundsTimestamp(),
        boundsDirtyTimestamp = this.boundsDirtyTimestamp(),
        m_bounds = this.bounds(),
        geomBounds = null,
        i = null;

    if (boundsDirtyTimestamp.getMTime() >
        computeBoundsTimestamp.getMTime()) {

      for (i = 0; i < m_geomDataArray.length; i += 1) {
        geomBounds = m_geomDataArray[i].bounds();

        if (m_bounds[0] > geomBounds[0]) {
          m_bounds[0] = geomBounds[0];
        }
        if (m_bounds[1] < geomBounds[1]) {
          m_bounds[1] = geomBounds[1];
        }
        if (m_bounds[2] > geomBounds[2]) {
          m_bounds[2] = geomBounds[2];
        }
        if (m_bounds[3] < geomBounds[3]) {
          m_bounds[3] = geomBounds[3];
        }
        if (m_bounds[4] > geomBounds[4]) {
          m_bounds[4] = geomBounds[4];
        }
        if (m_bounds[5] < geomBounds[5]) {
          m_bounds[5] = geomBounds[5];
        }
      }

      this.modified();
      computeBoundsTimestamp.modified();
    }
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Render the mapper
   */
  ////////////////////////////////////////////////////////////////////////////
  this.render = function (renderState) {
    var i = null;

    if (this.getMTime() > m_createMappersTimestamp.getMTime()) {
      // NOTE Hoping that it will release the graphics resources
      for (i = 0; i < m_geomDataArray.length; i += 1) {
        m_mappers.push(vgl.mapper());
        m_mappers[i].setGeometryData(m_geomDataArray[i]);
      }
      m_createMappersTimestamp.modified();
    }

    for (i = 0; i < m_mappers.length; i += 1) {
      m_mappers[i].render(renderState);
    }
  };

  return this;
};

inherit(vgl.groupMapper, vgl.mapper);

//////////////////////////////////////////////////////////////////////////////
/**
 * @module vgl
 */

/*global vgl, inherit*/
//////////////////////////////////////////////////////////////////////////////

vgl.materialAttributeType = {
  'Undefined' : 0x0,
  'ShaderProgram' : 0x1,
  'Texture' : 0x2,
  'Blend' : 0x3,
  'Depth' : 0x4
};

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class materialAttribute
 *
 * @class
 * @param type
 * @returns {vgl.materialAttribute}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.materialAttribute = function (type) {
  'use strict';

  if (!(this instanceof vgl.materialAttribute)) {
    return new vgl.materialAttribute(type);
  }
  vgl.graphicsObject.call(this);

  /** @private */
  var m_this = this,
      m_type = type,
      m_enabled = true;

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return tyep of the material attribute
   *
   * @returns {*}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.type = function () {
    return m_type;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return if material attribute is enabled or not
   *
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.enabled = function () {
    return m_enabled;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Bind and activate vertex specific data
   *
   * @param renderState
   * @param key
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.bindVertexData = function (renderState, key) {
    renderState = renderState; /* unused parameter */
    key = key; /* unused parameter */
    return false;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Undo bind and deactivate vertex specific data
   *
   * @param renderState
   * @param key
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.undoBindVertexData = function (renderState, key) {
    renderState = renderState; /* unused parameter */
    key = key; /* unused parameter */
    return false;
  };

  return m_this;
};

inherit(vgl.materialAttribute, vgl.graphicsObject);

//////////////////////////////////////////////////////////////////////////////
/**
 * @module vgl
 */

/*global vgl, inherit*/
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of clas blendFunction
 *
 * @class
 * @param source
 * @param destination
 * @returns {vgl.blendFunction}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.blendFunction = function (source, destination) {
  'use strict';

  if (!(this instanceof vgl.blendFunction)) {
    return new vgl.blendFunction(source, destination);
  }

  /** @private */
  var m_source = source,
      m_destination = destination;

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Apply blend function to the current state
   *
   * @param {vgl.renderState}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.apply = function (renderState) {
    renderState.m_context.blendFuncSeparate(m_source, m_destination,
                         vgl.GL.ONE, vgl.GL.ONE_MINUS_SRC_ALPHA);
  };

  return this;
};

////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class blend
 *
 * @returns {vgl.blend}
 */
////////////////////////////////////////////////////////////////////////////
vgl.blend = function () {
  'use strict';

  if (!(this instanceof vgl.blend)) {
    return new vgl.blend();
  }
  vgl.materialAttribute.call(
    this, vgl.materialAttributeType.Blend);

  /** @private */
  var m_wasEnabled = false,
      m_blendFunction = vgl.blendFunction(vgl.GL.SRC_ALPHA,
                                          vgl.GL.ONE_MINUS_SRC_ALPHA);

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Bind blend attribute
   *
   * @param {vgl.renderState}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.bind = function (renderState) {
    m_wasEnabled = renderState.m_context.isEnabled(vgl.GL.BLEND);

    if (this.enabled()) {
      renderState.m_context.enable(vgl.GL.BLEND);
      m_blendFunction.apply(renderState);
    } else {
      renderState.m_context.disable(vgl.GL.BLEND);
    }

    return true;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Undo bind blend attribute
   *
   * @param {vgl.renderState}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.undoBind = function (renderState) {
    if (m_wasEnabled) {
      renderState.m_context.enable(vgl.GL.BLEND);
    } else {
      renderState.m_context.disable(vgl.GL.BLEND);
    }

    return true;
  };

  return this;
};

inherit(vgl.blend, vgl.materialAttribute);

//////////////////////////////////////////////////////////////////////////////
/**
 * @module vgl
 */

/*global vgl, inherit*/
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class material
 *
 * @class
 * @returns {vgl.material}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.material = function () {
  'use strict';

  if (!(this instanceof vgl.material)) {
    return new vgl.material();
  }
  vgl.graphicsObject.call(this);

  // / Private member variables
  var m_this = this,
      m_shaderProgram = new vgl.shaderProgram(),
      m_binNumber = 100,
      m_textureAttributes = {},
      m_attributes = {};

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return bin number for the material
   *
   * @default 100
   * @returns {number}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.binNumber = function () {
    return m_binNumber;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set bin number for the material
   *
   * @param binNo
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setBinNumber = function (binNo) {
    m_binNumber = binNo;
    m_this.modified();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Check if incoming attribute already exists in the material
   *
   * @param attr
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.exists = function (attr) {
    if (attr.type() === vgl.materialAttributeType.Texture) {
      return m_textureAttributes.hasOwnProperty(attr.textureUnit());
    }
    return m_attributes.hasOwnProperty(attr.type());
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get uniform given a name

   * @param name Uniform name
   * @returns {vgl.uniform}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.uniform = function (name) {
    return m_shaderProgram ? m_shaderProgram.uniform(name) : null;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get material attribute

   * @param {number} type attribute type
   * @param {number} textureUnit attribute texture unit if type is Texture.
   * @returns {vgl.materialAttribute}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.attribute = function (type, textureUnit) {
    if (m_attributes.hasOwnProperty(type)) {
      return m_attributes[type];
    }

    if (type === vgl.materialAttributeType.Texture && m_textureAttributes.hasOwnProperty(textureUnit)) {
      return m_textureAttributes[textureUnit];
    }

    return null;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set a new attribute for the material
   *
   * This method replace any existing attribute except for textures as
   * materials can have multiple textures.
   *
   * @param attr
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setAttribute = function (attr) {
    if (attr.type() === vgl.materialAttributeType.Texture) {
      if (m_textureAttributes[attr.textureUnit()] === attr) {
        return false;
      }
      m_textureAttributes[attr.textureUnit()] = attr;
      m_this.modified();
      return true;
    }

    if (m_attributes[attr.type()] === attr) {
      return false;
    }

    // Shader is a very special attribute
    if (attr.type() === vgl.materialAttributeType.ShaderProgram) {
      m_shaderProgram = attr;
    }

    m_attributes[attr.type()] = attr;
    m_this.modified();
    return true;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Add a new attribute to the material.
   *
   * @param attr
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.addAttribute = function (attr) {
    if (m_this.exists(attr)) {
      return false;
    }

    if (attr.type() === vgl.materialAttributeType.Texture) {
      // TODO Currently we don't check if we are replacing or not.
      // It would be nice to have a flag for it.
      m_textureAttributes[attr.textureUnit()] = attr;
      m_this.modified();
      return true;
    }

    // Shader is a very special attribute
    if (attr.type() === vgl.materialAttributeType.ShaderProgram) {
      m_shaderProgram = attr;
    }

    m_attributes[attr.type()] = attr;
    m_this.modified();
    return true;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return shader program used by the material
   *
   * @returns {vgl.shaderProgram}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.shaderProgram = function () {
    return m_shaderProgram;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Setup (initialize) the material attribute
   *
   * @param renderState
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this._setup = function (renderState) {
    void renderState; /* unused parameter */
    return false;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Remove any resources acquired before deletion
   *
   * @param renderState
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this._cleanup = function (renderState) {
    for (var key in m_attributes) {
      if (m_attributes.hasOwnProperty(key)) {
        m_attributes[key]._cleanup(renderState);
      }
    }

    for (key in m_textureAttributes) {
      if (m_textureAttributes.hasOwnProperty(key)) {
        m_textureAttributes[key]._cleanup(renderState);
      }
    }
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Bind and activate material states
   *
   * @param renderState
   */
  ////////////////////////////////////////////////////////////////////////////
  this.bind = function (renderState) {
    var key = null;

    m_shaderProgram.bind(renderState);

    for (key in m_attributes) {
      if (m_attributes.hasOwnProperty(key)) {
        if (m_attributes[key] !== m_shaderProgram) {
          m_attributes[key].bind(renderState);
        }
      }
    }

    for (key in m_textureAttributes) {
      if (m_textureAttributes.hasOwnProperty(key)) {
        m_textureAttributes[key].bind(renderState);
      }
    }
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Undo-bind and de-activate material states
   *
   * @param renderState
   */
  ////////////////////////////////////////////////////////////////////////////
  this.undoBind = function (renderState) {
    var key = null;
    for (key in m_attributes) {
      if (m_attributes.hasOwnProperty(key)) {
        m_attributes[key].undoBind(renderState);
      }
    }

    for (key in m_textureAttributes) {
      if (m_textureAttributes.hasOwnProperty(key)) {
        m_textureAttributes[key].undoBind(renderState);
      }
    }
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Bind vertex data
   *
   * @param renderState
   * @param key
   */
  ////////////////////////////////////////////////////////////////////////////
  this.bindVertexData = function (renderState, key) {
    var i = null;

    for (i in m_attributes) {
      if (m_attributes.hasOwnProperty(i)) {
        m_attributes[i].bindVertexData(renderState, key);
      }
    }
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Undo bind vertex data
   *
   * @param renderState
   * @param key
   */
  ////////////////////////////////////////////////////////////////////////////
  this.undoBindVertexData = function (renderState, key) {
    var i = null;

    for (i in m_attributes) {
      if (m_attributes.hasOwnProperty(i)) {
        m_attributes[i].undoBindVertexData(renderState, key);
      }
    }
  };

  return m_this;
};

vgl.material.RenderBin = {
  'Base' : 0,
  'Default' : 100,
  'Opaque' : 100,
  'Transparent' : 1000,
  'Overlay' : 10000
};

inherit(vgl.material, vgl.graphicsObject);

//////////////////////////////////////////////////////////////////////////////
/**
 * @module vgl
 */

/*global vgl, vec2, vec3, vec4, mat4, inherit*/
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class renderState
 *
 * @returns {vgl.renderState}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.renderState = function () {
  'use strict';

  this.m_context = null;
  this.m_modelViewMatrix = mat4.create();
  this.m_normalMatrix = mat4.create();
  this.m_projectionMatrix = null;
  this.m_material = null;
  this.m_mapper = null;
};

////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class renderer *
 *
 * @returns {vgl.renderer}
 */
////////////////////////////////////////////////////////////////////////////
vgl.renderer = function (arg) {
  'use strict';

  if (!(this instanceof vgl.renderer)) {
    return new vgl.renderer(arg);
  }
  vgl.graphicsObject.call(this);
  arg = arg || {};

  /** @private */
  var m_this = this;
  m_this.m_renderWindow = null;
  m_this.m_contextChanged = false;
  m_this.m_sceneRoot = new vgl.groupNode();
  m_this.m_camera = new vgl.camera(arg);
  m_this.m_nearClippingPlaneTolerance = null;
  m_this.m_x = 0;
  m_this.m_y = 0;
  m_this.m_width = 0;
  m_this.m_height = 0;
  m_this.m_resizable = true;
  m_this.m_resetScene = true;
  m_this.m_layer = 0;
  m_this.m_renderPasses = null;
  m_this.m_resetClippingRange = true;
  m_this.m_depthBits = null;

  m_this.m_camera.addChild(m_this.m_sceneRoot);

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get width of the renderer
   */
  ////////////////////////////////////////////////////////////////////////////
  this.width = function () {
    return m_this.m_width;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get height of the renderer
   */
  ////////////////////////////////////////////////////////////////////////////
  this.height = function () {
    return m_this.m_height;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get layer this renderer is associated with
   *
   * @return {Number}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.layer = function () {
    return m_this.m_layer;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set the layer this renderer is associated with.
   *
   * @param layerNo
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setLayer = function (layerNo) {
    m_this.m_layer = layerNo;
    m_this.modified();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   *
   */
  ////////////////////////////////////////////////////////////////////////////
  this.isResizable = function () {
    return m_this.m_resizable;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   *
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setResizable = function (r) {
    m_this.m_resizable = r;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return render window (owner) of the renderer
   */
  ////////////////////////////////////////////////////////////////////////////
  this.renderWindow = function () {
    return m_this.m_renderWindow;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set render window for the renderer
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setRenderWindow = function (renWin) {
    if (m_this.m_renderWindow !== renWin) {
      if (m_this.m_renderWindow) {
        m_this.m_renderWindow.removeRenderer(this);
      }
      m_this.m_renderWindow = renWin;
      m_this.m_contextChanged = true;
      m_this.modified();
    }
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get background color
   */
  ////////////////////////////////////////////////////////////////////////////
  this.backgroundColor = function () {
    return m_this.m_camera.clearColor();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set background color of the renderer
   *
   * @param r
   * @param g
   * @param b
   * @param a
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setBackgroundColor = function (r, g, b, a) {
    m_this.m_camera.setClearColor(r, g, b, a);
    m_this.modified();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get scene root
   *
   * @returns {vgl.groupNode}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.sceneRoot = function () {
    return m_this.m_sceneRoot;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get main camera of the renderer
   *
   * @returns {vgl.camera}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.camera = function () {
    return m_this.m_camera;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Render the scene
   */
  ////////////////////////////////////////////////////////////////////////////
  this.render = function () {
    var i, renSt, children, actor = null, sortedActors = [],
        mvMatrixInv = mat4.create(), clearColor = null;

    renSt = new vgl.renderState();
    renSt.m_renderer = m_this;
    renSt.m_context = m_this.renderWindow().context();
    if (!m_this.m_depthBits || m_this.m_contextChanged) {
      m_this.m_depthBits = renSt.m_context.getParameter(vgl.GL.DEPTH_BITS);
    }
    renSt.m_contextChanged = m_this.m_contextChanged;

    if (m_this.m_renderPasses) {
      for (i = 0; i < m_this.m_renderPasses.length; i += 1) {
        if (m_this.m_renderPasses[i].render(renSt)) {
          // Stop the rendering if render pass returns false
          console.log('returning');
          m_this.m_renderPasses[i].remove(renSt);
          return;
        }
        m_this.m_renderPasses[i].remove(renSt);
      }
    }

    renSt.m_context.enable(vgl.GL.DEPTH_TEST);
    renSt.m_context.depthFunc(vgl.GL.LEQUAL);

    /*jshint bitwise: false */
    if (m_this.m_camera.clearMask() & vgl.GL.COLOR_BUFFER_BIT) {
      clearColor = m_this.m_camera.clearColor();
      renSt.m_context.clearColor(clearColor[0], clearColor[1],
                                 clearColor[2], clearColor[3]);
    }

    if (m_this.m_camera.clearMask() & vgl.GL.DEPTH_BUFFER_BIT) {
      renSt.m_context.clearDepth(m_this.m_camera.clearDepth());
    }
    /*jshint bitwise: true */

    renSt.m_context.clear(m_this.m_camera.clearMask());

    // Set the viewport for this renderer
    renSt.m_context.viewport(m_this.m_x, m_this.m_y,
                             m_this.m_width, m_this.m_height);

    children = m_this.m_sceneRoot.children();

    if (children.length > 0 && m_this.m_resetScene) {
      m_this.resetCamera();
      m_this.m_resetScene = false;
    }

    for (i = 0; i < children.length; i += 1) {
      actor = children[i];

      // Compute the bounds even if the actor is not visible
      actor.computeBounds();

      // If bin number is < 0, then don't even bother
      // rendering the data
      if (actor.visible() && actor.material().binNumber() >= 0) {
        sortedActors.push([actor.material().binNumber(), actor]);
      }
    }

    // Now perform sorting
    sortedActors.sort(function (a, b) { return a[0] - b[0]; });

    for (i = 0; i < sortedActors.length; i += 1) {
      actor = sortedActors[i][1];
      if (actor.referenceFrame() ===
          vgl.boundingObject.ReferenceFrame.Relative) {
        var view = m_this.m_camera.viewMatrix();
        /* If the view matrix is a plain array, keep it as such.  This is
         * intended to preserve precision, and will only be the case if the
         * view matrix was created by delibrately setting it as an array. */
        if (view instanceof Array) {
          renSt.m_modelViewMatrix = new Array(16);
        }
        mat4.multiply(renSt.m_modelViewMatrix, view, actor.matrix());
        renSt.m_projectionMatrix = m_this.m_camera.projectionMatrix();
        renSt.m_modelViewAlignment = m_this.m_camera.viewAlignment();
      } else {
        renSt.m_modelViewMatrix = actor.matrix();
        renSt.m_modelViewAlignment = null;
        renSt.m_projectionMatrix = mat4.create();
        mat4.ortho(renSt.m_projectionMatrix,
                   0, m_this.m_width, 0, m_this.m_height, -1, 1);
      }

      mat4.invert(mvMatrixInv, renSt.m_modelViewMatrix);
      mat4.transpose(renSt.m_normalMatrix, mvMatrixInv);
      renSt.m_material = actor.material();
      renSt.m_mapper = actor.mapper();

      // TODO Fix this shortcut
      renSt.m_material.bind(renSt);
      renSt.m_mapper.render(renSt);
      renSt.m_material.undoBind(renSt);
    }

    renSt.m_context.finish();
    m_this.m_contextChanged = false;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Automatically set up the camera based on visible actors
   */
  ////////////////////////////////////////////////////////////////////////////
  this.resetCamera = function () {
    m_this.m_camera.computeBounds();

    var vn = m_this.m_camera.directionOfProjection(),
        visibleBounds = m_this.m_camera.bounds(),
        center = [
          (visibleBounds[0] + visibleBounds[1]) / 2.0,
          (visibleBounds[2] + visibleBounds[3]) / 2.0,
          (visibleBounds[4] + visibleBounds[5]) / 2.0
        ],
        diagonals = [
          visibleBounds[1] - visibleBounds[0],
          visibleBounds[3] - visibleBounds[2],
          visibleBounds[5] - visibleBounds[4]
        ],
        radius = 0.0,
        aspect = m_this.m_camera.viewAspect(),
        angle = m_this.m_camera.viewAngle(),
        distance = null,
        vup = null;

    if (diagonals[0] > diagonals[1]) {
      if (diagonals[0] > diagonals[2]) {
        radius = diagonals[0] / 2.0;
      } else {
        radius = diagonals[2] / 2.0;
      }
    } else {
      if (diagonals[1] > diagonals[2]) {
        radius = diagonals[1] / 2.0;
      } else {
        radius = diagonals[2] / 2.0;
      }
    }

    // @todo Need to figure out what's happening here
    if (aspect >= 1.0) {
      angle = 2.0 * Math.atan(Math.tan(angle * 0.5) / aspect);
    } else {
      angle = 2.0 * Math.atan(Math.tan(angle * 0.5) * aspect);
    }

    distance = radius / Math.sin(angle * 0.5);
    vup = m_this.m_camera.viewUpDirection();

    if (Math.abs(vec3.dot(vup, vn)) > 0.999) {
      m_this.m_camera.setViewUpDirection(-vup[2], vup[0], vup[1]);
    }

    m_this.m_camera.setFocalPoint(center[0], center[1], center[2]);
    m_this.m_camera.setPosition(center[0] + distance * -vn[0],
      center[1] + distance * -vn[1], center[2] + distance * -vn[2]);

    m_this.resetCameraClippingRange(visibleBounds);
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
  * Check whether or not whether or not the bounds are valid
  */
  ////////////////////////////////////////////////////////////////////////////
  this.hasValidBounds = function (bounds) {
    if (bounds[0] === Number.MAX_VALUE ||
        bounds[1] === -Number.MAX_VALUE ||
        bounds[2] === Number.MAX_VALUE ||
        bounds[3] === -Number.MAX_VALUE ||
        bounds[4] === Number.MAX_VALUE ||
        bounds[5] === -Number.MAX_VALUE) {
      return false;
    }

    return true;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Recalculate camera's clipping range
   */
  ////////////////////////////////////////////////////////////////////////////
  this.resetCameraClippingRange = function (bounds) {
    if (typeof bounds === 'undefined') {
      m_this.m_camera.computeBounds();
      bounds = m_this.m_camera.bounds();
    }

    if (!m_this.hasValidBounds(bounds)) {
      return;
    }

    var vn = m_this.m_camera.viewPlaneNormal(),
        position = m_this.m_camera.position(),
        a = -vn[0],
        b = -vn[1],
        c = -vn[2],
        d = -(a * position[0] + b * position[1] + c * position[2]),
        range = vec2.create(),
        dist = null,
        i = null,
        j = null,
        k = null;

    if (!m_this.m_resetClippingRange) {
      return;
    }

    // Set the max near clipping plane and the min far clipping plane
    range[0] = a * bounds[0] + b * bounds[2] + c * bounds[4] + d;
    range[1] = 1e-18;

    // Find the closest / farthest bounding box vertex
    for (k = 0; k < 2; k += 1) {
      for (j = 0; j < 2; j += 1) {
        for (i = 0; i < 2; i += 1) {
          dist = a * bounds[i] + b * bounds[2 + j] + c * bounds[4 + k] + d;
          range[0] = (dist < range[0]) ? (dist) : (range[0]);
          range[1] = (dist > range[1]) ? (dist) : (range[1]);
        }
      }
    }

    // Do not let the range behind the camera throw off the calculation.
    if (range[0] < 0.0) {
      range[0] = 0.0;
    }

    // Give ourselves a little breathing room
    range[0] = 0.99 * range[0] - (range[1] - range[0]) * 0.5;
    range[1] = 1.01 * range[1] + (range[1] - range[0]) * 0.5;

    // Make sure near is not bigger than far
    range[0] = (range[0] >= range[1]) ? (0.01 * range[1]) : (range[0]);

    // Make sure near is at least some fraction of far - this prevents near
    // from being behind the camera or too close in front. How close is too
    // close depends on the resolution of the depth buffer.
    if (!m_this.m_nearClippingPlaneTolerance) {
      m_this.m_nearClippingPlaneTolerance = 0.01;

      if (m_this.m_depthBits && m_this.m_depthBits > 16) {
        m_this.m_nearClippingPlaneTolerance = 0.001;
      }
    }

    // make sure the front clipping range is not too far from the far clippnig
    // range, this is to make sure that the zbuffer resolution is effectively
    // used.
    if (range[0] < m_this.m_nearClippingPlaneTolerance * range[1]) {
      range[0] = m_this.m_nearClippingPlaneTolerance * range[1];
    }

    m_this.m_camera.setClippingRange(range[0], range[1]);
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Resize viewport given a width and height
   */
  ////////////////////////////////////////////////////////////////////////////
  this.resize = function (width, height) {
    if (!width || !height) {
      return;
    }
    // @note: where do m_this.m_x and m_this.m_y come from?
    m_this.positionAndResize(m_this.m_x, m_this.m_y, width, height);
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Resize viewport given a position, width and height
   */
  ////////////////////////////////////////////////////////////////////////////
  this.positionAndResize = function (x, y, width, height) {
    var i;

    // TODO move this code to camera
    if (x < 0 || y < 0 || width <= 0 || height <= 0) {
      console.log('[error] Invalid position and resize values',
        x, y, width, height);
      return;
    }

    //If we're allowing this renderer to resize ...
    if (m_this.m_resizable) {
      m_this.m_width = width;
      m_this.m_height = height;

      m_this.m_camera.setViewAspect(width / height);
      m_this.m_camera.setParallelExtents({width: width, height: height});
      m_this.modified();
    }

    if (m_this.m_renderPasses) {
      for (i = 0; i < m_this.m_renderPasses.length; i += 1) {
        m_this.m_renderPasses[i].resize(width, height);
        m_this.m_renderPasses[i].renderer().positionAndResize(x, y, width, height);
      }
    }
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Add new actor to the collection
   *
   * @param actor
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.addActor = function (actor) {
    if (actor instanceof vgl.actor) {
      m_this.m_sceneRoot.addChild(actor);
      m_this.modified();
      return true;
    }

    return false;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return true if this renderer has this actor attached, false otherwise.
   *
   * @param actor
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.hasActor = function (actor) {
    return m_this.m_sceneRoot.hasChild(actor);
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Add an array of actors to the collection
   */
  ////////////////////////////////////////////////////////////////////////////
  this.addActors = function (actors) {
    var i = null;
    if (actors instanceof Array) {
      for (i = 0; i < actors.length; i += 1) {
        m_this.m_sceneRoot.addChild(actors[i]);
      }
      m_this.modified();
    }
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Remove the actor from the collection
   *
   * @param actor
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.removeActor = function (actor) {
    if (m_this.m_sceneRoot.children().indexOf(actor) !== -1) {
      /* When we remove an actor, free the VBOs of the mapper and mark the
       * mapper as modified; it will reallocate VBOs as necessary. */
      if (actor.mapper()) {
        actor.mapper().deleteVertexBufferObjects();
        actor.mapper().modified();
      }
      m_this.m_sceneRoot.removeChild(actor);
      m_this.modified();
      return true;
    }

    return false;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Remove actors from the collection
   *
   * @param actors
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.removeActors = function (actors) {
    if (!(actors instanceof Array)) {
      return false;
    }

    var i;
    for (i = 0; i < actors.length; i += 1) {
      m_this.m_sceneRoot.removeChild(actors[i]);
    }
    m_this.modified();
    return true;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Remove all actors for a renderer
   *
   * @returns {*}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.removeAllActors = function () {
    return m_this.m_sceneRoot.removeChildren();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Transform a point in the world space to display space
   */
  ////////////////////////////////////////////////////////////////////////////
  this.worldToDisplay = function (worldPt, viewMatrix, projectionMatrix, width,
                                 height) {
    var viewProjectionMatrix = mat4.create(),
        winX = null,
        winY = null,
        winZ = null,
        winW = null,
        clipPt = null;

    mat4.multiply(viewProjectionMatrix, projectionMatrix, viewMatrix);

    // Transform world to clipping coordinates
    clipPt = vec4.create();
    vec4.transformMat4(clipPt, worldPt, viewProjectionMatrix);

    if (clipPt[3] !== 0.0) {
      clipPt[0] = clipPt[0] / clipPt[3];
      clipPt[1] = clipPt[1] / clipPt[3];
      clipPt[2] = clipPt[2] / clipPt[3];
      clipPt[3] = 1.0;
    }

    winX = (((clipPt[0]) + 1) / 2.0) * width;
    // We calculate -point3D.getY() because the screen Y axis is
    // oriented top->down
    winY = ((1 - clipPt[1]) / 2.0) * height;
    winZ = clipPt[2];
    winW = clipPt[3];

    return vec4.fromValues(winX, winY, winZ, winW);
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Transform a point in display space to world space
   * @param displayPt
   * @param viewMatrix
   * @param projectionMatrix
   * @param width
   * @param height
   * @returns {vec4}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.displayToWorld = function (displayPt, viewMatrix, projectionMatrix,
                                 width, height) {
    var x = (2.0 * displayPt[0] / width) - 1,
        y = -(2.0 * displayPt[1] / height) + 1,
        z = displayPt[2],
        viewProjectionInverse = mat4.create(),
        worldPt = null;

    mat4.multiply(viewProjectionInverse, projectionMatrix, viewMatrix);
    mat4.invert(viewProjectionInverse, viewProjectionInverse);

    worldPt = vec4.fromValues(x, y, z, 1);
    vec4.transformMat4(worldPt, worldPt, viewProjectionInverse);
    if (worldPt[3] !== 0.0) {
      worldPt[0] = worldPt[0] / worldPt[3];
      worldPt[1] = worldPt[1] / worldPt[3];
      worldPt[2] = worldPt[2] / worldPt[3];
      worldPt[3] = 1.0;
    }

    return worldPt;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get the focusDisplayPoint
   * @returns {vec4}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.focusDisplayPoint = function () {
    var focalPoint = m_this.m_camera.focalPoint(),
        focusWorldPt = vec4.fromValues(
        focalPoint[0], focalPoint[1], focalPoint[2], 1);

    return m_this.worldToDisplay(
      focusWorldPt, m_this.m_camera.viewMatrix(),
      m_this.m_camera.projectionMatrix(), m_this.m_width, m_this.m_height);
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Will the scene be reset.
   * @returns {bool}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.resetScene = function () {
    return m_this.m_resetScene;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * If true the scene will be reset, otherwise the scene will not be
   * automatically reset.
   *
   * @param reset
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setResetScene = function (reset) {
    if (m_this.m_resetScene !== reset) {
      m_this.m_resetScene = reset;
      m_this.modified();
    }
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Will the clipping range be reset
   * @returns {bool}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.resetClippingRange = function () {
    return m_this.m_resetClippingRange;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * If true the camera clipping range will be reset, otherwise the scene will
   * not be automatically reset.
   *
   * @param reset
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setResetClippingRange = function (reset) {
    if (m_this.m_resetClippingRange !== reset) {
      m_this.m_resetClippingRange = reset;
      m_this.modified();
    }
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   *
   */
  ////////////////////////////////////////////////////////////////////////////
  this.addRenderPass = function (renPass) {
    var i;

    if (m_this.m_renderPasses) {
      for (i = 0; i < m_this.m_renderPasses.length; i += 1) {
        if (renPass === m_this.m_renderPasses[i]) {
          return;
        }
      }
    }

    m_this.m_renderPasses = [];
    m_this.m_renderPasses.push(renPass);
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   *
   */
  ////////////////////////////////////////////////////////////////////////////
  this.removeRenderPass = function (renPass) {
    renPass = renPass; // TODO Implement this
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   *
   */
  ////////////////////////////////////////////////////////////////////////////
  this._cleanup = function (renderState) {
    var children = m_this.m_sceneRoot.children();
    for (var i = 0; i < children.length; i += 1) {
      var actor = children[i];
      actor.material()._cleanup(renderState);
      actor.mapper()._cleanup(renderState);
    }

    m_this.m_sceneRoot.removeChildren();
  };

  return m_this;
};

inherit(vgl.renderer, vgl.graphicsObject);

//////////////////////////////////////////////////////////////////////////////
/**
 * @module vgl
 */

/*global vgl, vec4, inherit*/
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class renderWindow
 *
 * @class
 * @returns {vgl.renderWindow}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.renderWindow = function (canvas) {
  'use strict';

  if (!(this instanceof vgl.renderWindow)) {
    return new vgl.renderWindow(canvas);
  }
  vgl.graphicsObject.call(this);

  /** @private */
  var m_this = this,
      m_x = 0,
      m_y = 0,
      m_width = 400,
      m_height = 400,
      m_canvas = canvas,
      m_activeRender = null,
      m_renderers = [],
      m_context = null;

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get size of the render window
   *
   * @returns {Array}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.windowSize = function () {
    return [m_width, m_height];
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set size of the render window
   *
   * @param width
   * @param height
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setWindowSize = function (width, height) {

    if (m_width !== width || m_height !== height) {
      m_width = width;
      m_height = height;

      m_this.modified();

      return true;
    }

    return false;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get window position (top left coordinates)
   *
   * @returns {Array}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.windowPosition = function () {
    return [m_x, m_y];
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set window position (top left coordinates)
   *
   * @param x
   * @param y
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setWindowPosition = function (x, y) {
    if ((m_x !== x) || (m_y !== y)) {
      m_x = x;
      m_y = y;
      m_this.modified();
      return true;
    }
    return false;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return all renderers contained in the render window
   * @returns {Array}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.renderers = function () {
    return m_renderers;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get active renderer of the the render window
   *
   * @returns vgl.renderer
   */
  ////////////////////////////////////////////////////////////////////////////
  this.activeRenderer = function () {
    return m_activeRender;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Add renderer to the render window
   *
   * @param ren
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.addRenderer = function (ren) {
    if (m_this.hasRenderer(ren) === false) {
      m_renderers.push(ren);
      ren.setRenderWindow(m_this);
      if (m_activeRender === null) {
        m_activeRender = ren;
      }
      if (ren.layer() !== 0) {
        ren.camera().setClearMask(vgl.GL.DepthBufferBit);
      }
      m_this.modified();
      return true;
    }
    return false;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Remove renderer from the render window
   *
   * @param ren
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.removeRenderer = function (ren) {
    var index = m_renderers.indexOf(ren);
    if (index !== -1) {
      if (m_activeRender === ren) {
        m_activeRender = null;
      }
      m_renderers.splice(index, 1);
      m_this.modified();
      return true;
    }
    return false;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return a renderer at a given index
   *
   * @param index
   * @returns {vgl.renderer}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.getRenderer = function (index) {
    if (index < m_renderers.length) {
      return m_renderers[index];
    }

    console.log('[WARNING] Out of index array');
    return null;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Check if the renderer exists
   *
   * @param ren
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.hasRenderer = function (ren) {
    var i;
    for (i = 0; i < m_renderers.length; i += 1) {
      if (ren === m_renderers[i]) {
        return true;
      }
    }

    return false;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Resize window
   *
   * @param width
   * @param height
   */
  ////////////////////////////////////////////////////////////////////////////
  this.resize = function (width, height) {
    m_this.positionAndResize(m_x, m_y, width, height);
    m_this.modified();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Resize and reposition the window
   *
   * @param x
   * @param y
   * @param width
   * @param height
   */
  ////////////////////////////////////////////////////////////////////////////
  this.positionAndResize = function (x, y, width, height) {
    m_x = x;
    m_y = y;
    m_width = width;
    m_height = height;
    var i;
    for (i = 0; i < m_renderers.length; i += 1) {
      m_renderers[i].positionAndResize(m_x, m_y, m_width, m_height);
    }
    m_this.modified();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Create the window
   *
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this._setup = function (renderState) {
    renderState = renderState; /* unused parameter */
    m_context = null;

    try {
      // Try to grab the standard context. If it fails, fallback to
      // experimental.
      m_context = m_canvas.getContext('webgl') ||
            m_canvas.getContext('experimental-webgl');

      // Set width and height of renderers if not set already
      var i;
      for (i = 0; i < m_renderers.length; i += 1) {
        if ((m_renderers[i].width() > m_width) ||
            m_renderers[i].width() === 0 ||
            (m_renderers[i].height() > m_height) ||
            m_renderers[i].height() === 0) {
          m_renderers[i].resize(m_x, m_y, m_width, m_height);
        }
      }

      return true;
    } catch (e) {
    }

    // If we don't have a GL context, give up now
    if (!m_context) {
      console('[ERROR] Unable to initialize WebGL. Your browser may not support it.');
    }

    return false;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return current GL context
   */
  ////////////////////////////////////////////////////////////////////////////
  this.context = function () {
    return m_context;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Delete this window and release any graphics resources
   */
  ////////////////////////////////////////////////////////////////////////////
  this._cleanup = function (renderState) {
    var i;
    for (i = 0; i < m_renderers.length; i += 1) {
      m_renderers[i]._cleanup(renderState);
    }
    vgl.clearCachedShaders(renderState ? renderState.m_context : null);
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Render the scene
   */
  ////////////////////////////////////////////////////////////////////////////
  this.render = function () {
    var i;
    m_renderers.sort(function (a, b) { return a.layer() - b.layer(); });
    for (i = 0; i < m_renderers.length; i += 1) {
      m_renderers[i].render();
    }
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get the focusDisplayPoint from the activeRenderer
   * @returns {vec4}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.focusDisplayPoint = function () {
    return m_activeRender.focusDisplayPoint();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Transform a point in display space to world space
   * @param {Number} x
   * @param {Number} y
   * @param {vec4} focusDisplayPoint
   * @returns {vec4}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.displayToWorld = function (x, y, focusDisplayPoint, ren) {
    ren = ren === undefined ? ren = m_activeRender : ren;

    var camera = ren.camera();
    if (!focusDisplayPoint) {
      focusDisplayPoint = ren.focusDisplayPoint();
    }

    return ren.displayToWorld(
      vec4.fromValues(x, y, focusDisplayPoint[2], 1.0), camera.viewMatrix(),
      camera.projectionMatrix(), m_width, m_height);
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Transform a point in display space to world space
   * @param {Number} x
   * @param {Number} y
   * @param {vec4} focusDisplayPoint
   * @returns {vec4}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.worldToDisplay = function (x, y, z, ren) {
    ren = ren === undefined ? ren = m_activeRender : ren;
    var camera = ren.camera();
    return ren.worldToDisplay(
      vec4.fromValues(x, y, z, 1.0), camera.viewMatrix(),
      camera.projectionMatrix(), m_width, m_height);
  };

  return m_this;
};

inherit(vgl.renderWindow, vgl.graphicsObject);

//////////////////////////////////////////////////////////////////////////////
/**
 * @module vgl
 */

/*global vgl, vec3, vec4, mat4, inherit*/
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class camera
 *
 * @class
 * @returns {vgl.camera}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.camera = function (arg) {
  'use strict';

  if (!(this instanceof vgl.camera)) {
    return new vgl.camera(arg);
  }
  vgl.groupNode.call(this);
  arg = arg || {};

  /** @private */
  var m_viewAngle = (Math.PI * 30) / 180.0,
      m_position = vec4.fromValues(0.0, 0.0, 1.0, 1.0),
      m_focalPoint = vec4.fromValues(0.0, 0.0, 0.0, 1.0),
      m_centerOfRotation = vec3.fromValues(0.0, 0.0, 0.0),
      m_viewUp = vec4.fromValues(0.0, 1.0, 0.0, 0.0),
      m_rightDir = vec4.fromValues(1.0, 0.0, 0.0, 0.0),
      m_near = 0.01,
      m_far = 10000.0,
      m_viewAspect = 1.0,
      m_directionOfProjection = vec4.fromValues(0.0, 0.0, -1.0, 0.0),
      m_viewPlaneNormal = vec4.fromValues(0.0, 0.0, 1.0, 0.0),
      m_viewMatrix = mat4.create(),
      m_projectionMatrix = mat4.create(),
      m_computeModelViewMatrixTime = vgl.timestamp(),
      m_computeProjectMatrixTime = vgl.timestamp(),
      m_left = -1.0,
      m_right = 1.0,
      m_top = +1.0,
      m_bottom = -1.0,
      m_parallelExtents = {zoom: 1, tilesize: 256},
      m_enableTranslation = true,
      m_enableRotation = true,
      m_enableScale = true,
      m_enableParallelProjection = false,
      m_clearColor = [0.0, 0.0, 0.0, 0.0],
      m_clearDepth = 1.0,
      /*jshint bitwise: false */
      m_clearMask = vgl.GL.COLOR_BUFFER_BIT |
                    vgl.GL.DEPTH_BUFFER_BIT;
  /*jshint bitwise: true */

  if (arg.parallelProjection !== undefined) {
    m_enableParallelProjection = arg.parallelProjection ? true : false;
  }

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get view angle of the camera
   */
  ////////////////////////////////////////////////////////////////////////////
  this.viewAngle = function () {
    return m_viewAngle;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set view angle of the camera in degrees, which is converted to radians.
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setViewAngleDegrees = function (a) {
    this.setViewAngle(Math.PI * a / 180.0);
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set view angle of the camera in degrees, which is converted to radians.
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setViewAngle = function (a) {
    if (m_enableScale) {
      m_viewAngle = a;
      this.modified();
    }
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get position of the camera
   */
  ////////////////////////////////////////////////////////////////////////////
  this.position = function () {
    return m_position;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set position of the camera
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setPosition = function (x, y, z) {
    if (m_enableTranslation) {
      m_position = vec4.fromValues(x, y, z, 1.0);
      this.modified();
    }
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get focal point of the camera
   */
  ////////////////////////////////////////////////////////////////////////////
  this.focalPoint = function () {
    return m_focalPoint;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set focal point of the camera
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setFocalPoint = function (x, y, z) {
    if (m_enableRotation && m_enableTranslation) {
      m_focalPoint = vec4.fromValues(x, y, z, 1.0);
      this.modified();
    }
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get view-up direction of camera
   */
  ////////////////////////////////////////////////////////////////////////////
  this.viewUpDirection = function () {
    return m_viewUp;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set view-up direction of the camera
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setViewUpDirection = function (x, y, z) {
    m_viewUp = vec4.fromValues(x, y, z, 0);
    this.modified();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get center of rotation for camera
   */
  ////////////////////////////////////////////////////////////////////////////
  this.centerOfRotation = function () {
    return m_centerOfRotation;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set center of rotation for camera
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setCenterOfRotation = function (centerOfRotation) {
    m_centerOfRotation = centerOfRotation;
    this.modified();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get clipping range of the camera
   */
  ////////////////////////////////////////////////////////////////////////////
  this.clippingRange = function () {
    return [m_near, m_far];
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set clipping range of the camera
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setClippingRange = function (near, far) {
    m_near = near;
    m_far = far;
    this.modified();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get view aspect
   */
  ////////////////////////////////////////////////////////////////////////////
  this.viewAspect = function () {
    return m_viewAspect;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set view aspect
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setViewAspect = function (aspect) {
    m_viewAspect = aspect;
    this.modified();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return active mode for scaling (enabled / disabled)
   */
  ////////////////////////////////////////////////////////////////////////////
  this.enableScale = function () {
    return m_enableScale;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Enable/disable scaling
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setEnableScale = function (flag) {
    if (flag !== m_enableScale) {
      m_enableScale = flag;
      this.modified();
      return true;
    }

    return m_enableScale;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return active mode for rotation (enabled / disabled)
   */
  ////////////////////////////////////////////////////////////////////////////
  this.enableRotation = function () {
    return m_enableRotation;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Enable / disable rotation
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setEnableRotation = function (flag) {
    if (flag !== m_enableRotation) {
      m_enableRotation = flag;
      this.modified();
      return true;
    }

    return m_enableRotation;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return active mode for translation (enabled/disabled)
   */
  ////////////////////////////////////////////////////////////////////////////
  this.enableTranslation = function () {
    return m_enableTranslation;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Enable / disable translation
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setEnableTranslation = function (flag) {
    if (flag !== m_enableTranslation) {
      m_enableTranslation = flag;
      this.modified();
      return true;
    }

    return m_enableTranslation;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return if parallel projection is enabled
   */
  ////////////////////////////////////////////////////////////////////////////
  this.isEnabledParallelProjection = function () {
    return m_enableParallelProjection;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Enable / disable parallel projection
   */
  ////////////////////////////////////////////////////////////////////////////
  this.enableParallelProjection = function (flag) {
    if (flag !== m_enableParallelProjection) {
      m_enableParallelProjection = flag;
      this.modified();
      return true;
    }

    return m_enableParallelProjection;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Enable / disable parallel projection
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setEnableParallelProjection = function (flag) {
    return this.enableParallelProjection(flag);
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get parallel projection parameters
   */
  ////////////////////////////////////////////////////////////////////////////
  this.parallelProjection = function () {
    return {left: m_left, right: m_right, top: m_top, bottom: m_bottom};
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set parallel projection parameters
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setParallelProjection = function (left, right, top, bottom) {
    m_left = left;
    m_right = right;
    m_top = top;
    m_bottom = bottom;
    this.modified();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get parallel projection extents parameters
   */
  ////////////////////////////////////////////////////////////////////////////
  this.parallelExtents = function () {
    return m_parallelExtents;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set parallel projection extents parameters
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setParallelExtents = function (extents) {
    var prop = ['width', 'height', 'zoom', 'tilesize'], mod = false, i, key;
    for (i = 0; i < prop.length; i += 1) {
      key = prop[i];
      if (extents[key] !== undefined &&
          extents[key] !== m_parallelExtents[key]) {
        m_parallelExtents[key] = extents[key];
        mod = true;
      }
    }
    if (mod && m_parallelExtents.width && m_parallelExtents.height &&
        m_parallelExtents.zoom !== undefined && m_parallelExtents.tilesize) {
      var unitsPerPixel = this.unitsPerPixel(m_parallelExtents.zoom,
                                             m_parallelExtents.tilesize);
      m_right = unitsPerPixel * m_parallelExtents.width / 2;
      m_left = -m_right;
      m_top = unitsPerPixel * m_parallelExtents.height / 2;
      m_bottom = -m_top;
      this.modified();
    }
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Compute the units per pixel.
   *
   * @param zoom: tile zoom level.
   * @param tilesize: number of pixels per tile (defaults to 256).
   * @returns: unitsPerPixel.
   */
  ////////////////////////////////////////////////////////////////////////////
  this.unitsPerPixel = function (zoom, tilesize) {
    tilesize = tilesize || 256;
    return 360.0 * Math.pow(2, -zoom) / tilesize;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return direction of projection
   */
  ////////////////////////////////////////////////////////////////////////////
  this.directionOfProjection = function () {
    this.computeDirectionOfProjection();
    return m_directionOfProjection;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return view plane normal direction
   */
  ////////////////////////////////////////////////////////////////////////////
  this.viewPlaneNormal = function () {
    this.computeViewPlaneNormal();
    return m_viewPlaneNormal;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return view-matrix for the camera This method does not compute the
   * view-matrix for the camera. It is assumed that a call to computeViewMatrix
   * has been made earlier.
   *
   * @returns {mat4}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.viewMatrix = function () {
    return this.computeViewMatrix();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set the view-matrix for the camera and mark that it is up to date so that
   * it won't be recomputed unless something else changes.
   *
   * @param {mat4} view: new view matrix.
   * @param {boolean} preserveType: if true, clone the input using slice.  This
   *    can be used to ensure the array is a specific precision.
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setViewMatrix = function (view, preserveType) {
    if (!preserveType) {
      mat4.copy(m_viewMatrix, view);
    } else {
      m_viewMatrix = view.slice();
    }
    m_computeModelViewMatrixTime.modified();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return camera projection matrix This method does not compute the
   * projection-matrix for the camera. It is assumed that a call to
   * computeProjectionMatrix has been made earlier.
   *
   * @returns {mat4}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.projectionMatrix = function () {
    return this.computeProjectionMatrix();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set the projection-matrix for the camera and mark that it is up to date so
   * that it won't be recomputed unless something else changes.
   *
   * @param {mat4} proj: new projection matrix.
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setProjectionMatrix = function (proj) {
    mat4.copy(m_projectionMatrix, proj);
    m_computeProjectMatrixTime.modified();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return clear mask used by this camera
   *
   * @returns {number}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.clearMask = function () {
    return m_clearMask;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set clear mask for camera
   *
   * @param mask
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setClearMask = function (mask) {
    m_clearMask = mask;
    this.modified();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get clear color (background color) of the camera
   *
   * @returns {Array}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.clearColor = function () {
    return m_clearColor;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set clear color (background color) for the camera
   *
   * @param color RGBA
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setClearColor = function (r, g, b, a) {
    m_clearColor[0] = r;
    m_clearColor[1] = g;
    m_clearColor[2] = b;
    m_clearColor[3] = a;

    this.modified();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   *
   * @returns {{1.0: null}}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.clearDepth = function () {
    return m_clearDepth;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   *
   * @param depth
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setClearDepth = function (depth) {
    m_clearDepth = depth;
    this.modified();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Compute direction of projection
   */
  ////////////////////////////////////////////////////////////////////////////
  this.computeDirectionOfProjection = function () {
    vec3.subtract(m_directionOfProjection, m_focalPoint, m_position);
    vec3.normalize(m_directionOfProjection, m_directionOfProjection);
    this.modified();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Compute view plane normal
   */
  ////////////////////////////////////////////////////////////////////////////
  this.computeViewPlaneNormal = function () {
    m_viewPlaneNormal[0] = -m_directionOfProjection[0];
    m_viewPlaneNormal[1] = -m_directionOfProjection[1];
    m_viewPlaneNormal[2] = -m_directionOfProjection[2];
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Move camera closer or further away from the scene
   */
  ////////////////////////////////////////////////////////////////////////////
  this.zoom = function (d, dir) {
    if (d === 0) {
      return;
    }

    if (!m_enableTranslation) {
      return;
    }

    d = d * vec3.distance(m_focalPoint, m_position);
    if (!dir) {
      dir = m_directionOfProjection;
      m_position[0] = m_focalPoint[0] - d * dir[0];
      m_position[1] = m_focalPoint[1] - d * dir[1];
      m_position[2] = m_focalPoint[2] - d * dir[2];
    } else {
      m_position[0] = m_position[0] + d * dir[0];
      m_position[1] = m_position[1] + d * dir[1];
      m_position[2] = m_position[2] + d * dir[2];
    }

    this.modified();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Move camera sideways
   */
  ////////////////////////////////////////////////////////////////////////////
  this.pan = function (dx, dy, dz) {
    if (!m_enableTranslation) {
      return;
    }

    m_position[0] += dx;
    m_position[1] += dy;
    m_position[2] += dz;

    m_focalPoint[0] += dx;
    m_focalPoint[1] += dy;
    m_focalPoint[2] += dz;

    this.modified();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Compute camera coordinate axes
   */
  ////////////////////////////////////////////////////////////////////////////
  this.computeOrthogonalAxes = function () {
    this.computeDirectionOfProjection();
    vec3.cross(m_rightDir, m_directionOfProjection, m_viewUp);
    vec3.normalize(m_rightDir, m_rightDir);
    this.modified();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Rotate camera around center of rotation
   * @param dx Rotation around vertical axis in degrees
   * @param dy Rotation around horizontal axis in degrees
   */
  ////////////////////////////////////////////////////////////////////////////
  this.rotate = function (dx, dy) {
    if (!m_enableRotation) {
      return;
    }

    // Convert degrees into radians
    dx = 0.5 * dx * (Math.PI / 180.0);
    dy = 0.5 * dy * (Math.PI / 180.0);

    var mat = mat4.create(),
        inverseCenterOfRotation = new vec3.create();

    mat4.identity(mat);

    inverseCenterOfRotation[0] = -m_centerOfRotation[0];
    inverseCenterOfRotation[1] = -m_centerOfRotation[1];
    inverseCenterOfRotation[2] = -m_centerOfRotation[2];

    mat4.translate(mat, mat, m_centerOfRotation);
    mat4.rotate(mat, mat, dx, m_viewUp);
    mat4.rotate(mat, mat, dy, m_rightDir);
    mat4.translate(mat, mat, inverseCenterOfRotation);

    vec4.transformMat4(m_position, m_position, mat);
    vec4.transformMat4(m_focalPoint, m_focalPoint, mat);

    // Update viewup vector
    vec4.transformMat4(m_viewUp, m_viewUp, mat);
    vec4.normalize(m_viewUp, m_viewUp);

    // Update direction of projection
    this.computeOrthogonalAxes();

    // Mark modified
    this.modified();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Compute camera view matrix
   */
  ////////////////////////////////////////////////////////////////////////////
  this.computeViewMatrix = function () {
    if (m_computeModelViewMatrixTime.getMTime() < this.getMTime()) {
      mat4.lookAt(m_viewMatrix, m_position, m_focalPoint, m_viewUp);
      m_computeModelViewMatrixTime.modified();
    }
    return m_viewMatrix;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Check if the texture should be aligned to the screen.  Alignment only
   * occurs if the parallel extents contain width, height, and a
   * close-to-integer zoom level, and if the units-per-pixel value has been
   * computed.  The camera must either be in parallel projection mode OR must
   * have a perspective camera which is oriented along the z-axis without any
   * rotation.
   *
   * @returns: either null if no alignment should occur, or an alignment object
   *           with the rounding value and offsets.
   */
  ////////////////////////////////////////////////////////////////////////////
  this.viewAlignment = function () {
    if (!m_enableParallelProjection) {
      /* If we aren't in parallel projection mode, ensure that the projection
       * matrix meets strict specifications */
      var proj = this.projectionMatrix();
      if (proj[1] || proj[2] || proj[3] || proj[4] || proj[6] || proj[7] ||
          proj[8] || proj[9] || proj[12] || proj[13] || proj[15]) {
        return null;
      }
    }
    var unitsPerPixel = this.unitsPerPixel(m_parallelExtents.zoom,
                                           m_parallelExtents.tilesize);
    /* If we don't have screen dimensions, we can't know how to align pixels */
    if (!m_parallelExtents.width || !m_parallelExtents.height ||
        !unitsPerPixel) {
      return null;
    }
    /* If we aren't at an integer zoom level, we shouldn't align pixels.  If
     * we are really close to an integer zoom level, that is good enough. */
    if (parseFloat(m_parallelExtents.zoom.toFixed(6)) !==
        parseFloat(m_parallelExtents.zoom.toFixed(0))) {
      return null;
    }
    var align = {roundx: unitsPerPixel, roundy: unitsPerPixel, dx: 0, dy: 0};
    /* If the screen is an odd number of pixels, shift the view center to the
     * center of a pixel so that the pixels fit discretely across the screen.
     * If an even number of pixels, align the view center between pixels for
     * the same reason. */
    if (m_parallelExtents.width % 2) {
      align.dx = unitsPerPixel * 0.5;
    }
    if (m_parallelExtents.height % 2) {
      align.dy = unitsPerPixel * 0.5;
    }
    return align;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Compute camera projection matrix
   */
  ////////////////////////////////////////////////////////////////////////////
  this.computeProjectionMatrix = function () {
    if (m_computeProjectMatrixTime.getMTime() < this.getMTime()) {
      if (!m_enableParallelProjection) {
        mat4.perspective(m_projectionMatrix, m_viewAngle, m_viewAspect, m_near, m_far);
      } else {
        mat4.ortho(m_projectionMatrix,
          m_left, m_right, m_bottom, m_top, m_near, m_far);
      }

      m_computeProjectMatrixTime.modified();
    }

    return m_projectionMatrix;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Convert a zoom level and window size to a camera height.
   */
  ////////////////////////////////////////////////////////////////////////////
  this.zoomToHeight = function (zoom, width, height) {
    return vgl.zoomToHeight(zoom, width, height, m_viewAngle);
  };

  this.computeDirectionOfProjection();

  return this;
};

inherit(vgl.camera, vgl.groupNode);

////////////////////////////////////////////////////////////////////////////
/**
 * Convert a zoom level and window size to a camera height.
 *
 * @param zoom: Zoom level, as used in OSM maps.
 * @param width: width of the window.
 * @param height: height of the window.
 * @returns: perspective camera height.
 */
////////////////////////////////////////////////////////////////////////////
vgl.zoomToHeight = function (zoom, width, height, viewAngle) {
  'use strict';
  viewAngle = viewAngle || (30 * Math.PI / 180.0);
  var newZ = 360 * Math.pow(2, -zoom);
  newZ /= Math.tan(viewAngle / 2) * 2 * 256 / height;
  return newZ;
};

////////////////////////////////////////////////////////////////////////////
/**
 * Convert a camera height and window size to a zoom level.
 *
 * @param z: perspective camera height.
 * @param width: width of the window.
 * @param height: height of the window.
 * @returns: zoom level.
 */
////////////////////////////////////////////////////////////////////////////
vgl.heightToZoom = function (z, width, height, viewAngle) {
  'use strict';
  viewAngle = viewAngle || (30 * Math.PI / 180.0);
  z *= Math.tan(viewAngle / 2) * 2 * 256 / height;
  var zoom = -Math.log2(z / 360);
  return zoom;
};

//////////////////////////////////////////////////////////////////////////////
/**
 * @module vgl
 */

/*global vgl, inherit, $*/
//////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class interactorStyle
 *
 * @class vgl.interactorStyle
 * interactorStyle is a base class for all interactor styles
 * @returns {vgl.interactorStyle}
 */
////////////////////////////////////////////////////////////////////////////
vgl.interactorStyle = function () {
  'use strict';

  if (!(this instanceof vgl.interactorStyle)) {
    return new vgl.interactorStyle();
  }
  vgl.object.call(this);

  // Private member variables
  var m_that = this,
      m_viewer = null;

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return viewer referenced by the interactor style
   *
   * @returns {null}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.viewer = function () {
    return m_viewer;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set viewer for the interactor style
   *
   * @param viewer
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setViewer = function (viewer) {
    if (viewer !== m_viewer) {
      m_viewer = viewer;
      $(m_viewer).on(vgl.event.mousePress, m_that.handleMouseDown);
      $(m_viewer).on(vgl.event.mouseRelease, m_that.handleMouseUp);
      $(m_viewer).on(vgl.event.mouseMove, m_that.handleMouseMove);
      $(m_viewer).on(vgl.event.mouseOut, m_that.handleMouseOut);
      $(m_viewer).on(vgl.event.mouseWheel, m_that.handleMouseWheel);
      $(m_viewer).on(vgl.event.keyPress, m_that.handleKeyPress);
      $(m_viewer).on(vgl.event.mouseContextMenu, m_that.handleContextMenu);
      $(m_viewer).on(vgl.event.click, m_that.handleClick);
      $(m_viewer).on(vgl.event.dblClick, m_that.handleDoubleClick);
      this.modified();
    }
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Handle mouse down event
   *
   * @param event
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.handleMouseDown = function (event) {
    event = event; /* unused parameter */
    return true;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Handle mouse up event
   *
   * @param event
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.handleMouseUp = function (event) {
    event = event; /* unused parameter */
    return true;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Handle mouse move event
   *
   * @param event
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.handleMouseMove = function (event) {
    event = event; /* unused parameter */
    return true;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Handle mouse move event
   *
   * @param event
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.handleMouseOut = function (event) {
    event = event; /* unused parameter */
    return true;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Handle mouse wheel event
   *
   * @param event
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.handleMouseWheel = function (event) {
    event = event; /* unused parameter */
    return true;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Handle click event
   *
   * @param event
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.handleClick = function (event) {
    event = event; /* unused parameter */
    return true;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Handle double click event
   *
   * @param event
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.handleDoubleClick = function (event) {
    event = event; /* unused parameter */
    return true;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Handle key press event
   *
   * @param event
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.handleKeyPress = function (event) {
    event = event; /* unused parameter */
    return true;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Handle context menu event
   *
   * @param event
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.handleContextMenu = function (event) {
    event = event; /* unused parameter */
    return true;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Reset to default
   */
  ////////////////////////////////////////////////////////////////////////////
  this.reset = function () {
    return true;
  };

  return this;
};

inherit(vgl.interactorStyle, vgl.object);

//////////////////////////////////////////////////////////////////////////////
/**
 * @module vgl
 */

/*global vgl, vec4, inherit*/
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of trackballInteractorStyle
 *
 * @class vgl.trackballInteractorStyle
 * @returns {vgl.trackballInteractorStyle}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.trackballInteractorStyle = function () {
  'use strict';

  if (!(this instanceof vgl.trackballInteractorStyle)) {
    return new vgl.trackballInteractorStyle();
  }
  vgl.interactorStyle.call(this);
  var m_that = this,
      m_leftMouseBtnDown = false,
      m_rightMouseBtnDown = false,
      m_midMouseBtnDown = false,
      m_outsideCanvas,
      m_currPos = {x: 0, y: 0},
      m_lastPos = {x: 0, y: 0};

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Handle mouse move event
   *
   * @param event
   * @returns {boolean}
   */
  /////////////////////////////////////////////////////////////////////////////
  this.handleMouseMove = function (event) {
    var width = m_that.viewer().renderWindow().windowSize()[0],
        height = m_that.viewer().renderWindow().windowSize()[1],
        ren = m_that.viewer().renderWindow().activeRenderer(),
        cam = ren.camera(), coords = m_that.viewer().relMouseCoords(event),
        fp, fdp, fwp, dp1, dp2, wp1, wp2, dx, dy, dz, m_zTrans;

    m_outsideCanvas = false;
    m_currPos = {x: 0, y: 0};

    if ((coords.x < 0) || (coords.x > width)) {
      m_currPos.x = 0;
      m_outsideCanvas = true;
    } else {
      m_currPos.x = coords.x;
    }
    if ((coords.y < 0) || (coords.y > height)) {
      m_currPos.y = 0;
      m_outsideCanvas = true;
    } else {
      m_currPos.y = coords.y;
    }
    if (m_outsideCanvas === true) {
      return;
    }

    fp = cam.focalPoint();
    fwp = vec4.fromValues(fp[0], fp[1], fp[2], 1);
    fdp = ren.worldToDisplay(fwp, cam.viewMatrix(),
                              cam.projectionMatrix(), width, height);

    dp1 = vec4.fromValues(m_currPos.x, m_currPos.y, fdp[2], 1.0);
    dp2 = vec4.fromValues(m_lastPos.x, m_lastPos.y, fdp[2], 1.0);

    wp1 = ren.displayToWorld(dp1, cam.viewMatrix(), cam.projectionMatrix(),
                             width, height);
    wp2 = ren.displayToWorld(dp2, cam.viewMatrix(), cam.projectionMatrix(),
                             width, height);

    dx = wp1[0] - wp2[0];
    dy = wp1[1] - wp2[1];
    dz = wp1[2] - wp2[2];

    if (m_midMouseBtnDown) {
      cam.pan(-dx, -dy, -dz);
      m_that.viewer().render();
    }
    if (m_leftMouseBtnDown) {
      cam.rotate((m_lastPos.x - m_currPos.x),
      (m_lastPos.y - m_currPos.y));
      ren.resetCameraClippingRange();
      m_that.viewer().render();
    }
    if (m_rightMouseBtnDown) {
      /// 2.0 is the speed up factor
      m_zTrans = 2.0 * (m_currPos.y - m_lastPos.y) / height;

      // Calculate zoom scale here
      if (m_zTrans > 0) {
        cam.zoom(1 - Math.abs(m_zTrans));
      } else {
        cam.zoom(1 + Math.abs(m_zTrans));
      }
      ren.resetCameraClippingRange();
      m_that.viewer().render();
    }
    m_lastPos.x = m_currPos.x;
    m_lastPos.y = m_currPos.y;
    return false;
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Handle mouse down event
   *
   * @param event
   * @returns {boolean}
   */
  /////////////////////////////////////////////////////////////////////////////
  this.handleMouseDown = function (event) {
    var coords;

    if (event.button === 0) {
      m_leftMouseBtnDown = true;
    }
    if (event.button === 1) {
      m_midMouseBtnDown = true;
    }
    if (event.button === 2) {
      m_rightMouseBtnDown = true;
    }
    coords = m_that.viewer().relMouseCoords(event);
    if (coords.x < 0) {
      m_lastPos.x = 0;
    } else {
      m_lastPos.x = coords.x;
    }
    if (coords.y < 0) {
      m_lastPos.y = 0;
    } else {
      m_lastPos.y = coords.y;
    }
    return false;
  };

  // @note We never get mouse up from scroll bar: See the bug report here
  // http://bugs.jquery.com/ticket/8184
  /////////////////////////////////////////////////////////////////////////////
  /**
   * Handle mouse up event
   *
   * @param event
   * @returns {boolean}
   */
  /////////////////////////////////////////////////////////////////////////////
  this.handleMouseUp = function (event) {
    if (event.button === 0) {
      m_leftMouseBtnDown = false;
    }
    if (event.button === 1) {
      m_midMouseBtnDown = false;
    }
    if (event.button === 2) {
      m_rightMouseBtnDown = false;
    }
    return false;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Handle mouse wheel event
   *
   * @param event
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.handleMouseWheel = function (event) {
    var ren = m_that.viewer().renderWindow().activeRenderer(),
        cam = ren.camera();

    // TODO Compute zoom factor intelligently
    if (event.originalEvent.wheelDelta < 0) {
      cam.zoom(0.9);
    } else {
      cam.zoom(1.1);
    }
    ren.resetCameraClippingRange();
    m_that.viewer().render();
    return true;
  };

  return this;
};
inherit(vgl.trackballInteractorStyle, vgl.interactorStyle);

//////////////////////////////////////////////////////////////////////////////
/**
 * @module vgl
 */

/*global vgl, vec4, inherit*/
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of pvwInteractorStyle (for ParaViewWeb)
 *
 * @class vgl.pvwInteractorStyle
 * @returns {vgl.pvwInteractorStyle}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.pvwInteractorStyle = function () {
  'use strict';

  if (!(this instanceof vgl.pvwInteractorStyle)) {
    return new vgl.pvwInteractorStyle();
  }
  vgl.trackballInteractorStyle.call(this);
  var m_that = this,
      m_leftMouseButtonDown = false,
      m_rightMouseButtonDown = false,
      m_middleMouseButtonDown = false,
      m_width,
      m_height,
      m_renderer,
      m_camera,
      m_outsideCanvas,
      m_coords,
      m_currentMousePos,
      m_focalPoint,
      m_focusWorldPt,
      m_focusDisplayPt,
      m_displayPt1,
      m_displayPt2,
      m_worldPt1,
      m_worldPt2,
      m_dx,
      m_dy,
      m_dz,
      m_zTrans,
      m_mouseLastPos = {
        x: 0,
        y: 0
      };

  function render() {
    m_renderer.resetCameraClippingRange();
    m_that.viewer().render();
  }

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Handle mouse move event
   *
   * @param event
   * @returns {boolean}
   */
  /////////////////////////////////////////////////////////////////////////////
  this.handleMouseMove = function (event) {
    var rens = [], i = null, secCameras = [], deltaxy = null;
    m_width = m_that.viewer().renderWindow().windowSize()[0];
    m_height = m_that.viewer().renderWindow().windowSize()[1];
    m_renderer = m_that.viewer().renderWindow().activeRenderer();
    m_camera = m_renderer.camera();
    m_outsideCanvas = false;
    m_coords = m_that.viewer().relMouseCoords(event);
    m_currentMousePos = {
      x: 0,
      y: 0
    };

    // Get secondary cameras
    rens = m_that.viewer().renderWindow().renderers();
    for (i = 0; i < rens.length; i += 1) {
      if (m_renderer !== rens[i]) {
        secCameras.push(rens[i].camera());
      }
    }

    if ((m_coords.x < 0) || (m_coords.x > m_width)) {
      m_currentMousePos.x = 0;
      m_outsideCanvas = true;
    } else {
      m_currentMousePos.x = m_coords.x;
    }
    if ((m_coords.y < 0) || (m_coords.y > m_height)) {
      m_currentMousePos.y = 0;
      m_outsideCanvas = true;
    } else {
      m_currentMousePos.y = m_coords.y;
    }
    if (m_outsideCanvas === true) {
      return;
    }
    m_focalPoint = m_camera.focalPoint();
    m_focusWorldPt = vec4.fromValues(m_focalPoint[0], m_focalPoint[1],
                                     m_focalPoint[2], 1);
    m_focusDisplayPt = m_renderer.worldToDisplay(m_focusWorldPt,
        m_camera.viewMatrix(), m_camera.projectionMatrix(), m_width, m_height);
    m_displayPt1 = vec4.fromValues(
      m_currentMousePos.x, m_currentMousePos.y, m_focusDisplayPt[2], 1.0);
    m_displayPt2 = vec4.fromValues(
      m_mouseLastPos.x, m_mouseLastPos.y, m_focusDisplayPt[2], 1.0);
    m_worldPt1 = m_renderer.displayToWorld(
      m_displayPt1, m_camera.viewMatrix(), m_camera.projectionMatrix(),
      m_width, m_height);
    m_worldPt2 = m_renderer.displayToWorld(
      m_displayPt2, m_camera.viewMatrix(), m_camera.projectionMatrix(),
      m_width, m_height);

    m_dx = m_worldPt1[0] - m_worldPt2[0];
    m_dy = m_worldPt1[1] - m_worldPt2[1];
    m_dz = m_worldPt1[2] - m_worldPt2[2];

    if (m_middleMouseButtonDown) {
      m_camera.pan(-m_dx, -m_dy, -m_dz);
      render();
    }
    if (m_leftMouseButtonDown) {
      deltaxy = [(m_mouseLastPos.x - m_currentMousePos.x),
      (m_mouseLastPos.y - m_currentMousePos.y)];
      m_camera.rotate(deltaxy[0], deltaxy[1]);

      // Apply rotation to all other cameras
      for (i = 0; i < secCameras.length; i += 1) {
        secCameras[i].rotate(deltaxy[0], deltaxy[1]);
      }

      // Apply rotation to all other cameras
      for (i = 0; i < rens.length; i += 1) {
        rens[i].resetCameraClippingRange();
      }
      render();
    }
    if (m_rightMouseButtonDown) {
      /// 2.0 is the speed up factor.
      m_zTrans = 2.0 * (m_currentMousePos.y - m_mouseLastPos.y) / m_height;

      // Calculate zoom scale here
      if (m_zTrans > 0) {
        m_camera.zoom(1 - Math.abs(m_zTrans));
      } else {
        m_camera.zoom(1 + Math.abs(m_zTrans));
      }
      render();
    }
    m_mouseLastPos.x = m_currentMousePos.x;
    m_mouseLastPos.y = m_currentMousePos.y;
    return false;
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Handle mouse down event
   *
   * @param event
   * @returns {boolean}
   */
  /////////////////////////////////////////////////////////////////////////////
  this.handleMouseDown = function (event) {
    if (event.button === 0) {
      m_leftMouseButtonDown = true;
    }
    if (event.button === 1) {
      m_middleMouseButtonDown = true;
    }
    if (event.button === 2) {
      m_rightMouseButtonDown = true;
    }
    m_coords = m_that.viewer().relMouseCoords(event);
    if (m_coords.x < 0) {
      m_mouseLastPos.x = 0;
    } else {
      m_mouseLastPos.x = m_coords.x;
    }
    if (m_coords.y < 0) {
      m_mouseLastPos.y = 0;
    } else {
      m_mouseLastPos.y = m_coords.y;
    }
    return false;
  };

  // @note We never get mouse up from scroll bar: See the bug report here
  // http://bugs.jquery.com/ticket/8184
  /////////////////////////////////////////////////////////////////////////////
  /**
   * Handle mouse up event
   *
   * @param event
   * @returns {boolean}
   */
  /////////////////////////////////////////////////////////////////////////////
  this.handleMouseUp = function (event) {
    if (event.button === 0) {
      m_leftMouseButtonDown = false;
    }
    if (event.button === 1) {
      m_middleMouseButtonDown = false;
    }
    if (event.button === 2) {
      m_rightMouseButtonDown = false;
    }
    return false;
  };

  return this;
};
inherit(vgl.pvwInteractorStyle, vgl.trackballInteractorStyle);

//////////////////////////////////////////////////////////////////////////////
/**
 * @module vgl
 */

/*global window, vgl, inherit, $*/
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class viewer
 *
 * @param canvas
 * @returns {vgl.viewer}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.viewer = function (canvas, options) {
  'use strict';

  if (!(this instanceof vgl.viewer)) {
    return new vgl.viewer(canvas, options);
  }

  vgl.object.call(this);

  var m_that = this,
      m_canvas = canvas,
      m_ready = true,
      m_interactorStyle = null,
      m_renderer = vgl.renderer(options),
      m_renderWindow = vgl.renderWindow(m_canvas);

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get canvas of the viewer
   *
   * @returns {*}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.canvas = function () {
    return m_canvas;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Return render window of the viewer
   *
   * @returns {vgl.renderWindow}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.renderWindow = function () {
    return m_renderWindow;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Initialize the viewer
   *
   * This is a must call or otherwise render context may not initialized
   * properly.
   */
  ////////////////////////////////////////////////////////////////////////////
  this.init = function () {
    if (m_renderWindow !== null) {
      m_renderWindow._setup();
    } else {
      console.log('[ERROR] No render window attached');
    }
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   *  Remove the viewer
   */
  ////////////////////////////////////////////////////////////////////////////
  this.exit = function (renderState) {
    if (m_renderWindow !== null) {
      m_renderWindow._cleanup(renderState);
    } else {
      console.log('[ERROR] No render window attached');
    }
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get interactor style of the viewer
   *
   * @returns {vgl.interactorStyle}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.interactorStyle = function () {
    return m_interactorStyle;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set interactor style to be used by the viewer
   *
   * @param {vgl.interactorStyle} style
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setInteractorStyle = function (style) {
    if (style !== m_interactorStyle) {
      m_interactorStyle = style;
      m_interactorStyle.setViewer(this);
      this.modified();
    }
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Handle mouse down event
   *
   * @param event
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.handleMouseDown = function (event) {
    if (m_ready === true) {
      var fixedEvent = $.event.fix(event || window.event);
      // Only prevent default action for right mouse button
      if (event.button === 2) {
        fixedEvent.preventDefault();
      }
      fixedEvent.state = 'down';
      fixedEvent.type = vgl.event.mousePress;
      $(m_that).trigger(fixedEvent);
    }

    return true;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Handle mouse up event
   *
   * @param event
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.handleMouseUp = function (event) {
    if (m_ready === true) {
      var fixedEvent = $.event.fix(event || window.event);
      fixedEvent.preventDefault();
      fixedEvent.state = 'up';
      fixedEvent.type = vgl.event.mouseRelease;
      $(m_that).trigger(fixedEvent);
    }

    return true;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Handle mouse move event
   *
   * @param event
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.handleMouseMove = function (event) {
    if (m_ready === true) {
      var fixedEvent = $.event.fix(event || window.event);
      fixedEvent.preventDefault();
      fixedEvent.type = vgl.event.mouseMove;
      $(m_that).trigger(fixedEvent);
    }

    return true;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Handle mouse wheel scroll
   *
   * @param event
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.handleMouseWheel = function (event) {
    if (m_ready === true) {
      var fixedEvent = $.event.fix(event || window.event);
      fixedEvent.preventDefault();
      fixedEvent.type = vgl.event.mouseWheel;
      $(m_that).trigger(fixedEvent);
    }

    return true;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Handle mouse move event
   *
   * @param event
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.handleMouseOut = function (event) {
    if (m_ready === true) {
      var fixedEvent = $.event.fix(event || window.event);
      fixedEvent.preventDefault();
      fixedEvent.type = vgl.event.mouseOut;
      $(m_that).trigger(fixedEvent);
    }

    return true;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Handle key press event
   *
   * @param event
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.handleKeyPress = function (event) {
    if (m_ready === true) {
      var fixedEvent = $.event.fix(event || window.event);
      fixedEvent.preventDefault();
      fixedEvent.type = vgl.event.keyPress;
      $(m_that).trigger(fixedEvent);
    }

    return true;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Handle context menu event
   *
   * @param event
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.handleContextMenu = function (event) {
    if (m_ready === true) {
      var fixedEvent = $.event.fix(event || window.event);
      fixedEvent.preventDefault();
      fixedEvent.type = vgl.event.contextMenu;
      $(m_that).trigger(fixedEvent);
    }

    return false;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Handle click event
   *
   * @param event
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.handleClick = function (event) {
    if (m_ready === true) {
      var fixedEvent = $.event.fix(event || window.event);
      fixedEvent.preventDefault();
      fixedEvent.type = vgl.event.click;
      $(m_that).trigger(fixedEvent);
    }

    return false;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Handle double click event
   *
   * @param event
   * @returns {boolean}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.handleDoubleClick = function (event) {
    if (m_ready === true) {
      var fixedEvent = $.event.fix(event || window.event);
      fixedEvent.preventDefault();
      fixedEvent.type = vgl.event.dblClick;
      $(m_that).trigger(fixedEvent);
    }

    return false;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get mouse coodinates related to canvas
   *
   * @param event
   * @returns {{x: number, y: number}}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.relMouseCoords = function (event) {
    if (event.pageX === undefined || event.pageY === undefined) {
      throw 'Missing attributes pageX and pageY on the event';
    }

    var totalOffsetX = 0,
        totalOffsetY = 0,
        canvasX = 0,
        canvasY = 0,
        currentElement = m_canvas;

    do {
      totalOffsetX += currentElement.offsetLeft - currentElement.scrollLeft;
      totalOffsetY += currentElement.offsetTop - currentElement.scrollTop;
      currentElement = currentElement.offsetParent;
    } while (currentElement);

    canvasX = event.pageX - totalOffsetX;
    canvasY = event.pageY - totalOffsetY;

    return {
      x: canvasX,
      y: canvasY
    };
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Render
   */
  ////////////////////////////////////////////////////////////////////////////
  this.render = function () {
    m_renderWindow.render();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Bind canvas mouse events to their default handlers
   */
  ////////////////////////////////////////////////////////////////////////////
  this.bindEventHandlers = function () {
    $(m_canvas).on('mousedown', this.handleMouseDown);
    $(m_canvas).on('mouseup', this.handleMouseUp);
    $(m_canvas).on('mousemove', this.handleMouseMove);
    $(m_canvas).on('mousewheel', this.handleMouseWheel);
    $(m_canvas).on('contextmenu', this.handleContextMenu);
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Undo earlier binded  handlers for canvas mouse events
   */
  ////////////////////////////////////////////////////////////////////////////
  this.unbindEventHandlers = function () {
    $(m_canvas).off('mousedown', this.handleMouseDown);
    $(m_canvas).off('mouseup', this.handleMouseUp);
    $(m_canvas).off('mousemove', this.handleMouseMove);
    $(m_canvas).off('mousewheel', this.handleMouseWheel);
    $(m_canvas).off('contextmenu', this.handleContextMenu);
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Initialize
   */
  ////////////////////////////////////////////////////////////////////////////
  this._init = function () {
    this.bindEventHandlers();
    m_renderWindow.addRenderer(m_renderer);
  };

  this._init();
  return this;
};

inherit(vgl.viewer, vgl.object);

//////////////////////////////////////////////////////////////////////////////
/**
 * @module vgl
 */

/*global vgl, inherit*/
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class shader
 *
 * @param type
 * @returns {vgl.shader}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.shader = function (type) {
  'use strict';

  if (!(this instanceof vgl.shader)) {
    return new vgl.shader(type);
  }
  vgl.object.call(this);

  var m_shaderContexts = [],
      m_shaderType = type,
      m_shaderSource = '';

  /**
   * A shader can be associated with multiple contexts.  Each context needs to
   * be compiled and attached separately.  These are tracked in the
   * m_shaderContexts array.
   *
   * @param renderState a renderState that includes a m_context value.
   * @return an object with context, compileTimestamp, and, if compiled, a
   *    shaderHandle entry.
   */
  this._getContextEntry = function (renderState) {
    var context = renderState.m_context, i, entry;
    for (i = 0; i < m_shaderContexts.length; i += 1) {
      if (m_shaderContexts[i].context === context) {
        return m_shaderContexts[i];
      }
    }
    entry = {
      context: context,
      compileTimestamp: vgl.timestamp()
    };
    m_shaderContexts.push(entry);
    return entry;
  };

  /**
   * Remove the context from the list of tracked contexts.  This allows the
   * associated shader handle to be GCed.  Does nothing if the context is not
   * in the list of tracked contexts.
   *
   * @param renderState a renderState that includes a m_context value.
   */
  this.removeContext = function (renderState) {
    var context = renderState.m_context, i;
    for (i = 0; i < m_shaderContexts.length; i += 1) {
      if (m_shaderContexts[i].context === context) {
        m_shaderContexts.splice(i, 1);
        return;
      }
    }
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Get shader handle
   */
  /////////////////////////////////////////////////////////////////////////////
  this.shaderHandle = function (renderState) {
    var entry = this._getContextEntry(renderState);
    return entry.shaderHandle;
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Get type of the shader
   *
   * @returns {*}
   */
  /////////////////////////////////////////////////////////////////////////////
  this.shaderType = function () {
    return m_shaderType;
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Get shader source
   *
   * @returns {string}
   */
  /////////////////////////////////////////////////////////////////////////////
  this.shaderSource = function () {
    return m_shaderSource;
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Set shader source
   *
   * @param {string} source
   */
  /////////////////////////////////////////////////////////////////////////////
  this.setShaderSource = function (source) {
    m_shaderSource = source;
    this.modified();
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Compile the shader
   *
   * @returns {null}
   */
  /////////////////////////////////////////////////////////////////////////////
  this.compile = function (renderState) {
    var entry = this._getContextEntry(renderState);
    if (this.getMTime() < entry.compileTimestamp.getMTime()) {
      return entry.shaderHandle;
    }

    renderState.m_context.deleteShader(entry.shaderHandle);
    entry.shaderHandle = renderState.m_context.createShader(m_shaderType);
    renderState.m_context.shaderSource(entry.shaderHandle, m_shaderSource);
    renderState.m_context.compileShader(entry.shaderHandle);

    // See if it compiled successfully
    if (!renderState.m_context.getShaderParameter(entry.shaderHandle,
        vgl.GL.COMPILE_STATUS)) {
      console.log('[ERROR] An error occurred compiling the shaders: ' +
                  renderState.m_context.getShaderInfoLog(entry.shaderHandle));
      console.log(m_shaderSource);
      renderState.m_context.deleteShader(entry.shaderHandle);
      return null;
    }

    entry.compileTimestamp.modified();

    return entry.shaderHandle;
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Attach shader to the program
   *
   * @param programHandle
   */
  /////////////////////////////////////////////////////////////////////////////
  this.attachShader = function (renderState, programHandle) {
    renderState.m_context.attachShader(
        programHandle, this.shaderHandle(renderState));
  };
};

inherit(vgl.shader, vgl.object);

/* We can use the same shader multiple times if it is identical.  This caches
 * the last N shaders and will reuse them when possible.  The cache keeps the
 * most recently requested shader at the front.  If you are doing anything more
 * to a shader then creating it and setting its source once, do not use this
 * cache.
 */
(function () {
  'use strict';
  var m_shaderCache = [],
      m_shaderCacheMaxSize = 10;

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Get a shader from the cache.  Create a new shader if necessary using a
   * specific source.
   *
   * @param type One of vgl.GL.*_SHADER
   * @param context the GL context for the shader.
   * @param {string} source the source code of the shader.
   */
  /////////////////////////////////////////////////////////////////////////////
  vgl.getCachedShader = function (type, context, source) {
    for (var i = 0; i < m_shaderCache.length; i += 1) {
      if (m_shaderCache[i].type === type &&
          m_shaderCache[i].context === context &&
          m_shaderCache[i].source === source) {
        if (i) {
          m_shaderCache.splice(0, 0, m_shaderCache.splice(i, 1)[0]);
        }
        return m_shaderCache[0].shader;
      }
    }
    var shader = new vgl.shader(type);
    shader.setShaderSource(source);
    m_shaderCache.unshift({
      type: type,
      context: context,
      source: source,
      shader: shader
    });
    if (m_shaderCache.length >= m_shaderCacheMaxSize) {
      m_shaderCache.splice(m_shaderCacheMaxSize,
                           m_shaderCache.length - m_shaderCacheMaxSize);
    }
    return shader;
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Clear the shader cache.
   *
   * @param context the GL context to clear, or null for clear all.
   */
  /////////////////////////////////////////////////////////////////////////////
  vgl.clearCachedShaders = function (context) {
    for (var i = m_shaderCache.length - 1; i >= 0; i -= 1) {
      if (context === null || context === undefined ||
          m_shaderCache[i].context === context) {
        m_shaderCache.splice(i, 1);
      }
    }
  };
})();

//////////////////////////////////////////////////////////////////////////////
/**
 * @module vgl
 */

/*global vgl, inherit, $*/
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instace of class shaderProgram
 *
 * @class
 * @returns {vgl.shaderProgram}
 */
//////////////////////////////////////////////////////////////////////////////

var getBaseUrl = (function () {
  'use strict';
  var baseUrl = '.';
  var scripts = document.getElementsByTagName('script');
  /* When run in certain environments, there may be no scripts loaded.  For
   * instance, jQuery's $.getScript won't add it to a script tag. */
  if (scripts.length > 0) {
    var index = scripts.length - 1;
    var vglScript = scripts[index];
    index = vglScript.src.lastIndexOf('/');
    baseUrl = vglScript.src.substring(0, index);
  }
  return function () { return baseUrl; };
})();

vgl.shaderProgram = function () {
  'use strict';

  if (!(this instanceof vgl.shaderProgram)) {
    return new vgl.shaderProgram();
  }
  vgl.materialAttribute.call(
    this, vgl.materialAttributeType.ShaderProgram);

  /** @private */
  var m_this = this,
      m_programHandle = 0,
      m_compileTimestamp = vgl.timestamp(),
      m_bindTimestamp = vgl.timestamp(),
      m_shaders = [],
      m_uniforms = [],
      m_vertexAttributes = {},
      m_uniformNameToLocation = {},
      m_vertexAttributeNameToLocation = {};

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Create a particular shader type using GLSL shader strings from a file
   */
  /////////////////////////////////////////////////////////////////////////////
  this.loadFromFile = function (type, sourceUrl) {
    var shader, success = false;
    $.ajax({
      url: sourceUrl,
      type: 'GET',
      dataType: 'text',
      async: false,
      success: function (result) {
        //console.log(result);
        shader = vgl.shader(type);
        shader.setShaderSource(result);
        m_this.addShader(shader);
        success = true;
      }
    });
    return success;
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Create a particular shader type using GLSL shader strings from a file
   * relative to VGL load URL.
   */
  /////////////////////////////////////////////////////////////////////////////
  this.loadShader = function (type, file) {
    return this.loadFromFile(type, getBaseUrl() + '/shaders/' + file);
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Query uniform location in the program
   *
   * @param name
   * @returns {*}
   */
  /////////////////////////////////////////////////////////////////////////////
  this.queryUniformLocation = function (renderState, name) {
    return renderState.m_context.getUniformLocation(m_programHandle, name);
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Query attribute location in the program
   *
   * @param name
   * @returns {*}
   */
  /////////////////////////////////////////////////////////////////////////////
  this.queryAttributeLocation = function (renderState, name) {
    return renderState.m_context.getAttribLocation(m_programHandle, name);
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Add a new shader to the program
   *
   * @param shader
   * @returns {boolean}
   */
  /////////////////////////////////////////////////////////////////////////////
  this.addShader = function (shader) {
    if (m_shaders.indexOf(shader) > -1) {
      return false;
    }

    var i;
    for (i = m_shaders.length - 2; i >= 0; i -= 1) {
      if (m_shaders[i].shaderType() === shader.shaderType()) {
        m_shaders.splice(i, 1);
      }
    }

    m_shaders.push(shader);
    m_this.modified();
    return true;
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Add a new uniform to the program
   *
   * @param uniform
   * @returns {boolean}
   */
  /////////////////////////////////////////////////////////////////////////////
  this.addUniform = function (uniform) {
    if (m_uniforms.indexOf(uniform) > -1) {
      return false;
    }

    m_uniforms.push(uniform);
    m_this.modified();
    return true;
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Add a new vertex attribute to the program
   *
   * @param attr
   * @param key
   */
  /////////////////////////////////////////////////////////////////////////////
  this.addVertexAttribute = function (attr, key) {
    m_vertexAttributes[key] = attr;
    m_this.modified();
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Get uniform location
   *
   * This method does not perform any query into the program but relies on
   * the fact that it depends on a call to queryUniformLocation earlier.
   *
   * @param name
   * @returns {number}
   */
  /////////////////////////////////////////////////////////////////////////////
  this.uniformLocation = function (name) {
    return m_uniformNameToLocation[name];
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Get attribute location
   *
   * This method does not perform any query into the program but relies on the
   * fact that it depends on a call to queryUniformLocation earlier.
   *
   * @param name
   * @returns {number}
   */
  /////////////////////////////////////////////////////////////////////////////
  this.attributeLocation = function (name) {
    return m_vertexAttributeNameToLocation[name];
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Get uniform object using name as the key
   *
   * @param name
   * @returns {*}
   */
  /////////////////////////////////////////////////////////////////////////////
  this.uniform = function (name) {
    var i;
    for (i = 0; i < m_uniforms.length; i += 1) {
      if (m_uniforms[i].name() === name) {
        return m_uniforms[i];
      }
    }

    return null;
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Update all uniforms
   *
   * This method should be used directly unless required
   */
  /////////////////////////////////////////////////////////////////////////////
  this.updateUniforms = function (renderState) {
    var i;

    for (i = 0; i < m_uniforms.length; i += 1) {
      m_uniforms[i].callGL(renderState,
        m_uniformNameToLocation[m_uniforms[i].name()]);
    }
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Link shader program
   *
   * @returns {boolean}
   */
  /////////////////////////////////////////////////////////////////////////////
  this.link = function (renderState) {
    renderState.m_context.linkProgram(m_programHandle);

    // If creating the shader program failed, alert
    if (!renderState.m_context.getProgramParameter(m_programHandle,
        vgl.GL.LINK_STATUS)) {
      console.log('[ERROR] Unable to initialize the shader program.');
      return false;
    }

    return true;
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Use the shader program
   */
  /////////////////////////////////////////////////////////////////////////////
  this.use = function (renderState) {
    renderState.m_context.useProgram(m_programHandle);
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Peform any initialization required
   */
  /////////////////////////////////////////////////////////////////////////////
  this._setup = function (renderState) {
    if (m_programHandle === 0) {
      m_programHandle = renderState.m_context.createProgram();
    }
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Peform any clean up required when the program gets deleted
   */
  /////////////////////////////////////////////////////////////////////////////
  this._cleanup = function (renderState) {
    m_this.deleteVertexAndFragment(renderState);
    m_this.deleteProgram(renderState);
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Delete the shader program
   */
  /////////////////////////////////////////////////////////////////////////////
  this.deleteProgram = function (renderState) {
    renderState.m_context.deleteProgram(m_programHandle);
    m_programHandle = 0;
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Delete vertex and fragment shaders
   */
  /////////////////////////////////////////////////////////////////////////////
  this.deleteVertexAndFragment = function (renderState) {
    var i;
    for (i = 0; i < m_shaders.length; i += 1) {
      renderState.m_context.detachShader(m_shaders[i].shaderHandle(renderState));
      renderState.m_context.deleteShader(m_shaders[i].shaderHandle(renderState));
      m_shaders[i].removeContext(renderState);
    }
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Compile and link a shader
   */
  /////////////////////////////////////////////////////////////////////////////
  this.compileAndLink = function (renderState) {
    var i;

    if (m_compileTimestamp.getMTime() >= this.getMTime()) {
      return;
    }

    m_this._setup(renderState);

    // Compile shaders
    for (i = 0; i < m_shaders.length; i += 1) {
      m_shaders[i].compile(renderState);
      m_shaders[i].attachShader(renderState, m_programHandle);
    }

    m_this.bindAttributes(renderState);

    // link program
    if (!m_this.link(renderState)) {
      console.log('[ERROR] Failed to link Program');
      m_this._cleanup(renderState);
    }

    m_compileTimestamp.modified();
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Bind the program with its shaders
   *
   * @param renderState
   * @returns {boolean}
   */
  /////////////////////////////////////////////////////////////////////////////
  this.bind = function (renderState) {
    var i = 0;

    if (m_bindTimestamp.getMTime() < m_this.getMTime()) {

      // Compile shaders
      m_this.compileAndLink(renderState);

      m_this.use(renderState);
      m_this.bindUniforms(renderState);
      m_bindTimestamp.modified();
    } else {
      m_this.use(renderState);
    }

    // Call update callback.
    for (i = 0; i < m_uniforms.length; i += 1) {
      m_uniforms[i].update(renderState, m_this);
    }

    // Now update values to GL.
    m_this.updateUniforms(renderState);
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Undo binding of the shader program
   *
   * @param renderState
   */
  /////////////////////////////////////////////////////////////////////////////
  this.undoBind = function (renderState) {
    // REF https://www.khronos.org/opengles/sdk/docs/man/xhtml/glUseProgram.xml
    // If program is 0, then the current rendering state refers to an invalid
    // program object, and the results of vertex and fragment shader execution
    // due to any glDrawArrays or glDrawElements commands are undefined
    renderState.m_context.useProgram(null);
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Bind vertex data
   *
   * @param renderState
   * @param key
   */
  /////////////////////////////////////////////////////////////////////////////
  this.bindVertexData = function (renderState, key) {
    if (m_vertexAttributes.hasOwnProperty(key)) {
      m_vertexAttributes[key].bindVertexData(renderState, key);
    }
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Undo bind vetex data
   *
   * @param renderState
   * @param key
   */
  /////////////////////////////////////////////////////////////////////////////
  this.undoBindVertexData = function (renderState, key) {
    if (m_vertexAttributes.hasOwnProperty(key)) {
      m_vertexAttributes[key].undoBindVertexData(renderState, key);
    }
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Bind uniforms
   */
  /////////////////////////////////////////////////////////////////////////////
  this.bindUniforms = function (renderState) {
    var i;
    for (i = 0; i < m_uniforms.length; i += 1) {
      m_uniformNameToLocation[m_uniforms[i].name()] = this
          .queryUniformLocation(renderState, m_uniforms[i].name());
    }
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Bind vertex attributes
   */
  /////////////////////////////////////////////////////////////////////////////
  this.bindAttributes = function (renderState) {
    var key, name;
    for (key in m_vertexAttributes) {
      if (m_vertexAttributes.hasOwnProperty(key)) {
        name = m_vertexAttributes[key].name();
        renderState.m_context.bindAttribLocation(m_programHandle, key, name);
        m_vertexAttributeNameToLocation[name] = key;
      }
    }
  };

  return m_this;
};

inherit(vgl.shaderProgram, vgl.materialAttribute);

//////////////////////////////////////////////////////////////////////////////
/**
 * @module vgl
 */

/*global Uint8Array, vgl, inherit*/
//////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class texture
 *
 * @class
 * @returns {vgl.texture}
 */
///////////////////////////////////////////////////////////////////////////////
vgl.texture = function () {
  'use strict';

  if (!(this instanceof vgl.texture)) {
    return new vgl.texture();
  }
  vgl.materialAttribute.call(
    this, vgl.materialAttributeType.Texture);

  this.m_width = 0;
  this.m_height = 0;
  this.m_depth = 0;

  this.m_textureHandle = null;
  this.m_textureUnit = 0;

  this.m_pixelFormat = vgl.GL.RGBA;
  this.m_pixelDataType = vgl.GL.UNSIGNED_BYTE;
  this.m_internalFormat = vgl.GL.RGBA;
  this.m_nearestPixel = false;

  this.m_image = null;

  var m_setupTimestamp = vgl.timestamp(),
      m_that = this;

  function activateTextureUnit(renderState) {
    switch (m_that.m_textureUnit) {
      case 0:
        renderState.m_context.activeTexture(vgl.GL.TEXTURE0);
        break;
      case 1:
        renderState.m_context.activeTexture(vgl.GL.TEXTURE1);
        break;
      case 2:
        renderState.m_context.activeTexture(vgl.GL.TEXTURE2);
        break;
      case 3:
        renderState.m_context.activeTexture(vgl.GL.TEXTURE3);
        break;
      case 4:
        renderState.m_context.activeTexture(vgl.GL.TEXTURE4);
        break;
      case 5:
        renderState.m_context.activeTexture(vgl.GL.TEXTURE5);
        break;
      case 6:
        renderState.m_context.activeTexture(vgl.GL.TEXTURE6);
        break;
      case 7:
        renderState.m_context.activeTexture(vgl.GL.TEXTURE7);
        break;
      case 8:
        renderState.m_context.activeTexture(vgl.GL.TEXTURE8);
        break;
      case 9:
        renderState.m_context.activeTexture(vgl.GL.TEXTURE9);
        break;
      case 10:
        renderState.m_context.activeTexture(vgl.GL.TEXTURE10);
        break;
      case 11:
        renderState.m_context.activeTexture(vgl.GL.TEXTURE11);
        break;
      case 12:
        renderState.m_context.activeTexture(vgl.GL.TEXTURE12);
        break;
      case 13:
        renderState.m_context.activeTexture(vgl.GL.TEXTURE13);
        break;
      case 14:
        renderState.m_context.activeTexture(vgl.GL.TEXTURE14);
        break;
      case 15:
        renderState.m_context.activeTexture(vgl.GL.TEXTURE15);
        break;
      default:
        throw '[error] Texture unit ' + m_that.m_textureUnit +
              ' is not supported';
    }
  }

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Create texture, update parameters, and bind data
   *
   * @param renderState
   */
  /////////////////////////////////////////////////////////////////////////////
  this.setup = function (renderState) {
    // Activate the texture unit first
    activateTextureUnit(renderState);

    renderState.m_context.deleteTexture(this.m_textureHandle);
    this.m_textureHandle = renderState.m_context.createTexture();
    renderState.m_context.bindTexture(vgl.GL.TEXTURE_2D, this.m_textureHandle);
    renderState.m_context.texParameteri(vgl.GL.TEXTURE_2D,
        vgl.GL.TEXTURE_MIN_FILTER,
        this.m_nearestPixel ? vgl.GL.NEAREST : vgl.GL.LINEAR);
    renderState.m_context.texParameteri(vgl.GL.TEXTURE_2D,
        vgl.GL.TEXTURE_MAG_FILTER,
        this.m_nearestPixel ? vgl.GL.NEAREST : vgl.GL.LINEAR);
    renderState.m_context.texParameteri(vgl.GL.TEXTURE_2D,
        vgl.GL.TEXTURE_WRAP_S, vgl.GL.CLAMP_TO_EDGE);
    renderState.m_context.texParameteri(vgl.GL.TEXTURE_2D,
        vgl.GL.TEXTURE_WRAP_T, vgl.GL.CLAMP_TO_EDGE);

    if (this.m_image !== null) {
      renderState.m_context.pixelStorei(vgl.GL.UNPACK_ALIGNMENT, 1);
      renderState.m_context.pixelStorei(vgl.GL.UNPACK_FLIP_Y_WEBGL, true);

      this.updateDimensions();
      this.computeInternalFormatUsingImage();

      // console.log('m_internalFormat ' + this.m_internalFormat);
      // console.log('m_pixelFormat ' + this.m_pixelFormat);
      // console.log('m_pixelDataType ' + this.m_pixelDataType);

      // FOR now support only 2D textures
      renderState.m_context.texImage2D(vgl.GL.TEXTURE_2D, 0, this.m_internalFormat,
        this.m_pixelFormat, this.m_pixelDataType, this.m_image);
    } else {
      renderState.m_context.texImage2D(vgl.GL.TEXTURE_2D, 0, this.m_internalFormat,
        this.m_width, this.m_height, 0, this.m_pixelFormat, this.m_pixelDataType, null);
    }

    renderState.m_context.bindTexture(vgl.GL.TEXTURE_2D, null);
    m_setupTimestamp.modified();
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Create texture and if already created use it
   *
   * @param renderState
   */
  /////////////////////////////////////////////////////////////////////////////
  this.bind = function (renderState) {
    // TODO Call setup via material setup
    if (this.getMTime() > m_setupTimestamp.getMTime()) {
      this.setup(renderState);
    }

    activateTextureUnit(renderState);
    renderState.m_context.bindTexture(vgl.GL.TEXTURE_2D, this.m_textureHandle);
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Turn off the use of this texture
   *
   * @param renderState
   */
  /////////////////////////////////////////////////////////////////////////////
  this.undoBind = function (renderState) {
    renderState.m_context.bindTexture(vgl.GL.TEXTURE_2D, null);
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Get image used by the texture
   *
   * @returns {vgl.image}
   */
  /////////////////////////////////////////////////////////////////////////////
  this.image = function () {
    return this.m_image;
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Set image for the texture
   *
   * @param {vgl.image} image
   * @returns {boolean}
   */
  /////////////////////////////////////////////////////////////////////////////
  this.setImage = function (image) {
    if (image !== null) {
      this.m_image = image;
      this.updateDimensions();
      this.modified();
      return true;
    }

    return false;
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Get nearest pixel flag for the texture
   *
   * @returns boolean
   */
  /////////////////////////////////////////////////////////////////////////////
  this.nearestPixel = function () {
    return this.m_nearestPixel;
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Set nearest pixel flag for the texture
   *
   * @param {boolean} nearest pixel flag
   * @returns {boolean}
   */
  /////////////////////////////////////////////////////////////////////////////
  this.setNearestPixel = function (nearest) {
    nearest = nearest ? true : false;
    if (nearest !== this.m_nearestPixel) {
      this.m_nearestPixel = nearest;
      this.modified();
      return true;
    }
    return false;
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Get texture unit of the texture
   *
   * @returns {number}
   */
  /////////////////////////////////////////////////////////////////////////////
  this.textureUnit = function () {
    return this.m_textureUnit;
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Set texture unit of the texture. Default is 0.
   *
   * @param {number} unit
   * @returns {boolean}
   */
  /////////////////////////////////////////////////////////////////////////////
  this.setTextureUnit = function (unit) {
    if (this.m_textureUnit === unit) {
      return false;
    }

    this.m_textureUnit = unit;
    this.modified();
    return true;
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Get width of the texture
   *
   * @returns {*}
   */
  /////////////////////////////////////////////////////////////////////////////
  this.width = function () {
    return this.m_width;
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Set width of the texture
   *
   * @param {number} width
   * @returns {boolean}
   */
  /////////////////////////////////////////////////////////////////////////////
  this.setWidth = function (width) {
    if (m_that.m_width !== width) {
      m_that.m_width = width;
      m_that.modified();
      return true;
    }

    return false;
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Get width of the texture
   *
   * @returns {*}
   */
  /////////////////////////////////////////////////////////////////////////////
  this.height = function () {
    return m_that.m_height;
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Set height of the texture
   *
   * @param {number} height
   * @returns {vgl.texture}
   */
  /////////////////////////////////////////////////////////////////////////////
  this.setHeight = function (height) {
    if (m_that.m_height !== height) {
      m_that.m_height = height;
      m_that.modified();
      return true;
    }

    return false;
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Get depth of the texture
   *
   * @returns {number}
   */
  /////////////////////////////////////////////////////////////////////////////
  this.depth = function () {
    return this.m_depth;
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Set depth of the texture
   *
   * @param {number} depth
   * @returns {boolean}
   */
  /////////////////////////////////////////////////////////////////////////////
  this.setDepth = function (depth) {
    if (this.m_image === null) {
      return false;
    }

    this.m_depth = depth;
    this.modified();
    return true;
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Get the texture handle (id) of the texture
   *
   * @returns {*}
   */
  /////////////////////////////////////////////////////////////////////////////
  this.textureHandle = function () {
    return this.m_textureHandle;
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Get internal format of the texture
   *
   * @returns {*}
   */
  /////////////////////////////////////////////////////////////////////////////
  this.internalFormat = function () {
    return this.m_internalFormat;
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Set internal format of the texture
   *
   * @param internalFormat
   * @returns {boolean}
   */
  /////////////////////////////////////////////////////////////////////////////
  this.setInternalFormat = function (internalFormat) {
    if (this.m_internalFormat !== internalFormat) {
      this.m_internalFormat = internalFormat;
      this.modified();
      return true;
    }

    return false;
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Get pixel format of the texture
   *
   * @returns {*}
   */
  /////////////////////////////////////////////////////////////////////////////
  this.pixelFormat = function () {
    return this.m_pixelFormat;
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Set pixel format of the texture
   *
   * @param pixelFormat
   * @returns {boolean}
   */
  /////////////////////////////////////////////////////////////////////////////
  this.setPixelFormat = function (pixelFormat) {
    if (this.m_image === null) {
      return false;
    }

    this.m_pixelFormat = pixelFormat;
    this.modified();
    return true;
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Get pixel data type
   *
   * @returns {*}
   */
  /////////////////////////////////////////////////////////////////////////////
  this.pixelDataType = function () {
    return this.m_pixelDataType;
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Set pixel data type
   *
   * @param pixelDataType
   * @returns {boolean}
   */
  /////////////////////////////////////////////////////////////////////////////
  this.setPixelDataType = function (pixelDataType) {
    if (this.m_image === null) {
      return false;
    }

    this.m_pixelDataType = pixelDataType;

    this.modified();

    return true;
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Compute internal format of the texture
   */
  /////////////////////////////////////////////////////////////////////////////
  this.computeInternalFormatUsingImage = function () {
    // Currently image does not define internal format
    // and hence it's pixel format is the only way to query
    // information on how color has been stored.
    // switch (this.m_image.pixelFormat()) {
    // case vgl.GL.RGB:
    // this.m_internalFormat = vgl.GL.RGB;
    // break;
    // case vgl.GL.RGBA:
    // this.m_internalFormat = vgl.GL.RGBA;
    // break;
    // case vgl.GL.Luminance:
    // this.m_internalFormat = vgl.GL.Luminance;
    // break;
    // case vgl.GL.LuminanceAlpha:
    // this.m_internalFormat = vgl.GL.LuminanceAlpha;
    // break;
    // // Do nothing when image pixel format is none or undefined.
    // default:
    // break;
    // };

    // TODO Fix this
    this.m_internalFormat = vgl.GL.RGBA;
    this.m_pixelFormat = vgl.GL.RGBA;
    this.m_pixelDataType = vgl.GL.UNSIGNED_BYTE;
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Update texture dimensions
   */
  /////////////////////////////////////////////////////////////////////////////
  this.updateDimensions = function () {
    if (this.m_image !== null) {
      this.m_width = this.m_image.width;
      this.m_height = this.m_image.height;
      this.m_depth = 0; // Only 2D images are supported now
    }
  };

  return this;
};

inherit(vgl.texture, vgl.materialAttribute);

///////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class lookupTable
 *
 * @class
 * @returns {vgl.lookupTable}
 */
///////////////////////////////////////////////////////////////////////////////
vgl.lookupTable = function () {
  'use strict';

  if (!(this instanceof vgl.lookupTable)) {
    return new vgl.lookupTable();
  }
  vgl.texture.call(this);

  var m_setupTimestamp = vgl.timestamp(),
      m_range = [0, 0];

  this.m_colorTable = //paraview bwr colortable
    [0.07514311, 0.468049805, 1, 1,
     0.247872569, 0.498782363, 1, 1,
     0.339526309, 0.528909511, 1, 1,
     0.409505078, 0.558608486, 1, 1,
     0.468487184, 0.588057293, 1, 1,
     0.520796675, 0.617435078, 1, 1,
     0.568724526, 0.646924167, 1, 1,
     0.613686735, 0.676713218, 1, 1,
     0.656658579, 0.707001303, 1, 1,
     0.698372844, 0.738002964, 1, 1,
     0.739424025, 0.769954435, 1, 1,
     0.780330104, 0.803121429, 1, 1,
     0.821573924, 0.837809045, 1, 1,
     0.863634967, 0.874374691, 1, 1,
     0.907017747, 0.913245283, 1, 1,
     0.936129275, 0.938743558, 0.983038586, 1,
     0.943467973, 0.943498599, 0.943398095, 1,
     0.990146732, 0.928791426, 0.917447482, 1,
     1, 0.88332677, 0.861943246, 1,
     1, 0.833985467, 0.803839606, 1,
     1, 0.788626485, 0.750707739, 1,
     1, 0.746206642, 0.701389973, 1,
     1, 0.70590052, 0.654994046, 1,
     1, 0.667019783, 0.610806959, 1,
     1, 0.6289553, 0.568237474, 1,
     1, 0.591130233, 0.526775617, 1,
     1, 0.552955184, 0.485962266, 1,
     1, 0.513776083, 0.445364274, 1,
     1, 0.472800903, 0.404551679, 1,
     1, 0.428977855, 0.363073592, 1,
     1, 0.380759558, 0.320428137, 1,
     0.961891484, 0.313155629, 0.265499262, 1,
     0.916482116, 0.236630659, 0.209939162, 1].map(
             function (x) { return x * 255; });

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Create lookup table, initialize parameters, and bind data to it
   *
   * @param {vgl.renderState} renderState
   */
  /////////////////////////////////////////////////////////////////////////////
  this.setup = function (renderState) {
    if (this.textureUnit() === 0) {
      renderState.m_context.activeTexture(vgl.GL.TEXTURE0);
    } else if (this.textureUnit() === 1) {
      renderState.m_context.activeTexture(vgl.GL.TEXTURE1);
    }

    renderState.m_context.deleteTexture(this.m_textureHandle);
    this.m_textureHandle = renderState.m_context.createTexture();
    renderState.m_context.bindTexture(vgl.GL.TEXTURE_2D, this.m_textureHandle);
    renderState.m_context.texParameteri(vgl.GL.TEXTURE_2D,
        vgl.GL.TEXTURE_MIN_FILTER, vgl.GL.LINEAR);
    renderState.m_context.texParameteri(vgl.GL.TEXTURE_2D,
        vgl.GL.TEXTURE_MAG_FILTER, vgl.GL.LINEAR);
    renderState.m_context.texParameteri(vgl.GL.TEXTURE_2D,
        vgl.GL.TEXTURE_WRAP_S, vgl.GL.CLAMP_TO_EDGE);
    renderState.m_context.texParameteri(vgl.GL.TEXTURE_2D,
        vgl.GL.TEXTURE_WRAP_T, vgl.GL.CLAMP_TO_EDGE);
    renderState.m_context.pixelStorei(vgl.GL.UNPACK_ALIGNMENT, 1);

    this.m_width = this.m_colorTable.length / 4;
    this.m_height = 1;
    this.m_depth = 0;
    renderState.m_context.texImage2D(vgl.GL.TEXTURE_2D,
        0, vgl.GL.RGBA, this.m_width, this.m_height, this.m_depth,
        vgl.GL.RGBA, vgl.GL.UNSIGNED_BYTE, new Uint8Array(this.m_colorTable));

    renderState.m_context.bindTexture(vgl.GL.TEXTURE_2D, null);
    m_setupTimestamp.modified();
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Get color table used by the lookup table
   *
   * @returns {*}
   */
  /////////////////////////////////////////////////////////////////////////////
  this.colorTable = function () {
    return this.m_colorTable;
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Set color table used by the lookup table
   *
   * @param colors
   * @returns {boolean}
   */
  /////////////////////////////////////////////////////////////////////////////
  this.setColorTable = function (colors) {
    if (this.m_colorTable === colors) {
      return false;
    }

    this.m_colorTable = colors;
    this.modified();
    return true;
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Get scalar range
   *
   * @returns {Array}
   */
  /////////////////////////////////////////////////////////////////////////////
  this.range = function () {
    return m_range;
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Set scalar range for the lookup table
   *
   * @param range
   * @returns {boolean}
   */
  /////////////////////////////////////////////////////////////////////////////
  this.setRange = function (range) {
    if (m_range === range) {
      return false;
    }
    m_range = range;
    this.modified();
    return true;
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Given a [min,max] range update the lookup table range
   *
   * @param range
   */
  /////////////////////////////////////////////////////////////////////////////
  this.updateRange = function (range) {
    if (!(range instanceof Array)) {
      console.log('[error] Invalid data type for range. Requires array [min,max]');
    }

    if (range[0] < m_range[0]) {
      m_range[0] = range[0];
      this.modified();
    }

    if (range[1] > m_range[1]) {
      m_range[1] = range[1];
      this.modified();
    }
  };

  return this;
};

inherit(vgl.lookupTable, vgl.texture);

//////////////////////////////////////////////////////////////////////////////
/**
 * @module vgl
 */

/*global vgl, mat4, inherit*/
//////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class uniform
 *
 * @param type
 * @param name
 * @returns {vgl.uniform} OpenGL uniform encapsulation
 */
///////////////////////////////////////////////////////////////////////////////
vgl.uniform = function (type, name) {
  'use strict';

  if (!(this instanceof vgl.uniform)) {
    return new vgl.uniform(type, name);
  }

  this.getTypeNumberOfComponents = function (type) {
    switch (type) {
      case vgl.GL.FLOAT:
      case vgl.GL.INT:
      case vgl.GL.BOOL:
        return 1;

      case vgl.GL.FLOAT_VEC2:
      case vgl.GL.INT_VEC2:
      case vgl.GL.BOOL_VEC2:
        return 2;

      case vgl.GL.FLOAT_VEC3:
      case vgl.GL.INT_VEC3:
      case vgl.GL.BOOL_VEC3:
        return 3;

      case vgl.GL.FLOAT_VEC4:
      case vgl.GL.INT_VEC4:
      case vgl.GL.BOOL_VEC4:
        return 4;

      case vgl.GL.FLOAT_MAT3:
        return 9;

      case vgl.GL.FLOAT_MAT4:
        return 16;

      default:
        return 0;
    }
  };

  var m_type = type,
      m_name = name,
      m_dataArray = [];

  m_dataArray.length = this.getTypeNumberOfComponents(m_type);

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Get name of the uniform
   *
   * @returns {*}
   */
  /////////////////////////////////////////////////////////////////////////////
  this.name = function () {
    return m_name;
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Get type of the uniform
   *
   * @returns {*}
   */
  /////////////////////////////////////////////////////////////////////////////
  this.type = function () {
    return m_type;
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Get value of the uniform
   *
   * @returns {Array}
   */
  /////////////////////////////////////////////////////////////////////////////
  this.get = function () {
    return m_dataArray;
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Set value of the uniform
   *
   * @param value
   */
  /////////////////////////////////////////////////////////////////////////////
  this.set = function (value) {
    var i = 0, lendata = m_dataArray.length;
    if (lendata !== 1) {
      for (i = 0; i < lendata; i += 1) {
        m_dataArray[i] = value[i];
      }
    } else {
      m_dataArray[0] = value;
    }
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Call GL and pass updated values to the current shader
   *
   * @param location
   */
  /////////////////////////////////////////////////////////////////////////////
  this.callGL = function (renderState, location) {
    switch (m_type) {
      case vgl.GL.BOOL:
      case vgl.GL.INT:
        renderState.m_context.uniform1iv(location, m_dataArray);
        break;
      case vgl.GL.FLOAT:
        renderState.m_context.uniform1fv(location, m_dataArray);
        break;
      case vgl.GL.BOOL_VEC2:
      case vgl.GL.INT_VEC2:
        renderState.m_context.uniform2iv(location, m_dataArray);
        break;
      case vgl.GL.FLOAT_VEC2:
        renderState.m_context.uniform2fv(location, m_dataArray);
        break;
      case vgl.GL.BOOL_VEC3:
      case vgl.GL.INT_VEC3:
        renderState.m_context.uniform3iv(location, m_dataArray);
        break;
      case vgl.GL.FLOAT_VEC3:
        renderState.m_context.uniform3fv(location, m_dataArray);
        break;
      case vgl.GL.BOOL_VEC4:
      case vgl.GL.INT_VEC4:
        renderState.m_context.uniform4iv(location, m_dataArray);
        break;
      case vgl.GL.FLOAT_VEC4:
        renderState.m_context.uniform4fv(location, m_dataArray);
        break;
      case vgl.GL.FLOAT_MAT3:
        renderState.m_context.uniformMatrix3fv(location, vgl.GL.FALSE, m_dataArray);
        break;
      case vgl.GL.FLOAT_MAT4:
        renderState.m_context.uniformMatrix4fv(location, vgl.GL.FALSE, m_dataArray);
        break;
      default:
        break;
    }
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Virtual method to update the uniform
   *
   * Should be implemented by the derived class.
   *
   * @param renderState
   * @param program
   */
  /////////////////////////////////////////////////////////////////////////////
  this.update = function (renderState, program) {
    void renderState; /* unused parameter */
    void program; /* unused parameter */
    // Should be implemented by the derived class
  };

  return this;
};

///////////////////////////////////////////////////////////////////////////////
/**
 * Create new instance of class modelViewUniform
 *
 * @param name
 * @returns {vgl.modelViewUniform}
 */
///////////////////////////////////////////////////////////////////////////////
vgl.modelViewUniform = function (name) {
  'use strict';

  if (!(this instanceof vgl.modelViewUniform)) {
    return new vgl.modelViewUniform(name);
  }

  if (!name) {
    name = 'modelViewMatrix';
  }

  vgl.uniform.call(this, vgl.GL.FLOAT_MAT4, name);

  this.set(mat4.create());

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Update the uniform given a render state and shader program
   *
   * @param {vgl.renderState} renderState
   * @param {vgl.shaderProgram} program
   */
  /////////////////////////////////////////////////////////////////////////////
  this.update = function (renderState, program) {
    void program; /* unused parameter */
    this.set(renderState.m_modelViewMatrix);
  };

  return this;
};

inherit(vgl.modelViewUniform, vgl.uniform);

///////////////////////////////////////////////////////////////////////////////
/**
 * Create new instance of class modelViewOriginUniform.
 *
 * @param name
 * @param {array} origin a triplet of floats.
 * @returns {vgl.modelViewUniform}
 */
///////////////////////////////////////////////////////////////////////////////
vgl.modelViewOriginUniform = function (name, origin) {
  'use strict';

  if (!(this instanceof vgl.modelViewOriginUniform)) {
    return new vgl.modelViewOriginUniform(name, origin);
  }

  if (!name) {
    name = 'modelViewMatrix';
  }
  origin = origin || [0, 0, 0];

  var m_origin = [origin[0], origin[1], origin[2] || 0];

  vgl.uniform.call(this, vgl.GL.FLOAT_MAT4, name);

  this.set(mat4.create());

  /**
   * Change the origin used by the uniform view matrix.
   *
   * @param {array} origin a triplet of floats.
   */
  this.setOrigin = function (origin) {
    origin = origin || [0, 0, 0];
    m_origin = [origin[0], origin[1], origin[2] || 0];
  };

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Update the uniform given a render state and shader program.  This offsets
   * the modelViewMatrix by the origin, and, if the model view should be
   * aligned, aligns it appropriately.  The alignment must be done after the
   * origin offset to maintain precision.
   *
   * @param {vgl.renderState} renderState
   * @param {vgl.shaderProgram} program
   */
  /////////////////////////////////////////////////////////////////////////////
  this.update = function (renderState, program) {
    void program; /* unused parameter */
    var view = renderState.m_modelViewMatrix;
    if (renderState.m_modelViewAlignment) {
      /* adjust alignment before origin.  Otherwise, a changing origin can
       * affect the rounding choice and result in a 1 pixe jitter. */
      var align = renderState.m_modelViewAlignment;
      /* Don't modify the orignal matrix.  If we are in an environment where
       * you can't slice an Float32Array, switch to a regular array */
      view = view.slice ? view.slice() : Array.prototype.slice.call(view);
      /* view[12] and view[13] are the x and y offsets.  align.round is the
       * units-per-pixel, and align.dx and .dy are either 0 or half the size of
       * a unit-per-pixel.  The alignment guarantees that the texels are
       * aligned with screen pixels. */
      view[12] = Math.round(view[12] / align.roundx) * align.roundx + align.dx;
      view[13] = Math.round(view[13] / align.roundy) * align.roundy + align.dy;
    }
    view = mat4.translate(mat4.create(), view, m_origin);
    this.set(view);
  };

  return this;
};

inherit(vgl.modelViewOriginUniform, vgl.uniform);

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class projectionUniform
 *
 * @param name
 * @returns {vgl.projectionUniform}
 */
///////////////////////////////////////////////////////////////////////////////
vgl.projectionUniform = function (name) {
  'use strict';

  if (!(this instanceof vgl.projectionUniform)) {
    return new vgl.projectionUniform(name);
  }

  if (!name) {
    name = 'projectionMatrix';
  }

  vgl.uniform.call(this, vgl.GL.FLOAT_MAT4, name);

  this.set(mat4.create());

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Update the uniform given a render state and shader program
   *
   * @param renderState
   * @param program
   */
  /////////////////////////////////////////////////////////////////////////////
  this.update = function (renderState, program) {
    void program; /* unused parameter */
    this.set(renderState.m_projectionMatrix);
  };

  return this;
};

inherit(vgl.projectionUniform, vgl.uniform);

///////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class floatUniform
 *
 * @param name
 * @param value
 * @returns {vgl.floatUniform}
 */
///////////////////////////////////////////////////////////////////////////////
vgl.floatUniform = function (name, value) {
  'use strict';

  if (!(this instanceof vgl.floatUniform)) {
    return new vgl.floatUniform(name, value);
  }

  if (!name) {
    name = 'floatUniform';
  }

  value = value === undefined ? 1.0 : value;

  vgl.uniform.call(this, vgl.GL.FLOAT, name);

  this.set(value);
};

inherit(vgl.floatUniform, vgl.uniform);

///////////////////////////////////////////////////////////////////////////////
/**
 * Create new instance of class normalMatrixUniform
 *
 * @param name
 * @returns {vgl.normalMatrixUniform}
 */
///////////////////////////////////////////////////////////////////////////////
vgl.normalMatrixUniform = function (name) {
  'use strict';

  if (!(this instanceof vgl.normalMatrixUniform)) {
    return new vgl.normalMatrixUniform(name);
  }

  if (!name) {
    name = 'normalMatrix';
  }

  vgl.uniform.call(this, vgl.GL.FLOAT_MAT4, name);

  this.set(mat4.create());

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Update the uniform given a render state and shader program
   *
   * @param {vgl.renderState} renderState
   * @param {vgl.shaderProgram} program
   */
  /////////////////////////////////////////////////////////////////////////////
  this.update = function (renderState, program) {
    void program; /* unused parameter */
    this.set(renderState.m_normalMatrix);
  };

  return this;
};

inherit(vgl.normalMatrixUniform, vgl.uniform);

//////////////////////////////////////////////////////////////////////////////
/**
 * @module vgl
 */

/*global vgl*/
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
/**
 * Keys to identify vertex attributes
 *
 * @type {{Position: number, Normal: number, TextureCoordinate: number,
 *         Color: number, Scalar: number, Scalar2: number, Scalar3: number,
 *         Scalar4: number, Scalar5: number, Scalar6: number, Scalar7: number,
 *         CountAttributeIndex: number}}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.vertexAttributeKeys = {
  'Position' : 0,
  'Normal' : 1,
  'TextureCoordinate' : 2,
  'Color' : 3,
  'Scalar': 4,
  'CountAttributeIndex' : 5
};

vgl.vertexAttributeKeysIndexed = {
  'Zero' : 0,
  'One' : 1,
  'Two' : 2,
  'Three' : 3,
  'Four' : 4,
  'Five' : 5,
  'Six' : 6,
  'Seven' : 7,
  'Eight' : 8,
  'Nine' : 9
};

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of vertexAttribute
 *
 * @param {string} name
 * @returns {vgl.vertexAttribute}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.vertexAttribute = function (name) {
  'use strict';

  if (!(this instanceof vgl.vertexAttribute)) {
    return new vgl.vertexAttribute(name);
  }

  var m_name = name;

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get name of the vertex attribute
   *
   * @returns {string}
   */
  //////////////////////////////////////////////////////////////////////////////
  this.name = function () {
    return m_name;
  };

  //////////////////////////////////////////////////////////////////////////////
  /**
   * Bind vertex data to the given render state
   *
   * @param {vgl.renderState} renderState
   * @param {vgl.vertexAttributeKeys} key
   */
  //////////////////////////////////////////////////////////////////////////////
  this.bindVertexData = function (renderState, key) {
    var geometryData = renderState.m_mapper.geometryData(),
        sourceData = geometryData.sourceData(key),
        program = renderState.m_material.shaderProgram();

    renderState.m_context.vertexAttribPointer(program.attributeLocation(
        m_name), sourceData
        .attributeNumberOfComponents(key), sourceData.attributeDataType(key),
                           sourceData.normalized(key), sourceData
                               .attributeStride(key), sourceData
                               .attributeOffset(key));

    renderState.m_context.enableVertexAttribArray(program.attributeLocation(m_name));
  };

  //////////////////////////////////////////////////////////////////////////////
  /**
   * Undo bind vertex data for a given render state
   *
   * @param {vgl.renderState} renderState
   * @param {vgl.vertexAttributeKeys} key
   */
  //////////////////////////////////////////////////////////////////////////////
  this.undoBindVertexData = function (renderState, key) {
    key = key; /* unused parameter */

    var program = renderState.m_material.shaderProgram();

    renderState.m_context.disableVertexAttribArray(program.attributeLocation(m_name));
  };
};

//////////////////////////////////////////////////////////////////////////////
/**
 * @module vgl
 */

/*global vgl, inherit*/
//////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class source
 *
 * @returns {vgl.source}
 */
///////////////////////////////////////////////////////////////////////////////
vgl.source = function () {
  'use strict';

  if (!(this instanceof vgl.source)) {
    return new vgl.source();
  }

  vgl.object.call(this);

  /////////////////////////////////////////////////////////////////////////////
  /**
   * Virtual function to create a source instance
   */
  /////////////////////////////////////////////////////////////////////////////
  this.create = function () {
  };

  return this;
};

inherit(vgl.source, vgl.object);

//////////////////////////////////////////////////////////////////////////////
/**
 * @module vgl
 */

/*global vgl, inherit*/
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class planeSource
 *
 * @class
 * @returns {vgl.planeSource}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.planeSource = function () {
  'use strict';

  if (!(this instanceof vgl.planeSource)) {
    return new vgl.planeSource();
  }
  vgl.source.call(this);

  var m_origin = [0.0, 0.0, 0.0],
      m_point1 = [1.0, 0.0, 0.0],
      m_point2 = [0.0, 1.0, 0.0],
      m_normal = [0.0, 0.0, 1.0],
      m_xresolution = 1,
      m_yresolution = 1,
      m_geom = null;

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set origin of the plane
   *
   * @param x
   * @param y
   * @param z
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setOrigin = function (x, y, z) {
    m_origin[0] = x;
    m_origin[1] = y;
    m_origin[2] = z;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set point that defines the first axis of the plane
   *
   * @param x
   * @param y
   * @param z
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setPoint1 = function (x, y, z) {
    m_point1[0] = x;
    m_point1[1] = y;
    m_point1[2] = z;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set point that defines the first axis of the plane
   *
   * @param x
   * @param y
   * @param z
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setPoint2 = function (x, y, z) {
    m_point2[0] = x;
    m_point2[1] = y;
    m_point2[2] = z;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Create a plane geometry given input parameters
   *
   * @returns {null}
   */
  ////////////////////////////////////////////////////////////////////////////
  this.create = function () {
    m_geom = new vgl.geometryData();

    var x = [], tc = [], v1 = [], v2 = [],
        pts = [], i, j, ii, numPts,
        posIndex = 0, normIndex = 0, colorIndex = 0, texCoordIndex = 0,
        positions = [], normals = [], colors = [],
        texCoords = [], indices = [], tristrip = null,
        sourcePositions = null, sourceColors = null, sourceTexCoords;

    x.length = 3;
    tc.length = 2;
    v1.length = 3;
    v2.length = 3;
    pts.length = 3;

    // Check input
    for (i = 0; i < 3; i += 1) {
      v1[i] = m_point1[i] - m_origin[i];
      v2[i] = m_point2[i] - m_origin[i];
    }

    // TODO Compute center and normal
    // Set things up; allocate memory
    numPts = (m_xresolution + 1) * (m_yresolution + 1);
    positions.length = 3 * numPts;
    normals.length = 3 * numPts;
    texCoords.length = 2 * numPts;
    indices.length = numPts;

    for (i = 0; i < (m_yresolution + 1); i += 1) {
      tc[1] = i / m_yresolution;

      for (j = 0; j < (m_xresolution + 1); j += 1) {
        tc[0] = j / m_xresolution;

        for (ii = 0; ii < 3; ii += 1) {
          x[ii] = m_origin[ii] + tc[0] * v1[ii] + tc[1] * v2[ii];
        }

        //jshint plusplus: false
        positions[posIndex++] = x[0];
        positions[posIndex++] = x[1];
        positions[posIndex++] = x[2];

        colors[colorIndex++] = 1.0;
        colors[colorIndex++] = 1.0;
        colors[colorIndex++] = 1.0;

        normals[normIndex++] = m_normal[0];
        normals[normIndex++] = m_normal[1];
        normals[normIndex++] = m_normal[2];

        texCoords[texCoordIndex++] = tc[0];
        texCoords[texCoordIndex++] = tc[1];
        //jshint plusplus: true
      }
    }

    /// Generate polygon connectivity
    for (i = 0; i < m_yresolution; i += 1) {
      for (j = 0; j < m_xresolution; j += 1) {
        pts[0] = j + i * (m_xresolution + 1);
        pts[1] = pts[0] + 1;
        pts[2] = pts[0] + m_xresolution + 2;
        pts[3] = pts[0] + m_xresolution + 1;
      }
    }

    for (i = 0; i < numPts; i += 1) {
      indices[i] = i;
    }

    tristrip = new vgl.triangleStrip();
    tristrip.setIndices(indices);

    sourcePositions = vgl.sourceDataP3fv();
    sourcePositions.pushBack(positions);

    sourceColors = vgl.sourceDataC3fv();
    sourceColors.pushBack(colors);

    sourceTexCoords = vgl.sourceDataT2fv();
    sourceTexCoords.pushBack(texCoords);

    m_geom.addSource(sourcePositions);
    m_geom.addSource(sourceColors);
    m_geom.addSource(sourceTexCoords);
    m_geom.addPrimitive(tristrip);

    return m_geom;
  };
};

inherit(vgl.planeSource, vgl.source);

//////////////////////////////////////////////////////////////////////////////
/**
 * @module vgl
 */

/*global vgl, inherit*/
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class pointSource
 *
 * @class
 * @returns {vgl.pointSource}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.pointSource = function () {
  'use strict';

  if (!(this instanceof vgl.pointSource)) {
    return new vgl.pointSource();
  }
  vgl.source.call(this);

  var m_this = this,
      m_positions = [],
      m_colors = [],
      m_textureCoords = [],
      m_size = [],
      m_geom = null;

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get positions for the points
   */
  ////////////////////////////////////////////////////////////////////////////
  this.getPositions = function () {
    return m_positions;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set positions for the source
   *
   * @param positions
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setPositions = function (positions) {
    if (positions instanceof Array) {
      m_positions = positions;
    } else {
      console
          .log('[ERROR] Invalid data type for positions. Array is required.');
    }
    m_this.modified();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get colors for the points
   */
  ////////////////////////////////////////////////////////////////////////////
  this.getColors = function () {
    return m_colors;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set colors for the points
   *
   * @param colors
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setColors = function (colors) {
    if (colors instanceof Array) {
      m_colors = colors;
    } else {
      console.log('[ERROR] Invalid data type for colors. Array is required.');
    }

    m_this.modified();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get size for the points
   */
  ////////////////////////////////////////////////////////////////////////////
  this.getSize = function () {
    return m_size;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set colors for the points
   *
   * @param colors
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setSize = function (size) {
    m_size = size;
    this.modified();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set texture coordinates for the points
   *
   * @param texcoords
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setTextureCoordinates = function (texcoords) {
    if (texcoords instanceof Array) {
      m_textureCoords = texcoords;
    } else {
      console.log('[ERROR] Invalid data type for ' +
                  'texture coordinates. Array is required.');
    }
    m_this.modified();
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Create a point geometry given input parameters
   */
  ////////////////////////////////////////////////////////////////////////////
  this.create = function () {
    m_geom = new vgl.geometryData();

    if (m_positions.length % 3 !== 0) {
      console.log('[ERROR] Invalid length of the points array');
      return;
    }

    var numPts = m_positions.length / 3,
        i = 0,
        indices = [],
        pointsPrimitive,
        sourcePositions,
        sourceColors,
        sourceTexCoords,
        sourceSize;

    indices.length = numPts;
    for (i = 0; i < numPts; i += 1) {
      indices[i] = i;
    }

    /// Generate array of size if needed
    sourceSize = vgl.sourceDataDf();
    if (numPts !== m_size.length) {
      for (i = 0; i < numPts; i += 1) {
        sourceSize.pushBack(m_size);
      }
    } else {
      sourceSize.setData(m_size);
    }
    m_geom.addSource(sourceSize);

    pointsPrimitive = new vgl.points();
    pointsPrimitive.setIndices(indices);

    sourcePositions = vgl.sourceDataP3fv();
    sourcePositions.pushBack(m_positions);
    m_geom.addSource(sourcePositions);

    if ((m_colors.length > 0) && m_colors.length === m_positions.length) {
      sourceColors = vgl.sourceDataC3fv();
      sourceColors.pushBack(m_colors);
      m_geom.addSource(sourceColors);
    } else if ((m_colors.length > 0) && m_colors.length !== m_positions.length) {
      console
          .log('[ERROR] Number of colors are different than number of points');
    }

    if (m_textureCoords.length > 0 &&
        m_textureCoords.length === m_positions.length) {
      sourceTexCoords = vgl.sourceDataT2fv();
      sourceTexCoords.pushBack(m_textureCoords);
      m_geom.addSource(sourceTexCoords);
    } else if (m_textureCoords.length > 0 &&
        (m_textureCoords.length / 2) !== (m_positions.length / 3)) {
      console
          .log('[ERROR] Number of texture coordinates are different than ' +
               'number of points');
    }

    m_geom.addPrimitive(pointsPrimitive);

    return m_geom;
  };
};

inherit(vgl.pointSource, vgl.source);

//////////////////////////////////////////////////////////////////////////////
/**
 * @module vgl
 */

/*global vgl, inherit*/
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class lineSource
 *
 * @class
 * @returns {vgl.lineSource}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.lineSource = function (positions, colors) {
  'use strict';

  if (!(this instanceof vgl.lineSource)) {
    return new vgl.lineSource();
  }
  vgl.source.call(this);

  var m_positions = positions,
      m_colors = colors;

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set start positions for the lines
   *
   * @param positions
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setPositions = function (positions) {
    if (positions instanceof Array) {
      m_positions = positions;
      this.modified();
      return true;
    }

    console
      .log('[ERROR] Invalid data type for positions. Array is required.');
    return false;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Set colors for the lines
   *
   * @param colors
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setColors = function (colors) {
    if (colors instanceof Array) {
      m_colors = colors;
      this.modified();
      return true;
    }

    console.log('[ERROR] Invalid data type for colors. Array is required.');
    return false;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Create a point geometry given input parameters
   */
  ////////////////////////////////////////////////////////////////////////////
  this.create = function () {
    if (!m_positions) {
      console.log('[error] Invalid positions');
      return;
    }

    if (m_positions.length % 3 !== 0) {
      console.log('[error] Line source requires 3d points');
      return;
    }

    if (m_positions.length % 3 !== 0) {
      console.log('[ERROR] Invalid length of the points array');
      return;
    }

    var m_geom = new vgl.geometryData(),
        numPts = m_positions.length / 3,
        i,
        indices = [],
        linesPrimitive,
        sourcePositions,
        sourceColors;

    indices.length = numPts;

    for (i = 0; i < numPts; i += 1) {
      indices[i] = i;
    }

    linesPrimitive = new vgl.lines();
    linesPrimitive.setIndices(indices);

    sourcePositions = vgl.sourceDataP3fv();
    sourcePositions.pushBack(m_positions);
    m_geom.addSource(sourcePositions);

    if (m_colors && (m_colors.length > 0) &&
         m_colors.length === m_positions.length) {
      sourceColors = vgl.sourceDataC3fv();
      sourceColors.pushBack(m_colors);
      m_geom.addSource(sourceColors);
    } else if (m_colors && (m_colors.length > 0) &&
             m_colors.length !== m_positions.length) {
      console
        .log('[error] Number of colors are different than number of points');
    }

    m_geom.addPrimitive(linesPrimitive);

    return m_geom;
  };
};

inherit(vgl.lineSource, vgl.source);

//////////////////////////////////////////////////////////////////////////////
/**
 * @module vgl
 */

/*global document, vgl, inherit*/
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class utils
 *
 * Utility class provides helper functions such as functions to create
 * shaders, geometry etc.
 *
 * @returns {vgl.utils}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.utils = function () {
  'use strict';

  if (!(this instanceof vgl.utils)) {
    return new vgl.utils();
  }
  vgl.object.call(this);

  return this;
};

inherit(vgl.utils, vgl.object);

//////////////////////////////////////////////////////////////////////////////
/**
 * Helper function to compute power of 2 number
 *
 * @param value
 * @param pow
 *
 * @returns {number}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.utils.computePowerOfTwo = function (value, pow) {
  'use strict';
  pow = pow || 1;
  while (pow < value) {
    pow *= 2;
  }
  return pow;
};

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of default vertex shader that uses a texture
 *
 * Helper function to create default vertex shader
 *
 * @param context
 * @returns {vgl.shader}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.utils.createTextureVertexShader = function (context) {
  'use strict';
  var vertexShaderSource = [
    'attribute vec3 vertexPosition;',
    'attribute vec3 textureCoord;',
    'uniform mediump float pointSize;',
    'uniform mat4 modelViewMatrix;',
    'uniform mat4 projectionMatrix;',
    'varying highp vec3 iTextureCoord;',
    'void main(void)',
    '{',
    'gl_PointSize = pointSize;',
    'gl_Position = projectionMatrix * modelViewMatrix * vec4(vertexPosition, 1.0);',
    ' iTextureCoord = textureCoord;', '}'].join('\n');
  return vgl.getCachedShader(vgl.GL.VERTEX_SHADER, context,
                             vertexShaderSource);
};

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of default fragment shader that uses a texture
 *
 * Helper function to create default fragment shader with sampler
 *
 * @param context
 * @returns {vgl.shader}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.utils.createTextureFragmentShader = function (context) {
  'use strict';
  var fragmentShaderSource = [
    'varying highp vec3 iTextureCoord;',
    'uniform sampler2D sampler2d;',
    'uniform mediump float opacity;',
    'void main(void) {',
    'gl_FragColor = vec4(texture2D(sampler2d, vec2(iTextureCoord.s, ' +
                        'iTextureCoord.t)).xyz, opacity);',
    '}'].join('\n');
  return vgl.getCachedShader(vgl.GL.FRAGMENT_SHADER, context,
                             fragmentShaderSource);
};

//////////////////////////////////////////////////////////////////////////////
/**
 * Create variation of createTextureFragmentShader which uses texture alpha
 *
 * Helper function to create default fragment shader with sampler
 *
 * @param context
 * @returns {vgl.shader}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.utils.createRgbaTextureFragmentShader = function (context) {
  'use strict';
  var fragmentShaderSource = [
    'varying highp vec3 iTextureCoord;',
    'uniform sampler2D sampler2d;',
    'uniform mediump float opacity;',
    'void main(void) {',
    '  mediump vec4 color = vec4(texture2D(sampler2d, vec2(' +
                                'iTextureCoord.s, iTextureCoord.t)).xyzw);',
    '  color.w *= opacity;',
    '  gl_FragColor = color;',
    '}'
  ].join('\n');
  return vgl.getCachedShader(vgl.GL.FRAGMENT_SHADER, context,
                             fragmentShaderSource);
};

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of default vertex shader
 *
 * Helper function to create default vertex shader
 *
 * @param context
 * @returns {vgl.shader}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.utils.createVertexShader = function (context) {
  'use strict';
  var vertexShaderSource = [
    'attribute vec3 vertexPosition;',
    'attribute vec3 vertexColor;',
    'uniform mediump float pointSize;',
    'uniform mat4 modelViewMatrix;',
    'uniform mat4 projectionMatrix;',
    'varying mediump vec3 iVertexColor;',
    'varying highp vec3 iTextureCoord;',
    'void main(void)',
    '{',
    'gl_PointSize = pointSize;',
    'gl_Position = projectionMatrix * modelViewMatrix * vec4(vertexPosition, 1.0);',
    ' iVertexColor = vertexColor;', '}'].join('\n');
  return vgl.getCachedShader(vgl.GL.VERTEX_SHADER, context,
                             vertexShaderSource);
};

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of default vertex shader
 *
 * Helper function to create default vertex shader
 *
 * @param context
 * @returns {vgl.shader}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.utils.createPointVertexShader = function (context) {
  'use strict';
  var vertexShaderSource = [
    'attribute vec3 vertexPosition;',
    'attribute vec3 vertexColor;',
    'attribute float vertexSize;',
    'uniform mat4 modelViewMatrix;',
    'uniform mat4 projectionMatrix;',
    'varying mediump vec3 iVertexColor;',
    'varying highp vec3 iTextureCoord;',
    'void main(void)',
    '{',
    'gl_PointSize =  vertexSize;',
    'gl_Position = projectionMatrix * modelViewMatrix * vec4(vertexPosition, 1.0);',
    ' iVertexColor = vertexColor;', '}'].join('\n');
  return vgl.getCachedShader(vgl.GL.VERTEX_SHADER, context,
                             vertexShaderSource);
};

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of vertex shader with a solid color
 *
 * Helper function to create default vertex shader
 *
 * @param context
 * @returns {vgl.shader}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.utils.createVertexShaderSolidColor = function (context) {
  'use strict';
  var vertexShaderSource = [
    'attribute vec3 vertexPosition;',
    'uniform mediump float pointSize;',
    'uniform mat4 modelViewMatrix;',
    'uniform mat4 projectionMatrix;',
    'void main(void)',
    '{',
    'gl_PointSize = pointSize;',
    'gl_Position = projectionMatrix * modelViewMatrix * vec4(vertexPosition, 1.0);',
    '}'].join('\n');
  return vgl.getCachedShader(vgl.GL.VERTEX_SHADER, context,
                             vertexShaderSource);
};

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of vertex shader that passes values through
 * for color mapping
 *
 * Helper function to create default vertex shader
 *
 * @param context
 * @returns {vgl.shader}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.utils.createVertexShaderColorMap = function (context, min, max) {
  'use strict';
  min = min; /* unused parameter */
  max = max; /* unused parameter */
  var vertexShaderSource = [
    'attribute vec3 vertexPosition;',
    'attribute float vertexScalar;',
    'uniform mediump float pointSize;',
    'uniform mat4 modelViewMatrix;',
    'uniform mat4 projectionMatrix;',
    'uniform float lutMin;',
    'uniform float lutMax;',
    'varying mediump float iVertexScalar;',
    'void main(void)',
    '{',
    'gl_PointSize = pointSize;',
    'gl_Position = projectionMatrix * modelViewMatrix * vec4(vertexPosition, 1.0);',
    'iVertexScalar = (vertexScalar-lutMin)/(lutMax-lutMin);',
    '}'].join('\n');
  return vgl.getCachedShader(vgl.GL.VERTEX_SHADER, context,
                             vertexShaderSource);
};

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of default fragment shader
 *
 * Helper function to create default fragment shader
 *
 * @param context
 * @returns {vgl.shader}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.utils.createFragmentShader = function (context) {
  'use strict';
  var fragmentShaderSource = ['varying mediump vec3 iVertexColor;',
                              'uniform mediump float opacity;',
                              'void main(void) {',
                              'gl_FragColor = vec4(iVertexColor, opacity);',
                              '}'].join('\n');
  return vgl.getCachedShader(vgl.GL.FRAGMENT_SHADER, context,
                             fragmentShaderSource);
};

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a Phong vertex shader
 *
 * Helper function to create Phong vertex shader
 *
 * @param context
 * @returns {vgl.shader}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.utils.createPhongVertexShader = function (context) {
  'use strict';
  var vertexShaderSource = [
    'attribute highp vec3 vertexPosition;',
    'attribute mediump vec3 vertexNormal;',
    'attribute mediump vec3 vertexColor;',

    'uniform highp mat4 projectionMatrix;',
    'uniform mat4 modelViewMatrix;',
    'uniform mat4 normalMatrix;',

    'varying highp vec4 varPosition;',
    'varying mediump vec3 varNormal;',
    'varying mediump vec3 varVertexColor;',

    'void main(void)',
    '{',
    'varPosition = modelViewMatrix * vec4(vertexPosition, 1.0);',
    'gl_Position = projectionMatrix * varPosition;',
    'varNormal = vec3(normalMatrix * vec4(vertexNormal, 0.0));',
    'varVertexColor = vertexColor;',
    '}'].join('\n');
  return vgl.getCachedShader(vgl.GL.VERTEX_SHADER, context,
                             vertexShaderSource);
};

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of Phong fragment shader
 *
 * Helper function to create Phong fragment shader
 *
 * NOTE: Shader assumes directional light
 *
 * @param context
 * @returns {vgl.shader}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.utils.createPhongFragmentShader = function (context) {
  'use strict';
  var fragmentShaderSource = [
    'uniform mediump float opacity;',
    'precision mediump float;',
    'varying vec3 varNormal;',
    'varying vec4 varPosition;',
    'varying mediump vec3 varVertexColor;',
    'const vec3 lightPos = vec3(0.0, 0.0,10000.0);',
    'const vec3 ambientColor = vec3(0.01, 0.01, 0.01);',
    'const vec3 specColor = vec3(0.0, 0.0, 0.0);',

    'void main() {',
    'vec3 normal = normalize(varNormal);',
    'vec3 lightDir = normalize(lightPos);',
    'vec3 reflectDir = -reflect(lightDir, normal);',
    'vec3 viewDir = normalize(-varPosition.xyz);',

    'float lambertian = max(dot(lightDir, normal), 0.0);',
    'vec3 color = vec3(0.0);',
    'if(lambertian > 0.0) {',
    '  color = lambertian * varVertexColor;',
    '}',
    'gl_FragColor = vec4(color * opacity, 1.0 - opacity);',
    '}'].join('\n');
  return vgl.getCachedShader(vgl.GL.FRAGMENT_SHADER, context,
                             fragmentShaderSource);
};

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of fragment shader with an assigned constant color.
 *
 * Helper function to create default fragment shader
 *
 * @param context
 * @returns {vgl.shader}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.utils.createFragmentShaderSolidColor = function (context, color) {
  'use strict';
  var fragmentShaderSource = [
    'uniform mediump float opacity;',
    'void main(void) {',
    'gl_FragColor = vec4(' + color[0] + ',' + color[1] + ',' + color[2] + ', opacity);',
    '}'].join('\n');
  return vgl.getCachedShader(vgl.GL.FRAGMENT_SHADER, context,
                             fragmentShaderSource);
};

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of fragment shader that maps values into colors bia lookup table
 *
 * Helper function to create default fragment shader
 *
 * @param context
 * @returns {vgl.shader}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.utils.createFragmentShaderColorMap = function (context) {
  'use strict';
  var fragmentShaderSource = [
    'varying mediump float iVertexScalar;',
    'uniform sampler2D sampler2d;',
    'uniform mediump float opacity;',
    'void main(void) {',
    'gl_FragColor = vec4(texture2D(sampler2d, vec2(iVertexScalar, ' +
            '0.0)).xyz, opacity);',
    '}'].join('\n');
  return vgl.getCachedShader(vgl.GL.FRAGMENT_SHADER, context,
                             fragmentShaderSource);
};

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of vertex shader for point sprites
 *
 * Helper function to create default point sprites vertex shader
 *
 * @param context
 * @returns {vgl.shader}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.utils.createPointSpritesVertexShader = function (context) {
  'use strict';
  var vertexShaderSource = [
    'attribute vec3 vertexPosition;',
    'attribute vec3 vertexColor;',
    'uniform mediump vec2 pointSize;',
    'uniform mat4 modelViewMatrix;',
    'uniform mat4 projectionMatrix;',
    'uniform float height;',
    'varying mediump vec3 iVertexColor;',
    'varying highp float iVertexScalar;',
    'void main(void)',
    '{',
    'mediump float realPointSize = pointSize.y;',
    'if (pointSize.x > pointSize.y) {',
    '  realPointSize = pointSize.x;}',
    'gl_PointSize = realPointSize ;',
    'iVertexScalar = vertexPosition.z;',
    'gl_Position = projectionMatrix * modelViewMatrix * ' +
            'vec4(vertexPosition.xy, height, 1.0);',
    ' iVertexColor = vertexColor;', '}'].join('\n');
  return vgl.getCachedShader(vgl.GL.VERTEX_SHADER, context,
                             vertexShaderSource);
};

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of fragment shader for point sprites
 *
 * Helper function to create default point sprites fragment shader
 *
 * @param context
 * @returns {vgl.shader}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.utils.createPointSpritesFragmentShader = function (context) {
  'use strict';
  var fragmentShaderSource = [
    'varying mediump vec3 iVertexColor;',
    'varying highp float iVertexScalar;',
    'uniform sampler2D opacityLookup;',
    'uniform highp float lutMin;',
    'uniform highp float lutMax;',
    'uniform sampler2D scalarsToColors;',
    'uniform int useScalarsToColors;',
    'uniform int useVertexColors;',
    'uniform mediump vec2 pointSize;',
    'uniform mediump float vertexColorWeight;',
    'void main(void) {',
    'mediump vec2 realTexCoord;',
    'if (pointSize.x > pointSize.y) {',
    '  realTexCoord = vec2(1.0, pointSize.y/pointSize.x) * gl_PointCoord;',
    '} else {',
    '  realTexCoord = vec2(pointSize.x/pointSize.y, 1.0) * gl_PointCoord;',
    '}',
    'highp float texOpacity = texture2D(opacityLookup, realTexCoord).w;',
    'if (useScalarsToColors == 1) {',
    '  gl_FragColor = vec4(texture2D(scalarsToColors, vec2((' +
            'iVertexScalar - lutMin)/(lutMax - lutMin), 0.0)).xyz, ' +
            'texOpacity);',
    '} else if (useVertexColors == 1) {',
    '  gl_FragColor = vec4(iVertexColor, texOpacity);',
    '} else {',
    '  gl_FragColor = vec4(texture2D(opacityLookup, realTexCoord).xyz, texOpacity);',
    '}}'
  ].join('\n');
  return vgl.getCachedShader(vgl.GL.FRAGMENT_SHADER, context,
                             fragmentShaderSource);
};

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of texture material
 *
 * Helper function to create a texture material
 *
 * @returns {vgl.material}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.utils.createTextureMaterial = function (isRgba, origin) {
  'use strict';
  var mat = new vgl.material(),
      blend = new vgl.blend(),
      prog = new vgl.shaderProgram(),
      vertexShader = vgl.utils.createTextureVertexShader(vgl.GL),
      fragmentShader = null,
      posVertAttr = new vgl.vertexAttribute('vertexPosition'),
      texCoordVertAttr = new vgl.vertexAttribute('textureCoord'),
      pointsizeUniform = new vgl.floatUniform('pointSize', 5.0),
      modelViewUniform,
      projectionUniform = new vgl.projectionUniform('projectionMatrix'),
      samplerUniform = new vgl.uniform(vgl.GL.INT, 'sampler2d'),
      opacityUniform = null;
  if (origin !== undefined) {
    modelViewUniform = new vgl.modelViewOriginUniform('modelViewMatrix',
                                                      origin);
  } else {
    modelViewUniform = new vgl.modelViewUniform('modelViewMatrix');
  }

  samplerUniform.set(0);

  prog.addVertexAttribute(posVertAttr, vgl.vertexAttributeKeys.Position);
  prog.addVertexAttribute(texCoordVertAttr,
                          vgl.vertexAttributeKeys.TextureCoordinate);
  prog.addUniform(pointsizeUniform);
  prog.addUniform(modelViewUniform);
  prog.addUniform(projectionUniform);

  if (isRgba) {
    fragmentShader = vgl.utils.createRgbaTextureFragmentShader(vgl.GL);
  } else {
    fragmentShader = vgl.utils.createTextureFragmentShader(vgl.GL);
  }
  opacityUniform = new vgl.floatUniform('opacity', 1.0);
  prog.addUniform(opacityUniform);

  prog.addShader(fragmentShader);
  prog.addShader(vertexShader);
  mat.addAttribute(prog);
  mat.addAttribute(blend);

  return mat;
};

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of geometry material
 *
 * Helper function to create geometry material
 *
 * @returns {vgl.material}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.utils.createGeometryMaterial = function () {
  'use strict';
  var mat = new vgl.material(),
      prog = new vgl.shaderProgram(),
      pointSize = 5.0,
      opacity = 1.0,
      vertexShader = vgl.utils.createVertexShader(vgl.GL),
      fragmentShader = vgl.utils.createFragmentShader(vgl.GL),
      posVertAttr = new vgl.vertexAttribute('vertexPosition'),
      colorVertAttr = new vgl.vertexAttribute('vertexColor'),
      pointsizeUniform = new vgl.floatUniform('pointSize', pointSize),
      opacityUniform = new vgl.floatUniform('opacity', opacity),
      modelViewUniform = new vgl.modelViewUniform('modelViewMatrix'),
      projectionUniform = new vgl.projectionUniform('projectionMatrix');

  prog.addVertexAttribute(posVertAttr, vgl.vertexAttributeKeys.Position);
  prog.addVertexAttribute(colorVertAttr, vgl.vertexAttributeKeys.Color);
  prog.addUniform(pointsizeUniform);
  prog.addUniform(opacityUniform);
  prog.addUniform(modelViewUniform);
  prog.addUniform(projectionUniform);
  prog.addShader(fragmentShader);
  prog.addShader(vertexShader);
  mat.addAttribute(prog);

  return mat;
};

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of geometry material
 *
 * Helper function to create geometry material
 *
 * @returns {vgl.material}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.utils.createPointGeometryMaterial = function (opacity) {
  'use strict';
  opacity = opacity === undefined ? 1.0 : opacity;
  var mat = new vgl.material(),
      blend = new vgl.blend(),
      prog = new vgl.shaderProgram(),
      vertexShader = vgl.utils.createPointVertexShader(vgl.GL),
      fragmentShader = vgl.utils.createFragmentShader(vgl.GL),
      posVertAttr = new vgl.vertexAttribute('vertexPosition'),
      colorVertAttr = new vgl.vertexAttribute('vertexColor'),
      sizeVertAttr = new vgl.vertexAttribute('vertexSize'),
      opacityUniform = new vgl.floatUniform('opacity', opacity),
      modelViewUniform = new vgl.modelViewUniform('modelViewMatrix'),
      projectionUniform = new vgl.projectionUniform('projectionMatrix');

  prog.addVertexAttribute(posVertAttr, vgl.vertexAttributeKeys.Position);
  prog.addVertexAttribute(colorVertAttr, vgl.vertexAttributeKeys.Color);
  prog.addVertexAttribute(sizeVertAttr, vgl.vertexAttributeKeys.Scalar);
  prog.addUniform(opacityUniform);
  prog.addUniform(modelViewUniform);
  prog.addUniform(projectionUniform);
  prog.addShader(fragmentShader);
  prog.addShader(vertexShader);
  mat.addAttribute(prog);
  mat.addAttribute(blend);

  return mat;
};

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of geometry material with the phong shader
 *
 * Helper function to create color phong shaded geometry material
 *
 * @returns {vgl.material}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.utils.createPhongMaterial = function () {
  'use strict';
  var mat = new vgl.material(),
      prog = new vgl.shaderProgram(),
      vertexShader = vgl.utils.createPhongVertexShader(vgl.GL),
      fragmentShader = vgl.utils.createPhongFragmentShader(vgl.GL),
      posVertAttr = new vgl.vertexAttribute('vertexPosition'),
      normalVertAttr = new vgl.vertexAttribute('vertexNormal'),
      colorVertAttr = new vgl.vertexAttribute('vertexColor'),
      opacityUniform = new vgl.floatUniform('opacity', 1.0),
      modelViewUniform = new vgl.modelViewUniform('modelViewMatrix'),
      normalUniform = new vgl.normalMatrixUniform('normalMatrix'),
      projectionUniform = new vgl.projectionUniform('projectionMatrix');

  prog.addVertexAttribute(posVertAttr, vgl.vertexAttributeKeys.Position);
  prog.addVertexAttribute(normalVertAttr, vgl.vertexAttributeKeys.Normal);
  prog.addVertexAttribute(colorVertAttr, vgl.vertexAttributeKeys.Color);
  prog.addUniform(opacityUniform);
  prog.addUniform(modelViewUniform);
  prog.addUniform(projectionUniform);
  prog.addUniform(normalUniform);
  prog.addShader(fragmentShader);
  prog.addShader(vertexShader);
  //mat.addAttribute(blend);
  mat.addAttribute(prog);

  return mat;
};

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of colored geometry material
 *
 * Helper function to create color geometry material
 *
 * @returns {vgl.material}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.utils.createColorMaterial = function () {
  'use strict';
  var mat = new vgl.material(),
      blend = new vgl.blend(),
      prog = new vgl.shaderProgram(),
      vertexShader = vgl.utils.createVertexShader(vgl.GL),
      fragmentShader = vgl.utils.createFragmentShader(vgl.GL),
      posVertAttr = new vgl.vertexAttribute('vertexPosition'),
      texCoordVertAttr = new vgl.vertexAttribute('textureCoord'),
      colorVertAttr = new vgl.vertexAttribute('vertexColor'),
      pointsizeUniform = new vgl.floatUniform('pointSize', 5.0),
      opacityUniform = new vgl.floatUniform('opacity', 1.0),
      modelViewUniform = new vgl.modelViewUniform('modelViewMatrix'),
      projectionUniform = new vgl.projectionUniform('projectionMatrix');

  prog.addVertexAttribute(posVertAttr, vgl.vertexAttributeKeys.Position);
  prog.addVertexAttribute(colorVertAttr, vgl.vertexAttributeKeys.Color);
  prog.addVertexAttribute(texCoordVertAttr,
                          vgl.vertexAttributeKeys.TextureCoordinate);
  prog.addUniform(pointsizeUniform);
  prog.addUniform(opacityUniform);
  prog.addUniform(modelViewUniform);
  prog.addUniform(projectionUniform);
  prog.addShader(fragmentShader);
  prog.addShader(vertexShader);
  mat.addAttribute(prog);
  mat.addAttribute(blend);

  return mat;
};

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of geometry material
 *
 * Helper function to create geometry material
 *
 * @returns {vgl.material}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.utils.createColorMappedMaterial = function (lut) {
  'use strict';
  if (!lut) {
    lut = new vgl.lookupTable();
  }

  var scalarRange = lut.range(),
      mat = new vgl.material(),
      blend = new vgl.blend(),
      prog = new vgl.shaderProgram(),
      vertexShader = vgl.utils.createVertexShaderColorMap(
        vgl.GL, scalarRange[0], scalarRange[1]),
      fragmentShader = vgl.utils.createFragmentShaderColorMap(vgl.GL),
      posVertAttr = new vgl.vertexAttribute('vertexPosition'),
      scalarVertAttr = new vgl.vertexAttribute('vertexScalar'),
      pointsizeUniform = new vgl.floatUniform('pointSize', 5.0),
      opacityUniform = new vgl.floatUniform('opacity', 1.0),
      lutMinUniform = new vgl.floatUniform('lutMin', scalarRange[0]),
      lutMaxUniform = new vgl.floatUniform('lutMax', scalarRange[1]),
      modelViewUniform = new vgl.modelViewUniform('modelViewMatrix'),
      projectionUniform = new vgl.projectionUniform('projectionMatrix'),
      samplerUniform = new vgl.uniform(vgl.GL.FLOAT, 'sampler2d'),
      lookupTable = lut;

  samplerUniform.set(0);

  prog.addVertexAttribute(posVertAttr, vgl.vertexAttributeKeys.Position);
  prog.addVertexAttribute(scalarVertAttr, vgl.vertexAttributeKeys.Scalar);
  prog.addUniform(pointsizeUniform);
  prog.addUniform(opacityUniform);
  prog.addUniform(lutMinUniform);
  prog.addUniform(lutMaxUniform);
  prog.addUniform(modelViewUniform);
  prog.addUniform(projectionUniform);
  prog.addShader(fragmentShader);
  prog.addShader(vertexShader);
  mat.addAttribute(prog);
  mat.addAttribute(blend);
  mat.addAttribute(lookupTable);

  return mat;
};

//////////////////////////////////////////////////////////////////////////////
/**
 * Update color mapped material
 *
 * @param mat
 * @param scalarRange
 * @param lut
 */
//////////////////////////////////////////////////////////////////////////////
vgl.utils.updateColorMappedMaterial = function (mat, lut) {
  'use strict';
  if (!mat) {
    console.log('[warning] Invalid material. Nothing to update.');
    return;
  }

  if (!lut) {
    console.log('[warning] Invalid lookup table. Nothing to update.');
    return;
  }

  var lutMin = mat.shaderProgram().uniform('lutMin'),
      lutMax = mat.shaderProgram().uniform('lutMax');

  lutMin.set(lut.range()[0]);
  lutMax.set(lut.range()[1]);

  // This will replace the existing lookup table
  mat.setAttribute(lut);
};

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of solid color material
 *
 * Helper function to create geometry material
 *
 * @returns {vgl.material}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.utils.createSolidColorMaterial = function (color) {
  'use strict';
  if (!color) {
    color = [1.0, 1.0, 1.0];
  }

  var mat = new vgl.material(),
      blend = new vgl.blend(),
      prog = new vgl.shaderProgram(),
      vertexShader = vgl.utils.createVertexShaderSolidColor(vgl.GL),
      fragmentShader = vgl.utils.createFragmentShaderSolidColor(vgl.GL, color),
      posVertAttr = new vgl.vertexAttribute('vertexPosition'),
      pointsizeUniform = new vgl.floatUniform('pointSize', 5.0),
      opacityUniform = new vgl.floatUniform('opacity', 1.0),
      modelViewUniform = new vgl.modelViewUniform('modelViewMatrix'),
      projectionUniform = new vgl.projectionUniform('projectionMatrix');

  prog.addVertexAttribute(posVertAttr, vgl.vertexAttributeKeys.Position);
  prog.addUniform(pointsizeUniform);
  prog.addUniform(opacityUniform);
  prog.addUniform(modelViewUniform);
  prog.addUniform(projectionUniform);
  prog.addShader(fragmentShader);
  prog.addShader(vertexShader);
  mat.addAttribute(prog);
  mat.addAttribute(blend);

  return mat;
};

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of point sprites material
 *
 * Helper function to create point sprites material
 *
 * @returns {vgl.material}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.utils.createPointSpritesMaterial = function (image, lut) {
  'use strict';
  var scalarRange = lut === undefined ? [0, 1] : lut.range(),
      mat = new vgl.material(),
      blend = new vgl.blend(),
      prog = new vgl.shaderProgram(),
      vertexShader = vgl.utils.createPointSpritesVertexShader(vgl.GL),
      fragmentShader = vgl.utils.createPointSpritesFragmentShader(vgl.GL),
      posVertAttr = new vgl.vertexAttribute('vertexPosition'),
      colorVertAttr = new vgl.vertexAttribute('vertexColor'),
      heightUniform = new vgl.floatUniform('height', 0.0),
      vertexColorWeightUniform =
        new vgl.floatUniform('vertexColorWeight', 0.0),
      lutMinUniform = new vgl.floatUniform('lutMin', scalarRange[0]),
      lutMaxUniform = new vgl.floatUniform('lutMax', scalarRange[1]),
      modelViewUniform = new vgl.modelViewUniform('modelViewMatrix'),
      projectionUniform = new vgl.projectionUniform('projectionMatrix'),
      samplerUniform = new vgl.uniform(vgl.GL.INT, 'opacityLookup'),
      scalarsToColors = new vgl.uniform(vgl.GL.INT, 'scalarsToColors'),
      useScalarsToColors = new vgl.uniform(vgl.GL.INT, 'useScalarsToColors'),
      useVertexColors = new vgl.uniform(vgl.GL.INT, 'useVertexColors'),
      pointSize = new vgl.uniform(vgl.GL.FLOAT_VEC2, 'pointSize'),
      texture = new vgl.texture();

  samplerUniform.set(0);
  scalarsToColors.set(1);
  useScalarsToColors.set(0);
  useVertexColors.set(0);
  pointSize.set([1.0, 1.0]);

  prog.addVertexAttribute(posVertAttr, vgl.vertexAttributeKeys.Position);
  prog.addVertexAttribute(colorVertAttr, vgl.vertexAttributeKeys.Color);
  prog.addUniform(heightUniform);
  prog.addUniform(vertexColorWeightUniform);
  prog.addUniform(modelViewUniform);
  prog.addUniform(projectionUniform);
  prog.addUniform(samplerUniform);
  prog.addUniform(useVertexColors);
  prog.addUniform(useScalarsToColors);
  prog.addUniform(pointSize);
  prog.addShader(fragmentShader);
  prog.addShader(vertexShader);
  mat.addAttribute(prog);
  mat.addAttribute(blend);

  if (lut) {
    prog.addUniform(scalarsToColors);
    useScalarsToColors.set(1);
    prog.addUniform(lutMinUniform);
    prog.addUniform(lutMaxUniform);
    lut.setTextureUnit(1);
    mat.addAttribute(lut);
  }

  texture.setImage(image);
  texture.setTextureUnit(0);
  mat.addAttribute(texture);
  return mat;
};

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of an actor that contains a plane geometry
 *
 * Function to create a plane node This method will create a plane actor
 * with texture coordinates, eventually normal, and plane material.
 *
 * @returns {vgl.actor}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.utils.createPlane = function (originX, originY, originZ,
                                 point1X, point1Y, point1Z,
                                 point2X, point2Y, point2Z) {
  'use strict';
  var mapper = new vgl.mapper(),
      planeSource = new vgl.planeSource(),
      mat = vgl.utils.createGeometryMaterial(),
      actor = new vgl.actor();

  planeSource.setOrigin(originX, originY, originZ);
  planeSource.setPoint1(point1X, point1Y, point1Z);
  planeSource.setPoint2(point2X, point2Y, point2Z);

  mapper.setGeometryData(planeSource.create());
  actor.setMapper(mapper);
  actor.setMaterial(mat);

  return actor;
};

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of an actor that contains a texture plane geometry
 *
 * Helper function to create a plane textured node This method will create
 * a plane actor with texture coordinates, eventually normal, and plane
 * material.
 *
 * @returns {vgl.actor}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.utils.createTexturePlane = function (originX, originY, originZ,
                                        point1X, point1Y, point1Z,
                                        point2X, point2Y, point2Z,
                                        isRgba) {
  'use strict';
  var mapper = new vgl.mapper(),
      planeSource = new vgl.planeSource(),
      mat = vgl.utils.createTextureMaterial(isRgba,
                                            [originX, originY, originZ]),
      actor = new vgl.actor();

  planeSource.setPoint1(point1X - originX, point1Y - originY, point1Z - originZ);
  planeSource.setPoint2(point2X - originX, point2Y - originY, point2Z - originZ);
  mapper.setGeometryData(planeSource.create());

  actor.setMapper(mapper);
  actor.setMaterial(mat);

  return actor;
};

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of an actor that contains points
 *
 * Helper function to create a point node This method will create a point
 * actor with texture coordinates, eventually normal, and plane material.
 *
 * @returns {vgl.actor}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.utils.createPoints = function (positions, size, colors, texcoords, opacity) {
  'use strict';
  if (!positions) {
    console.log('[ERROR] Cannot create points without positions');
    return null;
  }

  opacity = opacity === undefined ? 1.0 : opacity;
  var mapper = new vgl.mapper(),
      pointSource = new vgl.pointSource(),
      mat = vgl.utils.createPointGeometryMaterial(opacity),
      actor = new vgl.actor();

  pointSource.setPositions(positions);
  if (colors) {
    pointSource.setColors(colors);
  }

  if (texcoords) {
    pointSource.setTextureCoordinates(texcoords);
  }

  if (size) {
    pointSource.setSize(size);
  } else {
    pointSource.setSize(1.0);
  }

  mapper.setGeometryData(pointSource.create());
  actor.setMapper(mapper);
  actor.setMaterial(mat);

  return actor;
};

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of an actor that contains point sprites
 *
 * Helper function to create a point sprites node This method will create
 * a point sprites actor with texture coordinates, normals, and a point sprites
 * material.
 *
 * @returns {vgl.actor}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.utils.createPointSprites = function (image, positions, colors,
                                              texcoords) {
  'use strict';
  if (!image) {
    console.log('[ERROR] Point sprites requires an image');
    return null;
  }

  if (!positions) {
    console.log('[ERROR] Cannot create points without positions');
    return null;
  }

  var mapper = new vgl.mapper(),
      pointSource = new vgl.pointSource(),
      mat = vgl.utils.createPointSpritesMaterial(image),
      actor = new vgl.actor();

  pointSource.setPositions(positions);
  if (colors) {
    pointSource.setColors(colors);
  }

  if (texcoords) {
    pointSource.setTextureCoordinates(texcoords);
  }

  mapper.setGeometryData(pointSource.create());
  actor.setMapper(mapper);
  actor.setMaterial(mat);

  return actor;
};

//////////////////////////////////////////////////////////////////////////////
/**
 * Create lines given positions, colors, and desired length
 *
 * @param positions
 * @param colors
 */
//////////////////////////////////////////////////////////////////////////////
vgl.utils.createLines = function (positions, colors) {
  'use strict';
  if (!positions) {
    console.log('[ERROR] Cannot create points without positions');
    return null;
  }

  var mapper = new vgl.mapper(),
      lineSource = new vgl.lineSource(),
      mat = vgl.utils.createGeometryMaterial(),
      actor = new vgl.actor();

  lineSource.setPositions(positions);
  if (colors) {
    lineSource.setColors(colors);
  }

  mapper.setGeometryData(lineSource.create());
  actor.setMapper(mapper);
  actor.setMaterial(mat);

  return actor;
};

//////////////////////////////////////////////////////////////////////////////
/**
 * Create color legend
 *
 * @param lookupTable
 * @param width
 * @param height
 * @param origin
 * @param divs
 * @returns {Array}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.utils.createColorLegend = function (varname, lookupTable, origin,
                                             width, height, countMajor,
                                             countMinor) {
  'use strict';

  if (!lookupTable) {
    console.log('[error] Invalid lookup table');
    return [];
  }

  //////////////////////////////////////////////////////////////////////////////
  /**
   * Create labels for the legend
   *
   * @param ticks
   * @param range
   * @param divs
   */
  //////////////////////////////////////////////////////////////////////////////
  function createLabels(varname, positions, range) {
    if (!positions) {
      console.log('[error] Create labels requires positions (x,y,z) array');
      return;
    }

    if (positions.length % 3 !== 0) {
      console.log('[error] Create labels require positions array contain 3d points');
      return;
    }

    if (!range) {
      console.log('[error] Create labels requires Valid range');
      return;
    }

    var actor = null,
        size = vgl.utils.computePowerOfTwo(48),
        index = 0,
        actors = [],
        origin = [],
        pt1 = [],
        pt2 = [],
        delta = (positions[6] - positions[0]),
        axisLabelOffset = 4, i;

    origin.length = 3;
    pt1.length = 3;
    pt2.length = 3;

    // For now just create labels for end points
    for (i = 0; i < 2; i += 1) {
      index = i * (positions.length - 3);

      origin[0] = positions[index] - delta;
      origin[1] = positions[index + 1] - 2 * delta;
      origin[2] = positions[index + 2];

      pt1[0] = positions[index] + delta;
      pt1[1] = origin[1];
      pt1[2] = origin[2];

      pt2[0] = origin[0];
      pt2[1] = positions[1];
      pt2[2] = origin[2];

      actor = vgl.utils.createTexturePlane(
        origin[0], origin[1], origin[2],
        pt1[0], pt1[1], pt1[2],
        pt2[0], pt2[1], pt2[2], true);

      actor.setReferenceFrame(vgl.boundingObject.ReferenceFrame.Absolute);
      actor.material().setBinNumber(vgl.material.RenderBin.Overlay);
      actor.material().addAttribute(vgl.utils.create2DTexture(
        range[i].toFixed(2).toString(), 12, null));
      actors.push(actor);
    }

    // Create axis label
    origin[0] = (positions[0] + positions[positions.length - 3] - size) * 0.5;
    origin[1] = positions[1] + axisLabelOffset;
    origin[2] = positions[2];

    pt1[0] = origin[0] + size;
    pt1[1] = origin[1];
    pt1[2] = origin[2];

    pt2[0] = origin[0];
    pt2[1] = origin[1] + size;
    pt2[2] = origin[2];

    actor = vgl.utils.createTexturePlane(
      origin[0], origin[1], origin[2],
      pt1[0], pt1[1], pt1[2],
      pt2[0], pt2[1], pt2[2], true);
    actor.setReferenceFrame(vgl.boundingObject.ReferenceFrame.Absolute);
    actor.material().setBinNumber(vgl.material.RenderBin.Overlay);
    actor.material().addAttribute(vgl.utils.create2DTexture(
      varname, 24, null));
    actors.push(actor);

    return actors;
  }

  //////////////////////////////////////////////////////////////////////////////
  // TODO Currently we assume that the ticks are laid on x-axis
  // and this is on a 2D plane (ignoring Z axis. For now lets
  // not draw minor ticks.
  /**
   * Create ticks and labels
   *
   * @param originX
   * @param originY
   * @param originZ
   * @param pt1X
   * @param pt1Y
   * @param pt1Z
   * @param pt2X
   * @param pt2Y
   * @param pt2Z
   * @param divs
   * @param heightMajor
   * @param heightMinor
   * @returns {Array} Returns array of vgl.actor
   */
  //////////////////////////////////////////////////////////////////////////////
  function createTicksAndLabels(varname, lut,
                        originX, originY, originZ,
                        pt1X, pt1Y, pt1Z,
                        pt2X, pt2Y, pt2Z,
                        countMajor, countMinor,
                        heightMajor, heightMinor) {
    heightMinor = heightMinor; /* unused parameter */
    var width = pt2X - pt1X,
        index = null,
        delta = width / countMajor,
        positions = [],
        actors = [];

    for (index = 0; index <= countMajor; index += 1) {
      positions.push(pt1X + delta * index);
      positions.push(pt1Y);
      positions.push(pt1Z);

      positions.push(pt1X + delta * index);
      positions.push(pt1Y + heightMajor);
      positions.push(pt1Z);
    }

    // TODO: Fix this
    //actor = vgl.utils.createLines(positions, null);
    //actor.setReferenceFrame(vgl.boundingObject.ReferenceFrame.Absolute);
    //actor.material().setBinNumber(vgl.material.RenderBin.Overlay);
    //actors.push(actor);

    actors = actors.concat(createLabels(varname, positions, lut.range()));
    return actors;
  }

  // TODO Currently we create only one type of legend
  var pt1X = origin[0] + width,
      pt1Y = origin[1],
      pt1Z = 0.0,
      pt2X = origin[0],
      pt2Y = origin[1] + height,
      pt2Z = 0.0,
      actors = [],
      actor = null,
      mat = null,
      group = vgl.groupNode();

  actor = vgl.utils.createTexturePlane(
    origin[0], origin[1], origin[2],
    pt1X, pt1Y, pt1Z,
    pt2X, pt2Y, pt2Z, true
  );

  mat = actor.material();
  mat.addAttribute(lookupTable);
  actor.setMaterial(mat);
  group.addChild(actor);
  actor.material().setBinNumber(vgl.material.RenderBin.Overlay);
  actor.setReferenceFrame(vgl.boundingObject.ReferenceFrame.Absolute);
  actors.push(actor);
  actors = actors.concat(createTicksAndLabels(
                          varname,
                          lookupTable,
                          origin[0], origin[1], origin[1],
                          pt2X, pt1Y, pt1Z,
                          pt1X, pt1Y, pt1Z,
                          countMajor, countMinor, 5, 3));

  // TODO This needs to change so that we can return a group node
  // which should get appended to the scene graph
  return actors;
};

//////////////////////////////////////////////////////////////////////////////
/**
 * Create 2D texture by rendering text using canvas2D context
 *
 * @param textToWrite
 * @param textSize
 * @param color
 * @returns {vgl.texture}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.utils.create2DTexture = function (textToWrite, textSize,
  color, font, alignment, baseline, bold) {
  'use strict';

  var canvas = document.getElementById('textRendering'),
      ctx = null,
      texture = vgl.texture();

  font = font || 'sans-serif';
  alignment = alignment || 'center';
  baseline = baseline || 'bottom';

  if (typeof bold === 'undefined') {
    bold = true;
  }

  if (!canvas) {
    canvas = document.createElement('canvas');
  }
  ctx = canvas.getContext('2d');

  canvas.setAttribute('id', 'textRendering');
  canvas.style.display = 'none';

  // Make width and height equal so that we get pretty looking text.
  canvas.height = vgl.utils.computePowerOfTwo(8 * textSize);
  canvas.width = canvas.height;

  ctx.fillStyle = 'rgba(0, 0, 0, 0)';
  ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);

  // This determines the text colour, it can take a hex value or rgba value
  // (e.g. rgba(255,0,0,0.5))
  ctx.fillStyle = 'rgba(200, 85, 10, 1.0)';

  // This determines the alignment of text, e.g. left, center, right
  ctx.textAlign = alignment;

  // This determines the baseline of the text, e.g. top, middle, bottom
  ctx.textBaseline = baseline;

  // This determines the size of the text and the font family used
  ctx.font = 4 * textSize + 'px ' + font;
  if (bold) {
    ctx.font = 'bold ' + ctx.font;
  }

  ctx.fillText(textToWrite, canvas.width / 2, canvas.height / 2, canvas.width);

  texture.setImage(canvas);
  texture.updateDimensions();

  return texture;
};

//////////////////////////////////////////////////////////////////////////////
/**
 * @module vgl
 */

/*global vgl, vec4, inherit*/
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class picker
 *
 * @class vgl.picker
 * @returns {vgl.picker}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.picker = function () {
  'use strict';

  if (!(this instanceof vgl.picker)) {
    return new vgl.picker();
  }
  vgl.object.call(this);

  /** @private */
  var m_actors = [];

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Get actors intersected
   */
  ////////////////////////////////////////////////////////////////////////////
  this.getActors = function () {
    return m_actors;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Perform pick operation
   */
  ////////////////////////////////////////////////////////////////////////////
  this.pick = function (selectionX, selectionY, renderer) {
    // Check if variables are acceptable
    if (selectionX === undefined) {
      return 0;
    }
    if (selectionY === undefined) {
      return 0;
    }
    if (renderer === undefined) {
      return 0;
    }

    // Clean list of actors intersected previously
    m_actors = [];

    //
    var camera = renderer.camera(),
        width = renderer.width(),
        height = renderer.height(),
        fpoint = camera.focalPoint(),
        focusWorldPt = vec4.fromValues(fpoint[0], fpoint[1], fpoint[2], 1.0),
        focusDisplayPt = renderer.worldToDisplay(
          focusWorldPt, camera.viewMatrix(),
        camera.projectionMatrix(), width, height),
        displayPt = vec4.fromValues(selectionX,
                      selectionY, focusDisplayPt[2], 1.0),
        // Convert selection point into world coordinates
        worldPt = renderer.displayToWorld(displayPt, camera.viewMatrix(),
                    camera.projectionMatrix(), width, height),
        cameraPos = camera.position(), ray = [], actors, count, i, bb,
        tmin, tmax, tymin, tymax, tzmin, tzmax, actor;

    for (i = 0; i < 3; i += 1) {
      ray[i] = worldPt[i] - cameraPos[i];
    }

    // Go through all actors and check if intersects
    actors = renderer.sceneRoot().children();
    count = 0;

    for (i = 0; i < actors.length; i += 1) {
      actor = actors[i];
      if (actor.visible() === true) {
        bb = actor.bounds();
        // Ray-aabb intersection - Smits' method
        if (ray[0] >= 0) {
          tmin = (bb[0] - cameraPos[0]) / ray[0];
          tmax = (bb[1] - cameraPos[0]) / ray[0];
        } else {
          tmin = (bb[1] - cameraPos[0]) / ray[0];
          tmax = (bb[0] - cameraPos[0]) / ray[0];
        }
        if (ray[1] >= 0) {
          tymin = (bb[2] - cameraPos[1]) / ray[1];
          tymax = (bb[3] - cameraPos[1]) / ray[1];
        } else {
          tymin = (bb[3] - cameraPos[1]) / ray[1];
          tymax = (bb[2] - cameraPos[1]) / ray[1];
        }
        if ((tmin > tymax) || (tymin > tmax)) {
          //jscs:disable disallowKeywords
          continue;
          //jscs:enable disallowKeywords
        }

        if (tymin > tmin) {
          tmin = tymin;
        }
        if (tymax < tmax) {
          tmax = tymax;
        }
        if (ray[2] >= 0) {
          tzmin = (bb[4] - cameraPos[2]) / ray[2];
          tzmax = (bb[5] - cameraPos[2]) / ray[2];
        } else {
          tzmin = (bb[5] - cameraPos[2]) / ray[2];
          tzmax = (bb[4] - cameraPos[2]) / ray[2];
        }
        if ((tmin > tzmax) || (tzmin > tmax)) {
          //jscs:disable disallowKeywords
          continue;
          //jscs:enable disallowKeywords
        }
        if (tzmin > tmin) {
          tmin = tzmin;
        }
        if (tzmax < tmax) {
          tmax = tzmax;
        }

        m_actors[count] = actor;
        count += 1;
      }
    }
    return count;
  };

  return this;
};

inherit(vgl.picker, vgl.object);

//////////////////////////////////////////////////////////////////////////////
/**
 * @module vgl
 */

/*global vgl, $*/
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of shapefile reader
 *
 * This contains code that reads a shapefile and produces vgl geometries
 *
 * @class
 * @returns {vgl.shapefileReader}
 */
//////////////////////////////////////////////////////////////////////////////
vgl.shapefileReader = function () {
  'use strict';

  if (!(this instanceof vgl.shapefileReader)) {
    return new vgl.shapefileReader();
  }

  var m_that = this;
  var SHP_NULL = 0;
  var SHP_POINT = 1;
  var SHP_POLYGON = 5;
  var SHP_POLYLINE = 3;

  this.int8 = function (data, offset) {
    return data.charCodeAt(offset);
  };

  /*jshint bitwise: false */
  this.bint32 = function (data, offset) {
    return (
      ((data.charCodeAt(offset) & 0xff) << 24) +
        ((data.charCodeAt(offset + 1) & 0xff) << 16) +
        ((data.charCodeAt(offset + 2) & 0xff) << 8) +
        (data.charCodeAt(offset + 3) & 0xff)
    );
  };

  this.lint32 = function (data, offset) {
    return (
      ((data.charCodeAt(offset + 3) & 0xff) << 24) +
        ((data.charCodeAt(offset + 2) & 0xff) << 16) +
        ((data.charCodeAt(offset + 1) & 0xff) << 8) +
        (data.charCodeAt(offset) & 0xff)
    );
  };

  this.bint16 = function (data, offset) {
    return (
      ((data.charCodeAt(offset) & 0xff) << 8) +
        (data.charCodeAt(offset + 1) & 0xff)
    );
  };

  this.lint16 = function (data, offset) {
    return (
      ((data.charCodeAt(offset + 1) & 0xff) << 8) +
        (data.charCodeAt(offset) & 0xff)
    );
  };

  this.ldbl64 = function (data, offset) {
    var b0 = data.charCodeAt(offset) & 0xff;
    var b1 = data.charCodeAt(offset + 1) & 0xff;
    var b2 = data.charCodeAt(offset + 2) & 0xff;
    var b3 = data.charCodeAt(offset + 3) & 0xff;
    var b4 = data.charCodeAt(offset + 4) & 0xff;
    var b5 = data.charCodeAt(offset + 5) & 0xff;
    var b6 = data.charCodeAt(offset + 6) & 0xff;
    var b7 = data.charCodeAt(offset + 7) & 0xff;

    var sign = 1 - 2 * (b7 >> 7);
    var exp = (((b7 & 0x7f) << 4) + ((b6 & 0xf0) >> 4)) - 1023;
    //var frac = (b6 & 0x0f) * Math.pow (2, -4) + b5 * Math.pow (2, -12) + b4 *
    // Math.pow (2, -20) + b3 * Math.pow (2, -28) + b2 * Math.pow (2, -36) + b1 *
    // Math.pow (2, -44) + b0 * Math.pow (2, -52);

    //return sign * (1 + frac) * Math.pow (2, exp);
    var frac = (b6 & 0x0f) * Math.pow(2, 48) + b5 * Math.pow(2, 40) + b4 *
                 Math.pow(2, 32) + b3 * Math.pow(2, 24) + b2 *
                 Math.pow(2, 16) + b1 * Math.pow(2, 8) + b0;

    return sign * (1 + frac * Math.pow(2, -52)) * Math.pow(2, exp);
  };

  this.lfloat32 = function (data, offset) {
    var b0 = data.charCodeAt(offset) & 0xff;
    var b1 = data.charCodeAt(offset + 1) & 0xff;
    var b2 = data.charCodeAt(offset + 2) & 0xff;
    var b3 = data.charCodeAt(offset + 3) & 0xff;

    var sign = 1 - 2 * (b3 >> 7);
    var exp = (((b3 & 0x7f) << 1) + ((b2 & 0xfe) >> 7)) - 127;
    var frac = (b2 & 0x7f) * Math.pow(2, 16) + b1 * Math.pow(2, 8) + b0;

    return sign * (1 + frac * Math.pow(2, -23)) * Math.pow(2, exp);
  };
  /*jshint bitwise: true */

  this.str = function (data, offset, length) {
    var chars = [];
    var index = offset;
    while (index < offset + length) {
      var c = data[index];
      if (c.charCodeAt(0) !== 0) {
        chars.push(c);
      } else {
        break;
      }
      index += 1;
    }
    return chars.join('');
  };

  this.readHeader = function (data) {
    var code = this.bint32(data, 0);
    var length = this.bint32(data, 24);
    var version = this.lint32(data, 28);
    var shapetype = this.lint32(data, 32);

    /*
    var xmin = this.ldbl64(data, 36);
    var ymin = this.ldbl64(data, 44);
    var xmax = this.ldbl64(data, 52);
    var ymax = this.ldbl64(data, 60);
    */
    return {
      code: code,
      length: length,
      version: version,
      shapetype: shapetype
      // bounds: new Box (vect (xmin, ymin), vect (xmax, ymax))
    };
  };

  this.loadShx = function (data) {
    var indices = [];
    var appendIndex = function (offset) {
      indices.push(2 * m_that.bint32(data, offset));
      return offset + 8;
    };
    var offset = 100;
    while (offset < data.length) {
      offset = appendIndex(offset);
    }
    return indices;
  };

  this.Shapefile = function (options) {
    var path = options.path;
    $.ajax({
      url: path + '.shx',
      mimeType: 'text/plain; charset=x-user-defined',
      success: function (data) {
        var indices = this.loadShx(data);
        $.ajax({
          url: path + '.shp',
          mimeType: 'text/plain; charset=x-user-defined',
          success: function (data) {
            $.ajax({
              url: path + '.dbf',
              mimeType: 'text/plain; charset=x-user-defined',
              success: function (dbf_data) {
                var layer = this.loadShp(data, dbf_data, indices, options);
                options.success(layer);
              }
            });
          }
        });
      }
    });
  };

  this.localShapefile = function (options) {
    var shxFile = options.shx;
    var shpFile = options.shp;
    var dbfFile = options.dbf;
    var shxReader = new FileReader();
    shxReader.onloadend = function () {
      var indices = m_that.loadShx(shxReader.result);
      var shpReader = new FileReader();

      shpReader.onloadend = function () {
        var shpData = shpReader.result;

        var dbfReader = new FileReader();
        dbfReader.onloadend = function () {
          var dbfData = dbfReader.result;
          var layer = m_that.loadShp(shpData, dbfData, indices, options);
          options.success(layer);
        };
        dbfReader.readAsBinaryString(dbfFile);
      };
      shpReader.readAsBinaryString(shpFile);
    };
    shxReader.readAsBinaryString(shxFile);
  };

  this.loadDBF = function (data) {
    var readHeader = function (offset) {
      var name = m_that.str(data, offset, 10);
      var type = m_that.str(data, offset + 11, 1);
      var length = m_that.int8(data, offset + 16);
      return {
        name: name,
        type: type,
        length: length
      };
    };

    // Level of the dBASE file
    var level = m_that.int8(data, 0);
    if (level === 4) {
      throw 'Level 7 dBASE not supported';
    }

    // Date of last update
    /*
    var year = m_that.int8(data, 1);
    var month = m_that.int8(data, 2);
    var day = m_that.int8(data, 3);
    */

    var num_entries = m_that.lint32(data, 4);
    var header_size = m_that.lint16(data, 8);
    var record_size = m_that.lint16(data, 10);

    var FIELDS_START = 32;
    var HEADER_LENGTH = 32;

    var header_offset = FIELDS_START;
    var headers = [];
    while (header_offset < header_size - 1) {
      headers.push(readHeader(header_offset));
      header_offset += HEADER_LENGTH;
    }

    var records = [];
    var record_offset = header_size;
    while (record_offset < header_size + num_entries * record_size) {
      var declare = m_that.str(data, record_offset, 1);
      if (declare === '*') {
        // Record size in the header include the size of the delete indicator
        record_offset += record_size;
      } else {
        // Move offset to the start of the actual data
        record_offset += 1;
        var record = {};
        for (var i = 0; i < headers.length; i += 1) {
          var header = headers[i];
          var value;
          if (header.type === 'C') {
            value = m_that.str(data, record_offset, header.length).trim();
          } else if (header.type === 'N') {
            value = parseFloat(m_that.str(data, record_offset, header.length));
          }
          record_offset += header.length;
          record[header.name] = value;
        }
        records.push(record);
      }
    }
    return records;
  };

  this.loadShp = function (data, dbf_data, indices, options) {
    options = options; /* unused parameter */
    var features = [];
    var readRing = function (offset, start, end) {
      var ring = [];
      for (var i = end - 1; i >= start; i -= 1) {
        var x = m_that.ldbl64(data, offset + 16 * i);
        var y = m_that.ldbl64(data, offset + 16 * i + 8);
        ring.push([x, y]);
      }
      //if (ring.length <= 3)
      // return [];
      return ring;
    };

    var readRecord = function (offset) {
      // var index = m_that.bint32(data, offset);
      // var record_length = m_that.bint32(data, offset + 4);
      var record_offset = offset + 8;
      var geom_type = m_that.lint32(data, record_offset);
      var num_parts, num_points, parts_start, points_start, i,
          start, end, ring, rings;

      if (geom_type === SHP_NULL) {
        console.log('NULL Shape');
        //return offset + 12;
      } else if (geom_type === SHP_POINT) {
        var x = m_that.ldbl64(data, record_offset + 4);
        var y = m_that.ldbl64(data, record_offset + 12);

        features.push({
          type: 'Point',
          attr: {},
          geom: [[x, y]]
        });
      } else if (geom_type === SHP_POLYGON) {
        num_parts = m_that.lint32(data, record_offset + 36);
        num_points = m_that.lint32(data, record_offset + 40);

        parts_start = offset + 52;
        points_start = offset + 52 + 4 * num_parts;

        rings = [];
        for (i = 0; i < num_parts; i += 1) {
          start = m_that.lint32(data, parts_start + i * 4);
          if (i + 1 < num_parts) {
            end = m_that.lint32(data, parts_start + (i + 1) * 4);
          } else {
            end = num_points;
          }
          ring = readRing(points_start, start, end);
          rings.push(ring);
        }
        features.push({
          type: 'Polygon',
          attr: {},
          geom: [rings]
        });
      } else if (geom_type === SHP_POLYLINE) {
        num_parts = m_that.lint32(data, record_offset + 36);
        num_points = m_that.lint32(data, record_offset + 40);

        parts_start = offset + 52;
        points_start = offset + 52 + 4 * num_parts;

        rings = [];
        for (i = 0; i < num_parts; i += 1) {
          start = m_that.lint32(data, parts_start + i * 4);
          if (i + 1 < num_parts) {
            end = m_that.lint32(data, parts_start + (i + 1) * 4);
          } else {
            end = num_points;
          }
          ring = readRing(points_start, start, end);
          rings.push(ring);
        }
        features.push({
          type: 'Polyline',
          attr: {},
          geom: [rings]
        });
      } else {
        throw 'Not Implemented: ' + geom_type;
      }
      //return offset + 2 * record_length + SHP_HEADER_LEN;
    };

    var attr = this.loadDBF(dbf_data), i;

    //var offset = 100;
    //while (offset < length * 2) {
    // offset = readRecord (offset);
    //}
    for (i = 0; i < indices.length; i += 1) {
      var offset = indices[i];
      readRecord(offset);
    }

    var layer = []; //new Layer ();

    for (i = 0; i < features.length; i += 1) {
      var feature = features[i];
      feature.attr = attr[i];
      layer.push(feature);
    }
    return layer;
  };

  return this;
};

//////////////////////////////////////////////////////////////////////////////
/**
 * @module vgl
 */

/*global vgl, mat4, unescape, Float32Array, Int8Array, Uint16Array*/
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//
// vbgModule.vtkReader class
// This contains code that unpack a json base64 encoded vtkdataset,
// such as those produced by ParaView's webGL exporter (where much
// of the code originated from) and convert it to VGL representation.
//
//////////////////////////////////////////////////////////////////////////////

vgl.vtkReader = function () {
  'use strict';

  if (!(this instanceof vgl.vtkReader)) {
    return new vgl.vtkReader();
  }

  var m_base64Chars =
    ['A', 'B', 'C', 'D', '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', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
     'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
     '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'],
      m_reverseBase64Chars = [],
      m_vtkRenderedList = {},
      m_vtkObjectCount = 0,
      m_vtkScene = null,
      m_node = null,
      END_OF_INPUT = -1,
      m_base64Str = '',
      m_base64Count = 0,
      m_pos = 0,
      m_viewer = null,
      i = 0;

  //initialize the array here if not already done.
  if (m_reverseBase64Chars.length === 0) {
    for (i = 0; i < m_base64Chars.length; i += 1) {
      m_reverseBase64Chars[m_base64Chars[i]] = i;
    }
  }

  ////////////////////////////////////////////////////////////////////////////
  /**
   * ntos
   *
   * @param n
   * @returns unescaped n
   */
  ////////////////////////////////////////////////////////////////////////////
  this.ntos = function (n) {
    var unN;

    unN = n.toString(16);
    if (unN.length === 1) {
      unN = '0' + unN;
    }
    unN = '%' + unN;

    return unescape(unN);
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * readReverseBase64
   *
   * @returns
   */
  ////////////////////////////////////////////////////////////////////////////
  this.readReverseBase64 = function () {
    var nextCharacter;

    if (!m_base64Str) {
      return END_OF_INPUT;
    }

    while (true) {
      if (m_base64Count >= m_base64Str.length) {
        return END_OF_INPUT;
      }
      nextCharacter = m_base64Str.charAt(m_base64Count);
      m_base64Count += 1;

      if (m_reverseBase64Chars[nextCharacter]) {
        return m_reverseBase64Chars[nextCharacter];
      }
      if (nextCharacter === 'A') {
        return 0;
      }
    }

    return END_OF_INPUT;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * decode64
   *
   * @param str
   * @returns result
   */
  ////////////////////////////////////////////////////////////////////////////
  this.decode64 = function (str) {
    var result = '',
        inBuffer = new Array(4),
        done = false;

    m_base64Str = str;
    m_base64Count = 0;

    while (!done &&
           (inBuffer[0] = this.readReverseBase64()) !== END_OF_INPUT &&
           (inBuffer[1] = this.readReverseBase64()) !== END_OF_INPUT) {
      inBuffer[2] = this.readReverseBase64();
      inBuffer[3] = this.readReverseBase64();
      /*jshint bitwise: false */
      result += this.ntos((((inBuffer[0] << 2) & 0xff) | inBuffer[1] >> 4));
      if (inBuffer[2] !== END_OF_INPUT) {
        result += this.ntos((((inBuffer[1] << 4) & 0xff) | inBuffer[2] >> 2));
        if (inBuffer[3] !== END_OF_INPUT) {
          result += this.ntos((((inBuffer[2] << 6) & 0xff) | inBuffer[3]));
        } else {
          done = true;
        }
      } else {
        done = true;
      }
      /*jshint bitwise: true */
    }

    return result;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * readNumber
   *
   * @param ss
   * @returns v
   */
  ////////////////////////////////////////////////////////////////////////////
  this.readNumber = function (ss) {
    //jshint plusplus: false, bitwise: false
    var v = ((ss[m_pos++]) +
             (ss[m_pos++] << 8) +
             (ss[m_pos++] << 16) +
             (ss[m_pos++] << 24));
    //jshint plusplus: true, bitwise: true
    return v;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * readF3Array
   *
   * @param numberOfPoints
   * @param ss
   * @returns points
   */
  ////////////////////////////////////////////////////////////////////////////
  this.readF3Array = function (numberOfPoints, ss) {
    var size = numberOfPoints * 4 * 3, test = new Int8Array(size),
        points = null, i;

    for (i = 0; i < size; i += 1) {
      test[i] = ss[m_pos];
      m_pos += 1;
    }

    points = new Float32Array(test.buffer);

    return points;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * readColorArray
   *
   * @param numberOfPoints
   * @param ss
   * @param vglcolors
   * @returns points
   */
  ////////////////////////////////////////////////////////////////////////////
  this.readColorArray = function (numberOfPoints, ss, vglcolors) {
    var i, idx = 0, tmp = new Array(numberOfPoints * 3);
    //jshint plusplus: false
    for (i = 0; i < numberOfPoints; i += 1) {
      tmp[idx++] = ss[m_pos++] / 255.0;
      tmp[idx++] = ss[m_pos++] / 255.0;
      tmp[idx++] = ss[m_pos++] / 255.0;
      m_pos++;
    }
    //jshint plusplus: true
    vglcolors.insert(tmp);
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * parseObject
   *
   * @param buffer
   */
  ////////////////////////////////////////////////////////////////////////////
  this.parseObject = function (vtkObject) {
    var geom = new vgl.geometryData(), mapper = vgl.mapper(), ss = [],
        type = null, data = null, size, matrix = null, material = null,
        actor, colorMapData, shaderProg, opacityUniform, lookupTable,
        colorTable, windowSize, width, height, position;

    //dehexlify
    //data = this.decode64(vtkObject.data);
    data = atob(vtkObject.data);
    //jshint bitwise: false
    for (i = 0; i < data.length; i += 1) {
      ss[i] = data.charCodeAt(i) & 0xff;
    }
    //jshint bitwise: true

    //Determine the Object type
    m_pos = 0;
    size = this.readNumber(ss);
    type = String.fromCharCode(ss[m_pos]);
    m_pos += 1;
    geom.setName(type);

    // Lines
    if (type === 'L') {
      matrix = this.parseLineData(geom, ss);
      material = vgl.utils.createGeometryMaterial();
    // Mesh
    } else if (type === 'M') {
      matrix = this.parseMeshData(geom, ss);
      material = vgl.utils.createPhongMaterial();
    // Points
    } else if (type === 'P') {
      matrix = this.parsePointData(geom, ss);
      material = vgl.utils.createGeometryMaterial();
    // ColorMap
    } else if (type === 'C') {
      colorMapData = this.parseColorMapData(geom, ss, size);
      colorTable = [];

      for (i = 0; i < colorMapData.colors.length; i += 1) {
        colorTable.push(colorMapData.colors[i][1]);
        colorTable.push(colorMapData.colors[i][2]);
        colorTable.push(colorMapData.colors[i][3]);
        colorTable.push(colorMapData.colors[i][0] * 255);
      }

      lookupTable = new vgl.lookupTable();
      lookupTable.setColorTable(colorTable);

      windowSize = m_viewer.renderWindow().windowSize();
      width = colorMapData.size[0] * windowSize[0];
      height = colorMapData.size[1] * windowSize[1];

      position = [colorMapData.position[0] * windowSize[0],
                  (1 - colorMapData.position[1]) * windowSize[1], 0];
      position[1] = position[1] - height;

      // For now hardcode the height
      height = 30;

      return vgl.utils.createColorLegend(colorMapData.title,
          lookupTable, position, width, height, 3, 0);
    // Unknown
    } else {
      console.log('Ignoring unrecognized encoded data type ' + type);
    }

    mapper.setGeometryData(geom);

    //default opacity === solid. If were transparent, set it lower.
    if (vtkObject.hasTransparency) {
      shaderProg = material.shaderProgram();
      opacityUniform = shaderProg.uniform('opacity');
      console.log('opacity ', vtkObject.opacity);
      opacityUniform.set(vtkObject.opacity);
      material.setBinNumber(1000);
    }

    actor = vgl.actor();
    actor.setMapper(mapper);
    actor.setMaterial(material);
    actor.setMatrix(mat4.transpose(mat4.create(), matrix));

    return [actor];
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * parseLineData
   *
   * @param geom, ss
   * @returns matrix
   */
  ////////////////////////////////////////////////////////////////////////////
  this.parseLineData = function (geom, ss) {
    var vglpoints = null, vglcolors = null, vgllines = null,
        matrix = mat4.create(),
        numberOfIndex, numberOfPoints, points,
        temp, index, size, m, i,
        p = null, idx = 0;

    numberOfPoints = this.readNumber(ss);
    p = new Array(numberOfPoints * 3);

    //Getting Points
    vglpoints = new vgl.sourceDataP3fv();
    points = this.readF3Array(numberOfPoints, ss);

    //jshint plusplus: false
    for (i = 0; i < numberOfPoints; i += 1) {
      p[idx++] = points[i * 3];
      p[idx++] = points[i * 3 + 1];
      p[idx++] = points[i * 3 + 2];
    }
    //jshint plusplus: true
    vglpoints.insert(p);
    geom.addSource(vglpoints);

    //Getting Colors
    vglcolors = new vgl.sourceDataC3fv();
    this.readColorArray(numberOfPoints, ss, vglcolors);
    geom.addSource(vglcolors);

    //Getting connectivity
    vgllines = new vgl.lines();
    geom.addPrimitive(vgllines);
    numberOfIndex = this.readNumber(ss);

    temp = new Int8Array(numberOfIndex * 2);
    for (i = 0; i < numberOfIndex * 2; i += 1) {
      temp[i] = ss[m_pos];
      m_pos += 1;
    }

    index = new Uint16Array(temp.buffer);
    vgllines.setIndices(index);
    vgllines.setPrimitiveType(vgl.GL.LINES);

    //Getting Matrix
    size = 16 * 4;
    temp = new Int8Array(size);
    for (i = 0; i < size; i += 1) {
      temp[i] = ss[m_pos];
      m_pos += 1;
    }

    m = new Float32Array(temp.buffer);
    mat4.copy(matrix, m);

    return matrix;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * parseMeshData
   *
   * @param geom, ss
   * @returns matrix
   */
  ////////////////////////////////////////////////////////////////////////////
  this.parseMeshData = function (geom, ss) {
    var vglpoints = null, vglcolors = null,
        normals = null, matrix = mat4.create(),
        vgltriangles = null, numberOfIndex, numberOfPoints,
        points, temp, index, size, m, i,
        pn = null, idx = 0;

    numberOfPoints = this.readNumber(ss);
    pn = new Array(numberOfPoints * 6);
    //Getting Points
    vglpoints = new vgl.sourceDataP3N3f();
    points = this.readF3Array(numberOfPoints, ss);

    //Getting Normals
    normals = this.readF3Array(numberOfPoints, ss);
    //jshint plusplus: false
    for (i = 0; i < numberOfPoints; i += 1) {
      pn[idx++] = points[i * 3];
      pn[idx++] = points[i * 3 + 1];
      pn[idx++] = points[i * 3 + 2];
      pn[idx++] = normals[i * 3];
      pn[idx++] = normals[i * 3 + 1];
      pn[idx++] = normals[i * 3 + 2];
    }
    //jshint plusplus: true
    vglpoints.insert(pn);
    geom.addSource(vglpoints);

    //Getting Colors
    vglcolors = new vgl.sourceDataC3fv();
    this.readColorArray(numberOfPoints, ss, vglcolors);
    geom.addSource(vglcolors);

    //Getting connectivity
    temp = [];
    vgltriangles = new vgl.triangles();
    numberOfIndex = this.readNumber(ss);

    temp = new Int8Array(numberOfIndex * 2);
    for (i = 0; i < numberOfIndex * 2; i += 1) {
      temp[i] = ss[m_pos];
      m_pos += 1;
    }

    index = new Uint16Array(temp.buffer);
    vgltriangles.setIndices(index);
    geom.addPrimitive(vgltriangles);

    //Getting Matrix
    size = 16 * 4;
    temp = new Int8Array(size);
    for (i = 0; i < size; i += 1) {
      temp[i] = ss[m_pos];
      m_pos += 1;
    }

    m = new Float32Array(temp.buffer);
    mat4.copy(matrix, m);

    return matrix;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * parsePointData
   *
   * @param geom, ss
   * @returns matrix
   */
  ////////////////////////////////////////////////////////////////////////////
  this.parsePointData = function (geom, ss) {
    var numberOfPoints, points, indices, temp, size,
        matrix = mat4.create(), vglpoints = null,
        vglcolors = null, vglVertexes = null, m,
        p = null, idx = 0;

    numberOfPoints = this.readNumber(ss);
    p = new Array(numberOfPoints * 3);

    //Getting Points and creating 1:1 connectivity
    vglpoints = new vgl.sourceDataP3fv();
    points = this.readF3Array(numberOfPoints, ss);

    indices = new Uint16Array(numberOfPoints);

    //jshint plusplus: false
    for (i = 0; i < numberOfPoints; i += 1) {
      indices[i] = i;
      p[idx++] = points[i * 3];
      p[idx++] = points[i * 3 + 1];
      p[idx++] = points[i * 3 + 2];
    }
    //jshint plusplus: true
    vglpoints.insert(p);
    geom.addSource(vglpoints);

    //Getting Colors
    vglcolors = new vgl.sourceDataC3fv();
    this.readColorArray(numberOfPoints, ss, vglcolors);
    geom.addSource(vglcolors);

    //Getting connectivity
    vglVertexes = new vgl.points();
    vglVertexes.setIndices(indices);
    geom.addPrimitive(vglVertexes);

    //Getting matrix
    size = 16 * 4;
    temp = new Int8Array(size);
    for (i = 0; i < size; i += 1) {
      temp[i] = ss[m_pos];
      m_pos += 1;
    }

    m = new Float32Array(temp.buffer);
    mat4.copy(matrix, m);

    return matrix;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * parseColorMapData
   *
   * @param geom, ss
   * @returns matrix
   */
  ////////////////////////////////////////////////////////////////////////////
  this.parseColorMapData = function (geom, ss, numColors) {

    var tmpArray, size, xrgb, i, c, obj = {};

    // Set number of colors
    obj.numOfColors = numColors;

    // Getting Position
    size = 8;
    tmpArray = new Int8Array(size);
    for (i = 0; i < size; i += 1) {
      tmpArray[i] = ss[m_pos];
      m_pos += 1;
    }
    obj.position = new Float32Array(tmpArray.buffer);

    // Getting Size
    size = 8;
    tmpArray = new Int8Array(size);
    for (i = 0; i < size; i += 1) {
      tmpArray[i] = ss[m_pos];
      m_pos += 1;
    }
    obj.size = new Float32Array(tmpArray.buffer);

    //Getting Colors
    obj.colors = [];
    //jshint plusplus: false
    for (c = 0; c < obj.numOfColors; c += 1) {
      tmpArray = new Int8Array(4);
      for (i = 0; i < 4; i += 1) {
        tmpArray[i] = ss[m_pos];
        m_pos += 1;
      }

      xrgb = [
        new Float32Array(tmpArray.buffer)[0],
        ss[m_pos++],
        ss[m_pos++],
        ss[m_pos++]
      ];
      obj.colors[c] = xrgb;
    }

    obj.orientation = ss[m_pos++];
    obj.numOfLabels = ss[m_pos++];
    obj.title = '';
    while (m_pos < ss.length) {
      obj.title += String.fromCharCode(ss[m_pos++]);
    }
    //jshint plusplus: true

    return obj;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * parseSceneMetadata
   *
   * @param data
   * @returns renderer
   */
  ////////////////////////////////////////////////////////////////////////////
  this.parseSceneMetadata = function (renderer, layer) {

    var sceneRenderer = m_vtkScene.Renderers[layer],
        camera = renderer.camera(), bgc, localWidth, localHeight;

    localWidth = (sceneRenderer.size[0] - sceneRenderer.origin[0]) * m_node.width;
    localHeight = (sceneRenderer.size[1] - sceneRenderer.origin[1]) * m_node.height;
    renderer.resize(localWidth, localHeight);

    /// We are setting the center to the focal point because of
    /// a possible paraview web bug. The center of rotation isn't
    /// getting updated correctly on resetCamera.
    camera.setCenterOfRotation(
      [sceneRenderer.LookAt[1], sceneRenderer.LookAt[2],
       sceneRenderer.LookAt[3]]);
    camera.setViewAngleDegrees(sceneRenderer.LookAt[0]);
    camera.setPosition(
      sceneRenderer.LookAt[7], sceneRenderer.LookAt[8],
      sceneRenderer.LookAt[9]);
    camera.setFocalPoint(
      sceneRenderer.LookAt[1], sceneRenderer.LookAt[2],
      sceneRenderer.LookAt[3]);
    camera.setViewUpDirection(
      sceneRenderer.LookAt[4], sceneRenderer.LookAt[5],
      sceneRenderer.LookAt[6]);

    if (layer === 0) {
      bgc = sceneRenderer.Background1;
      renderer.setBackgroundColor(bgc[0], bgc[1], bgc[2], 1);
    } else {
      renderer.setResizable(false);
    }
    renderer.setLayer(layer);
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * initScene
   *
   * @returns viewer
   */
  ////////////////////////////////////////////////////////////////////////////
  this.initScene = function () {
    var renderer, layer;

    if (m_vtkScene === null) {
      return m_viewer;
    }
    for (layer = m_vtkScene.Renderers.length - 1; layer >= 0; layer -= 1) {

      renderer = this.getRenderer(layer);
      this.parseSceneMetadata(renderer, layer);
    }

    return m_viewer;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * createViewer - Creates a viewer object.
   *
   * @param
   *
   * @returns viewer
   */
  ////////////////////////////////////////////////////////////////////////////
  this.createViewer = function (node) {
    var interactorStyle;

    if (m_viewer === null) {
      m_node = node;
      m_viewer = vgl.viewer(node);
      m_viewer.init();
      m_viewer.renderWindow().removeRenderer(m_viewer.renderWindow().activeRenderer());
      m_viewer.renderWindow().addRenderer(new vgl.depthPeelRenderer());
      m_vtkRenderedList[0] = m_viewer.renderWindow().activeRenderer();
      m_viewer.renderWindow().resize(node.width, node.height);
      interactorStyle = vgl.pvwInteractorStyle();
      m_viewer.setInteractorStyle(interactorStyle);
    }

    return m_viewer;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * deleteViewer - Deletes the viewer object associated with the reader.
   *
   * @returns void
   */
  ////////////////////////////////////////////////////////////////////////////
  this.deleteViewer = function () {
    m_vtkRenderedList = {};
    m_viewer = null;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * updateCanvas -
   *
   * @param
   *
   * @returns void
   */
  ////////////////////////////////////////////////////////////////////////////
  this.updateCanvas = function (node) {
    m_node = node;
    m_viewer.renderWindow().resize(node.width, node.height);

    return m_viewer;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * clearVtkObjectData - Clear out the list of VTK geometry data.
   *
   * @param void
   * @returns void
   */
  ////////////////////////////////////////////////////////////////////////////
  this.numObjects = function () {
    return m_vtkObjectCount;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * getRenderer - Gets (or creates) the renderer for a layer.
   *
   * @param layer
   * @returns renderer
   */
  ////////////////////////////////////////////////////////////////////////////
  this.getRenderer = function (layer) {
    var renderer;

    renderer = m_vtkRenderedList[layer];
    if (renderer === null || typeof renderer === 'undefined') {
      renderer = new vgl.renderer();
      renderer.setResetScene(false);
      renderer.setResetClippingRange(false);
      m_viewer.renderWindow().addRenderer(renderer);

      if (layer !== 0) {
        renderer.camera().setClearMask(vgl.GL.DepthBufferBit);
      }

      m_vtkRenderedList[layer] = renderer;
    }

    return renderer;
  };

  ////////////////////////////////////////////////////////////////////////////
  /**
   * setVtkScene - Set the VTK scene data for camera initialization.
   *
   * @param scene
   * @returns void
   */
  ////////////////////////////////////////////////////////////////////////////
  this.setVtkScene = function (scene) {
    m_vtkScene = scene;
  };

  return this;
};

vgl.DataBuffers = function (initialSize) {
  'use strict';
  if (!(this instanceof vgl.DataBuffers)) {
    return new vgl.DataBuffers(initialSize);
  }

  var data = {};

  var size;
  if (!initialSize && initialSize !== 0) {
    size = 256;
  } else {
    size = initialSize;
  }

  var current = 0;

  var copyArray = function (dst, src, start, count) {
    if (!dst) {
      throw 'No destination';
    }
    if (!start) {
      start = 0;
    }
    if (!count) {
      count = src.length;
    }
    for (var i = 0; i < count; i += 1) {
      dst[start + i] = src[i];
    }
  };

  var resize = function (min_expand) {
    var new_size = size;
    /* If the array would increase substantially, don't just double its
     * size.  If the array has been increasing gradually, double it as the
     * expectation is that it will increase again. */
    if (new_size * 2 < min_expand) {
      new_size = min_expand;
    }
    while (new_size < min_expand) {
      new_size *= 2;
    }
    size = new_size;
    for (var name in data) {
      if (data.hasOwnProperty(name)) {
        var newArray = new Float32Array(new_size * data[name].len);
        var oldArray = data[name].array;
        copyArray(newArray, oldArray);
        data[name].array = newArray;
        data[name].dirty = true;
      }
    }
  };

  /**
   * Allocate a buffer with a name and a specific number of components per
   * entry.  If a buffer with the specified name already exists, it will be
   * overwritten.
   *
   * @param name: the name of the buffer to create or replace.
   * @param len: number of components per entry.  Most be a positive integer.
   */
  this.create = function (name, len) {
    if (!len || len < 0) {
      throw 'Length of buffer must be a positive integer';
    }
    var array = new Float32Array(size * len);
    data[name] = {
      array: array,
      len: len,
      dirty: false
    };
    return data[name].array;
  };

  this.alloc = function (num) {
    if ((current + num) >= size) {
      resize(current + num);
    }
    var start = current;
    current += num;
    return start;
  };

  this.get = function (name) {
    return data[name].array;
  };

  this.write = function (name, array, start, count) {
    if (start + count > size) {
      throw 'Write would exceed buffer size';
    }
    copyArray(data[name].array, array, start * data[name].len, count * data[name].len);
    data[name].dirty = true;
  };

  this.repeat = function (name, elem, start, count) {
    if (start + count > size) {
      throw 'Repeat would exceed buffer size';
    }
    for (var i = 0; i < count; i += 1) {
      copyArray(data[name].array, elem,
                 (start + i) * data[name].len, data[name].len);
    }
    data[name].dirty = true;
  };

  this.count = function () {
    return current;
  };

  this.data = function (name) {
    return data[name].array;
  };
};

////////////////////////////////////////////////////////////////////////////
/**
 * Create a new instance of class renderer *
 *
 * @returns {vgl.renderer}
 */
////////////////////////////////////////////////////////////////////////////

vgl.depthPeelRenderer = function () {
  'use strict';

  if (!(this instanceof vgl.depthPeelRenderer)) {
    return new vgl.depthPeelRenderer();
  }
  vgl.renderer.call(this);

  var m_this = this, fbo = [], texID = [], depthTexID = [],
      colorBlenderTexID, colorBlenderFBOID, setupTime = vgl.timestamp(),
      fpMaterial = vgl.material(), blMaterial = vgl.material(),
      fiMaterial = vgl.material(), frontPeelShader = null, blendShader = null,
      finalShader, NUM_PASSES = 6, m_quad = null, fpwidth, fpheight, blwidth, blheight,
      fiwidth, fiheight, fpopacity, fibackgroundColor;

  function drawFullScreenQuad(renderState, material) {
    m_quad.setMaterial(material);

    renderState.m_mapper = m_quad.mapper();
    renderState.m_material = material;

    renderState.m_material.bind(renderState);
    renderState.m_mapper.render(renderState);
    renderState.m_material.undoBind(renderState);

    m_quad.setMaterial(null);
  }

  function initScreenQuad(renderState, width, height) {
    console.log(width);
    console.log(height);
    m_quad = vgl.utils.createPlane(0.0, 0.0, 0.0,
                                   1.0, 0.0, 0.0,
                                   0.0, 1.0, 0.0);
  }

  function initShaders(renderState) {
    var fpmv, fpproj, fpvertex, fpcolor, fpdepthTex, fpnormal, fpnr,
        blvertex, blColorSamp, blPrevDepthSamp, blCurrDepthSamp,
        fivertex, fitempTex;

    // Load the front to back peeling shader
    fpvertex = new vgl.vertexAttribute('vertexPosition');
    fpnormal = new vgl.vertexAttribute('vertexNormal');
    fpcolor = new vgl.vertexAttribute('vertexColor');
    fpmv = new vgl.modelViewUniform('modelViewMatrix');
    fpnr = new vgl.modelViewUniform('normalMatrix');
    fpproj = new vgl.projectionUniform('projectionMatrix');
    fpwidth = new vgl.floatUniform('width');
    fpheight = new vgl.floatUniform('height');
    fpopacity = new vgl.floatUniform('opacity', 1.0);
    fpdepthTex = new vgl.uniform(vgl.GL.INT, 'depthTexture');
    fpdepthTex.set(0);

    frontPeelShader = new vgl.shaderProgram();
    frontPeelShader.loadShader(vgl.GL.VERTEX_SHADER, 'front_peel.vert');
    frontPeelShader.loadShader(vgl.GL.FRAGMENT_SHADER, 'front_peel.frag');

    frontPeelShader.addUniform(fpmv);
    frontPeelShader.addUniform(fpnr);
    frontPeelShader.addUniform(fpproj);
    frontPeelShader.addUniform(fpdepthTex);
    frontPeelShader.addUniform(fpwidth);
    frontPeelShader.addUniform(fpheight);
    frontPeelShader.addUniform(fpopacity);
    frontPeelShader.addVertexAttribute(fpvertex, vgl.vertexAttributeKeys.Position);
    frontPeelShader.addVertexAttribute(fpnormal, vgl.vertexAttributeKeys.Normal);
    frontPeelShader.addVertexAttribute(fpcolor, vgl.vertexAttributeKeys.Color);

    // Compile and link the shader
    frontPeelShader.compileAndLink(renderState);

    fpMaterial.addAttribute(frontPeelShader);

    //     //add attributes and uniforms
    //     frontPeelShader.AddAttribute('vVertex');
    //     frontPeelShader.AddUniform('MVP');
    //     frontPeelShader.AddUniform('vColor');
    //     frontPeelShader.AddUniform('depthTexture');
    //     //pass constant uniforms at initialization
    //     glUniform1i(frontPeelShader('depthTexture'), 0);
    // frontPeelShader.UnUse();

    // Load the blending shader
    blendShader = new vgl.shaderProgram();
    blendShader.loadShader(vgl.GL.VERTEX_SHADER, 'blend.vert');
    blendShader.loadShader(vgl.GL.FRAGMENT_SHADER, 'blend.frag');
    blColorSamp = new vgl.uniform(vgl.GL.INT, 'currColorTexture');
    blPrevDepthSamp = new vgl.uniform(vgl.GL.INT, 'prevDepthTexture');
    blCurrDepthSamp = new vgl.uniform(vgl.GL.INT, 'currDepthTexture');

    blwidth = new vgl.floatUniform('width');
    blheight = new vgl.floatUniform('height');
    blColorSamp.set(0);
    blPrevDepthSamp.set(1);
    blCurrDepthSamp.set(2);

    blvertex = new vgl.vertexAttribute('vertexPosition');

    blendShader.addUniform(blColorSamp);
    blendShader.addUniform(blPrevDepthSamp);
    blendShader.addUniform(blPrevDepthSamp);
    blendShader.addUniform(blwidth);
    blendShader.addUniform(blheight);
    blendShader.addVertexAttribute(blvertex,
      vgl.vertexAttributeKeys.Position);

    // Compile and link the shader
    blendShader.compileAndLink(renderState);
    blMaterial.addAttribute(blendShader);

    //     //add attributes and uniforms
    //     blendShader.AddAttribute('vVertex');
    //     blendShader.AddUniform('currColorTexture');
    //     //pass constant uniforms at initialization
    //     glUniform1i(blendShader('currColorTexture'), 0);
    // blendShader.UnUse();

    //Load the final shader
    finalShader = new vgl.shaderProgram();
    finalShader.loadShader(vgl.GL.VERTEX_SHADER, 'blend.vert');
    finalShader.loadShader(vgl.GL.FRAGMENT_SHADER, 'final.frag');

    //fimv = new vgl.modelViewUniform('modelViewMatrix');
    //fiproj = new vgl.projectionUniform('projectionMatrix');
    fitempTex = new vgl.uniform(vgl.GL.INT, 'colorTexture');
    fiwidth = new vgl.floatUniform('width');
    fiheight = new vgl.floatUniform('height');
    fibackgroundColor = new vgl.uniform(vgl.GL.FLOAT_VEC3, 'backgroundColor');
    fitempTex.set(0);
    fivertex = new vgl.vertexAttribute('vertexPosition');

    //finalShader.addUniform(fimv);
    //finalShader.addUniform(fiproj);
    finalShader.addUniform(fitempTex);
    finalShader.addUniform(fiwidth);
    finalShader.addUniform(fiheight);
    finalShader.addUniform(fibackgroundColor);
    finalShader.addVertexAttribute(fivertex, vgl.vertexAttributeKeys.Position);
    finalShader.compileAndLink(renderState);
    fiMaterial.addAttribute(finalShader);
  }

  function initFBO(renderState, WIDTH, HEIGHT) {
    var i, textureFloatExt, textureFloatLinearExt, depthTextureExt, filtering;

    // Or browser-appropriate prefix
    depthTextureExt = renderState.m_context.getExtension('WEBKIT_WEBGL_depth_texture');
    if (!depthTextureExt) {
      depthTextureExt = renderState.m_context.getExtension('WEBGL_depth_texture');

      if (!depthTextureExt) {
        console.log('Depth textures are not supported');
      }
    }

    var floatTextureExt = renderState.m_context.getExtension('OES_texture_float');
    if (!floatTextureExt) {
      console.log('float textures are not supported');
    }

    textureFloatExt = renderState.m_context.getExtension('OES_texture_float');
    if (!textureFloatExt) {
      console.log('Extension Texture Float is not working');
      window.alert(':( Sorry, Your browser doesn\'t support texture float extension.');
      return;
    }
    textureFloatLinearExt = renderState.m_context.getExtension(
        'OES_texture_float_linear');
    depthTextureExt = renderState.m_context.getExtension('WEBGL_depth_texture');

    if (!depthTextureExt) {
      console.log('Extension Depth texture is not working');
      window.alert(':( Sorry, Your browser doesn\'t support depth texture extension.');
      return;
    }

    filtering = textureFloatLinearExt ? vgl.GL.LINEAR : vgl.GL.NEAREST;

    //FBO initialization function
    // Generate 2 FBO
    fbo.push(renderState.m_context.createFramebuffer());
    fbo.push(renderState.m_context.createFramebuffer());

    // Create two textures
    texID.push(renderState.m_context.createTexture());
    texID.push(renderState.m_context.createTexture());

    //The FBO has two depth attachments
    depthTexID.push(renderState.m_context.createTexture());
    depthTexID.push(renderState.m_context.createTexture());

    // For each attachment
    for (i = 0; i < 2; i += 1) {
      // First initialize the depth texture
      renderState.m_context.bindTexture(vgl.GL.TEXTURE_2D, depthTexID[i]);
      renderState.m_context.texParameteri(vgl.GL.TEXTURE_2D,
        vgl.GL.TEXTURE_MAG_FILTER, vgl.GL.NEAREST);
      renderState.m_context.texParameteri(vgl.GL.TEXTURE_2D,
        vgl.GL.TEXTURE_MIN_FILTER, vgl.GL.NEAREST);
      renderState.m_context.texParameteri(vgl.GL.TEXTURE_2D,
        vgl.GL.TEXTURE_WRAP_S, vgl.GL.CLAMP_TO_EDGE);
      renderState.m_context.texParameteri(vgl.GL.TEXTURE_2D,
        vgl.GL.TEXTURE_WRAP_T, vgl.GL.CLAMP_TO_EDGE);
      renderState.m_context.texImage2D(vgl.GL.TEXTURE_2D, 0,
        vgl.GL.DEPTH_COMPONENT, WIDTH, HEIGHT, 0, vgl.GL.DEPTH_COMPONENT,
        vgl.GL.UNSIGNED_SHORT, null);

      // Second initialize the color attachment
      renderState.m_context.bindTexture(vgl.GL.TEXTURE_2D, texID[i]);
      renderState.m_context.texParameteri(vgl.GL.TEXTURE_2D,
        vgl.GL.TEXTURE_MAG_FILTER, filtering);
      renderState.m_context.texParameteri(vgl.GL.TEXTURE_2D,
        vgl.GL.TEXTURE_MIN_FILTER, filtering);
      renderState.m_context.texParameteri(vgl.GL.TEXTURE_2D,
        vgl.GL.TEXTURE_WRAP_S, vgl.GL.CLAMP_TO_EDGE);
      renderState.m_context.texParameteri(vgl.GL.TEXTURE_2D,
        vgl.GL.TEXTURE_WRAP_T, vgl.GL.CLAMP_TO_EDGE);
      renderState.m_context.texImage2D(vgl.GL.TEXTURE_2D, 0, vgl.GL.RGBA,
        WIDTH, HEIGHT, 0, vgl.GL.RGBA, vgl.GL.FLOAT, null);

      // Bind FBO and attach the depth and color attachments
      renderState.m_context.bindFramebuffer(vgl.GL.FRAMEBUFFER, fbo[i]);
      renderState.m_context.framebufferTexture2D(vgl.GL.FRAMEBUFFER,
        vgl.GL.DEPTH_ATTACHMENT, vgl.GL.TEXTURE_2D, depthTexID[i], 0);
      renderState.m_context.framebufferTexture2D(vgl.GL.FRAMEBUFFER,
        vgl.GL.COLOR_ATTACHMENT0, vgl.GL.TEXTURE_2D, texID[i], 0);
    }

    // Now setup the color attachment for color blend FBO
    colorBlenderTexID = renderState.m_context.createTexture();
    renderState.m_context.bindTexture(vgl.GL.TEXTURE_2D, colorBlenderTexID);
    renderState.m_context.texParameteri(vgl.GL.TEXTURE_2D,
        vgl.GL.TEXTURE_WRAP_S, vgl.GL.CLAMP_TO_EDGE);
    renderState.m_context.texParameteri(vgl.GL.TEXTURE_2D,
        vgl.GL.TEXTURE_WRAP_T, vgl.GL.CLAMP_TO_EDGE);
    renderState.m_context.texParameteri(vgl.GL.TEXTURE_2D,
        vgl.GL.TEXTURE_MIN_FILTER, vgl.GL.NEAREST);
    renderState.m_context.texParameteri(vgl.GL.TEXTURE_2D,
        vgl.GL.TEXTURE_MAG_FILTER, vgl.GL.NEAREST);
    renderState.m_context.texImage2D(vgl.GL.TEXTURE_2D, 0, vgl.GL.RGBA,
        WIDTH, HEIGHT, 0, vgl.GL.RGBA, vgl.GL.FLOAT, null);

    // Generate the color blend FBO ID
    colorBlenderFBOID = renderState.m_context.createFramebuffer();
    renderState.m_context.bindFramebuffer(vgl.GL.FRAMEBUFFER, colorBlenderFBOID);

    // Set the depth attachment of previous FBO as depth attachment for this FBO
    renderState.m_context.framebufferTexture2D(vgl.GL.FRAMEBUFFER,
        vgl.GL.DEPTH_ATTACHMENT, vgl.GL.TEXTURE_2D, depthTexID[0], 0);

    // Set the color blender texture as the FBO color attachment
    renderState.m_context.framebufferTexture2D(vgl.GL.FRAMEBUFFER,
        vgl.GL.COLOR_ATTACHMENT0, vgl.GL.TEXTURE_2D, colorBlenderTexID, 0);

    // Check the FBO completeness status
    var status = renderState.m_context.checkFramebufferStatus(
        vgl.GL.FRAMEBUFFER);
    if (status === vgl.GL.FRAMEBUFFER_COMPLETE) {
      console.log('FBO setup successful !!! \n');
    } else {
      console.log('Problem with FBO setup');
    }
    // Unbind FBO
    renderState.m_context.bindFramebuffer(vgl.GL.FRAMEBUFFER, null);
  }

  function setup(renderState) {
    if (setupTime.getMTime() < m_this.getMTime()) {
      initScreenQuad(renderState, m_this.width(), m_this.height());
      initShaders(renderState, m_this.width(), m_this.height());
      initFBO(renderState, m_this.width(), m_this.height());
      setupTime.modified();
    }
  }

  function drawScene(renderState, sortedActors, material) {

    var i, actor, mvMatrixInv = mat4.create();

    // // Enable alpha blending with over compositing
    // gl.enable(vgl.GL.BLEND);
    // gl.blendFunc(vgl.GL.SRC_ALPHA, vgl.GL.ONE_MINUS_SRC_ALPHA);

    for (i = 0; i < sortedActors.length; i += 1) {
      actor = sortedActors[i][2];

      if (actor.referenceFrame() ===
          vgl.boundingObject.ReferenceFrame.Relative) {
        mat4.multiply(renderState.m_modelViewMatrix, m_this.m_camera.viewMatrix(),
          actor.matrix());
        renderState.m_projectionMatrix = m_this.m_camera.projectionMatrix();
      } else {
        renderState.m_modelViewMatrix = actor.matrix();
        renderState.m_projectionMatrix = mat4.create();
        mat4.ortho(renderState.m_projectionMatrix, 0,
                   m_this.m_width, 0, m_this.m_height, -1, 1);
      }

      mat4.invert(mvMatrixInv, renderState.m_modelViewMatrix);
      mat4.transpose(renderState.m_normalMatrix, mvMatrixInv);
      renderState.m_mapper = actor.mapper();

      // TODO Fix this shortcut
      if (!material) {
        renderState.m_material = actor.material();
        renderState.m_material.bind(renderState);
        renderState.m_mapper.render(renderState);
        renderState.m_material.undoBind(renderState);
      } else {

        var ou = actor.material().shaderProgram().uniform('opacity');
        if (ou) {
          fpopacity.set(ou.get()[0]);
        } else {
          fpopacity.set(1.0);
        }
        renderState.m_material = material;
        renderState.m_material.bind(renderState);
        renderState.m_mapper.render(renderState);
        renderState.m_material.undoBind(renderState);
      }
    }
  }

  function depthPeelRender(renderState, actors) {
    var layer;

    fpwidth.set(m_this.width());
    fpheight.set(m_this.height());
    blwidth.set(m_this.width());
    blheight.set(m_this.height());
    fiwidth.set(m_this.width());
    fiheight.set(m_this.height());

    // Clear color and depth buffer
    renderState.m_context.clearColor(0.0, 0.0, 0.0, 0.0);
    /*jshint bitwise: false */
    renderState.m_context.clear(vgl.GL.OLOR_BUFFER_BIT | vgl.GL.DEPTH_BUFFER_BIT);
    /*jshint bitwise: true */

    // Bind the color blending FBO
    renderState.m_context.bindFramebuffer(vgl.GL.FRAMEBUFFER, colorBlenderFBOID);

    // 1. In the first pass, we render normally with depth test enabled to
    // get the nearest surface
    renderState.m_context.enable(vgl.GL.DEPTH_TEST);
    renderState.m_context.disable(vgl.GL.BLEND);

    var clearColor = m_this.m_camera.clearColor();
    renderState.m_context.clearColor(clearColor[0], clearColor[1], clearColor[2], 0.0);
    /*jshint bitwise: false */
    renderState.m_context.clear(vgl.GL.COLOR_BUFFER_BIT | vgl.GL.DEPTH_BUFFER_BIT);
    /*jshint bitwise: true */

    drawScene(renderState, actors);

    // 2. Depth peeling + blending pass
    var numLayers = (NUM_PASSES - 1) * 2;

    // For each pass
    for (layer = 1; layer < numLayers; layer += 1) {
      var currId = layer % 2;
      var prevId = 1 - currId;

      // Bind the current FBO
      renderState.m_context.bindFramebuffer(vgl.GL.FRAMEBUFFER, fbo[currId]);

      // Disbale blending and enable depth testing
      renderState.m_context.disable(vgl.GL.BLEND);
      renderState.m_context.enable(vgl.GL.DEPTH_TEST);

      // Bind the depth texture from the previous step
      renderState.m_context.bindTexture(vgl.GL.TEXTURE_2D, depthTexID[prevId]);

      // Set clear color to black
      renderState.m_context.clearColor(0.0, 0.0, 0.0, 0.0);

      // Clear the color and depth buffers
      /*jshint bitwise: false */
      renderState.m_context.clear(vgl.GL.COLOR_BUFFER_BIT | vgl.GL.DEPTH_BUFFER_BIT);
      /*jshint bitwise: true */

      // Render scene with the front to back peeling shader
      drawScene(renderState, actors, fpMaterial);

      // Bind the color blender FBO
      renderState.m_context.bindFramebuffer(vgl.GL.FRAMEBUFFER, colorBlenderFBOID);

      // Enable blending but disable depth testing
      renderState.m_context.disable(vgl.GL.DEPTH_TEST);
      renderState.m_context.enable(vgl.GL.BLEND);

      // Change the blending equation to add
      renderState.m_context.blendEquation(vgl.GL.FUNC_ADD);

      // Use separate blending function
      renderState.m_context.blendFuncSeparate(vgl.GL.DST_ALPHA, vgl.GL.ONE,
          vgl.GL.ZERO, vgl.GL.ONE_MINUS_SRC_ALPHA);

      // Bind the result from the previous iteration as texture
      renderState.m_context.activeTexture(vgl.GL.TEXTURE0);
      renderState.m_context.bindTexture(vgl.GL.TEXTURE_2D, texID[currId]);

      renderState.m_context.activeTexture(vgl.GL.TEXTURE1);
      renderState.m_context.bindTexture(vgl.GL.TEXTURE_2D, depthTexID[prevId]);

      renderState.m_context.activeTexture(vgl.GL.TEXTURE2);
      renderState.m_context.bindTexture(vgl.GL.TEXTURE_2D, depthTexID[currId]);

      drawFullScreenQuad(renderState, blMaterial);

      renderState.m_context.activeTexture(vgl.GL.TEXTURE0);

      // Disable blending
      renderState.m_context.disable(vgl.GL.BLEND);
    }

    // 3. Final render pass
    //remove the FBO
    renderState.m_context.bindFramebuffer(vgl.GL.FRAMEBUFFER, null);

    // Disable depth testing and blending
    renderState.m_context.disable(vgl.GL.DEPTH_TEST);
    renderState.m_context.disable(vgl.GL.BLEND);

    // Bind the color blender texture
    renderState.m_context.bindTexture(vgl.GL.TEXTURE_2D, colorBlenderTexID);

    fibackgroundColor.set(m_this.m_camera.clearColor());

    // Draw full screen quad
    drawFullScreenQuad(renderState, fiMaterial);

    renderState.m_context.bindTexture(vgl.GL.TEXTURE_2D, null);
  }

  ////////////////////////////////////////////////////////////////////////////
  /**
   * Render the scene
   */
  ////////////////////////////////////////////////////////////////////////////
  this.render = function () {
    var i, renSt, children, actor = null, sortedActors = [];

    renSt = new vgl.renderState();
    renSt.m_renderer = m_this;
    renSt.m_context = m_this.renderWindow().context();
    renSt.m_contextChanged = m_this.m_contextChanged;

    // Set the viewport for this renderer
    renSt.m_context.viewport(m_this.m_x, m_this.m_y, m_this.m_width, m_this.m_height);

    // Check if we have initialized
    setup(renSt);

    children = m_this.m_sceneRoot.children();

    if (children.length > 0 && m_this.m_resetScene) {
      this.resetCamera();
      m_this.m_resetScene = false;
    }

    for (i = 0; i < children.length; i += 1) {
      actor = children[i];
      actor.computeBounds();
      if (actor.visible()) {
        sortedActors.push([actor.material().binNumber(),
          actor.material().shaderProgram().uniform('opacity').get()[0],
          actor]);
      }
    }

    // Now perform sorting
    sortedActors.sort(function (a, b) { return a[0] - b[0]; });
    sortedActors.sort(function (a, b) { return b[1] - a[1]; });

    depthPeelRender(renSt, sortedActors);
  };
};

inherit(vgl.depthPeelRenderer, vgl.renderer);

return vgl;

}));



/***/ }),
/* 176 */
/***/ (function(module, exports) {

module.exports = create;

/**
 * Creates a new identity mat4
 *
 * @returns {mat4} a new 4x4 matrix
 */
function create() {
    var out = new Float32Array(16);
    out[0] = 1;
    out[1] = 0;
    out[2] = 0;
    out[3] = 0;
    out[4] = 0;
    out[5] = 1;
    out[6] = 0;
    out[7] = 0;
    out[8] = 0;
    out[9] = 0;
    out[10] = 1;
    out[11] = 0;
    out[12] = 0;
    out[13] = 0;
    out[14] = 0;
    out[15] = 1;
    return out;
};

/***/ }),
/* 177 */
/***/ (function(module, exports) {

module.exports = clone;

/**
 * Creates a new mat4 initialized with values from an existing matrix
 *
 * @param {mat4} a matrix to clone
 * @returns {mat4} a new 4x4 matrix
 */
function clone(a) {
    var out = new Float32Array(16);
    out[0] = a[0];
    out[1] = a[1];
    out[2] = a[2];
    out[3] = a[3];
    out[4] = a[4];
    out[5] = a[5];
    out[6] = a[6];
    out[7] = a[7];
    out[8] = a[8];
    out[9] = a[9];
    out[10] = a[10];
    out[11] = a[11];
    out[12] = a[12];
    out[13] = a[13];
    out[14] = a[14];
    out[15] = a[15];
    return out;
};

/***/ }),
/* 178 */
/***/ (function(module, exports) {

module.exports = copy;

/**
 * Copy the values from one mat4 to another
 *
 * @param {mat4} out the receiving matrix
 * @param {mat4} a the source matrix
 * @returns {mat4} out
 */
function copy(out, a) {
    out[0] = a[0];
    out[1] = a[1];
    out[2] = a[2];
    out[3] = a[3];
    out[4] = a[4];
    out[5] = a[5];
    out[6] = a[6];
    out[7] = a[7];
    out[8] = a[8];
    out[9] = a[9];
    out[10] = a[10];
    out[11] = a[11];
    out[12] = a[12];
    out[13] = a[13];
    out[14] = a[14];
    out[15] = a[15];
    return out;
};

/***/ }),
/* 179 */
/***/ (function(module, exports) {

module.exports = transpose;

/**
 * Transpose the values of a mat4
 *
 * @param {mat4} out the receiving matrix
 * @param {mat4} a the source matrix
 * @returns {mat4} out
 */
function transpose(out, a) {
    // If we are transposing ourselves we can skip a few steps but have to cache some values
    if (out === a) {
        var a01 = a[1], a02 = a[2], a03 = a[3],
            a12 = a[6], a13 = a[7],
            a23 = a[11];

        out[1] = a[4];
        out[2] = a[8];
        out[3] = a[12];
        out[4] = a01;
        out[6] = a[9];
        out[7] = a[13];
        out[8] = a02;
        out[9] = a12;
        out[11] = a[14];
        out[12] = a03;
        out[13] = a13;
        out[14] = a23;
    } else {
        out[0] = a[0];
        out[1] = a[4];
        out[2] = a[8];
        out[3] = a[12];
        out[4] = a[1];
        out[5] = a[5];
        out[6] = a[9];
        out[7] = a[13];
        out[8] = a[2];
        out[9] = a[6];
        out[10] = a[10];
        out[11] = a[14];
        out[12] = a[3];
        out[13] = a[7];
        out[14] = a[11];
        out[15] = a[15];
    }
    
    return out;
};

/***/ }),
/* 180 */
/***/ (function(module, exports) {

module.exports = invert;

/**
 * Inverts a mat4
 *
 * @param {mat4} out the receiving matrix
 * @param {mat4} a the source matrix
 * @returns {mat4} out
 */
function invert(out, a) {
    var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3],
        a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7],
        a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11],
        a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15],

        b00 = a00 * a11 - a01 * a10,
        b01 = a00 * a12 - a02 * a10,
        b02 = a00 * a13 - a03 * a10,
        b03 = a01 * a12 - a02 * a11,
        b04 = a01 * a13 - a03 * a11,
        b05 = a02 * a13 - a03 * a12,
        b06 = a20 * a31 - a21 * a30,
        b07 = a20 * a32 - a22 * a30,
        b08 = a20 * a33 - a23 * a30,
        b09 = a21 * a32 - a22 * a31,
        b10 = a21 * a33 - a23 * a31,
        b11 = a22 * a33 - a23 * a32,

        // Calculate the determinant
        det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;

    if (!det) { 
        return null; 
    }
    det = 1.0 / det;

    out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det;
    out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det;
    out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det;
    out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det;
    out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det;
    out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det;
    out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det;
    out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det;
    out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det;
    out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det;
    out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det;
    out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det;
    out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det;
    out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det;
    out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det;
    out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det;

    return out;
};

/***/ }),
/* 181 */
/***/ (function(module, exports) {

module.exports = adjoint;

/**
 * Calculates the adjugate of a mat4
 *
 * @param {mat4} out the receiving matrix
 * @param {mat4} a the source matrix
 * @returns {mat4} out
 */
function adjoint(out, a) {
    var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3],
        a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7],
        a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11],
        a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];

    out[0]  =  (a11 * (a22 * a33 - a23 * a32) - a21 * (a12 * a33 - a13 * a32) + a31 * (a12 * a23 - a13 * a22));
    out[1]  = -(a01 * (a22 * a33 - a23 * a32) - a21 * (a02 * a33 - a03 * a32) + a31 * (a02 * a23 - a03 * a22));
    out[2]  =  (a01 * (a12 * a33 - a13 * a32) - a11 * (a02 * a33 - a03 * a32) + a31 * (a02 * a13 - a03 * a12));
    out[3]  = -(a01 * (a12 * a23 - a13 * a22) - a11 * (a02 * a23 - a03 * a22) + a21 * (a02 * a13 - a03 * a12));
    out[4]  = -(a10 * (a22 * a33 - a23 * a32) - a20 * (a12 * a33 - a13 * a32) + a30 * (a12 * a23 - a13 * a22));
    out[5]  =  (a00 * (a22 * a33 - a23 * a32) - a20 * (a02 * a33 - a03 * a32) + a30 * (a02 * a23 - a03 * a22));
    out[6]  = -(a00 * (a12 * a33 - a13 * a32) - a10 * (a02 * a33 - a03 * a32) + a30 * (a02 * a13 - a03 * a12));
    out[7]  =  (a00 * (a12 * a23 - a13 * a22) - a10 * (a02 * a23 - a03 * a22) + a20 * (a02 * a13 - a03 * a12));
    out[8]  =  (a10 * (a21 * a33 - a23 * a31) - a20 * (a11 * a33 - a13 * a31) + a30 * (a11 * a23 - a13 * a21));
    out[9]  = -(a00 * (a21 * a33 - a23 * a31) - a20 * (a01 * a33 - a03 * a31) + a30 * (a01 * a23 - a03 * a21));
    out[10] =  (a00 * (a11 * a33 - a13 * a31) - a10 * (a01 * a33 - a03 * a31) + a30 * (a01 * a13 - a03 * a11));
    out[11] = -(a00 * (a11 * a23 - a13 * a21) - a10 * (a01 * a23 - a03 * a21) + a20 * (a01 * a13 - a03 * a11));
    out[12] = -(a10 * (a21 * a32 - a22 * a31) - a20 * (a11 * a32 - a12 * a31) + a30 * (a11 * a22 - a12 * a21));
    out[13] =  (a00 * (a21 * a32 - a22 * a31) - a20 * (a01 * a32 - a02 * a31) + a30 * (a01 * a22 - a02 * a21));
    out[14] = -(a00 * (a11 * a32 - a12 * a31) - a10 * (a01 * a32 - a02 * a31) + a30 * (a01 * a12 - a02 * a11));
    out[15] =  (a00 * (a11 * a22 - a12 * a21) - a10 * (a01 * a22 - a02 * a21) + a20 * (a01 * a12 - a02 * a11));
    return out;
};

/***/ }),
/* 182 */
/***/ (function(module, exports) {

module.exports = determinant;

/**
 * Calculates the determinant of a mat4
 *
 * @param {mat4} a the source matrix
 * @returns {Number} determinant of a
 */
function determinant(a) {
    var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3],
        a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7],
        a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11],
        a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15],

        b00 = a00 * a11 - a01 * a10,
        b01 = a00 * a12 - a02 * a10,
        b02 = a00 * a13 - a03 * a10,
        b03 = a01 * a12 - a02 * a11,
        b04 = a01 * a13 - a03 * a11,
        b05 = a02 * a13 - a03 * a12,
        b06 = a20 * a31 - a21 * a30,
        b07 = a20 * a32 - a22 * a30,
        b08 = a20 * a33 - a23 * a30,
        b09 = a21 * a32 - a22 * a31,
        b10 = a21 * a33 - a23 * a31,
        b11 = a22 * a33 - a23 * a32;

    // Calculate the determinant
    return b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
};

/***/ }),
/* 183 */
/***/ (function(module, exports) {

module.exports = multiply;

/**
 * Multiplies two mat4's
 *
 * @param {mat4} out the receiving matrix
 * @param {mat4} a the first operand
 * @param {mat4} b the second operand
 * @returns {mat4} out
 */
function multiply(out, a, b) {
    var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3],
        a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7],
        a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11],
        a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];

    // Cache only the current line of the second matrix
    var b0  = b[0], b1 = b[1], b2 = b[2], b3 = b[3];  
    out[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
    out[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
    out[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
    out[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33;

    b0 = b[4]; b1 = b[5]; b2 = b[6]; b3 = b[7];
    out[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
    out[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
    out[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
    out[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33;

    b0 = b[8]; b1 = b[9]; b2 = b[10]; b3 = b[11];
    out[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
    out[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
    out[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
    out[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33;

    b0 = b[12]; b1 = b[13]; b2 = b[14]; b3 = b[15];
    out[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
    out[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
    out[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
    out[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33;
    return out;
};

/***/ }),
/* 184 */
/***/ (function(module, exports) {

module.exports = translate;

/**
 * Translate a mat4 by the given vector
 *
 * @param {mat4} out the receiving matrix
 * @param {mat4} a the matrix to translate
 * @param {vec3} v vector to translate by
 * @returns {mat4} out
 */
function translate(out, a, v) {
    var x = v[0], y = v[1], z = v[2],
        a00, a01, a02, a03,
        a10, a11, a12, a13,
        a20, a21, a22, a23;

    if (a === out) {
        out[12] = a[0] * x + a[4] * y + a[8] * z + a[12];
        out[13] = a[1] * x + a[5] * y + a[9] * z + a[13];
        out[14] = a[2] * x + a[6] * y + a[10] * z + a[14];
        out[15] = a[3] * x + a[7] * y + a[11] * z + a[15];
    } else {
        a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3];
        a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7];
        a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11];

        out[0] = a00; out[1] = a01; out[2] = a02; out[3] = a03;
        out[4] = a10; out[5] = a11; out[6] = a12; out[7] = a13;
        out[8] = a20; out[9] = a21; out[10] = a22; out[11] = a23;

        out[12] = a00 * x + a10 * y + a20 * z + a[12];
        out[13] = a01 * x + a11 * y + a21 * z + a[13];
        out[14] = a02 * x + a12 * y + a22 * z + a[14];
        out[15] = a03 * x + a13 * y + a23 * z + a[15];
    }

    return out;
};

/***/ }),
/* 185 */
/***/ (function(module, exports) {

module.exports = scale;

/**
 * Scales the mat4 by the dimensions in the given vec3
 *
 * @param {mat4} out the receiving matrix
 * @param {mat4} a the matrix to scale
 * @param {vec3} v the vec3 to scale the matrix by
 * @returns {mat4} out
 **/
function scale(out, a, v) {
    var x = v[0], y = v[1], z = v[2];

    out[0] = a[0] * x;
    out[1] = a[1] * x;
    out[2] = a[2] * x;
    out[3] = a[3] * x;
    out[4] = a[4] * y;
    out[5] = a[5] * y;
    out[6] = a[6] * y;
    out[7] = a[7] * y;
    out[8] = a[8] * z;
    out[9] = a[9] * z;
    out[10] = a[10] * z;
    out[11] = a[11] * z;
    out[12] = a[12];
    out[13] = a[13];
    out[14] = a[14];
    out[15] = a[15];
    return out;
};

/***/ }),
/* 186 */
/***/ (function(module, exports) {

module.exports = rotate;

/**
 * Rotates a mat4 by the given angle
 *
 * @param {mat4} out the receiving matrix
 * @param {mat4} a the matrix to rotate
 * @param {Number} rad the angle to rotate the matrix by
 * @param {vec3} axis the axis to rotate around
 * @returns {mat4} out
 */
function rotate(out, a, rad, axis) {
    var x = axis[0], y = axis[1], z = axis[2],
        len = Math.sqrt(x * x + y * y + z * z),
        s, c, t,
        a00, a01, a02, a03,
        a10, a11, a12, a13,
        a20, a21, a22, a23,
        b00, b01, b02,
        b10, b11, b12,
        b20, b21, b22;

    if (Math.abs(len) < 0.000001) { return null; }
    
    len = 1 / len;
    x *= len;
    y *= len;
    z *= len;

    s = Math.sin(rad);
    c = Math.cos(rad);
    t = 1 - c;

    a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3];
    a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7];
    a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11];

    // Construct the elements of the rotation matrix
    b00 = x * x * t + c; b01 = y * x * t + z * s; b02 = z * x * t - y * s;
    b10 = x * y * t - z * s; b11 = y * y * t + c; b12 = z * y * t + x * s;
    b20 = x * z * t + y * s; b21 = y * z * t - x * s; b22 = z * z * t + c;

    // Perform rotation-specific matrix multiplication
    out[0] = a00 * b00 + a10 * b01 + a20 * b02;
    out[1] = a01 * b00 + a11 * b01 + a21 * b02;
    out[2] = a02 * b00 + a12 * b01 + a22 * b02;
    out[3] = a03 * b00 + a13 * b01 + a23 * b02;
    out[4] = a00 * b10 + a10 * b11 + a20 * b12;
    out[5] = a01 * b10 + a11 * b11 + a21 * b12;
    out[6] = a02 * b10 + a12 * b11 + a22 * b12;
    out[7] = a03 * b10 + a13 * b11 + a23 * b12;
    out[8] = a00 * b20 + a10 * b21 + a20 * b22;
    out[9] = a01 * b20 + a11 * b21 + a21 * b22;
    out[10] = a02 * b20 + a12 * b21 + a22 * b22;
    out[11] = a03 * b20 + a13 * b21 + a23 * b22;

    if (a !== out) { // If the source and destination differ, copy the unchanged last row
        out[12] = a[12];
        out[13] = a[13];
        out[14] = a[14];
        out[15] = a[15];
    }
    return out;
};

/***/ }),
/* 187 */
/***/ (function(module, exports) {

module.exports = rotateX;

/**
 * Rotates a matrix by the given angle around the X axis
 *
 * @param {mat4} out the receiving matrix
 * @param {mat4} a the matrix to rotate
 * @param {Number} rad the angle to rotate the matrix by
 * @returns {mat4} out
 */
function rotateX(out, a, rad) {
    var s = Math.sin(rad),
        c = Math.cos(rad),
        a10 = a[4],
        a11 = a[5],
        a12 = a[6],
        a13 = a[7],
        a20 = a[8],
        a21 = a[9],
        a22 = a[10],
        a23 = a[11];

    if (a !== out) { // If the source and destination differ, copy the unchanged rows
        out[0]  = a[0];
        out[1]  = a[1];
        out[2]  = a[2];
        out[3]  = a[3];
        out[12] = a[12];
        out[13] = a[13];
        out[14] = a[14];
        out[15] = a[15];
    }

    // Perform axis-specific matrix multiplication
    out[4] = a10 * c + a20 * s;
    out[5] = a11 * c + a21 * s;
    out[6] = a12 * c + a22 * s;
    out[7] = a13 * c + a23 * s;
    out[8] = a20 * c - a10 * s;
    out[9] = a21 * c - a11 * s;
    out[10] = a22 * c - a12 * s;
    out[11] = a23 * c - a13 * s;
    return out;
};

/***/ }),
/* 188 */
/***/ (function(module, exports) {

module.exports = rotateY;

/**
 * Rotates a matrix by the given angle around the Y axis
 *
 * @param {mat4} out the receiving matrix
 * @param {mat4} a the matrix to rotate
 * @param {Number} rad the angle to rotate the matrix by
 * @returns {mat4} out
 */
function rotateY(out, a, rad) {
    var s = Math.sin(rad),
        c = Math.cos(rad),
        a00 = a[0],
        a01 = a[1],
        a02 = a[2],
        a03 = a[3],
        a20 = a[8],
        a21 = a[9],
        a22 = a[10],
        a23 = a[11];

    if (a !== out) { // If the source and destination differ, copy the unchanged rows
        out[4]  = a[4];
        out[5]  = a[5];
        out[6]  = a[6];
        out[7]  = a[7];
        out[12] = a[12];
        out[13] = a[13];
        out[14] = a[14];
        out[15] = a[15];
    }

    // Perform axis-specific matrix multiplication
    out[0] = a00 * c - a20 * s;
    out[1] = a01 * c - a21 * s;
    out[2] = a02 * c - a22 * s;
    out[3] = a03 * c - a23 * s;
    out[8] = a00 * s + a20 * c;
    out[9] = a01 * s + a21 * c;
    out[10] = a02 * s + a22 * c;
    out[11] = a03 * s + a23 * c;
    return out;
};

/***/ }),
/* 189 */
/***/ (function(module, exports) {

module.exports = rotateZ;

/**
 * Rotates a matrix by the given angle around the Z axis
 *
 * @param {mat4} out the receiving matrix
 * @param {mat4} a the matrix to rotate
 * @param {Number} rad the angle to rotate the matrix by
 * @returns {mat4} out
 */
function rotateZ(out, a, rad) {
    var s = Math.sin(rad),
        c = Math.cos(rad),
        a00 = a[0],
        a01 = a[1],
        a02 = a[2],
        a03 = a[3],
        a10 = a[4],
        a11 = a[5],
        a12 = a[6],
        a13 = a[7];

    if (a !== out) { // If the source and destination differ, copy the unchanged last row
        out[8]  = a[8];
        out[9]  = a[9];
        out[10] = a[10];
        out[11] = a[11];
        out[12] = a[12];
        out[13] = a[13];
        out[14] = a[14];
        out[15] = a[15];
    }

    // Perform axis-specific matrix multiplication
    out[0] = a00 * c + a10 * s;
    out[1] = a01 * c + a11 * s;
    out[2] = a02 * c + a12 * s;
    out[3] = a03 * c + a13 * s;
    out[4] = a10 * c - a00 * s;
    out[5] = a11 * c - a01 * s;
    out[6] = a12 * c - a02 * s;
    out[7] = a13 * c - a03 * s;
    return out;
};

/***/ }),
/* 190 */
/***/ (function(module, exports) {

module.exports = fromRotation

/**
 * Creates a matrix from a given angle around a given axis
 * This is equivalent to (but much faster than):
 *
 *     mat4.identity(dest)
 *     mat4.rotate(dest, dest, rad, axis)
 *
 * @param {mat4} out mat4 receiving operation result
 * @param {Number} rad the angle to rotate the matrix by
 * @param {vec3} axis the axis to rotate around
 * @returns {mat4} out
 */
function fromRotation(out, rad, axis) {
  var s, c, t
  var x = axis[0]
  var y = axis[1]
  var z = axis[2]
  var len = Math.sqrt(x * x + y * y + z * z)

  if (Math.abs(len) < 0.000001) {
    return null
  }

  len = 1 / len
  x *= len
  y *= len
  z *= len

  s = Math.sin(rad)
  c = Math.cos(rad)
  t = 1 - c

  // Perform rotation-specific matrix multiplication
  out[0] = x * x * t + c
  out[1] = y * x * t + z * s
  out[2] = z * x * t - y * s
  out[3] = 0
  out[4] = x * y * t - z * s
  out[5] = y * y * t + c
  out[6] = z * y * t + x * s
  out[7] = 0
  out[8] = x * z * t + y * s
  out[9] = y * z * t - x * s
  out[10] = z * z * t + c
  out[11] = 0
  out[12] = 0
  out[13] = 0
  out[14] = 0
  out[15] = 1
  return out
}


/***/ }),
/* 191 */
/***/ (function(module, exports) {

module.exports = fromRotationTranslation;

/**
 * Creates a matrix from a quaternion rotation and vector translation
 * This is equivalent to (but much faster than):
 *
 *     mat4.identity(dest);
 *     mat4.translate(dest, vec);
 *     var quatMat = mat4.create();
 *     quat4.toMat4(quat, quatMat);
 *     mat4.multiply(dest, quatMat);
 *
 * @param {mat4} out mat4 receiving operation result
 * @param {quat4} q Rotation quaternion
 * @param {vec3} v Translation vector
 * @returns {mat4} out
 */
function fromRotationTranslation(out, q, v) {
    // Quaternion math
    var x = q[0], y = q[1], z = q[2], w = q[3],
        x2 = x + x,
        y2 = y + y,
        z2 = z + z,

        xx = x * x2,
        xy = x * y2,
        xz = x * z2,
        yy = y * y2,
        yz = y * z2,
        zz = z * z2,
        wx = w * x2,
        wy = w * y2,
        wz = w * z2;

    out[0] = 1 - (yy + zz);
    out[1] = xy + wz;
    out[2] = xz - wy;
    out[3] = 0;
    out[4] = xy - wz;
    out[5] = 1 - (xx + zz);
    out[6] = yz + wx;
    out[7] = 0;
    out[8] = xz + wy;
    out[9] = yz - wx;
    out[10] = 1 - (xx + yy);
    out[11] = 0;
    out[12] = v[0];
    out[13] = v[1];
    out[14] = v[2];
    out[15] = 1;
    
    return out;
};

/***/ }),
/* 192 */
/***/ (function(module, exports) {

module.exports = fromScaling

/**
 * Creates a matrix from a vector scaling
 * This is equivalent to (but much faster than):
 *
 *     mat4.identity(dest)
 *     mat4.scale(dest, dest, vec)
 *
 * @param {mat4} out mat4 receiving operation result
 * @param {vec3} v Scaling vector
 * @returns {mat4} out
 */
function fromScaling(out, v) {
  out[0] = v[0]
  out[1] = 0
  out[2] = 0
  out[3] = 0
  out[4] = 0
  out[5] = v[1]
  out[6] = 0
  out[7] = 0
  out[8] = 0
  out[9] = 0
  out[10] = v[2]
  out[11] = 0
  out[12] = 0
  out[13] = 0
  out[14] = 0
  out[15] = 1
  return out
}


/***/ }),
/* 193 */
/***/ (function(module, exports) {

module.exports = fromTranslation

/**
 * Creates a matrix from a vector translation
 * This is equivalent to (but much faster than):
 *
 *     mat4.identity(dest)
 *     mat4.translate(dest, dest, vec)
 *
 * @param {mat4} out mat4 receiving operation result
 * @param {vec3} v Translation vector
 * @returns {mat4} out
 */
function fromTranslation(out, v) {
  out[0] = 1
  out[1] = 0
  out[2] = 0
  out[3] = 0
  out[4] = 0
  out[5] = 1
  out[6] = 0
  out[7] = 0
  out[8] = 0
  out[9] = 0
  out[10] = 1
  out[11] = 0
  out[12] = v[0]
  out[13] = v[1]
  out[14] = v[2]
  out[15] = 1
  return out
}


/***/ }),
/* 194 */
/***/ (function(module, exports) {

module.exports = fromXRotation

/**
 * Creates a matrix from the given angle around the X axis
 * This is equivalent to (but much faster than):
 *
 *     mat4.identity(dest)
 *     mat4.rotateX(dest, dest, rad)
 *
 * @param {mat4} out mat4 receiving operation result
 * @param {Number} rad the angle to rotate the matrix by
 * @returns {mat4} out
 */
function fromXRotation(out, rad) {
    var s = Math.sin(rad),
        c = Math.cos(rad)

    // Perform axis-specific matrix multiplication
    out[0] = 1
    out[1] = 0
    out[2] = 0
    out[3] = 0
    out[4] = 0
    out[5] = c
    out[6] = s
    out[7] = 0
    out[8] = 0
    out[9] = -s
    out[10] = c
    out[11] = 0
    out[12] = 0
    out[13] = 0
    out[14] = 0
    out[15] = 1
    return out
}

/***/ }),
/* 195 */
/***/ (function(module, exports) {

module.exports = fromYRotation

/**
 * Creates a matrix from the given angle around the Y axis
 * This is equivalent to (but much faster than):
 *
 *     mat4.identity(dest)
 *     mat4.rotateY(dest, dest, rad)
 *
 * @param {mat4} out mat4 receiving operation result
 * @param {Number} rad the angle to rotate the matrix by
 * @returns {mat4} out
 */
function fromYRotation(out, rad) {
    var s = Math.sin(rad),
        c = Math.cos(rad)

    // Perform axis-specific matrix multiplication
    out[0] = c
    out[1] = 0
    out[2] = -s
    out[3] = 0
    out[4] = 0
    out[5] = 1
    out[6] = 0
    out[7] = 0
    out[8] = s
    out[9] = 0
    out[10] = c
    out[11] = 0
    out[12] = 0
    out[13] = 0
    out[14] = 0
    out[15] = 1
    return out
}

/***/ }),
/* 196 */
/***/ (function(module, exports) {

module.exports = fromZRotation

/**
 * Creates a matrix from the given angle around the Z axis
 * This is equivalent to (but much faster than):
 *
 *     mat4.identity(dest)
 *     mat4.rotateZ(dest, dest, rad)
 *
 * @param {mat4} out mat4 receiving operation result
 * @param {Number} rad the angle to rotate the matrix by
 * @returns {mat4} out
 */
function fromZRotation(out, rad) {
    var s = Math.sin(rad),
        c = Math.cos(rad)

    // Perform axis-specific matrix multiplication
    out[0] = c
    out[1] = s
    out[2] = 0
    out[3] = 0
    out[4] = -s
    out[5] = c
    out[6] = 0
    out[7] = 0
    out[8] = 0
    out[9] = 0
    out[10] = 1
    out[11] = 0
    out[12] = 0
    out[13] = 0
    out[14] = 0
    out[15] = 1
    return out
}

/***/ }),
/* 197 */
/***/ (function(module, exports) {

module.exports = fromQuat;

/**
 * Creates a matrix from a quaternion rotation.
 *
 * @param {mat4} out mat4 receiving operation result
 * @param {quat4} q Rotation quaternion
 * @returns {mat4} out
 */
function fromQuat(out, q) {
    var x = q[0], y = q[1], z = q[2], w = q[3],
        x2 = x + x,
        y2 = y + y,
        z2 = z + z,

        xx = x * x2,
        yx = y * x2,
        yy = y * y2,
        zx = z * x2,
        zy = z * y2,
        zz = z * z2,
        wx = w * x2,
        wy = w * y2,
        wz = w * z2;

    out[0] = 1 - yy - zz;
    out[1] = yx + wz;
    out[2] = zx - wy;
    out[3] = 0;

    out[4] = yx - wz;
    out[5] = 1 - xx - zz;
    out[6] = zy + wx;
    out[7] = 0;

    out[8] = zx + wy;
    out[9] = zy - wx;
    out[10] = 1 - xx - yy;
    out[11] = 0;

    out[12] = 0;
    out[13] = 0;
    out[14] = 0;
    out[15] = 1;

    return out;
};

/***/ }),
/* 198 */
/***/ (function(module, exports) {

module.exports = frustum;

/**
 * Generates a frustum matrix with the given bounds
 *
 * @param {mat4} out mat4 frustum matrix will be written into
 * @param {Number} left Left bound of the frustum
 * @param {Number} right Right bound of the frustum
 * @param {Number} bottom Bottom bound of the frustum
 * @param {Number} top Top bound of the frustum
 * @param {Number} near Near bound of the frustum
 * @param {Number} far Far bound of the frustum
 * @returns {mat4} out
 */
function frustum(out, left, right, bottom, top, near, far) {
    var rl = 1 / (right - left),
        tb = 1 / (top - bottom),
        nf = 1 / (near - far);
    out[0] = (near * 2) * rl;
    out[1] = 0;
    out[2] = 0;
    out[3] = 0;
    out[4] = 0;
    out[5] = (near * 2) * tb;
    out[6] = 0;
    out[7] = 0;
    out[8] = (right + left) * rl;
    out[9] = (top + bottom) * tb;
    out[10] = (far + near) * nf;
    out[11] = -1;
    out[12] = 0;
    out[13] = 0;
    out[14] = (far * near * 2) * nf;
    out[15] = 0;
    return out;
};

/***/ }),
/* 199 */
/***/ (function(module, exports) {

module.exports = perspective;

/**
 * Generates a perspective projection matrix with the given bounds
 *
 * @param {mat4} out mat4 frustum matrix will be written into
 * @param {number} fovy Vertical field of view in radians
 * @param {number} aspect Aspect ratio. typically viewport width/height
 * @param {number} near Near bound of the frustum
 * @param {number} far Far bound of the frustum
 * @returns {mat4} out
 */
function perspective(out, fovy, aspect, near, far) {
    var f = 1.0 / Math.tan(fovy / 2),
        nf = 1 / (near - far);
    out[0] = f / aspect;
    out[1] = 0;
    out[2] = 0;
    out[3] = 0;
    out[4] = 0;
    out[5] = f;
    out[6] = 0;
    out[7] = 0;
    out[8] = 0;
    out[9] = 0;
    out[10] = (far + near) * nf;
    out[11] = -1;
    out[12] = 0;
    out[13] = 0;
    out[14] = (2 * far * near) * nf;
    out[15] = 0;
    return out;
};

/***/ }),
/* 200 */
/***/ (function(module, exports) {

module.exports = perspectiveFromFieldOfView;

/**
 * Generates a perspective projection matrix with the given field of view.
 * This is primarily useful for generating projection matrices to be used
 * with the still experiemental WebVR API.
 *
 * @param {mat4} out mat4 frustum matrix will be written into
 * @param {number} fov Object containing the following values: upDegrees, downDegrees, leftDegrees, rightDegrees
 * @param {number} near Near bound of the frustum
 * @param {number} far Far bound of the frustum
 * @returns {mat4} out
 */
function perspectiveFromFieldOfView(out, fov, near, far) {
    var upTan = Math.tan(fov.upDegrees * Math.PI/180.0),
        downTan = Math.tan(fov.downDegrees * Math.PI/180.0),
        leftTan = Math.tan(fov.leftDegrees * Math.PI/180.0),
        rightTan = Math.tan(fov.rightDegrees * Math.PI/180.0),
        xScale = 2.0 / (leftTan + rightTan),
        yScale = 2.0 / (upTan + downTan);

    out[0] = xScale;
    out[1] = 0.0;
    out[2] = 0.0;
    out[3] = 0.0;
    out[4] = 0.0;
    out[5] = yScale;
    out[6] = 0.0;
    out[7] = 0.0;
    out[8] = -((leftTan - rightTan) * xScale * 0.5);
    out[9] = ((upTan - downTan) * yScale * 0.5);
    out[10] = far / (near - far);
    out[11] = -1.0;
    out[12] = 0.0;
    out[13] = 0.0;
    out[14] = (far * near) / (near - far);
    out[15] = 0.0;
    return out;
}



/***/ }),
/* 201 */
/***/ (function(module, exports) {

module.exports = ortho;

/**
 * Generates a orthogonal projection matrix with the given bounds
 *
 * @param {mat4} out mat4 frustum matrix will be written into
 * @param {number} left Left bound of the frustum
 * @param {number} right Right bound of the frustum
 * @param {number} bottom Bottom bound of the frustum
 * @param {number} top Top bound of the frustum
 * @param {number} near Near bound of the frustum
 * @param {number} far Far bound of the frustum
 * @returns {mat4} out
 */
function ortho(out, left, right, bottom, top, near, far) {
    var lr = 1 / (left - right),
        bt = 1 / (bottom - top),
        nf = 1 / (near - far);
    out[0] = -2 * lr;
    out[1] = 0;
    out[2] = 0;
    out[3] = 0;
    out[4] = 0;
    out[5] = -2 * bt;
    out[6] = 0;
    out[7] = 0;
    out[8] = 0;
    out[9] = 0;
    out[10] = 2 * nf;
    out[11] = 0;
    out[12] = (left + right) * lr;
    out[13] = (top + bottom) * bt;
    out[14] = (far + near) * nf;
    out[15] = 1;
    return out;
};

/***/ }),
/* 202 */
/***/ (function(module, exports, __webpack_require__) {

var identity = __webpack_require__(97);

module.exports = lookAt;

/**
 * Generates a look-at matrix with the given eye position, focal point, and up axis
 *
 * @param {mat4} out mat4 frustum matrix will be written into
 * @param {vec3} eye Position of the viewer
 * @param {vec3} center Point the viewer is looking at
 * @param {vec3} up vec3 pointing up
 * @returns {mat4} out
 */
function lookAt(out, eye, center, up) {
    var x0, x1, x2, y0, y1, y2, z0, z1, z2, len,
        eyex = eye[0],
        eyey = eye[1],
        eyez = eye[2],
        upx = up[0],
        upy = up[1],
        upz = up[2],
        centerx = center[0],
        centery = center[1],
        centerz = center[2];

    if (Math.abs(eyex - centerx) < 0.000001 &&
        Math.abs(eyey - centery) < 0.000001 &&
        Math.abs(eyez - centerz) < 0.000001) {
        return identity(out);
    }

    z0 = eyex - centerx;
    z1 = eyey - centery;
    z2 = eyez - centerz;

    len = 1 / Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2);
    z0 *= len;
    z1 *= len;
    z2 *= len;

    x0 = upy * z2 - upz * z1;
    x1 = upz * z0 - upx * z2;
    x2 = upx * z1 - upy * z0;
    len = Math.sqrt(x0 * x0 + x1 * x1 + x2 * x2);
    if (!len) {
        x0 = 0;
        x1 = 0;
        x2 = 0;
    } else {
        len = 1 / len;
        x0 *= len;
        x1 *= len;
        x2 *= len;
    }

    y0 = z1 * x2 - z2 * x1;
    y1 = z2 * x0 - z0 * x2;
    y2 = z0 * x1 - z1 * x0;

    len = Math.sqrt(y0 * y0 + y1 * y1 + y2 * y2);
    if (!len) {
        y0 = 0;
        y1 = 0;
        y2 = 0;
    } else {
        len = 1 / len;
        y0 *= len;
        y1 *= len;
        y2 *= len;
    }

    out[0] = x0;
    out[1] = y0;
    out[2] = z0;
    out[3] = 0;
    out[4] = x1;
    out[5] = y1;
    out[6] = z1;
    out[7] = 0;
    out[8] = x2;
    out[9] = y2;
    out[10] = z2;
    out[11] = 0;
    out[12] = -(x0 * eyex + x1 * eyey + x2 * eyez);
    out[13] = -(y0 * eyex + y1 * eyey + y2 * eyez);
    out[14] = -(z0 * eyex + z1 * eyey + z2 * eyez);
    out[15] = 1;

    return out;
};

/***/ }),
/* 203 */
/***/ (function(module, exports) {

module.exports = str;

/**
 * Returns a string representation of a mat4
 *
 * @param {mat4} mat matrix to represent as a string
 * @returns {String} string representation of the matrix
 */
function str(a) {
    return 'mat4(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ', ' +
                    a[4] + ', ' + a[5] + ', ' + a[6] + ', ' + a[7] + ', ' +
                    a[8] + ', ' + a[9] + ', ' + a[10] + ', ' + a[11] + ', ' + 
                    a[12] + ', ' + a[13] + ', ' + a[14] + ', ' + a[15] + ')';
};

/***/ }),
/* 204 */
/***/ (function(module, exports) {

module.exports = create

/**
 * Creates a new, empty vec4
 *
 * @returns {vec4} a new 4D vector
 */
function create () {
  var out = new Float32Array(4)
  out[0] = 0
  out[1] = 0
  out[2] = 0
  out[3] = 0
  return out
}


/***/ }),
/* 205 */
/***/ (function(module, exports) {

module.exports = clone

/**
 * Creates a new vec4 initialized with values from an existing vector
 *
 * @param {vec4} a vector to clone
 * @returns {vec4} a new 4D vector
 */
function clone (a) {
  var out = new Float32Array(4)
  out[0] = a[0]
  out[1] = a[1]
  out[2] = a[2]
  out[3] = a[3]
  return out
}


/***/ }),
/* 206 */
/***/ (function(module, exports) {

module.exports = fromValues

/**
 * Creates a new vec4 initialized with the given values
 *
 * @param {Number} x X component
 * @param {Number} y Y component
 * @param {Number} z Z component
 * @param {Number} w W component
 * @returns {vec4} a new 4D vector
 */
function fromValues (x, y, z, w) {
  var out = new Float32Array(4)
  out[0] = x
  out[1] = y
  out[2] = z
  out[3] = w
  return out
}


/***/ }),
/* 207 */
/***/ (function(module, exports) {

module.exports = copy

/**
 * Copy the values from one vec4 to another
 *
 * @param {vec4} out the receiving vector
 * @param {vec4} a the source vector
 * @returns {vec4} out
 */
function copy (out, a) {
  out[0] = a[0]
  out[1] = a[1]
  out[2] = a[2]
  out[3] = a[3]
  return out
}


/***/ }),
/* 208 */
/***/ (function(module, exports) {

module.exports = set

/**
 * Set the components of a vec4 to the given values
 *
 * @param {vec4} out the receiving vector
 * @param {Number} x X component
 * @param {Number} y Y component
 * @param {Number} z Z component
 * @param {Number} w W component
 * @returns {vec4} out
 */
function set (out, x, y, z, w) {
  out[0] = x
  out[1] = y
  out[2] = z
  out[3] = w
  return out
}


/***/ }),
/* 209 */
/***/ (function(module, exports) {

module.exports = add

/**
 * Adds two vec4's
 *
 * @param {vec4} out the receiving vector
 * @param {vec4} a the first operand
 * @param {vec4} b the second operand
 * @returns {vec4} out
 */
function add (out, a, b) {
  out[0] = a[0] + b[0]
  out[1] = a[1] + b[1]
  out[2] = a[2] + b[2]
  out[3] = a[3] + b[3]
  return out
}


/***/ }),
/* 210 */
/***/ (function(module, exports) {

module.exports = subtract

/**
 * Subtracts vector b from vector a
 *
 * @param {vec4} out the receiving vector
 * @param {vec4} a the first operand
 * @param {vec4} b the second operand
 * @returns {vec4} out
 */
function subtract (out, a, b) {
  out[0] = a[0] - b[0]
  out[1] = a[1] - b[1]
  out[2] = a[2] - b[2]
  out[3] = a[3] - b[3]
  return out
}


/***/ }),
/* 211 */
/***/ (function(module, exports) {

module.exports = multiply

/**
 * Multiplies two vec4's
 *
 * @param {vec4} out the receiving vector
 * @param {vec4} a the first operand
 * @param {vec4} b the second operand
 * @returns {vec4} out
 */
function multiply (out, a, b) {
  out[0] = a[0] * b[0]
  out[1] = a[1] * b[1]
  out[2] = a[2] * b[2]
  out[3] = a[3] * b[3]
  return out
}


/***/ }),
/* 212 */
/***/ (function(module, exports) {

module.exports = divide

/**
 * Divides two vec4's
 *
 * @param {vec4} out the receiving vector
 * @param {vec4} a the first operand
 * @param {vec4} b the second operand
 * @returns {vec4} out
 */
function divide (out, a, b) {
  out[0] = a[0] / b[0]
  out[1] = a[1] / b[1]
  out[2] = a[2] / b[2]
  out[3] = a[3] / b[3]
  return out
}


/***/ }),
/* 213 */
/***/ (function(module, exports) {

module.exports = min

/**
 * Returns the minimum of two vec4's
 *
 * @param {vec4} out the receiving vector
 * @param {vec4} a the first operand
 * @param {vec4} b the second operand
 * @returns {vec4} out
 */
function min (out, a, b) {
  out[0] = Math.min(a[0], b[0])
  out[1] = Math.min(a[1], b[1])
  out[2] = Math.min(a[2], b[2])
  out[3] = Math.min(a[3], b[3])
  return out
}


/***/ }),
/* 214 */
/***/ (function(module, exports) {

module.exports = max

/**
 * Returns the maximum of two vec4's
 *
 * @param {vec4} out the receiving vector
 * @param {vec4} a the first operand
 * @param {vec4} b the second operand
 * @returns {vec4} out
 */
function max (out, a, b) {
  out[0] = Math.max(a[0], b[0])
  out[1] = Math.max(a[1], b[1])
  out[2] = Math.max(a[2], b[2])
  out[3] = Math.max(a[3], b[3])
  return out
}


/***/ }),
/* 215 */
/***/ (function(module, exports) {

module.exports = scaleAndAdd

/**
 * Adds two vec4's after scaling the second operand by a scalar value
 *
 * @param {vec4} out the receiving vector
 * @param {vec4} a the first operand
 * @param {vec4} b the second operand
 * @param {Number} scale the amount to scale b by before adding
 * @returns {vec4} out
 */
function scaleAndAdd (out, a, b, scale) {
  out[0] = a[0] + (b[0] * scale)
  out[1] = a[1] + (b[1] * scale)
  out[2] = a[2] + (b[2] * scale)
  out[3] = a[3] + (b[3] * scale)
  return out
}


/***/ }),
/* 216 */
/***/ (function(module, exports) {

module.exports = distance

/**
 * Calculates the euclidian distance between two vec4's
 *
 * @param {vec4} a the first operand
 * @param {vec4} b the second operand
 * @returns {Number} distance between a and b
 */
function distance (a, b) {
  var x = b[0] - a[0],
    y = b[1] - a[1],
    z = b[2] - a[2],
    w = b[3] - a[3]
  return Math.sqrt(x * x + y * y + z * z + w * w)
}


/***/ }),
/* 217 */
/***/ (function(module, exports) {

module.exports = squaredDistance

/**
 * Calculates the squared euclidian distance between two vec4's
 *
 * @param {vec4} a the first operand
 * @param {vec4} b the second operand
 * @returns {Number} squared distance between a and b
 */
function squaredDistance (a, b) {
  var x = b[0] - a[0],
    y = b[1] - a[1],
    z = b[2] - a[2],
    w = b[3] - a[3]
  return x * x + y * y + z * z + w * w
}


/***/ }),
/* 218 */
/***/ (function(module, exports) {

module.exports = length

/**
 * Calculates the length of a vec4
 *
 * @param {vec4} a vector to calculate length of
 * @returns {Number} length of a
 */
function length (a) {
  var x = a[0],
    y = a[1],
    z = a[2],
    w = a[3]
  return Math.sqrt(x * x + y * y + z * z + w * w)
}


/***/ }),
/* 219 */
/***/ (function(module, exports) {

module.exports = squaredLength

/**
 * Calculates the squared length of a vec4
 *
 * @param {vec4} a vector to calculate squared length of
 * @returns {Number} squared length of a
 */
function squaredLength (a) {
  var x = a[0],
    y = a[1],
    z = a[2],
    w = a[3]
  return x * x + y * y + z * z + w * w
}


/***/ }),
/* 220 */
/***/ (function(module, exports) {

module.exports = negate

/**
 * Negates the components of a vec4
 *
 * @param {vec4} out the receiving vector
 * @param {vec4} a vector to negate
 * @returns {vec4} out
 */
function negate (out, a) {
  out[0] = -a[0]
  out[1] = -a[1]
  out[2] = -a[2]
  out[3] = -a[3]
  return out
}


/***/ }),
/* 221 */
/***/ (function(module, exports) {

module.exports = inverse

/**
 * Returns the inverse of the components of a vec4
 *
 * @param {vec4} out the receiving vector
 * @param {vec4} a vector to invert
 * @returns {vec4} out
 */
function inverse (out, a) {
  out[0] = 1.0 / a[0]
  out[1] = 1.0 / a[1]
  out[2] = 1.0 / a[2]
  out[3] = 1.0 / a[3]
  return out
}


/***/ }),
/* 222 */
/***/ (function(module, exports) {

module.exports = dot

/**
 * Calculates the dot product of two vec4's
 *
 * @param {vec4} a the first operand
 * @param {vec4} b the second operand
 * @returns {Number} dot product of a and b
 */
function dot (a, b) {
  return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]
}


/***/ }),
/* 223 */
/***/ (function(module, exports) {

module.exports = lerp

/**
 * Performs a linear interpolation between two vec4's
 *
 * @param {vec4} out the receiving vector
 * @param {vec4} a the first operand
 * @param {vec4} b the second operand
 * @param {Number} t interpolation amount between the two inputs
 * @returns {vec4} out
 */
function lerp (out, a, b, t) {
  var ax = a[0],
    ay = a[1],
    az = a[2],
    aw = a[3]
  out[0] = ax + t * (b[0] - ax)
  out[1] = ay + t * (b[1] - ay)
  out[2] = az + t * (b[2] - az)
  out[3] = aw + t * (b[3] - aw)
  return out
}


/***/ }),
/* 224 */
/***/ (function(module, exports, __webpack_require__) {

var vecNormalize = __webpack_require__(100)
var vecScale = __webpack_require__(99)

module.exports = random

/**
 * Generates a random vector with the given scale
 *
 * @param {vec4} out the receiving vector
 * @param {Number} [scale] Length of the resulting vector. If ommitted, a unit vector will be returned
 * @returns {vec4} out
 */
function random (out, scale) {
  scale = scale || 1.0

  // TODO: This is a pretty awful way of doing this. Find something better.
  out[0] = Math.random()
  out[1] = Math.random()
  out[2] = Math.random()
  out[3] = Math.random()
  vecNormalize(out, out)
  vecScale(out, out, scale)
  return out
}


/***/ }),
/* 225 */
/***/ (function(module, exports) {

module.exports = transformMat4

/**
 * Transforms the vec4 with a mat4.
 *
 * @param {vec4} out the receiving vector
 * @param {vec4} a the vector to transform
 * @param {mat4} m matrix to transform with
 * @returns {vec4} out
 */
function transformMat4 (out, a, m) {
  var x = a[0], y = a[1], z = a[2], w = a[3]
  out[0] = m[0] * x + m[4] * y + m[8] * z + m[12] * w
  out[1] = m[1] * x + m[5] * y + m[9] * z + m[13] * w
  out[2] = m[2] * x + m[6] * y + m[10] * z + m[14] * w
  out[3] = m[3] * x + m[7] * y + m[11] * z + m[15] * w
  return out
}


/***/ }),
/* 226 */
/***/ (function(module, exports) {

module.exports = transformQuat

/**
 * Transforms the vec4 with a quat
 *
 * @param {vec4} out the receiving vector
 * @param {vec4} a the vector to transform
 * @param {quat} q quaternion to transform with
 * @returns {vec4} out
 */
function transformQuat (out, a, q) {
  var x = a[0], y = a[1], z = a[2],
    qx = q[0], qy = q[1], qz = q[2], qw = q[3],

    // calculate quat * vec
    ix = qw * x + qy * z - qz * y,
    iy = qw * y + qz * x - qx * z,
    iz = qw * z + qx * y - qy * x,
    iw = -qx * x - qy * y - qz * z

  // calculate result * inverse quat
  out[0] = ix * qw + iw * -qx + iy * -qz - iz * -qy
  out[1] = iy * qw + iw * -qy + iz * -qx - ix * -qz
  out[2] = iz * qw + iw * -qz + ix * -qy - iy * -qx
  out[3] = a[3]
  return out
}


/***/ }),
/* 227 */
/***/ (function(module, exports) {

module.exports = clone;

/**
 * Creates a new vec3 initialized with values from an existing vector
 *
 * @param {vec3} a vector to clone
 * @returns {vec3} a new 3D vector
 */
function clone(a) {
    var out = new Float32Array(3)
    out[0] = a[0]
    out[1] = a[1]
    out[2] = a[2]
    return out
}

/***/ }),
/* 228 */
/***/ (function(module, exports, __webpack_require__) {

module.exports = angle

var fromValues = __webpack_require__(103)
var normalize = __webpack_require__(104)
var dot = __webpack_require__(105)

/**
 * Get the angle between two 3D vectors
 * @param {vec3} a The first operand
 * @param {vec3} b The second operand
 * @returns {Number} The angle in radians
 */
function angle(a, b) {
    var tempA = fromValues(a[0], a[1], a[2])
    var tempB = fromValues(b[0], b[1], b[2])
 
    normalize(tempA, tempA)
    normalize(tempB, tempB)
 
    var cosine = dot(tempA, tempB)

    if(cosine > 1.0){
        return 0
    } else {
        return Math.acos(cosine)
    }     
}


/***/ }),
/* 229 */
/***/ (function(module, exports) {

module.exports = copy;

/**
 * Copy the values from one vec3 to another
 *
 * @param {vec3} out the receiving vector
 * @param {vec3} a the source vector
 * @returns {vec3} out
 */
function copy(out, a) {
    out[0] = a[0]
    out[1] = a[1]
    out[2] = a[2]
    return out
}

/***/ }),
/* 230 */
/***/ (function(module, exports) {

module.exports = set;

/**
 * Set the components of a vec3 to the given values
 *
 * @param {vec3} out the receiving vector
 * @param {Number} x X component
 * @param {Number} y Y component
 * @param {Number} z Z component
 * @returns {vec3} out
 */
function set(out, x, y, z) {
    out[0] = x
    out[1] = y
    out[2] = z
    return out
}

/***/ }),
/* 231 */
/***/ (function(module, exports, __webpack_require__) {

module.exports = equals

var EPSILON = __webpack_require__(101)

/**
 * Returns whether or not the vectors have approximately the same elements in the same position.
 *
 * @param {vec3} a The first vector.
 * @param {vec3} b The second vector.
 * @returns {Boolean} True if the vectors are equal, false otherwise.
 */
function equals(a, b) {
  var a0 = a[0]
  var a1 = a[1]
  var a2 = a[2]
  var b0 = b[0]
  var b1 = b[1]
  var b2 = b[2]
  return (Math.abs(a0 - b0) <= EPSILON * Math.max(1.0, Math.abs(a0), Math.abs(b0)) &&
          Math.abs(a1 - b1) <= EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1)) &&
          Math.abs(a2 - b2) <= EPSILON * Math.max(1.0, Math.abs(a2), Math.abs(b2)))
}


/***/ }),
/* 232 */
/***/ (function(module, exports) {

module.exports = exactEquals

/**
 * Returns whether or not the vectors exactly have the same elements in the same position (when compared with ===)
 *
 * @param {vec3} a The first vector.
 * @param {vec3} b The second vector.
 * @returns {Boolean} True if the vectors are equal, false otherwise.
 */
function exactEquals(a, b) {
  return a[0] === b[0] && a[1] === b[1] && a[2] === b[2]
}


/***/ }),
/* 233 */
/***/ (function(module, exports) {

module.exports = add;

/**
 * Adds two vec3's
 *
 * @param {vec3} out the receiving vector
 * @param {vec3} a the first operand
 * @param {vec3} b the second operand
 * @returns {vec3} out
 */
function add(out, a, b) {
    out[0] = a[0] + b[0]
    out[1] = a[1] + b[1]
    out[2] = a[2] + b[2]
    return out
}

/***/ }),
/* 234 */
/***/ (function(module, exports, __webpack_require__) {

module.exports = __webpack_require__(106)


/***/ }),
/* 235 */
/***/ (function(module, exports, __webpack_require__) {

module.exports = __webpack_require__(107)


/***/ }),
/* 236 */
/***/ (function(module, exports, __webpack_require__) {

module.exports = __webpack_require__(108)


/***/ }),
/* 237 */
/***/ (function(module, exports) {

module.exports = min;

/**
 * Returns the minimum of two vec3's
 *
 * @param {vec3} out the receiving vector
 * @param {vec3} a the first operand
 * @param {vec3} b the second operand
 * @returns {vec3} out
 */
function min(out, a, b) {
    out[0] = Math.min(a[0], b[0])
    out[1] = Math.min(a[1], b[1])
    out[2] = Math.min(a[2], b[2])
    return out
}

/***/ }),
/* 238 */
/***/ (function(module, exports) {

module.exports = max;

/**
 * Returns the maximum of two vec3's
 *
 * @param {vec3} out the receiving vector
 * @param {vec3} a the first operand
 * @param {vec3} b the second operand
 * @returns {vec3} out
 */
function max(out, a, b) {
    out[0] = Math.max(a[0], b[0])
    out[1] = Math.max(a[1], b[1])
    out[2] = Math.max(a[2], b[2])
    return out
}

/***/ }),
/* 239 */
/***/ (function(module, exports) {

module.exports = floor

/**
 * Math.floor the components of a vec3
 *
 * @param {vec3} out the receiving vector
 * @param {vec3} a vector to floor
 * @returns {vec3} out
 */
function floor(out, a) {
  out[0] = Math.floor(a[0])
  out[1] = Math.floor(a[1])
  out[2] = Math.floor(a[2])
  return out
}


/***/ }),
/* 240 */
/***/ (function(module, exports) {

module.exports = ceil

/**
 * Math.ceil the components of a vec3
 *
 * @param {vec3} out the receiving vector
 * @param {vec3} a vector to ceil
 * @returns {vec3} out
 */
function ceil(out, a) {
  out[0] = Math.ceil(a[0])
  out[1] = Math.ceil(a[1])
  out[2] = Math.ceil(a[2])
  return out
}


/***/ }),
/* 241 */
/***/ (function(module, exports) {

module.exports = round

/**
 * Math.round the components of a vec3
 *
 * @param {vec3} out the receiving vector
 * @param {vec3} a vector to round
 * @returns {vec3} out
 */
function round(out, a) {
  out[0] = Math.round(a[0])
  out[1] = Math.round(a[1])
  out[2] = Math.round(a[2])
  return out
}


/***/ }),
/* 242 */
/***/ (function(module, exports) {

module.exports = scale;

/**
 * Scales a vec3 by a scalar number
 *
 * @param {vec3} out the receiving vector
 * @param {vec3} a the vector to scale
 * @param {Number} b amount to scale the vector by
 * @returns {vec3} out
 */
function scale(out, a, b) {
    out[0] = a[0] * b
    out[1] = a[1] * b
    out[2] = a[2] * b
    return out
}

/***/ }),
/* 243 */
/***/ (function(module, exports) {

module.exports = scaleAndAdd;

/**
 * Adds two vec3's after scaling the second operand by a scalar value
 *
 * @param {vec3} out the receiving vector
 * @param {vec3} a the first operand
 * @param {vec3} b the second operand
 * @param {Number} scale the amount to scale b by before adding
 * @returns {vec3} out
 */
function scaleAndAdd(out, a, b, scale) {
    out[0] = a[0] + (b[0] * scale)
    out[1] = a[1] + (b[1] * scale)
    out[2] = a[2] + (b[2] * scale)
    return out
}

/***/ }),
/* 244 */
/***/ (function(module, exports, __webpack_require__) {

module.exports = __webpack_require__(109)


/***/ }),
/* 245 */
/***/ (function(module, exports, __webpack_require__) {

module.exports = __webpack_require__(110)


/***/ }),
/* 246 */
/***/ (function(module, exports, __webpack_require__) {

module.exports = __webpack_require__(111)


/***/ }),
/* 247 */
/***/ (function(module, exports, __webpack_require__) {

module.exports = __webpack_require__(112)


/***/ }),
/* 248 */
/***/ (function(module, exports) {

module.exports = negate;

/**
 * Negates the components of a vec3
 *
 * @param {vec3} out the receiving vector
 * @param {vec3} a vector to negate
 * @returns {vec3} out
 */
function negate(out, a) {
    out[0] = -a[0]
    out[1] = -a[1]
    out[2] = -a[2]
    return out
}

/***/ }),
/* 249 */
/***/ (function(module, exports) {

module.exports = inverse;

/**
 * Returns the inverse of the components of a vec3
 *
 * @param {vec3} out the receiving vector
 * @param {vec3} a vector to invert
 * @returns {vec3} out
 */
function inverse(out, a) {
  out[0] = 1.0 / a[0]
  out[1] = 1.0 / a[1]
  out[2] = 1.0 / a[2]
  return out
}

/***/ }),
/* 250 */
/***/ (function(module, exports) {

module.exports = cross;

/**
 * Computes the cross product of two vec3's
 *
 * @param {vec3} out the receiving vector
 * @param {vec3} a the first operand
 * @param {vec3} b the second operand
 * @returns {vec3} out
 */
function cross(out, a, b) {
    var ax = a[0], ay = a[1], az = a[2],
        bx = b[0], by = b[1], bz = b[2]

    out[0] = ay * bz - az * by
    out[1] = az * bx - ax * bz
    out[2] = ax * by - ay * bx
    return out
}

/***/ }),
/* 251 */
/***/ (function(module, exports) {

module.exports = lerp;

/**
 * Performs a linear interpolation between two vec3's
 *
 * @param {vec3} out the receiving vector
 * @param {vec3} a the first operand
 * @param {vec3} b the second operand
 * @param {Number} t interpolation amount between the two inputs
 * @returns {vec3} out
 */
function lerp(out, a, b, t) {
    var ax = a[0],
        ay = a[1],
        az = a[2]
    out[0] = ax + t * (b[0] - ax)
    out[1] = ay + t * (b[1] - ay)
    out[2] = az + t * (b[2] - az)
    return out
}

/***/ }),
/* 252 */
/***/ (function(module, exports) {

module.exports = random;

/**
 * Generates a random vector with the given scale
 *
 * @param {vec3} out the receiving vector
 * @param {Number} [scale] Length of the resulting vector. If ommitted, a unit vector will be returned
 * @returns {vec3} out
 */
function random(out, scale) {
    scale = scale || 1.0

    var r = Math.random() * 2.0 * Math.PI
    var z = (Math.random() * 2.0) - 1.0
    var zScale = Math.sqrt(1.0-z*z) * scale

    out[0] = Math.cos(r) * zScale
    out[1] = Math.sin(r) * zScale
    out[2] = z * scale
    return out
}

/***/ }),
/* 253 */
/***/ (function(module, exports) {

module.exports = transformMat4;

/**
 * Transforms the vec3 with a mat4.
 * 4th vector component is implicitly '1'
 *
 * @param {vec3} out the receiving vector
 * @param {vec3} a the vector to transform
 * @param {mat4} m matrix to transform with
 * @returns {vec3} out
 */
function transformMat4(out, a, m) {
    var x = a[0], y = a[1], z = a[2],
        w = m[3] * x + m[7] * y + m[11] * z + m[15]
    w = w || 1.0
    out[0] = (m[0] * x + m[4] * y + m[8] * z + m[12]) / w
    out[1] = (m[1] * x + m[5] * y + m[9] * z + m[13]) / w
    out[2] = (m[2] * x + m[6] * y + m[10] * z + m[14]) / w
    return out
}

/***/ }),
/* 254 */
/***/ (function(module, exports) {

module.exports = transformMat3;

/**
 * Transforms the vec3 with a mat3.
 *
 * @param {vec3} out the receiving vector
 * @param {vec3} a the vector to transform
 * @param {mat4} m the 3x3 matrix to transform with
 * @returns {vec3} out
 */
function transformMat3(out, a, m) {
    var x = a[0], y = a[1], z = a[2]
    out[0] = x * m[0] + y * m[3] + z * m[6]
    out[1] = x * m[1] + y * m[4] + z * m[7]
    out[2] = x * m[2] + y * m[5] + z * m[8]
    return out
}

/***/ }),
/* 255 */
/***/ (function(module, exports) {

module.exports = transformQuat;

/**
 * Transforms the vec3 with a quat
 *
 * @param {vec3} out the receiving vector
 * @param {vec3} a the vector to transform
 * @param {quat} q quaternion to transform with
 * @returns {vec3} out
 */
function transformQuat(out, a, q) {
    // benchmarks: http://jsperf.com/quaternion-transform-vec3-implementations

    var x = a[0], y = a[1], z = a[2],
        qx = q[0], qy = q[1], qz = q[2], qw = q[3],

        // calculate quat * vec
        ix = qw * x + qy * z - qz * y,
        iy = qw * y + qz * x - qx * z,
        iz = qw * z + qx * y - qy * x,
        iw = -qx * x - qy * y - qz * z

    // calculate result * inverse quat
    out[0] = ix * qw + iw * -qx + iy * -qz - iz * -qy
    out[1] = iy * qw + iw * -qy + iz * -qx - ix * -qz
    out[2] = iz * qw + iw * -qz + ix * -qy - iy * -qx
    return out
}

/***/ }),
/* 256 */
/***/ (function(module, exports) {

module.exports = rotateX;

/**
 * Rotate a 3D vector around the x-axis
 * @param {vec3} out The receiving vec3
 * @param {vec3} a The vec3 point to rotate
 * @param {vec3} b The origin of the rotation
 * @param {Number} c The angle of rotation
 * @returns {vec3} out
 */
function rotateX(out, a, b, c){
    var by = b[1]
    var bz = b[2]

    // Translate point to the origin
    var py = a[1] - by
    var pz = a[2] - bz

    var sc = Math.sin(c)
    var cc = Math.cos(c)

    // perform rotation and translate to correct position
    out[0] = a[0]
    out[1] = by + py * cc - pz * sc
    out[2] = bz + py * sc + pz * cc

    return out
}


/***/ }),
/* 257 */
/***/ (function(module, exports) {

module.exports = rotateY;

/**
 * Rotate a 3D vector around the y-axis
 * @param {vec3} out The receiving vec3
 * @param {vec3} a The vec3 point to rotate
 * @param {vec3} b The origin of the rotation
 * @param {Number} c The angle of rotation
 * @returns {vec3} out
 */
function rotateY(out, a, b, c){
    var bx = b[0]
    var bz = b[2]

    // translate point to the origin
    var px = a[0] - bx
    var pz = a[2] - bz
    
    var sc = Math.sin(c)
    var cc = Math.cos(c)
  
    // perform rotation and translate to correct position
    out[0] = bx + pz * sc + px * cc
    out[1] = a[1]
    out[2] = bz + pz * cc - px * sc
  
    return out
}


/***/ }),
/* 258 */
/***/ (function(module, exports) {

module.exports = rotateZ;

/**
 * Rotate a 3D vector around the z-axis
 * @param {vec3} out The receiving vec3
 * @param {vec3} a The vec3 point to rotate
 * @param {vec3} b The origin of the rotation
 * @param {Number} c The angle of rotation
 * @returns {vec3} out
 */
function rotateZ(out, a, b, c){
    var bx = b[0]
    var by = b[1]

    //Translate point to the origin
    var px = a[0] - bx
    var py = a[1] - by
  
    var sc = Math.sin(c)
    var cc = Math.cos(c)

    // perform rotation and translate to correct position
    out[0] = bx + px * cc - py * sc
    out[1] = by + px * sc + py * cc
    out[2] = a[2]
  
    return out
}


/***/ }),
/* 259 */
/***/ (function(module, exports, __webpack_require__) {

module.exports = forEach;

var vec = __webpack_require__(102)()

/**
 * Perform some operation over an array of vec3s.
 *
 * @param {Array} a the array of vectors to iterate over
 * @param {Number} stride Number of elements between the start of each vec3. If 0 assumes tightly packed
 * @param {Number} offset Number of elements to skip at the beginning of the array
 * @param {Number} count Number of vec3s to iterate over. If 0 iterates over entire array
 * @param {Function} fn Function to call for each vector in the array
 * @param {Object} [arg] additional argument to pass to fn
 * @returns {Array} a
 * @function
 */
function forEach(a, stride, offset, count, fn, arg) {
        var i, l
        if(!stride) {
            stride = 3
        }

        if(!offset) {
            offset = 0
        }
        
        if(count) {
            l = Math.min((count * stride) + offset, a.length)
        } else {
            l = a.length
        }

        for(i = offset; i < l; i += stride) {
            vec[0] = a[i] 
            vec[1] = a[i+1] 
            vec[2] = a[i+2]
            fn(vec, vec, arg)
            a[i] = vec[0] 
            a[i+1] = vec[1] 
            a[i+2] = vec[2]
        }
        
        return a
}

/***/ }),
/* 260 */
/***/ (function(module, exports) {

module.exports = adjoint

/**
 * Calculates the adjugate of a mat3
 *
 * @alias mat3.adjoint
 * @param {mat3} out the receiving matrix
 * @param {mat3} a the source matrix
 * @returns {mat3} out
 */
function adjoint(out, a) {
  var a00 = a[0], a01 = a[1], a02 = a[2]
  var a10 = a[3], a11 = a[4], a12 = a[5]
  var a20 = a[6], a21 = a[7], a22 = a[8]

  out[0] = (a11 * a22 - a12 * a21)
  out[1] = (a02 * a21 - a01 * a22)
  out[2] = (a01 * a12 - a02 * a11)
  out[3] = (a12 * a20 - a10 * a22)
  out[4] = (a00 * a22 - a02 * a20)
  out[5] = (a02 * a10 - a00 * a12)
  out[6] = (a10 * a21 - a11 * a20)
  out[7] = (a01 * a20 - a00 * a21)
  out[8] = (a00 * a11 - a01 * a10)

  return out
}


/***/ }),
/* 261 */
/***/ (function(module, exports) {

module.exports = clone

/**
 * Creates a new mat3 initialized with values from an existing matrix
 *
 * @alias mat3.clone
 * @param {mat3} a matrix to clone
 * @returns {mat3} a new 3x3 matrix
 */
function clone(a) {
  var out = new Float32Array(9)
  out[0] = a[0]
  out[1] = a[1]
  out[2] = a[2]
  out[3] = a[3]
  out[4] = a[4]
  out[5] = a[5]
  out[6] = a[6]
  out[7] = a[7]
  out[8] = a[8]
  return out
}


/***/ }),
/* 262 */
/***/ (function(module, exports) {

module.exports = copy

/**
 * Copy the values from one mat3 to another
 *
 * @alias mat3.copy
 * @param {mat3} out the receiving matrix
 * @param {mat3} a the source matrix
 * @returns {mat3} out
 */
function copy(out, a) {
  out[0] = a[0]
  out[1] = a[1]
  out[2] = a[2]
  out[3] = a[3]
  out[4] = a[4]
  out[5] = a[5]
  out[6] = a[6]
  out[7] = a[7]
  out[8] = a[8]
  return out
}


/***/ }),
/* 263 */
/***/ (function(module, exports) {

module.exports = create

/**
 * Creates a new identity mat3
 *
 * @alias mat3.create
 * @returns {mat3} a new 3x3 matrix
 */
function create() {
  var out = new Float32Array(9)
  out[0] = 1
  out[1] = 0
  out[2] = 0
  out[3] = 0
  out[4] = 1
  out[5] = 0
  out[6] = 0
  out[7] = 0
  out[8] = 1
  return out
}


/***/ }),
/* 264 */
/***/ (function(module, exports) {

module.exports = determinant

/**
 * Calculates the determinant of a mat3
 *
 * @alias mat3.determinant
 * @param {mat3} a the source matrix
 * @returns {Number} determinant of a
 */
function determinant(a) {
  var a00 = a[0], a01 = a[1], a02 = a[2]
  var a10 = a[3], a11 = a[4], a12 = a[5]
  var a20 = a[6], a21 = a[7], a22 = a[8]

  return a00 * (a22 * a11 - a12 * a21)
       + a01 * (a12 * a20 - a22 * a10)
       + a02 * (a21 * a10 - a11 * a20)
}


/***/ }),
/* 265 */
/***/ (function(module, exports) {

module.exports = frob

/**
 * Returns Frobenius norm of a mat3
 *
 * @alias mat3.frob
 * @param {mat3} a the matrix to calculate Frobenius norm of
 * @returns {Number} Frobenius norm
 */
function frob(a) {
  return Math.sqrt(
      a[0]*a[0]
    + a[1]*a[1]
    + a[2]*a[2]
    + a[3]*a[3]
    + a[4]*a[4]
    + a[5]*a[5]
    + a[6]*a[6]
    + a[7]*a[7]
    + a[8]*a[8]
  )
}


/***/ }),
/* 266 */
/***/ (function(module, exports) {

module.exports = fromMat2d

/**
 * Copies the values from a mat2d into a mat3
 *
 * @alias mat3.fromMat2d
 * @param {mat3} out the receiving matrix
 * @param {mat2d} a the matrix to copy
 * @returns {mat3} out
 **/
function fromMat2d(out, a) {
  out[0] = a[0]
  out[1] = a[1]
  out[2] = 0

  out[3] = a[2]
  out[4] = a[3]
  out[5] = 0

  out[6] = a[4]
  out[7] = a[5]
  out[8] = 1

  return out
}


/***/ }),
/* 267 */
/***/ (function(module, exports) {

module.exports = fromMat4

/**
 * Copies the upper-left 3x3 values into the given mat3.
 *
 * @alias mat3.fromMat4
 * @param {mat3} out the receiving 3x3 matrix
 * @param {mat4} a   the source 4x4 matrix
 * @returns {mat3} out
 */
function fromMat4(out, a) {
  out[0] = a[0]
  out[1] = a[1]
  out[2] = a[2]
  out[3] = a[4]
  out[4] = a[5]
  out[5] = a[6]
  out[6] = a[8]
  out[7] = a[9]
  out[8] = a[10]
  return out
}


/***/ }),
/* 268 */
/***/ (function(module, exports) {

module.exports = fromQuat

/**
* Calculates a 3x3 matrix from the given quaternion
*
* @alias mat3.fromQuat
* @param {mat3} out mat3 receiving operation result
* @param {quat} q Quaternion to create matrix from
*
* @returns {mat3} out
*/
function fromQuat(out, q) {
  var x = q[0]
  var y = q[1]
  var z = q[2]
  var w = q[3]

  var x2 = x + x
  var y2 = y + y
  var z2 = z + z

  var xx = x * x2
  var yx = y * x2
  var yy = y * y2
  var zx = z * x2
  var zy = z * y2
  var zz = z * z2
  var wx = w * x2
  var wy = w * y2
  var wz = w * z2

  out[0] = 1 - yy - zz
  out[3] = yx - wz
  out[6] = zx + wy

  out[1] = yx + wz
  out[4] = 1 - xx - zz
  out[7] = zy - wx

  out[2] = zx - wy
  out[5] = zy + wx
  out[8] = 1 - xx - yy

  return out
}


/***/ }),
/* 269 */
/***/ (function(module, exports) {

module.exports = identity

/**
 * Set a mat3 to the identity matrix
 *
 * @alias mat3.identity
 * @param {mat3} out the receiving matrix
 * @returns {mat3} out
 */
function identity(out) {
  out[0] = 1
  out[1] = 0
  out[2] = 0
  out[3] = 0
  out[4] = 1
  out[5] = 0
  out[6] = 0
  out[7] = 0
  out[8] = 1
  return out
}


/***/ }),
/* 270 */
/***/ (function(module, exports) {

module.exports = invert

/**
 * Inverts a mat3
 *
 * @alias mat3.invert
 * @param {mat3} out the receiving matrix
 * @param {mat3} a the source matrix
 * @returns {mat3} out
 */
function invert(out, a) {
  var a00 = a[0], a01 = a[1], a02 = a[2]
  var a10 = a[3], a11 = a[4], a12 = a[5]
  var a20 = a[6], a21 = a[7], a22 = a[8]

  var b01 = a22 * a11 - a12 * a21
  var b11 = -a22 * a10 + a12 * a20
  var b21 = a21 * a10 - a11 * a20

  // Calculate the determinant
  var det = a00 * b01 + a01 * b11 + a02 * b21

  if (!det) return null
  det = 1.0 / det

  out[0] = b01 * det
  out[1] = (-a22 * a01 + a02 * a21) * det
  out[2] = (a12 * a01 - a02 * a11) * det
  out[3] = b11 * det
  out[4] = (a22 * a00 - a02 * a20) * det
  out[5] = (-a12 * a00 + a02 * a10) * det
  out[6] = b21 * det
  out[7] = (-a21 * a00 + a01 * a20) * det
  out[8] = (a11 * a00 - a01 * a10) * det

  return out
}


/***/ }),
/* 271 */
/***/ (function(module, exports) {

module.exports = multiply

/**
 * Multiplies two mat3's
 *
 * @alias mat3.multiply
 * @param {mat3} out the receiving matrix
 * @param {mat3} a the first operand
 * @param {mat3} b the second operand
 * @returns {mat3} out
 */
function multiply(out, a, b) {
  var a00 = a[0], a01 = a[1], a02 = a[2]
  var a10 = a[3], a11 = a[4], a12 = a[5]
  var a20 = a[6], a21 = a[7], a22 = a[8]

  var b00 = b[0], b01 = b[1], b02 = b[2]
  var b10 = b[3], b11 = b[4], b12 = b[5]
  var b20 = b[6], b21 = b[7], b22 = b[8]

  out[0] = b00 * a00 + b01 * a10 + b02 * a20
  out[1] = b00 * a01 + b01 * a11 + b02 * a21
  out[2] = b00 * a02 + b01 * a12 + b02 * a22

  out[3] = b10 * a00 + b11 * a10 + b12 * a20
  out[4] = b10 * a01 + b11 * a11 + b12 * a21
  out[5] = b10 * a02 + b11 * a12 + b12 * a22

  out[6] = b20 * a00 + b21 * a10 + b22 * a20
  out[7] = b20 * a01 + b21 * a11 + b22 * a21
  out[8] = b20 * a02 + b21 * a12 + b22 * a22

  return out
}


/***/ }),
/* 272 */
/***/ (function(module, exports) {

module.exports = normalFromMat4

/**
* Calculates a 3x3 normal matrix (transpose inverse) from the 4x4 matrix
*
* @alias mat3.normalFromMat4
* @param {mat3} out mat3 receiving operation result
* @param {mat4} a Mat4 to derive the normal matrix from
*
* @returns {mat3} out
*/
function normalFromMat4(out, a) {
  var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3]
  var a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7]
  var a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11]
  var a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]

  var b00 = a00 * a11 - a01 * a10
  var b01 = a00 * a12 - a02 * a10
  var b02 = a00 * a13 - a03 * a10
  var b03 = a01 * a12 - a02 * a11
  var b04 = a01 * a13 - a03 * a11
  var b05 = a02 * a13 - a03 * a12
  var b06 = a20 * a31 - a21 * a30
  var b07 = a20 * a32 - a22 * a30
  var b08 = a20 * a33 - a23 * a30
  var b09 = a21 * a32 - a22 * a31
  var b10 = a21 * a33 - a23 * a31
  var b11 = a22 * a33 - a23 * a32

  // Calculate the determinant
  var det = b00 * b11
          - b01 * b10
          + b02 * b09
          + b03 * b08
          - b04 * b07
          + b05 * b06

  if (!det) return null
  det = 1.0 / det

  out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det
  out[1] = (a12 * b08 - a10 * b11 - a13 * b07) * det
  out[2] = (a10 * b10 - a11 * b08 + a13 * b06) * det

  out[3] = (a02 * b10 - a01 * b11 - a03 * b09) * det
  out[4] = (a00 * b11 - a02 * b08 + a03 * b07) * det
  out[5] = (a01 * b08 - a00 * b10 - a03 * b06) * det

  out[6] = (a31 * b05 - a32 * b04 + a33 * b03) * det
  out[7] = (a32 * b02 - a30 * b05 - a33 * b01) * det
  out[8] = (a30 * b04 - a31 * b02 + a33 * b00) * det

  return out
}


/***/ }),
/* 273 */
/***/ (function(module, exports) {

module.exports = rotate

/**
 * Rotates a mat3 by the given angle
 *
 * @alias mat3.rotate
 * @param {mat3} out the receiving matrix
 * @param {mat3} a the matrix to rotate
 * @param {Number} rad the angle to rotate the matrix by
 * @returns {mat3} out
 */
function rotate(out, a, rad) {
  var a00 = a[0], a01 = a[1], a02 = a[2]
  var a10 = a[3], a11 = a[4], a12 = a[5]
  var a20 = a[6], a21 = a[7], a22 = a[8]

  var s = Math.sin(rad)
  var c = Math.cos(rad)

  out[0] = c * a00 + s * a10
  out[1] = c * a01 + s * a11
  out[2] = c * a02 + s * a12

  out[3] = c * a10 - s * a00
  out[4] = c * a11 - s * a01
  out[5] = c * a12 - s * a02

  out[6] = a20
  out[7] = a21
  out[8] = a22

  return out
}


/***/ }),
/* 274 */
/***/ (function(module, exports) {

module.exports = scale

/**
 * Scales the mat3 by the dimensions in the given vec2
 *
 * @alias mat3.scale
 * @param {mat3} out the receiving matrix
 * @param {mat3} a the matrix to rotate
 * @param {vec2} v the vec2 to scale the matrix by
 * @returns {mat3} out
 **/
function scale(out, a, v) {
  var x = v[0]
  var y = v[1]

  out[0] = x * a[0]
  out[1] = x * a[1]
  out[2] = x * a[2]

  out[3] = y * a[3]
  out[4] = y * a[4]
  out[5] = y * a[5]

  out[6] = a[6]
  out[7] = a[7]
  out[8] = a[8]

  return out
}


/***/ }),
/* 275 */
/***/ (function(module, exports) {

module.exports = str

/**
 * Returns a string representation of a mat3
 *
 * @alias mat3.str
 * @param {mat3} mat matrix to represent as a string
 * @returns {String} string representation of the matrix
 */
function str(a) {
  return 'mat3(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' +
                   a[3] + ', ' + a[4] + ', ' + a[5] + ', ' +
                   a[6] + ', ' + a[7] + ', ' + a[8] + ')'
}


/***/ }),
/* 276 */
/***/ (function(module, exports) {

module.exports = translate

/**
 * Translate a mat3 by the given vector
 *
 * @alias mat3.translate
 * @param {mat3} out the receiving matrix
 * @param {mat3} a the matrix to translate
 * @param {vec2} v vector to translate by
 * @returns {mat3} out
 */
function translate(out, a, v) {
  var a00 = a[0], a01 = a[1], a02 = a[2]
  var a10 = a[3], a11 = a[4], a12 = a[5]
  var a20 = a[6], a21 = a[7], a22 = a[8]
  var x = v[0], y = v[1]

  out[0] = a00
  out[1] = a01
  out[2] = a02

  out[3] = a10
  out[4] = a11
  out[5] = a12

  out[6] = x * a00 + y * a10 + a20
  out[7] = x * a01 + y * a11 + a21
  out[8] = x * a02 + y * a12 + a22

  return out
}


/***/ }),
/* 277 */
/***/ (function(module, exports) {

module.exports = transpose

/**
 * Transpose the values of a mat3
 *
 * @alias mat3.transpose
 * @param {mat3} out the receiving matrix
 * @param {mat3} a the source matrix
 * @returns {mat3} out
 */
function transpose(out, a) {
  // If we are transposing ourselves we can skip a few steps but have to cache some values
  if (out === a) {
    var a01 = a[1], a02 = a[2], a12 = a[5]
    out[1] = a[3]
    out[2] = a[6]
    out[3] = a01
    out[5] = a[7]
    out[6] = a02
    out[7] = a12
  } else {
    out[0] = a[0]
    out[1] = a[3]
    out[2] = a[6]
    out[3] = a[1]
    out[4] = a[4]
    out[5] = a[7]
    out[6] = a[2]
    out[7] = a[5]
    out[8] = a[8]
  }

  return out
}


/***/ }),
/* 278 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var sort = __webpack_require__(279);
var range = __webpack_require__(280);
var within = __webpack_require__(281);

module.exports = kdbush;

function kdbush(points, getX, getY, nodeSize, ArrayType) {
    return new KDBush(points, getX, getY, nodeSize, ArrayType);
}

function KDBush(points, getX, getY, nodeSize, ArrayType) {
    getX = getX || defaultGetX;
    getY = getY || defaultGetY;
    ArrayType = ArrayType || Array;

    this.nodeSize = nodeSize || 64;
    this.points = points;

    this.ids = new ArrayType(points.length);
    this.coords = new ArrayType(points.length * 2);

    for (var i = 0; i < points.length; i++) {
        this.ids[i] = i;
        this.coords[2 * i] = getX(points[i]);
        this.coords[2 * i + 1] = getY(points[i]);
    }

    sort(this.ids, this.coords, this.nodeSize, 0, this.ids.length - 1, 0);
}

KDBush.prototype = {
    range: function (minX, minY, maxX, maxY) {
        return range(this.ids, this.coords, minX, minY, maxX, maxY, this.nodeSize);
    },

    within: function (x, y, r) {
        return within(this.ids, this.coords, x, y, r, this.nodeSize);
    }
};

function defaultGetX(p) { return p[0]; }
function defaultGetY(p) { return p[1]; }


/***/ }),
/* 279 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


module.exports = sortKD;

function sortKD(ids, coords, nodeSize, left, right, depth) {
    if (right - left <= nodeSize) return;

    var m = Math.floor((left + right) / 2);

    select(ids, coords, m, left, right, depth % 2);

    sortKD(ids, coords, nodeSize, left, m - 1, depth + 1);
    sortKD(ids, coords, nodeSize, m + 1, right, depth + 1);
}

function select(ids, coords, k, left, right, inc) {

    while (right > left) {
        if (right - left > 600) {
            var n = right - left + 1;
            var m = k - left + 1;
            var z = Math.log(n);
            var s = 0.5 * Math.exp(2 * z / 3);
            var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
            var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
            var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
            select(ids, coords, k, newLeft, newRight, inc);
        }

        var t = coords[2 * k + inc];
        var i = left;
        var j = right;

        swapItem(ids, coords, left, k);
        if (coords[2 * right + inc] > t) swapItem(ids, coords, left, right);

        while (i < j) {
            swapItem(ids, coords, i, j);
            i++;
            j--;
            while (coords[2 * i + inc] < t) i++;
            while (coords[2 * j + inc] > t) j--;
        }

        if (coords[2 * left + inc] === t) swapItem(ids, coords, left, j);
        else {
            j++;
            swapItem(ids, coords, j, right);
        }

        if (j <= k) left = j + 1;
        if (k <= j) right = j - 1;
    }
}

function swapItem(ids, coords, i, j) {
    swap(ids, i, j);
    swap(coords, 2 * i, 2 * j);
    swap(coords, 2 * i + 1, 2 * j + 1);
}

function swap(arr, i, j) {
    var tmp = arr[i];
    arr[i] = arr[j];
    arr[j] = tmp;
}


/***/ }),
/* 280 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


module.exports = range;

function range(ids, coords, minX, minY, maxX, maxY, nodeSize) {
    var stack = [0, ids.length - 1, 0];
    var result = [];
    var x, y;

    while (stack.length) {
        var axis = stack.pop();
        var right = stack.pop();
        var left = stack.pop();

        if (right - left <= nodeSize) {
            for (var i = left; i <= right; i++) {
                x = coords[2 * i];
                y = coords[2 * i + 1];
                if (x >= minX && x <= maxX && y >= minY && y <= maxY) result.push(ids[i]);
            }
            continue;
        }

        var m = Math.floor((left + right) / 2);

        x = coords[2 * m];
        y = coords[2 * m + 1];

        if (x >= minX && x <= maxX && y >= minY && y <= maxY) result.push(ids[m]);

        var nextAxis = (axis + 1) % 2;

        if (axis === 0 ? minX <= x : minY <= y) {
            stack.push(left);
            stack.push(m - 1);
            stack.push(nextAxis);
        }
        if (axis === 0 ? maxX >= x : maxY >= y) {
            stack.push(m + 1);
            stack.push(right);
            stack.push(nextAxis);
        }
    }

    return result;
}


/***/ }),
/* 281 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


module.exports = within;

function within(ids, coords, qx, qy, r, nodeSize) {
    var stack = [0, ids.length - 1, 0];
    var result = [];
    var r2 = r * r;

    while (stack.length) {
        var axis = stack.pop();
        var right = stack.pop();
        var left = stack.pop();

        if (right - left <= nodeSize) {
            for (var i = left; i <= right; i++) {
                if (sqDist(coords[2 * i], coords[2 * i + 1], qx, qy) <= r2) result.push(ids[i]);
            }
            continue;
        }

        var m = Math.floor((left + right) / 2);

        var x = coords[2 * m];
        var y = coords[2 * m + 1];

        if (sqDist(x, y, qx, qy) <= r2) result.push(ids[m]);

        var nextAxis = (axis + 1) % 2;

        if (axis === 0 ? qx - r <= x : qy - r <= y) {
            stack.push(left);
            stack.push(m - 1);
            stack.push(nextAxis);
        }
        if (axis === 0 ? qx + r >= x : qy + r >= y) {
            stack.push(m + 1);
            stack.push(right);
            stack.push(nextAxis);
        }
    }

    return result;
}

function sqDist(ax, ay, bx, by) {
    var dx = ax - bx;
    var dy = ay - by;
    return dx * dx + dy * dy;
}


/***/ }),
/* 282 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var inherit = __webpack_require__(0);
var featureLayer = __webpack_require__(35);
var geo_annotation = __webpack_require__(57);
var geo_event = __webpack_require__(6);
var registry = __webpack_require__(1);
var transform = __webpack_require__(7);
var $ = __webpack_require__(2);
var Mousetrap = __webpack_require__(117);
var textFeature = __webpack_require__(25);

/**
 * @typedef {object} geo.annotationLayer.labelRecord
 * @property {string} text The text of the label
 * @property {geo.geoPosition} position The position of the label in map gcs
 *    coordinates.
 * @property {geo.textFeature.styleSpec} [style] A {@link geo.textFeature}
 *    style object.
 */

/**
 * Layer to handle direct interactions with different features.  Annotations
 * (features) can be created by calling mode(<name of feature>) or cancelled
 * with mode(null).  There is also an "edit" mode which is used when modifying
 * an annotation.
 *
 * @class
 * @alias geo.annotationLayer
 * @extends geo.featureLayer
 * @param {object} [args] Layer options.
 * @param {number} [args.dblClickTime=300] The delay in milliseconds that is
 *    treated as a double-click when working with annotations.
 * @param {number} [args.adjacentPointProximity=5] The minimum distance in
 *    display coordinates (pixels) between two adjacent points when creating a
 *    polygon or line.  A value of 0 requires an exact match.
 * @param {number} [args.continuousPointProximity=5] The minimum distance in
 *    display coordinates (pixels) between two adjacent points when dragging
 *    to create an annotation.  `false` disables continuous drawing mode.
 * @param {number} [args.continuousPointColinearity=1.0deg] The minimum
 *    angle between a series of three points when dragging to not interpret
 *    them as colinear.  Only applies if `continuousPointProximity` is not
 *    `false`.
 * @param {number} [args.finalPointProximity=10] The maximum distance in
 *    display coordinates (pixels) between the starting point and the mouse
 *    coordinates to signal closing a polygon.  A value of 0 requires an exact
 *    match.  A negative value disables closing a polygon by clicking on the
 *    start point.
 * @param {boolean} [args.showLabels=true] Truthy to show feature labels that
 *    are allowed by the associated feature to be shown.
 * @param {boolean} [args.clickToEdit=false] Truthy to allow clicking an
 *    annotation to place it in edit mode.
 * @param {object} [args.defaultLabelStyle] Default styles for labels.
 * @returns {geo.annotationLayer}
 * @fires geo.event.annotation.state
 * @fires geo.event.annotation.coordinates
 * @fires geo.event.annotation.edit_action
 * @fires geo.event.annotation.select_edit_handle
 */
var annotationLayer = function annotationLayer(args) {
  'use strict';

  if (!(this instanceof annotationLayer)) {
    return new annotationLayer(args);
  }
  featureLayer.call(this, args);

  var mapInteractor = __webpack_require__(51);
  var timestamp = __webpack_require__(10);
  var util = __webpack_require__(3);

  var m_this = this,
      s_init = this._init,
      s_exit = this._exit,
      s_draw = this.draw,
      s_update = this._update,
      m_buildTime = timestamp(),
      m_options,
      m_mode = null,
      m_annotations = [],
      m_features = [],
      m_labelFeature,
      m_labelLayer,
      m_keyHandler;

  var geojsonStyleProperties = {
    'closed': { dataType: 'boolean', keys: ['closed', 'close'] },
    'fill': { dataType: 'boolean', keys: ['fill'] },
    'fillColor': { dataType: 'color', keys: ['fillColor', 'fill-color', 'marker-color', 'fill'] },
    'fillOpacity': { dataType: 'opacity', keys: ['fillOpacity', 'fill-opacity'] },
    'lineCap': { dataType: 'text', keys: ['lineCap', 'line-cap'] },
    'lineJoin': { dataType: 'text', keys: ['lineJoin', 'line-join'] },
    'radius': { dataType: 'positive', keys: ['radius'] },
    'scaled': { dataType: 'booleanOrNumber', keys: ['scaled'] },
    'stroke': { dataType: 'boolean', keys: ['stroke'] },
    'strokeColor': { dataType: 'color', keys: ['strokeColor', 'stroke-color', 'stroke'] },
    'strokeOffset': { dataType: 'number', keys: ['strokeOffset', 'stroke-offset'] },
    'strokeOpacity': { dataType: 'opacity', keys: ['strokeOpacity', 'stroke-opacity'] },
    'strokeWidth': { dataType: 'positive', keys: ['strokeWidth', 'stroke-width'] }
  };
  textFeature.usedStyles.forEach(function (key) {
    geojsonStyleProperties[key] = {
      option: 'labelStyle',
      dataType: ['visible', 'rotateWithMap', 'scaleWithMap'].indexOf(key) >= 0 ? 'boolean' : ['scale'].indexOf(key) >= 0 ? 'booleanOrNumber' : ['rotation'].indexOf(key) >= 0 ? 'angle' : ['offset', 'shadowOffset'].indexOf(key) >= 0 ? 'coordinate2' : ['shadowBlur, strokeWidth'].indexOf(key) >= 0 ? 'numberOrBlank' : 'text',
      keys: [key, 'label' + key.charAt(0).toUpperCase() + key.slice(1), key.replace(/([A-Z])/g, '-$1').toLowerCase(), 'label-' + key.replace(/([A-Z])/g, '-$1').toLowerCase()]
    };
  });

  m_options = $.extend(true, {}, {
    dblClickTime: 300,
    adjacentPointProximity: 5, // in pixels, 0 is exact
    // in pixels; set to continuousPointProximity to false to disable
    // continuous drawing modes.
    continuousPointProximity: 5,
    // in radians, minimum angle between continuous points to interpret them as
    // being coliner
    continuousPointColinearity: 1.0 * Math.PI / 180,
    finalPointProximity: 10, // in pixels, 0 is exact
    showLabels: true,
    clickToEdit: false
  }, args);

  /**
   * Process an action event.  If we are in rectangle-creation mode, this
   * creates a rectangle.
   *
   * @param {geo.event} evt The selection event.
   * @fires geo.event.annotation.edit_action
   */
  this._processAction = function (evt) {
    var update;
    if (evt.state && evt.state.actionRecord && evt.state.actionRecord.owner === geo_annotation.actionOwner && m_this.currentAnnotation) {
      switch (m_this.mode()) {
        case m_this.modes.edit:
          update = m_this.currentAnnotation.processEditAction(evt);
          if (m_this.currentAnnotation && m_this.currentAnnotation._editHandle && m_this.currentAnnotation._editHandle.handle) {
            m_this.geoTrigger(geo_event.annotation.edit_action, {
              annotation: m_this.currentAnnotation,
              handle: m_this.currentAnnotation._editHandle ? m_this.currentAnnotation._editHandle.handle : undefined,
              action: evt.event
            });
            if (evt.event === geo_event.actionup) {
              m_this._selectEditHandle({
                data: m_this.currentAnnotation._editHandle.handle }, m_this.currentAnnotation._editHandle.handle.selected);
            }
          }
          break;
        default:
          update = m_this.currentAnnotation.processAction(evt);
          break;
      }
    }
    m_this._updateFromEvent(update);
  };

  /**
   * Handle updating the current annotation based on an update state.
   *
   * @param {string|undefined} update Truthy to update.  `'done'` if the
   *    annotation was completed and the mode should return to `null`.
   *    `'remove'` to remove the current annotation and set the mode to `null`.
   *    Falsy to do nothing.
   */
  this._updateFromEvent = function (update) {
    switch (update) {
      case 'remove':
        m_this.removeAnnotation(m_this.currentAnnotation, false);
        m_this.mode(null);
        break;
      case 'done':
        m_this.mode(null);
        break;
    }
    if (update) {
      m_this.modified();
      m_this.draw();
    }
  };

  /**
   * Handle mouse movement.  If there is a current annotation, the movement
   * event is sent to it.
   *
   * @param {geo.event} evt The mouse move event.
   */
  this._handleMouseMove = function (evt) {
    if (m_this.mode() && m_this.currentAnnotation) {
      var update = m_this.currentAnnotation.mouseMove(evt);
      if (update) {
        m_this.modified();
        m_this.draw();
      }
    }
  };

  /**
   * Select or deselect an edit handle.
   *
   * @param {geo.event} evt The mouse move event.
   * @param {boolean} enable Truthy to select the handle, falsy to deselect it.
   * @returns {this}
   */
  this._selectEditHandle = function (evt, enable) {
    if (!evt.data || !evt.data.editHandle) {
      return;
    }
    $.each(m_features[geo_annotation._editHandleFeatureLevel], function (type, feature) {
      feature.feature.modified();
    });
    m_this.currentAnnotation.selectEditHandle(evt.data, enable);
    m_this.draw();
    m_this.map().node().toggleClass('annotation-input', !!enable);
    m_this.map().interactor().removeAction(undefined, undefined, geo_annotation.actionOwner);
    if (enable) {
      var actions = m_this.currentAnnotation.actions(geo_annotation.state.edit);
      $.each(actions, function (idx, action) {
        m_this.map().interactor().addAction(action);
      });
    }
    return m_this;
  };

  /**
   * Handle mouse on events.  If there is no current annotation and
   * clickToEdit is enabled, any hovered annotation is highlighted.
   * event is sent to it.
   *
   * @param {geo.event} evt The mouse move event.
   */
  this._handleMouseOn = function (evt) {
    if (!evt.data || !evt.data.annotation) {
      return;
    }
    if (m_this.mode() === m_this.modes.edit && m_this.currentAnnotation) {
      m_this._selectEditHandle(evt, true);
      return;
    }
    if (m_this.mode() || m_this.currentAnnotation || !m_this.options('clickToEdit')) {
      return;
    }
    evt.data.annotation.state(geo_annotation.state.highlight);
    m_this.modified();
    m_this.draw();
  };

  /**
   * Handle mouse off events.  If the specific annotation is in the highlight
   * state, move it back to the done state.
   *
   * @param {geo.event} evt The mouse move event.
   */
  this._handleMouseOff = function (evt) {
    if (!evt.data || !evt.data.annotation) {
      return;
    }
    if (m_this.mode() === m_this.modes.edit && evt.data.editHandle && evt.data.selected) {
      m_this._selectEditHandle(evt, false);
      return;
    }
    if (evt.data.annotation.state() === geo_annotation.state.highlight) {
      evt.data.annotation.state(geo_annotation.state.done);
      m_this.modified();
      m_this.draw();
    }
  };

  /**
   * Handle mouse clicks.  If there is a current annotation, the click event is
   * sent to it.
   *
   * @param {geo.event} evt The mouse click event.
   */
  this._handleMouseClick = function (evt) {
    var retrigger = false,
        update;
    if (m_this.mode() === m_this.modes.edit) {
      if (m_this.map().interactor().hasAction(undefined, undefined, geo_annotation.actionOwner)) {
        update = m_this.currentAnnotation.mouseClickEdit(evt);
        m_this._updateFromEvent(update);
        return;
      }
      m_this.mode(null);
      m_this.draw();
      $.each(m_features, function (idx, featureLevel) {
        $.each(featureLevel, function (type, feature) {
          feature.feature._clearSelectedFeatures();
        });
      });
      retrigger = true;
    } else if (m_this.mode() && m_this.currentAnnotation) {
      update = m_this.currentAnnotation.mouseClick(evt);
      m_this._updateFromEvent(update);
      retrigger = !m_this.mode();
    } else if (!m_this.mode() && !m_this.currentAnnotation && m_this.options('clickToEdit')) {
      var highlighted = m_this.annotations().filter(function (ann) {
        return ann.state() === geo_annotation.state.highlight;
      });
      if (highlighted.length !== 1) {
        return;
      }
      m_this.mode(m_this.modes.edit, highlighted[0]);
      m_this.draw();
      retrigger = true;
    }
    if (retrigger) {
      // retrigger mouse move to ensure the correct events are attached
      m_this.map().interactor().retriggerMouseMove();
    }
  };

  /**
   * Set or get options.
   *
   * @param {string|object} [arg1] If `undefined`, return the options object.
   *    If a string, either set or return the option of that name.  If an
   *    object, update the options with the object's values.
   * @param {object} [arg2] If `arg1` is a string and this is defined, set
   *    the option to this value.
   * @returns {object|this} If options are set, return the annotation,
   *    otherwise return the requested option or the set of options.
   */
  this.options = function (arg1, arg2) {
    if (arg1 === undefined) {
      return m_options;
    }
    if (typeof arg1 === 'string' && arg2 === undefined) {
      return m_options[arg1];
    }
    if (arg2 === undefined) {
      m_options = $.extend(true, m_options, arg1);
    } else {
      m_options[arg1] = arg2;
    }
    m_this.modified();
    return m_this;
  };

  /**
   * Calculate the display distance for two coordinate in the current map.
   *
   * @param {geo.geoPosition|geo.screenPosition} coord1 The first coordinates.
   * @param {string|geo.transform|null} gcs1 `undefined` to use the interface
   *    gcs, `null` to use the map gcs, `'display'` if the coordinates are
   *    already in display coordinates, or any other transform.
   * @param {geo.geoPosition|geo.screenPosition} coord2 the second coordinates.
   * @param {string|geo.transform|null} [gcs2] `undefined` to use the interface
   *    gcs, `null` to use the map gcs, `'display'` if the coordinates are
   *    already in display coordinates, or any other transform.
   * @returns {number} the Euclidian distance between the two coordinates.
   */
  this.displayDistance = function (coord1, gcs1, coord2, gcs2) {
    var map = m_this.map();
    if (gcs1 !== 'display') {
      gcs1 = gcs1 === null ? map.gcs() : gcs1 === undefined ? map.ingcs() : gcs1;
      coord1 = map.gcsToDisplay(coord1, gcs1);
    }
    if (gcs2 !== 'display') {
      gcs2 = gcs2 === null ? map.gcs() : gcs2 === undefined ? map.ingcs() : gcs2;
      coord2 = map.gcsToDisplay(coord2, gcs2);
    }
    var dist = Math.sqrt(Math.pow(coord1.x - coord2.x, 2) + Math.pow(coord1.y - coord2.y, 2));
    return dist;
  };

  /**
   * Add an annotation to the layer.  The annotation could be in any state.
   *
   * @param {geo.annotation} annotation Te annotation to add.
   * @param {string|geo.transform|null} [gcs] `undefined` to use the interface
   *    gcs, `null` to use the map gcs, or any other transform.
   * @returns {this} The current layer.
   * @fires geo.event.annotation.add_before
   * @fires geo.event.annotation.add
   */
  this.addAnnotation = function (annotation, gcs) {
    var pos = $.inArray(annotation, m_annotations);
    if (pos < 0) {
      m_this.geoTrigger(geo_event.annotation.add_before, {
        annotation: annotation
      });
      m_annotations.push(annotation);
      annotation.layer(m_this);
      var map = m_this.map();
      gcs = gcs === null ? map.gcs() : gcs === undefined ? map.ingcs() : gcs;
      if (gcs !== map.gcs()) {
        annotation._coordinates(transform.transformCoordinates(gcs, map.gcs(), annotation._coordinates()));
      }
      m_this.modified();
      m_this.draw();
      m_this.geoTrigger(geo_event.annotation.add, {
        annotation: annotation
      });
    }
    return m_this;
  };

  /**
   * Remove an annotation from the layer.
   *
   * @param {geo.annoation} annotation The annotation to remove.
   * @param {boolean} update If `false`, don't update the layer after removing
   *    the annotation.
   * @returns {boolean} `true` if an annotation was removed.
   * @fires geo.event.annotation.remove
   */
  this.removeAnnotation = function (annotation, update) {
    var pos = $.inArray(annotation, m_annotations);
    if (pos >= 0) {
      if (annotation === m_this.currentAnnotation) {
        m_this.currentAnnotation = null;
      }
      annotation._exit();
      m_annotations.splice(pos, 1);
      if (update !== false) {
        m_this.modified();
        m_this.draw();
      }
      m_this.geoTrigger(geo_event.annotation.remove, {
        annotation: annotation
      });
    }
    return pos >= 0;
  };

  /**
   * Remove all annotations from the layer.
   *
   * @param {boolean} [skipCreating] If truthy, don't remove annotations that
   *    are in the create state.
   * @param {boolean} [update] If `false`, don't update the layer after
   *    removing the annotation.
   * @returns {number} The number of annotations that were removed.
   */
  this.removeAllAnnotations = function (skipCreating, update) {
    var removed = 0,
        annotation,
        pos = 0;
    while (pos < m_annotations.length) {
      annotation = m_annotations[pos];
      if (skipCreating && annotation.state() === geo_annotation.state.create) {
        pos += 1;
        continue;
      }
      m_this.removeAnnotation(annotation, false);
      removed += 1;
    }
    if (removed && update !== false) {
      m_this.modified();
      m_this.draw();
    }
    return removed;
  };

  /**
   * Get the list of annotations on the layer.
   *
   * @returns {geo.annoation[]} An array of annotations.
   */
  this.annotations = function () {
    return m_annotations.slice();
  };

  /**
   * Get an annotation by its id.
   *
   * @param {number} id The annotation ID.
   * @returns {geo.annotation} The selected annotation or `undefined` if none
   *    matches the id.
   */
  this.annotationById = function (id) {
    if (id !== undefined && id !== null) {
      id = +id; /* Cast to int */
    }
    var annotations = m_annotations.filter(function (annotation) {
      return annotation.id() === id;
    });
    if (annotations.length) {
      return annotations[0];
    }
  };

  /* A list of special modes */
  this.modes = {
    edit: 'edit'
  };

  /**
   * Get or set the current mode.
   *
   * @param {string|null} [arg] `undefined` to get the current mode, `null` to
   *    stop creating/editing, `this.modes.edit` (`'edit'`) plus an annotation
   *    to switch to edit mode, or the name of the type of annotation to
   *    create.  Available annotations can listed via
   *    {@link geo.listAnnotations}.
   * @param {geo.annotation} [editAnnotation] If `arg === this.modes.edit`,
   *    this is the annotation that should be edited.
   * @returns {string|null|this} The current mode or the layer.
   * @fires geo.event.annotation.mode
   */
  this.mode = function (arg, editAnnotation) {
    if (arg === undefined) {
      return m_mode;
    }
    if (arg !== m_mode || arg === m_this.modes.edit && editAnnotation !== m_this.editAnnotation) {
      var createAnnotation,
          actions,
          mapNode = m_this.map().node(),
          oldMode = m_mode;
      m_mode = arg;
      mapNode.toggleClass('annotation-input', !!(m_mode && m_mode !== m_this.modes.edit));
      if (!m_keyHandler) {
        m_keyHandler = Mousetrap(mapNode[0]);
      }
      if (m_mode) {
        m_keyHandler.bind('esc', function () {
          m_this.mode(null);
        });
      } else {
        m_keyHandler.unbind('esc');
      }
      if (m_this.currentAnnotation) {
        switch (m_this.currentAnnotation.state()) {
          case geo_annotation.state.create:
            m_this.removeAnnotation(m_this.currentAnnotation);
            break;
          case geo_annotation.state.edit:
            m_this.currentAnnotation.state(geo_annotation.state.done);
            m_this.modified();
            m_this.draw();
            break;
        }
        m_this.currentAnnotation = null;
      }
      switch (m_mode) {
        case m_this.modes.edit:
          m_this.currentAnnotation = editAnnotation;
          m_this.currentAnnotation.state(geo_annotation.state.edit);
          m_this.modified();
          break;
        case 'line':
          createAnnotation = geo_annotation.lineAnnotation;
          break;
        case 'point':
          createAnnotation = geo_annotation.pointAnnotation;
          break;
        case 'polygon':
          createAnnotation = geo_annotation.polygonAnnotation;
          break;
        case 'rectangle':
          createAnnotation = geo_annotation.rectangleAnnotation;
          break;
      }
      m_this.map().interactor().removeAction(undefined, undefined, geo_annotation.actionOwner);
      if (createAnnotation) {
        m_this.currentAnnotation = createAnnotation({
          state: geo_annotation.state.create,
          layer: this
        });
        m_this.addAnnotation(m_this.currentAnnotation, null);
        actions = m_this.currentAnnotation.actions(geo_annotation.state.create);
        $.each(actions, function (idx, action) {
          m_this.map().interactor().addAction(action);
        });
      }
      m_this.geoTrigger(geo_event.annotation.mode, {
        mode: m_mode, oldMode: oldMode });
      if (oldMode === m_this.modes.edit) {
        m_this.modified();
      }
    }
    return m_this;
  };

  /**
   * Return the current set of annotations as a geojson object.  Alternately,
   * add a set of annotations from a geojson object.
   *
   * @param {string|objectFile} [geojson] If present, add annotations based on
   *    the given geojson object.  If `undefined`, return the current
   *    annotations as geojson.  This may be a JSON string, a javascript
   *    object, or a File object.
   * @param {boolean|string} [clear] If `true`, when adding annotations, first
   *    remove all existing objects.  If `'update'`, update existing
   *    annotations and remove annotations that no longer exist.  If falsy,
   *    update existing annotations and leave annotations that have not chaged.
   * @param {string|geo.transform|null} [gcs] `undefined` to use the interface
   *    gcs, `null` to use the map gcs, or any other transform.
   * @param {boolean} [includeCrs] If truthy, include the coordinate system in
   *    the output.
   * @returns {object|number|undefined} If `geojson` was undefined, the current
   *    annotations is a javascript object that can be converted to geojson
   *    using JSON.stringify.  If `geojson` is specified, either the number of
   *    annotations now present upon success, or `undefined` if the value in
   *    `geojson` was not able to be parsed.
   */
  this.geojson = function (geojson, clear, gcs, includeCrs) {
    if (geojson !== undefined) {
      var reader = registry.createFileReader('geojsonReader', { layer: m_this });
      if (!reader.canRead(geojson)) {
        return;
      }
      if (clear === true) {
        m_this.removeAllAnnotations(true, false);
      }
      if (clear === 'update') {
        $.each(m_this.annotations(), function (idx, annotation) {
          annotation.options('updated', false);
        });
      }
      reader.read(geojson, function (features) {
        $.each(features.slice(), function (feature_idx, feature) {
          m_this._geojsonFeatureToAnnotation(feature, gcs);
          m_this.deleteFeature(feature);
        });
      });
      if (clear === 'update') {
        $.each(m_this.annotations(), function (idx, annotation) {
          if (annotation.options('updated') === false && annotation.state() === geo_annotation.state.done) {
            m_this.removeAnnotation(annotation, false);
          }
        });
      }
      m_this.modified();
      m_this.draw();
      return m_annotations.length;
    }
    geojson = null;
    var features = [];
    $.each(m_annotations, function (annotation_idx, annotation) {
      var obj = annotation.geojson(gcs, includeCrs);
      if (obj) {
        features.push(obj);
      }
    });
    if (features.length) {
      geojson = {
        type: 'FeatureCollection',
        features: features
      };
    }
    return geojson;
  };

  /**
   * Convert a feature as parsed by the geojson reader into one or more
   * annotations.
   *
   * @param {geo.feature} feature The feature to convert.
   * @param {string|geo.transform|null} [gcs] `undefined` to use the interface
   *    gcs, `null` to use the map gcs, or any other transform.
   * @fires geo.event.annotation.update
   */
  this._geojsonFeatureToAnnotation = function (feature, gcs) {
    var dataList = feature.data(),
        annotationList = registry.listAnnotations(),
        map = m_this.map();
    gcs = gcs === null ? map.gcs() : gcs === undefined ? map.ingcs() : gcs;
    $.each(dataList, function (data_idx, data) {
      var type = (data.properties || {}).annotationType || feature.featureType,
          options = $.extend({}, data.properties || {}),
          position,
          datagcs,
          i,
          existing;
      if ($.inArray(type, annotationList) < 0) {
        return;
      }
      options.style = options.style || {};
      options.labelStyle = options.labelStyle || {};
      delete options.annotationType;
      // the geoJSON reader can only emit line, polygon, and point
      switch (feature.featureType) {
        case 'line':
          position = feature.line()(data, data_idx);
          if (!position || position.length < 2) {
            return;
          }
          // make a copy of the position array to avoid mutating the original.
          position = position.slice();
          break;
        case 'polygon':
          position = feature.polygon()(data, data_idx);
          if (!position || !position.outer || position.outer.length < 3) {
            return;
          }
          // make a copy of the position array to avoid mutating the original.
          position = position.outer.slice();
          if (position[position.length - 1][0] === position[0][0] && position[position.length - 1][1] === position[0][1]) {
            position.splice(position.length - 1, 1);
            if (position.length < 3) {
              return;
            }
          }
          break;
        case 'point':
          position = [feature.position()(data, data_idx)];
          break;
      }
      for (i = 0; i < position.length; i += 1) {
        position[i] = util.normalizeCoordinates(position[i]);
      }
      datagcs = data.crs && data.crs.type === 'name' && data.crs.properties && data.crs.properties.type === 'proj4' && data.crs.properties.name ? data.crs.properties.name : gcs;
      if (datagcs !== map.gcs()) {
        position = transform.transformCoordinates(datagcs, map.gcs(), position);
      }
      options.coordinates = position;
      /* For each style listed in the geojsonStyleProperties object, check if
       * is given under any of the variety of keys as a valid instance of the
       * required data type.  If not, use the property from the feature. */
      $.each(geojsonStyleProperties, function (key, prop) {
        var value;
        $.each(prop.keys, function (idx, altkey) {
          if (value === undefined) {
            value = m_this.validateAttribute(options[altkey], prop.dataType);
          }
        });
        if (value === undefined) {
          value = m_this.validateAttribute(feature.style.get(key)(data, data_idx), prop.dataType);
        }
        if (value !== undefined) {
          options[prop.option || 'style'][key] = value;
        }
      });
      /* Delete property keys we have used */
      $.each(geojsonStyleProperties, function (key, prop) {
        $.each(prop.keys, function (idx, altkey) {
          delete options[altkey];
        });
      });
      if (options.annotationId !== undefined) {
        existing = m_this.annotationById(options.annotationId);
        if (existing) {
          delete options.annotationId;
        }
      }
      if (existing && existing.type() === type && existing.state() === geo_annotation.state.done && existing.options('updated') === false) {
        /* We could change the state of the existing annotation if it differs
         * from done. */
        delete options.state;
        delete options.layer;
        options.updated = true;
        existing.options(options);
        m_this.geoTrigger(geo_event.annotation.update, {
          annotation: existing
        });
      } else {
        options.state = geo_annotation.state.done;
        options.layer = m_this;
        options.updated = 'new';
        m_this.addAnnotation(registry.createAnnotation(type, options), null);
      }
    });
  };

  /**
   * Validate a value for an attribute based on a specified data type.  This
   * returns a sanitized value or `undefined` if the value was invalid.  Data
   * types include:
   * - `color`: a css string, `#rrggbb` hex string, `#rgb` hex string, number,
   *   or object with r, g, b properties in the range of [0-1].
   * - `opacity`: a floating point number in the range [0, 1].
   * - `positive`: a floating point number greater than zero.
   * - `boolean`: a string whose lowercase value is `'false'`, `'off'`, or
   *   `'no'`, and falsy values are false, all else is true.  `null` and
   *   `undefined` are still considered invalid values.
   * - `booleanOrNumber`: a string whose lowercase value is `'false'`, `'off'`,
   *   `'no'`, `'true'`, `'on'`, or `'yes'`, falsy values that aren't 0, and
   *   `true` are handled as booleans.  Otherwise, a floating point number that
   *   isn't NaN or an infinity.
   * - `coordinate2`: either an object with x and y properties that are
   *   numbers, or a string of the form <x>[,]<y> with optional whitespace, or
   *   a JSON encoded object with x and y values, or a JSON encoded list of at
   *   leasst two numbers.
   * - `number`: a floating point number that isn't NaN or an infinity.
   * - `angle`: a number that represents radians.  If followed by one of `deg`,
   *   `grad`, or `turn`, it is converted to radians.  An empty string is also
   *   allowed.
   * - `text`: any text string.
   * @param {number|string|object|boolean} value The value to validate.
   * @param {string} dataType The data type for validation.
   * @returns {number|string|object|boolean|undefined} The sanitized value or
   *    `undefined`.
   */
  this.validateAttribute = function (value, dataType) {
    var parts;

    if (value === undefined || value === null) {
      return;
    }
    switch (dataType) {
      case 'angle':
        if (value === '') {
          break;
        }
        parts = /^\s*([-.0-9eE]+)\s*(deg|rad|grad|turn)?\s*$/.exec(('' + value).toLowerCase());
        if (!parts || !isFinite(parts[1])) {
          return;
        }
        var factor = parts[2] === 'grad' ? Math.PI / 200 : parts[2] === 'deg' ? Math.PI / 180 : parts[2] === 'turn' ? 2 * Math.PI : 1;
        value = +parts[1] * factor;
        break;
      case 'boolean':
        value = !!value && ['false', 'no', 'off'].indexOf(('' + value).toLowerCase()) < 0;
        break;
      case 'booleanOrNumber':
        if (!value && value !== 0 && value !== '' || ['true', 'false', 'off', 'on', 'no', 'yes'].indexOf(('' + value).toLowerCase()) >= 0) {
          value = !!value && ['false', 'no', 'off'].indexOf(('' + value).toLowerCase()) < 0;
        } else {
          if (!util.isNonNullFinite(value)) {
            return;
          }
          value = +value;
        }
        break;
      case 'coordinate2':
        if (value === '') {
          break;
        }
        if (value && util.isNonNullFinite(value.x) && util.isNonNullFinite(value.y)) {
          value.x = +value.x;
          value.y = +value.y;
          break;
        }
        try {
          value = JSON.parse(value);
        } catch (err) {}
        if (value && util.isNonNullFinite(value.x) && util.isNonNullFinite(value.y)) {
          value.x = +value.x;
          value.y = +value.y;
          break;
        }
        if (Array.isArray(value) && util.isNonNullFinite(value[0]) && util.isNonNullFinite(value[1])) {
          value = { x: +value[0], y: +value[1] };
          break;
        }
        parts = /^\s*([-.0-9eE]+)(?:\s+|\s*,)\s*([-.0-9eE]+)\s*$/.exec('' + value);
        if (!parts || !isFinite(parts[1]) || !isFinite(parts[2])) {
          return;
        }
        value = { x: +parts[1], y: +parts[2] };
        break;
      case 'color':
        value = util.convertColor(value);
        if (value === undefined || value.r === undefined) {
          return;
        }
        break;
      case 'number':
        if (!util.isNonNullFinite(value)) {
          return;
        }
        value = +value;
        break;
      case 'numberOrBlank':
        if (value === '') {
          break;
        }
        if (!util.isNonNullFinite(value)) {
          return;
        }
        value = +value;
        break;
      case 'opacity':
        if (value === undefined || value === null || value === '') {
          return;
        }
        value = +value;
        if (isNaN(value) || value < 0 || value > 1) {
          return;
        }
        break;
      case 'positive':
        value = +value;
        if (!isFinite(value) || value <= 0) {
          return;
        }
        break;
      case 'text':
        value = '' + value;
        break;
    }
    return value;
  };

  /**
   * Update layer.
   *
   * @returns {this} The current layer.
   */
  this._update = function () {
    if (m_this.getMTime() > m_buildTime.getMTime()) {
      var labels = m_this.options('showLabels') ? [] : null,
          editable = m_this.options('clickToEdit') || m_this.mode() === m_this.modes.edit;
      /* Interally, we have a set of feature levels (to provide z-index
       * support), each of which can have data from multiple annotations.  We
       * clear the data on each of these features, then build it up from each
       * annotation.  Eventually, it may be necessary to optimize this and
       * only update the features that are changed.
       */
      $.each(m_features, function (idx, featureLevel) {
        $.each(featureLevel, function (type, feature) {
          feature.data = [];
          delete feature.feature.scaleOnZoom;
        });
      });
      $.each(m_annotations, function (annotation_idx, annotation) {
        var features = annotation.features();
        if (labels) {
          var annotationLabel = annotation.labelRecord();
          if (annotationLabel) {
            labels.push(annotationLabel);
          }
        }
        $.each(features, function (idx, featureLevel) {
          if (m_features[idx] === undefined) {
            m_features[idx] = {};
          }
          $.each(featureLevel, function (type, featureSpec) {
            /* Create features as needed */
            if (!m_features[idx][type]) {
              var feature = m_this.createFeature(type, {
                gcs: m_this.map().gcs(),
                selectionAPI: editable
              });
              if (!feature) {
                /* We can't create the desired feature, porbably because of the
                 * selected renderer.  Issue one warning only. */
                var key = 'error_feature_' + type;
                if (!m_this[key]) {
                  console.warn('Cannot create a ' + type + ' feature for ' + 'annotations.');
                  m_this[key] = true;
                }
                return;
              }
              if (editable) {
                feature.geoOn(geo_event.feature.mouseon, m_this._handleMouseOn);
                feature.geoOn(geo_event.feature.mouseoff, m_this._handleMouseOff);
              }

              /* Since each annotation can have separate styles, the styles are
               * combined together with a meta-style function.  Any style that
               * could be used should be in this list.  Color styles may be
               * restricted to {r, g, b} objects for efficiency, but this
               * hasn't been tested.
               */
              var style = {};
              $.each(['closed', 'fill', 'fillColor', 'fillOpacity', 'line', 'lineCap', 'lineJoin', 'polygon', 'position', 'radius', 'stroke', 'strokeColor', 'strokeOffset', 'strokeOpacity', 'strokeWidth', 'uniformPolygon'], function (keyidx, key) {
                var origFunc;
                if (feature.style()[key] !== undefined) {
                  origFunc = feature.style.get(key);
                }
                style[key] = function (d, i, d2, i2) {
                  var style = d && d.style ? d.style : d && d[2] && d[2].style ? d[2].style : d2.style;
                  var result = style ? style[key] : d;
                  if (util.isFunction(result)) {
                    result = result(d, i, d2, i2);
                  }
                  if (result === undefined && origFunc) {
                    result = origFunc(d, i, d2, i2);
                  }
                  return result;
                };
              });
              feature.style(style);
              m_features[idx][type] = {
                feature: feature,
                style: style,
                data: []
              };
            } else {
              feature = m_features[idx][type].feature;
              // update whether we check for selections on existing features
              if (feature.selectionAPI() !== !!editable) {
                feature.selectionAPI(editable);
                if (editable) {
                  feature.geoOn(geo_event.feature.mouseon, m_this._handleMouseOn);
                  feature.geoOn(geo_event.feature.mouseoff, m_this._handleMouseOff);
                } else {
                  feature.geoOff(geo_event.feature.mouseon, m_this._handleMouseOn);
                  feature.geoOff(geo_event.feature.mouseoff, m_this._handleMouseOff);
                }
              }
            }
            /* Collect the data for each feature */
            var dataEntry = featureSpec.data || featureSpec;
            if (!Array.isArray(dataEntry)) {
              dataEntry = [dataEntry];
            }
            dataEntry.forEach(function (dataElement) {
              dataElement.annotation = annotation;
              m_features[idx][type].data.push(dataElement);
              if (featureSpec.scaleOnZoom) {
                m_features[idx][type].feature.scaleOnZoom = true;
              }
            });
          });
        });
      });
      /* Update the data for each feature */
      $.each(m_features, function (idx, featureLevel) {
        $.each(featureLevel, function (type, feature) {
          feature.feature.data(feature.data);
        });
      });
      m_this._updateLabels(labels);
      m_buildTime.modified();
    }
    s_update.call(m_this, arguments);
    return m_this;
  };

  /**
   * Show or hide annotation labels.  Create or destroy a child layer or a
   * feature as needed.
   *
   * @param {object[]|null} labels The list of labels to display of `null` for
   *    no labels.
   * @returns {this} The class instance.
   * @fires geo.event.annotation.layerAdd
   */
  this._updateLabels = function (labels) {
    if (!labels || !labels.length) {
      m_this._removeLabelFeature();
      return m_this;
    }
    if (!m_labelFeature) {
      if (!(registry.registries.features[m_this.rendererName()] || {}).text) {
        var renderer = registry.rendererForFeatures(['text']);
        m_labelLayer = registry.createLayer('feature', m_this.map(), { renderer: renderer });
        m_this.addChild(m_labelLayer);
        m_labelLayer._update();
        m_this.geoTrigger(geo_event.layerAdd, {
          target: m_this,
          layer: m_labelLayer
        });
      }
      var style = {};
      textFeature.usedStyles.forEach(function (key) {
        style[key] = function (d, i) {
          if (d.style && d.style[key] !== undefined) {
            return d.style[key];
          }
          return (m_this.options('defaultLabelStyle') || {})[key];
        };
      });
      m_labelFeature = (m_labelLayer || m_this).createFeature('text', {
        style: style,
        gcs: m_this.map().gcs(),
        position: function position(d) {
          return d.position;
        }
      });
    }
    m_labelFeature.data(labels);
    return m_this;
  };

  /**
   * Check if any features are marked that they need to be updated when a zoom
   * occurs.  If so, mark that feature as modified.
   */
  this._handleZoom = function () {
    var i,
        features = m_this.features();
    for (i = 0; i < features.length; i += 1) {
      if (features[i].scaleOnZoom) {
        features[i].modified();
      }
    }
  };

  /**
   * Remove the label feature if it exists.
   *
   * @returns {this} The current layer.
   * @fires geo.event.annotation.layerRemove
   */
  this._removeLabelFeature = function () {
    if (m_labelLayer) {
      m_labelLayer._exit();
      m_this.removeChild(m_labelLayer);
      m_this.geoTrigger(geo_event.layerRemove, {
        target: m_this,
        layer: m_labelLayer
      });
      m_labelLayer = m_labelFeature = null;
    }
    if (m_labelFeature) {
      m_this.removeFeature(m_labelFeature);
      m_labelFeature = null;
    }
    return m_this;
  };

  /**
   * Update if necessary and draw the layer.
   *
   * @returns {this} The current layer.
   */
  this.draw = function () {
    m_this._update();
    s_draw.call(m_this);
    return m_this;
  };

  /**
   * Initialize.
   *
   * @returns {this} The current layer.
   */
  this._init = function () {
    // Call super class init
    s_init.call(m_this);

    if (!m_this.map().interactor()) {
      m_this.map().interactor(mapInteractor({ actions: [] }));
    }
    m_this.geoOn(geo_event.actionselection, m_this._processAction);
    m_this.geoOn(geo_event.actionmove, m_this._processAction);
    m_this.geoOn(geo_event.actionup, m_this._processAction);

    m_this.geoOn(geo_event.mouseclick, m_this._handleMouseClick);
    m_this.geoOn(geo_event.mousemove, m_this._handleMouseMove);

    m_this.geoOn(geo_event.zoom, m_this._handleZoom);

    return m_this;
  };

  /**
   * Free all resources.
   *
   * @returns {this} The current layer.
   */
  this._exit = function () {
    if (m_keyHandler) {
      m_keyHandler.reset();
    }
    m_this._removeLabelFeature();
    // Call super class exit
    s_exit.call(m_this);
    m_annotations = [];
    m_features = [];
    return m_this;
  };

  return m_this;
};

inherit(annotationLayer, featureLayer);
registry.registerLayer('annotation', annotationLayer);
module.exports = annotationLayer;

/***/ }),
/* 283 */
/***/ (function(module, exports) {

if(typeof __WEBPACK_EXTERNAL_MODULE_283__ === 'undefined') {var e = new Error("Cannot find module \"hammerjs\""); e.code = 'MODULE_NOT_FOUND'; throw e;}
module.exports = __WEBPACK_EXTERNAL_MODULE_283__;

/***/ }),
/* 284 */
/***/ (function(module, exports) {

if(typeof __WEBPACK_EXTERNAL_MODULE_284__ === 'undefined') {var e = new Error("Cannot find module \"d3\""); e.code = 'MODULE_NOT_FOUND'; throw e;}
module.exports = __WEBPACK_EXTERNAL_MODULE_284__;

/***/ }),
/* 285 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var inherit = __webpack_require__(0);
var renderer = __webpack_require__(22);
var registerRenderer = __webpack_require__(1).registerRenderer;

/**
 * Create a new instance of class domRenderer.
 *
 * @class
 * @alias geo.domRenderer
 * @extends geo.renderer
 * @param {object} arg Options for the renderer.
 * @param {geo.layer} [arg.layer] Layer associated with the renderer.
 * @param {HTMLElement} [arg.canvas] Canvas element associated with the
 *   renderer.
 * @returns {geo.domRenderer}
 */
var domRenderer = function domRenderer(arg) {
  'use strict';

  if (!(this instanceof domRenderer)) {
    return new domRenderer(arg);
  }
  renderer.call(this, arg);

  arg = arg || {};

  var m_this = this;

  /**
   * Get API used by the renderer.
   *
   * @returns {string} 'dom'.
   */
  this.api = function () {
    return 'dom';
  };

  /**
   * Initialize.
   *
   * @returns {this}
   */
  this._init = function () {
    var layer = m_this.layer().node();

    if (!m_this.canvas() && layer && layer.length) {
      // The renderer and the UI Layer share the same canvas
      // at least for now. This renderer is essentially a noop renderer
      // designed for backwards compatibility
      m_this.canvas(layer[0]);
    }
    return m_this;
  };

  this._init(arg);
  return this;
};

inherit(domRenderer, renderer);
registerRenderer('dom', domRenderer);
module.exports = domRenderer;

/***/ }),
/* 286 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

var inherit = __webpack_require__(0);
var registerFileReader = __webpack_require__(1).registerFileReader;
var fileReader = __webpack_require__(122);

/**
 * Object specification for a geojsonReader.
 *
 * @typedef {geo.fileReader.spec} geo.geojsonReader.spec
 * @extends geo.fileReader.spec
 * @property {geo.pointFeature.styleSpec} [pointStyle] Default style for
 *   points.
 * @property {geo.pointFeature.styleSpec} [lineStyle] Default style for lines.
 * @property {geo.pointFeature.styleSpec} [polygonStyle] Default style for
 *   polygons.
 */

/**
 * Create a new instance of class geo.geojsonReader.
 *
 * @class
 * @alias geo.geojsonReader
 * @extends geo.fileReader
 * @param {geo.fileReader.spec} arg
 * @returns {geo.geojsonReader}
 */
var geojsonReader = function geojsonReader(arg) {
  'use strict';

  if (!(this instanceof geojsonReader)) {
    return new geojsonReader(arg);
  }

  var $ = __webpack_require__(2);
  var convertColor = __webpack_require__(3).convertColor;

  var m_this = this,
      m_options = _extends({}, arg, {
    pointStyle: _extends({
      fill: true,
      fillColor: '#ff7800',
      fillOpacity: 0.8,
      stroke: true,
      strokeColor: '#000',
      strokeWidth: 1,
      strokeOpacity: 1,
      radius: 8
    }, arg.pointStyle),
    lineStyle: _extends({
      strokeColor: '#ff7800',
      strokeWidth: 4,
      strokeOpacity: 0.5,
      strokeOffset: 0,
      lineCap: 'butt',
      lineJoin: 'miter',
      closed: false
    }, arg.lineStyle),
    polygonStyle: _extends({
      fill: true,
      fillColor: '#b0de5c',
      fillOpacity: 0.8,
      stroke: true,
      strokeColor: '#999999',
      strokeWidth: 2,
      strokeOpacity: 1
    }, arg.polygonStyle)
  });

  fileReader.call(this, m_options);

  /**
   * Tells the caller if it can handle the given file by returning a boolean.
   *
   * @param {File|Blob|string|object} file This is either a `File` object, a
   *    `Blob` object, a string representation of a file, or an object
   *    representing data from a file.
   * @returns {boolean} `true` if this reader can read a file.
   */
  this.canRead = function (file) {
    if (file instanceof File || file instanceof Blob) {
      return !!(file.type === 'application/json' || file.name && file.name.match(/\.json$/));
    } else if (typeof file === 'string') {
      try {
        JSON.parse(file);
      } catch (e) {
        return false;
      }
      return true;
    }
    try {
      if (Array.isArray(m_this._featureArray(file))) {
        return true;
      }
    } catch (e) {}
    return false;
  };

  /**
   * Read or parse a file or object, then call a done function.
   *
   * @param {File|Blob|string|object} file This is either a `File` object, a
   *    `Blob` object, a string representation of a file, or an object
   *    representing data from a file.
   * @param {function} done A callback function when the read is complete.
   *    This is called with `false` on error or the object that was read but
   *    not yet parsed.
   * @param {function} [progress] A function which is passed `ProgressEvent`
   *    information from a `FileReader`.  This includes `loaded` and `total`
   *    each with a number of bytes.
   */
  this._readObject = function (file, done, progress) {
    var object;
    function onDone(fileString) {
      // if fileString is not a JSON string, expect it to be a URL.
      try {
        object = JSON.parse(fileString);
        done(object);
      } catch (err) {
        if (!object) {
          $.ajax({
            type: 'GET',
            url: fileString,
            dataType: 'text'
          }).done(function (data) {
            try {
              object = JSON.parse(data);
              done(object);
            } catch (err) {
              if (!object) {
                done(false);
              }
            }
          }).fail(function () {
            done(false);
          });
        }
      }
    }

    if (file instanceof File || file instanceof Blob) {
      m_this._getString(file, onDone, progress);
    } else if (typeof file === 'string') {
      onDone(file);
    } else {
      done(file);
    }
  };

  /**
   * Return an array of normalized geojson features.  This turns bare
   * geometries into features and multi-geometry features into single geometry
   * features.
   *
   * Returns an array of Point, LineString, or Polygon features.
   * @param {geojson.object} spec A parsed geojson object.
   * @returns {geojson.FeatureObject[]} An array of feature objects, none of
   *    which include multi-geometries, and none have empty geometries.
   */
  this._featureArray = function (spec) {
    var features,
        normalized = [];
    switch (spec.type) {
      case 'FeatureCollection':
        features = spec.features;
        break;

      case 'Feature':
        features = [spec];
        break;

      case 'GeometryCollection':
        features = spec.geometries.map(function (g) {
          return {
            type: 'Feature',
            geometry: g,
            properties: {}
          };
        });
        break;

      case 'Point':
      case 'LineString':
      case 'Polygon':
      case 'MultiPoint':
      case 'MultiLineString':
      case 'MultiPolygon':
        features = [{
          type: 'Feature',
          geometry: spec,
          properties: {}
        }];
        break;

      default:
        throw new Error('Invalid json type');
    }

    // flatten multi features
    features.forEach(function (feature) {
      Array.prototype.push.apply(normalized, m_this._feature(feature));
    });

    // remove features with empty geometries
    normalized = normalized.filter(function (feature) {
      return feature.geometry && feature.geometry.coordinates && feature.geometry.coordinates.length;
    });
    return normalized;
  };

  /**
   * Normalize a feature object turning multi geometry features into an array
   * of features, and single geometry features into an array containing one
   * feature.
   *
   * @param {geojson.object} spec A parsed geojson object.
   * @returns {geojson.FeatureObject[]} An array of feature objects, none of
   *    which include multi-geometries.
   */
  this._feature = function (spec) {
    if (spec.type !== 'Feature') {
      throw new Error('Invalid feature object');
    }
    switch (spec.geometry.type) {
      case 'Point':
      case 'LineString':
      case 'Polygon':
        return [spec];

      case 'MultiPoint':
      case 'MultiLineString':
      case 'MultiPolygon':
        return spec.geometry.coordinates.map(function (c) {
          return {
            type: 'Feature',
            geometry: {
              type: spec.geometry.type.replace('Multi', ''),
              coordinates: c
            },
            properties: spec.properties
          };
        });

      default:
        throw new Error('Invalid geometry type');
    }
  };

  /**
   * Convert from a geojson position array into a geojs position object.
   *
   * @param {number[]} p A coordinate in the form of an array with two or three
   *    components.
   * @returns {geo.geoPosition}
   */
  this._position = function (p) {
    return {
      x: p[0],
      y: p[1],
      z: p[2] || 0
    };
  };

  /**
   * Defines a style accessor the returns the given value of the property
   * object or a default value.
   *
   * @param {string} prop The property name.
   * @param {object} _default The default value.
   * @returns {function} A style function for the property.
   */
  this._style = function (prop, _default) {
    var isColor = prop.toLowerCase().match(/color$/);
    if (isColor) {
      _default = convertColor(_default);
    }
    return function (v, j, d, i) {
      var p;
      if (d !== undefined && d.properties) {
        p = d.properties;
      } else {
        p = v.properties;
      }
      if (p !== undefined && p.hasOwnProperty(prop)) {
        return isColor ? convertColor(p[prop]) : p[prop];
      }
      return _default;
    };
  };

  /**
   * Reads the file and optionally calls a function when finished.  The `done`
   * function is called with a list of {@link geo.feature} on success or
   * `false` on failure.
   *
   * @param {File|Blob|string|object} file This is either a `File` object, a
   *    `Blob` object, a string representation of a file, or an object
   *    representing data from a file.
   * @param {function} [done] An optional callback function when the read is
   *    complete.  This is called with `false` on error or the object that was
   *    read and parsed by the reader.
   * @param {function} [progress] A function which is passed `ProgressEvent`
   *    information from a `FileReader`.  This includes `loaded` and `total`
   *    each with a number of bytes.
   * @returns {Promise} A `Promise` that resolves with a list of
   *    {@link geo.feature} or is rejected if the reader fails.
   */
  this.read = function (file, done, progress) {
    var promise = new Promise(function (resolve, reject) {
      /**
       * Given a parsed GeoJSON object, convert it into features on the
       * reader's layer.
       *
       * @param {geojson.object|false} object Either a parse GeoJSON object or
       *    `false` for an error.
       */
      function _done(object) {
        if (object === false) {
          if (done) {
            done(object);
          }
          reject(new Error('Failed to parse GeoJSON'));
          return;
        }
        var features = void 0,
            allFeatures = [],
            points = void 0,
            lines = void 0,
            polygons = void 0,
            feature = void 0;

        try {
          features = m_this._featureArray(object);
        } catch (err) {
          reject(err);
          return;
        }

        // process points
        points = features.filter(function (f) {
          return f.geometry.type === 'Point';
        });
        if (points.length) {
          feature = m_this.layer().createFeature('point');
          if (feature) {
            feature.data(points).position(function (d) {
              return m_this._position(d.geometry.coordinates);
            })
            // create an object with each property in m_options.pointStyle,
            // mapping the values through the _style function.
            .style([{}].concat(Object.keys(m_options.pointStyle)).reduce(function (styleObj, key) {
              return _extends(_defineProperty({}, key, m_this._style(key, m_options.pointStyle[key])), styleObj);
            }));
            allFeatures.push(feature);
          }
        }

        // process lines
        lines = features.filter(function (f) {
          return f.geometry.type === 'LineString';
        });
        if (lines.length) {
          feature = m_this.layer().createFeature('line');
          if (feature) {
            feature.data(lines).line(function (d) {
              return d.geometry.coordinates;
            }).position(m_this._position)
            // create an object with each property in m_options.lineStyle,
            // mapping the values through the _style function.
            .style([{}].concat(Object.keys(m_options.lineStyle)).reduce(function (styleObj, key) {
              return _extends(_defineProperty({}, key, m_this._style(key, m_options.lineStyle[key])), styleObj);
            }));
            allFeatures.push(feature);
          }
        }

        // process polygons
        polygons = features.filter(function (f) {
          return f.geometry.type === 'Polygon';
        });
        if (polygons.length) {
          feature = m_this.layer().createFeature('polygon');
          if (feature) {
            feature.data(polygons).polygon(function (d, i) {
              return {
                outer: d.geometry.coordinates[0],
                inner: d.geometry.coordinates.slice(1)
              };
            }).position(m_this._position)
            // create an object with each property in m_options.polygonStyle,
            // mapping the values through the _style function.
            .style([{}].concat(Object.keys(m_options.polygonStyle)).reduce(function (styleObj, key) {
              return _extends(_defineProperty({}, key, m_this._style(key, m_options.polygonStyle[key])), styleObj);
            }));
            allFeatures.push(feature);
          }
        }
        if (done) {
          done(allFeatures);
        }
        resolve(allFeatures);
      }

      m_this._readObject(file, _done, progress);
    });
    this.addPromise(promise);
    return promise;
  };
};

inherit(geojsonReader, fileReader);
registerFileReader('geojsonReader', geojsonReader);
// Also register under an alternate name
registerFileReader('jsonReader', geojsonReader);
module.exports = geojsonReader;

/***/ }),
/* 287 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var $ = __webpack_require__(2);
var vgl = __webpack_require__(9);
var inherit = __webpack_require__(0);
var sceneObject = __webpack_require__(13);

/**
 * Creates a new map object.
 *
 * @class
 * @alias geo.map
 * @extends geo.sceneObject
 *
 * @param {object} arg Options object
 *
 * @param {string} arg.node DOM selector for the map container.
 *   *** Always required ***
 *
 * @param {string|geo.transform} [arg.gcs='EPSG:3857']
 *   The main coordinate system of the map (this is often web Mercator).
 *   * Required when using a domain/CS different from OSM *
 * @param {string|geo.transform} [arg.ingcs='EPSG:4326']
 *   The default coordinate system of interface calls (this is often latitude
 *   and longitude).
 * @param {number} [arg.unitsPerPixel=156543] GCS to pixel unit scaling at zoom
 *   0 (i.e. meters per pixel or degrees per pixel).
 * @param {object?} [arg.maxBounds] The maximum visible map bounds.
 * @param {number} [arg.maxBounds.left=-20037508] The left bound.
 * @param {number} [arg.maxBounds.right=20037508] The right bound.
 * @param {number} [arg.maxBounds.bottom=-20037508] The bottom bound.
 * @param {number} [arg.maxBounds.top=20037508] The top bound.
 * @param {number} [arg.maxBounds.gcs=arg.ingcs] The coordinate system for the
 *   bounds.
 *
 * @param {number} [arg.zoom=4] Initial zoom.
 * @param {object?} [arg.center] Initial map center.
 * @param {number} arg.center.x=0
 * @param {number} arg.center.y=0
 * @param {number} [arg.rotation=0] Initial clockwise rotation in radians.
 * @param {number?} [arg.width] The map width (default node width).
 * @param {number?} [arg.height] The map height (default node height).
 *
 * @param {number} [arg.min=0] Minimum zoom level (though fitting to the
 *   viewport may make it so this is smaller than the smallest possible value).
 * @param {number} [arg.max=16] Maximum zoom level.
 * @param {boolean} [arg.discreteZoom=false] If `true`, only allow integer zoom
 *   levels.  `false` for any zoom level.
 * @param {boolean|function} [arg.allowRotation=true] `false` prevents
 *   rotation, `true` allows any rotation.  If a function, the function is
 *   called with a rotation (angle in radians) and returns a valid rotation
 *   (this can be used to constrain the rotation to a range or specific to
 *   values).
 *
 * @param {geo.camera?} [arg.camera] The camera to control the view.
 * @param {geo.mapInteractor?} [arg.interactor] The UI event handler.  If
 *   `undefined`, a default interactor is created and used.  If `null`, no
 *   interactor is attached to the map.
 * @param {array} [arg.animationQueue] An array used to synchronize animations.
 *   If specified, this should be an empty array or the same array as passed to
 *   other map instances.
 * @param {boolean} [arg.autoResize=true] Adjust map size on window resize.
 * @param {boolean} [arg.clampBoundsX=false] Prevent panning outside of the
 *   maximum bounds in the horizontal direction.
 * @param {boolean} [arg.clampBoundsY=true] Prevent panning outside of the
 *   maximum bounds in the vertical direction.
 * @param {boolean} [arg.clampZoom=true] Prevent zooming out so that the map
 *   area is smaller than the window.
 *
 * @returns {geo.map}
 */
var map = function map(arg) {
  'use strict';

  if (!(this instanceof map)) {
    return new map(arg);
  }
  arg = arg || {};

  if (arg.node === undefined || arg.node === null) {
    console.warn('map creation requires a node');
    return this;
  }

  sceneObject.call(this, arg);

  var camera = __webpack_require__(49);
  var transform = __webpack_require__(7);
  var util = __webpack_require__(3);
  var registry = __webpack_require__(1);
  var geo_event = __webpack_require__(6);
  var mapInteractor = __webpack_require__(51);
  var uiLayer = __webpack_require__(126);

  /**
   * Private member variables
   * @private
   */
  var m_this = this,
      s_exit = this._exit,

  // See https://en.wikipedia.org/wiki/Web_Mercator
  // phiMax = 180 / Math.PI * (2 * Math.atan(Math.exp(Math.PI)) - Math.PI / 2),
  m_node = $(arg.node),
      m_width = arg.width || m_node.width() || 512,
      m_height = arg.height || m_node.height() || 512,
      m_gcs = arg.gcs === undefined ? 'EPSG:3857' : arg.gcs,
      m_ingcs = arg.ingcs === undefined ? 'EPSG:4326' : arg.ingcs,
      m_center = { x: 0, y: 0 },
      m_zoom = arg.zoom === undefined ? 4 : arg.zoom,
      m_rotation = arg.rotation ? arg.rotation : 0,
      m_fileReader = null,
      m_interactor = null,
      m_validZoomRange = { min: 0, max: 16, origMin: 0 },
      m_transition = null,
      m_queuedTransition = null,
      m_discreteZoom = arg.discreteZoom ? true : false,
      m_allowRotation = typeof arg.allowRotation === 'function' ? arg.allowRotation : arg.allowRotation === undefined ? true : !!arg.allowRotation,
      m_maxBounds = arg.maxBounds || {},
      m_camera = arg.camera || camera(),
      m_unitsPerPixel,
      m_clampBoundsX,
      m_clampBoundsY,
      m_clampZoom,
      m_animationQueue = arg.animationQueue || [],
      m_autoResize = arg.autoResize === undefined ? true : arg.autoResize,
      m_origin;

  /* Compute the maximum bounds on our map projection.  By default, x ranges
   * from [-180, 180] in the interface projection, and y matches the x range in
   * the map (not the interface) projection.  For images, this might be
   * [0, width] and [0, height] instead. */
  var mcx = ((m_maxBounds.left || 0) + (m_maxBounds.right || 0)) / 2,
      mcy = ((m_maxBounds.bottom || 0) + (m_maxBounds.top || 0)) / 2;
  m_maxBounds.left = transform.transformCoordinates(m_maxBounds.gcs || m_ingcs, m_gcs, {
    x: m_maxBounds.left !== undefined ? m_maxBounds.left : -180, y: mcy
  }).x;
  m_maxBounds.right = transform.transformCoordinates(m_maxBounds.gcs || m_ingcs, m_gcs, {
    x: m_maxBounds.right !== undefined ? m_maxBounds.right : 180, y: mcy
  }).x;
  m_maxBounds.top = m_maxBounds.top !== undefined ? transform.transformCoordinates(m_maxBounds.gcs || m_ingcs, m_gcs, {
    x: mcx, y: m_maxBounds.top }).y : m_maxBounds.right;
  m_maxBounds.bottom = m_maxBounds.bottom !== undefined ? transform.transformCoordinates(m_maxBounds.gcs || m_ingcs, m_gcs, {
    x: mcx, y: m_maxBounds.bottom }).y : m_maxBounds.left;
  m_unitsPerPixel = arg.unitsPerPixel || (m_maxBounds.right - m_maxBounds.left) / 256;

  m_camera.viewport = {
    width: m_width,
    height: m_height,
    left: m_node.offset().left,
    top: m_node.offset().top
  };
  arg.center = util.normalizeCoordinates(arg.center);
  m_clampBoundsX = arg.clampBoundsX === undefined ? false : arg.clampBoundsX;
  m_clampBoundsY = arg.clampBoundsY === undefined ? true : arg.clampBoundsY;
  m_clampZoom = arg.clampZoom === undefined ? true : arg.clampZoom;

  /**
   * Get/set the number of world space units per display pixel at the given
   * zoom level.
   *
   * @param {number} [zoom=0] The target zoom level.
   * @param {number?} [unit] If present, set the `unitsPerPixel` at the
   *   specified zoom level.  Otherwise return the current value.
   * @returns {number|this}
   */
  this.unitsPerPixel = function (zoom, unit) {
    zoom = zoom || 0;
    if (unit) {
      // get the units at level 0
      m_unitsPerPixel = Math.pow(2, zoom) * unit;

      // redraw all the things
      m_this.draw();
      return m_this;
    }
    return Math.pow(2, -zoom) * m_unitsPerPixel;
  };

  /**
   * Get/set the animation queue.  Two maps can share a single animation queue
   * to ensure synchronized animations.  When setting, the animation queue will
   * merge values from the existing queue into the new queue.
   *
   * @param {array} [queue] The animation queue to use.
   * @returns {array|this} The current animation queue or the current map.
   */
  this.animationQueue = function (queue) {
    if (queue === undefined) {
      return m_animationQueue;
    }
    if (queue !== m_animationQueue) {
      if (m_animationQueue.length) {
        /* If the specified queue already has data in, don't copy the 0th
         * element of the existing queue, since the 0th element is always the
         * actual requestAnimationFrame reference.  In this case, cancel the
         * existing requestAnimationFrame.  By using a property of window,
         * tests can override this if needed. */
        if (queue.length && queue[0] !== m_animationQueue[0]) {
          window['cancelAnimationFrame'](m_animationQueue[0]);
        }
        for (var i = queue.length ? 1 : 0; i < m_animationQueue.length; i += 1) {
          queue.push(m_animationQueue[i]);
        }
      }
      m_animationQueue = queue;
    }
    return this;
  };

  /**
   * Get/set the autoResize flag.
   *
   * @param {boolean} [autoResize] Truthy to automaticaly resize the map when
   *    the size of the browser window changes.
   * @returns {boolean|this} The current state of autoResize or the current map.
   */
  this.autoResize = function (autoResize) {
    if (autoResize === undefined) {
      return m_autoResize;
    }
    if (autoResize !== m_autoResize) {
      $(window).off('resize', resizeSelf);
      m_autoResize = autoResize;
      if (m_autoResize) {
        $(window).on('resize', resizeSelf);
      }
    }
    return this;
  };

  /**
   * Get/set the `clampBoundsX` setting.  If changed, adjust the bounds of the
   * map as needed.
   *
   * @param {boolean} [clamp] The new clamp value.
   * @returns {boolean|this}
   */
  this.clampBoundsX = function (clamp) {
    if (clamp === undefined) {
      return m_clampBoundsX;
    }
    if (clamp !== m_clampBoundsX) {
      m_clampBoundsX = !!clamp;
      m_this.pan({ x: 0, y: 0 });
    }
    return m_this;
  };

  /**
   * Get/set the `clampBoundsY` setting.  If changed, adjust the bounds of the
   * map as needed.
   *
   * @param {boolean} [clamp] The new clamp value.
   * @returns {boolean|this}
   */
  this.clampBoundsY = function (clamp) {
    if (clamp === undefined) {
      return m_clampBoundsY;
    }
    if (clamp !== m_clampBoundsY) {
      m_clampBoundsY = !!clamp;
      m_this.pan({ x: 0, y: 0 });
    }
    return m_this;
  };

  /**
   * Get/set the `clampZoom` setting.  If changed, adjust the bounds of the map
   * as needed.
   *
   * @param {boolean} [clamp] The new clamp value.
   * @returns {boolean|this}
   */
  this.clampZoom = function (clamp) {
    if (clamp === undefined) {
      return m_clampZoom;
    }
    if (clamp !== m_clampZoom) {
      m_clampZoom = !!clamp;
      reset_minimum_zoom();
      m_this.zoom(m_zoom);
    }
    return m_this;
  };

  /**
   * Get/set the `allowRotation` setting.  If changed, adjust the map as
   * needed.
   *
   * @param {boolean|function} [allowRotation] The new `allowRotation` value.
   *    `false` prevents rotation, `true` allows any rotation.  If a function,
   *    the function is called with a rotation (angle in radians) and returns a
   *    valid rotation (this can be used to constrain the rotation to a range
   *    or to specific values).
   * @returns {boolean|function|this}
   */
  this.allowRotation = function (allowRotation) {
    if (allowRotation === undefined) {
      return m_allowRotation;
    }
    if (typeof allowRotation !== 'function') {
      allowRotation = !!allowRotation;
    }
    if (allowRotation !== m_allowRotation) {
      m_allowRotation = allowRotation;
      m_this.rotation(m_rotation);
    }
    return m_this;
  };

  /**
   * Get the map's world coordinate origin in gcs coordinates.
   *
   * @returns {geo.geoPosition}
   */
  this.origin = function () {
    return $.extend({}, m_origin);
  };

  /**
   * Get the camera.
   *
   * @returns {geo.camera}
   */
  this.camera = function () {
    return m_camera;
  };

  /**
   * Get or set the map gcs.  This is the coordinate system used in drawing the
   * map.
   *
   * @param {string} [arg] If `undefined`, return the current gcs.  Otherwise,
   *    a new value for the gcs.
   * @returns {string|this} A string used by {@link geo.transform}.
   */
  this.gcs = function (arg) {
    if (arg === undefined) {
      return m_gcs;
    }
    if (arg !== m_gcs) {
      var oldCenter = m_this.center(undefined, undefined);
      m_gcs = arg;
      reset_minimum_zoom();
      var newZoom = fix_zoom(m_zoom);
      if (newZoom !== m_zoom) {
        m_this.zoom(newZoom);
      }
      m_this.center(oldCenter, undefined);
    }
    return m_this;
  };

  /**
   * Get or set the map interface gcs.  This is the coordinate system used when
   * getting or setting map bounds, center, and other values.
   *
   * @param {string} [arg] If `undefined`, returtn the current interface gcs.
   *    Otherwise, a new value for the interface gcs.
   * @returns {string|this} A string used by {@link geo.transform}.
   */
  this.ingcs = function (arg) {
    if (arg === undefined) {
      return m_ingcs;
    }
    m_ingcs = arg;
    return m_this;
  };

  /**
   * Get root DOM node of the map.
   *
   * @returns {object}
   */
  this.node = function () {
    return m_node;
  };

  /**
   * Get/Set zoom level of the map.
   *
   * @param {number} [val] If `undefined`, return the current zoom level.
   *    Otherwise, the new zoom level to set.
   * @param {object} [origin] If present, specifies the center of the zoom;
   *    otherwise the map's display center is used.
   * @param {geo.geoPosition} origin.geo The gcs coordinates of the zoom
   *    center.
   * @param {geo.screenPosition} origin.map The display coordinates of the zoom
   *    center.
   * @param {boolean} [ignoreDiscreteZoom] If `true`, ignore the discreteZoom
   *    option when determining the new view.
   * @returns {number|this}
   * @fires geo.event.zoom
   * @fires geo.event.pan
   */
  this.zoom = function (val, origin, ignoreDiscreteZoom) {
    if (val === undefined) {
      return m_zoom;
    }
    var evt, bounds;
    /* If we are zooming around a point, ignore the clamp bounds */
    var aroundPoint = origin && (origin.mapgcs || origin.geo) && origin.map;
    var ignoreClampBounds = aroundPoint;

    /* The ignoreDiscreteZoom flag is intended to allow non-integer zoom values
     * during animation. */
    val = fix_zoom(val, ignoreDiscreteZoom);
    if (val === m_zoom) {
      return m_this;
    }

    m_zoom = val;

    bounds = m_this.boundsFromZoomAndCenter(val, m_center, m_rotation, null, ignoreDiscreteZoom, ignoreClampBounds);
    m_this.modified();

    camera_bounds(bounds, m_rotation);
    evt = {
      zoomLevel: m_zoom,
      screenPosition: origin ? origin.map : undefined
    };
    m_this.geoTrigger(geo_event.zoom, evt);

    if (aroundPoint) {
      var shifted = m_this.gcsToDisplay(origin.mapgcs || origin.geo, origin.mapgcs ? null : undefined);
      m_this.pan({ x: origin.map.x - shifted.x, y: origin.map.y - shifted.y }, ignoreDiscreteZoom, true);
    } else {
      m_this.pan({ x: 0, y: 0 }, ignoreDiscreteZoom);
    }
    return m_this;
  };

  /**
   * Pan the map by a number of display pixels.
   *
   * @param {object} delta Amount to pan in display pixels.
   * @param {number} delta.x Horizontal distance on the display.
   * @param {number} delta.y Vertical distance on the display.
   * @param {boolean} [ignoreDiscreteZoom] If `true`, ignore the `discreteZoom`
   *    option when determining the new view.
   * @param {boolean|'limited'} [ignoreClampBounds] If `true` ignore the
   *    `clampBoundsX` and `clampBoundsY` options when determining the new
   *    view.  When `'limited'`, the `clampBoundsX` and `clampBoundsY` options
   *    are selectively enforced so that the map will not end up more out of
   *    bounds than its current state.
   * @returns {this}
   * @fires geo.event.pan
   */
  this.pan = function (delta, ignoreDiscreteZoom, ignoreClampBounds) {
    var evt = {
      screenDelta: delta
    };

    if (delta.x || delta.y) {
      var unit = m_this.unitsPerPixel(m_zoom);

      var sinr = Math.sin(m_rotation),
          cosr = Math.cos(m_rotation);
      m_camera.pan({
        x: (delta.x * cosr - -delta.y * sinr) * unit,
        y: (delta.x * sinr + -delta.y * cosr) * unit
      });
    }
    /* If m_clampBoundsX or m_clampBoundsY is true, clamp the pan */
    var bounds = m_camera.bounds;
    bounds = fix_bounds(bounds, m_rotation, ignoreClampBounds === 'limited' ? {
      x: delta.x, y: delta.y, unit: unit } : undefined, ignoreClampBounds === true);
    if (bounds !== m_camera.bounds) {
      var panPos = m_this.gcsToDisplay({
        x: m_camera.bounds.left, y: m_camera.bounds.top }, null);
      bounds = m_this.boundsFromZoomAndCenter(m_zoom, {
        x: (bounds.left + bounds.right) / 2,
        y: (bounds.top + bounds.bottom) / 2
      }, m_rotation, null, ignoreDiscreteZoom, true);
      camera_bounds(bounds, m_rotation);
      var clampPos = m_this.gcsToDisplay({
        x: m_camera.bounds.left, y: m_camera.bounds.top }, null);
      evt.screenDelta.x += clampPos.x - panPos.x;
      evt.screenDelta.y += clampPos.y - panPos.y;
    }

    m_center = m_camera.displayToWorld({
      x: m_width / 2,
      y: m_height / 2
    });

    m_this.geoTrigger(geo_event.pan, evt);

    m_this.modified();
    return m_this;
  };

  /**
   * Get/set the map rotation.  The rotation is performed around the current
   * view center.  Rotation mostly ignores `clampBoundsX`, as the behavior
   * feels peculiar otherwise.
   *
   * @param {number} rotation Absolute angle in radians (positive is
   *    clockwise).
   * @param {object} [origin] If specified, rotate about this origin.
   * @param {geo.geoPosition} origin.geo The gcs coordinates of the
   *    rotation center.
   * @param {geo.screenPosition} origin.map The display coordinates of the
   *    rotation center.
   * @param {boolean} [ignoreRotationFunc] If `true`, don't constrain the
   *    rotation.
   * @returns {number|this}
   * @fires geo.event.rotate
   * @fires geo.event.pan
   */
  this.rotation = function (rotation, origin, ignoreRotationFunc) {
    if (rotation === undefined) {
      return m_rotation;
    }
    var aroundPoint = origin && origin.geo && origin.map;

    rotation = fix_rotation(rotation, ignoreRotationFunc);
    if (rotation === m_rotation) {
      return m_this;
    }
    m_rotation = rotation;

    var bounds = m_this.boundsFromZoomAndCenter(m_zoom, m_center, m_rotation, null, ignoreRotationFunc, true);
    m_this.modified();

    camera_bounds(bounds, m_rotation);

    var evt = {
      rotation: m_rotation,
      screenPosition: origin ? origin.map : undefined
    };

    m_this.geoTrigger(geo_event.rotate, evt);

    if (aroundPoint) {
      var shifted = m_this.gcsToDisplay(origin.geo);
      m_this.pan({ x: origin.map.x - shifted.x, y: origin.map.y - shifted.y }, undefined, true);
    } else {
      m_this.pan({ x: 0, y: 0 }, undefined, true);
    }
    /* Changing the rotation can change our minimum zoom */
    reset_minimum_zoom();
    m_this.zoom(m_zoom, undefined, ignoreRotationFunc);
    return m_this;
  };

  /**
   * Set center of the map to the given geographic coordinates, or get the
   * current center.  Uses bare objects {x: 0, y: 0}.
   *
   * @param {geo.geoPosition} coordinates If specified, the new center of the
   *    map.
   * @param {string|geo.transform|null} [gcs] `undefined` to use the interface
   *    gcs, `null` to use the map gcs, or any other transform.  If setting the
   *    center, it is converted from this gcs to the map projection.  The
   *    returned center is converted from the map projection to this gcs.
   * @param {boolean} [ignoreDiscreteZoom] If `true`, ignore the `discreteZoom`
   *    option when determining the new view.
   * @param {boolean|'limited'} [ignoreClampBounds] If `true` ignore the
   *    `clampBoundsX` and `clampBoundsY` options when determining the new
   *    view.  When `'limited'`, the `clampBoundsX` and `clampBoundsY` options
   *    are selectively enforced so that the map will not end up more out of
   *    bounds than its current state.
   * @returns {geo.geoPosition|this}
   * @fires geo.event.pan
   */
  this.center = function (coordinates, gcs, ignoreDiscreteZoom, ignoreClampBounds) {
    var center;
    if (coordinates === undefined) {
      center = $.extend({}, m_this.worldToGcs(m_center, gcs));
      return center;
    }

    // get the screen coordinates of the new center
    center = m_this.gcsToWorld(coordinates, gcs);

    camera_bounds(m_this.boundsFromZoomAndCenter(m_zoom, center, m_rotation, null, ignoreDiscreteZoom, ignoreClampBounds), m_rotation);
    m_this.modified();
    // trigger a pan event
    m_this.geoTrigger(geo_event.pan, {
      screenDelta: { x: 0, y: 0 }
    });
    return m_this;
  };

  /**
   * Add a layer to the map.
   *
   * @param {string} layerName The type of layer to add to the map.
   * @param {object} arg Parameters for the new layer.
   * @returns {geo.layer}
   * @fires geo.event.layerAdd
   */
  this.createLayer = function (layerName, arg) {
    console.log('createLayer')
    arg = arg || {};
    var newLayer = registry.createLayer(layerName, m_this, arg);

    if (newLayer) {
      m_this.addChild(newLayer);
      m_this.children().forEach(function (c) {
        if (c instanceof uiLayer) {
          c.moveToTop();
        }
      });
      newLayer._update();
      m_this.modified();

      m_this.geoTrigger(geo_event.layerAdd, {
        target: m_this,
        layer: newLayer
      });
    }

    return newLayer;
  };

  /**
   * Remove a layer from the map.
   *
   * @param {geo.layer?} layer Layer to remove from the map.
   * @returns {geo.layer}
   * @fires geo.event.layerRemove
   */
  this.deleteLayer = function (layer) {

    if (layer !== null && layer !== undefined) {
      layer._exit();
      m_this.removeChild(layer);

      m_this.modified();

      m_this.geoTrigger(geo_event.layerRemove, {
        target: m_this,
        layer: layer
      });
    }

    // Return deleted layer (similar to createLayer) as in the future
    // we may provide extension of this method to support deletion of
    // layer using id or some sort.
    return layer;
  };

  /**
   * Get or set the size of the map.
   *
   * @param {geo.screenSize} [arg] Size in pixels.
   * @returns {geo.screenSize|this} The size in pixels or the map object.
   * @fires geo.event.resize
   */
  this.size = function (arg) {
    if (arg === undefined) {
      return {
        width: m_width,
        height: m_height
      };
    }
    // store the original center and restore it after the resize
    var oldCenter = m_this.center();
    m_width = arg.width || m_width;
    m_height = arg.height || m_height;

    reset_minimum_zoom();
    var newZoom = fix_zoom(m_zoom);
    if (newZoom !== m_zoom) {
      m_this.zoom(newZoom);
    }
    m_this.camera().viewport = {
      width: m_width,
      height: m_height,
      left: m_node.offset().left,
      top: m_node.offset().top
    };
    m_this.center(oldCenter);

    m_this.geoTrigger(geo_event.resize, {
      target: m_this,
      width: m_width,
      height: m_height
    });

    m_this.modified();
    return m_this;
  };

  /**
   * Get the rotated size of the map.  This is the width and height of the
   * non-rotated area necessary to enclose the rotated area in pixels.
   *
   * @returns {geo.screenSize} The size that fits the rotated map.
   */
  this.rotatedSize = function () {
    if (!this.rotation()) {
      return {
        width: m_width,
        height: m_height
      };
    }
    var bds = rotate_bounds_center({ x: 0, y: 0 }, { width: m_width, height: m_height }, this.rotation());
    return {
      width: Math.abs(bds.right - bds.left),
      height: Math.abs(bds.top - bds.bottom)
    };
  };

  /**
   * Convert from gcs coordinates to map world coordinates.
   *
   * @param {geo.geoPosition|geo.geoPosition[]} c The input coordinate to
   *    convert.
   * @param {string|geo.transform|null} [gcs] Input gcs.  `undefined` to use
   *    the interface gcs, `null` to use the map gcs, or any other transform.
   * @returns {geo.worldPosition|geo.worldPosition[]} World space coordinates.
   */
  this.gcsToWorld = function (c, gcs) {
    if (Array.isArray(c)) {
      return c.map(function (pt) {
        return m_this.gcsToWorld(pt, gcs);
      });
    }
    gcs = gcs === null ? m_gcs : gcs === undefined ? m_ingcs : gcs;
    if (gcs !== m_gcs) {
      c = transform.transformCoordinates(gcs, m_gcs, c);
    }
    if (m_origin.x || m_origin.y || m_origin.z) {
      c = transform.affineForward({ origin: m_origin }, [c])[0];
    } else if (!('z' in c)) {
      c = { x: c.x, y: c.y, z: 0 };
    }
    return c;
  };

  /**
   * Convert from map world coordinates to gcs coordinates.
   *
   * @param {geo.worldPosition|geo.worldPosition[]} c The input coordinate to
   *    convert.
   * @param {string|geo.transform|null} [gcs] output gcs.  `undefined` to use
   *    the interface gcs, `null` to use the map gcs, or any other transform.
   * @returns {geo.geoPosition|geo.geoPosition[]} GCS space coordinates.
   */
  this.worldToGcs = function (c, gcs) {
    if (Array.isArray(c)) {
      return c.map(function (pt) {
        return m_this.worldToGcs(pt, gcs);
      });
    }
    if (m_origin.x || m_origin.y || m_origin.z) {
      c = transform.affineInverse({ origin: m_origin }, [c])[0];
    } else if (!('z' in c)) {
      c = { x: c.x, y: c.y, z: 0 };
    }
    gcs = gcs === null ? m_gcs : gcs === undefined ? m_ingcs : gcs;
    if (gcs !== m_gcs) {
      c = transform.transformCoordinates(m_gcs, gcs, c);
    }
    return c;
  };

  /**
   * Convert from gcs coordinates to display coordinates.  This is identical to
   * calling `gcsToWorld` and then `worldToDisplay`.
   *
   * @param {geo.geoPosition|geo.geoPosition[]} c The input coordinate to
   *    convert.
   * @param {string|geo.transform|null} [gcs] Input gcs.  `undefined` to use
   *    the interface gcs, `null` to use the map gcs, or any other transform.
   * @returns {geo.screenPosition|geo.screenPosition[]} Display space
   *    coordinates.
   */
  this.gcsToDisplay = function (c, gcs) {
    c = m_this.gcsToWorld(c, gcs);
    return m_this.worldToDisplay(c);
  };

  /**
   * Convert from world coordinates to display coordinates using the attached
   * camera.
   *
   * @param {geo.worldPosition|geo.worldPosition[]} c The input coordinate to
   *    convert.
   * @returns {geo.screenPosition|geo.screenPosition[]} Display space
   *    coordinates.
   */
  this.worldToDisplay = function (c) {
    if (Array.isArray(c)) {
      return c.map(function (pt) {
        return m_camera.worldToDisplay(pt);
      });
    }
    return m_camera.worldToDisplay(c);
  };

  /**
   * Convert from display to gcs coordinates.  This is identical to calling
   * `displayToWorld` and then `worldToGcs`.
   *
   * @param {geo.screenPosition|geo.screenPosition[]} c The input display
   *    coordinate to convert.
   * @param {string|geo.transform|null} [gcs] Output gcs.  `undefined` to use
   *    the interface gcs, `null` to use the map gcs, or any other transform.
   * @returns {geo.geoPosition|geoPosition[]} GCS space coordinates.
   */
  this.displayToGcs = function (c, gcs) {
    c = m_this.displayToWorld(c); // done via camera
    return m_this.worldToGcs(c, gcs);
  };

  /**
   * Convert from display coordinates to world coordinates using the attached
   * camera.
   *
   * @param {geo.screenPosition|geo.screenPosition[]} c The input coordinate to
   *    convert.
   * @returns {geo.worldPosition|geo.worldPosition[]} World space coordinates.
   */
  this.displayToWorld = function (c) {
    if (Array.isArray(c)) {
      return c.map(function (pt) {
        return m_camera.displayToWorld(pt);
      });
    }
    return m_camera.displayToWorld(c);
  };

  /**
   * Redraw the map and all its layers.
   *
   * @returns {this} The map object.
   * @fires geo.event.draw
   * @fires geo.event.drawEnd
   */
  this.draw = function () {
    var i,
        layers = m_this.children();

    m_this.geoTrigger(geo_event.draw, {
      target: m_this
    });

    m_this._update();

    for (i = 0; i < layers.length; i += 1) {
      layers[i].draw();
    }

    m_this.geoTrigger(geo_event.drawEnd, {
      target: m_this
    });

    return m_this;
  };

  /**
   * Get, set, or create and set a file reader to a layer in the map to be used
   * as a drop target.
   *
   * @param {string|object} [readerOrName] `undefined` to get the current
   *    reader, an instance of a file reader to set the reader, or a name to
   *    create a file reader.
   * @param {object} [opts] options Parameters for creating a file reader when
   *    the reader is specified by name.  If this includes `layer`, use that
   *    layer, otherwise create a layer using these options.
   * @returns {geo.fileReader|this}
   */
  this.fileReader = function (readerOrName, opts) {
    if (readerOrName === undefined) {
      return m_fileReader;
    }
    if (typeof readerOrName === 'string') {
      opts = opts || {};
      if (!opts.layer) {
        opts.layer = m_this.createLayer('feature', $.extend({}, opts));
      }
      opts.renderer = opts.layer.renderer().api();
      m_fileReader = registry.createFileReader(readerOrName, opts);
    } else {
      m_fileReader = readerOrName;
    }
    return m_this;
  };

  /**
   * Initialize the map.
   *
   * @returns {this} The map object.
   */
  this._init = function () {

    if (m_node === undefined || m_node === null) {
      throw new Error('Map require DIV node');
    }

    if (m_node.data('data-geojs-map') && $.isFunction(m_node.data('data-geojs-map').exit)) {
      m_node.data('data-geojs-map').exit();
    }
    m_node.addClass('geojs-map');
    m_node.data('data-geojs-map', m_this);
    return m_this;
  };

  /**
   * Update map.  This updates all layers of the map.
   *
   * @param {object} [request] Optional information about the source of this
   *    update request.  This could be an event, for instance.  It is passed
   *    to individual layer's `_update` function.
   * @returns {this} The map object.
   */
  this._update = function (request) {
    var i,
        layers = m_this.children();
    for (i = 0; i < layers.length; i += 1) {
      layers[i]._update(request);
    }
    return m_this;
  };

  /**
   * Exit this map.  This removes all layers, destroys current interactor, and
   * empties the associated DOM node.
   */
  this.exit = function () {
    var i,
        layers = m_this.children();
    for (i = layers.length - 1; i >= 0; i -= 1) {
      layers[i]._exit();
      m_this.removeChild(layers[i]);
    }
    if (m_this.interactor()) {
      m_this.interactor().destroy();
      m_this.interactor(null);
    }
    // if the animation queue was shared, this clears it
    m_animationQueue = [];
    m_this.node().data('data-geojs-map', null);
    m_this.node().off('.geo');
    /* make sure the map node has nothing left in it */
    m_this.node().empty();
    $(window).off('resize', resizeSelf);
    s_exit();
  };

  /**
   * Get or set the map interactor.
   *
   * @param {geo.mapInteractor} [arg] If specified, the map interactor to set.
   * @returns {geo.mapInteractor|this} The current map interactor or the map
   *    object.
   */
  this.interactor = function (arg) {
    if (arg === undefined) {
      return m_interactor;
    }
    if (m_interactor && m_interactor !== arg) {
      m_interactor.destroy();
    }
    m_interactor = arg;

    // this makes it possible to set a null interactor
    // i.e. map.interactor(null);
    if (m_interactor) {
      /* If we set a map interactor, make sure we have a tabindex */
      if (!m_node.attr('tabindex')) {
        m_node.attr('tabindex', 0);
      }
      m_interactor.map(m_this);
    }
    return m_this;
  };

  /**
   * Get or set the min/max zoom range.
   *
   * @param {object} [arg] The zoom range.
   * @param {number} [arg.min] The minimum zoom level.
   * @param {number} [arg.max] The maximum zoom level.
   * @param {boolean} [noRefresh] If `true`, don't update the map if the zoom
   *    level has changed.
   * @returns {object|this} The current zoom range or the map object.  The
   *    `min` value is the minimum value that the map can go to based on the
   *    current dimensions and settings, the `origMin` value is the value that
   *    was specified via this function or when the map was created.
   */
  this.zoomRange = function (arg, noRefresh) {
    if (arg === undefined) {
      return $.extend({}, m_validZoomRange);
    }
    if (arg.max !== undefined) {
      m_validZoomRange.max = arg.max;
    }
    if (arg.min !== undefined) {
      m_validZoomRange.min = m_validZoomRange.origMin = arg.min;
    }
    reset_minimum_zoom();
    if (!noRefresh) {
      m_this.zoom(m_zoom);
    }
    return m_this;
  };

  /**
   * Get the current transition or start an animated zoom/pan/rotate.  If a
   * second transition is requested while a transition is already in progress,
   * a new transition is created that is functionally from wherever the map has
   * moved to (possibly partway through the first transition) going to the end
   * point of the new transition.
   *
   * @param {object} [opts] Options for a transition, or `undefined` to get the
   *    current transition.
   * @param {geo.geoPosition} [opts.center] A new map center.
   * @param {number} [opts.zoom] A new map zoom level.
   * @param {geo.geoPosition} [opts.zoomOrigin] An origin to use when zooming
   *    to a new zoom level.
   * @param {number} [opts.rotation] A new map rotation.
   * @param {number} [opts.duration=1000] Transition duration in milliseconds.
   * @param {function} [opts.ease] Easing function for the transition.  This is
   *    in the style of a d3 easing function.
   * @param {function} [opts.interp] Function to use when interpolating
   *    between values.  This gets passed two arrays, the start and end values
   *    for [`x`, `y`, `z` or `zoom`, `rotation`], and returns a function that,
   *    when passed a time value returns an array of the interpolated [`x`,
   *    `y`, `z` or `zoom`, `rotation`] values.
   * @param {boolean} [opts.zCoord] If `true`, convert zoom values to z values
   *    for interpolation.
   * @param {function} [opts.done] If specified, call this function when a
   *    transition completes.  The function is called with an object that
   *    contains `cancel`: a boolean if the transition was canceled, `source`:
   *    a value based on what canceled a transition, `transition`: the current
   *    transition that just completed, `next`: a boolean if another transition
   *    follows immediately.
   * @param {string|geo.transform|null} [gcs] Input gcs.  `undefined` to use
   *    the interface gcs, `null` to use the map gcs, or any other transform.
   *    Applies only to `opts.center` and to converting zoom values to height,
   *    if specified.
   * @param {number} [animTime] The animation frame time (from a
   *    `window.requestAnimationFrame` callback).  Used if a new transition is
   *    requested because the current transition has completed to keep things
   *    synchronized.
   * @returns {geo.map}
   * @fires geo.event.transitionstart
   * @fires geo.event.transitionend
   * @fires geo.event.transitioncancel
   */
  this.transition = function (opts, gcs, animTime) {

    if (opts === undefined) {
      return m_transition;
    }

    if (m_transition) {
      /* The queued transition needs to combine the current transition's
       * endpoint, any other queued transition, and the new transition to be
       * complete. */
      var transitionEnd = $.extend(true, {}, m_transition.end);
      if (transitionEnd.center && m_gcs !== m_ingcs) {
        transitionEnd.center = transform.transformCoordinates(m_gcs, m_ingcs, transitionEnd.center);
      }
      m_queuedTransition = $.extend({}, transitionEnd || {}, m_queuedTransition || {}, opts);
      return m_this;
    }

    /* Basic linear interpolation between two values. */
    function interp1(p0, p1, t) {
      return p0 + (p1 - p0) * t;
    }
    /**
     * Generate an interpolation function that interpolates all array entries.
     *
     * @param {array} p0 An array of numbers to interpolate from.
     * @param {array} p1 An array of numbers to interpolate to.
     * @returns {function} A function that, given `t`, returns an array of
     *      interpolated values.
     * @private
     */
    function defaultInterp(p0, p1) {
      return function (t) {
        var result = [];
        $.each(p0, function (idx) {
          result.push(interp1(p0[idx], p1[idx], t));
        });
        return result;
      };
    }

    var units = m_this.unitsPerPixel(0);

    // Transform zoom level into z-coordinate and inverse.
    function zoom2z(z) {
      return vgl.zoomToHeight(z + 1, m_width, m_height) * units;
    }
    function z2zoom(z) {
      return vgl.heightToZoom(z / units, m_width, m_height) - 1;
    }

    var defaultOpts = {
      center: undefined,
      zoom: m_this.zoom(),
      rotation: m_this.rotation(),
      duration: 1000,
      ease: function ease(t) {
        return t;
      },
      interp: defaultInterp,
      done: null,
      zCoord: true
    };

    if (opts.center) {
      gcs = gcs === null ? m_gcs : gcs === undefined ? m_ingcs : gcs;
      opts = $.extend(true, {}, opts);
      opts.center = util.normalizeCoordinates(opts.center);
      if (gcs !== m_gcs) {
        opts.center = transform.transformCoordinates(gcs, m_gcs, opts.center);
      }
    }
    opts = $.extend(true, {}, defaultOpts, opts);

    m_transition = {
      start: {
        center: m_this.center(undefined, null),
        zoom: m_this.zoom(),
        rotation: m_this.rotation()
      },
      end: {
        center: opts.center,
        zoom: fix_zoom(opts.zoom),
        rotation: fix_rotation(opts.rotation, undefined, true)
      },
      ease: opts.ease,
      zCoord: opts.zCoord,
      done: opts.done,
      duration: opts.duration,
      zoomOrigin: opts.zoomOrigin
    };

    m_transition.interp = opts.interp([m_transition.start.center.x, m_transition.start.center.y, opts.zCoord ? zoom2z(m_transition.start.zoom) : m_transition.start.zoom, m_transition.start.rotation], [m_transition.end.center ? m_transition.end.center.x : m_transition.start.center.x, m_transition.end.center ? m_transition.end.center.y : m_transition.start.center.y, opts.zCoord ? zoom2z(m_transition.end.zoom) : m_transition.end.zoom, m_transition.end.rotation]);

    /**
     * Process an animation from during a transition.
     *
     * @param {number} time The animation frame time.  Used to ensure multiple
     *      transitions are smooth.
     * @private
     */
    function anim(time) {
      var done = m_transition.done,
          next = m_queuedTransition;
      if (m_transition.cancel === true) {
        /* Finish cancelling a transition. */
        m_this.geoTrigger(geo_event.transitioncancel, opts);
        if (done) {
          done({
            cancel: true,
            source: m_transition.cancelSource,
            transition: m_transition
          });
        }
        m_transition = null;
        /* There will only be a queuedTransition if it was created after this
         * transition was cancelled */
        if (m_queuedTransition) {
          next = m_queuedTransition;
          m_queuedTransition = null;
          m_this.transition(next, undefined, time);
        }
        return;
      }

      if (!m_transition.start.time) {
        m_transition.start.time = time;
        m_transition.end.time = time + opts.duration;
      }
      m_transition.time = time - m_transition.start.time;
      if (time >= m_transition.end.time || next) {
        if (!next) {
          if (m_transition.end.center) {
            var needZoom = m_zoom !== fix_zoom(m_transition.end.zoom);
            m_this.center(m_transition.end.center, null, needZoom, needZoom);
          }
          m_this.zoom(m_transition.end.zoom, m_transition.zoomOrigin);
          m_this.rotation(fix_rotation(m_transition.end.rotation));
        }

        m_this.geoTrigger(geo_event.transitionend, opts);

        if (done) {
          done({ next: !!next });
        }

        m_transition = null;
        if (m_queuedTransition) {
          next = m_queuedTransition;
          m_queuedTransition = null;
          m_this.transition(next, undefined, time);
        }

        return;
      }

      var z = m_transition.ease((time - m_transition.start.time) / opts.duration);

      var p = m_transition.interp(z);
      if (m_transition.zCoord) {
        p[2] = z2zoom(p[2]);
      }
      if (fix_zoom(p[2], true) === m_zoom) {
        m_this.center({
          x: p[0],
          y: p[1]
        }, null, true, true);
      } else {
        m_center = m_this.gcsToWorld({ x: p[0], y: p[1] }, null, true, true);
        m_this.zoom(p[2], m_transition.zoomOrigin, true);
      }
      m_this.rotation(p[3], undefined, true);

      m_this.scheduleAnimationFrame(anim);
    }

    m_this.geoTrigger(geo_event.transitionstart, opts);

    if (geo_event.cancelNavigation) {
      m_transition = null;
      m_this.geoTrigger(geo_event.transitionend, opts);
      return m_this;
    } else if (geo_event.cancelAnimation) {
      // run the navigation synchronously
      opts.duration = 0;
      anim(0);
    } else if (animTime) {
      anim(animTime);
    } else {
      m_this.scheduleAnimationFrame(anim);
    }
    return m_this;
  };

  /**
   * Cancel any existing transition.  The transition will send a cancel event
   * at the next animation frame, but no further activity occurs.
   *
   * @param {string} [source] Optional cause of the cancel.  This can be any
   *    value, but something like `(method name).(action)` is recommended to
   *    allow other functions to determine the source and cause of the
   *    transition being canceled.
   * @returns {boolean} `true` if a transition was in progress.
   * @fires geo.event.transitioncancel
   */
  this.transitionCancel = function (source) {
    if (m_transition && (m_transition.cancel !== true || m_queuedTransition)) {
      m_transition.cancel = true;
      m_transition.cancelSource = source || m_transition.cancelSource || '';
      m_queuedTransition = null;
      return true;
    }
    return false;
  };

  /**
   * Get/set the locations of the current map edges.  When set, the left-top
   * and right-bottom corners are transformed to the map's gcs and then used
   * to set the bounds.
   *
   * @param {geo.geoBounds} [bds] The requested map bounds.
   * @param {string|geo.transform|null} [gcs] `undefined` to use the interface
   *    gcs, `null` to use the map gcs, or any other transform.  If setting the
   *    bounds, they are converted from this gcs to the map projection.  The
   *    returned bounds are converted from the map projection to this gcs.
   * @returns {geo.geoBounds} The actual new map bounds.
   */
  this.bounds = function (bds, gcs) {
    var nav;

    gcs = gcs === null ? m_gcs : gcs === undefined ? m_ingcs : gcs;
    if (bds !== undefined) {
      if (gcs !== m_gcs) {
        var trans = transform.transformCoordinates(gcs, m_gcs, [{
          x: bds.left, y: bds.top }, { x: bds.right, y: bds.bottom }]);
        bds = {
          left: trans[0].x,
          top: trans[0].y,
          right: trans[1].x,
          bottom: trans[1].y
        };
      }
      bds = fix_bounds(bds, m_rotation);
      nav = m_this.zoomAndCenterFromBounds(bds, m_rotation, null);

      // This might have consequences in terms of bounds/zoom clamping.
      // What behavior do we expect from this method in that case?
      m_this.zoom(nav.zoom);
      m_this.center(nav.center, null);
    }

    return m_this.boundsFromZoomAndCenter(m_zoom, m_center, m_rotation, gcs, true);
  };

  /**
   * Get/set the maximum view area of the map.  If the map wraps, this is the
   * unwrapped area.
   *
   * @param {geo.geoBounds} [bounds] The map bounds.
   * @param {string|geo.transform|null} [gcs] `undefined` to use the interface
   *    gcs, `null` to use the map gcs, or any other transform.  If setting the
   *    bounds, they are converted from this gcs to the map projection.  The
   *    returned bounds are converted from the map projection to this gcs.
   * @returns {geo.geoBounds|this} The map maximum bounds or the map object.
   */
  this.maxBounds = function (bounds, gcs) {
    gcs = gcs === null ? m_gcs : gcs === undefined ? m_ingcs : gcs;
    if (bounds === undefined) {
      return {
        left: transform.transformCoordinates(m_gcs, gcs, {
          x: m_maxBounds.left, y: 0 }).x,
        right: transform.transformCoordinates(m_gcs, gcs, {
          x: m_maxBounds.right, y: 0 }).x,
        bottom: transform.transformCoordinates(m_gcs, gcs, {
          x: 0, y: m_maxBounds.bottom }).y,
        top: transform.transformCoordinates(m_gcs, gcs, {
          x: 0, y: m_maxBounds.top }).y
      };
    }
    var cx = ((bounds.left || 0) + (bounds.right || 0)) / 2,
        cy = ((bounds.bottom || 0) + (bounds.top || 0)) / 2;
    if (bounds.left !== undefined) {
      m_maxBounds.left = transform.transformCoordinates(gcs, m_gcs, {
        x: bounds.left, y: cy }).x;
    }
    if (bounds.right !== undefined) {
      m_maxBounds.right = transform.transformCoordinates(gcs, m_gcs, {
        x: bounds.right, y: cy }).x;
    }
    if (bounds.bottom !== undefined) {
      m_maxBounds.bottom = transform.transformCoordinates(gcs, m_gcs, {
        x: cx, y: bounds.bottom }).y;
    }
    if (bounds.top !== undefined) {
      m_maxBounds.top = transform.transformCoordinates(gcs, m_gcs, {
        x: cx, y: bounds.top }).y;
    }
    reset_minimum_zoom();
    m_this.zoom(m_zoom);
    m_this.pan({ x: 0, y: 0 });
    return this;
  };

  /**
   * Get the center zoom level necessary to display the given bounds.
   *
   * @param {geo.geoBounds} bounds The requested map bounds.  `right` must be
   *    greater than `left` and `bottom` must be greater than `top` in the
   *    map's gcs (after conversion from the provided gcs).
   * @param {number} rotation Rotation in clockwise radians.
   * @param {string|geo.transform|null} [gcs] `undefined` to use the interface
   *    gcs, `null` to use the map gcs, or any other transform.
   * @returns {geo.zoomAndCenter}
   */
  this.zoomAndCenterFromBounds = function (bounds, rotation, gcs) {
    var center, zoom;

    gcs = gcs === null ? m_gcs : gcs === undefined ? m_ingcs : gcs;
    if (gcs !== m_gcs) {
      var trans = transform.transformCoordinates(gcs, m_gcs, [{
        x: bounds.left, y: bounds.top }, { x: bounds.right, y: bounds.bottom }]);
      bounds = {
        left: trans[0].x,
        top: trans[0].y,
        right: trans[1].x,
        bottom: trans[1].y
      };
    }
    if (bounds.left >= bounds.right || bounds.bottom >= bounds.top) {
      throw new Error('Invalid bounds provided');
    }

    // calculate the zoom to fit the bounds
    zoom = fix_zoom(calculate_zoom(bounds, rotation));

    // clamp bounds if necessary
    bounds = fix_bounds(bounds, rotation);

    /* This relies on having the map projection coordinates be uniform
     * regardless of location.  If not, the center will not be correct. */
    // calculate new center
    center = {
      x: (bounds.left + bounds.right) / 2 - m_origin.x,
      y: (bounds.top + bounds.bottom) / 2 - m_origin.y
    };
    if (gcs !== m_gcs) {
      center = transform.transformCoordinates(m_gcs, gcs, center);
    }
    return {
      zoom: zoom,
      center: center
    };
  };

  /**
   * Get the bounds that will be displayed with the given zoom and center.
   *
   * Note: the bounds may not have the requested zoom and center due to map
   * restrictions.
   *
   * @param {number} zoom The requested zoom level.
   * @param {geo.geoPosition} center The requested center.
   * @param {number} rotation The requested rotation in clockwise radians.
   * @param {string|geo.transform|null} [gcs] `undefined` to use the interface
   *    gcs, `null` to use the map gcs, or any other transform.
   * @param {boolean} ignoreDiscreteZoom If `true`, ignore the `discreteZoom`
   *    option when determining the new view.
   * @param {boolean} [ignoreClampBounds] If `true` and `clampBoundsX` or
   *    `clampBoundsY` is set, allow the bounds to be less clamped.
   *    The map's `maxBounds` can be shifted so that they lie no further than
   *    the center of the bounds (rather than being forced to be at the edge).
   * @returns {geo.geoBounds}
   */
  this.boundsFromZoomAndCenter = function (zoom, center, rotation, gcs, ignoreDiscreteZoom, ignoreClampBounds) {
    var width, height, halfw, halfh, bounds, units;

    gcs = gcs === null ? m_gcs : gcs === undefined ? m_ingcs : gcs;
    // preprocess the arguments
    zoom = fix_zoom(zoom, ignoreDiscreteZoom);
    units = m_this.unitsPerPixel(zoom);
    center = m_this.gcsToWorld(center, null);

    // get half the width and height in world coordinates
    width = m_width * units;
    height = m_height * units;
    halfw = width / 2;
    halfh = height / 2;

    // calculate the bounds.  This is only valid if the map projection has
    // uniform units in each direction.  If not, then worldToGcs should be
    // used.

    if (rotation) {
      center.x += m_origin.x;
      center.y += m_origin.y;
      bounds = rotate_bounds_center(center, { width: width, height: height }, rotation);
      // correct the bounds when clamping is enabled
      bounds.width = width;
      bounds.height = height;
      bounds = fix_bounds(bounds, rotation, undefined, ignoreClampBounds);
    } else {
      bounds = {
        left: center.x - halfw + m_origin.x,
        right: center.x + halfw + m_origin.x,
        bottom: center.y - halfh + m_origin.y,
        top: center.y + halfh + m_origin.y
      };
      // correct the bounds when clamping is enabled
      bounds = fix_bounds(bounds, 0, undefined, ignoreClampBounds);
    }
    if (gcs !== m_gcs) {
      var bds = transform.transformCoordinates(m_gcs, gcs, [[bounds.left, bounds.top], [bounds.right, bounds.bottom]]);
      bounds = {
        left: bds[0][0], top: bds[0][1], right: bds[1][0], bottom: bds[1][1]
      };
    }
    /* Add the original width and height of the viewport before rotation. */
    bounds.width = width;
    bounds.height = height;
    return bounds;
  };

  /**
   * Get/set the discrete zoom flag.  If `true`, the map will snap to integer
   * zoom levels.
   *
   * @param {boolean} [discreteZoom] If specified, the new discrete zoom flag.
   * @returns {boolean|this} The current discrete zoom flag or the map object.
   */
  this.discreteZoom = function (discreteZoom) {
    if (discreteZoom === undefined) {
      return m_discreteZoom;
    }
    discreteZoom = discreteZoom ? true : false;
    if (m_discreteZoom !== discreteZoom) {
      m_discreteZoom = discreteZoom;
      if (m_discreteZoom) {
        m_this.zoom(Math.round(m_this.zoom()));
      }
      if (m_this.interactor()) {
        m_this.interactor().options({ discreteZoom: m_discreteZoom });
      }
    }
    return m_this;
  };

  /**
   * Get the layers contained in the map.
   * Alias of {@link geo.sceneObject#children}.
   * @method
   */
  this.layers = this.children;

  /**
   * Update the attribution notice displayed on the bottom right corner of
   * the map.  The content of this notice is managed by individual layers.
   * This method queries all of the visible layers and joins the individual
   * attribution notices into a single element.  By default, this method
   * is called on each of the following events:
   *
   *   * {@link geo.event.layerAdd}
   *   * {@link geo.event.layerRemove}
   *
   * In addition, layers should call this method when their own attribution
   * notices have changed.  Users, in general, should not need to call this.
   *
   * @returns {this} Chainable.
   */
  this.updateAttribution = function () {
    // clear any existing attribution content
    m_this.node().find('.geo-attribution').remove();

    // generate a new attribution node
    var $a = $('<div/>').addClass('geo-attribution').on('mousedown', function (evt) {
      evt.stopPropagation();
    });

    // append content from each layer
    m_this.children().forEach(function (layer) {
      var content = layer.attribution();
      if (content) {
        $('<span/>').addClass('geo-attribution-layer').html(content).appendTo($a);
      }
    });

    /* Only add the element if there is at least one attribution */
    if ($('span', $a).length) {
      $a.appendTo(m_this.node());
    }
    return m_this;
  };

  /**
   * Get a screen-shot of all or some of the canvas layers of map.  Note that
   * webGL layers are rerendered, even if
   *   `window.overrideContextAttributes.preserveDrawingBuffer = true;`
   * is set before creating the map object.  Chrome, at least, may not keep the
   * drawing buffers if the tab loses focus (and returning focus won't
   * necessarily rerender).
   *
   * @param {geo.layer|geo.layer[]|false|object} [layers] Either a layer, a
   *    list of layers, falsy to get all layers, or an object that contains
   *    optional values of `layers`, `type`, `encoderOptions`, and additional
   *    values listed in the `opts` parameter (this last form allows a single
   *    argument for the function).
   * @param {string} [type='image/png'] See {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL
   *    canvas.toDataURL}.  Use `'canvas'` to return the canvas element (this
   *    can be used to get the results as a blob, which can be faster for some
   *    operations but is not supported as widely).
   * @param {number} [encoderOptions] See {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL
   *    canvas.toDataURL}.
   * @param {object} [opts] Additional screenshot options.
   * @param {false|string|CanvasRenderingContext2D.fillStyle}
   *    [opts.background='white'] If `false` or `null`, don't prefill the
   *    background.  Otherwise, a css color or
   *    `CanvasRenderingContext2D.fillStyle` to fill the initial canvas.  This
   *    could match the background of the browser page, for instance.
   * @param {boolean|'idle'} [opts.wait=false] If `'idle'`, wait for the map to
   *    be idle and one additional animation frame to occur.  If truthy, wait
   *    for an animation frame to occur.  Otherwise, take the screenshot as
   *    soon as possible.
   * @param {boolean|null} [opts.attribution=null] If `null` or unspecified,
   *    include the attribution only if all layers are used.  If false, never
   *    include the attribution.  If `true`, always include it.
   * @returns {deferred} A jQuery Deferred object.  The done function receives
   *    either a data URL or an `HTMLCanvasElement` with the result.
   * @fires geo.event.screenshot.ready
   */
  this.screenshot = function (layers, type, encoderOptions, opts) {
    var defer;

    if (layers && !Array.isArray(layers) && !layers.renderer) {
      type = type || layers.type;
      encoderOptions = encoderOptions || layers.encoderOptions;
      opts = opts || layers;
      layers = layers.layers;
    }
    opts = opts || {};
    /* if asked to wait, return a Deferred that will do so, calling the
     * screenshot function without waiting once it is done. */
    if (opts.wait) {
      var optsWithoutWait = $.extend({}, opts, { wait: false });
      defer = $.Deferred();

      var waitForRAF = function waitForRAF() {
        window.requestAnimationFrame(function () {
          defer.resolve();
        });
      };

      if (opts.wait === 'idle') {
        m_this.onIdle(waitForRAF);
      } else {
        waitForRAF();
      }
      return defer.then(function () {
        return m_this.screenshot(layers, type, encoderOptions, optsWithoutWait);
      });
    }
    defer = $.when();
    // ensure layers is a list of all the layers we want to include
    if (!layers) {
      layers = m_this.layers();
      if (opts.attribution === null || opts.attribution === undefined) {
        opts.attribution = true;
      }
    } else if (!Array.isArray(layers)) {
      layers = [layers];
    }
    // filter to only the included layers
    layers = layers.filter(function (l) {
      return m_this.layers().indexOf(l) >= 0 && l.opacity() > 0 && (!l.visible || l.visible());
    });
    // sort layers by z-index
    layers = layers.sort(function (a, b) {
      return a.zIndex() - b.zIndex();
    });
    // create a new canvas element
    var result = document.createElement('canvas');
    result.width = m_width;
    result.height = m_height;
    var context = result.getContext('2d');
    // optionally start with a white or custom background
    if (opts.background !== false && opts.background !== null) {
      var background = opts.background;
      if (opts.background === undefined) {
        /* If we are using the map's current background, start with white as a
         * fallback, then fill with the backgrounds of all parents and the map
         * node.  Since each may be partially transparent, this is required to
         * match the web page's color.  It won't use background patterns. */
        context.fillStyle = 'white';
        context.fillRect(0, 0, result.width, result.height);
        m_this.node().parents().get().reverse().forEach(function (elem) {
          background = window.getComputedStyle(elem).backgroundColor;
          if (background && background !== 'transparent') {
            context.fillStyle = background;
            context.fillRect(0, 0, result.width, result.height);
          }
        });
        background = window.getComputedStyle(m_this.node()[0]).backgroundColor;
      }
      if (background && background !== 'transparent') {
        context.fillStyle = background;
        context.fillRect(0, 0, result.width, result.height);
      }
    }
    // for each layer, copy to our new canvas.
    layers.forEach(function (layer) {
      var opacity = layer.opacity();
      layer.node().children('canvas').each(function () {
        var canvasElem = $(this);
        defer = defer.then(function () {
          if (layer.renderer().api() === 'vgl') {
            layer.renderer()._renderFrame();
          }
          drawLayerImageToContext(context, opacity, canvasElem, canvasElem[0]);
        });
      });
      if (layer.node().children().not('canvas').length || !layer.node().children().length) {
        defer = defer.then(function () {
          return util.htmlToImage(layer.node(), 1).done(function (img) {
            drawLayerImageToContext(context, 1, $([]), img);
          });
        });
      }
    });
    if (opts.attribution) {
      m_this.node().find('.geo-attribution').each(function () {
        var attrElem = $(this);
        defer = defer.then(function () {
          return util.htmlToImage(attrElem, 1).done(function (img) {
            drawLayerImageToContext(context, 1, $([]), img);
          });
        });
      });
    }
    defer = defer.then(function () {
      var canvas = result;
      if (type !== 'canvas') {
        try {
          result = result.toDataURL(type, encoderOptions);
        } catch (err) {
          console.warn('Failed to convert screenshot to output', err);
          var failure = $.Deferred();
          failure.reject();
          return failure;
        }
      }
      m_this.geoTrigger(geo_event.screenshot.ready, {
        canvas: canvas,
        screenshot: result
      });
      return result;
    });
    return defer;
  };

  /**
   * Instead of each function using `window.requestAnimationFrame`, schedule
   * all such frames through this function.  This allows the callbacks to be
   * reordered or removed as needed and reduces overhead in Chrome a small
   * amount.  Also, if the animation queue is shared between map instances, the
   * callbacks will be called in a single time slice, providing better
   * synchronization.
   *
   * @param {function} callback Function to call during the animation frame.
   *    It is called with an animation epoch, exactly as
   *    `requestAnimationFrame`.
   * @param {boolean|'remove'} [action=false] Falsy to only add the callback if
   *    it is not already scheduled.  `'remove'` to remove the callback (use
   *    this instead of `cancelAnimationFrame`).  Any other truthy value moves
   *    the callback to the end of the list.
   * @returns {number} An integer as returned by
   *    `window.requestAnimationFrame`.
   */
  this.scheduleAnimationFrame = function (callback, action) {
    if (!m_animationQueue.length) {
      /* By refering to requestAnimationFrame as a property of window, versus
       * explicitly using window.requestAnimationFrame, we prevent the
       * stripping of 'window' off of the reference and allow our tests to
       * override this if needed. */
      m_animationQueue.push(window['requestAnimationFrame'](processAnimationFrame));
    }
    var pos = m_animationQueue.indexOf(callback, 1);
    if (pos >= 0) {
      if (!action) {
        return;
      }
      m_animationQueue.splice(pos, 1);
      if (action === 'remove') {
        return;
      }
    }
    m_animationQueue.push(callback);
    return m_animationQueue[0];
  };

  /**
   * Draw a layer image to a canvas context.  The layer's opacity and transform
   * are applied.  This is used as part of making a screenshot.
   *
   * @param {context} context The 2d canvas context to draw into.
   * @param {number} opacity The opacity in the range [0, 1].
   * @param {object} elem A jQuery element that might have a transform.
   * @param {HTMLImageObject} img The image or canvas to draw to the canvas.
   * @private
   */
  function drawLayerImageToContext(context, opacity, elem, img) {
    context.globalAlpha = opacity;
    var transform = elem.css('transform');
    // if the canvas is being transformed, apply the same transformation
    if (transform && transform.substr(0, 7) === 'matrix(') {
      context.setTransform.apply(context, transform.substr(7, transform.length - 8).split(',').map(parseFloat));
    } else {
      context.setTransform(1, 0, 0, 1, 0, 0);
    }
    context.drawImage(img, 0, 0);
  }

  /**
   * Sevice the callback during an animation frame.  This uses splice to modify
   * the `animationQueue` to allow multiple map instances to share the queue.
   * @private
   */
  function processAnimationFrame() {
    var queue = m_animationQueue.splice(0, m_animationQueue.length);

    /* The first entry is the reference to the window.requestAnimationFrame. */
    for (var i = 1; i < queue.length; i += 1) {
      try {
        queue[i].apply(this, arguments);
      } catch (err) {
        console.error(err);
      }
    }
  }

  /*
   * The following are some private methods for interacting with the camera.
   * In order to hide the complexity of dealing with map aspect ratios,
   * clamping behavior, resetting zoom levels on resize, etc. from the
   * layers, the map handles camera movements directly.  This requires
   * passing all camera movement events through the map initially.  The
   * map uses these methods to fix up the events according to the constraints
   * of the display and passes the event to the layers.
   */
  /**
   * Calculate the scaling factor to fit the given map bounds into the viewport
   * with the correct aspect ratio.
   *
   * @param {geo.geoBounds} bounds A desired bounds.
   * @returns {object} Multiplicative aspect ratio correction with x and y
   *    values.
   * @private
   */
  function camera_scaling(bounds) {
    var width = bounds.right - bounds.left,
        height = bounds.top - bounds.bottom,
        ar_bds = Math.abs(width / height),
        ar_vp = m_width / m_height,
        sclx,
        scly;

    if (ar_bds > ar_vp) {
      // fit left and right
      sclx = 1;

      // grow top and bottom
      scly = ar_bds / ar_vp;
    } else {
      // fit top and bottom
      scly = 1;

      // grow left and right
      sclx = ar_vp / ar_bds;
    }
    return { x: sclx, y: scly };
  }

  /**
   * Adjust a set of bounds based on a rotation.  If a rotation exists, the
   * returned bounds are typically larger than the source bounds.
   *
   * @param {geo.geoBounds} bounds Bounds to adjust.
   * @param {number} rotation Angle in radians (positive is clockwise).
   * @returns {geo.geoBounds}
   * @private
   */
  function rotate_bounds(bounds, rotation) {
    if (rotation) {
      var center = {
        x: (bounds.left + bounds.right) / 2,
        y: (bounds.top + bounds.bottom) / 2
      };
      var size = {
        width: Math.abs(bounds.left - bounds.right),
        height: Math.abs(bounds.top - bounds.bottom)
      };
      bounds = rotate_bounds_center(center, size, rotation);
    }
    return bounds;
  }

  /**
   * Generate a set of bounds based on a center point, a width and height, and
   * a rotation.
   *
   * @param {geo.geoPosition} center
   * @param {object} size Size of the screen in map gcs.
   * @param {number} size.width
   * @param {number} size.height
   * @param {number} rotation Angle in radians (positive is clockwise).
   * @returns {geo.geoBounds}
   * @private
   */
  function rotate_bounds_center(center, size, rotation) {
    // calculate the half width and height
    var width = size.width / 2,
        height = size.height / 2;
    var sinr = Math.sin(rotation),
        cosr = Math.cos(rotation);
    var ul = {},
        ur = {},
        ll = {},
        lr = {};
    ul.x = center.x + -width * cosr - -height * sinr;
    ul.y = center.y + -width * sinr + -height * cosr;
    ur.x = center.x + width * cosr - -height * sinr;
    ur.y = center.y + width * sinr + -height * cosr;
    ll.x = center.x + -width * cosr - height * sinr;
    ll.y = center.y + -width * sinr + height * cosr;
    lr.x = center.x + width * cosr - height * sinr;
    lr.y = center.y + width * sinr + height * cosr;
    return {
      left: Math.min(ul.x, ur.x, ll.x, lr.x),
      right: Math.max(ul.x, ur.x, ll.x, lr.x),
      bottom: Math.min(ul.y, ur.y, ll.y, lr.y),
      top: Math.max(ul.y, ur.y, ll.y, lr.y)
    };
  }

  /**
   * Calculate the minimum zoom level to fit the given bounds inside the view
   * port using the view port size, the given bounds, and the number of units
   * per pixel.  The method sets the valid zoom bounds as well as the current
   * zoom level to be within that range.
   *
   * @param {geo.geoBounds} bounds Bounds to fit to the screen.
   * @param {number} [rotation] Rotation in radians.  If unspecified, use the
   *    current map rotation.
   * @returns {number} The necessary zoom level.
   * @private
   */
  function calculate_zoom(bounds, rotation) {
    if (rotation === undefined) {
      rotation = m_rotation;
    }
    bounds = rotate_bounds(bounds, rotation);
    // compare the aspect ratios of the viewport and bounds
    var scl = camera_scaling(bounds),
        z;

    if (scl.y > scl.x) {
      // left to right matches exactly
      // center map vertically and have blank borders on the
      // top and bottom (or repeat tiles)
      z = -Math.log2(Math.abs(bounds.right - bounds.left) * scl.x / (m_width * m_unitsPerPixel));
    } else {
      // top to bottom matches exactly, blank border on the
      // left and right (or repeat tiles)
      z = -Math.log2(Math.abs(bounds.top - bounds.bottom) * scl.y / (m_height * m_unitsPerPixel));
    }
    return z;
  }

  /**
   * Reset the minimum zoom level given the current window size.
   * @private
   */
  function reset_minimum_zoom() {
    if (m_clampZoom) {
      m_validZoomRange.min = Math.max(m_validZoomRange.origMin, calculate_zoom(m_maxBounds));
    } else {
      m_validZoomRange.min = m_validZoomRange.origMin;
    }
  }

  /**
   * Return the nearest valid zoom level to the requested zoom.
   * @param {number} zoom A zoom level to adjust to current settings
   * @param {boolean} ignoreDiscreteZoom If `true`, ignore the `discreteZoom`
   *    option when determining the new view.
   * @returns {number} The zoom level clamped to the allowed zoom range and
   *    with other settings applied.
   * @private
   */
  function fix_zoom(zoom, ignoreDiscreteZoom) {
    zoom = Math.round(zoom * 1e6) / 1e6;
    zoom = Math.max(Math.min(m_validZoomRange.max, zoom), m_validZoomRange.min);
    if (m_discreteZoom && !ignoreDiscreteZoom) {
      zoom = Math.round(zoom);
      if (zoom < m_validZoomRange.min) {
        zoom = Math.ceil(m_validZoomRange.min);
      }
    }
    return zoom;
  }

  /**
   * Return a valid rotation angle.
   *
   * @param {number} rotation Proposed rotation.
   * @param {boolean} ignoreRotationFunc If truthy and rotations are allowed,
   *    allow any rotation.  Otherwise, the rotation is passed through the
   *    `allowRotation` function.
   * @param {boolean} noRangeLimit If falsy, ensure that the rotation is in the
   *    range [0, 2*PI).  If it is very close to zero, it is snapped to zero.
   *    If true, the rotation can have any value.
   * @returns {number} the validated rotation
   * @private
   */
  function fix_rotation(rotation, ignoreRotationFunc, noRangeLimit) {
    if (!m_allowRotation) {
      return 0;
    }
    if (!ignoreRotationFunc && typeof m_allowRotation === 'function') {
      rotation = m_allowRotation(rotation);
    }
    /* Ensure that the rotation is in the range [0, 2pi) */
    if (!noRangeLimit) {
      var range = Math.PI * 2;
      rotation = rotation % range + (rotation >= 0 ? 0 : range);
      if (Math.min(Math.abs(rotation), Math.abs(rotation - range)) < 0.00001) {
        rotation = 0;
      }
    }
    return rotation;
  }

  /**
   * Return the nearest valid bounds maintaining the width and height.  Does
   * nothing if `clampBoundsX` and `clampBoundsY` are false.  If a delta is
   * specified, will only clamp if the out-of-bounds condition would be worse.
   * If `ignoreClampBounds` is true, clamping is applied only to prevent more
   * than half the image from being off screen.
   *
   * @param {geo.geoBounds} bounds The new bounds to apply in map gcs
   *    coordinates.
   * @param {number} [rotation] The angle of rotation in radians.  May be falsy
   *    to have no rotation.
   * @param {object} [delta] If present, the shift in position in screen
   *    coordinates.  Bounds will only be adjusted if the bounds would be
   *    more out of position after the shift.
   * @param {number} delta.x
   * @param {number} delta.y
   * @param {number} delta.unit Units per pixel at the current zoom level.
   * @param {boolean} [ignoreClampBounds] If `true` and `clampBoundsX` or
   *    `clampBoundsY` are set, allow the bounds to be less clamped.
   *    Specifically, the map's `maxBounds` can be shifted so that they lie no
   *    further than the center of the bounds (rather than being forced to be
   *    at the edge).
   * @returns {geo.geoBounds} The adjusted bounds.  This may be the same object
   *    passed in `bounds`.
   * @private
   */
  function fix_bounds(bounds, rotation, delta, ignoreClampBounds) {
    if (!m_clampBoundsX && !m_clampBoundsY) {
      return bounds;
    }
    var dx,
        dy,
        maxBounds = m_maxBounds;
    if (rotation) {
      maxBounds = $.extend({}, m_maxBounds);
      /* When rotated, expand the maximum bounds so that they will allow the
       * corners to be visible.  We know the rotated bounding box, plus the
       * original maximum bounds.  To fit the corners of the maximum bounds, we
       * can expand the total bounds by the same factor that the rotated
       * bounding box is expanded from the non-rotated bounding box (for a
       * small rotation, this is sin(rotation) * (original bounding box height)
       * in the width).  This feels like appropriate behaviour with one of the
       * two bounds clamped.  With both, it seems mildly peculiar. */
      var bw = Math.abs(bounds.right - bounds.left),
          bh = Math.abs(bounds.top - bounds.bottom),
          absinr = Math.abs(Math.sin(rotation)),
          abcosr = Math.abs(Math.cos(rotation)),
          ow,
          oh;
      if (bounds.width && bounds.height) {
        ow = bounds.width;
        oh = bounds.height;
      } else if (Math.abs(absinr - abcosr) < 0.0005) {
        /* If we are close to a 45 degree rotation, it is ill-determined to
         * compute the original (pre-rotation) bounds width and height.  In
         * this case, assume that we are using the map's aspect ratio. */
        if (m_width && m_height) {
          var aspect = Math.abs(m_width / m_height);
          var fac = Math.pow(1 + Math.pow(aspect, 2), 0.5);
          ow = Math.max(bw, bh) / fac;
          oh = ow * aspect;
        } else {
          /* Fallback if we don't have width or height */
          ow = bw * abcosr;
          oh = bh * absinr;
        }
      } else {
        /* Compute the pre-rotation (original) bounds width and height */
        ow = (abcosr * bw - absinr * bh) / (abcosr * abcosr - absinr * absinr);
        oh = (abcosr * bh - absinr * bw) / (abcosr * abcosr - absinr * absinr);
      }
      /* Our maximum bounds are expanded based on the projected length of a
       * tilted side of the original bounding box in the rotated bounding box.
       * To handle all rotations, take the minimum difference in width or
       * height. */
      var bdx = bw - Math.max(abcosr * ow, absinr * oh),
          bdy = bh - Math.max(abcosr * oh, absinr * ow);
      maxBounds.left -= bdx;
      maxBounds.right += bdx;
      maxBounds.top += bdy;
      maxBounds.bottom -= bdy;
    }
    if (ignoreClampBounds) {
      maxBounds = {
        left: maxBounds.left - (bounds.right - bounds.left) / 2,
        right: maxBounds.right + (bounds.right - bounds.left) / 2,
        top: maxBounds.top - (bounds.bottom - bounds.top) / 2,
        bottom: maxBounds.bottom + (bounds.bottom - bounds.top) / 2
      };
    }
    if (m_clampBoundsX) {
      if (bounds.right - bounds.left > maxBounds.right - maxBounds.left) {
        dx = maxBounds.left - (bounds.right - bounds.left - (maxBounds.right - maxBounds.left)) / 2 - bounds.left;
      } else if (bounds.left < maxBounds.left) {
        dx = maxBounds.left - bounds.left;
      } else if (bounds.right > maxBounds.right) {
        dx = maxBounds.right - bounds.right;
      }
      if (dx && (!delta || delta.x * dx > 0)) {
        if (delta && Math.abs(dx) > Math.abs(delta.x * delta.unit)) {
          dx = Math.abs(delta.x * delta.unit) * dx / Math.abs(dx);
        }
        bounds = {
          left: bounds.left += dx,
          right: bounds.right += dx,
          top: bounds.top,
          bottom: bounds.bottom
        };
      }
    }
    if (m_clampBoundsY) {
      if (bounds.top - bounds.bottom > maxBounds.top - maxBounds.bottom) {
        dy = maxBounds.bottom - (bounds.top - bounds.bottom - (maxBounds.top - maxBounds.bottom)) / 2 - bounds.bottom;
      } else if (bounds.top > maxBounds.top) {
        dy = maxBounds.top - bounds.top;
      } else if (bounds.bottom < maxBounds.bottom) {
        dy = maxBounds.bottom - bounds.bottom;
      }
      if (dy && (!delta || -delta.y * dy > 0)) {
        if (delta && Math.abs(dy) > Math.abs(delta.y * delta.unit)) {
          dy = Math.abs(delta.y * delta.unit) * dy / Math.abs(dy);
        }
        bounds = {
          top: bounds.top += dy,
          bottom: bounds.bottom += dy,
          left: bounds.left,
          right: bounds.right
        };
      }
    }
    return bounds;
  }

  /**
   * Call the camera bounds method with the given bounds, but correct for the
   * viewport aspect ratio.
   *
   * @param {geo.geoBounds} bounds The bounds for the camera.  If a rotation
   *    is specified, the bounds need to also contain the map gcs width and
   *    height.
   * @param {number} [rotation] The map rotation in radians.
   * @private
   */
  function camera_bounds(bounds, rotation) {
    m_camera.rotation = rotation || 0;
    /* When dealing with rotation, use the original width and height of the
     * bounds, as the rotation will have expanded them. */
    if (bounds.width && bounds.height && rotation) {
      var cx = (bounds.left + bounds.right) / 2,
          cy = (bounds.top + bounds.bottom) / 2;
      m_camera.viewFromCenterSizeRotation({ x: cx, y: cy }, bounds, rotation);
    } else {
      m_camera.bounds = bounds;
    }
    /* Update the center to what was set. */
    m_center = {
      x: (m_camera.bounds.left + m_camera.bounds.right) / 2,
      y: (m_camera.bounds.top + m_camera.bounds.bottom) / 2
    };
  }

  /**
   * Resize the map based on the size of the associated DOM node.
   * @private
   */
  function resizeSelf() {
    m_this.size({ width: m_node.width(), height: m_node.height() });
  }

  /*
   * All the methods are now defined.  From here, we are initializing all
   * internal variables and event handlers.
   */

  this._init(arg);

  // set up drag/drop handling
  this.node().on('dragover.geo', function (e) {
    var evt = e.originalEvent;

    if (m_this.fileReader()) {
      evt.stopPropagation();
      evt.preventDefault();
      evt.dataTransfer.dropEffect = 'copy';
    }
  }).on('drop.geo', function (e) {
    var evt = e.originalEvent,
        reader = m_this.fileReader(),
        i,
        file;

    function done() {
      m_this.draw();
    }

    if (reader) {
      evt.stopPropagation();
      evt.preventDefault();

      for (i = 0; i < evt.dataTransfer.files.length; i += 1) {
        file = evt.dataTransfer.files[i];
        if (reader.canRead(file)) {
          reader.read(file, done); // to do: trigger event on done
        }
      }
    }
  });

  /*
   * The map coordinates for the default world map, where c = half
   * circumference at equator in meters, o = origin:
   *   (-c, c) + o                   (c, c) + o
   *            (center.x, center.y) + o            <-- center of viewport
   *   (-c, -c) + o                  (c, -c) + o
   */
  // Set the world origin
  m_origin = { x: 0, y: 0 };

  // Fix the zoom level (minimum and initial)
  this.zoomRange(arg, true);
  m_zoom = fix_zoom(m_zoom);
  m_rotation = fix_rotation(m_rotation);
  // Now update to the correct center and zoom level
  this.center($.extend({}, arg.center || m_center), undefined);

  if (arg.interactor !== null) {
    this.interactor(arg.interactor || mapInteractor({ discreteZoom: m_discreteZoom }));
  }

  if (m_autoResize) {
    $(window).on('resize', resizeSelf);
  }

  // attach attribution updates to layer events
  m_this.geoOn([geo_event.layerAdd, geo_event.layerRemove], m_this.updateAttribution);

  return this;
};

/**
 * Create a map from an object.  Any errors in the creation
 * of the map will result in returning `null`.
 *
 * @param {geo.map.spec} spec The object specification.
 * @returns {geo.map|null}
 */
map.create = function (spec) {
  'use strict';

  var _map = map(spec),
      layer = __webpack_require__(24);

  /* If the spec is bad, we still end up with an object, but it won't have a
   * zoom function */
  if (!_map || !_map.zoom) {
    console.warn('Could not create map.');
    return null;
  }

  spec.data = spec.data || [];
  spec.layers = spec.layers || [];

  spec.layers.forEach(function (l) {
    l.data = l.data || spec.data;
    l.layer = layer.create(_map, l);
  });

  return _map;
};

inherit(map, sceneObject);
module.exports = map;

/***/ }),
/* 288 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var $ = __webpack_require__(2);
var inherit = __webpack_require__(0);
var tileLayer = __webpack_require__(127);
var registry = __webpack_require__(1);
var quadFeature = __webpack_require__(17);

/**
 * Create a new instance of osmLayer.  This is a {@link geo.tileLayer} with
 * an OSM url and attribution defaults and with the tiles centered on the
 * origin.
 *
 * @class
 * @alias geo.osmLayer
 * @extends geo.tileLayer
 *
 * @param {object} arg
 * @param {number} [arg.mapOpacity] If specified, and `arg.opacity` is not
 *    specified, use this as the layer opacity.
 */
var osmLayer = function osmLayer(arg) {

  var imageTile = __webpack_require__(125);

  if (!(this instanceof osmLayer)) {
    return new osmLayer(arg);
  }
  if (arg.mapOpacity !== undefined && arg.opacity === undefined) {
    arg.opacity = arg.mapOpacity;
  }
  tileLayer.call(this, arg);

  /* mapOpacity is just another name for the layer opacity. */
  this.mapOpacity = this.opacity;

  /**
   * Returns an instantiated imageTile object with the given indices.  This
   * method always returns a new tile object.  Use `_getTileCached` to use
   * the caching layer.
   *
   * @param {object} index The tile index.
   * @param {number} index.x
   * @param {number} index.y
   * @param {number} index.level
   * @param {object} source The tile index used for constructing the url.
   * @param {number} source.x
   * @param {number} source.y
   * @param {number} source.level
   * @returns {geo.tile}
   */
  this._getTile = function (index, source) {
    var urlParams = source || index;
    return imageTile({
      index: index,
      size: { x: this._options.tileWidth, y: this._options.tileHeight },
      queue: this._queue,
      overlap: this._options.tileOverlap,
      scale: this._options.tileScale,
      url: this._options.url.call(this, urlParams.x, urlParams.y, urlParams.level || 0, this._options.subdomains),
      crossDomain: this._options.crossDomain
    });
  }.bind(this);
};

/**
 * This object contains the default options used to initialize the osmLayer.
 */
osmLayer.defaults = $.extend({}, tileLayer.defaults, {
  tileOffset: function tileOffset(level) {
    var s = Math.pow(2, level - 1) * 256;
    return { x: s, y: s };
  },
  url: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
  attribution: 'Tile data &copy; <a href="http://osm.org/copyright">' + 'OpenStreetMap</a> contributors'
});

inherit(osmLayer, tileLayer);
/* By default, ask to support image quads.  If the user needs full
 * reprojection, they will need to require the
 * quadFeature.capabilities.imageFull feature */
registry.registerLayer('osm', osmLayer, [quadFeature.capabilities.image]);

module.exports = osmLayer;

/***/ }),
/* 289 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/*
 * Type definitions for jsdoc.
 */

/**
 * General object specification for map types.  Any additional values in the
 * object are passed to the map constructor.
 *
 * @typedef geo.map.spec
 * @type {object}
 * @property {object[]} [data=[]] The default data array to apply to each
 *      feature if none exists.
 * @property {geo.layer.spec[]} [layers=[]] Layers to create.
 */

/**
 * General representation of rectangular bounds in world coordinates.
 *
 * @typedef geo.geoBounds
 * @type {object}
 * @property {number} left Horizontal coordinate of the top-left corner.
 * @property {number} top Vertical coordinate of the top-left corner.
 * @property {number} right Horizontal coordinate of the bottom-right corner.
 * @property {number} bottom Vertical coordinate of the bottom-right corner.
 */

/**
 * A location and zoom value.
 *
 * @typedef geo.zoomAndCenter
 * @type {object}
 * @property {geo.geoPosition} center The center coordinates.
 * @property {number} zoom The zoom level.
 */

/**
 * General representation of rectangular bounds in pixel coordinates.
 *
 * @typedef geo.screenBounds
 * @type {object}
 * @property {geo.screenPosition} upperLeft Upper left corner.
 * @property {geo.screenPosition} upperRight Upper right corner.
 * @property {geo.screenPosition} lowerLeft Lower left corner.
 * @property {geo.screenPosition} lowerRight Lower right corner.
 */

/**
 * General representation of a point on the screen.
 *
 * @typedef geo.screenPosition
 * @type {object}
 * @property {number} x Horizontal coordinate in pixels.
 * @property {number} y Vertical coordinate in pixels.
 */

/**
 * General represention of a point on the earth.  The coordinates are most
 * commonly in longitude and latitude, but the coordinate system is changed
 * by the interface gcs.
 *
 * @typedef geo.geoPosition
 * @type {object}
 * @property {number} x Horizontal coordinate, often degrees longitude.
 * @property {number} y Vertical coordinate, often degrees latitude.
 * @property {number} [z=0] Altitude coordinate.
 */

/**
 * General represention of a two-dimensional point in any coordinate system.
 *
 * @typedef geo.point2D
 * @type {object}
 * @property {number} x Horizontal coordinate.
 * @property {number} y Vertical coordinate.
 */

/**
 * Represention of a point on the map.  The coordinates are in the map's
 * reference system, possibly with an affine transformation.
 *
 * @typedef geo.worldPosition
 * @type {object}
 * @property {number} x Horizontal coordinate in map coordinates.
 * @property {number} y Vertical coordinate in map coordinates.
 * @property {number} [z=0] Altitude coordinate, often zero.
 */

/**
 * Represention of a size in pixels.
 *
 * @typedef geo.screenSize
 * @type {object}
 * @property {number} width Width in pixels.
 * @property {number} height Height in pixels.
 */

/**
 * The status of all mouse buttons.
 *
 * @typedef geo.mouseButtons
 * @type {object}
 * @property {boolean} left True if the left mouse button is down.
 * @property {boolean} right True if the right mouse button is down.
 * @property {boolean} middle True if the middle mouse button is down.
 */

/**
 * The status of all modifier keys.  These are usually copied from the
 * standard DOM events.
 *
 * @typedef geo.modifierKeys
 * @type {object}
 * @property {boolean} alt True if the alt or option key is down.
 * @property {boolean} ctrl True if the control key is down.
 * @property {boolean} shift True if the shift key is down.
 * @property {boolean} meta True if the meta, windows, or command key
 *      is down.
 */

/**
 * The state of the mouse.
 *
 * @typedef geo.mouseState
 * @type {object}
 * @property {geo.screenPosition} page Mouse location in pixel space relative
 *      to the entire browser window.
 * @property {geo.screenPosition} map Mouse location in pixel space relative to
 *      the map DOM node.
 * @property {geo.geoPosition} geo Mouse location in interface gcs space.
 * @property {geo.geoPosition} mapgcs Mouse location in gcs space.
 * @property {geo.mouseButtons} buttons The current state of the mouse buttons.
 * @property {geo.modifierKeys} modifiers The current state of all modifier
 *      keys.
 * @property {Date} time The timestamp the event took place.
 * @property {number} deltaTime The time in milliseconds since the last mouse
 *      event.
 * @property {geo.screenPosition} velocity The velocity of the mouse pointer
 *      in pixels per millisecond.
 */

/**
 * The current brush selection (this is when a rectangular area is selected by
 * dragging).
 *
 * @typedef geo.brushSelection
 * @type {object}
 * @property {geo.screenBounds} display The selection bounds in pixel space.
 * @property {object} gcs The selection bounds in the map's gcs.
 * @property {geo.geoPosition} gcs.upperLeft Upper left corner.
 * @property {geo.geoPosition} gcs.upperRight Upper right corner.
 * @property {geo.geoPosition} gcs.lowerLeft Lower left corner.
 * @property {geo.geoPosition} gcs.lowerRight Lower right corner.
 * @property {geo.mouseState} mouse The current mouse state.
 * @property {geo.mouseState} origin The mouse state at the start of the
 *      brush action.
 */

/**
 * The conditions that are necessary to make an action occur.
 *
 * @typedef geo.actionRecord
 * @type {object}
 * @property {string} action The name of the action, from (@link geo.action}.
 * @property {string} [owner] A name of an owning process that can be used to
 *      locate or filter actions.
 * @property {string} [name] A human-readable name that can be used to locate
 *      or filter actions.
 * @property {string|object} input The name of an input that is used for the
 *      action, or an object with input names as keys and boolean values of
 *      inputs that are required to occur or required to not occur to trigger
 *      the action.  Input names include `left`, `right`, `middle` (for mouse
 *      buttons), `wheel` (the mouse wheel), `pan` (touch pan), `rotate` (touch
 *      rotate).
 * @property {string|object} [modifiers] The name of a modifier key or an
 *      object with modifiers as the keys and boolean values.  The listed
 *      modifiers must be set or unset depending on the boolean value.
 *      Modifiers include `shift`, `ctrl`, `alt`, and `meta`.
 * @property {boolean|string} [selectionRectangle] If truthy, a selection
 *      rectangle is shown during the action.  If a string, the name of an
 *      event that is triggered when the selection is complete.
 */

/**
 * The current action state a map interactor.
 *
 * @typedef geo.actionState
 * @type {object}
 * @property {string} action Name of the action that is being handled.
 * @property {geo.actionRecord} actionRecord The action record which triggered
 *      the current action.
 * @property {string} [origAction] The name of an action that triggered this
 *      action.
 * @property {geo.mouseState} origin The mouse state at the start of the
 *      action.
 * @property {number} initialZoom The zoom level at the start of the action.
 * @property {number} initialRotation The map's rotation in radians at the
 *      start of the action.
 * @property {number} initialEventRotation The rotation reported by the
 *      event that triggered this action.  For example, this could be the
 *      angle between two multi-touch points.
 * @property {object} delta The total movement of during the action in gcs
 *      coordinates.
 * @property {number} delta.x The horizontal movement during the action.
 * @property {number} delta.y The vertical movement during the action.
 * @property {boolean} boundDocumentHandlers `true` if the mouse is down and
 *      being tracked.
 * @property {Date} [start] The time when the action started.
 * @property {function} [handler] A function to call on every animation from
 *      while the action is occurring.
 * @property {geo.mouseState} [momentum] The mouse location when a momentum
 *      action starts.
 * @property {boolean} [zoomrotateAllowRotation] Truthy if enough movement has
 *      occurred that rotations are allowed.
 * @property {boolean} [zoomrotateAllowZoom] Truthy if enough movement has
 *      occurred that zooms are allowed.
 * @property {boolean} [zoomrotateAllowPan] Truthy if enough movement has
 *      occurred that pans are allowed.
 * @property {number} [lastRotationDelta] When rotating, the last amount that
 *      was rotated from the start of the action.  This is used to debounce
 *      jitter on touch events.
 * @property {geo.geoPosition} [initialEventGeo] The position of the mouse
 *      when significant movement first occurred.
 */

/**
 * A color value.  Although opacity can be specified, it is not always used.
 * When a string is specified, any of the following forms can be used:
 *   - CSS color name
 *   - `#rrggbb` The color specified in hexadecmial with each channel on a
 *     scale between 0 and 255 (`ff`).  Case insensitive.
 *   - `#rrggbbaa` The color and opacity specified in hexadecmial with each
 *     channel on a scale between 0 and 255 (`ff`).  Case insensitive.
 *   - `#rgb` The color specified in hexadecmial with each channel on a scale
 *     between 0 and 15 (`f`).  Case insensitive.
 *   - `#rgba` The color and opacity specified in hexadecmial with each channel
 *      on a scale between 0 and 15 (`f`).  Case insensitive.
 *   - `rgb(R, G, B)`, `rgb(R, G, B, A)`, `rgba(R, G, B)`, `rgba(R, G, B, A)`
 *     The color with the values of each color channel specified as numeric
 *     values between 0 and 255 or as percent (between 0 and 100) if a percent
 *     `%` follows the number.  The alpha (opacity) channel is optional and can
 *     either be a number between 0 and 1 or a percent.  White space may appear
 *     before and after numbers, and between the number and a percent symbol.
 *     Commas are not required.  A slash may be used as a separator before the
 *     alpha value instead of a comma.  The numbers conform to the CSS number
 *     specification, and can be signed floating-point values, possibly with
 *     exponents.
 *   - `hsl(H, S, L)`, `hsl(H, S, L, A)`, `hsla(H, S, L)`, `hsla(H, S, L, A)`
 *     Hue, saturation, and lightness with optional alpha (opacity).  Hue is a
 *     number between 0 and 360 and is interpretted as degrees unless an angle
 *     unit is specified.  CSS units of `deg`, `grad`, `rad`, and `turn` are
 *     supported.  Saturation and lightness are percentages between 0 and 100
 *     and *must* be followed by a percent `%` symbol.  The alpha (opacity)
 *     channel is optional and is specified as with `rgba(R, G, B, A)`.
 *   - `transparent` Black with 0 opacity.
 *
 * See {@link https://developer.mozilla.org/en-US/docs/Web/CSS/color_value} for
 * more details on CSS color values.
 *
 * @typedef geo.geoColor
 * @type {geo.geoColorObject|string}
 */

/**
 * A color value represented as an object.  Although opacity can be specified,
 * it is not always used.
 *
 * @typedef {object} geo.geoColorObject
 * @property {number} r The red intensity on a scale of [0-1].
 * @property {number} g The green intensity on a scale of [0-1].
 * @property {number} b The blue intensity on a scale of [0-1].
 * @property {number} [a] The opacity on a scale of [0-1].  If unspecified and
 *      used, it should be treated as 1.
 */

/**
 * Polygon as a flat array.  An array of vertices.  The polygon has no holes.
 * The first and last point of may be the same.
 *
 * @typedef {geo.geoPosition[]} geo.polygonFlat
 */

/**
 * Polygon as a object.  The polygon may have holes.
 *
 * @typedef {object} geo.polygonObject
 * @property {geo.geoPosition[]} outer An array of vertices defining the
 *   outside of the polygon.  The first and last point of may be the same.
 * @property {Array.<geo.geoPosition[]>} [inner] An array of holes, each of
 *   which is an array of vertices.  The first and last point of may be the
 *  same.
 */

/**
 * Polygon.
 *
 * @typedef {geo.polygonFlat|geo.polygonObject} geo.polygon
 */


/***/ }),
/* 290 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* global GEO_VERSION */

/**
 * The current geojs version.
 *
 * @alias geo.version
 * @constant
 * @type {string}
 */
module.exports = "0.18.1";

/***/ }),
/* 291 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/* global GEO_SHA */

/**
 * The current geojs git SHA.
 *
 * @alias geo.sha
 * @constant
 * @type {string}
 */
module.exports = "ff7e9348fbb039e6a0068e032aae2b37d284e23b";

/***/ }),
/* 292 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var geo_event = __webpack_require__(6);
geo_event.d3 = {
  rescale: __webpack_require__(119)
};

/**
 * @namespace geo.d3
 */
module.exports = {
  graphFeature: __webpack_require__(293),
  lineFeature: __webpack_require__(294),
  object: __webpack_require__(14),
  pathFeature: __webpack_require__(295),
  pointFeature: __webpack_require__(296),
  quadFeature: __webpack_require__(297),
  renderer: __webpack_require__(11),
  tileLayer: __webpack_require__(298),
  uniqueID: __webpack_require__(52),
  vectorFeature: __webpack_require__(299)
};

/***/ }),
/* 293 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var inherit = __webpack_require__(0);
var registerFeature = __webpack_require__(1).registerFeature;
var graphFeature = __webpack_require__(123);

/**
 * @class
 * @alias geo.d3.graphFeature
 * @extends geo.graphFeature
 */
var d3_graphFeature = function d3_graphFeature(arg) {
  'use strict';

  var m_this = this;

  if (!(this instanceof d3_graphFeature)) {
    return new d3_graphFeature(arg);
  }
  graphFeature.call(this, arg);

  /**
  *  Returns a d3 selection for the graph elements
  */
  this.select = function () {
    var renderer = m_this.renderer(),
        selection = {},
        node = m_this.nodeFeature(),
        links = m_this.linkFeatures();
    selection.nodes = renderer.select(node._d3id());
    selection.links = links.map(function (link) {
      return renderer.select(link._d3id());
    });
    return selection;
  };

  return this;
};

inherit(d3_graphFeature, graphFeature);

registerFeature('d3', 'graph', d3_graphFeature);

module.exports = d3_graphFeature;

/***/ }),
/* 294 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var inherit = __webpack_require__(0);
var registerFeature = __webpack_require__(1).registerFeature;
var lineFeature = __webpack_require__(23);

/**
 * Create a new instance of class lineFeature.
 *
 * @class
 * @alias geo.d3.lineFeature
 * @extends geo.lineFeature
 * @extends geo.d3.object
 * @param {geo.lineFeature.spec} arg
 * @returns {geo.d3.lineFeature}
 */
var d3_lineFeature = function d3_lineFeature(arg) {
  'use strict';

  if (!(this instanceof d3_lineFeature)) {
    return new d3_lineFeature(arg);
  }

  var d3 = __webpack_require__(11).d3;
  var object = __webpack_require__(14);
  var timestamp = __webpack_require__(10);
  var util = __webpack_require__(3);

  arg = arg || {};
  lineFeature.call(this, arg);
  object.call(this);

  /**
   * @private
   */
  var m_this = this,
      s_init = this._init,
      m_buildTime = timestamp(),
      m_maxIdx = 0,
      s_update = this._update;

  /**
   * Initialize.
   *
   * @param {geo.lineFeature.spec} arg The feature specification.
   * @returns {this}
   */
  this._init = function (arg) {
    s_init.call(m_this, arg);
    return m_this;
  };

  /**
   * Build.  Create the necessary elements to render lines.
   *
   * @returns {this}
   */
  this._build = function () {
    var data = m_this.data() || [],
        s_style = m_this.style(),
        m_renderer = m_this.renderer(),
        pos_func = m_this.position(),
        line,
        i;

    s_update.call(m_this);
    s_style.fill = function () {
      return false;
    };

    data.forEach(function (item, idx) {
      var m_style;
      var ln = m_this.line()(item, idx);

      var style = {},
          key;
      function wrapStyle(func) {
        if (util.isFunction(func)) {
          return function () {
            return func(ln[0], 0, item, idx);
          };
        } else {
          return func;
        }
      }
      for (key in s_style) {
        if (s_style.hasOwnProperty(key)) {
          style[key] = wrapStyle(s_style[key]);
        }
      }

      line = d3.svg.line().x(function (d) {
        return m_this.featureGcsToDisplay(d).x;
      }).y(function (d) {
        return m_this.featureGcsToDisplay(d).y;
      }).interpolate(m_this.style.get('closed')(item, idx) && ln.length > 2 ? 'linear-closed' : 'linear');
      // item is an object representing a single line
      // m_this.line()(item) is an array of coordinates
      m_style = {
        data: [ln.map(function (d, i) {
          return pos_func(d, i, item, idx);
        })],
        append: 'path',
        attributes: {
          d: line
        },
        id: m_this._d3id() + idx,
        classes: ['d3LineFeature', 'd3SubLine-' + idx],
        visible: m_this.visible,
        style: style
      };

      m_renderer._drawFeatures(m_style);
    });
    for (i = data.length; i < m_maxIdx; i += 1) {
      m_renderer._removeFeature(m_this._d3id() + i);
    }
    m_maxIdx = data.length;

    m_buildTime.modified();
    m_this.updateTime().modified();
    return m_this;
  };

  /**
   * Update.  Rebuild if necessary.
   *
   * @returns {this}
   */
  this._update = function () {
    s_update.call(m_this);

    if (m_this.getMTime() >= m_buildTime.getMTime()) {
      m_this._build();
    }

    return m_this;
  };

  this._init(arg);
  return this;
};

inherit(d3_lineFeature, lineFeature);

// Now register it
var capabilities = {};
capabilities[lineFeature.capabilities.basic] = true;
capabilities[lineFeature.capabilities.multicolor] = false;

registerFeature('d3', 'line', d3_lineFeature, capabilities);

module.exports = d3_lineFeature;

/***/ }),
/* 295 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var inherit = __webpack_require__(0);
var registerFeature = __webpack_require__(1).registerFeature;
var pathFeature = __webpack_require__(129);

/**
 * Create a new instance of class pathFeature
 *
 * @class
 * @alias geo.d3.pathFeature
 * @extends geo.pathFeature
 * @extends geo.d3.object
 * @returns {geo.d3.pathFeature}
 */
var d3_pathFeature = function d3_pathFeature(arg) {
  'use strict';

  if (!(this instanceof d3_pathFeature)) {
    return new d3_pathFeature(arg);
  }

  var $ = __webpack_require__(2);
  var d3 = __webpack_require__(11).d3;
  var object = __webpack_require__(14);
  var timestamp = __webpack_require__(10);

  arg = arg || {};
  pathFeature.call(this, arg);
  object.call(this);

  /**
   * @private
   */
  var m_this = this,
      s_init = this._init,
      m_buildTime = timestamp(),
      s_update = this._update,
      m_style = {};

  m_style.style = {};

  /**
   * Initialize
   */
  this._init = function (arg) {
    s_init.call(m_this, arg);
    return m_this;
  };

  /**
   * Build
   *
   * @override
   */
  this._build = function () {
    var data = m_this.data() || [],
        s_style = m_this.style(),
        tmp,
        diag;
    s_update.call(m_this);

    diag = function diag(d) {
      var p = {
        source: d.source,
        target: d.target
      };
      return d3.svg.diagonal()(p);
    };
    tmp = [];
    data.forEach(function (d, i) {
      var src, trg;
      if (i < data.length - 1) {
        src = d;
        trg = data[i + 1];
        tmp.push({
          source: m_this.featureGcsToDisplay(src),
          target: m_this.featureGcsToDisplay(trg)
        });
      }
    });
    m_style.data = tmp;
    m_style.attributes = {
      d: diag
    };

    m_style.id = m_this._d3id();
    m_style.append = 'path';
    m_style.classes = ['d3PathFeature'];
    m_style.style = $.extend({
      'fill': function fill() {
        return false;
      },
      'fillColor': function fillColor() {
        return { r: 0, g: 0, b: 0 };
      }
    }, s_style);
    m_style.visible = m_this.visible;

    m_this.renderer()._drawFeatures(m_style);

    m_buildTime.modified();
    m_this.updateTime().modified();
    return m_this;
  };

  /**
   * Update
   *
   * @override
   */
  this._update = function () {
    s_update.call(m_this);

    if (m_this.dataTime().getMTime() >= m_buildTime.getMTime()) {
      m_this._build();
    }

    return m_this;
  };

  this._init(arg);
  return this;
};

inherit(d3_pathFeature, pathFeature);

registerFeature('d3', 'path', d3_pathFeature);

module.exports = d3_pathFeature;

/***/ }),
/* 296 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var inherit = __webpack_require__(0);
var registerFeature = __webpack_require__(1).registerFeature;
var pointFeature = __webpack_require__(34);

/**
 *
 * Create a new instance of d3.pointFeature.
 *
 * @class
 * @alias geo.d3.pointFeature
 * @extends geo.pointFeature
 * @extends geo.d3.object
 * @param {geo.pointFeature.spec} arg
 * @returns {geo.d3.pointFeature}
 */
var d3_pointFeature = function d3_pointFeature(arg) {
  'use strict';

  if (!(this instanceof d3_pointFeature)) {
    return new d3_pointFeature(arg);
  }

  var d3_object = __webpack_require__(14);
  var timestamp = __webpack_require__(10);

  arg = arg || {};
  pointFeature.call(this, arg);
  d3_object.call(this);

  /**
   * @private
   */
  var m_this = this,
      s_init = this._init,
      s_update = this._update,
      m_buildTime = timestamp(),
      m_style = {};

  /**
   * Initialize.
   *
   * @param {geo.pointFeature.spec} arg The feature specification.
   * @returns {this}
   */
  this._init = function (arg) {
    s_init.call(m_this, arg);
    return m_this;
  };

  /**
   * Build.  Create the necessary elements to render points.
   *
   * @returns {this}
   */
  this._build = function () {
    var data = m_this.data(),
        s_style = m_this.style.get(),
        m_renderer = m_this.renderer(),
        pos_func = m_this.position();

    // call super-method
    s_update.call(m_this);

    // default to empty data array
    if (!data) {
      data = [];
    }

    // fill in d3 renderer style object defaults
    m_style.id = m_this._d3id();
    m_style.data = data;
    m_style.append = 'circle';
    m_style.attributes = {
      r: m_renderer._convertScale(s_style.radius),
      cx: function cx(d) {
        return m_this.featureGcsToDisplay(pos_func(d)).x;
      },
      cy: function cy(d) {
        return m_this.featureGcsToDisplay(pos_func(d)).y;
      }
    };
    m_style.style = s_style;
    m_style.classes = ['d3PointFeature'];
    m_style.visible = m_this.visible;

    // pass to renderer to draw
    m_this.renderer()._drawFeatures(m_style);

    // update time stamps
    m_buildTime.modified();
    m_this.updateTime().modified();
    return m_this;
  };
  /**
   * Update.  Rebuild if necessary.
   *
   * @returns {this}
   */
  this._update = function () {
    s_update.call(m_this);

    if (m_this.getMTime() >= m_buildTime.getMTime()) {
      m_this._build();
    }

    return m_this;
  };

  this._init(arg);
  return this;
};

inherit(d3_pointFeature, pointFeature);

// Now register it
registerFeature('d3', 'point', d3_pointFeature);

module.exports = d3_pointFeature;

/***/ }),
/* 297 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var inherit = __webpack_require__(0);
var registerFeature = __webpack_require__(1).registerFeature;
var quadFeature = __webpack_require__(17);

/**
 * Create a new instance of class quadFeature.
 *
 * @class
 * @alias geo.d3.quadFeature
 * @param {geo.quadFeature.spec} arg Options object.
 * @extends geo.quadFeature
 * @returns {geo.d3.quadFeature}
 */
var d3_quadFeature = function d3_quadFeature(arg) {
  'use strict';

  if (!(this instanceof d3_quadFeature)) {
    return new d3_quadFeature(arg);
  }

  var $ = __webpack_require__(2);
  var d3 = __webpack_require__(11).d3;
  var object = __webpack_require__(14);

  quadFeature.call(this, arg);
  object.call(this);

  var m_this = this,
      s_exit = this._exit,
      s_init = this._init,
      s_update = this._update,
      m_quads;

  /**
   * Build this feature.
   */
  this._build = function () {
    if (!this.position()) {
      return;
    }
    var renderer = this.renderer(),
        map = renderer.layer().map();

    m_quads = this._generateQuads();

    var data = [];
    $.each(m_quads.clrQuads, function (idx, quad) {
      data.push({ type: 'clr', quad: quad, zIndex: quad.pos[2] });
    });
    $.each(m_quads.imgQuads, function (idx, quad) {
      if (quad.image) {
        data.push({ type: 'img', quad: quad, zIndex: quad.pos[2] });
      }
    });

    var feature = {
      id: this._d3id(),
      data: data,
      dataIndex: function dataIndex(d) {
        return d.quad.quadId;
      },
      append: function append(d) {
        var ns = this.namespaceURI,
            element = d.type === 'clr' ? 'polygon' : 'image';
        return ns ? document.createElementNS(ns, element) : document.createElement(element);
      },
      attributes: {
        fill: function fill(d) {
          if (d.type === 'clr') {
            return d3.rgb(255 * d.quad.color.r, 255 * d.quad.color.g, 255 * d.quad.color.b);
          }
          /* set some styles here */
          if (d.quad.opacity !== 1) {
            d3.select(this).style('opacity', d.quad.opacity);
          }
        },
        height: function height(d) {
          return d.type === 'clr' ? undefined : 1;
        },
        points: function points(d) {
          if (d.type === 'clr' && !d.points) {
            var points = [],
                i;
            for (i = 0; i < d.quad.pos.length; i += 3) {
              var p = {
                x: d.quad.pos[i],
                y: d.quad.pos[i + 1],
                z: d.quad.pos[i + 2]
              };
              /* We don't use 'p = m_this.featureGcsToDisplay(p);' because the
               * quads have already been converted to the map's gcs (no longer
               * the feature's gcs or map's ingcs). */
              p = map.gcsToDisplay(p, null);
              p = renderer.baseToLocal(p);
              points.push('' + p.x + ',' + p.y);
            }
            d.points = points[0] + ' ' + points[1] + ' ' + points[3] + ' ' + points[2];
          }
          return d.type === 'clr' ? d.points : undefined;
        },
        preserveAspectRatio: function preserveAspectRatio(d) {
          return d.type === 'clr' ? undefined : 'none';
        },
        reference: function reference(d) {
          return d.quad.reference;
        },
        stroke: false,
        transform: function transform(d) {
          if (d.type === 'img' && d.quad.image && !d.svgTransform) {
            var pos = [],
                area,
                maxarea = -1,
                maxv,
                i,
                imgscale,
                imgw = d.quad.image.width,
                imgh = d.quad.image.height;
            for (i = 0; i < d.quad.pos.length; i += 3) {
              var p = {
                x: d.quad.pos[i],
                y: d.quad.pos[i + 1],
                z: d.quad.pos[i + 2]
              };
              /* We don't use 'p = m_this.featureGcsToDisplay(p);' because the
               * quads have already been converted to the map's gcs (no longer
               * the feature's gcs or map's ingcs). */
              p = map.gcsToDisplay(p, null);
              p = renderer.baseToLocal(p);
              pos.push(p);
            }
            /* We can only fit three corners of the quad to the image, but we
             * get to pick which three.  We choose to always include the
             * largest of the triangles formed by a set of three vertices.  The
             * image is always rendered as a parallelogram, so it may be larger
             * than desired, and, for convex quads, miss some of the intended
             * area. */
            for (i = 0; i < 4; i += 1) {
              area = Math.abs(pos[(i + 1) % 4].x * (pos[(i + 2) % 4].y - pos[(i + 3) % 4].y) + pos[(i + 2) % 4].x * (pos[(i + 3) % 4].y - pos[(i + 1) % 4].y) + pos[(i + 3) % 4].x * (pos[(i + 1) % 4].y - pos[(i + 2) % 4].y)) / 2;
              if (area > maxarea) {
                maxarea = area;
                maxv = i;
              }
            }
            d.svgTransform = [maxv === 3 || maxv === 2 ? pos[1].x - pos[0].x : pos[3].x - pos[2].x, maxv === 3 || maxv === 2 ? pos[1].y - pos[0].y : pos[3].y - pos[2].y, maxv === 0 || maxv === 2 ? pos[1].x - pos[3].x : pos[0].x - pos[2].x, maxv === 0 || maxv === 2 ? pos[1].y - pos[3].y : pos[0].y - pos[2].y, maxv === 2 ? pos[3].x + pos[0].x - pos[1].x : pos[2].x, maxv === 2 ? pos[3].y + pos[0].y - pos[1].y : pos[2].y];
            if (Math.abs(d.svgTransform[1] / imgw) < 1e-6 && Math.abs(d.svgTransform[2] / imgh) < 1e-6) {
              imgscale = d.svgTransform[0] / imgw;
              d.svgTransform[4] = Math.round(d.svgTransform[4] / imgscale) * imgscale;
              imgscale = d.svgTransform[3] / imgh;
              d.svgTransform[5] = Math.round(d.svgTransform[5] / imgscale) * imgscale;
            }
          }
          return d.type !== 'img' || !d.quad.image ? undefined : 'matrix(' + d.svgTransform.join(' ') + ')';
        },
        width: function width(d) {
          return d.type === 'clr' ? undefined : 1;
        },
        x: function x(d) {
          return d.type === 'clr' ? undefined : 0;
        },
        'xlink:href': function xlinkHref(d) {
          return d.type === 'clr' || !d.quad.image ? undefined : d.quad.image.src;
        },
        y: function y(d) {
          return d.type === 'clr' ? undefined : 0;
        }
      },
      style: {
        fillOpacity: function fillOpacity(d) {
          return d.type === 'clr' ? d.quad.opacity : undefined;
        }
      },
      onlyRenderNew: !this.style('previewColor') && !this.style('previewImage'),
      sortByZ: true,
      visible: m_this.visible,
      classes: ['d3QuadFeature']
    };
    renderer._drawFeatures(feature);

    this.buildTime().modified();
  };

  /**
   * Update the feature.
   *
   * @returns {this}
   */
  this._update = function () {
    s_update.call(m_this);
    if (m_this.buildTime().getMTime() <= m_this.dataTime().getMTime() || m_this.buildTime().getMTime() < m_this.getMTime()) {
      m_this._build();
    }
    return m_this;
  };

  /**
   * Initialize.
   */
  this._init = function () {
    s_init.call(m_this, arg);
  };

  /**
   * Destroy.
   */
  this._exit = function () {
    s_exit.call(m_this);
  };

  m_this._init(arg);
  return this;
};

inherit(d3_quadFeature, quadFeature);

// Now register it
var capabilities = {};
capabilities[quadFeature.capabilities.color] = true;
capabilities[quadFeature.capabilities.image] = true;
capabilities[quadFeature.capabilities.imageCrop] = false;
capabilities[quadFeature.capabilities.imageFixedScale] = false;
capabilities[quadFeature.capabilities.imageFull] = false;
capabilities[quadFeature.capabilities.canvas] = false;
capabilities[quadFeature.capabilities.video] = false;

registerFeature('d3', 'quad', d3_quadFeature, capabilities);
module.exports = d3_quadFeature;

/***/ }),
/* 298 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var registerLayerAdjustment = __webpack_require__(1).registerLayerAdjustment;

var d3_tileLayer = function d3_tileLayer() {
  'use strict';

  var m_this = this,
      s_init = this._init,
      s_exit = this._exit,
      m_quadFeature,
      m_nextTileId = 0,
      m_tiles = [];

  this._drawTile = function (tile) {
    if (!m_quadFeature) {
      return;
    }
    var bounds = m_this._tileBounds(tile),
        level = tile.index.level || 0,
        to = this._tileOffset(level),
        quad = {};
    quad.ul = this.fromLocal(this.fromLevel({
      x: bounds.left - to.x, y: bounds.top - to.y
    }, level), 0);
    quad.ll = this.fromLocal(this.fromLevel({
      x: bounds.left - to.x, y: bounds.bottom - to.y
    }, level), 0);
    quad.ur = this.fromLocal(this.fromLevel({
      x: bounds.right - to.x, y: bounds.top - to.y
    }, level), 0);
    quad.lr = this.fromLocal(this.fromLevel({
      x: bounds.right - to.x, y: bounds.bottom - to.y
    }, level), 0);
    quad.ul.z = quad.ll.z = quad.ur.z = quad.lr.z = level * m_this._levelZIncrement;
    m_nextTileId += 1;
    quad.id = m_nextTileId;
    tile.quadId = quad.id;
    quad.image = tile.image;
    quad.reference = tile.toString();
    m_tiles.push(quad);
    m_quadFeature.data(m_tiles);
    m_quadFeature._update();
    m_this.draw();
  };

  /* Remove the tile feature. */
  this._remove = function (tile) {
    if (tile.quadId !== undefined && m_quadFeature) {
      for (var i = 0; i < m_tiles.length; i += 1) {
        if (m_tiles[i].id === tile.quadId) {
          m_tiles.splice(i, 1);
          break;
        }
      }
      m_quadFeature.data(m_tiles);
      m_quadFeature._update();
      m_this.draw();
    }
  };

  /**
   * Clean up the layer.
   */
  this._exit = function () {
    m_this.deleteFeature(m_quadFeature);
    m_quadFeature = null;
    m_tiles = [];
    s_exit.apply(m_this, arguments);
  };

  /* Initialize the tile layer.  This creates a series of sublayers so that
   * the different layers will stack in the proper order.
   */
  this._init = function () {
    s_init.apply(m_this, arguments);
    m_quadFeature = this.createFeature('quad', {
      previewColor: m_this._options.previewColor,
      previewImage: m_this._options.previewImage
    });
    m_quadFeature.geoTrigger = undefined;
    m_quadFeature.gcs(m_this._options.gcs || m_this.map().gcs());
    m_quadFeature.data(m_tiles);
    m_quadFeature._update();
  };

  this._getSubLayer = function () {};
  this._updateSubLayers = undefined;
};

registerLayerAdjustment('d3', 'tile', d3_tileLayer);
module.exports = d3_tileLayer;

/***/ }),
/* 299 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var inherit = __webpack_require__(0);
var registerFeature = __webpack_require__(1).registerFeature;
var vectorFeature = __webpack_require__(131);

/**
 * Create a new instance of vectorFeature
 *
 * @class
 * @alias geo.d3.vectorFeature
 * @extends geo.vectorFeature
 * @extends geo.d3.object
 * @returns {geo.d3.vectorFeature}
 */
var d3_vectorFeature = function d3_vectorFeature(arg) {
  'use strict';

  if (!(this instanceof d3_vectorFeature)) {
    return new d3_vectorFeature(arg);
  }

  var object = __webpack_require__(14);
  var timestamp = __webpack_require__(10);
  var d3 = __webpack_require__(11).d3;

  arg = arg || {};
  vectorFeature.call(this, arg);
  object.call(this);

  /**
   * @private
   */
  var m_this = this,
      s_init = this._init,
      s_exit = this._exit,
      s_update = this._update,
      m_buildTime = timestamp(),
      m_style = {};

  /**
   * Generate a unique ID for a marker definition
   * @private
   * @param {object} d Unused datum (for d3 compat)
   * @param {number} i The marker index
   * @param {string} position The marker's vector position (head or tail)
   */
  function markerID(d, i, position) {
    return m_this._d3id() + '_marker_' + i + '_' + position;
  }

  /**
   * Add marker styles for vector arrows.
   * @private
   * @param {object[]} data The vector data array
   * @param {function} stroke The stroke accessor
   * @param {function} opacity The opacity accessor
   * @param {function} originStyle The marker style for the vector head
   * @param {function} endStyle The marker style for the vector tail
   */
  function updateMarkers(data, stroke, opacity, originStyle, endStyle) {

    var markerConfigs = {
      'arrow': {
        attrs: { 'class': 'geo-vector-arrow geo-vector-marker', 'viewBox': '0 0 10 10', 'refX': '1', 'refY': '5', 'markerHeight': '5', 'markerWidth': '5', 'orient': 'auto' },
        path: 'M 0 0 L 10 5 L 0 10 z'
      },
      'point': {
        attrs: { 'class': 'geo-vector-point geo-vector-marker', 'viewBox': '0 0 12 12', 'refX': '6', 'refY': '6', 'markerHeight': '8', 'markerWidth': '8', 'orient': 'auto' },
        path: 'M 6 3 A 3 3 0 1 1 5.99999 3 Z'
      },
      'bar': {
        attrs: { 'class': 'geo-vector-bar geo-vector-marker', 'viewBox': '0 0 10 10', 'refX': '0', 'refY': '5', 'markerHeight': '6', 'markerWidth': '6', 'orient': 'auto' },
        path: 'M 0 0 L 2 0 L 2 10 L 0 10 z'
      },
      'wedge': {
        attrs: { 'class': 'geo-vector-wedge geo-vector-marker', 'viewBox': '0 0 10 10', 'refX': '10', 'refY': '5', 'markerHeight': '5', 'markerWidth': '5', 'orient': 'auto' },
        path: 'M 0 0 L 1 0 L 10 5 L 1 10 L 0 10 L 9 5 L 0 0'
      }
    };

    //this allows for multiple VectorFeatures in a layer
    var markerGroup = m_this.renderer()._definitions().selectAll('g.marker-group#' + m_this._d3id()).data(data.length ? [1] : []);

    markerGroup.enter().append('g').attr('id', m_this._d3id).attr('class', 'marker-group');

    markerGroup.exit().remove();

    var markers = data.reduce(function (markers, d, i) {
      var head = markerConfigs[endStyle(d, i)];
      var tail = markerConfigs[originStyle(d, i)];
      if (head) {
        markers.push({
          data: d,
          dataIndex: i,
          head: true
        });
      }
      if (tail) {
        markers.push({
          data: d,
          dataIndex: i,
          head: false
        });
      }
      return markers;
    }, []);

    var sel = markerGroup.selectAll('marker.geo-vector-marker').data(markers);

    sel.enter().append('marker').append('path');

    var renderer = m_this.renderer();

    sel.each(function (d) {
      var marker = d3.select(this);
      var markerData = d.head ? markerConfigs[endStyle(d.data, d.dataIndex)] : markerConfigs[originStyle(d.data, d.dataIndex)];
      Object.keys(markerData.attrs).map(function (attrName) {
        marker.attr(attrName, markerData.attrs[attrName]);
      });
    }).attr('id', function (d) {
      return markerID(d.data, d.dataIndex, d.head ? 'head' : 'tail');
    }).style('stroke', function (d) {
      return renderer._convertColor(stroke)(d.data, d.dataIndex);
    }).style('fill', function (d) {
      return renderer._convertColor(stroke)(d.data, d.dataIndex);
    }).style('opacity', function (d) {
      return opacity(d.data, d.dataIndex);
    }).select('path').attr('d', function (d) {
      return d.head ? markerConfigs[endStyle(d.data, d.dataIndex)].path : markerConfigs[originStyle(d.data, d.dataIndex)].path;
    });

    sel.exit().remove();
  }

  /**
   * Initialize
   * @protected
   */
  this._init = function (arg) {
    s_init.call(m_this, arg);
    return m_this;
  };

  /**
   * Build
   * @protected
   */
  this._build = function () {
    var data = m_this.data(),
        s_style = m_this.style.get(),
        m_renderer = m_this.renderer(),
        orig_func = m_this.origin(),
        size_func = m_this.delta(),
        cache = [],
        scale = m_this.style('scale'),
        max = Number.NEGATIVE_INFINITY;

    // call super-method
    s_update.call(m_this);

    // default to empty data array
    if (!data) {
      data = [];
    }

    // cache the georeferencing
    cache = data.map(function (d, i) {
      var origin = m_this.featureGcsToDisplay(orig_func(d, i)),
          delta = size_func(d, i);
      max = Math.max(max, delta.x * delta.x + delta.y * delta.y);
      return {
        x1: origin.x,
        y1: origin.y,
        dx: delta.x,
        dy: -delta.y
      };
    });

    max = Math.sqrt(max);
    if (!scale) {
      scale = 75 / max;
    }

    function getScale() {
      return scale / m_renderer.scaleFactor();
    }

    // fill in d3 renderer style object defaults
    m_style.id = m_this._d3id();
    m_style.data = data;
    m_style.append = 'line';
    m_style.attributes = {
      x1: function x1(d, i) {
        return cache[i].x1;
      },
      y1: function y1(d, i) {
        return cache[i].y1;
      },
      x2: function x2(d, i) {
        return cache[i].x1 + getScale() * cache[i].dx;
      },
      y2: function y2(d, i) {
        return cache[i].y1 + getScale() * cache[i].dy;
      },
      'marker-start': function markerStart(d, i) {
        return 'url(#' + markerID(d, i, 'tail') + ')';
      },
      'marker-end': function markerEnd(d, i) {
        return 'url(#' + markerID(d, i, 'head') + ')';
      }
    };
    m_style.style = {
      stroke: function stroke() {
        return true;
      },
      strokeColor: s_style.strokeColor,
      strokeWidth: s_style.strokeWidth,
      strokeOpacity: s_style.strokeOpacity,
      originStyle: s_style.originStyle,
      endStyle: s_style.endStyle
    };
    m_style.classes = ['d3VectorFeature'];
    m_style.visible = m_this.visible;

    // Add markers to the defition list
    updateMarkers(data, s_style.strokeColor, s_style.strokeOpacity, s_style.originStyle, s_style.endStyle);

    // pass to renderer to draw
    m_this.renderer()._drawFeatures(m_style);

    // update time stamps
    m_buildTime.modified();
    m_this.updateTime().modified();
    return m_this;
  };

  /**
   * Update
   * @protected
   */
  this._update = function () {
    s_update.call(m_this);

    if (m_this.getMTime() >= m_buildTime.getMTime()) {
      m_this._build();
    } else {
      updateMarkers(m_style.data, m_style.style.strokeColor, m_style.style.strokeOpacity, m_style.style.originStyle, m_style.style.endStyle);
    }

    return m_this;
  };

  /**
   * Exit
   * @protected
   */
  this._exit = function () {
    s_exit.call(m_this);
    m_style = {};
    updateMarkers([], null, null, null, null);
  };

  this._init(arg);
  return this;
};

inherit(d3_vectorFeature, vectorFeature);

// Now register it
registerFeature('d3', 'vector', d3_vectorFeature);
module.exports = d3_vectorFeature;

/***/ }),
/* 300 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/**
 * @namespace geo.gl
 */
module.exports = {
  choroplethFeature: __webpack_require__(301),
  contourFeature: __webpack_require__(302),
  isolineFeature: __webpack_require__(303),
  lineFeature: __webpack_require__(304),
  pointFeature: __webpack_require__(305),
  polygonFeature: __webpack_require__(306),
  quadFeature: __webpack_require__(308),
  tileLayer: __webpack_require__(309),
  vglRenderer: __webpack_require__(113)
};

/***/ }),
/* 301 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var inherit = __webpack_require__(0);
var registerFeature = __webpack_require__(1).registerFeature;
var choroplethFeature = __webpack_require__(118);

/**
 * Create a new instance of choroplethFeature
 *
 * @class
 * @alias geo.gl.choroplethFeature
 * @extends geo.choroplethFeature
 * @returns {geo.gl.choroplethFeature}
 */
var gl_choroplethFeature = function gl_choroplethFeature(arg) {
  'use strict';

  if (!(this instanceof gl_choroplethFeature)) {
    return new gl_choroplethFeature(arg);
  }
  arg = arg || {};
  choroplethFeature.call(this, arg);

  /**
   * @private
   */
  var m_this = this,
      m_gl_polygons = null,
      s_exit = this._exit,
      s_init = this._init,
      s_draw = this.draw,
      s_update = this._update;

  /* Create the choropleth.  This calls the base class to generate the contours,
   * into the various gl uniforms and buffers.
   */
  function createGLChoropleth() {
    return m_this.createChoropleth();
  }

  this.draw = function () {
    m_this._update();
    if (m_gl_polygons) {
      for (var idx = 0; idx < m_gl_polygons.length; idx += 1) {
        m_gl_polygons[idx].draw();
      }
    }
    s_draw();
    return m_this;
  };

  /**
   * Initialize
   */
  this._init = function (arg) {
    s_init.call(m_this, arg);
  };

  /**
   * Build
   *
   * @override
   */
  this._build = function () {
    m_this.buildTime().modified();
    return m_gl_polygons = createGLChoropleth();
  };

  /**
   * Update
   *
   * @override
   */
  this._update = function () {
    s_update.call(m_this);
    if (m_this.dataTime().getMTime() >= m_this.buildTime().getMTime() || m_this.updateTime().getMTime() <= m_this.getMTime()) {
      m_this._wipePolygons();
      m_this._build();
    }
    m_this.updateTime().modified();
  };

  /**
   * Destroy Polygon Sub-Features
   */
  this._wipePolygons = function () {
    if (m_gl_polygons) {
      m_gl_polygons.map(function (polygon) {
        return polygon._exit();
      });
    }
    m_gl_polygons = null;
  };

  /**
   * Destroy
   */
  this._exit = function () {
    m_this._wipePolygons();
    s_exit();
  };

  this._init(arg);
  return this;
};

inherit(gl_choroplethFeature, choroplethFeature);

// Now register it
registerFeature('vgl', 'choropleth', gl_choroplethFeature);

module.exports = gl_choroplethFeature;

/***/ }),
/* 302 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var inherit = __webpack_require__(0);
var registerFeature = __webpack_require__(1).registerFeature;
var contourFeature = __webpack_require__(120);

/**
 * Create a new instance of contourFeature.
 *
 * @class
 * @alias geo.gl.contourFeature
 * @extends geo.contourFeature
 * @param {geo.contourFeature.spec} arg
 * @returns {geo.gl.contourFeature}
 */
var gl_contourFeature = function gl_contourFeature(arg) {
  'use strict';

  if (!(this instanceof gl_contourFeature)) {
    return new gl_contourFeature(arg);
  }
  arg = arg || {};
  contourFeature.call(this, arg);

  var vgl = __webpack_require__(9);
  var transform = __webpack_require__(7);
  var util = __webpack_require__(3);
  var object = __webpack_require__(18);

  object.call(this);

  /**
   * @private
   */
  var m_this = this,
      s_exit = this._exit,
      m_textureUnit = 7,
      m_actor = null,
      m_mapper = null,
      m_material = null,
      m_texture = null,
      m_minColorUniform = null,
      m_maxColorUniform = null,
      m_stepsUniform = null,
      m_steppedUniform = null,
      m_dynamicDraw = arg.dynamicDraw === undefined ? false : arg.dynamicDraw,
      s_init = this._init,
      s_update = this._update;

  function createVertexShader() {
    var vertexShaderSource = ['#ifdef GL_ES', '  precision highp float;', '#endif', 'attribute vec3 pos;', 'attribute float value;', 'attribute float opacity;', 'uniform mat4 modelViewMatrix;', 'uniform mat4 projectionMatrix;', 'varying float valueVar;', 'varying float opacityVar;', 'void main(void)', '{',
    /* Don't use z values; something is rotten in one of our matrices */
    '  vec4 scrPos = projectionMatrix * modelViewMatrix * vec4(pos.xy, 0, 1);', '  if (scrPos.w != 0.0) {', '    scrPos = scrPos / scrPos.w;', '  }', '  valueVar = value;', '  opacityVar = opacity;', '  gl_Position = scrPos;', '}'].join('\n'),
        shader = new vgl.shader(vgl.GL.VERTEX_SHADER);
    shader.setShaderSource(vertexShaderSource);
    return shader;
  }

  function createFragmentShader() {
    var fragmentShaderSource = ['#ifdef GL_ES', '  precision highp float;', '#endif', 'uniform vec4 minColor;', 'uniform vec4 maxColor;', 'uniform float steps;', 'uniform bool stepped;', 'uniform sampler2D sampler2d;', 'varying float valueVar;', 'varying float opacityVar;', 'void main () {', '  vec4 clr;', '  if (valueVar < 0.0) {', '    clr = minColor;', '  } else if (valueVar > steps) {', '    clr = maxColor;', '  } else {', '    float step;', '    if (stepped) {', '      step = floor(valueVar) + 0.5;', '      if (step > steps) {', '        step = steps - 0.5;', '      }', '    } else {', '      step = valueVar;', '    }',
    // our texture is padded on either end by a repeated value to ensure
    // we interpolate smoothly at the ends.
    '    clr = texture2D(sampler2d, vec2((step + 1.0) / (steps + 2.0), 0.0));', '  }', '  gl_FragColor = vec4(clr.rgb, clr.a * opacityVar);', '}'].join('\n'),
        shader = new vgl.shader(vgl.GL.FRAGMENT_SHADER);
    shader.setShaderSource(fragmentShaderSource);
    return shader;
  }

  /* Create the contours.  This calls the base class to generate the geometry,
   * color map, and other parameters.  The generated geoemtry is then loaded
   * into the various gl uniforms and buffers.
   */
  function createGLContours() {
    var contour = m_this._createContours(),
        numPts = contour.elements.length,
        colorTable = [],
        i,
        i3,
        j,
        j3,
        posBuf,
        opacityBuf,
        valueBuf,
        indicesBuf,
        geom = m_mapper.geometryData();

    m_minColorUniform.set([contour.minColor.r, contour.minColor.g, contour.minColor.b, contour.minColor.a]);
    m_maxColorUniform.set([contour.maxColor.r, contour.maxColor.g, contour.maxColor.b, contour.maxColor.a]);
    m_stepsUniform.set(contour.colorMap.length);
    m_steppedUniform.set(contour.stepped);
    // pad the colortable by repeating the end colors an extra time to ensure
    // interpolation never goes off of the colormap.
    for (i = -1; i < contour.colorMap.length + 1; i += 1) {
      j = Math.max(0, Math.min(contour.colorMap.length - 1, i));
      colorTable.push(contour.colorMap[j].r * 255);
      colorTable.push(contour.colorMap[j].g * 255);
      colorTable.push(contour.colorMap[j].b * 255);
      colorTable.push(contour.colorMap[j].a * 255);
    }
    m_texture.setColorTable(colorTable);
    contour.pos = transform.transformCoordinates(m_this.gcs(), m_this.layer().map().gcs(), contour.pos, 3);
    posBuf = util.getGeomBuffer(geom, 'pos', numPts * 3);
    opacityBuf = util.getGeomBuffer(geom, 'opacity', numPts);
    valueBuf = util.getGeomBuffer(geom, 'value', numPts);
    for (i = i3 = 0; i < numPts; i += 1, i3 += 3) {
      j = contour.elements[i];
      j3 = j * 3;
      posBuf[i3] = contour.pos[j3];
      posBuf[i3 + 1] = contour.pos[j3 + 1];
      posBuf[i3 + 2] = contour.pos[j3 + 2];
      opacityBuf[i] = contour.opacity[j];
      valueBuf[i] = contour.value[j];
    }
    indicesBuf = geom.primitive(0).indices();
    if (!(indicesBuf instanceof Uint16Array) || indicesBuf.length !== numPts) {
      indicesBuf = new Uint16Array(numPts);
      geom.primitive(0).setIndices(indicesBuf);
    }
    geom.boundsDirty(true);
    m_mapper.modified();
    m_mapper.boundsDirtyTimestamp().modified();
  }

  /**
   * Initialize.
   *
   * @param {geo.contourFeature.spec} arg The contour feature specification.
   */
  this._init = function (arg) {
    var blend = vgl.blend(),
        prog = vgl.shaderProgram(),
        mat = vgl.material(),
        tex = vgl.lookupTable(),
        geom = vgl.geometryData(),
        modelViewUniform = new vgl.modelViewUniform('modelViewMatrix'),
        projectionUniform = new vgl.projectionUniform('projectionMatrix'),
        samplerUniform = new vgl.uniform(vgl.GL.INT, 'sampler2d'),
        vertexShader = createVertexShader(),
        fragmentShader = createFragmentShader(),
        posAttr = vgl.vertexAttribute('pos'),
        valueAttr = vgl.vertexAttribute('value'),
        opacityAttr = vgl.vertexAttribute('opacity'),
        sourcePositions = vgl.sourceDataP3fv({ 'name': 'pos' }),
        sourceValues = vgl.sourceDataAnyfv(1, vgl.vertexAttributeKeysIndexed.One, { 'name': 'value' }),
        sourceOpacity = vgl.sourceDataAnyfv(1, vgl.vertexAttributeKeysIndexed.Two, { 'name': 'opacity' }),
        primitive = new vgl.triangles();

    s_init.call(m_this, arg);
    m_mapper = vgl.mapper({ dynamicDraw: m_dynamicDraw });

    prog.addVertexAttribute(posAttr, vgl.vertexAttributeKeys.Position);
    prog.addVertexAttribute(valueAttr, vgl.vertexAttributeKeysIndexed.One);
    prog.addVertexAttribute(opacityAttr, vgl.vertexAttributeKeysIndexed.Two);

    prog.addUniform(modelViewUniform);
    prog.addUniform(projectionUniform);
    m_minColorUniform = new vgl.uniform(vgl.GL.FLOAT_VEC4, 'minColor');
    prog.addUniform(m_minColorUniform);
    m_maxColorUniform = new vgl.uniform(vgl.GL.FLOAT_VEC4, 'maxColor');
    prog.addUniform(m_maxColorUniform);
    /* steps is always an integer, but it is more efficient if we use a float
     */
    m_stepsUniform = new vgl.uniform(vgl.GL.FLOAT, 'steps');
    prog.addUniform(m_stepsUniform);
    m_steppedUniform = new vgl.uniform(vgl.GL.BOOL, 'stepped');
    prog.addUniform(m_steppedUniform);

    prog.addShader(fragmentShader);
    prog.addShader(vertexShader);

    prog.addUniform(samplerUniform);
    tex.setTextureUnit(m_textureUnit);
    samplerUniform.set(m_textureUnit);

    m_material = mat;
    m_material.addAttribute(prog);
    m_material.addAttribute(blend);
    m_texture = tex;
    m_material.addAttribute(m_texture);

    m_actor = vgl.actor();
    m_actor.setMaterial(m_material);
    m_actor.setMapper(m_mapper);

    geom.addSource(sourcePositions);
    geom.addSource(sourceValues);
    geom.addSource(sourceOpacity);
    geom.addPrimitive(primitive);
    m_mapper.setGeometryData(geom);
  };

  /**
   * Build.
   */
  this._build = function () {
    if (m_actor) {
      m_this.renderer().contextRenderer().removeActor(m_actor);
    }

    createGLContours();

    m_this.renderer().contextRenderer().addActor(m_actor);
    m_this.buildTime().modified();
  };

  /**
   * Update.
   */
  this._update = function () {
    s_update.call(m_this);

    if (m_this.dataTime().getMTime() >= m_this.buildTime().getMTime() || m_this.updateTime().getMTime() <= m_this.getMTime()) {
      m_this._build();
    }

    m_actor.setVisible(m_this.visible());
    m_actor.material().setBinNumber(m_this.bin());
    m_this.updateTime().modified();
  };

  /**
   * Destroy.
   */
  this._exit = function () {
    m_this.renderer().contextRenderer().removeActor(m_actor);
    s_exit();
  };

  this._init(arg);
  return this;
};

inherit(gl_contourFeature, contourFeature);

// Now register it
registerFeature('vgl', 'contour', gl_contourFeature);

module.exports = gl_contourFeature;

/***/ }),
/* 303 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var inherit = __webpack_require__(0);
var registerFeature = __webpack_require__(1).registerFeature;
var isolineFeature = __webpack_require__(55);

/**
 * Create a new instance of isolineFeature.
 *
 * @class
 * @alias geo.gl.isolineFeature
 * @extends geo.isolineFeature
 * @param {geo.isolineFeature.spec} arg
 * @returns {geo.gl.isolineFeature}
 */
var gl_isolineFeature = function gl_isolineFeature(arg) {
  'use strict';

  if (!(this instanceof gl_isolineFeature)) {
    return new gl_isolineFeature(arg);
  }
  arg = arg || {};
  isolineFeature.call(this, arg);

  var object = __webpack_require__(18);
  object.call(this);

  this._init(arg);
  return this;
};

inherit(gl_isolineFeature, isolineFeature);

// Now register it
registerFeature('vgl', 'isoline', gl_isolineFeature);
module.exports = gl_isolineFeature;

/***/ }),
/* 304 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var inherit = __webpack_require__(0);
var registerFeature = __webpack_require__(1).registerFeature;
var lineFeature = __webpack_require__(23);

var MAX_MITER_LIMIT = 100;

/* Flags are passed to the vertex shader in a float.  Since a 32-bit float has
 * 24 bits of mantissa, including the sign bit, a maximum of 23 bits of flags
 * can be passed in a float without loss or complication. */
/* vertex flags specify which direction a vertex needs to be offset */
var flagsVertex = { // uses 2 bits
  corner: 0,
  near: 1,
  far: 3
};
var flagsLineCap = { // uses 3 bits with flagsLineJoin
  butt: 0,
  square: 1,
  round: 2
};
var flagsLineJoin = { // uses 3 bits with flagsLineCap
  passthrough: 3,
  miter: 4,
  bevel: 5,
  round: 6,
  'miter-clip': 7
};
var flagsNearLineShift = 2,
    flagsFarLineShift = 5;
var flagsNearOffsetShift = 8; // uses 11 bits
/* Fixed flags */
var flagsDebug = { // uses 1 bit
  normal: 0,
  debug: 1
};

/**
 * Create a new instance of lineFeature.
 *
 * @class
 * @alias geo.gl.lineFeature
 * @extends geo.lineFeature
 * @param {geo.lineFeature.spec} arg
 * @returns {geo.gl.lineFeature}
 */
var gl_lineFeature = function gl_lineFeature(arg) {
  'use strict';

  if (!(this instanceof gl_lineFeature)) {
    return new gl_lineFeature(arg);
  }
  arg = arg || {};
  lineFeature.call(this, arg);

  var vgl = __webpack_require__(9);
  var transform = __webpack_require__(7);
  var util = __webpack_require__(3);
  var object = __webpack_require__(18);

  object.call(this);

  /**
   * @private
   */
  var m_this = this,
      s_exit = this._exit,
      m_actor,
      m_mapper,
      m_material,
      m_pixelWidthUnif,
      m_aspectUniform,
      m_miterLimitUniform,
      m_antialiasingUniform,
      m_flagsUniform,
      m_dynamicDraw = arg.dynamicDraw === undefined ? false : arg.dynamicDraw,
      m_geometry,
      s_init = this._init,
      s_update = this._update;

  /**
   * Create the vertex shader for lines.
   *
   * @returns {vgl.shader}
   */
  function createVertexShader() {
    var vertexShaderSource = ['#ifdef GL_ES', '  precision highp float;', '#endif', 'attribute vec3 pos;', 'attribute vec3 prev;', 'attribute vec3 next;', 'attribute vec3 far;', 'attribute float flags;', 'attribute vec3 strokeColor;', 'attribute float strokeOpacity;', 'attribute float strokeWidth;', 'uniform mat4 modelViewMatrix;', 'uniform mat4 projectionMatrix;', 'uniform float pixelWidth;', 'uniform float aspect;', 'uniform float miterLimit;', 'uniform float antialiasing;', 'varying vec4 strokeColorVar;', 'varying vec4 subpos;', /* px, py, length - px, width */
    'varying vec4 info;', /* near mode, far mode, offset */
    'varying vec4 angles;', /* near angle cos, sin, far angle cos, sin */

    'const float PI = 3.14159265358979323846264;', 'vec4 viewCoord(vec3 c) {', '  vec4 result = projectionMatrix * modelViewMatrix * vec4(c.xyz, 1.0);', '  if (result.w != 0.0)  result = result / result.w;', '  return result;', '}', 'float atan2(float y, float x) {', '  if (x > 0.0)  return atan(y / x);', '  if (x < 0.0 && y >= 0.0)  return atan(y / x) + PI;', '  if (x < 0.0)  return atan(y / x) - PI;', '  return sign(y) * 0.5 * PI;', '}', 'void main(void)', '{',
    /* If any vertex has been deliberately set to a negative opacity,
     * skip doing computations on it. */
    '  if (strokeOpacity < 0.0) {', '    gl_Position = vec4(2.0, 2.0, 0.0, 1.0);', '    return;', '  }',
    /* convert coordinates.  We have four values, since we need to
     * calculate the angles between the lines formed by prev-pos and
     * pos-next, and between pos-next and next-far, plus know the angle
     *   (prev)---(pos)---(next)---(far) => A---B---C---D */
    '  vec4 A = viewCoord(prev);', '  vec4 B = viewCoord(pos);', '  vec4 C = viewCoord(next);', '  vec4 D = viewCoord(far);',
    // calculate line segment vector and angle
    '  vec2 deltaCB = C.xy - B.xy;', '  if (deltaCB == vec2(0.0, 0.0)) {', '    gl_Position = vec4(2.0, 2.0, 0.0, 1.0);', '    return;', '  }', '  float angleCB = atan2(deltaCB.y, deltaCB.x * aspect);',
    // values we need to pass along
    '  strokeColorVar = vec4(strokeColor, strokeOpacity);',
    // extract values from our flags field
    '  int vertex = int(mod(flags, 4.0));', '  int nearMode = int(mod(floor(flags / 4.0), 8.0));', '  int farMode = int(mod(floor(flags / 32.0), 8.0));',
    // we use 11 bits of the flags for the offset, where -1023 to 1023
    // maps to -1 to 1.  The 11 bits are a signed value, so simply
    // selecting the bits will result in an unsigned values that may be
    // greater than 1, in which case we have to subtract appropriately.
    '  float offset = mod(floor(flags / 256.0), 2048.0) / 1023.0;', '  if (offset > 1.0)  offset -= 2048.0 / 1023.0;',
    // by default, offset by the width and don't extend lines.  Later,
    // calculate line extensions based on end cap and end join modes
    '  float yOffset = strokeWidth + antialiasing;', '  if (vertex == 0 || vertex == 2)  yOffset *= -1.0;', '  yOffset += strokeWidth * offset;', '  float xOffset = 0.0;',
    // end caps
    '  if (nearMode == 0) {', '    xOffset = antialiasing;', '  } else if (nearMode == 1 || nearMode == 2) {', '    xOffset = strokeWidth + antialiasing;', '  }',

    // If joining lines, calculate the angles in screen space formed by
    // the near end (A-B-C) and far end (B-C-D), and determine how much
    // space is needed for the particular join.
    //   This could be changed: if the lines are not a uniform width and
    // offset, then the functional join angle is not simply half the
    // angle between the two lines, but rather half the angle of the
    // inside edge of the the two lines.
    '  float cosABC = 1.0, sinABC = 0.0, cosBCD = 1.0, sinBCD = 0.0;', // of half angles
    // handle near end
    '  if (nearMode >= 4) {', '    float angleBA = atan2(B.y - A.y, (B.x - A.x) * aspect);', '    if (A.xy == B.xy)  angleBA = angleCB;', '    float angleABC = angleCB - angleBA;',
    // ensure angle is in the range [-PI, PI], then take the half angle
    '    angleABC = (mod(angleABC + 3.0 * PI, 2.0 * PI) - PI) / 2.0;', '    cosABC = cos(angleABC);  sinABC = sin(angleABC);',
    // if this angle is close to flat, pass-through the join
    '    if (nearMode >= 4 && cosABC > 0.999999) {', '      nearMode = 3;', '    }',
    // miter, miter-clip
    '    if (nearMode == 4 || nearMode == 7) {', '      if (cosABC < 0.000001 || 1.0 / cosABC > miterLimit) {', '        if (nearMode == 4) {', '          nearMode = 5;', '        } else {', '          xOffset = miterLimit * strokeWidth * (1.0 - offset * sign(sinABC)) + antialiasing;', '        }', '      } else {',
    // we add an extra 1.0 to the xOffset to make sure that fragment
    // shader is doing the clipping
    '        xOffset = abs(sinABC / cosABC) * strokeWidth * (1.0 - offset * sign(sinABC)) + antialiasing + 1.0;', '        nearMode = 4;', '      }', '    }',
    // bevel or round join
    '    if (nearMode == 5 || nearMode == 6) {', '      xOffset = strokeWidth * (1.0 - offset * sign(sinABC)) + antialiasing;', '    }', '  }',

    // handle far end
    '  if (farMode >= 4) {', '    float angleDC = atan2(D.y - C.y, (D.x - C.x) * aspect);', '    if (D.xy == C.xy)  angleDC = angleCB;', '    float angleBCD = angleDC - angleCB;',
    // ensure angle is in the range [-PI, PI], then take the half angle
    '    angleBCD = (mod(angleBCD + 3.0 * PI, 2.0 * PI) - PI) / 2.0;', '    cosBCD = cos(angleBCD);  sinBCD = sin(angleBCD);',
    // if this angle is close to flat, pass-through the join
    '    if (farMode >= 4 && cosBCD > 0.999999) {', '      farMode = 3;', '    }',
    // miter, miter-clip
    '    if (farMode == 4 || farMode == 7) {', '      if (cosBCD < 0.000001 || 1.0 / cosBCD > miterLimit) {', '        if (farMode == 4)  farMode = 5;', '      } else {', '        farMode = 4;', '      }', '    }', '  }',

    // compute the location of a vertex to include everything that might
    // need to be rendered
    '  xOffset *= -1.0;', '  gl_Position = vec4(', '    B.x + (xOffset * cos(angleCB) - yOffset * sin(angleCB)) * pixelWidth,', '    B.y + (xOffset * sin(angleCB) + yOffset * cos(angleCB)) * pixelWidth * aspect,', '    B.z, 1.0);',
    // store other values needed to determine which pixels to plot.
    '  float lineLength = length(vec2(deltaCB.x, deltaCB.y / aspect)) / pixelWidth;', '  if (vertex == 0 || vertex == 1) {', '    subpos = vec4(xOffset, yOffset, lineLength - xOffset, strokeWidth);', '    info = vec4(float(nearMode), float(farMode), offset, 0.0);', '    angles = vec4(cosABC, sinABC, cosBCD, sinBCD);', '  } else {', '    subpos = vec4(lineLength - xOffset, -yOffset, xOffset, strokeWidth);', '    info = vec4(float(farMode), float(nearMode), -offset, 0.0);', '    angles = vec4(cosBCD, -sinBCD, cosABC, -sinABC);', '  }', '}'].join('\n'),
        shader = new vgl.shader(vgl.GL.VERTEX_SHADER);
    shader.setShaderSource(vertexShaderSource);
    return shader;
  }

  /**
   * Create the fragment shader for lines.
   *
   * @param {boolean} [allowDebug] If truthy, include code that can render
   *    in debug mode.  This is mildly less efficient, even if debugging is
   *    not turned on.
   * @returns {vgl.shader}
   */
  function createFragmentShader(allowDebug) {
    var fragmentShaderSource = ['#ifdef GL_ES', '  #ifdef GL_FRAGMENT_PRECISION_HIGH', '    precision highp float;', '  #else', '    precision mediump float;', '  #endif', '#endif', 'varying vec4 strokeColorVar;', 'varying vec4 subpos;', 'varying vec4 info;', 'varying vec4 angles;', 'uniform float antialiasing;', 'uniform float miterLimit;', 'uniform float fixedFlags;', 'void main () {', '  vec4 color = strokeColorVar;', allowDebug ? '  bool debug = bool(mod(fixedFlags, 2.0));' : '', '  float opacity = 1.0;', '  int nearMode = int(floor(info.x + 0.5));', '  int farMode = int(floor(info.y + 0.5));', '  float cosABC = angles.x;', '  float sinABC = angles.y;', '  float cosBCD = angles.z;', '  float sinBCD = angles.w;',
    // never render on the opposite side of a miter.  This uses a bit of
    // slop, via pow(smoothstep()) instead of step(), since there are
    // precision issues in this calculation.  This doesn't wholy solve
    // the precision issue; sometimes pixels are missed or double
    // rendered along the inside seam of a miter.
    '  if (nearMode >= 4) {', '    float dist = cosABC * subpos.x - sinABC * subpos.y;', '    opacity = min(opacity, pow(smoothstep(-0.02, 0.02, dist), 0.5));', '    if (opacity == 0.0) {', allowDebug ? 'if (debug) {color.r=255.0/255.0;gl_FragColor=color;return;}' : '', '      discard;', '    }', '  }', '  if (farMode >= 4) {', '    float dist = cosBCD * subpos.z - sinBCD * subpos.y;', '    opacity = min(opacity, pow(smoothstep(-0.02, 0.02, dist), 0.5));', '    if (opacity == 0.0) {', allowDebug ? 'if (debug) {color.r=254.0/255.0;gl_FragColor=color;return;}' : '', '      discard;', '    }', '  }',
    // butt or square cap
    '  if ((nearMode == 0 || nearMode == 1) && subpos.x < antialiasing) {', '    opacity = min(opacity, smoothstep(-antialiasing, antialiasing, subpos.x + subpos.w * float(nearMode)));', '  }', '  if ((farMode == 0 || farMode == 1) && subpos.z < antialiasing) {', '    opacity = min(opacity, smoothstep(-antialiasing, antialiasing, subpos.z + subpos.w * float(farMode)));', '  }',
    // round cap
    '  if (nearMode == 2 && subpos.x <= 0.0) {', '    opacity = min(opacity, smoothstep(-antialiasing, antialiasing, subpos.w - sqrt(pow(subpos.x, 2.0) + pow(subpos.y - info.z * subpos.w, 2.0))));', '  }', '  if (farMode == 2 && subpos.z <= 0.0) {', '    opacity = min(opacity, smoothstep(-antialiasing, antialiasing, subpos.w - sqrt(pow(subpos.z, 2.0) + pow(subpos.y - info.z * subpos.w, 2.0))));', '  }',
    // bevel and clip joins
    '  if ((nearMode == 5 || nearMode == 7) && subpos.x < antialiasing) {', '    float dist = (sinABC * subpos.x + cosABC * subpos.y) * sign(sinABC);', '    float w = subpos.w * (1.0 - info.z * sign(sinABC));', '    float maxDist;', '    if (nearMode == 5)  maxDist = cosABC * w;', '    else                maxDist = miterLimit * w;', '    opacity = min(opacity, smoothstep(-antialiasing, antialiasing, maxDist + dist));', '  }', '  if ((farMode == 5 || farMode == 7) && subpos.z < antialiasing) {', '    float dist = (sinBCD * subpos.z + cosBCD * subpos.y) * sign(sinBCD);', '    float w = subpos.w * (1.0 - info.z * sign(sinBCD));', '    float maxDist;', '    if (farMode == 5)  maxDist = cosBCD * w;', '    else               maxDist = miterLimit * w;', '    opacity = min(opacity, smoothstep(-antialiasing, antialiasing, maxDist + dist));', '  }',
    // round join
    '  if (nearMode == 6 && subpos.x <= 0.0) {', '    float w = subpos.w * (1.0 - info.z * sign(sinABC));', '    opacity = min(opacity, smoothstep(-antialiasing, antialiasing, w - sqrt(pow(subpos.x, 2.0) + pow(subpos.y, 2.0))));', '  }', '  if (farMode == 6 && subpos.z <= 0.0) {', '    float w = subpos.w * (1.0 - info.z * sign(sinBCD));', '    opacity = min(opacity, smoothstep(-antialiasing, antialiasing, w - sqrt(pow(subpos.z, 2.0) + pow(subpos.y, 2.0))));', '  }',
    // antialias along main edges
    '  if (antialiasing > 0.0) {', '    if (subpos.y > subpos.w * (1.0 + info.z) - antialiasing) {', '      opacity = min(opacity, smoothstep(antialiasing, -antialiasing, subpos.y - subpos.w * (1.0 + info.z)));', '    }', '    if (subpos.y < subpos.w * (-1.0 + info.z) + antialiasing) {', '      opacity = min(opacity, smoothstep(-antialiasing, antialiasing, subpos.y - subpos.w * (-1.0 + info.z)));', '    }', '  }', '  if (opacity == 0.0) {', allowDebug ? 'if (debug) {color.r=253.0/255.0;gl_FragColor=color;return;}' : '', '    discard;', '  }', '  color.a *= opacity;', '  gl_FragColor = color;', '}'].join('\n'),
        shader = new vgl.shader(vgl.GL.FRAGMENT_SHADER);
    shader.setShaderSource(fragmentShaderSource);
    return shader;
  }

  /**
   * Create and style the data needed to render the lines.
   *
   * @param {boolean} onlyStyle if true, use the existing geoemtry and just
   *    recalculate the style.
   */
  function createGLLines(onlyStyle) {
    var data = m_this.data(),
        d,
        i,
        j,
        k,
        v,
        v2,
        lidx,
        numSegments = 0,
        len,
        lineItemList,
        lineItem,
        lineItemData,
        vert = [{}, {}],
        v1 = vert[1],
        pos,
        posIdx3,
        firstpos,
        firstPosIdx3,
        strokeWidthFunc = m_this.style.get('strokeWidth'),
        strokeWidthVal,
        strokeColorFunc = m_this.style.get('strokeColor'),
        strokeColorVal,
        strokeOpacityFunc = m_this.style.get('strokeOpacity'),
        strokeOpacityVal,
        lineCapFunc = m_this.style.get('lineCap'),
        lineCapVal,
        lineJoinFunc = m_this.style.get('lineJoin'),
        lineJoinVal,
        strokeOffsetFunc = m_this.style.get('strokeOffset'),
        strokeOffsetVal,
        miterLimit = m_this.style.get('miterLimit')(data),
        antialiasing = m_this.style.get('antialiasing')(data) || 0,
        order = m_this.featureVertices(),
        orderLen = order.length,
        posBuf,
        prevBuf,
        nextBuf,
        farBuf,
        flagsBuf,
        indicesBuf,
        fixedFlags = flagsDebug[m_this.style.get('debug')(data) ? 'debug' : 'normal'] || 0,
        strokeWidthBuf,
        strokeColorBuf,
        strokeOpacityBuf,
        dest,
        dest3,
        geom = m_mapper.geometryData(),
        closedFunc = m_this.style.get('closed'),
        closedVal,
        closed = [],
        updateFlags = true;

    closedVal = util.isFunction(m_this.style('closed')) ? undefined : closedFunc() || false;
    lineCapVal = util.isFunction(m_this.style('lineCap')) ? undefined : lineCapFunc() || 'butt';
    lineJoinVal = util.isFunction(m_this.style('lineJoin')) ? undefined : lineJoinFunc() || 'miter';
    strokeColorVal = util.isFunction(m_this.style('strokeColor')) ? undefined : strokeColorFunc();
    strokeOffsetVal = util.isFunction(m_this.style('strokeOffset')) ? undefined : strokeOffsetFunc() || 0;
    strokeOpacityVal = util.isFunction(m_this.style('strokeOpacity')) ? undefined : strokeOpacityFunc();
    strokeWidthVal = util.isFunction(m_this.style('strokeWidth')) ? undefined : strokeWidthFunc();

    if (miterLimit !== undefined) {
      /* We impose a limit no matter what, since otherwise the growth is
       * unbounded.  Values less than 1 make no sense, since we are using the
       * SVG definition of miter length. */
      m_miterLimitUniform.set(Math.max(1, Math.min(MAX_MITER_LIMIT, miterLimit)));
    }
    m_flagsUniform.set(fixedFlags);
    m_antialiasingUniform.set(antialiasing);

    if (!onlyStyle) {
      var position = [],
          posFunc = m_this.position();
      lineItemList = new Array(data.length);
      for (i = 0; i < data.length; i += 1) {
        d = data[i];
        lineItem = m_this.line()(d, i);
        lineItemList[i] = lineItem;
        if (lineItem.length < 2) {
          continue;
        }
        numSegments += lineItem.length - 1;
        for (j = 0; j < lineItem.length; j += 1) {
          pos = posFunc(lineItem[j], j, d, i);
          position.push(pos.x);
          position.push(pos.y);
          position.push(pos.z || 0.0);
          if (!j) {
            firstpos = pos;
          }
        }
        if (lineItem.length > 2 && (closedVal === undefined ? closedFunc(d, i) : closedVal)) {
          /* line is closed */
          if (pos.x !== firstpos.x || pos.y !== firstpos.y || pos.z !== firstpos.z) {
            numSegments += 1;
            closed[i] = 2; /* first and last points are distinct */
          } else {
            closed[i] = 1; /* first point is repeated as last point */
          }
        }
      }

      position = transform.transformCoordinates(m_this.gcs(), m_this.layer().map().gcs(), position, 3);
      len = numSegments * orderLen;
      posBuf = util.getGeomBuffer(geom, 'pos', len * 3);
      prevBuf = util.getGeomBuffer(geom, 'prev', len * 3);
      nextBuf = util.getGeomBuffer(geom, 'next', len * 3);
      farBuf = util.getGeomBuffer(geom, 'far', len * 3);

      indicesBuf = geom.primitive(0).indices();
      if (!(indicesBuf instanceof Uint16Array) || indicesBuf.length !== len) {
        indicesBuf = new Uint16Array(len);
        geom.primitive(0).setIndices(indicesBuf);
      }
      // save some information to be reused when we update only style
      m_geometry = {
        numSegments: numSegments,
        closed: closed,
        lineItemList: lineItemList,
        lineCapVal: lineCapVal,
        lineJoinVal: lineJoinVal,
        strokeOffsetVal: strokeOffsetVal
      };
    } else {
      numSegments = m_geometry.numSegments;
      closed = m_geometry.closed;
      lineItemList = m_geometry.lineItemList;
      len = numSegments * orderLen;
      updateFlags = lineCapVal !== m_geometry.lineCapVal || lineCapVal === undefined || lineJoinVal !== m_geometry.lineJoinVal || lineJoinVal === undefined || strokeOffsetVal !== m_geometry.strokeOffsetVal || strokeOffsetVal === undefined;
    }

    flagsBuf = util.getGeomBuffer(geom, 'flags', len);
    strokeWidthBuf = util.getGeomBuffer(geom, 'strokeWidth', len);
    strokeColorBuf = util.getGeomBuffer(geom, 'strokeColor', len * 3);
    strokeOpacityBuf = util.getGeomBuffer(geom, 'strokeOpacity', len);

    for (i = posIdx3 = dest = dest3 = 0; i < data.length; i += 1) {
      lineItem = lineItemList[i];
      if (lineItem.length < 2) {
        continue;
      }
      d = data[i];
      firstPosIdx3 = posIdx3;
      for (j = 0; j < lineItem.length + (closed[i] === 2 ? 1 : 0); j += 1, posIdx3 += 3) {
        lidx = j;
        if (j === lineItem.length) {
          lidx = 0;
          posIdx3 -= 3;
        }
        lineItemData = lineItem[lidx];
        /* swap entries in vert so that vert[0] is the first vertex, and
         * vert[1] will be reused for the second vertex */
        if (j) {
          v1 = vert[0];
          vert[0] = vert[1];
          vert[1] = v1;
        }
        if (!onlyStyle) {
          v1.pos = j === lidx ? posIdx3 : firstPosIdx3;
          v1.prev = lidx ? posIdx3 - 3 : closed[i] ? firstPosIdx3 + (lineItem.length - 3 + closed[i]) * 3 : posIdx3;
          v1.next = j + 1 < lineItem.length ? posIdx3 + 3 : closed[i] ? j !== lidx ? firstPosIdx3 + 3 : firstPosIdx3 + 6 - closed[i] * 3 : posIdx3;
        }
        v1.strokeWidth = strokeWidthVal === undefined ? strokeWidthFunc(lineItemData, lidx, d, i) : strokeWidthVal;
        v1.strokeColor = strokeColorVal === undefined ? strokeColorFunc(lineItemData, lidx, d, i) : strokeColorVal;
        v1.strokeOpacity = strokeOpacityVal === undefined ? strokeOpacityFunc(lineItemData, lidx, d, i) : strokeOpacityVal;
        if (updateFlags) {
          v1.strokeOffset = (strokeOffsetVal === undefined ? strokeOffsetFunc(lineItemData, lidx, d, i) : strokeOffsetVal) || 0;
          if (v1.strokeOffset) {
            /* we use 11 bits to store the offset, and we want to store values
             * from -1 to 1, so multiply our values by 1023, and use some bit
             * manipulation to ensure that it is packed properly */
            v1.posStrokeOffset = Math.round(2048 + 1023 * Math.min(1, Math.max(-1, v1.strokeOffset))) & 0x7FF;
            v1.negStrokeOffset = Math.round(2048 - 1023 * Math.min(1, Math.max(-1, v1.strokeOffset))) & 0x7FF;
          } else {
            v1.posStrokeOffset = v1.negStrokeOffset = 0;
          }
          if (!closed[i] && (!j || j === lineItem.length - 1)) {
            v1.flags = flagsLineCap[lineCapVal === undefined ? lineCapFunc(lineItemData, lidx, d, i) : lineCapVal] || flagsLineCap.butt;
          } else {
            v1.flags = flagsLineJoin[lineJoinVal === undefined ? lineJoinFunc(lineItemData, lidx, d, i) : lineJoinVal] || flagsLineJoin.miter;
          }
        }

        if (j) {
          /* zero out the z position.  This can be changed if we handle it in
           * the shader. */
          for (k = 0; k < orderLen; k += 1, dest += 1, dest3 += 3) {
            v = vert[order[k][0]];
            v2 = vert[1 - order[k][0]];
            if (!onlyStyle) {
              posBuf[dest3] = position[v.pos];
              posBuf[dest3 + 1] = position[v.pos + 1];
              posBuf[dest3 + 2] = 0; // position[v.pos + 2];
            }
            if (!order[k][0]) {
              if (!onlyStyle) {
                prevBuf[dest3] = position[v.prev];
                prevBuf[dest3 + 1] = position[v.prev + 1];
                prevBuf[dest3 + 2] = 0; // position[v.prev + 2];
                nextBuf[dest3] = position[v.next];
                nextBuf[dest3 + 1] = position[v.next + 1];
                nextBuf[dest3 + 2] = 0; // position[v.next + 2];
                farBuf[dest3] = position[v2.next];
                farBuf[dest3 + 1] = position[v2.next + 1];
                farBuf[dest3 + 2] = 0; // position[v2.next + 2];
              }
              if (updateFlags) {
                flagsBuf[dest] = flagsVertex[order[k][1]] | v.flags << flagsNearLineShift | v2.flags << flagsFarLineShift | v.negStrokeOffset << flagsNearOffsetShift;
              }
            } else {
              if (!onlyStyle) {
                prevBuf[dest3] = position[v.next];
                prevBuf[dest3 + 1] = position[v.next + 1];
                prevBuf[dest3 + 2] = 0; // position[v.next + 2];
                nextBuf[dest3] = position[v.prev];
                nextBuf[dest3 + 1] = position[v.prev + 1];
                nextBuf[dest3 + 2] = 0; // position[v.prev + 2];
                farBuf[dest3] = position[v2.prev];
                farBuf[dest3 + 1] = position[v2.prev + 1];
                farBuf[dest3 + 2] = 0; // position[v2.prev + 2];
              }
              if (updateFlags) {
                flagsBuf[dest] = flagsVertex[order[k][1]] | v.flags << flagsNearLineShift | v2.flags << flagsFarLineShift | v.posStrokeOffset << flagsNearOffsetShift;
              }
            }
            strokeWidthBuf[dest] = v.strokeWidth;
            strokeColorBuf[dest3] = v.strokeColor.r;
            strokeColorBuf[dest3 + 1] = v.strokeColor.g;
            strokeColorBuf[dest3 + 2] = v.strokeColor.b;
            strokeOpacityBuf[dest] = v.strokeOpacity;
          }
        }
      }
    }

    m_mapper.modified();
    if (!onlyStyle) {
      geom.boundsDirty(true);
      m_mapper.boundsDirtyTimestamp().modified();
    }
  }

  /**
   * Return the arrangement of vertices used for each line segment.  Each line
   * is rendered by two triangles.  This reports how the vertices of those
   * triangles are arranged.  Each entry is a triple: the line-end number, the
   * vertex use, and the side of the line that the vertex is on.
   *
   * @returns {array[]}
   */
  this.featureVertices = function () {
    return [[0, 'corner', -1], [0, 'near', 1], [1, 'far', -1], [1, 'corner', 1], [1, 'near', -1], [0, 'far', 1]];
  };

  /**
   * Return the number of vertices used for each line segment.
   *
   * @returns {number}
   */
  this.verticesPerFeature = function () {
    return m_this.featureVertices().length;
  };

  /**
   * Initialize.
   *
   * @param {geo.lineFeature.spec} arg The feature specification.
   * @returns {this}
   */
  this._init = function (arg) {
    var prog = vgl.shaderProgram(),
        vs = createVertexShader(),
        fs = createFragmentShader(((arg || {}).style || {}).debug !== undefined),

    // Vertex attributes
    posAttr = vgl.vertexAttribute('pos'),
        prvAttr = vgl.vertexAttribute('prev'),
        nxtAttr = vgl.vertexAttribute('next'),
        farAttr = vgl.vertexAttribute('far'),
        flagsAttr = vgl.vertexAttribute('flags'),
        strkWidthAttr = vgl.vertexAttribute('strokeWidth'),
        strkColorAttr = vgl.vertexAttribute('strokeColor'),
        strkOpacityAttr = vgl.vertexAttribute('strokeOpacity'),

    // Shader uniforms
    mviUnif = new vgl.modelViewUniform('modelViewMatrix'),
        prjUnif = new vgl.projectionUniform('projectionMatrix'),
        geom = vgl.geometryData(),

    // Sources
    posData = vgl.sourceDataP3fv({ name: 'pos' }),
        prvPosData = vgl.sourceDataAnyfv(3, vgl.vertexAttributeKeysIndexed.Four, { name: 'prev' }),
        nxtPosData = vgl.sourceDataAnyfv(3, vgl.vertexAttributeKeysIndexed.Five, { name: 'next' }),
        farPosData = vgl.sourceDataAnyfv(3, vgl.vertexAttributeKeysIndexed.Six, { name: 'far' }),
        flagsData = vgl.sourceDataAnyfv(1, vgl.vertexAttributeKeysIndexed.Seven, { name: 'flags' }),
        strkWidthData = vgl.sourceDataAnyfv(1, vgl.vertexAttributeKeysIndexed.One, { name: 'strokeWidth' }),
        strkColorData = vgl.sourceDataAnyfv(3, vgl.vertexAttributeKeysIndexed.Two, { name: 'strokeColor' }),
        strkOpacityData = vgl.sourceDataAnyfv(1, vgl.vertexAttributeKeysIndexed.Three, { name: 'strokeOpacity' }),

    // Primitive indices
    triangles = vgl.triangles();

    m_pixelWidthUnif = new vgl.floatUniform('pixelWidth', 1.0 / m_this.renderer().width());
    m_aspectUniform = new vgl.floatUniform('aspect', m_this.renderer().width() / m_this.renderer().height());
    m_miterLimitUniform = new vgl.floatUniform('miterLimit', 10);
    m_antialiasingUniform = new vgl.floatUniform('antialiasing', 0);
    m_flagsUniform = new vgl.floatUniform('fixedFlags', 0);

    s_init.call(m_this, arg);
    m_material = vgl.material();
    m_mapper = vgl.mapper({ dynamicDraw: m_dynamicDraw });

    prog.addVertexAttribute(posAttr, vgl.vertexAttributeKeys.Position);
    prog.addVertexAttribute(strkWidthAttr, vgl.vertexAttributeKeysIndexed.One);
    prog.addVertexAttribute(strkColorAttr, vgl.vertexAttributeKeysIndexed.Two);
    prog.addVertexAttribute(strkOpacityAttr, vgl.vertexAttributeKeysIndexed.Three);
    prog.addVertexAttribute(prvAttr, vgl.vertexAttributeKeysIndexed.Four);
    prog.addVertexAttribute(nxtAttr, vgl.vertexAttributeKeysIndexed.Five);
    prog.addVertexAttribute(farAttr, vgl.vertexAttributeKeysIndexed.Six);
    prog.addVertexAttribute(flagsAttr, vgl.vertexAttributeKeysIndexed.Seven);

    prog.addUniform(mviUnif);
    prog.addUniform(prjUnif);
    prog.addUniform(m_pixelWidthUnif);
    prog.addUniform(m_aspectUniform);
    prog.addUniform(m_miterLimitUniform);
    prog.addUniform(m_antialiasingUniform);
    prog.addUniform(m_flagsUniform);

    prog.addShader(fs);
    prog.addShader(vs);

    m_material.addAttribute(prog);
    m_material.addAttribute(vgl.blend());

    m_actor = vgl.actor();
    m_actor.setMaterial(m_material);
    m_actor.setMapper(m_mapper);

    geom.addSource(posData);
    geom.addSource(prvPosData);
    geom.addSource(nxtPosData);
    geom.addSource(farPosData);
    geom.addSource(strkWidthData);
    geom.addSource(strkColorData);
    geom.addSource(strkOpacityData);
    geom.addSource(flagsData);
    geom.addPrimitive(triangles);
    m_mapper.setGeometryData(geom);
    return m_this;
  };

  /**
   * Return list of vgl actorss used for rendering.
   *
   * @returns {vgl.actor[]}
   */
  this.actors = function () {
    if (!m_actor) {
      return [];
    }
    return [m_actor];
  };

  /**
   * Build.  Create the necessary elements to render lines.
   *
   * There are several optimizations to do less work when possible.  If only
   * styles have changed, the geometry is not re-transformed.  If styles use
   * static values (rather than functions), they are only calculated once.  If
   * styles have not changed that would affect flags (lineCap, lineJoin, and
   * strokeOffset), the vertex flags are not recomputed -- this helps, as it is
   * a slow step due to most javascript interpreters not optimizing bit
   * operations.
   *
   * @returns {this}
   */
  this._build = function () {
    createGLLines(m_this.dataTime().getMTime() < m_this.buildTime().getMTime() && m_geometry);

    if (!m_this.renderer().contextRenderer().hasActor(m_actor)) {
      m_this.renderer().contextRenderer().addActor(m_actor);
    }
    m_this.buildTime().modified();
    return m_this;
  };

  /**
   * Update.  Rebuild if necessary.
   *
   * @returns {this}
   */
  this._update = function () {
    s_update.call(m_this);

    if (m_this.dataTime().getMTime() >= m_this.buildTime().getMTime() || m_this.updateTime().getMTime() <= m_this.getMTime()) {
      m_this._build();
    }

    m_pixelWidthUnif.set(1.0 / m_this.renderer().width());
    m_aspectUniform.set(m_this.renderer().width() / m_this.renderer().height());
    m_actor.setVisible(m_this.visible());
    m_actor.material().setBinNumber(m_this.bin());
    m_this.updateTime().modified();
    return m_this;
  };

  /**
   * Destroy.  Free used resources.
   */
  this._exit = function () {
    m_this.renderer().contextRenderer().removeActor(m_actor);
    m_actor = null;
    s_exit();
  };

  this._init(arg);
  return this;
};

inherit(gl_lineFeature, lineFeature);

var capabilities = {};
capabilities[lineFeature.capabilities.basic] = true;
capabilities[lineFeature.capabilities.multicolor] = true;

// Now register it
registerFeature('vgl', 'line', gl_lineFeature, capabilities);

module.exports = gl_lineFeature;

/***/ }),
/* 305 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var $ = __webpack_require__(2);
var inherit = __webpack_require__(0);
var registerFeature = __webpack_require__(1).registerFeature;
var pointFeature = __webpack_require__(34);

/**
 * Create a new instance of gl.pointFeature.
 *
 * @class
 * @alias geo.gl.pointFeature
 * @extends geo.pointFeature
 * @param {geo.pointFeature.spec} arg
 * @returns {geo.gl.pointFeature}
 */
var gl_pointFeature = function gl_pointFeature(arg) {
  'use strict';

  if (!(this instanceof gl_pointFeature)) {
    return new gl_pointFeature(arg);
  }
  arg = arg || {};
  pointFeature.call(this, arg);

  var vgl = __webpack_require__(9);
  var transform = __webpack_require__(7);
  var util = __webpack_require__(3);
  var object = __webpack_require__(18);

  object.call(this);

  /**
   * @private
   */
  var m_this = this,
      s_exit = this._exit,
      m_actor = null,
      m_mapper = null,
      m_pixelWidthUniform = null,
      m_aspectUniform = null,
      m_dynamicDraw = arg.dynamicDraw === undefined ? false : arg.dynamicDraw,

  /* If you are drawing very large points, you will often get better
   * performance using a different primitiveShape.  The 'sprite' shape uses
   * the least memory, but has hardware-specific limitations to its size.
   * 'triangle' seems to be fastest on low-powered hardware, but 'square'
   * visits fewer fragments. */
  m_primitiveShape = 'sprite',
      // arg can change this, below
  s_init = this._init,
      s_update = this._update,
      s_updateStyleFromArray = this.updateStyleFromArray,
      vertexShaderSource = null,
      fragmentShaderSource = null;

  if (arg.primitiveShape === 'triangle' || arg.primitiveShape === 'square' || arg.primitiveShape === 'sprite') {
    m_primitiveShape = arg.primitiveShape;
  }

  vertexShaderSource = ['#ifdef GL_ES', '  precision highp float;', '#endif', 'attribute vec3 pos;', 'attribute float radius;', 'attribute vec3 fillColor;', 'attribute vec3 strokeColor;', 'attribute float fillOpacity;', 'attribute float strokeWidth;', 'attribute float strokeOpacity;', 'attribute float fill;', 'attribute float stroke;', 'uniform float pixelWidth;', 'uniform float aspect;', 'uniform mat4 modelViewMatrix;', 'uniform mat4 projectionMatrix;', 'varying vec4 fillColorVar;', 'varying vec4 strokeColorVar;', 'varying float radiusVar;', 'varying float strokeWidthVar;', 'varying float fillVar;', 'varying float strokeVar;'];

  if (m_primitiveShape !== 'sprite') {
    vertexShaderSource = vertexShaderSource.concat(['attribute vec2 unit;', 'varying vec3 unitVar;']);
  }

  vertexShaderSource.push.apply(vertexShaderSource, ['void main(void)', '{', '  strokeWidthVar = strokeWidth;', '  // No stroke or fill implies nothing to draw', '  if (stroke < 1.0 || strokeWidth <= 0.0 || strokeOpacity <= 0.0) {', '    strokeVar = 0.0;', '    strokeWidthVar = 0.0;', '  }', '  else', '    strokeVar = 1.0;', '  if (fill < 1.0 || radius <= 0.0 || fillOpacity <= 0.0)', '    fillVar = 0.0;', '  else', '    fillVar = 1.0;',
  /* If the point has no visible pixels, skip doing computations on it. */
  '  if (fillVar == 0.0 && strokeVar == 0.0) {', '    gl_Position = vec4(2, 2, 0, 1);', '    return;', '  }', '  fillColorVar = vec4 (fillColor, fillOpacity);', '  strokeColorVar = vec4 (strokeColor, strokeOpacity);', '  radiusVar = radius;']);

  if (m_primitiveShape === 'sprite') {
    vertexShaderSource.push.apply(vertexShaderSource, ['  gl_Position = (projectionMatrix * modelViewMatrix * vec4(pos, 1.0)).xyzw;', '  gl_PointSize = 2.0 * (radius + strokeWidthVar); ', '}']);
  } else {
    vertexShaderSource.push.apply(vertexShaderSource, ['  unitVar = vec3 (unit, 1.0);', '  vec4 p = (projectionMatrix * modelViewMatrix * vec4(pos, 1.0)).xyzw;', '  if (p.w != 0.0) {', '    p = p / p.w;', '  }', '  p += (radius + strokeWidthVar) * ', '       vec4 (unit.x * pixelWidth, unit.y * pixelWidth * aspect, 0.0, 1.0);', '  gl_Position = vec4(p.xyz, 1.0);', '}']);
  }
  vertexShaderSource = vertexShaderSource.join('\n');

  fragmentShaderSource = ['#ifdef GL_ES', '  precision highp float;', '#endif', 'uniform float aspect;', 'varying vec4 fillColorVar;', 'varying vec4 strokeColorVar;', 'varying float radiusVar;', 'varying float strokeWidthVar;', 'varying float fillVar;', 'varying float strokeVar;'];

  if (m_primitiveShape !== 'sprite') {
    fragmentShaderSource.push('varying vec3 unitVar;');
  }

  fragmentShaderSource.push.apply(fragmentShaderSource, ['void main () {', '  vec4 strokeColor, fillColor;', '  float endStep;', '  // No stroke or fill implies nothing to draw', '  if (fillVar == 0.0 && strokeVar == 0.0)', '    discard;']);

  if (m_primitiveShape === 'sprite') {
    fragmentShaderSource.push('  float rad = 2.0 * length (gl_PointCoord - vec2(0.5));');
  } else {
    fragmentShaderSource.push('  float rad = length (unitVar.xy);');
  }

  fragmentShaderSource.push.apply(fragmentShaderSource, ['  if (rad > 1.0)', '    discard;', '  // If there is no stroke, the fill region should transition to nothing', '  if (strokeVar == 0.0) {', '    strokeColor = vec4 (fillColorVar.rgb, 0.0);', '    endStep = 1.0;', '  } else {', '    strokeColor = strokeColorVar;', '    endStep = radiusVar / (radiusVar + strokeWidthVar);', '  }', '  // Likewise, if there is no fill, the stroke should transition to nothing', '  if (fillVar == 0.0)', '    fillColor = vec4 (strokeColor.rgb, 0.0);', '  else', '    fillColor = fillColorVar;', '  // Distance to antialias over', '  float antialiasDist = 3.0 / (2.0 * radiusVar);', '  if (rad < endStep) {', '    float step = smoothstep (endStep - antialiasDist, endStep, rad);', '    gl_FragColor = mix (fillColor, strokeColor, step);', '  } else {', '    float step = smoothstep (1.0 - antialiasDist, 1.0, rad);', '    gl_FragColor = mix (strokeColor, vec4 (strokeColor.rgb, 0.0), step);', '  }', '}']);

  fragmentShaderSource = fragmentShaderSource.join('\n');

  /**
   * Create the vertex shader for points.
   *
   * @returns {vgl.shader}
   */
  function createVertexShader() {
    var shader = new vgl.shader(vgl.GL.VERTEX_SHADER);
    shader.setShaderSource(vertexShaderSource);
    return shader;
  }

  /**
   * Create the fragment shader for points.
   *
   * @returns {vgl.shader}
   */
  function createFragmentShader() {
    var shader = new vgl.shader(vgl.GL.FRAGMENT_SHADER);
    shader.setShaderSource(fragmentShaderSource);
    return shader;
  }

  /**
   * Given the current primitive shape and a basic size, return a set of
   * vertices that can be used for a generic point.
   *
   * @param {number} x The base x coordinate.  Usually 0.
   * @param {number} y The base y coordinate.  Usually 0.
   * @param {number} w The base width.  Usually 1.
   * @param {number} h The base height.  Usually 1.
   * @returns {number[]} A flat array of vertices in the form of
   *    `[x0, y0, x1, y1, ...]`.
   */
  function pointPolygon(x, y, w, h) {
    var verts;
    switch (m_primitiveShape) {
      case 'triangle':
        /* Use an equilateral triangle.  While this has 30% more area than a
         * square, the reduction in vertices should help more than the
         * processing the additional fragments. */
        verts = [x, y - h * 2, x - w * Math.sqrt(3.0), y + h, x + w * Math.sqrt(3.0), y + h];
        break;
      case 'sprite':
        /* Point sprite uses only one vertex per point. */
        verts = [x, y];
        break;
      default:
        // "square"
        /* Use a surrounding square split diagonally into two triangles. */
        verts = [x - w, y + h, x - w, y - h, x + w, y + h, x - w, y - h, x + w, y - h, x + w, y + h];
        break;
    }
    return verts;
  }

  /**
   * Create and style the data needed to render the points.
   */
  function createGLPoints() {
    // unit and associated data is not used when drawing sprite
    var i,
        j,
        numPts = m_this.data().length,
        unit = pointPolygon(0, 0, 1, 1),
        position = new Array(numPts * 3),
        posBuf,
        posVal,
        posFunc,
        unitBuf,
        indices,
        radius,
        radiusVal,
        radFunc,
        stroke,
        strokeVal,
        strokeFunc,
        strokeWidth,
        strokeWidthVal,
        strokeWidthFunc,
        strokeOpacity,
        strokeOpacityVal,
        strokeOpacityFunc,
        strokeColor,
        strokeColorVal,
        strokeColorFunc,
        fill,
        fillVal,
        fillFunc,
        fillOpacity,
        fillOpacityVal,
        fillOpacityFunc,
        fillColor,
        fillColorVal,
        fillColorFunc,
        vpf = m_this.verticesPerFeature(),
        data = m_this.data(),
        item,
        ivpf,
        ivpf3,
        iunit,
        i3,
        geom = m_mapper.geometryData(),
        nonzeroZ;

    posFunc = m_this.position();
    radFunc = m_this.style.get('radius');
    strokeFunc = m_this.style.get('stroke');
    strokeWidthFunc = m_this.style.get('strokeWidth');
    strokeOpacityFunc = m_this.style.get('strokeOpacity');
    strokeColorFunc = m_this.style.get('strokeColor');
    fillFunc = m_this.style.get('fill');
    fillOpacityFunc = m_this.style.get('fillOpacity');
    fillColorFunc = m_this.style.get('fillColor');

    /* It is more efficient to do a transform on a single array rather than on
     * an array of arrays or an array of objects. */
    for (i = i3 = 0; i < numPts; i += 1, i3 += 3) {
      posVal = posFunc(data[i], i);
      position[i3] = posVal.x;
      position[i3 + 1] = posVal.y;
      position[i3 + 2] = posVal.z || 0;
      nonzeroZ = nonzeroZ || position[i3 + 2];
    }
    position = transform.transformCoordinates(m_this.gcs(), m_this.layer().map().gcs(), position, 3);
    /* Some transforms modify the z-coordinate.  If we started with all zero z
     * coordinates, don't modify them.  This could be changed if the
     * z-coordinate space of the gl cube is scaled appropriately. */
    if (!nonzeroZ && m_this.gcs() !== m_this.layer().map().gcs()) {
      for (i = i3 = 0; i < numPts; i += 1, i3 += 3) {
        position[i3 + 2] = 0;
      }
    }

    posBuf = util.getGeomBuffer(geom, 'pos', vpf * numPts * 3);

    if (m_primitiveShape !== 'sprite') {
      unitBuf = util.getGeomBuffer(geom, 'unit', vpf * numPts * 2);
    }

    radius = util.getGeomBuffer(geom, 'radius', vpf * numPts);
    stroke = util.getGeomBuffer(geom, 'stroke', vpf * numPts);
    strokeWidth = util.getGeomBuffer(geom, 'strokeWidth', vpf * numPts);
    strokeOpacity = util.getGeomBuffer(geom, 'strokeOpacity', vpf * numPts);
    strokeColor = util.getGeomBuffer(geom, 'strokeColor', vpf * numPts * 3);
    fill = util.getGeomBuffer(geom, 'fill', vpf * numPts);
    fillOpacity = util.getGeomBuffer(geom, 'fillOpacity', vpf * numPts);
    fillColor = util.getGeomBuffer(geom, 'fillColor', vpf * numPts * 3);
    indices = geom.primitive(0).indices();
    if (!(indices instanceof Uint16Array) || indices.length !== vpf * numPts) {
      indices = new Uint16Array(vpf * numPts);
      geom.primitive(0).setIndices(indices);
    }

    for (i = ivpf = ivpf3 = iunit = i3 = 0; i < numPts; i += 1, i3 += 3) {
      item = data[i];
      if (m_primitiveShape !== 'sprite') {
        for (j = 0; j < unit.length; j += 1, iunit += 1) {
          unitBuf[iunit] = unit[j];
        }
      }
      /* We can ignore the indicies (they will all be zero) */
      radiusVal = radFunc(item, i);
      strokeVal = strokeFunc(item, i) ? 1.0 : 0.0;
      strokeWidthVal = strokeWidthFunc(item, i);
      strokeOpacityVal = strokeOpacityFunc(item, i);
      strokeColorVal = strokeColorFunc(item, i);
      fillVal = fillFunc(item, i) ? 1.0 : 0.0;
      fillOpacityVal = fillOpacityFunc(item, i);
      fillColorVal = fillColorFunc(item, i);
      for (j = 0; j < vpf; j += 1, ivpf += 1, ivpf3 += 3) {
        posBuf[ivpf3] = position[i3];
        posBuf[ivpf3 + 1] = position[i3 + 1];
        posBuf[ivpf3 + 2] = position[i3 + 2];
        radius[ivpf] = radiusVal;
        stroke[ivpf] = strokeVal;
        strokeWidth[ivpf] = strokeWidthVal;
        strokeOpacity[ivpf] = strokeOpacityVal;
        strokeColor[ivpf3] = strokeColorVal.r;
        strokeColor[ivpf3 + 1] = strokeColorVal.g;
        strokeColor[ivpf3 + 2] = strokeColorVal.b;
        fill[ivpf] = fillVal;
        fillOpacity[ivpf] = fillOpacityVal;
        fillColor[ivpf3] = fillColorVal.r;
        fillColor[ivpf3 + 1] = fillColorVal.g;
        fillColor[ivpf3 + 2] = fillColorVal.b;
      }
    }

    geom.boundsDirty(true);
    m_mapper.modified();
    m_mapper.boundsDirtyTimestamp().modified();
  }

  /**
   * Return list of vgl actorss used for rendering.
   *
   * @returns {vgl.actor[]}
   */
  this.actors = function () {
    if (!m_actor) {
      return [];
    }
    return [m_actor];
  };

  /**
   * Return the number of vertices used for each point.
   *
   * @returns {number}
   */
  this.verticesPerFeature = function () {
    var unit = pointPolygon(0, 0, 1, 1);
    return unit.length / 2;
  };

  /**
   * Set style(s) from array(s).  For each style, the array should have one
   * value per data item.  The values are not converted or validated.  Color
   * values are {@link geo.geoColorObject} objects.  If invalid values are
   * given the behavior is undefined.
   *   For some feature styles, if the first entry of an array is itself an
   * array, then each entry of the array is expected to be an array, and values
   * are used from these subarrays.  This allows a style to apply, for
   * instance, per vertex of a data item rather than per data item.
   *
   * @param {string|object} keyOrObject Either the name of a single style or
   *    an object where the keys are the names of styles and the values are
   *    each arrays.
   * @param {array} styleArray If keyOrObject is a string, an array of values
   *    for the style.  If keyOrObject is an object, this parameter is ignored.
   * @param {boolean} [refresh=false] `true` to redraw the feature when it has
   *    been updated.  If an object with styles is passed, the redraw is only
   *    done once.
   * @returns {this}
   */
  this.updateStyleFromArray = function (keyOrObject, styleArray, refresh) {
    var bufferedKeys = {
      fill: 'bool',
      fillColor: 3,
      fillOpacity: 1,
      radius: 1,
      stroke: 'bool',
      strokeColor: 3,
      strokeOpacity: 1,
      strokeWidth: 1
    };
    var needsRefresh, needsRender;
    if (typeof keyOrObject === 'string') {
      var obj = {};
      obj[keyOrObject] = styleArray;
      keyOrObject = obj;
    }
    $.each(keyOrObject, function (key, styleArray) {
      if (m_this.visible() && m_actor && bufferedKeys[key] && !needsRefresh && !m_this.clustering()) {
        var vpf, mapper, buffer, numPts, value, i, j, v, bpv;
        bpv = bufferedKeys[key] === 'bool' ? 1 : bufferedKeys[key];
        numPts = m_this.data().length;
        mapper = m_actor.mapper();
        buffer = mapper.getSourceBuffer(key);
        vpf = m_this.verticesPerFeature();
        if (!buffer || !numPts || numPts * vpf * bpv !== buffer.length) {
          needsRefresh = true;
        } else {
          switch (bufferedKeys[key]) {
            case 1:
              for (i = 0, v = 0; i < numPts; i += 1) {
                value = styleArray[i];
                for (j = 0; j < vpf; j += 1, v += 1) {
                  buffer[v] = value;
                }
              }
              break;
            case 3:
              for (i = 0, v = 0; i < numPts; i += 1) {
                value = styleArray[i];
                for (j = 0; j < vpf; j += 1, v += 3) {
                  buffer[v] = value.r;
                  buffer[v + 1] = value.g;
                  buffer[v + 2] = value.b;
                }
              }
              break;
            case 'bool':
              for (i = 0, v = 0; i < numPts; i += 1) {
                value = styleArray[i] ? 1.0 : 0.0;
                for (j = 0; j < vpf; j += 1, v += 1) {
                  buffer[v] = value;
                }
              }
              break;
          }
          mapper.updateSourceBuffer(key);
          /* This could probably be even faster than calling _render after
           * updating the buffer, if the context's buffer was bound and
           * updated.  This would requiring knowing the webgl context and
           * probably the source to buffer mapping. */
          needsRender = true;
        }
      } else {
        needsRefresh = true;
      }
      s_updateStyleFromArray(key, styleArray, false);
    });
    if (m_this.visible() && needsRefresh) {
      m_this.draw();
    } else if (needsRender) {
      m_this.renderer()._render();
    }
    return m_this;
  };

  /**
   * Initialize.
   */
  this._init = function () {
    var prog = vgl.shaderProgram(),
        vertexShader = createVertexShader(),
        fragmentShader = createFragmentShader(),
        posAttr = vgl.vertexAttribute('pos'),
        unitAttr = vgl.vertexAttribute('unit'),
        radAttr = vgl.vertexAttribute('radius'),
        strokeWidthAttr = vgl.vertexAttribute('strokeWidth'),
        fillColorAttr = vgl.vertexAttribute('fillColor'),
        fillAttr = vgl.vertexAttribute('fill'),
        strokeColorAttr = vgl.vertexAttribute('strokeColor'),
        strokeAttr = vgl.vertexAttribute('stroke'),
        fillOpacityAttr = vgl.vertexAttribute('fillOpacity'),
        strokeOpacityAttr = vgl.vertexAttribute('strokeOpacity'),
        modelViewUniform = new vgl.modelViewUniform('modelViewMatrix'),
        projectionUniform = new vgl.projectionUniform('projectionMatrix'),
        mat = vgl.material(),
        blend = vgl.blend(),
        geom = vgl.geometryData(),
        sourcePositions = vgl.sourceDataP3fv({ 'name': 'pos' }),
        sourceUnits = vgl.sourceDataAnyfv(2, vgl.vertexAttributeKeysIndexed.One, { 'name': 'unit' }),
        sourceRadius = vgl.sourceDataAnyfv(1, vgl.vertexAttributeKeysIndexed.Two, { 'name': 'radius' }),
        sourceStrokeWidth = vgl.sourceDataAnyfv(1, vgl.vertexAttributeKeysIndexed.Three, { 'name': 'strokeWidth' }),
        sourceFillColor = vgl.sourceDataAnyfv(3, vgl.vertexAttributeKeysIndexed.Four, { 'name': 'fillColor' }),
        sourceFill = vgl.sourceDataAnyfv(1, vgl.vertexAttributeKeysIndexed.Five, { 'name': 'fill' }),
        sourceStrokeColor = vgl.sourceDataAnyfv(3, vgl.vertexAttributeKeysIndexed.Six, { 'name': 'strokeColor' }),
        sourceStroke = vgl.sourceDataAnyfv(1, vgl.vertexAttributeKeysIndexed.Seven, { 'name': 'stroke' }),
        sourceAlpha = vgl.sourceDataAnyfv(1, vgl.vertexAttributeKeysIndexed.Eight, { 'name': 'fillOpacity' }),
        sourceStrokeOpacity = vgl.sourceDataAnyfv(1, vgl.vertexAttributeKeysIndexed.Nine, { 'name': 'strokeOpacity' }),
        primitive = new vgl.triangles();

    if (m_primitiveShape === 'sprite') {
      primitive = new vgl.points();
    }

    m_pixelWidthUniform = new vgl.floatUniform('pixelWidth', 2.0 / m_this.renderer().width());
    m_aspectUniform = new vgl.floatUniform('aspect', m_this.renderer().width() / m_this.renderer().height());

    s_init.call(m_this, arg);
    m_mapper = vgl.mapper({ dynamicDraw: m_dynamicDraw });

    // TODO: Right now this is ugly but we will fix it.
    prog.addVertexAttribute(posAttr, vgl.vertexAttributeKeys.Position);
    if (m_primitiveShape !== 'sprite') {
      prog.addVertexAttribute(unitAttr, vgl.vertexAttributeKeysIndexed.One);
    }

    prog.addVertexAttribute(radAttr, vgl.vertexAttributeKeysIndexed.Two);
    prog.addVertexAttribute(strokeWidthAttr, vgl.vertexAttributeKeysIndexed.Three);
    prog.addVertexAttribute(fillColorAttr, vgl.vertexAttributeKeysIndexed.Four);
    prog.addVertexAttribute(fillAttr, vgl.vertexAttributeKeysIndexed.Five);
    prog.addVertexAttribute(strokeColorAttr, vgl.vertexAttributeKeysIndexed.Six);
    prog.addVertexAttribute(strokeAttr, vgl.vertexAttributeKeysIndexed.Seven);
    prog.addVertexAttribute(fillOpacityAttr, vgl.vertexAttributeKeysIndexed.Eight);
    prog.addVertexAttribute(strokeOpacityAttr, vgl.vertexAttributeKeysIndexed.Nine);

    prog.addUniform(m_pixelWidthUniform);
    prog.addUniform(m_aspectUniform);
    prog.addUniform(modelViewUniform);
    prog.addUniform(projectionUniform);

    prog.addShader(fragmentShader);
    prog.addShader(vertexShader);

    mat.addAttribute(prog);
    mat.addAttribute(blend);

    m_actor = vgl.actor();
    m_actor.setMaterial(mat);
    m_actor.setMapper(m_mapper);

    geom.addSource(sourcePositions);
    geom.addSource(sourceUnits);
    geom.addSource(sourceRadius);
    geom.addSource(sourceStrokeWidth);
    geom.addSource(sourceFillColor);
    geom.addSource(sourceFill);
    geom.addSource(sourceStrokeColor);
    geom.addSource(sourceStroke);
    geom.addSource(sourceAlpha);
    geom.addSource(sourceStrokeOpacity);
    geom.addPrimitive(primitive);
    m_mapper.setGeometryData(geom);
  };

  /**
   * Build.  Create the necessary elements to render points.
   *
   * @returns {this}
   */
  this._build = function () {

    if (m_actor) {
      m_this.renderer().contextRenderer().removeActor(m_actor);
    }

    createGLPoints();

    m_this.renderer().contextRenderer().addActor(m_actor);
    m_this.renderer().contextRenderer().render();
    m_this.buildTime().modified();
    return m_this;
  };

  /**
   * Update.  Rebuild if necessary.
   *
   * @returns {this}
   */
  this._update = function () {

    s_update.call(m_this);

    // For now build if the data or style changes. In the future we may
    // we able to partially update the data using dynamic gl buffers.
    if (m_this.dataTime().getMTime() >= m_this.buildTime().getMTime() || m_this.updateTime().getMTime() < m_this.getMTime()) {
      m_this._build();
    }

    // Update uniforms
    m_pixelWidthUniform.set(2.0 / m_this.renderer().width());
    m_aspectUniform.set(m_this.renderer().width() / m_this.renderer().height());

    m_actor.setVisible(m_this.visible());
    m_actor.material().setBinNumber(m_this.bin());

    m_this.updateTime().modified();
    return m_this;
  };

  /**
   * Destroy.  Free used resources.
   */
  this._exit = function () {
    m_this.renderer().contextRenderer().removeActor(m_actor);
    m_actor = null;
    s_exit();
  };

  m_this._init();
  return this;
};

inherit(gl_pointFeature, pointFeature);

// Now register it
registerFeature('vgl', 'point', gl_pointFeature);

module.exports = gl_pointFeature;

/***/ }),
/* 306 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var inherit = __webpack_require__(0);
var registerFeature = __webpack_require__(1).registerFeature;
var polygonFeature = __webpack_require__(50);

/**
 * Create a new instance of gl.polygonFeature.
 *
 * @class
 * @alias geo.gl.polygonFeature
 * @extends geo.polygonFeature
 * @param {geo.polygonFeature.spec} arg
 * @returns {geo.gl.polygonFeature}
 */
var gl_polygonFeature = function gl_polygonFeature(arg) {
  'use strict';

  if (!(this instanceof gl_polygonFeature)) {
    return new gl_polygonFeature(arg);
  }
  arg = arg || {};
  polygonFeature.call(this, arg);

  var vgl = __webpack_require__(9);
  var earcut = __webpack_require__(307);
  var transform = __webpack_require__(7);
  var util = __webpack_require__(3);
  var object = __webpack_require__(18);

  object.call(this);

  /**
   * @private
   */
  var m_this = this,
      s_exit = this._exit,
      m_actor = vgl.actor(),
      m_mapper = vgl.mapper(),
      m_material = vgl.material(),
      m_geometry,
      s_init = this._init,
      s_update = this._update,
      m_builtOnce,
      m_updateAnimFrameRef;

  function createVertexShader() {
    var vertexShaderSource = ['attribute vec3 pos;', 'attribute vec3 fillColor;', 'attribute float fillOpacity;', 'uniform mat4 modelViewMatrix;', 'uniform mat4 projectionMatrix;', 'varying vec4 fillColorVar;', 'void main(void)', '{', '  vec4 clipPos = projectionMatrix * modelViewMatrix * vec4(pos.xyz, 1);', '  if (clipPos.w != 0.0) {', '    clipPos = clipPos/clipPos.w;', '  }', '  fillColorVar = vec4(fillColor, fillOpacity);', '  gl_Position = clipPos;', '}'].join('\n'),
        shader = new vgl.shader(vgl.GL.VERTEX_SHADER);
    shader.setShaderSource(vertexShaderSource);
    return shader;
  }

  function createFragmentShader() {
    var fragmentShaderSource = ['#ifdef GL_ES', '  precision highp float;', '#endif', 'varying vec4 fillColorVar;', 'void main () {', '  gl_FragColor = fillColorVar;', '}'].join('\n'),
        shader = new vgl.shader(vgl.GL.FRAGMENT_SHADER);
    shader.setShaderSource(fragmentShaderSource);
    return shader;
  }

  /**
   * Create and style the triangles needed to render the polygons.
   *
   * There are several optimizations to do less work when possible.  If only
   * styles have changed, the triangulation is not recomputed, nor is the
   * geometry re-transformed.  If styles use static values (rather than
   * functions), they are only calculated once.  If a polygon reports that it
   * has a uniform style, then styles are only calculated once for that polygon
   * (the uniform property may be different per polygon or per update).
   * Array.map is slower in Chrome that using a loop, so loops are used in
   * places that would be conceptually served by maps.
   *
   * @param {boolean} onlyStyle if true, use the existing geoemtry and just
   *    recalculate the style.
   */
  function createGLPolygons(onlyStyle) {
    var posBuf,
        posFunc,
        polyFunc,
        fillColor,
        fillColorFunc,
        fillColorVal,
        fillOpacity,
        fillOpacityFunc,
        fillOpacityVal,
        fillFunc,
        fillVal,
        uniformPolyFunc,
        uniform,
        indices,
        items = [],
        target_gcs = m_this.gcs(),
        map_gcs = m_this.layer().map().gcs(),
        numPts = 0,
        geom = m_mapper.geometryData(),
        color,
        opacity,
        fill,
        d,
        d3,
        vertices,
        i,
        j,
        k,
        n,
        record,
        item,
        itemIndex,
        original;

    fillColorFunc = m_this.style.get('fillColor');
    fillColorVal = util.isFunction(m_this.style('fillColor')) ? undefined : fillColorFunc();
    fillOpacityFunc = m_this.style.get('fillOpacity');
    fillOpacityVal = util.isFunction(m_this.style('fillOpacity')) ? undefined : fillOpacityFunc();
    fillFunc = m_this.style.get('fill');
    fillVal = util.isFunction(m_this.style('fill')) ? undefined : fillFunc();
    uniformPolyFunc = m_this.style.get('uniformPolygon');

    if (!onlyStyle) {
      posFunc = m_this.style.get('position');
      polyFunc = m_this.style.get('polygon');
      m_this.data().forEach(function (item, itemIndex) {
        var polygon, outer, geometry, c;

        polygon = polyFunc(item, itemIndex);
        if (!polygon) {
          return;
        }
        outer = polygon.outer || (Array.isArray(polygon) ? polygon : []);
        if (outer.length < 3) {
          return;
        }

        /* expand to an earcut polygon geometry.  We had been using a map call,
         * but using loops is much faster in Chrome (4 versus 33 ms for one
         * test). */
        geometry = new Array(outer.length * 3);
        for (i = d3 = 0; i < outer.length; i += 1, d3 += 3) {
          c = posFunc(outer[i], i, item, itemIndex);
          geometry[d3] = c.x;
          geometry[d3 + 1] = c.y;
          geometry[d3 + 2] = c.z || 0;
        }
        geometry = { vertices: geometry, dimensions: 3, holes: [] };
        original = outer;

        if (polygon.inner) {
          polygon.inner.forEach(function (hole) {
            if (hole.length < 3) {
              return;
            }
            original = original.concat(hole);
            geometry.holes.push(d3 / 3);
            for (i = 0; i < hole.length; i += 1, d3 += 3) {
              c = posFunc(hole[i], i, item, itemIndex);
              geometry.vertices[d3] = c.x;
              geometry.vertices[d3 + 1] = c.y;
              geometry.vertices[d3 + 2] = c.z || 0;
            }
          });
        }

        // transform to map gcs
        geometry.vertices = transform.transformCoordinates(target_gcs, map_gcs, geometry.vertices, geometry.dimensions);

        record = {
          // triangulate
          triangles: earcut(geometry.vertices, geometry.holes, geometry.dimensions),
          vertices: geometry.vertices,
          original: original,
          item: item,
          itemIndex: itemIndex
        };
        if (record.triangles.length) {
          items.push(record);
          numPts += record.triangles.length;
        }
      });
      posBuf = util.getGeomBuffer(geom, 'pos', numPts * 3);
      indices = geom.primitive(0).indices();
      if (!(indices instanceof Uint16Array) || indices.length !== numPts) {
        indices = new Uint16Array(numPts);
        geom.primitive(0).setIndices(indices);
      }
      m_geometry = { items: items, numPts: numPts };
    } else {
      items = m_geometry.items;
      numPts = m_geometry.numPts;
    }
    fillColor = util.getGeomBuffer(geom, 'fillColor', numPts * 3);
    fillOpacity = util.getGeomBuffer(geom, 'fillOpacity', numPts);
    d = d3 = 0;
    color = fillColorVal;
    fill = fillVal;
    for (k = 0; k < items.length; k += 1) {
      n = items[k].triangles.length;
      vertices = items[k].vertices;
      item = items[k].item;
      itemIndex = items[k].itemIndex;
      original = items[k].original;
      uniform = uniformPolyFunc(item, itemIndex);
      opacity = fillOpacityVal;
      if (uniform) {
        if (fillColorVal === undefined) {
          color = fillColorFunc(vertices[0], 0, item, itemIndex);
        }
        if (fillOpacityVal === undefined) {
          opacity = fillOpacityFunc(vertices[0], 0, item, itemIndex);
        }
      }
      if (fillVal === undefined) {
        fill = fillFunc(item, itemIndex);
      }
      if (!fill) {
        opacity = 0;
      }
      if (uniform && onlyStyle && items[k].uniform && items[k].color && color.r === items[k].color.r && color.g === items[k].color.g && color.b === items[k].color.b && opacity === items[k].opacity) {
        d += n;
        d3 += n * 3;
        continue;
      }
      for (i = 0; i < n; i += 1, d += 1, d3 += 3) {
        if (onlyStyle && uniform) {
          fillColor[d3] = color.r;
          fillColor[d3 + 1] = color.g;
          fillColor[d3 + 2] = color.b;
          fillOpacity[d] = opacity;
        } else {
          j = items[k].triangles[i] * 3;
          if (!onlyStyle) {
            posBuf[d3] = vertices[j];
            posBuf[d3 + 1] = vertices[j + 1];
            posBuf[d3 + 2] = vertices[j + 2];
            indices[d] = i;
          }
          if (!uniform && fillColorVal === undefined) {
            color = fillColorFunc(original[j], j, item, itemIndex);
          }
          fillColor[d3] = color.r;
          fillColor[d3 + 1] = color.g;
          fillColor[d3 + 2] = color.b;
          if (!uniform && fillOpacityVal === undefined) {
            opacity = fillOpacityFunc(original[j], j, item, itemIndex);
          }
          fillOpacity[d] = opacity;
        }
      }
      if (uniform || items[k].uniform) {
        items[k].uniform = uniform;
        items[k].color = color;
        items[k].opacity = opacity;
      }
    }
    m_mapper.modified();
    if (!onlyStyle) {
      geom.boundsDirty(true);
      m_mapper.boundsDirtyTimestamp().modified();
    }
  }

  /**
   * Initialize.
   *
   * @param {geo.polygonFeature.spec} arg An object with options for the
   *    feature.
   */
  this._init = function (arg) {
    var prog = vgl.shaderProgram(),
        posAttr = vgl.vertexAttribute('pos'),
        fillColorAttr = vgl.vertexAttribute('fillColor'),
        fillOpacityAttr = vgl.vertexAttribute('fillOpacity'),
        modelViewUniform = new vgl.modelViewUniform('modelViewMatrix'),
        projectionUniform = new vgl.projectionUniform('projectionMatrix'),
        vertexShader = createVertexShader(),
        fragmentShader = createFragmentShader(),
        blend = vgl.blend(),
        geom = vgl.geometryData(),
        sourcePositions = vgl.sourceDataP3fv({ 'name': 'pos' }),
        sourceFillColor = vgl.sourceDataAnyfv(3, vgl.vertexAttributeKeysIndexed.Two, { 'name': 'fillColor' }),
        sourceFillOpacity = vgl.sourceDataAnyfv(1, vgl.vertexAttributeKeysIndexed.Three, { 'name': 'fillOpacity' }),
        trianglePrimitive = vgl.triangles();

    prog.addVertexAttribute(posAttr, vgl.vertexAttributeKeys.Position);
    prog.addVertexAttribute(fillColorAttr, vgl.vertexAttributeKeysIndexed.Two);
    prog.addVertexAttribute(fillOpacityAttr, vgl.vertexAttributeKeysIndexed.Three);

    prog.addUniform(modelViewUniform);
    prog.addUniform(projectionUniform);

    prog.addShader(fragmentShader);
    prog.addShader(vertexShader);

    m_material.addAttribute(prog);
    m_material.addAttribute(blend);

    m_actor.setMaterial(m_material);
    m_actor.setMapper(m_mapper);

    geom.addSource(sourcePositions);
    geom.addSource(sourceFillColor);
    geom.addSource(sourceFillOpacity);
    geom.addPrimitive(trianglePrimitive);
    m_mapper.setGeometryData(geom);

    s_init.call(m_this, arg);
  };

  /**
   * Build.
   *
   * @override
   */
  this._build = function () {
    createGLPolygons(m_this.dataTime().getMTime() < m_this.buildTime().getMTime() && m_geometry);

    if (!m_this.renderer().contextRenderer().hasActor(m_actor)) {
      m_this.renderer().contextRenderer().addActor(m_actor);
      m_builtOnce = true;
    }
    m_this.buildTime().modified();
  };

  /**
   * Update.
   *
   * @param {object} [opts] Update options.
   * @param {boolean} [opts.mayDelay] If truthy, wait until the next animation
   *    frame for the update.
   */
  this._update = function (opts) {
    if (!m_this.ready) {
      return;
    }
    if (opts && opts.mayDelay && m_builtOnce) {
      m_updateAnimFrameRef = m_this.layer().map().scheduleAnimationFrame(m_this._update);
      return;
    }
    if (m_updateAnimFrameRef) {
      m_this.layer().map().scheduleAnimationFrame(m_this._update, 'remove');
      m_updateAnimFrameRef = null;
    }
    s_update.call(m_this);

    if (m_this.dataTime().getMTime() >= m_this.buildTime().getMTime() || m_this.updateTime().getMTime() <= m_this.getMTime()) {
      m_this._build();
    }

    m_actor.setVisible(m_this.visible());
    m_actor.material().setBinNumber(m_this.bin());
    m_this.updateTime().modified();
  };

  /**
   * Destroy.
   */
  this._exit = function () {
    if (m_updateAnimFrameRef && m_this.layer()) {
      m_this.layer().map().scheduleAnimationFrame(m_this._update, 'remove');
      m_updateAnimFrameRef = null;
    }
    m_this.renderer().contextRenderer().removeActor(m_actor);
    s_exit();
  };

  this._init(arg);
  return this;
};

inherit(gl_polygonFeature, polygonFeature);

// Now register it
registerFeature('vgl', 'polygon', gl_polygonFeature);
module.exports = gl_polygonFeature;

/***/ }),
/* 307 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


module.exports = earcut;
module.exports.default = earcut;

function earcut(data, holeIndices, dim) {

    dim = dim || 2;

    var hasHoles = holeIndices && holeIndices.length,
        outerLen = hasHoles ? holeIndices[0] * dim : data.length,
        outerNode = linkedList(data, 0, outerLen, dim, true),
        triangles = [];

    if (!outerNode) return triangles;

    var minX, minY, maxX, maxY, x, y, invSize;

    if (hasHoles) outerNode = eliminateHoles(data, holeIndices, outerNode, dim);

    // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox
    if (data.length > 80 * dim) {
        minX = maxX = data[0];
        minY = maxY = data[1];

        for (var i = dim; i < outerLen; i += dim) {
            x = data[i];
            y = data[i + 1];
            if (x < minX) minX = x;
            if (y < minY) minY = y;
            if (x > maxX) maxX = x;
            if (y > maxY) maxY = y;
        }

        // minX, minY and invSize are later used to transform coords into integers for z-order calculation
        invSize = Math.max(maxX - minX, maxY - minY);
        invSize = invSize !== 0 ? 1 / invSize : 0;
    }

    earcutLinked(outerNode, triangles, dim, minX, minY, invSize);

    return triangles;
}

// create a circular doubly linked list from polygon points in the specified winding order
function linkedList(data, start, end, dim, clockwise) {
    var i, last;

    if (clockwise === (signedArea(data, start, end, dim) > 0)) {
        for (i = start; i < end; i += dim) last = insertNode(i, data[i], data[i + 1], last);
    } else {
        for (i = end - dim; i >= start; i -= dim) last = insertNode(i, data[i], data[i + 1], last);
    }

    if (last && equals(last, last.next)) {
        removeNode(last);
        last = last.next;
    }

    return last;
}

// eliminate colinear or duplicate points
function filterPoints(start, end) {
    if (!start) return start;
    if (!end) end = start;

    var p = start,
        again;
    do {
        again = false;

        if (!p.steiner && (equals(p, p.next) || area(p.prev, p, p.next) === 0)) {
            removeNode(p);
            p = end = p.prev;
            if (p === p.next) break;
            again = true;

        } else {
            p = p.next;
        }
    } while (again || p !== end);

    return end;
}

// main ear slicing loop which triangulates a polygon (given as a linked list)
function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) {
    if (!ear) return;

    // interlink polygon nodes in z-order
    if (!pass && invSize) indexCurve(ear, minX, minY, invSize);

    var stop = ear,
        prev, next;

    // iterate through ears, slicing them one by one
    while (ear.prev !== ear.next) {
        prev = ear.prev;
        next = ear.next;

        if (invSize ? isEarHashed(ear, minX, minY, invSize) : isEar(ear)) {
            // cut off the triangle
            triangles.push(prev.i / dim);
            triangles.push(ear.i / dim);
            triangles.push(next.i / dim);

            removeNode(ear);

            // skipping the next vertice leads to less sliver triangles
            ear = next.next;
            stop = next.next;

            continue;
        }

        ear = next;

        // if we looped through the whole remaining polygon and can't find any more ears
        if (ear === stop) {
            // try filtering points and slicing again
            if (!pass) {
                earcutLinked(filterPoints(ear), triangles, dim, minX, minY, invSize, 1);

            // if this didn't work, try curing all small self-intersections locally
            } else if (pass === 1) {
                ear = cureLocalIntersections(ear, triangles, dim);
                earcutLinked(ear, triangles, dim, minX, minY, invSize, 2);

            // as a last resort, try splitting the remaining polygon into two
            } else if (pass === 2) {
                splitEarcut(ear, triangles, dim, minX, minY, invSize);
            }

            break;
        }
    }
}

// check whether a polygon node forms a valid ear with adjacent nodes
function isEar(ear) {
    var a = ear.prev,
        b = ear,
        c = ear.next;

    if (area(a, b, c) >= 0) return false; // reflex, can't be an ear

    // now make sure we don't have other points inside the potential ear
    var p = ear.next.next;

    while (p !== ear.prev) {
        if (pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) &&
            area(p.prev, p, p.next) >= 0) return false;
        p = p.next;
    }

    return true;
}

function isEarHashed(ear, minX, minY, invSize) {
    var a = ear.prev,
        b = ear,
        c = ear.next;

    if (area(a, b, c) >= 0) return false; // reflex, can't be an ear

    // triangle bbox; min & max are calculated like this for speed
    var minTX = a.x < b.x ? (a.x < c.x ? a.x : c.x) : (b.x < c.x ? b.x : c.x),
        minTY = a.y < b.y ? (a.y < c.y ? a.y : c.y) : (b.y < c.y ? b.y : c.y),
        maxTX = a.x > b.x ? (a.x > c.x ? a.x : c.x) : (b.x > c.x ? b.x : c.x),
        maxTY = a.y > b.y ? (a.y > c.y ? a.y : c.y) : (b.y > c.y ? b.y : c.y);

    // z-order range for the current triangle bbox;
    var minZ = zOrder(minTX, minTY, minX, minY, invSize),
        maxZ = zOrder(maxTX, maxTY, minX, minY, invSize);

    var p = ear.prevZ,
        n = ear.nextZ;

    // look for points inside the triangle in both directions
    while (p && p.z >= minZ && n && n.z <= maxZ) {
        if (p !== ear.prev && p !== ear.next &&
            pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) &&
            area(p.prev, p, p.next) >= 0) return false;
        p = p.prevZ;

        if (n !== ear.prev && n !== ear.next &&
            pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y) &&
            area(n.prev, n, n.next) >= 0) return false;
        n = n.nextZ;
    }

    // look for remaining points in decreasing z-order
    while (p && p.z >= minZ) {
        if (p !== ear.prev && p !== ear.next &&
            pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) &&
            area(p.prev, p, p.next) >= 0) return false;
        p = p.prevZ;
    }

    // look for remaining points in increasing z-order
    while (n && n.z <= maxZ) {
        if (n !== ear.prev && n !== ear.next &&
            pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y) &&
            area(n.prev, n, n.next) >= 0) return false;
        n = n.nextZ;
    }

    return true;
}

// go through all polygon nodes and cure small local self-intersections
function cureLocalIntersections(start, triangles, dim) {
    var p = start;
    do {
        var a = p.prev,
            b = p.next.next;

        if (!equals(a, b) && intersects(a, p, p.next, b) && locallyInside(a, b) && locallyInside(b, a)) {

            triangles.push(a.i / dim);
            triangles.push(p.i / dim);
            triangles.push(b.i / dim);

            // remove two nodes involved
            removeNode(p);
            removeNode(p.next);

            p = start = b;
        }
        p = p.next;
    } while (p !== start);

    return p;
}

// try splitting polygon into two and triangulate them independently
function splitEarcut(start, triangles, dim, minX, minY, invSize) {
    // look for a valid diagonal that divides the polygon into two
    var a = start;
    do {
        var b = a.next.next;
        while (b !== a.prev) {
            if (a.i !== b.i && isValidDiagonal(a, b)) {
                // split the polygon in two by the diagonal
                var c = splitPolygon(a, b);

                // filter colinear points around the cuts
                a = filterPoints(a, a.next);
                c = filterPoints(c, c.next);

                // run earcut on each half
                earcutLinked(a, triangles, dim, minX, minY, invSize);
                earcutLinked(c, triangles, dim, minX, minY, invSize);
                return;
            }
            b = b.next;
        }
        a = a.next;
    } while (a !== start);
}

// link every hole into the outer loop, producing a single-ring polygon without holes
function eliminateHoles(data, holeIndices, outerNode, dim) {
    var queue = [],
        i, len, start, end, list;

    for (i = 0, len = holeIndices.length; i < len; i++) {
        start = holeIndices[i] * dim;
        end = i < len - 1 ? holeIndices[i + 1] * dim : data.length;
        list = linkedList(data, start, end, dim, false);
        if (list === list.next) list.steiner = true;
        queue.push(getLeftmost(list));
    }

    queue.sort(compareX);

    // process holes from left to right
    for (i = 0; i < queue.length; i++) {
        eliminateHole(queue[i], outerNode);
        outerNode = filterPoints(outerNode, outerNode.next);
    }

    return outerNode;
}

function compareX(a, b) {
    return a.x - b.x;
}

// find a bridge between vertices that connects hole with an outer ring and and link it
function eliminateHole(hole, outerNode) {
    outerNode = findHoleBridge(hole, outerNode);
    if (outerNode) {
        var b = splitPolygon(outerNode, hole);
        filterPoints(b, b.next);
    }
}

// David Eberly's algorithm for finding a bridge between hole and outer polygon
function findHoleBridge(hole, outerNode) {
    var p = outerNode,
        hx = hole.x,
        hy = hole.y,
        qx = -Infinity,
        m;

    // find a segment intersected by a ray from the hole's leftmost point to the left;
    // segment's endpoint with lesser x will be potential connection point
    do {
        if (hy <= p.y && hy >= p.next.y && p.next.y !== p.y) {
            var x = p.x + (hy - p.y) * (p.next.x - p.x) / (p.next.y - p.y);
            if (x <= hx && x > qx) {
                qx = x;
                if (x === hx) {
                    if (hy === p.y) return p;
                    if (hy === p.next.y) return p.next;
                }
                m = p.x < p.next.x ? p : p.next;
            }
        }
        p = p.next;
    } while (p !== outerNode);

    if (!m) return null;

    if (hx === qx) return m.prev; // hole touches outer segment; pick lower endpoint

    // look for points inside the triangle of hole point, segment intersection and endpoint;
    // if there are no points found, we have a valid connection;
    // otherwise choose the point of the minimum angle with the ray as connection point

    var stop = m,
        mx = m.x,
        my = m.y,
        tanMin = Infinity,
        tan;

    p = m.next;

    while (p !== stop) {
        if (hx >= p.x && p.x >= mx && hx !== p.x &&
                pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y)) {

            tan = Math.abs(hy - p.y) / (hx - p.x); // tangential

            if ((tan < tanMin || (tan === tanMin && p.x > m.x)) && locallyInside(p, hole)) {
                m = p;
                tanMin = tan;
            }
        }

        p = p.next;
    }

    return m;
}

// interlink polygon nodes in z-order
function indexCurve(start, minX, minY, invSize) {
    var p = start;
    do {
        if (p.z === null) p.z = zOrder(p.x, p.y, minX, minY, invSize);
        p.prevZ = p.prev;
        p.nextZ = p.next;
        p = p.next;
    } while (p !== start);

    p.prevZ.nextZ = null;
    p.prevZ = null;

    sortLinked(p);
}

// Simon Tatham's linked list merge sort algorithm
// http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html
function sortLinked(list) {
    var i, p, q, e, tail, numMerges, pSize, qSize,
        inSize = 1;

    do {
        p = list;
        list = null;
        tail = null;
        numMerges = 0;

        while (p) {
            numMerges++;
            q = p;
            pSize = 0;
            for (i = 0; i < inSize; i++) {
                pSize++;
                q = q.nextZ;
                if (!q) break;
            }
            qSize = inSize;

            while (pSize > 0 || (qSize > 0 && q)) {

                if (pSize !== 0 && (qSize === 0 || !q || p.z <= q.z)) {
                    e = p;
                    p = p.nextZ;
                    pSize--;
                } else {
                    e = q;
                    q = q.nextZ;
                    qSize--;
                }

                if (tail) tail.nextZ = e;
                else list = e;

                e.prevZ = tail;
                tail = e;
            }

            p = q;
        }

        tail.nextZ = null;
        inSize *= 2;

    } while (numMerges > 1);

    return list;
}

// z-order of a point given coords and inverse of the longer side of data bbox
function zOrder(x, y, minX, minY, invSize) {
    // coords are transformed into non-negative 15-bit integer range
    x = 32767 * (x - minX) * invSize;
    y = 32767 * (y - minY) * invSize;

    x = (x | (x << 8)) & 0x00FF00FF;
    x = (x | (x << 4)) & 0x0F0F0F0F;
    x = (x | (x << 2)) & 0x33333333;
    x = (x | (x << 1)) & 0x55555555;

    y = (y | (y << 8)) & 0x00FF00FF;
    y = (y | (y << 4)) & 0x0F0F0F0F;
    y = (y | (y << 2)) & 0x33333333;
    y = (y | (y << 1)) & 0x55555555;

    return x | (y << 1);
}

// find the leftmost node of a polygon ring
function getLeftmost(start) {
    var p = start,
        leftmost = start;
    do {
        if (p.x < leftmost.x) leftmost = p;
        p = p.next;
    } while (p !== start);

    return leftmost;
}

// check if a point lies within a convex triangle
function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) {
    return (cx - px) * (ay - py) - (ax - px) * (cy - py) >= 0 &&
           (ax - px) * (by - py) - (bx - px) * (ay - py) >= 0 &&
           (bx - px) * (cy - py) - (cx - px) * (by - py) >= 0;
}

// check if a diagonal between two polygon nodes is valid (lies in polygon interior)
function isValidDiagonal(a, b) {
    return a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) &&
           locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b);
}

// signed area of a triangle
function area(p, q, r) {
    return (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
}

// check if two points are equal
function equals(p1, p2) {
    return p1.x === p2.x && p1.y === p2.y;
}

// check if two segments intersect
function intersects(p1, q1, p2, q2) {
    if ((equals(p1, q1) && equals(p2, q2)) ||
        (equals(p1, q2) && equals(p2, q1))) return true;
    return area(p1, q1, p2) > 0 !== area(p1, q1, q2) > 0 &&
           area(p2, q2, p1) > 0 !== area(p2, q2, q1) > 0;
}

// check if a polygon diagonal intersects any polygon segments
function intersectsPolygon(a, b) {
    var p = a;
    do {
        if (p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i &&
                intersects(p, p.next, a, b)) return true;
        p = p.next;
    } while (p !== a);

    return false;
}

// check if a polygon diagonal is locally inside the polygon
function locallyInside(a, b) {
    return area(a.prev, a, a.next) < 0 ?
        area(a, b, a.next) >= 0 && area(a, a.prev, b) >= 0 :
        area(a, b, a.prev) < 0 || area(a, a.next, b) < 0;
}

// check if the middle point of a polygon diagonal is inside the polygon
function middleInside(a, b) {
    var p = a,
        inside = false,
        px = (a.x + b.x) / 2,
        py = (a.y + b.y) / 2;
    do {
        if (((p.y > py) !== (p.next.y > py)) && p.next.y !== p.y &&
                (px < (p.next.x - p.x) * (py - p.y) / (p.next.y - p.y) + p.x))
            inside = !inside;
        p = p.next;
    } while (p !== a);

    return inside;
}

// link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two;
// if one belongs to the outer ring and another to a hole, it merges it into a single ring
function splitPolygon(a, b) {
    var a2 = new Node(a.i, a.x, a.y),
        b2 = new Node(b.i, b.x, b.y),
        an = a.next,
        bp = b.prev;

    a.next = b;
    b.prev = a;

    a2.next = an;
    an.prev = a2;

    b2.next = a2;
    a2.prev = b2;

    bp.next = b2;
    b2.prev = bp;

    return b2;
}

// create a node and optionally link it with previous one (in a circular doubly linked list)
function insertNode(i, x, y, last) {
    var p = new Node(i, x, y);

    if (!last) {
        p.prev = p;
        p.next = p;

    } else {
        p.next = last.next;
        p.prev = last;
        last.next.prev = p;
        last.next = p;
    }
    return p;
}

function removeNode(p) {
    p.next.prev = p.prev;
    p.prev.next = p.next;

    if (p.prevZ) p.prevZ.nextZ = p.nextZ;
    if (p.nextZ) p.nextZ.prevZ = p.prevZ;
}

function Node(i, x, y) {
    // vertice index in coordinates array
    this.i = i;

    // vertex coordinates
    this.x = x;
    this.y = y;

    // previous and next vertice nodes in a polygon ring
    this.prev = null;
    this.next = null;

    // z-order curve value
    this.z = null;

    // previous and next nodes in z-order
    this.prevZ = null;
    this.nextZ = null;

    // indicates whether this is a steiner point
    this.steiner = false;
}

// return a percentage difference between the polygon area and its triangulation area;
// used to verify correctness of triangulation
earcut.deviation = function (data, holeIndices, dim, triangles) {
    var hasHoles = holeIndices && holeIndices.length;
    var outerLen = hasHoles ? holeIndices[0] * dim : data.length;

    var polygonArea = Math.abs(signedArea(data, 0, outerLen, dim));
    if (hasHoles) {
        for (var i = 0, len = holeIndices.length; i < len; i++) {
            var start = holeIndices[i] * dim;
            var end = i < len - 1 ? holeIndices[i + 1] * dim : data.length;
            polygonArea -= Math.abs(signedArea(data, start, end, dim));
        }
    }

    var trianglesArea = 0;
    for (i = 0; i < triangles.length; i += 3) {
        var a = triangles[i] * dim;
        var b = triangles[i + 1] * dim;
        var c = triangles[i + 2] * dim;
        trianglesArea += Math.abs(
            (data[a] - data[c]) * (data[b + 1] - data[a + 1]) -
            (data[a] - data[b]) * (data[c + 1] - data[a + 1]));
    }

    return polygonArea === 0 && trianglesArea === 0 ? 0 :
        Math.abs((trianglesArea - polygonArea) / polygonArea);
};

function signedArea(data, start, end, dim) {
    var sum = 0;
    for (var i = start, j = end - dim; i < end; i += dim) {
        sum += (data[j] - data[i]) * (data[i + 1] + data[j + 1]);
        j = i;
    }
    return sum;
}

// turn a polygon in a multi-dimensional array form (e.g. as in GeoJSON) into a form Earcut accepts
earcut.flatten = function (data) {
    var dim = data[0][0].length,
        result = {vertices: [], holes: [], dimensions: dim},
        holeIndex = 0;

    for (var i = 0; i < data.length; i++) {
        for (var j = 0; j < data[i].length; j++) {
            for (var d = 0; d < dim; d++) result.vertices.push(data[i][j][d]);
        }
        if (i > 0) {
            holeIndex += data[i - 1].length;
            result.holes.push(holeIndex);
        }
    }
    return result;
};


/***/ }),
/* 308 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var inherit = __webpack_require__(0);
var registerFeature = __webpack_require__(1).registerFeature;
var quadFeature = __webpack_require__(17);

/**
 * Create a new instance of class quadFeature.
 *
 * @class
 * @alias geo.gl.quadFeature
 * @param {geo.quadFeature.spec} arg Options object.
 * @extends geo.quadFeature
 * @returns {geo.gl.quadFeature}
 */
var gl_quadFeature = function gl_quadFeature(arg) {
  'use strict';

  if (!(this instanceof gl_quadFeature)) {
    return new gl_quadFeature(arg);
  }
  quadFeature.call(this, arg);

  var $ = __webpack_require__(2);
  var vgl = __webpack_require__(9);
  var object = __webpack_require__(18);

  object.call(this);

  var m_this = this,
      s_exit = this._exit,
      s_init = this._init,
      s_update = this._update,
      m_modelViewUniform,
      m_actor_image,
      m_actor_color,
      m_glBuffers = {},
      m_imgposbuf,
      m_clrposbuf,
      m_clrModelViewUniform,
      m_glCompileTimestamp = vgl.timestamp(),
      m_glColorCompileTimestamp = vgl.timestamp(),
      m_quads;
  var fragmentShaderImageSource = ['varying highp vec2 iTextureCoord;', 'uniform sampler2D sampler2d;', 'uniform mediump float opacity;', 'uniform highp vec2 crop;', 'void main(void) {', '  mediump vec4 color = texture2D(sampler2d, iTextureCoord);', '  if ((crop.s < 1.0 && iTextureCoord.s > crop.s) || (crop.t < 1.0 && 1.0 - iTextureCoord.t > crop.t)) {', '    discard;', '  }', '  color.w *= opacity;', '  gl_FragColor = color;', '}'].join('\n');
  var vertexShaderImageSource = ['attribute vec3 vertexPosition;', 'attribute vec2 textureCoord;', 'uniform mat4 modelViewMatrix;', 'uniform mat4 projectionMatrix;', 'varying highp vec2 iTextureCoord;', 'void main(void) {', '  gl_Position = projectionMatrix * modelViewMatrix * vec4(vertexPosition, 1.0);', '  iTextureCoord = textureCoord;', '}'].join('\n');
  var vertexShaderColorSource = ['attribute vec3 vertexPosition;', 'uniform vec3 vertexColor;', 'uniform mat4 modelViewMatrix;', 'uniform mat4 projectionMatrix;', 'varying mediump vec3 iVertexColor;', 'void main(void) {', '  gl_Position = projectionMatrix * modelViewMatrix * vec4(vertexPosition, 1.0);', '  iVertexColor = vertexColor;', '}'].join('\n');

  /**
   * Allocate buffers that we need to control for image quads.  This mimics
   * the actions from vgl.mapper to some degree.
   *
   * @private
   * @param {vgl.renderState} renderState An object that contains the context
   *   used for drawing.
   */
  function setupDrawObjects(renderState) {
    var context = renderState.m_context,
        newbuf = false;

    if (m_quads.imgQuads.length) {
      if (!m_imgposbuf || m_imgposbuf.length < m_quads.imgQuads.length * 12 || !m_glBuffers.imgQuadsPosition) {
        if (m_glBuffers.imgQuadsPosition) {
          context.deleteBuffer(m_glBuffers.imgQuadsPosition);
        }
        m_glBuffers.imgQuadsPosition = context.createBuffer();
        m_imgposbuf = new Float32Array(Math.max(128, m_quads.imgQuads.length * 2) * 12);
        newbuf = true;
      }
      $.each(m_quads.imgQuads, function (idx, quad) {
        for (var i = 0; i < 12; i += 1) {
          m_imgposbuf[idx * 12 + i] = quad.pos[i] - m_quads.origin[i % 3];
        }
      });
      context.bindBuffer(vgl.GL.ARRAY_BUFFER, m_glBuffers.imgQuadsPosition);
      if (newbuf) {
        context.bufferData(vgl.GL.ARRAY_BUFFER, m_imgposbuf, vgl.GL.DYNAMIC_DRAW);
      } else {
        context.bufferSubData(vgl.GL.ARRAY_BUFFER, 0, m_imgposbuf);
      }
    }
    m_glCompileTimestamp.modified();
  }

  /**
   * Allocate buffers that we need to control for color quads.  This mimics
   * the actions from vgl.mapper to some degree.
   *
   * @private
   * @param {vgl.renderState} renderState An object that contains the context
   *   used for drawing.
   */
  function setupColorDrawObjects(renderState) {
    var context = renderState.m_context,
        newbuf = false;

    if (m_quads.clrQuads.length) {
      if (!m_clrposbuf || m_clrposbuf.length < m_quads.clrQuads.length * 12 || !m_glBuffers.clrQuadsPosition) {
        if (m_glBuffers.clrQuadsPosition) {
          context.deleteBuffer(m_glBuffers.clrQuadsPosition);
        }
        m_glBuffers.clrQuadsPosition = context.createBuffer();
        m_clrposbuf = new Float32Array(Math.max(128, m_quads.clrQuads.length * 2) * 12);
        newbuf = true;
      }
      $.each(m_quads.clrQuads, function (idx, quad) {
        for (var i = 0; i < 12; i += 1) {
          m_clrposbuf[idx * 12 + i] = quad.pos[i] - m_quads.origin[i % 3];
        }
      });
      context.bindBuffer(vgl.GL.ARRAY_BUFFER, m_glBuffers.clrQuadsPosition);
      if (newbuf) {
        context.bufferData(vgl.GL.ARRAY_BUFFER, m_clrposbuf, vgl.GL.DYNAMIC_DRAW);
      } else {
        context.bufferSubData(vgl.GL.ARRAY_BUFFER, 0, m_clrposbuf);
      }
    }
    m_glColorCompileTimestamp.modified();
  }

  /**
   * Build this feature.
   */
  this._build = function () {
    var mapper, mat, prog, srctex, unicrop, geom, context;

    if (!m_this.position()) {
      return;
    }
    m_quads = this._generateQuads();
    /* Create an actor to render image quads */
    if (m_quads.imgQuads.length && !m_actor_image) {
      m_this.visible(false);
      mapper = new vgl.mapper({ dynamicDraw: true });
      m_actor_image = new vgl.actor();
      /* This is similar to vgl.utils.createTextureMaterial */
      m_actor_image.setMapper(mapper);
      mat = new vgl.material();
      prog = new vgl.shaderProgram();
      prog.addVertexAttribute(new vgl.vertexAttribute('vertexPosition'), vgl.vertexAttributeKeys.Position);
      prog.addVertexAttribute(new vgl.vertexAttribute('textureCoord'), vgl.vertexAttributeKeys.TextureCoordinate);
      m_modelViewUniform = new vgl.modelViewOriginUniform('modelViewMatrix', m_quads.origin);
      prog.addUniform(m_modelViewUniform);
      prog.addUniform(new vgl.projectionUniform('projectionMatrix'));
      prog.addUniform(new vgl.floatUniform('opacity', 1.0));
      unicrop = new vgl.uniform(vgl.GL.FLOAT_VEC2, 'crop');
      unicrop.set([1.0, 1.0]);
      prog.addUniform(unicrop);
      context = m_this.renderer()._glContext();
      prog.addShader(vgl.getCachedShader(vgl.GL.VERTEX_SHADER, context, vertexShaderImageSource));
      prog.addShader(vgl.getCachedShader(vgl.GL.FRAGMENT_SHADER, context, fragmentShaderImageSource));
      mat.addAttribute(prog);
      mat.addAttribute(new vgl.blend());
      /* This is similar to vgl.planeSource */
      geom = new vgl.geometryData();
      m_imgposbuf = undefined;
      srctex = new vgl.sourceDataT2fv();
      srctex.pushBack([0, 0, 1, 0, 0, 1, 1, 1]);
      geom.addSource(srctex);
      /* We deliberately do not add a primitive to our geometry -- we take care
       * of that ourselves. */

      mapper.setGeometryData(geom);
      m_actor_image.setMaterial(mat);

      mapper.s_render = mapper.render;
      mapper.render = m_this._renderImageQuads;
      m_this.renderer().contextRenderer().addActor(m_actor_image);
      m_this.visible(true);
    }
    /* Create an actor to render color quads */
    if (m_quads.clrQuads.length && !m_actor_color) {
      m_this.visible(false);
      mapper = new vgl.mapper({ dynamicDraw: true });
      m_actor_color = new vgl.actor();
      /* This is similar to vgl.utils.createTextureMaterial */
      m_actor_color.setMapper(mapper);
      mat = new vgl.material();
      prog = new vgl.shaderProgram();
      prog.addVertexAttribute(new vgl.vertexAttribute('vertexPosition'), vgl.vertexAttributeKeys.Position);
      m_clrModelViewUniform = new vgl.modelViewOriginUniform('modelViewMatrix', m_quads.origin);
      prog.addUniform(m_clrModelViewUniform);
      prog.addUniform(new vgl.projectionUniform('projectionMatrix'));
      prog.addUniform(new vgl.floatUniform('opacity', 1.0));
      prog.addUniform(new vgl.uniform(vgl.GL.FLOAT_VEC3, 'vertexColor'));
      context = m_this.renderer()._glContext();
      prog.addShader(vgl.getCachedShader(vgl.GL.VERTEX_SHADER, context, vertexShaderColorSource));
      prog.addShader(vgl.utils.createFragmentShader(context));
      mat.addAttribute(prog);
      mat.addAttribute(new vgl.blend());
      /* This is similar to vgl.planeSource */
      geom = new vgl.geometryData();
      m_clrposbuf = undefined;
      /* We deliberately do not add a primitive to our geometry -- we take care
       * of that ourselves. */

      mapper.setGeometryData(geom);
      m_actor_color.setMaterial(mat);

      mapper.s_render = mapper.render;
      mapper.render = m_this._renderColorQuads;
      m_this.renderer().contextRenderer().addActor(m_actor_color);
      m_this.visible(true);
    }
    if (m_modelViewUniform) {
      m_modelViewUniform.setOrigin(m_quads.origin);
    }
    if (m_clrModelViewUniform) {
      m_clrModelViewUniform.setOrigin(m_quads.origin);
    }
    m_this._updateTextures();
    m_this.buildTime().modified();
  };

  /**
   * Check all of the image quads.  If any do not have the correct texture,
   * update them. */
  this._updateTextures = function () {
    var texture;

    $.each(m_quads.imgQuads, function (idx, quad) {
      if (!quad.image) {
        return;
      }
      if (quad.image._texture) {
        quad.texture = quad.image._texture;
      } else {
        texture = new vgl.texture();
        texture.setImage(quad.image);
        quad.texture = quad.image._texture = texture;
      }
    });
  };

  /**
   * Render all of the color quads using a single mapper.
   *
   * @param {vgl.renderState} renderState An object that contains the context
   *   used for drawing.
   */
  this._renderColorQuads = function (renderState) {
    if (!m_quads.clrQuads.length) {
      return;
    }
    var mapper = this;
    if (mapper.getMTime() > m_glColorCompileTimestamp.getMTime() || m_this.dataTime().getMTime() > m_glColorCompileTimestamp.getMTime() || renderState.m_contextChanged || !m_clrposbuf || m_quads.clrQuads.length * 12 > m_clrposbuf.length) {
      setupColorDrawObjects(renderState);
    }
    mapper.s_render(renderState, true);

    var context = renderState.m_context,
        opacity = 1,
        color;

    context.bindBuffer(vgl.GL.ARRAY_BUFFER, m_glBuffers.clrQuadsPosition);
    $.each(m_quads.clrQuads, function (idx, quad) {
      if (quad.opacity !== opacity) {
        opacity = quad.opacity;
        context.uniform1fv(renderState.m_material.shaderProgram().uniformLocation('opacity'), new Float32Array([opacity]));
      }
      if (!color || color.r !== quad.color.r || color.g !== quad.color.g || color.b !== quad.color.b) {
        color = quad.color;
        context.uniform3fv(renderState.m_material.shaderProgram().uniformLocation('vertexColor'), new Float32Array([color.r, color.g, color.b]));
      }

      context.bindBuffer(vgl.GL.ARRAY_BUFFER, m_glBuffers.clrQuadsPosition);
      context.vertexAttribPointer(vgl.vertexAttributeKeys.Position, 3, vgl.GL.FLOAT, false, 12, idx * 12 * 4);
      context.enableVertexAttribArray(vgl.vertexAttributeKeys.Position);

      context.drawArrays(vgl.GL.TRIANGLE_STRIP, 0, 4);
    });
    context.bindBuffer(vgl.GL.ARRAY_BUFFER, null);
    mapper.undoBindVertexData(renderState);
  };

  /**
   * Render all of the image quads using a single mapper.
   *
   * @param {vgl.renderState} renderState An object that contains the context
   *   used for drawing.
   */
  this._renderImageQuads = function (renderState) {
    if (!m_quads.imgQuads.length) {
      return;
    }
    var mapper = this;
    if (mapper.getMTime() > m_glCompileTimestamp.getMTime() || m_this.dataTime().getMTime() > m_glCompileTimestamp.getMTime() || renderState.m_contextChanged || !m_imgposbuf || m_quads.imgQuads.length * 12 > m_imgposbuf.length) {
      setupDrawObjects(renderState);
    }
    mapper.s_render(renderState, true);

    var context = renderState.m_context,
        opacity = 1,
        crop = { x: 1, y: 1 },
        quadcrop;

    context.bindBuffer(vgl.GL.ARRAY_BUFFER, m_glBuffers.imgQuadsPosition);
    $.each(m_quads.imgQuads, function (idx, quad) {
      if (!quad.image) {
        return;
      }
      quad.texture.bind(renderState);

      if (quad.opacity !== opacity) {
        opacity = quad.opacity;
        context.uniform1fv(renderState.m_material.shaderProgram().uniformLocation('opacity'), new Float32Array([opacity]));
      }
      quadcrop = quad.crop || { x: 1, y: 1 };
      if (!crop || quadcrop.x !== crop.x || quadcrop.y !== crop.y) {
        crop = quadcrop;
        context.uniform2fv(renderState.m_material.shaderProgram().uniformLocation('crop'), new Float32Array([crop.x, crop.y]));
      }
      context.bindBuffer(vgl.GL.ARRAY_BUFFER, m_glBuffers.imgQuadsPosition);
      context.vertexAttribPointer(vgl.vertexAttributeKeys.Position, 3, vgl.GL.FLOAT, false, 12, idx * 12 * 4);
      context.enableVertexAttribArray(vgl.vertexAttributeKeys.Position);

      context.drawArrays(vgl.GL.TRIANGLE_STRIP, 0, 4);
      quad.texture.undoBind(renderState);
    });
    context.bindBuffer(vgl.GL.ARRAY_BUFFER, null);
    mapper.undoBindVertexData(renderState);
  };

  /**
   * Update.
   */
  this._update = function () {
    s_update.call(m_this);
    if (m_this.buildTime().getMTime() <= m_this.dataTime().getMTime() || m_this.updateTime().getMTime() < m_this.getMTime()) {
      m_this._build();
    }
    if (m_actor_color) {
      m_actor_color.setVisible(m_this.visible(undefined, true));
      m_actor_color.material().setBinNumber(m_this.bin());
    }
    if (m_actor_image) {
      m_actor_image.setVisible(m_this.visible(undefined, true));
      m_actor_image.material().setBinNumber(m_this.bin());
    }
    m_this.updateTime().modified();
  };

  /**
   * Initialize.
   */
  this._init = function () {
    s_init.call(m_this, arg);
  };

  /**
   * Destroy.
   */
  this._exit = function () {
    if (m_actor_image) {
      m_this.renderer().contextRenderer().removeActor(m_actor_image);
      m_actor_image = null;
    }
    if (m_actor_color) {
      m_this.renderer().contextRenderer().removeActor(m_actor_color);
      m_actor_color = null;
    }
    s_exit.call(m_this);
  };

  m_this._init(arg);
  return this;
};

inherit(gl_quadFeature, quadFeature);

// Now register it
var capabilities = {};
capabilities[quadFeature.capabilities.color] = true;
capabilities[quadFeature.capabilities.image] = true;
capabilities[quadFeature.capabilities.imageCrop] = true;
capabilities[quadFeature.capabilities.imageFixedScale] = false;
capabilities[quadFeature.capabilities.imageFull] = true;
capabilities[quadFeature.capabilities.canvas] = false;
capabilities[quadFeature.capabilities.video] = false;

registerFeature('vgl', 'quad', gl_quadFeature, capabilities);
module.exports = gl_quadFeature;

/***/ }),
/* 309 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var registerLayerAdjustment = __webpack_require__(1).registerLayerAdjustment;

var gl_tileLayer = function gl_tileLayer() {
  'use strict';

  var m_this = this,
      s_init = this._init,
      s_exit = this._exit,
      m_quadFeature,
      m_nextTileId = 0,
      m_tiles = [];

  /* Add a tile to the list of quads */
  this._drawTile = function (tile) {
    if (!m_quadFeature) {
      return;
    }
    var bounds = this._tileBounds(tile),
        level = tile.index.level || 0,
        to = this._tileOffset(level),
        crop = this.tileCropFromBounds(tile),
        quad = {};
    if (crop) {
      quad.crop = {
        x: crop.x / m_this._options.tileWidth,
        y: crop.y / m_this._options.tileHeight
      };
    }
    quad.ul = this.fromLocal(this.fromLevel({
      x: bounds.left - to.x, y: bounds.top - to.y
    }, level), 0);
    quad.ll = this.fromLocal(this.fromLevel({
      x: bounds.left - to.x, y: bounds.bottom - to.y
    }, level), 0);
    quad.ur = this.fromLocal(this.fromLevel({
      x: bounds.right - to.x, y: bounds.top - to.y
    }, level), 0);
    quad.lr = this.fromLocal(this.fromLevel({
      x: bounds.right - to.x, y: bounds.bottom - to.y
    }, level), 0);
    /* Make sure our level increments are within the clipbounds and ordered so
     * that lower levels are farther away that higher levels. */
    var clipbounds = m_this.map().camera().clipbounds;
    var z = level * m_this._levelZIncrement;
    z = clipbounds.far + (clipbounds.near - clipbounds.far) * z;
    quad.ul.z = quad.ll.z = quad.ur.z = quad.lr.z = z;
    m_nextTileId += 1;
    quad.id = m_nextTileId;
    tile.quadId = quad.id;
    quad.image = tile.image;
    m_tiles.push(quad);
    m_quadFeature.data(m_tiles);
    m_quadFeature._update();
    m_this.draw();
  };

  /* Remove the tile feature. */
  this._remove = function (tile) {
    if (tile.quadId !== undefined && m_quadFeature) {
      for (var i = 0; i < m_tiles.length; i += 1) {
        if (m_tiles[i].id === tile.quadId) {
          m_tiles.splice(i, 1);
          break;
        }
      }
      m_quadFeature.data(m_tiles);
      m_quadFeature._update();
      m_this.draw();
    }
  };

  /**
   * Clean up the layer.
   */
  this._exit = function () {
    m_this.deleteFeature(m_quadFeature);
    m_quadFeature = null;
    m_tiles = [];
    s_exit.apply(m_this, arguments);
  };

  /**
   * Initialize after the layer is added to the map.
   */
  this._init = function () {
    s_init.apply(m_this, arguments);
    m_quadFeature = this.createFeature('quad', {
      previewColor: m_this._options.previewColor,
      previewImage: m_this._options.previewImage
    });
    m_quadFeature.geoTrigger = undefined;
    m_quadFeature.gcs(m_this._options.gcs || m_this.map().gcs());
    m_quadFeature.data(m_tiles);
    m_quadFeature._update();
  };

  /* These functions don't need to do anything. */
  this._getSubLayer = function () {};
  this._updateSubLayers = undefined;
};

registerLayerAdjustment('vgl', 'tile', gl_tileLayer);

module.exports = gl_tileLayer;

/***/ }),
/* 310 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/**
 * @namespace geo.canvas
 */
module.exports = {
  canvasRenderer: __webpack_require__(311),
  heatmapFeature: __webpack_require__(312),
  isolineFeature: __webpack_require__(313),
  lineFeature: __webpack_require__(314),
  pixelmapFeature: __webpack_require__(315),
  quadFeature: __webpack_require__(316),
  textFeature: __webpack_require__(317),
  tileLayer: __webpack_require__(318)
};

/***/ }),
/* 311 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var inherit = __webpack_require__(0);
var registerRenderer = __webpack_require__(1).registerRenderer;
var renderer = __webpack_require__(22);

/**
 * Create a new instance of class canvasRenderer.
 *
 * @class
 * @alias geo.canvas.renderer
 * @extends geo.renderer
 * @param {object} arg Options for the renderer.
 * @param {geo.layer} [arg.layer] Layer associated with the renderer.
 * @param {HTMLElement} [arg.canvas] Canvas element associated with the
 *   renderer.
 * @returns {geo.canvas.canvasRenderer}
 */
var canvasRenderer = function canvasRenderer(arg) {
  'use strict';

  var $ = __webpack_require__(2);

  if (!(this instanceof canvasRenderer)) {
    return new canvasRenderer(arg);
  }
  arg = arg || {};
  renderer.call(this, arg);

  var m_this = this,
      m_clearCanvas = true,
      s_init = this._init,
      s_exit = this._exit;

  /**
   * Set the clear canvas flag.  If truthy, the canvas is erased at the start
   * of the render cycle.  If falsy, the old data is kept.
   *
   * @param {boolean} arg Truthy to clear the canvas when rendering is started.
   */
  this.clearCanvas = function (arg) {
    m_clearCanvas = arg;
  };

  /**
   * Get API used by the renderer.
   *
   * @returns {string} 'canvas'.
   */
  this.api = function () {
    return 'canvas';
  };

  /**
   * Initialize.
   *
   * @returns {this}
   */
  this._init = function () {
    if (m_this.initialized()) {
      return m_this;
    }

    s_init.call(m_this);

    var canvas = $(document.createElement('canvas'));
    m_this.context2d = canvas[0].getContext('2d');

    canvas.addClass('canvas-canvas');
    $(m_this.layer().node().get(0)).append(canvas);

    m_this.canvas(canvas);
    /* Initialize the size of the renderer */
    var map = m_this.layer().map(),
        mapSize = map.size();
    m_this._resize(0, 0, mapSize.width, mapSize.height);

    return m_this;
  };

  /**
   * Handle resize event.
   *
   * @param {number} x Ignored.
   * @param {number} y Ignored.
   * @param {number} w New width in pixels.
   * @param {number} h New height in pixels.
   * @returns {this}
   */
  this._resize = function (x, y, w, h) {
    var canvas = m_this.canvas();
    if (parseInt(canvas.attr('width'), 10) !== w || parseInt(canvas.attr('height'), 10) !== h) {
      canvas.attr('width', w);
      canvas.attr('height', h);
      m_this._render();
    }
    return m_this;
  };

  /**
   * Render.
   *
   * @returns {this}
   */
  this._render = function () {
    m_this.layer().map().scheduleAnimationFrame(this._renderFrame);
    return m_this;
  };

  /**
   * Render during an animation frame callback.
   */
  this._renderFrame = function () {
    var layer = m_this.layer(),
        map = layer.map(),
        camera = map.camera(),
        viewport = camera._viewport,
        features = layer.features(),
        i;

    for (i = 0; i < features.length; i += 1) {
      if (features[i]._delayRender()) {
        // reschedule the render for the next animation frame
        m_this._render();
        // exit this render loop so it doesn't occur
        return;
      }
    }

    // Clear the canvas.
    if (m_clearCanvas) {
      m_this.context2d.setTransform(1, 0, 0, 1, 0, 0);
      m_this.context2d.clearRect(0, 0, viewport.width, viewport.height);
    }

    for (i = 0; i < features.length; i += 1) {
      if (features[i].visible()) {
        features[i]._renderOnCanvas(m_this.context2d, map);
      }
    }
  };

  /**
   * Exit.
   */
  this._exit = function () {
    m_this.canvas().remove();
    s_exit();
  };

  return this;
};

inherit(canvasRenderer, renderer);

registerRenderer('canvas', canvasRenderer);

/* Code for checking if the renderer is supported */

var checkedCanvas;

/**
 * Report if the canvas renderer is supported.
 *
 * @returns {boolean} true if available.
 */
canvasRenderer.supported = function () {
  if (checkedCanvas === undefined) {
    /* This is extracted from what Modernizr uses. */
    var canvas; // eslint-disable-line no-unused-vars
    try {
      canvas = document.createElement('canvas');
      checkedCanvas = !!(canvas.getContext && canvas.getContext('2d'));
    } catch (e) {
      checkedCanvas = false;
    }
    canvas = undefined;
  }
  return checkedCanvas;
};

/**
 * If the canvas renderer is not supported, supply the name of a renderer that
 * should be used instead.  This asks for the null renderer.
 *
 * @returns {null} `null` for the null renderer.
 */
canvasRenderer.fallback = function () {
  return null;
};

module.exports = canvasRenderer;

/***/ }),
/* 312 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var inherit = __webpack_require__(0);
var registerFeature = __webpack_require__(1).registerFeature;
var heatmapFeature = __webpack_require__(124);
var timestamp = __webpack_require__(10);
var util = __webpack_require__(3);

/**
 * Create a new instance of class canvas.heatmapFeature.
 * Inspired from
 *    https://github.com/mourner/simpleheat/blob/gh-pages/simpleheat.js .
 *
 * @class
 * @alias geo.canvas.heatmapFeature
 * @param {geo.heatmapFeature.spec} arg
 * @extends geo.heatmapFeature
 * @returns {canvas_heatmapFeature}
 */
var canvas_heatmapFeature = function canvas_heatmapFeature(arg) {
  'use strict';

  if (!(this instanceof canvas_heatmapFeature)) {
    return new canvas_heatmapFeature(arg);
  }
  heatmapFeature.call(this, arg);
  var object = __webpack_require__(19);

  object.call(this);

  /**
   * @private
   */
  var geo_event = __webpack_require__(6);

  var m_this = this,
      m_typedBuffer,
      m_typedClampedBuffer,
      m_typedBufferData,
      m_heatMapPosition,
      m_heatMapTransform,
      s_init = this._init,
      s_update = this._update,
      m_renderTime = timestamp();

  /**
   * Compute gradient.  This creates a color lookup table.
   *
   * @returns {this}
   */
  this._computeGradient = function () {
    var canvas, stop, context2d, gradient, colors;

    colors = m_this.style('color');
    if (!m_this._grad || m_this._gradColors !== colors) {
      canvas = document.createElement('canvas');
      context2d = canvas.getContext('2d');
      gradient = context2d.createLinearGradient(0, 0, 0, 256);

      canvas.width = 1;
      canvas.height = 256;

      for (stop in colors) {
        gradient.addColorStop(stop, util.convertColorToRGBA(colors[stop]));
      }

      context2d.fillStyle = gradient;
      context2d.fillRect(0, 0, 1, 256);
      m_this._grad = context2d.getImageData(0, 0, 1, 256).data;
      m_this._gradColors = colors;
    }

    return m_this;
  };

  /**
   * Create circle for each data point.
   *
   * @returns {this}
   */
  this._createCircle = function () {
    var circle, ctx, r, r2, blur, gaussian;
    r = m_this.style('radius');
    blur = m_this.style('blurRadius');
    gaussian = m_this.style('gaussian');
    if (!m_this._circle || m_this._circle.gaussian !== gaussian || m_this._circle.radius !== r || m_this._circle.blurRadius !== blur) {
      circle = m_this._circle = document.createElement('canvas');
      ctx = circle.getContext('2d');
      r2 = blur + r;
      circle.width = circle.height = r2 * 2;
      if (!gaussian) {
        ctx.shadowOffsetX = ctx.shadowOffsetY = r2 * 2;
        ctx.shadowBlur = blur;
        ctx.shadowColor = 'black';
        ctx.beginPath();
        ctx.arc(-r2, -r2, r, 0, Math.PI * 2, true);
        ctx.closePath();
        ctx.fill();
      } else {
        /* This approximates a gaussian distribution by using a 10-step
         * piecewise linear radial gradient.  Strictly, it should not stop at
         * the radius, but should be attenuated further.  The scale has been
         * selected such that the values at the radius are around 1/256th of
         * the maximum, and therefore would not be visible using an 8-bit alpha
         * channel for the summation.  The values for opacity were generated by
         * the python expression:
         *   from scipy.stats import norm
         *   for r in [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1]:
         *     opacity = norm.pdf(r, scale=0.3) / norm.pdf(0, scale=0.3)
         * Usng a 10-interval approximation is accurate to within 0.5% of the
         * actual Gaussian magnitude.  Switching to a 20-interval approximation
         * would get within 0.1%, at which point there is more error from using
         * a Gaussian truncated at the radius than from the approximation.
         */
        var grad = ctx.createRadialGradient(r2, r2, 0, r2, r2, r2);
        grad.addColorStop(0.0, 'rgba(255,255,255,1)');
        grad.addColorStop(0.1, 'rgba(255,255,255,0.946)');
        grad.addColorStop(0.2, 'rgba(255,255,255,0.801)');
        grad.addColorStop(0.3, 'rgba(255,255,255,0.607)');
        grad.addColorStop(0.4, 'rgba(255,255,255,0.411)');
        grad.addColorStop(0.5, 'rgba(255,255,255,0.249)');
        grad.addColorStop(0.6, 'rgba(255,255,255,0.135)');
        grad.addColorStop(0.7, 'rgba(255,255,255,0.066)');
        grad.addColorStop(0.8, 'rgba(255,255,255,0.029)');
        grad.addColorStop(0.9, 'rgba(255,255,255,0.011)');
        grad.addColorStop(1.0, 'rgba(255,255,255,0)');
        ctx.fillStyle = grad;
        ctx.fillRect(0, 0, r2 * 2, r2 * 2);
      }
      circle.radius = r;
      circle.blurRadius = blur;
      circle.gaussian = gaussian;
      m_this._circle = circle;
    }
    return m_this;
  };

  /**
   * Compute color for each pixel on the screen.
   *
   * @param {Uint8ClampedArray} pixels A 2D canvas `getImageData` buffer.
   * @param {Uint8ClampedArray} gradient A 2D canvas with 256 pixels that
   *    contain a color gradient.
   * @protected
   */
  this._colorize = function (pixels, gradient) {
    var grad = new Uint32Array(gradient.buffer),
        pixlen = pixels.length,
        i,
        j,
        k;
    if (!m_typedBuffer || m_typedBuffer.length !== pixlen) {
      m_typedBuffer = new ArrayBuffer(pixlen);
      m_typedClampedBuffer = new Uint8ClampedArray(m_typedBuffer);
      m_typedBufferData = new Uint32Array(m_typedBuffer);
    }
    for (i = 3, k = 0; i < pixlen; i += 4, k += 1) {
      // Get opacity from the temporary canvas image and look up the final
      // value from gradient
      j = pixels[i];
      if (j) {
        m_typedBufferData[k] = grad[j];
      }
    }
    pixels.set(m_typedClampedBuffer);
  };

  /**
   * Render individual data points on the canvas.
   *
   * @param {RenderingContext} context2d The canvas context to draw in.
   * @param {geo.map} map The parent map object.
   * @param {array} data The main data array.
   * @param {number} radius The sum of `radius` and `blurRadius`.
   */
  this._renderPoints = function (context2d, map, data, radius) {
    var position = m_this.gcsPosition(),
        intensityFunc = m_this.intensity(),
        minIntensity = m_this.minIntensity(),
        rangeIntensity = m_this.maxIntensity() - minIntensity || 1,
        idx,
        pos,
        intensity;

    for (idx = data.length - 1; idx >= 0; idx -= 1) {
      pos = map.worldToDisplay(position[idx]);
      intensity = (intensityFunc(data[idx], idx) - minIntensity) / rangeIntensity;
      if (intensity <= 0) {
        continue;
      }
      // Small values are not visible because globalAlpha < .01
      // cannot be read from imageData
      context2d.globalAlpha = intensity < 0.01 ? 0.01 : intensity > 1 ? 1 : intensity;
      context2d.drawImage(m_this._circle, pos.x - radius, pos.y - radius);
    }
  };

  /**
   * Render data points on the canvas by binning.
   *
   * @param {RenderingContext} context2d The canvas context to draw in.
   * @param {geo.map} map The parent map object.
   * @param {array} data The main data array.
   * @param {number} radius The sum of `radius` and `blurRadius`.
   * @param {number} binSize Size of the bins in pixels.
   */
  this._renderBinnedData = function (context2d, map, data, radius, binSize) {
    var position = m_this.gcsPosition(),
        intensityFunc = m_this.intensity(),
        minIntensity = m_this.minIntensity(),
        rangeIntensity = m_this.maxIntensity() - minIntensity || 1,
        viewport = map.camera()._viewport,
        bins = [],
        rw = Math.ceil(radius / binSize),
        maxx = Math.ceil(viewport.width / binSize) + rw * 2 + 2,
        maxy = Math.ceil(viewport.height / binSize) + rw * 2 + 2,
        datalen = data.length,
        idx,
        pos,
        intensity,
        x,
        y,
        binrow,
        offsetx,
        offsety;

    /* We create bins of size (binSize) pixels on a side.  We only track bins
     * that are on the viewport or within the radius of it, plus one extra bin
     * width. */
    for (idx = 0; idx < datalen; idx += 1) {
      pos = map.worldToDisplay(position[idx]);
      /* To make the results look more stable, we use the first data point as a
       * hard-reference to where the bins should line up.  Otherwise, as we pan
       * points would shift which bin they are in and the display would ripple
       * oddly. */
      if (isNaN(pos.x) || isNaN(pos.y)) {
        continue;
      }
      if (offsetx === undefined) {
        offsetx = (pos.x % binSize + binSize) % binSize;
        offsety = (pos.y % binSize + binSize) % binSize;
      }
      /* We handle points that are in the viewport, plus the radius on either
       * side, as they will add into the visual effect, plus one additional bin
       * to account for the offset alignment. */
      x = Math.floor((pos.x - offsetx) / binSize) + rw + 1;
      if (x < 0 || x >= maxx) {
        continue;
      }
      y = Math.floor((pos.y - offsety) / binSize) + rw + 1;
      if (y < 0 || y >= maxy) {
        continue;
      }
      intensity = (intensityFunc(data[idx], idx) - minIntensity) / rangeIntensity;
      if (intensity <= 0) {
        continue;
      }
      if (intensity > 1) {
        intensity = 1;
      }
      /* bins is an array of arrays.  The subarrays would be conceptually
       * better represented as an array of dicts, but having a sparse array is
       * uses much less memory and is faster.  Each bin uses four array entries
       * that are (weight, intensity, x, y).  The weight is the sum of the
       * intensities for all points in the bin.  The intensity is the geometric
       * sum of the intensities to approximate what happens to the unbinned
       * data on the alpha channel of the canvas.  The x and y coordinates are
       * weighted by the intensity of each point. */
      bins[y] = bins[y] || [];
      x *= 4;
      binrow = bins[y];
      if (!binrow[x]) {
        binrow[x] = binrow[x + 1] = intensity;
        binrow[x + 2] = pos.x * intensity;
        binrow[x + 3] = pos.y * intensity;
      } else {
        binrow[x] += intensity; // weight
        binrow[x + 1] += (1 - binrow[x + 1]) * intensity;
        binrow[x + 2] += pos.x * intensity;
        binrow[x + 3] += pos.y * intensity;
      }
    }
    /* For each bin, render a point on the canvas. */
    for (y = bins.length - 1; y >= 0; y -= 1) {
      binrow = bins[y];
      if (binrow) {
        for (x = binrow.length - 4; x >= 0; x -= 4) {
          if (binrow[x]) {
            intensity = binrow[x + 1];
            context2d.globalAlpha = intensity < 0.01 ? 0.01 : intensity > 1 ? 1 : intensity;
            /* The position is eighted by the intensities, so we have to divide
             * it to get the necessary position */
            context2d.drawImage(m_this._circle, binrow[x + 2] / binrow[x] - radius, binrow[x + 3] / binrow[x] - radius);
          }
        }
      }
    }
  };

  /**
   * Render the data on the canvas, then colorize the resulting opacity map.
   *
   * @param {RenderingContext} context2d The canvas context to draw in.
   * @param {geo.map} map The parent map object.
   * @returns {this}
   */
  this._renderOnCanvas = function (context2d, map) {

    if (m_renderTime.getMTime() < m_this.buildTime().getMTime()) {
      var data = m_this.data() || [],
          radius = m_this.style('radius') + m_this.style('blurRadius'),
          binned = m_this.binned(),
          canvas,
          pixelArray,
          layer = m_this.layer(),
          viewport = map.camera()._viewport;

      /* Determine if we should bin the data */
      if (binned === true || binned === 'auto') {
        binned = Math.max(Math.floor(radius / 8), 3);
        if (m_this.binned() === 'auto') {
          var numbins = Math.ceil((viewport.width + radius * 2) / binned) * Math.ceil((viewport.height + radius * 2) / binned);
          if (numbins >= data.length) {
            binned = 0;
          }
        }
      }
      if (binned < 1 || isNaN(binned)) {
        binned = false;
      }
      /* Store what we did, in case this is ever useful elsewhere */
      m_this._binned = binned;

      context2d.setTransform(1, 0, 0, 1, 0, 0);
      context2d.clearRect(0, 0, viewport.width, viewport.height);
      m_heatMapTransform = '';
      map.scheduleAnimationFrame(m_this._setTransform, false);
      layer.canvas().css({ transform: '' });

      m_this._createCircle();
      m_this._computeGradient();
      if (!binned) {
        m_this._renderPoints(context2d, map, data, radius);
      } else {
        m_this._renderBinnedData(context2d, map, data, radius, binned);
      }
      canvas = layer.canvas()[0];
      pixelArray = context2d.getImageData(0, 0, canvas.width, canvas.height);
      m_this._colorize(pixelArray.data, m_this._grad);
      context2d.putImageData(pixelArray, 0, 0);

      m_heatMapPosition = {
        zoom: map.zoom(),
        gcsOrigin: map.displayToGcs({ x: 0, y: 0 }, null),
        rotation: map.rotation(),
        lastScale: undefined,
        lastOrigin: { x: 0, y: 0 },
        lastRotation: undefined
      };
      m_renderTime.modified();
      layer.renderer().clearCanvas(false);
    }

    return m_this;
  };

  /**
   * Initialize.
   *
   * @returns {this}
   */
  this._init = function () {
    s_init.call(m_this, arg);

    m_this.geoOn(geo_event.pan, m_this._animatePan);

    return m_this;
  };

  /**
   * Update the feature.
   *
   * @returns {this}
   */
  this._update = function () {
    s_update.call(m_this);
    if (m_this.buildTime().getMTime() <= m_this.dataTime().getMTime() || m_this.updateTime().getMTime() < m_this.getMTime()) {
      m_this._build();
    }
    m_this.updateTime().modified();
    return m_this;
  };

  /**
   * Update the css transform for the layer as part of an animation frame.
   * This allows an existing rendered version of the heatmap to appear with a
   * transform until a new version can be computed.
   */
  this._setTransform = function () {
    if (m_this.layer() && m_this.layer().canvas() && m_this.layer().canvas()[0]) {
      m_this.layer().canvas()[0].style.transform = m_heatMapTransform;
    }
  };

  /**
   * Animate pan and zoom.
   */
  this._animatePan = function () {

    if (!m_heatMapPosition) {
      return;
    }
    var map = m_this.layer().map(),
        zoom = map.zoom(),
        scale = Math.pow(2, zoom - m_heatMapPosition.zoom),
        origin = map.gcsToDisplay(m_heatMapPosition.gcsOrigin, null),
        rotation = map.rotation();

    if (m_heatMapPosition.lastScale === scale && m_heatMapPosition.lastOrigin.x === origin.x && m_heatMapPosition.lastOrigin.y === origin.y && m_heatMapPosition.lastRotation === rotation) {
      return;
    }

    var transform = '' + ' translate(' + origin.x + 'px' + ',' + origin.y + 'px' + ')' + ' scale(' + scale + ')' + ' rotate(' + (rotation - m_heatMapPosition.rotation) * 180 / Math.PI + 'deg)';

    map.scheduleAnimationFrame(m_this._setTransform);

    m_heatMapTransform = transform;
    m_heatMapPosition.lastScale = scale;
    m_heatMapPosition.lastOrigin.x = origin.x;
    m_heatMapPosition.lastOrigin.y = origin.y;
    m_heatMapPosition.lastRotation = rotation;

    if (m_heatMapPosition.timeout) {
      window.clearTimeout(m_heatMapPosition.timeout);
      m_heatMapPosition.timeout = undefined;
    }
    /* This conditional can change if we compute the heatmap beyond the visable
     * viewport so that we don't have to update on pans as often.  If we are
     * close to where the heatmap was originally computed, don't bother
     * updating it. */
    if (parseFloat(scale.toFixed(4)) !== 1 || parseFloat((rotation - m_heatMapPosition.rotation).toFixed(4)) !== 0 || parseFloat(origin.x.toFixed(1)) !== 0 || parseFloat(origin.y.toFixed(1)) !== 0) {
      m_heatMapPosition.timeout = window.setTimeout(function () {
        m_heatMapPosition.timeout = undefined;
        m_this.buildTime().modified();
        m_this.layer().draw();
      }, m_this.updateDelay());
    }
  };

  m_this._init(arg);
  return this;
};

inherit(canvas_heatmapFeature, heatmapFeature);

// Now register it
registerFeature('canvas', 'heatmap', canvas_heatmapFeature);
module.exports = canvas_heatmapFeature;

/***/ }),
/* 313 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var inherit = __webpack_require__(0);
var registerFeature = __webpack_require__(1).registerFeature;
var isolineFeature = __webpack_require__(55);

/**
 * Create a new instance of class isolineFeature.
 *
 * @class
 * @alias geo.canvas.isolineFeature
 * @extends geo.isolineFeature
 * @param {geo.isolineFeature.spec} arg
 * @returns {geo.canvas.isolineFeature}
 */
var canvas_isolineFeature = function canvas_isolineFeature(arg) {
  'use strict';

  if (!(this instanceof canvas_isolineFeature)) {
    return new canvas_isolineFeature(arg);
  }

  arg = arg || {};
  isolineFeature.call(this, arg);

  var object = __webpack_require__(19);
  object.call(this);

  this._init(arg);
  return this;
};

inherit(canvas_isolineFeature, isolineFeature);

// Now register it
registerFeature('canvas', 'isoline', canvas_isolineFeature);
module.exports = canvas_isolineFeature;

/***/ }),
/* 314 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var inherit = __webpack_require__(0);
var registerFeature = __webpack_require__(1).registerFeature;
var lineFeature = __webpack_require__(23);

/**
 * Create a new instance of class lineFeature.
 *
 * @class
 * @alias geo.canvas.lineFeature
 * @extends geo.lineFeature
 * @param {geo.lineFeature.spec} arg
 * @returns {geo.canvas.lineFeature}
 */
var canvas_lineFeature = function canvas_lineFeature(arg) {
  'use strict';

  if (!(this instanceof canvas_lineFeature)) {
    return new canvas_lineFeature(arg);
  }

  var object = __webpack_require__(19);

  arg = arg || {};
  lineFeature.call(this, arg);
  object.call(this);

  /**
   * @private
   */
  var m_this = this;

  /**
   * Render the data on the canvas.
   *
   * @protected
   * @param {object} context2d the canvas context to draw in.
   * @param {geo.map} map the parent map object.
   */
  this._renderOnCanvas = function (context2d, map) {
    var data = m_this.data(),
        posFunc = m_this.position(),
        lineFunc = m_this.line(),
        strokeWidthFunc = m_this.style.get('strokeWidth'),
        strokeColorFunc = m_this.style.get('strokeColor'),
        strokeOpacityFunc = m_this.style.get('strokeOpacity'),
        lineCapFunc = m_this.style.get('lineCap'),
        lineJoinFunc = m_this.style.get('lineJoin'),
        miterLimit = m_this.style.get('miterLimit')(data),
        closedFunc = m_this.style.get('closed'),
        last = {},
        cur = {},
        temp,
        line,
        pos,
        firstPos,
        j;

    data.forEach(function (d, i) {
      line = lineFunc(d, i);
      if (line.length < 2) {
        return;
      }
      cur.closed = closedFunc(d, i);
      cur.width = strokeWidthFunc(line[0], 0, d, i);
      cur.color = strokeColorFunc(line[0], 0, d, i);
      cur.opacity = strokeOpacityFunc(line[0], 0, d, i);
      cur.linecap = lineCapFunc(line[0], 0, d, i);
      cur.linejoin = lineJoinFunc(line[0], 0, d, i);
      cur.strokeStyle = 'rgba(' + cur.color.g * 255 + ', ' + cur.color.g * 255 + ', ' + cur.color.b * 255 + ', ' + (cur.opacity !== undefined ? cur.opacity : 1) + ')';
      if (last.strokeStyle !== cur.strokeStyle || last.width !== cur.width || last.linecap !== cur.linecap || last.linejoin !== cur.linejoin || last.miterlimit !== cur.miterlimit) {
        if (last.strokeStyle !== undefined) {
          context2d.stroke();
        }
        context2d.beginPath();
        context2d.strokeStyle = cur.strokeStyle;
        context2d.lineWidth = cur.width;
        context2d.lineCap = cur.linecap;
        context2d.lineJoin = cur.linejoin;
        context2d.miterLimit = miterLimit;
      }
      for (j = 0; j < line.length; j += 1) {
        pos = m_this.featureGcsToDisplay(posFunc(line[j], j, d, i));
        if (!j) {
          firstPos = pos;
          context2d.moveTo(pos.x, pos.y);
        } else {
          context2d.lineTo(pos.x, pos.y);
        }
      }
      if (cur.closed) {
        context2d.lineTo(firstPos.x, firstPos.y);
      }
      temp = last;
      last = cur;
      cur = temp;
    });
    if (last.strokeStyle !== undefined) {
      context2d.stroke();
    }
  };

  this._init(arg);
  return this;
};

inherit(canvas_lineFeature, lineFeature);

// Now register it
var capabilities = {};
capabilities[lineFeature.capabilities.basic] = true;
capabilities[lineFeature.capabilities.multicolor] = false;

registerFeature('canvas', 'line', canvas_lineFeature, capabilities);

module.exports = canvas_lineFeature;

/***/ }),
/* 315 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var inherit = __webpack_require__(0);
var registerFeature = __webpack_require__(1).registerFeature;
var pixelmapFeature = __webpack_require__(130);

/**
 * Create a new instance of class pixelmapFeature
 *
 * @class
 * @alias geo.canvas.pixelmapFeature
 * @param {Object} arg Options object
 * @extends geo.pixelmapFeature
 * @returns {canvas_pixelmapFeature}
 */
var canvas_pixelmapFeature = function canvas_pixelmapFeature(arg) {
  'use strict';

  if (!(this instanceof canvas_pixelmapFeature)) {
    return new canvas_pixelmapFeature(arg);
  }
  pixelmapFeature.call(this, arg);

  var object = __webpack_require__(19);
  object.call(this);

  this._init(arg);
  return this;
};

inherit(canvas_pixelmapFeature, pixelmapFeature);

// Now register it
registerFeature('canvas', 'pixelmap', canvas_pixelmapFeature);
module.exports = canvas_pixelmapFeature;

/***/ }),
/* 316 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var inherit = __webpack_require__(0);
var registerFeature = __webpack_require__(1).registerFeature;
var quadFeature = __webpack_require__(17);
var util = __webpack_require__(3);

/**
 * Create a new instance of class quadFeature.
 *
 * @class
 * @alias geo.canvas.quadFeature
 * @param {geo.quadFeature.spec} arg Options object.
 * @extends geo.quadFeature
 * @returns {geo.canvas.quadFeature}
 */
var canvas_quadFeature = function canvas_quadFeature(arg) {
  'use strict';

  if (!(this instanceof canvas_quadFeature)) {
    return new canvas_quadFeature(arg);
  }
  quadFeature.call(this, arg);

  var object = __webpack_require__(19);
  object.call(this);

  var $ = __webpack_require__(2);

  var m_this = this,
      s_exit = this._exit,
      s_init = this._init,
      s_update = this._update,
      m_quads;

  /**
   * Build this feature.
   */
  this._build = function () {
    if (!m_this.position()) {
      return;
    }
    m_quads = this._generateQuads();

    if (m_quads.imgQuads) {
      m_quads.imgQuads.sort(function (a, b) {
        return a.pos[2] - b.pos[2];
      });
    }
    m_this.buildTime().modified();
  };

  /**
   * When any quad may have changed, ask for a animation frame callback so we
   * can update the quad on the next animation cycle.
   *
   * This is called when a video qaud may have changed play state.
   * @param {object} quad The quad record that triggered this.
   * @param {jQuery.Event} [evt] The event that triggered this.
   */
  this._checkQuadUpdate = function (quad, evt) {
    m_this.layer().map().scheduleAnimationFrame(m_this._checkIfChanged);
  };

  /**
   * Check if any video quads are changing or need rerendering.  If any are
   * changing (because they are seeking), defer rendering and check again.  If
   * any need rendering, schedule it.
   */
  this._checkIfChanged = function () {
    if (!m_quads || !m_quads.vidQuads || !m_quads.vidQuads.length) {
      return;
    }
    var render = false,
        changing = false;

    $.each(m_quads.vidQuads, function (idx, quad) {
      if (quad.video && quad.video.HAVE_CURRENT_DATA !== undefined) {
        if (!quad.video.seeking && quad.video.readyState >= quad.video.HAVE_CURRENT_DATA) {
          render = true;
        }
        if (!quad.video.paused || quad.video.seeking) {
          changing = true;
        }
      }
    });
    if (render) {
      m_this.renderer()._render();
    }
    if (changing) {
      m_this.layer().map().scheduleAnimationFrame(m_this._checkIfChanged);
    }
  };

  /**
   * Render all of the color quads.
   *
   * @param {CanvasRenderingContext2D} context2d The rendering context.
   * @param {geo.map} map The current renderer's parent map.
   */
  this._renderColorQuads = function (context2d, map) {
    if (!m_quads.clrQuads || !m_quads.clrQuads.length) {
      return;
    }
    var oldAlpha = context2d.globalAlpha;
    var opacity = oldAlpha;
    $.each(m_quads.clrQuads, function (idx, quad) {
      var p0 = map.gcsToDisplay({ x: quad.pos[0], y: quad.pos[1] }, null),
          p1 = map.gcsToDisplay({ x: quad.pos[3], y: quad.pos[4] }, null),
          p2 = map.gcsToDisplay({ x: quad.pos[6], y: quad.pos[7] }, null),
          p3 = map.gcsToDisplay({ x: quad.pos[9], y: quad.pos[10] }, null);
      if (quad.opacity !== opacity) {
        opacity = quad.opacity;
        context2d.globalAlpha = opacity;
      }
      context2d.fillStyle = util.convertColorToHex(quad.color, true);
      context2d.beginPath();
      context2d.moveTo(p0.x, p0.y);
      context2d.lineTo(p1.x, p1.y);
      context2d.lineTo(p3.x, p3.y);
      context2d.lineTo(p2.x, p2.y);
      context2d.closePath();
      context2d.fill();
    });
    if (opacity !== oldAlpha) {
      context2d.globalAlpha = oldAlpha;
    }
  };

  /**
   * Render all of the image and video quads.
   *
   * @param {CanvasRenderingContext2D} context2d The rendering context.
   * @param {geo.map} map The current renderer's parent map.
   */
  this._renderImageAndVideoQuads = function (context2d, map) {
    if ((!m_quads.imgQuads || !m_quads.imgQuads.length) && (!m_quads.vidQuads || !m_quads.vidQuads.length)) {
      return;
    }

    var oldAlpha = context2d.globalAlpha;
    var opacity = oldAlpha;
    $.each([m_quads.imgQuads, m_quads.vidQuads], function (listidx, quadlist) {
      if (!quadlist) {
        return;
      }
      $.each(quadlist, function (idx, quad) {
        var src, w, h;
        if (quad.image) {
          src = quad.image;
          w = src.width;
          h = src.height;
        } else if (quad.video) {
          src = quad.video;
          w = src.videoWidth;
          h = src.videoHeight;
          if (src.seeking) {
            return;
          }
        }
        if (!src || !w || !h || quad.opacity <= 0) {
          return;
        }
        // Canvas transform is affine, so quad has to be a parallelogram
        // Also, canvas has no way to render z.
        var p0 = map.gcsToDisplay({ x: quad.pos[0], y: quad.pos[1] }, null),
            p2 = map.gcsToDisplay({ x: quad.pos[6], y: quad.pos[7] }, null),
            p3 = map.gcsToDisplay({ x: quad.pos[9], y: quad.pos[10] }, null);
        context2d.setTransform((p3.x - p2.x) / w, (p3.y - p2.y) / w, (p0.x - p2.x) / h, (p0.y - p2.y) / h, p2.x, p2.y);
        if (quad.opacity !== opacity) {
          opacity = quad.opacity;
          context2d.globalAlpha = opacity;
        }
        if (!quad.crop) {
          context2d.drawImage(src, 0, 0);
        } else {
          var cropx = Math.min(src.w, quad.crop.x),
              cropy = Math.min(src.h, quad.crop.y);
          if (cropx > 0 && cropy > 0) {
            context2d.drawImage(src, 0, 0, cropx, cropy, 0, 0, cropx, cropy);
          }
        }
      });
    });
    if (opacity !== oldAlpha) {
      context2d.globalAlpha = oldAlpha;
    }
    context2d.setTransform(1, 0, 0, 1, 0, 0);
  };

  /**
   * If this returns true, the render will be skipped and tried again on the
   * next animation frame.
   *
   * @returns {boolean} Truthy to delay rendering.
   */
  this._delayRender = function () {
    var delay = false;
    if (m_quads && m_quads.vidQuads && m_quads.vidQuads.length) {
      $.each(m_quads.vidQuads, function (idx, quad) {
        if (quad.video && quad.video.HAVE_CURRENT_DATA !== undefined) {
          delay |= quad.video.seeking && quad.delayRenderWhenSeeking;
        }
      });
    }
    return delay;
  };

  /**
   * Render all of the quads.
   *
   * @param {CanvasRenderingContext2D} context The rendering context.
   * @param {geo.map} map The current renderer's parent map.
   */
  this._renderOnCanvas = function (context, map) {
    if (m_quads) {
      this._renderImageAndVideoQuads(context, map);
      this._renderColorQuads(context, map);
    }
  };

  /**
   * Update.
   */
  this._update = function () {
    s_update.call(m_this);
    if (m_this.buildTime().getMTime() <= m_this.dataTime().getMTime() || m_this.updateTime().getMTime() < m_this.getMTime()) {
      m_this._build();
    }

    m_this.updateTime().modified();
    m_this.layer().map().scheduleAnimationFrame(m_this._checkIfChanged);
  };

  /**
   * Initialize.
   */
  this._init = function () {
    s_init.call(m_this, arg);
  };

  /**
   * Destroy.
   */
  this._exit = function () {

    s_exit.call(m_this);
  };

  m_this._init(arg);
  return this;
};

inherit(canvas_quadFeature, quadFeature);

// Now register it
var capabilities = {};
capabilities[quadFeature.capabilities.color] = true;
capabilities[quadFeature.capabilities.image] = true;
capabilities[quadFeature.capabilities.imageCrop] = true;
capabilities[quadFeature.capabilities.imageFixedScale] = true;
capabilities[quadFeature.capabilities.imageFull] = false;
capabilities[quadFeature.capabilities.canvas] = true;
capabilities[quadFeature.capabilities.video] = true;

registerFeature('canvas', 'quad', canvas_quadFeature, capabilities);
module.exports = canvas_quadFeature;

/***/ }),
/* 317 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var inherit = __webpack_require__(0);
var registerFeature = __webpack_require__(1).registerFeature;
var textFeature = __webpack_require__(25);
var util = __webpack_require__(3);
var mat3 = __webpack_require__(116);
var vec3 = __webpack_require__(48);

/**
 * Create a new instance of class canvas.textFeature.
 *
 * @class
 * @alias geo.canvas.textFeature
 * @extends geo.textFeature
 * @extends geo.canvas.object
 *
 * @param {geo.textFeature.spec} [arg] Options for the feature.
 * @returns {geo.canvas.textFeature} The created feature.
 */
var canvas_textFeature = function canvas_textFeature(arg) {
  'use strict';

  if (!(this instanceof canvas_textFeature)) {
    return new canvas_textFeature(arg);
  }

  var object = __webpack_require__(19);

  arg = arg || {};
  textFeature.call(this, arg);
  object.call(this);

  /**
   * @private
   */
  var m_this = this,
      m_defaultFont = 'bold 16px sans-serif',

  /* This regexp parses css font specifications into style, variant,
   * weight, stretch, size, line height, and family.  It is based on a
   * regexp here: https://stackoverflow.com/questions/10135697/regex-to-parse-any-css-font,
   * but has been modified to fix some issues and handle font stretch. */
  m_cssFontRegExp = new RegExp('^\\s*' + '(?=(?:(?:[-a-z0-9]+\\s+){0,3}(italic|oblique))?)' + '(?=(?:(?:[-a-z0-9]+\\s+){0,3}(small-caps))?)' + '(?=(?:(?:[-a-z0-9]+\\s+){0,3}(bold(?:er)?|lighter|[1-9]00))?)' + '(?=(?:(?:[-a-z0-9]+\\s+){0,3}((?:ultra-|extra-|semi-)?(?:condensed|expanded)))?)' + '(?:(?:normal|\\1|\\2|\\3|\\4)\\s+){0,4}' + '((?:xx?-)?(?:small|large)|medium|smaller|larger|[.\\d]+(?:\\%|in|[cem]m|ex|p[ctx]))' + '(?:/(normal|[.\\d]+(?:\\%|in|[cem]m|ex|p[ctx])))?\\s+' + '([-,\\"\\sa-z]+?)\\s*$', 'i');

  /**
   * Get the font for a specific data item.  This falls back to the default
   * font if the value is unset or doesn't contain sufficient information.
   *
   * @param {boolean} useSubValues If truthy, check all font styles (such as
   *    `fontSize`, `lineHeight`, etc., and override the code `font` style
   *    with those values.  If falsy, only use `font`.
   * @param {object} d The current data element.
   * @param {number} i The index of the current data element.
   * @returns {string} The font style.
   */
  this.getFontFromStyles = function (useSubValues, d, i) {
    var font = m_this.style.get('font')(d, i) || m_defaultFont;
    if (useSubValues) {
      var parts = m_cssFontRegExp.exec(font);
      if (parts === null) {
        parts = m_cssFontRegExp.exec(m_defaultFont);
      }
      parts[1] = m_this.style.get('fontStyle')(d, i) || parts[1];
      parts[2] = m_this.style.get('fontVariant')(d, i) || parts[2];
      parts[3] = m_this.style.get('fontWeight')(d, i) || parts[3];
      parts[4] = m_this.style.get('fontStretch')(d, i) || parts[4];
      parts[5] = m_this.style.get('fontSize')(d, i) || parts[5] || '16px';
      parts[6] = m_this.style.get('lineHeight')(d, i) || parts[6];
      parts[7] = m_this.style.get('fontFamily')(d, i) || parts[7] || 'sans-serif';
      font = (parts[1] || '') + ' ' + (parts[2] || '') + ' ' + (parts[3] || '') + ' ' + (parts[4] || '') + ' ' + (parts[5] || '') + (parts[6] ? '/' + parts[6] : '') + ' ' + parts[7];
      font = font.trim().replace(/\s\s+/g, ' ');
    }
    return font;
  };

  /**
   * Render the data on the canvas.
   *
   * This does not currently support multiline text or word wrapping, since
   * canvas doesn't implement that directly.  To support these, each text item
   * would need to be split on line breaks, and have the width of the text
   * calculated with context2d.measureText to determine word wrapping.  This
   * would also need to calculate the effective line height from the font
   * specification.
   *
   * @protected
   * @param {CanvasRenderingContext2D} context2d The canvas context to draw in.
   * @param {geo.map} map The parent map object.
   */
  this._renderOnCanvas = function (context2d, map) {
    var data = m_this.data(),
        posFunc = m_this.style.get('position'),
        renderThreshold = m_this.style.get('renderThreshold')(data),
        textFunc = m_this.style.get('text'),
        mapRotation = map.rotation(),
        mapZoom = map.zoom(),
        mapSize = map.size(),
        fontFromSubValues,
        text,
        posArray,
        pos,
        visible,
        color,
        blur,
        stroke,
        width,
        rotation,
        rotateWithMap,
        scale,
        offset,
        transform,
        lastTransform = util.mat3AsArray();

    /* If any of the font styles other than `font` have values, then we need to
     * construct a single font value from the subvalues.  Otherwise, we can
     * skip it. */
    fontFromSubValues = ['fontStyle', 'fontVariant', 'fontWeight', 'fontStretch', 'fontSize', 'lineHeight', 'fontFamily'].some(function (key) {
      return m_this.style(key) !== null && m_this.style(key) !== undefined;
    });
    /* Clear the canvas property buffer */
    m_this._canvasProperty();
    posArray = m_this.featureGcsToDisplay(data.map(posFunc));
    data.forEach(function (d, i) {
      /* If the position is far enough outside of the map viewport, don't
       * render it, even if the offset of size would be sufficient to make it
       * appear in the viewport. */
      pos = posArray[i];
      if (renderThreshold > 0 && (pos.x < -renderThreshold || pos.x > mapSize.width + renderThreshold || pos.y < -renderThreshold || pos.y > mapSize.height + renderThreshold)) {
        return;
      }
      visible = m_this.style.get('visible')(d, i);
      if (!visible && visible !== undefined) {
        return;
      }
      color = util.convertColorAndOpacity(m_this.style.get('color')(d, i), m_this.style.get('textOpacity')(d, i));
      stroke = util.convertColorAndOpacity(m_this.style.get('textStrokeColor')(d, i), m_this.style.get('textOpacity')(d, i), { r: 0, g: 0, b: 0, a: 0 });
      if (color.a === 0 && stroke.a === 0) {
        return;
      }
      m_this._canvasProperty(context2d, 'fillStyle', util.convertColorToRGBA(color));
      text = textFunc(d, i);
      m_this._canvasProperty(context2d, 'font', m_this.getFontFromStyles(fontFromSubValues, d, i));
      m_this._canvasProperty(context2d, 'textAlign', m_this.style.get('textAlign')(d, i) || 'center');
      m_this._canvasProperty(context2d, 'textBaseline', m_this.style.get('textBaseline')(d, i) || 'middle');
      /* rotation, scale, and offset */
      rotation = m_this.style.get('rotation')(d, i) || 0;
      rotateWithMap = m_this.style.get('rotateWithMap')(d, i) && mapRotation;
      scale = m_this.style.get('textScaled')(d, i);
      scale = util.isNonNullFinite(scale) ? Math.pow(2, mapZoom - scale) : null;
      offset = m_this.style.get('offset')(d, i);
      transform = util.mat3AsArray();
      if (rotation || rotateWithMap || scale && scale !== 1 || offset && (offset.x || offset.y)) {
        mat3.translate(transform, transform, [pos.x, pos.y]);
        if (rotateWithMap && mapRotation) {
          mat3.rotate(transform, transform, mapRotation);
        }
        mat3.translate(transform, transform, [offset && offset.x ? +offset.x : 0, offset && offset.y ? +offset.y : 0]);
        if (rotation) {
          mat3.rotate(transform, transform, rotation);
        }
        if (scale && scale !== 1) {
          mat3.scale(transform, transform, [scale, scale]);
        }
        mat3.translate(transform, transform, [-pos.x, -pos.y]);
      }
      if (lastTransform[0] !== transform[0] || lastTransform[1] !== transform[1] || lastTransform[3] !== transform[3] || lastTransform[4] !== transform[4] || lastTransform[6] !== transform[6] || lastTransform[7] !== transform[7]) {
        context2d.setTransform(transform[0], transform[1], transform[3], transform[4], transform[6], transform[7]);
        mat3.copy(lastTransform, transform);
      }
      /* shadow */
      color = util.convertColorAndOpacity(m_this.style.get('shadowColor')(d, i), undefined, { r: 0, g: 0, b: 0, a: 0 });
      if (color.a) {
        offset = m_this.style.get('shadowOffset')(d, i);
        blur = m_this.style.get('shadowBlur')(d, i);
      }
      if (color.a && (offset && (offset.x || offset.y) || blur)) {
        m_this._canvasProperty(context2d, 'shadowColor', util.convertColorToRGBA(color));
        if (offset && (rotation || rotateWithMap) && m_this.style.get('shadowRotate')(d, i)) {
          transform = [+offset.x, +offset.y, 0];
          vec3.rotateZ(transform, transform, [0, 0, 0], rotation + (rotateWithMap ? mapRotation : 0));
          offset = { x: transform[0], y: transform[1] };
        }
        m_this._canvasProperty(context2d, 'shadowOffsetX', offset && offset.x ? +offset.x : 0);
        m_this._canvasProperty(context2d, 'shadowOffsetY', offset && offset.y ? +offset.y : 0);
        m_this._canvasProperty(context2d, 'shadowBlur', blur || 0);
      } else {
        m_this._canvasProperty(context2d, 'shadowColor', 'rgba(0,0,0,0)');
      }
      /* draw the text */
      if (stroke.a) {
        width = m_this.style.get('textStrokeWidth')(d, i);
        if (isFinite(width) && width > 0) {
          m_this._canvasProperty(context2d, 'strokeStyle', util.convertColorToRGBA(stroke));
          m_this._canvasProperty(context2d, 'lineWidth', width);
          context2d.strokeText(text, pos.x, pos.y);
          m_this._canvasProperty(context2d, 'shadowColor', 'rgba(0,0,0,0)');
        }
      }
      context2d.fillText(text, pos.x, pos.y);
    });
    m_this._canvasProperty(context2d, 'globalAlpha', 1);
    context2d.setTransform(1, 0, 0, 1, 0, 0);
  };

  return this;
};

inherit(canvas_textFeature, textFeature);

// Now register it
var capabilities = {};

registerFeature('canvas', 'text', canvas_textFeature, capabilities);

module.exports = canvas_textFeature;

/***/ }),
/* 318 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var registerLayerAdjustment = __webpack_require__(1).registerLayerAdjustment;

var canvas_tileLayer = function canvas_tileLayer() {
  'use strict';

  var m_this = this,
      s_init = this._init,
      s_exit = this._exit,
      m_quadFeature,
      m_nextTileId = 0,
      m_tiles = [];

  /* Add a tile to the list of quads */
  this._drawTile = function (tile) {
    if (!m_quadFeature) {
      return;
    }
    var bounds = this._tileBounds(tile),
        level = tile.index.level || 0,
        to = this._tileOffset(level),
        crop = this.tileCropFromBounds(tile),
        quad = {};
    if (crop) {
      quad.crop = crop;
    }
    quad.ul = this.fromLocal(this.fromLevel({
      x: bounds.left - to.x, y: bounds.top - to.y
    }, level), 0);
    quad.ll = this.fromLocal(this.fromLevel({
      x: bounds.left - to.x, y: bounds.bottom - to.y
    }, level), 0);
    quad.ur = this.fromLocal(this.fromLevel({
      x: bounds.right - to.x, y: bounds.top - to.y
    }, level), 0);
    quad.lr = this.fromLocal(this.fromLevel({
      x: bounds.right - to.x, y: bounds.bottom - to.y
    }, level), 0);
    quad.ul.z = quad.ll.z = quad.ur.z = quad.lr.z = level * m_this._levelZIncrement;
    m_nextTileId += 1;
    quad.id = m_nextTileId;
    tile.quadId = quad.id;
    quad.image = tile.image;
    m_tiles.push(quad);
    m_quadFeature.data(m_tiles);
    m_quadFeature._update();
    m_this.draw();
  };

  /* Remove the tile feature. */
  this._remove = function (tile) {
    if (tile.quadId !== undefined && m_quadFeature) {
      for (var i = 0; i < m_tiles.length; i += 1) {
        if (m_tiles[i].id === tile.quadId) {
          m_tiles.splice(i, 1);
          break;
        }
      }
      m_quadFeature.data(m_tiles);
      m_quadFeature._update();
      m_this.draw();
    }
  };

  /**
   * Clean up the layer.
   */
  this._exit = function () {
    m_this.deleteFeature(m_quadFeature);
    m_quadFeature = null;
    m_tiles = [];
    s_exit.apply(m_this, arguments);
  };

  /**
   * Initialize after the layer is added to the map.
   */
  this._init = function () {
    s_init.apply(m_this, arguments);
    m_quadFeature = this.createFeature('quad', {
      previewColor: m_this._options.previewColor,
      previewImage: m_this._options.previewImage
    });
    m_quadFeature.geoTrigger = undefined;
    m_quadFeature.gcs(m_this._options.gcs || m_this.map().gcs());
    m_quadFeature.data(m_tiles);
    m_quadFeature._update();
  };

  /* These functions don't need to do anything. */
  this._getSubLayer = function () {};
  this._updateSubLayers = undefined;
};

registerLayerAdjustment('canvas', 'tile', canvas_tileLayer);
module.exports = canvas_tileLayer;

/***/ }),
/* 319 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/**
 * @namespace geo.gui
 */
module.exports = {
  colorLegendWidget: __webpack_require__(320),
  domWidget: __webpack_require__(132),
  legendWidget: __webpack_require__(323),
  scaleWidget: __webpack_require__(324),
  sliderWidget: __webpack_require__(327),
  svgWidget: __webpack_require__(36),
  uiLayer: __webpack_require__(126),
  widget: __webpack_require__(56)
};

/***/ }),
/* 320 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var d3 = __webpack_require__(11).d3;
var domWidget = __webpack_require__(132);
var inherit = __webpack_require__(0);
var registerWidget = __webpack_require__(1).registerWidget;
var util = __webpack_require__(3);
var uniqueID = __webpack_require__(52);

__webpack_require__(321);

/**
 * @typedef {object} geo.gui.colorLegendWidget.category
 * @property {string} name The text label of the legend.
 * @property {string} type The type of the legend, either discrete or continuous.
 * @property {string} scale The scale of the legend. For discrete type,
 * linear, log, sqrt, pow, ordinal, and quantile is supported.
 * For continuous type, linear, log, sqrt, and pow is supported.
 * @property {number[]|string[]} domain Only for ordinal scale legend, string
 * values are acceptable. For ordinal legend, the number in the domain array
 * should be the same number of colors. For quantile scale legend, the domain
 * should be an array of all values. For other scales, the domain needs to be
 * an array of two number for marking the upper bound and lower bound.
 * This domain property will be used with d3 scale object internally.
 * @property {geo.geoColor[]} colors The colors of the legend.
 * All valid svg color can be used. For discrete type, multiple values
 * are accepted. For continuous type, an array of two values is supported.
 * @property {number} [base] The base of log when log scale is used.
 * default to 10.
 * @property {number} [exponent] The exponent of power when power scale is used.
 * default to 1.
 * @property {boolean} [endAxisLabelOnly] Only show left most and right most
 * axis label
 * default to null.
 */

/**
 * A UI widget that enables display discrete colors or two-color continuous
 *  transition legend.
 *
 * @class
 * @alias geo.gui.colorLegendWidget
 * @extends geo.gui.domWidget
 * @param {object} [arg] Widget options.
 * @param {geo.gui.widget.position} [arg.position] Position setting relatively to the map
 * container.
 * @param {geo.gui.colorLegendWidget.category[]} [arg.categories] An array
 * of category definitions for the initial color legends
 * @param {number} [arg.width=300] The width of the widget in pixels.
 * @param {number} [arg.ticks=6] The maximum number of ticks on the axis of a legend, default is 6.
 * @returns {geo.gui.colorLegendWidget}
 */
var colorLegendWidget = function colorLegendWidget(arg) {
  'use strict';

  if (!(this instanceof colorLegendWidget)) {
    return new colorLegendWidget(arg);
  }

  domWidget.call(this, arg);

  var m_this = this,
      m_categories = [],
      m_width = arg.width || 300,
      m_ticks = arg.ticks || 6,
      s_init = this._init;
  // get the widget container ready
  this._init = function () {
    s_init();
    var canvas = m_this.canvas();
    d3.select(canvas).attr('class', 'color-legend-container');

    m_this.popup = d3.select(canvas).append('div').attr('class', 'color-legend-popup');

    if (arg.categories) {
      this.categories(arg.categories);
    }
  };

  /**
   * Clear the DOM container and create legends.
   */
  this._draw = function () {
    d3.select(m_this.canvas()).selectAll('div.legends').remove();

    if (!m_categories.length) {
      d3.select(m_this.canvas()).style('display', 'none');
      return;
    } else {
      d3.select(m_this.canvas()).style('display', 'block');
    }

    var container = d3.select(m_this.canvas()).append('div').attr('class', 'legends');

    var width = m_width;
    var margin = 20;

    m_categories.forEach(function (category, index) {
      var legendContainer = container.append('div').attr('class', 'legend');

      legendContainer.append('div').attr('class', 'title').text(category.name);

      var legendSvg = legendContainer.append('svg').attr({
        'class': 'svg',
        'width': width,
        'height': '40px',
        'viewBox': -margin + ' 0 ' + width + ' 40'
      });

      if (category.type === 'discrete') {
        m_this._drawDiscrete(legendSvg, width - 2 * margin, category);
      } else if (category.type === 'continuous') {
        m_this._drawContinous(legendSvg, width - 2 * margin, category);
      }
    });
  };

  /**
   * Set or get categories.
   * @param {geo.gui.colorLegendWidget.category[]} [categories] If `undefined`,
   * return the current legend categories array. If an array is provided,
   * remove current legends and recreate with the new categories.
   * @returns {geo.gui.colorLegendWidget.category[]|this}
   * The current list of categories or the current class instance.
   */
  this.categories = function (categories) {
    if (categories === undefined) {
      return m_categories;
    }
    m_categories = this._prepareCategories(categories);
    this._draw();
    return this;
  };

  /**
   * Add additional categories.
   * @param {geo.gui.colorLegendWidget.category[]} categories Append additional
   * legend categories to the end the of the current list of legends.
   * @returns {this} The current class instance.
   */
  this.addCategories = function (categories) {
    m_categories = m_categories.concat(this._prepareCategories(categories));
    this._draw();
    return this;
  };

  /**
   * Remove categories.
   *
   * @param {geo.gui.colorLegendWidget.category[]} categories If a category
   * object exists in the current legend categories, that category will be
   * removed.
   * @returns {this} The current class instance.
   */
  this.removeCategories = function (categories) {
    m_categories = m_categories.filter(function (category) {
      return categories.indexOf(category) === -1;
    });
    this._draw();
    return this;
  };

  /**
   * This function normalize color input string with the utility function. It modifies the original object.
   * @param {geo.gui.colorLegendWidget.category[]} categories The categories
   * @returns {geo.gui.colorLegendWidget.category[]} prepared categories
   */
  this._prepareCategories = function (categories) {
    categories.forEach(function (category) {
      category.color = category.colors.map(function (color) {
        return util.convertColorToHex(color, true);
      });
    });
    return categories;
  };

  /**
   * Draw an individual discrete type legend.
   * @param {Element} svg svg element that the legend will be drawn
   * @param {number} width width of the svg element in pixel
   * @param {geo.gui.colorLegendWidget.category} category The discrete type legend category
   */
  this._drawDiscrete = function (svg, width, category) {
    if (['linear', 'log', 'sqrt', 'pow', 'quantile', 'ordinal'].indexOf(category.scale) === -1) {
      throw new Error('unsupported scale');
    }
    var valueRange, valueScale, colorScale, axisScale, axis, steps, ticks;
    if (category.scale === 'ordinal') {
      colorScale = d3.scale.ordinal().domain(category.domain).range(category.colors);
      m_this._renderDiscreteColors(svg, category.domain, colorScale, width, function (d) {
        return d;
      });

      axisScale = d3.scale.ordinal().domain(category.domain).rangeRoundBands([0, width]);
      axis = d3.svg.axis().scale(axisScale).tickValues(function () {
        var skip = Math.ceil(category.domain.length / m_ticks);
        return category.domain.filter(function (d, i) {
          return i % skip === 0;
        });
      });
    } else if (category.scale === 'quantile') {
      valueRange = [0, category.colors.length];
      steps = util.range(0, category.colors.length - 1);
      valueScale = d3.scale.quantile().domain(category.domain).range(steps);
      colorScale = d3.scale.quantize().domain(valueRange).range(category.colors);
      m_this._renderDiscreteColors(svg, steps, colorScale, width, function (d) {
        return valueScale.invertExtent(d).join(' - ');
      });

      var axisDomain = [valueScale.invertExtent(0)[0]];
      axisDomain = axisDomain.concat(steps.map(function (step) {
        return valueScale.invertExtent(step)[1];
      }));

      axisScale = d3.scale.ordinal().domain(axisDomain).rangePoints([0, width]);
      axis = createAxis(axisScale);
    } else if (['linear', 'log', 'sqrt', 'pow'].indexOf(category.scale) !== -1) {
      valueRange = [0, category.colors.length];
      valueScale = d3.scale[category.scale]().domain(category.domain).range(valueRange).nice();
      colorScale = d3.scale.quantize().domain(valueRange).range(category.colors);
      steps = util.range(0, category.colors.length - 1);
      var precision = Math.max.apply(null, category.domain.map(function (number) {
        return getPrecision(number);
      }));
      m_this._renderDiscreteColors(svg, steps, colorScale, width, function (d) {
        return m_this._popupFormatter(valueScale.invert(d), precision) + ' - ' + m_this._popupFormatter(valueScale.invert(d + 1), precision);
      });

      ticks = steps.slice();
      ticks.push(category.colors.length);
      axisScale = d3.scale.ordinal().domain(ticks.map(function (tick) {
        return valueScale.invert(tick);
      })).rangePoints([0, width]);
      axis = createAxis(axisScale);
    }
    if (category.endAxisLabelOnly) {
      axis.tickValues([axisScale.domain()[0], axisScale.domain()[axisScale.domain().length - 1]]);
    }
    m_this._renderAxis(svg, axis);

    /**
     * Render the d3 axis object based on the axis d3 Scale.
     * @param {object} axisScale d3 scale object
     * @returns {object} d3 axis object
     */
    function createAxis(axisScale) {
      return d3.svg.axis().scale(axisScale).tickFormat(d3.format('.2s')).tickValues(function () {
        var skip = Math.ceil(axisScale.domain().length / m_ticks);
        return axisScale.domain().filter(function (d, i) {
          return i % skip === 0;
        });
      });
    }
  };

  /**
   * Render colors for discrete type with d3.
   * @param {Element} svg svg element that the legend will be drawn
   * @param {number[]} steps discrete input scale domain for d3 scale
   * @param {object} colorScale d3 scale for transform input into color
   * @param {number} width width of the svg element in pixel
   * @param {function} getValue function that transforms raw domain into desired discrete range
   */
  this._renderDiscreteColors = function (svg, steps, colorScale, width, getValue) {
    svg.selectAll('rect').data(steps).enter().append('rect').attr('width', width / steps.length).attr('height', '20px').attr('fill', function (d) {
      return colorScale(d);
    }).attr('transform', function (d, i) {
      return 'translate(' + i * width / steps.length + ' ,0)';
    }).on('mousemove', function (d) {
      m_this._showPopup(getValue(d));
    }).on('mouseout', m_this._hidePopup);
  };

  /**
   * Draw an individual continous type legend.
   * @param {Element} svg svg element that the legend will be drawn
   * @param {number} width width of the svg element in pixel
   * @param {geo.gui.colorLegendWidget.category} category The continuous type legend category
   */
  this._drawContinous = function (svg, width, category) {
    var axisScale, axis, i;
    if (['linear', 'log', 'sqrt', 'pow'].indexOf(category.scale) === -1) {
      throw new Error('unsupported scale');
    }
    var range = [0];
    for (i = 1; i < category.domain.length - 1; i++) {
      range.push(width / (category.domain.length - 1) * i);
    }
    range.push(width);
    axisScale = d3.scale[category.scale]().domain(category.domain).range(range).nice();
    if (category.scale === 'log' && category.base) {
      axisScale.base(category.base);
    }
    if (category.scale === 'pow' && category.exponent) {
      axisScale.exponent(category.exponent);
    }
    var id = uniqueID();
    var precision = Math.max.apply(null, category.domain.map(function (number) {
      return getPrecision(number);
    }));

    var gradient = svg.append('defs').append('linearGradient').attr('id', 'gradient' + id);
    gradient.append('stop').attr('offset', '0%').attr('stop-color', category.colors[0]);
    for (i = 1; i < category.colors.length - 1; i++) {
      gradient.append('stop').attr('offset', (100 / (category.colors.length - 1) * i).toFixed(6) + '%').attr('stop-color', category.colors[i]);
    }
    gradient.append('stop').attr('offset', '100%').attr('stop-color', category.colors[category.colors.length - 1]);
    svg.append('rect').attr('fill', 'url(#gradient' + id + ')').attr('width', width).attr('height', '20px').on('mousemove', function () {
      var value = axisScale.invert(d3.mouse(this)[0]);
      var text = m_this._popupFormatter(value, precision);
      m_this._showPopup(text);
    }).on('mouseout', m_this._hidePopup);

    axis = d3.svg.axis().scale(axisScale).ticks(m_ticks, '.2s');
    if (category.endAxisLabelOnly) {
      axis.tickValues([category.domain[0], category.domain[category.domain.length - 1]]);
    }
    this._renderAxis(svg, axis);
  };

  /**
   * Actually render the axis with d3.
   * @param {Element} svg svg element that the axis will be drawn
   * @param {object} axis d3 axis object
   */
  this._renderAxis = function (svg, axis) {
    svg.append('g').attr('class', 'axis x').attr('transform', 'translate(0, 20)').call(function (g) {
      g.call(axis);
    });
  };

  /**
   * Formatter of number that tries to maximize the precision
   * while making the output shorter.
   * @param {number} number to be formatted
   * @param {number} precision maximum number of decimal places that are kept
   * @returns {string} formatted string output
   */
  this._popupFormatter = function (number, precision) {
    number = parseFloat(number.toFixed(8));
    precision = Math.min(precision, getPrecision(number));
    precision = Math.min(precision, Math.max(3, 7 - Math.trunc(number).toString().length));
    return d3.format('.' + precision + 'f')(number);
  };

  /**
   * Show the popup based on current mouse event.
   * @param {string} text content to be shown in the popup
   */
  this._showPopup = function (text) {
    // The cursor location relative to the container
    var offset = d3.mouse(m_this.canvas());
    m_this.popup.text(text);
    var containerWidth = m_this.canvas().clientWidth;
    var popupWidth = m_this.popup[0][0].clientWidth;
    m_this.popup.style({
      // If the popup will be longer or almost longer than the container
      'left': offset[0] - (offset[0] + popupWidth - containerWidth > -10 ? popupWidth : 0) + 'px',
      'top': offset[1] - 22 + 'px'
    }).transition().duration(200).style('opacity', 1);
  };

  /**
   * Hide the popup.
   */
  this._hidePopup = function () {
    m_this.popup.transition().duration(200).style('opacity', 0);
  };

  return this;
};

/**
 * Get the number of decimals of a number.
 *
 * @private
 * @param {number} number the number input
 * @returns {number} the number of decimal
 */
function getPrecision(number) {
  if (!isFinite(number)) return 0;
  var e = 1,
      p = 0;
  while (Math.round(number * e) / e !== number) {
    if (!isFinite(number * e)) {
      return 0;
    }
    e *= 10;
    p++;
  }
  return p;
}

inherit(colorLegendWidget, domWidget);

registerWidget('dom', 'colorLegend', colorLegendWidget);
module.exports = colorLegendWidget;

/***/ }),
/* 321 */
/***/ (function(module, exports, __webpack_require__) {


var content = __webpack_require__(322);

if(typeof content === 'string') content = [[module.i, content, '']];

var transform;
var insertInto;



var options = {"hmr":true}

options.transform = transform
options.insertInto = undefined;

var update = __webpack_require__(38)(content, options);

if(content.locals) module.exports = content.locals;

if(false) {
	module.hot.accept("!!../../node_modules/css-loader/index.js!../../node_modules/stylus-loader/index.js!./colorLegendWidget.styl", function() {
		var newContent = require("!!../../node_modules/css-loader/index.js!../../node_modules/stylus-loader/index.js!./colorLegendWidget.styl");

		if(typeof newContent === 'string') newContent = [[module.id, newContent, '']];

		var locals = (function(a, b) {
			var key, idx = 0;

			for(key in a) {
				if(!b || a[key] !== b[key]) return false;
				idx++;
			}

			for(key in b) idx--;

			return idx === 0;
		}(content.locals, newContent.locals));

		if(!locals) throw new Error('Aborting CSS HMR due to changed css-modules locals.');

		update(newContent);
	});

	module.hot.dispose(function() { update(); });
}

/***/ }),
/* 322 */
/***/ (function(module, exports, __webpack_require__) {

exports = module.exports = __webpack_require__(37)(false);
// imports


// module
exports.push([module.i, ".color-legend-container {\n  display: none;\n  padding: 10px;\n  border: 1.5px solid #000;\n  border-radius: 3px;\n  transition: 250ms background linear;\n  background-color: rgba(255,255,255,0.75);\n}\n.color-legend-container:hover {\n  background-color: #fff;\n}\n.color-legend-container .legends .legend {\n  margin-bottom: 10px;\n}\n.color-legend-container .legends .legend .title {\n  text-align: center;\n}\n.color-legend-container .legends .legend svg.svg {\n  display: block;\n}\n.color-legend-container .legends .legend svg.svg .axis.x path.domain,\n.color-legend-container .legends .legend svg.svg .axis.x line {\n  fill: none;\n  stroke: #000;\n  stroke-width: 0.7;\n}\n.color-legend-container .legends .legend svg.svg .axis.x text {\n  font-size: 12px;\n}\n.color-legend-container .color-legend-popup {\n  position: absolute;\n  background: #fff;\n  height: 22px;\n  font-size: 14px;\n  border: solid 1px #000;\n  padding: 0 5px;\n  pointer-events: none;\n  white-space: nowrap;\n  z-index: 100000;\n  opacity: 0;\n}\n", ""]);

// exports


/***/ }),
/* 323 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var svgWidget = __webpack_require__(36);
var inherit = __webpack_require__(0);
var registerWidget = __webpack_require__(1).registerWidget;

/**
 * Create a new instance of class legendWidget
 *
 * @class
 * @alias geo.gui.legendWidget
 * @extends geo.gui.svgWidget
 * @returns {geo.gui.legendWidget}
 */
var legendWidget = function legendWidget(arg) {
  'use strict';

  if (!(this instanceof legendWidget)) {
    return new legendWidget(arg);
  }
  svgWidget.call(this, arg);

  var d3 = __webpack_require__(11).d3;
  var geo_event = __webpack_require__(6);

  /** @private */
  var m_this = this,
      m_categories = [],
      m_top = null,
      m_group = null,
      m_border = null,
      m_spacing = 20,
      // distance in pixels between lines
  m_padding = 12; // padding in pixels inside the border

  /**
   * Get or set the category array associated with
   * the legend.  Each element of this array is
   * an object: ::
   *     {
   *         name: string,
   *         style: object,
   *         type: 'point' | 'line' | ...
   *     }
   *
   * The style property can contain the following feature styles:
   *     * fill: bool
   *     * fillColor: object | string
   *     * fillOpacity: number
   *     * stroke: bool
   *     * strokeColor: object | string
   *     * strokeWidth: number
   *     * strokeOpacity: number
   *
   * The type controls how the element is displayed, point as a circle,
   * line as a line segment.  Any other value will display as a rounded
   * rectangle.
   *
   * @param {object[]?} categories The categories to display
   */
  this.categories = function (arg) {
    if (arg === undefined) {
      return m_categories.slice();
    }
    m_categories = arg.slice().map(function (d) {
      if (d.type === 'line') {
        d.style.fill = false;
        d.style.stroke = true;
      }
      return d;
    });
    m_this.draw();
    return m_this;
  };

  /**
   * Get the widget's size
   * @return {{width: number, height: number}} The size in pixels
   */
  this.size = function () {
    var width = 1,
        height;
    var test = d3.select(m_this.canvas()).append('text').style('opacity', 1e-6);

    m_categories.forEach(function (d) {
      test.text(d.name);
      width = Math.max(width, test.node().getBBox().width);
    });
    test.remove();

    height = m_spacing * (m_categories.length + 1);
    return {
      width: width + 50,
      height: height
    };
  };

  /**
   * Redraw the legend
   */
  this.draw = function () {

    m_this._init();
    function applyColor(selection) {
      selection.style('fill', function (d) {
        if (d.style.fill || d.style.fill === undefined) {
          return d.style.fillColor;
        } else {
          return 'none';
        }
      }).style('fill-opacity', function (d) {
        if (d.style.fillOpacity === undefined) {
          return 1;
        }
        return d.style.fillOpacity;
      }).style('stroke', function (d) {
        if (d.style.stroke || d.style.stroke === undefined) {
          return d.style.strokeColor;
        } else {
          return 'none';
        }
      }).style('stroke-opacity', function (d) {
        if (d.style.strokeOpacity === undefined) {
          return 1;
        }
        return d.style.strokeOpacity;
      }).style('stroke-width', function (d) {
        if (d.style.strokeWidth === undefined) {
          return 1.5;
        }
        return d.style.strokeWidth;
      });
    }

    m_border.attr('height', m_this.size().height + 2 * m_padding).style('display', null);

    var scale = m_this._scale();

    var labels = m_group.selectAll('g.geo-label').data(m_categories, function (d) {
      return d.name;
    });

    var g = labels.enter().append('g').attr('class', 'geo-label').attr('transform', function (d, i) {
      return 'translate(0,' + scale.y(i) + ')';
    });

    applyColor(g.filter(function (d) {
      return d.type !== 'point' && d.type !== 'line';
    }).append('rect').attr('x', 0).attr('y', -6).attr('rx', 5).attr('ry', 5).attr('width', 40).attr('height', 12));

    applyColor(g.filter(function (d) {
      return d.type === 'point';
    }).append('circle').attr('cx', 20).attr('cy', 0).attr('r', 6));

    applyColor(g.filter(function (d) {
      return d.type === 'line';
    }).append('line').attr('x1', 0).attr('y1', 0).attr('x2', 40).attr('y2', 0));

    g.append('text').attr('x', '50px').attr('y', 0).attr('dy', '0.3em').text(function (d) {
      return d.name;
    });

    m_this.reposition();

    return m_this;
  };

  /**
   * Get scales for the x and y axis for the current size.
   * @private
   */
  this._scale = function () {
    return {
      x: d3.scale.linear().domain([0, 1]).range([0, m_this.size().width]),
      y: d3.scale.linear().domain([0, m_categories.length - 1]).range([m_padding / 2, m_this.size().height - m_padding / 2])
    };
  };

  /**
   * Private initialization.  Creates the widget's DOM container and internal
   * variables.
   * @private
   */
  this._init = function () {
    // adding categories redraws the entire thing by calling _init, see
    // the m_top.remove() line below
    if (!m_top) {
      m_this._createCanvas();
      m_this._appendCanvasToParent();
    }

    // total size = interior size + 2 * padding + 2 * width of the border
    var w = m_this.size().width + 2 * m_padding + 4,
        h = m_this.size().height + 2 * m_padding + 4;

    // @todo - removing after creating to maintain the appendChild structure
    if (m_top) {
      m_top.remove();
    }

    d3.select(m_this.canvas()).attr('width', w).attr('height', h);

    m_top = d3.select(m_this.canvas()).append('g');
    m_group = m_top.append('g').attr('transform', 'translate(' + [m_padding + 2, m_padding + 2] + ')');
    m_border = m_group.append('rect').attr('x', -m_padding).attr('y', -m_padding).attr('width', w - 4).attr('height', h - 4).attr('rx', 3).attr('ry', 3).style({
      'stroke': 'black',
      'stroke-width': '1.5px',
      'fill': 'white',
      'fill-opacity': 0.75,
      'display': 'none'
    });
    m_group.on('mousedown', function () {
      d3.event.stopPropagation();
    });
    m_group.on('mouseover', function () {
      m_border.transition().duration(250).style('fill-opacity', 1);
    });
    m_group.on('mouseout', function () {
      m_border.transition().duration(250).style('fill-opacity', 0.75);
    });

    m_this.reposition();
  };

  this.geoOn(geo_event.resize, function () {
    m_this.draw();
  });
};

inherit(legendWidget, svgWidget);

registerWidget('dom', 'legend', legendWidget);
module.exports = legendWidget;

/***/ }),
/* 324 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var $ = __webpack_require__(2);
var inherit = __webpack_require__(0);
var svgWidget = __webpack_require__(36);
var registerWidget = __webpack_require__(1).registerWidget;

__webpack_require__(325);

/**
 * Scale widget specification.
 *
 * @typedef {object} geo.gui.scaleWidget.spec
 * @property {number} [scale=1] A scale applied to the map gcs units to convert
 *   to the scale units.
 * @property {number} [maxWidth=200] The maximum width of the scale in pixels.
 *   For horizontal scales (orientation is `top` or `bottom`) this is the
 *   maximum length of the scale bar.  For vertical scales, this is the width
 *   available for the scale text.
 * @property {number} [maxHeight] The maximum height of the scale in pixels.
 *   For vertical scales (orientation is `left` or `right`) this is the
 *   maximum length of the scale bar.  For horizontal scales, this is the
 *   height available for the scale text.  Default is 200 for vertical scales,
 *   20 for horizontal scales.
 * @property {string} [orientation='bottom'] One of `left`, `right`, `top`, or
 *   `bottom`.  The scale text is placed in that location relative to the scale
 *   bar.
 * @property {number} [strokeWidth=2] The width of the ticks and scale bar in
 *   pixels.
 * @property {number} [tickLength=10] The length of the end ticks in pixels.
 * @property {string|geo.gui.scaleWidget.unit[]} [units='si'] One of either
 *   `'si'` or `'miles'` or an array of units in ascending order.  See the
 *   `UnitsTable` for examples.
 * @property {function} [distance] The function used to compute the length of
 *   the scale bar.  This defaults to `transform.sphericalDistance` for all
 *   maps except those with a gcs of `'+proj=longlat +axis=enu'`, where
 *   `math.sqrt(util.distance2dSquared(pt1, pt2))` is used instead.
 */

/**
 * Scale widget unit specification.
 *
 * @typedef {object} geo.gui.scaleWidget.unit
 * @property {string} unit Display name for the unit.
 * @property {number} scale Scale for 1 unit in the current system.
 * @property {number} [minimum=1] Minimum value where this applies after
 *   scaling.  This can be used to handle singular and plural words (e.g.,
 *   `[{units: 'meter', scale: 1}, {units: 'meters', scale: 1, minimum: 1.5}]`)
 * @property {number} [basis=10] The basis for the multiples value.
 * @property {object[]} [multiples] A list of objects in ascending value order
 *   that determine what round values are displayed.
 * @property {number} multiples.multiple The value that is selected for display.
 * @property {number} multiples.digit The number of significant digits in
 *   `mutliple`.
 */

/**
 * Create a new instance of class geo.gui.scaleWidget.
 *
 * @class
 * @alias geo.gui.scaleWidget
 * @extends {geo.gui.svgWidget}
 * @param {geo.gui.scaleWidget.spec} arg
 * @returns {geo.gui.scaleWidget}
 */
var scaleWidget = function scaleWidget(arg) {
  'use strict';

  if (!(this instanceof scaleWidget)) {
    return new scaleWidget(arg);
  }
  svgWidget.call(this, arg);

  var geo_event = __webpack_require__(6);
  var transform = __webpack_require__(7);
  var util = __webpack_require__(3);
  var d3 = __webpack_require__(11).d3;

  var m_this = this,
      s_exit = this._exit,
      m_options = $.extend({}, {
    scale: 1,
    maxWidth: 200,
    maxHeight: arg.orientation === 'left' || arg.orientation === 'right' ? 200 : 20,
    orientation: 'bottom',
    strokeWidth: 2,
    tickLength: 10,
    units: 'si',
    distance: function distance(pt1, pt2, gcs) {
      if (gcs === '+proj=longlat +axis=enu') {
        return Math.sqrt(util.distance2dSquared(pt1, pt2));
      }
      /* We can use either the spherical distance or the Vincenty distance
       * here in much the same way.
      return transform.vincentyDistance(pt1, pt2, gcs).distance;
       */
      return transform.sphericalDistance(pt1, pt2, gcs);
    }
  }, arg);

  /**
   * Initialize the scale widget.
   *
   * @returns {this}
   */
  this._init = function () {
    m_this._createCanvas();
    m_this._appendCanvasToParent();
    m_this.reposition();

    d3.select(m_this.canvas()).attr({
      width: m_options.maxWidth,
      height: m_options.maxHeight
    });
    // Update the scale on pan
    m_this.geoOn(geo_event.pan, m_this._update);
    m_this._render();
    return m_this;
  };

  /**
   * Clean up after the widget.
   */
  this._exit = function () {
    m_this.geoOff(geo_event.pan, m_this._update);
    s_exit();
  };

  /**
   * Return true if the scale is vertically oriented.
   *
   * @returns {boolean} `true` if the scale is vertical, `false` if horizontal.
   */
  this._vertical = function () {
    return m_options.orientation === 'left' || m_options.orientation === 'right';
  };

  /**
   * Given a maximum value, return a value that is no larger than it but at a
   * round number of a set of units.
   *
   * @param {number} maxValue The maximum value to return.  The returned value
   *    will never be smaller than 3/5 of this value.
   * @param {number} pixels A number that is scaled by the ratio of the
   *    returned value to `maxValue`.
   * @param {string|geo.gui.scaleWidget.unit[]} [units] The units to use.  If
   *    not specified, the instance's option units value is used.
   * @returns {object} An object with `html`, `value`, and `pixels` values
   *    representing the calculated value.
   */
  this._scaleValue = function (maxValue, pixels, units) {
    units = scaleWidget.unitsTable[units] || units || scaleWidget.unitsTable[m_options.units] || m_options.units;
    var multiples = [{ multiple: 1, digits: 1 }, { multiple: 1.5, digits: 2 }, { multiple: 2, digits: 1 }, { multiple: 3, digits: 1 }, { multiple: 5, digits: 1 }, { multiple: 8, digits: 1 }];
    var unit = units[0],
        multiple,
        power,
        value;
    units.forEach(function (unitEntry) {
      if (maxValue >= unitEntry.scale * (unitEntry.minimum || 1)) {
        unit = unitEntry;
      }
    });
    power = Math.floor(Math.log(maxValue / unit.scale) / Math.log(unit.basis || 10));
    multiples = unit.multiples || multiples;
    multiples.forEach(function (mul) {
      var mulValue = unit.scale * mul.multiple * Math.pow(10, power);
      if (mulValue <= maxValue) {
        multiple = mul;
        value = mulValue;
      }
    });
    return {
      html: (multiple.multiple * Math.pow(10, power)).toFixed(Math.max(0, -power + multiple.digits - 1)) + ' ' + unit.unit,
      value: value,
      pixels: value / maxValue * pixels,
      power: power,
      multiple: multiple,
      unitRecord: unit,
      originalValue: maxValue,
      originalPixels: pixels
    };
  };

  /**
   * Create and draw the scale based on the current display distance at the
   * location of the scale.
   */
  this._render = function () {
    var svg = d3.select(m_this.canvas()),
        map = m_this.layer().map(),
        width = m_options.maxWidth,
        height = m_options.maxHeight,
        sw = m_options.strokeWidth,
        sw2 = sw * 0.5,
        tl = m_options.tickLength,
        vert = m_this._vertical(),
        pixels,
        pt1,
        pt2,
        dist,
        value,
        pts;

    pixels = (vert ? m_options.maxHeight : m_options.maxWidth) - sw;
    /* Calculate the distance that the maximum length scale bar can occupy at
     * the location that the scale bar will be drawn. */
    // svg.attr({width: width, height: height});
    pt1 = $(svg[0][0]).offset();
    pt1 = {
      x: pt1.left + (m_options.orientation === 'left' ? width - sw2 : sw2),
      y: pt1.top + (m_options.orientation === 'top' ? height - sw2 : sw2)
    };
    pt2 = { x: pt1.x + (vert ? 0 : pixels), y: pt1.y + (vert ? pixels : 0) };
    dist = m_options.distance(map.displayToGcs(pt1, null), map.displayToGcs(pt2, null), map.gcs()) * m_options.scale;
    if (dist <= 0 || !isFinite(dist)) {
      console.warn('The distance calculated for the scale is invalid: ' + dist);
      return;
    }
    value = m_this._scaleValue(dist, pixels);
    if (vert) {
      height = value.pixels + sw;
    } else {
      width = value.pixels + sw;
    }
    svg.attr({ width: width, height: height });
    if (svg.select('polyline').empty()) {
      svg.append('polyline').classed('geojs-scale-widget-bar', true).attr({
        fill: 'none',
        'stroke-width': sw
      });
    }
    if (svg.select('text').empty()) {
      svg.append('text').classed('geojs-scale-widget-text', true);
    }
    switch (m_options.orientation) {
      case 'bottom':
        pts = [[sw2, tl], [sw2, sw2], [width - sw2, sw2], [width - sw2, tl]];
        svg.select('text').attr({
          x: width / 2,
          y: sw * 2,
          'text-anchor': 'middle',
          'dominant-baseline': 'hanging'
        });
        break;
      case 'top':
        pts = [[sw2, height - tl], [sw2, height - sw2], [width - sw2, height - sw2], [width - sw2, height - tl]];
        svg.select('text').attr({
          x: width / 2,
          y: height - sw * 2,
          'text-anchor': 'middle',
          'dominant-baseline': 'alphabetic'
        });
        break;
      case 'left':
        pts = [[width - tl, sw2], [width - sw2, sw2], [width - sw2, height - sw2], [width - tl, height - sw2]];
        svg.select('text').attr({
          x: width - sw * 2,
          y: height / 2,
          'text-anchor': 'end',
          'dominant-baseline': 'middle'
        });
        break;
      case 'right':
        pts = [[tl, sw2], [sw2, sw2], [sw2, height - sw2], [tl, height - sw2]];
        svg.select('text').attr({
          x: sw * 2,
          y: height / 2,
          'text-anchor': 'start',
          'dominant-baseline': 'middle'
        });
        break;
    }
    svg.select('polyline').attr('points', pts.map(function (pt) {
      return pt.join(',');
    }).join(' '));
    svg.select('text').html(value.html);
  };

  /**
   * Update the widget upon panning.
   */
  this._update = function () {
    this._render();
  };

  /**
   * Set or get options.
   *
   * @param {string|object} [arg1] If `undefined`, return the options object.
   *    If a string, either set or return the option of that name.  If an
   *    object, update the options with the object's values.
   * @param {object} [arg2] If `arg1` is a string and this is defined, set
   *    the option to this value.
   * @returns {object|this} If options are set, return the annotation,
   *    otherwise return the requested option or the set of options.
   */
  this.options = function (arg1, arg2) {
    if (arg1 === undefined) {
      var result = $.extend({}, m_options);
      result.position = m_this.position(undefined, true);
      return result;
    }
    if (typeof arg1 === 'string' && arg2 === undefined) {
      return arg1 === 'position' ? m_this.position(undefined, true) : m_options[arg1];
    }
    if (arg2 === undefined) {
      m_options = $.extend(true, m_options, arg1);
    } else {
      m_options[arg1] = arg2;
    }
    if (arg1.position || arg1 === 'position') {
      m_this.position(arg1.position || arg2);
    }
    m_this._render();
    return m_this;
  };
};

inherit(scaleWidget, svgWidget);

/* The unitsTable has predefined unit sets.  Each entry is an array that must
 * be in ascending order. */
scaleWidget.unitsTable = {
  si: [{ unit: 'nm', scale: 1e-9 }, { unit: '&mu;m', scale: 1e-6 }, { unit: 'mm', scale: 0.001 }, { unit: 'm', scale: 1 }, { unit: 'km', scale: 1000 }],
  miles: [{ unit: 'in', scale: 0.0254 }, // applies to < 1 in
  {
    /* By specifying inches a second time, the first entry will apply to
     * values less than 1 inch, and those will be rounded by powers of 10
     * using the default rules.  This entry will round values differently,
     * so one will see 1, 1.5, 2, 3, 6, 9 rather than the default which would
     * be 1, 1.5, 2, 3, 5, 8, 10. */
    unit: 'in',
    scale: 0.0254,
    basis: 12,
    multiples: [{ multiple: 1, digits: 1 }, { multiple: 1.5, digits: 2 }, { multiple: 2, digits: 1 }, { multiple: 3, digits: 1 }, { multiple: 6, digits: 1 }, { multiple: 9, digits: 1 }]
  }, { unit: 'ft', scale: 0.3048 }, { unit: 'mi', scale: 1609.344 }]
};

registerWidget('dom', 'scale', scaleWidget);
module.exports = scaleWidget;

/***/ }),
/* 325 */
/***/ (function(module, exports, __webpack_require__) {


var content = __webpack_require__(326);

if(typeof content === 'string') content = [[module.i, content, '']];

var transform;
var insertInto;



var options = {"hmr":true}

options.transform = transform
options.insertInto = undefined;

var update = __webpack_require__(38)(content, options);

if(content.locals) module.exports = content.locals;

if(false) {
	module.hot.accept("!!../../node_modules/css-loader/index.js!../../node_modules/stylus-loader/index.js!./scaleWidget.styl", function() {
		var newContent = require("!!../../node_modules/css-loader/index.js!../../node_modules/stylus-loader/index.js!./scaleWidget.styl");

		if(typeof newContent === 'string') newContent = [[module.id, newContent, '']];

		var locals = (function(a, b) {
			var key, idx = 0;

			for(key in a) {
				if(!b || a[key] !== b[key]) return false;
				idx++;
			}

			for(key in b) idx--;

			return idx === 0;
		}(content.locals, newContent.locals));

		if(!locals) throw new Error('Aborting CSS HMR due to changed css-modules locals.');

		update(newContent);
	});

	module.hot.dispose(function() { update(); });
}

/***/ }),
/* 326 */
/***/ (function(module, exports, __webpack_require__) {

exports = module.exports = __webpack_require__(37)(false);
// imports


// module
exports.push([module.i, ".geojs-scale-widget-bar {\n  stroke: #000;\n}\n.geojs-scale-widget-text {\n  font-weight: bold;\n  font-size: 16px;\n  font-family: serif;\n}\n", ""]);

// exports


/***/ }),
/* 327 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var svgWidget = __webpack_require__(36);
var inherit = __webpack_require__(0);
var registerWidget = __webpack_require__(1).registerWidget;

/**
 * Create a new instance of class sliderWidget
 *
 * @class
 * @alias geo.gui.sliderWidget
 * @extends {geo.gui.svgWidget}
 * @returns {geo.gui.sliderWidget}
 */
var sliderWidget = function sliderWidget(arg) {
  'use strict';

  if (!(this instanceof sliderWidget)) {
    return new sliderWidget(arg);
  }
  svgWidget.call(this, arg);

  var d3 = __webpack_require__(11).d3;
  var geo_event = __webpack_require__(6);

  var m_this = this,
      s_exit = this._exit,
      m_xscale,
      m_yscale,
      m_plus,
      m_minus,
      m_nub,
      m_width = arg.width || 20,
      // Size of the widget in pixels
  m_height = arg.height || 160,
      // slider height + 3 * width
  m_nubSize = arg.width ? arg.width * 0.5 : 10,
      m_plusIcon,
      m_minusIcon,
      m_group,
      m_lowContrast,
      m_highlightDur = 100;

  /* http://icomoon.io */
  /* CC BY 3.0 http://creativecommons.org/licenses/by/3.0/ */
  m_plusIcon = 'M512 81.92c-237.568 0-430.080 192.614-430.080 430.080 0 237.568 192.563 430.080 430.080 430.080s430.080-192.563 430.080-430.080c0-237.517-192.563-430.080-430.080-430.080zM564.326 564.326v206.182h-104.653v-206.182h-206.234v-104.653h206.182v-206.234h104.704v206.182h206.182v104.704h-206.182z';
  m_minusIcon = 'M512 81.92c-237.568 0-430.080 192.614-430.080 430.080 0 237.568 192.563 430.080 430.080 430.080s430.080-192.563 430.080-430.080c0-237.517-192.563-430.080-430.080-430.080zM770.56 459.674v104.704h-517.12v-104.704h517.12z';

  // Define off-white gray colors for low contrast ui (unselected).
  m_lowContrast = {
    white: '#f4f4f4',
    black: '#505050'
  };

  /**
   * Add an icon from a path string.  Returns a d3 group element.
   *
   * @function
   * @argument {String} icon svg path string
   * @argument {Array} base where to append the element (d3 selection)
   * @argument {Number} cx Center x-coordinate
   * @argument {Number} cy Center y-coordinate
   * @argument {Number} size Icon size in pixels
   * @returns {object}
   * @private
   */
  function put_icon(icon, base, cx, cy, size) {
    var g = base.append('g');

    // the scale factor
    var s = size / 1024;

    g.append('g').append('g').attr('transform', 'translate(' + cx + ',' + cy + ') scale(' + s + ') translate(-512,-512)').append('path').attr('d', icon).attr('class', 'geo-glyphicon');

    return g;
  }

  this.size = function () {
    return { width: m_width, height: m_height };
  };

  /**
   * Initialize the slider widget in the map.
   *
   * @function
   * @returns {geo.gui.sliderWidget}
   * @private
   */
  this._init = function () {
    m_this._createCanvas();
    m_this._appendCanvasToParent();

    m_this.reposition();

    var svg = d3.select(m_this.canvas()),
        map = m_this.layer().map();

    svg.attr('width', m_width).attr('height', m_height);

    // create d3 scales for positioning
    // TODO: make customizable and responsive
    m_xscale = d3.scale.linear().domain([-4, 4]).range([0, m_width]);
    m_yscale = d3.scale.linear().domain([0, 1]).range([m_width * 1.5, m_height - m_width * 1.5]);

    // Create the main group element
    svg = svg.append('g').classed('geo-ui-slider', true);
    m_group = svg;

    // Create + zoom button
    m_plus = svg.append('g');
    m_plus.append('circle').datum({
      fill: 'white',
      stroke: null
    }).classed('geo-zoom-in', true).attr('cx', m_xscale(0)).attr('cy', m_yscale(0.0) - m_width + 2).attr('r', (m_width - 2) / 2).style({
      'cursor': 'pointer'
    }).on('click', function () {
      var z = map.zoom();
      map.transition({
        zoom: z + 1,
        ease: d3.ease('cubic-in-out'),
        duration: 500
      });
    }).on('mousedown', function () {
      d3.event.stopPropagation();
    });

    put_icon(m_plusIcon, m_plus, m_xscale(0), m_yscale(0) - m_width + 2, m_width + 4).style('cursor', 'pointer').style('pointer-events', 'none').select('path').datum({
      fill: 'black',
      stroke: null
    });

    // Create the - zoom button
    m_minus = svg.append('g');
    m_minus.append('circle').datum({
      fill: 'white',
      stroke: null
    }).classed('geo-zoom-out', true).attr('cx', m_xscale(0)).attr('cy', m_yscale(1.0) + m_width - 2).attr('r', (m_width - 2) / 2).style({
      'cursor': 'pointer'
    }).on('click', function () {
      var z = map.zoom();
      map.transition({
        zoom: z - 1,
        ease: d3.ease('cubic-in-out'),
        duration: 500
      });
    }).on('mousedown', function () {
      d3.event.stopPropagation();
    });

    put_icon(m_minusIcon, m_minus, m_xscale(0), m_yscale(1) + m_width - 2, m_width + 4).style('cursor', 'pointer').style('pointer-events', 'none').select('path').datum({
      fill: 'black',
      stroke: null
    });

    // Respond to a mouse event on the widget
    function respond(evt, trans) {
      var z = m_yscale.invert(d3.mouse(svg.node())[1]),
          zrange = map.zoomRange();
      z = (1 - z) * (zrange.max - zrange.min) + zrange.min;
      if (trans) {
        map.transition({
          zoom: z,
          ease: d3.ease('cubic-in-out'),
          duration: 500,
          done: m_this._update()
        });
      } else {
        map.zoom(z);
        m_this._update();
      }
      evt.stopPropagation();
    }

    // Create the track
    svg.append('rect').datum({
      fill: 'white',
      stroke: 'black'
    }).classed('geo-zoom-track', true).attr('x', m_xscale(0) - m_width / 6).attr('y', m_yscale(0)).attr('rx', m_width / 10).attr('ry', m_width / 10).attr('width', m_width / 3).attr('height', m_height - m_width * 3).style({
      'cursor': 'pointer'
    }).on('click', function () {
      respond(d3.event, true);
    });

    // Create the nub
    m_nub = svg.append('rect').datum({
      fill: 'black',
      stroke: null
    }).classed('geo-zoom-nub', true).attr('x', m_xscale(-4)).attr('y', m_yscale(0.5) - m_nubSize / 2).attr('rx', 3).attr('ry', 3).attr('width', m_width).attr('height', m_nubSize).style({
      'cursor': 'pointer'
    }).on('mousedown', function () {
      d3.select(document).on('mousemove.geo.slider', function () {
        respond(d3.event);
      });
      d3.select(document).on('mouseup.geo.slider', function () {
        respond(d3.event);
        d3.select(document).on('.geo.slider', null);
      });
      d3.event.stopPropagation();
    });

    var mouseOver = function mouseOver() {
      d3.select(this).attr('filter', 'url(#geo-highlight)');
      m_group.selectAll('rect,path,circle').transition().duration(m_highlightDur).style('fill', function (d) {
        return d.fill || null;
      }).style('stroke', function (d) {
        return d.stroke || null;
      });
    };

    var mouseOut = function mouseOut() {
      d3.select(this).attr('filter', null);
      m_group.selectAll('circle,rect,path').transition().duration(m_highlightDur).style('fill', function (d) {
        return m_lowContrast[d.fill] || null;
      }).style('stroke', function (d) {
        return m_lowContrast[d.stroke] || null;
      });
    };

    m_group.selectAll('*').on('mouseover', mouseOver).on('mouseout', mouseOut);

    // Update the nub position on zoom
    m_this.geoOn(geo_event.zoom, m_this._update);

    mouseOut();
    m_this._update();
  };

  /**
   * Removes the slider element from the map and unbinds all handlers.
   *
   * @function
   * @returns {geo.gui.sliderWidget}
   * @private
   */
  this._exit = function () {
    m_this.geoOff(geo_event.zoom, m_this._update);
    m_group.remove();
    s_exit();
  };

  /**
   * Update the slider widget state in reponse to map changes.  I.e. zoom
   * range changes.
   *
   * @function
   * @returns {geo.gui.sliderWidget}
   * @private
   */
  this._update = function (obj) {
    var map = m_this.layer().map(),
        zoomRange = map.zoomRange(),
        zoom = map.zoom(),
        zoomScale = d3.scale.linear();

    obj = obj || {};
    zoom = obj.value || zoom;
    zoomScale.domain([zoomRange.min, zoomRange.max]).range([1, 0]).clamp(true);

    m_nub.attr('y', m_yscale(zoomScale(zoom)) - m_nubSize / 2);
  };
};

inherit(sliderWidget, svgWidget);

registerWidget('dom', 'slider', sliderWidget);
module.exports = sliderWidget;

/***/ })
/******/ ]);
});
//# sourceMappingURL=geo.lean.js.map

/***/ }),
/* 2 */
/***/ (function(module, exports) {

module.exports = __WEBPACK_EXTERNAL_MODULE_2__;

/***/ }),
/* 3 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

function __export(m) {
    for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}
Object.defineProperty(exports, "__esModule", { value: true });
__export(__webpack_require__(9));
__export(__webpack_require__(15));
__export(__webpack_require__(38));
/**
 * The current package version.
 */
exports.version = __webpack_require__(39).version;
//# sourceMappingURL=index.js.map

/***/ }),
/* 4 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";



var TYPED_OK =  (typeof Uint8Array !== 'undefined') &&
                (typeof Uint16Array !== 'undefined') &&
                (typeof Int32Array !== 'undefined');

function _has(obj, key) {
  return Object.prototype.hasOwnProperty.call(obj, key);
}

exports.assign = function (obj /*from1, from2, from3, ...*/) {
  var sources = Array.prototype.slice.call(arguments, 1);
  while (sources.length) {
    var source = sources.shift();
    if (!source) { continue; }

    if (typeof source !== 'object') {
      throw new TypeError(source + 'must be non-object');
    }

    for (var p in source) {
      if (_has(source, p)) {
        obj[p] = source[p];
      }
    }
  }

  return obj;
};


// reduce buffer size, avoiding mem copy
exports.shrinkBuf = function (buf, size) {
  if (buf.length === size) { return buf; }
  if (buf.subarray) { return buf.subarray(0, size); }
  buf.length = size;
  return buf;
};


var fnTyped = {
  arraySet: function (dest, src, src_offs, len, dest_offs) {
    if (src.subarray && dest.subarray) {
      dest.set(src.subarray(src_offs, src_offs + len), dest_offs);
      return;
    }
    // Fallback to ordinary array
    for (var i = 0; i < len; i++) {
      dest[dest_offs + i] = src[src_offs + i];
    }
  },
  // Join array of chunks to single array.
  flattenChunks: function (chunks) {
    var i, l, len, pos, chunk, result;

    // calculate data length
    len = 0;
    for (i = 0, l = chunks.length; i < l; i++) {
      len += chunks[i].length;
    }

    // join chunks
    result = new Uint8Array(len);
    pos = 0;
    for (i = 0, l = chunks.length; i < l; i++) {
      chunk = chunks[i];
      result.set(chunk, pos);
      pos += chunk.length;
    }

    return result;
  }
};

var fnUntyped = {
  arraySet: function (dest, src, src_offs, len, dest_offs) {
    for (var i = 0; i < len; i++) {
      dest[dest_offs + i] = src[src_offs + i];
    }
  },
  // Join array of chunks to single array.
  flattenChunks: function (chunks) {
    return [].concat.apply([], chunks);
  }
};


// Enable/Disable typed arrays use, for testing
//
exports.setTyped = function (on) {
  if (on) {
    exports.Buf8  = Uint8Array;
    exports.Buf16 = Uint16Array;
    exports.Buf32 = Int32Array;
    exports.assign(exports, fnTyped);
  } else {
    exports.Buf8  = Array;
    exports.Buf16 = Array;
    exports.Buf32 = Array;
    exports.assign(exports, fnUntyped);
  }
};

exports.setTyped(TYPED_OK);


/***/ }),
/* 5 */
/***/ (function(module, exports, __webpack_require__) {

// Todo - base class for all current source
var _ = __webpack_require__(0);
var widgets = __webpack_require__(2);
// var Promise = require('bluebird');
// var ndarray = require('ndarray');
var dataserializers = __webpack_require__(3);

var geojs = __webpack_require__(1);

// var Enums = require('./enums');

// var utils = require('./utils');

//var EXTENSION_SPEC_VERSION = require('../version').EXTENSION_SPEC_VERSION;
// Hard-code version for now
var EXTENSION_SPEC_VERSION = '0.1.0';

/**
 * Helper function for listening to child models in lists/dicts
 *
 * @param {any} model The parent model
 * @param {any} propNames The propetry names that are lists/dicts
 * @param {any} callback The callback to call when child changes
 */
function listenNested(model, propNames, callback) {
    propNames.forEach(function(propName) {
        // listen to current values in array
        var curr = model.get(propName) || [];
        // support properties that are either an instance, or a
        // sequence of instances:
        if (curr instanceof ThreeModel) {
            model.listenTo(curr, 'change', callback);
            model.listenTo(curr, 'childchange', callback);
        } else {
            utils.childModelsNested(curr).forEach(function(childModel) {
                model.listenTo(childModel, 'change', callback);
                model.listenTo(childModel, 'childchange', callback);
            });
        }

        // make sure to (un)hook listeners when array changes
        model.on('change:' + propName, function(model, value) {
            var prev = model.previous(propName) || [];
            var curr = value || [];

            // Check for instance values:
            if (prev instanceof ThreeModel) {
                model.stopListening(prev);
            }
            if (curr instanceof ThreeModel) {
                model.listenTo(curr, 'change', callback);
                model.listenTo(curr, 'childchange', callback);
            }
            // Done if both are instance values:
            if (prev instanceof ThreeModel && curr instanceof ThreeModel) {
                return;
            }
            if (prev instanceof ThreeModel) {
                // Implies curr is array
                utils.childModelsNested(curr).forEach(function(childModel) {
                    model.listenTo(childModel, 'change', callback);
                    model.listenTo(childModel, 'childchange', callback);
                });
            } else if (curr instanceof ThreeModel) {
                // Implies prev is array
                utils.childModelsNested(prev).forEach(function(childModel) {
                    model.stopListening(childModel);
                });
            } else {
                // Both are arrays
                var diff = utils.nestedDiff(curr, prev);

                diff.added.forEach(function(childModel) {
                    model.listenTo(childModel, 'change', callback);
                    model.listenTo(childModel, 'childchange', callback);
                });
                diff.removed.forEach(function(childModel) {
                    model.stopListening(childModel);
                });
            }
        });
    });
}


var sceneObjectModel = widgets.WidgetModel.extend({

    defaults: function() {
        return _.extend(widgets.WidgetModel.prototype.defaults.call(this), {
            _model_name: this.constructor.model_name,
            _model_module: this.constructor.model_module,
            _model_module_version: this.constructor.model_module_version
            // _model_name : 'sceneObjectModel',
            // _model_module : 'pygeojs',
            // _model_module_version : '0.1.0',
        });
    },

    initialize: function(attributes, options) {
        widgets.WidgetModel.prototype.initialize.apply(this, arguments);
        //console.log(`init sceneObjectModel; constructor ${this.constructor}:`);
        console.log(`this.constructor.model_name ${this.constructor.model_name}`);
        //console.log(`this.constructor.model_module`);
        this.obj = {};
        this.createPropertiesArrays();

        // if (options.three_obj) {
        //     // We are defining the object from a given THREE object!

        //     // We need to push a default state first, as comm open does
        //     // not support buffers!
        //     // Fixed in:
        //     // - @jupyterlab/services@0.49.0
        //     // - notebook 5.1.0
        //     this.save_changes();

        //     var obj = options.three_obj;
        //     delete options.three_obj;

        //     this.processNewObj(obj);

        //     this.initPromise = this.createUninitializedChildren().bind(this).then(function() {

        //         // sync in all the properties from the THREE object
        //         this.syncToModel(true);

        //         // setup msg, model, and children change listeners
        //         this.setupListeners();

        //     });
        //     return;
        // }

        // Instantiate GeoJS.js object
        this.initPromise = this.createGeoJSObjectAsync().then(() => {
            return this.createUninitializedChildren();
        }).then(() => {

            // pull in props created by geojs
            this.syncToModel();

            // // sync the rest from the server to the model
            this.syncToGeoJSObj(true);

            // setup msg, model, and children change listeners
            this.setupListeners();
        });

    },

    // Over-ride this method to customize how GeoJS object is created
    constructGeoJSObject: function() {console.log('sceneObject.constructGeoJSObject')},

    createPropertiesArrays: function() {
        console.log('sceneObjectModel.createPropertiesArrays()');
        this.geojs_properties = [];
        this.geojs_nested_properties = [];
        this.datawidget_properties = [];

        this.props_created_by_geojs = {};
        this.property_converters = {};
        this.property_assigners = {};
        this.property_mappers = {};

        this.initialized_from_geojs = {};
    },

    createGeoJSObjectAsync: function() {

        var objPromise;

        // call constructor method overridden by every class
        // check for custom async three obj creator
        if (this.constructGeoJSObjectAsync) {
            objPromise = this.constructGeoJSObjectAsync();
        } else if (this.constructGeoJSObject) {
            objPromise = Promise.resolve(this.constructGeoJSObject());
        } else {
            throw new Error('no THREE construct method exists: this.createThreeObjectAsync');
        }

        return objPromise.then(obj => this.processNewObj);


    },

    createUninitializedChildren: function() {

        // Get any properties to create from this side
        var uninit = _.filter(this.geojs_properties, function(propName) {
            return this.get(propName) === 'uninitialized';
        }, this);

        // Return promise for their creation
        return Promise.all(_.map(uninit, function(propName) {
            this.initialized_from_three[propName] = true;
            var obj = this.obj[propName];
            // First, we need to figure out which model constructor to use
            var ctorName = utils.lookupThreeConstructorName(obj) + 'Model';
            var index = __webpack_require__(7);
            var ctor = index[ctorName];
            // Create the model
            var modelPromise = utils.createModel(ctor, this.widget_manager, obj);
            return modelPromise;
        }, this));
    },

    // For subclasses to retrieve list of propNames for (immutable) constructor args
    getConstructorArgNames: function() {
        return [];
    },

    //
    // Data-binding methods for syncing between model and three.js object
    //

    onChange: function(model, options) {
        if (options !== 'pushFromThree') {
            this.syncToGeoJSObj();
            // Also sync back out any generated properties:
            this.syncToModel();
        }
    },

    onChildChanged: function(model) {
        console.debug('child changed: ' + model.model_id);
        // Propagate up hierarchy:
        this.trigger('childchange', this);
    },

    processNewObj: function(obj) {

        obj.ipymodelId = this.model_id; // brand that sucker
        obj.ipymodel = this;

        this.obj = obj;
        return obj;
    },

    setupListeners: function() {

        // Handle changes in three instance props
        this.geojs_properties.forEach(function(propName) {
            // register listener for current child value
            var curValue = this.get(propName);
            if (curValue) {
                this.listenTo(curValue, 'change', this.onChildChanged.bind(this));
                this.listenTo(curValue, 'childchange', this.onChildChanged.bind(this));
            }

            // make sure to (un)hook listeners when child points to new object
            this.on('change:' + propName, function(model, value) {
                var prevModel = this.previous(propName);
                var currModel = value;
                if (prevModel) {
                    this.stopListening(prevModel);
                }
                if (currModel) {
                    this.listenTo(currModel, 'change', this.onChildChanged.bind(this));
                    this.listenTo(currModel, 'childchange', this.onChildChanged.bind(this));
                }
            }, this);
        }, this);

        // Handle changes in three instance nested props (arrays/dicts, possibly nested)
        listenNested(this, this.geojs_nested_properties, this.onChildChanged.bind(this));

        // Handle changes in data widgets/union properties
        this.datawidget_properties.forEach(function(propName) {
            dataserializers.listenToUnion(this, propName, this.onChildChanged.bind(this), false);
        }, this);

        this.on('change', this.onChange, this);
        this.on('msg:custom', this.onCustomMessage, this);

    },

    // push data from model to geojs object
    syncToGeoJSObj: function(force) {
            // console.log('property_assigners:');
            // console.dir(this.property_assigners);
            console.debug(`${this.constructor.model_name} syncToGeoJSObj(), obj:`);

        _.each(this.property_converters, function(converterName, propName) {
            // console.log(`converterName ${converterName}, propName ${propName}`)
            if (!force && !this.hasChanged(propName)) {
                // Only set changed properties unless forced
                return;
            }
            var assigner = this[this.property_assigners[propName]] || this.assignDirect;
            assigner = assigner.bind(this);
            if (!converterName) {
                assigner(this.obj, propName, this.get(propName));
                return;
            }

            converterName = converterName + 'ModelToThree';
            var converterFn = this[converterName];
            if (!converterFn) {
                throw new Error('invalid converter name: ' + converterName);
            }
            assigner(this.obj, propName, converterFn.bind(this)(this.get(propName), propName));
        }, this);

        // mappers are used for more complicated conversions between model and three props
        // see: DataTexture
        _.each(this.property_mappers, function(mapperName) {
            if (!mapperName) {
                throw new Error('invalid mapper name: ' + mapperName);
            }

            mapperName = mapperName + 'ModelToThree';
            var mapperFn = this[mapperName];
            if (!mapperFn) {
                throw new Error('invalid mapper name: ' + mapperName);
            }

            mapperFn.bind(this)();
        }, this);
    },


    // push data from geojs object to model
    syncToModel: function(syncAllProps) {

        syncAllProps = syncAllProps === null ? false : syncAllProps;

        // Collect all the keys to set in one go
        var toSet = {};

        _.each(this.property_converters, function(converterName, propName) {
            // console.log(`converterName ${converterName}, propName ${propName}`)
            if (!syncAllProps && !(propName in this.props_created_by_geojs)) {
                if (this.initialized_from_geojs[propName]) {
                    delete this.initialized_from_geojs[propName];
                } else {
                    return;
                }
            }
            if (!converterName) {
                toSet[propName] = this.obj[propName]();
                return;
            }

            converterName = converterName + 'ThreeToModel';
            var converterFn = this[converterName];
            if (!converterFn) {
                throw new Error('invalid converter name: ' + converterName);
            }

            toSet[propName] = converterFn.bind(this)(this.obj[propName], propName);
        }, this);

        // console.log('toSet:');
        // console.dir(toSet);
        if (toSet) {
            // Apply all direct changes at once
            this.set(toSet, 'pushFromGeoJS');
        }

        // mappers are used for more complicated conversions between model and geojs props
        // see: DataTexture
        _.each(this.property_mappers, function(mapperName, dataKey) {
            if (!mapperName) {
                throw new Error('invalid mapper name: ' + mapperName);
            }

            mapperName = mapperName + 'ThreeToModel';
            var mapperFn = this[mapperName];
            if (!mapperFn) {
                throw new Error('invalid mapper name: ' + mapperName);
            }

            mapperFn.bind(this)(dataKey);
        }, this);

        this.save_changes();
    },


    //
    // Conversions
    //

    assignDirect: function(obj, key, value) {
        // For some reason, obj is undefined at least sometimes...
        if (!obj) {
            console.log(`model ${this.constructor.model_name}, obj ${obj}, key ${key}, value ${value}, typeof ${typeof obj[key]}`);
            return;
        }
        if (typeof obj[key] == 'function') {
            obj[key](value);
        }
        else {
            console.log(`model ${this.constructor.model_name}, obj ${obj}, key ${key}, value ${value}, typeof ${typeof obj[key]}`);
            obj[key] = value;
        }
    },

    /**
     * Check if array exists, if so replace content. Otherwise assign value.
     */
    assignArray: function(obj, key, value) {
        // For some reason, obj is undefined sometimes (wish I knew why)
        if (!obj) {
            return;
        }
        var existing = obj[key];
        if (existing !== null && existing !== undefined) {
            // existing.splice(0, existing.length, ...value);
            existing.splice.apply(existing, [0, existing.length].concat(value));
        } else {
            obj[key] = value;
        }
    },

    // Float
    convertFloatModelToThree: function(v) {
        if (typeof v === 'string' || v instanceof String) {
            v = v.toLowerCase();
            if (v === 'inf') {
                return Infinity;
            } else if (v === '-inf') {
                return -Infinity;
            } else if (v === 'nan') {
                return NaN;
            }
        }
        return v;
    },

    convertFloatThreeToModel: function(v) {
        if (isFinite(v)) { // Most common first
            return v;
        } else if (isNaN(v)) {
            return 'nan';
        } else if (v === Infinity) {
            return 'inf';
        } else if (v === -Infinity) {
            return '-inf';
        }
        return v;
    },

    // Bool
    convertBoolModelToThree: function(v) {
        return v;
    },

    convertBoolThreeToModel: function(v) {
        if (v === null) {
            return null;
        }
        // Coerce falsy/truthy:
        return !!v;
    },

    // Enum
    convertEnumModelToThree: function(e) {
        if (e === null) {
            return null;
        }
        return THREE[e];
    },

    convertEnumThreeToModel: function(e, propName) {
        if (e === null) {
            return null;
        }
        var enumType = this.enum_property_types[propName];
        var enumValues = Enums[enumType];
        var enumValueName = enumValues[e];
        return enumValueName;
    },

    // Vectors
    convertVectorModelToThree: function(v) {
        var result;
        switch(v.length) {
        case 2: result = new THREE.Vector2(); break;
        case 3: result = new THREE.Vector3(); break;
        case 4: result = new THREE.Vector4(); break;
        default:
            throw new Error('model vector has invalid length: ' + v.length);
        }
        result.fromArray(v);
        return result;
    },

    convertVectorThreeToModel: function(v) {
        return v.toArray();
    },

    assignVector: function(obj, key, value) {
        obj[key].copy(value);
    },

    // Euler
    convertEulerModelToThree: function(v) {
        return new THREE.Euler().fromArray(v);
    },

    convertEulerThreeToModel: function(v) {
        return v.toArray();
    },

    assignEuler: function(obj, key, value) {
        obj[key].copy(value);
    },

    // Vector Array
    convertVectorArrayModelToThree: function(varr, propName) {
        return varr.map(function(v) {
            return this.convertVectorModelToThree(v, propName);
        }, this);
    },

    convertVectorArrayThreeToModel: function(varr, propName) {
        return varr.map(function(v) {
            return this.convertVectorThreeToModel(v, propName);
        }, this);
    },

    // Color Array
    convertColorArrayModelToThree: function(carr, propName) {
        return carr.map(function(c) {
            return this.convertColorModelToThree(c, propName);
        }, this);
    },

    convertColorArrayThreeToModel: function(carr, propName) {
        return carr.map(function(c) {
            return this.convertColorThreeToModel(c, propName);
        }, this);
    },

    // Faces
    convertFaceModelToThree: function(f) {
        var normal = f[3];
        if (normal !== undefined && normal !== null) {
            if (Array.isArray(normal) && normal.length > 0 && Array.isArray(normal[0])) {
                normal = normal.map(function (value) {
                    return this.convertVectorModelToThree(value);
                }, this);
            } else {
                normal = this.convertVectorModelToThree(normal);
            }
        }
        var color = f[4];
        if (color !== undefined && color !== null) {
            if (Array.isArray(color)) {
                color = color.map(function (value) {
                    return new THREE.Color(value);
                }, this);
            } else {
                color = new THREE.Color(color);
            }
        }
        var result = new THREE.Face3(
            f[0],                                           // a
            f[1],                                           // b
            f[2],                                           // c
            normal,                                         // normal
            color,                                          // color
            f[5]                                            // materialIndex
        );

        return result;
    },

    convertFaceThreeToModel: function(f) {
        return [
            f.a,
            f.b,
            f.c,
            f.normal.toArray(),
            this.convertColorThreeToModel(f.color),
            f.materialIndex,
            this.convertVectorArrayThreeToModel(f.vertexNormals),
            this.convertColorArrayThreeToModel(f.vertexColors),
        ];
    },

    // Face Array
    convertFaceArrayModelToThree: function(farr, propName) {
        return farr.map(function(f) {
            return this.convertFaceModelToThree(f, propName);
        }, this);
    },

    convertFaceArrayThreeToModel: function(farr, propName) {
        return farr.map(function(f) {
            return this.convertFaceThreeToModel(f, propName);
        }, this);
    },

    // Matrices
    convertMatrixModelToThree: function(m) {
        var result;
        switch(m.length) {
        case 9: result = new THREE.Matrix3(); break;
        case 16: result = new THREE.Matrix4(); break;
        default:
            throw new Error('model matrix has invalid length: ' + m.length);
        }
        result.fromArray(m);
        return result;
    },

    convertMatrixThreeToModel: function(m) {
        return m.toArray();
    },

    assignMatrix: function(obj, key, value) {
        obj[key].copy(value);
    },

    // Functions
    convertFunctionModelToThree: function(fnStr) {
        var fn;
        eval('fn = ' + fnStr);
        return fn;
    },

    convertFunctionThreeToModelToThree: function(fn) {
        return fn.toString();
    },

    // ThreeType
    convertThreeTypeModelToThree: function(model) {
        if (model) {
            return model.obj;
        }
        return null;
    },

    convertThreeTypeThreeToModel: function(threeType) {
        if (!threeType) {
            return threeType;
        }
        return threeType.ipymodel;
    },

    // Dict
    assignDict: function(obj, key, value) {
        if (obj[key] === value) {
            // If instance equality, do nothing.
            return;
        }
        if (obj[key] === undefined || obj[key] === null) {
            if (value === null || value === undefined) {
                // Leave it as it is
                return;
            }
            obj[key] = {};
        }
        // Clear the dict
        Object.keys(obj[key]).forEach(function(k) {
            delete obj[key][k];
        });
        // Put in the new values
        Object.assign(obj[key], value);
    },

    convertUniformDictModelToThree: function(modelDict) {
        if (modelDict === null) {
            return null;
        }
        // Convert any strings to THREE.Color
        // Just modify dict in-place, as it should serialize the same
        Object.keys(modelDict).forEach(function(k) {
            var value = modelDict[k].value;
            if (value && (typeof value === 'string' || value instanceof String)) {
                modelDict[k].value = new THREE.Color(value);
            }
        });
        return modelDict;
    },

    convertUniformDictThreeToModel: function(threeDict) {
        // No-op
        return threeDict;
    },

    // ThreeTypeArray
    convertThreeTypeArrayModelToThree: function(modelArr, propName) {
        if (!Array.isArray(modelArr)) {
            return this.convertThreeTypeModelToThree(modelArr, propName);
        }
        return modelArr.map(function(model) {
            return this.convertThreeTypeModelToThree(model, propName);
        }, this);
    },

    convertThreeTypeArrayThreeToModel: function(threeTypeArr, propName) {
        if (!Array.isArray(threeTypeArr)) {
            return this.convertThreeTypeThreeToModel(threeTypeArr, propName);
        }
        return threeTypeArr.map(function(threeType) {
            return this.convertThreeTypeThreeToModel(threeType, propName);
        }, this);
    },

    // ThreeTypeDict
    convertThreeTypeDictModelToThree: function(modelDict, propName) {
        return _.mapObject(modelDict, function(model) {
            return this.convertThreeTypeModelToThree(model, propName);
        }, this);
    },

    convertThreeTypeDictThreeToModel: function(threeTypeDict, propName) {
        return _.mapObject(threeTypeDict, function(threeType) {
            return this.convertThreeTypeThreeToModel(threeType, propName);
        }, this);
    },

    // BufferMorphAttributes
    convertMorphAttributesModelToThree: function(modelDict, propName) {
        return _.mapObject(modelDict, function(arr) {
            return arr.map(function(model) {
                return this.convertThreeTypeModelToThree(model, propName);
            }, this);
        }, this);
    },

    convertMorphAttributesThreeToModel: function(threeTypeDict, propName) {
        return _.mapObject(threeTypeDict, function(arr) {
            return arr.map(function(model) {
                return this.convertThreeTypeThreeToModel(model, propName);
            }, this);
        }, this);
    },

    // ArrayBuffer
    convertArrayBufferModelToThree: function(arr) {
        if (arr === null) {
            return null;
        }
        if (arr instanceof widgets.WidgetModel) {
            return arr.get('array').data;
        }
        return arr.data;
    },

    convertArrayBufferThreeToModel: function(arrBuffer) {
        if (arrBuffer === null) {
            return null;
        }
        // Never back-convert to a new widget
        return ndarray(arrBuffer);
    },

    // Color
    convertColorModelToThree: function(c) {
        if (c === null) {
            return null;
        }
        return new THREE.Color(c);
    },

    convertColorThreeToModel: function(c) {
        if (c === null) {
            return null;
        }
        return '#' + c.getHexString();
    },



});

// {
//     model_module: 'pygeojs',
//     model_name: 'sceneObjectModel',
//     model_module_version: EXTENSION_SPEC_VERSION,
// });

module.exports = {
    sceneObjectModel: sceneObjectModel,
};


/***/ }),
/* 6 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


// (C) 1995-2013 Jean-loup Gailly and Mark Adler
// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
//   claim that you wrote the original software. If you use this software
//   in a product, an acknowledgment in the product documentation would be
//   appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
//   misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.

module.exports = {
  2:      'need dictionary',     /* Z_NEED_DICT       2  */
  1:      'stream end',          /* Z_STREAM_END      1  */
  0:      '',                    /* Z_OK              0  */
  '-1':   'file error',          /* Z_ERRNO         (-1) */
  '-2':   'stream error',        /* Z_STREAM_ERROR  (-2) */
  '-3':   'data error',          /* Z_DATA_ERROR    (-3) */
  '-4':   'insufficient memory', /* Z_MEM_ERROR     (-4) */
  '-5':   'buffer error',        /* Z_BUF_ERROR     (-5) */
  '-6':   'incompatible version' /* Z_VERSION_ERROR (-6) */
};


/***/ }),
/* 7 */
/***/ (function(module, exports, __webpack_require__) {

module.exports['version'] = __webpack_require__(19).version

var loadedModules = [
  __webpack_require__(8),
  __webpack_require__(16),
  __webpack_require__(17),
  __webpack_require__(41),
  __webpack_require__(42),
  __webpack_require__(45),
  __webpack_require__(5),
  __webpack_require__(18)
];

for (var i in loadedModules) {
  if (loadedModules.hasOwnProperty(i)) {
    var loadedModule = loadedModules[i];
    for (var targetName in loadedModule) {
      if (loadedModule.hasOwnProperty(targetName)) {
        module.exports[targetName] = loadedModule[targetName];
      }
    }
  }
}


/***/ }),
/* 8 */
/***/ (function(module, exports, __webpack_require__) {

//
// This file auto-generated with generate-wrappers.js
//

var _ = __webpack_require__(0);
var GeoJS = __webpack_require__(1);
var widgets = __webpack_require__(2);
var dataserializers = __webpack_require__(3);

var sceneObjectModel = __webpack_require__(5).sceneObjectModel;


var featureModel = sceneObjectModel.extend({

    defaults: function() {
        return _.extend(sceneObjectModel.prototype.defaults.call(this), {

            _data: [],
            bin: null,
            data: [],
            gcs: null,
            selectionAPI: false,
            style: {},
            visible: true,

        });
    },

    createPropertiesArrays: function() {

        sceneObjectModel.prototype.createPropertiesArrays.call(this);

        this.props_created_by_geojs['bin'] = true;
        this.props_created_by_geojs['gcs'] = true;

        this.property_converters['_data'] = null;
        this.property_converters['bin'] = 'convertFloat';
        this.property_converters['data'] = null;
        this.property_converters['gcs'] = null;
        this.property_converters['selectionAPI'] = 'convertBool';
        this.property_converters['style'] = null;
        this.property_converters['visible'] = 'convertBool';

        this.property_assigners['_data'] = 'assignArray';
        this.property_assigners['data'] = 'assignArray';
        this.property_assigners['style'] = 'assignDict';

    },

}, {

    model_name: 'featureModel',

    serializers: _.extend({
    },  sceneObjectModel.serializers),
});

module.exports = {
    featureModel: featureModel,
};


/***/ }),
/* 9 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __generator = (this && this.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (_) try {
            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [op[0] & 2, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
Object.defineProperty(exports, "__esModule", { value: true });
var util_1 = __webpack_require__(24);
var compression_1 = __webpack_require__(25);
var ndarray = __webpack_require__(35);
;
function ensureSerializableDtype(dtype) {
    if (dtype === 'array' || dtype === 'buffer' || dtype === 'generic') {
        throw new Error("Cannot serialize ndarray with dtype: " + dtype + ".");
    }
    else if (dtype === 'uint8_clamped') {
        dtype = 'uint8';
    }
    return dtype;
}
exports.ensureSerializableDtype = ensureSerializableDtype;
/**
 * Deserialize from JSON to an ndarray object.
 *
 * @param obj The deserialized JSON to convert
 * @param manager The owning widget manager
 *
 * @returns A new ndarray object.
 */
function JSONToArray(obj, manager) {
    if (obj === null) {
        return null;
    }
    // obj is {shape: list, dtype: string, array: DataView}
    // return an ndarray object
    return ndarray(new exports.typesToArray[obj.dtype](obj.buffer.buffer), obj.shape);
}
exports.JSONToArray = JSONToArray;
/**
 * Serialize to JSON from an ndarray object.
 *
 * @param obj The ndarray object to convert
 * @param manager The owning widget model
 *
 * @returns The JSON object representing the ndarray.
 */
function arrayToJSON(obj, widget) {
    if (obj === null) {
        return null;
    }
    var dtype = ensureSerializableDtype(obj.dtype);
    // serialize to {shape: list, dtype: string, array: buffer}
    return { shape: obj.shape, dtype: dtype, buffer: obj.data };
}
exports.arrayToJSON = arrayToJSON;
/**
 * Serializers for to/from ndarrays
 */
exports.array_serialization = { deserialize: JSONToArray, serialize: arrayToJSON };
exports.typesToArray = {
    int8: Int8Array,
    int16: Int16Array,
    int32: Int32Array,
    uint8: Uint8Array,
    uint8_clamped: Uint8ClampedArray,
    uint16: Uint16Array,
    uint32: Uint32Array,
    float32: Float32Array,
    float64: Float64Array
};
function compressedJSONToArray(obj, manager) {
    return __awaiter(this, void 0, void 0, function () {
        var buffer;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    if (obj === null) {
                        return [2 /*return*/, null];
                    }
                    if (!(obj.compressed_buffer !== undefined)) return [3 /*break*/, 2];
                    return [4 /*yield*/, compression_1.decompress(obj.compressed_buffer.buffer)];
                case 1:
                    buffer = _a.sent();
                    return [3 /*break*/, 3];
                case 2:
                    buffer = obj.buffer.buffer;
                    _a.label = 3;
                case 3: 
                // obj is {shape: list, dtype: string, array: DataView}
                // return an ndarray object
                return [2 /*return*/, ndarray(new exports.typesToArray[obj.dtype](buffer), obj.shape)];
            }
        });
    });
}
exports.compressedJSONToArray = compressedJSONToArray;
function arrayToCompressedJSON(obj, widget) {
    return __awaiter(this, void 0, void 0, function () {
        var dtype, level, compressed_buffer;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    if (obj === null) {
                        return [2 /*return*/, null];
                    }
                    dtype = ensureSerializableDtype(obj.dtype);
                    level = widget ? widget.get('compression_level') : 0;
                    if (!(level !== undefined && level > 0)) return [3 /*break*/, 2];
                    return [4 /*yield*/, compression_1.compress(obj.data.buffer, level)];
                case 1:
                    compressed_buffer = _a.sent();
                    // serialize to {shape: list, dtype: string, array: buffer}
                    return [2 /*return*/, { shape: obj.shape, dtype: dtype, compressed_buffer: compressed_buffer }];
                case 2: 
                // serialize to {shape: list, dtype: string, array: buffer}
                return [2 /*return*/, { shape: obj.shape, dtype: dtype, buffer: obj.data }];
            }
        });
    });
}
exports.arrayToCompressedJSON = arrayToCompressedJSON;
exports.compressed_array_serialization = {
    deserialize: compressedJSONToArray,
    serialize: arrayToCompressedJSON
};
function typedArrayToType(array) {
    if (array instanceof Int8Array) {
        return 'int8';
    }
    else if (array instanceof Int16Array) {
        return 'int16';
    }
    else if (array instanceof Int32Array) {
        return 'int32';
    }
    else if (array instanceof Uint8Array) {
        return 'uint8';
    }
    else if (array instanceof Uint8ClampedArray) {
        return 'uint8';
    }
    else if (array instanceof Uint16Array) {
        return 'uint16';
    }
    else if (array instanceof Uint32Array) {
        return 'uint32';
    }
    else if (array instanceof Float32Array) {
        return 'float32';
    }
    else if (array instanceof Float64Array) {
        return 'float64';
    }
    else {
        throw new Error("Unknown TypedArray type: " + array);
    }
}
exports.typedArrayToType = typedArrayToType;
;
/**
 * Deserialize from JSON to a typed array. Discards shape information.
 *
 * @param obj The deserialized JSON to convert
 * @param manager The owning widget manager
 *
 * @returns A new typed array.
 */
function JSONToTypedArray(obj, manager) {
    if (obj === null) {
        return null;
    }
    // obj is {shape: list, dtype: string, array: DataView}
    return new exports.typesToArray[obj.dtype](obj.buffer.buffer);
}
exports.JSONToTypedArray = JSONToTypedArray;
/**
 * Serialize to JSON from a typed array.
 *
 * @param obj The typed array to convert
 * @param manager The owning widget model
 *
 * @returns The JSON object representing the typed array.
 */
function typedArrayToJSON(obj, widget) {
    if (obj === null) {
        return null;
    }
    // serialize to {shape: list, dtype: string, array: buffer}
    return { shape: [obj.length], dtype: typedArrayToType(obj), buffer: obj };
}
exports.typedArrayToJSON = typedArrayToJSON;
/**
 * Serializers for to/from 1D typed array
 */
exports.typedarray_serialization = { deserialize: JSONToTypedArray, serialize: typedArrayToJSON };
/**
 * Deserialize from JSON to a simple object with shape and typed array.
 *
 * @param obj The deserialized JSON to convert
 * @param manager The owning widget manager
 *
 * @returns A new object containg the data.
 */
function JSONToSimple(obj, manager) {
    if (obj === null) {
        return null;
    }
    // obj is {shape: list, dtype: string, array: DataView}
    return { array: new exports.typesToArray[obj.dtype](obj.buffer.buffer), shape: obj.shape };
}
exports.JSONToSimple = JSONToSimple;
/**
 * Serialize to JSON from a simple object.
 *
 * @param obj The simple object to convert
 * @param manager The owning widget model
 *
 * @returns The JSON object representing the simple object.
 */
function simpleToJSON(obj, widget) {
    if (obj === null) {
        return null;
    }
    // serialize to {shape: list, dtype: string, array: buffer}
    return { shape: obj.shape, dtype: typedArrayToType(obj.array), buffer: obj.array };
}
exports.simpleToJSON = simpleToJSON;
/**
 * Serializers for to/from 1D simple object containing array and metadata.
 */
exports.simplearray_serialization = { deserialize: JSONToSimple, serialize: simpleToJSON };
/**
 * Factory for serialziers to/from a typed array with fixed shape.
 */
function fixed_shape_serialization(shape) {
    var fixedLength = 1;
    for (var _i = 0, shape_1 = shape; _i < shape_1.length; _i++) {
        var dim = shape_1[_i];
        fixedLength *= dim;
    }
    function JSONToFixedShape(obj, manager) {
        if (obj === null) {
            return null;
        }
        if (!util_1.arraysEqual(obj.shape, shape)) {
            throw new Error("Incoming data unexpected shape: " + obj.shape + ", expected " + shape);
        }
        // obj is {shape: list, dtype: string, array: DataView}
        return new exports.typesToArray[obj.dtype](obj.buffer.buffer);
    }
    function fixedShapeToJSON(obj, widget) {
        if (obj === null) {
            return null;
        }
        if (obj.length !== fixedLength) {
            throw new Error("Data has wrong size for fixed shape serialization! Expected " + fixedLength + " elements, got " + obj.length + ".");
        }
        // serialize to {shape: list, dtype: string, array: buffer}
        return { shape: shape, dtype: typedArrayToType(obj), buffer: obj };
    }
    return { deserialize: JSONToFixedShape, serialize: fixedShapeToJSON };
}
exports.fixed_shape_serialization = fixed_shape_serialization;
//# sourceMappingURL=ndarray.js.map

/***/ }),
/* 10 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


// Note: adler32 takes 12% for level 0 and 2% for level 6.
// It isn't worth it to make additional optimizations as in original.
// Small size is preferable.

// (C) 1995-2013 Jean-loup Gailly and Mark Adler
// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
//   claim that you wrote the original software. If you use this software
//   in a product, an acknowledgment in the product documentation would be
//   appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
//   misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.

function adler32(adler, buf, len, pos) {
  var s1 = (adler & 0xffff) |0,
      s2 = ((adler >>> 16) & 0xffff) |0,
      n = 0;

  while (len !== 0) {
    // Set limit ~ twice less than 5552, to keep
    // s2 in 31-bits, because we force signed ints.
    // in other case %= will fail.
    n = len > 2000 ? 2000 : len;
    len -= n;

    do {
      s1 = (s1 + buf[pos++]) |0;
      s2 = (s2 + s1) |0;
    } while (--n);

    s1 %= 65521;
    s2 %= 65521;
  }

  return (s1 | (s2 << 16)) |0;
}


module.exports = adler32;


/***/ }),
/* 11 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


// Note: we can't get significant speed boost here.
// So write code to minimize size - no pregenerated tables
// and array tools dependencies.

// (C) 1995-2013 Jean-loup Gailly and Mark Adler
// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
//   claim that you wrote the original software. If you use this software
//   in a product, an acknowledgment in the product documentation would be
//   appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
//   misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.

// Use ordinary array, since untyped makes no boost here
function makeTable() {
  var c, table = [];

  for (var n = 0; n < 256; n++) {
    c = n;
    for (var k = 0; k < 8; k++) {
      c = ((c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1));
    }
    table[n] = c;
  }

  return table;
}

// Create table on load. Just 255 signed longs. Not a problem.
var crcTable = makeTable();


function crc32(crc, buf, len, pos) {
  var t = crcTable,
      end = pos + len;

  crc ^= -1;

  for (var i = pos; i < end; i++) {
    crc = (crc >>> 8) ^ t[(crc ^ buf[i]) & 0xFF];
  }

  return (crc ^ (-1)); // >>> 0;
}


module.exports = crc32;


/***/ }),
/* 12 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
// String encode/decode helpers



var utils = __webpack_require__(4);


// Quick check if we can use fast array to bin string conversion
//
// - apply(Array) can fail on Android 2.2
// - apply(Uint8Array) can fail on iOS 5.1 Safari
//
var STR_APPLY_OK = true;
var STR_APPLY_UIA_OK = true;

try { String.fromCharCode.apply(null, [ 0 ]); } catch (__) { STR_APPLY_OK = false; }
try { String.fromCharCode.apply(null, new Uint8Array(1)); } catch (__) { STR_APPLY_UIA_OK = false; }


// Table with utf8 lengths (calculated by first byte of sequence)
// Note, that 5 & 6-byte values and some 4-byte values can not be represented in JS,
// because max possible codepoint is 0x10ffff
var _utf8len = new utils.Buf8(256);
for (var q = 0; q < 256; q++) {
  _utf8len[q] = (q >= 252 ? 6 : q >= 248 ? 5 : q >= 240 ? 4 : q >= 224 ? 3 : q >= 192 ? 2 : 1);
}
_utf8len[254] = _utf8len[254] = 1; // Invalid sequence start


// convert string to array (typed, when possible)
exports.string2buf = function (str) {
  var buf, c, c2, m_pos, i, str_len = str.length, buf_len = 0;

  // count binary size
  for (m_pos = 0; m_pos < str_len; m_pos++) {
    c = str.charCodeAt(m_pos);
    if ((c & 0xfc00) === 0xd800 && (m_pos + 1 < str_len)) {
      c2 = str.charCodeAt(m_pos + 1);
      if ((c2 & 0xfc00) === 0xdc00) {
        c = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00);
        m_pos++;
      }
    }
    buf_len += c < 0x80 ? 1 : c < 0x800 ? 2 : c < 0x10000 ? 3 : 4;
  }

  // allocate buffer
  buf = new utils.Buf8(buf_len);

  // convert
  for (i = 0, m_pos = 0; i < buf_len; m_pos++) {
    c = str.charCodeAt(m_pos);
    if ((c & 0xfc00) === 0xd800 && (m_pos + 1 < str_len)) {
      c2 = str.charCodeAt(m_pos + 1);
      if ((c2 & 0xfc00) === 0xdc00) {
        c = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00);
        m_pos++;
      }
    }
    if (c < 0x80) {
      /* one byte */
      buf[i++] = c;
    } else if (c < 0x800) {
      /* two bytes */
      buf[i++] = 0xC0 | (c >>> 6);
      buf[i++] = 0x80 | (c & 0x3f);
    } else if (c < 0x10000) {
      /* three bytes */
      buf[i++] = 0xE0 | (c >>> 12);
      buf[i++] = 0x80 | (c >>> 6 & 0x3f);
      buf[i++] = 0x80 | (c & 0x3f);
    } else {
      /* four bytes */
      buf[i++] = 0xf0 | (c >>> 18);
      buf[i++] = 0x80 | (c >>> 12 & 0x3f);
      buf[i++] = 0x80 | (c >>> 6 & 0x3f);
      buf[i++] = 0x80 | (c & 0x3f);
    }
  }

  return buf;
};

// Helper (used in 2 places)
function buf2binstring(buf, len) {
  // use fallback for big arrays to avoid stack overflow
  if (len < 65537) {
    if ((buf.subarray && STR_APPLY_UIA_OK) || (!buf.subarray && STR_APPLY_OK)) {
      return String.fromCharCode.apply(null, utils.shrinkBuf(buf, len));
    }
  }

  var result = '';
  for (var i = 0; i < len; i++) {
    result += String.fromCharCode(buf[i]);
  }
  return result;
}


// Convert byte array to binary string
exports.buf2binstring = function (buf) {
  return buf2binstring(buf, buf.length);
};


// Convert binary string (typed, when possible)
exports.binstring2buf = function (str) {
  var buf = new utils.Buf8(str.length);
  for (var i = 0, len = buf.length; i < len; i++) {
    buf[i] = str.charCodeAt(i);
  }
  return buf;
};


// convert array to string
exports.buf2string = function (buf, max) {
  var i, out, c, c_len;
  var len = max || buf.length;

  // Reserve max possible length (2 words per char)
  // NB: by unknown reasons, Array is significantly faster for
  //     String.fromCharCode.apply than Uint16Array.
  var utf16buf = new Array(len * 2);

  for (out = 0, i = 0; i < len;) {
    c = buf[i++];
    // quick process ascii
    if (c < 0x80) { utf16buf[out++] = c; continue; }

    c_len = _utf8len[c];
    // skip 5 & 6 byte codes
    if (c_len > 4) { utf16buf[out++] = 0xfffd; i += c_len - 1; continue; }

    // apply mask on first byte
    c &= c_len === 2 ? 0x1f : c_len === 3 ? 0x0f : 0x07;
    // join the rest
    while (c_len > 1 && i < len) {
      c = (c << 6) | (buf[i++] & 0x3f);
      c_len--;
    }

    // terminated by end of string?
    if (c_len > 1) { utf16buf[out++] = 0xfffd; continue; }

    if (c < 0x10000) {
      utf16buf[out++] = c;
    } else {
      c -= 0x10000;
      utf16buf[out++] = 0xd800 | ((c >> 10) & 0x3ff);
      utf16buf[out++] = 0xdc00 | (c & 0x3ff);
    }
  }

  return buf2binstring(utf16buf, out);
};


// Calculate max possible position in utf8 buffer,
// that will not break sequence. If that's not possible
// - (very small limits) return max size as is.
//
// buf[] - utf8 bytes array
// max   - length limit (mandatory);
exports.utf8border = function (buf, max) {
  var pos;

  max = max || buf.length;
  if (max > buf.length) { max = buf.length; }

  // go back from last position, until start of sequence found
  pos = max - 1;
  while (pos >= 0 && (buf[pos] & 0xC0) === 0x80) { pos--; }

  // Very small and broken sequence,
  // return max, because we should return something anyway.
  if (pos < 0) { return max; }

  // If we came to start of buffer - that means buffer is too small,
  // return max too.
  if (pos === 0) { return max; }

  return (pos + _utf8len[buf[pos]] > max) ? pos : max;
};


/***/ }),
/* 13 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


// (C) 1995-2013 Jean-loup Gailly and Mark Adler
// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
//   claim that you wrote the original software. If you use this software
//   in a product, an acknowledgment in the product documentation would be
//   appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
//   misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.

function ZStream() {
  /* next input byte */
  this.input = null; // JS specific, because we have no pointers
  this.next_in = 0;
  /* number of bytes available at input */
  this.avail_in = 0;
  /* total number of input bytes read so far */
  this.total_in = 0;
  /* next output byte should be put there */
  this.output = null; // JS specific, because we have no pointers
  this.next_out = 0;
  /* remaining free space at output */
  this.avail_out = 0;
  /* total number of bytes output so far */
  this.total_out = 0;
  /* last error message, NULL if no error */
  this.msg = ''/*Z_NULL*/;
  /* not visible by applications */
  this.state = null;
  /* best guess about the data type: binary or text */
  this.data_type = 2/*Z_UNKNOWN*/;
  /* adler32 value of the uncompressed data */
  this.adler = 0;
}

module.exports = ZStream;


/***/ }),
/* 14 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


// (C) 1995-2013 Jean-loup Gailly and Mark Adler
// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
//   claim that you wrote the original software. If you use this software
//   in a product, an acknowledgment in the product documentation would be
//   appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
//   misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.

module.exports = {

  /* Allowed flush values; see deflate() and inflate() below for details */
  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,

  /* Return codes for the compression/decompression functions. Negative values
  * are errors, positive values are used for special but normal events.
  */
  Z_OK:               0,
  Z_STREAM_END:       1,
  Z_NEED_DICT:        2,
  Z_ERRNO:           -1,
  Z_STREAM_ERROR:    -2,
  Z_DATA_ERROR:      -3,
  //Z_MEM_ERROR:     -4,
  Z_BUF_ERROR:       -5,
  //Z_VERSION_ERROR: -6,

  /* compression levels */
  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,

  /* Possible values of the data_type field (though see inflate()) */
  Z_BINARY:                 0,
  Z_TEXT:                   1,
  //Z_ASCII:                1, // = Z_TEXT (deprecated)
  Z_UNKNOWN:                2,

  /* The deflate compression method */
  Z_DEFLATED:               8
  //Z_NULL:                 null // Use -1 or null inline, depending on var type
};


/***/ }),
/* 15 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

Object.defineProperty(exports, "__esModule", { value: true });
/**
 * Whether an object implements the data source interface.
 */
function isDataSource(candidate) {
    return candidate && typeof candidate.getNDArray === 'function';
}
exports.isDataSource = isDataSource;
/**
 * Gets the array of any array source.
 */
function getArray(source) {
    if (isDataSource(source)) {
        return source.getNDArray();
    }
    return source;
}
exports.getArray = getArray;
//# sourceMappingURL=common.js.map

/***/ }),
/* 16 */
/***/ (function(module, exports, __webpack_require__) {

var _ = __webpack_require__(0);
var GeoJS = __webpack_require__(1);

var autogenFeatureLayerModel = __webpack_require__(40).featureLayerModel;


var featureLayerModel = autogenFeatureLayerModel.extend({

    defaults: function() {
        return _.extend(autogenFeatureLayerModel.prototype.defaults.call(this), {
            _features: [],
        });
    },

    constructGeoJSObject: function(layer) {
        console.log('featureLayer.buildGeoJSObject()');
        let map_model_id = this.get('map_id');
        console.log(`map_model_id: ${map_model_id}`);
        let map_model = this.widget_manager.get_model(map_model_id);
        console.log(`map_model:`);
        console.dir(map_model);

/*
        //console.log('featureLayerModel.buildGeoJSObject()');
        this.layer = layer;
        for (let _feature of this.get('_features')) {
            let model_id = _feature.slice(10);
            this.widget_manager.get_model(model_id)
                .then(model => {
                    //console.dir(model);
                    let featureModelName = model.get('_model_name');
                    let featureData = model.get('_data');
                    let positionData = model.get('position');
                    let styleData = model.get('style');
                    let geojsFeature = null;
                    switch(featureModelName) {
                        case 'pointFeatureModel':
                            geojsFeature = layer.createFeature('point');
                            geojsFeature.data(featureData);
                            geojsFeature.position((dataItem, dataIndex) => {
                              //console.debug(`dataIndex ${dataIndex}`);

                              // Workaround undiagnosed problem where dataIndex
                              // is sometimes undefined. It appears to be realted
                              // to mousemove events.
                              if (dataIndex === undefined) {
                                // Check for Kitware workaround
                                if ('__i' in dataItem) {
                                  //console.debug('dataItem is undefined');
                                  dataIndex = dataItem.__i;
                                }
                                else {
                                  throw Error('dataIndex is undefined ')
                                }
                              }  // if
                              let position = positionData[dataIndex];
                              // console.debug(`Position ${position}`);
                              return position;
                            });

                            geojsFeature.style(styleData);
                            //console.log(`Created feature ${geojsFeature}`);
                            //console.dir(geojsFeature);
                            break;

                        default:
                            console.log(`Unrecognized model name: ${featureModelName}`);
                            break;
                    }  // switch(featureModelName)
                });
        }  // for (_feature)
*/
    }  // buildGeoJSObject()

});

module.exports = {
    featureLayerModel: featureLayerModel,
};


/***/ }),
/* 17 */
/***/ (function(module, exports, __webpack_require__) {

//
// This file auto-generated with generate-wrappers.js
//

var _ = __webpack_require__(0);
var GeoJS = __webpack_require__(1);
var widgets = __webpack_require__(2);
var dataserializers = __webpack_require__(3);

var sceneObjectModel = __webpack_require__(5).sceneObjectModel;


var layerModel = sceneObjectModel.extend({

    defaults: function() {
        return _.extend(sceneObjectModel.prototype.defaults.call(this), {

            active: true,
            annotations: [],
            attribution: null,
            map_id: "",
            name: "",
            opacity: 1,
            selectionAPI: true,
            sticky: true,
            visible: true,
            zIndex: null,

        });
    },

    createPropertiesArrays: function() {

        sceneObjectModel.prototype.createPropertiesArrays.call(this);

        this.props_created_by_geojs['attribution'] = true;
        this.props_created_by_geojs['zIndex'] = true;

        this.property_converters['active'] = 'convertBool';
        this.property_converters['annotations'] = null;
        this.property_converters['attribution'] = null;
        this.property_converters['map_id'] = null;
        this.property_converters['name'] = null;
        this.property_converters['opacity'] = 'convertFloat';
        this.property_converters['selectionAPI'] = 'convertBool';
        this.property_converters['sticky'] = 'convertBool';
        this.property_converters['visible'] = 'convertBool';
        this.property_converters['zIndex'] = null;

        this.property_assigners['annotations'] = 'assignArray';

    },

}, {

    model_name: 'layerModel',

    serializers: _.extend({
    },  sceneObjectModel.serializers),
});

module.exports = {
    layerModel: layerModel,
};


/***/ }),
/* 18 */
/***/ (function(module, exports, __webpack_require__) {

//
// This file auto-generated with generate-wrappers.js
//

var _ = __webpack_require__(0);
var GeoJS = __webpack_require__(1);
var widgets = __webpack_require__(2);
var dataserializers = __webpack_require__(3);

var featureLayerModel = __webpack_require__(16).featureLayerModel;


var tileLayerModel = featureLayerModel.extend({

    defaults: function() {
        return _.extend(featureLayerModel.prototype.defaults.call(this), {


        });
    },

    createPropertiesArrays: function() {

        featureLayerModel.prototype.createPropertiesArrays.call(this);

        this.props_created_by_geojs['attribution'] = true;
        this.props_created_by_geojs['zIndex'] = true;



    },

}, {

    model_name: 'tileLayerModel',

    serializers: _.extend({
    },  featureLayerModel.serializers),
});

module.exports = {
    tileLayerModel: tileLayerModel,
};


/***/ }),
/* 19 */
/***/ (function(module, exports) {

module.exports = {"name":"@johnkit/pygeojs","version":"0.1.1","description":"ipywidget wrapper for GeoJS","author":"john Tourtellott","main":"src/index.js","repository":{"type":"git","url":"https://github.com//pygeojs.git"},"keywords":["jupyter","widgets","ipython","ipywidgets","jupyterlab-extension"],"files":["src/*.js","dist/*.js"],"scripts":{"autogen":"node ./scripts/generate-wrappers.js","clean":"rimraf dist/","prepublish":"webpack","test":"echo \"Error: no test specified\" && exit 1"},"devDependencies":{"fs-extra":"^7.0.0","handlebars":"^4.0.12","rimraf":"^2.6.1","webpack":"^3.5.5"},"dependencies":{"@jupyter-widgets/base":"^1.0.0","geojs":"^0.18.1","jupyter-dataserializers":"^1.3.0","lodash":"^4.17.4"},"jupyterlab":{"extension":"src/labplugin"}}

/***/ }),
/* 20 */
/***/ (function(module, exports) {

var g;

// This works in non-strict mode
g = (function() {
	return this;
})();

try {
	// This works if eval is allowed (see CSP)
	g = g || Function("return this")() || (1,eval)("this");
} catch(e) {
	// This works if the window reference is available
	if(typeof window === "object")
		g = window;
}

// g can still be undefined, but nothing to do about it...
// We return undefined, instead of nothing here, so it's
// easier to handle this case. if(!global) { ...}

module.exports = g;


/***/ }),
/* 21 */
/***/ (function(module, exports) {

module.exports = function(module) {
	if(!module.webpackPolyfill) {
		module.deprecate = function() {};
		module.paths = [];
		// module.parent = undefined by default
		if(!module.children) module.children = [];
		Object.defineProperty(module, "loaded", {
			enumerable: true,
			get: function() {
				return module.l;
			}
		});
		Object.defineProperty(module, "id", {
			enumerable: true,
			get: function() {
				return module.i;
			}
		});
		module.webpackPolyfill = 1;
	}
	return module;
};


/***/ }),
/* 22 */
/***/ (function(module, exports, __webpack_require__) {

var __WEBPACK_AMD_DEFINE_RESULT__;/*! Hammer.JS - v2.0.7 - 2016-04-22
 * http://hammerjs.github.io/
 *
 * Copyright (c) 2016 Jorik Tangelder;
 * Licensed under the MIT license */
(function(window, document, exportName, undefined) {
  'use strict';

var VENDOR_PREFIXES = ['', 'webkit', 'Moz', 'MS', 'ms', 'o'];
var TEST_ELEMENT = document.createElement('div');

var TYPE_FUNCTION = 'function';

var round = Math.round;
var abs = Math.abs;
var now = Date.now;

/**
 * set a timeout with a given scope
 * @param {Function} fn
 * @param {Number} timeout
 * @param {Object} context
 * @returns {number}
 */
function setTimeoutContext(fn, timeout, context) {
    return setTimeout(bindFn(fn, context), timeout);
}

/**
 * if the argument is an array, we want to execute the fn on each entry
 * if it aint an array we don't want to do a thing.
 * this is used by all the methods that accept a single and array argument.
 * @param {*|Array} arg
 * @param {String} fn
 * @param {Object} [context]
 * @returns {Boolean}
 */
function invokeArrayArg(arg, fn, context) {
    if (Array.isArray(arg)) {
        each(arg, context[fn], context);
        return true;
    }
    return false;
}

/**
 * walk objects and arrays
 * @param {Object} obj
 * @param {Function} iterator
 * @param {Object} context
 */
function each(obj, iterator, context) {
    var i;

    if (!obj) {
        return;
    }

    if (obj.forEach) {
        obj.forEach(iterator, context);
    } else if (obj.length !== undefined) {
        i = 0;
        while (i < obj.length) {
            iterator.call(context, obj[i], i, obj);
            i++;
        }
    } else {
        for (i in obj) {
            obj.hasOwnProperty(i) && iterator.call(context, obj[i], i, obj);
        }
    }
}

/**
 * wrap a method with a deprecation warning and stack trace
 * @param {Function} method
 * @param {String} name
 * @param {String} message
 * @returns {Function} A new function wrapping the supplied method.
 */
function deprecate(method, name, message) {
    var deprecationMessage = 'DEPRECATED METHOD: ' + name + '\n' + message + ' AT \n';
    return function() {
        var e = new Error('get-stack-trace');
        var stack = e && e.stack ? e.stack.replace(/^[^\(]+?[\n$]/gm, '')
            .replace(/^\s+at\s+/gm, '')
            .replace(/^Object.<anonymous>\s*\(/gm, '{anonymous}()@') : 'Unknown Stack Trace';

        var log = window.console && (window.console.warn || window.console.log);
        if (log) {
            log.call(window.console, deprecationMessage, stack);
        }
        return method.apply(this, arguments);
    };
}

/**
 * extend object.
 * means that properties in dest will be overwritten by the ones in src.
 * @param {Object} target
 * @param {...Object} objects_to_assign
 * @returns {Object} target
 */
var assign;
if (typeof Object.assign !== 'function') {
    assign = function assign(target) {
        if (target === undefined || target === null) {
            throw new TypeError('Cannot convert undefined or null to object');
        }

        var output = Object(target);
        for (var index = 1; index < arguments.length; index++) {
            var source = arguments[index];
            if (source !== undefined && source !== null) {
                for (var nextKey in source) {
                    if (source.hasOwnProperty(nextKey)) {
                        output[nextKey] = source[nextKey];
                    }
                }
            }
        }
        return output;
    };
} else {
    assign = Object.assign;
}

/**
 * extend object.
 * means that properties in dest will be overwritten by the ones in src.
 * @param {Object} dest
 * @param {Object} src
 * @param {Boolean} [merge=false]
 * @returns {Object} dest
 */
var extend = deprecate(function extend(dest, src, merge) {
    var keys = Object.keys(src);
    var i = 0;
    while (i < keys.length) {
        if (!merge || (merge && dest[keys[i]] === undefined)) {
            dest[keys[i]] = src[keys[i]];
        }
        i++;
    }
    return dest;
}, 'extend', 'Use `assign`.');

/**
 * merge the values from src in the dest.
 * means that properties that exist in dest will not be overwritten by src
 * @param {Object} dest
 * @param {Object} src
 * @returns {Object} dest
 */
var merge = deprecate(function merge(dest, src) {
    return extend(dest, src, true);
}, 'merge', 'Use `assign`.');

/**
 * simple class inheritance
 * @param {Function} child
 * @param {Function} base
 * @param {Object} [properties]
 */
function inherit(child, base, properties) {
    var baseP = base.prototype,
        childP;

    childP = child.prototype = Object.create(baseP);
    childP.constructor = child;
    childP._super = baseP;

    if (properties) {
        assign(childP, properties);
    }
}

/**
 * simple function bind
 * @param {Function} fn
 * @param {Object} context
 * @returns {Function}
 */
function bindFn(fn, context) {
    return function boundFn() {
        return fn.apply(context, arguments);
    };
}

/**
 * let a boolean value also be a function that must return a boolean
 * this first item in args will be used as the context
 * @param {Boolean|Function} val
 * @param {Array} [args]
 * @returns {Boolean}
 */
function boolOrFn(val, args) {
    if (typeof val == TYPE_FUNCTION) {
        return val.apply(args ? args[0] || undefined : undefined, args);
    }
    return val;
}

/**
 * use the val2 when val1 is undefined
 * @param {*} val1
 * @param {*} val2
 * @returns {*}
 */
function ifUndefined(val1, val2) {
    return (val1 === undefined) ? val2 : val1;
}

/**
 * addEventListener with multiple events at once
 * @param {EventTarget} target
 * @param {String} types
 * @param {Function} handler
 */
function addEventListeners(target, types, handler) {
    each(splitStr(types), function(type) {
        target.addEventListener(type, handler, false);
    });
}

/**
 * removeEventListener with multiple events at once
 * @param {EventTarget} target
 * @param {String} types
 * @param {Function} handler
 */
function removeEventListeners(target, types, handler) {
    each(splitStr(types), function(type) {
        target.removeEventListener(type, handler, false);
    });
}

/**
 * find if a node is in the given parent
 * @method hasParent
 * @param {HTMLElement} node
 * @param {HTMLElement} parent
 * @return {Boolean} found
 */
function hasParent(node, parent) {
    while (node) {
        if (node == parent) {
            return true;
        }
        node = node.parentNode;
    }
    return false;
}

/**
 * small indexOf wrapper
 * @param {String} str
 * @param {String} find
 * @returns {Boolean} found
 */
function inStr(str, find) {
    return str.indexOf(find) > -1;
}

/**
 * split string on whitespace
 * @param {String} str
 * @returns {Array} words
 */
function splitStr(str) {
    return str.trim().split(/\s+/g);
}

/**
 * find if a array contains the object using indexOf or a simple polyFill
 * @param {Array} src
 * @param {String} find
 * @param {String} [findByKey]
 * @return {Boolean|Number} false when not found, or the index
 */
function inArray(src, find, findByKey) {
    if (src.indexOf && !findByKey) {
        return src.indexOf(find);
    } else {
        var i = 0;
        while (i < src.length) {
            if ((findByKey && src[i][findByKey] == find) || (!findByKey && src[i] === find)) {
                return i;
            }
            i++;
        }
        return -1;
    }
}

/**
 * convert array-like objects to real arrays
 * @param {Object} obj
 * @returns {Array}
 */
function toArray(obj) {
    return Array.prototype.slice.call(obj, 0);
}

/**
 * unique array with objects based on a key (like 'id') or just by the array's value
 * @param {Array} src [{id:1},{id:2},{id:1}]
 * @param {String} [key]
 * @param {Boolean} [sort=False]
 * @returns {Array} [{id:1},{id:2}]
 */
function uniqueArray(src, key, sort) {
    var results = [];
    var values = [];
    var i = 0;

    while (i < src.length) {
        var val = key ? src[i][key] : src[i];
        if (inArray(values, val) < 0) {
            results.push(src[i]);
        }
        values[i] = val;
        i++;
    }

    if (sort) {
        if (!key) {
            results = results.sort();
        } else {
            results = results.sort(function sortUniqueArray(a, b) {
                return a[key] > b[key];
            });
        }
    }

    return results;
}

/**
 * get the prefixed property
 * @param {Object} obj
 * @param {String} property
 * @returns {String|Undefined} prefixed
 */
function prefixed(obj, property) {
    var prefix, prop;
    var camelProp = property[0].toUpperCase() + property.slice(1);

    var i = 0;
    while (i < VENDOR_PREFIXES.length) {
        prefix = VENDOR_PREFIXES[i];
        prop = (prefix) ? prefix + camelProp : property;

        if (prop in obj) {
            return prop;
        }
        i++;
    }
    return undefined;
}

/**
 * get a unique id
 * @returns {number} uniqueId
 */
var _uniqueId = 1;
function uniqueId() {
    return _uniqueId++;
}

/**
 * get the window object of an element
 * @param {HTMLElement} element
 * @returns {DocumentView|Window}
 */
function getWindowForElement(element) {
    var doc = element.ownerDocument || element;
    return (doc.defaultView || doc.parentWindow || window);
}

var MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i;

var SUPPORT_TOUCH = ('ontouchstart' in window);
var SUPPORT_POINTER_EVENTS = prefixed(window, 'PointerEvent') !== undefined;
var SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent);

var INPUT_TYPE_TOUCH = 'touch';
var INPUT_TYPE_PEN = 'pen';
var INPUT_TYPE_MOUSE = 'mouse';
var INPUT_TYPE_KINECT = 'kinect';

var COMPUTE_INTERVAL = 25;

var INPUT_START = 1;
var INPUT_MOVE = 2;
var INPUT_END = 4;
var INPUT_CANCEL = 8;

var DIRECTION_NONE = 1;
var DIRECTION_LEFT = 2;
var DIRECTION_RIGHT = 4;
var DIRECTION_UP = 8;
var DIRECTION_DOWN = 16;

var DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT;
var DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN;
var DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL;

var PROPS_XY = ['x', 'y'];
var PROPS_CLIENT_XY = ['clientX', 'clientY'];

/**
 * create new input type manager
 * @param {Manager} manager
 * @param {Function} callback
 * @returns {Input}
 * @constructor
 */
function Input(manager, callback) {
    var self = this;
    this.manager = manager;
    this.callback = callback;
    this.element = manager.element;
    this.target = manager.options.inputTarget;

    // smaller wrapper around the handler, for the scope and the enabled state of the manager,
    // so when disabled the input events are completely bypassed.
    this.domHandler = function(ev) {
        if (boolOrFn(manager.options.enable, [manager])) {
            self.handler(ev);
        }
    };

    this.init();

}

Input.prototype = {
    /**
     * should handle the inputEvent data and trigger the callback
     * @virtual
     */
    handler: function() { },

    /**
     * bind the events
     */
    init: function() {
        this.evEl && addEventListeners(this.element, this.evEl, this.domHandler);
        this.evTarget && addEventListeners(this.target, this.evTarget, this.domHandler);
        this.evWin && addEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);
    },

    /**
     * unbind the events
     */
    destroy: function() {
        this.evEl && removeEventListeners(this.element, this.evEl, this.domHandler);
        this.evTarget && removeEventListeners(this.target, this.evTarget, this.domHandler);
        this.evWin && removeEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);
    }
};

/**
 * create new input type manager
 * called by the Manager constructor
 * @param {Hammer} manager
 * @returns {Input}
 */
function createInputInstance(manager) {
    var Type;
    var inputClass = manager.options.inputClass;

    if (inputClass) {
        Type = inputClass;
    } else if (SUPPORT_POINTER_EVENTS) {
        Type = PointerEventInput;
    } else if (SUPPORT_ONLY_TOUCH) {
        Type = TouchInput;
    } else if (!SUPPORT_TOUCH) {
        Type = MouseInput;
    } else {
        Type = TouchMouseInput;
    }
    return new (Type)(manager, inputHandler);
}

/**
 * handle input events
 * @param {Manager} manager
 * @param {String} eventType
 * @param {Object} input
 */
function inputHandler(manager, eventType, input) {
    var pointersLen = input.pointers.length;
    var changedPointersLen = input.changedPointers.length;
    var isFirst = (eventType & INPUT_START && (pointersLen - changedPointersLen === 0));
    var isFinal = (eventType & (INPUT_END | INPUT_CANCEL) && (pointersLen - changedPointersLen === 0));

    input.isFirst = !!isFirst;
    input.isFinal = !!isFinal;

    if (isFirst) {
        manager.session = {};
    }

    // source event is the normalized value of the domEvents
    // like 'touchstart, mouseup, pointerdown'
    input.eventType = eventType;

    // compute scale, rotation etc
    computeInputData(manager, input);

    // emit secret event
    manager.emit('hammer.input', input);

    manager.recognize(input);
    manager.session.prevInput = input;
}

/**
 * extend the data with some usable properties like scale, rotate, velocity etc
 * @param {Object} manager
 * @param {Object} input
 */
function computeInputData(manager, input) {
    var session = manager.session;
    var pointers = input.pointers;
    var pointersLength = pointers.length;

    // store the first input to calculate the distance and direction
    if (!session.firstInput) {
        session.firstInput = simpleCloneInputData(input);
    }

    // to compute scale and rotation we need to store the multiple touches
    if (pointersLength > 1 && !session.firstMultiple) {
        session.firstMultiple = simpleCloneInputData(input);
    } else if (pointersLength === 1) {
        session.firstMultiple = false;
    }

    var firstInput = session.firstInput;
    var firstMultiple = session.firstMultiple;
    var offsetCenter = firstMultiple ? firstMultiple.center : firstInput.center;

    var center = input.center = getCenter(pointers);
    input.timeStamp = now();
    input.deltaTime = input.timeStamp - firstInput.timeStamp;

    input.angle = getAngle(offsetCenter, center);
    input.distance = getDistance(offsetCenter, center);

    computeDeltaXY(session, input);
    input.offsetDirection = getDirection(input.deltaX, input.deltaY);

    var overallVelocity = getVelocity(input.deltaTime, input.deltaX, input.deltaY);
    input.overallVelocityX = overallVelocity.x;
    input.overallVelocityY = overallVelocity.y;
    input.overallVelocity = (abs(overallVelocity.x) > abs(overallVelocity.y)) ? overallVelocity.x : overallVelocity.y;

    input.scale = firstMultiple ? getScale(firstMultiple.pointers, pointers) : 1;
    input.rotation = firstMultiple ? getRotation(firstMultiple.pointers, pointers) : 0;

    input.maxPointers = !session.prevInput ? input.pointers.length : ((input.pointers.length >
        session.prevInput.maxPointers) ? input.pointers.length : session.prevInput.maxPointers);

    computeIntervalInputData(session, input);

    // find the correct target
    var target = manager.element;
    if (hasParent(input.srcEvent.target, target)) {
        target = input.srcEvent.target;
    }
    input.target = target;
}

function computeDeltaXY(session, input) {
    var center = input.center;
    var offset = session.offsetDelta || {};
    var prevDelta = session.prevDelta || {};
    var prevInput = session.prevInput || {};

    if (input.eventType === INPUT_START || prevInput.eventType === INPUT_END) {
        prevDelta = session.prevDelta = {
            x: prevInput.deltaX || 0,
            y: prevInput.deltaY || 0
        };

        offset = session.offsetDelta = {
            x: center.x,
            y: center.y
        };
    }

    input.deltaX = prevDelta.x + (center.x - offset.x);
    input.deltaY = prevDelta.y + (center.y - offset.y);
}

/**
 * velocity is calculated every x ms
 * @param {Object} session
 * @param {Object} input
 */
function computeIntervalInputData(session, input) {
    var last = session.lastInterval || input,
        deltaTime = input.timeStamp - last.timeStamp,
        velocity, velocityX, velocityY, direction;

    if (input.eventType != INPUT_CANCEL && (deltaTime > COMPUTE_INTERVAL || last.velocity === undefined)) {
        var deltaX = input.deltaX - last.deltaX;
        var deltaY = input.deltaY - last.deltaY;

        var v = getVelocity(deltaTime, deltaX, deltaY);
        velocityX = v.x;
        velocityY = v.y;
        velocity = (abs(v.x) > abs(v.y)) ? v.x : v.y;
        direction = getDirection(deltaX, deltaY);

        session.lastInterval = input;
    } else {
        // use latest velocity info if it doesn't overtake a minimum period
        velocity = last.velocity;
        velocityX = last.velocityX;
        velocityY = last.velocityY;
        direction = last.direction;
    }

    input.velocity = velocity;
    input.velocityX = velocityX;
    input.velocityY = velocityY;
    input.direction = direction;
}

/**
 * create a simple clone from the input used for storage of firstInput and firstMultiple
 * @param {Object} input
 * @returns {Object} clonedInputData
 */
function simpleCloneInputData(input) {
    // make a simple copy of the pointers because we will get a reference if we don't
    // we only need clientXY for the calculations
    var pointers = [];
    var i = 0;
    while (i < input.pointers.length) {
        pointers[i] = {
            clientX: round(input.pointers[i].clientX),
            clientY: round(input.pointers[i].clientY)
        };
        i++;
    }

    return {
        timeStamp: now(),
        pointers: pointers,
        center: getCenter(pointers),
        deltaX: input.deltaX,
        deltaY: input.deltaY
    };
}

/**
 * get the center of all the pointers
 * @param {Array} pointers
 * @return {Object} center contains `x` and `y` properties
 */
function getCenter(pointers) {
    var pointersLength = pointers.length;

    // no need to loop when only one touch
    if (pointersLength === 1) {
        return {
            x: round(pointers[0].clientX),
            y: round(pointers[0].clientY)
        };
    }

    var x = 0, y = 0, i = 0;
    while (i < pointersLength) {
        x += pointers[i].clientX;
        y += pointers[i].clientY;
        i++;
    }

    return {
        x: round(x / pointersLength),
        y: round(y / pointersLength)
    };
}

/**
 * calculate the velocity between two points. unit is in px per ms.
 * @param {Number} deltaTime
 * @param {Number} x
 * @param {Number} y
 * @return {Object} velocity `x` and `y`
 */
function getVelocity(deltaTime, x, y) {
    return {
        x: x / deltaTime || 0,
        y: y / deltaTime || 0
    };
}

/**
 * get the direction between two points
 * @param {Number} x
 * @param {Number} y
 * @return {Number} direction
 */
function getDirection(x, y) {
    if (x === y) {
        return DIRECTION_NONE;
    }

    if (abs(x) >= abs(y)) {
        return x < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT;
    }
    return y < 0 ? DIRECTION_UP : DIRECTION_DOWN;
}

/**
 * calculate the absolute distance between two points
 * @param {Object} p1 {x, y}
 * @param {Object} p2 {x, y}
 * @param {Array} [props] containing x and y keys
 * @return {Number} distance
 */
function getDistance(p1, p2, props) {
    if (!props) {
        props = PROPS_XY;
    }
    var x = p2[props[0]] - p1[props[0]],
        y = p2[props[1]] - p1[props[1]];

    return Math.sqrt((x * x) + (y * y));
}

/**
 * calculate the angle between two coordinates
 * @param {Object} p1
 * @param {Object} p2
 * @param {Array} [props] containing x and y keys
 * @return {Number} angle
 */
function getAngle(p1, p2, props) {
    if (!props) {
        props = PROPS_XY;
    }
    var x = p2[props[0]] - p1[props[0]],
        y = p2[props[1]] - p1[props[1]];
    return Math.atan2(y, x) * 180 / Math.PI;
}

/**
 * calculate the rotation degrees between two pointersets
 * @param {Array} start array of pointers
 * @param {Array} end array of pointers
 * @return {Number} rotation
 */
function getRotation(start, end) {
    return getAngle(end[1], end[0], PROPS_CLIENT_XY) + getAngle(start[1], start[0], PROPS_CLIENT_XY);
}

/**
 * calculate the scale factor between two pointersets
 * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out
 * @param {Array} start array of pointers
 * @param {Array} end array of pointers
 * @return {Number} scale
 */
function getScale(start, end) {
    return getDistance(end[0], end[1], PROPS_CLIENT_XY) / getDistance(start[0], start[1], PROPS_CLIENT_XY);
}

var MOUSE_INPUT_MAP = {
    mousedown: INPUT_START,
    mousemove: INPUT_MOVE,
    mouseup: INPUT_END
};

var MOUSE_ELEMENT_EVENTS = 'mousedown';
var MOUSE_WINDOW_EVENTS = 'mousemove mouseup';

/**
 * Mouse events input
 * @constructor
 * @extends Input
 */
function MouseInput() {
    this.evEl = MOUSE_ELEMENT_EVENTS;
    this.evWin = MOUSE_WINDOW_EVENTS;

    this.pressed = false; // mousedown state

    Input.apply(this, arguments);
}

inherit(MouseInput, Input, {
    /**
     * handle mouse events
     * @param {Object} ev
     */
    handler: function MEhandler(ev) {
        var eventType = MOUSE_INPUT_MAP[ev.type];

        // on start we want to have the left mouse button down
        if (eventType & INPUT_START && ev.button === 0) {
            this.pressed = true;
        }

        if (eventType & INPUT_MOVE && ev.which !== 1) {
            eventType = INPUT_END;
        }

        // mouse must be down
        if (!this.pressed) {
            return;
        }

        if (eventType & INPUT_END) {
            this.pressed = false;
        }

        this.callback(this.manager, eventType, {
            pointers: [ev],
            changedPointers: [ev],
            pointerType: INPUT_TYPE_MOUSE,
            srcEvent: ev
        });
    }
});

var POINTER_INPUT_MAP = {
    pointerdown: INPUT_START,
    pointermove: INPUT_MOVE,
    pointerup: INPUT_END,
    pointercancel: INPUT_CANCEL,
    pointerout: INPUT_CANCEL
};

// in IE10 the pointer types is defined as an enum
var IE10_POINTER_TYPE_ENUM = {
    2: INPUT_TYPE_TOUCH,
    3: INPUT_TYPE_PEN,
    4: INPUT_TYPE_MOUSE,
    5: INPUT_TYPE_KINECT // see https://twitter.com/jacobrossi/status/480596438489890816
};

var POINTER_ELEMENT_EVENTS = 'pointerdown';
var POINTER_WINDOW_EVENTS = 'pointermove pointerup pointercancel';

// IE10 has prefixed support, and case-sensitive
if (window.MSPointerEvent && !window.PointerEvent) {
    POINTER_ELEMENT_EVENTS = 'MSPointerDown';
    POINTER_WINDOW_EVENTS = 'MSPointerMove MSPointerUp MSPointerCancel';
}

/**
 * Pointer events input
 * @constructor
 * @extends Input
 */
function PointerEventInput() {
    this.evEl = POINTER_ELEMENT_EVENTS;
    this.evWin = POINTER_WINDOW_EVENTS;

    Input.apply(this, arguments);

    this.store = (this.manager.session.pointerEvents = []);
}

inherit(PointerEventInput, Input, {
    /**
     * handle mouse events
     * @param {Object} ev
     */
    handler: function PEhandler(ev) {
        var store = this.store;
        var removePointer = false;

        var eventTypeNormalized = ev.type.toLowerCase().replace('ms', '');
        var eventType = POINTER_INPUT_MAP[eventTypeNormalized];
        var pointerType = IE10_POINTER_TYPE_ENUM[ev.pointerType] || ev.pointerType;

        var isTouch = (pointerType == INPUT_TYPE_TOUCH);

        // get index of the event in the store
        var storeIndex = inArray(store, ev.pointerId, 'pointerId');

        // start and mouse must be down
        if (eventType & INPUT_START && (ev.button === 0 || isTouch)) {
            if (storeIndex < 0) {
                store.push(ev);
                storeIndex = store.length - 1;
            }
        } else if (eventType & (INPUT_END | INPUT_CANCEL)) {
            removePointer = true;
        }

        // it not found, so the pointer hasn't been down (so it's probably a hover)
        if (storeIndex < 0) {
            return;
        }

        // update the event in the store
        store[storeIndex] = ev;

        this.callback(this.manager, eventType, {
            pointers: store,
            changedPointers: [ev],
            pointerType: pointerType,
            srcEvent: ev
        });

        if (removePointer) {
            // remove from the store
            store.splice(storeIndex, 1);
        }
    }
});

var SINGLE_TOUCH_INPUT_MAP = {
    touchstart: INPUT_START,
    touchmove: INPUT_MOVE,
    touchend: INPUT_END,
    touchcancel: INPUT_CANCEL
};

var SINGLE_TOUCH_TARGET_EVENTS = 'touchstart';
var SINGLE_TOUCH_WINDOW_EVENTS = 'touchstart touchmove touchend touchcancel';

/**
 * Touch events input
 * @constructor
 * @extends Input
 */
function SingleTouchInput() {
    this.evTarget = SINGLE_TOUCH_TARGET_EVENTS;
    this.evWin = SINGLE_TOUCH_WINDOW_EVENTS;
    this.started = false;

    Input.apply(this, arguments);
}

inherit(SingleTouchInput, Input, {
    handler: function TEhandler(ev) {
        var type = SINGLE_TOUCH_INPUT_MAP[ev.type];

        // should we handle the touch events?
        if (type === INPUT_START) {
            this.started = true;
        }

        if (!this.started) {
            return;
        }

        var touches = normalizeSingleTouches.call(this, ev, type);

        // when done, reset the started state
        if (type & (INPUT_END | INPUT_CANCEL) && touches[0].length - touches[1].length === 0) {
            this.started = false;
        }

        this.callback(this.manager, type, {
            pointers: touches[0],
            changedPointers: touches[1],
            pointerType: INPUT_TYPE_TOUCH,
            srcEvent: ev
        });
    }
});

/**
 * @this {TouchInput}
 * @param {Object} ev
 * @param {Number} type flag
 * @returns {undefined|Array} [all, changed]
 */
function normalizeSingleTouches(ev, type) {
    var all = toArray(ev.touches);
    var changed = toArray(ev.changedTouches);

    if (type & (INPUT_END | INPUT_CANCEL)) {
        all = uniqueArray(all.concat(changed), 'identifier', true);
    }

    return [all, changed];
}

var TOUCH_INPUT_MAP = {
    touchstart: INPUT_START,
    touchmove: INPUT_MOVE,
    touchend: INPUT_END,
    touchcancel: INPUT_CANCEL
};

var TOUCH_TARGET_EVENTS = 'touchstart touchmove touchend touchcancel';

/**
 * Multi-user touch events input
 * @constructor
 * @extends Input
 */
function TouchInput() {
    this.evTarget = TOUCH_TARGET_EVENTS;
    this.targetIds = {};

    Input.apply(this, arguments);
}

inherit(TouchInput, Input, {
    handler: function MTEhandler(ev) {
        var type = TOUCH_INPUT_MAP[ev.type];
        var touches = getTouches.call(this, ev, type);
        if (!touches) {
            return;
        }

        this.callback(this.manager, type, {
            pointers: touches[0],
            changedPointers: touches[1],
            pointerType: INPUT_TYPE_TOUCH,
            srcEvent: ev
        });
    }
});

/**
 * @this {TouchInput}
 * @param {Object} ev
 * @param {Number} type flag
 * @returns {undefined|Array} [all, changed]
 */
function getTouches(ev, type) {
    var allTouches = toArray(ev.touches);
    var targetIds = this.targetIds;

    // when there is only one touch, the process can be simplified
    if (type & (INPUT_START | INPUT_MOVE) && allTouches.length === 1) {
        targetIds[allTouches[0].identifier] = true;
        return [allTouches, allTouches];
    }

    var i,
        targetTouches,
        changedTouches = toArray(ev.changedTouches),
        changedTargetTouches = [],
        target = this.target;

    // get target touches from touches
    targetTouches = allTouches.filter(function(touch) {
        return hasParent(touch.target, target);
    });

    // collect touches
    if (type === INPUT_START) {
        i = 0;
        while (i < targetTouches.length) {
            targetIds[targetTouches[i].identifier] = true;
            i++;
        }
    }

    // filter changed touches to only contain touches that exist in the collected target ids
    i = 0;
    while (i < changedTouches.length) {
        if (targetIds[changedTouches[i].identifier]) {
            changedTargetTouches.push(changedTouches[i]);
        }

        // cleanup removed touches
        if (type & (INPUT_END | INPUT_CANCEL)) {
            delete targetIds[changedTouches[i].identifier];
        }
        i++;
    }

    if (!changedTargetTouches.length) {
        return;
    }

    return [
        // merge targetTouches with changedTargetTouches so it contains ALL touches, including 'end' and 'cancel'
        uniqueArray(targetTouches.concat(changedTargetTouches), 'identifier', true),
        changedTargetTouches
    ];
}

/**
 * Combined touch and mouse input
 *
 * Touch has a higher priority then mouse, and while touching no mouse events are allowed.
 * This because touch devices also emit mouse events while doing a touch.
 *
 * @constructor
 * @extends Input
 */

var DEDUP_TIMEOUT = 2500;
var DEDUP_DISTANCE = 25;

function TouchMouseInput() {
    Input.apply(this, arguments);

    var handler = bindFn(this.handler, this);
    this.touch = new TouchInput(this.manager, handler);
    this.mouse = new MouseInput(this.manager, handler);

    this.primaryTouch = null;
    this.lastTouches = [];
}

inherit(TouchMouseInput, Input, {
    /**
     * handle mouse and touch events
     * @param {Hammer} manager
     * @param {String} inputEvent
     * @param {Object} inputData
     */
    handler: function TMEhandler(manager, inputEvent, inputData) {
        var isTouch = (inputData.pointerType == INPUT_TYPE_TOUCH),
            isMouse = (inputData.pointerType == INPUT_TYPE_MOUSE);

        if (isMouse && inputData.sourceCapabilities && inputData.sourceCapabilities.firesTouchEvents) {
            return;
        }

        // when we're in a touch event, record touches to  de-dupe synthetic mouse event
        if (isTouch) {
            recordTouches.call(this, inputEvent, inputData);
        } else if (isMouse && isSyntheticEvent.call(this, inputData)) {
            return;
        }

        this.callback(manager, inputEvent, inputData);
    },

    /**
     * remove the event listeners
     */
    destroy: function destroy() {
        this.touch.destroy();
        this.mouse.destroy();
    }
});

function recordTouches(eventType, eventData) {
    if (eventType & INPUT_START) {
        this.primaryTouch = eventData.changedPointers[0].identifier;
        setLastTouch.call(this, eventData);
    } else if (eventType & (INPUT_END | INPUT_CANCEL)) {
        setLastTouch.call(this, eventData);
    }
}

function setLastTouch(eventData) {
    var touch = eventData.changedPointers[0];

    if (touch.identifier === this.primaryTouch) {
        var lastTouch = {x: touch.clientX, y: touch.clientY};
        this.lastTouches.push(lastTouch);
        var lts = this.lastTouches;
        var removeLastTouch = function() {
            var i = lts.indexOf(lastTouch);
            if (i > -1) {
                lts.splice(i, 1);
            }
        };
        setTimeout(removeLastTouch, DEDUP_TIMEOUT);
    }
}

function isSyntheticEvent(eventData) {
    var x = eventData.srcEvent.clientX, y = eventData.srcEvent.clientY;
    for (var i = 0; i < this.lastTouches.length; i++) {
        var t = this.lastTouches[i];
        var dx = Math.abs(x - t.x), dy = Math.abs(y - t.y);
        if (dx <= DEDUP_DISTANCE && dy <= DEDUP_DISTANCE) {
            return true;
        }
    }
    return false;
}

var PREFIXED_TOUCH_ACTION = prefixed(TEST_ELEMENT.style, 'touchAction');
var NATIVE_TOUCH_ACTION = PREFIXED_TOUCH_ACTION !== undefined;

// magical touchAction value
var TOUCH_ACTION_COMPUTE = 'compute';
var TOUCH_ACTION_AUTO = 'auto';
var TOUCH_ACTION_MANIPULATION = 'manipulation'; // not implemented
var TOUCH_ACTION_NONE = 'none';
var TOUCH_ACTION_PAN_X = 'pan-x';
var TOUCH_ACTION_PAN_Y = 'pan-y';
var TOUCH_ACTION_MAP = getTouchActionProps();

/**
 * Touch Action
 * sets the touchAction property or uses the js alternative
 * @param {Manager} manager
 * @param {String} value
 * @constructor
 */
function TouchAction(manager, value) {
    this.manager = manager;
    this.set(value);
}

TouchAction.prototype = {
    /**
     * set the touchAction value on the element or enable the polyfill
     * @param {String} value
     */
    set: function(value) {
        // find out the touch-action by the event handlers
        if (value == TOUCH_ACTION_COMPUTE) {
            value = this.compute();
        }

        if (NATIVE_TOUCH_ACTION && this.manager.element.style && TOUCH_ACTION_MAP[value]) {
            this.manager.element.style[PREFIXED_TOUCH_ACTION] = value;
        }
        this.actions = value.toLowerCase().trim();
    },

    /**
     * just re-set the touchAction value
     */
    update: function() {
        this.set(this.manager.options.touchAction);
    },

    /**
     * compute the value for the touchAction property based on the recognizer's settings
     * @returns {String} value
     */
    compute: function() {
        var actions = [];
        each(this.manager.recognizers, function(recognizer) {
            if (boolOrFn(recognizer.options.enable, [recognizer])) {
                actions = actions.concat(recognizer.getTouchAction());
            }
        });
        return cleanTouchActions(actions.join(' '));
    },

    /**
     * this method is called on each input cycle and provides the preventing of the browser behavior
     * @param {Object} input
     */
    preventDefaults: function(input) {
        var srcEvent = input.srcEvent;
        var direction = input.offsetDirection;

        // if the touch action did prevented once this session
        if (this.manager.session.prevented) {
            srcEvent.preventDefault();
            return;
        }

        var actions = this.actions;
        var hasNone = inStr(actions, TOUCH_ACTION_NONE) && !TOUCH_ACTION_MAP[TOUCH_ACTION_NONE];
        var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_Y];
        var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_X];

        if (hasNone) {
            //do not prevent defaults if this is a tap gesture

            var isTapPointer = input.pointers.length === 1;
            var isTapMovement = input.distance < 2;
            var isTapTouchTime = input.deltaTime < 250;

            if (isTapPointer && isTapMovement && isTapTouchTime) {
                return;
            }
        }

        if (hasPanX && hasPanY) {
            // `pan-x pan-y` means browser handles all scrolling/panning, do not prevent
            return;
        }

        if (hasNone ||
            (hasPanY && direction & DIRECTION_HORIZONTAL) ||
            (hasPanX && direction & DIRECTION_VERTICAL)) {
            return this.preventSrc(srcEvent);
        }
    },

    /**
     * call preventDefault to prevent the browser's default behavior (scrolling in most cases)
     * @param {Object} srcEvent
     */
    preventSrc: function(srcEvent) {
        this.manager.session.prevented = true;
        srcEvent.preventDefault();
    }
};

/**
 * when the touchActions are collected they are not a valid value, so we need to clean things up. *
 * @param {String} actions
 * @returns {*}
 */
function cleanTouchActions(actions) {
    // none
    if (inStr(actions, TOUCH_ACTION_NONE)) {
        return TOUCH_ACTION_NONE;
    }

    var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X);
    var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y);

    // if both pan-x and pan-y are set (different recognizers
    // for different directions, e.g. horizontal pan but vertical swipe?)
    // we need none (as otherwise with pan-x pan-y combined none of these
    // recognizers will work, since the browser would handle all panning
    if (hasPanX && hasPanY) {
        return TOUCH_ACTION_NONE;
    }

    // pan-x OR pan-y
    if (hasPanX || hasPanY) {
        return hasPanX ? TOUCH_ACTION_PAN_X : TOUCH_ACTION_PAN_Y;
    }

    // manipulation
    if (inStr(actions, TOUCH_ACTION_MANIPULATION)) {
        return TOUCH_ACTION_MANIPULATION;
    }

    return TOUCH_ACTION_AUTO;
}

function getTouchActionProps() {
    if (!NATIVE_TOUCH_ACTION) {
        return false;
    }
    var touchMap = {};
    var cssSupports = window.CSS && window.CSS.supports;
    ['auto', 'manipulation', 'pan-y', 'pan-x', 'pan-x pan-y', 'none'].forEach(function(val) {

        // If css.supports is not supported but there is native touch-action assume it supports
        // all values. This is the case for IE 10 and 11.
        touchMap[val] = cssSupports ? window.CSS.supports('touch-action', val) : true;
    });
    return touchMap;
}

/**
 * Recognizer flow explained; *
 * All recognizers have the initial state of POSSIBLE when a input session starts.
 * The definition of a input session is from the first input until the last input, with all it's movement in it. *
 * Example session for mouse-input: mousedown -> mousemove -> mouseup
 *
 * On each recognizing cycle (see Manager.recognize) the .recognize() method is executed
 * which determines with state it should be.
 *
 * If the recognizer has the state FAILED, CANCELLED or RECOGNIZED (equals ENDED), it is reset to
 * POSSIBLE to give it another change on the next cycle.
 *
 *               Possible
 *                  |
 *            +-----+---------------+
 *            |                     |
 *      +-----+-----+               |
 *      |           |               |
 *   Failed      Cancelled          |
 *                          +-------+------+
 *                          |              |
 *                      Recognized       Began
 *                                         |
 *                                      Changed
 *                                         |
 *                                  Ended/Recognized
 */
var STATE_POSSIBLE = 1;
var STATE_BEGAN = 2;
var STATE_CHANGED = 4;
var STATE_ENDED = 8;
var STATE_RECOGNIZED = STATE_ENDED;
var STATE_CANCELLED = 16;
var STATE_FAILED = 32;

/**
 * Recognizer
 * Every recognizer needs to extend from this class.
 * @constructor
 * @param {Object} options
 */
function Recognizer(options) {
    this.options = assign({}, this.defaults, options || {});

    this.id = uniqueId();

    this.manager = null;

    // default is enable true
    this.options.enable = ifUndefined(this.options.enable, true);

    this.state = STATE_POSSIBLE;

    this.simultaneous = {};
    this.requireFail = [];
}

Recognizer.prototype = {
    /**
     * @virtual
     * @type {Object}
     */
    defaults: {},

    /**
     * set options
     * @param {Object} options
     * @return {Recognizer}
     */
    set: function(options) {
        assign(this.options, options);

        // also update the touchAction, in case something changed about the directions/enabled state
        this.manager && this.manager.touchAction.update();
        return this;
    },

    /**
     * recognize simultaneous with an other recognizer.
     * @param {Recognizer} otherRecognizer
     * @returns {Recognizer} this
     */
    recognizeWith: function(otherRecognizer) {
        if (invokeArrayArg(otherRecognizer, 'recognizeWith', this)) {
            return this;
        }

        var simultaneous = this.simultaneous;
        otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
        if (!simultaneous[otherRecognizer.id]) {
            simultaneous[otherRecognizer.id] = otherRecognizer;
            otherRecognizer.recognizeWith(this);
        }
        return this;
    },

    /**
     * drop the simultaneous link. it doesnt remove the link on the other recognizer.
     * @param {Recognizer} otherRecognizer
     * @returns {Recognizer} this
     */
    dropRecognizeWith: function(otherRecognizer) {
        if (invokeArrayArg(otherRecognizer, 'dropRecognizeWith', this)) {
            return this;
        }

        otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
        delete this.simultaneous[otherRecognizer.id];
        return this;
    },

    /**
     * recognizer can only run when an other is failing
     * @param {Recognizer} otherRecognizer
     * @returns {Recognizer} this
     */
    requireFailure: function(otherRecognizer) {
        if (invokeArrayArg(otherRecognizer, 'requireFailure', this)) {
            return this;
        }

        var requireFail = this.requireFail;
        otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
        if (inArray(requireFail, otherRecognizer) === -1) {
            requireFail.push(otherRecognizer);
            otherRecognizer.requireFailure(this);
        }
        return this;
    },

    /**
     * drop the requireFailure link. it does not remove the link on the other recognizer.
     * @param {Recognizer} otherRecognizer
     * @returns {Recognizer} this
     */
    dropRequireFailure: function(otherRecognizer) {
        if (invokeArrayArg(otherRecognizer, 'dropRequireFailure', this)) {
            return this;
        }

        otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this);
        var index = inArray(this.requireFail, otherRecognizer);
        if (index > -1) {
            this.requireFail.splice(index, 1);
        }
        return this;
    },

    /**
     * has require failures boolean
     * @returns {boolean}
     */
    hasRequireFailures: function() {
        return this.requireFail.length > 0;
    },

    /**
     * if the recognizer can recognize simultaneous with an other recognizer
     * @param {Recognizer} otherRecognizer
     * @returns {Boolean}
     */
    canRecognizeWith: function(otherRecognizer) {
        return !!this.simultaneous[otherRecognizer.id];
    },

    /**
     * You should use `tryEmit` instead of `emit` directly to check
     * that all the needed recognizers has failed before emitting.
     * @param {Object} input
     */
    emit: function(input) {
        var self = this;
        var state = this.state;

        function emit(event) {
            self.manager.emit(event, input);
        }

        // 'panstart' and 'panmove'
        if (state < STATE_ENDED) {
            emit(self.options.event + stateStr(state));
        }

        emit(self.options.event); // simple 'eventName' events

        if (input.additionalEvent) { // additional event(panleft, panright, pinchin, pinchout...)
            emit(input.additionalEvent);
        }

        // panend and pancancel
        if (state >= STATE_ENDED) {
            emit(self.options.event + stateStr(state));
        }
    },

    /**
     * Check that all the require failure recognizers has failed,
     * if true, it emits a gesture event,
     * otherwise, setup the state to FAILED.
     * @param {Object} input
     */
    tryEmit: function(input) {
        if (this.canEmit()) {
            return this.emit(input);
        }
        // it's failing anyway
        this.state = STATE_FAILED;
    },

    /**
     * can we emit?
     * @returns {boolean}
     */
    canEmit: function() {
        var i = 0;
        while (i < this.requireFail.length) {
            if (!(this.requireFail[i].state & (STATE_FAILED | STATE_POSSIBLE))) {
                return false;
            }
            i++;
        }
        return true;
    },

    /**
     * update the recognizer
     * @param {Object} inputData
     */
    recognize: function(inputData) {
        // make a new copy of the inputData
        // so we can change the inputData without messing up the other recognizers
        var inputDataClone = assign({}, inputData);

        // is is enabled and allow recognizing?
        if (!boolOrFn(this.options.enable, [this, inputDataClone])) {
            this.reset();
            this.state = STATE_FAILED;
            return;
        }

        // reset when we've reached the end
        if (this.state & (STATE_RECOGNIZED | STATE_CANCELLED | STATE_FAILED)) {
            this.state = STATE_POSSIBLE;
        }

        this.state = this.process(inputDataClone);

        // the recognizer has recognized a gesture
        // so trigger an event
        if (this.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED | STATE_CANCELLED)) {
            this.tryEmit(inputDataClone);
        }
    },

    /**
     * return the state of the recognizer
     * the actual recognizing happens in this method
     * @virtual
     * @param {Object} inputData
     * @returns {Const} STATE
     */
    process: function(inputData) { }, // jshint ignore:line

    /**
     * return the preferred touch-action
     * @virtual
     * @returns {Array}
     */
    getTouchAction: function() { },

    /**
     * called when the gesture isn't allowed to recognize
     * like when another is being recognized or it is disabled
     * @virtual
     */
    reset: function() { }
};

/**
 * get a usable string, used as event postfix
 * @param {Const} state
 * @returns {String} state
 */
function stateStr(state) {
    if (state & STATE_CANCELLED) {
        return 'cancel';
    } else if (state & STATE_ENDED) {
        return 'end';
    } else if (state & STATE_CHANGED) {
        return 'move';
    } else if (state & STATE_BEGAN) {
        return 'start';
    }
    return '';
}

/**
 * direction cons to string
 * @param {Const} direction
 * @returns {String}
 */
function directionStr(direction) {
    if (direction == DIRECTION_DOWN) {
        return 'down';
    } else if (direction == DIRECTION_UP) {
        return 'up';
    } else if (direction == DIRECTION_LEFT) {
        return 'left';
    } else if (direction == DIRECTION_RIGHT) {
        return 'right';
    }
    return '';
}

/**
 * get a recognizer by name if it is bound to a manager
 * @param {Recognizer|String} otherRecognizer
 * @param {Recognizer} recognizer
 * @returns {Recognizer}
 */
function getRecognizerByNameIfManager(otherRecognizer, recognizer) {
    var manager = recognizer.manager;
    if (manager) {
        return manager.get(otherRecognizer);
    }
    return otherRecognizer;
}

/**
 * This recognizer is just used as a base for the simple attribute recognizers.
 * @constructor
 * @extends Recognizer
 */
function AttrRecognizer() {
    Recognizer.apply(this, arguments);
}

inherit(AttrRecognizer, Recognizer, {
    /**
     * @namespace
     * @memberof AttrRecognizer
     */
    defaults: {
        /**
         * @type {Number}
         * @default 1
         */
        pointers: 1
    },

    /**
     * Used to check if it the recognizer receives valid input, like input.distance > 10.
     * @memberof AttrRecognizer
     * @param {Object} input
     * @returns {Boolean} recognized
     */
    attrTest: function(input) {
        var optionPointers = this.options.pointers;
        return optionPointers === 0 || input.pointers.length === optionPointers;
    },

    /**
     * Process the input and return the state for the recognizer
     * @memberof AttrRecognizer
     * @param {Object} input
     * @returns {*} State
     */
    process: function(input) {
        var state = this.state;
        var eventType = input.eventType;

        var isRecognized = state & (STATE_BEGAN | STATE_CHANGED);
        var isValid = this.attrTest(input);

        // on cancel input and we've recognized before, return STATE_CANCELLED
        if (isRecognized && (eventType & INPUT_CANCEL || !isValid)) {
            return state | STATE_CANCELLED;
        } else if (isRecognized || isValid) {
            if (eventType & INPUT_END) {
                return state | STATE_ENDED;
            } else if (!(state & STATE_BEGAN)) {
                return STATE_BEGAN;
            }
            return state | STATE_CHANGED;
        }
        return STATE_FAILED;
    }
});

/**
 * Pan
 * Recognized when the pointer is down and moved in the allowed direction.
 * @constructor
 * @extends AttrRecognizer
 */
function PanRecognizer() {
    AttrRecognizer.apply(this, arguments);

    this.pX = null;
    this.pY = null;
}

inherit(PanRecognizer, AttrRecognizer, {
    /**
     * @namespace
     * @memberof PanRecognizer
     */
    defaults: {
        event: 'pan',
        threshold: 10,
        pointers: 1,
        direction: DIRECTION_ALL
    },

    getTouchAction: function() {
        var direction = this.options.direction;
        var actions = [];
        if (direction & DIRECTION_HORIZONTAL) {
            actions.push(TOUCH_ACTION_PAN_Y);
        }
        if (direction & DIRECTION_VERTICAL) {
            actions.push(TOUCH_ACTION_PAN_X);
        }
        return actions;
    },

    directionTest: function(input) {
        var options = this.options;
        var hasMoved = true;
        var distance = input.distance;
        var direction = input.direction;
        var x = input.deltaX;
        var y = input.deltaY;

        // lock to axis?
        if (!(direction & options.direction)) {
            if (options.direction & DIRECTION_HORIZONTAL) {
                direction = (x === 0) ? DIRECTION_NONE : (x < 0) ? DIRECTION_LEFT : DIRECTION_RIGHT;
                hasMoved = x != this.pX;
                distance = Math.abs(input.deltaX);
            } else {
                direction = (y === 0) ? DIRECTION_NONE : (y < 0) ? DIRECTION_UP : DIRECTION_DOWN;
                hasMoved = y != this.pY;
                distance = Math.abs(input.deltaY);
            }
        }
        input.direction = direction;
        return hasMoved && distance > options.threshold && direction & options.direction;
    },

    attrTest: function(input) {
        return AttrRecognizer.prototype.attrTest.call(this, input) &&
            (this.state & STATE_BEGAN || (!(this.state & STATE_BEGAN) && this.directionTest(input)));
    },

    emit: function(input) {

        this.pX = input.deltaX;
        this.pY = input.deltaY;

        var direction = directionStr(input.direction);

        if (direction) {
            input.additionalEvent = this.options.event + direction;
        }
        this._super.emit.call(this, input);
    }
});

/**
 * Pinch
 * Recognized when two or more pointers are moving toward (zoom-in) or away from each other (zoom-out).
 * @constructor
 * @extends AttrRecognizer
 */
function PinchRecognizer() {
    AttrRecognizer.apply(this, arguments);
}

inherit(PinchRecognizer, AttrRecognizer, {
    /**
     * @namespace
     * @memberof PinchRecognizer
     */
    defaults: {
        event: 'pinch',
        threshold: 0,
        pointers: 2
    },

    getTouchAction: function() {
        return [TOUCH_ACTION_NONE];
    },

    attrTest: function(input) {
        return this._super.attrTest.call(this, input) &&
            (Math.abs(input.scale - 1) > this.options.threshold || this.state & STATE_BEGAN);
    },

    emit: function(input) {
        if (input.scale !== 1) {
            var inOut = input.scale < 1 ? 'in' : 'out';
            input.additionalEvent = this.options.event + inOut;
        }
        this._super.emit.call(this, input);
    }
});

/**
 * Press
 * Recognized when the pointer is down for x ms without any movement.
 * @constructor
 * @extends Recognizer
 */
function PressRecognizer() {
    Recognizer.apply(this, arguments);

    this._timer = null;
    this._input = null;
}

inherit(PressRecognizer, Recognizer, {
    /**
     * @namespace
     * @memberof PressRecognizer
     */
    defaults: {
        event: 'press',
        pointers: 1,
        time: 251, // minimal time of the pointer to be pressed
        threshold: 9 // a minimal movement is ok, but keep it low
    },

    getTouchAction: function() {
        return [TOUCH_ACTION_AUTO];
    },

    process: function(input) {
        var options = this.options;
        var validPointers = input.pointers.length === options.pointers;
        var validMovement = input.distance < options.threshold;
        var validTime = input.deltaTime > options.time;

        this._input = input;

        // we only allow little movement
        // and we've reached an end event, so a tap is possible
        if (!validMovement || !validPointers || (input.eventType & (INPUT_END | INPUT_CANCEL) && !validTime)) {
            this.reset();
        } else if (input.eventType & INPUT_START) {
            this.reset();
            this._timer = setTimeoutContext(function() {
                this.state = STATE_RECOGNIZED;
                this.tryEmit();
            }, options.time, this);
        } else if (input.eventType & INPUT_END) {
            return STATE_RECOGNIZED;
        }
        return STATE_FAILED;
    },

    reset: function() {
        clearTimeout(this._timer);
    },

    emit: function(input) {
        if (this.state !== STATE_RECOGNIZED) {
            return;
        }

        if (input && (input.eventType & INPUT_END)) {
            this.manager.emit(this.options.event + 'up', input);
        } else {
            this._input.timeStamp = now();
            this.manager.emit(this.options.event, this._input);
        }
    }
});

/**
 * Rotate
 * Recognized when two or more pointer are moving in a circular motion.
 * @constructor
 * @extends AttrRecognizer
 */
function RotateRecognizer() {
    AttrRecognizer.apply(this, arguments);
}

inherit(RotateRecognizer, AttrRecognizer, {
    /**
     * @namespace
     * @memberof RotateRecognizer
     */
    defaults: {
        event: 'rotate',
        threshold: 0,
        pointers: 2
    },

    getTouchAction: function() {
        return [TOUCH_ACTION_NONE];
    },

    attrTest: function(input) {
        return this._super.attrTest.call(this, input) &&
            (Math.abs(input.rotation) > this.options.threshold || this.state & STATE_BEGAN);
    }
});

/**
 * Swipe
 * Recognized when the pointer is moving fast (velocity), with enough distance in the allowed direction.
 * @constructor
 * @extends AttrRecognizer
 */
function SwipeRecognizer() {
    AttrRecognizer.apply(this, arguments);
}

inherit(SwipeRecognizer, AttrRecognizer, {
    /**
     * @namespace
     * @memberof SwipeRecognizer
     */
    defaults: {
        event: 'swipe',
        threshold: 10,
        velocity: 0.3,
        direction: DIRECTION_HORIZONTAL | DIRECTION_VERTICAL,
        pointers: 1
    },

    getTouchAction: function() {
        return PanRecognizer.prototype.getTouchAction.call(this);
    },

    attrTest: function(input) {
        var direction = this.options.direction;
        var velocity;

        if (direction & (DIRECTION_HORIZONTAL | DIRECTION_VERTICAL)) {
            velocity = input.overallVelocity;
        } else if (direction & DIRECTION_HORIZONTAL) {
            velocity = input.overallVelocityX;
        } else if (direction & DIRECTION_VERTICAL) {
            velocity = input.overallVelocityY;
        }

        return this._super.attrTest.call(this, input) &&
            direction & input.offsetDirection &&
            input.distance > this.options.threshold &&
            input.maxPointers == this.options.pointers &&
            abs(velocity) > this.options.velocity && input.eventType & INPUT_END;
    },

    emit: function(input) {
        var direction = directionStr(input.offsetDirection);
        if (direction) {
            this.manager.emit(this.options.event + direction, input);
        }

        this.manager.emit(this.options.event, input);
    }
});

/**
 * A tap is ecognized when the pointer is doing a small tap/click. Multiple taps are recognized if they occur
 * between the given interval and position. The delay option can be used to recognize multi-taps without firing
 * a single tap.
 *
 * The eventData from the emitted event contains the property `tapCount`, which contains the amount of
 * multi-taps being recognized.
 * @constructor
 * @extends Recognizer
 */
function TapRecognizer() {
    Recognizer.apply(this, arguments);

    // previous time and center,
    // used for tap counting
    this.pTime = false;
    this.pCenter = false;

    this._timer = null;
    this._input = null;
    this.count = 0;
}

inherit(TapRecognizer, Recognizer, {
    /**
     * @namespace
     * @memberof PinchRecognizer
     */
    defaults: {
        event: 'tap',
        pointers: 1,
        taps: 1,
        interval: 300, // max time between the multi-tap taps
        time: 250, // max time of the pointer to be down (like finger on the screen)
        threshold: 9, // a minimal movement is ok, but keep it low
        posThreshold: 10 // a multi-tap can be a bit off the initial position
    },

    getTouchAction: function() {
        return [TOUCH_ACTION_MANIPULATION];
    },

    process: function(input) {
        var options = this.options;

        var validPointers = input.pointers.length === options.pointers;
        var validMovement = input.distance < options.threshold;
        var validTouchTime = input.deltaTime < options.time;

        this.reset();

        if ((input.eventType & INPUT_START) && (this.count === 0)) {
            return this.failTimeout();
        }

        // we only allow little movement
        // and we've reached an end event, so a tap is possible
        if (validMovement && validTouchTime && validPointers) {
            if (input.eventType != INPUT_END) {
                return this.failTimeout();
            }

            var validInterval = this.pTime ? (input.timeStamp - this.pTime < options.interval) : true;
            var validMultiTap = !this.pCenter || getDistance(this.pCenter, input.center) < options.posThreshold;

            this.pTime = input.timeStamp;
            this.pCenter = input.center;

            if (!validMultiTap || !validInterval) {
                this.count = 1;
            } else {
                this.count += 1;
            }

            this._input = input;

            // if tap count matches we have recognized it,
            // else it has began recognizing...
            var tapCount = this.count % options.taps;
            if (tapCount === 0) {
                // no failing requirements, immediately trigger the tap event
                // or wait as long as the multitap interval to trigger
                if (!this.hasRequireFailures()) {
                    return STATE_RECOGNIZED;
                } else {
                    this._timer = setTimeoutContext(function() {
                        this.state = STATE_RECOGNIZED;
                        this.tryEmit();
                    }, options.interval, this);
                    return STATE_BEGAN;
                }
            }
        }
        return STATE_FAILED;
    },

    failTimeout: function() {
        this._timer = setTimeoutContext(function() {
            this.state = STATE_FAILED;
        }, this.options.interval, this);
        return STATE_FAILED;
    },

    reset: function() {
        clearTimeout(this._timer);
    },

    emit: function() {
        if (this.state == STATE_RECOGNIZED) {
            this._input.tapCount = this.count;
            this.manager.emit(this.options.event, this._input);
        }
    }
});

/**
 * Simple way to create a manager with a default set of recognizers.
 * @param {HTMLElement} element
 * @param {Object} [options]
 * @constructor
 */
function Hammer(element, options) {
    options = options || {};
    options.recognizers = ifUndefined(options.recognizers, Hammer.defaults.preset);
    return new Manager(element, options);
}

/**
 * @const {string}
 */
Hammer.VERSION = '2.0.7';

/**
 * default settings
 * @namespace
 */
Hammer.defaults = {
    /**
     * set if DOM events are being triggered.
     * But this is slower and unused by simple implementations, so disabled by default.
     * @type {Boolean}
     * @default false
     */
    domEvents: false,

    /**
     * The value for the touchAction property/fallback.
     * When set to `compute` it will magically set the correct value based on the added recognizers.
     * @type {String}
     * @default compute
     */
    touchAction: TOUCH_ACTION_COMPUTE,

    /**
     * @type {Boolean}
     * @default true
     */
    enable: true,

    /**
     * EXPERIMENTAL FEATURE -- can be removed/changed
     * Change the parent input target element.
     * If Null, then it is being set the to main element.
     * @type {Null|EventTarget}
     * @default null
     */
    inputTarget: null,

    /**
     * force an input class
     * @type {Null|Function}
     * @default null
     */
    inputClass: null,

    /**
     * Default recognizer setup when calling `Hammer()`
     * When creating a new Manager these will be skipped.
     * @type {Array}
     */
    preset: [
        // RecognizerClass, options, [recognizeWith, ...], [requireFailure, ...]
        [RotateRecognizer, {enable: false}],
        [PinchRecognizer, {enable: false}, ['rotate']],
        [SwipeRecognizer, {direction: DIRECTION_HORIZONTAL}],
        [PanRecognizer, {direction: DIRECTION_HORIZONTAL}, ['swipe']],
        [TapRecognizer],
        [TapRecognizer, {event: 'doubletap', taps: 2}, ['tap']],
        [PressRecognizer]
    ],

    /**
     * Some CSS properties can be used to improve the working of Hammer.
     * Add them to this method and they will be set when creating a new Manager.
     * @namespace
     */
    cssProps: {
        /**
         * Disables text selection to improve the dragging gesture. Mainly for desktop browsers.
         * @type {String}
         * @default 'none'
         */
        userSelect: 'none',

        /**
         * Disable the Windows Phone grippers when pressing an element.
         * @type {String}
         * @default 'none'
         */
        touchSelect: 'none',

        /**
         * Disables the default callout shown when you touch and hold a touch target.
         * On iOS, when you touch and hold a touch target such as a link, Safari displays
         * a callout containing information about the link. This property allows you to disable that callout.
         * @type {String}
         * @default 'none'
         */
        touchCallout: 'none',

        /**
         * Specifies whether zooming is enabled. Used by IE10>
         * @type {String}
         * @default 'none'
         */
        contentZooming: 'none',

        /**
         * Specifies that an entire element should be draggable instead of its contents. Mainly for desktop browsers.
         * @type {String}
         * @default 'none'
         */
        userDrag: 'none',

        /**
         * Overrides the highlight color shown when the user taps a link or a JavaScript
         * clickable element in iOS. This property obeys the alpha value, if specified.
         * @type {String}
         * @default 'rgba(0,0,0,0)'
         */
        tapHighlightColor: 'rgba(0,0,0,0)'
    }
};

var STOP = 1;
var FORCED_STOP = 2;

/**
 * Manager
 * @param {HTMLElement} element
 * @param {Object} [options]
 * @constructor
 */
function Manager(element, options) {
    this.options = assign({}, Hammer.defaults, options || {});

    this.options.inputTarget = this.options.inputTarget || element;

    this.handlers = {};
    this.session = {};
    this.recognizers = [];
    this.oldCssProps = {};

    this.element = element;
    this.input = createInputInstance(this);
    this.touchAction = new TouchAction(this, this.options.touchAction);

    toggleCssProps(this, true);

    each(this.options.recognizers, function(item) {
        var recognizer = this.add(new (item[0])(item[1]));
        item[2] && recognizer.recognizeWith(item[2]);
        item[3] && recognizer.requireFailure(item[3]);
    }, this);
}

Manager.prototype = {
    /**
     * set options
     * @param {Object} options
     * @returns {Manager}
     */
    set: function(options) {
        assign(this.options, options);

        // Options that need a little more setup
        if (options.touchAction) {
            this.touchAction.update();
        }
        if (options.inputTarget) {
            // Clean up existing event listeners and reinitialize
            this.input.destroy();
            this.input.target = options.inputTarget;
            this.input.init();
        }
        return this;
    },

    /**
     * stop recognizing for this session.
     * This session will be discarded, when a new [input]start event is fired.
     * When forced, the recognizer cycle is stopped immediately.
     * @param {Boolean} [force]
     */
    stop: function(force) {
        this.session.stopped = force ? FORCED_STOP : STOP;
    },

    /**
     * run the recognizers!
     * called by the inputHandler function on every movement of the pointers (touches)
     * it walks through all the recognizers and tries to detect the gesture that is being made
     * @param {Object} inputData
     */
    recognize: function(inputData) {
        var session = this.session;
        if (session.stopped) {
            return;
        }

        // run the touch-action polyfill
        this.touchAction.preventDefaults(inputData);

        var recognizer;
        var recognizers = this.recognizers;

        // this holds the recognizer that is being recognized.
        // so the recognizer's state needs to be BEGAN, CHANGED, ENDED or RECOGNIZED
        // if no recognizer is detecting a thing, it is set to `null`
        var curRecognizer = session.curRecognizer;

        // reset when the last recognizer is recognized
        // or when we're in a new session
        if (!curRecognizer || (curRecognizer && curRecognizer.state & STATE_RECOGNIZED)) {
            curRecognizer = session.curRecognizer = null;
        }

        var i = 0;
        while (i < recognizers.length) {
            recognizer = recognizers[i];

            // find out if we are allowed try to recognize the input for this one.
            // 1.   allow if the session is NOT forced stopped (see the .stop() method)
            // 2.   allow if we still haven't recognized a gesture in this session, or the this recognizer is the one
            //      that is being recognized.
            // 3.   allow if the recognizer is allowed to run simultaneous with the current recognized recognizer.
            //      this can be setup with the `recognizeWith()` method on the recognizer.
            if (session.stopped !== FORCED_STOP && ( // 1
                    !curRecognizer || recognizer == curRecognizer || // 2
                    recognizer.canRecognizeWith(curRecognizer))) { // 3
                recognizer.recognize(inputData);
            } else {
                recognizer.reset();
            }

            // if the recognizer has been recognizing the input as a valid gesture, we want to store this one as the
            // current active recognizer. but only if we don't already have an active recognizer
            if (!curRecognizer && recognizer.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED)) {
                curRecognizer = session.curRecognizer = recognizer;
            }
            i++;
        }
    },

    /**
     * get a recognizer by its event name.
     * @param {Recognizer|String} recognizer
     * @returns {Recognizer|Null}
     */
    get: function(recognizer) {
        if (recognizer instanceof Recognizer) {
            return recognizer;
        }

        var recognizers = this.recognizers;
        for (var i = 0; i < recognizers.length; i++) {
            if (recognizers[i].options.event == recognizer) {
                return recognizers[i];
            }
        }
        return null;
    },

    /**
     * add a recognizer to the manager
     * existing recognizers with the same event name will be removed
     * @param {Recognizer} recognizer
     * @returns {Recognizer|Manager}
     */
    add: function(recognizer) {
        if (invokeArrayArg(recognizer, 'add', this)) {
            return this;
        }

        // remove existing
        var existing = this.get(recognizer.options.event);
        if (existing) {
            this.remove(existing);
        }

        this.recognizers.push(recognizer);
        recognizer.manager = this;

        this.touchAction.update();
        return recognizer;
    },

    /**
     * remove a recognizer by name or instance
     * @param {Recognizer|String} recognizer
     * @returns {Manager}
     */
    remove: function(recognizer) {
        if (invokeArrayArg(recognizer, 'remove', this)) {
            return this;
        }

        recognizer = this.get(recognizer);

        // let's make sure this recognizer exists
        if (recognizer) {
            var recognizers = this.recognizers;
            var index = inArray(recognizers, recognizer);

            if (index !== -1) {
                recognizers.splice(index, 1);
                this.touchAction.update();
            }
        }

        return this;
    },

    /**
     * bind event
     * @param {String} events
     * @param {Function} handler
     * @returns {EventEmitter} this
     */
    on: function(events, handler) {
        if (events === undefined) {
            return;
        }
        if (handler === undefined) {
            return;
        }

        var handlers = this.handlers;
        each(splitStr(events), function(event) {
            handlers[event] = handlers[event] || [];
            handlers[event].push(handler);
        });
        return this;
    },

    /**
     * unbind event, leave emit blank to remove all handlers
     * @param {String} events
     * @param {Function} [handler]
     * @returns {EventEmitter} this
     */
    off: function(events, handler) {
        if (events === undefined) {
            return;
        }

        var handlers = this.handlers;
        each(splitStr(events), function(event) {
            if (!handler) {
                delete handlers[event];
            } else {
                handlers[event] && handlers[event].splice(inArray(handlers[event], handler), 1);
            }
        });
        return this;
    },

    /**
     * emit event to the listeners
     * @param {String} event
     * @param {Object} data
     */
    emit: function(event, data) {
        // we also want to trigger dom events
        if (this.options.domEvents) {
            triggerDomEvent(event, data);
        }

        // no handlers, so skip it all
        var handlers = this.handlers[event] && this.handlers[event].slice();
        if (!handlers || !handlers.length) {
            return;
        }

        data.type = event;
        data.preventDefault = function() {
            data.srcEvent.preventDefault();
        };

        var i = 0;
        while (i < handlers.length) {
            handlers[i](data);
            i++;
        }
    },

    /**
     * destroy the manager and unbinds all events
     * it doesn't unbind dom events, that is the user own responsibility
     */
    destroy: function() {
        this.element && toggleCssProps(this, false);

        this.handlers = {};
        this.session = {};
        this.input.destroy();
        this.element = null;
    }
};

/**
 * add/remove the css properties as defined in manager.options.cssProps
 * @param {Manager} manager
 * @param {Boolean} add
 */
function toggleCssProps(manager, add) {
    var element = manager.element;
    if (!element.style) {
        return;
    }
    var prop;
    each(manager.options.cssProps, function(value, name) {
        prop = prefixed(element.style, name);
        if (add) {
            manager.oldCssProps[prop] = element.style[prop];
            element.style[prop] = value;
        } else {
            element.style[prop] = manager.oldCssProps[prop] || '';
        }
    });
    if (!add) {
        manager.oldCssProps = {};
    }
}

/**
 * trigger dom event
 * @param {String} event
 * @param {Object} data
 */
function triggerDomEvent(event, data) {
    var gestureEvent = document.createEvent('Event');
    gestureEvent.initEvent(event, true, true);
    gestureEvent.gesture = data;
    data.target.dispatchEvent(gestureEvent);
}

assign(Hammer, {
    INPUT_START: INPUT_START,
    INPUT_MOVE: INPUT_MOVE,
    INPUT_END: INPUT_END,
    INPUT_CANCEL: INPUT_CANCEL,

    STATE_POSSIBLE: STATE_POSSIBLE,
    STATE_BEGAN: STATE_BEGAN,
    STATE_CHANGED: STATE_CHANGED,
    STATE_ENDED: STATE_ENDED,
    STATE_RECOGNIZED: STATE_RECOGNIZED,
    STATE_CANCELLED: STATE_CANCELLED,
    STATE_FAILED: STATE_FAILED,

    DIRECTION_NONE: DIRECTION_NONE,
    DIRECTION_LEFT: DIRECTION_LEFT,
    DIRECTION_RIGHT: DIRECTION_RIGHT,
    DIRECTION_UP: DIRECTION_UP,
    DIRECTION_DOWN: DIRECTION_DOWN,
    DIRECTION_HORIZONTAL: DIRECTION_HORIZONTAL,
    DIRECTION_VERTICAL: DIRECTION_VERTICAL,
    DIRECTION_ALL: DIRECTION_ALL,

    Manager: Manager,
    Input: Input,
    TouchAction: TouchAction,

    TouchInput: TouchInput,
    MouseInput: MouseInput,
    PointerEventInput: PointerEventInput,
    TouchMouseInput: TouchMouseInput,
    SingleTouchInput: SingleTouchInput,

    Recognizer: Recognizer,
    AttrRecognizer: AttrRecognizer,
    Tap: TapRecognizer,
    Pan: PanRecognizer,
    Swipe: SwipeRecognizer,
    Pinch: PinchRecognizer,
    Rotate: RotateRecognizer,
    Press: PressRecognizer,

    on: addEventListeners,
    off: removeEventListeners,
    each: each,
    merge: merge,
    extend: extend,
    assign: assign,
    inherit: inherit,
    bindFn: bindFn,
    prefixed: prefixed
});

// this prevents errors when Hammer is loaded in the presence of an AMD
//  style loader but by script tag, not by the loader.
var freeGlobal = (typeof window !== 'undefined' ? window : (typeof self !== 'undefined' ? self : {})); // jshint ignore:line
freeGlobal.Hammer = Hammer;

if (true) {
    !(__WEBPACK_AMD_DEFINE_RESULT__ = (function() {
        return Hammer;
    }).call(exports, __webpack_require__, exports, module),
				__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
} else if (typeof module != 'undefined' && module.exports) {
    module.exports = Hammer;
} else {
    window[exportName] = Hammer;
}

})(window, document, 'Hammer');


/***/ }),
/* 23 */
/***/ (function(module, exports, __webpack_require__) {

var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_RESULT__;!function() {
  var d3 = {
    version: "3.5.17"
  };
  var d3_arraySlice = [].slice, d3_array = function(list) {
    return d3_arraySlice.call(list);
  };
  var d3_document = this.document;
  function d3_documentElement(node) {
    return node && (node.ownerDocument || node.document || node).documentElement;
  }
  function d3_window(node) {
    return node && (node.ownerDocument && node.ownerDocument.defaultView || node.document && node || node.defaultView);
  }
  if (d3_document) {
    try {
      d3_array(d3_document.documentElement.childNodes)[0].nodeType;
    } catch (e) {
      d3_array = function(list) {
        var i = list.length, array = new Array(i);
        while (i--) array[i] = list[i];
        return array;
      };
    }
  }
  if (!Date.now) Date.now = function() {
    return +new Date();
  };
  if (d3_document) {
    try {
      d3_document.createElement("DIV").style.setProperty("opacity", 0, "");
    } catch (error) {
      var d3_element_prototype = this.Element.prototype, d3_element_setAttribute = d3_element_prototype.setAttribute, d3_element_setAttributeNS = d3_element_prototype.setAttributeNS, d3_style_prototype = this.CSSStyleDeclaration.prototype, d3_style_setProperty = d3_style_prototype.setProperty;
      d3_element_prototype.setAttribute = function(name, value) {
        d3_element_setAttribute.call(this, name, value + "");
      };
      d3_element_prototype.setAttributeNS = function(space, local, value) {
        d3_element_setAttributeNS.call(this, space, local, value + "");
      };
      d3_style_prototype.setProperty = function(name, value, priority) {
        d3_style_setProperty.call(this, name, value + "", priority);
      };
    }
  }
  d3.ascending = d3_ascending;
  function d3_ascending(a, b) {
    return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
  }
  d3.descending = function(a, b) {
    return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;
  };
  d3.min = function(array, f) {
    var i = -1, n = array.length, a, b;
    if (arguments.length === 1) {
      while (++i < n) if ((b = array[i]) != null && b >= b) {
        a = b;
        break;
      }
      while (++i < n) if ((b = array[i]) != null && a > b) a = b;
    } else {
      while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) {
        a = b;
        break;
      }
      while (++i < n) if ((b = f.call(array, array[i], i)) != null && a > b) a = b;
    }
    return a;
  };
  d3.max = function(array, f) {
    var i = -1, n = array.length, a, b;
    if (arguments.length === 1) {
      while (++i < n) if ((b = array[i]) != null && b >= b) {
        a = b;
        break;
      }
      while (++i < n) if ((b = array[i]) != null && b > a) a = b;
    } else {
      while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) {
        a = b;
        break;
      }
      while (++i < n) if ((b = f.call(array, array[i], i)) != null && b > a) a = b;
    }
    return a;
  };
  d3.extent = function(array, f) {
    var i = -1, n = array.length, a, b, c;
    if (arguments.length === 1) {
      while (++i < n) if ((b = array[i]) != null && b >= b) {
        a = c = b;
        break;
      }
      while (++i < n) if ((b = array[i]) != null) {
        if (a > b) a = b;
        if (c < b) c = b;
      }
    } else {
      while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) {
        a = c = b;
        break;
      }
      while (++i < n) if ((b = f.call(array, array[i], i)) != null) {
        if (a > b) a = b;
        if (c < b) c = b;
      }
    }
    return [ a, c ];
  };
  function d3_number(x) {
    return x === null ? NaN : +x;
  }
  function d3_numeric(x) {
    return !isNaN(x);
  }
  d3.sum = function(array, f) {
    var s = 0, n = array.length, a, i = -1;
    if (arguments.length === 1) {
      while (++i < n) if (d3_numeric(a = +array[i])) s += a;
    } else {
      while (++i < n) if (d3_numeric(a = +f.call(array, array[i], i))) s += a;
    }
    return s;
  };
  d3.mean = function(array, f) {
    var s = 0, n = array.length, a, i = -1, j = n;
    if (arguments.length === 1) {
      while (++i < n) if (d3_numeric(a = d3_number(array[i]))) s += a; else --j;
    } else {
      while (++i < n) if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) s += a; else --j;
    }
    if (j) return s / j;
  };
  d3.quantile = function(values, p) {
    var H = (values.length - 1) * p + 1, h = Math.floor(H), v = +values[h - 1], e = H - h;
    return e ? v + e * (values[h] - v) : v;
  };
  d3.median = function(array, f) {
    var numbers = [], n = array.length, a, i = -1;
    if (arguments.length === 1) {
      while (++i < n) if (d3_numeric(a = d3_number(array[i]))) numbers.push(a);
    } else {
      while (++i < n) if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) numbers.push(a);
    }
    if (numbers.length) return d3.quantile(numbers.sort(d3_ascending), .5);
  };
  d3.variance = function(array, f) {
    var n = array.length, m = 0, a, d, s = 0, i = -1, j = 0;
    if (arguments.length === 1) {
      while (++i < n) {
        if (d3_numeric(a = d3_number(array[i]))) {
          d = a - m;
          m += d / ++j;
          s += d * (a - m);
        }
      }
    } else {
      while (++i < n) {
        if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) {
          d = a - m;
          m += d / ++j;
          s += d * (a - m);
        }
      }
    }
    if (j > 1) return s / (j - 1);
  };
  d3.deviation = function() {
    var v = d3.variance.apply(this, arguments);
    return v ? Math.sqrt(v) : v;
  };
  function d3_bisector(compare) {
    return {
      left: function(a, x, lo, hi) {
        if (arguments.length < 3) lo = 0;
        if (arguments.length < 4) hi = a.length;
        while (lo < hi) {
          var mid = lo + hi >>> 1;
          if (compare(a[mid], x) < 0) lo = mid + 1; else hi = mid;
        }
        return lo;
      },
      right: function(a, x, lo, hi) {
        if (arguments.length < 3) lo = 0;
        if (arguments.length < 4) hi = a.length;
        while (lo < hi) {
          var mid = lo + hi >>> 1;
          if (compare(a[mid], x) > 0) hi = mid; else lo = mid + 1;
        }
        return lo;
      }
    };
  }
  var d3_bisect = d3_bisector(d3_ascending);
  d3.bisectLeft = d3_bisect.left;
  d3.bisect = d3.bisectRight = d3_bisect.right;
  d3.bisector = function(f) {
    return d3_bisector(f.length === 1 ? function(d, x) {
      return d3_ascending(f(d), x);
    } : f);
  };
  d3.shuffle = function(array, i0, i1) {
    if ((m = arguments.length) < 3) {
      i1 = array.length;
      if (m < 2) i0 = 0;
    }
    var m = i1 - i0, t, i;
    while (m) {
      i = Math.random() * m-- | 0;
      t = array[m + i0], array[m + i0] = array[i + i0], array[i + i0] = t;
    }
    return array;
  };
  d3.permute = function(array, indexes) {
    var i = indexes.length, permutes = new Array(i);
    while (i--) permutes[i] = array[indexes[i]];
    return permutes;
  };
  d3.pairs = function(array) {
    var i = 0, n = array.length - 1, p0, p1 = array[0], pairs = new Array(n < 0 ? 0 : n);
    while (i < n) pairs[i] = [ p0 = p1, p1 = array[++i] ];
    return pairs;
  };
  d3.transpose = function(matrix) {
    if (!(n = matrix.length)) return [];
    for (var i = -1, m = d3.min(matrix, d3_transposeLength), transpose = new Array(m); ++i < m; ) {
      for (var j = -1, n, row = transpose[i] = new Array(n); ++j < n; ) {
        row[j] = matrix[j][i];
      }
    }
    return transpose;
  };
  function d3_transposeLength(d) {
    return d.length;
  }
  d3.zip = function() {
    return d3.transpose(arguments);
  };
  d3.keys = function(map) {
    var keys = [];
    for (var key in map) keys.push(key);
    return keys;
  };
  d3.values = function(map) {
    var values = [];
    for (var key in map) values.push(map[key]);
    return values;
  };
  d3.entries = function(map) {
    var entries = [];
    for (var key in map) entries.push({
      key: key,
      value: map[key]
    });
    return entries;
  };
  d3.merge = function(arrays) {
    var n = arrays.length, m, i = -1, j = 0, merged, array;
    while (++i < n) j += arrays[i].length;
    merged = new Array(j);
    while (--n >= 0) {
      array = arrays[n];
      m = array.length;
      while (--m >= 0) {
        merged[--j] = array[m];
      }
    }
    return merged;
  };
  var abs = Math.abs;
  d3.range = function(start, stop, step) {
    if (arguments.length < 3) {
      step = 1;
      if (arguments.length < 2) {
        stop = start;
        start = 0;
      }
    }
    if ((stop - start) / step === Infinity) throw new Error("infinite range");
    var range = [], k = d3_range_integerScale(abs(step)), i = -1, j;
    start *= k, stop *= k, step *= k;
    if (step < 0) while ((j = start + step * ++i) > stop) range.push(j / k); else while ((j = start + step * ++i) < stop) range.push(j / k);
    return range;
  };
  function d3_range_integerScale(x) {
    var k = 1;
    while (x * k % 1) k *= 10;
    return k;
  }
  function d3_class(ctor, properties) {
    for (var key in properties) {
      Object.defineProperty(ctor.prototype, key, {
        value: properties[key],
        enumerable: false
      });
    }
  }
  d3.map = function(object, f) {
    var map = new d3_Map();
    if (object instanceof d3_Map) {
      object.forEach(function(key, value) {
        map.set(key, value);
      });
    } else if (Array.isArray(object)) {
      var i = -1, n = object.length, o;
      if (arguments.length === 1) while (++i < n) map.set(i, object[i]); else while (++i < n) map.set(f.call(object, o = object[i], i), o);
    } else {
      for (var key in object) map.set(key, object[key]);
    }
    return map;
  };
  function d3_Map() {
    this._ = Object.create(null);
  }
  var d3_map_proto = "__proto__", d3_map_zero = "\x00";
  d3_class(d3_Map, {
    has: d3_map_has,
    get: function(key) {
      return this._[d3_map_escape(key)];
    },
    set: function(key, value) {
      return this._[d3_map_escape(key)] = value;
    },
    remove: d3_map_remove,
    keys: d3_map_keys,
    values: function() {
      var values = [];
      for (var key in this._) values.push(this._[key]);
      return values;
    },
    entries: function() {
      var entries = [];
      for (var key in this._) entries.push({
        key: d3_map_unescape(key),
        value: this._[key]
      });
      return entries;
    },
    size: d3_map_size,
    empty: d3_map_empty,
    forEach: function(f) {
      for (var key in this._) f.call(this, d3_map_unescape(key), this._[key]);
    }
  });
  function d3_map_escape(key) {
    return (key += "") === d3_map_proto || key[0] === d3_map_zero ? d3_map_zero + key : key;
  }
  function d3_map_unescape(key) {
    return (key += "")[0] === d3_map_zero ? key.slice(1) : key;
  }
  function d3_map_has(key) {
    return d3_map_escape(key) in this._;
  }
  function d3_map_remove(key) {
    return (key = d3_map_escape(key)) in this._ && delete this._[key];
  }
  function d3_map_keys() {
    var keys = [];
    for (var key in this._) keys.push(d3_map_unescape(key));
    return keys;
  }
  function d3_map_size() {
    var size = 0;
    for (var key in this._) ++size;
    return size;
  }
  function d3_map_empty() {
    for (var key in this._) return false;
    return true;
  }
  d3.nest = function() {
    var nest = {}, keys = [], sortKeys = [], sortValues, rollup;
    function map(mapType, array, depth) {
      if (depth >= keys.length) return rollup ? rollup.call(nest, array) : sortValues ? array.sort(sortValues) : array;
      var i = -1, n = array.length, key = keys[depth++], keyValue, object, setter, valuesByKey = new d3_Map(), values;
      while (++i < n) {
        if (values = valuesByKey.get(keyValue = key(object = array[i]))) {
          values.push(object);
        } else {
          valuesByKey.set(keyValue, [ object ]);
        }
      }
      if (mapType) {
        object = mapType();
        setter = function(keyValue, values) {
          object.set(keyValue, map(mapType, values, depth));
        };
      } else {
        object = {};
        setter = function(keyValue, values) {
          object[keyValue] = map(mapType, values, depth);
        };
      }
      valuesByKey.forEach(setter);
      return object;
    }
    function entries(map, depth) {
      if (depth >= keys.length) return map;
      var array = [], sortKey = sortKeys[depth++];
      map.forEach(function(key, keyMap) {
        array.push({
          key: key,
          values: entries(keyMap, depth)
        });
      });
      return sortKey ? array.sort(function(a, b) {
        return sortKey(a.key, b.key);
      }) : array;
    }
    nest.map = function(array, mapType) {
      return map(mapType, array, 0);
    };
    nest.entries = function(array) {
      return entries(map(d3.map, array, 0), 0);
    };
    nest.key = function(d) {
      keys.push(d);
      return nest;
    };
    nest.sortKeys = function(order) {
      sortKeys[keys.length - 1] = order;
      return nest;
    };
    nest.sortValues = function(order) {
      sortValues = order;
      return nest;
    };
    nest.rollup = function(f) {
      rollup = f;
      return nest;
    };
    return nest;
  };
  d3.set = function(array) {
    var set = new d3_Set();
    if (array) for (var i = 0, n = array.length; i < n; ++i) set.add(array[i]);
    return set;
  };
  function d3_Set() {
    this._ = Object.create(null);
  }
  d3_class(d3_Set, {
    has: d3_map_has,
    add: function(key) {
      this._[d3_map_escape(key += "")] = true;
      return key;
    },
    remove: d3_map_remove,
    values: d3_map_keys,
    size: d3_map_size,
    empty: d3_map_empty,
    forEach: function(f) {
      for (var key in this._) f.call(this, d3_map_unescape(key));
    }
  });
  d3.behavior = {};
  function d3_identity(d) {
    return d;
  }
  d3.rebind = function(target, source) {
    var i = 1, n = arguments.length, method;
    while (++i < n) target[method = arguments[i]] = d3_rebind(target, source, source[method]);
    return target;
  };
  function d3_rebind(target, source, method) {
    return function() {
      var value = method.apply(source, arguments);
      return value === source ? target : value;
    };
  }
  function d3_vendorSymbol(object, name) {
    if (name in object) return name;
    name = name.charAt(0).toUpperCase() + name.slice(1);
    for (var i = 0, n = d3_vendorPrefixes.length; i < n; ++i) {
      var prefixName = d3_vendorPrefixes[i] + name;
      if (prefixName in object) return prefixName;
    }
  }
  var d3_vendorPrefixes = [ "webkit", "ms", "moz", "Moz", "o", "O" ];
  function d3_noop() {}
  d3.dispatch = function() {
    var dispatch = new d3_dispatch(), i = -1, n = arguments.length;
    while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch);
    return dispatch;
  };
  function d3_dispatch() {}
  d3_dispatch.prototype.on = function(type, listener) {
    var i = type.indexOf("."), name = "";
    if (i >= 0) {
      name = type.slice(i + 1);
      type = type.slice(0, i);
    }
    if (type) return arguments.length < 2 ? this[type].on(name) : this[type].on(name, listener);
    if (arguments.length === 2) {
      if (listener == null) for (type in this) {
        if (this.hasOwnProperty(type)) this[type].on(name, null);
      }
      return this;
    }
  };
  function d3_dispatch_event(dispatch) {
    var listeners = [], listenerByName = new d3_Map();
    function event() {
      var z = listeners, i = -1, n = z.length, l;
      while (++i < n) if (l = z[i].on) l.apply(this, arguments);
      return dispatch;
    }
    event.on = function(name, listener) {
      var l = listenerByName.get(name), i;
      if (arguments.length < 2) return l && l.on;
      if (l) {
        l.on = null;
        listeners = listeners.slice(0, i = listeners.indexOf(l)).concat(listeners.slice(i + 1));
        listenerByName.remove(name);
      }
      if (listener) listeners.push(listenerByName.set(name, {
        on: listener
      }));
      return dispatch;
    };
    return event;
  }
  d3.event = null;
  function d3_eventPreventDefault() {
    d3.event.preventDefault();
  }
  function d3_eventSource() {
    var e = d3.event, s;
    while (s = e.sourceEvent) e = s;
    return e;
  }
  function d3_eventDispatch(target) {
    var dispatch = new d3_dispatch(), i = 0, n = arguments.length;
    while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch);
    dispatch.of = function(thiz, argumentz) {
      return function(e1) {
        try {
          var e0 = e1.sourceEvent = d3.event;
          e1.target = target;
          d3.event = e1;
          dispatch[e1.type].apply(thiz, argumentz);
        } finally {
          d3.event = e0;
        }
      };
    };
    return dispatch;
  }
  d3.requote = function(s) {
    return s.replace(d3_requote_re, "\\$&");
  };
  var d3_requote_re = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;
  var d3_subclass = {}.__proto__ ? function(object, prototype) {
    object.__proto__ = prototype;
  } : function(object, prototype) {
    for (var property in prototype) object[property] = prototype[property];
  };
  function d3_selection(groups) {
    d3_subclass(groups, d3_selectionPrototype);
    return groups;
  }
  var d3_select = function(s, n) {
    return n.querySelector(s);
  }, d3_selectAll = function(s, n) {
    return n.querySelectorAll(s);
  }, d3_selectMatches = function(n, s) {
    var d3_selectMatcher = n.matches || n[d3_vendorSymbol(n, "matchesSelector")];
    d3_selectMatches = function(n, s) {
      return d3_selectMatcher.call(n, s);
    };
    return d3_selectMatches(n, s);
  };
  if (typeof Sizzle === "function") {
    d3_select = function(s, n) {
      return Sizzle(s, n)[0] || null;
    };
    d3_selectAll = Sizzle;
    d3_selectMatches = Sizzle.matchesSelector;
  }
  d3.selection = function() {
    return d3.select(d3_document.documentElement);
  };
  var d3_selectionPrototype = d3.selection.prototype = [];
  d3_selectionPrototype.select = function(selector) {
    var subgroups = [], subgroup, subnode, group, node;
    selector = d3_selection_selector(selector);
    for (var j = -1, m = this.length; ++j < m; ) {
      subgroups.push(subgroup = []);
      subgroup.parentNode = (group = this[j]).parentNode;
      for (var i = -1, n = group.length; ++i < n; ) {
        if (node = group[i]) {
          subgroup.push(subnode = selector.call(node, node.__data__, i, j));
          if (subnode && "__data__" in node) subnode.__data__ = node.__data__;
        } else {
          subgroup.push(null);
        }
      }
    }
    return d3_selection(subgroups);
  };
  function d3_selection_selector(selector) {
    return typeof selector === "function" ? selector : function() {
      return d3_select(selector, this);
    };
  }
  d3_selectionPrototype.selectAll = function(selector) {
    var subgroups = [], subgroup, node;
    selector = d3_selection_selectorAll(selector);
    for (var j = -1, m = this.length; ++j < m; ) {
      for (var group = this[j], i = -1, n = group.length; ++i < n; ) {
        if (node = group[i]) {
          subgroups.push(subgroup = d3_array(selector.call(node, node.__data__, i, j)));
          subgroup.parentNode = node;
        }
      }
    }
    return d3_selection(subgroups);
  };
  function d3_selection_selectorAll(selector) {
    return typeof selector === "function" ? selector : function() {
      return d3_selectAll(selector, this);
    };
  }
  var d3_nsXhtml = "http://www.w3.org/1999/xhtml";
  var d3_nsPrefix = {
    svg: "http://www.w3.org/2000/svg",
    xhtml: d3_nsXhtml,
    xlink: "http://www.w3.org/1999/xlink",
    xml: "http://www.w3.org/XML/1998/namespace",
    xmlns: "http://www.w3.org/2000/xmlns/"
  };
  d3.ns = {
    prefix: d3_nsPrefix,
    qualify: function(name) {
      var i = name.indexOf(":"), prefix = name;
      if (i >= 0 && (prefix = name.slice(0, i)) !== "xmlns") name = name.slice(i + 1);
      return d3_nsPrefix.hasOwnProperty(prefix) ? {
        space: d3_nsPrefix[prefix],
        local: name
      } : name;
    }
  };
  d3_selectionPrototype.attr = function(name, value) {
    if (arguments.length < 2) {
      if (typeof name === "string") {
        var node = this.node();
        name = d3.ns.qualify(name);
        return name.local ? node.getAttributeNS(name.space, name.local) : node.getAttribute(name);
      }
      for (value in name) this.each(d3_selection_attr(value, name[value]));
      return this;
    }
    return this.each(d3_selection_attr(name, value));
  };
  function d3_selection_attr(name, value) {
    name = d3.ns.qualify(name);
    function attrNull() {
      this.removeAttribute(name);
    }
    function attrNullNS() {
      this.removeAttributeNS(name.space, name.local);
    }
    function attrConstant() {
      this.setAttribute(name, value);
    }
    function attrConstantNS() {
      this.setAttributeNS(name.space, name.local, value);
    }
    function attrFunction() {
      var x = value.apply(this, arguments);
      if (x == null) this.removeAttribute(name); else this.setAttribute(name, x);
    }
    function attrFunctionNS() {
      var x = value.apply(this, arguments);
      if (x == null) this.removeAttributeNS(name.space, name.local); else this.setAttributeNS(name.space, name.local, x);
    }
    return value == null ? name.local ? attrNullNS : attrNull : typeof value === "function" ? name.local ? attrFunctionNS : attrFunction : name.local ? attrConstantNS : attrConstant;
  }
  function d3_collapse(s) {
    return s.trim().replace(/\s+/g, " ");
  }
  d3_selectionPrototype.classed = function(name, value) {
    if (arguments.length < 2) {
      if (typeof name === "string") {
        var node = this.node(), n = (name = d3_selection_classes(name)).length, i = -1;
        if (value = node.classList) {
          while (++i < n) if (!value.contains(name[i])) return false;
        } else {
          value = node.getAttribute("class");
          while (++i < n) if (!d3_selection_classedRe(name[i]).test(value)) return false;
        }
        return true;
      }
      for (value in name) this.each(d3_selection_classed(value, name[value]));
      return this;
    }
    return this.each(d3_selection_classed(name, value));
  };
  function d3_selection_classedRe(name) {
    return new RegExp("(?:^|\\s+)" + d3.requote(name) + "(?:\\s+|$)", "g");
  }
  function d3_selection_classes(name) {
    return (name + "").trim().split(/^|\s+/);
  }
  function d3_selection_classed(name, value) {
    name = d3_selection_classes(name).map(d3_selection_classedName);
    var n = name.length;
    function classedConstant() {
      var i = -1;
      while (++i < n) name[i](this, value);
    }
    function classedFunction() {
      var i = -1, x = value.apply(this, arguments);
      while (++i < n) name[i](this, x);
    }
    return typeof value === "function" ? classedFunction : classedConstant;
  }
  function d3_selection_classedName(name) {
    var re = d3_selection_classedRe(name);
    return function(node, value) {
      if (c = node.classList) return value ? c.add(name) : c.remove(name);
      var c = node.getAttribute("class") || "";
      if (value) {
        re.lastIndex = 0;
        if (!re.test(c)) node.setAttribute("class", d3_collapse(c + " " + name));
      } else {
        node.setAttribute("class", d3_collapse(c.replace(re, " ")));
      }
    };
  }
  d3_selectionPrototype.style = function(name, value, priority) {
    var n = arguments.length;
    if (n < 3) {
      if (typeof name !== "string") {
        if (n < 2) value = "";
        for (priority in name) this.each(d3_selection_style(priority, name[priority], value));
        return this;
      }
      if (n < 2) {
        var node = this.node();
        return d3_window(node).getComputedStyle(node, null).getPropertyValue(name);
      }
      priority = "";
    }
    return this.each(d3_selection_style(name, value, priority));
  };
  function d3_selection_style(name, value, priority) {
    function styleNull() {
      this.style.removeProperty(name);
    }
    function styleConstant() {
      this.style.setProperty(name, value, priority);
    }
    function styleFunction() {
      var x = value.apply(this, arguments);
      if (x == null) this.style.removeProperty(name); else this.style.setProperty(name, x, priority);
    }
    return value == null ? styleNull : typeof value === "function" ? styleFunction : styleConstant;
  }
  d3_selectionPrototype.property = function(name, value) {
    if (arguments.length < 2) {
      if (typeof name === "string") return this.node()[name];
      for (value in name) this.each(d3_selection_property(value, name[value]));
      return this;
    }
    return this.each(d3_selection_property(name, value));
  };
  function d3_selection_property(name, value) {
    function propertyNull() {
      delete this[name];
    }
    function propertyConstant() {
      this[name] = value;
    }
    function propertyFunction() {
      var x = value.apply(this, arguments);
      if (x == null) delete this[name]; else this[name] = x;
    }
    return value == null ? propertyNull : typeof value === "function" ? propertyFunction : propertyConstant;
  }
  d3_selectionPrototype.text = function(value) {
    return arguments.length ? this.each(typeof value === "function" ? function() {
      var v = value.apply(this, arguments);
      this.textContent = v == null ? "" : v;
    } : value == null ? function() {
      this.textContent = "";
    } : function() {
      this.textContent = value;
    }) : this.node().textContent;
  };
  d3_selectionPrototype.html = function(value) {
    return arguments.length ? this.each(typeof value === "function" ? function() {
      var v = value.apply(this, arguments);
      this.innerHTML = v == null ? "" : v;
    } : value == null ? function() {
      this.innerHTML = "";
    } : function() {
      this.innerHTML = value;
    }) : this.node().innerHTML;
  };
  d3_selectionPrototype.append = function(name) {
    name = d3_selection_creator(name);
    return this.select(function() {
      return this.appendChild(name.apply(this, arguments));
    });
  };
  function d3_selection_creator(name) {
    function create() {
      var document = this.ownerDocument, namespace = this.namespaceURI;
      return namespace === d3_nsXhtml && document.documentElement.namespaceURI === d3_nsXhtml ? document.createElement(name) : document.createElementNS(namespace, name);
    }
    function createNS() {
      return this.ownerDocument.createElementNS(name.space, name.local);
    }
    return typeof name === "function" ? name : (name = d3.ns.qualify(name)).local ? createNS : create;
  }
  d3_selectionPrototype.insert = function(name, before) {
    name = d3_selection_creator(name);
    before = d3_selection_selector(before);
    return this.select(function() {
      return this.insertBefore(name.apply(this, arguments), before.apply(this, arguments) || null);
    });
  };
  d3_selectionPrototype.remove = function() {
    return this.each(d3_selectionRemove);
  };
  function d3_selectionRemove() {
    var parent = this.parentNode;
    if (parent) parent.removeChild(this);
  }
  d3_selectionPrototype.data = function(value, key) {
    var i = -1, n = this.length, group, node;
    if (!arguments.length) {
      value = new Array(n = (group = this[0]).length);
      while (++i < n) {
        if (node = group[i]) {
          value[i] = node.__data__;
        }
      }
      return value;
    }
    function bind(group, groupData) {
      var i, n = group.length, m = groupData.length, n0 = Math.min(n, m), updateNodes = new Array(m), enterNodes = new Array(m), exitNodes = new Array(n), node, nodeData;
      if (key) {
        var nodeByKeyValue = new d3_Map(), keyValues = new Array(n), keyValue;
        for (i = -1; ++i < n; ) {
          if (node = group[i]) {
            if (nodeByKeyValue.has(keyValue = key.call(node, node.__data__, i))) {
              exitNodes[i] = node;
            } else {
              nodeByKeyValue.set(keyValue, node);
            }
            keyValues[i] = keyValue;
          }
        }
        for (i = -1; ++i < m; ) {
          if (!(node = nodeByKeyValue.get(keyValue = key.call(groupData, nodeData = groupData[i], i)))) {
            enterNodes[i] = d3_selection_dataNode(nodeData);
          } else if (node !== true) {
            updateNodes[i] = node;
            node.__data__ = nodeData;
          }
          nodeByKeyValue.set(keyValue, true);
        }
        for (i = -1; ++i < n; ) {
          if (i in keyValues && nodeByKeyValue.get(keyValues[i]) !== true) {
            exitNodes[i] = group[i];
          }
        }
      } else {
        for (i = -1; ++i < n0; ) {
          node = group[i];
          nodeData = groupData[i];
          if (node) {
            node.__data__ = nodeData;
            updateNodes[i] = node;
          } else {
            enterNodes[i] = d3_selection_dataNode(nodeData);
          }
        }
        for (;i < m; ++i) {
          enterNodes[i] = d3_selection_dataNode(groupData[i]);
        }
        for (;i < n; ++i) {
          exitNodes[i] = group[i];
        }
      }
      enterNodes.update = updateNodes;
      enterNodes.parentNode = updateNodes.parentNode = exitNodes.parentNode = group.parentNode;
      enter.push(enterNodes);
      update.push(updateNodes);
      exit.push(exitNodes);
    }
    var enter = d3_selection_enter([]), update = d3_selection([]), exit = d3_selection([]);
    if (typeof value === "function") {
      while (++i < n) {
        bind(group = this[i], value.call(group, group.parentNode.__data__, i));
      }
    } else {
      while (++i < n) {
        bind(group = this[i], value);
      }
    }
    update.enter = function() {
      return enter;
    };
    update.exit = function() {
      return exit;
    };
    return update;
  };
  function d3_selection_dataNode(data) {
    return {
      __data__: data
    };
  }
  d3_selectionPrototype.datum = function(value) {
    return arguments.length ? this.property("__data__", value) : this.property("__data__");
  };
  d3_selectionPrototype.filter = function(filter) {
    var subgroups = [], subgroup, group, node;
    if (typeof filter !== "function") filter = d3_selection_filter(filter);
    for (var j = 0, m = this.length; j < m; j++) {
      subgroups.push(subgroup = []);
      subgroup.parentNode = (group = this[j]).parentNode;
      for (var i = 0, n = group.length; i < n; i++) {
        if ((node = group[i]) && filter.call(node, node.__data__, i, j)) {
          subgroup.push(node);
        }
      }
    }
    return d3_selection(subgroups);
  };
  function d3_selection_filter(selector) {
    return function() {
      return d3_selectMatches(this, selector);
    };
  }
  d3_selectionPrototype.order = function() {
    for (var j = -1, m = this.length; ++j < m; ) {
      for (var group = this[j], i = group.length - 1, next = group[i], node; --i >= 0; ) {
        if (node = group[i]) {
          if (next && next !== node.nextSibling) next.parentNode.insertBefore(node, next);
          next = node;
        }
      }
    }
    return this;
  };
  d3_selectionPrototype.sort = function(comparator) {
    comparator = d3_selection_sortComparator.apply(this, arguments);
    for (var j = -1, m = this.length; ++j < m; ) this[j].sort(comparator);
    return this.order();
  };
  function d3_selection_sortComparator(comparator) {
    if (!arguments.length) comparator = d3_ascending;
    return function(a, b) {
      return a && b ? comparator(a.__data__, b.__data__) : !a - !b;
    };
  }
  d3_selectionPrototype.each = function(callback) {
    return d3_selection_each(this, function(node, i, j) {
      callback.call(node, node.__data__, i, j);
    });
  };
  function d3_selection_each(groups, callback) {
    for (var j = 0, m = groups.length; j < m; j++) {
      for (var group = groups[j], i = 0, n = group.length, node; i < n; i++) {
        if (node = group[i]) callback(node, i, j);
      }
    }
    return groups;
  }
  d3_selectionPrototype.call = function(callback) {
    var args = d3_array(arguments);
    callback.apply(args[0] = this, args);
    return this;
  };
  d3_selectionPrototype.empty = function() {
    return !this.node();
  };
  d3_selectionPrototype.node = function() {
    for (var j = 0, m = this.length; j < m; j++) {
      for (var group = this[j], i = 0, n = group.length; i < n; i++) {
        var node = group[i];
        if (node) return node;
      }
    }
    return null;
  };
  d3_selectionPrototype.size = function() {
    var n = 0;
    d3_selection_each(this, function() {
      ++n;
    });
    return n;
  };
  function d3_selection_enter(selection) {
    d3_subclass(selection, d3_selection_enterPrototype);
    return selection;
  }
  var d3_selection_enterPrototype = [];
  d3.selection.enter = d3_selection_enter;
  d3.selection.enter.prototype = d3_selection_enterPrototype;
  d3_selection_enterPrototype.append = d3_selectionPrototype.append;
  d3_selection_enterPrototype.empty = d3_selectionPrototype.empty;
  d3_selection_enterPrototype.node = d3_selectionPrototype.node;
  d3_selection_enterPrototype.call = d3_selectionPrototype.call;
  d3_selection_enterPrototype.size = d3_selectionPrototype.size;
  d3_selection_enterPrototype.select = function(selector) {
    var subgroups = [], subgroup, subnode, upgroup, group, node;
    for (var j = -1, m = this.length; ++j < m; ) {
      upgroup = (group = this[j]).update;
      subgroups.push(subgroup = []);
      subgroup.parentNode = group.parentNode;
      for (var i = -1, n = group.length; ++i < n; ) {
        if (node = group[i]) {
          subgroup.push(upgroup[i] = subnode = selector.call(group.parentNode, node.__data__, i, j));
          subnode.__data__ = node.__data__;
        } else {
          subgroup.push(null);
        }
      }
    }
    return d3_selection(subgroups);
  };
  d3_selection_enterPrototype.insert = function(name, before) {
    if (arguments.length < 2) before = d3_selection_enterInsertBefore(this);
    return d3_selectionPrototype.insert.call(this, name, before);
  };
  function d3_selection_enterInsertBefore(enter) {
    var i0, j0;
    return function(d, i, j) {
      var group = enter[j].update, n = group.length, node;
      if (j != j0) j0 = j, i0 = 0;
      if (i >= i0) i0 = i + 1;
      while (!(node = group[i0]) && ++i0 < n) ;
      return node;
    };
  }
  d3.select = function(node) {
    var group;
    if (typeof node === "string") {
      group = [ d3_select(node, d3_document) ];
      group.parentNode = d3_document.documentElement;
    } else {
      group = [ node ];
      group.parentNode = d3_documentElement(node);
    }
    return d3_selection([ group ]);
  };
  d3.selectAll = function(nodes) {
    var group;
    if (typeof nodes === "string") {
      group = d3_array(d3_selectAll(nodes, d3_document));
      group.parentNode = d3_document.documentElement;
    } else {
      group = d3_array(nodes);
      group.parentNode = null;
    }
    return d3_selection([ group ]);
  };
  d3_selectionPrototype.on = function(type, listener, capture) {
    var n = arguments.length;
    if (n < 3) {
      if (typeof type !== "string") {
        if (n < 2) listener = false;
        for (capture in type) this.each(d3_selection_on(capture, type[capture], listener));
        return this;
      }
      if (n < 2) return (n = this.node()["__on" + type]) && n._;
      capture = false;
    }
    return this.each(d3_selection_on(type, listener, capture));
  };
  function d3_selection_on(type, listener, capture) {
    var name = "__on" + type, i = type.indexOf("."), wrap = d3_selection_onListener;
    if (i > 0) type = type.slice(0, i);
    var filter = d3_selection_onFilters.get(type);
    if (filter) type = filter, wrap = d3_selection_onFilter;
    function onRemove() {
      var l = this[name];
      if (l) {
        this.removeEventListener(type, l, l.$);
        delete this[name];
      }
    }
    function onAdd() {
      var l = wrap(listener, d3_array(arguments));
      onRemove.call(this);
      this.addEventListener(type, this[name] = l, l.$ = capture);
      l._ = listener;
    }
    function removeAll() {
      var re = new RegExp("^__on([^.]+)" + d3.requote(type) + "$"), match;
      for (var name in this) {
        if (match = name.match(re)) {
          var l = this[name];
          this.removeEventListener(match[1], l, l.$);
          delete this[name];
        }
      }
    }
    return i ? listener ? onAdd : onRemove : listener ? d3_noop : removeAll;
  }
  var d3_selection_onFilters = d3.map({
    mouseenter: "mouseover",
    mouseleave: "mouseout"
  });
  if (d3_document) {
    d3_selection_onFilters.forEach(function(k) {
      if ("on" + k in d3_document) d3_selection_onFilters.remove(k);
    });
  }
  function d3_selection_onListener(listener, argumentz) {
    return function(e) {
      var o = d3.event;
      d3.event = e;
      argumentz[0] = this.__data__;
      try {
        listener.apply(this, argumentz);
      } finally {
        d3.event = o;
      }
    };
  }
  function d3_selection_onFilter(listener, argumentz) {
    var l = d3_selection_onListener(listener, argumentz);
    return function(e) {
      var target = this, related = e.relatedTarget;
      if (!related || related !== target && !(related.compareDocumentPosition(target) & 8)) {
        l.call(target, e);
      }
    };
  }
  var d3_event_dragSelect, d3_event_dragId = 0;
  function d3_event_dragSuppress(node) {
    var name = ".dragsuppress-" + ++d3_event_dragId, click = "click" + name, w = d3.select(d3_window(node)).on("touchmove" + name, d3_eventPreventDefault).on("dragstart" + name, d3_eventPreventDefault).on("selectstart" + name, d3_eventPreventDefault);
    if (d3_event_dragSelect == null) {
      d3_event_dragSelect = "onselectstart" in node ? false : d3_vendorSymbol(node.style, "userSelect");
    }
    if (d3_event_dragSelect) {
      var style = d3_documentElement(node).style, select = style[d3_event_dragSelect];
      style[d3_event_dragSelect] = "none";
    }
    return function(suppressClick) {
      w.on(name, null);
      if (d3_event_dragSelect) style[d3_event_dragSelect] = select;
      if (suppressClick) {
        var off = function() {
          w.on(click, null);
        };
        w.on(click, function() {
          d3_eventPreventDefault();
          off();
        }, true);
        setTimeout(off, 0);
      }
    };
  }
  d3.mouse = function(container) {
    return d3_mousePoint(container, d3_eventSource());
  };
  var d3_mouse_bug44083 = this.navigator && /WebKit/.test(this.navigator.userAgent) ? -1 : 0;
  function d3_mousePoint(container, e) {
    if (e.changedTouches) e = e.changedTouches[0];
    var svg = container.ownerSVGElement || container;
    if (svg.createSVGPoint) {
      var point = svg.createSVGPoint();
      if (d3_mouse_bug44083 < 0) {
        var window = d3_window(container);
        if (window.scrollX || window.scrollY) {
          svg = d3.select("body").append("svg").style({
            position: "absolute",
            top: 0,
            left: 0,
            margin: 0,
            padding: 0,
            border: "none"
          }, "important");
          var ctm = svg[0][0].getScreenCTM();
          d3_mouse_bug44083 = !(ctm.f || ctm.e);
          svg.remove();
        }
      }
      if (d3_mouse_bug44083) point.x = e.pageX, point.y = e.pageY; else point.x = e.clientX, 
      point.y = e.clientY;
      point = point.matrixTransform(container.getScreenCTM().inverse());
      return [ point.x, point.y ];
    }
    var rect = container.getBoundingClientRect();
    return [ e.clientX - rect.left - container.clientLeft, e.clientY - rect.top - container.clientTop ];
  }
  d3.touch = function(container, touches, identifier) {
    if (arguments.length < 3) identifier = touches, touches = d3_eventSource().changedTouches;
    if (touches) for (var i = 0, n = touches.length, touch; i < n; ++i) {
      if ((touch = touches[i]).identifier === identifier) {
        return d3_mousePoint(container, touch);
      }
    }
  };
  d3.behavior.drag = function() {
    var event = d3_eventDispatch(drag, "drag", "dragstart", "dragend"), origin = null, mousedown = dragstart(d3_noop, d3.mouse, d3_window, "mousemove", "mouseup"), touchstart = dragstart(d3_behavior_dragTouchId, d3.touch, d3_identity, "touchmove", "touchend");
    function drag() {
      this.on("mousedown.drag", mousedown).on("touchstart.drag", touchstart);
    }
    function dragstart(id, position, subject, move, end) {
      return function() {
        var that = this, target = d3.event.target.correspondingElement || d3.event.target, parent = that.parentNode, dispatch = event.of(that, arguments), dragged = 0, dragId = id(), dragName = ".drag" + (dragId == null ? "" : "-" + dragId), dragOffset, dragSubject = d3.select(subject(target)).on(move + dragName, moved).on(end + dragName, ended), dragRestore = d3_event_dragSuppress(target), position0 = position(parent, dragId);
        if (origin) {
          dragOffset = origin.apply(that, arguments);
          dragOffset = [ dragOffset.x - position0[0], dragOffset.y - position0[1] ];
        } else {
          dragOffset = [ 0, 0 ];
        }
        dispatch({
          type: "dragstart"
        });
        function moved() {
          var position1 = position(parent, dragId), dx, dy;
          if (!position1) return;
          dx = position1[0] - position0[0];
          dy = position1[1] - position0[1];
          dragged |= dx | dy;
          position0 = position1;
          dispatch({
            type: "drag",
            x: position1[0] + dragOffset[0],
            y: position1[1] + dragOffset[1],
            dx: dx,
            dy: dy
          });
        }
        function ended() {
          if (!position(parent, dragId)) return;
          dragSubject.on(move + dragName, null).on(end + dragName, null);
          dragRestore(dragged);
          dispatch({
            type: "dragend"
          });
        }
      };
    }
    drag.origin = function(x) {
      if (!arguments.length) return origin;
      origin = x;
      return drag;
    };
    return d3.rebind(drag, event, "on");
  };
  function d3_behavior_dragTouchId() {
    return d3.event.changedTouches[0].identifier;
  }
  d3.touches = function(container, touches) {
    if (arguments.length < 2) touches = d3_eventSource().touches;
    return touches ? d3_array(touches).map(function(touch) {
      var point = d3_mousePoint(container, touch);
      point.identifier = touch.identifier;
      return point;
    }) : [];
  };
  var ε = 1e-6, ε2 = ε * ε, π = Math.PI, τ = 2 * π, τε = τ - ε, halfπ = π / 2, d3_radians = π / 180, d3_degrees = 180 / π;
  function d3_sgn(x) {
    return x > 0 ? 1 : x < 0 ? -1 : 0;
  }
  function d3_cross2d(a, b, c) {
    return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]);
  }
  function d3_acos(x) {
    return x > 1 ? 0 : x < -1 ? π : Math.acos(x);
  }
  function d3_asin(x) {
    return x > 1 ? halfπ : x < -1 ? -halfπ : Math.asin(x);
  }
  function d3_sinh(x) {
    return ((x = Math.exp(x)) - 1 / x) / 2;
  }
  function d3_cosh(x) {
    return ((x = Math.exp(x)) + 1 / x) / 2;
  }
  function d3_tanh(x) {
    return ((x = Math.exp(2 * x)) - 1) / (x + 1);
  }
  function d3_haversin(x) {
    return (x = Math.sin(x / 2)) * x;
  }
  var ρ = Math.SQRT2, ρ2 = 2, ρ4 = 4;
  d3.interpolateZoom = function(p0, p1) {
    var ux0 = p0[0], uy0 = p0[1], w0 = p0[2], ux1 = p1[0], uy1 = p1[1], w1 = p1[2], dx = ux1 - ux0, dy = uy1 - uy0, d2 = dx * dx + dy * dy, i, S;
    if (d2 < ε2) {
      S = Math.log(w1 / w0) / ρ;
      i = function(t) {
        return [ ux0 + t * dx, uy0 + t * dy, w0 * Math.exp(ρ * t * S) ];
      };
    } else {
      var d1 = Math.sqrt(d2), b0 = (w1 * w1 - w0 * w0 + ρ4 * d2) / (2 * w0 * ρ2 * d1), b1 = (w1 * w1 - w0 * w0 - ρ4 * d2) / (2 * w1 * ρ2 * d1), r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0), r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1);
      S = (r1 - r0) / ρ;
      i = function(t) {
        var s = t * S, coshr0 = d3_cosh(r0), u = w0 / (ρ2 * d1) * (coshr0 * d3_tanh(ρ * s + r0) - d3_sinh(r0));
        return [ ux0 + u * dx, uy0 + u * dy, w0 * coshr0 / d3_cosh(ρ * s + r0) ];
      };
    }
    i.duration = S * 1e3;
    return i;
  };
  d3.behavior.zoom = function() {
    var view = {
      x: 0,
      y: 0,
      k: 1
    }, translate0, center0, center, size = [ 960, 500 ], scaleExtent = d3_behavior_zoomInfinity, duration = 250, zooming = 0, mousedown = "mousedown.zoom", mousemove = "mousemove.zoom", mouseup = "mouseup.zoom", mousewheelTimer, touchstart = "touchstart.zoom", touchtime, event = d3_eventDispatch(zoom, "zoomstart", "zoom", "zoomend"), x0, x1, y0, y1;
    if (!d3_behavior_zoomWheel) {
      d3_behavior_zoomWheel = "onwheel" in d3_document ? (d3_behavior_zoomDelta = function() {
        return -d3.event.deltaY * (d3.event.deltaMode ? 120 : 1);
      }, "wheel") : "onmousewheel" in d3_document ? (d3_behavior_zoomDelta = function() {
        return d3.event.wheelDelta;
      }, "mousewheel") : (d3_behavior_zoomDelta = function() {
        return -d3.event.detail;
      }, "MozMousePixelScroll");
    }
    function zoom(g) {
      g.on(mousedown, mousedowned).on(d3_behavior_zoomWheel + ".zoom", mousewheeled).on("dblclick.zoom", dblclicked).on(touchstart, touchstarted);
    }
    zoom.event = function(g) {
      g.each(function() {
        var dispatch = event.of(this, arguments), view1 = view;
        if (d3_transitionInheritId) {
          d3.select(this).transition().each("start.zoom", function() {
            view = this.__chart__ || {
              x: 0,
              y: 0,
              k: 1
            };
            zoomstarted(dispatch);
          }).tween("zoom:zoom", function() {
            var dx = size[0], dy = size[1], cx = center0 ? center0[0] : dx / 2, cy = center0 ? center0[1] : dy / 2, i = d3.interpolateZoom([ (cx - view.x) / view.k, (cy - view.y) / view.k, dx / view.k ], [ (cx - view1.x) / view1.k, (cy - view1.y) / view1.k, dx / view1.k ]);
            return function(t) {
              var l = i(t), k = dx / l[2];
              this.__chart__ = view = {
                x: cx - l[0] * k,
                y: cy - l[1] * k,
                k: k
              };
              zoomed(dispatch);
            };
          }).each("interrupt.zoom", function() {
            zoomended(dispatch);
          }).each("end.zoom", function() {
            zoomended(dispatch);
          });
        } else {
          this.__chart__ = view;
          zoomstarted(dispatch);
          zoomed(dispatch);
          zoomended(dispatch);
        }
      });
    };
    zoom.translate = function(_) {
      if (!arguments.length) return [ view.x, view.y ];
      view = {
        x: +_[0],
        y: +_[1],
        k: view.k
      };
      rescale();
      return zoom;
    };
    zoom.scale = function(_) {
      if (!arguments.length) return view.k;
      view = {
        x: view.x,
        y: view.y,
        k: null
      };
      scaleTo(+_);
      rescale();
      return zoom;
    };
    zoom.scaleExtent = function(_) {
      if (!arguments.length) return scaleExtent;
      scaleExtent = _ == null ? d3_behavior_zoomInfinity : [ +_[0], +_[1] ];
      return zoom;
    };
    zoom.center = function(_) {
      if (!arguments.length) return center;
      center = _ && [ +_[0], +_[1] ];
      return zoom;
    };
    zoom.size = function(_) {
      if (!arguments.length) return size;
      size = _ && [ +_[0], +_[1] ];
      return zoom;
    };
    zoom.duration = function(_) {
      if (!arguments.length) return duration;
      duration = +_;
      return zoom;
    };
    zoom.x = function(z) {
      if (!arguments.length) return x1;
      x1 = z;
      x0 = z.copy();
      view = {
        x: 0,
        y: 0,
        k: 1
      };
      return zoom;
    };
    zoom.y = function(z) {
      if (!arguments.length) return y1;
      y1 = z;
      y0 = z.copy();
      view = {
        x: 0,
        y: 0,
        k: 1
      };
      return zoom;
    };
    function location(p) {
      return [ (p[0] - view.x) / view.k, (p[1] - view.y) / view.k ];
    }
    function point(l) {
      return [ l[0] * view.k + view.x, l[1] * view.k + view.y ];
    }
    function scaleTo(s) {
      view.k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], s));
    }
    function translateTo(p, l) {
      l = point(l);
      view.x += p[0] - l[0];
      view.y += p[1] - l[1];
    }
    function zoomTo(that, p, l, k) {
      that.__chart__ = {
        x: view.x,
        y: view.y,
        k: view.k
      };
      scaleTo(Math.pow(2, k));
      translateTo(center0 = p, l);
      that = d3.select(that);
      if (duration > 0) that = that.transition().duration(duration);
      that.call(zoom.event);
    }
    function rescale() {
      if (x1) x1.domain(x0.range().map(function(x) {
        return (x - view.x) / view.k;
      }).map(x0.invert));
      if (y1) y1.domain(y0.range().map(function(y) {
        return (y - view.y) / view.k;
      }).map(y0.invert));
    }
    function zoomstarted(dispatch) {
      if (!zooming++) dispatch({
        type: "zoomstart"
      });
    }
    function zoomed(dispatch) {
      rescale();
      dispatch({
        type: "zoom",
        scale: view.k,
        translate: [ view.x, view.y ]
      });
    }
    function zoomended(dispatch) {
      if (!--zooming) dispatch({
        type: "zoomend"
      }), center0 = null;
    }
    function mousedowned() {
      var that = this, dispatch = event.of(that, arguments), dragged = 0, subject = d3.select(d3_window(that)).on(mousemove, moved).on(mouseup, ended), location0 = location(d3.mouse(that)), dragRestore = d3_event_dragSuppress(that);
      d3_selection_interrupt.call(that);
      zoomstarted(dispatch);
      function moved() {
        dragged = 1;
        translateTo(d3.mouse(that), location0);
        zoomed(dispatch);
      }
      function ended() {
        subject.on(mousemove, null).on(mouseup, null);
        dragRestore(dragged);
        zoomended(dispatch);
      }
    }
    function touchstarted() {
      var that = this, dispatch = event.of(that, arguments), locations0 = {}, distance0 = 0, scale0, zoomName = ".zoom-" + d3.event.changedTouches[0].identifier, touchmove = "touchmove" + zoomName, touchend = "touchend" + zoomName, targets = [], subject = d3.select(that), dragRestore = d3_event_dragSuppress(that);
      started();
      zoomstarted(dispatch);
      subject.on(mousedown, null).on(touchstart, started);
      function relocate() {
        var touches = d3.touches(that);
        scale0 = view.k;
        touches.forEach(function(t) {
          if (t.identifier in locations0) locations0[t.identifier] = location(t);
        });
        return touches;
      }
      function started() {
        var target = d3.event.target;
        d3.select(target).on(touchmove, moved).on(touchend, ended);
        targets.push(target);
        var changed = d3.event.changedTouches;
        for (var i = 0, n = changed.length; i < n; ++i) {
          locations0[changed[i].identifier] = null;
        }
        var touches = relocate(), now = Date.now();
        if (touches.length === 1) {
          if (now - touchtime < 500) {
            var p = touches[0];
            zoomTo(that, p, locations0[p.identifier], Math.floor(Math.log(view.k) / Math.LN2) + 1);
            d3_eventPreventDefault();
          }
          touchtime = now;
        } else if (touches.length > 1) {
          var p = touches[0], q = touches[1], dx = p[0] - q[0], dy = p[1] - q[1];
          distance0 = dx * dx + dy * dy;
        }
      }
      function moved() {
        var touches = d3.touches(that), p0, l0, p1, l1;
        d3_selection_interrupt.call(that);
        for (var i = 0, n = touches.length; i < n; ++i, l1 = null) {
          p1 = touches[i];
          if (l1 = locations0[p1.identifier]) {
            if (l0) break;
            p0 = p1, l0 = l1;
          }
        }
        if (l1) {
          var distance1 = (distance1 = p1[0] - p0[0]) * distance1 + (distance1 = p1[1] - p0[1]) * distance1, scale1 = distance0 && Math.sqrt(distance1 / distance0);
          p0 = [ (p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2 ];
          l0 = [ (l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2 ];
          scaleTo(scale1 * scale0);
        }
        touchtime = null;
        translateTo(p0, l0);
        zoomed(dispatch);
      }
      function ended() {
        if (d3.event.touches.length) {
          var changed = d3.event.changedTouches;
          for (var i = 0, n = changed.length; i < n; ++i) {
            delete locations0[changed[i].identifier];
          }
          for (var identifier in locations0) {
            return void relocate();
          }
        }
        d3.selectAll(targets).on(zoomName, null);
        subject.on(mousedown, mousedowned).on(touchstart, touchstarted);
        dragRestore();
        zoomended(dispatch);
      }
    }
    function mousewheeled() {
      var dispatch = event.of(this, arguments);
      if (mousewheelTimer) clearTimeout(mousewheelTimer); else d3_selection_interrupt.call(this), 
      translate0 = location(center0 = center || d3.mouse(this)), zoomstarted(dispatch);
      mousewheelTimer = setTimeout(function() {
        mousewheelTimer = null;
        zoomended(dispatch);
      }, 50);
      d3_eventPreventDefault();
      scaleTo(Math.pow(2, d3_behavior_zoomDelta() * .002) * view.k);
      translateTo(center0, translate0);
      zoomed(dispatch);
    }
    function dblclicked() {
      var p = d3.mouse(this), k = Math.log(view.k) / Math.LN2;
      zoomTo(this, p, location(p), d3.event.shiftKey ? Math.ceil(k) - 1 : Math.floor(k) + 1);
    }
    return d3.rebind(zoom, event, "on");
  };
  var d3_behavior_zoomInfinity = [ 0, Infinity ], d3_behavior_zoomDelta, d3_behavior_zoomWheel;
  d3.color = d3_color;
  function d3_color() {}
  d3_color.prototype.toString = function() {
    return this.rgb() + "";
  };
  d3.hsl = d3_hsl;
  function d3_hsl(h, s, l) {
    return this instanceof d3_hsl ? void (this.h = +h, this.s = +s, this.l = +l) : arguments.length < 2 ? h instanceof d3_hsl ? new d3_hsl(h.h, h.s, h.l) : d3_rgb_parse("" + h, d3_rgb_hsl, d3_hsl) : new d3_hsl(h, s, l);
  }
  var d3_hslPrototype = d3_hsl.prototype = new d3_color();
  d3_hslPrototype.brighter = function(k) {
    k = Math.pow(.7, arguments.length ? k : 1);
    return new d3_hsl(this.h, this.s, this.l / k);
  };
  d3_hslPrototype.darker = function(k) {
    k = Math.pow(.7, arguments.length ? k : 1);
    return new d3_hsl(this.h, this.s, k * this.l);
  };
  d3_hslPrototype.rgb = function() {
    return d3_hsl_rgb(this.h, this.s, this.l);
  };
  function d3_hsl_rgb(h, s, l) {
    var m1, m2;
    h = isNaN(h) ? 0 : (h %= 360) < 0 ? h + 360 : h;
    s = isNaN(s) ? 0 : s < 0 ? 0 : s > 1 ? 1 : s;
    l = l < 0 ? 0 : l > 1 ? 1 : l;
    m2 = l <= .5 ? l * (1 + s) : l + s - l * s;
    m1 = 2 * l - m2;
    function v(h) {
      if (h > 360) h -= 360; else if (h < 0) h += 360;
      if (h < 60) return m1 + (m2 - m1) * h / 60;
      if (h < 180) return m2;
      if (h < 240) return m1 + (m2 - m1) * (240 - h) / 60;
      return m1;
    }
    function vv(h) {
      return Math.round(v(h) * 255);
    }
    return new d3_rgb(vv(h + 120), vv(h), vv(h - 120));
  }
  d3.hcl = d3_hcl;
  function d3_hcl(h, c, l) {
    return this instanceof d3_hcl ? void (this.h = +h, this.c = +c, this.l = +l) : arguments.length < 2 ? h instanceof d3_hcl ? new d3_hcl(h.h, h.c, h.l) : h instanceof d3_lab ? d3_lab_hcl(h.l, h.a, h.b) : d3_lab_hcl((h = d3_rgb_lab((h = d3.rgb(h)).r, h.g, h.b)).l, h.a, h.b) : new d3_hcl(h, c, l);
  }
  var d3_hclPrototype = d3_hcl.prototype = new d3_color();
  d3_hclPrototype.brighter = function(k) {
    return new d3_hcl(this.h, this.c, Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1)));
  };
  d3_hclPrototype.darker = function(k) {
    return new d3_hcl(this.h, this.c, Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1)));
  };
  d3_hclPrototype.rgb = function() {
    return d3_hcl_lab(this.h, this.c, this.l).rgb();
  };
  function d3_hcl_lab(h, c, l) {
    if (isNaN(h)) h = 0;
    if (isNaN(c)) c = 0;
    return new d3_lab(l, Math.cos(h *= d3_radians) * c, Math.sin(h) * c);
  }
  d3.lab = d3_lab;
  function d3_lab(l, a, b) {
    return this instanceof d3_lab ? void (this.l = +l, this.a = +a, this.b = +b) : arguments.length < 2 ? l instanceof d3_lab ? new d3_lab(l.l, l.a, l.b) : l instanceof d3_hcl ? d3_hcl_lab(l.h, l.c, l.l) : d3_rgb_lab((l = d3_rgb(l)).r, l.g, l.b) : new d3_lab(l, a, b);
  }
  var d3_lab_K = 18;
  var d3_lab_X = .95047, d3_lab_Y = 1, d3_lab_Z = 1.08883;
  var d3_labPrototype = d3_lab.prototype = new d3_color();
  d3_labPrototype.brighter = function(k) {
    return new d3_lab(Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1)), this.a, this.b);
  };
  d3_labPrototype.darker = function(k) {
    return new d3_lab(Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1)), this.a, this.b);
  };
  d3_labPrototype.rgb = function() {
    return d3_lab_rgb(this.l, this.a, this.b);
  };
  function d3_lab_rgb(l, a, b) {
    var y = (l + 16) / 116, x = y + a / 500, z = y - b / 200;
    x = d3_lab_xyz(x) * d3_lab_X;
    y = d3_lab_xyz(y) * d3_lab_Y;
    z = d3_lab_xyz(z) * d3_lab_Z;
    return new d3_rgb(d3_xyz_rgb(3.2404542 * x - 1.5371385 * y - .4985314 * z), d3_xyz_rgb(-.969266 * x + 1.8760108 * y + .041556 * z), d3_xyz_rgb(.0556434 * x - .2040259 * y + 1.0572252 * z));
  }
  function d3_lab_hcl(l, a, b) {
    return l > 0 ? new d3_hcl(Math.atan2(b, a) * d3_degrees, Math.sqrt(a * a + b * b), l) : new d3_hcl(NaN, NaN, l);
  }
  function d3_lab_xyz(x) {
    return x > .206893034 ? x * x * x : (x - 4 / 29) / 7.787037;
  }
  function d3_xyz_lab(x) {
    return x > .008856 ? Math.pow(x, 1 / 3) : 7.787037 * x + 4 / 29;
  }
  function d3_xyz_rgb(r) {
    return Math.round(255 * (r <= .00304 ? 12.92 * r : 1.055 * Math.pow(r, 1 / 2.4) - .055));
  }
  d3.rgb = d3_rgb;
  function d3_rgb(r, g, b) {
    return this instanceof d3_rgb ? void (this.r = ~~r, this.g = ~~g, this.b = ~~b) : arguments.length < 2 ? r instanceof d3_rgb ? new d3_rgb(r.r, r.g, r.b) : d3_rgb_parse("" + r, d3_rgb, d3_hsl_rgb) : new d3_rgb(r, g, b);
  }
  function d3_rgbNumber(value) {
    return new d3_rgb(value >> 16, value >> 8 & 255, value & 255);
  }
  function d3_rgbString(value) {
    return d3_rgbNumber(value) + "";
  }
  var d3_rgbPrototype = d3_rgb.prototype = new d3_color();
  d3_rgbPrototype.brighter = function(k) {
    k = Math.pow(.7, arguments.length ? k : 1);
    var r = this.r, g = this.g, b = this.b, i = 30;
    if (!r && !g && !b) return new d3_rgb(i, i, i);
    if (r && r < i) r = i;
    if (g && g < i) g = i;
    if (b && b < i) b = i;
    return new d3_rgb(Math.min(255, r / k), Math.min(255, g / k), Math.min(255, b / k));
  };
  d3_rgbPrototype.darker = function(k) {
    k = Math.pow(.7, arguments.length ? k : 1);
    return new d3_rgb(k * this.r, k * this.g, k * this.b);
  };
  d3_rgbPrototype.hsl = function() {
    return d3_rgb_hsl(this.r, this.g, this.b);
  };
  d3_rgbPrototype.toString = function() {
    return "#" + d3_rgb_hex(this.r) + d3_rgb_hex(this.g) + d3_rgb_hex(this.b);
  };
  function d3_rgb_hex(v) {
    return v < 16 ? "0" + Math.max(0, v).toString(16) : Math.min(255, v).toString(16);
  }
  function d3_rgb_parse(format, rgb, hsl) {
    var r = 0, g = 0, b = 0, m1, m2, color;
    m1 = /([a-z]+)\((.*)\)/.exec(format = format.toLowerCase());
    if (m1) {
      m2 = m1[2].split(",");
      switch (m1[1]) {
       case "hsl":
        {
          return hsl(parseFloat(m2[0]), parseFloat(m2[1]) / 100, parseFloat(m2[2]) / 100);
        }

       case "rgb":
        {
          return rgb(d3_rgb_parseNumber(m2[0]), d3_rgb_parseNumber(m2[1]), d3_rgb_parseNumber(m2[2]));
        }
      }
    }
    if (color = d3_rgb_names.get(format)) {
      return rgb(color.r, color.g, color.b);
    }
    if (format != null && format.charAt(0) === "#" && !isNaN(color = parseInt(format.slice(1), 16))) {
      if (format.length === 4) {
        r = (color & 3840) >> 4;
        r = r >> 4 | r;
        g = color & 240;
        g = g >> 4 | g;
        b = color & 15;
        b = b << 4 | b;
      } else if (format.length === 7) {
        r = (color & 16711680) >> 16;
        g = (color & 65280) >> 8;
        b = color & 255;
      }
    }
    return rgb(r, g, b);
  }
  function d3_rgb_hsl(r, g, b) {
    var min = Math.min(r /= 255, g /= 255, b /= 255), max = Math.max(r, g, b), d = max - min, h, s, l = (max + min) / 2;
    if (d) {
      s = l < .5 ? d / (max + min) : d / (2 - max - min);
      if (r == max) h = (g - b) / d + (g < b ? 6 : 0); else if (g == max) h = (b - r) / d + 2; else h = (r - g) / d + 4;
      h *= 60;
    } else {
      h = NaN;
      s = l > 0 && l < 1 ? 0 : h;
    }
    return new d3_hsl(h, s, l);
  }
  function d3_rgb_lab(r, g, b) {
    r = d3_rgb_xyz(r);
    g = d3_rgb_xyz(g);
    b = d3_rgb_xyz(b);
    var x = d3_xyz_lab((.4124564 * r + .3575761 * g + .1804375 * b) / d3_lab_X), y = d3_xyz_lab((.2126729 * r + .7151522 * g + .072175 * b) / d3_lab_Y), z = d3_xyz_lab((.0193339 * r + .119192 * g + .9503041 * b) / d3_lab_Z);
    return d3_lab(116 * y - 16, 500 * (x - y), 200 * (y - z));
  }
  function d3_rgb_xyz(r) {
    return (r /= 255) <= .04045 ? r / 12.92 : Math.pow((r + .055) / 1.055, 2.4);
  }
  function d3_rgb_parseNumber(c) {
    var f = parseFloat(c);
    return c.charAt(c.length - 1) === "%" ? Math.round(f * 2.55) : f;
  }
  var d3_rgb_names = d3.map({
    aliceblue: 15792383,
    antiquewhite: 16444375,
    aqua: 65535,
    aquamarine: 8388564,
    azure: 15794175,
    beige: 16119260,
    bisque: 16770244,
    black: 0,
    blanchedalmond: 16772045,
    blue: 255,
    blueviolet: 9055202,
    brown: 10824234,
    burlywood: 14596231,
    cadetblue: 6266528,
    chartreuse: 8388352,
    chocolate: 13789470,
    coral: 16744272,
    cornflowerblue: 6591981,
    cornsilk: 16775388,
    crimson: 14423100,
    cyan: 65535,
    darkblue: 139,
    darkcyan: 35723,
    darkgoldenrod: 12092939,
    darkgray: 11119017,
    darkgreen: 25600,
    darkgrey: 11119017,
    darkkhaki: 12433259,
    darkmagenta: 9109643,
    darkolivegreen: 5597999,
    darkorange: 16747520,
    darkorchid: 10040012,
    darkred: 9109504,
    darksalmon: 15308410,
    darkseagreen: 9419919,
    darkslateblue: 4734347,
    darkslategray: 3100495,
    darkslategrey: 3100495,
    darkturquoise: 52945,
    darkviolet: 9699539,
    deeppink: 16716947,
    deepskyblue: 49151,
    dimgray: 6908265,
    dimgrey: 6908265,
    dodgerblue: 2003199,
    firebrick: 11674146,
    floralwhite: 16775920,
    forestgreen: 2263842,
    fuchsia: 16711935,
    gainsboro: 14474460,
    ghostwhite: 16316671,
    gold: 16766720,
    goldenrod: 14329120,
    gray: 8421504,
    green: 32768,
    greenyellow: 11403055,
    grey: 8421504,
    honeydew: 15794160,
    hotpink: 16738740,
    indianred: 13458524,
    indigo: 4915330,
    ivory: 16777200,
    khaki: 15787660,
    lavender: 15132410,
    lavenderblush: 16773365,
    lawngreen: 8190976,
    lemonchiffon: 16775885,
    lightblue: 11393254,
    lightcoral: 15761536,
    lightcyan: 14745599,
    lightgoldenrodyellow: 16448210,
    lightgray: 13882323,
    lightgreen: 9498256,
    lightgrey: 13882323,
    lightpink: 16758465,
    lightsalmon: 16752762,
    lightseagreen: 2142890,
    lightskyblue: 8900346,
    lightslategray: 7833753,
    lightslategrey: 7833753,
    lightsteelblue: 11584734,
    lightyellow: 16777184,
    lime: 65280,
    limegreen: 3329330,
    linen: 16445670,
    magenta: 16711935,
    maroon: 8388608,
    mediumaquamarine: 6737322,
    mediumblue: 205,
    mediumorchid: 12211667,
    mediumpurple: 9662683,
    mediumseagreen: 3978097,
    mediumslateblue: 8087790,
    mediumspringgreen: 64154,
    mediumturquoise: 4772300,
    mediumvioletred: 13047173,
    midnightblue: 1644912,
    mintcream: 16121850,
    mistyrose: 16770273,
    moccasin: 16770229,
    navajowhite: 16768685,
    navy: 128,
    oldlace: 16643558,
    olive: 8421376,
    olivedrab: 7048739,
    orange: 16753920,
    orangered: 16729344,
    orchid: 14315734,
    palegoldenrod: 15657130,
    palegreen: 10025880,
    paleturquoise: 11529966,
    palevioletred: 14381203,
    papayawhip: 16773077,
    peachpuff: 16767673,
    peru: 13468991,
    pink: 16761035,
    plum: 14524637,
    powderblue: 11591910,
    purple: 8388736,
    rebeccapurple: 6697881,
    red: 16711680,
    rosybrown: 12357519,
    royalblue: 4286945,
    saddlebrown: 9127187,
    salmon: 16416882,
    sandybrown: 16032864,
    seagreen: 3050327,
    seashell: 16774638,
    sienna: 10506797,
    silver: 12632256,
    skyblue: 8900331,
    slateblue: 6970061,
    slategray: 7372944,
    slategrey: 7372944,
    snow: 16775930,
    springgreen: 65407,
    steelblue: 4620980,
    tan: 13808780,
    teal: 32896,
    thistle: 14204888,
    tomato: 16737095,
    turquoise: 4251856,
    violet: 15631086,
    wheat: 16113331,
    white: 16777215,
    whitesmoke: 16119285,
    yellow: 16776960,
    yellowgreen: 10145074
  });
  d3_rgb_names.forEach(function(key, value) {
    d3_rgb_names.set(key, d3_rgbNumber(value));
  });
  function d3_functor(v) {
    return typeof v === "function" ? v : function() {
      return v;
    };
  }
  d3.functor = d3_functor;
  d3.xhr = d3_xhrType(d3_identity);
  function d3_xhrType(response) {
    return function(url, mimeType, callback) {
      if (arguments.length === 2 && typeof mimeType === "function") callback = mimeType, 
      mimeType = null;
      return d3_xhr(url, mimeType, response, callback);
    };
  }
  function d3_xhr(url, mimeType, response, callback) {
    var xhr = {}, dispatch = d3.dispatch("beforesend", "progress", "load", "error"), headers = {}, request = new XMLHttpRequest(), responseType = null;
    if (this.XDomainRequest && !("withCredentials" in request) && /^(http(s)?:)?\/\//.test(url)) request = new XDomainRequest();
    "onload" in request ? request.onload = request.onerror = respond : request.onreadystatechange = function() {
      request.readyState > 3 && respond();
    };
    function respond() {
      var status = request.status, result;
      if (!status && d3_xhrHasResponse(request) || status >= 200 && status < 300 || status === 304) {
        try {
          result = response.call(xhr, request);
        } catch (e) {
          dispatch.error.call(xhr, e);
          return;
        }
        dispatch.load.call(xhr, result);
      } else {
        dispatch.error.call(xhr, request);
      }
    }
    request.onprogress = function(event) {
      var o = d3.event;
      d3.event = event;
      try {
        dispatch.progress.call(xhr, request);
      } finally {
        d3.event = o;
      }
    };
    xhr.header = function(name, value) {
      name = (name + "").toLowerCase();
      if (arguments.length < 2) return headers[name];
      if (value == null) delete headers[name]; else headers[name] = value + "";
      return xhr;
    };
    xhr.mimeType = function(value) {
      if (!arguments.length) return mimeType;
      mimeType = value == null ? null : value + "";
      return xhr;
    };
    xhr.responseType = function(value) {
      if (!arguments.length) return responseType;
      responseType = value;
      return xhr;
    };
    xhr.response = function(value) {
      response = value;
      return xhr;
    };
    [ "get", "post" ].forEach(function(method) {
      xhr[method] = function() {
        return xhr.send.apply(xhr, [ method ].concat(d3_array(arguments)));
      };
    });
    xhr.send = function(method, data, callback) {
      if (arguments.length === 2 && typeof data === "function") callback = data, data = null;
      request.open(method, url, true);
      if (mimeType != null && !("accept" in headers)) headers["accept"] = mimeType + ",*/*";
      if (request.setRequestHeader) for (var name in headers) request.setRequestHeader(name, headers[name]);
      if (mimeType != null && request.overrideMimeType) request.overrideMimeType(mimeType);
      if (responseType != null) request.responseType = responseType;
      if (callback != null) xhr.on("error", callback).on("load", function(request) {
        callback(null, request);
      });
      dispatch.beforesend.call(xhr, request);
      request.send(data == null ? null : data);
      return xhr;
    };
    xhr.abort = function() {
      request.abort();
      return xhr;
    };
    d3.rebind(xhr, dispatch, "on");
    return callback == null ? xhr : xhr.get(d3_xhr_fixCallback(callback));
  }
  function d3_xhr_fixCallback(callback) {
    return callback.length === 1 ? function(error, request) {
      callback(error == null ? request : null);
    } : callback;
  }
  function d3_xhrHasResponse(request) {
    var type = request.responseType;
    return type && type !== "text" ? request.response : request.responseText;
  }
  d3.dsv = function(delimiter, mimeType) {
    var reFormat = new RegExp('["' + delimiter + "\n]"), delimiterCode = delimiter.charCodeAt(0);
    function dsv(url, row, callback) {
      if (arguments.length < 3) callback = row, row = null;
      var xhr = d3_xhr(url, mimeType, row == null ? response : typedResponse(row), callback);
      xhr.row = function(_) {
        return arguments.length ? xhr.response((row = _) == null ? response : typedResponse(_)) : row;
      };
      return xhr;
    }
    function response(request) {
      return dsv.parse(request.responseText);
    }
    function typedResponse(f) {
      return function(request) {
        return dsv.parse(request.responseText, f);
      };
    }
    dsv.parse = function(text, f) {
      var o;
      return dsv.parseRows(text, function(row, i) {
        if (o) return o(row, i - 1);
        var a = new Function("d", "return {" + row.map(function(name, i) {
          return JSON.stringify(name) + ": d[" + i + "]";
        }).join(",") + "}");
        o = f ? function(row, i) {
          return f(a(row), i);
        } : a;
      });
    };
    dsv.parseRows = function(text, f) {
      var EOL = {}, EOF = {}, rows = [], N = text.length, I = 0, n = 0, t, eol;
      function token() {
        if (I >= N) return EOF;
        if (eol) return eol = false, EOL;
        var j = I;
        if (text.charCodeAt(j) === 34) {
          var i = j;
          while (i++ < N) {
            if (text.charCodeAt(i) === 34) {
              if (text.charCodeAt(i + 1) !== 34) break;
              ++i;
            }
          }
          I = i + 2;
          var c = text.charCodeAt(i + 1);
          if (c === 13) {
            eol = true;
            if (text.charCodeAt(i + 2) === 10) ++I;
          } else if (c === 10) {
            eol = true;
          }
          return text.slice(j + 1, i).replace(/""/g, '"');
        }
        while (I < N) {
          var c = text.charCodeAt(I++), k = 1;
          if (c === 10) eol = true; else if (c === 13) {
            eol = true;
            if (text.charCodeAt(I) === 10) ++I, ++k;
          } else if (c !== delimiterCode) continue;
          return text.slice(j, I - k);
        }
        return text.slice(j);
      }
      while ((t = token()) !== EOF) {
        var a = [];
        while (t !== EOL && t !== EOF) {
          a.push(t);
          t = token();
        }
        if (f && (a = f(a, n++)) == null) continue;
        rows.push(a);
      }
      return rows;
    };
    dsv.format = function(rows) {
      if (Array.isArray(rows[0])) return dsv.formatRows(rows);
      var fieldSet = new d3_Set(), fields = [];
      rows.forEach(function(row) {
        for (var field in row) {
          if (!fieldSet.has(field)) {
            fields.push(fieldSet.add(field));
          }
        }
      });
      return [ fields.map(formatValue).join(delimiter) ].concat(rows.map(function(row) {
        return fields.map(function(field) {
          return formatValue(row[field]);
        }).join(delimiter);
      })).join("\n");
    };
    dsv.formatRows = function(rows) {
      return rows.map(formatRow).join("\n");
    };
    function formatRow(row) {
      return row.map(formatValue).join(delimiter);
    }
    function formatValue(text) {
      return reFormat.test(text) ? '"' + text.replace(/\"/g, '""') + '"' : text;
    }
    return dsv;
  };
  d3.csv = d3.dsv(",", "text/csv");
  d3.tsv = d3.dsv("	", "text/tab-separated-values");
  var d3_timer_queueHead, d3_timer_queueTail, d3_timer_interval, d3_timer_timeout, d3_timer_frame = this[d3_vendorSymbol(this, "requestAnimationFrame")] || function(callback) {
    setTimeout(callback, 17);
  };
  d3.timer = function() {
    d3_timer.apply(this, arguments);
  };
  function d3_timer(callback, delay, then) {
    var n = arguments.length;
    if (n < 2) delay = 0;
    if (n < 3) then = Date.now();
    var time = then + delay, timer = {
      c: callback,
      t: time,
      n: null
    };
    if (d3_timer_queueTail) d3_timer_queueTail.n = timer; else d3_timer_queueHead = timer;
    d3_timer_queueTail = timer;
    if (!d3_timer_interval) {
      d3_timer_timeout = clearTimeout(d3_timer_timeout);
      d3_timer_interval = 1;
      d3_timer_frame(d3_timer_step);
    }
    return timer;
  }
  function d3_timer_step() {
    var now = d3_timer_mark(), delay = d3_timer_sweep() - now;
    if (delay > 24) {
      if (isFinite(delay)) {
        clearTimeout(d3_timer_timeout);
        d3_timer_timeout = setTimeout(d3_timer_step, delay);
      }
      d3_timer_interval = 0;
    } else {
      d3_timer_interval = 1;
      d3_timer_frame(d3_timer_step);
    }
  }
  d3.timer.flush = function() {
    d3_timer_mark();
    d3_timer_sweep();
  };
  function d3_timer_mark() {
    var now = Date.now(), timer = d3_timer_queueHead;
    while (timer) {
      if (now >= timer.t && timer.c(now - timer.t)) timer.c = null;
      timer = timer.n;
    }
    return now;
  }
  function d3_timer_sweep() {
    var t0, t1 = d3_timer_queueHead, time = Infinity;
    while (t1) {
      if (t1.c) {
        if (t1.t < time) time = t1.t;
        t1 = (t0 = t1).n;
      } else {
        t1 = t0 ? t0.n = t1.n : d3_timer_queueHead = t1.n;
      }
    }
    d3_timer_queueTail = t0;
    return time;
  }
  function d3_format_precision(x, p) {
    return p - (x ? Math.ceil(Math.log(x) / Math.LN10) : 1);
  }
  d3.round = function(x, n) {
    return n ? Math.round(x * (n = Math.pow(10, n))) / n : Math.round(x);
  };
  var d3_formatPrefixes = [ "y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y" ].map(d3_formatPrefix);
  d3.formatPrefix = function(value, precision) {
    var i = 0;
    if (value = +value) {
      if (value < 0) value *= -1;
      if (precision) value = d3.round(value, d3_format_precision(value, precision));
      i = 1 + Math.floor(1e-12 + Math.log(value) / Math.LN10);
      i = Math.max(-24, Math.min(24, Math.floor((i - 1) / 3) * 3));
    }
    return d3_formatPrefixes[8 + i / 3];
  };
  function d3_formatPrefix(d, i) {
    var k = Math.pow(10, abs(8 - i) * 3);
    return {
      scale: i > 8 ? function(d) {
        return d / k;
      } : function(d) {
        return d * k;
      },
      symbol: d
    };
  }
  function d3_locale_numberFormat(locale) {
    var locale_decimal = locale.decimal, locale_thousands = locale.thousands, locale_grouping = locale.grouping, locale_currency = locale.currency, formatGroup = locale_grouping && locale_thousands ? function(value, width) {
      var i = value.length, t = [], j = 0, g = locale_grouping[0], length = 0;
      while (i > 0 && g > 0) {
        if (length + g + 1 > width) g = Math.max(1, width - length);
        t.push(value.substring(i -= g, i + g));
        if ((length += g + 1) > width) break;
        g = locale_grouping[j = (j + 1) % locale_grouping.length];
      }
      return t.reverse().join(locale_thousands);
    } : d3_identity;
    return function(specifier) {
      var match = d3_format_re.exec(specifier), fill = match[1] || " ", align = match[2] || ">", sign = match[3] || "-", symbol = match[4] || "", zfill = match[5], width = +match[6], comma = match[7], precision = match[8], type = match[9], scale = 1, prefix = "", suffix = "", integer = false, exponent = true;
      if (precision) precision = +precision.substring(1);
      if (zfill || fill === "0" && align === "=") {
        zfill = fill = "0";
        align = "=";
      }
      switch (type) {
       case "n":
        comma = true;
        type = "g";
        break;

       case "%":
        scale = 100;
        suffix = "%";
        type = "f";
        break;

       case "p":
        scale = 100;
        suffix = "%";
        type = "r";
        break;

       case "b":
       case "o":
       case "x":
       case "X":
        if (symbol === "#") prefix = "0" + type.toLowerCase();

       case "c":
        exponent = false;

       case "d":
        integer = true;
        precision = 0;
        break;

       case "s":
        scale = -1;
        type = "r";
        break;
      }
      if (symbol === "$") prefix = locale_currency[0], suffix = locale_currency[1];
      if (type == "r" && !precision) type = "g";
      if (precision != null) {
        if (type == "g") precision = Math.max(1, Math.min(21, precision)); else if (type == "e" || type == "f") precision = Math.max(0, Math.min(20, precision));
      }
      type = d3_format_types.get(type) || d3_format_typeDefault;
      var zcomma = zfill && comma;
      return function(value) {
        var fullSuffix = suffix;
        if (integer && value % 1) return "";
        var negative = value < 0 || value === 0 && 1 / value < 0 ? (value = -value, "-") : sign === "-" ? "" : sign;
        if (scale < 0) {
          var unit = d3.formatPrefix(value, precision);
          value = unit.scale(value);
          fullSuffix = unit.symbol + suffix;
        } else {
          value *= scale;
        }
        value = type(value, precision);
        var i = value.lastIndexOf("."), before, after;
        if (i < 0) {
          var j = exponent ? value.lastIndexOf("e") : -1;
          if (j < 0) before = value, after = ""; else before = value.substring(0, j), after = value.substring(j);
        } else {
          before = value.substring(0, i);
          after = locale_decimal + value.substring(i + 1);
        }
        if (!zfill && comma) before = formatGroup(before, Infinity);
        var length = prefix.length + before.length + after.length + (zcomma ? 0 : negative.length), padding = length < width ? new Array(length = width - length + 1).join(fill) : "";
        if (zcomma) before = formatGroup(padding + before, padding.length ? width - after.length : Infinity);
        negative += prefix;
        value = before + after;
        return (align === "<" ? negative + value + padding : align === ">" ? padding + negative + value : align === "^" ? padding.substring(0, length >>= 1) + negative + value + padding.substring(length) : negative + (zcomma ? value : padding + value)) + fullSuffix;
      };
    };
  }
  var d3_format_re = /(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i;
  var d3_format_types = d3.map({
    b: function(x) {
      return x.toString(2);
    },
    c: function(x) {
      return String.fromCharCode(x);
    },
    o: function(x) {
      return x.toString(8);
    },
    x: function(x) {
      return x.toString(16);
    },
    X: function(x) {
      return x.toString(16).toUpperCase();
    },
    g: function(x, p) {
      return x.toPrecision(p);
    },
    e: function(x, p) {
      return x.toExponential(p);
    },
    f: function(x, p) {
      return x.toFixed(p);
    },
    r: function(x, p) {
      return (x = d3.round(x, d3_format_precision(x, p))).toFixed(Math.max(0, Math.min(20, d3_format_precision(x * (1 + 1e-15), p))));
    }
  });
  function d3_format_typeDefault(x) {
    return x + "";
  }
  var d3_time = d3.time = {}, d3_date = Date;
  function d3_date_utc() {
    this._ = new Date(arguments.length > 1 ? Date.UTC.apply(this, arguments) : arguments[0]);
  }
  d3_date_utc.prototype = {
    getDate: function() {
      return this._.getUTCDate();
    },
    getDay: function() {
      return this._.getUTCDay();
    },
    getFullYear: function() {
      return this._.getUTCFullYear();
    },
    getHours: function() {
      return this._.getUTCHours();
    },
    getMilliseconds: function() {
      return this._.getUTCMilliseconds();
    },
    getMinutes: function() {
      return this._.getUTCMinutes();
    },
    getMonth: function() {
      return this._.getUTCMonth();
    },
    getSeconds: function() {
      return this._.getUTCSeconds();
    },
    getTime: function() {
      return this._.getTime();
    },
    getTimezoneOffset: function() {
      return 0;
    },
    valueOf: function() {
      return this._.valueOf();
    },
    setDate: function() {
      d3_time_prototype.setUTCDate.apply(this._, arguments);
    },
    setDay: function() {
      d3_time_prototype.setUTCDay.apply(this._, arguments);
    },
    setFullYear: function() {
      d3_time_prototype.setUTCFullYear.apply(this._, arguments);
    },
    setHours: function() {
      d3_time_prototype.setUTCHours.apply(this._, arguments);
    },
    setMilliseconds: function() {
      d3_time_prototype.setUTCMilliseconds.apply(this._, arguments);
    },
    setMinutes: function() {
      d3_time_prototype.setUTCMinutes.apply(this._, arguments);
    },
    setMonth: function() {
      d3_time_prototype.setUTCMonth.apply(this._, arguments);
    },
    setSeconds: function() {
      d3_time_prototype.setUTCSeconds.apply(this._, arguments);
    },
    setTime: function() {
      d3_time_prototype.setTime.apply(this._, arguments);
    }
  };
  var d3_time_prototype = Date.prototype;
  function d3_time_interval(local, step, number) {
    function round(date) {
      var d0 = local(date), d1 = offset(d0, 1);
      return date - d0 < d1 - date ? d0 : d1;
    }
    function ceil(date) {
      step(date = local(new d3_date(date - 1)), 1);
      return date;
    }
    function offset(date, k) {
      step(date = new d3_date(+date), k);
      return date;
    }
    function range(t0, t1, dt) {
      var time = ceil(t0), times = [];
      if (dt > 1) {
        while (time < t1) {
          if (!(number(time) % dt)) times.push(new Date(+time));
          step(time, 1);
        }
      } else {
        while (time < t1) times.push(new Date(+time)), step(time, 1);
      }
      return times;
    }
    function range_utc(t0, t1, dt) {
      try {
        d3_date = d3_date_utc;
        var utc = new d3_date_utc();
        utc._ = t0;
        return range(utc, t1, dt);
      } finally {
        d3_date = Date;
      }
    }
    local.floor = local;
    local.round = round;
    local.ceil = ceil;
    local.offset = offset;
    local.range = range;
    var utc = local.utc = d3_time_interval_utc(local);
    utc.floor = utc;
    utc.round = d3_time_interval_utc(round);
    utc.ceil = d3_time_interval_utc(ceil);
    utc.offset = d3_time_interval_utc(offset);
    utc.range = range_utc;
    return local;
  }
  function d3_time_interval_utc(method) {
    return function(date, k) {
      try {
        d3_date = d3_date_utc;
        var utc = new d3_date_utc();
        utc._ = date;
        return method(utc, k)._;
      } finally {
        d3_date = Date;
      }
    };
  }
  d3_time.year = d3_time_interval(function(date) {
    date = d3_time.day(date);
    date.setMonth(0, 1);
    return date;
  }, function(date, offset) {
    date.setFullYear(date.getFullYear() + offset);
  }, function(date) {
    return date.getFullYear();
  });
  d3_time.years = d3_time.year.range;
  d3_time.years.utc = d3_time.year.utc.range;
  d3_time.day = d3_time_interval(function(date) {
    var day = new d3_date(2e3, 0);
    day.setFullYear(date.getFullYear(), date.getMonth(), date.getDate());
    return day;
  }, function(date, offset) {
    date.setDate(date.getDate() + offset);
  }, function(date) {
    return date.getDate() - 1;
  });
  d3_time.days = d3_time.day.range;
  d3_time.days.utc = d3_time.day.utc.range;
  d3_time.dayOfYear = function(date) {
    var year = d3_time.year(date);
    return Math.floor((date - year - (date.getTimezoneOffset() - year.getTimezoneOffset()) * 6e4) / 864e5);
  };
  [ "sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday" ].forEach(function(day, i) {
    i = 7 - i;
    var interval = d3_time[day] = d3_time_interval(function(date) {
      (date = d3_time.day(date)).setDate(date.getDate() - (date.getDay() + i) % 7);
      return date;
    }, function(date, offset) {
      date.setDate(date.getDate() + Math.floor(offset) * 7);
    }, function(date) {
      var day = d3_time.year(date).getDay();
      return Math.floor((d3_time.dayOfYear(date) + (day + i) % 7) / 7) - (day !== i);
    });
    d3_time[day + "s"] = interval.range;
    d3_time[day + "s"].utc = interval.utc.range;
    d3_time[day + "OfYear"] = function(date) {
      var day = d3_time.year(date).getDay();
      return Math.floor((d3_time.dayOfYear(date) + (day + i) % 7) / 7);
    };
  });
  d3_time.week = d3_time.sunday;
  d3_time.weeks = d3_time.sunday.range;
  d3_time.weeks.utc = d3_time.sunday.utc.range;
  d3_time.weekOfYear = d3_time.sundayOfYear;
  function d3_locale_timeFormat(locale) {
    var locale_dateTime = locale.dateTime, locale_date = locale.date, locale_time = locale.time, locale_periods = locale.periods, locale_days = locale.days, locale_shortDays = locale.shortDays, locale_months = locale.months, locale_shortMonths = locale.shortMonths;
    function d3_time_format(template) {
      var n = template.length;
      function format(date) {
        var string = [], i = -1, j = 0, c, p, f;
        while (++i < n) {
          if (template.charCodeAt(i) === 37) {
            string.push(template.slice(j, i));
            if ((p = d3_time_formatPads[c = template.charAt(++i)]) != null) c = template.charAt(++i);
            if (f = d3_time_formats[c]) c = f(date, p == null ? c === "e" ? " " : "0" : p);
            string.push(c);
            j = i + 1;
          }
        }
        string.push(template.slice(j, i));
        return string.join("");
      }
      format.parse = function(string) {
        var d = {
          y: 1900,
          m: 0,
          d: 1,
          H: 0,
          M: 0,
          S: 0,
          L: 0,
          Z: null
        }, i = d3_time_parse(d, template, string, 0);
        if (i != string.length) return null;
        if ("p" in d) d.H = d.H % 12 + d.p * 12;
        var localZ = d.Z != null && d3_date !== d3_date_utc, date = new (localZ ? d3_date_utc : d3_date)();
        if ("j" in d) date.setFullYear(d.y, 0, d.j); else if ("W" in d || "U" in d) {
          if (!("w" in d)) d.w = "W" in d ? 1 : 0;
          date.setFullYear(d.y, 0, 1);
          date.setFullYear(d.y, 0, "W" in d ? (d.w + 6) % 7 + d.W * 7 - (date.getDay() + 5) % 7 : d.w + d.U * 7 - (date.getDay() + 6) % 7);
        } else date.setFullYear(d.y, d.m, d.d);
        date.setHours(d.H + (d.Z / 100 | 0), d.M + d.Z % 100, d.S, d.L);
        return localZ ? date._ : date;
      };
      format.toString = function() {
        return template;
      };
      return format;
    }
    function d3_time_parse(date, template, string, j) {
      var c, p, t, i = 0, n = template.length, m = string.length;
      while (i < n) {
        if (j >= m) return -1;
        c = template.charCodeAt(i++);
        if (c === 37) {
          t = template.charAt(i++);
          p = d3_time_parsers[t in d3_time_formatPads ? template.charAt(i++) : t];
          if (!p || (j = p(date, string, j)) < 0) return -1;
        } else if (c != string.charCodeAt(j++)) {
          return -1;
        }
      }
      return j;
    }
    d3_time_format.utc = function(template) {
      var local = d3_time_format(template);
      function format(date) {
        try {
          d3_date = d3_date_utc;
          var utc = new d3_date();
          utc._ = date;
          return local(utc);
        } finally {
          d3_date = Date;
        }
      }
      format.parse = function(string) {
        try {
          d3_date = d3_date_utc;
          var date = local.parse(string);
          return date && date._;
        } finally {
          d3_date = Date;
        }
      };
      format.toString = local.toString;
      return format;
    };
    d3_time_format.multi = d3_time_format.utc.multi = d3_time_formatMulti;
    var d3_time_periodLookup = d3.map(), d3_time_dayRe = d3_time_formatRe(locale_days), d3_time_dayLookup = d3_time_formatLookup(locale_days), d3_time_dayAbbrevRe = d3_time_formatRe(locale_shortDays), d3_time_dayAbbrevLookup = d3_time_formatLookup(locale_shortDays), d3_time_monthRe = d3_time_formatRe(locale_months), d3_time_monthLookup = d3_time_formatLookup(locale_months), d3_time_monthAbbrevRe = d3_time_formatRe(locale_shortMonths), d3_time_monthAbbrevLookup = d3_time_formatLookup(locale_shortMonths);
    locale_periods.forEach(function(p, i) {
      d3_time_periodLookup.set(p.toLowerCase(), i);
    });
    var d3_time_formats = {
      a: function(d) {
        return locale_shortDays[d.getDay()];
      },
      A: function(d) {
        return locale_days[d.getDay()];
      },
      b: function(d) {
        return locale_shortMonths[d.getMonth()];
      },
      B: function(d) {
        return locale_months[d.getMonth()];
      },
      c: d3_time_format(locale_dateTime),
      d: function(d, p) {
        return d3_time_formatPad(d.getDate(), p, 2);
      },
      e: function(d, p) {
        return d3_time_formatPad(d.getDate(), p, 2);
      },
      H: function(d, p) {
        return d3_time_formatPad(d.getHours(), p, 2);
      },
      I: function(d, p) {
        return d3_time_formatPad(d.getHours() % 12 || 12, p, 2);
      },
      j: function(d, p) {
        return d3_time_formatPad(1 + d3_time.dayOfYear(d), p, 3);
      },
      L: function(d, p) {
        return d3_time_formatPad(d.getMilliseconds(), p, 3);
      },
      m: function(d, p) {
        return d3_time_formatPad(d.getMonth() + 1, p, 2);
      },
      M: function(d, p) {
        return d3_time_formatPad(d.getMinutes(), p, 2);
      },
      p: function(d) {
        return locale_periods[+(d.getHours() >= 12)];
      },
      S: function(d, p) {
        return d3_time_formatPad(d.getSeconds(), p, 2);
      },
      U: function(d, p) {
        return d3_time_formatPad(d3_time.sundayOfYear(d), p, 2);
      },
      w: function(d) {
        return d.getDay();
      },
      W: function(d, p) {
        return d3_time_formatPad(d3_time.mondayOfYear(d), p, 2);
      },
      x: d3_time_format(locale_date),
      X: d3_time_format(locale_time),
      y: function(d, p) {
        return d3_time_formatPad(d.getFullYear() % 100, p, 2);
      },
      Y: function(d, p) {
        return d3_time_formatPad(d.getFullYear() % 1e4, p, 4);
      },
      Z: d3_time_zone,
      "%": function() {
        return "%";
      }
    };
    var d3_time_parsers = {
      a: d3_time_parseWeekdayAbbrev,
      A: d3_time_parseWeekday,
      b: d3_time_parseMonthAbbrev,
      B: d3_time_parseMonth,
      c: d3_time_parseLocaleFull,
      d: d3_time_parseDay,
      e: d3_time_parseDay,
      H: d3_time_parseHour24,
      I: d3_time_parseHour24,
      j: d3_time_parseDayOfYear,
      L: d3_time_parseMilliseconds,
      m: d3_time_parseMonthNumber,
      M: d3_time_parseMinutes,
      p: d3_time_parseAmPm,
      S: d3_time_parseSeconds,
      U: d3_time_parseWeekNumberSunday,
      w: d3_time_parseWeekdayNumber,
      W: d3_time_parseWeekNumberMonday,
      x: d3_time_parseLocaleDate,
      X: d3_time_parseLocaleTime,
      y: d3_time_parseYear,
      Y: d3_time_parseFullYear,
      Z: d3_time_parseZone,
      "%": d3_time_parseLiteralPercent
    };
    function d3_time_parseWeekdayAbbrev(date, string, i) {
      d3_time_dayAbbrevRe.lastIndex = 0;
      var n = d3_time_dayAbbrevRe.exec(string.slice(i));
      return n ? (date.w = d3_time_dayAbbrevLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
    }
    function d3_time_parseWeekday(date, string, i) {
      d3_time_dayRe.lastIndex = 0;
      var n = d3_time_dayRe.exec(string.slice(i));
      return n ? (date.w = d3_time_dayLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
    }
    function d3_time_parseMonthAbbrev(date, string, i) {
      d3_time_monthAbbrevRe.lastIndex = 0;
      var n = d3_time_monthAbbrevRe.exec(string.slice(i));
      return n ? (date.m = d3_time_monthAbbrevLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
    }
    function d3_time_parseMonth(date, string, i) {
      d3_time_monthRe.lastIndex = 0;
      var n = d3_time_monthRe.exec(string.slice(i));
      return n ? (date.m = d3_time_monthLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
    }
    function d3_time_parseLocaleFull(date, string, i) {
      return d3_time_parse(date, d3_time_formats.c.toString(), string, i);
    }
    function d3_time_parseLocaleDate(date, string, i) {
      return d3_time_parse(date, d3_time_formats.x.toString(), string, i);
    }
    function d3_time_parseLocaleTime(date, string, i) {
      return d3_time_parse(date, d3_time_formats.X.toString(), string, i);
    }
    function d3_time_parseAmPm(date, string, i) {
      var n = d3_time_periodLookup.get(string.slice(i, i += 2).toLowerCase());
      return n == null ? -1 : (date.p = n, i);
    }
    return d3_time_format;
  }
  var d3_time_formatPads = {
    "-": "",
    _: " ",
    "0": "0"
  }, d3_time_numberRe = /^\s*\d+/, d3_time_percentRe = /^%/;
  function d3_time_formatPad(value, fill, width) {
    var sign = value < 0 ? "-" : "", string = (sign ? -value : value) + "", length = string.length;
    return sign + (length < width ? new Array(width - length + 1).join(fill) + string : string);
  }
  function d3_time_formatRe(names) {
    return new RegExp("^(?:" + names.map(d3.requote).join("|") + ")", "i");
  }
  function d3_time_formatLookup(names) {
    var map = new d3_Map(), i = -1, n = names.length;
    while (++i < n) map.set(names[i].toLowerCase(), i);
    return map;
  }
  function d3_time_parseWeekdayNumber(date, string, i) {
    d3_time_numberRe.lastIndex = 0;
    var n = d3_time_numberRe.exec(string.slice(i, i + 1));
    return n ? (date.w = +n[0], i + n[0].length) : -1;
  }
  function d3_time_parseWeekNumberSunday(date, string, i) {
    d3_time_numberRe.lastIndex = 0;
    var n = d3_time_numberRe.exec(string.slice(i));
    return n ? (date.U = +n[0], i + n[0].length) : -1;
  }
  function d3_time_parseWeekNumberMonday(date, string, i) {
    d3_time_numberRe.lastIndex = 0;
    var n = d3_time_numberRe.exec(string.slice(i));
    return n ? (date.W = +n[0], i + n[0].length) : -1;
  }
  function d3_time_parseFullYear(date, string, i) {
    d3_time_numberRe.lastIndex = 0;
    var n = d3_time_numberRe.exec(string.slice(i, i + 4));
    return n ? (date.y = +n[0], i + n[0].length) : -1;
  }
  function d3_time_parseYear(date, string, i) {
    d3_time_numberRe.lastIndex = 0;
    var n = d3_time_numberRe.exec(string.slice(i, i + 2));
    return n ? (date.y = d3_time_expandYear(+n[0]), i + n[0].length) : -1;
  }
  function d3_time_parseZone(date, string, i) {
    return /^[+-]\d{4}$/.test(string = string.slice(i, i + 5)) ? (date.Z = -string, 
    i + 5) : -1;
  }
  function d3_time_expandYear(d) {
    return d + (d > 68 ? 1900 : 2e3);
  }
  function d3_time_parseMonthNumber(date, string, i) {
    d3_time_numberRe.lastIndex = 0;
    var n = d3_time_numberRe.exec(string.slice(i, i + 2));
    return n ? (date.m = n[0] - 1, i + n[0].length) : -1;
  }
  function d3_time_parseDay(date, string, i) {
    d3_time_numberRe.lastIndex = 0;
    var n = d3_time_numberRe.exec(string.slice(i, i + 2));
    return n ? (date.d = +n[0], i + n[0].length) : -1;
  }
  function d3_time_parseDayOfYear(date, string, i) {
    d3_time_numberRe.lastIndex = 0;
    var n = d3_time_numberRe.exec(string.slice(i, i + 3));
    return n ? (date.j = +n[0], i + n[0].length) : -1;
  }
  function d3_time_parseHour24(date, string, i) {
    d3_time_numberRe.lastIndex = 0;
    var n = d3_time_numberRe.exec(string.slice(i, i + 2));
    return n ? (date.H = +n[0], i + n[0].length) : -1;
  }
  function d3_time_parseMinutes(date, string, i) {
    d3_time_numberRe.lastIndex = 0;
    var n = d3_time_numberRe.exec(string.slice(i, i + 2));
    return n ? (date.M = +n[0], i + n[0].length) : -1;
  }
  function d3_time_parseSeconds(date, string, i) {
    d3_time_numberRe.lastIndex = 0;
    var n = d3_time_numberRe.exec(string.slice(i, i + 2));
    return n ? (date.S = +n[0], i + n[0].length) : -1;
  }
  function d3_time_parseMilliseconds(date, string, i) {
    d3_time_numberRe.lastIndex = 0;
    var n = d3_time_numberRe.exec(string.slice(i, i + 3));
    return n ? (date.L = +n[0], i + n[0].length) : -1;
  }
  function d3_time_zone(d) {
    var z = d.getTimezoneOffset(), zs = z > 0 ? "-" : "+", zh = abs(z) / 60 | 0, zm = abs(z) % 60;
    return zs + d3_time_formatPad(zh, "0", 2) + d3_time_formatPad(zm, "0", 2);
  }
  function d3_time_parseLiteralPercent(date, string, i) {
    d3_time_percentRe.lastIndex = 0;
    var n = d3_time_percentRe.exec(string.slice(i, i + 1));
    return n ? i + n[0].length : -1;
  }
  function d3_time_formatMulti(formats) {
    var n = formats.length, i = -1;
    while (++i < n) formats[i][0] = this(formats[i][0]);
    return function(date) {
      var i = 0, f = formats[i];
      while (!f[1](date)) f = formats[++i];
      return f[0](date);
    };
  }
  d3.locale = function(locale) {
    return {
      numberFormat: d3_locale_numberFormat(locale),
      timeFormat: d3_locale_timeFormat(locale)
    };
  };
  var d3_locale_enUS = d3.locale({
    decimal: ".",
    thousands: ",",
    grouping: [ 3 ],
    currency: [ "$", "" ],
    dateTime: "%a %b %e %X %Y",
    date: "%m/%d/%Y",
    time: "%H:%M:%S",
    periods: [ "AM", "PM" ],
    days: [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ],
    shortDays: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ],
    months: [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ],
    shortMonths: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ]
  });
  d3.format = d3_locale_enUS.numberFormat;
  d3.geo = {};
  function d3_adder() {}
  d3_adder.prototype = {
    s: 0,
    t: 0,
    add: function(y) {
      d3_adderSum(y, this.t, d3_adderTemp);
      d3_adderSum(d3_adderTemp.s, this.s, this);
      if (this.s) this.t += d3_adderTemp.t; else this.s = d3_adderTemp.t;
    },
    reset: function() {
      this.s = this.t = 0;
    },
    valueOf: function() {
      return this.s;
    }
  };
  var d3_adderTemp = new d3_adder();
  function d3_adderSum(a, b, o) {
    var x = o.s = a + b, bv = x - a, av = x - bv;
    o.t = a - av + (b - bv);
  }
  d3.geo.stream = function(object, listener) {
    if (object && d3_geo_streamObjectType.hasOwnProperty(object.type)) {
      d3_geo_streamObjectType[object.type](object, listener);
    } else {
      d3_geo_streamGeometry(object, listener);
    }
  };
  function d3_geo_streamGeometry(geometry, listener) {
    if (geometry && d3_geo_streamGeometryType.hasOwnProperty(geometry.type)) {
      d3_geo_streamGeometryType[geometry.type](geometry, listener);
    }
  }
  var d3_geo_streamObjectType = {
    Feature: function(feature, listener) {
      d3_geo_streamGeometry(feature.geometry, listener);
    },
    FeatureCollection: function(object, listener) {
      var features = object.features, i = -1, n = features.length;
      while (++i < n) d3_geo_streamGeometry(features[i].geometry, listener);
    }
  };
  var d3_geo_streamGeometryType = {
    Sphere: function(object, listener) {
      listener.sphere();
    },
    Point: function(object, listener) {
      object = object.coordinates;
      listener.point(object[0], object[1], object[2]);
    },
    MultiPoint: function(object, listener) {
      var coordinates = object.coordinates, i = -1, n = coordinates.length;
      while (++i < n) object = coordinates[i], listener.point(object[0], object[1], object[2]);
    },
    LineString: function(object, listener) {
      d3_geo_streamLine(object.coordinates, listener, 0);
    },
    MultiLineString: function(object, listener) {
      var coordinates = object.coordinates, i = -1, n = coordinates.length;
      while (++i < n) d3_geo_streamLine(coordinates[i], listener, 0);
    },
    Polygon: function(object, listener) {
      d3_geo_streamPolygon(object.coordinates, listener);
    },
    MultiPolygon: function(object, listener) {
      var coordinates = object.coordinates, i = -1, n = coordinates.length;
      while (++i < n) d3_geo_streamPolygon(coordinates[i], listener);
    },
    GeometryCollection: function(object, listener) {
      var geometries = object.geometries, i = -1, n = geometries.length;
      while (++i < n) d3_geo_streamGeometry(geometries[i], listener);
    }
  };
  function d3_geo_streamLine(coordinates, listener, closed) {
    var i = -1, n = coordinates.length - closed, coordinate;
    listener.lineStart();
    while (++i < n) coordinate = coordinates[i], listener.point(coordinate[0], coordinate[1], coordinate[2]);
    listener.lineEnd();
  }
  function d3_geo_streamPolygon(coordinates, listener) {
    var i = -1, n = coordinates.length;
    listener.polygonStart();
    while (++i < n) d3_geo_streamLine(coordinates[i], listener, 1);
    listener.polygonEnd();
  }
  d3.geo.area = function(object) {
    d3_geo_areaSum = 0;
    d3.geo.stream(object, d3_geo_area);
    return d3_geo_areaSum;
  };
  var d3_geo_areaSum, d3_geo_areaRingSum = new d3_adder();
  var d3_geo_area = {
    sphere: function() {
      d3_geo_areaSum += 4 * π;
    },
    point: d3_noop,
    lineStart: d3_noop,
    lineEnd: d3_noop,
    polygonStart: function() {
      d3_geo_areaRingSum.reset();
      d3_geo_area.lineStart = d3_geo_areaRingStart;
    },
    polygonEnd: function() {
      var area = 2 * d3_geo_areaRingSum;
      d3_geo_areaSum += area < 0 ? 4 * π + area : area;
      d3_geo_area.lineStart = d3_geo_area.lineEnd = d3_geo_area.point = d3_noop;
    }
  };
  function d3_geo_areaRingStart() {
    var λ00, φ00, λ0, cosφ0, sinφ0;
    d3_geo_area.point = function(λ, φ) {
      d3_geo_area.point = nextPoint;
      λ0 = (λ00 = λ) * d3_radians, cosφ0 = Math.cos(φ = (φ00 = φ) * d3_radians / 2 + π / 4), 
      sinφ0 = Math.sin(φ);
    };
    function nextPoint(λ, φ) {
      λ *= d3_radians;
      φ = φ * d3_radians / 2 + π / 4;
      var dλ = λ - λ0, sdλ = dλ >= 0 ? 1 : -1, adλ = sdλ * dλ, cosφ = Math.cos(φ), sinφ = Math.sin(φ), k = sinφ0 * sinφ, u = cosφ0 * cosφ + k * Math.cos(adλ), v = k * sdλ * Math.sin(adλ);
      d3_geo_areaRingSum.add(Math.atan2(v, u));
      λ0 = λ, cosφ0 = cosφ, sinφ0 = sinφ;
    }
    d3_geo_area.lineEnd = function() {
      nextPoint(λ00, φ00);
    };
  }
  function d3_geo_cartesian(spherical) {
    var λ = spherical[0], φ = spherical[1], cosφ = Math.cos(φ);
    return [ cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ) ];
  }
  function d3_geo_cartesianDot(a, b) {
    return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
  }
  function d3_geo_cartesianCross(a, b) {
    return [ a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0] ];
  }
  function d3_geo_cartesianAdd(a, b) {
    a[0] += b[0];
    a[1] += b[1];
    a[2] += b[2];
  }
  function d3_geo_cartesianScale(vector, k) {
    return [ vector[0] * k, vector[1] * k, vector[2] * k ];
  }
  function d3_geo_cartesianNormalize(d) {
    var l = Math.sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]);
    d[0] /= l;
    d[1] /= l;
    d[2] /= l;
  }
  function d3_geo_spherical(cartesian) {
    return [ Math.atan2(cartesian[1], cartesian[0]), d3_asin(cartesian[2]) ];
  }
  function d3_geo_sphericalEqual(a, b) {
    return abs(a[0] - b[0]) < ε && abs(a[1] - b[1]) < ε;
  }
  d3.geo.bounds = function() {
    var λ0, φ0, λ1, φ1, λ_, λ__, φ__, p0, dλSum, ranges, range;
    var bound = {
      point: point,
      lineStart: lineStart,
      lineEnd: lineEnd,
      polygonStart: function() {
        bound.point = ringPoint;
        bound.lineStart = ringStart;
        bound.lineEnd = ringEnd;
        dλSum = 0;
        d3_geo_area.polygonStart();
      },
      polygonEnd: function() {
        d3_geo_area.polygonEnd();
        bound.point = point;
        bound.lineStart = lineStart;
        bound.lineEnd = lineEnd;
        if (d3_geo_areaRingSum < 0) λ0 = -(λ1 = 180), φ0 = -(φ1 = 90); else if (dλSum > ε) φ1 = 90; else if (dλSum < -ε) φ0 = -90;
        range[0] = λ0, range[1] = λ1;
      }
    };
    function point(λ, φ) {
      ranges.push(range = [ λ0 = λ, λ1 = λ ]);
      if (φ < φ0) φ0 = φ;
      if (φ > φ1) φ1 = φ;
    }
    function linePoint(λ, φ) {
      var p = d3_geo_cartesian([ λ * d3_radians, φ * d3_radians ]);
      if (p0) {
        var normal = d3_geo_cartesianCross(p0, p), equatorial = [ normal[1], -normal[0], 0 ], inflection = d3_geo_cartesianCross(equatorial, normal);
        d3_geo_cartesianNormalize(inflection);
        inflection = d3_geo_spherical(inflection);
        var dλ = λ - λ_, s = dλ > 0 ? 1 : -1, λi = inflection[0] * d3_degrees * s, antimeridian = abs(dλ) > 180;
        if (antimeridian ^ (s * λ_ < λi && λi < s * λ)) {
          var φi = inflection[1] * d3_degrees;
          if (φi > φ1) φ1 = φi;
        } else if (λi = (λi + 360) % 360 - 180, antimeridian ^ (s * λ_ < λi && λi < s * λ)) {
          var φi = -inflection[1] * d3_degrees;
          if (φi < φ0) φ0 = φi;
        } else {
          if (φ < φ0) φ0 = φ;
          if (φ > φ1) φ1 = φ;
        }
        if (antimeridian) {
          if (λ < λ_) {
            if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ;
          } else {
            if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ;
          }
        } else {
          if (λ1 >= λ0) {
            if (λ < λ0) λ0 = λ;
            if (λ > λ1) λ1 = λ;
          } else {
            if (λ > λ_) {
              if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ;
            } else {
              if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ;
            }
          }
        }
      } else {
        point(λ, φ);
      }
      p0 = p, λ_ = λ;
    }
    function lineStart() {
      bound.point = linePoint;
    }
    function lineEnd() {
      range[0] = λ0, range[1] = λ1;
      bound.point = point;
      p0 = null;
    }
    function ringPoint(λ, φ) {
      if (p0) {
        var dλ = λ - λ_;
        dλSum += abs(dλ) > 180 ? dλ + (dλ > 0 ? 360 : -360) : dλ;
      } else λ__ = λ, φ__ = φ;
      d3_geo_area.point(λ, φ);
      linePoint(λ, φ);
    }
    function ringStart() {
      d3_geo_area.lineStart();
    }
    function ringEnd() {
      ringPoint(λ__, φ__);
      d3_geo_area.lineEnd();
      if (abs(dλSum) > ε) λ0 = -(λ1 = 180);
      range[0] = λ0, range[1] = λ1;
      p0 = null;
    }
    function angle(λ0, λ1) {
      return (λ1 -= λ0) < 0 ? λ1 + 360 : λ1;
    }
    function compareRanges(a, b) {
      return a[0] - b[0];
    }
    function withinRange(x, range) {
      return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x;
    }
    return function(feature) {
      φ1 = λ1 = -(λ0 = φ0 = Infinity);
      ranges = [];
      d3.geo.stream(feature, bound);
      var n = ranges.length;
      if (n) {
        ranges.sort(compareRanges);
        for (var i = 1, a = ranges[0], b, merged = [ a ]; i < n; ++i) {
          b = ranges[i];
          if (withinRange(b[0], a) || withinRange(b[1], a)) {
            if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1];
            if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0];
          } else {
            merged.push(a = b);
          }
        }
        var best = -Infinity, dλ;
        for (var n = merged.length - 1, i = 0, a = merged[n], b; i <= n; a = b, ++i) {
          b = merged[i];
          if ((dλ = angle(a[1], b[0])) > best) best = dλ, λ0 = b[0], λ1 = a[1];
        }
      }
      ranges = range = null;
      return λ0 === Infinity || φ0 === Infinity ? [ [ NaN, NaN ], [ NaN, NaN ] ] : [ [ λ0, φ0 ], [ λ1, φ1 ] ];
    };
  }();
  d3.geo.centroid = function(object) {
    d3_geo_centroidW0 = d3_geo_centroidW1 = d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 = d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 = d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0;
    d3.geo.stream(object, d3_geo_centroid);
    var x = d3_geo_centroidX2, y = d3_geo_centroidY2, z = d3_geo_centroidZ2, m = x * x + y * y + z * z;
    if (m < ε2) {
      x = d3_geo_centroidX1, y = d3_geo_centroidY1, z = d3_geo_centroidZ1;
      if (d3_geo_centroidW1 < ε) x = d3_geo_centroidX0, y = d3_geo_centroidY0, z = d3_geo_centroidZ0;
      m = x * x + y * y + z * z;
      if (m < ε2) return [ NaN, NaN ];
    }
    return [ Math.atan2(y, x) * d3_degrees, d3_asin(z / Math.sqrt(m)) * d3_degrees ];
  };
  var d3_geo_centroidW0, d3_geo_centroidW1, d3_geo_centroidX0, d3_geo_centroidY0, d3_geo_centroidZ0, d3_geo_centroidX1, d3_geo_centroidY1, d3_geo_centroidZ1, d3_geo_centroidX2, d3_geo_centroidY2, d3_geo_centroidZ2;
  var d3_geo_centroid = {
    sphere: d3_noop,
    point: d3_geo_centroidPoint,
    lineStart: d3_geo_centroidLineStart,
    lineEnd: d3_geo_centroidLineEnd,
    polygonStart: function() {
      d3_geo_centroid.lineStart = d3_geo_centroidRingStart;
    },
    polygonEnd: function() {
      d3_geo_centroid.lineStart = d3_geo_centroidLineStart;
    }
  };
  function d3_geo_centroidPoint(λ, φ) {
    λ *= d3_radians;
    var cosφ = Math.cos(φ *= d3_radians);
    d3_geo_centroidPointXYZ(cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ));
  }
  function d3_geo_centroidPointXYZ(x, y, z) {
    ++d3_geo_centroidW0;
    d3_geo_centroidX0 += (x - d3_geo_centroidX0) / d3_geo_centroidW0;
    d3_geo_centroidY0 += (y - d3_geo_centroidY0) / d3_geo_centroidW0;
    d3_geo_centroidZ0 += (z - d3_geo_centroidZ0) / d3_geo_centroidW0;
  }
  function d3_geo_centroidLineStart() {
    var x0, y0, z0;
    d3_geo_centroid.point = function(λ, φ) {
      λ *= d3_radians;
      var cosφ = Math.cos(φ *= d3_radians);
      x0 = cosφ * Math.cos(λ);
      y0 = cosφ * Math.sin(λ);
      z0 = Math.sin(φ);
      d3_geo_centroid.point = nextPoint;
      d3_geo_centroidPointXYZ(x0, y0, z0);
    };
    function nextPoint(λ, φ) {
      λ *= d3_radians;
      var cosφ = Math.cos(φ *= d3_radians), x = cosφ * Math.cos(λ), y = cosφ * Math.sin(λ), z = Math.sin(φ), w = Math.atan2(Math.sqrt((w = y0 * z - z0 * y) * w + (w = z0 * x - x0 * z) * w + (w = x0 * y - y0 * x) * w), x0 * x + y0 * y + z0 * z);
      d3_geo_centroidW1 += w;
      d3_geo_centroidX1 += w * (x0 + (x0 = x));
      d3_geo_centroidY1 += w * (y0 + (y0 = y));
      d3_geo_centroidZ1 += w * (z0 + (z0 = z));
      d3_geo_centroidPointXYZ(x0, y0, z0);
    }
  }
  function d3_geo_centroidLineEnd() {
    d3_geo_centroid.point = d3_geo_centroidPoint;
  }
  function d3_geo_centroidRingStart() {
    var λ00, φ00, x0, y0, z0;
    d3_geo_centroid.point = function(λ, φ) {
      λ00 = λ, φ00 = φ;
      d3_geo_centroid.point = nextPoint;
      λ *= d3_radians;
      var cosφ = Math.cos(φ *= d3_radians);
      x0 = cosφ * Math.cos(λ);
      y0 = cosφ * Math.sin(λ);
      z0 = Math.sin(φ);
      d3_geo_centroidPointXYZ(x0, y0, z0);
    };
    d3_geo_centroid.lineEnd = function() {
      nextPoint(λ00, φ00);
      d3_geo_centroid.lineEnd = d3_geo_centroidLineEnd;
      d3_geo_centroid.point = d3_geo_centroidPoint;
    };
    function nextPoint(λ, φ) {
      λ *= d3_radians;
      var cosφ = Math.cos(φ *= d3_radians), x = cosφ * Math.cos(λ), y = cosφ * Math.sin(λ), z = Math.sin(φ), cx = y0 * z - z0 * y, cy = z0 * x - x0 * z, cz = x0 * y - y0 * x, m = Math.sqrt(cx * cx + cy * cy + cz * cz), u = x0 * x + y0 * y + z0 * z, v = m && -d3_acos(u) / m, w = Math.atan2(m, u);
      d3_geo_centroidX2 += v * cx;
      d3_geo_centroidY2 += v * cy;
      d3_geo_centroidZ2 += v * cz;
      d3_geo_centroidW1 += w;
      d3_geo_centroidX1 += w * (x0 + (x0 = x));
      d3_geo_centroidY1 += w * (y0 + (y0 = y));
      d3_geo_centroidZ1 += w * (z0 + (z0 = z));
      d3_geo_centroidPointXYZ(x0, y0, z0);
    }
  }
  function d3_geo_compose(a, b) {
    function compose(x, y) {
      return x = a(x, y), b(x[0], x[1]);
    }
    if (a.invert && b.invert) compose.invert = function(x, y) {
      return x = b.invert(x, y), x && a.invert(x[0], x[1]);
    };
    return compose;
  }
  function d3_true() {
    return true;
  }
  function d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener) {
    var subject = [], clip = [];
    segments.forEach(function(segment) {
      if ((n = segment.length - 1) <= 0) return;
      var n, p0 = segment[0], p1 = segment[n];
      if (d3_geo_sphericalEqual(p0, p1)) {
        listener.lineStart();
        for (var i = 0; i < n; ++i) listener.point((p0 = segment[i])[0], p0[1]);
        listener.lineEnd();
        return;
      }
      var a = new d3_geo_clipPolygonIntersection(p0, segment, null, true), b = new d3_geo_clipPolygonIntersection(p0, null, a, false);
      a.o = b;
      subject.push(a);
      clip.push(b);
      a = new d3_geo_clipPolygonIntersection(p1, segment, null, false);
      b = new d3_geo_clipPolygonIntersection(p1, null, a, true);
      a.o = b;
      subject.push(a);
      clip.push(b);
    });
    clip.sort(compare);
    d3_geo_clipPolygonLinkCircular(subject);
    d3_geo_clipPolygonLinkCircular(clip);
    if (!subject.length) return;
    for (var i = 0, entry = clipStartInside, n = clip.length; i < n; ++i) {
      clip[i].e = entry = !entry;
    }
    var start = subject[0], points, point;
    while (1) {
      var current = start, isSubject = true;
      while (current.v) if ((current = current.n) === start) return;
      points = current.z;
      listener.lineStart();
      do {
        current.v = current.o.v = true;
        if (current.e) {
          if (isSubject) {
            for (var i = 0, n = points.length; i < n; ++i) listener.point((point = points[i])[0], point[1]);
          } else {
            interpolate(current.x, current.n.x, 1, listener);
          }
          current = current.n;
        } else {
          if (isSubject) {
            points = current.p.z;
            for (var i = points.length - 1; i >= 0; --i) listener.point((point = points[i])[0], point[1]);
          } else {
            interpolate(current.x, current.p.x, -1, listener);
          }
          current = current.p;
        }
        current = current.o;
        points = current.z;
        isSubject = !isSubject;
      } while (!current.v);
      listener.lineEnd();
    }
  }
  function d3_geo_clipPolygonLinkCircular(array) {
    if (!(n = array.length)) return;
    var n, i = 0, a = array[0], b;
    while (++i < n) {
      a.n = b = array[i];
      b.p = a;
      a = b;
    }
    a.n = b = array[0];
    b.p = a;
  }
  function d3_geo_clipPolygonIntersection(point, points, other, entry) {
    this.x = point;
    this.z = points;
    this.o = other;
    this.e = entry;
    this.v = false;
    this.n = this.p = null;
  }
  function d3_geo_clip(pointVisible, clipLine, interpolate, clipStart) {
    return function(rotate, listener) {
      var line = clipLine(listener), rotatedClipStart = rotate.invert(clipStart[0], clipStart[1]);
      var clip = {
        point: point,
        lineStart: lineStart,
        lineEnd: lineEnd,
        polygonStart: function() {
          clip.point = pointRing;
          clip.lineStart = ringStart;
          clip.lineEnd = ringEnd;
          segments = [];
          polygon = [];
        },
        polygonEnd: function() {
          clip.point = point;
          clip.lineStart = lineStart;
          clip.lineEnd = lineEnd;
          segments = d3.merge(segments);
          var clipStartInside = d3_geo_pointInPolygon(rotatedClipStart, polygon);
          if (segments.length) {
            if (!polygonStarted) listener.polygonStart(), polygonStarted = true;
            d3_geo_clipPolygon(segments, d3_geo_clipSort, clipStartInside, interpolate, listener);
          } else if (clipStartInside) {
            if (!polygonStarted) listener.polygonStart(), polygonStarted = true;
            listener.lineStart();
            interpolate(null, null, 1, listener);
            listener.lineEnd();
          }
          if (polygonStarted) listener.polygonEnd(), polygonStarted = false;
          segments = polygon = null;
        },
        sphere: function() {
          listener.polygonStart();
          listener.lineStart();
          interpolate(null, null, 1, listener);
          listener.lineEnd();
          listener.polygonEnd();
        }
      };
      function point(λ, φ) {
        var point = rotate(λ, φ);
        if (pointVisible(λ = point[0], φ = point[1])) listener.point(λ, φ);
      }
      function pointLine(λ, φ) {
        var point = rotate(λ, φ);
        line.point(point[0], point[1]);
      }
      function lineStart() {
        clip.point = pointLine;
        line.lineStart();
      }
      function lineEnd() {
        clip.point = point;
        line.lineEnd();
      }
      var segments;
      var buffer = d3_geo_clipBufferListener(), ringListener = clipLine(buffer), polygonStarted = false, polygon, ring;
      function pointRing(λ, φ) {
        ring.push([ λ, φ ]);
        var point = rotate(λ, φ);
        ringListener.point(point[0], point[1]);
      }
      function ringStart() {
        ringListener.lineStart();
        ring = [];
      }
      function ringEnd() {
        pointRing(ring[0][0], ring[0][1]);
        ringListener.lineEnd();
        var clean = ringListener.clean(), ringSegments = buffer.buffer(), segment, n = ringSegments.length;
        ring.pop();
        polygon.push(ring);
        ring = null;
        if (!n) return;
        if (clean & 1) {
          segment = ringSegments[0];
          var n = segment.length - 1, i = -1, point;
          if (n > 0) {
            if (!polygonStarted) listener.polygonStart(), polygonStarted = true;
            listener.lineStart();
            while (++i < n) listener.point((point = segment[i])[0], point[1]);
            listener.lineEnd();
          }
          return;
        }
        if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift()));
        segments.push(ringSegments.filter(d3_geo_clipSegmentLength1));
      }
      return clip;
    };
  }
  function d3_geo_clipSegmentLength1(segment) {
    return segment.length > 1;
  }
  function d3_geo_clipBufferListener() {
    var lines = [], line;
    return {
      lineStart: function() {
        lines.push(line = []);
      },
      point: function(λ, φ) {
        line.push([ λ, φ ]);
      },
      lineEnd: d3_noop,
      buffer: function() {
        var buffer = lines;
        lines = [];
        line = null;
        return buffer;
      },
      rejoin: function() {
        if (lines.length > 1) lines.push(lines.pop().concat(lines.shift()));
      }
    };
  }
  function d3_geo_clipSort(a, b) {
    return ((a = a.x)[0] < 0 ? a[1] - halfπ - ε : halfπ - a[1]) - ((b = b.x)[0] < 0 ? b[1] - halfπ - ε : halfπ - b[1]);
  }
  var d3_geo_clipAntimeridian = d3_geo_clip(d3_true, d3_geo_clipAntimeridianLine, d3_geo_clipAntimeridianInterpolate, [ -π, -π / 2 ]);
  function d3_geo_clipAntimeridianLine(listener) {
    var λ0 = NaN, φ0 = NaN, sλ0 = NaN, clean;
    return {
      lineStart: function() {
        listener.lineStart();
        clean = 1;
      },
      point: function(λ1, φ1) {
        var sλ1 = λ1 > 0 ? π : -π, dλ = abs(λ1 - λ0);
        if (abs(dλ - π) < ε) {
          listener.point(λ0, φ0 = (φ0 + φ1) / 2 > 0 ? halfπ : -halfπ);
          listener.point(sλ0, φ0);
          listener.lineEnd();
          listener.lineStart();
          listener.point(sλ1, φ0);
          listener.point(λ1, φ0);
          clean = 0;
        } else if (sλ0 !== sλ1 && dλ >= π) {
          if (abs(λ0 - sλ0) < ε) λ0 -= sλ0 * ε;
          if (abs(λ1 - sλ1) < ε) λ1 -= sλ1 * ε;
          φ0 = d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1);
          listener.point(sλ0, φ0);
          listener.lineEnd();
          listener.lineStart();
          listener.point(sλ1, φ0);
          clean = 0;
        }
        listener.point(λ0 = λ1, φ0 = φ1);
        sλ0 = sλ1;
      },
      lineEnd: function() {
        listener.lineEnd();
        λ0 = φ0 = NaN;
      },
      clean: function() {
        return 2 - clean;
      }
    };
  }
  function d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1) {
    var cosφ0, cosφ1, sinλ0_λ1 = Math.sin(λ0 - λ1);
    return abs(sinλ0_λ1) > ε ? Math.atan((Math.sin(φ0) * (cosφ1 = Math.cos(φ1)) * Math.sin(λ1) - Math.sin(φ1) * (cosφ0 = Math.cos(φ0)) * Math.sin(λ0)) / (cosφ0 * cosφ1 * sinλ0_λ1)) : (φ0 + φ1) / 2;
  }
  function d3_geo_clipAntimeridianInterpolate(from, to, direction, listener) {
    var φ;
    if (from == null) {
      φ = direction * halfπ;
      listener.point(-π, φ);
      listener.point(0, φ);
      listener.point(π, φ);
      listener.point(π, 0);
      listener.point(π, -φ);
      listener.point(0, -φ);
      listener.point(-π, -φ);
      listener.point(-π, 0);
      listener.point(-π, φ);
    } else if (abs(from[0] - to[0]) > ε) {
      var s = from[0] < to[0] ? π : -π;
      φ = direction * s / 2;
      listener.point(-s, φ);
      listener.point(0, φ);
      listener.point(s, φ);
    } else {
      listener.point(to[0], to[1]);
    }
  }
  function d3_geo_pointInPolygon(point, polygon) {
    var meridian = point[0], parallel = point[1], meridianNormal = [ Math.sin(meridian), -Math.cos(meridian), 0 ], polarAngle = 0, winding = 0;
    d3_geo_areaRingSum.reset();
    for (var i = 0, n = polygon.length; i < n; ++i) {
      var ring = polygon[i], m = ring.length;
      if (!m) continue;
      var point0 = ring[0], λ0 = point0[0], φ0 = point0[1] / 2 + π / 4, sinφ0 = Math.sin(φ0), cosφ0 = Math.cos(φ0), j = 1;
      while (true) {
        if (j === m) j = 0;
        point = ring[j];
        var λ = point[0], φ = point[1] / 2 + π / 4, sinφ = Math.sin(φ), cosφ = Math.cos(φ), dλ = λ - λ0, sdλ = dλ >= 0 ? 1 : -1, adλ = sdλ * dλ, antimeridian = adλ > π, k = sinφ0 * sinφ;
        d3_geo_areaRingSum.add(Math.atan2(k * sdλ * Math.sin(adλ), cosφ0 * cosφ + k * Math.cos(adλ)));
        polarAngle += antimeridian ? dλ + sdλ * τ : dλ;
        if (antimeridian ^ λ0 >= meridian ^ λ >= meridian) {
          var arc = d3_geo_cartesianCross(d3_geo_cartesian(point0), d3_geo_cartesian(point));
          d3_geo_cartesianNormalize(arc);
          var intersection = d3_geo_cartesianCross(meridianNormal, arc);
          d3_geo_cartesianNormalize(intersection);
          var φarc = (antimeridian ^ dλ >= 0 ? -1 : 1) * d3_asin(intersection[2]);
          if (parallel > φarc || parallel === φarc && (arc[0] || arc[1])) {
            winding += antimeridian ^ dλ >= 0 ? 1 : -1;
          }
        }
        if (!j++) break;
        λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ, point0 = point;
      }
    }
    return (polarAngle < -ε || polarAngle < ε && d3_geo_areaRingSum < -ε) ^ winding & 1;
  }
  function d3_geo_clipCircle(radius) {
    var cr = Math.cos(radius), smallRadius = cr > 0, notHemisphere = abs(cr) > ε, interpolate = d3_geo_circleInterpolate(radius, 6 * d3_radians);
    return d3_geo_clip(visible, clipLine, interpolate, smallRadius ? [ 0, -radius ] : [ -π, radius - π ]);
    function visible(λ, φ) {
      return Math.cos(λ) * Math.cos(φ) > cr;
    }
    function clipLine(listener) {
      var point0, c0, v0, v00, clean;
      return {
        lineStart: function() {
          v00 = v0 = false;
          clean = 1;
        },
        point: function(λ, φ) {
          var point1 = [ λ, φ ], point2, v = visible(λ, φ), c = smallRadius ? v ? 0 : code(λ, φ) : v ? code(λ + (λ < 0 ? π : -π), φ) : 0;
          if (!point0 && (v00 = v0 = v)) listener.lineStart();
          if (v !== v0) {
            point2 = intersect(point0, point1);
            if (d3_geo_sphericalEqual(point0, point2) || d3_geo_sphericalEqual(point1, point2)) {
              point1[0] += ε;
              point1[1] += ε;
              v = visible(point1[0], point1[1]);
            }
          }
          if (v !== v0) {
            clean = 0;
            if (v) {
              listener.lineStart();
              point2 = intersect(point1, point0);
              listener.point(point2[0], point2[1]);
            } else {
              point2 = intersect(point0, point1);
              listener.point(point2[0], point2[1]);
              listener.lineEnd();
            }
            point0 = point2;
          } else if (notHemisphere && point0 && smallRadius ^ v) {
            var t;
            if (!(c & c0) && (t = intersect(point1, point0, true))) {
              clean = 0;
              if (smallRadius) {
                listener.lineStart();
                listener.point(t[0][0], t[0][1]);
                listener.point(t[1][0], t[1][1]);
                listener.lineEnd();
              } else {
                listener.point(t[1][0], t[1][1]);
                listener.lineEnd();
                listener.lineStart();
                listener.point(t[0][0], t[0][1]);
              }
            }
          }
          if (v && (!point0 || !d3_geo_sphericalEqual(point0, point1))) {
            listener.point(point1[0], point1[1]);
          }
          point0 = point1, v0 = v, c0 = c;
        },
        lineEnd: function() {
          if (v0) listener.lineEnd();
          point0 = null;
        },
        clean: function() {
          return clean | (v00 && v0) << 1;
        }
      };
    }
    function intersect(a, b, two) {
      var pa = d3_geo_cartesian(a), pb = d3_geo_cartesian(b);
      var n1 = [ 1, 0, 0 ], n2 = d3_geo_cartesianCross(pa, pb), n2n2 = d3_geo_cartesianDot(n2, n2), n1n2 = n2[0], determinant = n2n2 - n1n2 * n1n2;
      if (!determinant) return !two && a;
      var c1 = cr * n2n2 / determinant, c2 = -cr * n1n2 / determinant, n1xn2 = d3_geo_cartesianCross(n1, n2), A = d3_geo_cartesianScale(n1, c1), B = d3_geo_cartesianScale(n2, c2);
      d3_geo_cartesianAdd(A, B);
      var u = n1xn2, w = d3_geo_cartesianDot(A, u), uu = d3_geo_cartesianDot(u, u), t2 = w * w - uu * (d3_geo_cartesianDot(A, A) - 1);
      if (t2 < 0) return;
      var t = Math.sqrt(t2), q = d3_geo_cartesianScale(u, (-w - t) / uu);
      d3_geo_cartesianAdd(q, A);
      q = d3_geo_spherical(q);
      if (!two) return q;
      var λ0 = a[0], λ1 = b[0], φ0 = a[1], φ1 = b[1], z;
      if (λ1 < λ0) z = λ0, λ0 = λ1, λ1 = z;
      var δλ = λ1 - λ0, polar = abs(δλ - π) < ε, meridian = polar || δλ < ε;
      if (!polar && φ1 < φ0) z = φ0, φ0 = φ1, φ1 = z;
      if (meridian ? polar ? φ0 + φ1 > 0 ^ q[1] < (abs(q[0] - λ0) < ε ? φ0 : φ1) : φ0 <= q[1] && q[1] <= φ1 : δλ > π ^ (λ0 <= q[0] && q[0] <= λ1)) {
        var q1 = d3_geo_cartesianScale(u, (-w + t) / uu);
        d3_geo_cartesianAdd(q1, A);
        return [ q, d3_geo_spherical(q1) ];
      }
    }
    function code(λ, φ) {
      var r = smallRadius ? radius : π - radius, code = 0;
      if (λ < -r) code |= 1; else if (λ > r) code |= 2;
      if (φ < -r) code |= 4; else if (φ > r) code |= 8;
      return code;
    }
  }
  function d3_geom_clipLine(x0, y0, x1, y1) {
    return function(line) {
      var a = line.a, b = line.b, ax = a.x, ay = a.y, bx = b.x, by = b.y, t0 = 0, t1 = 1, dx = bx - ax, dy = by - ay, r;
      r = x0 - ax;
      if (!dx && r > 0) return;
      r /= dx;
      if (dx < 0) {
        if (r < t0) return;
        if (r < t1) t1 = r;
      } else if (dx > 0) {
        if (r > t1) return;
        if (r > t0) t0 = r;
      }
      r = x1 - ax;
      if (!dx && r < 0) return;
      r /= dx;
      if (dx < 0) {
        if (r > t1) return;
        if (r > t0) t0 = r;
      } else if (dx > 0) {
        if (r < t0) return;
        if (r < t1) t1 = r;
      }
      r = y0 - ay;
      if (!dy && r > 0) return;
      r /= dy;
      if (dy < 0) {
        if (r < t0) return;
        if (r < t1) t1 = r;
      } else if (dy > 0) {
        if (r > t1) return;
        if (r > t0) t0 = r;
      }
      r = y1 - ay;
      if (!dy && r < 0) return;
      r /= dy;
      if (dy < 0) {
        if (r > t1) return;
        if (r > t0) t0 = r;
      } else if (dy > 0) {
        if (r < t0) return;
        if (r < t1) t1 = r;
      }
      if (t0 > 0) line.a = {
        x: ax + t0 * dx,
        y: ay + t0 * dy
      };
      if (t1 < 1) line.b = {
        x: ax + t1 * dx,
        y: ay + t1 * dy
      };
      return line;
    };
  }
  var d3_geo_clipExtentMAX = 1e9;
  d3.geo.clipExtent = function() {
    var x0, y0, x1, y1, stream, clip, clipExtent = {
      stream: function(output) {
        if (stream) stream.valid = false;
        stream = clip(output);
        stream.valid = true;
        return stream;
      },
      extent: function(_) {
        if (!arguments.length) return [ [ x0, y0 ], [ x1, y1 ] ];
        clip = d3_geo_clipExtent(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]);
        if (stream) stream.valid = false, stream = null;
        return clipExtent;
      }
    };
    return clipExtent.extent([ [ 0, 0 ], [ 960, 500 ] ]);
  };
  function d3_geo_clipExtent(x0, y0, x1, y1) {
    return function(listener) {
      var listener_ = listener, bufferListener = d3_geo_clipBufferListener(), clipLine = d3_geom_clipLine(x0, y0, x1, y1), segments, polygon, ring;
      var clip = {
        point: point,
        lineStart: lineStart,
        lineEnd: lineEnd,
        polygonStart: function() {
          listener = bufferListener;
          segments = [];
          polygon = [];
          clean = true;
        },
        polygonEnd: function() {
          listener = listener_;
          segments = d3.merge(segments);
          var clipStartInside = insidePolygon([ x0, y1 ]), inside = clean && clipStartInside, visible = segments.length;
          if (inside || visible) {
            listener.polygonStart();
            if (inside) {
              listener.lineStart();
              interpolate(null, null, 1, listener);
              listener.lineEnd();
            }
            if (visible) {
              d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener);
            }
            listener.polygonEnd();
          }
          segments = polygon = ring = null;
        }
      };
      function insidePolygon(p) {
        var wn = 0, n = polygon.length, y = p[1];
        for (var i = 0; i < n; ++i) {
          for (var j = 1, v = polygon[i], m = v.length, a = v[0], b; j < m; ++j) {
            b = v[j];
            if (a[1] <= y) {
              if (b[1] > y && d3_cross2d(a, b, p) > 0) ++wn;
            } else {
              if (b[1] <= y && d3_cross2d(a, b, p) < 0) --wn;
            }
            a = b;
          }
        }
        return wn !== 0;
      }
      function interpolate(from, to, direction, listener) {
        var a = 0, a1 = 0;
        if (from == null || (a = corner(from, direction)) !== (a1 = corner(to, direction)) || comparePoints(from, to) < 0 ^ direction > 0) {
          do {
            listener.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0);
          } while ((a = (a + direction + 4) % 4) !== a1);
        } else {
          listener.point(to[0], to[1]);
        }
      }
      function pointVisible(x, y) {
        return x0 <= x && x <= x1 && y0 <= y && y <= y1;
      }
      function point(x, y) {
        if (pointVisible(x, y)) listener.point(x, y);
      }
      var x__, y__, v__, x_, y_, v_, first, clean;
      function lineStart() {
        clip.point = linePoint;
        if (polygon) polygon.push(ring = []);
        first = true;
        v_ = false;
        x_ = y_ = NaN;
      }
      function lineEnd() {
        if (segments) {
          linePoint(x__, y__);
          if (v__ && v_) bufferListener.rejoin();
          segments.push(bufferListener.buffer());
        }
        clip.point = point;
        if (v_) listener.lineEnd();
      }
      function linePoint(x, y) {
        x = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, x));
        y = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, y));
        var v = pointVisible(x, y);
        if (polygon) ring.push([ x, y ]);
        if (first) {
          x__ = x, y__ = y, v__ = v;
          first = false;
          if (v) {
            listener.lineStart();
            listener.point(x, y);
          }
        } else {
          if (v && v_) listener.point(x, y); else {
            var l = {
              a: {
                x: x_,
                y: y_
              },
              b: {
                x: x,
                y: y
              }
            };
            if (clipLine(l)) {
              if (!v_) {
                listener.lineStart();
                listener.point(l.a.x, l.a.y);
              }
              listener.point(l.b.x, l.b.y);
              if (!v) listener.lineEnd();
              clean = false;
            } else if (v) {
              listener.lineStart();
              listener.point(x, y);
              clean = false;
            }
          }
        }
        x_ = x, y_ = y, v_ = v;
      }
      return clip;
    };
    function corner(p, direction) {
      return abs(p[0] - x0) < ε ? direction > 0 ? 0 : 3 : abs(p[0] - x1) < ε ? direction > 0 ? 2 : 1 : abs(p[1] - y0) < ε ? direction > 0 ? 1 : 0 : direction > 0 ? 3 : 2;
    }
    function compare(a, b) {
      return comparePoints(a.x, b.x);
    }
    function comparePoints(a, b) {
      var ca = corner(a, 1), cb = corner(b, 1);
      return ca !== cb ? ca - cb : ca === 0 ? b[1] - a[1] : ca === 1 ? a[0] - b[0] : ca === 2 ? a[1] - b[1] : b[0] - a[0];
    }
  }
  function d3_geo_conic(projectAt) {
    var φ0 = 0, φ1 = π / 3, m = d3_geo_projectionMutator(projectAt), p = m(φ0, φ1);
    p.parallels = function(_) {
      if (!arguments.length) return [ φ0 / π * 180, φ1 / π * 180 ];
      return m(φ0 = _[0] * π / 180, φ1 = _[1] * π / 180);
    };
    return p;
  }
  function d3_geo_conicEqualArea(φ0, φ1) {
    var sinφ0 = Math.sin(φ0), n = (sinφ0 + Math.sin(φ1)) / 2, C = 1 + sinφ0 * (2 * n - sinφ0), ρ0 = Math.sqrt(C) / n;
    function forward(λ, φ) {
      var ρ = Math.sqrt(C - 2 * n * Math.sin(φ)) / n;
      return [ ρ * Math.sin(λ *= n), ρ0 - ρ * Math.cos(λ) ];
    }
    forward.invert = function(x, y) {
      var ρ0_y = ρ0 - y;
      return [ Math.atan2(x, ρ0_y) / n, d3_asin((C - (x * x + ρ0_y * ρ0_y) * n * n) / (2 * n)) ];
    };
    return forward;
  }
  (d3.geo.conicEqualArea = function() {
    return d3_geo_conic(d3_geo_conicEqualArea);
  }).raw = d3_geo_conicEqualArea;
  d3.geo.albers = function() {
    return d3.geo.conicEqualArea().rotate([ 96, 0 ]).center([ -.6, 38.7 ]).parallels([ 29.5, 45.5 ]).scale(1070);
  };
  d3.geo.albersUsa = function() {
    var lower48 = d3.geo.albers();
    var alaska = d3.geo.conicEqualArea().rotate([ 154, 0 ]).center([ -2, 58.5 ]).parallels([ 55, 65 ]);
    var hawaii = d3.geo.conicEqualArea().rotate([ 157, 0 ]).center([ -3, 19.9 ]).parallels([ 8, 18 ]);
    var point, pointStream = {
      point: function(x, y) {
        point = [ x, y ];
      }
    }, lower48Point, alaskaPoint, hawaiiPoint;
    function albersUsa(coordinates) {
      var x = coordinates[0], y = coordinates[1];
      point = null;
      (lower48Point(x, y), point) || (alaskaPoint(x, y), point) || hawaiiPoint(x, y);
      return point;
    }
    albersUsa.invert = function(coordinates) {
      var k = lower48.scale(), t = lower48.translate(), x = (coordinates[0] - t[0]) / k, y = (coordinates[1] - t[1]) / k;
      return (y >= .12 && y < .234 && x >= -.425 && x < -.214 ? alaska : y >= .166 && y < .234 && x >= -.214 && x < -.115 ? hawaii : lower48).invert(coordinates);
    };
    albersUsa.stream = function(stream) {
      var lower48Stream = lower48.stream(stream), alaskaStream = alaska.stream(stream), hawaiiStream = hawaii.stream(stream);
      return {
        point: function(x, y) {
          lower48Stream.point(x, y);
          alaskaStream.point(x, y);
          hawaiiStream.point(x, y);
        },
        sphere: function() {
          lower48Stream.sphere();
          alaskaStream.sphere();
          hawaiiStream.sphere();
        },
        lineStart: function() {
          lower48Stream.lineStart();
          alaskaStream.lineStart();
          hawaiiStream.lineStart();
        },
        lineEnd: function() {
          lower48Stream.lineEnd();
          alaskaStream.lineEnd();
          hawaiiStream.lineEnd();
        },
        polygonStart: function() {
          lower48Stream.polygonStart();
          alaskaStream.polygonStart();
          hawaiiStream.polygonStart();
        },
        polygonEnd: function() {
          lower48Stream.polygonEnd();
          alaskaStream.polygonEnd();
          hawaiiStream.polygonEnd();
        }
      };
    };
    albersUsa.precision = function(_) {
      if (!arguments.length) return lower48.precision();
      lower48.precision(_);
      alaska.precision(_);
      hawaii.precision(_);
      return albersUsa;
    };
    albersUsa.scale = function(_) {
      if (!arguments.length) return lower48.scale();
      lower48.scale(_);
      alaska.scale(_ * .35);
      hawaii.scale(_);
      return albersUsa.translate(lower48.translate());
    };
    albersUsa.translate = function(_) {
      if (!arguments.length) return lower48.translate();
      var k = lower48.scale(), x = +_[0], y = +_[1];
      lower48Point = lower48.translate(_).clipExtent([ [ x - .455 * k, y - .238 * k ], [ x + .455 * k, y + .238 * k ] ]).stream(pointStream).point;
      alaskaPoint = alaska.translate([ x - .307 * k, y + .201 * k ]).clipExtent([ [ x - .425 * k + ε, y + .12 * k + ε ], [ x - .214 * k - ε, y + .234 * k - ε ] ]).stream(pointStream).point;
      hawaiiPoint = hawaii.translate([ x - .205 * k, y + .212 * k ]).clipExtent([ [ x - .214 * k + ε, y + .166 * k + ε ], [ x - .115 * k - ε, y + .234 * k - ε ] ]).stream(pointStream).point;
      return albersUsa;
    };
    return albersUsa.scale(1070);
  };
  var d3_geo_pathAreaSum, d3_geo_pathAreaPolygon, d3_geo_pathArea = {
    point: d3_noop,
    lineStart: d3_noop,
    lineEnd: d3_noop,
    polygonStart: function() {
      d3_geo_pathAreaPolygon = 0;
      d3_geo_pathArea.lineStart = d3_geo_pathAreaRingStart;
    },
    polygonEnd: function() {
      d3_geo_pathArea.lineStart = d3_geo_pathArea.lineEnd = d3_geo_pathArea.point = d3_noop;
      d3_geo_pathAreaSum += abs(d3_geo_pathAreaPolygon / 2);
    }
  };
  function d3_geo_pathAreaRingStart() {
    var x00, y00, x0, y0;
    d3_geo_pathArea.point = function(x, y) {
      d3_geo_pathArea.point = nextPoint;
      x00 = x0 = x, y00 = y0 = y;
    };
    function nextPoint(x, y) {
      d3_geo_pathAreaPolygon += y0 * x - x0 * y;
      x0 = x, y0 = y;
    }
    d3_geo_pathArea.lineEnd = function() {
      nextPoint(x00, y00);
    };
  }
  var d3_geo_pathBoundsX0, d3_geo_pathBoundsY0, d3_geo_pathBoundsX1, d3_geo_pathBoundsY1;
  var d3_geo_pathBounds = {
    point: d3_geo_pathBoundsPoint,
    lineStart: d3_noop,
    lineEnd: d3_noop,
    polygonStart: d3_noop,
    polygonEnd: d3_noop
  };
  function d3_geo_pathBoundsPoint(x, y) {
    if (x < d3_geo_pathBoundsX0) d3_geo_pathBoundsX0 = x;
    if (x > d3_geo_pathBoundsX1) d3_geo_pathBoundsX1 = x;
    if (y < d3_geo_pathBoundsY0) d3_geo_pathBoundsY0 = y;
    if (y > d3_geo_pathBoundsY1) d3_geo_pathBoundsY1 = y;
  }
  function d3_geo_pathBuffer() {
    var pointCircle = d3_geo_pathBufferCircle(4.5), buffer = [];
    var stream = {
      point: point,
      lineStart: function() {
        stream.point = pointLineStart;
      },
      lineEnd: lineEnd,
      polygonStart: function() {
        stream.lineEnd = lineEndPolygon;
      },
      polygonEnd: function() {
        stream.lineEnd = lineEnd;
        stream.point = point;
      },
      pointRadius: function(_) {
        pointCircle = d3_geo_pathBufferCircle(_);
        return stream;
      },
      result: function() {
        if (buffer.length) {
          var result = buffer.join("");
          buffer = [];
          return result;
        }
      }
    };
    function point(x, y) {
      buffer.push("M", x, ",", y, pointCircle);
    }
    function pointLineStart(x, y) {
      buffer.push("M", x, ",", y);
      stream.point = pointLine;
    }
    function pointLine(x, y) {
      buffer.push("L", x, ",", y);
    }
    function lineEnd() {
      stream.point = point;
    }
    function lineEndPolygon() {
      buffer.push("Z");
    }
    return stream;
  }
  function d3_geo_pathBufferCircle(radius) {
    return "m0," + radius + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius + "a" + radius + "," + radius + " 0 1,1 0," + 2 * radius + "z";
  }
  var d3_geo_pathCentroid = {
    point: d3_geo_pathCentroidPoint,
    lineStart: d3_geo_pathCentroidLineStart,
    lineEnd: d3_geo_pathCentroidLineEnd,
    polygonStart: function() {
      d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidRingStart;
    },
    polygonEnd: function() {
      d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint;
      d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidLineStart;
      d3_geo_pathCentroid.lineEnd = d3_geo_pathCentroidLineEnd;
    }
  };
  function d3_geo_pathCentroidPoint(x, y) {
    d3_geo_centroidX0 += x;
    d3_geo_centroidY0 += y;
    ++d3_geo_centroidZ0;
  }
  function d3_geo_pathCentroidLineStart() {
    var x0, y0;
    d3_geo_pathCentroid.point = function(x, y) {
      d3_geo_pathCentroid.point = nextPoint;
      d3_geo_pathCentroidPoint(x0 = x, y0 = y);
    };
    function nextPoint(x, y) {
      var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy);
      d3_geo_centroidX1 += z * (x0 + x) / 2;
      d3_geo_centroidY1 += z * (y0 + y) / 2;
      d3_geo_centroidZ1 += z;
      d3_geo_pathCentroidPoint(x0 = x, y0 = y);
    }
  }
  function d3_geo_pathCentroidLineEnd() {
    d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint;
  }
  function d3_geo_pathCentroidRingStart() {
    var x00, y00, x0, y0;
    d3_geo_pathCentroid.point = function(x, y) {
      d3_geo_pathCentroid.point = nextPoint;
      d3_geo_pathCentroidPoint(x00 = x0 = x, y00 = y0 = y);
    };
    function nextPoint(x, y) {
      var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy);
      d3_geo_centroidX1 += z * (x0 + x) / 2;
      d3_geo_centroidY1 += z * (y0 + y) / 2;
      d3_geo_centroidZ1 += z;
      z = y0 * x - x0 * y;
      d3_geo_centroidX2 += z * (x0 + x);
      d3_geo_centroidY2 += z * (y0 + y);
      d3_geo_centroidZ2 += z * 3;
      d3_geo_pathCentroidPoint(x0 = x, y0 = y);
    }
    d3_geo_pathCentroid.lineEnd = function() {
      nextPoint(x00, y00);
    };
  }
  function d3_geo_pathContext(context) {
    var pointRadius = 4.5;
    var stream = {
      point: point,
      lineStart: function() {
        stream.point = pointLineStart;
      },
      lineEnd: lineEnd,
      polygonStart: function() {
        stream.lineEnd = lineEndPolygon;
      },
      polygonEnd: function() {
        stream.lineEnd = lineEnd;
        stream.point = point;
      },
      pointRadius: function(_) {
        pointRadius = _;
        return stream;
      },
      result: d3_noop
    };
    function point(x, y) {
      context.moveTo(x + pointRadius, y);
      context.arc(x, y, pointRadius, 0, τ);
    }
    function pointLineStart(x, y) {
      context.moveTo(x, y);
      stream.point = pointLine;
    }
    function pointLine(x, y) {
      context.lineTo(x, y);
    }
    function lineEnd() {
      stream.point = point;
    }
    function lineEndPolygon() {
      context.closePath();
    }
    return stream;
  }
  function d3_geo_resample(project) {
    var δ2 = .5, cosMinDistance = Math.cos(30 * d3_radians), maxDepth = 16;
    function resample(stream) {
      return (maxDepth ? resampleRecursive : resampleNone)(stream);
    }
    function resampleNone(stream) {
      return d3_geo_transformPoint(stream, function(x, y) {
        x = project(x, y);
        stream.point(x[0], x[1]);
      });
    }
    function resampleRecursive(stream) {
      var λ00, φ00, x00, y00, a00, b00, c00, λ0, x0, y0, a0, b0, c0;
      var resample = {
        point: point,
        lineStart: lineStart,
        lineEnd: lineEnd,
        polygonStart: function() {
          stream.polygonStart();
          resample.lineStart = ringStart;
        },
        polygonEnd: function() {
          stream.polygonEnd();
          resample.lineStart = lineStart;
        }
      };
      function point(x, y) {
        x = project(x, y);
        stream.point(x[0], x[1]);
      }
      function lineStart() {
        x0 = NaN;
        resample.point = linePoint;
        stream.lineStart();
      }
      function linePoint(λ, φ) {
        var c = d3_geo_cartesian([ λ, φ ]), p = project(λ, φ);
        resampleLineTo(x0, y0, λ0, a0, b0, c0, x0 = p[0], y0 = p[1], λ0 = λ, a0 = c[0], b0 = c[1], c0 = c[2], maxDepth, stream);
        stream.point(x0, y0);
      }
      function lineEnd() {
        resample.point = point;
        stream.lineEnd();
      }
      function ringStart() {
        lineStart();
        resample.point = ringPoint;
        resample.lineEnd = ringEnd;
      }
      function ringPoint(λ, φ) {
        linePoint(λ00 = λ, φ00 = φ), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0;
        resample.point = linePoint;
      }
      function ringEnd() {
        resampleLineTo(x0, y0, λ0, a0, b0, c0, x00, y00, λ00, a00, b00, c00, maxDepth, stream);
        resample.lineEnd = lineEnd;
        lineEnd();
      }
      return resample;
    }
    function resampleLineTo(x0, y0, λ0, a0, b0, c0, x1, y1, λ1, a1, b1, c1, depth, stream) {
      var dx = x1 - x0, dy = y1 - y0, d2 = dx * dx + dy * dy;
      if (d2 > 4 * δ2 && depth--) {
        var a = a0 + a1, b = b0 + b1, c = c0 + c1, m = Math.sqrt(a * a + b * b + c * c), φ2 = Math.asin(c /= m), λ2 = abs(abs(c) - 1) < ε || abs(λ0 - λ1) < ε ? (λ0 + λ1) / 2 : Math.atan2(b, a), p = project(λ2, φ2), x2 = p[0], y2 = p[1], dx2 = x2 - x0, dy2 = y2 - y0, dz = dy * dx2 - dx * dy2;
        if (dz * dz / d2 > δ2 || abs((dx * dx2 + dy * dy2) / d2 - .5) > .3 || a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) {
          resampleLineTo(x0, y0, λ0, a0, b0, c0, x2, y2, λ2, a /= m, b /= m, c, depth, stream);
          stream.point(x2, y2);
          resampleLineTo(x2, y2, λ2, a, b, c, x1, y1, λ1, a1, b1, c1, depth, stream);
        }
      }
    }
    resample.precision = function(_) {
      if (!arguments.length) return Math.sqrt(δ2);
      maxDepth = (δ2 = _ * _) > 0 && 16;
      return resample;
    };
    return resample;
  }
  d3.geo.path = function() {
    var pointRadius = 4.5, projection, context, projectStream, contextStream, cacheStream;
    function path(object) {
      if (object) {
        if (typeof pointRadius === "function") contextStream.pointRadius(+pointRadius.apply(this, arguments));
        if (!cacheStream || !cacheStream.valid) cacheStream = projectStream(contextStream);
        d3.geo.stream(object, cacheStream);
      }
      return contextStream.result();
    }
    path.area = function(object) {
      d3_geo_pathAreaSum = 0;
      d3.geo.stream(object, projectStream(d3_geo_pathArea));
      return d3_geo_pathAreaSum;
    };
    path.centroid = function(object) {
      d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 = d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 = d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0;
      d3.geo.stream(object, projectStream(d3_geo_pathCentroid));
      return d3_geo_centroidZ2 ? [ d3_geo_centroidX2 / d3_geo_centroidZ2, d3_geo_centroidY2 / d3_geo_centroidZ2 ] : d3_geo_centroidZ1 ? [ d3_geo_centroidX1 / d3_geo_centroidZ1, d3_geo_centroidY1 / d3_geo_centroidZ1 ] : d3_geo_centroidZ0 ? [ d3_geo_centroidX0 / d3_geo_centroidZ0, d3_geo_centroidY0 / d3_geo_centroidZ0 ] : [ NaN, NaN ];
    };
    path.bounds = function(object) {
      d3_geo_pathBoundsX1 = d3_geo_pathBoundsY1 = -(d3_geo_pathBoundsX0 = d3_geo_pathBoundsY0 = Infinity);
      d3.geo.stream(object, projectStream(d3_geo_pathBounds));
      return [ [ d3_geo_pathBoundsX0, d3_geo_pathBoundsY0 ], [ d3_geo_pathBoundsX1, d3_geo_pathBoundsY1 ] ];
    };
    path.projection = function(_) {
      if (!arguments.length) return projection;
      projectStream = (projection = _) ? _.stream || d3_geo_pathProjectStream(_) : d3_identity;
      return reset();
    };
    path.context = function(_) {
      if (!arguments.length) return context;
      contextStream = (context = _) == null ? new d3_geo_pathBuffer() : new d3_geo_pathContext(_);
      if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius);
      return reset();
    };
    path.pointRadius = function(_) {
      if (!arguments.length) return pointRadius;
      pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_);
      return path;
    };
    function reset() {
      cacheStream = null;
      return path;
    }
    return path.projection(d3.geo.albersUsa()).context(null);
  };
  function d3_geo_pathProjectStream(project) {
    var resample = d3_geo_resample(function(x, y) {
      return project([ x * d3_degrees, y * d3_degrees ]);
    });
    return function(stream) {
      return d3_geo_projectionRadians(resample(stream));
    };
  }
  d3.geo.transform = function(methods) {
    return {
      stream: function(stream) {
        var transform = new d3_geo_transform(stream);
        for (var k in methods) transform[k] = methods[k];
        return transform;
      }
    };
  };
  function d3_geo_transform(stream) {
    this.stream = stream;
  }
  d3_geo_transform.prototype = {
    point: function(x, y) {
      this.stream.point(x, y);
    },
    sphere: function() {
      this.stream.sphere();
    },
    lineStart: function() {
      this.stream.lineStart();
    },
    lineEnd: function() {
      this.stream.lineEnd();
    },
    polygonStart: function() {
      this.stream.polygonStart();
    },
    polygonEnd: function() {
      this.stream.polygonEnd();
    }
  };
  function d3_geo_transformPoint(stream, point) {
    return {
      point: point,
      sphere: function() {
        stream.sphere();
      },
      lineStart: function() {
        stream.lineStart();
      },
      lineEnd: function() {
        stream.lineEnd();
      },
      polygonStart: function() {
        stream.polygonStart();
      },
      polygonEnd: function() {
        stream.polygonEnd();
      }
    };
  }
  d3.geo.projection = d3_geo_projection;
  d3.geo.projectionMutator = d3_geo_projectionMutator;
  function d3_geo_projection(project) {
    return d3_geo_projectionMutator(function() {
      return project;
    })();
  }
  function d3_geo_projectionMutator(projectAt) {
    var project, rotate, projectRotate, projectResample = d3_geo_resample(function(x, y) {
      x = project(x, y);
      return [ x[0] * k + δx, δy - x[1] * k ];
    }), k = 150, x = 480, y = 250, λ = 0, φ = 0, δλ = 0, δφ = 0, δγ = 0, δx, δy, preclip = d3_geo_clipAntimeridian, postclip = d3_identity, clipAngle = null, clipExtent = null, stream;
    function projection(point) {
      point = projectRotate(point[0] * d3_radians, point[1] * d3_radians);
      return [ point[0] * k + δx, δy - point[1] * k ];
    }
    function invert(point) {
      point = projectRotate.invert((point[0] - δx) / k, (δy - point[1]) / k);
      return point && [ point[0] * d3_degrees, point[1] * d3_degrees ];
    }
    projection.stream = function(output) {
      if (stream) stream.valid = false;
      stream = d3_geo_projectionRadians(preclip(rotate, projectResample(postclip(output))));
      stream.valid = true;
      return stream;
    };
    projection.clipAngle = function(_) {
      if (!arguments.length) return clipAngle;
      preclip = _ == null ? (clipAngle = _, d3_geo_clipAntimeridian) : d3_geo_clipCircle((clipAngle = +_) * d3_radians);
      return invalidate();
    };
    projection.clipExtent = function(_) {
      if (!arguments.length) return clipExtent;
      clipExtent = _;
      postclip = _ ? d3_geo_clipExtent(_[0][0], _[0][1], _[1][0], _[1][1]) : d3_identity;
      return invalidate();
    };
    projection.scale = function(_) {
      if (!arguments.length) return k;
      k = +_;
      return reset();
    };
    projection.translate = function(_) {
      if (!arguments.length) return [ x, y ];
      x = +_[0];
      y = +_[1];
      return reset();
    };
    projection.center = function(_) {
      if (!arguments.length) return [ λ * d3_degrees, φ * d3_degrees ];
      λ = _[0] % 360 * d3_radians;
      φ = _[1] % 360 * d3_radians;
      return reset();
    };
    projection.rotate = function(_) {
      if (!arguments.length) return [ δλ * d3_degrees, δφ * d3_degrees, δγ * d3_degrees ];
      δλ = _[0] % 360 * d3_radians;
      δφ = _[1] % 360 * d3_radians;
      δγ = _.length > 2 ? _[2] % 360 * d3_radians : 0;
      return reset();
    };
    d3.rebind(projection, projectResample, "precision");
    function reset() {
      projectRotate = d3_geo_compose(rotate = d3_geo_rotation(δλ, δφ, δγ), project);
      var center = project(λ, φ);
      δx = x - center[0] * k;
      δy = y + center[1] * k;
      return invalidate();
    }
    function invalidate() {
      if (stream) stream.valid = false, stream = null;
      return projection;
    }
    return function() {
      project = projectAt.apply(this, arguments);
      projection.invert = project.invert && invert;
      return reset();
    };
  }
  function d3_geo_projectionRadians(stream) {
    return d3_geo_transformPoint(stream, function(x, y) {
      stream.point(x * d3_radians, y * d3_radians);
    });
  }
  function d3_geo_equirectangular(λ, φ) {
    return [ λ, φ ];
  }
  (d3.geo.equirectangular = function() {
    return d3_geo_projection(d3_geo_equirectangular);
  }).raw = d3_geo_equirectangular.invert = d3_geo_equirectangular;
  d3.geo.rotation = function(rotate) {
    rotate = d3_geo_rotation(rotate[0] % 360 * d3_radians, rotate[1] * d3_radians, rotate.length > 2 ? rotate[2] * d3_radians : 0);
    function forward(coordinates) {
      coordinates = rotate(coordinates[0] * d3_radians, coordinates[1] * d3_radians);
      return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates;
    }
    forward.invert = function(coordinates) {
      coordinates = rotate.invert(coordinates[0] * d3_radians, coordinates[1] * d3_radians);
      return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates;
    };
    return forward;
  };
  function d3_geo_identityRotation(λ, φ) {
    return [ λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ ];
  }
  d3_geo_identityRotation.invert = d3_geo_equirectangular;
  function d3_geo_rotation(δλ, δφ, δγ) {
    return δλ ? δφ || δγ ? d3_geo_compose(d3_geo_rotationλ(δλ), d3_geo_rotationφγ(δφ, δγ)) : d3_geo_rotationλ(δλ) : δφ || δγ ? d3_geo_rotationφγ(δφ, δγ) : d3_geo_identityRotation;
  }
  function d3_geo_forwardRotationλ(δλ) {
    return function(λ, φ) {
      return λ += δλ, [ λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ ];
    };
  }
  function d3_geo_rotationλ(δλ) {
    var rotation = d3_geo_forwardRotationλ(δλ);
    rotation.invert = d3_geo_forwardRotationλ(-δλ);
    return rotation;
  }
  function d3_geo_rotationφγ(δφ, δγ) {
    var cosδφ = Math.cos(δφ), sinδφ = Math.sin(δφ), cosδγ = Math.cos(δγ), sinδγ = Math.sin(δγ);
    function rotation(λ, φ) {
      var cosφ = Math.cos(φ), x = Math.cos(λ) * cosφ, y = Math.sin(λ) * cosφ, z = Math.sin(φ), k = z * cosδφ + x * sinδφ;
      return [ Math.atan2(y * cosδγ - k * sinδγ, x * cosδφ - z * sinδφ), d3_asin(k * cosδγ + y * sinδγ) ];
    }
    rotation.invert = function(λ, φ) {
      var cosφ = Math.cos(φ), x = Math.cos(λ) * cosφ, y = Math.sin(λ) * cosφ, z = Math.sin(φ), k = z * cosδγ - y * sinδγ;
      return [ Math.atan2(y * cosδγ + z * sinδγ, x * cosδφ + k * sinδφ), d3_asin(k * cosδφ - x * sinδφ) ];
    };
    return rotation;
  }
  d3.geo.circle = function() {
    var origin = [ 0, 0 ], angle, precision = 6, interpolate;
    function circle() {
      var center = typeof origin === "function" ? origin.apply(this, arguments) : origin, rotate = d3_geo_rotation(-center[0] * d3_radians, -center[1] * d3_radians, 0).invert, ring = [];
      interpolate(null, null, 1, {
        point: function(x, y) {
          ring.push(x = rotate(x, y));
          x[0] *= d3_degrees, x[1] *= d3_degrees;
        }
      });
      return {
        type: "Polygon",
        coordinates: [ ring ]
      };
    }
    circle.origin = function(x) {
      if (!arguments.length) return origin;
      origin = x;
      return circle;
    };
    circle.angle = function(x) {
      if (!arguments.length) return angle;
      interpolate = d3_geo_circleInterpolate((angle = +x) * d3_radians, precision * d3_radians);
      return circle;
    };
    circle.precision = function(_) {
      if (!arguments.length) return precision;
      interpolate = d3_geo_circleInterpolate(angle * d3_radians, (precision = +_) * d3_radians);
      return circle;
    };
    return circle.angle(90);
  };
  function d3_geo_circleInterpolate(radius, precision) {
    var cr = Math.cos(radius), sr = Math.sin(radius);
    return function(from, to, direction, listener) {
      var step = direction * precision;
      if (from != null) {
        from = d3_geo_circleAngle(cr, from);
        to = d3_geo_circleAngle(cr, to);
        if (direction > 0 ? from < to : from > to) from += direction * τ;
      } else {
        from = radius + direction * τ;
        to = radius - .5 * step;
      }
      for (var point, t = from; direction > 0 ? t > to : t < to; t -= step) {
        listener.point((point = d3_geo_spherical([ cr, -sr * Math.cos(t), -sr * Math.sin(t) ]))[0], point[1]);
      }
    };
  }
  function d3_geo_circleAngle(cr, point) {
    var a = d3_geo_cartesian(point);
    a[0] -= cr;
    d3_geo_cartesianNormalize(a);
    var angle = d3_acos(-a[1]);
    return ((-a[2] < 0 ? -angle : angle) + 2 * Math.PI - ε) % (2 * Math.PI);
  }
  d3.geo.distance = function(a, b) {
    var Δλ = (b[0] - a[0]) * d3_radians, φ0 = a[1] * d3_radians, φ1 = b[1] * d3_radians, sinΔλ = Math.sin(Δλ), cosΔλ = Math.cos(Δλ), sinφ0 = Math.sin(φ0), cosφ0 = Math.cos(φ0), sinφ1 = Math.sin(φ1), cosφ1 = Math.cos(φ1), t;
    return Math.atan2(Math.sqrt((t = cosφ1 * sinΔλ) * t + (t = cosφ0 * sinφ1 - sinφ0 * cosφ1 * cosΔλ) * t), sinφ0 * sinφ1 + cosφ0 * cosφ1 * cosΔλ);
  };
  d3.geo.graticule = function() {
    var x1, x0, X1, X0, y1, y0, Y1, Y0, dx = 10, dy = dx, DX = 90, DY = 360, x, y, X, Y, precision = 2.5;
    function graticule() {
      return {
        type: "MultiLineString",
        coordinates: lines()
      };
    }
    function lines() {
      return d3.range(Math.ceil(X0 / DX) * DX, X1, DX).map(X).concat(d3.range(Math.ceil(Y0 / DY) * DY, Y1, DY).map(Y)).concat(d3.range(Math.ceil(x0 / dx) * dx, x1, dx).filter(function(x) {
        return abs(x % DX) > ε;
      }).map(x)).concat(d3.range(Math.ceil(y0 / dy) * dy, y1, dy).filter(function(y) {
        return abs(y % DY) > ε;
      }).map(y));
    }
    graticule.lines = function() {
      return lines().map(function(coordinates) {
        return {
          type: "LineString",
          coordinates: coordinates
        };
      });
    };
    graticule.outline = function() {
      return {
        type: "Polygon",
        coordinates: [ X(X0).concat(Y(Y1).slice(1), X(X1).reverse().slice(1), Y(Y0).reverse().slice(1)) ]
      };
    };
    graticule.extent = function(_) {
      if (!arguments.length) return graticule.minorExtent();
      return graticule.majorExtent(_).minorExtent(_);
    };
    graticule.majorExtent = function(_) {
      if (!arguments.length) return [ [ X0, Y0 ], [ X1, Y1 ] ];
      X0 = +_[0][0], X1 = +_[1][0];
      Y0 = +_[0][1], Y1 = +_[1][1];
      if (X0 > X1) _ = X0, X0 = X1, X1 = _;
      if (Y0 > Y1) _ = Y0, Y0 = Y1, Y1 = _;
      return graticule.precision(precision);
    };
    graticule.minorExtent = function(_) {
      if (!arguments.length) return [ [ x0, y0 ], [ x1, y1 ] ];
      x0 = +_[0][0], x1 = +_[1][0];
      y0 = +_[0][1], y1 = +_[1][1];
      if (x0 > x1) _ = x0, x0 = x1, x1 = _;
      if (y0 > y1) _ = y0, y0 = y1, y1 = _;
      return graticule.precision(precision);
    };
    graticule.step = function(_) {
      if (!arguments.length) return graticule.minorStep();
      return graticule.majorStep(_).minorStep(_);
    };
    graticule.majorStep = function(_) {
      if (!arguments.length) return [ DX, DY ];
      DX = +_[0], DY = +_[1];
      return graticule;
    };
    graticule.minorStep = function(_) {
      if (!arguments.length) return [ dx, dy ];
      dx = +_[0], dy = +_[1];
      return graticule;
    };
    graticule.precision = function(_) {
      if (!arguments.length) return precision;
      precision = +_;
      x = d3_geo_graticuleX(y0, y1, 90);
      y = d3_geo_graticuleY(x0, x1, precision);
      X = d3_geo_graticuleX(Y0, Y1, 90);
      Y = d3_geo_graticuleY(X0, X1, precision);
      return graticule;
    };
    return graticule.majorExtent([ [ -180, -90 + ε ], [ 180, 90 - ε ] ]).minorExtent([ [ -180, -80 - ε ], [ 180, 80 + ε ] ]);
  };
  function d3_geo_graticuleX(y0, y1, dy) {
    var y = d3.range(y0, y1 - ε, dy).concat(y1);
    return function(x) {
      return y.map(function(y) {
        return [ x, y ];
      });
    };
  }
  function d3_geo_graticuleY(x0, x1, dx) {
    var x = d3.range(x0, x1 - ε, dx).concat(x1);
    return function(y) {
      return x.map(function(x) {
        return [ x, y ];
      });
    };
  }
  function d3_source(d) {
    return d.source;
  }
  function d3_target(d) {
    return d.target;
  }
  d3.geo.greatArc = function() {
    var source = d3_source, source_, target = d3_target, target_;
    function greatArc() {
      return {
        type: "LineString",
        coordinates: [ source_ || source.apply(this, arguments), target_ || target.apply(this, arguments) ]
      };
    }
    greatArc.distance = function() {
      return d3.geo.distance(source_ || source.apply(this, arguments), target_ || target.apply(this, arguments));
    };
    greatArc.source = function(_) {
      if (!arguments.length) return source;
      source = _, source_ = typeof _ === "function" ? null : _;
      return greatArc;
    };
    greatArc.target = function(_) {
      if (!arguments.length) return target;
      target = _, target_ = typeof _ === "function" ? null : _;
      return greatArc;
    };
    greatArc.precision = function() {
      return arguments.length ? greatArc : 0;
    };
    return greatArc;
  };
  d3.geo.interpolate = function(source, target) {
    return d3_geo_interpolate(source[0] * d3_radians, source[1] * d3_radians, target[0] * d3_radians, target[1] * d3_radians);
  };
  function d3_geo_interpolate(x0, y0, x1, y1) {
    var cy0 = Math.cos(y0), sy0 = Math.sin(y0), cy1 = Math.cos(y1), sy1 = Math.sin(y1), kx0 = cy0 * Math.cos(x0), ky0 = cy0 * Math.sin(x0), kx1 = cy1 * Math.cos(x1), ky1 = cy1 * Math.sin(x1), d = 2 * Math.asin(Math.sqrt(d3_haversin(y1 - y0) + cy0 * cy1 * d3_haversin(x1 - x0))), k = 1 / Math.sin(d);
    var interpolate = d ? function(t) {
      var B = Math.sin(t *= d) * k, A = Math.sin(d - t) * k, x = A * kx0 + B * kx1, y = A * ky0 + B * ky1, z = A * sy0 + B * sy1;
      return [ Math.atan2(y, x) * d3_degrees, Math.atan2(z, Math.sqrt(x * x + y * y)) * d3_degrees ];
    } : function() {
      return [ x0 * d3_degrees, y0 * d3_degrees ];
    };
    interpolate.distance = d;
    return interpolate;
  }
  d3.geo.length = function(object) {
    d3_geo_lengthSum = 0;
    d3.geo.stream(object, d3_geo_length);
    return d3_geo_lengthSum;
  };
  var d3_geo_lengthSum;
  var d3_geo_length = {
    sphere: d3_noop,
    point: d3_noop,
    lineStart: d3_geo_lengthLineStart,
    lineEnd: d3_noop,
    polygonStart: d3_noop,
    polygonEnd: d3_noop
  };
  function d3_geo_lengthLineStart() {
    var λ0, sinφ0, cosφ0;
    d3_geo_length.point = function(λ, φ) {
      λ0 = λ * d3_radians, sinφ0 = Math.sin(φ *= d3_radians), cosφ0 = Math.cos(φ);
      d3_geo_length.point = nextPoint;
    };
    d3_geo_length.lineEnd = function() {
      d3_geo_length.point = d3_geo_length.lineEnd = d3_noop;
    };
    function nextPoint(λ, φ) {
      var sinφ = Math.sin(φ *= d3_radians), cosφ = Math.cos(φ), t = abs((λ *= d3_radians) - λ0), cosΔλ = Math.cos(t);
      d3_geo_lengthSum += Math.atan2(Math.sqrt((t = cosφ * Math.sin(t)) * t + (t = cosφ0 * sinφ - sinφ0 * cosφ * cosΔλ) * t), sinφ0 * sinφ + cosφ0 * cosφ * cosΔλ);
      λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ;
    }
  }
  function d3_geo_azimuthal(scale, angle) {
    function azimuthal(λ, φ) {
      var cosλ = Math.cos(λ), cosφ = Math.cos(φ), k = scale(cosλ * cosφ);
      return [ k * cosφ * Math.sin(λ), k * Math.sin(φ) ];
    }
    azimuthal.invert = function(x, y) {
      var ρ = Math.sqrt(x * x + y * y), c = angle(ρ), sinc = Math.sin(c), cosc = Math.cos(c);
      return [ Math.atan2(x * sinc, ρ * cosc), Math.asin(ρ && y * sinc / ρ) ];
    };
    return azimuthal;
  }
  var d3_geo_azimuthalEqualArea = d3_geo_azimuthal(function(cosλcosφ) {
    return Math.sqrt(2 / (1 + cosλcosφ));
  }, function(ρ) {
    return 2 * Math.asin(ρ / 2);
  });
  (d3.geo.azimuthalEqualArea = function() {
    return d3_geo_projection(d3_geo_azimuthalEqualArea);
  }).raw = d3_geo_azimuthalEqualArea;
  var d3_geo_azimuthalEquidistant = d3_geo_azimuthal(function(cosλcosφ) {
    var c = Math.acos(cosλcosφ);
    return c && c / Math.sin(c);
  }, d3_identity);
  (d3.geo.azimuthalEquidistant = function() {
    return d3_geo_projection(d3_geo_azimuthalEquidistant);
  }).raw = d3_geo_azimuthalEquidistant;
  function d3_geo_conicConformal(φ0, φ1) {
    var cosφ0 = Math.cos(φ0), t = function(φ) {
      return Math.tan(π / 4 + φ / 2);
    }, n = φ0 === φ1 ? Math.sin(φ0) : Math.log(cosφ0 / Math.cos(φ1)) / Math.log(t(φ1) / t(φ0)), F = cosφ0 * Math.pow(t(φ0), n) / n;
    if (!n) return d3_geo_mercator;
    function forward(λ, φ) {
      if (F > 0) {
        if (φ < -halfπ + ε) φ = -halfπ + ε;
      } else {
        if (φ > halfπ - ε) φ = halfπ - ε;
      }
      var ρ = F / Math.pow(t(φ), n);
      return [ ρ * Math.sin(n * λ), F - ρ * Math.cos(n * λ) ];
    }
    forward.invert = function(x, y) {
      var ρ0_y = F - y, ρ = d3_sgn(n) * Math.sqrt(x * x + ρ0_y * ρ0_y);
      return [ Math.atan2(x, ρ0_y) / n, 2 * Math.atan(Math.pow(F / ρ, 1 / n)) - halfπ ];
    };
    return forward;
  }
  (d3.geo.conicConformal = function() {
    return d3_geo_conic(d3_geo_conicConformal);
  }).raw = d3_geo_conicConformal;
  function d3_geo_conicEquidistant(φ0, φ1) {
    var cosφ0 = Math.cos(φ0), n = φ0 === φ1 ? Math.sin(φ0) : (cosφ0 - Math.cos(φ1)) / (φ1 - φ0), G = cosφ0 / n + φ0;
    if (abs(n) < ε) return d3_geo_equirectangular;
    function forward(λ, φ) {
      var ρ = G - φ;
      return [ ρ * Math.sin(n * λ), G - ρ * Math.cos(n * λ) ];
    }
    forward.invert = function(x, y) {
      var ρ0_y = G - y;
      return [ Math.atan2(x, ρ0_y) / n, G - d3_sgn(n) * Math.sqrt(x * x + ρ0_y * ρ0_y) ];
    };
    return forward;
  }
  (d3.geo.conicEquidistant = function() {
    return d3_geo_conic(d3_geo_conicEquidistant);
  }).raw = d3_geo_conicEquidistant;
  var d3_geo_gnomonic = d3_geo_azimuthal(function(cosλcosφ) {
    return 1 / cosλcosφ;
  }, Math.atan);
  (d3.geo.gnomonic = function() {
    return d3_geo_projection(d3_geo_gnomonic);
  }).raw = d3_geo_gnomonic;
  function d3_geo_mercator(λ, φ) {
    return [ λ, Math.log(Math.tan(π / 4 + φ / 2)) ];
  }
  d3_geo_mercator.invert = function(x, y) {
    return [ x, 2 * Math.atan(Math.exp(y)) - halfπ ];
  };
  function d3_geo_mercatorProjection(project) {
    var m = d3_geo_projection(project), scale = m.scale, translate = m.translate, clipExtent = m.clipExtent, clipAuto;
    m.scale = function() {
      var v = scale.apply(m, arguments);
      return v === m ? clipAuto ? m.clipExtent(null) : m : v;
    };
    m.translate = function() {
      var v = translate.apply(m, arguments);
      return v === m ? clipAuto ? m.clipExtent(null) : m : v;
    };
    m.clipExtent = function(_) {
      var v = clipExtent.apply(m, arguments);
      if (v === m) {
        if (clipAuto = _ == null) {
          var k = π * scale(), t = translate();
          clipExtent([ [ t[0] - k, t[1] - k ], [ t[0] + k, t[1] + k ] ]);
        }
      } else if (clipAuto) {
        v = null;
      }
      return v;
    };
    return m.clipExtent(null);
  }
  (d3.geo.mercator = function() {
    return d3_geo_mercatorProjection(d3_geo_mercator);
  }).raw = d3_geo_mercator;
  var d3_geo_orthographic = d3_geo_azimuthal(function() {
    return 1;
  }, Math.asin);
  (d3.geo.orthographic = function() {
    return d3_geo_projection(d3_geo_orthographic);
  }).raw = d3_geo_orthographic;
  var d3_geo_stereographic = d3_geo_azimuthal(function(cosλcosφ) {
    return 1 / (1 + cosλcosφ);
  }, function(ρ) {
    return 2 * Math.atan(ρ);
  });
  (d3.geo.stereographic = function() {
    return d3_geo_projection(d3_geo_stereographic);
  }).raw = d3_geo_stereographic;
  function d3_geo_transverseMercator(λ, φ) {
    return [ Math.log(Math.tan(π / 4 + φ / 2)), -λ ];
  }
  d3_geo_transverseMercator.invert = function(x, y) {
    return [ -y, 2 * Math.atan(Math.exp(x)) - halfπ ];
  };
  (d3.geo.transverseMercator = function() {
    var projection = d3_geo_mercatorProjection(d3_geo_transverseMercator), center = projection.center, rotate = projection.rotate;
    projection.center = function(_) {
      return _ ? center([ -_[1], _[0] ]) : (_ = center(), [ _[1], -_[0] ]);
    };
    projection.rotate = function(_) {
      return _ ? rotate([ _[0], _[1], _.length > 2 ? _[2] + 90 : 90 ]) : (_ = rotate(), 
      [ _[0], _[1], _[2] - 90 ]);
    };
    return rotate([ 0, 0, 90 ]);
  }).raw = d3_geo_transverseMercator;
  d3.geom = {};
  function d3_geom_pointX(d) {
    return d[0];
  }
  function d3_geom_pointY(d) {
    return d[1];
  }
  d3.geom.hull = function(vertices) {
    var x = d3_geom_pointX, y = d3_geom_pointY;
    if (arguments.length) return hull(vertices);
    function hull(data) {
      if (data.length < 3) return [];
      var fx = d3_functor(x), fy = d3_functor(y), i, n = data.length, points = [], flippedPoints = [];
      for (i = 0; i < n; i++) {
        points.push([ +fx.call(this, data[i], i), +fy.call(this, data[i], i), i ]);
      }
      points.sort(d3_geom_hullOrder);
      for (i = 0; i < n; i++) flippedPoints.push([ points[i][0], -points[i][1] ]);
      var upper = d3_geom_hullUpper(points), lower = d3_geom_hullUpper(flippedPoints);
      var skipLeft = lower[0] === upper[0], skipRight = lower[lower.length - 1] === upper[upper.length - 1], polygon = [];
      for (i = upper.length - 1; i >= 0; --i) polygon.push(data[points[upper[i]][2]]);
      for (i = +skipLeft; i < lower.length - skipRight; ++i) polygon.push(data[points[lower[i]][2]]);
      return polygon;
    }
    hull.x = function(_) {
      return arguments.length ? (x = _, hull) : x;
    };
    hull.y = function(_) {
      return arguments.length ? (y = _, hull) : y;
    };
    return hull;
  };
  function d3_geom_hullUpper(points) {
    var n = points.length, hull = [ 0, 1 ], hs = 2;
    for (var i = 2; i < n; i++) {
      while (hs > 1 && d3_cross2d(points[hull[hs - 2]], points[hull[hs - 1]], points[i]) <= 0) --hs;
      hull[hs++] = i;
    }
    return hull.slice(0, hs);
  }
  function d3_geom_hullOrder(a, b) {
    return a[0] - b[0] || a[1] - b[1];
  }
  d3.geom.polygon = function(coordinates) {
    d3_subclass(coordinates, d3_geom_polygonPrototype);
    return coordinates;
  };
  var d3_geom_polygonPrototype = d3.geom.polygon.prototype = [];
  d3_geom_polygonPrototype.area = function() {
    var i = -1, n = this.length, a, b = this[n - 1], area = 0;
    while (++i < n) {
      a = b;
      b = this[i];
      area += a[1] * b[0] - a[0] * b[1];
    }
    return area * .5;
  };
  d3_geom_polygonPrototype.centroid = function(k) {
    var i = -1, n = this.length, x = 0, y = 0, a, b = this[n - 1], c;
    if (!arguments.length) k = -1 / (6 * this.area());
    while (++i < n) {
      a = b;
      b = this[i];
      c = a[0] * b[1] - b[0] * a[1];
      x += (a[0] + b[0]) * c;
      y += (a[1] + b[1]) * c;
    }
    return [ x * k, y * k ];
  };
  d3_geom_polygonPrototype.clip = function(subject) {
    var input, closed = d3_geom_polygonClosed(subject), i = -1, n = this.length - d3_geom_polygonClosed(this), j, m, a = this[n - 1], b, c, d;
    while (++i < n) {
      input = subject.slice();
      subject.length = 0;
      b = this[i];
      c = input[(m = input.length - closed) - 1];
      j = -1;
      while (++j < m) {
        d = input[j];
        if (d3_geom_polygonInside(d, a, b)) {
          if (!d3_geom_polygonInside(c, a, b)) {
            subject.push(d3_geom_polygonIntersect(c, d, a, b));
          }
          subject.push(d);
        } else if (d3_geom_polygonInside(c, a, b)) {
          subject.push(d3_geom_polygonIntersect(c, d, a, b));
        }
        c = d;
      }
      if (closed) subject.push(subject[0]);
      a = b;
    }
    return subject;
  };
  function d3_geom_polygonInside(p, a, b) {
    return (b[0] - a[0]) * (p[1] - a[1]) < (b[1] - a[1]) * (p[0] - a[0]);
  }
  function d3_geom_polygonIntersect(c, d, a, b) {
    var x1 = c[0], x3 = a[0], x21 = d[0] - x1, x43 = b[0] - x3, y1 = c[1], y3 = a[1], y21 = d[1] - y1, y43 = b[1] - y3, ua = (x43 * (y1 - y3) - y43 * (x1 - x3)) / (y43 * x21 - x43 * y21);
    return [ x1 + ua * x21, y1 + ua * y21 ];
  }
  function d3_geom_polygonClosed(coordinates) {
    var a = coordinates[0], b = coordinates[coordinates.length - 1];
    return !(a[0] - b[0] || a[1] - b[1]);
  }
  var d3_geom_voronoiEdges, d3_geom_voronoiCells, d3_geom_voronoiBeaches, d3_geom_voronoiBeachPool = [], d3_geom_voronoiFirstCircle, d3_geom_voronoiCircles, d3_geom_voronoiCirclePool = [];
  function d3_geom_voronoiBeach() {
    d3_geom_voronoiRedBlackNode(this);
    this.edge = this.site = this.circle = null;
  }
  function d3_geom_voronoiCreateBeach(site) {
    var beach = d3_geom_voronoiBeachPool.pop() || new d3_geom_voronoiBeach();
    beach.site = site;
    return beach;
  }
  function d3_geom_voronoiDetachBeach(beach) {
    d3_geom_voronoiDetachCircle(beach);
    d3_geom_voronoiBeaches.remove(beach);
    d3_geom_voronoiBeachPool.push(beach);
    d3_geom_voronoiRedBlackNode(beach);
  }
  function d3_geom_voronoiRemoveBeach(beach) {
    var circle = beach.circle, x = circle.x, y = circle.cy, vertex = {
      x: x,
      y: y
    }, previous = beach.P, next = beach.N, disappearing = [ beach ];
    d3_geom_voronoiDetachBeach(beach);
    var lArc = previous;
    while (lArc.circle && abs(x - lArc.circle.x) < ε && abs(y - lArc.circle.cy) < ε) {
      previous = lArc.P;
      disappearing.unshift(lArc);
      d3_geom_voronoiDetachBeach(lArc);
      lArc = previous;
    }
    disappearing.unshift(lArc);
    d3_geom_voronoiDetachCircle(lArc);
    var rArc = next;
    while (rArc.circle && abs(x - rArc.circle.x) < ε && abs(y - rArc.circle.cy) < ε) {
      next = rArc.N;
      disappearing.push(rArc);
      d3_geom_voronoiDetachBeach(rArc);
      rArc = next;
    }
    disappearing.push(rArc);
    d3_geom_voronoiDetachCircle(rArc);
    var nArcs = disappearing.length, iArc;
    for (iArc = 1; iArc < nArcs; ++iArc) {
      rArc = disappearing[iArc];
      lArc = disappearing[iArc - 1];
      d3_geom_voronoiSetEdgeEnd(rArc.edge, lArc.site, rArc.site, vertex);
    }
    lArc = disappearing[0];
    rArc = disappearing[nArcs - 1];
    rArc.edge = d3_geom_voronoiCreateEdge(lArc.site, rArc.site, null, vertex);
    d3_geom_voronoiAttachCircle(lArc);
    d3_geom_voronoiAttachCircle(rArc);
  }
  function d3_geom_voronoiAddBeach(site) {
    var x = site.x, directrix = site.y, lArc, rArc, dxl, dxr, node = d3_geom_voronoiBeaches._;
    while (node) {
      dxl = d3_geom_voronoiLeftBreakPoint(node, directrix) - x;
      if (dxl > ε) node = node.L; else {
        dxr = x - d3_geom_voronoiRightBreakPoint(node, directrix);
        if (dxr > ε) {
          if (!node.R) {
            lArc = node;
            break;
          }
          node = node.R;
        } else {
          if (dxl > -ε) {
            lArc = node.P;
            rArc = node;
          } else if (dxr > -ε) {
            lArc = node;
            rArc = node.N;
          } else {
            lArc = rArc = node;
          }
          break;
        }
      }
    }
    var newArc = d3_geom_voronoiCreateBeach(site);
    d3_geom_voronoiBeaches.insert(lArc, newArc);
    if (!lArc && !rArc) return;
    if (lArc === rArc) {
      d3_geom_voronoiDetachCircle(lArc);
      rArc = d3_geom_voronoiCreateBeach(lArc.site);
      d3_geom_voronoiBeaches.insert(newArc, rArc);
      newArc.edge = rArc.edge = d3_geom_voronoiCreateEdge(lArc.site, newArc.site);
      d3_geom_voronoiAttachCircle(lArc);
      d3_geom_voronoiAttachCircle(rArc);
      return;
    }
    if (!rArc) {
      newArc.edge = d3_geom_voronoiCreateEdge(lArc.site, newArc.site);
      return;
    }
    d3_geom_voronoiDetachCircle(lArc);
    d3_geom_voronoiDetachCircle(rArc);
    var lSite = lArc.site, ax = lSite.x, ay = lSite.y, bx = site.x - ax, by = site.y - ay, rSite = rArc.site, cx = rSite.x - ax, cy = rSite.y - ay, d = 2 * (bx * cy - by * cx), hb = bx * bx + by * by, hc = cx * cx + cy * cy, vertex = {
      x: (cy * hb - by * hc) / d + ax,
      y: (bx * hc - cx * hb) / d + ay
    };
    d3_geom_voronoiSetEdgeEnd(rArc.edge, lSite, rSite, vertex);
    newArc.edge = d3_geom_voronoiCreateEdge(lSite, site, null, vertex);
    rArc.edge = d3_geom_voronoiCreateEdge(site, rSite, null, vertex);
    d3_geom_voronoiAttachCircle(lArc);
    d3_geom_voronoiAttachCircle(rArc);
  }
  function d3_geom_voronoiLeftBreakPoint(arc, directrix) {
    var site = arc.site, rfocx = site.x, rfocy = site.y, pby2 = rfocy - directrix;
    if (!pby2) return rfocx;
    var lArc = arc.P;
    if (!lArc) return -Infinity;
    site = lArc.site;
    var lfocx = site.x, lfocy = site.y, plby2 = lfocy - directrix;
    if (!plby2) return lfocx;
    var hl = lfocx - rfocx, aby2 = 1 / pby2 - 1 / plby2, b = hl / plby2;
    if (aby2) return (-b + Math.sqrt(b * b - 2 * aby2 * (hl * hl / (-2 * plby2) - lfocy + plby2 / 2 + rfocy - pby2 / 2))) / aby2 + rfocx;
    return (rfocx + lfocx) / 2;
  }
  function d3_geom_voronoiRightBreakPoint(arc, directrix) {
    var rArc = arc.N;
    if (rArc) return d3_geom_voronoiLeftBreakPoint(rArc, directrix);
    var site = arc.site;
    return site.y === directrix ? site.x : Infinity;
  }
  function d3_geom_voronoiCell(site) {
    this.site = site;
    this.edges = [];
  }
  d3_geom_voronoiCell.prototype.prepare = function() {
    var halfEdges = this.edges, iHalfEdge = halfEdges.length, edge;
    while (iHalfEdge--) {
      edge = halfEdges[iHalfEdge].edge;
      if (!edge.b || !edge.a) halfEdges.splice(iHalfEdge, 1);
    }
    halfEdges.sort(d3_geom_voronoiHalfEdgeOrder);
    return halfEdges.length;
  };
  function d3_geom_voronoiCloseCells(extent) {
    var x0 = extent[0][0], x1 = extent[1][0], y0 = extent[0][1], y1 = extent[1][1], x2, y2, x3, y3, cells = d3_geom_voronoiCells, iCell = cells.length, cell, iHalfEdge, halfEdges, nHalfEdges, start, end;
    while (iCell--) {
      cell = cells[iCell];
      if (!cell || !cell.prepare()) continue;
      halfEdges = cell.edges;
      nHalfEdges = halfEdges.length;
      iHalfEdge = 0;
      while (iHalfEdge < nHalfEdges) {
        end = halfEdges[iHalfEdge].end(), x3 = end.x, y3 = end.y;
        start = halfEdges[++iHalfEdge % nHalfEdges].start(), x2 = start.x, y2 = start.y;
        if (abs(x3 - x2) > ε || abs(y3 - y2) > ε) {
          halfEdges.splice(iHalfEdge, 0, new d3_geom_voronoiHalfEdge(d3_geom_voronoiCreateBorderEdge(cell.site, end, abs(x3 - x0) < ε && y1 - y3 > ε ? {
            x: x0,
            y: abs(x2 - x0) < ε ? y2 : y1
          } : abs(y3 - y1) < ε && x1 - x3 > ε ? {
            x: abs(y2 - y1) < ε ? x2 : x1,
            y: y1
          } : abs(x3 - x1) < ε && y3 - y0 > ε ? {
            x: x1,
            y: abs(x2 - x1) < ε ? y2 : y0
          } : abs(y3 - y0) < ε && x3 - x0 > ε ? {
            x: abs(y2 - y0) < ε ? x2 : x0,
            y: y0
          } : null), cell.site, null));
          ++nHalfEdges;
        }
      }
    }
  }
  function d3_geom_voronoiHalfEdgeOrder(a, b) {
    return b.angle - a.angle;
  }
  function d3_geom_voronoiCircle() {
    d3_geom_voronoiRedBlackNode(this);
    this.x = this.y = this.arc = this.site = this.cy = null;
  }
  function d3_geom_voronoiAttachCircle(arc) {
    var lArc = arc.P, rArc = arc.N;
    if (!lArc || !rArc) return;
    var lSite = lArc.site, cSite = arc.site, rSite = rArc.site;
    if (lSite === rSite) return;
    var bx = cSite.x, by = cSite.y, ax = lSite.x - bx, ay = lSite.y - by, cx = rSite.x - bx, cy = rSite.y - by;
    var d = 2 * (ax * cy - ay * cx);
    if (d >= -ε2) return;
    var ha = ax * ax + ay * ay, hc = cx * cx + cy * cy, x = (cy * ha - ay * hc) / d, y = (ax * hc - cx * ha) / d, cy = y + by;
    var circle = d3_geom_voronoiCirclePool.pop() || new d3_geom_voronoiCircle();
    circle.arc = arc;
    circle.site = cSite;
    circle.x = x + bx;
    circle.y = cy + Math.sqrt(x * x + y * y);
    circle.cy = cy;
    arc.circle = circle;
    var before = null, node = d3_geom_voronoiCircles._;
    while (node) {
      if (circle.y < node.y || circle.y === node.y && circle.x <= node.x) {
        if (node.L) node = node.L; else {
          before = node.P;
          break;
        }
      } else {
        if (node.R) node = node.R; else {
          before = node;
          break;
        }
      }
    }
    d3_geom_voronoiCircles.insert(before, circle);
    if (!before) d3_geom_voronoiFirstCircle = circle;
  }
  function d3_geom_voronoiDetachCircle(arc) {
    var circle = arc.circle;
    if (circle) {
      if (!circle.P) d3_geom_voronoiFirstCircle = circle.N;
      d3_geom_voronoiCircles.remove(circle);
      d3_geom_voronoiCirclePool.push(circle);
      d3_geom_voronoiRedBlackNode(circle);
      arc.circle = null;
    }
  }
  function d3_geom_voronoiClipEdges(extent) {
    var edges = d3_geom_voronoiEdges, clip = d3_geom_clipLine(extent[0][0], extent[0][1], extent[1][0], extent[1][1]), i = edges.length, e;
    while (i--) {
      e = edges[i];
      if (!d3_geom_voronoiConnectEdge(e, extent) || !clip(e) || abs(e.a.x - e.b.x) < ε && abs(e.a.y - e.b.y) < ε) {
        e.a = e.b = null;
        edges.splice(i, 1);
      }
    }
  }
  function d3_geom_voronoiConnectEdge(edge, extent) {
    var vb = edge.b;
    if (vb) return true;
    var va = edge.a, x0 = extent[0][0], x1 = extent[1][0], y0 = extent[0][1], y1 = extent[1][1], lSite = edge.l, rSite = edge.r, lx = lSite.x, ly = lSite.y, rx = rSite.x, ry = rSite.y, fx = (lx + rx) / 2, fy = (ly + ry) / 2, fm, fb;
    if (ry === ly) {
      if (fx < x0 || fx >= x1) return;
      if (lx > rx) {
        if (!va) va = {
          x: fx,
          y: y0
        }; else if (va.y >= y1) return;
        vb = {
          x: fx,
          y: y1
        };
      } else {
        if (!va) va = {
          x: fx,
          y: y1
        }; else if (va.y < y0) return;
        vb = {
          x: fx,
          y: y0
        };
      }
    } else {
      fm = (lx - rx) / (ry - ly);
      fb = fy - fm * fx;
      if (fm < -1 || fm > 1) {
        if (lx > rx) {
          if (!va) va = {
            x: (y0 - fb) / fm,
            y: y0
          }; else if (va.y >= y1) return;
          vb = {
            x: (y1 - fb) / fm,
            y: y1
          };
        } else {
          if (!va) va = {
            x: (y1 - fb) / fm,
            y: y1
          }; else if (va.y < y0) return;
          vb = {
            x: (y0 - fb) / fm,
            y: y0
          };
        }
      } else {
        if (ly < ry) {
          if (!va) va = {
            x: x0,
            y: fm * x0 + fb
          }; else if (va.x >= x1) return;
          vb = {
            x: x1,
            y: fm * x1 + fb
          };
        } else {
          if (!va) va = {
            x: x1,
            y: fm * x1 + fb
          }; else if (va.x < x0) return;
          vb = {
            x: x0,
            y: fm * x0 + fb
          };
        }
      }
    }
    edge.a = va;
    edge.b = vb;
    return true;
  }
  function d3_geom_voronoiEdge(lSite, rSite) {
    this.l = lSite;
    this.r = rSite;
    this.a = this.b = null;
  }
  function d3_geom_voronoiCreateEdge(lSite, rSite, va, vb) {
    var edge = new d3_geom_voronoiEdge(lSite, rSite);
    d3_geom_voronoiEdges.push(edge);
    if (va) d3_geom_voronoiSetEdgeEnd(edge, lSite, rSite, va);
    if (vb) d3_geom_voronoiSetEdgeEnd(edge, rSite, lSite, vb);
    d3_geom_voronoiCells[lSite.i].edges.push(new d3_geom_voronoiHalfEdge(edge, lSite, rSite));
    d3_geom_voronoiCells[rSite.i].edges.push(new d3_geom_voronoiHalfEdge(edge, rSite, lSite));
    return edge;
  }
  function d3_geom_voronoiCreateBorderEdge(lSite, va, vb) {
    var edge = new d3_geom_voronoiEdge(lSite, null);
    edge.a = va;
    edge.b = vb;
    d3_geom_voronoiEdges.push(edge);
    return edge;
  }
  function d3_geom_voronoiSetEdgeEnd(edge, lSite, rSite, vertex) {
    if (!edge.a && !edge.b) {
      edge.a = vertex;
      edge.l = lSite;
      edge.r = rSite;
    } else if (edge.l === rSite) {
      edge.b = vertex;
    } else {
      edge.a = vertex;
    }
  }
  function d3_geom_voronoiHalfEdge(edge, lSite, rSite) {
    var va = edge.a, vb = edge.b;
    this.edge = edge;
    this.site = lSite;
    this.angle = rSite ? Math.atan2(rSite.y - lSite.y, rSite.x - lSite.x) : edge.l === lSite ? Math.atan2(vb.x - va.x, va.y - vb.y) : Math.atan2(va.x - vb.x, vb.y - va.y);
  }
  d3_geom_voronoiHalfEdge.prototype = {
    start: function() {
      return this.edge.l === this.site ? this.edge.a : this.edge.b;
    },
    end: function() {
      return this.edge.l === this.site ? this.edge.b : this.edge.a;
    }
  };
  function d3_geom_voronoiRedBlackTree() {
    this._ = null;
  }
  function d3_geom_voronoiRedBlackNode(node) {
    node.U = node.C = node.L = node.R = node.P = node.N = null;
  }
  d3_geom_voronoiRedBlackTree.prototype = {
    insert: function(after, node) {
      var parent, grandpa, uncle;
      if (after) {
        node.P = after;
        node.N = after.N;
        if (after.N) after.N.P = node;
        after.N = node;
        if (after.R) {
          after = after.R;
          while (after.L) after = after.L;
          after.L = node;
        } else {
          after.R = node;
        }
        parent = after;
      } else if (this._) {
        after = d3_geom_voronoiRedBlackFirst(this._);
        node.P = null;
        node.N = after;
        after.P = after.L = node;
        parent = after;
      } else {
        node.P = node.N = null;
        this._ = node;
        parent = null;
      }
      node.L = node.R = null;
      node.U = parent;
      node.C = true;
      after = node;
      while (parent && parent.C) {
        grandpa = parent.U;
        if (parent === grandpa.L) {
          uncle = grandpa.R;
          if (uncle && uncle.C) {
            parent.C = uncle.C = false;
            grandpa.C = true;
            after = grandpa;
          } else {
            if (after === parent.R) {
              d3_geom_voronoiRedBlackRotateLeft(this, parent);
              after = parent;
              parent = after.U;
            }
            parent.C = false;
            grandpa.C = true;
            d3_geom_voronoiRedBlackRotateRight(this, grandpa);
          }
        } else {
          uncle = grandpa.L;
          if (uncle && uncle.C) {
            parent.C = uncle.C = false;
            grandpa.C = true;
            after = grandpa;
          } else {
            if (after === parent.L) {
              d3_geom_voronoiRedBlackRotateRight(this, parent);
              after = parent;
              parent = after.U;
            }
            parent.C = false;
            grandpa.C = true;
            d3_geom_voronoiRedBlackRotateLeft(this, grandpa);
          }
        }
        parent = after.U;
      }
      this._.C = false;
    },
    remove: function(node) {
      if (node.N) node.N.P = node.P;
      if (node.P) node.P.N = node.N;
      node.N = node.P = null;
      var parent = node.U, sibling, left = node.L, right = node.R, next, red;
      if (!left) next = right; else if (!right) next = left; else next = d3_geom_voronoiRedBlackFirst(right);
      if (parent) {
        if (parent.L === node) parent.L = next; else parent.R = next;
      } else {
        this._ = next;
      }
      if (left && right) {
        red = next.C;
        next.C = node.C;
        next.L = left;
        left.U = next;
        if (next !== right) {
          parent = next.U;
          next.U = node.U;
          node = next.R;
          parent.L = node;
          next.R = right;
          right.U = next;
        } else {
          next.U = parent;
          parent = next;
          node = next.R;
        }
      } else {
        red = node.C;
        node = next;
      }
      if (node) node.U = parent;
      if (red) return;
      if (node && node.C) {
        node.C = false;
        return;
      }
      do {
        if (node === this._) break;
        if (node === parent.L) {
          sibling = parent.R;
          if (sibling.C) {
            sibling.C = false;
            parent.C = true;
            d3_geom_voronoiRedBlackRotateLeft(this, parent);
            sibling = parent.R;
          }
          if (sibling.L && sibling.L.C || sibling.R && sibling.R.C) {
            if (!sibling.R || !sibling.R.C) {
              sibling.L.C = false;
              sibling.C = true;
              d3_geom_voronoiRedBlackRotateRight(this, sibling);
              sibling = parent.R;
            }
            sibling.C = parent.C;
            parent.C = sibling.R.C = false;
            d3_geom_voronoiRedBlackRotateLeft(this, parent);
            node = this._;
            break;
          }
        } else {
          sibling = parent.L;
          if (sibling.C) {
            sibling.C = false;
            parent.C = true;
            d3_geom_voronoiRedBlackRotateRight(this, parent);
            sibling = parent.L;
          }
          if (sibling.L && sibling.L.C || sibling.R && sibling.R.C) {
            if (!sibling.L || !sibling.L.C) {
              sibling.R.C = false;
              sibling.C = true;
              d3_geom_voronoiRedBlackRotateLeft(this, sibling);
              sibling = parent.L;
            }
            sibling.C = parent.C;
            parent.C = sibling.L.C = false;
            d3_geom_voronoiRedBlackRotateRight(this, parent);
            node = this._;
            break;
          }
        }
        sibling.C = true;
        node = parent;
        parent = parent.U;
      } while (!node.C);
      if (node) node.C = false;
    }
  };
  function d3_geom_voronoiRedBlackRotateLeft(tree, node) {
    var p = node, q = node.R, parent = p.U;
    if (parent) {
      if (parent.L === p) parent.L = q; else parent.R = q;
    } else {
      tree._ = q;
    }
    q.U = parent;
    p.U = q;
    p.R = q.L;
    if (p.R) p.R.U = p;
    q.L = p;
  }
  function d3_geom_voronoiRedBlackRotateRight(tree, node) {
    var p = node, q = node.L, parent = p.U;
    if (parent) {
      if (parent.L === p) parent.L = q; else parent.R = q;
    } else {
      tree._ = q;
    }
    q.U = parent;
    p.U = q;
    p.L = q.R;
    if (p.L) p.L.U = p;
    q.R = p;
  }
  function d3_geom_voronoiRedBlackFirst(node) {
    while (node.L) node = node.L;
    return node;
  }
  function d3_geom_voronoi(sites, bbox) {
    var site = sites.sort(d3_geom_voronoiVertexOrder).pop(), x0, y0, circle;
    d3_geom_voronoiEdges = [];
    d3_geom_voronoiCells = new Array(sites.length);
    d3_geom_voronoiBeaches = new d3_geom_voronoiRedBlackTree();
    d3_geom_voronoiCircles = new d3_geom_voronoiRedBlackTree();
    while (true) {
      circle = d3_geom_voronoiFirstCircle;
      if (site && (!circle || site.y < circle.y || site.y === circle.y && site.x < circle.x)) {
        if (site.x !== x0 || site.y !== y0) {
          d3_geom_voronoiCells[site.i] = new d3_geom_voronoiCell(site);
          d3_geom_voronoiAddBeach(site);
          x0 = site.x, y0 = site.y;
        }
        site = sites.pop();
      } else if (circle) {
        d3_geom_voronoiRemoveBeach(circle.arc);
      } else {
        break;
      }
    }
    if (bbox) d3_geom_voronoiClipEdges(bbox), d3_geom_voronoiCloseCells(bbox);
    var diagram = {
      cells: d3_geom_voronoiCells,
      edges: d3_geom_voronoiEdges
    };
    d3_geom_voronoiBeaches = d3_geom_voronoiCircles = d3_geom_voronoiEdges = d3_geom_voronoiCells = null;
    return diagram;
  }
  function d3_geom_voronoiVertexOrder(a, b) {
    return b.y - a.y || b.x - a.x;
  }
  d3.geom.voronoi = function(points) {
    var x = d3_geom_pointX, y = d3_geom_pointY, fx = x, fy = y, clipExtent = d3_geom_voronoiClipExtent;
    if (points) return voronoi(points);
    function voronoi(data) {
      var polygons = new Array(data.length), x0 = clipExtent[0][0], y0 = clipExtent[0][1], x1 = clipExtent[1][0], y1 = clipExtent[1][1];
      d3_geom_voronoi(sites(data), clipExtent).cells.forEach(function(cell, i) {
        var edges = cell.edges, site = cell.site, polygon = polygons[i] = edges.length ? edges.map(function(e) {
          var s = e.start();
          return [ s.x, s.y ];
        }) : site.x >= x0 && site.x <= x1 && site.y >= y0 && site.y <= y1 ? [ [ x0, y1 ], [ x1, y1 ], [ x1, y0 ], [ x0, y0 ] ] : [];
        polygon.point = data[i];
      });
      return polygons;
    }
    function sites(data) {
      return data.map(function(d, i) {
        return {
          x: Math.round(fx(d, i) / ε) * ε,
          y: Math.round(fy(d, i) / ε) * ε,
          i: i
        };
      });
    }
    voronoi.links = function(data) {
      return d3_geom_voronoi(sites(data)).edges.filter(function(edge) {
        return edge.l && edge.r;
      }).map(function(edge) {
        return {
          source: data[edge.l.i],
          target: data[edge.r.i]
        };
      });
    };
    voronoi.triangles = function(data) {
      var triangles = [];
      d3_geom_voronoi(sites(data)).cells.forEach(function(cell, i) {
        var site = cell.site, edges = cell.edges.sort(d3_geom_voronoiHalfEdgeOrder), j = -1, m = edges.length, e0, s0, e1 = edges[m - 1].edge, s1 = e1.l === site ? e1.r : e1.l;
        while (++j < m) {
          e0 = e1;
          s0 = s1;
          e1 = edges[j].edge;
          s1 = e1.l === site ? e1.r : e1.l;
          if (i < s0.i && i < s1.i && d3_geom_voronoiTriangleArea(site, s0, s1) < 0) {
            triangles.push([ data[i], data[s0.i], data[s1.i] ]);
          }
        }
      });
      return triangles;
    };
    voronoi.x = function(_) {
      return arguments.length ? (fx = d3_functor(x = _), voronoi) : x;
    };
    voronoi.y = function(_) {
      return arguments.length ? (fy = d3_functor(y = _), voronoi) : y;
    };
    voronoi.clipExtent = function(_) {
      if (!arguments.length) return clipExtent === d3_geom_voronoiClipExtent ? null : clipExtent;
      clipExtent = _ == null ? d3_geom_voronoiClipExtent : _;
      return voronoi;
    };
    voronoi.size = function(_) {
      if (!arguments.length) return clipExtent === d3_geom_voronoiClipExtent ? null : clipExtent && clipExtent[1];
      return voronoi.clipExtent(_ && [ [ 0, 0 ], _ ]);
    };
    return voronoi;
  };
  var d3_geom_voronoiClipExtent = [ [ -1e6, -1e6 ], [ 1e6, 1e6 ] ];
  function d3_geom_voronoiTriangleArea(a, b, c) {
    return (a.x - c.x) * (b.y - a.y) - (a.x - b.x) * (c.y - a.y);
  }
  d3.geom.delaunay = function(vertices) {
    return d3.geom.voronoi().triangles(vertices);
  };
  d3.geom.quadtree = function(points, x1, y1, x2, y2) {
    var x = d3_geom_pointX, y = d3_geom_pointY, compat;
    if (compat = arguments.length) {
      x = d3_geom_quadtreeCompatX;
      y = d3_geom_quadtreeCompatY;
      if (compat === 3) {
        y2 = y1;
        x2 = x1;
        y1 = x1 = 0;
      }
      return quadtree(points);
    }
    function quadtree(data) {
      var d, fx = d3_functor(x), fy = d3_functor(y), xs, ys, i, n, x1_, y1_, x2_, y2_;
      if (x1 != null) {
        x1_ = x1, y1_ = y1, x2_ = x2, y2_ = y2;
      } else {
        x2_ = y2_ = -(x1_ = y1_ = Infinity);
        xs = [], ys = [];
        n = data.length;
        if (compat) for (i = 0; i < n; ++i) {
          d = data[i];
          if (d.x < x1_) x1_ = d.x;
          if (d.y < y1_) y1_ = d.y;
          if (d.x > x2_) x2_ = d.x;
          if (d.y > y2_) y2_ = d.y;
          xs.push(d.x);
          ys.push(d.y);
        } else for (i = 0; i < n; ++i) {
          var x_ = +fx(d = data[i], i), y_ = +fy(d, i);
          if (x_ < x1_) x1_ = x_;
          if (y_ < y1_) y1_ = y_;
          if (x_ > x2_) x2_ = x_;
          if (y_ > y2_) y2_ = y_;
          xs.push(x_);
          ys.push(y_);
        }
      }
      var dx = x2_ - x1_, dy = y2_ - y1_;
      if (dx > dy) y2_ = y1_ + dx; else x2_ = x1_ + dy;
      function insert(n, d, x, y, x1, y1, x2, y2) {
        if (isNaN(x) || isNaN(y)) return;
        if (n.leaf) {
          var nx = n.x, ny = n.y;
          if (nx != null) {
            if (abs(nx - x) + abs(ny - y) < .01) {
              insertChild(n, d, x, y, x1, y1, x2, y2);
            } else {
              var nPoint = n.point;
              n.x = n.y = n.point = null;
              insertChild(n, nPoint, nx, ny, x1, y1, x2, y2);
              insertChild(n, d, x, y, x1, y1, x2, y2);
            }
          } else {
            n.x = x, n.y = y, n.point = d;
          }
        } else {
          insertChild(n, d, x, y, x1, y1, x2, y2);
        }
      }
      function insertChild(n, d, x, y, x1, y1, x2, y2) {
        var xm = (x1 + x2) * .5, ym = (y1 + y2) * .5, right = x >= xm, below = y >= ym, i = below << 1 | right;
        n.leaf = false;
        n = n.nodes[i] || (n.nodes[i] = d3_geom_quadtreeNode());
        if (right) x1 = xm; else x2 = xm;
        if (below) y1 = ym; else y2 = ym;
        insert(n, d, x, y, x1, y1, x2, y2);
      }
      var root = d3_geom_quadtreeNode();
      root.add = function(d) {
        insert(root, d, +fx(d, ++i), +fy(d, i), x1_, y1_, x2_, y2_);
      };
      root.visit = function(f) {
        d3_geom_quadtreeVisit(f, root, x1_, y1_, x2_, y2_);
      };
      root.find = function(point) {
        return d3_geom_quadtreeFind(root, point[0], point[1], x1_, y1_, x2_, y2_);
      };
      i = -1;
      if (x1 == null) {
        while (++i < n) {
          insert(root, data[i], xs[i], ys[i], x1_, y1_, x2_, y2_);
        }
        --i;
      } else data.forEach(root.add);
      xs = ys = data = d = null;
      return root;
    }
    quadtree.x = function(_) {
      return arguments.length ? (x = _, quadtree) : x;
    };
    quadtree.y = function(_) {
      return arguments.length ? (y = _, quadtree) : y;
    };
    quadtree.extent = function(_) {
      if (!arguments.length) return x1 == null ? null : [ [ x1, y1 ], [ x2, y2 ] ];
      if (_ == null) x1 = y1 = x2 = y2 = null; else x1 = +_[0][0], y1 = +_[0][1], x2 = +_[1][0], 
      y2 = +_[1][1];
      return quadtree;
    };
    quadtree.size = function(_) {
      if (!arguments.length) return x1 == null ? null : [ x2 - x1, y2 - y1 ];
      if (_ == null) x1 = y1 = x2 = y2 = null; else x1 = y1 = 0, x2 = +_[0], y2 = +_[1];
      return quadtree;
    };
    return quadtree;
  };
  function d3_geom_quadtreeCompatX(d) {
    return d.x;
  }
  function d3_geom_quadtreeCompatY(d) {
    return d.y;
  }
  function d3_geom_quadtreeNode() {
    return {
      leaf: true,
      nodes: [],
      point: null,
      x: null,
      y: null
    };
  }
  function d3_geom_quadtreeVisit(f, node, x1, y1, x2, y2) {
    if (!f(node, x1, y1, x2, y2)) {
      var sx = (x1 + x2) * .5, sy = (y1 + y2) * .5, children = node.nodes;
      if (children[0]) d3_geom_quadtreeVisit(f, children[0], x1, y1, sx, sy);
      if (children[1]) d3_geom_quadtreeVisit(f, children[1], sx, y1, x2, sy);
      if (children[2]) d3_geom_quadtreeVisit(f, children[2], x1, sy, sx, y2);
      if (children[3]) d3_geom_quadtreeVisit(f, children[3], sx, sy, x2, y2);
    }
  }
  function d3_geom_quadtreeFind(root, x, y, x0, y0, x3, y3) {
    var minDistance2 = Infinity, closestPoint;
    (function find(node, x1, y1, x2, y2) {
      if (x1 > x3 || y1 > y3 || x2 < x0 || y2 < y0) return;
      if (point = node.point) {
        var point, dx = x - node.x, dy = y - node.y, distance2 = dx * dx + dy * dy;
        if (distance2 < minDistance2) {
          var distance = Math.sqrt(minDistance2 = distance2);
          x0 = x - distance, y0 = y - distance;
          x3 = x + distance, y3 = y + distance;
          closestPoint = point;
        }
      }
      var children = node.nodes, xm = (x1 + x2) * .5, ym = (y1 + y2) * .5, right = x >= xm, below = y >= ym;
      for (var i = below << 1 | right, j = i + 4; i < j; ++i) {
        if (node = children[i & 3]) switch (i & 3) {
         case 0:
          find(node, x1, y1, xm, ym);
          break;

         case 1:
          find(node, xm, y1, x2, ym);
          break;

         case 2:
          find(node, x1, ym, xm, y2);
          break;

         case 3:
          find(node, xm, ym, x2, y2);
          break;
        }
      }
    })(root, x0, y0, x3, y3);
    return closestPoint;
  }
  d3.interpolateRgb = d3_interpolateRgb;
  function d3_interpolateRgb(a, b) {
    a = d3.rgb(a);
    b = d3.rgb(b);
    var ar = a.r, ag = a.g, ab = a.b, br = b.r - ar, bg = b.g - ag, bb = b.b - ab;
    return function(t) {
      return "#" + d3_rgb_hex(Math.round(ar + br * t)) + d3_rgb_hex(Math.round(ag + bg * t)) + d3_rgb_hex(Math.round(ab + bb * t));
    };
  }
  d3.interpolateObject = d3_interpolateObject;
  function d3_interpolateObject(a, b) {
    var i = {}, c = {}, k;
    for (k in a) {
      if (k in b) {
        i[k] = d3_interpolate(a[k], b[k]);
      } else {
        c[k] = a[k];
      }
    }
    for (k in b) {
      if (!(k in a)) {
        c[k] = b[k];
      }
    }
    return function(t) {
      for (k in i) c[k] = i[k](t);
      return c;
    };
  }
  d3.interpolateNumber = d3_interpolateNumber;
  function d3_interpolateNumber(a, b) {
    a = +a, b = +b;
    return function(t) {
      return a * (1 - t) + b * t;
    };
  }
  d3.interpolateString = d3_interpolateString;
  function d3_interpolateString(a, b) {
    var bi = d3_interpolate_numberA.lastIndex = d3_interpolate_numberB.lastIndex = 0, am, bm, bs, i = -1, s = [], q = [];
    a = a + "", b = b + "";
    while ((am = d3_interpolate_numberA.exec(a)) && (bm = d3_interpolate_numberB.exec(b))) {
      if ((bs = bm.index) > bi) {
        bs = b.slice(bi, bs);
        if (s[i]) s[i] += bs; else s[++i] = bs;
      }
      if ((am = am[0]) === (bm = bm[0])) {
        if (s[i]) s[i] += bm; else s[++i] = bm;
      } else {
        s[++i] = null;
        q.push({
          i: i,
          x: d3_interpolateNumber(am, bm)
        });
      }
      bi = d3_interpolate_numberB.lastIndex;
    }
    if (bi < b.length) {
      bs = b.slice(bi);
      if (s[i]) s[i] += bs; else s[++i] = bs;
    }
    return s.length < 2 ? q[0] ? (b = q[0].x, function(t) {
      return b(t) + "";
    }) : function() {
      return b;
    } : (b = q.length, function(t) {
      for (var i = 0, o; i < b; ++i) s[(o = q[i]).i] = o.x(t);
      return s.join("");
    });
  }
  var d3_interpolate_numberA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g, d3_interpolate_numberB = new RegExp(d3_interpolate_numberA.source, "g");
  d3.interpolate = d3_interpolate;
  function d3_interpolate(a, b) {
    var i = d3.interpolators.length, f;
    while (--i >= 0 && !(f = d3.interpolators[i](a, b))) ;
    return f;
  }
  d3.interpolators = [ function(a, b) {
    var t = typeof b;
    return (t === "string" ? d3_rgb_names.has(b.toLowerCase()) || /^(#|rgb\(|hsl\()/i.test(b) ? d3_interpolateRgb : d3_interpolateString : b instanceof d3_color ? d3_interpolateRgb : Array.isArray(b) ? d3_interpolateArray : t === "object" && isNaN(b) ? d3_interpolateObject : d3_interpolateNumber)(a, b);
  } ];
  d3.interpolateArray = d3_interpolateArray;
  function d3_interpolateArray(a, b) {
    var x = [], c = [], na = a.length, nb = b.length, n0 = Math.min(a.length, b.length), i;
    for (i = 0; i < n0; ++i) x.push(d3_interpolate(a[i], b[i]));
    for (;i < na; ++i) c[i] = a[i];
    for (;i < nb; ++i) c[i] = b[i];
    return function(t) {
      for (i = 0; i < n0; ++i) c[i] = x[i](t);
      return c;
    };
  }
  var d3_ease_default = function() {
    return d3_identity;
  };
  var d3_ease = d3.map({
    linear: d3_ease_default,
    poly: d3_ease_poly,
    quad: function() {
      return d3_ease_quad;
    },
    cubic: function() {
      return d3_ease_cubic;
    },
    sin: function() {
      return d3_ease_sin;
    },
    exp: function() {
      return d3_ease_exp;
    },
    circle: function() {
      return d3_ease_circle;
    },
    elastic: d3_ease_elastic,
    back: d3_ease_back,
    bounce: function() {
      return d3_ease_bounce;
    }
  });
  var d3_ease_mode = d3.map({
    "in": d3_identity,
    out: d3_ease_reverse,
    "in-out": d3_ease_reflect,
    "out-in": function(f) {
      return d3_ease_reflect(d3_ease_reverse(f));
    }
  });
  d3.ease = function(name) {
    var i = name.indexOf("-"), t = i >= 0 ? name.slice(0, i) : name, m = i >= 0 ? name.slice(i + 1) : "in";
    t = d3_ease.get(t) || d3_ease_default;
    m = d3_ease_mode.get(m) || d3_identity;
    return d3_ease_clamp(m(t.apply(null, d3_arraySlice.call(arguments, 1))));
  };
  function d3_ease_clamp(f) {
    return function(t) {
      return t <= 0 ? 0 : t >= 1 ? 1 : f(t);
    };
  }
  function d3_ease_reverse(f) {
    return function(t) {
      return 1 - f(1 - t);
    };
  }
  function d3_ease_reflect(f) {
    return function(t) {
      return .5 * (t < .5 ? f(2 * t) : 2 - f(2 - 2 * t));
    };
  }
  function d3_ease_quad(t) {
    return t * t;
  }
  function d3_ease_cubic(t) {
    return t * t * t;
  }
  function d3_ease_cubicInOut(t) {
    if (t <= 0) return 0;
    if (t >= 1) return 1;
    var t2 = t * t, t3 = t2 * t;
    return 4 * (t < .5 ? t3 : 3 * (t - t2) + t3 - .75);
  }
  function d3_ease_poly(e) {
    return function(t) {
      return Math.pow(t, e);
    };
  }
  function d3_ease_sin(t) {
    return 1 - Math.cos(t * halfπ);
  }
  function d3_ease_exp(t) {
    return Math.pow(2, 10 * (t - 1));
  }
  function d3_ease_circle(t) {
    return 1 - Math.sqrt(1 - t * t);
  }
  function d3_ease_elastic(a, p) {
    var s;
    if (arguments.length < 2) p = .45;
    if (arguments.length) s = p / τ * Math.asin(1 / a); else a = 1, s = p / 4;
    return function(t) {
      return 1 + a * Math.pow(2, -10 * t) * Math.sin((t - s) * τ / p);
    };
  }
  function d3_ease_back(s) {
    if (!s) s = 1.70158;
    return function(t) {
      return t * t * ((s + 1) * t - s);
    };
  }
  function d3_ease_bounce(t) {
    return t < 1 / 2.75 ? 7.5625 * t * t : t < 2 / 2.75 ? 7.5625 * (t -= 1.5 / 2.75) * t + .75 : t < 2.5 / 2.75 ? 7.5625 * (t -= 2.25 / 2.75) * t + .9375 : 7.5625 * (t -= 2.625 / 2.75) * t + .984375;
  }
  d3.interpolateHcl = d3_interpolateHcl;
  function d3_interpolateHcl(a, b) {
    a = d3.hcl(a);
    b = d3.hcl(b);
    var ah = a.h, ac = a.c, al = a.l, bh = b.h - ah, bc = b.c - ac, bl = b.l - al;
    if (isNaN(bc)) bc = 0, ac = isNaN(ac) ? b.c : ac;
    if (isNaN(bh)) bh = 0, ah = isNaN(ah) ? b.h : ah; else if (bh > 180) bh -= 360; else if (bh < -180) bh += 360;
    return function(t) {
      return d3_hcl_lab(ah + bh * t, ac + bc * t, al + bl * t) + "";
    };
  }
  d3.interpolateHsl = d3_interpolateHsl;
  function d3_interpolateHsl(a, b) {
    a = d3.hsl(a);
    b = d3.hsl(b);
    var ah = a.h, as = a.s, al = a.l, bh = b.h - ah, bs = b.s - as, bl = b.l - al;
    if (isNaN(bs)) bs = 0, as = isNaN(as) ? b.s : as;
    if (isNaN(bh)) bh = 0, ah = isNaN(ah) ? b.h : ah; else if (bh > 180) bh -= 360; else if (bh < -180) bh += 360;
    return function(t) {
      return d3_hsl_rgb(ah + bh * t, as + bs * t, al + bl * t) + "";
    };
  }
  d3.interpolateLab = d3_interpolateLab;
  function d3_interpolateLab(a, b) {
    a = d3.lab(a);
    b = d3.lab(b);
    var al = a.l, aa = a.a, ab = a.b, bl = b.l - al, ba = b.a - aa, bb = b.b - ab;
    return function(t) {
      return d3_lab_rgb(al + bl * t, aa + ba * t, ab + bb * t) + "";
    };
  }
  d3.interpolateRound = d3_interpolateRound;
  function d3_interpolateRound(a, b) {
    b -= a;
    return function(t) {
      return Math.round(a + b * t);
    };
  }
  d3.transform = function(string) {
    var g = d3_document.createElementNS(d3.ns.prefix.svg, "g");
    return (d3.transform = function(string) {
      if (string != null) {
        g.setAttribute("transform", string);
        var t = g.transform.baseVal.consolidate();
      }
      return new d3_transform(t ? t.matrix : d3_transformIdentity);
    })(string);
  };
  function d3_transform(m) {
    var r0 = [ m.a, m.b ], r1 = [ m.c, m.d ], kx = d3_transformNormalize(r0), kz = d3_transformDot(r0, r1), ky = d3_transformNormalize(d3_transformCombine(r1, r0, -kz)) || 0;
    if (r0[0] * r1[1] < r1[0] * r0[1]) {
      r0[0] *= -1;
      r0[1] *= -1;
      kx *= -1;
      kz *= -1;
    }
    this.rotate = (kx ? Math.atan2(r0[1], r0[0]) : Math.atan2(-r1[0], r1[1])) * d3_degrees;
    this.translate = [ m.e, m.f ];
    this.scale = [ kx, ky ];
    this.skew = ky ? Math.atan2(kz, ky) * d3_degrees : 0;
  }
  d3_transform.prototype.toString = function() {
    return "translate(" + this.translate + ")rotate(" + this.rotate + ")skewX(" + this.skew + ")scale(" + this.scale + ")";
  };
  function d3_transformDot(a, b) {
    return a[0] * b[0] + a[1] * b[1];
  }
  function d3_transformNormalize(a) {
    var k = Math.sqrt(d3_transformDot(a, a));
    if (k) {
      a[0] /= k;
      a[1] /= k;
    }
    return k;
  }
  function d3_transformCombine(a, b, k) {
    a[0] += k * b[0];
    a[1] += k * b[1];
    return a;
  }
  var d3_transformIdentity = {
    a: 1,
    b: 0,
    c: 0,
    d: 1,
    e: 0,
    f: 0
  };
  d3.interpolateTransform = d3_interpolateTransform;
  function d3_interpolateTransformPop(s) {
    return s.length ? s.pop() + "," : "";
  }
  function d3_interpolateTranslate(ta, tb, s, q) {
    if (ta[0] !== tb[0] || ta[1] !== tb[1]) {
      var i = s.push("translate(", null, ",", null, ")");
      q.push({
        i: i - 4,
        x: d3_interpolateNumber(ta[0], tb[0])
      }, {
        i: i - 2,
        x: d3_interpolateNumber(ta[1], tb[1])
      });
    } else if (tb[0] || tb[1]) {
      s.push("translate(" + tb + ")");
    }
  }
  function d3_interpolateRotate(ra, rb, s, q) {
    if (ra !== rb) {
      if (ra - rb > 180) rb += 360; else if (rb - ra > 180) ra += 360;
      q.push({
        i: s.push(d3_interpolateTransformPop(s) + "rotate(", null, ")") - 2,
        x: d3_interpolateNumber(ra, rb)
      });
    } else if (rb) {
      s.push(d3_interpolateTransformPop(s) + "rotate(" + rb + ")");
    }
  }
  function d3_interpolateSkew(wa, wb, s, q) {
    if (wa !== wb) {
      q.push({
        i: s.push(d3_interpolateTransformPop(s) + "skewX(", null, ")") - 2,
        x: d3_interpolateNumber(wa, wb)
      });
    } else if (wb) {
      s.push(d3_interpolateTransformPop(s) + "skewX(" + wb + ")");
    }
  }
  function d3_interpolateScale(ka, kb, s, q) {
    if (ka[0] !== kb[0] || ka[1] !== kb[1]) {
      var i = s.push(d3_interpolateTransformPop(s) + "scale(", null, ",", null, ")");
      q.push({
        i: i - 4,
        x: d3_interpolateNumber(ka[0], kb[0])
      }, {
        i: i - 2,
        x: d3_interpolateNumber(ka[1], kb[1])
      });
    } else if (kb[0] !== 1 || kb[1] !== 1) {
      s.push(d3_interpolateTransformPop(s) + "scale(" + kb + ")");
    }
  }
  function d3_interpolateTransform(a, b) {
    var s = [], q = [];
    a = d3.transform(a), b = d3.transform(b);
    d3_interpolateTranslate(a.translate, b.translate, s, q);
    d3_interpolateRotate(a.rotate, b.rotate, s, q);
    d3_interpolateSkew(a.skew, b.skew, s, q);
    d3_interpolateScale(a.scale, b.scale, s, q);
    a = b = null;
    return function(t) {
      var i = -1, n = q.length, o;
      while (++i < n) s[(o = q[i]).i] = o.x(t);
      return s.join("");
    };
  }
  function d3_uninterpolateNumber(a, b) {
    b = (b -= a = +a) || 1 / b;
    return function(x) {
      return (x - a) / b;
    };
  }
  function d3_uninterpolateClamp(a, b) {
    b = (b -= a = +a) || 1 / b;
    return function(x) {
      return Math.max(0, Math.min(1, (x - a) / b));
    };
  }
  d3.layout = {};
  d3.layout.bundle = function() {
    return function(links) {
      var paths = [], i = -1, n = links.length;
      while (++i < n) paths.push(d3_layout_bundlePath(links[i]));
      return paths;
    };
  };
  function d3_layout_bundlePath(link) {
    var start = link.source, end = link.target, lca = d3_layout_bundleLeastCommonAncestor(start, end), points = [ start ];
    while (start !== lca) {
      start = start.parent;
      points.push(start);
    }
    var k = points.length;
    while (end !== lca) {
      points.splice(k, 0, end);
      end = end.parent;
    }
    return points;
  }
  function d3_layout_bundleAncestors(node) {
    var ancestors = [], parent = node.parent;
    while (parent != null) {
      ancestors.push(node);
      node = parent;
      parent = parent.parent;
    }
    ancestors.push(node);
    return ancestors;
  }
  function d3_layout_bundleLeastCommonAncestor(a, b) {
    if (a === b) return a;
    var aNodes = d3_layout_bundleAncestors(a), bNodes = d3_layout_bundleAncestors(b), aNode = aNodes.pop(), bNode = bNodes.pop(), sharedNode = null;
    while (aNode === bNode) {
      sharedNode = aNode;
      aNode = aNodes.pop();
      bNode = bNodes.pop();
    }
    return sharedNode;
  }
  d3.layout.chord = function() {
    var chord = {}, chords, groups, matrix, n, padding = 0, sortGroups, sortSubgroups, sortChords;
    function relayout() {
      var subgroups = {}, groupSums = [], groupIndex = d3.range(n), subgroupIndex = [], k, x, x0, i, j;
      chords = [];
      groups = [];
      k = 0, i = -1;
      while (++i < n) {
        x = 0, j = -1;
        while (++j < n) {
          x += matrix[i][j];
        }
        groupSums.push(x);
        subgroupIndex.push(d3.range(n));
        k += x;
      }
      if (sortGroups) {
        groupIndex.sort(function(a, b) {
          return sortGroups(groupSums[a], groupSums[b]);
        });
      }
      if (sortSubgroups) {
        subgroupIndex.forEach(function(d, i) {
          d.sort(function(a, b) {
            return sortSubgroups(matrix[i][a], matrix[i][b]);
          });
        });
      }
      k = (τ - padding * n) / k;
      x = 0, i = -1;
      while (++i < n) {
        x0 = x, j = -1;
        while (++j < n) {
          var di = groupIndex[i], dj = subgroupIndex[di][j], v = matrix[di][dj], a0 = x, a1 = x += v * k;
          subgroups[di + "-" + dj] = {
            index: di,
            subindex: dj,
            startAngle: a0,
            endAngle: a1,
            value: v
          };
        }
        groups[di] = {
          index: di,
          startAngle: x0,
          endAngle: x,
          value: groupSums[di]
        };
        x += padding;
      }
      i = -1;
      while (++i < n) {
        j = i - 1;
        while (++j < n) {
          var source = subgroups[i + "-" + j], target = subgroups[j + "-" + i];
          if (source.value || target.value) {
            chords.push(source.value < target.value ? {
              source: target,
              target: source
            } : {
              source: source,
              target: target
            });
          }
        }
      }
      if (sortChords) resort();
    }
    function resort() {
      chords.sort(function(a, b) {
        return sortChords((a.source.value + a.target.value) / 2, (b.source.value + b.target.value) / 2);
      });
    }
    chord.matrix = function(x) {
      if (!arguments.length) return matrix;
      n = (matrix = x) && matrix.length;
      chords = groups = null;
      return chord;
    };
    chord.padding = function(x) {
      if (!arguments.length) return padding;
      padding = x;
      chords = groups = null;
      return chord;
    };
    chord.sortGroups = function(x) {
      if (!arguments.length) return sortGroups;
      sortGroups = x;
      chords = groups = null;
      return chord;
    };
    chord.sortSubgroups = function(x) {
      if (!arguments.length) return sortSubgroups;
      sortSubgroups = x;
      chords = null;
      return chord;
    };
    chord.sortChords = function(x) {
      if (!arguments.length) return sortChords;
      sortChords = x;
      if (chords) resort();
      return chord;
    };
    chord.chords = function() {
      if (!chords) relayout();
      return chords;
    };
    chord.groups = function() {
      if (!groups) relayout();
      return groups;
    };
    return chord;
  };
  d3.layout.force = function() {
    var force = {}, event = d3.dispatch("start", "tick", "end"), timer, size = [ 1, 1 ], drag, alpha, friction = .9, linkDistance = d3_layout_forceLinkDistance, linkStrength = d3_layout_forceLinkStrength, charge = -30, chargeDistance2 = d3_layout_forceChargeDistance2, gravity = .1, theta2 = .64, nodes = [], links = [], distances, strengths, charges;
    function repulse(node) {
      return function(quad, x1, _, x2) {
        if (quad.point !== node) {
          var dx = quad.cx - node.x, dy = quad.cy - node.y, dw = x2 - x1, dn = dx * dx + dy * dy;
          if (dw * dw / theta2 < dn) {
            if (dn < chargeDistance2) {
              var k = quad.charge / dn;
              node.px -= dx * k;
              node.py -= dy * k;
            }
            return true;
          }
          if (quad.point && dn && dn < chargeDistance2) {
            var k = quad.pointCharge / dn;
            node.px -= dx * k;
            node.py -= dy * k;
          }
        }
        return !quad.charge;
      };
    }
    force.tick = function() {
      if ((alpha *= .99) < .005) {
        timer = null;
        event.end({
          type: "end",
          alpha: alpha = 0
        });
        return true;
      }
      var n = nodes.length, m = links.length, q, i, o, s, t, l, k, x, y;
      for (i = 0; i < m; ++i) {
        o = links[i];
        s = o.source;
        t = o.target;
        x = t.x - s.x;
        y = t.y - s.y;
        if (l = x * x + y * y) {
          l = alpha * strengths[i] * ((l = Math.sqrt(l)) - distances[i]) / l;
          x *= l;
          y *= l;
          t.x -= x * (k = s.weight + t.weight ? s.weight / (s.weight + t.weight) : .5);
          t.y -= y * k;
          s.x += x * (k = 1 - k);
          s.y += y * k;
        }
      }
      if (k = alpha * gravity) {
        x = size[0] / 2;
        y = size[1] / 2;
        i = -1;
        if (k) while (++i < n) {
          o = nodes[i];
          o.x += (x - o.x) * k;
          o.y += (y - o.y) * k;
        }
      }
      if (charge) {
        d3_layout_forceAccumulate(q = d3.geom.quadtree(nodes), alpha, charges);
        i = -1;
        while (++i < n) {
          if (!(o = nodes[i]).fixed) {
            q.visit(repulse(o));
          }
        }
      }
      i = -1;
      while (++i < n) {
        o = nodes[i];
        if (o.fixed) {
          o.x = o.px;
          o.y = o.py;
        } else {
          o.x -= (o.px - (o.px = o.x)) * friction;
          o.y -= (o.py - (o.py = o.y)) * friction;
        }
      }
      event.tick({
        type: "tick",
        alpha: alpha
      });
    };
    force.nodes = function(x) {
      if (!arguments.length) return nodes;
      nodes = x;
      return force;
    };
    force.links = function(x) {
      if (!arguments.length) return links;
      links = x;
      return force;
    };
    force.size = function(x) {
      if (!arguments.length) return size;
      size = x;
      return force;
    };
    force.linkDistance = function(x) {
      if (!arguments.length) return linkDistance;
      linkDistance = typeof x === "function" ? x : +x;
      return force;
    };
    force.distance = force.linkDistance;
    force.linkStrength = function(x) {
      if (!arguments.length) return linkStrength;
      linkStrength = typeof x === "function" ? x : +x;
      return force;
    };
    force.friction = function(x) {
      if (!arguments.length) return friction;
      friction = +x;
      return force;
    };
    force.charge = function(x) {
      if (!arguments.length) return charge;
      charge = typeof x === "function" ? x : +x;
      return force;
    };
    force.chargeDistance = function(x) {
      if (!arguments.length) return Math.sqrt(chargeDistance2);
      chargeDistance2 = x * x;
      return force;
    };
    force.gravity = function(x) {
      if (!arguments.length) return gravity;
      gravity = +x;
      return force;
    };
    force.theta = function(x) {
      if (!arguments.length) return Math.sqrt(theta2);
      theta2 = x * x;
      return force;
    };
    force.alpha = function(x) {
      if (!arguments.length) return alpha;
      x = +x;
      if (alpha) {
        if (x > 0) {
          alpha = x;
        } else {
          timer.c = null, timer.t = NaN, timer = null;
          event.end({
            type: "end",
            alpha: alpha = 0
          });
        }
      } else if (x > 0) {
        event.start({
          type: "start",
          alpha: alpha = x
        });
        timer = d3_timer(force.tick);
      }
      return force;
    };
    force.start = function() {
      var i, n = nodes.length, m = links.length, w = size[0], h = size[1], neighbors, o;
      for (i = 0; i < n; ++i) {
        (o = nodes[i]).index = i;
        o.weight = 0;
      }
      for (i = 0; i < m; ++i) {
        o = links[i];
        if (typeof o.source == "number") o.source = nodes[o.source];
        if (typeof o.target == "number") o.target = nodes[o.target];
        ++o.source.weight;
        ++o.target.weight;
      }
      for (i = 0; i < n; ++i) {
        o = nodes[i];
        if (isNaN(o.x)) o.x = position("x", w);
        if (isNaN(o.y)) o.y = position("y", h);
        if (isNaN(o.px)) o.px = o.x;
        if (isNaN(o.py)) o.py = o.y;
      }
      distances = [];
      if (typeof linkDistance === "function") for (i = 0; i < m; ++i) distances[i] = +linkDistance.call(this, links[i], i); else for (i = 0; i < m; ++i) distances[i] = linkDistance;
      strengths = [];
      if (typeof linkStrength === "function") for (i = 0; i < m; ++i) strengths[i] = +linkStrength.call(this, links[i], i); else for (i = 0; i < m; ++i) strengths[i] = linkStrength;
      charges = [];
      if (typeof charge === "function") for (i = 0; i < n; ++i) charges[i] = +charge.call(this, nodes[i], i); else for (i = 0; i < n; ++i) charges[i] = charge;
      function position(dimension, size) {
        if (!neighbors) {
          neighbors = new Array(n);
          for (j = 0; j < n; ++j) {
            neighbors[j] = [];
          }
          for (j = 0; j < m; ++j) {
            var o = links[j];
            neighbors[o.source.index].push(o.target);
            neighbors[o.target.index].push(o.source);
          }
        }
        var candidates = neighbors[i], j = -1, l = candidates.length, x;
        while (++j < l) if (!isNaN(x = candidates[j][dimension])) return x;
        return Math.random() * size;
      }
      return force.resume();
    };
    force.resume = function() {
      return force.alpha(.1);
    };
    force.stop = function() {
      return force.alpha(0);
    };
    force.drag = function() {
      if (!drag) drag = d3.behavior.drag().origin(d3_identity).on("dragstart.force", d3_layout_forceDragstart).on("drag.force", dragmove).on("dragend.force", d3_layout_forceDragend);
      if (!arguments.length) return drag;
      this.on("mouseover.force", d3_layout_forceMouseover).on("mouseout.force", d3_layout_forceMouseout).call(drag);
    };
    function dragmove(d) {
      d.px = d3.event.x, d.py = d3.event.y;
      force.resume();
    }
    return d3.rebind(force, event, "on");
  };
  function d3_layout_forceDragstart(d) {
    d.fixed |= 2;
  }
  function d3_layout_forceDragend(d) {
    d.fixed &= ~6;
  }
  function d3_layout_forceMouseover(d) {
    d.fixed |= 4;
    d.px = d.x, d.py = d.y;
  }
  function d3_layout_forceMouseout(d) {
    d.fixed &= ~4;
  }
  function d3_layout_forceAccumulate(quad, alpha, charges) {
    var cx = 0, cy = 0;
    quad.charge = 0;
    if (!quad.leaf) {
      var nodes = quad.nodes, n = nodes.length, i = -1, c;
      while (++i < n) {
        c = nodes[i];
        if (c == null) continue;
        d3_layout_forceAccumulate(c, alpha, charges);
        quad.charge += c.charge;
        cx += c.charge * c.cx;
        cy += c.charge * c.cy;
      }
    }
    if (quad.point) {
      if (!quad.leaf) {
        quad.point.x += Math.random() - .5;
        quad.point.y += Math.random() - .5;
      }
      var k = alpha * charges[quad.point.index];
      quad.charge += quad.pointCharge = k;
      cx += k * quad.point.x;
      cy += k * quad.point.y;
    }
    quad.cx = cx / quad.charge;
    quad.cy = cy / quad.charge;
  }
  var d3_layout_forceLinkDistance = 20, d3_layout_forceLinkStrength = 1, d3_layout_forceChargeDistance2 = Infinity;
  d3.layout.hierarchy = function() {
    var sort = d3_layout_hierarchySort, children = d3_layout_hierarchyChildren, value = d3_layout_hierarchyValue;
    function hierarchy(root) {
      var stack = [ root ], nodes = [], node;
      root.depth = 0;
      while ((node = stack.pop()) != null) {
        nodes.push(node);
        if ((childs = children.call(hierarchy, node, node.depth)) && (n = childs.length)) {
          var n, childs, child;
          while (--n >= 0) {
            stack.push(child = childs[n]);
            child.parent = node;
            child.depth = node.depth + 1;
          }
          if (value) node.value = 0;
          node.children = childs;
        } else {
          if (value) node.value = +value.call(hierarchy, node, node.depth) || 0;
          delete node.children;
        }
      }
      d3_layout_hierarchyVisitAfter(root, function(node) {
        var childs, parent;
        if (sort && (childs = node.children)) childs.sort(sort);
        if (value && (parent = node.parent)) parent.value += node.value;
      });
      return nodes;
    }
    hierarchy.sort = function(x) {
      if (!arguments.length) return sort;
      sort = x;
      return hierarchy;
    };
    hierarchy.children = function(x) {
      if (!arguments.length) return children;
      children = x;
      return hierarchy;
    };
    hierarchy.value = function(x) {
      if (!arguments.length) return value;
      value = x;
      return hierarchy;
    };
    hierarchy.revalue = function(root) {
      if (value) {
        d3_layout_hierarchyVisitBefore(root, function(node) {
          if (node.children) node.value = 0;
        });
        d3_layout_hierarchyVisitAfter(root, function(node) {
          var parent;
          if (!node.children) node.value = +value.call(hierarchy, node, node.depth) || 0;
          if (parent = node.parent) parent.value += node.value;
        });
      }
      return root;
    };
    return hierarchy;
  };
  function d3_layout_hierarchyRebind(object, hierarchy) {
    d3.rebind(object, hierarchy, "sort", "children", "value");
    object.nodes = object;
    object.links = d3_layout_hierarchyLinks;
    return object;
  }
  function d3_layout_hierarchyVisitBefore(node, callback) {
    var nodes = [ node ];
    while ((node = nodes.pop()) != null) {
      callback(node);
      if ((children = node.children) && (n = children.length)) {
        var n, children;
        while (--n >= 0) nodes.push(children[n]);
      }
    }
  }
  function d3_layout_hierarchyVisitAfter(node, callback) {
    var nodes = [ node ], nodes2 = [];
    while ((node = nodes.pop()) != null) {
      nodes2.push(node);
      if ((children = node.children) && (n = children.length)) {
        var i = -1, n, children;
        while (++i < n) nodes.push(children[i]);
      }
    }
    while ((node = nodes2.pop()) != null) {
      callback(node);
    }
  }
  function d3_layout_hierarchyChildren(d) {
    return d.children;
  }
  function d3_layout_hierarchyValue(d) {
    return d.value;
  }
  function d3_layout_hierarchySort(a, b) {
    return b.value - a.value;
  }
  function d3_layout_hierarchyLinks(nodes) {
    return d3.merge(nodes.map(function(parent) {
      return (parent.children || []).map(function(child) {
        return {
          source: parent,
          target: child
        };
      });
    }));
  }
  d3.layout.partition = function() {
    var hierarchy = d3.layout.hierarchy(), size = [ 1, 1 ];
    function position(node, x, dx, dy) {
      var children = node.children;
      node.x = x;
      node.y = node.depth * dy;
      node.dx = dx;
      node.dy = dy;
      if (children && (n = children.length)) {
        var i = -1, n, c, d;
        dx = node.value ? dx / node.value : 0;
        while (++i < n) {
          position(c = children[i], x, d = c.value * dx, dy);
          x += d;
        }
      }
    }
    function depth(node) {
      var children = node.children, d = 0;
      if (children && (n = children.length)) {
        var i = -1, n;
        while (++i < n) d = Math.max(d, depth(children[i]));
      }
      return 1 + d;
    }
    function partition(d, i) {
      var nodes = hierarchy.call(this, d, i);
      position(nodes[0], 0, size[0], size[1] / depth(nodes[0]));
      return nodes;
    }
    partition.size = function(x) {
      if (!arguments.length) return size;
      size = x;
      return partition;
    };
    return d3_layout_hierarchyRebind(partition, hierarchy);
  };
  d3.layout.pie = function() {
    var value = Number, sort = d3_layout_pieSortByValue, startAngle = 0, endAngle = τ, padAngle = 0;
    function pie(data) {
      var n = data.length, values = data.map(function(d, i) {
        return +value.call(pie, d, i);
      }), a = +(typeof startAngle === "function" ? startAngle.apply(this, arguments) : startAngle), da = (typeof endAngle === "function" ? endAngle.apply(this, arguments) : endAngle) - a, p = Math.min(Math.abs(da) / n, +(typeof padAngle === "function" ? padAngle.apply(this, arguments) : padAngle)), pa = p * (da < 0 ? -1 : 1), sum = d3.sum(values), k = sum ? (da - n * pa) / sum : 0, index = d3.range(n), arcs = [], v;
      if (sort != null) index.sort(sort === d3_layout_pieSortByValue ? function(i, j) {
        return values[j] - values[i];
      } : function(i, j) {
        return sort(data[i], data[j]);
      });
      index.forEach(function(i) {
        arcs[i] = {
          data: data[i],
          value: v = values[i],
          startAngle: a,
          endAngle: a += v * k + pa,
          padAngle: p
        };
      });
      return arcs;
    }
    pie.value = function(_) {
      if (!arguments.length) return value;
      value = _;
      return pie;
    };
    pie.sort = function(_) {
      if (!arguments.length) return sort;
      sort = _;
      return pie;
    };
    pie.startAngle = function(_) {
      if (!arguments.length) return startAngle;
      startAngle = _;
      return pie;
    };
    pie.endAngle = function(_) {
      if (!arguments.length) return endAngle;
      endAngle = _;
      return pie;
    };
    pie.padAngle = function(_) {
      if (!arguments.length) return padAngle;
      padAngle = _;
      return pie;
    };
    return pie;
  };
  var d3_layout_pieSortByValue = {};
  d3.layout.stack = function() {
    var values = d3_identity, order = d3_layout_stackOrderDefault, offset = d3_layout_stackOffsetZero, out = d3_layout_stackOut, x = d3_layout_stackX, y = d3_layout_stackY;
    function stack(data, index) {
      if (!(n = data.length)) return data;
      var series = data.map(function(d, i) {
        return values.call(stack, d, i);
      });
      var points = series.map(function(d) {
        return d.map(function(v, i) {
          return [ x.call(stack, v, i), y.call(stack, v, i) ];
        });
      });
      var orders = order.call(stack, points, index);
      series = d3.permute(series, orders);
      points = d3.permute(points, orders);
      var offsets = offset.call(stack, points, index);
      var m = series[0].length, n, i, j, o;
      for (j = 0; j < m; ++j) {
        out.call(stack, series[0][j], o = offsets[j], points[0][j][1]);
        for (i = 1; i < n; ++i) {
          out.call(stack, series[i][j], o += points[i - 1][j][1], points[i][j][1]);
        }
      }
      return data;
    }
    stack.values = function(x) {
      if (!arguments.length) return values;
      values = x;
      return stack;
    };
    stack.order = function(x) {
      if (!arguments.length) return order;
      order = typeof x === "function" ? x : d3_layout_stackOrders.get(x) || d3_layout_stackOrderDefault;
      return stack;
    };
    stack.offset = function(x) {
      if (!arguments.length) return offset;
      offset = typeof x === "function" ? x : d3_layout_stackOffsets.get(x) || d3_layout_stackOffsetZero;
      return stack;
    };
    stack.x = function(z) {
      if (!arguments.length) return x;
      x = z;
      return stack;
    };
    stack.y = function(z) {
      if (!arguments.length) return y;
      y = z;
      return stack;
    };
    stack.out = function(z) {
      if (!arguments.length) return out;
      out = z;
      return stack;
    };
    return stack;
  };
  function d3_layout_stackX(d) {
    return d.x;
  }
  function d3_layout_stackY(d) {
    return d.y;
  }
  function d3_layout_stackOut(d, y0, y) {
    d.y0 = y0;
    d.y = y;
  }
  var d3_layout_stackOrders = d3.map({
    "inside-out": function(data) {
      var n = data.length, i, j, max = data.map(d3_layout_stackMaxIndex), sums = data.map(d3_layout_stackReduceSum), index = d3.range(n).sort(function(a, b) {
        return max[a] - max[b];
      }), top = 0, bottom = 0, tops = [], bottoms = [];
      for (i = 0; i < n; ++i) {
        j = index[i];
        if (top < bottom) {
          top += sums[j];
          tops.push(j);
        } else {
          bottom += sums[j];
          bottoms.push(j);
        }
      }
      return bottoms.reverse().concat(tops);
    },
    reverse: function(data) {
      return d3.range(data.length).reverse();
    },
    "default": d3_layout_stackOrderDefault
  });
  var d3_layout_stackOffsets = d3.map({
    silhouette: function(data) {
      var n = data.length, m = data[0].length, sums = [], max = 0, i, j, o, y0 = [];
      for (j = 0; j < m; ++j) {
        for (i = 0, o = 0; i < n; i++) o += data[i][j][1];
        if (o > max) max = o;
        sums.push(o);
      }
      for (j = 0; j < m; ++j) {
        y0[j] = (max - sums[j]) / 2;
      }
      return y0;
    },
    wiggle: function(data) {
      var n = data.length, x = data[0], m = x.length, i, j, k, s1, s2, s3, dx, o, o0, y0 = [];
      y0[0] = o = o0 = 0;
      for (j = 1; j < m; ++j) {
        for (i = 0, s1 = 0; i < n; ++i) s1 += data[i][j][1];
        for (i = 0, s2 = 0, dx = x[j][0] - x[j - 1][0]; i < n; ++i) {
          for (k = 0, s3 = (data[i][j][1] - data[i][j - 1][1]) / (2 * dx); k < i; ++k) {
            s3 += (data[k][j][1] - data[k][j - 1][1]) / dx;
          }
          s2 += s3 * data[i][j][1];
        }
        y0[j] = o -= s1 ? s2 / s1 * dx : 0;
        if (o < o0) o0 = o;
      }
      for (j = 0; j < m; ++j) y0[j] -= o0;
      return y0;
    },
    expand: function(data) {
      var n = data.length, m = data[0].length, k = 1 / n, i, j, o, y0 = [];
      for (j = 0; j < m; ++j) {
        for (i = 0, o = 0; i < n; i++) o += data[i][j][1];
        if (o) for (i = 0; i < n; i++) data[i][j][1] /= o; else for (i = 0; i < n; i++) data[i][j][1] = k;
      }
      for (j = 0; j < m; ++j) y0[j] = 0;
      return y0;
    },
    zero: d3_layout_stackOffsetZero
  });
  function d3_layout_stackOrderDefault(data) {
    return d3.range(data.length);
  }
  function d3_layout_stackOffsetZero(data) {
    var j = -1, m = data[0].length, y0 = [];
    while (++j < m) y0[j] = 0;
    return y0;
  }
  function d3_layout_stackMaxIndex(array) {
    var i = 1, j = 0, v = array[0][1], k, n = array.length;
    for (;i < n; ++i) {
      if ((k = array[i][1]) > v) {
        j = i;
        v = k;
      }
    }
    return j;
  }
  function d3_layout_stackReduceSum(d) {
    return d.reduce(d3_layout_stackSum, 0);
  }
  function d3_layout_stackSum(p, d) {
    return p + d[1];
  }
  d3.layout.histogram = function() {
    var frequency = true, valuer = Number, ranger = d3_layout_histogramRange, binner = d3_layout_histogramBinSturges;
    function histogram(data, i) {
      var bins = [], values = data.map(valuer, this), range = ranger.call(this, values, i), thresholds = binner.call(this, range, values, i), bin, i = -1, n = values.length, m = thresholds.length - 1, k = frequency ? 1 : 1 / n, x;
      while (++i < m) {
        bin = bins[i] = [];
        bin.dx = thresholds[i + 1] - (bin.x = thresholds[i]);
        bin.y = 0;
      }
      if (m > 0) {
        i = -1;
        while (++i < n) {
          x = values[i];
          if (x >= range[0] && x <= range[1]) {
            bin = bins[d3.bisect(thresholds, x, 1, m) - 1];
            bin.y += k;
            bin.push(data[i]);
          }
        }
      }
      return bins;
    }
    histogram.value = function(x) {
      if (!arguments.length) return valuer;
      valuer = x;
      return histogram;
    };
    histogram.range = function(x) {
      if (!arguments.length) return ranger;
      ranger = d3_functor(x);
      return histogram;
    };
    histogram.bins = function(x) {
      if (!arguments.length) return binner;
      binner = typeof x === "number" ? function(range) {
        return d3_layout_histogramBinFixed(range, x);
      } : d3_functor(x);
      return histogram;
    };
    histogram.frequency = function(x) {
      if (!arguments.length) return frequency;
      frequency = !!x;
      return histogram;
    };
    return histogram;
  };
  function d3_layout_histogramBinSturges(range, values) {
    return d3_layout_histogramBinFixed(range, Math.ceil(Math.log(values.length) / Math.LN2 + 1));
  }
  function d3_layout_histogramBinFixed(range, n) {
    var x = -1, b = +range[0], m = (range[1] - b) / n, f = [];
    while (++x <= n) f[x] = m * x + b;
    return f;
  }
  function d3_layout_histogramRange(values) {
    return [ d3.min(values), d3.max(values) ];
  }
  d3.layout.pack = function() {
    var hierarchy = d3.layout.hierarchy().sort(d3_layout_packSort), padding = 0, size = [ 1, 1 ], radius;
    function pack(d, i) {
      var nodes = hierarchy.call(this, d, i), root = nodes[0], w = size[0], h = size[1], r = radius == null ? Math.sqrt : typeof radius === "function" ? radius : function() {
        return radius;
      };
      root.x = root.y = 0;
      d3_layout_hierarchyVisitAfter(root, function(d) {
        d.r = +r(d.value);
      });
      d3_layout_hierarchyVisitAfter(root, d3_layout_packSiblings);
      if (padding) {
        var dr = padding * (radius ? 1 : Math.max(2 * root.r / w, 2 * root.r / h)) / 2;
        d3_layout_hierarchyVisitAfter(root, function(d) {
          d.r += dr;
        });
        d3_layout_hierarchyVisitAfter(root, d3_layout_packSiblings);
        d3_layout_hierarchyVisitAfter(root, function(d) {
          d.r -= dr;
        });
      }
      d3_layout_packTransform(root, w / 2, h / 2, radius ? 1 : 1 / Math.max(2 * root.r / w, 2 * root.r / h));
      return nodes;
    }
    pack.size = function(_) {
      if (!arguments.length) return size;
      size = _;
      return pack;
    };
    pack.radius = function(_) {
      if (!arguments.length) return radius;
      radius = _ == null || typeof _ === "function" ? _ : +_;
      return pack;
    };
    pack.padding = function(_) {
      if (!arguments.length) return padding;
      padding = +_;
      return pack;
    };
    return d3_layout_hierarchyRebind(pack, hierarchy);
  };
  function d3_layout_packSort(a, b) {
    return a.value - b.value;
  }
  function d3_layout_packInsert(a, b) {
    var c = a._pack_next;
    a._pack_next = b;
    b._pack_prev = a;
    b._pack_next = c;
    c._pack_prev = b;
  }
  function d3_layout_packSplice(a, b) {
    a._pack_next = b;
    b._pack_prev = a;
  }
  function d3_layout_packIntersects(a, b) {
    var dx = b.x - a.x, dy = b.y - a.y, dr = a.r + b.r;
    return .999 * dr * dr > dx * dx + dy * dy;
  }
  function d3_layout_packSiblings(node) {
    if (!(nodes = node.children) || !(n = nodes.length)) return;
    var nodes, xMin = Infinity, xMax = -Infinity, yMin = Infinity, yMax = -Infinity, a, b, c, i, j, k, n;
    function bound(node) {
      xMin = Math.min(node.x - node.r, xMin);
      xMax = Math.max(node.x + node.r, xMax);
      yMin = Math.min(node.y - node.r, yMin);
      yMax = Math.max(node.y + node.r, yMax);
    }
    nodes.forEach(d3_layout_packLink);
    a = nodes[0];
    a.x = -a.r;
    a.y = 0;
    bound(a);
    if (n > 1) {
      b = nodes[1];
      b.x = b.r;
      b.y = 0;
      bound(b);
      if (n > 2) {
        c = nodes[2];
        d3_layout_packPlace(a, b, c);
        bound(c);
        d3_layout_packInsert(a, c);
        a._pack_prev = c;
        d3_layout_packInsert(c, b);
        b = a._pack_next;
        for (i = 3; i < n; i++) {
          d3_layout_packPlace(a, b, c = nodes[i]);
          var isect = 0, s1 = 1, s2 = 1;
          for (j = b._pack_next; j !== b; j = j._pack_next, s1++) {
            if (d3_layout_packIntersects(j, c)) {
              isect = 1;
              break;
            }
          }
          if (isect == 1) {
            for (k = a._pack_prev; k !== j._pack_prev; k = k._pack_prev, s2++) {
              if (d3_layout_packIntersects(k, c)) {
                break;
              }
            }
          }
          if (isect) {
            if (s1 < s2 || s1 == s2 && b.r < a.r) d3_layout_packSplice(a, b = j); else d3_layout_packSplice(a = k, b);
            i--;
          } else {
            d3_layout_packInsert(a, c);
            b = c;
            bound(c);
          }
        }
      }
    }
    var cx = (xMin + xMax) / 2, cy = (yMin + yMax) / 2, cr = 0;
    for (i = 0; i < n; i++) {
      c = nodes[i];
      c.x -= cx;
      c.y -= cy;
      cr = Math.max(cr, c.r + Math.sqrt(c.x * c.x + c.y * c.y));
    }
    node.r = cr;
    nodes.forEach(d3_layout_packUnlink);
  }
  function d3_layout_packLink(node) {
    node._pack_next = node._pack_prev = node;
  }
  function d3_layout_packUnlink(node) {
    delete node._pack_next;
    delete node._pack_prev;
  }
  function d3_layout_packTransform(node, x, y, k) {
    var children = node.children;
    node.x = x += k * node.x;
    node.y = y += k * node.y;
    node.r *= k;
    if (children) {
      var i = -1, n = children.length;
      while (++i < n) d3_layout_packTransform(children[i], x, y, k);
    }
  }
  function d3_layout_packPlace(a, b, c) {
    var db = a.r + c.r, dx = b.x - a.x, dy = b.y - a.y;
    if (db && (dx || dy)) {
      var da = b.r + c.r, dc = dx * dx + dy * dy;
      da *= da;
      db *= db;
      var x = .5 + (db - da) / (2 * dc), y = Math.sqrt(Math.max(0, 2 * da * (db + dc) - (db -= dc) * db - da * da)) / (2 * dc);
      c.x = a.x + x * dx + y * dy;
      c.y = a.y + x * dy - y * dx;
    } else {
      c.x = a.x + db;
      c.y = a.y;
    }
  }
  d3.layout.tree = function() {
    var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ], nodeSize = null;
    function tree(d, i) {
      var nodes = hierarchy.call(this, d, i), root0 = nodes[0], root1 = wrapTree(root0);
      d3_layout_hierarchyVisitAfter(root1, firstWalk), root1.parent.m = -root1.z;
      d3_layout_hierarchyVisitBefore(root1, secondWalk);
      if (nodeSize) d3_layout_hierarchyVisitBefore(root0, sizeNode); else {
        var left = root0, right = root0, bottom = root0;
        d3_layout_hierarchyVisitBefore(root0, function(node) {
          if (node.x < left.x) left = node;
          if (node.x > right.x) right = node;
          if (node.depth > bottom.depth) bottom = node;
        });
        var tx = separation(left, right) / 2 - left.x, kx = size[0] / (right.x + separation(right, left) / 2 + tx), ky = size[1] / (bottom.depth || 1);
        d3_layout_hierarchyVisitBefore(root0, function(node) {
          node.x = (node.x + tx) * kx;
          node.y = node.depth * ky;
        });
      }
      return nodes;
    }
    function wrapTree(root0) {
      var root1 = {
        A: null,
        children: [ root0 ]
      }, queue = [ root1 ], node1;
      while ((node1 = queue.pop()) != null) {
        for (var children = node1.children, child, i = 0, n = children.length; i < n; ++i) {
          queue.push((children[i] = child = {
            _: children[i],
            parent: node1,
            children: (child = children[i].children) && child.slice() || [],
            A: null,
            a: null,
            z: 0,
            m: 0,
            c: 0,
            s: 0,
            t: null,
            i: i
          }).a = child);
        }
      }
      return root1.children[0];
    }
    function firstWalk(v) {
      var children = v.children, siblings = v.parent.children, w = v.i ? siblings[v.i - 1] : null;
      if (children.length) {
        d3_layout_treeShift(v);
        var midpoint = (children[0].z + children[children.length - 1].z) / 2;
        if (w) {
          v.z = w.z + separation(v._, w._);
          v.m = v.z - midpoint;
        } else {
          v.z = midpoint;
        }
      } else if (w) {
        v.z = w.z + separation(v._, w._);
      }
      v.parent.A = apportion(v, w, v.parent.A || siblings[0]);
    }
    function secondWalk(v) {
      v._.x = v.z + v.parent.m;
      v.m += v.parent.m;
    }
    function apportion(v, w, ancestor) {
      if (w) {
        var vip = v, vop = v, vim = w, vom = vip.parent.children[0], sip = vip.m, sop = vop.m, sim = vim.m, som = vom.m, shift;
        while (vim = d3_layout_treeRight(vim), vip = d3_layout_treeLeft(vip), vim && vip) {
          vom = d3_layout_treeLeft(vom);
          vop = d3_layout_treeRight(vop);
          vop.a = v;
          shift = vim.z + sim - vip.z - sip + separation(vim._, vip._);
          if (shift > 0) {
            d3_layout_treeMove(d3_layout_treeAncestor(vim, v, ancestor), v, shift);
            sip += shift;
            sop += shift;
          }
          sim += vim.m;
          sip += vip.m;
          som += vom.m;
          sop += vop.m;
        }
        if (vim && !d3_layout_treeRight(vop)) {
          vop.t = vim;
          vop.m += sim - sop;
        }
        if (vip && !d3_layout_treeLeft(vom)) {
          vom.t = vip;
          vom.m += sip - som;
          ancestor = v;
        }
      }
      return ancestor;
    }
    function sizeNode(node) {
      node.x *= size[0];
      node.y = node.depth * size[1];
    }
    tree.separation = function(x) {
      if (!arguments.length) return separation;
      separation = x;
      return tree;
    };
    tree.size = function(x) {
      if (!arguments.length) return nodeSize ? null : size;
      nodeSize = (size = x) == null ? sizeNode : null;
      return tree;
    };
    tree.nodeSize = function(x) {
      if (!arguments.length) return nodeSize ? size : null;
      nodeSize = (size = x) == null ? null : sizeNode;
      return tree;
    };
    return d3_layout_hierarchyRebind(tree, hierarchy);
  };
  function d3_layout_treeSeparation(a, b) {
    return a.parent == b.parent ? 1 : 2;
  }
  function d3_layout_treeLeft(v) {
    var children = v.children;
    return children.length ? children[0] : v.t;
  }
  function d3_layout_treeRight(v) {
    var children = v.children, n;
    return (n = children.length) ? children[n - 1] : v.t;
  }
  function d3_layout_treeMove(wm, wp, shift) {
    var change = shift / (wp.i - wm.i);
    wp.c -= change;
    wp.s += shift;
    wm.c += change;
    wp.z += shift;
    wp.m += shift;
  }
  function d3_layout_treeShift(v) {
    var shift = 0, change = 0, children = v.children, i = children.length, w;
    while (--i >= 0) {
      w = children[i];
      w.z += shift;
      w.m += shift;
      shift += w.s + (change += w.c);
    }
  }
  function d3_layout_treeAncestor(vim, v, ancestor) {
    return vim.a.parent === v.parent ? vim.a : ancestor;
  }
  d3.layout.cluster = function() {
    var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ], nodeSize = false;
    function cluster(d, i) {
      var nodes = hierarchy.call(this, d, i), root = nodes[0], previousNode, x = 0;
      d3_layout_hierarchyVisitAfter(root, function(node) {
        var children = node.children;
        if (children && children.length) {
          node.x = d3_layout_clusterX(children);
          node.y = d3_layout_clusterY(children);
        } else {
          node.x = previousNode ? x += separation(node, previousNode) : 0;
          node.y = 0;
          previousNode = node;
        }
      });
      var left = d3_layout_clusterLeft(root), right = d3_layout_clusterRight(root), x0 = left.x - separation(left, right) / 2, x1 = right.x + separation(right, left) / 2;
      d3_layout_hierarchyVisitAfter(root, nodeSize ? function(node) {
        node.x = (node.x - root.x) * size[0];
        node.y = (root.y - node.y) * size[1];
      } : function(node) {
        node.x = (node.x - x0) / (x1 - x0) * size[0];
        node.y = (1 - (root.y ? node.y / root.y : 1)) * size[1];
      });
      return nodes;
    }
    cluster.separation = function(x) {
      if (!arguments.length) return separation;
      separation = x;
      return cluster;
    };
    cluster.size = function(x) {
      if (!arguments.length) return nodeSize ? null : size;
      nodeSize = (size = x) == null;
      return cluster;
    };
    cluster.nodeSize = function(x) {
      if (!arguments.length) return nodeSize ? size : null;
      nodeSize = (size = x) != null;
      return cluster;
    };
    return d3_layout_hierarchyRebind(cluster, hierarchy);
  };
  function d3_layout_clusterY(children) {
    return 1 + d3.max(children, function(child) {
      return child.y;
    });
  }
  function d3_layout_clusterX(children) {
    return children.reduce(function(x, child) {
      return x + child.x;
    }, 0) / children.length;
  }
  function d3_layout_clusterLeft(node) {
    var children = node.children;
    return children && children.length ? d3_layout_clusterLeft(children[0]) : node;
  }
  function d3_layout_clusterRight(node) {
    var children = node.children, n;
    return children && (n = children.length) ? d3_layout_clusterRight(children[n - 1]) : node;
  }
  d3.layout.treemap = function() {
    var hierarchy = d3.layout.hierarchy(), round = Math.round, size = [ 1, 1 ], padding = null, pad = d3_layout_treemapPadNull, sticky = false, stickies, mode = "squarify", ratio = .5 * (1 + Math.sqrt(5));
    function scale(children, k) {
      var i = -1, n = children.length, child, area;
      while (++i < n) {
        area = (child = children[i]).value * (k < 0 ? 0 : k);
        child.area = isNaN(area) || area <= 0 ? 0 : area;
      }
    }
    function squarify(node) {
      var children = node.children;
      if (children && children.length) {
        var rect = pad(node), row = [], remaining = children.slice(), child, best = Infinity, score, u = mode === "slice" ? rect.dx : mode === "dice" ? rect.dy : mode === "slice-dice" ? node.depth & 1 ? rect.dy : rect.dx : Math.min(rect.dx, rect.dy), n;
        scale(remaining, rect.dx * rect.dy / node.value);
        row.area = 0;
        while ((n = remaining.length) > 0) {
          row.push(child = remaining[n - 1]);
          row.area += child.area;
          if (mode !== "squarify" || (score = worst(row, u)) <= best) {
            remaining.pop();
            best = score;
          } else {
            row.area -= row.pop().area;
            position(row, u, rect, false);
            u = Math.min(rect.dx, rect.dy);
            row.length = row.area = 0;
            best = Infinity;
          }
        }
        if (row.length) {
          position(row, u, rect, true);
          row.length = row.area = 0;
        }
        children.forEach(squarify);
      }
    }
    function stickify(node) {
      var children = node.children;
      if (children && children.length) {
        var rect = pad(node), remaining = children.slice(), child, row = [];
        scale(remaining, rect.dx * rect.dy / node.value);
        row.area = 0;
        while (child = remaining.pop()) {
          row.push(child);
          row.area += child.area;
          if (child.z != null) {
            position(row, child.z ? rect.dx : rect.dy, rect, !remaining.length);
            row.length = row.area = 0;
          }
        }
        children.forEach(stickify);
      }
    }
    function worst(row, u) {
      var s = row.area, r, rmax = 0, rmin = Infinity, i = -1, n = row.length;
      while (++i < n) {
        if (!(r = row[i].area)) continue;
        if (r < rmin) rmin = r;
        if (r > rmax) rmax = r;
      }
      s *= s;
      u *= u;
      return s ? Math.max(u * rmax * ratio / s, s / (u * rmin * ratio)) : Infinity;
    }
    function position(row, u, rect, flush) {
      var i = -1, n = row.length, x = rect.x, y = rect.y, v = u ? round(row.area / u) : 0, o;
      if (u == rect.dx) {
        if (flush || v > rect.dy) v = rect.dy;
        while (++i < n) {
          o = row[i];
          o.x = x;
          o.y = y;
          o.dy = v;
          x += o.dx = Math.min(rect.x + rect.dx - x, v ? round(o.area / v) : 0);
        }
        o.z = true;
        o.dx += rect.x + rect.dx - x;
        rect.y += v;
        rect.dy -= v;
      } else {
        if (flush || v > rect.dx) v = rect.dx;
        while (++i < n) {
          o = row[i];
          o.x = x;
          o.y = y;
          o.dx = v;
          y += o.dy = Math.min(rect.y + rect.dy - y, v ? round(o.area / v) : 0);
        }
        o.z = false;
        o.dy += rect.y + rect.dy - y;
        rect.x += v;
        rect.dx -= v;
      }
    }
    function treemap(d) {
      var nodes = stickies || hierarchy(d), root = nodes[0];
      root.x = root.y = 0;
      if (root.value) root.dx = size[0], root.dy = size[1]; else root.dx = root.dy = 0;
      if (stickies) hierarchy.revalue(root);
      scale([ root ], root.dx * root.dy / root.value);
      (stickies ? stickify : squarify)(root);
      if (sticky) stickies = nodes;
      return nodes;
    }
    treemap.size = function(x) {
      if (!arguments.length) return size;
      size = x;
      return treemap;
    };
    treemap.padding = function(x) {
      if (!arguments.length) return padding;
      function padFunction(node) {
        var p = x.call(treemap, node, node.depth);
        return p == null ? d3_layout_treemapPadNull(node) : d3_layout_treemapPad(node, typeof p === "number" ? [ p, p, p, p ] : p);
      }
      function padConstant(node) {
        return d3_layout_treemapPad(node, x);
      }
      var type;
      pad = (padding = x) == null ? d3_layout_treemapPadNull : (type = typeof x) === "function" ? padFunction : type === "number" ? (x = [ x, x, x, x ], 
      padConstant) : padConstant;
      return treemap;
    };
    treemap.round = function(x) {
      if (!arguments.length) return round != Number;
      round = x ? Math.round : Number;
      return treemap;
    };
    treemap.sticky = function(x) {
      if (!arguments.length) return sticky;
      sticky = x;
      stickies = null;
      return treemap;
    };
    treemap.ratio = function(x) {
      if (!arguments.length) return ratio;
      ratio = x;
      return treemap;
    };
    treemap.mode = function(x) {
      if (!arguments.length) return mode;
      mode = x + "";
      return treemap;
    };
    return d3_layout_hierarchyRebind(treemap, hierarchy);
  };
  function d3_layout_treemapPadNull(node) {
    return {
      x: node.x,
      y: node.y,
      dx: node.dx,
      dy: node.dy
    };
  }
  function d3_layout_treemapPad(node, padding) {
    var x = node.x + padding[3], y = node.y + padding[0], dx = node.dx - padding[1] - padding[3], dy = node.dy - padding[0] - padding[2];
    if (dx < 0) {
      x += dx / 2;
      dx = 0;
    }
    if (dy < 0) {
      y += dy / 2;
      dy = 0;
    }
    return {
      x: x,
      y: y,
      dx: dx,
      dy: dy
    };
  }
  d3.random = {
    normal: function(µ, σ) {
      var n = arguments.length;
      if (n < 2) σ = 1;
      if (n < 1) µ = 0;
      return function() {
        var x, y, r;
        do {
          x = Math.random() * 2 - 1;
          y = Math.random() * 2 - 1;
          r = x * x + y * y;
        } while (!r || r > 1);
        return µ + σ * x * Math.sqrt(-2 * Math.log(r) / r);
      };
    },
    logNormal: function() {
      var random = d3.random.normal.apply(d3, arguments);
      return function() {
        return Math.exp(random());
      };
    },
    bates: function(m) {
      var random = d3.random.irwinHall(m);
      return function() {
        return random() / m;
      };
    },
    irwinHall: function(m) {
      return function() {
        for (var s = 0, j = 0; j < m; j++) s += Math.random();
        return s;
      };
    }
  };
  d3.scale = {};
  function d3_scaleExtent(domain) {
    var start = domain[0], stop = domain[domain.length - 1];
    return start < stop ? [ start, stop ] : [ stop, start ];
  }
  function d3_scaleRange(scale) {
    return scale.rangeExtent ? scale.rangeExtent() : d3_scaleExtent(scale.range());
  }
  function d3_scale_bilinear(domain, range, uninterpolate, interpolate) {
    var u = uninterpolate(domain[0], domain[1]), i = interpolate(range[0], range[1]);
    return function(x) {
      return i(u(x));
    };
  }
  function d3_scale_nice(domain, nice) {
    var i0 = 0, i1 = domain.length - 1, x0 = domain[i0], x1 = domain[i1], dx;
    if (x1 < x0) {
      dx = i0, i0 = i1, i1 = dx;
      dx = x0, x0 = x1, x1 = dx;
    }
    domain[i0] = nice.floor(x0);
    domain[i1] = nice.ceil(x1);
    return domain;
  }
  function d3_scale_niceStep(step) {
    return step ? {
      floor: function(x) {
        return Math.floor(x / step) * step;
      },
      ceil: function(x) {
        return Math.ceil(x / step) * step;
      }
    } : d3_scale_niceIdentity;
  }
  var d3_scale_niceIdentity = {
    floor: d3_identity,
    ceil: d3_identity
  };
  function d3_scale_polylinear(domain, range, uninterpolate, interpolate) {
    var u = [], i = [], j = 0, k = Math.min(domain.length, range.length) - 1;
    if (domain[k] < domain[0]) {
      domain = domain.slice().reverse();
      range = range.slice().reverse();
    }
    while (++j <= k) {
      u.push(uninterpolate(domain[j - 1], domain[j]));
      i.push(interpolate(range[j - 1], range[j]));
    }
    return function(x) {
      var j = d3.bisect(domain, x, 1, k) - 1;
      return i[j](u[j](x));
    };
  }
  d3.scale.linear = function() {
    return d3_scale_linear([ 0, 1 ], [ 0, 1 ], d3_interpolate, false);
  };
  function d3_scale_linear(domain, range, interpolate, clamp) {
    var output, input;
    function rescale() {
      var linear = Math.min(domain.length, range.length) > 2 ? d3_scale_polylinear : d3_scale_bilinear, uninterpolate = clamp ? d3_uninterpolateClamp : d3_uninterpolateNumber;
      output = linear(domain, range, uninterpolate, interpolate);
      input = linear(range, domain, uninterpolate, d3_interpolate);
      return scale;
    }
    function scale(x) {
      return output(x);
    }
    scale.invert = function(y) {
      return input(y);
    };
    scale.domain = function(x) {
      if (!arguments.length) return domain;
      domain = x.map(Number);
      return rescale();
    };
    scale.range = function(x) {
      if (!arguments.length) return range;
      range = x;
      return rescale();
    };
    scale.rangeRound = function(x) {
      return scale.range(x).interpolate(d3_interpolateRound);
    };
    scale.clamp = function(x) {
      if (!arguments.length) return clamp;
      clamp = x;
      return rescale();
    };
    scale.interpolate = function(x) {
      if (!arguments.length) return interpolate;
      interpolate = x;
      return rescale();
    };
    scale.ticks = function(m) {
      return d3_scale_linearTicks(domain, m);
    };
    scale.tickFormat = function(m, format) {
      return d3_scale_linearTickFormat(domain, m, format);
    };
    scale.nice = function(m) {
      d3_scale_linearNice(domain, m);
      return rescale();
    };
    scale.copy = function() {
      return d3_scale_linear(domain, range, interpolate, clamp);
    };
    return rescale();
  }
  function d3_scale_linearRebind(scale, linear) {
    return d3.rebind(scale, linear, "range", "rangeRound", "interpolate", "clamp");
  }
  function d3_scale_linearNice(domain, m) {
    d3_scale_nice(domain, d3_scale_niceStep(d3_scale_linearTickRange(domain, m)[2]));
    d3_scale_nice(domain, d3_scale_niceStep(d3_scale_linearTickRange(domain, m)[2]));
    return domain;
  }
  function d3_scale_linearTickRange(domain, m) {
    if (m == null) m = 10;
    var extent = d3_scaleExtent(domain), span = extent[1] - extent[0], step = Math.pow(10, Math.floor(Math.log(span / m) / Math.LN10)), err = m / span * step;
    if (err <= .15) step *= 10; else if (err <= .35) step *= 5; else if (err <= .75) step *= 2;
    extent[0] = Math.ceil(extent[0] / step) * step;
    extent[1] = Math.floor(extent[1] / step) * step + step * .5;
    extent[2] = step;
    return extent;
  }
  function d3_scale_linearTicks(domain, m) {
    return d3.range.apply(d3, d3_scale_linearTickRange(domain, m));
  }
  function d3_scale_linearTickFormat(domain, m, format) {
    var range = d3_scale_linearTickRange(domain, m);
    if (format) {
      var match = d3_format_re.exec(format);
      match.shift();
      if (match[8] === "s") {
        var prefix = d3.formatPrefix(Math.max(abs(range[0]), abs(range[1])));
        if (!match[7]) match[7] = "." + d3_scale_linearPrecision(prefix.scale(range[2]));
        match[8] = "f";
        format = d3.format(match.join(""));
        return function(d) {
          return format(prefix.scale(d)) + prefix.symbol;
        };
      }
      if (!match[7]) match[7] = "." + d3_scale_linearFormatPrecision(match[8], range);
      format = match.join("");
    } else {
      format = ",." + d3_scale_linearPrecision(range[2]) + "f";
    }
    return d3.format(format);
  }
  var d3_scale_linearFormatSignificant = {
    s: 1,
    g: 1,
    p: 1,
    r: 1,
    e: 1
  };
  function d3_scale_linearPrecision(value) {
    return -Math.floor(Math.log(value) / Math.LN10 + .01);
  }
  function d3_scale_linearFormatPrecision(type, range) {
    var p = d3_scale_linearPrecision(range[2]);
    return type in d3_scale_linearFormatSignificant ? Math.abs(p - d3_scale_linearPrecision(Math.max(abs(range[0]), abs(range[1])))) + +(type !== "e") : p - (type === "%") * 2;
  }
  d3.scale.log = function() {
    return d3_scale_log(d3.scale.linear().domain([ 0, 1 ]), 10, true, [ 1, 10 ]);
  };
  function d3_scale_log(linear, base, positive, domain) {
    function log(x) {
      return (positive ? Math.log(x < 0 ? 0 : x) : -Math.log(x > 0 ? 0 : -x)) / Math.log(base);
    }
    function pow(x) {
      return positive ? Math.pow(base, x) : -Math.pow(base, -x);
    }
    function scale(x) {
      return linear(log(x));
    }
    scale.invert = function(x) {
      return pow(linear.invert(x));
    };
    scale.domain = function(x) {
      if (!arguments.length) return domain;
      positive = x[0] >= 0;
      linear.domain((domain = x.map(Number)).map(log));
      return scale;
    };
    scale.base = function(_) {
      if (!arguments.length) return base;
      base = +_;
      linear.domain(domain.map(log));
      return scale;
    };
    scale.nice = function() {
      var niced = d3_scale_nice(domain.map(log), positive ? Math : d3_scale_logNiceNegative);
      linear.domain(niced);
      domain = niced.map(pow);
      return scale;
    };
    scale.ticks = function() {
      var extent = d3_scaleExtent(domain), ticks = [], u = extent[0], v = extent[1], i = Math.floor(log(u)), j = Math.ceil(log(v)), n = base % 1 ? 2 : base;
      if (isFinite(j - i)) {
        if (positive) {
          for (;i < j; i++) for (var k = 1; k < n; k++) ticks.push(pow(i) * k);
          ticks.push(pow(i));
        } else {
          ticks.push(pow(i));
          for (;i++ < j; ) for (var k = n - 1; k > 0; k--) ticks.push(pow(i) * k);
        }
        for (i = 0; ticks[i] < u; i++) {}
        for (j = ticks.length; ticks[j - 1] > v; j--) {}
        ticks = ticks.slice(i, j);
      }
      return ticks;
    };
    scale.tickFormat = function(n, format) {
      if (!arguments.length) return d3_scale_logFormat;
      if (arguments.length < 2) format = d3_scale_logFormat; else if (typeof format !== "function") format = d3.format(format);
      var k = Math.max(1, base * n / scale.ticks().length);
      return function(d) {
        var i = d / pow(Math.round(log(d)));
        if (i * base < base - .5) i *= base;
        return i <= k ? format(d) : "";
      };
    };
    scale.copy = function() {
      return d3_scale_log(linear.copy(), base, positive, domain);
    };
    return d3_scale_linearRebind(scale, linear);
  }
  var d3_scale_logFormat = d3.format(".0e"), d3_scale_logNiceNegative = {
    floor: function(x) {
      return -Math.ceil(-x);
    },
    ceil: function(x) {
      return -Math.floor(-x);
    }
  };
  d3.scale.pow = function() {
    return d3_scale_pow(d3.scale.linear(), 1, [ 0, 1 ]);
  };
  function d3_scale_pow(linear, exponent, domain) {
    var powp = d3_scale_powPow(exponent), powb = d3_scale_powPow(1 / exponent);
    function scale(x) {
      return linear(powp(x));
    }
    scale.invert = function(x) {
      return powb(linear.invert(x));
    };
    scale.domain = function(x) {
      if (!arguments.length) return domain;
      linear.domain((domain = x.map(Number)).map(powp));
      return scale;
    };
    scale.ticks = function(m) {
      return d3_scale_linearTicks(domain, m);
    };
    scale.tickFormat = function(m, format) {
      return d3_scale_linearTickFormat(domain, m, format);
    };
    scale.nice = function(m) {
      return scale.domain(d3_scale_linearNice(domain, m));
    };
    scale.exponent = function(x) {
      if (!arguments.length) return exponent;
      powp = d3_scale_powPow(exponent = x);
      powb = d3_scale_powPow(1 / exponent);
      linear.domain(domain.map(powp));
      return scale;
    };
    scale.copy = function() {
      return d3_scale_pow(linear.copy(), exponent, domain);
    };
    return d3_scale_linearRebind(scale, linear);
  }
  function d3_scale_powPow(e) {
    return function(x) {
      return x < 0 ? -Math.pow(-x, e) : Math.pow(x, e);
    };
  }
  d3.scale.sqrt = function() {
    return d3.scale.pow().exponent(.5);
  };
  d3.scale.ordinal = function() {
    return d3_scale_ordinal([], {
      t: "range",
      a: [ [] ]
    });
  };
  function d3_scale_ordinal(domain, ranger) {
    var index, range, rangeBand;
    function scale(x) {
      return range[((index.get(x) || (ranger.t === "range" ? index.set(x, domain.push(x)) : NaN)) - 1) % range.length];
    }
    function steps(start, step) {
      return d3.range(domain.length).map(function(i) {
        return start + step * i;
      });
    }
    scale.domain = function(x) {
      if (!arguments.length) return domain;
      domain = [];
      index = new d3_Map();
      var i = -1, n = x.length, xi;
      while (++i < n) if (!index.has(xi = x[i])) index.set(xi, domain.push(xi));
      return scale[ranger.t].apply(scale, ranger.a);
    };
    scale.range = function(x) {
      if (!arguments.length) return range;
      range = x;
      rangeBand = 0;
      ranger = {
        t: "range",
        a: arguments
      };
      return scale;
    };
    scale.rangePoints = function(x, padding) {
      if (arguments.length < 2) padding = 0;
      var start = x[0], stop = x[1], step = domain.length < 2 ? (start = (start + stop) / 2, 
      0) : (stop - start) / (domain.length - 1 + padding);
      range = steps(start + step * padding / 2, step);
      rangeBand = 0;
      ranger = {
        t: "rangePoints",
        a: arguments
      };
      return scale;
    };
    scale.rangeRoundPoints = function(x, padding) {
      if (arguments.length < 2) padding = 0;
      var start = x[0], stop = x[1], step = domain.length < 2 ? (start = stop = Math.round((start + stop) / 2), 
      0) : (stop - start) / (domain.length - 1 + padding) | 0;
      range = steps(start + Math.round(step * padding / 2 + (stop - start - (domain.length - 1 + padding) * step) / 2), step);
      rangeBand = 0;
      ranger = {
        t: "rangeRoundPoints",
        a: arguments
      };
      return scale;
    };
    scale.rangeBands = function(x, padding, outerPadding) {
      if (arguments.length < 2) padding = 0;
      if (arguments.length < 3) outerPadding = padding;
      var reverse = x[1] < x[0], start = x[reverse - 0], stop = x[1 - reverse], step = (stop - start) / (domain.length - padding + 2 * outerPadding);
      range = steps(start + step * outerPadding, step);
      if (reverse) range.reverse();
      rangeBand = step * (1 - padding);
      ranger = {
        t: "rangeBands",
        a: arguments
      };
      return scale;
    };
    scale.rangeRoundBands = function(x, padding, outerPadding) {
      if (arguments.length < 2) padding = 0;
      if (arguments.length < 3) outerPadding = padding;
      var reverse = x[1] < x[0], start = x[reverse - 0], stop = x[1 - reverse], step = Math.floor((stop - start) / (domain.length - padding + 2 * outerPadding));
      range = steps(start + Math.round((stop - start - (domain.length - padding) * step) / 2), step);
      if (reverse) range.reverse();
      rangeBand = Math.round(step * (1 - padding));
      ranger = {
        t: "rangeRoundBands",
        a: arguments
      };
      return scale;
    };
    scale.rangeBand = function() {
      return rangeBand;
    };
    scale.rangeExtent = function() {
      return d3_scaleExtent(ranger.a[0]);
    };
    scale.copy = function() {
      return d3_scale_ordinal(domain, ranger);
    };
    return scale.domain(domain);
  }
  d3.scale.category10 = function() {
    return d3.scale.ordinal().range(d3_category10);
  };
  d3.scale.category20 = function() {
    return d3.scale.ordinal().range(d3_category20);
  };
  d3.scale.category20b = function() {
    return d3.scale.ordinal().range(d3_category20b);
  };
  d3.scale.category20c = function() {
    return d3.scale.ordinal().range(d3_category20c);
  };
  var d3_category10 = [ 2062260, 16744206, 2924588, 14034728, 9725885, 9197131, 14907330, 8355711, 12369186, 1556175 ].map(d3_rgbString);
  var d3_category20 = [ 2062260, 11454440, 16744206, 16759672, 2924588, 10018698, 14034728, 16750742, 9725885, 12955861, 9197131, 12885140, 14907330, 16234194, 8355711, 13092807, 12369186, 14408589, 1556175, 10410725 ].map(d3_rgbString);
  var d3_category20b = [ 3750777, 5395619, 7040719, 10264286, 6519097, 9216594, 11915115, 13556636, 9202993, 12426809, 15186514, 15190932, 8666169, 11356490, 14049643, 15177372, 8077683, 10834324, 13528509, 14589654 ].map(d3_rgbString);
  var d3_category20c = [ 3244733, 7057110, 10406625, 13032431, 15095053, 16616764, 16625259, 16634018, 3253076, 7652470, 10607003, 13101504, 7695281, 10394312, 12369372, 14342891, 6513507, 9868950, 12434877, 14277081 ].map(d3_rgbString);
  d3.scale.quantile = function() {
    return d3_scale_quantile([], []);
  };
  function d3_scale_quantile(domain, range) {
    var thresholds;
    function rescale() {
      var k = 0, q = range.length;
      thresholds = [];
      while (++k < q) thresholds[k - 1] = d3.quantile(domain, k / q);
      return scale;
    }
    function scale(x) {
      if (!isNaN(x = +x)) return range[d3.bisect(thresholds, x)];
    }
    scale.domain = function(x) {
      if (!arguments.length) return domain;
      domain = x.map(d3_number).filter(d3_numeric).sort(d3_ascending);
      return rescale();
    };
    scale.range = function(x) {
      if (!arguments.length) return range;
      range = x;
      return rescale();
    };
    scale.quantiles = function() {
      return thresholds;
    };
    scale.invertExtent = function(y) {
      y = range.indexOf(y);
      return y < 0 ? [ NaN, NaN ] : [ y > 0 ? thresholds[y - 1] : domain[0], y < thresholds.length ? thresholds[y] : domain[domain.length - 1] ];
    };
    scale.copy = function() {
      return d3_scale_quantile(domain, range);
    };
    return rescale();
  }
  d3.scale.quantize = function() {
    return d3_scale_quantize(0, 1, [ 0, 1 ]);
  };
  function d3_scale_quantize(x0, x1, range) {
    var kx, i;
    function scale(x) {
      return range[Math.max(0, Math.min(i, Math.floor(kx * (x - x0))))];
    }
    function rescale() {
      kx = range.length / (x1 - x0);
      i = range.length - 1;
      return scale;
    }
    scale.domain = function(x) {
      if (!arguments.length) return [ x0, x1 ];
      x0 = +x[0];
      x1 = +x[x.length - 1];
      return rescale();
    };
    scale.range = function(x) {
      if (!arguments.length) return range;
      range = x;
      return rescale();
    };
    scale.invertExtent = function(y) {
      y = range.indexOf(y);
      y = y < 0 ? NaN : y / kx + x0;
      return [ y, y + 1 / kx ];
    };
    scale.copy = function() {
      return d3_scale_quantize(x0, x1, range);
    };
    return rescale();
  }
  d3.scale.threshold = function() {
    return d3_scale_threshold([ .5 ], [ 0, 1 ]);
  };
  function d3_scale_threshold(domain, range) {
    function scale(x) {
      if (x <= x) return range[d3.bisect(domain, x)];
    }
    scale.domain = function(_) {
      if (!arguments.length) return domain;
      domain = _;
      return scale;
    };
    scale.range = function(_) {
      if (!arguments.length) return range;
      range = _;
      return scale;
    };
    scale.invertExtent = function(y) {
      y = range.indexOf(y);
      return [ domain[y - 1], domain[y] ];
    };
    scale.copy = function() {
      return d3_scale_threshold(domain, range);
    };
    return scale;
  }
  d3.scale.identity = function() {
    return d3_scale_identity([ 0, 1 ]);
  };
  function d3_scale_identity(domain) {
    function identity(x) {
      return +x;
    }
    identity.invert = identity;
    identity.domain = identity.range = function(x) {
      if (!arguments.length) return domain;
      domain = x.map(identity);
      return identity;
    };
    identity.ticks = function(m) {
      return d3_scale_linearTicks(domain, m);
    };
    identity.tickFormat = function(m, format) {
      return d3_scale_linearTickFormat(domain, m, format);
    };
    identity.copy = function() {
      return d3_scale_identity(domain);
    };
    return identity;
  }
  d3.svg = {};
  function d3_zero() {
    return 0;
  }
  d3.svg.arc = function() {
    var innerRadius = d3_svg_arcInnerRadius, outerRadius = d3_svg_arcOuterRadius, cornerRadius = d3_zero, padRadius = d3_svg_arcAuto, startAngle = d3_svg_arcStartAngle, endAngle = d3_svg_arcEndAngle, padAngle = d3_svg_arcPadAngle;
    function arc() {
      var r0 = Math.max(0, +innerRadius.apply(this, arguments)), r1 = Math.max(0, +outerRadius.apply(this, arguments)), a0 = startAngle.apply(this, arguments) - halfπ, a1 = endAngle.apply(this, arguments) - halfπ, da = Math.abs(a1 - a0), cw = a0 > a1 ? 0 : 1;
      if (r1 < r0) rc = r1, r1 = r0, r0 = rc;
      if (da >= τε) return circleSegment(r1, cw) + (r0 ? circleSegment(r0, 1 - cw) : "") + "Z";
      var rc, cr, rp, ap, p0 = 0, p1 = 0, x0, y0, x1, y1, x2, y2, x3, y3, path = [];
      if (ap = (+padAngle.apply(this, arguments) || 0) / 2) {
        rp = padRadius === d3_svg_arcAuto ? Math.sqrt(r0 * r0 + r1 * r1) : +padRadius.apply(this, arguments);
        if (!cw) p1 *= -1;
        if (r1) p1 = d3_asin(rp / r1 * Math.sin(ap));
        if (r0) p0 = d3_asin(rp / r0 * Math.sin(ap));
      }
      if (r1) {
        x0 = r1 * Math.cos(a0 + p1);
        y0 = r1 * Math.sin(a0 + p1);
        x1 = r1 * Math.cos(a1 - p1);
        y1 = r1 * Math.sin(a1 - p1);
        var l1 = Math.abs(a1 - a0 - 2 * p1) <= π ? 0 : 1;
        if (p1 && d3_svg_arcSweep(x0, y0, x1, y1) === cw ^ l1) {
          var h1 = (a0 + a1) / 2;
          x0 = r1 * Math.cos(h1);
          y0 = r1 * Math.sin(h1);
          x1 = y1 = null;
        }
      } else {
        x0 = y0 = 0;
      }
      if (r0) {
        x2 = r0 * Math.cos(a1 - p0);
        y2 = r0 * Math.sin(a1 - p0);
        x3 = r0 * Math.cos(a0 + p0);
        y3 = r0 * Math.sin(a0 + p0);
        var l0 = Math.abs(a0 - a1 + 2 * p0) <= π ? 0 : 1;
        if (p0 && d3_svg_arcSweep(x2, y2, x3, y3) === 1 - cw ^ l0) {
          var h0 = (a0 + a1) / 2;
          x2 = r0 * Math.cos(h0);
          y2 = r0 * Math.sin(h0);
          x3 = y3 = null;
        }
      } else {
        x2 = y2 = 0;
      }
      if (da > ε && (rc = Math.min(Math.abs(r1 - r0) / 2, +cornerRadius.apply(this, arguments))) > .001) {
        cr = r0 < r1 ^ cw ? 0 : 1;
        var rc1 = rc, rc0 = rc;
        if (da < π) {
          var oc = x3 == null ? [ x2, y2 ] : x1 == null ? [ x0, y0 ] : d3_geom_polygonIntersect([ x0, y0 ], [ x3, y3 ], [ x1, y1 ], [ x2, y2 ]), ax = x0 - oc[0], ay = y0 - oc[1], bx = x1 - oc[0], by = y1 - oc[1], kc = 1 / Math.sin(Math.acos((ax * bx + ay * by) / (Math.sqrt(ax * ax + ay * ay) * Math.sqrt(bx * bx + by * by))) / 2), lc = Math.sqrt(oc[0] * oc[0] + oc[1] * oc[1]);
          rc0 = Math.min(rc, (r0 - lc) / (kc - 1));
          rc1 = Math.min(rc, (r1 - lc) / (kc + 1));
        }
        if (x1 != null) {
          var t30 = d3_svg_arcCornerTangents(x3 == null ? [ x2, y2 ] : [ x3, y3 ], [ x0, y0 ], r1, rc1, cw), t12 = d3_svg_arcCornerTangents([ x1, y1 ], [ x2, y2 ], r1, rc1, cw);
          if (rc === rc1) {
            path.push("M", t30[0], "A", rc1, ",", rc1, " 0 0,", cr, " ", t30[1], "A", r1, ",", r1, " 0 ", 1 - cw ^ d3_svg_arcSweep(t30[1][0], t30[1][1], t12[1][0], t12[1][1]), ",", cw, " ", t12[1], "A", rc1, ",", rc1, " 0 0,", cr, " ", t12[0]);
          } else {
            path.push("M", t30[0], "A", rc1, ",", rc1, " 0 1,", cr, " ", t12[0]);
          }
        } else {
          path.push("M", x0, ",", y0);
        }
        if (x3 != null) {
          var t03 = d3_svg_arcCornerTangents([ x0, y0 ], [ x3, y3 ], r0, -rc0, cw), t21 = d3_svg_arcCornerTangents([ x2, y2 ], x1 == null ? [ x0, y0 ] : [ x1, y1 ], r0, -rc0, cw);
          if (rc === rc0) {
            path.push("L", t21[0], "A", rc0, ",", rc0, " 0 0,", cr, " ", t21[1], "A", r0, ",", r0, " 0 ", cw ^ d3_svg_arcSweep(t21[1][0], t21[1][1], t03[1][0], t03[1][1]), ",", 1 - cw, " ", t03[1], "A", rc0, ",", rc0, " 0 0,", cr, " ", t03[0]);
          } else {
            path.push("L", t21[0], "A", rc0, ",", rc0, " 0 0,", cr, " ", t03[0]);
          }
        } else {
          path.push("L", x2, ",", y2);
        }
      } else {
        path.push("M", x0, ",", y0);
        if (x1 != null) path.push("A", r1, ",", r1, " 0 ", l1, ",", cw, " ", x1, ",", y1);
        path.push("L", x2, ",", y2);
        if (x3 != null) path.push("A", r0, ",", r0, " 0 ", l0, ",", 1 - cw, " ", x3, ",", y3);
      }
      path.push("Z");
      return path.join("");
    }
    function circleSegment(r1, cw) {
      return "M0," + r1 + "A" + r1 + "," + r1 + " 0 1," + cw + " 0," + -r1 + "A" + r1 + "," + r1 + " 0 1," + cw + " 0," + r1;
    }
    arc.innerRadius = function(v) {
      if (!arguments.length) return innerRadius;
      innerRadius = d3_functor(v);
      return arc;
    };
    arc.outerRadius = function(v) {
      if (!arguments.length) return outerRadius;
      outerRadius = d3_functor(v);
      return arc;
    };
    arc.cornerRadius = function(v) {
      if (!arguments.length) return cornerRadius;
      cornerRadius = d3_functor(v);
      return arc;
    };
    arc.padRadius = function(v) {
      if (!arguments.length) return padRadius;
      padRadius = v == d3_svg_arcAuto ? d3_svg_arcAuto : d3_functor(v);
      return arc;
    };
    arc.startAngle = function(v) {
      if (!arguments.length) return startAngle;
      startAngle = d3_functor(v);
      return arc;
    };
    arc.endAngle = function(v) {
      if (!arguments.length) return endAngle;
      endAngle = d3_functor(v);
      return arc;
    };
    arc.padAngle = function(v) {
      if (!arguments.length) return padAngle;
      padAngle = d3_functor(v);
      return arc;
    };
    arc.centroid = function() {
      var r = (+innerRadius.apply(this, arguments) + +outerRadius.apply(this, arguments)) / 2, a = (+startAngle.apply(this, arguments) + +endAngle.apply(this, arguments)) / 2 - halfπ;
      return [ Math.cos(a) * r, Math.sin(a) * r ];
    };
    return arc;
  };
  var d3_svg_arcAuto = "auto";
  function d3_svg_arcInnerRadius(d) {
    return d.innerRadius;
  }
  function d3_svg_arcOuterRadius(d) {
    return d.outerRadius;
  }
  function d3_svg_arcStartAngle(d) {
    return d.startAngle;
  }
  function d3_svg_arcEndAngle(d) {
    return d.endAngle;
  }
  function d3_svg_arcPadAngle(d) {
    return d && d.padAngle;
  }
  function d3_svg_arcSweep(x0, y0, x1, y1) {
    return (x0 - x1) * y0 - (y0 - y1) * x0 > 0 ? 0 : 1;
  }
  function d3_svg_arcCornerTangents(p0, p1, r1, rc, cw) {
    var x01 = p0[0] - p1[0], y01 = p0[1] - p1[1], lo = (cw ? rc : -rc) / Math.sqrt(x01 * x01 + y01 * y01), ox = lo * y01, oy = -lo * x01, x1 = p0[0] + ox, y1 = p0[1] + oy, x2 = p1[0] + ox, y2 = p1[1] + oy, x3 = (x1 + x2) / 2, y3 = (y1 + y2) / 2, dx = x2 - x1, dy = y2 - y1, d2 = dx * dx + dy * dy, r = r1 - rc, D = x1 * y2 - x2 * y1, d = (dy < 0 ? -1 : 1) * Math.sqrt(Math.max(0, r * r * d2 - D * D)), cx0 = (D * dy - dx * d) / d2, cy0 = (-D * dx - dy * d) / d2, cx1 = (D * dy + dx * d) / d2, cy1 = (-D * dx + dy * d) / d2, dx0 = cx0 - x3, dy0 = cy0 - y3, dx1 = cx1 - x3, dy1 = cy1 - y3;
    if (dx0 * dx0 + dy0 * dy0 > dx1 * dx1 + dy1 * dy1) cx0 = cx1, cy0 = cy1;
    return [ [ cx0 - ox, cy0 - oy ], [ cx0 * r1 / r, cy0 * r1 / r ] ];
  }
  function d3_svg_line(projection) {
    var x = d3_geom_pointX, y = d3_geom_pointY, defined = d3_true, interpolate = d3_svg_lineLinear, interpolateKey = interpolate.key, tension = .7;
    function line(data) {
      var segments = [], points = [], i = -1, n = data.length, d, fx = d3_functor(x), fy = d3_functor(y);
      function segment() {
        segments.push("M", interpolate(projection(points), tension));
      }
      while (++i < n) {
        if (defined.call(this, d = data[i], i)) {
          points.push([ +fx.call(this, d, i), +fy.call(this, d, i) ]);
        } else if (points.length) {
          segment();
          points = [];
        }
      }
      if (points.length) segment();
      return segments.length ? segments.join("") : null;
    }
    line.x = function(_) {
      if (!arguments.length) return x;
      x = _;
      return line;
    };
    line.y = function(_) {
      if (!arguments.length) return y;
      y = _;
      return line;
    };
    line.defined = function(_) {
      if (!arguments.length) return defined;
      defined = _;
      return line;
    };
    line.interpolate = function(_) {
      if (!arguments.length) return interpolateKey;
      if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key;
      return line;
    };
    line.tension = function(_) {
      if (!arguments.length) return tension;
      tension = _;
      return line;
    };
    return line;
  }
  d3.svg.line = function() {
    return d3_svg_line(d3_identity);
  };
  var d3_svg_lineInterpolators = d3.map({
    linear: d3_svg_lineLinear,
    "linear-closed": d3_svg_lineLinearClosed,
    step: d3_svg_lineStep,
    "step-before": d3_svg_lineStepBefore,
    "step-after": d3_svg_lineStepAfter,
    basis: d3_svg_lineBasis,
    "basis-open": d3_svg_lineBasisOpen,
    "basis-closed": d3_svg_lineBasisClosed,
    bundle: d3_svg_lineBundle,
    cardinal: d3_svg_lineCardinal,
    "cardinal-open": d3_svg_lineCardinalOpen,
    "cardinal-closed": d3_svg_lineCardinalClosed,
    monotone: d3_svg_lineMonotone
  });
  d3_svg_lineInterpolators.forEach(function(key, value) {
    value.key = key;
    value.closed = /-closed$/.test(key);
  });
  function d3_svg_lineLinear(points) {
    return points.length > 1 ? points.join("L") : points + "Z";
  }
  function d3_svg_lineLinearClosed(points) {
    return points.join("L") + "Z";
  }
  function d3_svg_lineStep(points) {
    var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ];
    while (++i < n) path.push("H", (p[0] + (p = points[i])[0]) / 2, "V", p[1]);
    if (n > 1) path.push("H", p[0]);
    return path.join("");
  }
  function d3_svg_lineStepBefore(points) {
    var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ];
    while (++i < n) path.push("V", (p = points[i])[1], "H", p[0]);
    return path.join("");
  }
  function d3_svg_lineStepAfter(points) {
    var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ];
    while (++i < n) path.push("H", (p = points[i])[0], "V", p[1]);
    return path.join("");
  }
  function d3_svg_lineCardinalOpen(points, tension) {
    return points.length < 4 ? d3_svg_lineLinear(points) : points[1] + d3_svg_lineHermite(points.slice(1, -1), d3_svg_lineCardinalTangents(points, tension));
  }
  function d3_svg_lineCardinalClosed(points, tension) {
    return points.length < 3 ? d3_svg_lineLinearClosed(points) : points[0] + d3_svg_lineHermite((points.push(points[0]), 
    points), d3_svg_lineCardinalTangents([ points[points.length - 2] ].concat(points, [ points[1] ]), tension));
  }
  function d3_svg_lineCardinal(points, tension) {
    return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineCardinalTangents(points, tension));
  }
  function d3_svg_lineHermite(points, tangents) {
    if (tangents.length < 1 || points.length != tangents.length && points.length != tangents.length + 2) {
      return d3_svg_lineLinear(points);
    }
    var quad = points.length != tangents.length, path = "", p0 = points[0], p = points[1], t0 = tangents[0], t = t0, pi = 1;
    if (quad) {
      path += "Q" + (p[0] - t0[0] * 2 / 3) + "," + (p[1] - t0[1] * 2 / 3) + "," + p[0] + "," + p[1];
      p0 = points[1];
      pi = 2;
    }
    if (tangents.length > 1) {
      t = tangents[1];
      p = points[pi];
      pi++;
      path += "C" + (p0[0] + t0[0]) + "," + (p0[1] + t0[1]) + "," + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1];
      for (var i = 2; i < tangents.length; i++, pi++) {
        p = points[pi];
        t = tangents[i];
        path += "S" + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1];
      }
    }
    if (quad) {
      var lp = points[pi];
      path += "Q" + (p[0] + t[0] * 2 / 3) + "," + (p[1] + t[1] * 2 / 3) + "," + lp[0] + "," + lp[1];
    }
    return path;
  }
  function d3_svg_lineCardinalTangents(points, tension) {
    var tangents = [], a = (1 - tension) / 2, p0, p1 = points[0], p2 = points[1], i = 1, n = points.length;
    while (++i < n) {
      p0 = p1;
      p1 = p2;
      p2 = points[i];
      tangents.push([ a * (p2[0] - p0[0]), a * (p2[1] - p0[1]) ]);
    }
    return tangents;
  }
  function d3_svg_lineBasis(points) {
    if (points.length < 3) return d3_svg_lineLinear(points);
    var i = 1, n = points.length, pi = points[0], x0 = pi[0], y0 = pi[1], px = [ x0, x0, x0, (pi = points[1])[0] ], py = [ y0, y0, y0, pi[1] ], path = [ x0, ",", y0, "L", d3_svg_lineDot4(d3_svg_lineBasisBezier3, px), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, py) ];
    points.push(points[n - 1]);
    while (++i <= n) {
      pi = points[i];
      px.shift();
      px.push(pi[0]);
      py.shift();
      py.push(pi[1]);
      d3_svg_lineBasisBezier(path, px, py);
    }
    points.pop();
    path.push("L", pi);
    return path.join("");
  }
  function d3_svg_lineBasisOpen(points) {
    if (points.length < 4) return d3_svg_lineLinear(points);
    var path = [], i = -1, n = points.length, pi, px = [ 0 ], py = [ 0 ];
    while (++i < 3) {
      pi = points[i];
      px.push(pi[0]);
      py.push(pi[1]);
    }
    path.push(d3_svg_lineDot4(d3_svg_lineBasisBezier3, px) + "," + d3_svg_lineDot4(d3_svg_lineBasisBezier3, py));
    --i;
    while (++i < n) {
      pi = points[i];
      px.shift();
      px.push(pi[0]);
      py.shift();
      py.push(pi[1]);
      d3_svg_lineBasisBezier(path, px, py);
    }
    return path.join("");
  }
  function d3_svg_lineBasisClosed(points) {
    var path, i = -1, n = points.length, m = n + 4, pi, px = [], py = [];
    while (++i < 4) {
      pi = points[i % n];
      px.push(pi[0]);
      py.push(pi[1]);
    }
    path = [ d3_svg_lineDot4(d3_svg_lineBasisBezier3, px), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, py) ];
    --i;
    while (++i < m) {
      pi = points[i % n];
      px.shift();
      px.push(pi[0]);
      py.shift();
      py.push(pi[1]);
      d3_svg_lineBasisBezier(path, px, py);
    }
    return path.join("");
  }
  function d3_svg_lineBundle(points, tension) {
    var n = points.length - 1;
    if (n) {
      var x0 = points[0][0], y0 = points[0][1], dx = points[n][0] - x0, dy = points[n][1] - y0, i = -1, p, t;
      while (++i <= n) {
        p = points[i];
        t = i / n;
        p[0] = tension * p[0] + (1 - tension) * (x0 + t * dx);
        p[1] = tension * p[1] + (1 - tension) * (y0 + t * dy);
      }
    }
    return d3_svg_lineBasis(points);
  }
  function d3_svg_lineDot4(a, b) {
    return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3];
  }
  var d3_svg_lineBasisBezier1 = [ 0, 2 / 3, 1 / 3, 0 ], d3_svg_lineBasisBezier2 = [ 0, 1 / 3, 2 / 3, 0 ], d3_svg_lineBasisBezier3 = [ 0, 1 / 6, 2 / 3, 1 / 6 ];
  function d3_svg_lineBasisBezier(path, x, y) {
    path.push("C", d3_svg_lineDot4(d3_svg_lineBasisBezier1, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier1, y), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, y), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, y));
  }
  function d3_svg_lineSlope(p0, p1) {
    return (p1[1] - p0[1]) / (p1[0] - p0[0]);
  }
  function d3_svg_lineFiniteDifferences(points) {
    var i = 0, j = points.length - 1, m = [], p0 = points[0], p1 = points[1], d = m[0] = d3_svg_lineSlope(p0, p1);
    while (++i < j) {
      m[i] = (d + (d = d3_svg_lineSlope(p0 = p1, p1 = points[i + 1]))) / 2;
    }
    m[i] = d;
    return m;
  }
  function d3_svg_lineMonotoneTangents(points) {
    var tangents = [], d, a, b, s, m = d3_svg_lineFiniteDifferences(points), i = -1, j = points.length - 1;
    while (++i < j) {
      d = d3_svg_lineSlope(points[i], points[i + 1]);
      if (abs(d) < ε) {
        m[i] = m[i + 1] = 0;
      } else {
        a = m[i] / d;
        b = m[i + 1] / d;
        s = a * a + b * b;
        if (s > 9) {
          s = d * 3 / Math.sqrt(s);
          m[i] = s * a;
          m[i + 1] = s * b;
        }
      }
    }
    i = -1;
    while (++i <= j) {
      s = (points[Math.min(j, i + 1)][0] - points[Math.max(0, i - 1)][0]) / (6 * (1 + m[i] * m[i]));
      tangents.push([ s || 0, m[i] * s || 0 ]);
    }
    return tangents;
  }
  function d3_svg_lineMonotone(points) {
    return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineMonotoneTangents(points));
  }
  d3.svg.line.radial = function() {
    var line = d3_svg_line(d3_svg_lineRadial);
    line.radius = line.x, delete line.x;
    line.angle = line.y, delete line.y;
    return line;
  };
  function d3_svg_lineRadial(points) {
    var point, i = -1, n = points.length, r, a;
    while (++i < n) {
      point = points[i];
      r = point[0];
      a = point[1] - halfπ;
      point[0] = r * Math.cos(a);
      point[1] = r * Math.sin(a);
    }
    return points;
  }
  function d3_svg_area(projection) {
    var x0 = d3_geom_pointX, x1 = d3_geom_pointX, y0 = 0, y1 = d3_geom_pointY, defined = d3_true, interpolate = d3_svg_lineLinear, interpolateKey = interpolate.key, interpolateReverse = interpolate, L = "L", tension = .7;
    function area(data) {
      var segments = [], points0 = [], points1 = [], i = -1, n = data.length, d, fx0 = d3_functor(x0), fy0 = d3_functor(y0), fx1 = x0 === x1 ? function() {
        return x;
      } : d3_functor(x1), fy1 = y0 === y1 ? function() {
        return y;
      } : d3_functor(y1), x, y;
      function segment() {
        segments.push("M", interpolate(projection(points1), tension), L, interpolateReverse(projection(points0.reverse()), tension), "Z");
      }
      while (++i < n) {
        if (defined.call(this, d = data[i], i)) {
          points0.push([ x = +fx0.call(this, d, i), y = +fy0.call(this, d, i) ]);
          points1.push([ +fx1.call(this, d, i), +fy1.call(this, d, i) ]);
        } else if (points0.length) {
          segment();
          points0 = [];
          points1 = [];
        }
      }
      if (points0.length) segment();
      return segments.length ? segments.join("") : null;
    }
    area.x = function(_) {
      if (!arguments.length) return x1;
      x0 = x1 = _;
      return area;
    };
    area.x0 = function(_) {
      if (!arguments.length) return x0;
      x0 = _;
      return area;
    };
    area.x1 = function(_) {
      if (!arguments.length) return x1;
      x1 = _;
      return area;
    };
    area.y = function(_) {
      if (!arguments.length) return y1;
      y0 = y1 = _;
      return area;
    };
    area.y0 = function(_) {
      if (!arguments.length) return y0;
      y0 = _;
      return area;
    };
    area.y1 = function(_) {
      if (!arguments.length) return y1;
      y1 = _;
      return area;
    };
    area.defined = function(_) {
      if (!arguments.length) return defined;
      defined = _;
      return area;
    };
    area.interpolate = function(_) {
      if (!arguments.length) return interpolateKey;
      if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key;
      interpolateReverse = interpolate.reverse || interpolate;
      L = interpolate.closed ? "M" : "L";
      return area;
    };
    area.tension = function(_) {
      if (!arguments.length) return tension;
      tension = _;
      return area;
    };
    return area;
  }
  d3_svg_lineStepBefore.reverse = d3_svg_lineStepAfter;
  d3_svg_lineStepAfter.reverse = d3_svg_lineStepBefore;
  d3.svg.area = function() {
    return d3_svg_area(d3_identity);
  };
  d3.svg.area.radial = function() {
    var area = d3_svg_area(d3_svg_lineRadial);
    area.radius = area.x, delete area.x;
    area.innerRadius = area.x0, delete area.x0;
    area.outerRadius = area.x1, delete area.x1;
    area.angle = area.y, delete area.y;
    area.startAngle = area.y0, delete area.y0;
    area.endAngle = area.y1, delete area.y1;
    return area;
  };
  d3.svg.chord = function() {
    var source = d3_source, target = d3_target, radius = d3_svg_chordRadius, startAngle = d3_svg_arcStartAngle, endAngle = d3_svg_arcEndAngle;
    function chord(d, i) {
      var s = subgroup(this, source, d, i), t = subgroup(this, target, d, i);
      return "M" + s.p0 + arc(s.r, s.p1, s.a1 - s.a0) + (equals(s, t) ? curve(s.r, s.p1, s.r, s.p0) : curve(s.r, s.p1, t.r, t.p0) + arc(t.r, t.p1, t.a1 - t.a0) + curve(t.r, t.p1, s.r, s.p0)) + "Z";
    }
    function subgroup(self, f, d, i) {
      var subgroup = f.call(self, d, i), r = radius.call(self, subgroup, i), a0 = startAngle.call(self, subgroup, i) - halfπ, a1 = endAngle.call(self, subgroup, i) - halfπ;
      return {
        r: r,
        a0: a0,
        a1: a1,
        p0: [ r * Math.cos(a0), r * Math.sin(a0) ],
        p1: [ r * Math.cos(a1), r * Math.sin(a1) ]
      };
    }
    function equals(a, b) {
      return a.a0 == b.a0 && a.a1 == b.a1;
    }
    function arc(r, p, a) {
      return "A" + r + "," + r + " 0 " + +(a > π) + ",1 " + p;
    }
    function curve(r0, p0, r1, p1) {
      return "Q 0,0 " + p1;
    }
    chord.radius = function(v) {
      if (!arguments.length) return radius;
      radius = d3_functor(v);
      return chord;
    };
    chord.source = function(v) {
      if (!arguments.length) return source;
      source = d3_functor(v);
      return chord;
    };
    chord.target = function(v) {
      if (!arguments.length) return target;
      target = d3_functor(v);
      return chord;
    };
    chord.startAngle = function(v) {
      if (!arguments.length) return startAngle;
      startAngle = d3_functor(v);
      return chord;
    };
    chord.endAngle = function(v) {
      if (!arguments.length) return endAngle;
      endAngle = d3_functor(v);
      return chord;
    };
    return chord;
  };
  function d3_svg_chordRadius(d) {
    return d.radius;
  }
  d3.svg.diagonal = function() {
    var source = d3_source, target = d3_target, projection = d3_svg_diagonalProjection;
    function diagonal(d, i) {
      var p0 = source.call(this, d, i), p3 = target.call(this, d, i), m = (p0.y + p3.y) / 2, p = [ p0, {
        x: p0.x,
        y: m
      }, {
        x: p3.x,
        y: m
      }, p3 ];
      p = p.map(projection);
      return "M" + p[0] + "C" + p[1] + " " + p[2] + " " + p[3];
    }
    diagonal.source = function(x) {
      if (!arguments.length) return source;
      source = d3_functor(x);
      return diagonal;
    };
    diagonal.target = function(x) {
      if (!arguments.length) return target;
      target = d3_functor(x);
      return diagonal;
    };
    diagonal.projection = function(x) {
      if (!arguments.length) return projection;
      projection = x;
      return diagonal;
    };
    return diagonal;
  };
  function d3_svg_diagonalProjection(d) {
    return [ d.x, d.y ];
  }
  d3.svg.diagonal.radial = function() {
    var diagonal = d3.svg.diagonal(), projection = d3_svg_diagonalProjection, projection_ = diagonal.projection;
    diagonal.projection = function(x) {
      return arguments.length ? projection_(d3_svg_diagonalRadialProjection(projection = x)) : projection;
    };
    return diagonal;
  };
  function d3_svg_diagonalRadialProjection(projection) {
    return function() {
      var d = projection.apply(this, arguments), r = d[0], a = d[1] - halfπ;
      return [ r * Math.cos(a), r * Math.sin(a) ];
    };
  }
  d3.svg.symbol = function() {
    var type = d3_svg_symbolType, size = d3_svg_symbolSize;
    function symbol(d, i) {
      return (d3_svg_symbols.get(type.call(this, d, i)) || d3_svg_symbolCircle)(size.call(this, d, i));
    }
    symbol.type = function(x) {
      if (!arguments.length) return type;
      type = d3_functor(x);
      return symbol;
    };
    symbol.size = function(x) {
      if (!arguments.length) return size;
      size = d3_functor(x);
      return symbol;
    };
    return symbol;
  };
  function d3_svg_symbolSize() {
    return 64;
  }
  function d3_svg_symbolType() {
    return "circle";
  }
  function d3_svg_symbolCircle(size) {
    var r = Math.sqrt(size / π);
    return "M0," + r + "A" + r + "," + r + " 0 1,1 0," + -r + "A" + r + "," + r + " 0 1,1 0," + r + "Z";
  }
  var d3_svg_symbols = d3.map({
    circle: d3_svg_symbolCircle,
    cross: function(size) {
      var r = Math.sqrt(size / 5) / 2;
      return "M" + -3 * r + "," + -r + "H" + -r + "V" + -3 * r + "H" + r + "V" + -r + "H" + 3 * r + "V" + r + "H" + r + "V" + 3 * r + "H" + -r + "V" + r + "H" + -3 * r + "Z";
    },
    diamond: function(size) {
      var ry = Math.sqrt(size / (2 * d3_svg_symbolTan30)), rx = ry * d3_svg_symbolTan30;
      return "M0," + -ry + "L" + rx + ",0" + " 0," + ry + " " + -rx + ",0" + "Z";
    },
    square: function(size) {
      var r = Math.sqrt(size) / 2;
      return "M" + -r + "," + -r + "L" + r + "," + -r + " " + r + "," + r + " " + -r + "," + r + "Z";
    },
    "triangle-down": function(size) {
      var rx = Math.sqrt(size / d3_svg_symbolSqrt3), ry = rx * d3_svg_symbolSqrt3 / 2;
      return "M0," + ry + "L" + rx + "," + -ry + " " + -rx + "," + -ry + "Z";
    },
    "triangle-up": function(size) {
      var rx = Math.sqrt(size / d3_svg_symbolSqrt3), ry = rx * d3_svg_symbolSqrt3 / 2;
      return "M0," + -ry + "L" + rx + "," + ry + " " + -rx + "," + ry + "Z";
    }
  });
  d3.svg.symbolTypes = d3_svg_symbols.keys();
  var d3_svg_symbolSqrt3 = Math.sqrt(3), d3_svg_symbolTan30 = Math.tan(30 * d3_radians);
  d3_selectionPrototype.transition = function(name) {
    var id = d3_transitionInheritId || ++d3_transitionId, ns = d3_transitionNamespace(name), subgroups = [], subgroup, node, transition = d3_transitionInherit || {
      time: Date.now(),
      ease: d3_ease_cubicInOut,
      delay: 0,
      duration: 250
    };
    for (var j = -1, m = this.length; ++j < m; ) {
      subgroups.push(subgroup = []);
      for (var group = this[j], i = -1, n = group.length; ++i < n; ) {
        if (node = group[i]) d3_transitionNode(node, i, ns, id, transition);
        subgroup.push(node);
      }
    }
    return d3_transition(subgroups, ns, id);
  };
  d3_selectionPrototype.interrupt = function(name) {
    return this.each(name == null ? d3_selection_interrupt : d3_selection_interruptNS(d3_transitionNamespace(name)));
  };
  var d3_selection_interrupt = d3_selection_interruptNS(d3_transitionNamespace());
  function d3_selection_interruptNS(ns) {
    return function() {
      var lock, activeId, active;
      if ((lock = this[ns]) && (active = lock[activeId = lock.active])) {
        active.timer.c = null;
        active.timer.t = NaN;
        if (--lock.count) delete lock[activeId]; else delete this[ns];
        lock.active += .5;
        active.event && active.event.interrupt.call(this, this.__data__, active.index);
      }
    };
  }
  function d3_transition(groups, ns, id) {
    d3_subclass(groups, d3_transitionPrototype);
    groups.namespace = ns;
    groups.id = id;
    return groups;
  }
  var d3_transitionPrototype = [], d3_transitionId = 0, d3_transitionInheritId, d3_transitionInherit;
  d3_transitionPrototype.call = d3_selectionPrototype.call;
  d3_transitionPrototype.empty = d3_selectionPrototype.empty;
  d3_transitionPrototype.node = d3_selectionPrototype.node;
  d3_transitionPrototype.size = d3_selectionPrototype.size;
  d3.transition = function(selection, name) {
    return selection && selection.transition ? d3_transitionInheritId ? selection.transition(name) : selection : d3.selection().transition(selection);
  };
  d3.transition.prototype = d3_transitionPrototype;
  d3_transitionPrototype.select = function(selector) {
    var id = this.id, ns = this.namespace, subgroups = [], subgroup, subnode, node;
    selector = d3_selection_selector(selector);
    for (var j = -1, m = this.length; ++j < m; ) {
      subgroups.push(subgroup = []);
      for (var group = this[j], i = -1, n = group.length; ++i < n; ) {
        if ((node = group[i]) && (subnode = selector.call(node, node.__data__, i, j))) {
          if ("__data__" in node) subnode.__data__ = node.__data__;
          d3_transitionNode(subnode, i, ns, id, node[ns][id]);
          subgroup.push(subnode);
        } else {
          subgroup.push(null);
        }
      }
    }
    return d3_transition(subgroups, ns, id);
  };
  d3_transitionPrototype.selectAll = function(selector) {
    var id = this.id, ns = this.namespace, subgroups = [], subgroup, subnodes, node, subnode, transition;
    selector = d3_selection_selectorAll(selector);
    for (var j = -1, m = this.length; ++j < m; ) {
      for (var group = this[j], i = -1, n = group.length; ++i < n; ) {
        if (node = group[i]) {
          transition = node[ns][id];
          subnodes = selector.call(node, node.__data__, i, j);
          subgroups.push(subgroup = []);
          for (var k = -1, o = subnodes.length; ++k < o; ) {
            if (subnode = subnodes[k]) d3_transitionNode(subnode, k, ns, id, transition);
            subgroup.push(subnode);
          }
        }
      }
    }
    return d3_transition(subgroups, ns, id);
  };
  d3_transitionPrototype.filter = function(filter) {
    var subgroups = [], subgroup, group, node;
    if (typeof filter !== "function") filter = d3_selection_filter(filter);
    for (var j = 0, m = this.length; j < m; j++) {
      subgroups.push(subgroup = []);
      for (var group = this[j], i = 0, n = group.length; i < n; i++) {
        if ((node = group[i]) && filter.call(node, node.__data__, i, j)) {
          subgroup.push(node);
        }
      }
    }
    return d3_transition(subgroups, this.namespace, this.id);
  };
  d3_transitionPrototype.tween = function(name, tween) {
    var id = this.id, ns = this.namespace;
    if (arguments.length < 2) return this.node()[ns][id].tween.get(name);
    return d3_selection_each(this, tween == null ? function(node) {
      node[ns][id].tween.remove(name);
    } : function(node) {
      node[ns][id].tween.set(name, tween);
    });
  };
  function d3_transition_tween(groups, name, value, tween) {
    var id = groups.id, ns = groups.namespace;
    return d3_selection_each(groups, typeof value === "function" ? function(node, i, j) {
      node[ns][id].tween.set(name, tween(value.call(node, node.__data__, i, j)));
    } : (value = tween(value), function(node) {
      node[ns][id].tween.set(name, value);
    }));
  }
  d3_transitionPrototype.attr = function(nameNS, value) {
    if (arguments.length < 2) {
      for (value in nameNS) this.attr(value, nameNS[value]);
      return this;
    }
    var interpolate = nameNS == "transform" ? d3_interpolateTransform : d3_interpolate, name = d3.ns.qualify(nameNS);
    function attrNull() {
      this.removeAttribute(name);
    }
    function attrNullNS() {
      this.removeAttributeNS(name.space, name.local);
    }
    function attrTween(b) {
      return b == null ? attrNull : (b += "", function() {
        var a = this.getAttribute(name), i;
        return a !== b && (i = interpolate(a, b), function(t) {
          this.setAttribute(name, i(t));
        });
      });
    }
    function attrTweenNS(b) {
      return b == null ? attrNullNS : (b += "", function() {
        var a = this.getAttributeNS(name.space, name.local), i;
        return a !== b && (i = interpolate(a, b), function(t) {
          this.setAttributeNS(name.space, name.local, i(t));
        });
      });
    }
    return d3_transition_tween(this, "attr." + nameNS, value, name.local ? attrTweenNS : attrTween);
  };
  d3_transitionPrototype.attrTween = function(nameNS, tween) {
    var name = d3.ns.qualify(nameNS);
    function attrTween(d, i) {
      var f = tween.call(this, d, i, this.getAttribute(name));
      return f && function(t) {
        this.setAttribute(name, f(t));
      };
    }
    function attrTweenNS(d, i) {
      var f = tween.call(this, d, i, this.getAttributeNS(name.space, name.local));
      return f && function(t) {
        this.setAttributeNS(name.space, name.local, f(t));
      };
    }
    return this.tween("attr." + nameNS, name.local ? attrTweenNS : attrTween);
  };
  d3_transitionPrototype.style = function(name, value, priority) {
    var n = arguments.length;
    if (n < 3) {
      if (typeof name !== "string") {
        if (n < 2) value = "";
        for (priority in name) this.style(priority, name[priority], value);
        return this;
      }
      priority = "";
    }
    function styleNull() {
      this.style.removeProperty(name);
    }
    function styleString(b) {
      return b == null ? styleNull : (b += "", function() {
        var a = d3_window(this).getComputedStyle(this, null).getPropertyValue(name), i;
        return a !== b && (i = d3_interpolate(a, b), function(t) {
          this.style.setProperty(name, i(t), priority);
        });
      });
    }
    return d3_transition_tween(this, "style." + name, value, styleString);
  };
  d3_transitionPrototype.styleTween = function(name, tween, priority) {
    if (arguments.length < 3) priority = "";
    function styleTween(d, i) {
      var f = tween.call(this, d, i, d3_window(this).getComputedStyle(this, null).getPropertyValue(name));
      return f && function(t) {
        this.style.setProperty(name, f(t), priority);
      };
    }
    return this.tween("style." + name, styleTween);
  };
  d3_transitionPrototype.text = function(value) {
    return d3_transition_tween(this, "text", value, d3_transition_text);
  };
  function d3_transition_text(b) {
    if (b == null) b = "";
    return function() {
      this.textContent = b;
    };
  }
  d3_transitionPrototype.remove = function() {
    var ns = this.namespace;
    return this.each("end.transition", function() {
      var p;
      if (this[ns].count < 2 && (p = this.parentNode)) p.removeChild(this);
    });
  };
  d3_transitionPrototype.ease = function(value) {
    var id = this.id, ns = this.namespace;
    if (arguments.length < 1) return this.node()[ns][id].ease;
    if (typeof value !== "function") value = d3.ease.apply(d3, arguments);
    return d3_selection_each(this, function(node) {
      node[ns][id].ease = value;
    });
  };
  d3_transitionPrototype.delay = function(value) {
    var id = this.id, ns = this.namespace;
    if (arguments.length < 1) return this.node()[ns][id].delay;
    return d3_selection_each(this, typeof value === "function" ? function(node, i, j) {
      node[ns][id].delay = +value.call(node, node.__data__, i, j);
    } : (value = +value, function(node) {
      node[ns][id].delay = value;
    }));
  };
  d3_transitionPrototype.duration = function(value) {
    var id = this.id, ns = this.namespace;
    if (arguments.length < 1) return this.node()[ns][id].duration;
    return d3_selection_each(this, typeof value === "function" ? function(node, i, j) {
      node[ns][id].duration = Math.max(1, value.call(node, node.__data__, i, j));
    } : (value = Math.max(1, value), function(node) {
      node[ns][id].duration = value;
    }));
  };
  d3_transitionPrototype.each = function(type, listener) {
    var id = this.id, ns = this.namespace;
    if (arguments.length < 2) {
      var inherit = d3_transitionInherit, inheritId = d3_transitionInheritId;
      try {
        d3_transitionInheritId = id;
        d3_selection_each(this, function(node, i, j) {
          d3_transitionInherit = node[ns][id];
          type.call(node, node.__data__, i, j);
        });
      } finally {
        d3_transitionInherit = inherit;
        d3_transitionInheritId = inheritId;
      }
    } else {
      d3_selection_each(this, function(node) {
        var transition = node[ns][id];
        (transition.event || (transition.event = d3.dispatch("start", "end", "interrupt"))).on(type, listener);
      });
    }
    return this;
  };
  d3_transitionPrototype.transition = function() {
    var id0 = this.id, id1 = ++d3_transitionId, ns = this.namespace, subgroups = [], subgroup, group, node, transition;
    for (var j = 0, m = this.length; j < m; j++) {
      subgroups.push(subgroup = []);
      for (var group = this[j], i = 0, n = group.length; i < n; i++) {
        if (node = group[i]) {
          transition = node[ns][id0];
          d3_transitionNode(node, i, ns, id1, {
            time: transition.time,
            ease: transition.ease,
            delay: transition.delay + transition.duration,
            duration: transition.duration
          });
        }
        subgroup.push(node);
      }
    }
    return d3_transition(subgroups, ns, id1);
  };
  function d3_transitionNamespace(name) {
    return name == null ? "__transition__" : "__transition_" + name + "__";
  }
  function d3_transitionNode(node, i, ns, id, inherit) {
    var lock = node[ns] || (node[ns] = {
      active: 0,
      count: 0
    }), transition = lock[id], time, timer, duration, ease, tweens;
    function schedule(elapsed) {
      var delay = transition.delay;
      timer.t = delay + time;
      if (delay <= elapsed) return start(elapsed - delay);
      timer.c = start;
    }
    function start(elapsed) {
      var activeId = lock.active, active = lock[activeId];
      if (active) {
        active.timer.c = null;
        active.timer.t = NaN;
        --lock.count;
        delete lock[activeId];
        active.event && active.event.interrupt.call(node, node.__data__, active.index);
      }
      for (var cancelId in lock) {
        if (+cancelId < id) {
          var cancel = lock[cancelId];
          cancel.timer.c = null;
          cancel.timer.t = NaN;
          --lock.count;
          delete lock[cancelId];
        }
      }
      timer.c = tick;
      d3_timer(function() {
        if (timer.c && tick(elapsed || 1)) {
          timer.c = null;
          timer.t = NaN;
        }
        return 1;
      }, 0, time);
      lock.active = id;
      transition.event && transition.event.start.call(node, node.__data__, i);
      tweens = [];
      transition.tween.forEach(function(key, value) {
        if (value = value.call(node, node.__data__, i)) {
          tweens.push(value);
        }
      });
      ease = transition.ease;
      duration = transition.duration;
    }
    function tick(elapsed) {
      var t = elapsed / duration, e = ease(t), n = tweens.length;
      while (n > 0) {
        tweens[--n].call(node, e);
      }
      if (t >= 1) {
        transition.event && transition.event.end.call(node, node.__data__, i);
        if (--lock.count) delete lock[id]; else delete node[ns];
        return 1;
      }
    }
    if (!transition) {
      time = inherit.time;
      timer = d3_timer(schedule, 0, time);
      transition = lock[id] = {
        tween: new d3_Map(),
        time: time,
        timer: timer,
        delay: inherit.delay,
        duration: inherit.duration,
        ease: inherit.ease,
        index: i
      };
      inherit = null;
      ++lock.count;
    }
  }
  d3.svg.axis = function() {
    var scale = d3.scale.linear(), orient = d3_svg_axisDefaultOrient, innerTickSize = 6, outerTickSize = 6, tickPadding = 3, tickArguments_ = [ 10 ], tickValues = null, tickFormat_;
    function axis(g) {
      g.each(function() {
        var g = d3.select(this);
        var scale0 = this.__chart__ || scale, scale1 = this.__chart__ = scale.copy();
        var ticks = tickValues == null ? scale1.ticks ? scale1.ticks.apply(scale1, tickArguments_) : scale1.domain() : tickValues, tickFormat = tickFormat_ == null ? scale1.tickFormat ? scale1.tickFormat.apply(scale1, tickArguments_) : d3_identity : tickFormat_, tick = g.selectAll(".tick").data(ticks, scale1), tickEnter = tick.enter().insert("g", ".domain").attr("class", "tick").style("opacity", ε), tickExit = d3.transition(tick.exit()).style("opacity", ε).remove(), tickUpdate = d3.transition(tick.order()).style("opacity", 1), tickSpacing = Math.max(innerTickSize, 0) + tickPadding, tickTransform;
        var range = d3_scaleRange(scale1), path = g.selectAll(".domain").data([ 0 ]), pathUpdate = (path.enter().append("path").attr("class", "domain"), 
        d3.transition(path));
        tickEnter.append("line");
        tickEnter.append("text");
        var lineEnter = tickEnter.select("line"), lineUpdate = tickUpdate.select("line"), text = tick.select("text").text(tickFormat), textEnter = tickEnter.select("text"), textUpdate = tickUpdate.select("text"), sign = orient === "top" || orient === "left" ? -1 : 1, x1, x2, y1, y2;
        if (orient === "bottom" || orient === "top") {
          tickTransform = d3_svg_axisX, x1 = "x", y1 = "y", x2 = "x2", y2 = "y2";
          text.attr("dy", sign < 0 ? "0em" : ".71em").style("text-anchor", "middle");
          pathUpdate.attr("d", "M" + range[0] + "," + sign * outerTickSize + "V0H" + range[1] + "V" + sign * outerTickSize);
        } else {
          tickTransform = d3_svg_axisY, x1 = "y", y1 = "x", x2 = "y2", y2 = "x2";
          text.attr("dy", ".32em").style("text-anchor", sign < 0 ? "end" : "start");
          pathUpdate.attr("d", "M" + sign * outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + sign * outerTickSize);
        }
        lineEnter.attr(y2, sign * innerTickSize);
        textEnter.attr(y1, sign * tickSpacing);
        lineUpdate.attr(x2, 0).attr(y2, sign * innerTickSize);
        textUpdate.attr(x1, 0).attr(y1, sign * tickSpacing);
        if (scale1.rangeBand) {
          var x = scale1, dx = x.rangeBand() / 2;
          scale0 = scale1 = function(d) {
            return x(d) + dx;
          };
        } else if (scale0.rangeBand) {
          scale0 = scale1;
        } else {
          tickExit.call(tickTransform, scale1, scale0);
        }
        tickEnter.call(tickTransform, scale0, scale1);
        tickUpdate.call(tickTransform, scale1, scale1);
      });
    }
    axis.scale = function(x) {
      if (!arguments.length) return scale;
      scale = x;
      return axis;
    };
    axis.orient = function(x) {
      if (!arguments.length) return orient;
      orient = x in d3_svg_axisOrients ? x + "" : d3_svg_axisDefaultOrient;
      return axis;
    };
    axis.ticks = function() {
      if (!arguments.length) return tickArguments_;
      tickArguments_ = d3_array(arguments);
      return axis;
    };
    axis.tickValues = function(x) {
      if (!arguments.length) return tickValues;
      tickValues = x;
      return axis;
    };
    axis.tickFormat = function(x) {
      if (!arguments.length) return tickFormat_;
      tickFormat_ = x;
      return axis;
    };
    axis.tickSize = function(x) {
      var n = arguments.length;
      if (!n) return innerTickSize;
      innerTickSize = +x;
      outerTickSize = +arguments[n - 1];
      return axis;
    };
    axis.innerTickSize = function(x) {
      if (!arguments.length) return innerTickSize;
      innerTickSize = +x;
      return axis;
    };
    axis.outerTickSize = function(x) {
      if (!arguments.length) return outerTickSize;
      outerTickSize = +x;
      return axis;
    };
    axis.tickPadding = function(x) {
      if (!arguments.length) return tickPadding;
      tickPadding = +x;
      return axis;
    };
    axis.tickSubdivide = function() {
      return arguments.length && axis;
    };
    return axis;
  };
  var d3_svg_axisDefaultOrient = "bottom", d3_svg_axisOrients = {
    top: 1,
    right: 1,
    bottom: 1,
    left: 1
  };
  function d3_svg_axisX(selection, x0, x1) {
    selection.attr("transform", function(d) {
      var v0 = x0(d);
      return "translate(" + (isFinite(v0) ? v0 : x1(d)) + ",0)";
    });
  }
  function d3_svg_axisY(selection, y0, y1) {
    selection.attr("transform", function(d) {
      var v0 = y0(d);
      return "translate(0," + (isFinite(v0) ? v0 : y1(d)) + ")";
    });
  }
  d3.svg.brush = function() {
    var event = d3_eventDispatch(brush, "brushstart", "brush", "brushend"), x = null, y = null, xExtent = [ 0, 0 ], yExtent = [ 0, 0 ], xExtentDomain, yExtentDomain, xClamp = true, yClamp = true, resizes = d3_svg_brushResizes[0];
    function brush(g) {
      g.each(function() {
        var g = d3.select(this).style("pointer-events", "all").style("-webkit-tap-highlight-color", "rgba(0,0,0,0)").on("mousedown.brush", brushstart).on("touchstart.brush", brushstart);
        var background = g.selectAll(".background").data([ 0 ]);
        background.enter().append("rect").attr("class", "background").style("visibility", "hidden").style("cursor", "crosshair");
        g.selectAll(".extent").data([ 0 ]).enter().append("rect").attr("class", "extent").style("cursor", "move");
        var resize = g.selectAll(".resize").data(resizes, d3_identity);
        resize.exit().remove();
        resize.enter().append("g").attr("class", function(d) {
          return "resize " + d;
        }).style("cursor", function(d) {
          return d3_svg_brushCursor[d];
        }).append("rect").attr("x", function(d) {
          return /[ew]$/.test(d) ? -3 : null;
        }).attr("y", function(d) {
          return /^[ns]/.test(d) ? -3 : null;
        }).attr("width", 6).attr("height", 6).style("visibility", "hidden");
        resize.style("display", brush.empty() ? "none" : null);
        var gUpdate = d3.transition(g), backgroundUpdate = d3.transition(background), range;
        if (x) {
          range = d3_scaleRange(x);
          backgroundUpdate.attr("x", range[0]).attr("width", range[1] - range[0]);
          redrawX(gUpdate);
        }
        if (y) {
          range = d3_scaleRange(y);
          backgroundUpdate.attr("y", range[0]).attr("height", range[1] - range[0]);
          redrawY(gUpdate);
        }
        redraw(gUpdate);
      });
    }
    brush.event = function(g) {
      g.each(function() {
        var event_ = event.of(this, arguments), extent1 = {
          x: xExtent,
          y: yExtent,
          i: xExtentDomain,
          j: yExtentDomain
        }, extent0 = this.__chart__ || extent1;
        this.__chart__ = extent1;
        if (d3_transitionInheritId) {
          d3.select(this).transition().each("start.brush", function() {
            xExtentDomain = extent0.i;
            yExtentDomain = extent0.j;
            xExtent = extent0.x;
            yExtent = extent0.y;
            event_({
              type: "brushstart"
            });
          }).tween("brush:brush", function() {
            var xi = d3_interpolateArray(xExtent, extent1.x), yi = d3_interpolateArray(yExtent, extent1.y);
            xExtentDomain = yExtentDomain = null;
            return function(t) {
              xExtent = extent1.x = xi(t);
              yExtent = extent1.y = yi(t);
              event_({
                type: "brush",
                mode: "resize"
              });
            };
          }).each("end.brush", function() {
            xExtentDomain = extent1.i;
            yExtentDomain = extent1.j;
            event_({
              type: "brush",
              mode: "resize"
            });
            event_({
              type: "brushend"
            });
          });
        } else {
          event_({
            type: "brushstart"
          });
          event_({
            type: "brush",
            mode: "resize"
          });
          event_({
            type: "brushend"
          });
        }
      });
    };
    function redraw(g) {
      g.selectAll(".resize").attr("transform", function(d) {
        return "translate(" + xExtent[+/e$/.test(d)] + "," + yExtent[+/^s/.test(d)] + ")";
      });
    }
    function redrawX(g) {
      g.select(".extent").attr("x", xExtent[0]);
      g.selectAll(".extent,.n>rect,.s>rect").attr("width", xExtent[1] - xExtent[0]);
    }
    function redrawY(g) {
      g.select(".extent").attr("y", yExtent[0]);
      g.selectAll(".extent,.e>rect,.w>rect").attr("height", yExtent[1] - yExtent[0]);
    }
    function brushstart() {
      var target = this, eventTarget = d3.select(d3.event.target), event_ = event.of(target, arguments), g = d3.select(target), resizing = eventTarget.datum(), resizingX = !/^(n|s)$/.test(resizing) && x, resizingY = !/^(e|w)$/.test(resizing) && y, dragging = eventTarget.classed("extent"), dragRestore = d3_event_dragSuppress(target), center, origin = d3.mouse(target), offset;
      var w = d3.select(d3_window(target)).on("keydown.brush", keydown).on("keyup.brush", keyup);
      if (d3.event.changedTouches) {
        w.on("touchmove.brush", brushmove).on("touchend.brush", brushend);
      } else {
        w.on("mousemove.brush", brushmove).on("mouseup.brush", brushend);
      }
      g.interrupt().selectAll("*").interrupt();
      if (dragging) {
        origin[0] = xExtent[0] - origin[0];
        origin[1] = yExtent[0] - origin[1];
      } else if (resizing) {
        var ex = +/w$/.test(resizing), ey = +/^n/.test(resizing);
        offset = [ xExtent[1 - ex] - origin[0], yExtent[1 - ey] - origin[1] ];
        origin[0] = xExtent[ex];
        origin[1] = yExtent[ey];
      } else if (d3.event.altKey) center = origin.slice();
      g.style("pointer-events", "none").selectAll(".resize").style("display", null);
      d3.select("body").style("cursor", eventTarget.style("cursor"));
      event_({
        type: "brushstart"
      });
      brushmove();
      function keydown() {
        if (d3.event.keyCode == 32) {
          if (!dragging) {
            center = null;
            origin[0] -= xExtent[1];
            origin[1] -= yExtent[1];
            dragging = 2;
          }
          d3_eventPreventDefault();
        }
      }
      function keyup() {
        if (d3.event.keyCode == 32 && dragging == 2) {
          origin[0] += xExtent[1];
          origin[1] += yExtent[1];
          dragging = 0;
          d3_eventPreventDefault();
        }
      }
      function brushmove() {
        var point = d3.mouse(target), moved = false;
        if (offset) {
          point[0] += offset[0];
          point[1] += offset[1];
        }
        if (!dragging) {
          if (d3.event.altKey) {
            if (!center) center = [ (xExtent[0] + xExtent[1]) / 2, (yExtent[0] + yExtent[1]) / 2 ];
            origin[0] = xExtent[+(point[0] < center[0])];
            origin[1] = yExtent[+(point[1] < center[1])];
          } else center = null;
        }
        if (resizingX && move1(point, x, 0)) {
          redrawX(g);
          moved = true;
        }
        if (resizingY && move1(point, y, 1)) {
          redrawY(g);
          moved = true;
        }
        if (moved) {
          redraw(g);
          event_({
            type: "brush",
            mode: dragging ? "move" : "resize"
          });
        }
      }
      function move1(point, scale, i) {
        var range = d3_scaleRange(scale), r0 = range[0], r1 = range[1], position = origin[i], extent = i ? yExtent : xExtent, size = extent[1] - extent[0], min, max;
        if (dragging) {
          r0 -= position;
          r1 -= size + position;
        }
        min = (i ? yClamp : xClamp) ? Math.max(r0, Math.min(r1, point[i])) : point[i];
        if (dragging) {
          max = (min += position) + size;
        } else {
          if (center) position = Math.max(r0, Math.min(r1, 2 * center[i] - min));
          if (position < min) {
            max = min;
            min = position;
          } else {
            max = position;
          }
        }
        if (extent[0] != min || extent[1] != max) {
          if (i) yExtentDomain = null; else xExtentDomain = null;
          extent[0] = min;
          extent[1] = max;
          return true;
        }
      }
      function brushend() {
        brushmove();
        g.style("pointer-events", "all").selectAll(".resize").style("display", brush.empty() ? "none" : null);
        d3.select("body").style("cursor", null);
        w.on("mousemove.brush", null).on("mouseup.brush", null).on("touchmove.brush", null).on("touchend.brush", null).on("keydown.brush", null).on("keyup.brush", null);
        dragRestore();
        event_({
          type: "brushend"
        });
      }
    }
    brush.x = function(z) {
      if (!arguments.length) return x;
      x = z;
      resizes = d3_svg_brushResizes[!x << 1 | !y];
      return brush;
    };
    brush.y = function(z) {
      if (!arguments.length) return y;
      y = z;
      resizes = d3_svg_brushResizes[!x << 1 | !y];
      return brush;
    };
    brush.clamp = function(z) {
      if (!arguments.length) return x && y ? [ xClamp, yClamp ] : x ? xClamp : y ? yClamp : null;
      if (x && y) xClamp = !!z[0], yClamp = !!z[1]; else if (x) xClamp = !!z; else if (y) yClamp = !!z;
      return brush;
    };
    brush.extent = function(z) {
      var x0, x1, y0, y1, t;
      if (!arguments.length) {
        if (x) {
          if (xExtentDomain) {
            x0 = xExtentDomain[0], x1 = xExtentDomain[1];
          } else {
            x0 = xExtent[0], x1 = xExtent[1];
            if (x.invert) x0 = x.invert(x0), x1 = x.invert(x1);
            if (x1 < x0) t = x0, x0 = x1, x1 = t;
          }
        }
        if (y) {
          if (yExtentDomain) {
            y0 = yExtentDomain[0], y1 = yExtentDomain[1];
          } else {
            y0 = yExtent[0], y1 = yExtent[1];
            if (y.invert) y0 = y.invert(y0), y1 = y.invert(y1);
            if (y1 < y0) t = y0, y0 = y1, y1 = t;
          }
        }
        return x && y ? [ [ x0, y0 ], [ x1, y1 ] ] : x ? [ x0, x1 ] : y && [ y0, y1 ];
      }
      if (x) {
        x0 = z[0], x1 = z[1];
        if (y) x0 = x0[0], x1 = x1[0];
        xExtentDomain = [ x0, x1 ];
        if (x.invert) x0 = x(x0), x1 = x(x1);
        if (x1 < x0) t = x0, x0 = x1, x1 = t;
        if (x0 != xExtent[0] || x1 != xExtent[1]) xExtent = [ x0, x1 ];
      }
      if (y) {
        y0 = z[0], y1 = z[1];
        if (x) y0 = y0[1], y1 = y1[1];
        yExtentDomain = [ y0, y1 ];
        if (y.invert) y0 = y(y0), y1 = y(y1);
        if (y1 < y0) t = y0, y0 = y1, y1 = t;
        if (y0 != yExtent[0] || y1 != yExtent[1]) yExtent = [ y0, y1 ];
      }
      return brush;
    };
    brush.clear = function() {
      if (!brush.empty()) {
        xExtent = [ 0, 0 ], yExtent = [ 0, 0 ];
        xExtentDomain = yExtentDomain = null;
      }
      return brush;
    };
    brush.empty = function() {
      return !!x && xExtent[0] == xExtent[1] || !!y && yExtent[0] == yExtent[1];
    };
    return d3.rebind(brush, event, "on");
  };
  var d3_svg_brushCursor = {
    n: "ns-resize",
    e: "ew-resize",
    s: "ns-resize",
    w: "ew-resize",
    nw: "nwse-resize",
    ne: "nesw-resize",
    se: "nwse-resize",
    sw: "nesw-resize"
  };
  var d3_svg_brushResizes = [ [ "n", "e", "s", "w", "nw", "ne", "se", "sw" ], [ "e", "w" ], [ "n", "s" ], [] ];
  var d3_time_format = d3_time.format = d3_locale_enUS.timeFormat;
  var d3_time_formatUtc = d3_time_format.utc;
  var d3_time_formatIso = d3_time_formatUtc("%Y-%m-%dT%H:%M:%S.%LZ");
  d3_time_format.iso = Date.prototype.toISOString && +new Date("2000-01-01T00:00:00.000Z") ? d3_time_formatIsoNative : d3_time_formatIso;
  function d3_time_formatIsoNative(date) {
    return date.toISOString();
  }
  d3_time_formatIsoNative.parse = function(string) {
    var date = new Date(string);
    return isNaN(date) ? null : date;
  };
  d3_time_formatIsoNative.toString = d3_time_formatIso.toString;
  d3_time.second = d3_time_interval(function(date) {
    return new d3_date(Math.floor(date / 1e3) * 1e3);
  }, function(date, offset) {
    date.setTime(date.getTime() + Math.floor(offset) * 1e3);
  }, function(date) {
    return date.getSeconds();
  });
  d3_time.seconds = d3_time.second.range;
  d3_time.seconds.utc = d3_time.second.utc.range;
  d3_time.minute = d3_time_interval(function(date) {
    return new d3_date(Math.floor(date / 6e4) * 6e4);
  }, function(date, offset) {
    date.setTime(date.getTime() + Math.floor(offset) * 6e4);
  }, function(date) {
    return date.getMinutes();
  });
  d3_time.minutes = d3_time.minute.range;
  d3_time.minutes.utc = d3_time.minute.utc.range;
  d3_time.hour = d3_time_interval(function(date) {
    var timezone = date.getTimezoneOffset() / 60;
    return new d3_date((Math.floor(date / 36e5 - timezone) + timezone) * 36e5);
  }, function(date, offset) {
    date.setTime(date.getTime() + Math.floor(offset) * 36e5);
  }, function(date) {
    return date.getHours();
  });
  d3_time.hours = d3_time.hour.range;
  d3_time.hours.utc = d3_time.hour.utc.range;
  d3_time.month = d3_time_interval(function(date) {
    date = d3_time.day(date);
    date.setDate(1);
    return date;
  }, function(date, offset) {
    date.setMonth(date.getMonth() + offset);
  }, function(date) {
    return date.getMonth();
  });
  d3_time.months = d3_time.month.range;
  d3_time.months.utc = d3_time.month.utc.range;
  function d3_time_scale(linear, methods, format) {
    function scale(x) {
      return linear(x);
    }
    scale.invert = function(x) {
      return d3_time_scaleDate(linear.invert(x));
    };
    scale.domain = function(x) {
      if (!arguments.length) return linear.domain().map(d3_time_scaleDate);
      linear.domain(x);
      return scale;
    };
    function tickMethod(extent, count) {
      var span = extent[1] - extent[0], target = span / count, i = d3.bisect(d3_time_scaleSteps, target);
      return i == d3_time_scaleSteps.length ? [ methods.year, d3_scale_linearTickRange(extent.map(function(d) {
        return d / 31536e6;
      }), count)[2] ] : !i ? [ d3_time_scaleMilliseconds, d3_scale_linearTickRange(extent, count)[2] ] : methods[target / d3_time_scaleSteps[i - 1] < d3_time_scaleSteps[i] / target ? i - 1 : i];
    }
    scale.nice = function(interval, skip) {
      var domain = scale.domain(), extent = d3_scaleExtent(domain), method = interval == null ? tickMethod(extent, 10) : typeof interval === "number" && tickMethod(extent, interval);
      if (method) interval = method[0], skip = method[1];
      function skipped(date) {
        return !isNaN(date) && !interval.range(date, d3_time_scaleDate(+date + 1), skip).length;
      }
      return scale.domain(d3_scale_nice(domain, skip > 1 ? {
        floor: function(date) {
          while (skipped(date = interval.floor(date))) date = d3_time_scaleDate(date - 1);
          return date;
        },
        ceil: function(date) {
          while (skipped(date = interval.ceil(date))) date = d3_time_scaleDate(+date + 1);
          return date;
        }
      } : interval));
    };
    scale.ticks = function(interval, skip) {
      var extent = d3_scaleExtent(scale.domain()), method = interval == null ? tickMethod(extent, 10) : typeof interval === "number" ? tickMethod(extent, interval) : !interval.range && [ {
        range: interval
      }, skip ];
      if (method) interval = method[0], skip = method[1];
      return interval.range(extent[0], d3_time_scaleDate(+extent[1] + 1), skip < 1 ? 1 : skip);
    };
    scale.tickFormat = function() {
      return format;
    };
    scale.copy = function() {
      return d3_time_scale(linear.copy(), methods, format);
    };
    return d3_scale_linearRebind(scale, linear);
  }
  function d3_time_scaleDate(t) {
    return new Date(t);
  }
  var d3_time_scaleSteps = [ 1e3, 5e3, 15e3, 3e4, 6e4, 3e5, 9e5, 18e5, 36e5, 108e5, 216e5, 432e5, 864e5, 1728e5, 6048e5, 2592e6, 7776e6, 31536e6 ];
  var d3_time_scaleLocalMethods = [ [ d3_time.second, 1 ], [ d3_time.second, 5 ], [ d3_time.second, 15 ], [ d3_time.second, 30 ], [ d3_time.minute, 1 ], [ d3_time.minute, 5 ], [ d3_time.minute, 15 ], [ d3_time.minute, 30 ], [ d3_time.hour, 1 ], [ d3_time.hour, 3 ], [ d3_time.hour, 6 ], [ d3_time.hour, 12 ], [ d3_time.day, 1 ], [ d3_time.day, 2 ], [ d3_time.week, 1 ], [ d3_time.month, 1 ], [ d3_time.month, 3 ], [ d3_time.year, 1 ] ];
  var d3_time_scaleLocalFormat = d3_time_format.multi([ [ ".%L", function(d) {
    return d.getMilliseconds();
  } ], [ ":%S", function(d) {
    return d.getSeconds();
  } ], [ "%I:%M", function(d) {
    return d.getMinutes();
  } ], [ "%I %p", function(d) {
    return d.getHours();
  } ], [ "%a %d", function(d) {
    return d.getDay() && d.getDate() != 1;
  } ], [ "%b %d", function(d) {
    return d.getDate() != 1;
  } ], [ "%B", function(d) {
    return d.getMonth();
  } ], [ "%Y", d3_true ] ]);
  var d3_time_scaleMilliseconds = {
    range: function(start, stop, step) {
      return d3.range(Math.ceil(start / step) * step, +stop, step).map(d3_time_scaleDate);
    },
    floor: d3_identity,
    ceil: d3_identity
  };
  d3_time_scaleLocalMethods.year = d3_time.year;
  d3_time.scale = function() {
    return d3_time_scale(d3.scale.linear(), d3_time_scaleLocalMethods, d3_time_scaleLocalFormat);
  };
  var d3_time_scaleUtcMethods = d3_time_scaleLocalMethods.map(function(m) {
    return [ m[0].utc, m[1] ];
  });
  var d3_time_scaleUtcFormat = d3_time_formatUtc.multi([ [ ".%L", function(d) {
    return d.getUTCMilliseconds();
  } ], [ ":%S", function(d) {
    return d.getUTCSeconds();
  } ], [ "%I:%M", function(d) {
    return d.getUTCMinutes();
  } ], [ "%I %p", function(d) {
    return d.getUTCHours();
  } ], [ "%a %d", function(d) {
    return d.getUTCDay() && d.getUTCDate() != 1;
  } ], [ "%b %d", function(d) {
    return d.getUTCDate() != 1;
  } ], [ "%B", function(d) {
    return d.getUTCMonth();
  } ], [ "%Y", d3_true ] ]);
  d3_time_scaleUtcMethods.year = d3_time.year.utc;
  d3_time.scale.utc = function() {
    return d3_time_scale(d3.scale.linear(), d3_time_scaleUtcMethods, d3_time_scaleUtcFormat);
  };
  d3.text = d3_xhrType(function(request) {
    return request.responseText;
  });
  d3.json = function(url, callback) {
    return d3_xhr(url, "application/json", d3_json, callback);
  };
  function d3_json(request) {
    return JSON.parse(request.responseText);
  }
  d3.html = function(url, callback) {
    return d3_xhr(url, "text/html", d3_html, callback);
  };
  function d3_html(request) {
    var range = d3_document.createRange();
    range.selectNode(d3_document.body);
    return range.createContextualFragment(request.responseText);
  }
  d3.xml = d3_xhrType(function(request) {
    return request.responseXML;
  });
  if (true) this.d3 = d3, !(__WEBPACK_AMD_DEFINE_FACTORY__ = (d3),
				__WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ?
				(__WEBPACK_AMD_DEFINE_FACTORY__.call(exports, __webpack_require__, exports, module)) :
				__WEBPACK_AMD_DEFINE_FACTORY__),
				__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); else if (typeof module === "object" && module.exports) module.exports = d3; else this.d3 = d3;
}();

/***/ }),
/* 24 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
Object.defineProperty(exports, "__esModule", { value: true });
function arraysEqual(a, b) {
    if (a === b)
        return true;
    if (a == null || b == null)
        return false;
    if (a.length !== b.length)
        return false;
    for (var i = 0; i < a.length; ++i) {
        if (a[i] !== b[i])
            return false;
    }
    return true;
}
exports.arraysEqual = arraysEqual;
//# sourceMappingURL=util.js.map

/***/ }),
/* 25 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
Object.defineProperty(exports, "__esModule", { value: true });
var pako = __webpack_require__(26);
/**
 * Compress the buffer using zlib compression.
 */
function compress(buffer, level) {
    return pako.deflate(new Uint8Array(buffer), { level: level });
}
exports.compress = compress;
/**
 * Decompress a zlib compressed buffer.
 */
function decompress(buffer) {
    return pako.inflate(new Uint8Array(buffer)).buffer;
}
exports.decompress = decompress;
//# sourceMappingURL=compression.js.map

/***/ }),
/* 26 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
// Top level file is just a mixin of submodules & constants


var assign    = __webpack_require__(4).assign;

var deflate   = __webpack_require__(27);
var inflate   = __webpack_require__(30);
var constants = __webpack_require__(14);

var pako = {};

assign(pako, deflate, inflate, constants);

module.exports = pako;


/***/ }),
/* 27 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";



var zlib_deflate = __webpack_require__(28);
var utils        = __webpack_require__(4);
var strings      = __webpack_require__(12);
var msg          = __webpack_require__(6);
var ZStream      = __webpack_require__(13);

var toString = Object.prototype.toString;

/* Public constants ==========================================================*/
/* ===========================================================================*/

var Z_NO_FLUSH      = 0;
var Z_FINISH        = 4;

var Z_OK            = 0;
var Z_STREAM_END    = 1;
var Z_SYNC_FLUSH    = 2;

var Z_DEFAULT_COMPRESSION = -1;

var Z_DEFAULT_STRATEGY    = 0;

var Z_DEFLATED  = 8;

/* ===========================================================================*/


/**
 * class Deflate
 *
 * Generic JS-style wrapper for zlib calls. If you don't need
 * streaming behaviour - use more simple functions: [[deflate]],
 * [[deflateRaw]] and [[gzip]].
 **/

/* internal
 * Deflate.chunks -> Array
 *
 * Chunks of output data, if [[Deflate#onData]] not overridden.
 **/

/**
 * Deflate.result -> Uint8Array|Array
 *
 * Compressed result, generated by default [[Deflate#onData]]
 * and [[Deflate#onEnd]] handlers. Filled after you push last chunk
 * (call [[Deflate#push]] with `Z_FINISH` / `true` param)  or if you
 * push a chunk with explicit flush (call [[Deflate#push]] with
 * `Z_SYNC_FLUSH` param).
 **/

/**
 * Deflate.err -> Number
 *
 * Error code after deflate finished. 0 (Z_OK) on success.
 * You will not need it in real life, because deflate errors
 * are possible only on wrong options or bad `onData` / `onEnd`
 * custom handlers.
 **/

/**
 * Deflate.msg -> String
 *
 * Error message, if [[Deflate.err]] != 0
 **/


/**
 * new Deflate(options)
 * - options (Object): zlib deflate options.
 *
 * Creates new deflator instance with specified params. Throws exception
 * on bad params. Supported options:
 *
 * - `level`
 * - `windowBits`
 * - `memLevel`
 * - `strategy`
 * - `dictionary`
 *
 * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
 * for more information on these.
 *
 * Additional options, for internal needs:
 *
 * - `chunkSize` - size of generated data chunks (16K by default)
 * - `raw` (Boolean) - do raw deflate
 * - `gzip` (Boolean) - create gzip wrapper
 * - `to` (String) - if equal to 'string', then result will be "binary string"
 *    (each char code [0..255])
 * - `header` (Object) - custom header for gzip
 *   - `text` (Boolean) - true if compressed data believed to be text
 *   - `time` (Number) - modification time, unix timestamp
 *   - `os` (Number) - operation system code
 *   - `extra` (Array) - array of bytes with extra data (max 65536)
 *   - `name` (String) - file name (binary string)
 *   - `comment` (String) - comment (binary string)
 *   - `hcrc` (Boolean) - true if header crc should be added
 *
 * ##### Example:
 *
 * ```javascript
 * var pako = require('pako')
 *   , chunk1 = Uint8Array([1,2,3,4,5,6,7,8,9])
 *   , chunk2 = Uint8Array([10,11,12,13,14,15,16,17,18,19]);
 *
 * var deflate = new pako.Deflate({ level: 3});
 *
 * deflate.push(chunk1, false);
 * deflate.push(chunk2, true);  // true -> last chunk
 *
 * if (deflate.err) { throw new Error(deflate.err); }
 *
 * console.log(deflate.result);
 * ```
 **/
function Deflate(options) {
  if (!(this instanceof Deflate)) return new Deflate(options);

  this.options = utils.assign({
    level: Z_DEFAULT_COMPRESSION,
    method: Z_DEFLATED,
    chunkSize: 16384,
    windowBits: 15,
    memLevel: 8,
    strategy: Z_DEFAULT_STRATEGY,
    to: ''
  }, options || {});

  var opt = this.options;

  if (opt.raw && (opt.windowBits > 0)) {
    opt.windowBits = -opt.windowBits;
  }

  else if (opt.gzip && (opt.windowBits > 0) && (opt.windowBits < 16)) {
    opt.windowBits += 16;
  }

  this.err    = 0;      // error code, if happens (0 = Z_OK)
  this.msg    = '';     // error message
  this.ended  = false;  // used to avoid multiple onEnd() calls
  this.chunks = [];     // chunks of compressed data

  this.strm = new ZStream();
  this.strm.avail_out = 0;

  var status = zlib_deflate.deflateInit2(
    this.strm,
    opt.level,
    opt.method,
    opt.windowBits,
    opt.memLevel,
    opt.strategy
  );

  if (status !== Z_OK) {
    throw new Error(msg[status]);
  }

  if (opt.header) {
    zlib_deflate.deflateSetHeader(this.strm, opt.header);
  }

  if (opt.dictionary) {
    var dict;
    // Convert data if needed
    if (typeof opt.dictionary === 'string') {
      // If we need to compress text, change encoding to utf8.
      dict = strings.string2buf(opt.dictionary);
    } else if (toString.call(opt.dictionary) === '[object ArrayBuffer]') {
      dict = new Uint8Array(opt.dictionary);
    } else {
      dict = opt.dictionary;
    }

    status = zlib_deflate.deflateSetDictionary(this.strm, dict);

    if (status !== Z_OK) {
      throw new Error(msg[status]);
    }

    this._dict_set = true;
  }
}

/**
 * Deflate#push(data[, mode]) -> Boolean
 * - data (Uint8Array|Array|ArrayBuffer|String): input data. Strings will be
 *   converted to utf8 byte sequence.
 * - mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE modes.
 *   See constants. Skipped or `false` means Z_NO_FLUSH, `true` means Z_FINISH.
 *
 * Sends input data to deflate pipe, generating [[Deflate#onData]] calls with
 * new compressed chunks. Returns `true` on success. The last data block must have
 * mode Z_FINISH (or `true`). That will flush internal pending buffers and call
 * [[Deflate#onEnd]]. For interim explicit flushes (without ending the stream) you
 * can use mode Z_SYNC_FLUSH, keeping the compression context.
 *
 * On fail call [[Deflate#onEnd]] with error code and return false.
 *
 * We strongly recommend to use `Uint8Array` on input for best speed (output
 * array format is detected automatically). Also, don't skip last param and always
 * use the same type in your code (boolean or number). That will improve JS speed.
 *
 * For regular `Array`-s make sure all elements are [0..255].
 *
 * ##### Example
 *
 * ```javascript
 * push(chunk, false); // push one of data chunks
 * ...
 * push(chunk, true);  // push last chunk
 * ```
 **/
Deflate.prototype.push = function (data, mode) {
  var strm = this.strm;
  var chunkSize = this.options.chunkSize;
  var status, _mode;

  if (this.ended) { return false; }

  _mode = (mode === ~~mode) ? mode : ((mode === true) ? Z_FINISH : Z_NO_FLUSH);

  // Convert data if needed
  if (typeof data === 'string') {
    // If we need to compress text, change encoding to utf8.
    strm.input = strings.string2buf(data);
  } else if (toString.call(data) === '[object ArrayBuffer]') {
    strm.input = new Uint8Array(data);
  } else {
    strm.input = data;
  }

  strm.next_in = 0;
  strm.avail_in = strm.input.length;

  do {
    if (strm.avail_out === 0) {
      strm.output = new utils.Buf8(chunkSize);
      strm.next_out = 0;
      strm.avail_out = chunkSize;
    }
    status = zlib_deflate.deflate(strm, _mode);    /* no bad return value */

    if (status !== Z_STREAM_END && status !== Z_OK) {
      this.onEnd(status);
      this.ended = true;
      return false;
    }
    if (strm.avail_out === 0 || (strm.avail_in === 0 && (_mode === Z_FINISH || _mode === Z_SYNC_FLUSH))) {
      if (this.options.to === 'string') {
        this.onData(strings.buf2binstring(utils.shrinkBuf(strm.output, strm.next_out)));
      } else {
        this.onData(utils.shrinkBuf(strm.output, strm.next_out));
      }
    }
  } while ((strm.avail_in > 0 || strm.avail_out === 0) && status !== Z_STREAM_END);

  // Finalize on the last chunk.
  if (_mode === Z_FINISH) {
    status = zlib_deflate.deflateEnd(this.strm);
    this.onEnd(status);
    this.ended = true;
    return status === Z_OK;
  }

  // callback interim results if Z_SYNC_FLUSH.
  if (_mode === Z_SYNC_FLUSH) {
    this.onEnd(Z_OK);
    strm.avail_out = 0;
    return true;
  }

  return true;
};


/**
 * Deflate#onData(chunk) -> Void
 * - chunk (Uint8Array|Array|String): output data. Type of array depends
 *   on js engine support. When string output requested, each chunk
 *   will be string.
 *
 * By default, stores data blocks in `chunks[]` property and glue
 * those in `onEnd`. Override this handler, if you need another behaviour.
 **/
Deflate.prototype.onData = function (chunk) {
  this.chunks.push(chunk);
};


/**
 * Deflate#onEnd(status) -> Void
 * - status (Number): deflate status. 0 (Z_OK) on success,
 *   other if not.
 *
 * Called once after you tell deflate that the input stream is
 * complete (Z_FINISH) or should be flushed (Z_SYNC_FLUSH)
 * or if an error happened. By default - join collected chunks,
 * free memory and fill `results` / `err` properties.
 **/
Deflate.prototype.onEnd = function (status) {
  // On success - join
  if (status === Z_OK) {
    if (this.options.to === 'string') {
      this.result = this.chunks.join('');
    } else {
      this.result = utils.flattenChunks(this.chunks);
    }
  }
  this.chunks = [];
  this.err = status;
  this.msg = this.strm.msg;
};


/**
 * deflate(data[, options]) -> Uint8Array|Array|String
 * - data (Uint8Array|Array|String): input data to compress.
 * - options (Object): zlib deflate options.
 *
 * Compress `data` with deflate algorithm and `options`.
 *
 * Supported options are:
 *
 * - level
 * - windowBits
 * - memLevel
 * - strategy
 * - dictionary
 *
 * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
 * for more information on these.
 *
 * Sugar (options):
 *
 * - `raw` (Boolean) - say that we work with raw stream, if you don't wish to specify
 *   negative windowBits implicitly.
 * - `to` (String) - if equal to 'string', then result will be "binary string"
 *    (each char code [0..255])
 *
 * ##### Example:
 *
 * ```javascript
 * var pako = require('pako')
 *   , data = Uint8Array([1,2,3,4,5,6,7,8,9]);
 *
 * console.log(pako.deflate(data));
 * ```
 **/
function deflate(input, options) {
  var deflator = new Deflate(options);

  deflator.push(input, true);

  // That will never happens, if you don't cheat with options :)
  if (deflator.err) { throw deflator.msg || msg[deflator.err]; }

  return deflator.result;
}


/**
 * deflateRaw(data[, options]) -> Uint8Array|Array|String
 * - data (Uint8Array|Array|String): input data to compress.
 * - options (Object): zlib deflate options.
 *
 * The same as [[deflate]], but creates raw data, without wrapper
 * (header and adler32 crc).
 **/
function deflateRaw(input, options) {
  options = options || {};
  options.raw = true;
  return deflate(input, options);
}


/**
 * gzip(data[, options]) -> Uint8Array|Array|String
 * - data (Uint8Array|Array|String): input data to compress.
 * - options (Object): zlib deflate options.
 *
 * The same as [[deflate]], but create gzip wrapper instead of
 * deflate one.
 **/
function gzip(input, options) {
  options = options || {};
  options.gzip = true;
  return deflate(input, options);
}


exports.Deflate = Deflate;
exports.deflate = deflate;
exports.deflateRaw = deflateRaw;
exports.gzip = gzip;


/***/ }),
/* 28 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


// (C) 1995-2013 Jean-loup Gailly and Mark Adler
// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
//   claim that you wrote the original software. If you use this software
//   in a product, an acknowledgment in the product documentation would be
//   appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
//   misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.

var utils   = __webpack_require__(4);
var trees   = __webpack_require__(29);
var adler32 = __webpack_require__(10);
var crc32   = __webpack_require__(11);
var msg     = __webpack_require__(6);

/* Public constants ==========================================================*/
/* ===========================================================================*/


/* Allowed flush values; see deflate() and inflate() below for details */
var Z_NO_FLUSH      = 0;
var Z_PARTIAL_FLUSH = 1;
//var Z_SYNC_FLUSH    = 2;
var Z_FULL_FLUSH    = 3;
var Z_FINISH        = 4;
var Z_BLOCK         = 5;
//var Z_TREES         = 6;


/* Return codes for the compression/decompression functions. Negative values
 * are errors, positive values are used for special but normal events.
 */
var Z_OK            = 0;
var Z_STREAM_END    = 1;
//var Z_NEED_DICT     = 2;
//var Z_ERRNO         = -1;
var Z_STREAM_ERROR  = -2;
var Z_DATA_ERROR    = -3;
//var Z_MEM_ERROR     = -4;
var Z_BUF_ERROR     = -5;
//var Z_VERSION_ERROR = -6;


/* compression levels */
//var Z_NO_COMPRESSION      = 0;
//var Z_BEST_SPEED          = 1;
//var Z_BEST_COMPRESSION    = 9;
var Z_DEFAULT_COMPRESSION = -1;


var Z_FILTERED            = 1;
var Z_HUFFMAN_ONLY        = 2;
var Z_RLE                 = 3;
var Z_FIXED               = 4;
var Z_DEFAULT_STRATEGY    = 0;

/* Possible values of the data_type field (though see inflate()) */
//var Z_BINARY              = 0;
//var Z_TEXT                = 1;
//var Z_ASCII               = 1; // = Z_TEXT
var Z_UNKNOWN             = 2;


/* The deflate compression method */
var Z_DEFLATED  = 8;

/*============================================================================*/


var MAX_MEM_LEVEL = 9;
/* Maximum value for memLevel in deflateInit2 */
var MAX_WBITS = 15;
/* 32K LZ77 window */
var DEF_MEM_LEVEL = 8;


var LENGTH_CODES  = 29;
/* number of length codes, not counting the special END_BLOCK code */
var LITERALS      = 256;
/* number of literal bytes 0..255 */
var L_CODES       = LITERALS + 1 + LENGTH_CODES;
/* number of Literal or Length codes, including the END_BLOCK code */
var D_CODES       = 30;
/* number of distance codes */
var BL_CODES      = 19;
/* number of codes used to transfer the bit lengths */
var HEAP_SIZE     = 2 * L_CODES + 1;
/* maximum heap size */
var MAX_BITS  = 15;
/* All codes must not exceed MAX_BITS bits */

var MIN_MATCH = 3;
var MAX_MATCH = 258;
var MIN_LOOKAHEAD = (MAX_MATCH + MIN_MATCH + 1);

var PRESET_DICT = 0x20;

var INIT_STATE = 42;
var EXTRA_STATE = 69;
var NAME_STATE = 73;
var COMMENT_STATE = 91;
var HCRC_STATE = 103;
var BUSY_STATE = 113;
var FINISH_STATE = 666;

var BS_NEED_MORE      = 1; /* block not completed, need more input or more output */
var BS_BLOCK_DONE     = 2; /* block flush performed */
var BS_FINISH_STARTED = 3; /* finish started, need only more output at next deflate */
var BS_FINISH_DONE    = 4; /* finish done, accept no more input or output */

var OS_CODE = 0x03; // Unix :) . Don't detect, use this default.

function err(strm, errorCode) {
  strm.msg = msg[errorCode];
  return errorCode;
}

function rank(f) {
  return ((f) << 1) - ((f) > 4 ? 9 : 0);
}

function zero(buf) { var len = buf.length; while (--len >= 0) { buf[len] = 0; } }


/* =========================================================================
 * Flush as much pending output as possible. All deflate() output goes
 * through this function so some applications may wish to modify it
 * to avoid allocating a large strm->output buffer and copying into it.
 * (See also read_buf()).
 */
function flush_pending(strm) {
  var s = strm.state;

  //_tr_flush_bits(s);
  var len = s.pending;
  if (len > strm.avail_out) {
    len = strm.avail_out;
  }
  if (len === 0) { return; }

  utils.arraySet(strm.output, s.pending_buf, s.pending_out, len, strm.next_out);
  strm.next_out += len;
  s.pending_out += len;
  strm.total_out += len;
  strm.avail_out -= len;
  s.pending -= len;
  if (s.pending === 0) {
    s.pending_out = 0;
  }
}


function flush_block_only(s, last) {
  trees._tr_flush_block(s, (s.block_start >= 0 ? s.block_start : -1), s.strstart - s.block_start, last);
  s.block_start = s.strstart;
  flush_pending(s.strm);
}


function put_byte(s, b) {
  s.pending_buf[s.pending++] = b;
}


/* =========================================================================
 * Put a short in the pending buffer. The 16-bit value is put in MSB order.
 * IN assertion: the stream state is correct and there is enough room in
 * pending_buf.
 */
function putShortMSB(s, b) {
//  put_byte(s, (Byte)(b >> 8));
//  put_byte(s, (Byte)(b & 0xff));
  s.pending_buf[s.pending++] = (b >>> 8) & 0xff;
  s.pending_buf[s.pending++] = b & 0xff;
}


/* ===========================================================================
 * Read a new buffer from the current input stream, update the adler32
 * and total number of bytes read.  All deflate() input goes through
 * this function so some applications may wish to modify it to avoid
 * allocating a large strm->input buffer and copying from it.
 * (See also flush_pending()).
 */
function read_buf(strm, buf, start, size) {
  var len = strm.avail_in;

  if (len > size) { len = size; }
  if (len === 0) { return 0; }

  strm.avail_in -= len;

  // zmemcpy(buf, strm->next_in, len);
  utils.arraySet(buf, strm.input, strm.next_in, len, start);
  if (strm.state.wrap === 1) {
    strm.adler = adler32(strm.adler, buf, len, start);
  }

  else if (strm.state.wrap === 2) {
    strm.adler = crc32(strm.adler, buf, len, start);
  }

  strm.next_in += len;
  strm.total_in += len;

  return len;
}


/* ===========================================================================
 * 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
 * OUT assertion: the match length is not greater than s->lookahead.
 */
function longest_match(s, cur_match) {
  var chain_length = s.max_chain_length;      /* max hash chain length */
  var scan = s.strstart; /* current string */
  var match;                       /* matched string */
  var len;                           /* length of current match */
  var best_len = s.prev_length;              /* best match length so far */
  var nice_match = s.nice_match;             /* stop if match long enough */
  var limit = (s.strstart > (s.w_size - MIN_LOOKAHEAD)) ?
      s.strstart - (s.w_size - MIN_LOOKAHEAD) : 0/*NIL*/;

  var _win = s.window; // shortcut

  var wmask = s.w_mask;
  var prev  = s.prev;

  /* Stop when cur_match becomes <= limit. To simplify the code,
   * we prevent matches with the string of window index 0.
   */

  var strend = s.strstart + MAX_MATCH;
  var scan_end1  = _win[scan + best_len - 1];
  var scan_end   = _win[scan + best_len];

  /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
   * It is easy to get rid of this optimization if necessary.
   */
  // Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever");

  /* Do not waste too much time if we already have a good match: */
  if (s.prev_length >= s.good_match) {
    chain_length >>= 2;
  }
  /* Do not look for matches beyond the end of the input. This is necessary
   * to make deflate deterministic.
   */
  if (nice_match > s.lookahead) { nice_match = s.lookahead; }

  // Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");

  do {
    // Assert(cur_match < s->strstart, "no future");
    match = cur_match;

    /* Skip to next match if the match length cannot increase
     * or if the match length is less than 2.  Note that the checks below
     * for insufficient lookahead only occur occasionally for performance
     * reasons.  Therefore uninitialized memory will be accessed, and
     * conditional jumps will be made that depend on those values.
     * However the length of the match is limited to the lookahead, so
     * the output of deflate is not affected by the uninitialized values.
     */

    if (_win[match + best_len]     !== scan_end  ||
        _win[match + best_len - 1] !== scan_end1 ||
        _win[match]                !== _win[scan] ||
        _win[++match]              !== _win[scan + 1]) {
      continue;
    }

    /* The check at best_len-1 can be removed because it will be made
     * again later. (This heuristic is not always a win.)
     * It is not necessary to compare scan[2] and match[2] since they
     * are always equal when the other bytes match, given that
     * the hash keys are equal and that HASH_BITS >= 8.
     */
    scan += 2;
    match++;
    // Assert(*scan == *match, "match[2]?");

    /* We check for insufficient lookahead only every 8th comparison;
     * the 256th check will be made at strstart+258.
     */
    do {
      /*jshint noempty:false*/
    } while (_win[++scan] === _win[++match] && _win[++scan] === _win[++match] &&
             _win[++scan] === _win[++match] && _win[++scan] === _win[++match] &&
             _win[++scan] === _win[++match] && _win[++scan] === _win[++match] &&
             _win[++scan] === _win[++match] && _win[++scan] === _win[++match] &&
             scan < strend);

    // Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");

    len = MAX_MATCH - (strend - scan);
    scan = strend - MAX_MATCH;

    if (len > best_len) {
      s.match_start = cur_match;
      best_len = len;
      if (len >= nice_match) {
        break;
      }
      scan_end1  = _win[scan + best_len - 1];
      scan_end   = _win[scan + best_len];
    }
  } while ((cur_match = prev[cur_match & wmask]) > limit && --chain_length !== 0);

  if (best_len <= s.lookahead) {
    return best_len;
  }
  return s.lookahead;
}


/* ===========================================================================
 * Fill the window when the lookahead becomes insufficient.
 * Updates strstart and lookahead.
 *
 * IN assertion: lookahead < MIN_LOOKAHEAD
 * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD
 *    At least one byte has been read, or avail_in == 0; reads are
 *    performed for at least two bytes (required for the zip translate_eol
 *    option -- not supported here).
 */
function fill_window(s) {
  var _w_size = s.w_size;
  var p, n, m, more, str;

  //Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead");

  do {
    more = s.window_size - s.lookahead - s.strstart;

    // JS ints have 32 bit, block below not needed
    /* Deal with !@#$% 64K limit: */
    //if (sizeof(int) <= 2) {
    //    if (more == 0 && s->strstart == 0 && s->lookahead == 0) {
    //        more = wsize;
    //
    //  } else if (more == (unsigned)(-1)) {
    //        /* Very unlikely, but possible on 16 bit machine if
    //         * strstart == 0 && lookahead == 1 (input done a byte at time)
    //         */
    //        more--;
    //    }
    //}


    /* If the window is almost full and there is insufficient lookahead,
     * move the upper half to the lower one to make room in the upper half.
     */
    if (s.strstart >= _w_size + (_w_size - MIN_LOOKAHEAD)) {

      utils.arraySet(s.window, s.window, _w_size, _w_size, 0);
      s.match_start -= _w_size;
      s.strstart -= _w_size;
      /* we now have strstart >= MAX_DIST */
      s.block_start -= _w_size;

      /* Slide the hash table (could be avoided with 32 bit values
       at the expense of memory usage). We slide even when level == 0
       to keep the hash table consistent if we switch back to level > 0
       later. (Using level 0 permanently is not an optimal usage of
       zlib, so we don't care about this pathological case.)
       */

      n = s.hash_size;
      p = n;
      do {
        m = s.head[--p];
        s.head[p] = (m >= _w_size ? m - _w_size : 0);
      } while (--n);

      n = _w_size;
      p = n;
      do {
        m = s.prev[--p];
        s.prev[p] = (m >= _w_size ? m - _w_size : 0);
        /* If n is not on any hash chain, prev[n] is garbage but
         * its value will never be used.
         */
      } while (--n);

      more += _w_size;
    }
    if (s.strm.avail_in === 0) {
      break;
    }

    /* If there was no sliding:
     *    strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 &&
     *    more == window_size - lookahead - strstart
     * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1)
     * => more >= window_size - 2*WSIZE + 2
     * In the BIG_MEM or MMAP case (not yet supported),
     *   window_size == input_size + MIN_LOOKAHEAD  &&
     *   strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD.
     * Otherwise, window_size == 2*WSIZE so more >= 2.
     * If there was sliding, more >= WSIZE. So in all cases, more >= 2.
     */
    //Assert(more >= 2, "more < 2");
    n = read_buf(s.strm, s.window, s.strstart + s.lookahead, more);
    s.lookahead += n;

    /* Initialize the hash value now that we have some input: */
    if (s.lookahead + s.insert >= MIN_MATCH) {
      str = s.strstart - s.insert;
      s.ins_h = s.window[str];

      /* UPDATE_HASH(s, s->ins_h, s->window[str + 1]); */
      s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[str + 1]) & s.hash_mask;
//#if MIN_MATCH != 3
//        Call update_hash() MIN_MATCH-3 more times
//#endif
      while (s.insert) {
        /* UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); */
        s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[str + MIN_MATCH - 1]) & s.hash_mask;

        s.prev[str & s.w_mask] = s.head[s.ins_h];
        s.head[s.ins_h] = str;
        str++;
        s.insert--;
        if (s.lookahead + s.insert < MIN_MATCH) {
          break;
        }
      }
    }
    /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage,
     * but this is not important since only literal bytes will be emitted.
     */

  } while (s.lookahead < MIN_LOOKAHEAD && s.strm.avail_in !== 0);

  /* If the WIN_INIT bytes after the end of the current data have never been
   * written, then zero those bytes in order to avoid memory check reports of
   * the use of uninitialized (or uninitialised as Julian writes) bytes by
   * the longest match routines.  Update the high water mark for the next
   * time through here.  WIN_INIT is set to MAX_MATCH since the longest match
   * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead.
   */
//  if (s.high_water < s.window_size) {
//    var curr = s.strstart + s.lookahead;
//    var init = 0;
//
//    if (s.high_water < curr) {
//      /* Previous high water mark below current data -- zero WIN_INIT
//       * bytes or up to end of window, whichever is less.
//       */
//      init = s.window_size - curr;
//      if (init > WIN_INIT)
//        init = WIN_INIT;
//      zmemzero(s->window + curr, (unsigned)init);
//      s->high_water = curr + init;
//    }
//    else if (s->high_water < (ulg)curr + WIN_INIT) {
//      /* High water mark at or above current data, but below current data
//       * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up
//       * to end of window, whichever is less.
//       */
//      init = (ulg)curr + WIN_INIT - s->high_water;
//      if (init > s->window_size - s->high_water)
//        init = s->window_size - s->high_water;
//      zmemzero(s->window + s->high_water, (unsigned)init);
//      s->high_water += init;
//    }
//  }
//
//  Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD,
//    "not enough room for search");
}

/* ===========================================================================
 * Copy without compression as much as possible from the input stream, return
 * the current block state.
 * This function does not insert new strings in the dictionary since
 * uncompressible data is probably not useful. This function is used
 * only for the level=0 compression option.
 * NOTE: this function should be optimized to avoid extra copying from
 * window to pending_buf.
 */
function deflate_stored(s, flush) {
  /* Stored blocks are limited to 0xffff bytes, pending_buf is limited
   * to pending_buf_size, and each stored block has a 5 byte header:
   */
  var max_block_size = 0xffff;

  if (max_block_size > s.pending_buf_size - 5) {
    max_block_size = s.pending_buf_size - 5;
  }

  /* Copy as much as possible from input to output: */
  for (;;) {
    /* Fill the window as much as possible: */
    if (s.lookahead <= 1) {

      //Assert(s->strstart < s->w_size+MAX_DIST(s) ||
      //  s->block_start >= (long)s->w_size, "slide too late");
//      if (!(s.strstart < s.w_size + (s.w_size - MIN_LOOKAHEAD) ||
//        s.block_start >= s.w_size)) {
//        throw  new Error("slide too late");
//      }

      fill_window(s);
      if (s.lookahead === 0 && flush === Z_NO_FLUSH) {
        return BS_NEED_MORE;
      }

      if (s.lookahead === 0) {
        break;
      }
      /* flush the current block */
    }
    //Assert(s->block_start >= 0L, "block gone");
//    if (s.block_start < 0) throw new Error("block gone");

    s.strstart += s.lookahead;
    s.lookahead = 0;

    /* Emit a stored block if pending_buf will be full: */
    var max_start = s.block_start + max_block_size;

    if (s.strstart === 0 || s.strstart >= max_start) {
      /* strstart == 0 is possible when wraparound on 16-bit machine */
      s.lookahead = s.strstart - max_start;
      s.strstart = max_start;
      /*** FLUSH_BLOCK(s, 0); ***/
      flush_block_only(s, false);
      if (s.strm.avail_out === 0) {
        return BS_NEED_MORE;
      }
      /***/


    }
    /* Flush if we may have to slide, otherwise block_start may become
     * negative and the data will be gone:
     */
    if (s.strstart - s.block_start >= (s.w_size - MIN_LOOKAHEAD)) {
      /*** FLUSH_BLOCK(s, 0); ***/
      flush_block_only(s, false);
      if (s.strm.avail_out === 0) {
        return BS_NEED_MORE;
      }
      /***/
    }
  }

  s.insert = 0;

  if (flush === Z_FINISH) {
    /*** FLUSH_BLOCK(s, 1); ***/
    flush_block_only(s, true);
    if (s.strm.avail_out === 0) {
      return BS_FINISH_STARTED;
    }
    /***/
    return BS_FINISH_DONE;
  }

  if (s.strstart > s.block_start) {
    /*** FLUSH_BLOCK(s, 0); ***/
    flush_block_only(s, false);
    if (s.strm.avail_out === 0) {
      return BS_NEED_MORE;
    }
    /***/
  }

  return BS_NEED_MORE;
}

/* ===========================================================================
 * Compress as much as possible from the input stream, return the current
 * block state.
 * This function does not perform lazy evaluation of matches and inserts
 * new strings in the dictionary only for unmatched strings or for short
 * matches. It is used only for the fast compression options.
 */
function deflate_fast(s, flush) {
  var hash_head;        /* head of the hash chain */
  var bflush;           /* set if current block must be flushed */

  for (;;) {
    /* Make sure that we always have enough lookahead, except
     * at the end of the input file. We need MAX_MATCH bytes
     * for the next match, plus MIN_MATCH bytes to insert the
     * string following the next match.
     */
    if (s.lookahead < MIN_LOOKAHEAD) {
      fill_window(s);
      if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH) {
        return BS_NEED_MORE;
      }
      if (s.lookahead === 0) {
        break; /* flush the current block */
      }
    }

    /* Insert the string window[strstart .. strstart+2] in the
     * dictionary, and set hash_head to the head of the hash chain:
     */
    hash_head = 0/*NIL*/;
    if (s.lookahead >= MIN_MATCH) {
      /*** INSERT_STRING(s, s.strstart, hash_head); ***/
      s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask;
      hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];
      s.head[s.ins_h] = s.strstart;
      /***/
    }

    /* Find the longest match, discarding those <= prev_length.
     * At this point we have always match_length < MIN_MATCH
     */
    if (hash_head !== 0/*NIL*/ && ((s.strstart - hash_head) <= (s.w_size - MIN_LOOKAHEAD))) {
      /* To simplify the code, we prevent matches with the string
       * of window index 0 (in particular we have to avoid a match
       * of the string with itself at the start of the input file).
       */
      s.match_length = longest_match(s, hash_head);
      /* longest_match() sets match_start */
    }
    if (s.match_length >= MIN_MATCH) {
      // check_match(s, s.strstart, s.match_start, s.match_length); // for debug only

      /*** _tr_tally_dist(s, s.strstart - s.match_start,
                     s.match_length - MIN_MATCH, bflush); ***/
      bflush = trees._tr_tally(s, s.strstart - s.match_start, s.match_length - MIN_MATCH);

      s.lookahead -= s.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 (s.match_length <= s.max_lazy_match/*max_insert_length*/ && s.lookahead >= MIN_MATCH) {
        s.match_length--; /* string at strstart already in table */
        do {
          s.strstart++;
          /*** INSERT_STRING(s, s.strstart, hash_head); ***/
          s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask;
          hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];
          s.head[s.ins_h] = s.strstart;
          /***/
          /* strstart never exceeds WSIZE-MAX_MATCH, so there are
           * always MIN_MATCH bytes ahead.
           */
        } while (--s.match_length !== 0);
        s.strstart++;
      } else
      {
        s.strstart += s.match_length;
        s.match_length = 0;
        s.ins_h = s.window[s.strstart];
        /* UPDATE_HASH(s, s.ins_h, s.window[s.strstart+1]); */
        s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + 1]) & s.hash_mask;

//#if MIN_MATCH != 3
//                Call UPDATE_HASH() MIN_MATCH-3 more times
//#endif
        /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not
         * matter since it will be recomputed at next deflate call.
         */
      }
    } else {
      /* No match, output a literal byte */
      //Tracevv((stderr,"%c", s.window[s.strstart]));
      /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/
      bflush = trees._tr_tally(s, 0, s.window[s.strstart]);

      s.lookahead--;
      s.strstart++;
    }
    if (bflush) {
      /*** FLUSH_BLOCK(s, 0); ***/
      flush_block_only(s, false);
      if (s.strm.avail_out === 0) {
        return BS_NEED_MORE;
      }
      /***/
    }
  }
  s.insert = ((s.strstart < (MIN_MATCH - 1)) ? s.strstart : MIN_MATCH - 1);
  if (flush === Z_FINISH) {
    /*** FLUSH_BLOCK(s, 1); ***/
    flush_block_only(s, true);
    if (s.strm.avail_out === 0) {
      return BS_FINISH_STARTED;
    }
    /***/
    return BS_FINISH_DONE;
  }
  if (s.last_lit) {
    /*** FLUSH_BLOCK(s, 0); ***/
    flush_block_only(s, false);
    if (s.strm.avail_out === 0) {
      return BS_NEED_MORE;
    }
    /***/
  }
  return BS_BLOCK_DONE;
}

/* ===========================================================================
 * Same as above, but achieves better compression. We use a lazy
 * evaluation for matches: a match is finally adopted only if there is
 * no better match at the next window position.
 */
function deflate_slow(s, flush) {
  var hash_head;          /* head of hash chain */
  var bflush;              /* set if current block must be flushed */

  var max_insert;

  /* Process the input block. */
  for (;;) {
    /* Make sure that we always have enough lookahead, except
     * at the end of the input file. We need MAX_MATCH bytes
     * for the next match, plus MIN_MATCH bytes to insert the
     * string following the next match.
     */
    if (s.lookahead < MIN_LOOKAHEAD) {
      fill_window(s);
      if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH) {
        return BS_NEED_MORE;
      }
      if (s.lookahead === 0) { break; } /* flush the current block */
    }

    /* Insert the string window[strstart .. strstart+2] in the
     * dictionary, and set hash_head to the head of the hash chain:
     */
    hash_head = 0/*NIL*/;
    if (s.lookahead >= MIN_MATCH) {
      /*** INSERT_STRING(s, s.strstart, hash_head); ***/
      s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask;
      hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];
      s.head[s.ins_h] = s.strstart;
      /***/
    }

    /* Find the longest match, discarding those <= prev_length.
     */
    s.prev_length = s.match_length;
    s.prev_match = s.match_start;
    s.match_length = MIN_MATCH - 1;

    if (hash_head !== 0/*NIL*/ && s.prev_length < s.max_lazy_match &&
        s.strstart - hash_head <= (s.w_size - MIN_LOOKAHEAD)/*MAX_DIST(s)*/) {
      /* 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).
       */
      s.match_length = longest_match(s, hash_head);
      /* longest_match() sets match_start */

      if (s.match_length <= 5 &&
         (s.strategy === Z_FILTERED || (s.match_length === MIN_MATCH && s.strstart - s.match_start > 4096/*TOO_FAR*/))) {

        /* If prev_match is also MIN_MATCH, match_start is garbage
         * but we will ignore the current match anyway.
         */
        s.match_length = MIN_MATCH - 1;
      }
    }
    /* If there was a match at the previous step and the current
     * match is not better, output the previous match:
     */
    if (s.prev_length >= MIN_MATCH && s.match_length <= s.prev_length) {
      max_insert = s.strstart + s.lookahead - MIN_MATCH;
      /* Do not insert strings in hash table beyond this. */

      //check_match(s, s.strstart-1, s.prev_match, s.prev_length);

      /***_tr_tally_dist(s, s.strstart - 1 - s.prev_match,
                     s.prev_length - MIN_MATCH, bflush);***/
      bflush = trees._tr_tally(s, s.strstart - 1 - s.prev_match, s.prev_length - MIN_MATCH);
      /* Insert in hash table all strings up to the end of the match.
       * strstart-1 and strstart are already inserted. If there is not
       * enough lookahead, the last two strings are not inserted in
       * the hash table.
       */
      s.lookahead -= s.prev_length - 1;
      s.prev_length -= 2;
      do {
        if (++s.strstart <= max_insert) {
          /*** INSERT_STRING(s, s.strstart, hash_head); ***/
          s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask;
          hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];
          s.head[s.ins_h] = s.strstart;
          /***/
        }
      } while (--s.prev_length !== 0);
      s.match_available = 0;
      s.match_length = MIN_MATCH - 1;
      s.strstart++;

      if (bflush) {
        /*** FLUSH_BLOCK(s, 0); ***/
        flush_block_only(s, false);
        if (s.strm.avail_out === 0) {
          return BS_NEED_MORE;
        }
        /***/
      }

    } else if (s.match_available) {
      /* 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.
       */
      //Tracevv((stderr,"%c", s->window[s->strstart-1]));
      /*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/
      bflush = trees._tr_tally(s, 0, s.window[s.strstart - 1]);

      if (bflush) {
        /*** FLUSH_BLOCK_ONLY(s, 0) ***/
        flush_block_only(s, false);
        /***/
      }
      s.strstart++;
      s.lookahead--;
      if (s.strm.avail_out === 0) {
        return BS_NEED_MORE;
      }
    } else {
      /* There is no previous match to compare with, wait for
       * the next step to decide.
       */
      s.match_available = 1;
      s.strstart++;
      s.lookahead--;
    }
  }
  //Assert (flush != Z_NO_FLUSH, "no flush?");
  if (s.match_available) {
    //Tracevv((stderr,"%c", s->window[s->strstart-1]));
    /*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/
    bflush = trees._tr_tally(s, 0, s.window[s.strstart - 1]);

    s.match_available = 0;
  }
  s.insert = s.strstart < MIN_MATCH - 1 ? s.strstart : MIN_MATCH - 1;
  if (flush === Z_FINISH) {
    /*** FLUSH_BLOCK(s, 1); ***/
    flush_block_only(s, true);
    if (s.strm.avail_out === 0) {
      return BS_FINISH_STARTED;
    }
    /***/
    return BS_FINISH_DONE;
  }
  if (s.last_lit) {
    /*** FLUSH_BLOCK(s, 0); ***/
    flush_block_only(s, false);
    if (s.strm.avail_out === 0) {
      return BS_NEED_MORE;
    }
    /***/
  }

  return BS_BLOCK_DONE;
}


/* ===========================================================================
 * For Z_RLE, simply look for runs of bytes, generate matches only of distance
 * one.  Do not maintain a hash table.  (It will be regenerated if this run of
 * deflate switches away from Z_RLE.)
 */
function deflate_rle(s, flush) {
  var bflush;            /* set if current block must be flushed */
  var prev;              /* byte at distance one to match */
  var scan, strend;      /* scan goes up to strend for length of run */

  var _win = s.window;

  for (;;) {
    /* Make sure that we always have enough lookahead, except
     * at the end of the input file. We need MAX_MATCH bytes
     * for the longest run, plus one for the unrolled loop.
     */
    if (s.lookahead <= MAX_MATCH) {
      fill_window(s);
      if (s.lookahead <= MAX_MATCH && flush === Z_NO_FLUSH) {
        return BS_NEED_MORE;
      }
      if (s.lookahead === 0) { break; } /* flush the current block */
    }

    /* See how many times the previous byte repeats */
    s.match_length = 0;
    if (s.lookahead >= MIN_MATCH && s.strstart > 0) {
      scan = s.strstart - 1;
      prev = _win[scan];
      if (prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan]) {
        strend = s.strstart + MAX_MATCH;
        do {
          /*jshint noempty:false*/
        } while (prev === _win[++scan] && prev === _win[++scan] &&
                 prev === _win[++scan] && prev === _win[++scan] &&
                 prev === _win[++scan] && prev === _win[++scan] &&
                 prev === _win[++scan] && prev === _win[++scan] &&
                 scan < strend);
        s.match_length = MAX_MATCH - (strend - scan);
        if (s.match_length > s.lookahead) {
          s.match_length = s.lookahead;
        }
      }
      //Assert(scan <= s->window+(uInt)(s->window_size-1), "wild scan");
    }

    /* Emit match if have run of MIN_MATCH or longer, else emit literal */
    if (s.match_length >= MIN_MATCH) {
      //check_match(s, s.strstart, s.strstart - 1, s.match_length);

      /*** _tr_tally_dist(s, 1, s.match_length - MIN_MATCH, bflush); ***/
      bflush = trees._tr_tally(s, 1, s.match_length - MIN_MATCH);

      s.lookahead -= s.match_length;
      s.strstart += s.match_length;
      s.match_length = 0;
    } else {
      /* No match, output a literal byte */
      //Tracevv((stderr,"%c", s->window[s->strstart]));
      /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/
      bflush = trees._tr_tally(s, 0, s.window[s.strstart]);

      s.lookahead--;
      s.strstart++;
    }
    if (bflush) {
      /*** FLUSH_BLOCK(s, 0); ***/
      flush_block_only(s, false);
      if (s.strm.avail_out === 0) {
        return BS_NEED_MORE;
      }
      /***/
    }
  }
  s.insert = 0;
  if (flush === Z_FINISH) {
    /*** FLUSH_BLOCK(s, 1); ***/
    flush_block_only(s, true);
    if (s.strm.avail_out === 0) {
      return BS_FINISH_STARTED;
    }
    /***/
    return BS_FINISH_DONE;
  }
  if (s.last_lit) {
    /*** FLUSH_BLOCK(s, 0); ***/
    flush_block_only(s, false);
    if (s.strm.avail_out === 0) {
      return BS_NEED_MORE;
    }
    /***/
  }
  return BS_BLOCK_DONE;
}

/* ===========================================================================
 * For Z_HUFFMAN_ONLY, do not look for matches.  Do not maintain a hash table.
 * (It will be regenerated if this run of deflate switches away from Huffman.)
 */
function deflate_huff(s, flush) {
  var bflush;             /* set if current block must be flushed */

  for (;;) {
    /* Make sure that we have a literal to write. */
    if (s.lookahead === 0) {
      fill_window(s);
      if (s.lookahead === 0) {
        if (flush === Z_NO_FLUSH) {
          return BS_NEED_MORE;
        }
        break;      /* flush the current block */
      }
    }

    /* Output a literal byte */
    s.match_length = 0;
    //Tracevv((stderr,"%c", s->window[s->strstart]));
    /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/
    bflush = trees._tr_tally(s, 0, s.window[s.strstart]);
    s.lookahead--;
    s.strstart++;
    if (bflush) {
      /*** FLUSH_BLOCK(s, 0); ***/
      flush_block_only(s, false);
      if (s.strm.avail_out === 0) {
        return BS_NEED_MORE;
      }
      /***/
    }
  }
  s.insert = 0;
  if (flush === Z_FINISH) {
    /*** FLUSH_BLOCK(s, 1); ***/
    flush_block_only(s, true);
    if (s.strm.avail_out === 0) {
      return BS_FINISH_STARTED;
    }
    /***/
    return BS_FINISH_DONE;
  }
  if (s.last_lit) {
    /*** FLUSH_BLOCK(s, 0); ***/
    flush_block_only(s, false);
    if (s.strm.avail_out === 0) {
      return BS_NEED_MORE;
    }
    /***/
  }
  return BS_BLOCK_DONE;
}

/* 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.
 */
function Config(good_length, max_lazy, nice_length, max_chain, func) {
  this.good_length = good_length;
  this.max_lazy = max_lazy;
  this.nice_length = nice_length;
  this.max_chain = max_chain;
  this.func = func;
}

var configuration_table;

configuration_table = [
  /*      good lazy nice chain */
  new Config(0, 0, 0, 0, deflate_stored),          /* 0 store only */
  new Config(4, 4, 8, 4, deflate_fast),            /* 1 max speed, no lazy matches */
  new Config(4, 5, 16, 8, deflate_fast),           /* 2 */
  new Config(4, 6, 32, 32, deflate_fast),          /* 3 */

  new Config(4, 4, 16, 16, deflate_slow),          /* 4 lazy matches */
  new Config(8, 16, 32, 32, deflate_slow),         /* 5 */
  new Config(8, 16, 128, 128, deflate_slow),       /* 6 */
  new Config(8, 32, 128, 256, deflate_slow),       /* 7 */
  new Config(32, 128, 258, 1024, deflate_slow),    /* 8 */
  new Config(32, 258, 258, 4096, deflate_slow)     /* 9 max compression */
];


/* ===========================================================================
 * Initialize the "longest match" routines for a new zlib stream
 */
function lm_init(s) {
  s.window_size = 2 * s.w_size;

  /*** CLEAR_HASH(s); ***/
  zero(s.head); // Fill with NIL (= 0);

  /* Set the default configuration parameters:
   */
  s.max_lazy_match = configuration_table[s.level].max_lazy;
  s.good_match = configuration_table[s.level].good_length;
  s.nice_match = configuration_table[s.level].nice_length;
  s.max_chain_length = configuration_table[s.level].max_chain;

  s.strstart = 0;
  s.block_start = 0;
  s.lookahead = 0;
  s.insert = 0;
  s.match_length = s.prev_length = MIN_MATCH - 1;
  s.match_available = 0;
  s.ins_h = 0;
}


function DeflateState() {
  this.strm = null;            /* pointer back to this zlib stream */
  this.status = 0;            /* as the name implies */
  this.pending_buf = null;      /* output still pending */
  this.pending_buf_size = 0;  /* size of pending_buf */
  this.pending_out = 0;       /* next pending byte to output to the stream */
  this.pending = 0;           /* nb of bytes in the pending buffer */
  this.wrap = 0;              /* bit 0 true for zlib, bit 1 true for gzip */
  this.gzhead = null;         /* gzip header information to write */
  this.gzindex = 0;           /* where in extra, name, or comment */
  this.method = Z_DEFLATED; /* can only be DEFLATED */
  this.last_flush = -1;   /* value of flush param for previous deflate call */

  this.w_size = 0;  /* LZ77 window size (32K by default) */
  this.w_bits = 0;  /* log2(w_size)  (8..16) */
  this.w_mask = 0;  /* w_size - 1 */

  this.window = null;
  /* Sliding window. Input bytes are read into the second half of the window,
   * and move to the first half later to keep a dictionary of at least wSize
   * bytes. With this organization, matches are limited to a distance of
   * wSize-MAX_MATCH bytes, but this ensures that IO is always
   * performed with a length multiple of the block size.
   */

  this.window_size = 0;
  /* Actual size of window: 2*wSize, except when the user input buffer
   * is directly used as sliding window.
   */

  this.prev = null;
  /* Link to older string with same hash index. To limit the size of this
   * array to 64K, this link is maintained only for the last 32K strings.
   * An index in this array is thus a window index modulo 32K.
   */

  this.head = null;   /* Heads of the hash chains or NIL. */

  this.ins_h = 0;       /* hash index of string to be inserted */
  this.hash_size = 0;   /* number of elements in hash table */
  this.hash_bits = 0;   /* log2(hash_size) */
  this.hash_mask = 0;   /* hash_size-1 */

  this.hash_shift = 0;
  /* Number of bits by which ins_h must be shifted at each input
   * step. It must be such that after MIN_MATCH steps, the oldest
   * byte no longer takes part in the hash key, that is:
   *   hash_shift * MIN_MATCH >= hash_bits
   */

  this.block_start = 0;
  /* Window position at the beginning of the current output block. Gets
   * negative when the window is moved backwards.
   */

  this.match_length = 0;      /* length of best match */
  this.prev_match = 0;        /* previous match */
  this.match_available = 0;   /* set if previous match exists */
  this.strstart = 0;          /* start of string to insert */
  this.match_start = 0;       /* start of matching string */
  this.lookahead = 0;         /* number of valid bytes ahead in window */

  this.prev_length = 0;
  /* Length of the best match at previous step. Matches not greater than this
   * are discarded. This is used in the lazy match evaluation.
   */

  this.max_chain_length = 0;
  /* To speed up deflation, hash chains are never searched beyond this
   * length.  A higher limit improves compression ratio but degrades the
   * speed.
   */

  this.max_lazy_match = 0;
  /* Attempt to find a better match only when the current match is strictly
   * smaller than this value. This mechanism is used only for compression
   * levels >= 4.
   */
  // That's alias to max_lazy_match, don't use directly
  //this.max_insert_length = 0;
  /* Insert new strings in the hash table only if the match length is not
   * greater than this length. This saves time but degrades compression.
   * max_insert_length is used only for compression levels <= 3.
   */

  this.level = 0;     /* compression level (1..9) */
  this.strategy = 0;  /* favor or force Huffman coding*/

  this.good_match = 0;
  /* Use a faster search when the previous match is longer than this */

  this.nice_match = 0; /* Stop searching when current match exceeds this */

              /* used by trees.c: */

  /* Didn't use ct_data typedef below to suppress compiler warning */

  // struct ct_data_s dyn_ltree[HEAP_SIZE];   /* literal and length tree */
  // struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */
  // struct ct_data_s bl_tree[2*BL_CODES+1];  /* Huffman tree for bit lengths */

  // Use flat array of DOUBLE size, with interleaved fata,
  // because JS does not support effective
  this.dyn_ltree  = new utils.Buf16(HEAP_SIZE * 2);
  this.dyn_dtree  = new utils.Buf16((2 * D_CODES + 1) * 2);
  this.bl_tree    = new utils.Buf16((2 * BL_CODES + 1) * 2);
  zero(this.dyn_ltree);
  zero(this.dyn_dtree);
  zero(this.bl_tree);

  this.l_desc   = null;         /* desc. for literal tree */
  this.d_desc   = null;         /* desc. for distance tree */
  this.bl_desc  = null;         /* desc. for bit length tree */

  //ush bl_count[MAX_BITS+1];
  this.bl_count = new utils.Buf16(MAX_BITS + 1);
  /* number of codes at each bit length for an optimal tree */

  //int heap[2*L_CODES+1];      /* heap used to build the Huffman trees */
  this.heap = new utils.Buf16(2 * L_CODES + 1);  /* heap used to build the Huffman trees */
  zero(this.heap);

  this.heap_len = 0;               /* number of elements in the heap */
  this.heap_max = 0;               /* element of largest frequency */
  /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used.
   * The same heap array is used to build all trees.
   */

  this.depth = new utils.Buf16(2 * L_CODES + 1); //uch depth[2*L_CODES+1];
  zero(this.depth);
  /* Depth of each subtree used as tie breaker for trees of equal frequency
   */

  this.l_buf = 0;          /* buffer index for literals or lengths */

  this.lit_bufsize = 0;
  /* Size of match buffer for literals/lengths.  There are 4 reasons for
   * limiting lit_bufsize to 64K:
   *   - frequencies can be kept in 16 bit counters
   *   - if compression is not successful for the first block, all input
   *     data is still in the window so we can still emit a stored block even
   *     when input comes from standard input.  (This can also be done for
   *     all blocks if lit_bufsize is not greater than 32K.)
   *   - if compression is not successful for a file smaller than 64K, we can
   *     even emit a stored file instead of a stored block (saving 5 bytes).
   *     This is applicable only for zip (not gzip or zlib).
   *   - creating new Huffman trees less frequently may not provide fast
   *     adaptation to changes in the input data statistics. (Take for
   *     example a binary file with poorly compressible code followed by
   *     a highly compressible string table.) Smaller buffer sizes give
   *     fast adaptation but have of course the overhead of transmitting
   *     trees more frequently.
   *   - I can't count above 4
   */

  this.last_lit = 0;      /* running index in l_buf */

  this.d_buf = 0;
  /* Buffer index for distances. To simplify the code, d_buf and l_buf have
   * the same number of elements. To use different lengths, an extra flag
   * array would be necessary.
   */

  this.opt_len = 0;       /* bit length of current block with optimal trees */
  this.static_len = 0;    /* bit length of current block with static trees */
  this.matches = 0;       /* number of string matches in current block */
  this.insert = 0;        /* bytes at end of window left to insert */


  this.bi_buf = 0;
  /* Output buffer. bits are inserted starting at the bottom (least
   * significant bits).
   */
  this.bi_valid = 0;
  /* Number of valid bits in bi_buf.  All bits above the last valid bit
   * are always zero.
   */

  // Used for window memory init. We safely ignore it for JS. That makes
  // sense only for pointers and memory check tools.
  //this.high_water = 0;
  /* High water mark offset in window for initialized bytes -- bytes above
   * this are set to zero in order to avoid memory check warnings when
   * longest match routines access bytes past the input.  This is then
   * updated to the new high water mark.
   */
}


function deflateResetKeep(strm) {
  var s;

  if (!strm || !strm.state) {
    return err(strm, Z_STREAM_ERROR);
  }

  strm.total_in = strm.total_out = 0;
  strm.data_type = Z_UNKNOWN;

  s = strm.state;
  s.pending = 0;
  s.pending_out = 0;

  if (s.wrap < 0) {
    s.wrap = -s.wrap;
    /* was made negative by deflate(..., Z_FINISH); */
  }
  s.status = (s.wrap ? INIT_STATE : BUSY_STATE);
  strm.adler = (s.wrap === 2) ?
    0  // crc32(0, Z_NULL, 0)
  :
    1; // adler32(0, Z_NULL, 0)
  s.last_flush = Z_NO_FLUSH;
  trees._tr_init(s);
  return Z_OK;
}


function deflateReset(strm) {
  var ret = deflateResetKeep(strm);
  if (ret === Z_OK) {
    lm_init(strm.state);
  }
  return ret;
}


function deflateSetHeader(strm, head) {
  if (!strm || !strm.state) { return Z_STREAM_ERROR; }
  if (strm.state.wrap !== 2) { return Z_STREAM_ERROR; }
  strm.state.gzhead = head;
  return Z_OK;
}


function deflateInit2(strm, level, method, windowBits, memLevel, strategy) {
  if (!strm) { // === Z_NULL
    return Z_STREAM_ERROR;
  }
  var wrap = 1;

  if (level === Z_DEFAULT_COMPRESSION) {
    level = 6;
  }

  if (windowBits < 0) { /* suppress zlib wrapper */
    wrap = 0;
    windowBits = -windowBits;
  }

  else if (windowBits > 15) {
    wrap = 2;           /* write gzip wrapper instead */
    windowBits -= 16;
  }


  if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method !== Z_DEFLATED ||
    windowBits < 8 || windowBits > 15 || level < 0 || level > 9 ||
    strategy < 0 || strategy > Z_FIXED) {
    return err(strm, Z_STREAM_ERROR);
  }


  if (windowBits === 8) {
    windowBits = 9;
  }
  /* until 256-byte window bug fixed */

  var s = new DeflateState();

  strm.state = s;
  s.strm = strm;

  s.wrap = wrap;
  s.gzhead = null;
  s.w_bits = windowBits;
  s.w_size = 1 << s.w_bits;
  s.w_mask = s.w_size - 1;

  s.hash_bits = memLevel + 7;
  s.hash_size = 1 << s.hash_bits;
  s.hash_mask = s.hash_size - 1;
  s.hash_shift = ~~((s.hash_bits + MIN_MATCH - 1) / MIN_MATCH);

  s.window = new utils.Buf8(s.w_size * 2);
  s.head = new utils.Buf16(s.hash_size);
  s.prev = new utils.Buf16(s.w_size);

  // Don't need mem init magic for JS.
  //s.high_water = 0;  /* nothing written to s->window yet */

  s.lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */

  s.pending_buf_size = s.lit_bufsize * 4;

  //overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2);
  //s->pending_buf = (uchf *) overlay;
  s.pending_buf = new utils.Buf8(s.pending_buf_size);

  // It is offset from `s.pending_buf` (size is `s.lit_bufsize * 2`)
  //s->d_buf = overlay + s->lit_bufsize/sizeof(ush);
  s.d_buf = 1 * s.lit_bufsize;

  //s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize;
  s.l_buf = (1 + 2) * s.lit_bufsize;

  s.level = level;
  s.strategy = strategy;
  s.method = method;

  return deflateReset(strm);
}

function deflateInit(strm, level) {
  return deflateInit2(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
}


function deflate(strm, flush) {
  var old_flush, s;
  var beg, val; // for gzip header write only

  if (!strm || !strm.state ||
    flush > Z_BLOCK || flush < 0) {
    return strm ? err(strm, Z_STREAM_ERROR) : Z_STREAM_ERROR;
  }

  s = strm.state;

  if (!strm.output ||
      (!strm.input && strm.avail_in !== 0) ||
      (s.status === FINISH_STATE && flush !== Z_FINISH)) {
    return err(strm, (strm.avail_out === 0) ? Z_BUF_ERROR : Z_STREAM_ERROR);
  }

  s.strm = strm; /* just in case */
  old_flush = s.last_flush;
  s.last_flush = flush;

  /* Write the header */
  if (s.status === INIT_STATE) {

    if (s.wrap === 2) { // GZIP header
      strm.adler = 0;  //crc32(0L, Z_NULL, 0);
      put_byte(s, 31);
      put_byte(s, 139);
      put_byte(s, 8);
      if (!s.gzhead) { // s->gzhead == Z_NULL
        put_byte(s, 0);
        put_byte(s, 0);
        put_byte(s, 0);
        put_byte(s, 0);
        put_byte(s, 0);
        put_byte(s, s.level === 9 ? 2 :
                    (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2 ?
                     4 : 0));
        put_byte(s, OS_CODE);
        s.status = BUSY_STATE;
      }
      else {
        put_byte(s, (s.gzhead.text ? 1 : 0) +
                    (s.gzhead.hcrc ? 2 : 0) +
                    (!s.gzhead.extra ? 0 : 4) +
                    (!s.gzhead.name ? 0 : 8) +
                    (!s.gzhead.comment ? 0 : 16)
                );
        put_byte(s, s.gzhead.time & 0xff);
        put_byte(s, (s.gzhead.time >> 8) & 0xff);
        put_byte(s, (s.gzhead.time >> 16) & 0xff);
        put_byte(s, (s.gzhead.time >> 24) & 0xff);
        put_byte(s, s.level === 9 ? 2 :
                    (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2 ?
                     4 : 0));
        put_byte(s, s.gzhead.os & 0xff);
        if (s.gzhead.extra && s.gzhead.extra.length) {
          put_byte(s, s.gzhead.extra.length & 0xff);
          put_byte(s, (s.gzhead.extra.length >> 8) & 0xff);
        }
        if (s.gzhead.hcrc) {
          strm.adler = crc32(strm.adler, s.pending_buf, s.pending, 0);
        }
        s.gzindex = 0;
        s.status = EXTRA_STATE;
      }
    }
    else // DEFLATE header
    {
      var header = (Z_DEFLATED + ((s.w_bits - 8) << 4)) << 8;
      var level_flags = -1;

      if (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2) {
        level_flags = 0;
      } else if (s.level < 6) {
        level_flags = 1;
      } else if (s.level === 6) {
        level_flags = 2;
      } else {
        level_flags = 3;
      }
      header |= (level_flags << 6);
      if (s.strstart !== 0) { header |= PRESET_DICT; }
      header += 31 - (header % 31);

      s.status = BUSY_STATE;
      putShortMSB(s, header);

      /* Save the adler32 of the preset dictionary: */
      if (s.strstart !== 0) {
        putShortMSB(s, strm.adler >>> 16);
        putShortMSB(s, strm.adler & 0xffff);
      }
      strm.adler = 1; // adler32(0L, Z_NULL, 0);
    }
  }

//#ifdef GZIP
  if (s.status === EXTRA_STATE) {
    if (s.gzhead.extra/* != Z_NULL*/) {
      beg = s.pending;  /* start of bytes to update crc */

      while (s.gzindex < (s.gzhead.extra.length & 0xffff)) {
        if (s.pending === s.pending_buf_size) {
          if (s.gzhead.hcrc && s.pending > beg) {
            strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
          }
          flush_pending(strm);
          beg = s.pending;
          if (s.pending === s.pending_buf_size) {
            break;
          }
        }
        put_byte(s, s.gzhead.extra[s.gzindex] & 0xff);
        s.gzindex++;
      }
      if (s.gzhead.hcrc && s.pending > beg) {
        strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
      }
      if (s.gzindex === s.gzhead.extra.length) {
        s.gzindex = 0;
        s.status = NAME_STATE;
      }
    }
    else {
      s.status = NAME_STATE;
    }
  }
  if (s.status === NAME_STATE) {
    if (s.gzhead.name/* != Z_NULL*/) {
      beg = s.pending;  /* start of bytes to update crc */
      //int val;

      do {
        if (s.pending === s.pending_buf_size) {
          if (s.gzhead.hcrc && s.pending > beg) {
            strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
          }
          flush_pending(strm);
          beg = s.pending;
          if (s.pending === s.pending_buf_size) {
            val = 1;
            break;
          }
        }
        // JS specific: little magic to add zero terminator to end of string
        if (s.gzindex < s.gzhead.name.length) {
          val = s.gzhead.name.charCodeAt(s.gzindex++) & 0xff;
        } else {
          val = 0;
        }
        put_byte(s, val);
      } while (val !== 0);

      if (s.gzhead.hcrc && s.pending > beg) {
        strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
      }
      if (val === 0) {
        s.gzindex = 0;
        s.status = COMMENT_STATE;
      }
    }
    else {
      s.status = COMMENT_STATE;
    }
  }
  if (s.status === COMMENT_STATE) {
    if (s.gzhead.comment/* != Z_NULL*/) {
      beg = s.pending;  /* start of bytes to update crc */
      //int val;

      do {
        if (s.pending === s.pending_buf_size) {
          if (s.gzhead.hcrc && s.pending > beg) {
            strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
          }
          flush_pending(strm);
          beg = s.pending;
          if (s.pending === s.pending_buf_size) {
            val = 1;
            break;
          }
        }
        // JS specific: little magic to add zero terminator to end of string
        if (s.gzindex < s.gzhead.comment.length) {
          val = s.gzhead.comment.charCodeAt(s.gzindex++) & 0xff;
        } else {
          val = 0;
        }
        put_byte(s, val);
      } while (val !== 0);

      if (s.gzhead.hcrc && s.pending > beg) {
        strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
      }
      if (val === 0) {
        s.status = HCRC_STATE;
      }
    }
    else {
      s.status = HCRC_STATE;
    }
  }
  if (s.status === HCRC_STATE) {
    if (s.gzhead.hcrc) {
      if (s.pending + 2 > s.pending_buf_size) {
        flush_pending(strm);
      }
      if (s.pending + 2 <= s.pending_buf_size) {
        put_byte(s, strm.adler & 0xff);
        put_byte(s, (strm.adler >> 8) & 0xff);
        strm.adler = 0; //crc32(0L, Z_NULL, 0);
        s.status = BUSY_STATE;
      }
    }
    else {
      s.status = BUSY_STATE;
    }
  }
//#endif

  /* Flush as much pending output as possible */
  if (s.pending !== 0) {
    flush_pending(strm);
    if (strm.avail_out === 0) {
      /* Since avail_out is 0, deflate will be called again with
       * more output space, but possibly with both pending and
       * avail_in equal to zero. There won't be anything to do,
       * but this is not an error situation so make sure we
       * return OK instead of BUF_ERROR at next call of deflate:
       */
      s.last_flush = -1;
      return Z_OK;
    }

    /* Make sure there is something to do and avoid duplicate consecutive
     * flushes. For repeated and useless calls with Z_FINISH, we keep
     * returning Z_STREAM_END instead of Z_BUF_ERROR.
     */
  } else if (strm.avail_in === 0 && rank(flush) <= rank(old_flush) &&
    flush !== Z_FINISH) {
    return err(strm, Z_BUF_ERROR);
  }

  /* User must not provide more input after the first FINISH: */
  if (s.status === FINISH_STATE && strm.avail_in !== 0) {
    return err(strm, Z_BUF_ERROR);
  }

  /* Start a new block or continue the current one.
   */
  if (strm.avail_in !== 0 || s.lookahead !== 0 ||
    (flush !== Z_NO_FLUSH && s.status !== FINISH_STATE)) {
    var bstate = (s.strategy === Z_HUFFMAN_ONLY) ? deflate_huff(s, flush) :
      (s.strategy === Z_RLE ? deflate_rle(s, flush) :
        configuration_table[s.level].func(s, flush));

    if (bstate === BS_FINISH_STARTED || bstate === BS_FINISH_DONE) {
      s.status = FINISH_STATE;
    }
    if (bstate === BS_NEED_MORE || bstate === BS_FINISH_STARTED) {
      if (strm.avail_out === 0) {
        s.last_flush = -1;
        /* avoid BUF_ERROR next call, see above */
      }
      return Z_OK;
      /* If flush != Z_NO_FLUSH && avail_out == 0, the next call
       * of deflate should use the same flush parameter to make sure
       * that the flush is complete. So we don't have to output an
       * empty block here, this will be done at next call. This also
       * ensures that for a very small output buffer, we emit at most
       * one empty block.
       */
    }
    if (bstate === BS_BLOCK_DONE) {
      if (flush === Z_PARTIAL_FLUSH) {
        trees._tr_align(s);
      }
      else if (flush !== Z_BLOCK) { /* FULL_FLUSH or SYNC_FLUSH */

        trees._tr_stored_block(s, 0, 0, false);
        /* For a full flush, this empty block will be recognized
         * as a special marker by inflate_sync().
         */
        if (flush === Z_FULL_FLUSH) {
          /*** CLEAR_HASH(s); ***/             /* forget history */
          zero(s.head); // Fill with NIL (= 0);

          if (s.lookahead === 0) {
            s.strstart = 0;
            s.block_start = 0;
            s.insert = 0;
          }
        }
      }
      flush_pending(strm);
      if (strm.avail_out === 0) {
        s.last_flush = -1; /* avoid BUF_ERROR at next call, see above */
        return Z_OK;
      }
    }
  }
  //Assert(strm->avail_out > 0, "bug2");
  //if (strm.avail_out <= 0) { throw new Error("bug2");}

  if (flush !== Z_FINISH) { return Z_OK; }
  if (s.wrap <= 0) { return Z_STREAM_END; }

  /* Write the trailer */
  if (s.wrap === 2) {
    put_byte(s, strm.adler & 0xff);
    put_byte(s, (strm.adler >> 8) & 0xff);
    put_byte(s, (strm.adler >> 16) & 0xff);
    put_byte(s, (strm.adler >> 24) & 0xff);
    put_byte(s, strm.total_in & 0xff);
    put_byte(s, (strm.total_in >> 8) & 0xff);
    put_byte(s, (strm.total_in >> 16) & 0xff);
    put_byte(s, (strm.total_in >> 24) & 0xff);
  }
  else
  {
    putShortMSB(s, strm.adler >>> 16);
    putShortMSB(s, strm.adler & 0xffff);
  }

  flush_pending(strm);
  /* If avail_out is zero, the application will call deflate again
   * to flush the rest.
   */
  if (s.wrap > 0) { s.wrap = -s.wrap; }
  /* write the trailer only once! */
  return s.pending !== 0 ? Z_OK : Z_STREAM_END;
}

function deflateEnd(strm) {
  var status;

  if (!strm/*== Z_NULL*/ || !strm.state/*== Z_NULL*/) {
    return Z_STREAM_ERROR;
  }

  status = strm.state.status;
  if (status !== INIT_STATE &&
    status !== EXTRA_STATE &&
    status !== NAME_STATE &&
    status !== COMMENT_STATE &&
    status !== HCRC_STATE &&
    status !== BUSY_STATE &&
    status !== FINISH_STATE
  ) {
    return err(strm, Z_STREAM_ERROR);
  }

  strm.state = null;

  return status === BUSY_STATE ? err(strm, Z_DATA_ERROR) : Z_OK;
}


/* =========================================================================
 * Initializes the compression dictionary from the given byte
 * sequence without producing any compressed output.
 */
function deflateSetDictionary(strm, dictionary) {
  var dictLength = dictionary.length;

  var s;
  var str, n;
  var wrap;
  var avail;
  var next;
  var input;
  var tmpDict;

  if (!strm/*== Z_NULL*/ || !strm.state/*== Z_NULL*/) {
    return Z_STREAM_ERROR;
  }

  s = strm.state;
  wrap = s.wrap;

  if (wrap === 2 || (wrap === 1 && s.status !== INIT_STATE) || s.lookahead) {
    return Z_STREAM_ERROR;
  }

  /* when using zlib wrappers, compute Adler-32 for provided dictionary */
  if (wrap === 1) {
    /* adler32(strm->adler, dictionary, dictLength); */
    strm.adler = adler32(strm.adler, dictionary, dictLength, 0);
  }

  s.wrap = 0;   /* avoid computing Adler-32 in read_buf */

  /* if dictionary would fill window, just replace the history */
  if (dictLength >= s.w_size) {
    if (wrap === 0) {            /* already empty otherwise */
      /*** CLEAR_HASH(s); ***/
      zero(s.head); // Fill with NIL (= 0);
      s.strstart = 0;
      s.block_start = 0;
      s.insert = 0;
    }
    /* use the tail */
    // dictionary = dictionary.slice(dictLength - s.w_size);
    tmpDict = new utils.Buf8(s.w_size);
    utils.arraySet(tmpDict, dictionary, dictLength - s.w_size, s.w_size, 0);
    dictionary = tmpDict;
    dictLength = s.w_size;
  }
  /* insert dictionary into window and hash */
  avail = strm.avail_in;
  next = strm.next_in;
  input = strm.input;
  strm.avail_in = dictLength;
  strm.next_in = 0;
  strm.input = dictionary;
  fill_window(s);
  while (s.lookahead >= MIN_MATCH) {
    str = s.strstart;
    n = s.lookahead - (MIN_MATCH - 1);
    do {
      /* UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); */
      s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[str + MIN_MATCH - 1]) & s.hash_mask;

      s.prev[str & s.w_mask] = s.head[s.ins_h];

      s.head[s.ins_h] = str;
      str++;
    } while (--n);
    s.strstart = str;
    s.lookahead = MIN_MATCH - 1;
    fill_window(s);
  }
  s.strstart += s.lookahead;
  s.block_start = s.strstart;
  s.insert = s.lookahead;
  s.lookahead = 0;
  s.match_length = s.prev_length = MIN_MATCH - 1;
  s.match_available = 0;
  strm.next_in = next;
  strm.input = input;
  strm.avail_in = avail;
  s.wrap = wrap;
  return Z_OK;
}


exports.deflateInit = deflateInit;
exports.deflateInit2 = deflateInit2;
exports.deflateReset = deflateReset;
exports.deflateResetKeep = deflateResetKeep;
exports.deflateSetHeader = deflateSetHeader;
exports.deflate = deflate;
exports.deflateEnd = deflateEnd;
exports.deflateSetDictionary = deflateSetDictionary;
exports.deflateInfo = 'pako deflate (from Nodeca project)';

/* Not implemented
exports.deflateBound = deflateBound;
exports.deflateCopy = deflateCopy;
exports.deflateParams = deflateParams;
exports.deflatePending = deflatePending;
exports.deflatePrime = deflatePrime;
exports.deflateTune = deflateTune;
*/


/***/ }),
/* 29 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


// (C) 1995-2013 Jean-loup Gailly and Mark Adler
// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
//   claim that you wrote the original software. If you use this software
//   in a product, an acknowledgment in the product documentation would be
//   appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
//   misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.

var utils = __webpack_require__(4);

/* Public constants ==========================================================*/
/* ===========================================================================*/


//var Z_FILTERED          = 1;
//var Z_HUFFMAN_ONLY      = 2;
//var Z_RLE               = 3;
var Z_FIXED               = 4;
//var Z_DEFAULT_STRATEGY  = 0;

/* Possible values of the data_type field (though see inflate()) */
var Z_BINARY              = 0;
var Z_TEXT                = 1;
//var Z_ASCII             = 1; // = Z_TEXT
var Z_UNKNOWN             = 2;

/*============================================================================*/


function zero(buf) { var len = buf.length; while (--len >= 0) { buf[len] = 0; } }

// From zutil.h

var STORED_BLOCK = 0;
var STATIC_TREES = 1;
var DYN_TREES    = 2;
/* The three kinds of block type */

var MIN_MATCH    = 3;
var MAX_MATCH    = 258;
/* The minimum and maximum match lengths */

// From deflate.h
/* ===========================================================================
 * Internal compression state.
 */

var LENGTH_CODES  = 29;
/* number of length codes, not counting the special END_BLOCK code */

var LITERALS      = 256;
/* number of literal bytes 0..255 */

var L_CODES       = LITERALS + 1 + LENGTH_CODES;
/* number of Literal or Length codes, including the END_BLOCK code */

var D_CODES       = 30;
/* number of distance codes */

var BL_CODES      = 19;
/* number of codes used to transfer the bit lengths */

var HEAP_SIZE     = 2 * L_CODES + 1;
/* maximum heap size */

var MAX_BITS      = 15;
/* All codes must not exceed MAX_BITS bits */

var Buf_size      = 16;
/* size of bit buffer in bi_buf */


/* ===========================================================================
 * Constants
 */

var MAX_BL_BITS = 7;
/* Bit length codes must not exceed MAX_BL_BITS bits */

var END_BLOCK   = 256;
/* end of block literal code */

var REP_3_6     = 16;
/* repeat previous bit length 3-6 times (2 bits of repeat count) */

var REPZ_3_10   = 17;
/* repeat a zero length 3-10 times  (3 bits of repeat count) */

var REPZ_11_138 = 18;
/* repeat a zero length 11-138 times  (7 bits of repeat count) */

/* eslint-disable comma-spacing,array-bracket-spacing */
var extra_lbits =   /* extra bits for each length code */
  [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 extra_dbits =   /* extra bits for each distance code */
  [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 extra_blbits =  /* extra bits for each bit length code */
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7];

var bl_order =
  [16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15];
/* eslint-enable comma-spacing,array-bracket-spacing */

/* The lengths of the bit length codes are sent in order of decreasing
 * probability, to avoid transmitting the lengths for unused bit length codes.
 */

/* ===========================================================================
 * Local data. These are initialized only once.
 */

// We pre-fill arrays with 0 to avoid uninitialized gaps

var DIST_CODE_LEN = 512; /* see definition of array dist_code below */

// !!!! Use flat array instead of structure, Freq = i*2, Len = i*2+1
var static_ltree  = new Array((L_CODES + 2) * 2);
zero(static_ltree);
/* The static literal tree. Since the bit lengths are imposed, there is no
 * need for the L_CODES extra codes used during heap construction. However
 * The codes 286 and 287 are needed to build a canonical tree (see _tr_init
 * below).
 */

var static_dtree  = new Array(D_CODES * 2);
zero(static_dtree);
/* The static distance tree. (Actually a trivial tree since all codes use
 * 5 bits.)
 */

var _dist_code    = new Array(DIST_CODE_LEN);
zero(_dist_code);
/* Distance codes. The first 256 values correspond to the distances
 * 3 .. 258, the last 256 values correspond to the top 8 bits of
 * the 15 bit distances.
 */

var _length_code  = new Array(MAX_MATCH - MIN_MATCH + 1);
zero(_length_code);
/* length code for each normalized match length (0 == MIN_MATCH) */

var base_length   = new Array(LENGTH_CODES);
zero(base_length);
/* First normalized length for each code (0 = MIN_MATCH) */

var base_dist     = new Array(D_CODES);
zero(base_dist);
/* First normalized distance for each code (0 = distance of 1) */


function StaticTreeDesc(static_tree, extra_bits, extra_base, elems, max_length) {

  this.static_tree  = static_tree;  /* static tree or NULL */
  this.extra_bits   = extra_bits;   /* extra bits for each code or NULL */
  this.extra_base   = extra_base;   /* base index for extra_bits */
  this.elems        = elems;        /* max number of elements in the tree */
  this.max_length   = max_length;   /* max bit length for the codes */

  // show if `static_tree` has data or dummy - needed for monomorphic objects
  this.has_stree    = static_tree && static_tree.length;
}


var static_l_desc;
var static_d_desc;
var static_bl_desc;


function TreeDesc(dyn_tree, stat_desc) {
  this.dyn_tree = dyn_tree;     /* the dynamic tree */
  this.max_code = 0;            /* largest code with non zero frequency */
  this.stat_desc = stat_desc;   /* the corresponding static tree */
}



function d_code(dist) {
  return dist < 256 ? _dist_code[dist] : _dist_code[256 + (dist >>> 7)];
}


/* ===========================================================================
 * Output a short LSB first on the stream.
 * IN assertion: there is enough room in pendingBuf.
 */
function put_short(s, w) {
//    put_byte(s, (uch)((w) & 0xff));
//    put_byte(s, (uch)((ush)(w) >> 8));
  s.pending_buf[s.pending++] = (w) & 0xff;
  s.pending_buf[s.pending++] = (w >>> 8) & 0xff;
}


/* ===========================================================================
 * Send a value on a given number of bits.
 * IN assertion: length <= 16 and value fits in length bits.
 */
function send_bits(s, value, length) {
  if (s.bi_valid > (Buf_size - length)) {
    s.bi_buf |= (value << s.bi_valid) & 0xffff;
    put_short(s, s.bi_buf);
    s.bi_buf = value >> (Buf_size - s.bi_valid);
    s.bi_valid += length - Buf_size;
  } else {
    s.bi_buf |= (value << s.bi_valid) & 0xffff;
    s.bi_valid += length;
  }
}


function send_code(s, c, tree) {
  send_bits(s, tree[c * 2]/*.Code*/, tree[c * 2 + 1]/*.Len*/);
}


/* ===========================================================================
 * Reverse the first len bits of a code, using straightforward code (a faster
 * method would use a table)
 * IN assertion: 1 <= len <= 15
 */
function bi_reverse(code, len) {
  var res = 0;
  do {
    res |= code & 1;
    code >>>= 1;
    res <<= 1;
  } while (--len > 0);
  return res >>> 1;
}


/* ===========================================================================
 * Flush the bit buffer, keeping at most 7 bits in it.
 */
function bi_flush(s) {
  if (s.bi_valid === 16) {
    put_short(s, s.bi_buf);
    s.bi_buf = 0;
    s.bi_valid = 0;

  } else if (s.bi_valid >= 8) {
    s.pending_buf[s.pending++] = s.bi_buf & 0xff;
    s.bi_buf >>= 8;
    s.bi_valid -= 8;
  }
}


/* ===========================================================================
 * Compute the optimal bit lengths for a tree and update the total bit length
 * for the current block.
 * IN assertion: the fields freq and dad are set, heap[heap_max] and
 *    above are the tree nodes sorted by increasing frequency.
 * OUT assertions: the field len is set to the optimal bit length, the
 *     array bl_count contains the frequencies for each bit length.
 *     The length opt_len is updated; static_len is also updated if stree is
 *     not null.
 */
function gen_bitlen(s, desc)
//    deflate_state *s;
//    tree_desc *desc;    /* the tree descriptor */
{
  var tree            = desc.dyn_tree;
  var max_code        = desc.max_code;
  var stree           = desc.stat_desc.static_tree;
  var has_stree       = desc.stat_desc.has_stree;
  var extra           = desc.stat_desc.extra_bits;
  var base            = desc.stat_desc.extra_base;
  var max_length      = desc.stat_desc.max_length;
  var h;              /* heap index */
  var n, m;           /* iterate over the tree elements */
  var bits;           /* bit length */
  var xbits;          /* extra bits */
  var f;              /* frequency */
  var overflow = 0;   /* number of elements with bit length too large */

  for (bits = 0; bits <= MAX_BITS; bits++) {
    s.bl_count[bits] = 0;
  }

  /* In a first pass, compute the optimal bit lengths (which may
   * overflow in the case of the bit length tree).
   */
  tree[s.heap[s.heap_max] * 2 + 1]/*.Len*/ = 0; /* root of the heap */

  for (h = s.heap_max + 1; h < HEAP_SIZE; h++) {
    n = s.heap[h];
    bits = tree[tree[n * 2 + 1]/*.Dad*/ * 2 + 1]/*.Len*/ + 1;
    if (bits > max_length) {
      bits = max_length;
      overflow++;
    }
    tree[n * 2 + 1]/*.Len*/ = bits;
    /* We overwrite tree[n].Dad which is no longer needed */

    if (n > max_code) { continue; } /* not a leaf node */

    s.bl_count[bits]++;
    xbits = 0;
    if (n >= base) {
      xbits = extra[n - base];
    }
    f = tree[n * 2]/*.Freq*/;
    s.opt_len += f * (bits + xbits);
    if (has_stree) {
      s.static_len += f * (stree[n * 2 + 1]/*.Len*/ + xbits);
    }
  }
  if (overflow === 0) { return; }

  // Trace((stderr,"\nbit length overflow\n"));
  /* This happens for example on obj2 and pic of the Calgary corpus */

  /* Find the first bit length which could increase: */
  do {
    bits = max_length - 1;
    while (s.bl_count[bits] === 0) { bits--; }
    s.bl_count[bits]--;      /* move one leaf down the tree */
    s.bl_count[bits + 1] += 2; /* move one overflow item as its brother */
    s.bl_count[max_length]--;
    /* The brother of the overflow item also moves one step up,
     * but this does not affect bl_count[max_length]
     */
    overflow -= 2;
  } while (overflow > 0);

  /* 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 = s.bl_count[bits];
    while (n !== 0) {
      m = s.heap[--h];
      if (m > max_code) { continue; }
      if (tree[m * 2 + 1]/*.Len*/ !== bits) {
        // Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits));
        s.opt_len += (bits - tree[m * 2 + 1]/*.Len*/) * tree[m * 2]/*.Freq*/;
        tree[m * 2 + 1]/*.Len*/ = 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.
 */
function gen_codes(tree, max_code, bl_count)
//    ct_data *tree;             /* the tree to decorate */
//    int max_code;              /* largest code with non zero frequency */
//    ushf *bl_count;            /* number of codes at each bit length */
{
  var next_code = new Array(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 <= MAX_BITS; bits++) {
    next_code[bits] = code = (code + bl_count[bits - 1]) << 1;
  }
  /* Check that the bit counts in bl_count are consistent. The last code
   * must be all ones.
   */
  //Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1,
  //        "inconsistent bit counts");
  //Tracev((stderr,"\ngen_codes: max_code %d ", max_code));

  for (n = 0;  n <= max_code; n++) {
    var len = tree[n * 2 + 1]/*.Len*/;
    if (len === 0) { continue; }
    /* Now reverse the bits */
    tree[n * 2]/*.Code*/ = bi_reverse(next_code[len]++, len);

    //Tracecv(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ",
    //     n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len]-1));
  }
}


/* ===========================================================================
 * Initialize the various 'constant' tables.
 */
function tr_static_init() {
  var n;        /* iterates over tree elements */
  var bits;     /* bit counter */
  var length;   /* length value */
  var code;     /* code value */
  var dist;     /* distance index */
  var bl_count = new Array(MAX_BITS + 1);
  /* number of codes at each bit length for an optimal tree */

  // do check in _tr_init()
  //if (static_init_done) return;

  /* For some embedded targets, global variables are not initialized: */
/*#ifdef NO_INIT_GLOBAL_POINTERS
  static_l_desc.static_tree = static_ltree;
  static_l_desc.extra_bits = extra_lbits;
  static_d_desc.static_tree = static_dtree;
  static_d_desc.extra_bits = extra_dbits;
  static_bl_desc.extra_bits = extra_blbits;
#endif*/

  /* Initialize the mapping length (0..255) -> length code (0..28) */
  length = 0;
  for (code = 0; code < LENGTH_CODES - 1; code++) {
    base_length[code] = length;
    for (n = 0; n < (1 << extra_lbits[code]); n++) {
      _length_code[length++] = code;
    }
  }
  //Assert (length == 256, "tr_static_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:
   */
  _length_code[length - 1] = code;

  /* Initialize the mapping dist (0..32K) -> dist code (0..29) */
  dist = 0;
  for (code = 0; code < 16; code++) {
    base_dist[code] = dist;
    for (n = 0; n < (1 << extra_dbits[code]); n++) {
      _dist_code[dist++] = code;
    }
  }
  //Assert (dist == 256, "tr_static_init: dist != 256");
  dist >>= 7; /* from now on, all distances are divided by 128 */
  for (; code < D_CODES; code++) {
    base_dist[code] = dist << 7;
    for (n = 0; n < (1 << (extra_dbits[code] - 7)); n++) {
      _dist_code[256 + dist++] = code;
    }
  }
  //Assert (dist == 256, "tr_static_init: 256+dist != 512");

  /* Construct the codes of the static literal tree */
  for (bits = 0; bits <= MAX_BITS; bits++) {
    bl_count[bits] = 0;
  }

  n = 0;
  while (n <= 143) {
    static_ltree[n * 2 + 1]/*.Len*/ = 8;
    n++;
    bl_count[8]++;
  }
  while (n <= 255) {
    static_ltree[n * 2 + 1]/*.Len*/ = 9;
    n++;
    bl_count[9]++;
  }
  while (n <= 279) {
    static_ltree[n * 2 + 1]/*.Len*/ = 7;
    n++;
    bl_count[7]++;
  }
  while (n <= 287) {
    static_ltree[n * 2 + 1]/*.Len*/ = 8;
    n++;
    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)
   */
  gen_codes(static_ltree, L_CODES + 1, bl_count);

  /* The static distance tree is trivial: */
  for (n = 0; n < D_CODES; n++) {
    static_dtree[n * 2 + 1]/*.Len*/ = 5;
    static_dtree[n * 2]/*.Code*/ = bi_reverse(n, 5);
  }

  // Now data ready and we can init static trees
  static_l_desc = new StaticTreeDesc(static_ltree, extra_lbits, LITERALS + 1, L_CODES, MAX_BITS);
  static_d_desc = new StaticTreeDesc(static_dtree, extra_dbits, 0,          D_CODES, MAX_BITS);
  static_bl_desc = new StaticTreeDesc(new Array(0), extra_blbits, 0,         BL_CODES, MAX_BL_BITS);

  //static_init_done = true;
}


/* ===========================================================================
 * Initialize a new block.
 */
function init_block(s) {
  var n; /* iterates over tree elements */

  /* Initialize the trees. */
  for (n = 0; n < L_CODES;  n++) { s.dyn_ltree[n * 2]/*.Freq*/ = 0; }
  for (n = 0; n < D_CODES;  n++) { s.dyn_dtree[n * 2]/*.Freq*/ = 0; }
  for (n = 0; n < BL_CODES; n++) { s.bl_tree[n * 2]/*.Freq*/ = 0; }

  s.dyn_ltree[END_BLOCK * 2]/*.Freq*/ = 1;
  s.opt_len = s.static_len = 0;
  s.last_lit = s.matches = 0;
}


/* ===========================================================================
 * Flush the bit buffer and align the output on a byte boundary
 */
function bi_windup(s)
{
  if (s.bi_valid > 8) {
    put_short(s, s.bi_buf);
  } else if (s.bi_valid > 0) {
    //put_byte(s, (Byte)s->bi_buf);
    s.pending_buf[s.pending++] = s.bi_buf;
  }
  s.bi_buf = 0;
  s.bi_valid = 0;
}

/* ===========================================================================
 * Copy a stored block, storing first the length and its
 * one's complement if requested.
 */
function copy_block(s, buf, len, header)
//DeflateState *s;
//charf    *buf;    /* the input data */
//unsigned len;     /* its length */
//int      header;  /* true if block header must be written */
{
  bi_windup(s);        /* align on byte boundary */

  if (header) {
    put_short(s, len);
    put_short(s, ~len);
  }
//  while (len--) {
//    put_byte(s, *buf++);
//  }
  utils.arraySet(s.pending_buf, s.window, buf, len, s.pending);
  s.pending += len;
}

/* ===========================================================================
 * Compares to subtrees, using the tree depth as tie breaker when
 * the subtrees have equal frequency. This minimizes the worst case length.
 */
function smaller(tree, n, m, depth) {
  var _n2 = n * 2;
  var _m2 = m * 2;
  return (tree[_n2]/*.Freq*/ < tree[_m2]/*.Freq*/ ||
         (tree[_n2]/*.Freq*/ === tree[_m2]/*.Freq*/ && depth[n] <= depth[m]));
}

/* ===========================================================================
 * 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).
 */
function pqdownheap(s, tree, k)
//    deflate_state *s;
//    ct_data *tree;  /* the tree to restore */
//    int k;               /* node to move down */
{
  var v = s.heap[k];
  var j = k << 1;  /* left son of k */
  while (j <= s.heap_len) {
    /* Set j to the smallest of the two sons: */
    if (j < s.heap_len &&
      smaller(tree, s.heap[j + 1], s.heap[j], s.depth)) {
      j++;
    }
    /* Exit if v is smaller than both sons */
    if (smaller(tree, v, s.heap[j], s.depth)) { break; }

    /* Exchange v with the smallest son */
    s.heap[k] = s.heap[j];
    k = j;

    /* And continue down the tree, setting j to the left son of k */
    j <<= 1;
  }
  s.heap[k] = v;
}


// inlined manually
// var SMALLEST = 1;

/* ===========================================================================
 * Send the block data compressed using the given Huffman trees
 */
function compress_block(s, ltree, dtree)
//    deflate_state *s;
//    const ct_data *ltree; /* literal tree */
//    const ct_data *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 code;           /* the code to send */
  var extra;          /* number of extra bits to send */

  if (s.last_lit !== 0) {
    do {
      dist = (s.pending_buf[s.d_buf + lx * 2] << 8) | (s.pending_buf[s.d_buf + lx * 2 + 1]);
      lc = s.pending_buf[s.l_buf + lx];
      lx++;

      if (dist === 0) {
        send_code(s, lc, ltree); /* send a literal byte */
        //Tracecv(isgraph(lc), (stderr," '%c' ", lc));
      } else {
        /* Here, lc is the match length - MIN_MATCH */
        code = _length_code[lc];
        send_code(s, code + LITERALS + 1, ltree); /* send the length code */
        extra = extra_lbits[code];
        if (extra !== 0) {
          lc -= base_length[code];
          send_bits(s, lc, extra);       /* send the extra length bits */
        }
        dist--; /* dist is now the match distance - 1 */
        code = d_code(dist);
        //Assert (code < D_CODES, "bad d_code");

        send_code(s, code, dtree);       /* send the distance code */
        extra = extra_dbits[code];
        if (extra !== 0) {
          dist -= base_dist[code];
          send_bits(s, dist, extra);   /* send the extra distance bits */
        }
      } /* literal or match pair ? */

      /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */
      //Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx,
      //       "pendingBuf overflow");

    } while (lx < s.last_lit);
  }

  send_code(s, END_BLOCK, ltree);
}


/* ===========================================================================
 * 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.
 */
function build_tree(s, desc)
//    deflate_state *s;
//    tree_desc *desc; /* the tree descriptor */
{
  var tree     = desc.dyn_tree;
  var stree    = desc.stat_desc.static_tree;
  var has_stree = desc.stat_desc.has_stree;
  var elems    = desc.stat_desc.elems;
  var n, m;          /* iterate over heap elements */
  var max_code = -1; /* largest code with non zero frequency */
  var node;          /* new node being created */

  /* Construct the initial heap, with least frequent element in
   * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1].
   * heap[0] is not used.
   */
  s.heap_len = 0;
  s.heap_max = HEAP_SIZE;

  for (n = 0; n < elems; n++) {
    if (tree[n * 2]/*.Freq*/ !== 0) {
      s.heap[++s.heap_len] = max_code = n;
      s.depth[n] = 0;

    } else {
      tree[n * 2 + 1]/*.Len*/ = 0;
    }
  }

  /* The pkzip format requires that at least one distance code exists,
   * and that at least one bit should be sent even if there is only one
   * possible code. So to avoid special checks later on we force at least
   * two codes of non zero frequency.
   */
  while (s.heap_len < 2) {
    node = s.heap[++s.heap_len] = (max_code < 2 ? ++max_code : 0);
    tree[node * 2]/*.Freq*/ = 1;
    s.depth[node] = 0;
    s.opt_len--;

    if (has_stree) {
      s.static_len -= stree[node * 2 + 1]/*.Len*/;
    }
    /* node 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 = (s.heap_len >> 1/*int /2*/); n >= 1; n--) { pqdownheap(s, tree, n); }

  /* Construct the Huffman tree by repeatedly combining the least two
   * frequent nodes.
   */
  node = elems;              /* next internal node of the tree */
  do {
    //pqremove(s, tree, n);  /* n = node of least frequency */
    /*** pqremove ***/
    n = s.heap[1/*SMALLEST*/];
    s.heap[1/*SMALLEST*/] = s.heap[s.heap_len--];
    pqdownheap(s, tree, 1/*SMALLEST*/);
    /***/

    m = s.heap[1/*SMALLEST*/]; /* m = node of next least frequency */

    s.heap[--s.heap_max] = n; /* keep the nodes sorted by frequency */
    s.heap[--s.heap_max] = m;

    /* Create a new node father of n and m */
    tree[node * 2]/*.Freq*/ = tree[n * 2]/*.Freq*/ + tree[m * 2]/*.Freq*/;
    s.depth[node] = (s.depth[n] >= s.depth[m] ? s.depth[n] : s.depth[m]) + 1;
    tree[n * 2 + 1]/*.Dad*/ = tree[m * 2 + 1]/*.Dad*/ = node;

    /* and insert the new node in the heap */
    s.heap[1/*SMALLEST*/] = node++;
    pqdownheap(s, tree, 1/*SMALLEST*/);

  } while (s.heap_len >= 2);

  s.heap[--s.heap_max] = s.heap[1/*SMALLEST*/];

  /* At this point, the fields freq and dad are set. We can now
   * generate the bit lengths.
   */
  gen_bitlen(s, desc);

  /* The field len is now set, we can generate the bit codes */
  gen_codes(tree, max_code, s.bl_count);
}


/* ===========================================================================
 * Scan a literal or distance tree to determine the frequencies of the codes
 * in the bit length tree.
 */
function scan_tree(s, tree, max_code)
//    deflate_state *s;
//    ct_data *tree;   /* the tree to be scanned */
//    int max_code;    /* and its largest code of non zero frequency */
{
  var n;                     /* iterates over all tree elements */
  var prevlen = -1;          /* last emitted length */
  var curlen;                /* length of current code */

  var nextlen = tree[0 * 2 + 1]/*.Len*/; /* length of next code */

  var count = 0;             /* repeat count of the current code */
  var max_count = 7;         /* max repeat count */
  var min_count = 4;         /* min repeat count */

  if (nextlen === 0) {
    max_count = 138;
    min_count = 3;
  }
  tree[(max_code + 1) * 2 + 1]/*.Len*/ = 0xffff; /* guard */

  for (n = 0; n <= max_code; n++) {
    curlen = nextlen;
    nextlen = tree[(n + 1) * 2 + 1]/*.Len*/;

    if (++count < max_count && curlen === nextlen) {
      continue;

    } else if (count < min_count) {
      s.bl_tree[curlen * 2]/*.Freq*/ += count;

    } else if (curlen !== 0) {

      if (curlen !== prevlen) { s.bl_tree[curlen * 2]/*.Freq*/++; }
      s.bl_tree[REP_3_6 * 2]/*.Freq*/++;

    } else if (count <= 10) {
      s.bl_tree[REPZ_3_10 * 2]/*.Freq*/++;

    } else {
      s.bl_tree[REPZ_11_138 * 2]/*.Freq*/++;
    }

    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.
 */
function send_tree(s, tree, max_code)
//    deflate_state *s;
//    ct_data *tree; /* the tree to be scanned */
//    int max_code;       /* and its largest code of non zero frequency */
{
  var n;                     /* iterates over all tree elements */
  var prevlen = -1;          /* last emitted length */
  var curlen;                /* length of current code */

  var nextlen = tree[0 * 2 + 1]/*.Len*/; /* 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].Len = -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) * 2 + 1]/*.Len*/;

    if (++count < max_count && curlen === nextlen) {
      continue;

    } else if (count < min_count) {
      do { send_code(s, curlen, s.bl_tree); } while (--count !== 0);

    } else if (curlen !== 0) {
      if (curlen !== prevlen) {
        send_code(s, curlen, s.bl_tree);
        count--;
      }
      //Assert(count >= 3 && count <= 6, " 3_6?");
      send_code(s, REP_3_6, s.bl_tree);
      send_bits(s, count - 3, 2);

    } else if (count <= 10) {
      send_code(s, REPZ_3_10, s.bl_tree);
      send_bits(s, count - 3, 3);

    } else {
      send_code(s, REPZ_11_138, s.bl_tree);
      send_bits(s, count - 11, 7);
    }

    count = 0;
    prevlen = curlen;
    if (nextlen === 0) {
      max_count = 138;
      min_count = 3;

    } else if (curlen === nextlen) {
      max_count = 6;
      min_count = 3;

    } else {
      max_count = 7;
      min_count = 4;
    }
  }
}


/* ===========================================================================
 * Construct the Huffman tree for the bit lengths and return the index in
 * bl_order of the last bit length code to send.
 */
function build_bl_tree(s) {
  var max_blindex;  /* index of last bit length code of non zero freq */

  /* Determine the bit length frequencies for literal and distance trees */
  scan_tree(s, s.dyn_ltree, s.l_desc.max_code);
  scan_tree(s, s.dyn_dtree, s.d_desc.max_code);

  /* Build the bit length tree: */
  build_tree(s, s.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 = BL_CODES - 1; max_blindex >= 3; max_blindex--) {
    if (s.bl_tree[bl_order[max_blindex] * 2 + 1]/*.Len*/ !== 0) {
      break;
    }
  }
  /* Update opt_len to include the bit length tree and counts */
  s.opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4;
  //Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld",
  //        s->opt_len, s->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.
 */
function send_all_trees(s, lcodes, dcodes, blcodes)
//    deflate_state *s;
//    int 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: "));
  send_bits(s, lcodes - 257, 5); /* not +255 as stated in appnote.txt */
  send_bits(s, dcodes - 1,   5);
  send_bits(s, blcodes - 4,  4); /* not -3 as stated in appnote.txt */
  for (rank = 0; rank < blcodes; rank++) {
    //Tracev((stderr, "\nbl code %2d ", bl_order[rank]));
    send_bits(s, s.bl_tree[bl_order[rank] * 2 + 1]/*.Len*/, 3);
  }
  //Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent));

  send_tree(s, s.dyn_ltree, lcodes - 1); /* literal tree */
  //Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent));

  send_tree(s, s.dyn_dtree, dcodes - 1); /* distance tree */
  //Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent));
}


/* ===========================================================================
 * Check if the data type is TEXT or BINARY, using the following algorithm:
 * - TEXT if the two conditions below are satisfied:
 *    a) There are no non-portable control characters belonging to the
 *       "black list" (0..6, 14..25, 28..31).
 *    b) There is at least one printable character belonging to the
 *       "white list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255).
 * - BINARY otherwise.
 * - The following partially-portable control characters form a
 *   "gray list" that is ignored in this detection algorithm:
 *   (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}).
 * IN assertion: the fields Freq of dyn_ltree are set.
 */
function detect_data_type(s) {
  /* black_mask is the bit mask of black-listed bytes
   * set bits 0..6, 14..25, and 28..31
   * 0xf3ffc07f = binary 11110011111111111100000001111111
   */
  var black_mask = 0xf3ffc07f;
  var n;

  /* Check for non-textual ("black-listed") bytes. */
  for (n = 0; n <= 31; n++, black_mask >>>= 1) {
    if ((black_mask & 1) && (s.dyn_ltree[n * 2]/*.Freq*/ !== 0)) {
      return Z_BINARY;
    }
  }

  /* Check for textual ("white-listed") bytes. */
  if (s.dyn_ltree[9 * 2]/*.Freq*/ !== 0 || s.dyn_ltree[10 * 2]/*.Freq*/ !== 0 ||
      s.dyn_ltree[13 * 2]/*.Freq*/ !== 0) {
    return Z_TEXT;
  }
  for (n = 32; n < LITERALS; n++) {
    if (s.dyn_ltree[n * 2]/*.Freq*/ !== 0) {
      return Z_TEXT;
    }
  }

  /* There are no "black-listed" or "white-listed" bytes:
   * this stream either is empty or has tolerated ("gray-listed") bytes only.
   */
  return Z_BINARY;
}


var static_init_done = false;

/* ===========================================================================
 * Initialize the tree data structures for a new zlib stream.
 */
function _tr_init(s)
{

  if (!static_init_done) {
    tr_static_init();
    static_init_done = true;
  }

  s.l_desc  = new TreeDesc(s.dyn_ltree, static_l_desc);
  s.d_desc  = new TreeDesc(s.dyn_dtree, static_d_desc);
  s.bl_desc = new TreeDesc(s.bl_tree, static_bl_desc);

  s.bi_buf = 0;
  s.bi_valid = 0;

  /* Initialize the first block of the first file: */
  init_block(s);
}


/* ===========================================================================
 * Send a stored block
 */
function _tr_stored_block(s, buf, stored_len, last)
//DeflateState *s;
//charf *buf;       /* input block */
//ulg stored_len;   /* length of input block */
//int last;         /* one if this is the last block for a file */
{
  send_bits(s, (STORED_BLOCK << 1) + (last ? 1 : 0), 3);    /* send block type */
  copy_block(s, buf, stored_len, true); /* with header */
}


/* ===========================================================================
 * Send one empty static block to give enough lookahead for inflate.
 * This takes 10 bits, of which 7 may remain in the bit buffer.
 */
function _tr_align(s) {
  send_bits(s, STATIC_TREES << 1, 3);
  send_code(s, END_BLOCK, static_ltree);
  bi_flush(s);
}


/* ===========================================================================
 * Determine the best encoding for the current block: dynamic trees, static
 * trees or store, and output the encoded block to the zip file.
 */
function _tr_flush_block(s, buf, stored_len, last)
//DeflateState *s;
//charf *buf;       /* input block, or NULL if too old */
//ulg stored_len;   /* length of input block */
//int last;         /* one if this is the last block for a file */
{
  var opt_lenb, static_lenb;  /* opt_len and static_len in bytes */
  var max_blindex = 0;        /* index of last bit length code of non zero freq */

  /* Build the Huffman trees unless a stored block is forced */
  if (s.level > 0) {

    /* Check if the file is binary or text */
    if (s.strm.data_type === Z_UNKNOWN) {
      s.strm.data_type = detect_data_type(s);
    }

    /* Construct the literal and distance trees */
    build_tree(s, s.l_desc);
    // Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len,
    //        s->static_len));

    build_tree(s, s.d_desc);
    // Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len,
    //        s->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 = build_bl_tree(s);

    /* Determine the best encoding. Compute the block lengths in bytes. */
    opt_lenb = (s.opt_len + 3 + 7) >>> 3;
    static_lenb = (s.static_len + 3 + 7) >>> 3;

    // Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ",
    //        opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len,
    //        s->last_lit));

    if (static_lenb <= opt_lenb) { opt_lenb = static_lenb; }

  } else {
    // Assert(buf != (char*)0, "lost buf");
    opt_lenb = static_lenb = stored_len + 5; /* force a stored block */
  }

  if ((stored_len + 4 <= opt_lenb) && (buf !== -1)) {
    /* 4: two words for the lengths */

    /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE.
     * Otherwise we can't have processed more than WSIZE input bytes since
     * the last block flush, because compression would have been
     * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to
     * transform a block into a stored block.
     */
    _tr_stored_block(s, buf, stored_len, last);

  } else if (s.strategy === Z_FIXED || static_lenb === opt_lenb) {

    send_bits(s, (STATIC_TREES << 1) + (last ? 1 : 0), 3);
    compress_block(s, static_ltree, static_dtree);

  } else {
    send_bits(s, (DYN_TREES << 1) + (last ? 1 : 0), 3);
    send_all_trees(s, s.l_desc.max_code + 1, s.d_desc.max_code + 1, max_blindex + 1);
    compress_block(s, s.dyn_ltree, s.dyn_dtree);
  }
  // Assert (s->compressed_len == s->bits_sent, "bad compressed size");
  /* The above check is made mod 2^32, for files larger than 512 MB
   * and uLong implemented on 32 bits.
   */
  init_block(s);

  if (last) {
    bi_windup(s);
  }
  // Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3,
  //       s->compressed_len-7*last));
}

/* ===========================================================================
 * Save the match info and tally the frequency counts. Return true if
 * the current block must be flushed.
 */
function _tr_tally(s, dist, lc)
//    deflate_state *s;
//    unsigned dist;  /* distance of matched string */
//    unsigned lc;    /* match length-MIN_MATCH or unmatched char (if dist==0) */
{
  //var out_length, in_length, dcode;

  s.pending_buf[s.d_buf + s.last_lit * 2]     = (dist >>> 8) & 0xff;
  s.pending_buf[s.d_buf + s.last_lit * 2 + 1] = dist & 0xff;

  s.pending_buf[s.l_buf + s.last_lit] = lc & 0xff;
  s.last_lit++;

  if (dist === 0) {
    /* lc is the unmatched char */
    s.dyn_ltree[lc * 2]/*.Freq*/++;
  } else {
    s.matches++;
    /* Here, lc is the match length - MIN_MATCH */
    dist--;             /* dist = match distance - 1 */
    //Assert((ush)dist < (ush)MAX_DIST(s) &&
    //       (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) &&
    //       (ush)d_code(dist) < (ush)D_CODES,  "_tr_tally: bad match");

    s.dyn_ltree[(_length_code[lc] + LITERALS + 1) * 2]/*.Freq*/++;
    s.dyn_dtree[d_code(dist) * 2]/*.Freq*/++;
  }

// (!) This block is disabled in zlib defaults,
// don't enable it for binary compatibility

//#ifdef TRUNCATE_BLOCK
//  /* Try to guess if it is profitable to stop the current block here */
//  if ((s.last_lit & 0x1fff) === 0 && s.level > 2) {
//    /* Compute an upper bound for the compressed length */
//    out_length = s.last_lit*8;
//    in_length = s.strstart - s.block_start;
//
//    for (dcode = 0; dcode < D_CODES; dcode++) {
//      out_length += s.dyn_dtree[dcode*2]/*.Freq*/ * (5 + extra_dbits[dcode]);
//    }
//    out_length >>>= 3;
//    //Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ",
//    //       s->last_lit, in_length, out_length,
//    //       100L - out_length*100L/in_length));
//    if (s.matches < (s.last_lit>>1)/*int /2*/ && out_length < (in_length>>1)/*int /2*/) {
//      return true;
//    }
//  }
//#endif

  return (s.last_lit === s.lit_bufsize - 1);
  /* We avoid equality with lit_bufsize because of wraparound at 64K
   * on 16 bit machines and because stored blocks are restricted to
   * 64K-1 bytes.
   */
}

exports._tr_init  = _tr_init;
exports._tr_stored_block = _tr_stored_block;
exports._tr_flush_block  = _tr_flush_block;
exports._tr_tally = _tr_tally;
exports._tr_align = _tr_align;


/***/ }),
/* 30 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";



var zlib_inflate = __webpack_require__(31);
var utils        = __webpack_require__(4);
var strings      = __webpack_require__(12);
var c            = __webpack_require__(14);
var msg          = __webpack_require__(6);
var ZStream      = __webpack_require__(13);
var GZheader     = __webpack_require__(34);

var toString = Object.prototype.toString;

/**
 * class Inflate
 *
 * Generic JS-style wrapper for zlib calls. If you don't need
 * streaming behaviour - use more simple functions: [[inflate]]
 * and [[inflateRaw]].
 **/

/* internal
 * inflate.chunks -> Array
 *
 * Chunks of output data, if [[Inflate#onData]] not overridden.
 **/

/**
 * Inflate.result -> Uint8Array|Array|String
 *
 * Uncompressed result, generated by default [[Inflate#onData]]
 * and [[Inflate#onEnd]] handlers. Filled after you push last chunk
 * (call [[Inflate#push]] with `Z_FINISH` / `true` param) or if you
 * push a chunk with explicit flush (call [[Inflate#push]] with
 * `Z_SYNC_FLUSH` param).
 **/

/**
 * Inflate.err -> Number
 *
 * Error code after inflate finished. 0 (Z_OK) on success.
 * Should be checked if broken data possible.
 **/

/**
 * Inflate.msg -> String
 *
 * Error message, if [[Inflate.err]] != 0
 **/


/**
 * new Inflate(options)
 * - options (Object): zlib inflate options.
 *
 * Creates new inflator instance with specified params. Throws exception
 * on bad params. Supported options:
 *
 * - `windowBits`
 * - `dictionary`
 *
 * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
 * for more information on these.
 *
 * Additional options, for internal needs:
 *
 * - `chunkSize` - size of generated data chunks (16K by default)
 * - `raw` (Boolean) - do raw inflate
 * - `to` (String) - if equal to 'string', then result will be converted
 *   from utf8 to utf16 (javascript) string. When string output requested,
 *   chunk length can differ from `chunkSize`, depending on content.
 *
 * By default, when no options set, autodetect deflate/gzip data format via
 * wrapper header.
 *
 * ##### Example:
 *
 * ```javascript
 * var pako = require('pako')
 *   , chunk1 = Uint8Array([1,2,3,4,5,6,7,8,9])
 *   , chunk2 = Uint8Array([10,11,12,13,14,15,16,17,18,19]);
 *
 * var inflate = new pako.Inflate({ level: 3});
 *
 * inflate.push(chunk1, false);
 * inflate.push(chunk2, true);  // true -> last chunk
 *
 * if (inflate.err) { throw new Error(inflate.err); }
 *
 * console.log(inflate.result);
 * ```
 **/
function Inflate(options) {
  if (!(this instanceof Inflate)) return new Inflate(options);

  this.options = utils.assign({
    chunkSize: 16384,
    windowBits: 0,
    to: ''
  }, options || {});

  var opt = this.options;

  // Force window size for `raw` data, if not set directly,
  // because we have no header for autodetect.
  if (opt.raw && (opt.windowBits >= 0) && (opt.windowBits < 16)) {
    opt.windowBits = -opt.windowBits;
    if (opt.windowBits === 0) { opt.windowBits = -15; }
  }

  // If `windowBits` not defined (and mode not raw) - set autodetect flag for gzip/deflate
  if ((opt.windowBits >= 0) && (opt.windowBits < 16) &&
      !(options && options.windowBits)) {
    opt.windowBits += 32;
  }

  // Gzip header has no info about windows size, we can do autodetect only
  // for deflate. So, if window size not set, force it to max when gzip possible
  if ((opt.windowBits > 15) && (opt.windowBits < 48)) {
    // bit 3 (16) -> gzipped data
    // bit 4 (32) -> autodetect gzip/deflate
    if ((opt.windowBits & 15) === 0) {
      opt.windowBits |= 15;
    }
  }

  this.err    = 0;      // error code, if happens (0 = Z_OK)
  this.msg    = '';     // error message
  this.ended  = false;  // used to avoid multiple onEnd() calls
  this.chunks = [];     // chunks of compressed data

  this.strm   = new ZStream();
  this.strm.avail_out = 0;

  var status  = zlib_inflate.inflateInit2(
    this.strm,
    opt.windowBits
  );

  if (status !== c.Z_OK) {
    throw new Error(msg[status]);
  }

  this.header = new GZheader();

  zlib_inflate.inflateGetHeader(this.strm, this.header);
}

/**
 * Inflate#push(data[, mode]) -> Boolean
 * - data (Uint8Array|Array|ArrayBuffer|String): input data
 * - mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE modes.
 *   See constants. Skipped or `false` means Z_NO_FLUSH, `true` means Z_FINISH.
 *
 * Sends input data to inflate pipe, generating [[Inflate#onData]] calls with
 * new output chunks. Returns `true` on success. The last data block must have
 * mode Z_FINISH (or `true`). That will flush internal pending buffers and call
 * [[Inflate#onEnd]]. For interim explicit flushes (without ending the stream) you
 * can use mode Z_SYNC_FLUSH, keeping the decompression context.
 *
 * On fail call [[Inflate#onEnd]] with error code and return false.
 *
 * We strongly recommend to use `Uint8Array` on input for best speed (output
 * format is detected automatically). Also, don't skip last param and always
 * use the same type in your code (boolean or number). That will improve JS speed.
 *
 * For regular `Array`-s make sure all elements are [0..255].
 *
 * ##### Example
 *
 * ```javascript
 * push(chunk, false); // push one of data chunks
 * ...
 * push(chunk, true);  // push last chunk
 * ```
 **/
Inflate.prototype.push = function (data, mode) {
  var strm = this.strm;
  var chunkSize = this.options.chunkSize;
  var dictionary = this.options.dictionary;
  var status, _mode;
  var next_out_utf8, tail, utf8str;
  var dict;

  // Flag to properly process Z_BUF_ERROR on testing inflate call
  // when we check that all output data was flushed.
  var allowBufError = false;

  if (this.ended) { return false; }
  _mode = (mode === ~~mode) ? mode : ((mode === true) ? c.Z_FINISH : c.Z_NO_FLUSH);

  // Convert data if needed
  if (typeof data === 'string') {
    // Only binary strings can be decompressed on practice
    strm.input = strings.binstring2buf(data);
  } else if (toString.call(data) === '[object ArrayBuffer]') {
    strm.input = new Uint8Array(data);
  } else {
    strm.input = data;
  }

  strm.next_in = 0;
  strm.avail_in = strm.input.length;

  do {
    if (strm.avail_out === 0) {
      strm.output = new utils.Buf8(chunkSize);
      strm.next_out = 0;
      strm.avail_out = chunkSize;
    }

    status = zlib_inflate.inflate(strm, c.Z_NO_FLUSH);    /* no bad return value */

    if (status === c.Z_NEED_DICT && dictionary) {
      // Convert data if needed
      if (typeof dictionary === 'string') {
        dict = strings.string2buf(dictionary);
      } else if (toString.call(dictionary) === '[object ArrayBuffer]') {
        dict = new Uint8Array(dictionary);
      } else {
        dict = dictionary;
      }

      status = zlib_inflate.inflateSetDictionary(this.strm, dict);

    }

    if (status === c.Z_BUF_ERROR && allowBufError === true) {
      status = c.Z_OK;
      allowBufError = false;
    }

    if (status !== c.Z_STREAM_END && status !== c.Z_OK) {
      this.onEnd(status);
      this.ended = true;
      return false;
    }

    if (strm.next_out) {
      if (strm.avail_out === 0 || status === c.Z_STREAM_END || (strm.avail_in === 0 && (_mode === c.Z_FINISH || _mode === c.Z_SYNC_FLUSH))) {

        if (this.options.to === 'string') {

          next_out_utf8 = strings.utf8border(strm.output, strm.next_out);

          tail = strm.next_out - next_out_utf8;
          utf8str = strings.buf2string(strm.output, next_out_utf8);

          // move tail
          strm.next_out = tail;
          strm.avail_out = chunkSize - tail;
          if (tail) { utils.arraySet(strm.output, strm.output, next_out_utf8, tail, 0); }

          this.onData(utf8str);

        } else {
          this.onData(utils.shrinkBuf(strm.output, strm.next_out));
        }
      }
    }

    // When no more input data, we should check that internal inflate buffers
    // are flushed. The only way to do it when avail_out = 0 - run one more
    // inflate pass. But if output data not exists, inflate return Z_BUF_ERROR.
    // Here we set flag to process this error properly.
    //
    // NOTE. Deflate does not return error in this case and does not needs such
    // logic.
    if (strm.avail_in === 0 && strm.avail_out === 0) {
      allowBufError = true;
    }

  } while ((strm.avail_in > 0 || strm.avail_out === 0) && status !== c.Z_STREAM_END);

  if (status === c.Z_STREAM_END) {
    _mode = c.Z_FINISH;
  }

  // Finalize on the last chunk.
  if (_mode === c.Z_FINISH) {
    status = zlib_inflate.inflateEnd(this.strm);
    this.onEnd(status);
    this.ended = true;
    return status === c.Z_OK;
  }

  // callback interim results if Z_SYNC_FLUSH.
  if (_mode === c.Z_SYNC_FLUSH) {
    this.onEnd(c.Z_OK);
    strm.avail_out = 0;
    return true;
  }

  return true;
};


/**
 * Inflate#onData(chunk) -> Void
 * - chunk (Uint8Array|Array|String): output data. Type of array depends
 *   on js engine support. When string output requested, each chunk
 *   will be string.
 *
 * By default, stores data blocks in `chunks[]` property and glue
 * those in `onEnd`. Override this handler, if you need another behaviour.
 **/
Inflate.prototype.onData = function (chunk) {
  this.chunks.push(chunk);
};


/**
 * Inflate#onEnd(status) -> Void
 * - status (Number): inflate status. 0 (Z_OK) on success,
 *   other if not.
 *
 * Called either after you tell inflate that the input stream is
 * complete (Z_FINISH) or should be flushed (Z_SYNC_FLUSH)
 * or if an error happened. By default - join collected chunks,
 * free memory and fill `results` / `err` properties.
 **/
Inflate.prototype.onEnd = function (status) {
  // On success - join
  if (status === c.Z_OK) {
    if (this.options.to === 'string') {
      // Glue & convert here, until we teach pako to send
      // utf8 aligned strings to onData
      this.result = this.chunks.join('');
    } else {
      this.result = utils.flattenChunks(this.chunks);
    }
  }
  this.chunks = [];
  this.err = status;
  this.msg = this.strm.msg;
};


/**
 * inflate(data[, options]) -> Uint8Array|Array|String
 * - data (Uint8Array|Array|String): input data to decompress.
 * - options (Object): zlib inflate options.
 *
 * Decompress `data` with inflate/ungzip and `options`. Autodetect
 * format via wrapper header by default. That's why we don't provide
 * separate `ungzip` method.
 *
 * Supported options are:
 *
 * - windowBits
 *
 * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
 * for more information.
 *
 * Sugar (options):
 *
 * - `raw` (Boolean) - say that we work with raw stream, if you don't wish to specify
 *   negative windowBits implicitly.
 * - `to` (String) - if equal to 'string', then result will be converted
 *   from utf8 to utf16 (javascript) string. When string output requested,
 *   chunk length can differ from `chunkSize`, depending on content.
 *
 *
 * ##### Example:
 *
 * ```javascript
 * var pako = require('pako')
 *   , input = pako.deflate([1,2,3,4,5,6,7,8,9])
 *   , output;
 *
 * try {
 *   output = pako.inflate(input);
 * } catch (err)
 *   console.log(err);
 * }
 * ```
 **/
function inflate(input, options) {
  var inflator = new Inflate(options);

  inflator.push(input, true);

  // That will never happens, if you don't cheat with options :)
  if (inflator.err) { throw inflator.msg || msg[inflator.err]; }

  return inflator.result;
}


/**
 * inflateRaw(data[, options]) -> Uint8Array|Array|String
 * - data (Uint8Array|Array|String): input data to decompress.
 * - options (Object): zlib inflate options.
 *
 * The same as [[inflate]], but creates raw data, without wrapper
 * (header and adler32 crc).
 **/
function inflateRaw(input, options) {
  options = options || {};
  options.raw = true;
  return inflate(input, options);
}


/**
 * ungzip(data[, options]) -> Uint8Array|Array|String
 * - data (Uint8Array|Array|String): input data to decompress.
 * - options (Object): zlib inflate options.
 *
 * Just shortcut to [[inflate]], because it autodetects format
 * by header.content. Done for convenience.
 **/


exports.Inflate = Inflate;
exports.inflate = inflate;
exports.inflateRaw = inflateRaw;
exports.ungzip  = inflate;


/***/ }),
/* 31 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


// (C) 1995-2013 Jean-loup Gailly and Mark Adler
// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
//   claim that you wrote the original software. If you use this software
//   in a product, an acknowledgment in the product documentation would be
//   appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
//   misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.

var utils         = __webpack_require__(4);
var adler32       = __webpack_require__(10);
var crc32         = __webpack_require__(11);
var inflate_fast  = __webpack_require__(32);
var inflate_table = __webpack_require__(33);

var CODES = 0;
var LENS = 1;
var DISTS = 2;

/* Public constants ==========================================================*/
/* ===========================================================================*/


/* Allowed flush values; see deflate() and inflate() below for details */
//var Z_NO_FLUSH      = 0;
//var Z_PARTIAL_FLUSH = 1;
//var Z_SYNC_FLUSH    = 2;
//var Z_FULL_FLUSH    = 3;
var Z_FINISH        = 4;
var Z_BLOCK         = 5;
var Z_TREES         = 6;


/* Return codes for the compression/decompression functions. Negative values
 * are errors, positive values are used for special but normal events.
 */
var Z_OK            = 0;
var Z_STREAM_END    = 1;
var Z_NEED_DICT     = 2;
//var Z_ERRNO         = -1;
var Z_STREAM_ERROR  = -2;
var Z_DATA_ERROR    = -3;
var Z_MEM_ERROR     = -4;
var Z_BUF_ERROR     = -5;
//var Z_VERSION_ERROR = -6;

/* The deflate compression method */
var Z_DEFLATED  = 8;


/* STATES ====================================================================*/
/* ===========================================================================*/


var    HEAD = 1;       /* i: waiting for magic header */
var    FLAGS = 2;      /* i: waiting for method and flags (gzip) */
var    TIME = 3;       /* i: waiting for modification time (gzip) */
var    OS = 4;         /* i: waiting for extra flags and operating system (gzip) */
var    EXLEN = 5;      /* i: waiting for extra length (gzip) */
var    EXTRA = 6;      /* i: waiting for extra bytes (gzip) */
var    NAME = 7;       /* i: waiting for end of file name (gzip) */
var    COMMENT = 8;    /* i: waiting for end of comment (gzip) */
var    HCRC = 9;       /* i: waiting for header crc (gzip) */
var    DICTID = 10;    /* i: waiting for dictionary check value */
var    DICT = 11;      /* waiting for inflateSetDictionary() call */
var        TYPE = 12;      /* i: waiting for type bits, including last-flag bit */
var        TYPEDO = 13;    /* i: same, but skip check to exit inflate on new block */
var        STORED = 14;    /* i: waiting for stored size (length and complement) */
var        COPY_ = 15;     /* i/o: same as COPY below, but only first time in */
var        COPY = 16;      /* i/o: waiting for input or output to copy stored block */
var        TABLE = 17;     /* i: waiting for dynamic block table lengths */
var        LENLENS = 18;   /* i: waiting for code length code lengths */
var        CODELENS = 19;  /* i: waiting for length/lit and distance code lengths */
var            LEN_ = 20;      /* i: same as LEN below, but only first time in */
var            LEN = 21;       /* i: waiting for length/lit/eob code */
var            LENEXT = 22;    /* i: waiting for length extra bits */
var            DIST = 23;      /* i: waiting for distance code */
var            DISTEXT = 24;   /* i: waiting for distance extra bits */
var            MATCH = 25;     /* o: waiting for output space to copy string */
var            LIT = 26;       /* o: waiting for output space to write literal */
var    CHECK = 27;     /* i: waiting for 32-bit check value */
var    LENGTH = 28;    /* i: waiting for 32-bit length (gzip) */
var    DONE = 29;      /* finished check, done -- remain here until reset */
var    BAD = 30;       /* got a data error -- remain here until reset */
var    MEM = 31;       /* got an inflate() memory error -- remain here until reset */
var    SYNC = 32;      /* looking for synchronization bytes to restart inflate() */

/* ===========================================================================*/



var ENOUGH_LENS = 852;
var ENOUGH_DISTS = 592;
//var ENOUGH =  (ENOUGH_LENS+ENOUGH_DISTS);

var MAX_WBITS = 15;
/* 32K LZ77 window */
var DEF_WBITS = MAX_WBITS;


function zswap32(q) {
  return  (((q >>> 24) & 0xff) +
          ((q >>> 8) & 0xff00) +
          ((q & 0xff00) << 8) +
          ((q & 0xff) << 24));
}


function InflateState() {
  this.mode = 0;             /* current inflate mode */
  this.last = false;          /* true if processing last block */
  this.wrap = 0;              /* bit 0 true for zlib, bit 1 true for gzip */
  this.havedict = false;      /* true if dictionary provided */
  this.flags = 0;             /* gzip header method and flags (0 if zlib) */
  this.dmax = 0;              /* zlib header max distance (INFLATE_STRICT) */
  this.check = 0;             /* protected copy of check value */
  this.total = 0;             /* protected copy of output count */
  // TODO: may be {}
  this.head = null;           /* where to save gzip header information */

  /* sliding window */
  this.wbits = 0;             /* log base 2 of requested window size */
  this.wsize = 0;             /* window size or zero if not using window */
  this.whave = 0;             /* valid bytes in the window */
  this.wnext = 0;             /* window write index */
  this.window = null;         /* allocated sliding window, if needed */

  /* bit accumulator */
  this.hold = 0;              /* input bit accumulator */
  this.bits = 0;              /* number of bits in "in" */

  /* for string and stored block copying */
  this.length = 0;            /* literal or length of data to copy */
  this.offset = 0;            /* distance back to copy string from */

  /* for table and code decoding */
  this.extra = 0;             /* extra bits needed */

  /* fixed and dynamic code tables */
  this.lencode = null;          /* starting table for length/literal codes */
  this.distcode = null;         /* starting table for distance codes */
  this.lenbits = 0;           /* index bits for lencode */
  this.distbits = 0;          /* index bits for distcode */

  /* dynamic table building */
  this.ncode = 0;             /* number of code length code lengths */
  this.nlen = 0;              /* number of length code lengths */
  this.ndist = 0;             /* number of distance code lengths */
  this.have = 0;              /* number of code lengths in lens[] */
  this.next = null;              /* next available space in codes[] */

  this.lens = new utils.Buf16(320); /* temporary storage for code lengths */
  this.work = new utils.Buf16(288); /* work area for code table building */

  /*
   because we don't have pointers in js, we use lencode and distcode directly
   as buffers so we don't need codes
  */
  //this.codes = new utils.Buf32(ENOUGH);       /* space for code tables */
  this.lendyn = null;              /* dynamic table for length/literal codes (JS specific) */
  this.distdyn = null;             /* dynamic table for distance codes (JS specific) */
  this.sane = 0;                   /* if false, allow invalid distance too far */
  this.back = 0;                   /* bits back of last unprocessed length/lit */
  this.was = 0;                    /* initial length of match */
}

function inflateResetKeep(strm) {
  var state;

  if (!strm || !strm.state) { return Z_STREAM_ERROR; }
  state = strm.state;
  strm.total_in = strm.total_out = state.total = 0;
  strm.msg = ''; /*Z_NULL*/
  if (state.wrap) {       /* to support ill-conceived Java test suite */
    strm.adler = state.wrap & 1;
  }
  state.mode = HEAD;
  state.last = 0;
  state.havedict = 0;
  state.dmax = 32768;
  state.head = null/*Z_NULL*/;
  state.hold = 0;
  state.bits = 0;
  //state.lencode = state.distcode = state.next = state.codes;
  state.lencode = state.lendyn = new utils.Buf32(ENOUGH_LENS);
  state.distcode = state.distdyn = new utils.Buf32(ENOUGH_DISTS);

  state.sane = 1;
  state.back = -1;
  //Tracev((stderr, "inflate: reset\n"));
  return Z_OK;
}

function inflateReset(strm) {
  var state;

  if (!strm || !strm.state) { return Z_STREAM_ERROR; }
  state = strm.state;
  state.wsize = 0;
  state.whave = 0;
  state.wnext = 0;
  return inflateResetKeep(strm);

}

function inflateReset2(strm, windowBits) {
  var wrap;
  var state;

  /* get the state */
  if (!strm || !strm.state) { return Z_STREAM_ERROR; }
  state = strm.state;

  /* extract wrap request from windowBits parameter */
  if (windowBits < 0) {
    wrap = 0;
    windowBits = -windowBits;
  }
  else {
    wrap = (windowBits >> 4) + 1;
    if (windowBits < 48) {
      windowBits &= 15;
    }
  }

  /* set number of window bits, free window if different */
  if (windowBits && (windowBits < 8 || windowBits > 15)) {
    return Z_STREAM_ERROR;
  }
  if (state.window !== null && state.wbits !== windowBits) {
    state.window = null;
  }

  /* update state and reset the rest of it */
  state.wrap = wrap;
  state.wbits = windowBits;
  return inflateReset(strm);
}

function inflateInit2(strm, windowBits) {
  var ret;
  var state;

  if (!strm) { return Z_STREAM_ERROR; }
  //strm.msg = Z_NULL;                 /* in case we return an error */

  state = new InflateState();

  //if (state === Z_NULL) return Z_MEM_ERROR;
  //Tracev((stderr, "inflate: allocated\n"));
  strm.state = state;
  state.window = null/*Z_NULL*/;
  ret = inflateReset2(strm, windowBits);
  if (ret !== Z_OK) {
    strm.state = null/*Z_NULL*/;
  }
  return ret;
}

function inflateInit(strm) {
  return inflateInit2(strm, DEF_WBITS);
}


/*
 Return state with length and distance decoding tables and index sizes set to
 fixed code decoding.  Normally this returns fixed tables from inffixed.h.
 If BUILDFIXED is defined, then instead this routine builds the tables the
 first time it's called, and returns those tables the first time and
 thereafter.  This reduces the size of the code by about 2K bytes, in
 exchange for a little execution time.  However, BUILDFIXED should not be
 used for threaded applications, since the rewriting of the tables and virgin
 may not be thread-safe.
 */
var virgin = true;

var lenfix, distfix; // We have no pointers in JS, so keep tables separate

function fixedtables(state) {
  /* build fixed huffman tables if first call (may not be thread safe) */
  if (virgin) {
    var sym;

    lenfix = new utils.Buf32(512);
    distfix = new utils.Buf32(32);

    /* literal/length table */
    sym = 0;
    while (sym < 144) { state.lens[sym++] = 8; }
    while (sym < 256) { state.lens[sym++] = 9; }
    while (sym < 280) { state.lens[sym++] = 7; }
    while (sym < 288) { state.lens[sym++] = 8; }

    inflate_table(LENS,  state.lens, 0, 288, lenfix,   0, state.work, { bits: 9 });

    /* distance table */
    sym = 0;
    while (sym < 32) { state.lens[sym++] = 5; }

    inflate_table(DISTS, state.lens, 0, 32,   distfix, 0, state.work, { bits: 5 });

    /* do this just once */
    virgin = false;
  }

  state.lencode = lenfix;
  state.lenbits = 9;
  state.distcode = distfix;
  state.distbits = 5;
}


/*
 Update the window with the last wsize (normally 32K) bytes written before
 returning.  If window does not exist yet, create it.  This is only called
 when a window is already in use, or when output has been written during this
 inflate call, but the end of the deflate stream has not been reached yet.
 It is also called to create a window for dictionary data when a dictionary
 is loaded.

 Providing output buffers larger than 32K to inflate() should provide a speed
 advantage, since only the last 32K of output is copied to the sliding window
 upon return from inflate(), and since all distances after the first 32K of
 output will fall in the output data, making match copies simpler and faster.
 The advantage may be dependent on the size of the processor's data caches.
 */
function updatewindow(strm, src, end, copy) {
  var dist;
  var state = strm.state;

  /* if it hasn't been done already, allocate space for the window */
  if (state.window === null) {
    state.wsize = 1 << state.wbits;
    state.wnext = 0;
    state.whave = 0;

    state.window = new utils.Buf8(state.wsize);
  }

  /* copy state->wsize or less output bytes into the circular window */
  if (copy >= state.wsize) {
    utils.arraySet(state.window, src, end - state.wsize, state.wsize, 0);
    state.wnext = 0;
    state.whave = state.wsize;
  }
  else {
    dist = state.wsize - state.wnext;
    if (dist > copy) {
      dist = copy;
    }
    //zmemcpy(state->window + state->wnext, end - copy, dist);
    utils.arraySet(state.window, src, end - copy, dist, state.wnext);
    copy -= dist;
    if (copy) {
      //zmemcpy(state->window, end - copy, copy);
      utils.arraySet(state.window, src, end - copy, copy, 0);
      state.wnext = copy;
      state.whave = state.wsize;
    }
    else {
      state.wnext += dist;
      if (state.wnext === state.wsize) { state.wnext = 0; }
      if (state.whave < state.wsize) { state.whave += dist; }
    }
  }
  return 0;
}

function inflate(strm, flush) {
  var state;
  var input, output;          // input/output buffers
  var next;                   /* next input INDEX */
  var put;                    /* next output INDEX */
  var have, left;             /* available input and output */
  var hold;                   /* bit buffer */
  var bits;                   /* bits in bit buffer */
  var _in, _out;              /* save starting available input and output */
  var copy;                   /* number of stored or match bytes to copy */
  var from;                   /* where to copy match bytes from */
  var from_source;
  var here = 0;               /* current decoding table entry */
  var here_bits, here_op, here_val; // paked "here" denormalized (JS specific)
  //var last;                   /* parent table entry */
  var last_bits, last_op, last_val; // paked "last" denormalized (JS specific)
  var len;                    /* length to copy for repeats, bits to drop */
  var ret;                    /* return code */
  var hbuf = new utils.Buf8(4);    /* buffer for gzip header crc calculation */
  var opts;

  var n; // temporary var for NEED_BITS

  var order = /* permutation of code lengths */
    [ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ];


  if (!strm || !strm.state || !strm.output ||
      (!strm.input && strm.avail_in !== 0)) {
    return Z_STREAM_ERROR;
  }

  state = strm.state;
  if (state.mode === TYPE) { state.mode = TYPEDO; }    /* skip check */


  //--- LOAD() ---
  put = strm.next_out;
  output = strm.output;
  left = strm.avail_out;
  next = strm.next_in;
  input = strm.input;
  have = strm.avail_in;
  hold = state.hold;
  bits = state.bits;
  //---

  _in = have;
  _out = left;
  ret = Z_OK;

  inf_leave: // goto emulation
  for (;;) {
    switch (state.mode) {
      case HEAD:
        if (state.wrap === 0) {
          state.mode = TYPEDO;
          break;
        }
        //=== NEEDBITS(16);
        while (bits < 16) {
          if (have === 0) { break inf_leave; }
          have--;
          hold += input[next++] << bits;
          bits += 8;
        }
        //===//
        if ((state.wrap & 2) && hold === 0x8b1f) {  /* gzip header */
          state.check = 0/*crc32(0L, Z_NULL, 0)*/;
          //=== CRC2(state.check, hold);
          hbuf[0] = hold & 0xff;
          hbuf[1] = (hold >>> 8) & 0xff;
          state.check = crc32(state.check, hbuf, 2, 0);
          //===//

          //=== INITBITS();
          hold = 0;
          bits = 0;
          //===//
          state.mode = FLAGS;
          break;
        }
        state.flags = 0;           /* expect zlib header */
        if (state.head) {
          state.head.done = false;
        }
        if (!(state.wrap & 1) ||   /* check if zlib header allowed */
          (((hold & 0xff)/*BITS(8)*/ << 8) + (hold >> 8)) % 31) {
          strm.msg = 'incorrect header check';
          state.mode = BAD;
          break;
        }
        if ((hold & 0x0f)/*BITS(4)*/ !== Z_DEFLATED) {
          strm.msg = 'unknown compression method';
          state.mode = BAD;
          break;
        }
        //--- DROPBITS(4) ---//
        hold >>>= 4;
        bits -= 4;
        //---//
        len = (hold & 0x0f)/*BITS(4)*/ + 8;
        if (state.wbits === 0) {
          state.wbits = len;
        }
        else if (len > state.wbits) {
          strm.msg = 'invalid window size';
          state.mode = BAD;
          break;
        }
        state.dmax = 1 << len;
        //Tracev((stderr, "inflate:   zlib header ok\n"));
        strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/;
        state.mode = hold & 0x200 ? DICTID : TYPE;
        //=== INITBITS();
        hold = 0;
        bits = 0;
        //===//
        break;
      case FLAGS:
        //=== NEEDBITS(16); */
        while (bits < 16) {
          if (have === 0) { break inf_leave; }
          have--;
          hold += input[next++] << bits;
          bits += 8;
        }
        //===//
        state.flags = hold;
        if ((state.flags & 0xff) !== Z_DEFLATED) {
          strm.msg = 'unknown compression method';
          state.mode = BAD;
          break;
        }
        if (state.flags & 0xe000) {
          strm.msg = 'unknown header flags set';
          state.mode = BAD;
          break;
        }
        if (state.head) {
          state.head.text = ((hold >> 8) & 1);
        }
        if (state.flags & 0x0200) {
          //=== CRC2(state.check, hold);
          hbuf[0] = hold & 0xff;
          hbuf[1] = (hold >>> 8) & 0xff;
          state.check = crc32(state.check, hbuf, 2, 0);
          //===//
        }
        //=== INITBITS();
        hold = 0;
        bits = 0;
        //===//
        state.mode = TIME;
        /* falls through */
      case TIME:
        //=== NEEDBITS(32); */
        while (bits < 32) {
          if (have === 0) { break inf_leave; }
          have--;
          hold += input[next++] << bits;
          bits += 8;
        }
        //===//
        if (state.head) {
          state.head.time = hold;
        }
        if (state.flags & 0x0200) {
          //=== CRC4(state.check, hold)
          hbuf[0] = hold & 0xff;
          hbuf[1] = (hold >>> 8) & 0xff;
          hbuf[2] = (hold >>> 16) & 0xff;
          hbuf[3] = (hold >>> 24) & 0xff;
          state.check = crc32(state.check, hbuf, 4, 0);
          //===
        }
        //=== INITBITS();
        hold = 0;
        bits = 0;
        //===//
        state.mode = OS;
        /* falls through */
      case OS:
        //=== NEEDBITS(16); */
        while (bits < 16) {
          if (have === 0) { break inf_leave; }
          have--;
          hold += input[next++] << bits;
          bits += 8;
        }
        //===//
        if (state.head) {
          state.head.xflags = (hold & 0xff);
          state.head.os = (hold >> 8);
        }
        if (state.flags & 0x0200) {
          //=== CRC2(state.check, hold);
          hbuf[0] = hold & 0xff;
          hbuf[1] = (hold >>> 8) & 0xff;
          state.check = crc32(state.check, hbuf, 2, 0);
          //===//
        }
        //=== INITBITS();
        hold = 0;
        bits = 0;
        //===//
        state.mode = EXLEN;
        /* falls through */
      case EXLEN:
        if (state.flags & 0x0400) {
          //=== NEEDBITS(16); */
          while (bits < 16) {
            if (have === 0) { break inf_leave; }
            have--;
            hold += input[next++] << bits;
            bits += 8;
          }
          //===//
          state.length = hold;
          if (state.head) {
            state.head.extra_len = hold;
          }
          if (state.flags & 0x0200) {
            //=== CRC2(state.check, hold);
            hbuf[0] = hold & 0xff;
            hbuf[1] = (hold >>> 8) & 0xff;
            state.check = crc32(state.check, hbuf, 2, 0);
            //===//
          }
          //=== INITBITS();
          hold = 0;
          bits = 0;
          //===//
        }
        else if (state.head) {
          state.head.extra = null/*Z_NULL*/;
        }
        state.mode = EXTRA;
        /* falls through */
      case EXTRA:
        if (state.flags & 0x0400) {
          copy = state.length;
          if (copy > have) { copy = have; }
          if (copy) {
            if (state.head) {
              len = state.head.extra_len - state.length;
              if (!state.head.extra) {
                // Use untyped array for more convenient processing later
                state.head.extra = new Array(state.head.extra_len);
              }
              utils.arraySet(
                state.head.extra,
                input,
                next,
                // extra field is limited to 65536 bytes
                // - no need for additional size check
                copy,
                /*len + copy > state.head.extra_max - len ? state.head.extra_max : copy,*/
                len
              );
              //zmemcpy(state.head.extra + len, next,
              //        len + copy > state.head.extra_max ?
              //        state.head.extra_max - len : copy);
            }
            if (state.flags & 0x0200) {
              state.check = crc32(state.check, input, copy, next);
            }
            have -= copy;
            next += copy;
            state.length -= copy;
          }
          if (state.length) { break inf_leave; }
        }
        state.length = 0;
        state.mode = NAME;
        /* falls through */
      case NAME:
        if (state.flags & 0x0800) {
          if (have === 0) { break inf_leave; }
          copy = 0;
          do {
            // TODO: 2 or 1 bytes?
            len = input[next + copy++];
            /* use constant limit because in js we should not preallocate memory */
            if (state.head && len &&
                (state.length < 65536 /*state.head.name_max*/)) {
              state.head.name += String.fromCharCode(len);
            }
          } while (len && copy < have);

          if (state.flags & 0x0200) {
            state.check = crc32(state.check, input, copy, next);
          }
          have -= copy;
          next += copy;
          if (len) { break inf_leave; }
        }
        else if (state.head) {
          state.head.name = null;
        }
        state.length = 0;
        state.mode = COMMENT;
        /* falls through */
      case COMMENT:
        if (state.flags & 0x1000) {
          if (have === 0) { break inf_leave; }
          copy = 0;
          do {
            len = input[next + copy++];
            /* use constant limit because in js we should not preallocate memory */
            if (state.head && len &&
                (state.length < 65536 /*state.head.comm_max*/)) {
              state.head.comment += String.fromCharCode(len);
            }
          } while (len && copy < have);
          if (state.flags & 0x0200) {
            state.check = crc32(state.check, input, copy, next);
          }
          have -= copy;
          next += copy;
          if (len) { break inf_leave; }
        }
        else if (state.head) {
          state.head.comment = null;
        }
        state.mode = HCRC;
        /* falls through */
      case HCRC:
        if (state.flags & 0x0200) {
          //=== NEEDBITS(16); */
          while (bits < 16) {
            if (have === 0) { break inf_leave; }
            have--;
            hold += input[next++] << bits;
            bits += 8;
          }
          //===//
          if (hold !== (state.check & 0xffff)) {
            strm.msg = 'header crc mismatch';
            state.mode = BAD;
            break;
          }
          //=== INITBITS();
          hold = 0;
          bits = 0;
          //===//
        }
        if (state.head) {
          state.head.hcrc = ((state.flags >> 9) & 1);
          state.head.done = true;
        }
        strm.adler = state.check = 0;
        state.mode = TYPE;
        break;
      case DICTID:
        //=== NEEDBITS(32); */
        while (bits < 32) {
          if (have === 0) { break inf_leave; }
          have--;
          hold += input[next++] << bits;
          bits += 8;
        }
        //===//
        strm.adler = state.check = zswap32(hold);
        //=== INITBITS();
        hold = 0;
        bits = 0;
        //===//
        state.mode = DICT;
        /* falls through */
      case DICT:
        if (state.havedict === 0) {
          //--- RESTORE() ---
          strm.next_out = put;
          strm.avail_out = left;
          strm.next_in = next;
          strm.avail_in = have;
          state.hold = hold;
          state.bits = bits;
          //---
          return Z_NEED_DICT;
        }
        strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/;
        state.mode = TYPE;
        /* falls through */
      case TYPE:
        if (flush === Z_BLOCK || flush === Z_TREES) { break inf_leave; }
        /* falls through */
      case TYPEDO:
        if (state.last) {
          //--- BYTEBITS() ---//
          hold >>>= bits & 7;
          bits -= bits & 7;
          //---//
          state.mode = CHECK;
          break;
        }
        //=== NEEDBITS(3); */
        while (bits < 3) {
          if (have === 0) { break inf_leave; }
          have--;
          hold += input[next++] << bits;
          bits += 8;
        }
        //===//
        state.last = (hold & 0x01)/*BITS(1)*/;
        //--- DROPBITS(1) ---//
        hold >>>= 1;
        bits -= 1;
        //---//

        switch ((hold & 0x03)/*BITS(2)*/) {
          case 0:                             /* stored block */
            //Tracev((stderr, "inflate:     stored block%s\n",
            //        state.last ? " (last)" : ""));
            state.mode = STORED;
            break;
          case 1:                             /* fixed block */
            fixedtables(state);
            //Tracev((stderr, "inflate:     fixed codes block%s\n",
            //        state.last ? " (last)" : ""));
            state.mode = LEN_;             /* decode codes */
            if (flush === Z_TREES) {
              //--- DROPBITS(2) ---//
              hold >>>= 2;
              bits -= 2;
              //---//
              break inf_leave;
            }
            break;
          case 2:                             /* dynamic block */
            //Tracev((stderr, "inflate:     dynamic codes block%s\n",
            //        state.last ? " (last)" : ""));
            state.mode = TABLE;
            break;
          case 3:
            strm.msg = 'invalid block type';
            state.mode = BAD;
        }
        //--- DROPBITS(2) ---//
        hold >>>= 2;
        bits -= 2;
        //---//
        break;
      case STORED:
        //--- BYTEBITS() ---// /* go to byte boundary */
        hold >>>= bits & 7;
        bits -= bits & 7;
        //---//
        //=== NEEDBITS(32); */
        while (bits < 32) {
          if (have === 0) { break inf_leave; }
          have--;
          hold += input[next++] << bits;
          bits += 8;
        }
        //===//
        if ((hold & 0xffff) !== ((hold >>> 16) ^ 0xffff)) {
          strm.msg = 'invalid stored block lengths';
          state.mode = BAD;
          break;
        }
        state.length = hold & 0xffff;
        //Tracev((stderr, "inflate:       stored length %u\n",
        //        state.length));
        //=== INITBITS();
        hold = 0;
        bits = 0;
        //===//
        state.mode = COPY_;
        if (flush === Z_TREES) { break inf_leave; }
        /* falls through */
      case COPY_:
        state.mode = COPY;
        /* falls through */
      case COPY:
        copy = state.length;
        if (copy) {
          if (copy > have) { copy = have; }
          if (copy > left) { copy = left; }
          if (copy === 0) { break inf_leave; }
          //--- zmemcpy(put, next, copy); ---
          utils.arraySet(output, input, next, copy, put);
          //---//
          have -= copy;
          next += copy;
          left -= copy;
          put += copy;
          state.length -= copy;
          break;
        }
        //Tracev((stderr, "inflate:       stored end\n"));
        state.mode = TYPE;
        break;
      case TABLE:
        //=== NEEDBITS(14); */
        while (bits < 14) {
          if (have === 0) { break inf_leave; }
          have--;
          hold += input[next++] << bits;
          bits += 8;
        }
        //===//
        state.nlen = (hold & 0x1f)/*BITS(5)*/ + 257;
        //--- DROPBITS(5) ---//
        hold >>>= 5;
        bits -= 5;
        //---//
        state.ndist = (hold & 0x1f)/*BITS(5)*/ + 1;
        //--- DROPBITS(5) ---//
        hold >>>= 5;
        bits -= 5;
        //---//
        state.ncode = (hold & 0x0f)/*BITS(4)*/ + 4;
        //--- DROPBITS(4) ---//
        hold >>>= 4;
        bits -= 4;
        //---//
//#ifndef PKZIP_BUG_WORKAROUND
        if (state.nlen > 286 || state.ndist > 30) {
          strm.msg = 'too many length or distance symbols';
          state.mode = BAD;
          break;
        }
//#endif
        //Tracev((stderr, "inflate:       table sizes ok\n"));
        state.have = 0;
        state.mode = LENLENS;
        /* falls through */
      case LENLENS:
        while (state.have < state.ncode) {
          //=== NEEDBITS(3);
          while (bits < 3) {
            if (have === 0) { break inf_leave; }
            have--;
            hold += input[next++] << bits;
            bits += 8;
          }
          //===//
          state.lens[order[state.have++]] = (hold & 0x07);//BITS(3);
          //--- DROPBITS(3) ---//
          hold >>>= 3;
          bits -= 3;
          //---//
        }
        while (state.have < 19) {
          state.lens[order[state.have++]] = 0;
        }
        // We have separate tables & no pointers. 2 commented lines below not needed.
        //state.next = state.codes;
        //state.lencode = state.next;
        // Switch to use dynamic table
        state.lencode = state.lendyn;
        state.lenbits = 7;

        opts = { bits: state.lenbits };
        ret = inflate_table(CODES, state.lens, 0, 19, state.lencode, 0, state.work, opts);
        state.lenbits = opts.bits;

        if (ret) {
          strm.msg = 'invalid code lengths set';
          state.mode = BAD;
          break;
        }
        //Tracev((stderr, "inflate:       code lengths ok\n"));
        state.have = 0;
        state.mode = CODELENS;
        /* falls through */
      case CODELENS:
        while (state.have < state.nlen + state.ndist) {
          for (;;) {
            here = state.lencode[hold & ((1 << state.lenbits) - 1)];/*BITS(state.lenbits)*/
            here_bits = here >>> 24;
            here_op = (here >>> 16) & 0xff;
            here_val = here & 0xffff;

            if ((here_bits) <= bits) { break; }
            //--- PULLBYTE() ---//
            if (have === 0) { break inf_leave; }
            have--;
            hold += input[next++] << bits;
            bits += 8;
            //---//
          }
          if (here_val < 16) {
            //--- DROPBITS(here.bits) ---//
            hold >>>= here_bits;
            bits -= here_bits;
            //---//
            state.lens[state.have++] = here_val;
          }
          else {
            if (here_val === 16) {
              //=== NEEDBITS(here.bits + 2);
              n = here_bits + 2;
              while (bits < n) {
                if (have === 0) { break inf_leave; }
                have--;
                hold += input[next++] << bits;
                bits += 8;
              }
              //===//
              //--- DROPBITS(here.bits) ---//
              hold >>>= here_bits;
              bits -= here_bits;
              //---//
              if (state.have === 0) {
                strm.msg = 'invalid bit length repeat';
                state.mode = BAD;
                break;
              }
              len = state.lens[state.have - 1];
              copy = 3 + (hold & 0x03);//BITS(2);
              //--- DROPBITS(2) ---//
              hold >>>= 2;
              bits -= 2;
              //---//
            }
            else if (here_val === 17) {
              //=== NEEDBITS(here.bits + 3);
              n = here_bits + 3;
              while (bits < n) {
                if (have === 0) { break inf_leave; }
                have--;
                hold += input[next++] << bits;
                bits += 8;
              }
              //===//
              //--- DROPBITS(here.bits) ---//
              hold >>>= here_bits;
              bits -= here_bits;
              //---//
              len = 0;
              copy = 3 + (hold & 0x07);//BITS(3);
              //--- DROPBITS(3) ---//
              hold >>>= 3;
              bits -= 3;
              //---//
            }
            else {
              //=== NEEDBITS(here.bits + 7);
              n = here_bits + 7;
              while (bits < n) {
                if (have === 0) { break inf_leave; }
                have--;
                hold += input[next++] << bits;
                bits += 8;
              }
              //===//
              //--- DROPBITS(here.bits) ---//
              hold >>>= here_bits;
              bits -= here_bits;
              //---//
              len = 0;
              copy = 11 + (hold & 0x7f);//BITS(7);
              //--- DROPBITS(7) ---//
              hold >>>= 7;
              bits -= 7;
              //---//
            }
            if (state.have + copy > state.nlen + state.ndist) {
              strm.msg = 'invalid bit length repeat';
              state.mode = BAD;
              break;
            }
            while (copy--) {
              state.lens[state.have++] = len;
            }
          }
        }

        /* handle error breaks in while */
        if (state.mode === BAD) { break; }

        /* check for end-of-block code (better have one) */
        if (state.lens[256] === 0) {
          strm.msg = 'invalid code -- missing end-of-block';
          state.mode = BAD;
          break;
        }

        /* build code tables -- note: do not change the lenbits or distbits
           values here (9 and 6) without reading the comments in inftrees.h
           concerning the ENOUGH constants, which depend on those values */
        state.lenbits = 9;

        opts = { bits: state.lenbits };
        ret = inflate_table(LENS, state.lens, 0, state.nlen, state.lencode, 0, state.work, opts);
        // We have separate tables & no pointers. 2 commented lines below not needed.
        // state.next_index = opts.table_index;
        state.lenbits = opts.bits;
        // state.lencode = state.next;

        if (ret) {
          strm.msg = 'invalid literal/lengths set';
          state.mode = BAD;
          break;
        }

        state.distbits = 6;
        //state.distcode.copy(state.codes);
        // Switch to use dynamic table
        state.distcode = state.distdyn;
        opts = { bits: state.distbits };
        ret = inflate_table(DISTS, state.lens, state.nlen, state.ndist, state.distcode, 0, state.work, opts);
        // We have separate tables & no pointers. 2 commented lines below not needed.
        // state.next_index = opts.table_index;
        state.distbits = opts.bits;
        // state.distcode = state.next;

        if (ret) {
          strm.msg = 'invalid distances set';
          state.mode = BAD;
          break;
        }
        //Tracev((stderr, 'inflate:       codes ok\n'));
        state.mode = LEN_;
        if (flush === Z_TREES) { break inf_leave; }
        /* falls through */
      case LEN_:
        state.mode = LEN;
        /* falls through */
      case LEN:
        if (have >= 6 && left >= 258) {
          //--- RESTORE() ---
          strm.next_out = put;
          strm.avail_out = left;
          strm.next_in = next;
          strm.avail_in = have;
          state.hold = hold;
          state.bits = bits;
          //---
          inflate_fast(strm, _out);
          //--- LOAD() ---
          put = strm.next_out;
          output = strm.output;
          left = strm.avail_out;
          next = strm.next_in;
          input = strm.input;
          have = strm.avail_in;
          hold = state.hold;
          bits = state.bits;
          //---

          if (state.mode === TYPE) {
            state.back = -1;
          }
          break;
        }
        state.back = 0;
        for (;;) {
          here = state.lencode[hold & ((1 << state.lenbits) - 1)];  /*BITS(state.lenbits)*/
          here_bits = here >>> 24;
          here_op = (here >>> 16) & 0xff;
          here_val = here & 0xffff;

          if (here_bits <= bits) { break; }
          //--- PULLBYTE() ---//
          if (have === 0) { break inf_leave; }
          have--;
          hold += input[next++] << bits;
          bits += 8;
          //---//
        }
        if (here_op && (here_op & 0xf0) === 0) {
          last_bits = here_bits;
          last_op = here_op;
          last_val = here_val;
          for (;;) {
            here = state.lencode[last_val +
                    ((hold & ((1 << (last_bits + last_op)) - 1))/*BITS(last.bits + last.op)*/ >> last_bits)];
            here_bits = here >>> 24;
            here_op = (here >>> 16) & 0xff;
            here_val = here & 0xffff;

            if ((last_bits + here_bits) <= bits) { break; }
            //--- PULLBYTE() ---//
            if (have === 0) { break inf_leave; }
            have--;
            hold += input[next++] << bits;
            bits += 8;
            //---//
          }
          //--- DROPBITS(last.bits) ---//
          hold >>>= last_bits;
          bits -= last_bits;
          //---//
          state.back += last_bits;
        }
        //--- DROPBITS(here.bits) ---//
        hold >>>= here_bits;
        bits -= here_bits;
        //---//
        state.back += here_bits;
        state.length = here_val;
        if (here_op === 0) {
          //Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
          //        "inflate:         literal '%c'\n" :
          //        "inflate:         literal 0x%02x\n", here.val));
          state.mode = LIT;
          break;
        }
        if (here_op & 32) {
          //Tracevv((stderr, "inflate:         end of block\n"));
          state.back = -1;
          state.mode = TYPE;
          break;
        }
        if (here_op & 64) {
          strm.msg = 'invalid literal/length code';
          state.mode = BAD;
          break;
        }
        state.extra = here_op & 15;
        state.mode = LENEXT;
        /* falls through */
      case LENEXT:
        if (state.extra) {
          //=== NEEDBITS(state.extra);
          n = state.extra;
          while (bits < n) {
            if (have === 0) { break inf_leave; }
            have--;
            hold += input[next++] << bits;
            bits += 8;
          }
          //===//
          state.length += hold & ((1 << state.extra) - 1)/*BITS(state.extra)*/;
          //--- DROPBITS(state.extra) ---//
          hold >>>= state.extra;
          bits -= state.extra;
          //---//
          state.back += state.extra;
        }
        //Tracevv((stderr, "inflate:         length %u\n", state.length));
        state.was = state.length;
        state.mode = DIST;
        /* falls through */
      case DIST:
        for (;;) {
          here = state.distcode[hold & ((1 << state.distbits) - 1)];/*BITS(state.distbits)*/
          here_bits = here >>> 24;
          here_op = (here >>> 16) & 0xff;
          here_val = here & 0xffff;

          if ((here_bits) <= bits) { break; }
          //--- PULLBYTE() ---//
          if (have === 0) { break inf_leave; }
          have--;
          hold += input[next++] << bits;
          bits += 8;
          //---//
        }
        if ((here_op & 0xf0) === 0) {
          last_bits = here_bits;
          last_op = here_op;
          last_val = here_val;
          for (;;) {
            here = state.distcode[last_val +
                    ((hold & ((1 << (last_bits + last_op)) - 1))/*BITS(last.bits + last.op)*/ >> last_bits)];
            here_bits = here >>> 24;
            here_op = (here >>> 16) & 0xff;
            here_val = here & 0xffff;

            if ((last_bits + here_bits) <= bits) { break; }
            //--- PULLBYTE() ---//
            if (have === 0) { break inf_leave; }
            have--;
            hold += input[next++] << bits;
            bits += 8;
            //---//
          }
          //--- DROPBITS(last.bits) ---//
          hold >>>= last_bits;
          bits -= last_bits;
          //---//
          state.back += last_bits;
        }
        //--- DROPBITS(here.bits) ---//
        hold >>>= here_bits;
        bits -= here_bits;
        //---//
        state.back += here_bits;
        if (here_op & 64) {
          strm.msg = 'invalid distance code';
          state.mode = BAD;
          break;
        }
        state.offset = here_val;
        state.extra = (here_op) & 15;
        state.mode = DISTEXT;
        /* falls through */
      case DISTEXT:
        if (state.extra) {
          //=== NEEDBITS(state.extra);
          n = state.extra;
          while (bits < n) {
            if (have === 0) { break inf_leave; }
            have--;
            hold += input[next++] << bits;
            bits += 8;
          }
          //===//
          state.offset += hold & ((1 << state.extra) - 1)/*BITS(state.extra)*/;
          //--- DROPBITS(state.extra) ---//
          hold >>>= state.extra;
          bits -= state.extra;
          //---//
          state.back += state.extra;
        }
//#ifdef INFLATE_STRICT
        if (state.offset > state.dmax) {
          strm.msg = 'invalid distance too far back';
          state.mode = BAD;
          break;
        }
//#endif
        //Tracevv((stderr, "inflate:         distance %u\n", state.offset));
        state.mode = MATCH;
        /* falls through */
      case MATCH:
        if (left === 0) { break inf_leave; }
        copy = _out - left;
        if (state.offset > copy) {         /* copy from window */
          copy = state.offset - copy;
          if (copy > state.whave) {
            if (state.sane) {
              strm.msg = 'invalid distance too far back';
              state.mode = BAD;
              break;
            }
// (!) This block is disabled in zlib defaults,
// don't enable it for binary compatibility
//#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
//          Trace((stderr, "inflate.c too far\n"));
//          copy -= state.whave;
//          if (copy > state.length) { copy = state.length; }
//          if (copy > left) { copy = left; }
//          left -= copy;
//          state.length -= copy;
//          do {
//            output[put++] = 0;
//          } while (--copy);
//          if (state.length === 0) { state.mode = LEN; }
//          break;
//#endif
          }
          if (copy > state.wnext) {
            copy -= state.wnext;
            from = state.wsize - copy;
          }
          else {
            from = state.wnext - copy;
          }
          if (copy > state.length) { copy = state.length; }
          from_source = state.window;
        }
        else {                              /* copy from output */
          from_source = output;
          from = put - state.offset;
          copy = state.length;
        }
        if (copy > left) { copy = left; }
        left -= copy;
        state.length -= copy;
        do {
          output[put++] = from_source[from++];
        } while (--copy);
        if (state.length === 0) { state.mode = LEN; }
        break;
      case LIT:
        if (left === 0) { break inf_leave; }
        output[put++] = state.length;
        left--;
        state.mode = LEN;
        break;
      case CHECK:
        if (state.wrap) {
          //=== NEEDBITS(32);
          while (bits < 32) {
            if (have === 0) { break inf_leave; }
            have--;
            // Use '|' instead of '+' to make sure that result is signed
            hold |= input[next++] << bits;
            bits += 8;
          }
          //===//
          _out -= left;
          strm.total_out += _out;
          state.total += _out;
          if (_out) {
            strm.adler = state.check =
                /*UPDATE(state.check, put - _out, _out);*/
                (state.flags ? crc32(state.check, output, _out, put - _out) : adler32(state.check, output, _out, put - _out));

          }
          _out = left;
          // NB: crc32 stored as signed 32-bit int, zswap32 returns signed too
          if ((state.flags ? hold : zswap32(hold)) !== state.check) {
            strm.msg = 'incorrect data check';
            state.mode = BAD;
            break;
          }
          //=== INITBITS();
          hold = 0;
          bits = 0;
          //===//
          //Tracev((stderr, "inflate:   check matches trailer\n"));
        }
        state.mode = LENGTH;
        /* falls through */
      case LENGTH:
        if (state.wrap && state.flags) {
          //=== NEEDBITS(32);
          while (bits < 32) {
            if (have === 0) { break inf_leave; }
            have--;
            hold += input[next++] << bits;
            bits += 8;
          }
          //===//
          if (hold !== (state.total & 0xffffffff)) {
            strm.msg = 'incorrect length check';
            state.mode = BAD;
            break;
          }
          //=== INITBITS();
          hold = 0;
          bits = 0;
          //===//
          //Tracev((stderr, "inflate:   length matches trailer\n"));
        }
        state.mode = DONE;
        /* falls through */
      case DONE:
        ret = Z_STREAM_END;
        break inf_leave;
      case BAD:
        ret = Z_DATA_ERROR;
        break inf_leave;
      case MEM:
        return Z_MEM_ERROR;
      case SYNC:
        /* falls through */
      default:
        return Z_STREAM_ERROR;
    }
  }

  // inf_leave <- here is real place for "goto inf_leave", emulated via "break inf_leave"

  /*
     Return from inflate(), updating the total counts and the check value.
     If there was no progress during the inflate() call, return a buffer
     error.  Call updatewindow() to create and/or update the window state.
     Note: a memory error from inflate() is non-recoverable.
   */

  //--- RESTORE() ---
  strm.next_out = put;
  strm.avail_out = left;
  strm.next_in = next;
  strm.avail_in = have;
  state.hold = hold;
  state.bits = bits;
  //---

  if (state.wsize || (_out !== strm.avail_out && state.mode < BAD &&
                      (state.mode < CHECK || flush !== Z_FINISH))) {
    if (updatewindow(strm, strm.output, strm.next_out, _out - strm.avail_out)) {
      state.mode = MEM;
      return Z_MEM_ERROR;
    }
  }
  _in -= strm.avail_in;
  _out -= strm.avail_out;
  strm.total_in += _in;
  strm.total_out += _out;
  state.total += _out;
  if (state.wrap && _out) {
    strm.adler = state.check = /*UPDATE(state.check, strm.next_out - _out, _out);*/
      (state.flags ? crc32(state.check, output, _out, strm.next_out - _out) : adler32(state.check, output, _out, strm.next_out - _out));
  }
  strm.data_type = state.bits + (state.last ? 64 : 0) +
                    (state.mode === TYPE ? 128 : 0) +
                    (state.mode === LEN_ || state.mode === COPY_ ? 256 : 0);
  if (((_in === 0 && _out === 0) || flush === Z_FINISH) && ret === Z_OK) {
    ret = Z_BUF_ERROR;
  }
  return ret;
}

function inflateEnd(strm) {

  if (!strm || !strm.state /*|| strm->zfree == (free_func)0*/) {
    return Z_STREAM_ERROR;
  }

  var state = strm.state;
  if (state.window) {
    state.window = null;
  }
  strm.state = null;
  return Z_OK;
}

function inflateGetHeader(strm, head) {
  var state;

  /* check state */
  if (!strm || !strm.state) { return Z_STREAM_ERROR; }
  state = strm.state;
  if ((state.wrap & 2) === 0) { return Z_STREAM_ERROR; }

  /* save header structure */
  state.head = head;
  head.done = false;
  return Z_OK;
}

function inflateSetDictionary(strm, dictionary) {
  var dictLength = dictionary.length;

  var state;
  var dictid;
  var ret;

  /* check state */
  if (!strm /* == Z_NULL */ || !strm.state /* == Z_NULL */) { return Z_STREAM_ERROR; }
  state = strm.state;

  if (state.wrap !== 0 && state.mode !== DICT) {
    return Z_STREAM_ERROR;
  }

  /* check for correct dictionary identifier */
  if (state.mode === DICT) {
    dictid = 1; /* adler32(0, null, 0)*/
    /* dictid = adler32(dictid, dictionary, dictLength); */
    dictid = adler32(dictid, dictionary, dictLength, 0);
    if (dictid !== state.check) {
      return Z_DATA_ERROR;
    }
  }
  /* copy dictionary to window using updatewindow(), which will amend the
   existing dictionary if appropriate */
  ret = updatewindow(strm, dictionary, dictLength, dictLength);
  if (ret) {
    state.mode = MEM;
    return Z_MEM_ERROR;
  }
  state.havedict = 1;
  // Tracev((stderr, "inflate:   dictionary set\n"));
  return Z_OK;
}

exports.inflateReset = inflateReset;
exports.inflateReset2 = inflateReset2;
exports.inflateResetKeep = inflateResetKeep;
exports.inflateInit = inflateInit;
exports.inflateInit2 = inflateInit2;
exports.inflate = inflate;
exports.inflateEnd = inflateEnd;
exports.inflateGetHeader = inflateGetHeader;
exports.inflateSetDictionary = inflateSetDictionary;
exports.inflateInfo = 'pako inflate (from Nodeca project)';

/* Not implemented
exports.inflateCopy = inflateCopy;
exports.inflateGetDictionary = inflateGetDictionary;
exports.inflateMark = inflateMark;
exports.inflatePrime = inflatePrime;
exports.inflateSync = inflateSync;
exports.inflateSyncPoint = inflateSyncPoint;
exports.inflateUndermine = inflateUndermine;
*/


/***/ }),
/* 32 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


// (C) 1995-2013 Jean-loup Gailly and Mark Adler
// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
//   claim that you wrote the original software. If you use this software
//   in a product, an acknowledgment in the product documentation would be
//   appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
//   misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.

// See state defs from inflate.js
var BAD = 30;       /* got a data error -- remain here until reset */
var TYPE = 12;      /* i: waiting for type bits, including last-flag bit */

/*
   Decode literal, length, and distance codes and write out the resulting
   literal and match bytes until either not enough input or output is
   available, an end-of-block is encountered, or a data error is encountered.
   When large enough input and output buffers are supplied to inflate(), for
   example, a 16K input buffer and a 64K output buffer, more than 95% of the
   inflate execution time is spent in this routine.

   Entry assumptions:

        state.mode === LEN
        strm.avail_in >= 6
        strm.avail_out >= 258
        start >= strm.avail_out
        state.bits < 8

   On return, state.mode is one of:

        LEN -- ran out of enough output space or enough available input
        TYPE -- reached end of block code, inflate() to interpret next block
        BAD -- error in block data

   Notes:

    - The maximum input bits used by a length/distance pair is 15 bits for the
      length code, 5 bits for the length extra, 15 bits for the distance code,
      and 13 bits for the distance extra.  This totals 48 bits, or six bytes.
      Therefore if strm.avail_in >= 6, then there is enough input to avoid
      checking for available input while decoding.

    - The maximum bytes that a single length/distance pair can output is 258
      bytes, which is the maximum length that can be coded.  inflate_fast()
      requires strm.avail_out >= 258 for each loop to avoid checking for
      output space.
 */
module.exports = function inflate_fast(strm, start) {
  var state;
  var _in;                    /* local strm.input */
  var last;                   /* have enough input while in < last */
  var _out;                   /* local strm.output */
  var beg;                    /* inflate()'s initial strm.output */
  var end;                    /* while out < end, enough space available */
//#ifdef INFLATE_STRICT
  var dmax;                   /* maximum distance from zlib header */
//#endif
  var wsize;                  /* window size or zero if not using window */
  var whave;                  /* valid bytes in the window */
  var wnext;                  /* window write index */
  // Use `s_window` instead `window`, avoid conflict with instrumentation tools
  var s_window;               /* allocated sliding window, if wsize != 0 */
  var hold;                   /* local strm.hold */
  var bits;                   /* local strm.bits */
  var lcode;                  /* local strm.lencode */
  var dcode;                  /* local strm.distcode */
  var lmask;                  /* mask for first level of length codes */
  var dmask;                  /* mask for first level of distance codes */
  var here;                   /* retrieved table entry */
  var op;                     /* code bits, operation, extra bits, or */
                              /*  window position, window bytes to copy */
  var len;                    /* match length, unused bytes */
  var dist;                   /* match distance */
  var from;                   /* where to copy match from */
  var from_source;


  var input, output; // JS specific, because we have no pointers

  /* copy state to local variables */
  state = strm.state;
  //here = state.here;
  _in = strm.next_in;
  input = strm.input;
  last = _in + (strm.avail_in - 5);
  _out = strm.next_out;
  output = strm.output;
  beg = _out - (start - strm.avail_out);
  end = _out + (strm.avail_out - 257);
//#ifdef INFLATE_STRICT
  dmax = state.dmax;
//#endif
  wsize = state.wsize;
  whave = state.whave;
  wnext = state.wnext;
  s_window = state.window;
  hold = state.hold;
  bits = state.bits;
  lcode = state.lencode;
  dcode = state.distcode;
  lmask = (1 << state.lenbits) - 1;
  dmask = (1 << state.distbits) - 1;


  /* decode literals and length/distances until end-of-block or not enough
     input data or output space */

  top:
  do {
    if (bits < 15) {
      hold += input[_in++] << bits;
      bits += 8;
      hold += input[_in++] << bits;
      bits += 8;
    }

    here = lcode[hold & lmask];

    dolen:
    for (;;) { // Goto emulation
      op = here >>> 24/*here.bits*/;
      hold >>>= op;
      bits -= op;
      op = (here >>> 16) & 0xff/*here.op*/;
      if (op === 0) {                          /* literal */
        //Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
        //        "inflate:         literal '%c'\n" :
        //        "inflate:         literal 0x%02x\n", here.val));
        output[_out++] = here & 0xffff/*here.val*/;
      }
      else if (op & 16) {                     /* length base */
        len = here & 0xffff/*here.val*/;
        op &= 15;                           /* number of extra bits */
        if (op) {
          if (bits < op) {
            hold += input[_in++] << bits;
            bits += 8;
          }
          len += hold & ((1 << op) - 1);
          hold >>>= op;
          bits -= op;
        }
        //Tracevv((stderr, "inflate:         length %u\n", len));
        if (bits < 15) {
          hold += input[_in++] << bits;
          bits += 8;
          hold += input[_in++] << bits;
          bits += 8;
        }
        here = dcode[hold & dmask];

        dodist:
        for (;;) { // goto emulation
          op = here >>> 24/*here.bits*/;
          hold >>>= op;
          bits -= op;
          op = (here >>> 16) & 0xff/*here.op*/;

          if (op & 16) {                      /* distance base */
            dist = here & 0xffff/*here.val*/;
            op &= 15;                       /* number of extra bits */
            if (bits < op) {
              hold += input[_in++] << bits;
              bits += 8;
              if (bits < op) {
                hold += input[_in++] << bits;
                bits += 8;
              }
            }
            dist += hold & ((1 << op) - 1);
//#ifdef INFLATE_STRICT
            if (dist > dmax) {
              strm.msg = 'invalid distance too far back';
              state.mode = BAD;
              break top;
            }
//#endif
            hold >>>= op;
            bits -= op;
            //Tracevv((stderr, "inflate:         distance %u\n", dist));
            op = _out - beg;                /* max distance in output */
            if (dist > op) {                /* see if copy from window */
              op = dist - op;               /* distance back in window */
              if (op > whave) {
                if (state.sane) {
                  strm.msg = 'invalid distance too far back';
                  state.mode = BAD;
                  break top;
                }

// (!) This block is disabled in zlib defaults,
// don't enable it for binary compatibility
//#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
//                if (len <= op - whave) {
//                  do {
//                    output[_out++] = 0;
//                  } while (--len);
//                  continue top;
//                }
//                len -= op - whave;
//                do {
//                  output[_out++] = 0;
//                } while (--op > whave);
//                if (op === 0) {
//                  from = _out - dist;
//                  do {
//                    output[_out++] = output[from++];
//                  } while (--len);
//                  continue top;
//                }
//#endif
              }
              from = 0; // window index
              from_source = s_window;
              if (wnext === 0) {           /* very common case */
                from += wsize - op;
                if (op < len) {         /* some from window */
                  len -= op;
                  do {
                    output[_out++] = s_window[from++];
                  } while (--op);
                  from = _out - dist;  /* rest from output */
                  from_source = output;
                }
              }
              else if (wnext < op) {      /* wrap around window */
                from += wsize + wnext - op;
                op -= wnext;
                if (op < len) {         /* some from end of window */
                  len -= op;
                  do {
                    output[_out++] = s_window[from++];
                  } while (--op);
                  from = 0;
                  if (wnext < len) {  /* some from start of window */
                    op = wnext;
                    len -= op;
                    do {
                      output[_out++] = s_window[from++];
                    } while (--op);
                    from = _out - dist;      /* rest from output */
                    from_source = output;
                  }
                }
              }
              else {                      /* contiguous in window */
                from += wnext - op;
                if (op < len) {         /* some from window */
                  len -= op;
                  do {
                    output[_out++] = s_window[from++];
                  } while (--op);
                  from = _out - dist;  /* rest from output */
                  from_source = output;
                }
              }
              while (len > 2) {
                output[_out++] = from_source[from++];
                output[_out++] = from_source[from++];
                output[_out++] = from_source[from++];
                len -= 3;
              }
              if (len) {
                output[_out++] = from_source[from++];
                if (len > 1) {
                  output[_out++] = from_source[from++];
                }
              }
            }
            else {
              from = _out - dist;          /* copy direct from output */
              do {                        /* minimum length is three */
                output[_out++] = output[from++];
                output[_out++] = output[from++];
                output[_out++] = output[from++];
                len -= 3;
              } while (len > 2);
              if (len) {
                output[_out++] = output[from++];
                if (len > 1) {
                  output[_out++] = output[from++];
                }
              }
            }
          }
          else if ((op & 64) === 0) {          /* 2nd level distance code */
            here = dcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))];
            continue dodist;
          }
          else {
            strm.msg = 'invalid distance code';
            state.mode = BAD;
            break top;
          }

          break; // need to emulate goto via "continue"
        }
      }
      else if ((op & 64) === 0) {              /* 2nd level length code */
        here = lcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))];
        continue dolen;
      }
      else if (op & 32) {                     /* end-of-block */
        //Tracevv((stderr, "inflate:         end of block\n"));
        state.mode = TYPE;
        break top;
      }
      else {
        strm.msg = 'invalid literal/length code';
        state.mode = BAD;
        break top;
      }

      break; // need to emulate goto via "continue"
    }
  } while (_in < last && _out < end);

  /* return unused bytes (on entry, bits < 8, so in won't go too far back) */
  len = bits >> 3;
  _in -= len;
  bits -= len << 3;
  hold &= (1 << bits) - 1;

  /* update state and return */
  strm.next_in = _in;
  strm.next_out = _out;
  strm.avail_in = (_in < last ? 5 + (last - _in) : 5 - (_in - last));
  strm.avail_out = (_out < end ? 257 + (end - _out) : 257 - (_out - end));
  state.hold = hold;
  state.bits = bits;
  return;
};


/***/ }),
/* 33 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


// (C) 1995-2013 Jean-loup Gailly and Mark Adler
// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
//   claim that you wrote the original software. If you use this software
//   in a product, an acknowledgment in the product documentation would be
//   appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
//   misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.

var utils = __webpack_require__(4);

var MAXBITS = 15;
var ENOUGH_LENS = 852;
var ENOUGH_DISTS = 592;
//var ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS);

var CODES = 0;
var LENS = 1;
var DISTS = 2;

var lbase = [ /* Length codes 257..285 base */
  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
];

var lext = [ /* Length codes 257..285 extra */
  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
];

var dbase = [ /* Distance codes 0..29 base */
  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
];

var dext = [ /* Distance codes 0..29 extra */
  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
];

module.exports = function inflate_table(type, lens, lens_index, codes, table, table_index, work, opts)
{
  var bits = opts.bits;
      //here = opts.here; /* table entry for duplication */

  var len = 0;               /* a code's length in bits */
  var sym = 0;               /* index of code symbols */
  var min = 0, max = 0;          /* minimum and maximum code lengths */
  var root = 0;              /* number of index bits for root table */
  var curr = 0;              /* number of index bits for current table */
  var drop = 0;              /* code bits to drop for sub-table */
  var left = 0;                   /* number of prefix codes available */
  var used = 0;              /* code entries in table used */
  var huff = 0;              /* Huffman code */
  var incr;              /* for incrementing code, index */
  var fill;              /* index for replicating entries */
  var low;               /* low bits for current root entry */
  var mask;              /* mask for low root bits */
  var next;             /* next available space in table */
  var base = null;     /* base value table to use */
  var base_index = 0;
//  var shoextra;    /* extra bits table to use */
  var end;                    /* use base and extra for symbol > end */
  var count = new utils.Buf16(MAXBITS + 1); //[MAXBITS+1];    /* number of codes of each length */
  var offs = new utils.Buf16(MAXBITS + 1); //[MAXBITS+1];     /* offsets in table for each length */
  var extra = null;
  var extra_index = 0;

  var here_bits, here_op, here_val;

  /*
   Process a set of code lengths to create a canonical Huffman code.  The
   code lengths are lens[0..codes-1].  Each length corresponds to the
   symbols 0..codes-1.  The Huffman code is generated by first sorting the
   symbols by length from short to long, and retaining the symbol order
   for codes with equal lengths.  Then the code starts with all zero bits
   for the first code of the shortest length, and the codes are integer
   increments for the same length, and zeros are appended as the length
   increases.  For the deflate format, these bits are stored backwards
   from their more natural integer increment ordering, and so when the
   decoding tables are built in the large loop below, the integer codes
   are incremented backwards.

   This routine assumes, but does not check, that all of the entries in
   lens[] are in the range 0..MAXBITS.  The caller must assure this.
   1..MAXBITS is interpreted as that code length.  zero means that that
   symbol does not occur in this code.

   The codes are sorted by computing a count of codes for each length,
   creating from that a table of starting indices for each length in the
   sorted table, and then entering the symbols in order in the sorted
   table.  The sorted table is work[], with that space being provided by
   the caller.

   The length counts are used for other purposes as well, i.e. finding
   the minimum and maximum length codes, determining if there are any
   codes at all, checking for a valid set of lengths, and looking ahead
   at length counts to determine sub-table sizes when building the
   decoding tables.
   */

  /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */
  for (len = 0; len <= MAXBITS; len++) {
    count[len] = 0;
  }
  for (sym = 0; sym < codes; sym++) {
    count[lens[lens_index + sym]]++;
  }

  /* bound code lengths, force root to be within code lengths */
  root = bits;
  for (max = MAXBITS; max >= 1; max--) {
    if (count[max] !== 0) { break; }
  }
  if (root > max) {
    root = max;
  }
  if (max === 0) {                     /* no symbols to code at all */
    //table.op[opts.table_index] = 64;  //here.op = (var char)64;    /* invalid code marker */
    //table.bits[opts.table_index] = 1;   //here.bits = (var char)1;
    //table.val[opts.table_index++] = 0;   //here.val = (var short)0;
    table[table_index++] = (1 << 24) | (64 << 16) | 0;


    //table.op[opts.table_index] = 64;
    //table.bits[opts.table_index] = 1;
    //table.val[opts.table_index++] = 0;
    table[table_index++] = (1 << 24) | (64 << 16) | 0;

    opts.bits = 1;
    return 0;     /* no symbols, but wait for decoding to report error */
  }
  for (min = 1; min < max; min++) {
    if (count[min] !== 0) { break; }
  }
  if (root < min) {
    root = min;
  }

  /* check for an over-subscribed or incomplete set of lengths */
  left = 1;
  for (len = 1; len <= MAXBITS; len++) {
    left <<= 1;
    left -= count[len];
    if (left < 0) {
      return -1;
    }        /* over-subscribed */
  }
  if (left > 0 && (type === CODES || max !== 1)) {
    return -1;                      /* incomplete set */
  }

  /* generate offsets into symbol table for each length for sorting */
  offs[1] = 0;
  for (len = 1; len < MAXBITS; len++) {
    offs[len + 1] = offs[len] + count[len];
  }

  /* sort symbols by length, by symbol order within each length */
  for (sym = 0; sym < codes; sym++) {
    if (lens[lens_index + sym] !== 0) {
      work[offs[lens[lens_index + sym]]++] = sym;
    }
  }

  /*
   Create and fill in decoding tables.  In this loop, the table being
   filled is at next and has curr index bits.  The code being used is huff
   with length len.  That code is converted to an index by dropping drop
   bits off of the bottom.  For codes where len is less than drop + curr,
   those top drop + curr - len bits are incremented through all values to
   fill the table with replicated entries.

   root is the number of index bits for the root table.  When len exceeds
   root, sub-tables are created pointed to by the root entry with an index
   of the low root bits of huff.  This is saved in low to check for when a
   new sub-table should be started.  drop is zero when the root table is
   being filled, and drop is root when sub-tables are being filled.

   When a new sub-table is needed, it is necessary to look ahead in the
   code lengths to determine what size sub-table is needed.  The length
   counts are used for this, and so count[] is decremented as codes are
   entered in the tables.

   used keeps track of how many table entries have been allocated from the
   provided *table space.  It is checked for LENS and DIST tables against
   the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in
   the initial root table size constants.  See the comments in inftrees.h
   for more information.

   sym increments through all symbols, and the loop terminates when
   all codes of length max, i.e. all codes, have been processed.  This
   routine permits incomplete codes, so another loop after this one fills
   in the rest of the decoding tables with invalid code markers.
   */

  /* set up for code type */
  // poor man optimization - use if-else instead of switch,
  // to avoid deopts in old v8
  if (type === CODES) {
    base = extra = work;    /* dummy value--not used */
    end = 19;

  } else if (type === LENS) {
    base = lbase;
    base_index -= 257;
    extra = lext;
    extra_index -= 257;
    end = 256;

  } else {                    /* DISTS */
    base = dbase;
    extra = dext;
    end = -1;
  }

  /* initialize opts for loop */
  huff = 0;                   /* starting code */
  sym = 0;                    /* starting code symbol */
  len = min;                  /* starting code length */
  next = table_index;              /* current table to fill in */
  curr = root;                /* current table index bits */
  drop = 0;                   /* current bits to drop from code for index */
  low = -1;                   /* trigger new sub-table when len > root */
  used = 1 << root;          /* use root table entries */
  mask = used - 1;            /* mask for comparing low */

  /* check available table space */
  if ((type === LENS && used > ENOUGH_LENS) ||
    (type === DISTS && used > ENOUGH_DISTS)) {
    return 1;
  }

  /* process all codes and make table entries */
  for (;;) {
    /* create table entry */
    here_bits = len - drop;
    if (work[sym] < end) {
      here_op = 0;
      here_val = work[sym];
    }
    else if (work[sym] > end) {
      here_op = extra[extra_index + work[sym]];
      here_val = base[base_index + work[sym]];
    }
    else {
      here_op = 32 + 64;         /* end of block */
      here_val = 0;
    }

    /* replicate for those indices with low len bits equal to huff */
    incr = 1 << (len - drop);
    fill = 1 << curr;
    min = fill;                 /* save offset to next table */
    do {
      fill -= incr;
      table[next + (huff >> drop) + fill] = (here_bits << 24) | (here_op << 16) | here_val |0;
    } while (fill !== 0);

    /* backwards increment the len-bit code huff */
    incr = 1 << (len - 1);
    while (huff & incr) {
      incr >>= 1;
    }
    if (incr !== 0) {
      huff &= incr - 1;
      huff += incr;
    } else {
      huff = 0;
    }

    /* go to next symbol, update count, len */
    sym++;
    if (--count[len] === 0) {
      if (len === max) { break; }
      len = lens[lens_index + work[sym]];
    }

    /* create new sub-table if needed */
    if (len > root && (huff & mask) !== low) {
      /* if first time, transition to sub-tables */
      if (drop === 0) {
        drop = root;
      }

      /* increment past last table */
      next += min;            /* here min is 1 << curr */

      /* determine length of next table */
      curr = len - drop;
      left = 1 << curr;
      while (curr + drop < max) {
        left -= count[curr + drop];
        if (left <= 0) { break; }
        curr++;
        left <<= 1;
      }

      /* check for enough space */
      used += 1 << curr;
      if ((type === LENS && used > ENOUGH_LENS) ||
        (type === DISTS && used > ENOUGH_DISTS)) {
        return 1;
      }

      /* point entry in root table to sub-table */
      low = huff & mask;
      /*table.op[low] = curr;
      table.bits[low] = root;
      table.val[low] = next - opts.table_index;*/
      table[low] = (root << 24) | (curr << 16) | (next - table_index) |0;
    }
  }

  /* fill in remaining table entry if code is incomplete (guaranteed to have
   at most one remaining entry, since if the code is incomplete, the
   maximum code length that was allowed to get this far is one bit) */
  if (huff !== 0) {
    //table.op[next + huff] = 64;            /* invalid code marker */
    //table.bits[next + huff] = len - drop;
    //table.val[next + huff] = 0;
    table[next + huff] = ((len - drop) << 24) | (64 << 16) |0;
  }

  /* set return parameters */
  //opts.table_index += used;
  opts.bits = root;
  return 0;
};


/***/ }),
/* 34 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


// (C) 1995-2013 Jean-loup Gailly and Mark Adler
// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
//   claim that you wrote the original software. If you use this software
//   in a product, an acknowledgment in the product documentation would be
//   appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
//   misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.

function GZheader() {
  /* true if compressed data believed to be text */
  this.text       = 0;
  /* modification time */
  this.time       = 0;
  /* extra flags (not used when writing a gzip file) */
  this.xflags     = 0;
  /* operating system */
  this.os         = 0;
  /* pointer to extra field or Z_NULL if none */
  this.extra      = null;
  /* extra field length (valid if extra != Z_NULL) */
  this.extra_len  = 0; // Actually, we don't need it in JS,
                       // but leave for few code modifications

  //
  // Setup limits is not necessary because in js we should not preallocate memory
  // for inflate use constant limit in 65536 bytes
  //

  /* space at extra (only when reading header) */
  // this.extra_max  = 0;
  /* pointer to zero-terminated file name or Z_NULL */
  this.name       = '';
  /* space at name (only when reading header) */
  // this.name_max   = 0;
  /* pointer to zero-terminated comment or Z_NULL */
  this.comment    = '';
  /* space at comment (only when reading header) */
  // this.comm_max   = 0;
  /* true if there was or will be a header crc */
  this.hcrc       = 0;
  /* true when done reading gzip header (not used when writing a gzip file) */
  this.done       = false;
}

module.exports = GZheader;


/***/ }),
/* 35 */
/***/ (function(module, exports, __webpack_require__) {

var iota = __webpack_require__(36)
var isBuffer = __webpack_require__(37)

var hasTypedArrays  = ((typeof Float64Array) !== "undefined")

function compare1st(a, b) {
  return a[0] - b[0]
}

function order() {
  var stride = this.stride
  var terms = new Array(stride.length)
  var i
  for(i=0; i<terms.length; ++i) {
    terms[i] = [Math.abs(stride[i]), i]
  }
  terms.sort(compare1st)
  var result = new Array(terms.length)
  for(i=0; i<result.length; ++i) {
    result[i] = terms[i][1]
  }
  return result
}

function compileConstructor(dtype, dimension) {
  var className = ["View", dimension, "d", dtype].join("")
  if(dimension < 0) {
    className = "View_Nil" + dtype
  }
  var useGetters = (dtype === "generic")

  if(dimension === -1) {
    //Special case for trivial arrays
    var code =
      "function "+className+"(a){this.data=a;};\
var proto="+className+".prototype;\
proto.dtype='"+dtype+"';\
proto.index=function(){return -1};\
proto.size=0;\
proto.dimension=-1;\
proto.shape=proto.stride=proto.order=[];\
proto.lo=proto.hi=proto.transpose=proto.step=\
function(){return new "+className+"(this.data);};\
proto.get=proto.set=function(){};\
proto.pick=function(){return null};\
return function construct_"+className+"(a){return new "+className+"(a);}"
    var procedure = new Function(code)
    return procedure()
  } else if(dimension === 0) {
    //Special case for 0d arrays
    var code =
      "function "+className+"(a,d) {\
this.data = a;\
this.offset = d\
};\
var proto="+className+".prototype;\
proto.dtype='"+dtype+"';\
proto.index=function(){return this.offset};\
proto.dimension=0;\
proto.size=1;\
proto.shape=\
proto.stride=\
proto.order=[];\
proto.lo=\
proto.hi=\
proto.transpose=\
proto.step=function "+className+"_copy() {\
return new "+className+"(this.data,this.offset)\
};\
proto.pick=function "+className+"_pick(){\
return TrivialArray(this.data);\
};\
proto.valueOf=proto.get=function "+className+"_get(){\
return "+(useGetters ? "this.data.get(this.offset)" : "this.data[this.offset]")+
"};\
proto.set=function "+className+"_set(v){\
return "+(useGetters ? "this.data.set(this.offset,v)" : "this.data[this.offset]=v")+"\
};\
return function construct_"+className+"(a,b,c,d){return new "+className+"(a,d)}"
    var procedure = new Function("TrivialArray", code)
    return procedure(CACHED_CONSTRUCTORS[dtype][0])
  }

  var code = ["'use strict'"]

  //Create constructor for view
  var indices = iota(dimension)
  var args = indices.map(function(i) { return "i"+i })
  var index_str = "this.offset+" + indices.map(function(i) {
        return "this.stride[" + i + "]*i" + i
      }).join("+")
  var shapeArg = indices.map(function(i) {
      return "b"+i
    }).join(",")
  var strideArg = indices.map(function(i) {
      return "c"+i
    }).join(",")
  code.push(
    "function "+className+"(a," + shapeArg + "," + strideArg + ",d){this.data=a",
      "this.shape=[" + shapeArg + "]",
      "this.stride=[" + strideArg + "]",
      "this.offset=d|0}",
    "var proto="+className+".prototype",
    "proto.dtype='"+dtype+"'",
    "proto.dimension="+dimension)

  //view.size:
  code.push("Object.defineProperty(proto,'size',{get:function "+className+"_size(){\
return "+indices.map(function(i) { return "this.shape["+i+"]" }).join("*"),
"}})")

  //view.order:
  if(dimension === 1) {
    code.push("proto.order=[0]")
  } else {
    code.push("Object.defineProperty(proto,'order',{get:")
    if(dimension < 4) {
      code.push("function "+className+"_order(){")
      if(dimension === 2) {
        code.push("return (Math.abs(this.stride[0])>Math.abs(this.stride[1]))?[1,0]:[0,1]}})")
      } else if(dimension === 3) {
        code.push(
"var s0=Math.abs(this.stride[0]),s1=Math.abs(this.stride[1]),s2=Math.abs(this.stride[2]);\
if(s0>s1){\
if(s1>s2){\
return [2,1,0];\
}else if(s0>s2){\
return [1,2,0];\
}else{\
return [1,0,2];\
}\
}else if(s0>s2){\
return [2,0,1];\
}else if(s2>s1){\
return [0,1,2];\
}else{\
return [0,2,1];\
}}})")
      }
    } else {
      code.push("ORDER})")
    }
  }

  //view.set(i0, ..., v):
  code.push(
"proto.set=function "+className+"_set("+args.join(",")+",v){")
  if(useGetters) {
    code.push("return this.data.set("+index_str+",v)}")
  } else {
    code.push("return this.data["+index_str+"]=v}")
  }

  //view.get(i0, ...):
  code.push("proto.get=function "+className+"_get("+args.join(",")+"){")
  if(useGetters) {
    code.push("return this.data.get("+index_str+")}")
  } else {
    code.push("return this.data["+index_str+"]}")
  }

  //view.index:
  code.push(
    "proto.index=function "+className+"_index(", args.join(), "){return "+index_str+"}")

  //view.hi():
  code.push("proto.hi=function "+className+"_hi("+args.join(",")+"){return new "+className+"(this.data,"+
    indices.map(function(i) {
      return ["(typeof i",i,"!=='number'||i",i,"<0)?this.shape[", i, "]:i", i,"|0"].join("")
    }).join(",")+","+
    indices.map(function(i) {
      return "this.stride["+i + "]"
    }).join(",")+",this.offset)}")

  //view.lo():
  var a_vars = indices.map(function(i) { return "a"+i+"=this.shape["+i+"]" })
  var c_vars = indices.map(function(i) { return "c"+i+"=this.stride["+i+"]" })
  code.push("proto.lo=function "+className+"_lo("+args.join(",")+"){var b=this.offset,d=0,"+a_vars.join(",")+","+c_vars.join(","))
  for(var i=0; i<dimension; ++i) {
    code.push(
"if(typeof i"+i+"==='number'&&i"+i+">=0){\
d=i"+i+"|0;\
b+=c"+i+"*d;\
a"+i+"-=d}")
  }
  code.push("return new "+className+"(this.data,"+
    indices.map(function(i) {
      return "a"+i
    }).join(",")+","+
    indices.map(function(i) {
      return "c"+i
    }).join(",")+",b)}")

  //view.step():
  code.push("proto.step=function "+className+"_step("+args.join(",")+"){var "+
    indices.map(function(i) {
      return "a"+i+"=this.shape["+i+"]"
    }).join(",")+","+
    indices.map(function(i) {
      return "b"+i+"=this.stride["+i+"]"
    }).join(",")+",c=this.offset,d=0,ceil=Math.ceil")
  for(var i=0; i<dimension; ++i) {
    code.push(
"if(typeof i"+i+"==='number'){\
d=i"+i+"|0;\
if(d<0){\
c+=b"+i+"*(a"+i+"-1);\
a"+i+"=ceil(-a"+i+"/d)\
}else{\
a"+i+"=ceil(a"+i+"/d)\
}\
b"+i+"*=d\
}")
  }
  code.push("return new "+className+"(this.data,"+
    indices.map(function(i) {
      return "a" + i
    }).join(",")+","+
    indices.map(function(i) {
      return "b" + i
    }).join(",")+",c)}")

  //view.transpose():
  var tShape = new Array(dimension)
  var tStride = new Array(dimension)
  for(var i=0; i<dimension; ++i) {
    tShape[i] = "a[i"+i+"]"
    tStride[i] = "b[i"+i+"]"
  }
  code.push("proto.transpose=function "+className+"_transpose("+args+"){"+
    args.map(function(n,idx) { return n + "=(" + n + "===undefined?" + idx + ":" + n + "|0)"}).join(";"),
    "var a=this.shape,b=this.stride;return new "+className+"(this.data,"+tShape.join(",")+","+tStride.join(",")+",this.offset)}")

  //view.pick():
  code.push("proto.pick=function "+className+"_pick("+args+"){var a=[],b=[],c=this.offset")
  for(var i=0; i<dimension; ++i) {
    code.push("if(typeof i"+i+"==='number'&&i"+i+">=0){c=(c+this.stride["+i+"]*i"+i+")|0}else{a.push(this.shape["+i+"]);b.push(this.stride["+i+"])}")
  }
  code.push("var ctor=CTOR_LIST[a.length+1];return ctor(this.data,a,b,c)}")

  //Add return statement
  code.push("return function construct_"+className+"(data,shape,stride,offset){return new "+className+"(data,"+
    indices.map(function(i) {
      return "shape["+i+"]"
    }).join(",")+","+
    indices.map(function(i) {
      return "stride["+i+"]"
    }).join(",")+",offset)}")

  //Compile procedure
  var procedure = new Function("CTOR_LIST", "ORDER", code.join("\n"))
  return procedure(CACHED_CONSTRUCTORS[dtype], order)
}

function arrayDType(data) {
  if(isBuffer(data)) {
    return "buffer"
  }
  if(hasTypedArrays) {
    switch(Object.prototype.toString.call(data)) {
      case "[object Float64Array]":
        return "float64"
      case "[object Float32Array]":
        return "float32"
      case "[object Int8Array]":
        return "int8"
      case "[object Int16Array]":
        return "int16"
      case "[object Int32Array]":
        return "int32"
      case "[object Uint8Array]":
        return "uint8"
      case "[object Uint16Array]":
        return "uint16"
      case "[object Uint32Array]":
        return "uint32"
      case "[object Uint8ClampedArray]":
        return "uint8_clamped"
    }
  }
  if(Array.isArray(data)) {
    return "array"
  }
  return "generic"
}

var CACHED_CONSTRUCTORS = {
  "float32":[],
  "float64":[],
  "int8":[],
  "int16":[],
  "int32":[],
  "uint8":[],
  "uint16":[],
  "uint32":[],
  "array":[],
  "uint8_clamped":[],
  "buffer":[],
  "generic":[]
}

;(function() {
  for(var id in CACHED_CONSTRUCTORS) {
    CACHED_CONSTRUCTORS[id].push(compileConstructor(id, -1))
  }
});

function wrappedNDArrayCtor(data, shape, stride, offset) {
  if(data === undefined) {
    var ctor = CACHED_CONSTRUCTORS.array[0]
    return ctor([])
  } else if(typeof data === "number") {
    data = [data]
  }
  if(shape === undefined) {
    shape = [ data.length ]
  }
  var d = shape.length
  if(stride === undefined) {
    stride = new Array(d)
    for(var i=d-1, sz=1; i>=0; --i) {
      stride[i] = sz
      sz *= shape[i]
    }
  }
  if(offset === undefined) {
    offset = 0
    for(var i=0; i<d; ++i) {
      if(stride[i] < 0) {
        offset -= (shape[i]-1)*stride[i]
      }
    }
  }
  var dtype = arrayDType(data)
  var ctor_list = CACHED_CONSTRUCTORS[dtype]
  while(ctor_list.length <= d+1) {
    ctor_list.push(compileConstructor(dtype, ctor_list.length-1))
  }
  var ctor = ctor_list[d+1]
  return ctor(data, shape, stride, offset)
}

module.exports = wrappedNDArrayCtor


/***/ }),
/* 36 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


function iota(n) {
  var result = new Array(n)
  for(var i=0; i<n; ++i) {
    result[i] = i
  }
  return result
}

module.exports = iota

/***/ }),
/* 37 */
/***/ (function(module, exports) {

/*!
 * Determine if an object is a Buffer
 *
 * @author   Feross Aboukhadijeh <https://feross.org>
 * @license  MIT
 */

// The _isBuffer check is for Safari 5-7 support, because it's missing
// Object.prototype.constructor. Remove this eventually
module.exports = function (obj) {
  return obj != null && (isBuffer(obj) || isSlowBuffer(obj) || !!obj._isBuffer)
}

function isBuffer (obj) {
  return !!obj.constructor && typeof obj.constructor.isBuffer === 'function' && obj.constructor.isBuffer(obj)
}

// For Node v0.10 support. Remove this eventually.
function isSlowBuffer (obj) {
  return typeof obj.readFloatLE === 'function' && typeof obj.slice === 'function' && isBuffer(obj.slice(0, 0))
}


/***/ }),
/* 38 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";

// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __generator = (this && this.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (_) try {
            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [op[0] & 2, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
Object.defineProperty(exports, "__esModule", { value: true });
var base_1 = __webpack_require__(2);
var ndarray_1 = __webpack_require__(9);
// This is OK, as long as we only use it for type declarations
var common_1 = __webpack_require__(15);
/**
 * Deserializes union JSON to an ndarray or a NDArrayModel, as appropriate.
 */
function JSONToUnion(obj, manager) {
    return __awaiter(this, void 0, void 0, function () {
        var modelPromise;
        return __generator(this, function (_a) {
            if (typeof obj === 'string') {
                modelPromise = base_1.unpack_models(obj, manager);
                return [2 /*return*/, modelPromise];
            }
            else {
                return [2 /*return*/, Promise.resolve(ndarray_1.JSONToArray(obj, manager))];
            }
            return [2 /*return*/];
        });
    });
}
exports.JSONToUnion = JSONToUnion;
/**
 * Deserializes union JSON to an ndarray, regardless of whether it is a widget reference or direct data.
 */
function JSONToUnionArray(obj, manager) {
    return __awaiter(this, void 0, void 0, function () {
        var _a;
        return __generator(this, function (_b) {
            switch (_b.label) {
                case 0:
                    _a = common_1.getArray;
                    return [4 /*yield*/, JSONToUnion(obj, manager)];
                case 1: return [2 /*return*/, _a.apply(void 0, [_b.sent()])];
            }
        });
    });
}
exports.JSONToUnionArray = JSONToUnionArray;
/**
 * Serializes a union to JSON.
 */
function unionToJSON(obj, widget) {
    if (obj instanceof base_1.WidgetModel) {
        return obj.toJSON(undefined);
    }
    else {
        return ndarray_1.arrayToJSON(obj, widget);
    }
}
exports.unionToJSON = unionToJSON;
/**
 * Sets up backbone events for listening to union changes.
 *
 * The callback will be called when:
 *  - The model is a widget, and its data changes
 *
 * Specify `allChanges` as truthy to also cover these cases:
 *  - The union changes from a widget to an array or vice-versa
 *  - The union is an array and its content changes
 *
 * To stop listening, call the return value.
 */
function listenToUnion(model, unionName, callback, allChanges) {
    function listenToWidgetChanges(union) {
        if (union instanceof base_1.WidgetModel) {
            // listen to changes in current model
            model.listenTo(union, 'change', callback);
        }
    }
    function onUnionChange(unionModel, value, subOptions) {
        var prev = model.previous(unionName) || [];
        var curr = value || [];
        if (prev instanceof base_1.WidgetModel) {
            model.stopListening(prev);
        }
        else if (allChanges && !(curr instanceof base_1.WidgetModel)) {
            // The union was an array, and has changed to a new array
            callback(unionModel, subOptions);
        }
        if (allChanges && (prev instanceof base_1.WidgetModel) !== (curr instanceof base_1.WidgetModel)) {
            // Union type has changed, call out
            callback(unionModel, subOptions);
        }
        listenToWidgetChanges(curr);
    }
    listenToWidgetChanges(model.get(unionName));
    // make sure to (un)hook listeners when property changes
    model.on('change:' + unionName, onUnionChange);
    function stopListening() {
        var curr = model.get(unionName);
        if (curr instanceof base_1.WidgetModel) {
            model.stopListening(curr);
        }
        model.off('change:' + unionName, onUnionChange);
    }
    return stopListening;
}
exports.listenToUnion = listenToUnion;
exports.data_union_array_serialization = { deserialize: JSONToUnionArray, serialize: unionToJSON };
exports.data_union_serialization = { deserialize: JSONToUnion, serialize: unionToJSON };
/**
 * Deserializes union JSON to an ndarray, regardless of whether it is a widget reference or direct data.
 */
function JSONToUnionTypedArray(obj, manager) {
    if (typeof obj === 'string') {
        var modelPromise = base_1.unpack_models(obj, manager);
        return modelPromise.then(function (model) {
            var array = model.getNDArray();
            if (array === null) {
                return array;
            }
            return array.data;
        });
    }
    else {
        return Promise.resolve(ndarray_1.JSONToTypedArray(obj, manager));
    }
}
exports.JSONToUnionTypedArray = JSONToUnionTypedArray;
/**
 * Serializes a union to JSON.
 */
function unionTypedArrayToJSON(obj, widget) {
    if (obj instanceof base_1.WidgetModel) {
        return obj.toJSON(undefined);
    }
    else {
        return ndarray_1.typedArrayToJSON(obj, widget);
    }
}
exports.unionTypedArrayToJSON = unionTypedArrayToJSON;
exports.data_union_typedarray_serialization = {
    deserialize: JSONToUnionTypedArray,
    serialize: unionTypedArrayToJSON
};
/**
 * Deserializes union JSON to an ndarray, regardless of whether it is a widget reference or direct data.
 */
function JSONToSimpleUnion(obj, manager) {
    if (typeof obj === 'string') {
        var modelPromise = base_1.unpack_models(obj, manager);
        return modelPromise.then(function (model) {
            var array = model.getNDArray();
            if (array === null) {
                return null;
            }
            return { array: array.data, shape: array.shape };
        });
    }
    else {
        return Promise.resolve(ndarray_1.JSONToSimple(obj, manager));
    }
}
exports.JSONToSimpleUnion = JSONToSimpleUnion;
/**
 * Serializes a union to JSON.
 */
function simpleUnionToJSON(obj, widget) {
    if (obj instanceof base_1.WidgetModel) {
        return obj.toJSON(undefined);
    }
    else {
        return ndarray_1.simpleToJSON(obj, widget);
    }
}
exports.simpleUnionToJSON = simpleUnionToJSON;
exports.data_union_simple_serialization = {
    deserialize: JSONToSimpleUnion,
    serialize: simpleUnionToJSON
};
//# sourceMappingURL=union.js.map

/***/ }),
/* 39 */
/***/ (function(module, exports) {

module.exports = {"_from":"jupyter-dataserializers","_id":"jupyter-dataserializers@1.3.0","_inBundle":false,"_integrity":"sha512-ARHgrB/rZ8CX1mJoOHtOJg8CtPuyZbCm9rK8sPA9JU65x80G1HhOSiL9z1h1zXjFr2sQuJk4RN01cc7odTLRZw==","_location":"/jupyter-dataserializers","_phantomChildren":{},"_requested":{"type":"tag","registry":true,"raw":"jupyter-dataserializers","name":"jupyter-dataserializers","escapedName":"jupyter-dataserializers","rawSpec":"","saveSpec":null,"fetchSpec":"latest"},"_requiredBy":["#USER","/"],"_resolved":"https://registry.npmjs.org/jupyter-dataserializers/-/jupyter-dataserializers-1.3.0.tgz","_shasum":"f596effc826c38a34e19da44b75997e4d28c81c2","_spec":"jupyter-dataserializers","_where":"/home/john/projects/jupyter/git/pygeojs/js","author":{"name":"Vidar T. Fauske"},"bundleDependencies":false,"dependencies":{"@jupyter-widgets/base":"^1.1.8","ndarray":"^1.0.18","pako":"^1.0.6"},"deprecated":false,"description":"A set of widget utilities for array serialization","devDependencies":{"@types/expect.js":"^0.3.29","@types/mocha":"^2.2.48","@types/ndarray":"^1.0.5","@types/node":"^9.4.6","@types/pako":"^1.0.0","@types/webpack-env":"^1.13.6","expect.js":"^0.3.1","karma":"^2.0.0","karma-chrome-launcher":"^2.2.0","karma-firefox-launcher":"^1.1.0","karma-ie-launcher":"^1.0.0","karma-mocha":"^1.3.0","karma-mocha-reporter":"^2.2.5","karma-typescript":"^3.0.12","mocha":"^5.0.1","typescript":"~3.1.3"},"keywords":["jupyter","widgets"],"license":"BSD-3-Clause","main":"lib/index.js","name":"jupyter-dataserializers","scripts":{"build":"tsc --build","clean":"rimraf lib","test":"npm run test:firefox","test:chrome":"karma start --browsers=Chrome tests/karma.conf.js","test:debug":"karma start --browsers=Chrome --singleRun=false --debug=true tests/karma.conf.js","test:firefox":"karma start --browsers=Firefox tests/karma.conf.js","test:ie":"karma start --browsers=IE tests/karma.conf.js","watch":"tsc --build --watch"},"types":"./lib/index.d.ts","version":"1.3.0"}

/***/ }),
/* 40 */
/***/ (function(module, exports, __webpack_require__) {

//
// This file auto-generated with generate-wrappers.js
//

var _ = __webpack_require__(0);
var GeoJS = __webpack_require__(1);
var widgets = __webpack_require__(2);
var dataserializers = __webpack_require__(3);

var layerModel = __webpack_require__(17).layerModel;


var featureLayerModel = layerModel.extend({

    defaults: function() {
        return _.extend(layerModel.prototype.defaults.call(this), {


        });
    },

    createPropertiesArrays: function() {

        layerModel.prototype.createPropertiesArrays.call(this);

        this.props_created_by_geojs['attribution'] = true;
        this.props_created_by_geojs['zIndex'] = true;



    },

}, {

    model_name: 'featureLayerModel',

    serializers: _.extend({
    },  layerModel.serializers),
});

module.exports = {
    featureLayerModel: featureLayerModel,
};


/***/ }),
/* 41 */
/***/ (function(module, exports, __webpack_require__) {

var _ = __webpack_require__(0);
const geojs = __webpack_require__(1);
console.debug(`Using geojs ${geojs.version}`);
var widgets = __webpack_require__(2);
var dataserializers = __webpack_require__(3);

var sceneObjectModel = __webpack_require__(5).sceneObjectModel;

var sceneModel = widgets.DOMWidgetModel.extend({

    initialize: function() {
        console.log('initialize sceneModel')
        sceneModel.__super__.initialize.apply(this, arguments);
        this.obj = null;
        this.createPropertiesArrays();
        this.constructGeoJSObject();
        this.syncToGeoJSObj(true);
        sceneObjectModel.prototype.setupListeners.call(this);
    },

    defaults: function() {
        return _.extend(sceneObjectModel.prototype.defaults.call(this), {
            _model_name : 'sceneModel',
            _view_name : 'sceneView',
            _model_module : 'pygeojs',
            _view_module : 'pygeojs',
            _model_module_version : '0.1.0',
            _view_module_version : '0.1.0',

            allowRotation: true,
            autoResize: true,
            // bounds
            // camera
            center: {x: 0.0, y: 0.0},
            clampBoundsX: false,
            clampBoundsY: true,
            clampZoom: true,
            discreteZoom: false,
            gcs: "EPSG:3857",
            ingcs: "EPSG:4326",
            // interactor
            max: 16,
            maxBounds: {},
            min: 0,
            // origin
            // rotatedSize
            rotation: 0,
            unitsPerPixel: 156543,
            zoom: 4,
            // zoomRange

        });
    },

    constructGeoJSObject: function() {
        console.log('scene.constructGeoJSObject()');
        // console.dir(this);
        //console.assert(this.el, 'Error: this.el not defined');
        let doc = new Document;
        this.mapElement = doc.createElement('div');
        this.mapElement.setAttribute('class', 'geojs-doc-element');
        this.mapElement.setAttribute('style', 'height: 100%; width: 100%');
        this.obj = geojs.map({node: this.mapElement});
        // console.log('map object:');
        // console.dir(this.obj);

        // Sync kernel on user pan
        this.obj.geoOn(geojs.event.pan, function(evnt) {
            const geoCenter = this.obj.center();
            // console.log(`geoCenter ${geoCenter.x}, ${geoCenter.y}`);
            const modelCenter = this.get('center');
            // console.log(`modelCenter ${modelCenter.x}, ${modelCenter.y}`)

            // Check that center hasn't changed "alot".
            // This averts a lockup condition that can occur when using
            // the mouse wheel to zoom. The cause is not fully understood,
            // but is related to numerical precision of the center coords.
            if ((geoCenter.x.toFixed(6) === modelCenter.x.toFixed(6)) &&
                (geoCenter.y.toFixed(6) === modelCenter.y.toFixed(6))) {
                return;  // close enough
            }
            this.set({center: geoCenter});
            this.save_changes();
        }.bind(this));

        // Sync kernel when user zooms map
        this.obj.geoOn(geojs.event.zoom, function(evnt) {
            // console.log(`Zoom event`);
            // console.dir(evnt);
            this.set('zoom', evnt.zoomLevel);
            this.save_changes();
        }.bind(this));

    },

    createPropertiesArrays: function() {
        console.log('Enter createPropertiesArrays()');

        sceneObjectModel.prototype.createPropertiesArrays.call(this);


        this.property_converters['allowRotation'] = 'convertBool';
        this.property_converters['autoResize'] = 'convertBool';
        this.property_converters['center'] = null;
        this.property_converters['clampBoundsX'] = 'convertBool';
        this.property_converters['clampBoundsY'] = 'convertBool';
        this.property_converters['clampZoom'] = 'convertBool';
        this.property_converters['discreteZoom'] = 'convertBool';
        this.property_converters['gcs'] = null;
        this.property_converters['ingcs'] = null;
        //this.property_converters['max'] = null;
        this.property_converters['maxBounds'] = null;
        //this.property_converters['min'] = null;
        this.property_converters['rotation'] = 'convertFloat';
        this.property_converters['unitsPerPixel'] = null;
        this.property_converters['zoom'] = 'convertFloat';

        // this.property_assigners['center'] = 'assignDict';
        // this.property_assigners['maxBounds'] = 'assignDict';
    },


    onChange: function(model, options) {
        // Next line dont work, and I wish I knew why...
        // model.prototype.updateGeoJSObj(false).bind(model);
        this.updateGeoJSObj(model, false);
    },

    onChildChanged: function(model, options) {
        console.debug('child changed: ' + model.model_id);
        // Let listeners (e.g. views) know:
        this.trigger('childchange', this);
    },

    // push data from model to geojs object
    syncToGeoJSObj: function(force) {
        this.updateGeoJSObj(this, force);
    },


    // Push changes in model to geojs object
    updateGeoJSObj: function(model, force) {
        // console.debug('onChange(), model:, options:');
        // console.log(model);
        // console.log(options);

        _.each(model.property_converters, function(converterName, propName) {
            //console.log(`converterName ${converterName}, propName ${propName}`)
            if (!model.hasChanged(propName)) {
                // Only set changed properties unless forced
                return;
            }
            console.debug(`propName ${propName} has changed`);

            // console.dir(this);
            var assigner = model[model.property_assigners[propName]] || sceneObjectModel.prototype.assignDirect;
            assigner = assigner.bind(model);
            if (!converterName) {
                assigner(this.obj, propName, model.get(propName));
                return;
            }

            converterName = converterName + 'ModelToThree';
            var converterFn = sceneObjectModel.prototype[converterName];
            if (!converterFn) {
                throw new Error('invalid converter name: ' + converterName);
            }

            assigner(model.obj, propName, converterFn.bind(sceneObjectModel)(model.get(propName), propName));
        }, model);

    },

});
// , {

//     model_name: 'mapModel',

//     serializers: _.extend({
//     },  sceneObjectModel.serializers),
// });

var sceneView = widgets.DOMWidgetView.extend({
    initialize: function(parameters) {
        console.log('initialize mapView:');
        // console.dir(parameters);
        // console.dir(arguments);
        sceneView.__super__.initialize.apply(this, arguments);

        // console.log('this:')
        // console.dir(this);

        this.obj = this.model.obj;
    },  // initialize()

    // Capture resize message in order to update map size
    processPhosphorMessage: function(msg) {
        // console.log('Message:');
        // console.dir(msg);
        sceneView.__super__.processPhosphorMessage.apply(this, arguments);
        if (this.el && this.obj && (msg.type == 'resize')) {
            console.debug(`resize`);
            // console.log('this.el:');
            // console.dir(this.el);
            this.obj.size({
                width: this.el.clientWidth,
                height: this.el.clientHeight
            });

            // Adjust for offset due to mapElement having relative position
            //this.model.mapElement.style.marginTop = this.model.mapElement.clientHeight;
            this.model.mapElement.setAttribute('style',
                `height: 100%; width: 100%; margin-top: ${this.model.mapElement.clientHeight}`);
            console.log(`mapElement (type ${typeof this.model.mapElement}):`);
            // console.dir(this.model.mapElement);

        }
    },  // processPhosphorMessage()

    render: function() {
        console.log('render sceneView');
        sceneView.__super__.render.apply(this, arguments);
        if (!this.obj) {
            console.assert('Scene not initialized');
            return;
        }

        console.assert(this.el, 'ERROR: this.el not defined');
        //this.el.textContent = 'Sure wish I could get this to work...';
        // console.log('mapElement:');
        // console.dir(this.model.mapElement);
        this.el.appendChild(this.model.mapElement);
        // console.log('this.el');
        // console.dir(this.el);

        this.obj.draw();
    },  // render()

});  // sceneView


module.exports = {
    sceneModel: sceneModel,
    sceneView: sceneView,
};


/***/ }),
/* 42 */
/***/ (function(module, exports, __webpack_require__) {

var _ = __webpack_require__(0);
var GeoJS = __webpack_require__(1);

var autogenosmLayerModel = __webpack_require__(43).osmLayerModel;


var osmLayerModel = autogenosmLayerModel.extend({

    defaults: function() {
        return _.extend(autogenosmLayerModel.prototype.defaults.call(this), {
            _model_name : 'osmLayerModel',
            _model_module : 'pygeojs',
            _model_module_version : '0.1.0',
        });
    },

    constructGeoJSObjectAsync: function() {
        console.log('osmLayer.constructGeoJSObjectAsync()');
        let map_model_id = this.get('map_id');
        console.log(`map_model_id: ${map_model_id}`);

        // Build dictionary of constructor-only args (tileLayer)
        let args = {};
        let argNames = this.getConstructorArgNames();
        for (let argName of argNames) {
            let argValue = this.get(argName);
            console.assert(argValue !== undefined, `${argName} not defined`);
            if (argValue != null) { // null ==> default
                args[argName] = argValue;
            }
        }

        return new Promise(resolve => {
            this.widget_manager.get_model(map_model_id)
                .then((map_model => {
                    // console.log(`map_model:`);
                    // console.dir(map_model);

                    this.obj = map_model.obj.createLayer('osm', args);
                    // console.log(`osmLayer.obj:`);
                    // console.dir(this.obj);

                    // console.log(`map layers:`);
                    // console.dir(map_model.obj.layers());

                    resolve(this.obj);
                }));
        });
    }  // buildGeoJSObject()

}, {
    model_name: 'osmLayerModel',

    serializers: _.extend({
    },  autogenosmLayerModel.serializers),

});

module.exports = {
    osmLayerModel: osmLayerModel,
};


/***/ }),
/* 43 */
/***/ (function(module, exports, __webpack_require__) {

//
// This file auto-generated with generate-wrappers.js
//

var _ = __webpack_require__(0);
var GeoJS = __webpack_require__(1);
var widgets = __webpack_require__(2);
var dataserializers = __webpack_require__(3);

var tileLayerModel = __webpack_require__(44).tileLayerModel;


var osmLayerModel = tileLayerModel.extend({

    defaults: function() {
        return _.extend(tileLayerModel.prototype.defaults.call(this), {

            mapOpacity: null,

        });
    },

    createPropertiesArrays: function() {

        tileLayerModel.prototype.createPropertiesArrays.call(this);

        this.props_created_by_geojs['mapOpacity'] = true;
        this.props_created_by_geojs['attribution'] = true;
        this.props_created_by_geojs['zIndex'] = true;

        this.property_converters['mapOpacity'] = 'convertFloat';


    },

}, {

    model_name: 'osmLayerModel',

    serializers: _.extend({
    },  tileLayerModel.serializers),
});

module.exports = {
    osmLayerModel: osmLayerModel,
};


/***/ }),
/* 44 */
/***/ (function(module, exports, __webpack_require__) {

var _ = __webpack_require__(0);
var GeoJS = __webpack_require__(1);

var autogen_tileLayerModel = __webpack_require__(18).tileLayerModel;


var tileLayerModel = autogen_tileLayerModel.extend({
    initialize: function() {
        tileLayerModel.__super__.initialize.apply(this, arguments);
    },

    // Returns list of immutable constructor argument names (hard-coded)
    getConstructorArgNames: function() {
        let argNames = [
            'animationDuration', 'baseUrl', 'cacheSize', 'imgFormat', 'keepLower',
            'maxLevel', 'minLevel', 'tileHeight', 'tileWidth', 'topDown', 'url',
            'wrapX', 'wrapY',
        ];
        return argNames.concat(tileLayerModel.__super__.getConstructorArgNames());
    },

    defaults: function() {
        return _.extend(autogen_tileLayerModel.prototype.defaults.call(this), {
        });
    },

});

module.exports = {
    tileLayerModel: tileLayerModel,
};


/***/ }),
/* 45 */
/***/ (function(module, exports, __webpack_require__) {

//
// This file auto-generated with generate-wrappers.js
//

var _ = __webpack_require__(0);
var GeoJS = __webpack_require__(1);
var widgets = __webpack_require__(2);
var dataserializers = __webpack_require__(3);

var featureModel = __webpack_require__(8).featureModel;


var pointFeatureModel = featureModel.extend({

    defaults: function() {
        return _.extend(featureModel.prototype.defaults.call(this), {

            dynamicDraw: false,
            position: [],
            primitiveShape: "sprite",
            style: {},

        });
    },

    createPropertiesArrays: function() {

        featureModel.prototype.createPropertiesArrays.call(this);

        this.props_created_by_geojs['bin'] = true;
        this.props_created_by_geojs['gcs'] = true;

        this.property_converters['dynamicDraw'] = 'convertBool';
        this.property_converters['position'] = null;
        this.property_converters['primitiveShape'] = null;
        this.property_converters['style'] = null;

        this.property_assigners['position'] = 'assignArray';
        this.property_assigners['style'] = 'assignDict';

    },

}, {

    model_name: 'pointFeatureModel',

    serializers: _.extend({
    },  featureModel.serializers),
});

module.exports = {
    pointFeatureModel: pointFeatureModel,
};


/***/ })
/******/ ])});;
//# sourceMappingURL=index.js.map