From 54c8680e37ead365369bcad4c43e7116b99173ad Mon Sep 17 00:00:00 2001 From: David Manthey <david.manthey@kitware.com> Date: Tue, 26 Jun 2018 11:41:45 -0400 Subject: [PATCH] Remove bower support. This resolves issue #850. --- .gitignore | 1 - .npmignore | 1 - bower.json | 29 - geo.js | 66946 --------------------------------------------------- 4 files changed, 66977 deletions(-) delete mode 100644 bower.json delete mode 100644 geo.js diff --git a/.gitignore b/.gitignore index 517cafe493..b840ed0c1f 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,6 @@ .DS_Store .*.swp /node_modules/* -/bower_components/* .vimrc /geo.min.js *.py[ocd] diff --git a/.npmignore b/.npmignore index a263b5faa9..573411fbd7 100644 --- a/.npmignore +++ b/.npmignore @@ -7,7 +7,6 @@ scripts/ built/ *build*/ .git/ -bower_components/ dist/ .eslintcache tutorials/ diff --git a/bower.json b/bower.json deleted file mode 100644 index e3276b0622..0000000000 --- a/bower.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "name": "geojs", - "version": "0.16.0", - "description": "JavaScript Geo visualization and Analysis Library", - "homepage": "https://github.com/OpenGeoscience/geojs", - "main": "geo.js", - "bugs": { - "url": "https://github.com/OpenGeoscience/geojs/issues" - }, - "repository": { - "type": "git", - "url": "https://github.com/OpenGeoscience/geojs" - }, - "dependencies": { - "d3": "^3.5.16" - }, - "ignore": [ - ".git", - ".gitignore", - ".travis.yml", - ".eslintcache", - "dist", - "test", - "cmake", - "testing", - "scripts", - "dashboard" - ] -} diff --git a/geo.js b/geo.js deleted file mode 100644 index 864b482a58..0000000000 --- a/geo.js +++ /dev/null @@ -1,66946 +0,0 @@ -(function webpackUniversalModuleDefinition(root, factory) { - if(typeof exports === 'object' && typeof module === 'object') - module.exports = factory((function webpackLoadOptionalExternalModule() { try { return require("hammerjs"); } catch(e) {} }()), (function webpackLoadOptionalExternalModule() { try { return require("d3"); } 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"]); -})(this, function(__WEBPACK_EXTERNAL_MODULE_224__, __WEBPACK_EXTERNAL_MODULE_230__) { -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] = { -/******/ exports: {}, -/******/ id: moduleId, -/******/ loaded: false -/******/ }; - -/******/ // Execute the module function -/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); - -/******/ // Flag the module as loaded -/******/ module.loaded = 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; - -/******/ // __webpack_public_path__ -/******/ __webpack_require__.p = "dist/built"; - -/******/ // Load entry module and return exports -/******/ return __webpack_require__(0); -/******/ }) -/************************************************************************/ -/******/ ([ -/* 0 */ -/***/ (function(module, exports, __webpack_require__) { - - /** @namespace geo */ - // License headers that will be preserved in distributed bundles. - /** - * GeoJS - * @copyright 2013-2017, Kitware, Inc. - * @license Apache-2.0 - * - * 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__(1); - __webpack_require__(2); - - __webpack_require__(3); - - module.exports = $.extend({ - annotation: __webpack_require__(7), - annotationLayer: __webpack_require__(219), - camera: __webpack_require__(211), - choroplethFeature: __webpack_require__(225), - contourFeature: __webpack_require__(231), - domRenderer: __webpack_require__(232), - event: __webpack_require__(9), - feature: __webpack_require__(207), - featureLayer: __webpack_require__(220), - fetchQueue: __webpack_require__(233), - fileReader: __webpack_require__(234), - geo_action: __webpack_require__(10), - graphFeature: __webpack_require__(235), - heatmapFeature: __webpack_require__(236), - imageTile: __webpack_require__(237), - jsonReader: __webpack_require__(239), - layer: __webpack_require__(210), - lineFeature: __webpack_require__(206), - map: __webpack_require__(240), - mapInteractor: __webpack_require__(222), - object: __webpack_require__(203), - osmLayer: __webpack_require__(242), - pathFeature: __webpack_require__(245), - pointFeature: __webpack_require__(212), - polygonFeature: __webpack_require__(217), - quadFeature: __webpack_require__(223), - pixelmapFeature: __webpack_require__(246), - renderer: __webpack_require__(202), - sceneObject: __webpack_require__(208), - textFeature: __webpack_require__(218), - tile: __webpack_require__(238), - tileCache: __webpack_require__(244), - tileLayer: __webpack_require__(243), - timestamp: __webpack_require__(209), - transform: __webpack_require__(11), - typedef: __webpack_require__(247), - vectorFeature: __webpack_require__(248), - inherit: __webpack_require__(8), - version: __webpack_require__(249), - sha: __webpack_require__(250), - - util: __webpack_require__(83), - jQuery: $, - d3: __webpack_require__(251), - gl: __webpack_require__(259), - canvas: __webpack_require__(269), - gui: __webpack_require__(297) - }, __webpack_require__(201)); - - if (window && !window.$) { - window.$ = $; - } - if (window && !window.jQuery) { - window.jQuery = $; - } - - -/***/ }), -/* 1 */ -/***/ (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; - } ); - - -/***/ }), -/* 2 */ -/***/ (function(module, exports) { - - // Add a polyfill for window.requestAnimationFrame. - if (!window.requestAnimationFrame) { - var _animationFrameFunc = []; - if (!window.performance || !window.performance.now) { - window.performance = {now: function () { return new Date().getTime(); }}; - } - window.requestAnimationFrame = function (func) { - 'use strict'; - - if (!_animationFrameFunc.length) { - var time = window.performance.now(); - window.setTimeout(function () { - var funcs = _animationFrameFunc; - _animationFrameFunc = []; - var curtime = window.performance.now(); - for (var i = 0; i < funcs.length; i += 1) { - funcs[i].call(window, curtime); - } - }, 15 - (parseInt(time, 10) % 15)); - } - _animationFrameFunc.push(func); - }; - } - - // Add a polyfill for Math.log2 - if (!Math.log2) { - Math.log2 = function () { - return Math.log.apply(Math, arguments) / Math.LN2; - }; - } - - // Add a polyfill for Math.sinh - Math.sinh = Math.sinh || function (x) { - var y = Math.exp(x); - return (y - 1 / y) / 2; - }; - - -/***/ }), -/* 3 */ -/***/ (function(module, exports, __webpack_require__) { - - // style-loader: Adds some css to the DOM by adding a <style> tag - - // load the styles - var content = __webpack_require__(4); - if(typeof content === 'string') content = [[module.id, content, '']]; - // add the styles to the DOM - var update = __webpack_require__(6)(content, {}); - if(content.locals) module.exports = content.locals; - // Hot Module Replacement - if(false) { - // When the styles change, update the <style> tags - if(!content.locals) { - 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, '']]; - update(newContent); - }); - } - // When the module is disposed, remove the <style> tags - module.hot.dispose(function() { update(); }); - } - -/***/ }), -/* 4 */ -/***/ (function(module, exports, __webpack_require__) { - - exports = module.exports = __webpack_require__(5)(); - // imports - - - // module - exports.push([module.id, ".geojs-map{position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.geojs-map .geo-attribution{position:absolute;right:0;bottom:0;padding-right:5px;cursor:auto;font:11px/1.5 Helvetica Neue,Arial,Helvetica,sans-serif;z-index:1001;background:hsla(0,0%,100%,.7);clear:both;display:block;pointer-events:auto}.geojs-map .geo-attribution .geo-attribution-layer{padding-left:5px}.geojs-map .canvas-canvas{display:block;-webkit-transform-origin:0 0;transform-origin:0 0}.geojs-map .webgl-canvas{display:block}.geojs-map .geojs-layer{position:absolute;width:100%;height:100%;pointer-events:none}.geojs-map .geojs-layer.active>*{pointer-events:auto}.geojs-map .geo-tile-layer{-webkit-transform-origin:0 0;transform-origin:0 0;line-height:0;font-size:0}.geojs-map.annotation-input{cursor:crosshair}.geojs-map.highlight-focus:after{content:\"\";display:block;position:absolute;box-sizing:border-box;left:0;top:0;right:0;bottom:0;border:3px solid Highlight;opacity:1;-ms-filter:none;filter:none;transition:opacity 0s;visibility:hidden}.geojs-map.highlight-focus:focus:after{visibility:visible;transition:opacity 2.5s ease-in;opacity:0;-ms-filter:\"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)\";filter:alpha(opacity=0)}.geo-tile-container{position:absolute}.geo-tile-container.crop{overflow:hidden}", ""]); - - // exports - - -/***/ }), -/* 5 */ -/***/ (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() { - var list = []; - - // return the list of modules as css string - list.toString = function toString() { - var result = []; - for(var i = 0; i < this.length; i++) { - var item = this[i]; - if(item[2]) { - result.push("@media " + item[2] + "{" + item[1] + "}"); - } else { - result.push(item[1]); - } - } - return result.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; - }; - - -/***/ }), -/* 6 */ -/***/ (function(module, exports, __webpack_require__) { - - /* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra - */ - var stylesInDom = {}, - memoize = function(fn) { - var memo; - return function () { - if (typeof memo === "undefined") memo = fn.apply(this, arguments); - return memo; - }; - }, - isOldIE = memoize(function() { - return /msie [6-9]\b/.test(self.navigator.userAgent.toLowerCase()); - }), - getHeadElement = memoize(function () { - return document.head || document.getElementsByTagName("head")[0]; - }), - singletonElement = null, - singletonCounter = 0, - styleElementsInsertedAtTop = []; - - module.exports = function(list, options) { - if(false) { - if(typeof document !== "object") throw new Error("The style-loader cannot be used in a non-browser environment"); - } - - options = options || {}; - // Force single-tag solution on IE6-9, which has a hard limit on the # of <style> - // tags it will allow on a page - if (typeof options.singleton === "undefined") options.singleton = isOldIE(); - - // By default, add <style> tags to the bottom of <head>. - if (typeof options.insertAt === "undefined") options.insertAt = "bottom"; - - var styles = listToStyles(list); - 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); - 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) { - var styles = []; - var newStyles = {}; - for(var i = 0; i < list.length; i++) { - var item = list[i]; - var id = 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, styleElement) { - var head = getHeadElement(); - var lastStyleElementInsertedAtTop = styleElementsInsertedAtTop[styleElementsInsertedAtTop.length - 1]; - if (options.insertAt === "top") { - if(!lastStyleElementInsertedAtTop) { - head.insertBefore(styleElement, head.firstChild); - } else if(lastStyleElementInsertedAtTop.nextSibling) { - head.insertBefore(styleElement, lastStyleElementInsertedAtTop.nextSibling); - } else { - head.appendChild(styleElement); - } - styleElementsInsertedAtTop.push(styleElement); - } else if (options.insertAt === "bottom") { - head.appendChild(styleElement); - } else { - throw new Error("Invalid value for parameter 'insertAt'. Must be 'top' or 'bottom'."); - } - } - - function removeStyleElement(styleElement) { - styleElement.parentNode.removeChild(styleElement); - var idx = styleElementsInsertedAtTop.indexOf(styleElement); - if(idx >= 0) { - styleElementsInsertedAtTop.splice(idx, 1); - } - } - - function createStyleElement(options) { - var styleElement = document.createElement("style"); - styleElement.type = "text/css"; - insertStyleElement(options, styleElement); - return styleElement; - } - - function createLinkElement(options) { - var linkElement = document.createElement("link"); - linkElement.rel = "stylesheet"; - insertStyleElement(options, linkElement); - return linkElement; - } - - function addStyle(obj, options) { - var styleElement, update, remove; - - if (options.singleton) { - var styleIndex = singletonCounter++; - styleElement = singletonElement || (singletonElement = createStyleElement(options)); - update = applyToSingletonTag.bind(null, styleElement, styleIndex, false); - remove = applyToSingletonTag.bind(null, styleElement, styleIndex, true); - } else if(obj.sourceMap && - typeof URL === "function" && - typeof URL.createObjectURL === "function" && - typeof URL.revokeObjectURL === "function" && - typeof Blob === "function" && - typeof btoa === "function") { - styleElement = createLinkElement(options); - update = updateLink.bind(null, styleElement); - remove = function() { - removeStyleElement(styleElement); - if(styleElement.href) - URL.revokeObjectURL(styleElement.href); - }; - } else { - styleElement = createStyleElement(options); - update = applyToTag.bind(null, styleElement); - remove = function() { - removeStyleElement(styleElement); - }; - } - - 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(styleElement, index, remove, obj) { - var css = remove ? "" : obj.css; - - if (styleElement.styleSheet) { - styleElement.styleSheet.cssText = replaceText(index, css); - } else { - var cssNode = document.createTextNode(css); - var childNodes = styleElement.childNodes; - if (childNodes[index]) styleElement.removeChild(childNodes[index]); - if (childNodes.length) { - styleElement.insertBefore(cssNode, childNodes[index]); - } else { - styleElement.appendChild(cssNode); - } - } - } - - function applyToTag(styleElement, obj) { - var css = obj.css; - var media = obj.media; - - if(media) { - styleElement.setAttribute("media", media) - } - - if(styleElement.styleSheet) { - styleElement.styleSheet.cssText = css; - } else { - while(styleElement.firstChild) { - styleElement.removeChild(styleElement.firstChild); - } - styleElement.appendChild(document.createTextNode(css)); - } - } - - function updateLink(linkElement, obj) { - var css = obj.css; - var sourceMap = obj.sourceMap; - - 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 = linkElement.href; - - linkElement.href = URL.createObjectURL(blob); - - if(oldSrc) - URL.revokeObjectURL(oldSrc); - } - - -/***/ }), -/* 7 */ -/***/ (function(module, exports, __webpack_require__) { - - var $ = __webpack_require__(1); - var inherit = __webpack_require__(8); - var geo_event = __webpack_require__(9); - var geo_action = __webpack_require__(10); - var transform = __webpack_require__(11); - var util = __webpack_require__(83); - var registerAnnotation = __webpack_require__(201).registerAnnotation; - var lineFeature = __webpack_require__(206); - var pointFeature = __webpack_require__(212); - var polygonFeature = __webpack_require__(217); - var textFeature = __webpack_require__(218); - - var annotationId = 0; - - var annotationState = { - create: 'create', - done: 'done', - highlight: 'highlight', - edit: 'edit' - }; - - var annotationActionOwner = 'annotationAction'; - - var defaultEditHandleStyle = { - fill: true, - fillColor: function (d) { - return d.selected ? {r: 0, g: 1, b: 1} : {r: 0.3, g: 0.3, b: 0.3}; - }, - fillOpacity: function (d) { - return d.selected ? 0.5 : 0.25; - }, - radius: function (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 (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; - - /** - * Base annotation class. - * - * @class - * @alias geo.annotation - * @param {string} type The type of annotation. These should be registered - * with utils.registerAnnotation and can be listed with same function. - * @param {object?} [args] Individual annotations have additional options. - * @param {string} [args.name] A name for the annotation. This defaults to - * the type with a unique ID suffixed to it. - * @param {geo.annotationLayer} [arg.layer] A reference to the controlling - * layer. This is used for coordinate transforms. - * @param {string} [args.state] Initial annotation state. One of the - * `geo.annotation.state` values. - * @param {boolean|string[]} [args.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. - * @returns {geo.annotation} - */ - var annotation = function (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 `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 - * `geo.annotation.state` values. - * @returns {this|string} The current state or this annotation. - */ - 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} - */ - 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) - }; - 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. - */ - 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); - /* 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 { - 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(); - 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; - }; - - ['createStyle', 'editStyle', 'editHandleStyle', 'labelStyle', 'highlightStyle' - ].forEach(function (styleType) { - /** - * Set or get a specific style type. - * - * @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. - */ - 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 []; - }; - - /** - * 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 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 {object?} [args] Options for the annotation. - * @param {string} [args.name] A name for the annotation. This defaults to - * the type with a unique ID suffixed to it. - * @param {string} [args.state] initial annotation state. One of the - * annotation.state values. - * @param {boolean|string[]} [args.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. - * @param {geo.geoPosition[]} [args.corners] A list of four corners in map - * gcs coordinates. These must be in order around the perimeter of the - * rectangle (in either direction). - * @param {geo.geoPosition[]} [args.coordinates] An alternate name for - * `args.corners`. - * @param {object} [args.style] The style to apply to a finished rectangle. - * This uses styles for polygons, including `fill`, `fillColor`, - * `fillOpacity`, `stroke`, `strokeWidth`, `strokeColor`, and - * `strokeOpacity`. - * @param {object} [args.editStyle] The style to apply to a rectangle in edit - * mode. This uses styles for polygons and lines, including `fill`, - * `fillColor`, `fillOpacity`, `stroke`, `strokeWidth`, `strokeColor`, and - * `strokeOpacity`. - */ - var rectangleAnnotation = function (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 (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 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'); - }; - - /** - * 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 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 {object?} [args] Options for the annotation. - * @param {string} [args.name] A name for the annotation. This defaults to - * the type with a unique ID suffixed to it. - * @param {string} [args.state] initial annotation state. One of the - * annotation.state values. - * @param {boolean|string[]} [args.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. - * @param {geo.geoPosition[]} [args.vertices] A list of vertices in map gcs - * coordinates. These must be in order around the perimeter of the - * polygon (in either direction). - * @param {geo.geoPosition[]} [args.coordinates] An alternate name for - * `args.vertices`. - * @param {object} [args.style] The style to apply to a finished polygon. - * This uses styles for polygons, including `fill`, `fillColor`, - * `fillOpacity`, `stroke`, `strokeWidth`, `strokeColor`, and - * `strokeOpacity`. - * @param {object} [args.editStyle] The style to apply to a polygon in edit - * mode. This uses styles for polygons and lines, including `fill`, - * `fillColor`, `fillOpacity`, `stroke`, `strokeWidth`, `strokeColor`, and - * `strokeOpacity`. - */ - var polygonAnnotation = function (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 (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 (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 (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 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 class. - * - * @class - * @alias geo.lineAnnotation - * @extends geo.annotation - * - * @param {object?} [args] Options for the annotation. - * @param {string} [args.name] A name for the annotation. This defaults to - * the type with a unique ID suffixed to it. - * @param {string} [args.state] initial annotation state. One of the - * annotation.state values. - * @param {boolean|string[]} [args.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. - * @param {geo.geoPosition[]} [args.vertices] A list of vertices in map gcs - * coordinates. - * @param {geo.geoPosition[]} [args.coordinates] An alternate name for - * `args.corners`. - * @param {object} [args.style] The style to apply to a finished line. - * This uses styles for lines, including `strokeWidth`, `strokeColor`, - * `strokeOpacity`, `strokeOffset`, `closed`, `lineCap`, and `lineJoin`. - * @param {object} [args.editStyle] The style to apply to a line in edit - * mode. This uses styles for lines, including `strokeWidth`, - * `strokeColor`, `strokeOpacity`, `strokeOffset`, `closed`, `lineCap`, - * and `lineJoin`. - */ - var lineAnnotation = function (args) { - 'use strict'; - if (!(this instanceof lineAnnotation)) { - return new lineAnnotation(args); - } - - args = $.extend(true, {}, { - style: { - line: function (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 (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 (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 (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 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 class. - * - * @class - * @alias geo.poinyAnnotation - * @extends geo.annotation - * - * @param {object?} [args] Options for the annotation. - * @param {string} [args.name] A name for the annotation. This defaults to - * the type with a unique ID suffixed to it. - * @param {string} [args.state] initial annotation state. One of the - * annotation.state values. - * @param {boolean|string[]} [args.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. - * @param {geo.geoPosition} [args.position] A coordinate in map gcs - * coordinates. - * @param {geo.geoPosition[]} [args.coordinates] An array with one coordinate - * to use in place of `args.position`. - * @param {object} [args.style] The style to apply to a finished point. - * This uses styles for points, including `radius`, `fill`, `fillColor`, - * `fillOpacity`, `stroke`, `strokeWidth`, `strokeColor`, `strokeOpacity`, - * and `scaled`. If `scaled` is `false`, the point is not scaled with - * zoom level. If it is `true`, the radius is based on the zoom level at - * first instantiation. Otherwise, if it is a number, the radius is used - * at that zoom level. - * @param {object} [args.editStyle] The style to apply to a point in edit - * mode. - */ - var pointAnnotation = function (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 () { - 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 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')]; - }; - - /** - * 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 - }; - - -/***/ }), -/* 8 */ -/***/ (function(module, exports) { - - 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; - }; - - -/***/ }), -/* 9 */ -/***/ (function(module, exports) { - - /** - * 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.<br> - * <br> - * The following properties are common to all event objects: - * - * @namespace - * @alias geo.event - * @type {object} - * @property {string} event The event type that was triggered. - * @property {object} geo A universal event object for controlling propagation. - * - * @example - * map.geoOn(geo.event.layerAdd, function (event) { - * // event is an object with type: {@link geo.event.layerAdd} - * }); - * - */ - var geo_event = {}; - - /* - * Event types - */ - - /** - * Triggered when a layer is added to the map. - * - * @event geo.event.layerAdd - * @type {object} - * @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 {object} - * @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 {object} - * @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 {object} - * @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 {object} - * @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 {object} - * @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 {object} - * @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 {object} - * @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 - */ - 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 - * @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 - */ - geo_event.brush = 'geo_brush'; - - /** - * Triggered after a brush selection ends. - * The event object extends {@link geo.brushSelection}. - * - * @event geo.event.brushend - */ - geo_event.brushend = 'geo_brushend'; - - /** - * Triggered when a brush selection starts. - * The event object extends {@link geo.brushSelection}. - * - * @event geo.event.brushstart - */ - geo_event.brushstart = 'geo_brushstart'; - - /** - * Triggered when brushing results in a selection. - * The event object extends {@link geo.brushSelection}. - * - * @event geo.event.select - */ - geo_event.select = 'geo_select'; - - /** - * Triggered when brushing results in a zoom selection. - * The event object extends {@link geo.brushSelection}. - * - * @event geo.event.zoomselect - */ - 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 - */ - - geo_event.unzoomselect = 'geo_unzoomselect'; - - /** - * Triggered when an action is initiated with mouse down. - * - * @event geo.event.actiondown - * @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 - * @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 - * @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 - * @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 - * @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 - * @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 {object} - * @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 {object} - * @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 {object} - * @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 {object} - * @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}. - * @event geo.event.feature.mousemove - */ - mousemove: 'geo_feature_mousemove', - /** - * The event is the feature version of {@link geo.event.mouseover}. - * @event geo.event.feature.mouseover - */ - mouseover: 'geo_feature_mouseover', - /** - * The event contains the `feature`, the `mouse` record, the `previous` - * record of data elements that were under the mouse, and `over`, the new - * record of data elements that are unrder the mouse. - * @event geo.event.feature.mouseover_order - */ - mouseover_order: 'geo_feature_mouseover_order', - /** - * The event is the feature version of {@link geo.event.mouseout}. - * @event geo.event.feature.mouseout - */ - mouseout: 'geo_feature_mouseout', - /** - * The event is the feature version of {@link geo.event.mouseon}. - * @event geo.event.feature.mouseon - */ - mouseon: 'geo_feature_mouseon', - /** - * The event is the feature version of {@link geo.event.mouseoff}. - * @event geo.event.feature.mouseoff - */ - mouseoff: 'geo_feature_mouseoff', - /** - * The event is the feature version of {@link geo.event.mouseclick}. - * @event geo.event.feature.mouseclick - */ - 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 - */ - mouseclick_order: 'geo_feature_mouseclick_order', - /** - * The event is the feature version of {@link geo.event.brushend}. - * @event geo.event.feature.brushend - */ - brushend: 'geo_feature_brushend', - /** - * The event is the feature version of {@link geo.event.brush}. - * @event geo.event.feature.brush - */ - 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 {object} - * @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 - * @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 {object} - * @property {geo.camera} camera The camera instance. - */ - geo_event.camera.view = 'geo_camera_view'; - - /** - * Triggered after a projection change. - * - * @event geo.event.camera.projection - * @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 - * @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 {object} - * @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 {object} - * @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 {object} - * @property {geo.annotation} annotation The annotation that was altered. - */ - geo_event.annotation.update = 'geo_annotation_update'; - - /** - * Triggered when an annotation has been removed. - * - * @event geo.event.annotation.remove - * @type {object} - * @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 {object} - * @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 {object} - * @property {string?} mode The new annotation mode. This is one of the values - * from `geo.annotation.annotationState`. - * @property {string?} oldMode The annotation mode before this change. This is - * one of the values from `geo.annotation.annotationState`. - */ - geo_event.annotation.mode = 'geo_annotation_mode'; - - module.exports = geo_event; - - -/***/ }), -/* 10 */ -/***/ (function(module, exports) { - - /** - * Common object containing all action types that are provided by the GeoJS - * API. - */ - 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; - - -/***/ }), -/* 11 */ -/***/ (function(module, exports, __webpack_require__) { - - var proj4 = __webpack_require__(12); - var util = __webpack_require__(83); - - /** - * 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 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 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; - - var transform = function (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__(1); - 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 () { - return 0.0; - }; - - // Helper methods - function handleArrayCoordinates() { - if (Array.isArray(coordinates[0])) { - if (coordinates[0].length === 2) { - xAcc = function (index) { - return coordinates[index][0]; - }; - yAcc = function (index) { - return coordinates[index][1]; - }; - writer = function (index, x, y) { - output[index] = [x, y]; - }; - } else if (coordinates[0].length === 3) { - xAcc = function (index) { - return coordinates[index][0]; - }; - yAcc = function (index) { - return coordinates[index][1]; - }; - zAcc = function (index) { - return coordinates[index][2]; - }; - writer = function (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 (index) { - return coordinates[index * offset]; - }; - yAcc = function (index) { - return coordinates[index * offset + 1]; - }; - writer = function (index, x, y) { - output[index] = x; - output[index + 1] = y; - }; - } else if (coordinates.length === 3) { - offset = 3; - - xAcc = function (index) { - return coordinates[index * offset]; - }; - yAcc = function (index) { - return coordinates[index * offset + 1]; - }; - zAcc = function (index) { - return coordinates[index * offset + 2]; - }; - writer = function (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 (index) { - return coordinates[index]; - }; - yAcc = function (index) { - return coordinates[index + 1]; - }; - if (numberOfComponents === 2) { - writer = function (index, x, y) { - output[index] = x; - output[index + 1] = y; - }; - } else { - zAcc = function (index) { - return coordinates[index + 2]; - }; - writer = function (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 (index) { - return coordinates[index].x; - }; - yAcc = function (index) { - return coordinates[index].y; - }; - - if ('z' in coordinates[0]) { - zAcc = function (index) { - return coordinates[index].z; - }; - writer = function (index, x, y, z) { - output[i] = {x: x, y: y, z: z}; - }; - } else { - writer = function (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)) - }; - }; - - module.exports = transform; - - -/***/ }), -/* 12 */ -/***/ (function(module, exports, __webpack_require__) { - - var proj4 = __webpack_require__(13); - proj4.defaultDatum = 'WGS84'; //default datum - proj4.Proj = __webpack_require__(14); - proj4.WGS84 = new proj4.Proj('WGS84'); - proj4.Point = __webpack_require__(40); - proj4.toPoint = __webpack_require__(39); - proj4.defs = __webpack_require__(16); - proj4.transform = __webpack_require__(35); - proj4.mgrs = __webpack_require__(41); - proj4.version = __webpack_require__(42); - __webpack_require__(43)(proj4); - module.exports = proj4; - - -/***/ }), -/* 13 */ -/***/ (function(module, exports, __webpack_require__) { - - var proj = __webpack_require__(14); - var transform = __webpack_require__(35); - var wgs84 = proj('WGS84'); - - function transformer(from, to, coords) { - var transformedArray; - if (Array.isArray(coords)) { - transformedArray = transform(from, to, coords); - if (coords.length === 3) { - return [transformedArray.x, transformedArray.y, transformedArray.z]; - } - else { - return [transformedArray.x, transformedArray.y]; - } - } - else { - return transform(from, to, coords); - } - } - - function checkProj(item) { - if (item instanceof proj) { - return item; - } - if (item.oProj) { - return item.oProj; - } - return proj(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; - } - } - module.exports = proj4; - -/***/ }), -/* 14 */ -/***/ (function(module, exports, __webpack_require__) { - - var parseCode = __webpack_require__(15); - var extend = __webpack_require__(22); - var projections = __webpack_require__(23); - var deriveConstants = __webpack_require__(31); - var Datum = __webpack_require__(33); - var datum = __webpack_require__(34); - - - function Projection(srsCode,callback) { - if (!(this instanceof Projection)) { - return new Projection(srsCode); - } - callback = callback || function(error){ - if(error){ - throw error; - } - }; - var json = parseCode(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 = Datum[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'; - - var sphere = deriveConstants.sphere(json.a, json.b, json.rf, json.ellps, json.sphere); - var ecc = deriveConstants.eccentricity(sphere.a, sphere.b, sphere.rf, json.R_A); - var datumObj = json.datum || datum(json.datumCode, json.datum_params, sphere.a, sphere.b, ecc.es, ecc.ep2); - - extend(this, json); // transfer everything over from the projection because we don't know what we'll need - extend(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 = projections; - Projection.projections.start(); - module.exports = Projection; - - -/***/ }), -/* 15 */ -/***/ (function(module, exports, __webpack_require__) { - - var defs = __webpack_require__(16); - var wkt = __webpack_require__(21); - var projStr = __webpack_require__(18); - function testObj(code){ - return typeof code === 'string'; - } - function testDef(code){ - return code in defs; - } - var codeWords = ['GEOGCS','GEOCCS','PROJCS','LOCAL_CS']; - - function testWKT(code){ - return codeWords.some(function (word) { - return code.indexOf(word) > -1; - }); - } - 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 defs[code]; - } - if (testWKT(code)) { - return wkt(code); - } - if (testProj(code)) { - return projStr(code); - } - }else{ - return code; - } - } - - module.exports = parse; - - -/***/ }), -/* 16 */ -/***/ (function(module, exports, __webpack_require__) { - - var globals = __webpack_require__(17); - var parseProj = __webpack_require__(18); - var wkt = __webpack_require__(21); - - 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] = parseProj(arguments[1]); - } - else { - defs[name] = wkt(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; - } - - - } - globals(defs); - module.exports = defs; - - -/***/ }), -/* 17 */ -/***/ (function(module, exports) { - - module.exports = 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']; - }; - - -/***/ }), -/* 18 */ -/***/ (function(module, exports, __webpack_require__) { - - var D2R = 0.01745329251994329577; - var PrimeMeridian = __webpack_require__(19); - var units = __webpack_require__(20); - - module.exports = 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 * D2R; - }, - lat_1: function(v) { - self.lat1 = v * D2R; - }, - lat_2: function(v) { - self.lat2 = v * D2R; - }, - lat_ts: function(v) { - self.lat_ts = v * D2R; - }, - lon_0: function(v) { - self.long0 = v * D2R; - }, - lon_1: function(v) { - self.long1 = v * D2R; - }, - lon_2: function(v) { - self.long2 = v * D2R; - }, - alpha: function(v) { - self.alpha = parseFloat(v) * D2R; - }, - lonc: function(v) { - self.longc = v * 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; - if (units[v]) { - self.to_meter = units[v].to_meter; - } - }, - from_greenwich: function(v) { - self.from_greenwich = v * D2R; - }, - pm: function(v) { - self.from_greenwich = (PrimeMeridian[v] ? PrimeMeridian[v] : parseFloat(v)) * 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; - }; - - -/***/ }), -/* 19 */ -/***/ (function(module, 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" - -/***/ }), -/* 20 */ -/***/ (function(module, exports) { - - exports.ft = {to_meter: 0.3048}; - exports['us-ft'] = {to_meter: 1200 / 3937}; - - -/***/ }), -/* 21 */ -/***/ (function(module, exports, __webpack_require__) { - - var D2R = 0.01745329251994329577; - var extend = __webpack_require__(22); - - function mapit(obj, key, v) { - obj[key] = v.map(function(aa) { - var o = {}; - sExpr(aa, o); - return o; - }).reduce(function(a, b) { - return extend(a, b); - }, {}); - } - - function sExpr(v, obj) { - var key; - if (!Array.isArray(v)) { - obj[v] = true; - return; - } - else { - 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]); - } - else { - obj[key] = v[0]; - } - } - else if (!v.length) { - obj[key] = true; - } - else if (key === 'TOWGS84') { - obj[key] = v; - } - else { - obj[key] = {}; - if (['UNIT', 'PRIMEM', 'VERT_DATUM'].indexOf(key) > -1) { - obj[key] = { - name: v[0].toLowerCase(), - convert: v[1] - }; - if (v.length === 3) { - obj[key].auth = v[2]; - } - } - else if (key === 'SPHEROID') { - obj[key] = { - name: v[0], - a: v[1], - rf: v[2] - }; - if (v.length === 4) { - obj[key].auth = v[3]; - } - } - else if (['GEOGCS', 'GEOCCS', 'DATUM', 'VERT_CS', 'COMPD_CS', 'LOCAL_CS', 'FITTED_CS', 'LOCAL_DATUM'].indexOf(key) > -1) { - v[0] = ['name', v[0]]; - mapit(obj, key, v); - } - else if (v.every(function(aa) { - return Array.isArray(aa); - })) { - mapit(obj, key, v); - } - else { - sExpr(v, obj[key]); - } - } - } - } - - 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 = parseFloat(wkt.UNIT.convert, 10)*wkt.DATUM.SPHEROID.a; - } - } else { - wkt.to_meter = parseFloat(wkt.UNIT.convert, 10); - } - } - } - - if (wkt.GEOGCS) { - //if(wkt.GEOGCS.PRIMEM&&wkt.GEOGCS.PRIMEM.convert){ - // wkt.from_greenwich=wkt.GEOGCS.PRIMEM.convert*D2R; - //} - if (wkt.GEOGCS.DATUM) { - wkt.datumCode = wkt.GEOGCS.DATUM.name.toLowerCase(); - } - else { - wkt.datumCode = wkt.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 (wkt.GEOGCS.DATUM && wkt.GEOGCS.DATUM.SPHEROID) { - wkt.ellps = wkt.GEOGCS.DATUM.SPHEROID.name.replace('_19', '').replace(/[Cc]larke\_18/, 'clrk'); - if (wkt.ellps.toLowerCase().slice(0, 13) === "international") { - wkt.ellps = 'intl'; - } - - wkt.a = wkt.GEOGCS.DATUM.SPHEROID.a; - wkt.rf = parseFloat(wkt.GEOGCS.DATUM.SPHEROID.rf, 10); - } - if (~wkt.datumCode.indexOf('osgb_1936')) { - wkt.datumCode = "osgb36"; - } - } - if (wkt.b && !isFinite(wkt.b)) { - wkt.b = wkt.a; - } - - function toMeter(input) { - var ratio = wkt.to_meter || 1; - return parseFloat(input, 10) * 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'], - ['lat0', 'latitude_of_center', d2r], - ['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], - ['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; - } - } - module.exports = function(wkt, self) { - var lisp = JSON.parse(("," + wkt).replace(/\s*\,\s*([A-Z_0-9]+?)(\[)/g, ',["$1",').slice(1).replace(/\s*\,\s*([A-Z_0-9]+?)\]/g, ',"$1"]').replace(/,\["VERTCS".+/,'')); - var type = lisp.shift(); - var name = lisp.shift(); - lisp.unshift(['name', name]); - lisp.unshift(['type', type]); - lisp.unshift('output'); - var obj = {}; - sExpr(lisp, obj); - cleanWKT(obj.output); - return extend(self, obj.output); - }; - - -/***/ }), -/* 22 */ -/***/ (function(module, exports) { - - module.exports = 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; - }; - - -/***/ }), -/* 23 */ -/***/ (function(module, exports, __webpack_require__) { - - var projs = [ - __webpack_require__(24), - __webpack_require__(30) - ]; - 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; - } - - exports.add = add; - - exports.get = function(name) { - if (!name) { - return false; - } - var n = name.toLowerCase(); - if (typeof names[n] !== 'undefined' && projStore[names[n]]) { - return projStore[names[n]]; - } - }; - exports.start = function() { - projs.forEach(add); - }; - - -/***/ }), -/* 24 */ -/***/ (function(module, exports, __webpack_require__) { - - var msfnz = __webpack_require__(25); - var HALF_PI = Math.PI/2; - var EPSLN = 1.0e-10; - var R2D = 57.29577951308232088; - var adjust_lon = __webpack_require__(26); - var FORTPI = Math.PI/4; - var tsfnz = __webpack_require__(28); - var phi2z = __webpack_require__(29); - exports.init = function() { - 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 = msfnz(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 - --------------------------------------------------*/ - - exports.forward = function(p) { - var lon = p.x; - var lat = p.y; - // convert to radians - if (lat * R2D > 90 && lat * R2D < -90 && lon * R2D > 180 && lon * R2D < -180) { - return null; - } - - var x, y; - if (Math.abs(Math.abs(lat) - HALF_PI) <= EPSLN) { - return null; - } - else { - if (this.sphere) { - x = this.x0 + this.a * this.k0 * adjust_lon(lon - this.long0); - y = this.y0 + this.a * this.k0 * Math.log(Math.tan(FORTPI + 0.5 * lat)); - } - else { - var sinphi = Math.sin(lat); - var ts = tsfnz(this.e, lat, sinphi); - x = this.x0 + this.a * this.k0 * adjust_lon(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 - --------------------------------------------------*/ - exports.inverse = function(p) { - - var x = p.x - this.x0; - var y = p.y - this.y0; - var lon, lat; - - if (this.sphere) { - lat = HALF_PI - 2 * Math.atan(Math.exp(-y / (this.a * this.k0))); - } - else { - var ts = Math.exp(-y / (this.a * this.k0)); - lat = phi2z(this.e, ts); - if (lat === -9999) { - return null; - } - } - lon = adjust_lon(this.long0 + x / (this.a * this.k0)); - - p.x = lon; - p.y = lat; - return p; - }; - - exports.names = ["Mercator", "Popular Visualisation Pseudo Mercator", "Mercator_1SP", "Mercator_Auxiliary_Sphere", "merc"]; - - -/***/ }), -/* 25 */ -/***/ (function(module, exports) { - - module.exports = function(eccent, sinphi, cosphi) { - var con = eccent * sinphi; - return cosphi / (Math.sqrt(1 - con * con)); - }; - -/***/ }), -/* 26 */ -/***/ (function(module, exports, __webpack_require__) { - - 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; - var sign = __webpack_require__(27); - - module.exports = function(x) { - return (Math.abs(x) <= SPI) ? x : (x - (sign(x) * TWO_PI)); - }; - -/***/ }), -/* 27 */ -/***/ (function(module, exports) { - - module.exports = function(x) { - return x<0 ? -1 : 1; - }; - -/***/ }), -/* 28 */ -/***/ (function(module, exports) { - - var HALF_PI = Math.PI/2; - - module.exports = 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 * (HALF_PI - phi)) / con); - }; - -/***/ }), -/* 29 */ -/***/ (function(module, exports) { - - var HALF_PI = Math.PI/2; - module.exports = function(eccent, ts) { - var eccnth = 0.5 * eccent; - var con, dphi; - var phi = HALF_PI - 2 * Math.atan(ts); - for (var i = 0; i <= 15; i++) { - con = eccent * Math.sin(phi); - dphi = 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; - }; - -/***/ }), -/* 30 */ -/***/ (function(module, exports) { - - exports.init = function() { - //no-op for longlat - }; - - function identity(pt) { - return pt; - } - exports.forward = identity; - exports.inverse = identity; - exports.names = ["longlat", "identity"]; - - -/***/ }), -/* 31 */ -/***/ (function(module, exports, __webpack_require__) { - - // 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; - var Ellipsoid = __webpack_require__(32); - - exports.eccentricity = function(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 * (SIXTH + es * (RA4 + es * 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 - }; - }; - exports.sphere = function (a, b, rf, ellps, sphere) { - if (!a) { // do we have an ellipsoid? - var ellipse = Ellipsoid[ellps]; - if (!ellipse) { - ellipse = Ellipsoid.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) < EPSLN) { - sphere = true; - b = a; - } - return { - a: a, - b: b, - rf: rf, - sphere: sphere - }; - }; - - -/***/ }), -/* 32 */ -/***/ (function(module, 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" - }; - exports.WGS84 = { - a: 6378137.0, - rf: 298.257223563, - ellipseName: "WGS 84" - }; - exports.sphere = { - a: 6370997.0, - b: 6370997.0, - ellipseName: "Normal Sphere (r=6370997)" - }; - -/***/ }), -/* 33 */ -/***/ (function(module, 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.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" - }; - -/***/ }), -/* 34 */ -/***/ (function(module, exports) { - - 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; - - function datum(datumCode, datum_params, a, b, es, ep2) { - var out = {}; - out.datum_type = PJD_WGS84; //default setting - if (datumCode && datumCode === 'none') { - out.datum_type = PJD_NODATUM; - } - - 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 = 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 = PJD_7PARAM; - out.datum_params[3] *= SEC_TO_RAD; - out.datum_params[4] *= SEC_TO_RAD; - out.datum_params[5] *= 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; - } - - module.exports = datum; - - -/***/ }), -/* 35 */ -/***/ (function(module, exports, __webpack_require__) { - - var D2R = 0.01745329251994329577; - var R2D = 57.29577951308232088; - var PJD_3PARAM = 1; - var PJD_7PARAM = 2; - var datum_transform = __webpack_require__(36); - var adjust_axis = __webpack_require__(38); - var proj = __webpack_require__(14); - var toPoint = __webpack_require__(39); - function checkNotWGS(source, dest) { - return ((source.datum.datum_type === PJD_3PARAM || source.datum.datum_type === PJD_7PARAM) && dest.datumCode !== 'WGS84') || ((dest.datum.datum_type === PJD_3PARAM || dest.datum.datum_type === PJD_7PARAM) && source.datumCode !== 'WGS84'); - } - module.exports = function transform(source, dest, point) { - var wgs84; - if (Array.isArray(point)) { - point = toPoint(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 proj('WGS84'); - point = transform(source, wgs84, point); - source = wgs84; - } - // DGR, 2010/11/12 - if (source.axis !== 'enu') { - point = adjust_axis(source, false, point); - } - // Transform source points to long/lat, if they aren't already. - if (source.projName === 'longlat') { - point = { - x: point.x * D2R, - y: point.y * 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 = datum_transform(source.datum, dest.datum, point); - - // Adjust for the prime meridian if necessary - if (dest.from_greenwich) { - point = { - x: point.x - dest.grom_greenwich, - y: point.y - }; - } - - if (dest.projName === 'longlat') { - // convert radians to decimal degrees - point = { - x: point.x * R2D, - y: point.y * 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 adjust_axis(dest, true, point); - } - - return point; - }; - - -/***/ }), -/* 36 */ -/***/ (function(module, exports, __webpack_require__) { - - var PJD_3PARAM = 1; - var PJD_7PARAM = 2; - var PJD_NODATUM = 5; // WGS84 or equivalent - var datum = __webpack_require__(37); - function checkParams(type) { - return (type === PJD_3PARAM || type === PJD_7PARAM); - } - module.exports = function(source, dest, point) { - // Short cut if the datums are identical. - if (datum.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 === PJD_NODATUM || dest.datum_type === 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 = datum.geodeticToGeocentric(point, source.es, source.a); - // Convert between datums - if (checkParams(source.datum_type)) { - point = datum.geocentricToWgs84(point, source.datum_type, source.datum_params); - } - if (checkParams(dest.datum_type)) { - point = datum.geocentricFromWgs84(point, dest.datum_type, dest.datum_params); - } - return datum.geocentricToGeodetic(point, dest.es, dest.a, dest.b); - - }; - - -/***/ }), -/* 37 */ -/***/ (function(module, exports) { - - 'use strict'; - var PJD_3PARAM = 1; - var PJD_7PARAM = 2; - var HALF_PI = Math.PI/2; - - exports.compareDatums = function(source, dest) { - if (source.datum_type !== dest.datum_type) { - return false; // false, datums are not equal - } else if (source.a !== dest.a || Math.abs(this.es - dest.es) > 0.000000000050) { - // the tolerence for es is to ensure that GRS80 and WGS84 - // are considered identical - return false; - } else if (source.datum_type === PJD_3PARAM) { - return (this.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 === 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) - * - */ - exports.geodeticToGeocentric = function(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 < -HALF_PI && Latitude > -1.001 * HALF_PI) { - Latitude = -HALF_PI; - } else if (Latitude > HALF_PI && Latitude < 1.001 * HALF_PI) { - Latitude = HALF_PI; - } else if ((Latitude < -HALF_PI) || (Latitude > HALF_PI)) { - /* Latitude out of range */ - //..reportError('geocent:lat out of range:' + Latitude); - return null; - } - - 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() - - - exports.geocentricToGeodetic = function(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 = 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. - */ - exports.geocentricToWgs84 = function(p, datum_type, datum_params) { - - if (datum_type === 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 === 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) - exports.geocentricFromWgs84 = function(p, datum_type, datum_params) { - - if (datum_type === 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 === 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() - }; - - -/***/ }), -/* 38 */ -/***/ (function(module, exports) { - - module.exports = 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; - }; - - -/***/ }), -/* 39 */ -/***/ (function(module, exports) { - - module.exports = 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; - }; - -/***/ }), -/* 40 */ -/***/ (function(module, exports, __webpack_require__) { - - var mgrs = __webpack_require__(41); - - 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(mgrs.toPoint(mgrsStr)); - }; - Point.prototype.toMGRS = function(accuracy) { - return mgrs.forward([this.x, this.y], accuracy); - }; - module.exports = Point; - - -/***/ }), -/* 41 */ -/***/ (function(module, exports) { - - - - - /** - * 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 - - /** - * 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. - */ - exports.forward = function(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. - */ - exports.inverse = function(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]; - }; - - exports.toPoint = function(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); - } - - } - - -/***/ }), -/* 42 */ -/***/ (function(module, exports) { - - module.exports = '2.3.16'; - - -/***/ }), -/* 43 */ -/***/ (function(module, exports, __webpack_require__) { - - var projs = [ - __webpack_require__(44), - __webpack_require__(48), - __webpack_require__(50), - __webpack_require__(53), - __webpack_require__(54), - __webpack_require__(55), - __webpack_require__(56), - __webpack_require__(57), - __webpack_require__(58), - __webpack_require__(67), - __webpack_require__(69), - __webpack_require__(71), - __webpack_require__(72), - __webpack_require__(74), - __webpack_require__(75), - __webpack_require__(76), - __webpack_require__(77), - __webpack_require__(78), - __webpack_require__(79), - __webpack_require__(80), - __webpack_require__(81), - __webpack_require__(82) - ]; - module.exports = function(proj4){ - projs.forEach(function(proj){ - proj4.Proj.projections.add(proj); - }); - }; - -/***/ }), -/* 44 */ -/***/ (function(module, exports, __webpack_require__) { - - // Heavily based on this tmerc projection implementation - // https://github.com/mbloch/mapshaper-proj/blob/master/src/projections/tmerc.js - - var pj_enfn = __webpack_require__(45); - var pj_mlfn = __webpack_require__(46); - var pj_inv_mlfn = __webpack_require__(47); - var adjust_lon = __webpack_require__(26); - var HALF_PI = Math.PI / 2; - var EPSLN = 1.0e-10; - var sign = __webpack_require__(27); - - exports.init = function() { - 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 = pj_enfn(this.es); - this.ml0 = pj_mlfn(this.lat0, Math.sin(this.lat0), Math.cos(this.lat0), this.en); - } - }; - - /** - Transverse Mercator Forward - long/lat to x/y - long/lat in radians - */ - exports.forward = function(p) { - var lon = p.x; - var lat = p.y; - - var delta_lon = adjust_lon(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)) < 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) > 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) > 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 = pj_mlfn(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 - */ - exports.inverse = function(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 = adjust_lon(Math.atan2(g, h) + this.long0); - } - } - else { // ellipsoidal form - con = this.ml0 + y / this.k0; - phi = pj_inv_mlfn(con, this.es, this.en); - - if (Math.abs(phi) < HALF_PI) { - var sin_phi = Math.sin(phi); - var cos_phi = Math.cos(phi); - var tan_phi = Math.abs(cos_phi) > 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 = adjust_lon(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 = HALF_PI * sign(y); - lon = 0; - } - } - - p.x = lon; - p.y = lat; - - return p; - }; - - exports.names = ["Transverse_Mercator", "Transverse Mercator", "tmerc"]; - - -/***/ }), -/* 45 */ -/***/ (function(module, exports) { - - 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; - - module.exports = 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; - }; - -/***/ }), -/* 46 */ -/***/ (function(module, exports) { - - module.exports = 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])))); - }; - -/***/ }), -/* 47 */ -/***/ (function(module, exports, __webpack_require__) { - - var pj_mlfn = __webpack_require__(46); - var EPSLN = 1.0e-10; - var MAX_ITER = 20; - module.exports = 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 = (pj_mlfn(phi, s, Math.cos(phi), en) - arg) * (t * Math.sqrt(t)) * k; - phi -= t; - if (Math.abs(t) < EPSLN) { - return phi; - } - } - //..reportError("cass:pj_inv_mlfn: Convergence error"); - return phi; - }; - -/***/ }), -/* 48 */ -/***/ (function(module, exports, __webpack_require__) { - - var adjust_zone = __webpack_require__(49); - var tmerc = __webpack_require__(44); - - exports.dependsOn = 'tmerc'; - - exports.init = function() { - var zone = adjust_zone(this.zone, this.long0); - if (!zone) { - return; - } - - this.lat0 = 0; - this.long0 = (zone + 0.5) * Math.PI / 30 - Math.PI; - this.x0 = 500000; - this.y0 = this.utmSouth ? 10000000 : 0; - this.k0 = 0.9996; - - tmerc.init.apply(this); - this.forward = tmerc.forward; - this.inverse = tmerc.inverse; - }; - - exports.names = ["Universal Transverse Mercator System", "utm"]; - - -/***/ }), -/* 49 */ -/***/ (function(module, exports, __webpack_require__) { - - var adjust_lon = __webpack_require__(26); - - module.exports = function(zone, lon) { - if (!zone) { - zone = Math.floor((adjust_lon(lon) + Math.PI) * 30 / Math.PI); - - if (zone < 0) { - return 0; - } - else if (zone >= 60) { - return 59; - } - } - else { - if (zone > 0 && zone <= 60) { - return zone - 1; - } - } - }; - - -/***/ }), -/* 50 */ -/***/ (function(module, exports, __webpack_require__) { - - var gauss = __webpack_require__(51); - var adjust_lon = __webpack_require__(26); - exports.init = function() { - gauss.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"; - } - }; - - exports.forward = function(p) { - var sinc, cosc, cosl, k; - p.x = adjust_lon(p.x - this.long0); - gauss.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; - }; - - exports.inverse = function(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; - gauss.inverse.apply(this, [p]); - p.x = adjust_lon(p.x + this.long0); - return p; - }; - - exports.names = ["Stereographic_North_Pole", "Oblique_Stereographic", "Polar_Stereographic", "sterea","Oblique Stereographic Alternative"]; - - -/***/ }), -/* 51 */ -/***/ (function(module, exports, __webpack_require__) { - - var FORTPI = Math.PI/4; - var srat = __webpack_require__(52); - var HALF_PI = Math.PI/2; - var MAX_ITER = 20; - exports.init = function() { - 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 + FORTPI) / (Math.pow(Math.tan(0.5 * this.lat0 + FORTPI), this.C) * srat(this.e * sphi, this.ratexp)); - }; - - exports.forward = function(p) { - var lon = p.x; - var lat = p.y; - - p.y = 2 * Math.atan(this.K * Math.pow(Math.tan(0.5 * lat + FORTPI), this.C) * srat(this.e * Math.sin(lat), this.ratexp)) - HALF_PI; - p.x = this.C * lon; - return p; - }; - - exports.inverse = function(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 + FORTPI) / this.K, 1 / this.C); - for (var i = MAX_ITER; i > 0; --i) { - lat = 2 * Math.atan(num * srat(this.e * Math.sin(p.y), - 0.5 * this.e)) - 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; - }; - exports.names = ["gauss"]; - - -/***/ }), -/* 52 */ -/***/ (function(module, exports) { - - module.exports = function(esinp, exp) { - return (Math.pow((1 - esinp) / (1 + esinp), exp)); - }; - -/***/ }), -/* 53 */ -/***/ (function(module, exports, __webpack_require__) { - - var HALF_PI = Math.PI/2; - var EPSLN = 1.0e-10; - var sign = __webpack_require__(27); - var msfnz = __webpack_require__(25); - var tsfnz = __webpack_require__(28); - var phi2z = __webpack_require__(29); - var adjust_lon = __webpack_require__(26); - exports.ssfn_ = function(phit, sinphi, eccen) { - sinphi *= eccen; - return (Math.tan(0.5 * (HALF_PI + phit)) * Math.pow((1 - sinphi) / (1 + sinphi), 0.5 * eccen)); - }; - - exports.init = function() { - 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) <= EPSLN) { - this.k0 = 0.5 * (1 + sign(this.lat0) * Math.sin(this.lat_ts)); - } - } - else { - if (Math.abs(this.coslat0) <= 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) <= EPSLN) { - this.k0 = 0.5 * this.cons * msfnz(this.e, Math.sin(this.lat_ts), Math.cos(this.lat_ts)) / tsfnz(this.e, this.con * this.lat_ts, this.con * Math.sin(this.lat_ts)); - } - this.ms1 = msfnz(this.e, this.sinlat0, this.coslat0); - this.X0 = 2 * Math.atan(this.ssfn_(this.lat0, this.sinlat0, this.e)) - HALF_PI; - this.cosX0 = Math.cos(this.X0); - this.sinX0 = Math.sin(this.X0); - } - }; - - // Stereographic forward equations--mapping lat,long to x,y - exports.forward = function(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 = adjust_lon(lon - this.long0); - - if (Math.abs(Math.abs(lon - this.long0) - Math.PI) <= EPSLN && Math.abs(lat + this.lat0) <= 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)) - HALF_PI; - cosX = Math.cos(X); - sinX = Math.sin(X); - if (Math.abs(this.coslat0) <= EPSLN) { - ts = tsfnz(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) < 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 - exports.inverse = function(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 / (0.5 * this.a * this.k0)); - lon = this.long0; - lat = this.lat0; - if (rh <= 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) < EPSLN) { - if (this.lat0 > 0) { - lon = adjust_lon(this.long0 + Math.atan2(p.x, - 1 * p.y)); - } - else { - lon = adjust_lon(this.long0 + Math.atan2(p.x, p.y)); - } - } - else { - lon = adjust_lon(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) <= EPSLN) { - if (rh <= 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 * phi2z(this.e, ts); - lon = this.con * adjust_lon(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 <= EPSLN) { - Chi = this.X0; - } - else { - Chi = Math.asin(Math.cos(ce) * this.sinX0 + p.y * Math.sin(ce) * this.cosX0 / rh); - lon = adjust_lon(this.long0 + Math.atan2(p.x * Math.sin(ce), rh * this.cosX0 * Math.cos(ce) - p.y * this.sinX0 * Math.sin(ce))); - } - lat = -1 * phi2z(this.e, Math.tan(0.5 * (HALF_PI + Chi))); - } - } - p.x = lon; - p.y = lat; - - //trace(p.toString()); - return p; - - }; - exports.names = ["stere", "Stereographic_South_Pole", "Polar Stereographic (variant B)"]; - - -/***/ }), -/* 54 */ -/***/ (function(module, exports) { - - /* - 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 - */ - exports.init = function() { - 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; - }; - - - exports.forward = function(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; - }; - - exports.inverse = function(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; - }; - - exports.names = ["somerc"]; - - -/***/ }), -/* 55 */ -/***/ (function(module, exports, __webpack_require__) { - - var tsfnz = __webpack_require__(28); - var adjust_lon = __webpack_require__(26); - var phi2z = __webpack_require__(29); - var HALF_PI = Math.PI/2; - var FORTPI = Math.PI/4; - var EPSLN = 1.0e-10; - - /* Initialize the Oblique Mercator projection - ------------------------------------------*/ - exports.init = function() { - 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 = tsfnz(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 = tsfnz(this.e, this.lat1, Math.sin(this.lat1)); - var t2 = tsfnz(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 = adjust_lon(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 = adjust_lon(this.long0); - var dlon10 = adjust_lon(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 - ----------------------------------------------------------*/ - exports.forward = function(p) { - var lon = p.x; - var lat = p.y; - var dlon = adjust_lon(lon - this.long0); - var us, vs; - var con; - if (Math.abs(Math.abs(lat) - HALF_PI) <= EPSLN) { - if (lat > 0) { - con = -1; - } - else { - con = 1; - } - vs = this.al / this.bl * Math.log(Math.tan(FORTPI + con * this.gamma0 * 0.5)); - us = -1 * con * HALF_PI * this.al / this.bl; - } - else { - var t = tsfnz(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) <= 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))) <= 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; - }; - - exports.inverse = function(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) < EPSLN) { - p.x = this.long0; - p.y = HALF_PI; - } - else if (Math.abs(up + 1) < EPSLN) { - p.x = this.long0; - p.y = -1 * HALF_PI; - } - else { - p.y = phi2z(this.e, ts); - p.x = adjust_lon(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; - }; - - exports.names = ["Hotine_Oblique_Mercator", "Hotine Oblique Mercator", "Hotine_Oblique_Mercator_Azimuth_Natural_Origin", "Hotine_Oblique_Mercator_Azimuth_Center", "omerc"]; - -/***/ }), -/* 56 */ -/***/ (function(module, exports, __webpack_require__) { - - var EPSLN = 1.0e-10; - var msfnz = __webpack_require__(25); - var tsfnz = __webpack_require__(28); - var HALF_PI = Math.PI/2; - var sign = __webpack_require__(27); - var adjust_lon = __webpack_require__(26); - var phi2z = __webpack_require__(29); - exports.init = function() { - - // 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) < 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 = msfnz(this.e, sin1, cos1); - var ts1 = tsfnz(this.e, this.lat1, sin1); - - var sin2 = Math.sin(this.lat2); - var cos2 = Math.cos(this.lat2); - var ms2 = msfnz(this.e, sin2, cos2); - var ts2 = tsfnz(this.e, this.lat2, sin2); - - var ts0 = tsfnz(this.e, this.lat0, Math.sin(this.lat0)); - - if (Math.abs(this.lat1 - this.lat2) > 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 - // ----------------------------------------------------------------- - exports.forward = function(p) { - - var lon = p.x; - var lat = p.y; - - // singular cases : - if (Math.abs(2 * Math.abs(lat) - Math.PI) <= EPSLN) { - lat = sign(lat) * (HALF_PI - 2 * EPSLN); - } - - var con = Math.abs(Math.abs(lat) - HALF_PI); - var ts, rh1; - if (con > EPSLN) { - ts = tsfnz(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 * adjust_lon(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 - // ----------------------------------------------------------------- - exports.inverse = function(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 = phi2z(this.e, ts); - if (lat === -9999) { - return null; - } - } - else { - lat = -HALF_PI; - } - lon = adjust_lon(theta / this.ns + this.long0); - - p.x = lon; - p.y = lat; - return p; - }; - - exports.names = ["Lambert Tangential Conformal Conic Projection", "Lambert_Conformal_Conic", "Lambert_Conformal_Conic_2SP", "lcc"]; - - -/***/ }), -/* 57 */ -/***/ (function(module, exports, __webpack_require__) { - - var adjust_lon = __webpack_require__(26); - exports.init = function() { - 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 */ - exports.forward = function(p) { - var gfi, u, deltav, s, d, eps, ro; - var lon = p.x; - var lat = p.y; - var delta_lon = adjust_lon(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 */ - exports.inverse = function(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); - }; - exports.names = ["Krovak", "krovak"]; - - -/***/ }), -/* 58 */ -/***/ (function(module, exports, __webpack_require__) { - - var mlfn = __webpack_require__(59); - var e0fn = __webpack_require__(60); - var e1fn = __webpack_require__(61); - var e2fn = __webpack_require__(62); - var e3fn = __webpack_require__(63); - var gN = __webpack_require__(64); - var adjust_lon = __webpack_require__(26); - var adjust_lat = __webpack_require__(65); - var imlfn = __webpack_require__(66); - var HALF_PI = Math.PI/2; - var EPSLN = 1.0e-10; - exports.init = function() { - if (!this.sphere) { - this.e0 = e0fn(this.es); - this.e1 = e1fn(this.es); - this.e2 = e2fn(this.es); - this.e3 = e3fn(this.es); - this.ml0 = this.a * mlfn(this.e0, this.e1, this.e2, this.e3, this.lat0); - } - }; - - - - /* Cassini forward equations--mapping lat,long to x,y - -----------------------------------------------------------------------*/ - exports.forward = function(p) { - - /* Forward equations - -----------------*/ - var x, y; - var lam = p.x; - var phi = p.y; - lam = adjust_lon(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 = gN(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 * mlfn(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 - -----------------*/ - exports.inverse = function(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 = imlfn(ml1, this.e0, this.e1, this.e2, this.e3); - if (Math.abs(Math.abs(phi1) - HALF_PI) <= EPSLN) { - p.x = this.long0; - p.y = HALF_PI; - if (y < 0) { - p.y *= -1; - } - return p; - } - var nl1 = gN(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 = adjust_lon(lam + this.long0); - p.y = adjust_lat(phi); - return p; - - }; - exports.names = ["Cassini", "Cassini_Soldner", "cass"]; - -/***/ }), -/* 59 */ -/***/ (function(module, exports) { - - module.exports = function(e0, e1, e2, e3, phi) { - return (e0 * phi - e1 * Math.sin(2 * phi) + e2 * Math.sin(4 * phi) - e3 * Math.sin(6 * phi)); - }; - -/***/ }), -/* 60 */ -/***/ (function(module, exports) { - - module.exports = function(x) { - return (1 - 0.25 * x * (1 + x / 16 * (3 + 1.25 * x))); - }; - -/***/ }), -/* 61 */ -/***/ (function(module, exports) { - - module.exports = function(x) { - return (0.375 * x * (1 + 0.25 * x * (1 + 0.46875 * x))); - }; - -/***/ }), -/* 62 */ -/***/ (function(module, exports) { - - module.exports = function(x) { - return (0.05859375 * x * x * (1 + 0.75 * x)); - }; - -/***/ }), -/* 63 */ -/***/ (function(module, exports) { - - module.exports = function(x) { - return (x * x * x * (35 / 3072)); - }; - -/***/ }), -/* 64 */ -/***/ (function(module, exports) { - - module.exports = function(a, e, sinphi) { - var temp = e * sinphi; - return a / Math.sqrt(1 - temp * temp); - }; - -/***/ }), -/* 65 */ -/***/ (function(module, exports, __webpack_require__) { - - var HALF_PI = Math.PI/2; - var sign = __webpack_require__(27); - - module.exports = function(x) { - return (Math.abs(x) < HALF_PI) ? x : (x - (sign(x) * Math.PI)); - }; - -/***/ }), -/* 66 */ -/***/ (function(module, exports) { - - module.exports = 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; - }; - -/***/ }), -/* 67 */ -/***/ (function(module, exports, __webpack_require__) { - - var HALF_PI = Math.PI/2; - var FORTPI = Math.PI/4; - var EPSLN = 1.0e-10; - var qsfnz = __webpack_require__(68); - var adjust_lon = __webpack_require__(26); - /* - reference - "New Equal-Area Map Projections for Noncircular Regions", John P. Snyder, - The American Cartographer, Vol 15, No. 4, October 1988, pp. 341-355. - */ - - exports.S_POLE = 1; - exports.N_POLE = 2; - exports.EQUIT = 3; - exports.OBLIQ = 4; - - - /* Initialize the Lambert Azimuthal Equal Area projection - ------------------------------------------------------*/ - exports.init = function() { - var t = Math.abs(this.lat0); - if (Math.abs(t - HALF_PI) < EPSLN) { - this.mode = this.lat0 < 0 ? this.S_POLE : this.N_POLE; - } - else if (Math.abs(t) < EPSLN) { - this.mode = this.EQUIT; - } - else { - this.mode = this.OBLIQ; - } - if (this.es > 0) { - var sinphi; - - this.qp = qsfnz(this.e, 1); - this.mmf = 0.5 / (1 - this.es); - this.apa = this.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 = qsfnz(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 - -----------------------------------------------------------------------*/ - exports.forward = function(p) { - - /* Forward equations - -----------------*/ - var x, y, coslam, sinlam, sinphi, q, sinb, cosb, b, cosphi; - var lam = p.x; - var phi = p.y; - - lam = adjust_lon(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 <= 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) < EPSLN) { - return null; - } - y = 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 = qsfnz(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 = HALF_PI + phi; - q = this.qp - q; - break; - case this.S_POLE: - b = phi - HALF_PI; - q = this.qp + q; - break; - } - if (Math.abs(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 - -----------------*/ - exports.inverse = function(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) <= EPSLN) ? 0 : Math.asin(y * sinz / rh); - x *= sinz; - y = cosz * rh; - break; - case this.OBLIQ: - phi = (Math.abs(rh) <= 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 = HALF_PI - phi; - break; - case this.S_POLE: - phi -= 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 < 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 = this.authlat(Math.asin(ab), this.apa); - } - - - p.x = adjust_lon(this.long0 + lam); - p.y = phi; - return p; - }; - - /* determine latitude from authalic latitude */ - exports.P00 = 0.33333333333333333333; - exports.P01 = 0.17222222222222222222; - exports.P02 = 0.10257936507936507936; - exports.P10 = 0.06388888888888888888; - exports.P11 = 0.06640211640211640211; - exports.P20 = 0.01641501294219154443; - - exports.authset = function(es) { - var t; - var APA = []; - APA[0] = es * this.P00; - t = es * es; - APA[0] += t * this.P01; - APA[1] = t * this.P10; - t *= es; - APA[0] += t * this.P02; - APA[1] += t * this.P11; - APA[2] = t * this.P20; - return APA; - }; - - exports.authlat = function(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)); - }; - exports.names = ["Lambert Azimuthal Equal Area", "Lambert_Azimuthal_Equal_Area", "laea"]; - - -/***/ }), -/* 68 */ -/***/ (function(module, exports) { - - module.exports = 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); - } - }; - -/***/ }), -/* 69 */ -/***/ (function(module, exports, __webpack_require__) { - - var EPSLN = 1.0e-10; - var msfnz = __webpack_require__(25); - var qsfnz = __webpack_require__(68); - var adjust_lon = __webpack_require__(26); - var asinz = __webpack_require__(70); - exports.init = function() { - - if (Math.abs(this.lat1 + this.lat2) < 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 = msfnz(this.e3, this.sin_po, this.cos_po); - this.qs1 = qsfnz(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 = msfnz(this.e3, this.sin_po, this.cos_po); - this.qs2 = qsfnz(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 = qsfnz(this.e3, this.sin_po, this.cos_po); - - if (Math.abs(this.lat1 - this.lat2) > 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 - -------------------------------------------------------------------*/ - exports.forward = function(p) { - - var lon = p.x; - var lat = p.y; - - this.sin_phi = Math.sin(lat); - this.cos_phi = Math.cos(lat); - - var qs = qsfnz(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 * adjust_lon(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; - }; - - - exports.inverse = function(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 = adjust_lon(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. - -------------------------------------------*/ - exports.phi1z = function(eccent, qs) { - var sinphi, cosphi, con, com, dphi; - var phi = asinz(0.5 * qs); - if (eccent < 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; - }; - exports.names = ["Albers_Conic_Equal_Area", "Albers", "aea"]; - - -/***/ }), -/* 70 */ -/***/ (function(module, exports) { - - module.exports = function(x) { - if (Math.abs(x) > 1) { - x = (x > 1) ? 1 : -1; - } - return Math.asin(x); - }; - -/***/ }), -/* 71 */ -/***/ (function(module, exports, __webpack_require__) { - - var adjust_lon = __webpack_require__(26); - var EPSLN = 1.0e-10; - var asinz = __webpack_require__(70); - - /* - reference: - Wolfram Mathworld "Gnomonic Projection" - http://mathworld.wolfram.com/GnomonicProjection.html - Accessed: 12th November 2009 - */ - exports.init = function() { - - /* 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 - ---------------------------------------------------*/ - exports.forward = function(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 = adjust_lon(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) <= 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; - }; - - - exports.inverse = function(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 = asinz(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 = adjust_lon(this.long0 + lon); - } - else { - lat = this.phic0; - lon = 0; - } - - p.x = lon; - p.y = lat; - return p; - }; - exports.names = ["gnom"]; - - -/***/ }), -/* 72 */ -/***/ (function(module, exports, __webpack_require__) { - - var adjust_lon = __webpack_require__(26); - var qsfnz = __webpack_require__(68); - var msfnz = __webpack_require__(25); - var iqsfnz = __webpack_require__(73); - /* - 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) - */ - exports.init = function() { - //no-op - if (!this.sphere) { - this.k0 = msfnz(this.e, Math.sin(this.lat_ts), Math.cos(this.lat_ts)); - } - }; - - - /* Cylindrical Equal Area forward equations--mapping lat,long to x,y - ------------------------------------------------------------*/ - exports.forward = function(p) { - var lon = p.x; - var lat = p.y; - var x, y; - /* Forward equations - -----------------*/ - var dlon = adjust_lon(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 = qsfnz(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 - ------------------------------------------------------------*/ - exports.inverse = function(p) { - p.x -= this.x0; - p.y -= this.y0; - var lon, lat; - - if (this.sphere) { - lon = adjust_lon(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 = iqsfnz(this.e, 2 * p.y * this.k0 / this.a); - lon = adjust_lon(this.long0 + p.x / (this.a * this.k0)); - } - - p.x = lon; - p.y = lat; - return p; - }; - exports.names = ["cea"]; - - -/***/ }), -/* 73 */ -/***/ (function(module, exports) { - - var HALF_PI = Math.PI/2; - - module.exports = 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 * HALF_PI); - } - else { - return 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; - }; - -/***/ }), -/* 74 */ -/***/ (function(module, exports, __webpack_require__) { - - var adjust_lon = __webpack_require__(26); - var adjust_lat = __webpack_require__(65); - exports.init = function() { - - 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 - // ----------------------------------------------------------------- - exports.forward = function(p) { - - var lon = p.x; - var lat = p.y; - - var dlon = adjust_lon(lon - this.long0); - var dlat = adjust_lat(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 - // ----------------------------------------------------------------- - exports.inverse = function(p) { - - var x = p.x; - var y = p.y; - - p.x = adjust_lon(this.long0 + ((x - this.x0) / (this.a * this.rc))); - p.y = adjust_lat(this.lat0 + ((y - this.y0) / (this.a))); - return p; - }; - exports.names = ["Equirectangular", "Equidistant_Cylindrical", "eqc"]; - - -/***/ }), -/* 75 */ -/***/ (function(module, exports, __webpack_require__) { - - var e0fn = __webpack_require__(60); - var e1fn = __webpack_require__(61); - var e2fn = __webpack_require__(62); - var e3fn = __webpack_require__(63); - var adjust_lon = __webpack_require__(26); - var adjust_lat = __webpack_require__(65); - var mlfn = __webpack_require__(59); - var EPSLN = 1.0e-10; - var gN = __webpack_require__(64); - var MAX_ITER = 20; - exports.init = function() { - /* 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 = e0fn(this.es); - this.e1 = e1fn(this.es); - this.e2 = e2fn(this.es); - this.e3 = e3fn(this.es); - this.ml0 = this.a * mlfn(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 - ---------------------------------------------------*/ - exports.forward = function(p) { - var lon = p.x; - var lat = p.y; - var x, y, el; - var dlon = adjust_lon(lon - this.long0); - el = dlon * Math.sin(lat); - if (this.sphere) { - if (Math.abs(lat) <= EPSLN) { - x = this.a * dlon; - y = -1 * this.a * this.lat0; - } - else { - x = this.a * Math.sin(el) / Math.tan(lat); - y = this.a * (adjust_lat(lat - this.lat0) + (1 - Math.cos(el)) / Math.tan(lat)); - } - } - else { - if (Math.abs(lat) <= EPSLN) { - x = this.a * dlon; - y = -1 * this.ml0; - } - else { - var nl = gN(this.a, this.e, Math.sin(lat)) / Math.tan(lat); - x = nl * Math.sin(el); - y = this.a * mlfn(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 - -----------------*/ - exports.inverse = function(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) <= EPSLN) { - lon = adjust_lon(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) <= EPSLN) { - lat = phi; - break; - } - } - lon = adjust_lon(this.long0 + (Math.asin(x * Math.tan(phi) / this.a)) / Math.sin(lat)); - } - } - else { - if (Math.abs(y + this.ml0) <= EPSLN) { - lat = 0; - lon = adjust_lon(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 * mlfn(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) <= 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 = adjust_lon(this.long0 + Math.asin(x * cl / this.a) / Math.sin(lat)); - } - } - - p.x = lon; - p.y = lat; - return p; - }; - exports.names = ["Polyconic", "poly"]; - -/***/ }), -/* 76 */ -/***/ (function(module, exports) { - - var SEC_TO_RAD = 4.84813681109535993589914102357e-6; - /* - 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 - */ - exports.iterations = 1; - - exports.init = function() { - 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 - */ - exports.forward = function(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 / 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 - */ - exports.inverse = function(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 * SEC_TO_RAD * 1E5); - var lon = this.long0 + d_lambda; - - p.x = lon; - p.y = lat; - - return p; - }; - exports.names = ["New_Zealand_Map_Grid", "nzmg"]; - -/***/ }), -/* 77 */ -/***/ (function(module, exports, __webpack_require__) { - - var adjust_lon = __webpack_require__(26); - /* - 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 - -------------------------------------------*/ - exports.init = function() { - //no-op - }; - - - /* Miller Cylindrical forward equations--mapping lat,long to x,y - ------------------------------------------------------------*/ - exports.forward = function(p) { - var lon = p.x; - var lat = p.y; - /* Forward equations - -----------------*/ - var dlon = adjust_lon(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 - ------------------------------------------------------------*/ - exports.inverse = function(p) { - p.x -= this.x0; - p.y -= this.y0; - - var lon = adjust_lon(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; - }; - exports.names = ["Miller_Cylindrical", "mill"]; - - -/***/ }), -/* 78 */ -/***/ (function(module, exports, __webpack_require__) { - - var adjust_lon = __webpack_require__(26); - var adjust_lat = __webpack_require__(65); - var pj_enfn = __webpack_require__(45); - var MAX_ITER = 20; - var pj_mlfn = __webpack_require__(46); - var pj_inv_mlfn = __webpack_require__(47); - var HALF_PI = Math.PI/2; - var EPSLN = 1.0e-10; - var asinz = __webpack_require__(70); - exports.init = function() { - /* Place parameters in static storage for common use - -------------------------------------------------*/ - - - if (!this.sphere) { - this.en = pj_enfn(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 - -----------------------------------------------------*/ - exports.forward = function(p) { - var x, y; - var lon = p.x; - var lat = p.y; - /* Forward equations - -----------------*/ - lon = adjust_lon(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) < 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 * pj_mlfn(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; - }; - - exports.inverse = function(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 = asinz((this.m * lat + Math.sin(lat)) / this.n); - } - else if (this.n !== 1) { - lat = asinz(Math.sin(lat) / this.n); - } - lon = adjust_lon(lon + this.long0); - lat = adjust_lat(lat); - } - else { - lat = pj_inv_mlfn(p.y / this.a, this.es, this.en); - s = Math.abs(lat); - if (s < 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 = adjust_lon(temp); - } - else if ((s - EPSLN) < HALF_PI) { - lon = this.long0; - } - } - p.x = lon; - p.y = lat; - return p; - }; - exports.names = ["Sinusoidal", "sinu"]; - -/***/ }), -/* 79 */ -/***/ (function(module, exports, __webpack_require__) { - - var adjust_lon = __webpack_require__(26); - var EPSLN = 1.0e-10; - exports.init = function() {}; - - /* Mollweide forward equations--mapping lat,long to x,y - ----------------------------------------------------*/ - exports.forward = function(p) { - - /* Forward equations - -----------------*/ - var lon = p.x; - var lat = p.y; - - var delta_lon = adjust_lon(lon - this.long0); - var theta = lat; - var con = Math.PI * Math.sin(lat); - - /* Iterate using the Newton-Raphson method to find theta - -----------------------------------------------------*/ - for (var i = 0; true; i++) { - var delta_theta = -(theta + Math.sin(theta) - con) / (1 + Math.cos(theta)); - theta += delta_theta; - if (Math.abs(delta_theta) < 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) < 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; - }; - - exports.inverse = function(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 = adjust_lon(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; - }; - exports.names = ["Mollweide", "moll"]; - - -/***/ }), -/* 80 */ -/***/ (function(module, exports, __webpack_require__) { - - var e0fn = __webpack_require__(60); - var e1fn = __webpack_require__(61); - var e2fn = __webpack_require__(62); - var e3fn = __webpack_require__(63); - var msfnz = __webpack_require__(25); - var mlfn = __webpack_require__(59); - var adjust_lon = __webpack_require__(26); - var adjust_lat = __webpack_require__(65); - var imlfn = __webpack_require__(66); - var EPSLN = 1.0e-10; - exports.init = function() { - - /* 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) < 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 = e0fn(this.es); - this.e1 = e1fn(this.es); - this.e2 = e2fn(this.es); - this.e3 = e3fn(this.es); - - this.sinphi = Math.sin(this.lat1); - this.cosphi = Math.cos(this.lat1); - - this.ms1 = msfnz(this.e, this.sinphi, this.cosphi); - this.ml1 = mlfn(this.e0, this.e1, this.e2, this.e3, this.lat1); - - if (Math.abs(this.lat1 - this.lat2) < EPSLN) { - this.ns = this.sinphi; - } - else { - this.sinphi = Math.sin(this.lat2); - this.cosphi = Math.cos(this.lat2); - this.ms2 = msfnz(this.e, this.sinphi, this.cosphi); - this.ml2 = mlfn(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 = mlfn(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 - -----------------------------------------------------------*/ - exports.forward = function(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 = mlfn(this.e0, this.e1, this.e2, this.e3, lat); - rh1 = this.a * (this.g - ml); - } - var theta = this.ns * adjust_lon(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 - -----------------*/ - exports.inverse = function(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 = adjust_lon(this.long0 + theta / this.ns); - lat = adjust_lat(this.g - rh1 / this.a); - p.x = lon; - p.y = lat; - return p; - } - else { - var ml = this.g - rh1 / this.a; - lat = imlfn(ml, this.e0, this.e1, this.e2, this.e3); - lon = adjust_lon(this.long0 + theta / this.ns); - p.x = lon; - p.y = lat; - return p; - } - - }; - exports.names = ["Equidistant_Conic", "eqdc"]; - - -/***/ }), -/* 81 */ -/***/ (function(module, exports, __webpack_require__) { - - var adjust_lon = __webpack_require__(26); - var HALF_PI = Math.PI/2; - var EPSLN = 1.0e-10; - var asinz = __webpack_require__(70); - /* Initialize the Van Der Grinten projection - ----------------------------------------*/ - exports.init = function() { - //this.R = 6370997; //Radius of earth - this.R = this.a; - }; - - exports.forward = function(p) { - - var lon = p.x; - var lat = p.y; - - /* Forward equations - -----------------*/ - var dlon = adjust_lon(lon - this.long0); - var x, y; - - if (Math.abs(lat) <= EPSLN) { - x = this.x0 + this.R * dlon; - y = this.y0; - } - var theta = asinz(2 * Math.abs(lat / Math.PI)); - if ((Math.abs(dlon) <= EPSLN) || (Math.abs(Math.abs(lat) - HALF_PI) <= 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 - ---------------------------------------------------------*/ - exports.inverse = function(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) < EPSLN) { - lon = this.long0; - } - else { - lon = adjust_lon(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; - }; - exports.names = ["Van_der_Grinten_I", "VanDerGrinten", "vandg"]; - -/***/ }), -/* 82 */ -/***/ (function(module, exports, __webpack_require__) { - - var adjust_lon = __webpack_require__(26); - var HALF_PI = Math.PI/2; - var EPSLN = 1.0e-10; - var mlfn = __webpack_require__(59); - var e0fn = __webpack_require__(60); - var e1fn = __webpack_require__(61); - var e2fn = __webpack_require__(62); - var e3fn = __webpack_require__(63); - var gN = __webpack_require__(64); - var asinz = __webpack_require__(70); - var imlfn = __webpack_require__(66); - exports.init = function() { - this.sin_p12 = Math.sin(this.lat0); - this.cos_p12 = Math.cos(this.lat0); - }; - - exports.forward = function(p) { - var lon = p.x; - var lat = p.y; - var sinphi = Math.sin(p.y); - var cosphi = Math.cos(p.y); - var dlon = adjust_lon(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) <= EPSLN) { - //North Pole case - p.x = this.x0 + this.a * (HALF_PI - lat) * Math.sin(dlon); - p.y = this.y0 - this.a * (HALF_PI - lat) * Math.cos(dlon); - return p; - } - else if (Math.abs(this.sin_p12 + 1) <= EPSLN) { - //South Pole case - p.x = this.x0 + this.a * (HALF_PI + lat) * Math.sin(dlon); - p.y = this.y0 + this.a * (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 = e0fn(this.es); - e1 = e1fn(this.es); - e2 = e2fn(this.es); - e3 = e3fn(this.es); - if (Math.abs(this.sin_p12 - 1) <= EPSLN) { - //North Pole case - Mlp = this.a * mlfn(e0, e1, e2, e3, HALF_PI); - Ml = this.a * mlfn(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) <= EPSLN) { - //South Pole case - Mlp = this.a * mlfn(e0, e1, e2, e3, HALF_PI); - Ml = this.a * mlfn(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 = gN(this.a, this.e, this.sin_p12); - Nl = gN(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) <= 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; - } - } - - - }; - - exports.inverse = function(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 * HALF_PI * this.a)) { - return; - } - z = rh / this.a; - - sinz = Math.sin(z); - cosz = Math.cos(z); - - lon = this.long0; - if (Math.abs(rh) <= EPSLN) { - lat = this.lat0; - } - else { - lat = asinz(cosz * this.sin_p12 + (p.y * sinz * this.cos_p12) / rh); - con = Math.abs(this.lat0) - HALF_PI; - if (Math.abs(con) <= EPSLN) { - if (this.lat0 >= 0) { - lon = adjust_lon(this.long0 + Math.atan2(p.x, - p.y)); - } - else { - lon = adjust_lon(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 = adjust_lon(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 = e0fn(this.es); - e1 = e1fn(this.es); - e2 = e2fn(this.es); - e3 = e3fn(this.es); - if (Math.abs(this.sin_p12 - 1) <= EPSLN) { - //North pole case - Mlp = this.a * mlfn(e0, e1, e2, e3, HALF_PI); - rh = Math.sqrt(p.x * p.x + p.y * p.y); - M = Mlp - rh; - lat = imlfn(M / this.a, e0, e1, e2, e3); - lon = adjust_lon(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) <= EPSLN) { - //South pole case - Mlp = this.a * mlfn(e0, e1, e2, e3, HALF_PI); - rh = Math.sqrt(p.x * p.x + p.y * p.y); - M = rh - Mlp; - - lat = imlfn(M / this.a, e0, e1, e2, e3); - lon = adjust_lon(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 = gN(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 = adjust_lon(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; - } - } - - }; - exports.names = ["Azimuthal_Equidistant", "aeqd"]; - - -/***/ }), -/* 83 */ -/***/ (function(module, exports, __webpack_require__) { - - var $ = __webpack_require__(1); - var proj4 = __webpack_require__(12); - - var throttle = __webpack_require__(84); - var mockVGL = __webpack_require__(85); - var DistanceGrid = __webpack_require__(204); - var ClusterGroup = __webpack_require__(205); - - 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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 (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 () { - 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 () { - 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 () { - 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 (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 (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 (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 (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 (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 (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 (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 () { - 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 (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 (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 (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 (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 (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 (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 (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); - }, - - /** - * 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 (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 (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 (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) + ')'; - 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 () { - 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 (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 (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 (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 (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 (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 (value) { - var type = 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 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`. - * @alias 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 (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 (color, match) { - /* Conversion from https://www.w3.org/TR/2011/REC-css3-color-20110607 - */ - var hue_to_rgb = function (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); - - -/***/ }), -/* 84 */ -/***/ (function(module, exports) { - - /** - * 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 - * `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 (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 () {}; - } - - // 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 `geo.util.throttle` function. - * - * @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 (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 - }; - - -/***/ }), -/* 85 */ -/***/ (function(module, exports, __webpack_require__) { - - /* eslint-disable camelcase */ - /* eslint-disable underscore/prefer-constant */ - - var $ = __webpack_require__(1); - var vgl = __webpack_require__(86); - var vglRenderer = __webpack_require__(200); - - 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__(86); - - if (supported === undefined) { - supported = true; - } - - if (vgl._mocked) { - throw new Error('VGL renderer already mocked'); - } - - var mockCounts = {}; - var count = function (name) { - mockCounts[name] = (mockCounts[name] || 0) + 1; - }; - var noop = function (name) { - return function () { - count(name); - }; - }; - var _id = 0, - incID = function (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 (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 (key) { - count('getParameter'); - if (key === vgl.GL.DEPTH_BITS) { - return 16; - } - }, - getProgramParameter: function (id, key) { - count('getProgramParameter'); - if (key === vgl.GL.LINK_STATUS) { - return true; - } - }, - getShaderInfoLog: function () { - count('getShaderInfoLog'); - return 'log'; - }, - getShaderParameter: function (id, key) { - count('getShaderParameter'); - if (key === vgl.GL.COMPILE_STATUS) { - return true; - } - }, - getUniformLocation: incID('getUniformLocation'), - isEnabled: function (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 () { - /* 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; - } - }; - - -/***/ }), -/* 86 */ -/***/ (function(module, exports, __webpack_require__) { - - /* WEBPACK VAR INJECTION */(function(global) {module.exports = global["vgl"] = __webpack_require__(87); - /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()))) - -/***/ }), -/* 87 */ -/***/ (function(module, exports, __webpack_require__) { - - var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*** IMPORTS FROM imports-loader ***/ - var mat4 = __webpack_require__(88); - var vec4 = __webpack_require__(112); - var vec3 = __webpack_require__(138); - var vec2 = __webpack_require__(171); - var $ = __webpack_require__(1); - - (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; - - })); - - - -/***/ }), -/* 88 */ -/***/ (function(module, exports, __webpack_require__) { - - module.exports = { - create: __webpack_require__(89) - , clone: __webpack_require__(90) - , copy: __webpack_require__(91) - , identity: __webpack_require__(92) - , transpose: __webpack_require__(93) - , invert: __webpack_require__(94) - , adjoint: __webpack_require__(95) - , determinant: __webpack_require__(96) - , multiply: __webpack_require__(97) - , translate: __webpack_require__(98) - , scale: __webpack_require__(99) - , rotate: __webpack_require__(100) - , rotateX: __webpack_require__(101) - , rotateY: __webpack_require__(102) - , rotateZ: __webpack_require__(103) - , fromRotationTranslation: __webpack_require__(104) - , fromQuat: __webpack_require__(105) - , frustum: __webpack_require__(106) - , perspective: __webpack_require__(107) - , perspectiveFromFieldOfView: __webpack_require__(108) - , ortho: __webpack_require__(109) - , lookAt: __webpack_require__(110) - , str: __webpack_require__(111) - } - -/***/ }), -/* 89 */ -/***/ (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; - }; - -/***/ }), -/* 90 */ -/***/ (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; - }; - -/***/ }), -/* 91 */ -/***/ (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; - }; - -/***/ }), -/* 92 */ -/***/ (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; - }; - -/***/ }), -/* 93 */ -/***/ (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; - }; - -/***/ }), -/* 94 */ -/***/ (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; - }; - -/***/ }), -/* 95 */ -/***/ (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; - }; - -/***/ }), -/* 96 */ -/***/ (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; - }; - -/***/ }), -/* 97 */ -/***/ (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; - }; - -/***/ }), -/* 98 */ -/***/ (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; - }; - -/***/ }), -/* 99 */ -/***/ (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; - }; - -/***/ }), -/* 100 */ -/***/ (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; - }; - -/***/ }), -/* 101 */ -/***/ (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; - }; - -/***/ }), -/* 102 */ -/***/ (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; - }; - -/***/ }), -/* 103 */ -/***/ (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; - }; - -/***/ }), -/* 104 */ -/***/ (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; - }; - -/***/ }), -/* 105 */ -/***/ (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; - }; - -/***/ }), -/* 106 */ -/***/ (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; - }; - -/***/ }), -/* 107 */ -/***/ (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; - }; - -/***/ }), -/* 108 */ -/***/ (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; - } - - - -/***/ }), -/* 109 */ -/***/ (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; - }; - -/***/ }), -/* 110 */ -/***/ (function(module, exports, __webpack_require__) { - - var identity = __webpack_require__(92); - - 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; - }; - -/***/ }), -/* 111 */ -/***/ (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] + ')'; - }; - -/***/ }), -/* 112 */ -/***/ (function(module, exports, __webpack_require__) { - - module.exports = { - create: __webpack_require__(113), - clone: __webpack_require__(114), - fromValues: __webpack_require__(115), - copy: __webpack_require__(116), - set: __webpack_require__(117), - add: __webpack_require__(118), - subtract: __webpack_require__(119), - multiply: __webpack_require__(120), - divide: __webpack_require__(121), - min: __webpack_require__(122), - max: __webpack_require__(123), - scale: __webpack_require__(124), - scaleAndAdd: __webpack_require__(125), - distance: __webpack_require__(126), - squaredDistance: __webpack_require__(127), - length: __webpack_require__(128), - squaredLength: __webpack_require__(129), - negate: __webpack_require__(130), - inverse: __webpack_require__(131), - normalize: __webpack_require__(132), - dot: __webpack_require__(133), - lerp: __webpack_require__(134), - random: __webpack_require__(135), - transformMat4: __webpack_require__(136), - transformQuat: __webpack_require__(137) - } - - -/***/ }), -/* 113 */ -/***/ (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 - } - - -/***/ }), -/* 114 */ -/***/ (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 - } - - -/***/ }), -/* 115 */ -/***/ (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 - } - - -/***/ }), -/* 116 */ -/***/ (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 - } - - -/***/ }), -/* 117 */ -/***/ (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 - } - - -/***/ }), -/* 118 */ -/***/ (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 - } - - -/***/ }), -/* 119 */ -/***/ (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 - } - - -/***/ }), -/* 120 */ -/***/ (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 - } - - -/***/ }), -/* 121 */ -/***/ (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 - } - - -/***/ }), -/* 122 */ -/***/ (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 - } - - -/***/ }), -/* 123 */ -/***/ (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 - } - - -/***/ }), -/* 124 */ -/***/ (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 - } - - -/***/ }), -/* 125 */ -/***/ (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 - } - - -/***/ }), -/* 126 */ -/***/ (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) - } - - -/***/ }), -/* 127 */ -/***/ (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 - } - - -/***/ }), -/* 128 */ -/***/ (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) - } - - -/***/ }), -/* 129 */ -/***/ (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 - } - - -/***/ }), -/* 130 */ -/***/ (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 - } - - -/***/ }), -/* 131 */ -/***/ (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 - } - - -/***/ }), -/* 132 */ -/***/ (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 - } - - -/***/ }), -/* 133 */ -/***/ (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] - } - - -/***/ }), -/* 134 */ -/***/ (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 - } - - -/***/ }), -/* 135 */ -/***/ (function(module, exports, __webpack_require__) { - - var vecNormalize = __webpack_require__(132) - var vecScale = __webpack_require__(124) - - 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 - } - - -/***/ }), -/* 136 */ -/***/ (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 - } - - -/***/ }), -/* 137 */ -/***/ (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 - } - - -/***/ }), -/* 138 */ -/***/ (function(module, exports, __webpack_require__) { - - module.exports = { - create: __webpack_require__(139) - , clone: __webpack_require__(140) - , angle: __webpack_require__(141) - , fromValues: __webpack_require__(142) - , copy: __webpack_require__(145) - , set: __webpack_require__(146) - , add: __webpack_require__(147) - , subtract: __webpack_require__(148) - , multiply: __webpack_require__(149) - , divide: __webpack_require__(150) - , min: __webpack_require__(151) - , max: __webpack_require__(152) - , scale: __webpack_require__(153) - , scaleAndAdd: __webpack_require__(154) - , distance: __webpack_require__(155) - , squaredDistance: __webpack_require__(156) - , length: __webpack_require__(157) - , squaredLength: __webpack_require__(158) - , negate: __webpack_require__(159) - , inverse: __webpack_require__(160) - , normalize: __webpack_require__(143) - , dot: __webpack_require__(144) - , cross: __webpack_require__(161) - , lerp: __webpack_require__(162) - , random: __webpack_require__(163) - , transformMat4: __webpack_require__(164) - , transformMat3: __webpack_require__(165) - , transformQuat: __webpack_require__(166) - , rotateX: __webpack_require__(167) - , rotateY: __webpack_require__(168) - , rotateZ: __webpack_require__(169) - , forEach: __webpack_require__(170) - } - -/***/ }), -/* 139 */ -/***/ (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 - } - -/***/ }), -/* 140 */ -/***/ (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 - } - -/***/ }), -/* 141 */ -/***/ (function(module, exports, __webpack_require__) { - - module.exports = angle - - var fromValues = __webpack_require__(142) - var normalize = __webpack_require__(143) - var dot = __webpack_require__(144) - - /** - * 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) - } - } - - -/***/ }), -/* 142 */ -/***/ (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 - } - -/***/ }), -/* 143 */ -/***/ (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 - } - -/***/ }), -/* 144 */ -/***/ (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] - } - -/***/ }), -/* 145 */ -/***/ (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 - } - -/***/ }), -/* 146 */ -/***/ (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 - } - -/***/ }), -/* 147 */ -/***/ (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 - } - -/***/ }), -/* 148 */ -/***/ (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 - } - -/***/ }), -/* 149 */ -/***/ (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 - } - -/***/ }), -/* 150 */ -/***/ (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 - } - -/***/ }), -/* 151 */ -/***/ (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 - } - -/***/ }), -/* 152 */ -/***/ (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 - } - -/***/ }), -/* 153 */ -/***/ (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 - } - -/***/ }), -/* 154 */ -/***/ (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 - } - -/***/ }), -/* 155 */ -/***/ (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) - } - -/***/ }), -/* 156 */ -/***/ (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 - } - -/***/ }), -/* 157 */ -/***/ (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) - } - -/***/ }), -/* 158 */ -/***/ (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 - } - -/***/ }), -/* 159 */ -/***/ (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 - } - -/***/ }), -/* 160 */ -/***/ (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 - } - -/***/ }), -/* 161 */ -/***/ (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 - } - -/***/ }), -/* 162 */ -/***/ (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 - } - -/***/ }), -/* 163 */ -/***/ (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 - } - -/***/ }), -/* 164 */ -/***/ (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 - } - -/***/ }), -/* 165 */ -/***/ (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 - } - -/***/ }), -/* 166 */ -/***/ (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 - } - -/***/ }), -/* 167 */ -/***/ (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 p = [], r=[] - //Translate point to the origin - p[0] = a[0] - b[0] - p[1] = a[1] - b[1] - p[2] = a[2] - b[2] - - //perform rotation - r[0] = p[0] - r[1] = p[1]*Math.cos(c) - p[2]*Math.sin(c) - r[2] = p[1]*Math.sin(c) + p[2]*Math.cos(c) - - //translate to correct position - out[0] = r[0] + b[0] - out[1] = r[1] + b[1] - out[2] = r[2] + b[2] - - return out - } - -/***/ }), -/* 168 */ -/***/ (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 p = [], r=[] - //Translate point to the origin - p[0] = a[0] - b[0] - p[1] = a[1] - b[1] - p[2] = a[2] - b[2] - - //perform rotation - r[0] = p[2]*Math.sin(c) + p[0]*Math.cos(c) - r[1] = p[1] - r[2] = p[2]*Math.cos(c) - p[0]*Math.sin(c) - - //translate to correct position - out[0] = r[0] + b[0] - out[1] = r[1] + b[1] - out[2] = r[2] + b[2] - - return out - } - -/***/ }), -/* 169 */ -/***/ (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 p = [], r=[] - //Translate point to the origin - p[0] = a[0] - b[0] - p[1] = a[1] - b[1] - p[2] = a[2] - b[2] - - //perform rotation - r[0] = p[0]*Math.cos(c) - p[1]*Math.sin(c) - r[1] = p[0]*Math.sin(c) + p[1]*Math.cos(c) - r[2] = p[2] - - //translate to correct position - out[0] = r[0] + b[0] - out[1] = r[1] + b[1] - out[2] = r[2] + b[2] - - return out - } - -/***/ }), -/* 170 */ -/***/ (function(module, exports, __webpack_require__) { - - module.exports = forEach; - - var vec = __webpack_require__(139)() - - /** - * 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 - } - -/***/ }), -/* 171 */ -/***/ (function(module, exports, __webpack_require__) { - - module.exports = { - create: __webpack_require__(172) - , clone: __webpack_require__(173) - , fromValues: __webpack_require__(174) - , copy: __webpack_require__(175) - , set: __webpack_require__(176) - , add: __webpack_require__(177) - , subtract: __webpack_require__(178) - , multiply: __webpack_require__(179) - , divide: __webpack_require__(180) - , min: __webpack_require__(181) - , max: __webpack_require__(182) - , scale: __webpack_require__(183) - , scaleAndAdd: __webpack_require__(184) - , distance: __webpack_require__(185) - , squaredDistance: __webpack_require__(186) - , length: __webpack_require__(187) - , squaredLength: __webpack_require__(188) - , negate: __webpack_require__(189) - , normalize: __webpack_require__(190) - , dot: __webpack_require__(191) - , cross: __webpack_require__(192) - , lerp: __webpack_require__(193) - , random: __webpack_require__(194) - , transformMat2: __webpack_require__(195) - , transformMat2d: __webpack_require__(196) - , transformMat3: __webpack_require__(197) - , transformMat4: __webpack_require__(198) - , forEach: __webpack_require__(199) - } - -/***/ }), -/* 172 */ -/***/ (function(module, exports) { - - module.exports = create - - /** - * Creates a new, empty vec2 - * - * @returns {vec2} a new 2D vector - */ - function create() { - var out = new Float32Array(2) - out[0] = 0 - out[1] = 0 - return out - } - -/***/ }), -/* 173 */ -/***/ (function(module, exports) { - - module.exports = clone - - /** - * Creates a new vec2 initialized with values from an existing vector - * - * @param {vec2} a vector to clone - * @returns {vec2} a new 2D vector - */ - function clone(a) { - var out = new Float32Array(2) - out[0] = a[0] - out[1] = a[1] - return out - } - -/***/ }), -/* 174 */ -/***/ (function(module, exports) { - - module.exports = fromValues - - /** - * Creates a new vec2 initialized with the given values - * - * @param {Number} x X component - * @param {Number} y Y component - * @returns {vec2} a new 2D vector - */ - function fromValues(x, y) { - var out = new Float32Array(2) - out[0] = x - out[1] = y - return out - } - -/***/ }), -/* 175 */ -/***/ (function(module, exports) { - - module.exports = copy - - /** - * Copy the values from one vec2 to another - * - * @param {vec2} out the receiving vector - * @param {vec2} a the source vector - * @returns {vec2} out - */ - function copy(out, a) { - out[0] = a[0] - out[1] = a[1] - return out - } - -/***/ }), -/* 176 */ -/***/ (function(module, exports) { - - module.exports = set - - /** - * Set the components of a vec2 to the given values - * - * @param {vec2} out the receiving vector - * @param {Number} x X component - * @param {Number} y Y component - * @returns {vec2} out - */ - function set(out, x, y) { - out[0] = x - out[1] = y - return out - } - -/***/ }), -/* 177 */ -/***/ (function(module, exports) { - - module.exports = add - - /** - * Adds two vec2's - * - * @param {vec2} out the receiving vector - * @param {vec2} a the first operand - * @param {vec2} b the second operand - * @returns {vec2} out - */ - function add(out, a, b) { - out[0] = a[0] + b[0] - out[1] = a[1] + b[1] - return out - } - -/***/ }), -/* 178 */ -/***/ (function(module, exports) { - - module.exports = subtract - - /** - * Subtracts vector b from vector a - * - * @param {vec2} out the receiving vector - * @param {vec2} a the first operand - * @param {vec2} b the second operand - * @returns {vec2} out - */ - function subtract(out, a, b) { - out[0] = a[0] - b[0] - out[1] = a[1] - b[1] - return out - } - -/***/ }), -/* 179 */ -/***/ (function(module, exports) { - - module.exports = multiply - - /** - * Multiplies two vec2's - * - * @param {vec2} out the receiving vector - * @param {vec2} a the first operand - * @param {vec2} b the second operand - * @returns {vec2} out - */ - function multiply(out, a, b) { - out[0] = a[0] * b[0] - out[1] = a[1] * b[1] - return out - } - -/***/ }), -/* 180 */ -/***/ (function(module, exports) { - - module.exports = divide - - /** - * Divides two vec2's - * - * @param {vec2} out the receiving vector - * @param {vec2} a the first operand - * @param {vec2} b the second operand - * @returns {vec2} out - */ - function divide(out, a, b) { - out[0] = a[0] / b[0] - out[1] = a[1] / b[1] - return out - } - -/***/ }), -/* 181 */ -/***/ (function(module, exports) { - - module.exports = min - - /** - * Returns the minimum of two vec2's - * - * @param {vec2} out the receiving vector - * @param {vec2} a the first operand - * @param {vec2} b the second operand - * @returns {vec2} out - */ - function min(out, a, b) { - out[0] = Math.min(a[0], b[0]) - out[1] = Math.min(a[1], b[1]) - return out - } - -/***/ }), -/* 182 */ -/***/ (function(module, exports) { - - module.exports = max - - /** - * Returns the maximum of two vec2's - * - * @param {vec2} out the receiving vector - * @param {vec2} a the first operand - * @param {vec2} b the second operand - * @returns {vec2} out - */ - function max(out, a, b) { - out[0] = Math.max(a[0], b[0]) - out[1] = Math.max(a[1], b[1]) - return out - } - -/***/ }), -/* 183 */ -/***/ (function(module, exports) { - - module.exports = scale - - /** - * Scales a vec2 by a scalar number - * - * @param {vec2} out the receiving vector - * @param {vec2} a the vector to scale - * @param {Number} b amount to scale the vector by - * @returns {vec2} out - */ - function scale(out, a, b) { - out[0] = a[0] * b - out[1] = a[1] * b - return out - } - -/***/ }), -/* 184 */ -/***/ (function(module, exports) { - - module.exports = scaleAndAdd - - /** - * Adds two vec2's after scaling the second operand by a scalar value - * - * @param {vec2} out the receiving vector - * @param {vec2} a the first operand - * @param {vec2} b the second operand - * @param {Number} scale the amount to scale b by before adding - * @returns {vec2} out - */ - function scaleAndAdd(out, a, b, scale) { - out[0] = a[0] + (b[0] * scale) - out[1] = a[1] + (b[1] * scale) - return out - } - -/***/ }), -/* 185 */ -/***/ (function(module, exports) { - - module.exports = distance - - /** - * Calculates the euclidian distance between two vec2's - * - * @param {vec2} a the first operand - * @param {vec2} 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] - return Math.sqrt(x*x + y*y) - } - -/***/ }), -/* 186 */ -/***/ (function(module, exports) { - - module.exports = squaredDistance - - /** - * Calculates the squared euclidian distance between two vec2's - * - * @param {vec2} a the first operand - * @param {vec2} 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] - return x*x + y*y - } - -/***/ }), -/* 187 */ -/***/ (function(module, exports) { - - module.exports = length - - /** - * Calculates the length of a vec2 - * - * @param {vec2} a vector to calculate length of - * @returns {Number} length of a - */ - function length(a) { - var x = a[0], - y = a[1] - return Math.sqrt(x*x + y*y) - } - -/***/ }), -/* 188 */ -/***/ (function(module, exports) { - - module.exports = squaredLength - - /** - * Calculates the squared length of a vec2 - * - * @param {vec2} a vector to calculate squared length of - * @returns {Number} squared length of a - */ - function squaredLength(a) { - var x = a[0], - y = a[1] - return x*x + y*y - } - -/***/ }), -/* 189 */ -/***/ (function(module, exports) { - - module.exports = negate - - /** - * Negates the components of a vec2 - * - * @param {vec2} out the receiving vector - * @param {vec2} a vector to negate - * @returns {vec2} out - */ - function negate(out, a) { - out[0] = -a[0] - out[1] = -a[1] - return out - } - -/***/ }), -/* 190 */ -/***/ (function(module, exports) { - - module.exports = normalize - - /** - * Normalize a vec2 - * - * @param {vec2} out the receiving vector - * @param {vec2} a vector to normalize - * @returns {vec2} out - */ - function normalize(out, a) { - var x = a[0], - y = a[1] - var len = x*x + y*y - 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 - } - return out - } - -/***/ }), -/* 191 */ -/***/ (function(module, exports) { - - module.exports = dot - - /** - * Calculates the dot product of two vec2's - * - * @param {vec2} a the first operand - * @param {vec2} 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] - } - -/***/ }), -/* 192 */ -/***/ (function(module, exports) { - - module.exports = cross - - /** - * Computes the cross product of two vec2's - * Note that the cross product must by definition produce a 3D vector - * - * @param {vec3} out the receiving vector - * @param {vec2} a the first operand - * @param {vec2} b the second operand - * @returns {vec3} out - */ - function cross(out, a, b) { - var z = a[0] * b[1] - a[1] * b[0] - out[0] = out[1] = 0 - out[2] = z - return out - } - -/***/ }), -/* 193 */ -/***/ (function(module, exports) { - - module.exports = lerp - - /** - * Performs a linear interpolation between two vec2's - * - * @param {vec2} out the receiving vector - * @param {vec2} a the first operand - * @param {vec2} b the second operand - * @param {Number} t interpolation amount between the two inputs - * @returns {vec2} out - */ - function lerp(out, a, b, t) { - var ax = a[0], - ay = a[1] - out[0] = ax + t * (b[0] - ax) - out[1] = ay + t * (b[1] - ay) - return out - } - -/***/ }), -/* 194 */ -/***/ (function(module, exports) { - - module.exports = random - - /** - * Generates a random vector with the given scale - * - * @param {vec2} out the receiving vector - * @param {Number} [scale] Length of the resulting vector. If ommitted, a unit vector will be returned - * @returns {vec2} out - */ - function random(out, scale) { - scale = scale || 1.0 - var r = Math.random() * 2.0 * Math.PI - out[0] = Math.cos(r) * scale - out[1] = Math.sin(r) * scale - return out - } - -/***/ }), -/* 195 */ -/***/ (function(module, exports) { - - module.exports = transformMat2 - - /** - * Transforms the vec2 with a mat2 - * - * @param {vec2} out the receiving vector - * @param {vec2} a the vector to transform - * @param {mat2} m matrix to transform with - * @returns {vec2} out - */ - function transformMat2(out, a, m) { - var x = a[0], - y = a[1] - out[0] = m[0] * x + m[2] * y - out[1] = m[1] * x + m[3] * y - return out - } - -/***/ }), -/* 196 */ -/***/ (function(module, exports) { - - module.exports = transformMat2d - - /** - * Transforms the vec2 with a mat2d - * - * @param {vec2} out the receiving vector - * @param {vec2} a the vector to transform - * @param {mat2d} m matrix to transform with - * @returns {vec2} out - */ - function transformMat2d(out, a, m) { - var x = a[0], - y = a[1] - out[0] = m[0] * x + m[2] * y + m[4] - out[1] = m[1] * x + m[3] * y + m[5] - return out - } - -/***/ }), -/* 197 */ -/***/ (function(module, exports) { - - module.exports = transformMat3 - - /** - * Transforms the vec2 with a mat3 - * 3rd vector component is implicitly '1' - * - * @param {vec2} out the receiving vector - * @param {vec2} a the vector to transform - * @param {mat3} m matrix to transform with - * @returns {vec2} out - */ - function transformMat3(out, a, m) { - var x = a[0], - y = a[1] - out[0] = m[0] * x + m[3] * y + m[6] - out[1] = m[1] * x + m[4] * y + m[7] - return out - } - -/***/ }), -/* 198 */ -/***/ (function(module, exports) { - - module.exports = transformMat4 - - /** - * Transforms the vec2 with a mat4 - * 3rd vector component is implicitly '0' - * 4th vector component is implicitly '1' - * - * @param {vec2} out the receiving vector - * @param {vec2} a the vector to transform - * @param {mat4} m matrix to transform with - * @returns {vec2} out - */ - function transformMat4(out, a, m) { - var x = a[0], - y = a[1] - out[0] = m[0] * x + m[4] * y + m[12] - out[1] = m[1] * x + m[5] * y + m[13] - return out - } - -/***/ }), -/* 199 */ -/***/ (function(module, exports, __webpack_require__) { - - module.exports = forEach - - var vec = __webpack_require__(172)() - - /** - * Perform some operation over an array of vec2s. - * - * @param {Array} a the array of vectors to iterate over - * @param {Number} stride Number of elements between the start of each vec2. If 0 assumes tightly packed - * @param {Number} offset Number of elements to skip at the beginning of the array - * @param {Number} count Number of vec2s 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 = 2 - } - - 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] - fn(vec, vec, arg) - a[i] = vec[0] - a[i+1] = vec[1] - } - - return a - } - -/***/ }), -/* 200 */ -/***/ (function(module, exports, __webpack_require__) { - - var inherit = __webpack_require__(8); - var registerRenderer = __webpack_require__(201).registerRenderer; - var renderer = __webpack_require__(202); - - /** - * Create a new instance of class vglRenderer. - * - * @class 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 (arg) { - 'use strict'; - - if (!(this instanceof vglRenderer)) { - return new vglRenderer(arg); - } - arg = arg || {}; - renderer.call(this, arg); - - var $ = __webpack_require__(1); - var vgl = __webpack_require__(86); - var mat4 = __webpack_require__(88); - var util = __webpack_require__(83); - var geo_event = __webpack_require__(9); - - 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(); - - 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]) { - /* we want positive z to be closer to the camera, but webGL does the - * converse, so reverse the z coordinates. */ - proj = mat4.scale(util.mat4AsArray(), proj, [1, 1, -1]); - } - /* A similar kluge as in the base camera class worldToDisplay4. With this, - * we can show z values from 0 to 1. */ - proj = mat4.translate(util.mat4AsArray(), proj, - [0, 0, camera.constructor.bounds.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); - - (function () { - 'use strict'; - - 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; - - -/***/ }), -/* 201 */ -/***/ (function(module, exports, __webpack_require__) { - - var $ = __webpack_require__(1); - 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. - * - * @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. - * - * @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. - * - * @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. - * - * @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. - * - * @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. - * - * @param {array|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. - * - * @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. - * - * @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. - * - * @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. - * - * @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. - * - * @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 {array} [defaultFeatures] An optional list of feature capabailities - * 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. - * - * @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 - 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. - * - * @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. - * - * @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. - * - * @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. - * - * @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. - * - * @returns {array} A list of registered annotations. - */ - util.listAnnotations = function () { - return Object.keys(annotations); - }; - - /** - * Get a list of required features for a set of annotations. - * - * @param {array|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 {array} 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. - * - * @param {array|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)); - }; - - module.exports = util; - - -/***/ }), -/* 202 */ -/***/ (function(module, exports, __webpack_require__) { - - var inherit = __webpack_require__(8); - var object = __webpack_require__(203); - - /** - * Create a new instance of class renderer. - * - * @class 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 (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; - - -/***/ }), -/* 203 */ -/***/ (function(module, exports, __webpack_require__) { - - var vgl = __webpack_require__(86); - var inherit = __webpack_require__(8); - - /** - * Create a new instance of class object. - * - * @class - * @alias geo.object - * @extends vgl.object - * @returns {geo.object} - */ - var object = function () { - 'use strict'; - if (!(this instanceof object)) { - return new object(); - } - - var util = __webpack_require__(83); - - var m_this = this, - m_eventHandlers = {}, - m_idleHandlers = [], - m_promiseCount = 0; - - /** - * Bind a handler that will be called once when all internal promises are - * resolved. - * - * @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; - }; - - /** - * 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 falsey 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; - - -/***/ }), -/* 204 */ -/***/ (function(module, exports, __webpack_require__) { - - /* - 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__(1); - var L = {}; - L.Util = { - // return unique ID of an object - stamp: function (obj) { - obj._leaflet_id = obj._leaflet_id || ++L.Util.lastId; - return obj._leaflet_id; - }, - lastId: 0 - }; - - /** - * @class - * @alias geo.util.DistanceGrid - */ - var DistanceGrid = function (cellSize) { - this._cellSize = cellSize; - this._sqCellSize = cellSize * cellSize; - this._grid = {}; - this._objectPoint = {}; - }; - - DistanceGrid.prototype = { - - addObject: function (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 (obj, point) { - this.removeObject(obj); - this.addObject(obj, point); - }, - - //Returns true if the object was found - removeObject: function (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 (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 (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 () { - return $.map(this._objectPoint, function (val) { return val; }); - }, - - _getCoord: function (x) { - return Math.floor(x / this._cellSize); - }, - - _sqDist: function (p, p2) { - var dx = p2.x - p.x, - dy = p2.y - p.y; - return dx * dx + dy * dy; - } - }; - - module.exports = DistanceGrid; - - -/***/ }), -/* 205 */ -/***/ (function(module, exports, __webpack_require__) { - - /** - * Using methods adapted from leaflet to cluster an array of positions - * hierarchically given an array of length scales (zoom levels). - */ - - var $ = __webpack_require__(1); - var vgl = __webpack_require__(86); - - /** - * 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 - * @private - * - * @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) - */ - 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. - */ - 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__(204); - - // 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 - */ - 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 - */ - 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 - * @return {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 - * @return {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; - - -/***/ }), -/* 206 */ -/***/ (function(module, exports, __webpack_require__) { - - var inherit = __webpack_require__(8); - var feature = __webpack_require__(207); - var timestamp = __webpack_require__(209); - var transform = __webpack_require__(11); - var util = __webpack_require__(83); - - /** - * Line feature specification. - * - * @typedef {geo.feature.spec} geo.lineFeature.spec - * @param {object|Function} [position] Position of the data. Default is - * (data). - * @param {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)`. - * @param {object} [style] Style object with default style options. - * @param {geo.geoColor|Function} [style.strokeColor] Color to stroke each - * line. The color can vary by point. - * @param {number|Function} [style.strokeOpacity] Opacity for each line - * stroke. The opacity can vary by point. Opacity is on a [0-1] scale. - * @param {number|Function} [style.strokeWidth] The weight of the line - * stroke in pixels. The width can vary by point. - * @param {number|Function} [style.strokeOffset] This is a value from -1 - * (left) to 1 (right), with 0 being centered. This can vary by point. - * @param {string|Function} [style.lineCap='butt'] One of 'butt', 'square', or - * 'round'. This can vary by point. - * @param {string|Function} [style.lineJoin='miter'] One of 'miter', 'bevel', - * 'round', or 'miter-clip'. This can vary by point. - * @param {boolean|Function} [style.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)`. - * @param {number|Function} [style.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)`. - * @param {number|Function} [style.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. - * @param {string|Function} [style.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 geo.lineFeature - * @extends geo.feature - * @param {geo.lineFeature.spec} arg - * @returns {geo.lineFeature} - */ - var lineFeature = function (arg) { - 'use strict'; - if (!(this instanceof lineFeature)) { - return new lineFeature(arg); - } - - var $ = __webpack_require__(1); - - 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 lineaccessor. - * - * @param {object} [val] if specified, use this for the line accessor - * and return the feature. If not specified, return the current line. - * @returns {object|this} The current line 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 {object} [val] if specified, use this for the position accessor - * and return the feature. If not specified, return the current - * position. - * @returns {object|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 linen 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 (d) { return d; }, - position: function (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; - - -/***/ }), -/* 207 */ -/***/ (function(module, exports, __webpack_require__) { - - var $ = __webpack_require__(1); - var inherit = __webpack_require__(8); - var sceneObject = __webpack_require__(208); - var timestamp = __webpack_require__(209); - var geo_event = __webpack_require__(9); - - /** - * 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 `geo.event.feature`. - * They can be bound via a call like - * <pre><code> - * feature.geoOn(geo.event.feature.mousemove, function (evt) { - * // do something with the feature - * }); - * </code></pre> - * where the handler is passed a `geo.feature.event` 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 {@linkcode geo.transform}. - * @property {number} [bin=0] 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. - * @property {geo.renderer?} [renderer] A reference to the renderer used for - * the feature. - * @property {object} [style] An object that contains style values for the - * feature. - * @property {function|number} [style.opacity=1] The opacity 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 - * `geo.event.feature.mousemove`, `geo.event.feature.mouseclick`, - * `geo.event.feature.mouseover`, `geo.event.feature.mouseout`, - * `geo.event.feature.brush`, and `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 - * `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 (arg) { - 'use strict'; - if (!(this instanceof feature)) { - return new feature(arg); - } - sceneObject.call(this); - - var util = __webpack_require__(83); - - /** - * @private - */ - arg = arg || {}; - - var m_this = this, - s_exit = this._exit, - 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 ? 0 : 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 = {}; - - /** - * 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 (!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 - * @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: 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 - */ - 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: 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. - * - * @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. - * - * @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 () { - return util.convertColor( - m_style[key].apply(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 `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 {@linkcode 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, 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. - * - * @param {number} [val] The new bin number. If `undefined`, return the - * current bin number. - * @returns {number|this} The current bin number or a reference to `this`. - */ - this.bin = function (val) { - if (val === undefined) { - 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; - this._unbindMouseHandlers(); - this._bindMouseHandlers(); - } - return 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 - * `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(); - }; - - /** - * 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(); - }; - - this._init(arg); - return this; - }; - - /** - * The most recent `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__(210))) { - console.warn('Invalid layer'); - return null; - } - if (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; - - -/***/ }), -/* 208 */ -/***/ (function(module, exports, __webpack_require__) { - - var inherit = __webpack_require__(8); - var object = __webpack_require__(203); - - /** - * 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 (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; - - -/***/ }), -/* 209 */ -/***/ (function(module, exports, __webpack_require__) { - - var vgl = __webpack_require__(86); - var inherit = __webpack_require__(8); - - /** - * Create a new instance of class timestamp. - * - * @class geo.timestamp - * @extends vgl.timestamp - * @returns {geo.timestamp} - */ - var timestamp = function () { - 'use strict'; - if (!(this instanceof timestamp)) { - return new timestamp(); - } - vgl.timestamp.call(this); - }; - - inherit(timestamp, vgl.timestamp); - module.exports = timestamp; - - -/***/ }), -/* 210 */ -/***/ (function(module, exports, __webpack_require__) { - - var inherit = __webpack_require__(8); - var sceneObject = __webpack_require__(208); - var feature = __webpack_require__(207); - var checkRenderer = __webpack_require__(201).checkRenderer; - var rendererForFeatures = __webpack_require__(201).rendererForFeatures; - var rendererForAnnotations = __webpack_require__(201).rendererForAnnotations; - - /** - * Create a new layer. - * - * @class - * @alias geo.layer - * @extends geo.sceneObject - * @param {object} [arg] Options for the new layer. - * @param {string} arg.attribution An attribution string to display - * @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 (arg) { - 'use strict'; - - if (!(this instanceof layer)) { - return new layer(arg); - } - arg = arg || {}; - sceneObject.call(this, arg); - - var $ = __webpack_require__(1); - var timestamp = __webpack_require__(209); - var createRenderer = __webpack_require__(201).createRenderer; - var newLayerId = __webpack_require__(83).newLayerId; - var geo_event = __webpack_require__(9); - var camera = __webpack_require__(211); - - /** - * @private - */ - var m_this = this, - s_exit = this._exit, - m_id = arg.id === undefined ? layer.newLayerId() : arg.id, - m_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 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. - m_map.children().reverse().forEach(function (child) { - if (child.zIndex && child !== this && child.zIndex() === zIndex) { - child.zIndex(zIndex + 1); - } - }); - } - m_zIndex = zIndex; - m_node.css('z-index', 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 = m_this.map().layers().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(m_this.map().children().length - 1); - }; - - /** - * Bring the layer to the bottom of the map layers. - * - * @returns {this} - */ - this.moveToBottom = function () { - return m_this.moveDown(m_this.map().children().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; - }; - - if (arg.zIndex === undefined) { - var maxZ = -1; - m_map.children().forEach(function (child) { - if (child.zIndex) { - maxZ = Math.max(maxZ, child.zIndex()); - } - }); - arg.zIndex = maxZ + 1; - } - m_zIndex = arg.zIndex; - - // 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 - m_this.zIndex(m_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; - - -/***/ }), -/* 211 */ -/***/ (function(module, exports, __webpack_require__) { - - (function () { - 'use strict'; - - var inherit = __webpack_require__(8); - var object = __webpack_require__(203); - var util = __webpack_require__(83); - var mat4 = __webpack_require__(88); - var vec4 = __webpack_require__(112); - - /** - * 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 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 (spec) { - if (!(this instanceof camera)) { - return new camera(spec); - } - - var geo_event = __webpack_require__(9); - - 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 s = this.constructor.bounds.near / this.constructor.bounds.far; - - // call mat4.frustum or mat4.ortho here - if (this._projection === 'perspective') { - mat4.frustum( - this._proj, - this.constructor.bounds.left * s, - this.constructor.bounds.right * s, - this.constructor.bounds.bottom * s, - this.constructor.bounds.top * s, - -this.constructor.bounds.near, - -this.constructor.bounds.far - ); - } else if (this._projection === 'parallel') { - mat4.ortho( - this._proj, - this.constructor.bounds.left, - this.constructor.bounds.right, - this.constructor.bounds.bottom, - this.constructor.bounds.top, - this.constructor.bounds.near, - this.constructor.bounds.far - ); - } - }; - - /** - * Update the internal state of the camera on change to camera - * parameters. - * @protected - */ - this._update = function () { - this._bounds = null; - this._display = null; - this._world = null; - this._transform = camera.combine(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. - */ - Object.defineProperty(this, 'view', { - get: function () { - return this._view; - }, - set: function (view) { - mat4.copy(this._view, view); - this._update(); - } - }); - - /** - * Getter/setter for the view bounds. - * - * If not provided, near and far bounds will be set to [-1, 1] by - * default. We will probably want to change this to a unit specific - * value initialized by the map when drawing true 3D objects or - * tilting the camera. - * - * Returned near/far bounds are also -1, 1 for the moment. - */ - Object.defineProperty(this, 'bounds', { - get: function () { - if (this._bounds === null) { - this._bounds = this._getBounds(); - } - return this._bounds; - }, - set: function (bounds) { - this._setBounds(bounds); - this._update(); - } - }); - - /** - * Getter for the "display" matrix. This matrix converts from - * world coordinates into display coordinates. This matrix exists to - * generate matrix3d css transforms that can be used in layers that - * render on the DOM. - */ - Object.defineProperty(this, 'display', { - get: function () { - var mat; - if (this._display === null) { - mat = camera.affine( - {x: 1, y: 1}, // translate to: [0, 2] x [0, 2] - { - x: this.viewport.width / 2, - y: this.viewport.height / -2 - } // scale to: [0, width] x [-height, 0] - ); - - // applies mat to the transform (world -> normalized) - this._display = camera.combine( - mat, - this._transform - ); - } - return this._display; - } - }); - - /** - * Getter for the "world" matrix. This matrix converts from - * display coordinates into world coordinates. This is constructed - * by inverting the "display" matrix. - */ - Object.defineProperty(this, 'world', { - get: function () { - if (this._world === null) { - this._world = mat4.invert( - util.mat4AsArray(), - this.display - ); - } - return this._world; - } - }); - - /** - * Getter/setter for the projection type. - */ - Object.defineProperty(this, 'projection', { - get: function () { - return this._projection; - }, - set: function (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 (when applicable). - * This generally shouldn't be modified directly because - * the rest of the code assumes that the clipping bounds - * are [-1, -1, -1] to [1, 1, 1] in camera coordinates. - */ - Object.defineProperty(this, 'projectionMatrix', { - get: function () { - return this._proj; - } - }); - - /** - * Getter for the transform matrix. - */ - Object.defineProperty(this, 'transform', { - get: function () { - return this._transform; - } - }); - - /** - * Getter for the inverse transform matrix. - */ - Object.defineProperty(this, 'inverse', { - get: function () { - 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. - */ - Object.defineProperty(this, 'viewport', { - get: function () { - return { - width: this._viewport.width, - height: this._viewport.height, - left: this._viewport.left, - top: this._viewport.top - }; - }, - set: function (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 camera.applyTransform(this._transform, point); - }; - - /** - * 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 camera.applyTransform(this._inverse, point); - }; - - /** - * 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 vec4 from world space into viewport space. - * @param {vec4} point The point in world coordinates (mutated). - * @returns {vec4} The point in display coordinates. - * - * @note For the moment, this computation assumes the following: - * * point[3] > 0 - * * depth range [0, 1] - * - * The clip space z and w coordinates are returned with the window - * x/y coordinates. - */ - this.worldToDisplay4 = function (point) { - // This is because z = 0 is the far plane exposed to the user, but - // internally the far plane is at -2. - point[2] -= 2; - - // convert to clip space - this._worldToClip4(point); - - // apply projection specific transformation - point = this.applyProjection(point); - - // convert to display space - point[0] = this._viewport.width * (1 + point[0]) / 2.0; - point[1] = this._viewport.height * (1 - point[1]) / 2.0; - point[2] = (1 + point[2]) / 2.0; - return point; - }; - - /** - * Project a vec4 from display space into world space in place. - * @param {vec4} point The point in display coordinates (mutated). - * @returns {vec4} The point in world space coordinates. - * - * @note For the moment, this computation assumes the following: - * * point[3] > 0 - * * depth range [0, 1] - */ - this.displayToWorld4 = function (point) { - // convert to clip space - point[0] = 2 * point[0] / this._viewport.width - 1; - point[1] = -2 * point[1] / this._viewport.height + 1; - point[2] = 2 * point[2] - 1; - - // invert projection transform - point = this.unapplyProjection(point); - - // convert to world coordinates - this._clipToWorld4(point); - - // move far surface to z = 0 - point[2] += 2; - return point; - }; - - /** - * Project a point object from world space into viewport 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) { - // define some magic numbers: - var z = 0, // z coordinate of the surface in world coordinates - w = 1; // enables perspective divide (i.e. for point conversion) - point = this.worldToDisplay4( - [point.x, point.y, z, w] - ); - return {x: point[0], y: point[1]}; - }; - - /** - * Project a point object from viewport space into world 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) { - // define some magic numbers: - var z = 1, // the z coordinate of the surface - w = 2; // perspective divide at z = 1 - point = this.displayToWorld4( - [point.x, point.y, z, w] - ); - 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. - * - * @note This transform will not take into account projection specific - * transforms. For perspective projections, one can use the properties - * `perspective` and `perspective-origin` to apply the projection - * in css directly. - * - * @param {string} transform The transform to return - * * display - * * 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; - }; - - // 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. - */ - camera.projection = { - perspective: true, - parallel: true - }; - - /** - * Camera clipping bounds, probably shouldn't be modified. - */ - camera.bounds = { - left: -1, - right: 1, - top: 1, - bottom: -1, - far: -2, - near: -1 - }; - - /** - * Output a mat4 as a css transform. - * @param {mat4} t A matrix transform. - * @returns {string} A css transform string. - */ - camera.css = function (t) { - return ( - 'matrix3d(' + - [ - t[0].toFixed(20), - t[1].toFixed(20), - t[2].toFixed(20), - t[3].toFixed(20), - t[4].toFixed(20), - t[5].toFixed(20), - t[6].toFixed(20), - t[7].toFixed(20), - t[8].toFixed(20), - t[9].toFixed(20), - t[10].toFixed(20), - t[11].toFixed(20), - t[12].toFixed(20), - t[13].toFixed(20), - t[14].toFixed(20), - t[15].toFixed(20) - ].join(',') + - ')' - ); - }; - - /** - * Generate a mat4 representing an affine coordinate transformation. - * - * For the following affine transform: - * - * x |-> m * (x + a) + b - * - * applies the css transform: - * - * translate(b) scale(m) translate(a) . - * - * If a parameter is `null` or `undefined`, that component is skipped. - * - * @param {object?} pre Coordinate offset **before** scaling. - * @param {object?} scale Coordinate scaling. - * @param {object?} post Coordinate offset **after** scaling. - * @returns {mat4} The new transform matrix. - */ - camera.affine = function (pre, scale, post) { - var mat = util.mat4AsArray(); - - // Note: mat4 operations are applied to the right side of the current - // transform, so the first applied here is the last applied to the - // coordinate. - if (post) { - mat4.translate(mat, mat, [post.x || 0, post.y || 0, post.z || 0]); - } - if (scale) { - mat4.scale(mat, mat, [scale.x || 1, scale.y || 1, scale.z || 1]); - } - if (pre) { - mat4.translate(mat, mat, [pre.x || 0, pre.y || 0, pre.z || 0]); - } - return mat; - }; - - /** - * Apply the given transform matrix to a point in place. - * @param {mat4} t - * @param {vec4} pt - * @returns {vec4} - */ - camera.applyTransform = function (t, pt) { - return vec4.transformMat4(pt, pt, t); - }; - - /** - * Combine two transforms by multiplying their matrix representations. - * @note The second transform provided will be the first applied in the - * coordinate transform. - * @param {mat4} A - * @param {mat4} B - * @returns {mat4} A * B - */ - camera.combine = function (A, B) { - return mat4.multiply(util.mat4AsArray(), A, B); - }; - - inherit(camera, object); - module.exports = camera; - })(); - - -/***/ }), -/* 212 */ -/***/ (function(module, exports, __webpack_require__) { - - var inherit = __webpack_require__(8); - var feature = __webpack_require__(207); - - /** - * Create a new instance of class pointFeature - * - * @class geo.pointFeature - * @param {object} arg Options object - * @param {boolean} arg.clustering Enable point clustering - * @extends geo.feature - * @returns {geo.pointFeature} - */ - var pointFeature = function (arg) { - 'use strict'; - if (!(this instanceof pointFeature)) { - return new pointFeature(arg); - } - arg = arg || {}; - feature.call(this, arg); - - var $ = __webpack_require__(1); - var timestamp = __webpack_require__(209); - var ClusterGroup = __webpack_require__(205); - var geo_event = __webpack_require__(9); - var util = __webpack_require__(83); - var kdbush = __webpack_require__(213); - - /** - * @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 - * - * @returns {geo.pointFeature|boolean} - */ - 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. - */ - 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 - * - * @returns {geo.pointFeature} - */ - 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.__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); - - // 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.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; - 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.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); - 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. - */ - 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 - */ - 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, - sprites: false, - sprites_image: null, - position: function (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; - }; - - /** - * Object specification for a point feature. - * - * @extends geo.feature.spec // need to make a jsdoc plugin for this to work - * @typedef geo.pointFeature.spec - * @type {object} - */ - - /** - * 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; - - -/***/ }), -/* 213 */ -/***/ (function(module, exports, __webpack_require__) { - - 'use strict'; - - var sort = __webpack_require__(214); - var range = __webpack_require__(215); - var within = __webpack_require__(216); - - 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]; } - - -/***/ }), -/* 214 */ -/***/ (function(module, exports) { - - '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; - } - - -/***/ }), -/* 215 */ -/***/ (function(module, exports) { - - '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; - } - - -/***/ }), -/* 216 */ -/***/ (function(module, exports) { - - '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; - } - - -/***/ }), -/* 217 */ -/***/ (function(module, exports, __webpack_require__) { - - var $ = __webpack_require__(1); - var inherit = __webpack_require__(8); - var feature = __webpack_require__(207); - var transform = __webpack_require__(11); - - /** - * Polygon feature specification. - * - * @typedef {geo.feature.spec} geo.polygonFeature.spec - * @param {object|Function} [position] Position of the data. Default is - * (data). - * @param {object|Function} [polygon] Polygons from the data. Default is - * (data). Typically, the data is an array of polygons, each of which is of - * the form {outer: [(coordinates)], inner: [[(coordinates of first hole)], - * [(coordinates of second hole)], ...]}. The inner record is optional. - * Alternately, if there are no holes, a polygon can just be an array of - * coordinates in the form of geo.geoPosition. The first and last point of - * each polygon may be the same. - * @param {object} [style] Style object with default style options. - * @param {boolean|Function} [style.fill] True to fill polygon. Defaults to - * true. - * @param {geo.geoColor|Function} [style.fillColor] Color to fill each polygon. - * The color can vary by vertex. - * @param {number|Function} [style.fillOpacity] Opacity for each polygon. The - * opacity can vary by vertex. Opacity is on a [0-1] scale. - * @param {boolean|Function} [style.stroke] True to stroke polygon. Defaults - * to false. - * @param {geo.geoColor|Function} [style.strokeColor] Color to stroke each - * polygon. The color can vary by vertex. - * @param {number|Function} [style.strokeOpacity] Opacity for each polygon - * stroke. The opacity can vary by vertex. Opacity is on a [0-1] scale. - * @param {number|Function} [style.strokeWidth] The weight of the polygon - * stroke in pixels. The width can vary by vertex. - * @param {boolean|Function} [style.uniformPolygon] Boolean indicating if each - * polygon has a uniform style (uniform fill color, fill opacity, stroke - * color, and stroke opacity). Defaults to false. Can vary by polygon. - */ - - /** - * 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 (arg) { - 'use strict'; - if (!(this instanceof polygonFeature)) { - return new polygonFeature(arg); - } - arg = arg || {}; - feature.call(this, arg); - - var util = __webpack_require__(83); - - /** - * @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 {object[]} 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 {object[]} 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} [val] if specified, use this for the polygon accessor - * and return the feature. If not specified, return the current polygon. - * @returns {object|this} The current polygon 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 {object} [val] if specified, use this for the position accessor - * and return the feature. If not specified, return the current - * position. - * @returns {object|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. - * - * @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 - * `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 (d) { return d; }, - position: function (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; - - -/***/ }), -/* 218 */ -/***/ (function(module, exports, __webpack_require__) { - - var inherit = __webpack_require__(8); - var feature = __webpack_require__(207); - - /** - * 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 {object} [style] The style to apply to each data element. - * @property {boolean|function} [style.visible=true] If falsy, don't show this - * data element. - * @property {string|function} [style.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} [style.fontStyle='normal'] The font style. One - * of `normal`, `italic`, or `oblique`. - * @property {string|function} [style.fontVariant='normal'] The font variant. - * This can have values such as `small-caps` or `slashed-zero`. - * @property {string|function} [style.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} [style.fontStretch='normal'] The font stretch, - * such as `condensed`. - * @property {string|function} [style.fontSize='medium'] The font size. - * @property {string|function} [style.lineHeight='normal'] The font line - * height. - * @property {string|function} [style.fontFamily] The font family. - * @property {string|function} [style.textAlign='center'] The horizontal text - * alignment. One of `start`, `end`, `left`, `right`, or `center`. - * @property {string|function} [style.textBaseline='middle'] The vertical text - * alignment. One of `top`, `hanging`, `middle`, `alphabetic`, - * `ideographic`, or `bottom`. - * @property {geo.geoColor|function} [style.color='black'] Text color. May - * include opacity. - * @property {number|function} [style.textOpacity=1] The opacity of the text. - * If the color includes opacity, this is combined with that value. - * @property {number|function} [style.rotation=0] Text rotation in radians. - * @property {boolean|function} [style.rotateWithMap=false] If truthy, rotate - * the text when the map rotates. Otherwise, the text is always in the - * same orientation. - * @property {number|function} [style.scale=4] The zoom basis value used when - * `scaleWithMap` is truthy. - * @property {boolean|function} [style.scaleWithMap=false] If truthy, use the - * `scale` style as the basis of the map zoom value for the font size. - * The size is scaled from this point. - * @property {geo.screenPosition|function} [style.offset] Offset from the - * default position for the text. This is applied before rotation. - * @property {geo.geoColor|function} [style.shadowColor='black'] Text shadow - * color. May include opacity. - * @property {geo.screenPosition|function} [style.shadowOffset] Offset for a - * text shadow. This is applied before rotation. - * @property {number|null|function} [style.shadowBlur] If not null, add a text - * shadow with this much blur. - * @property {boolean|function} [style.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} [style.textStrokeColor='transparent'] Text - * stroke color. May include opacity. - * @property {geo.geoColor|function} [style.textStrokeWidth=0] Text stroke - * width in pixels. - */ - - /** - * 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 (arg) { - 'use strict'; - if (!(this instanceof textFeature)) { - return new textFeature(arg); - } - arg = arg || {}; - feature.call(this, arg); - - var $ = __webpack_require__(1); - - /** - * @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 (d) { return d; }, - text: function (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; - - -/***/ }), -/* 219 */ -/***/ (function(module, exports, __webpack_require__) { - - var inherit = __webpack_require__(8); - var featureLayer = __webpack_require__(220); - var geo_annotation = __webpack_require__(7); - var geo_event = __webpack_require__(9); - var registry = __webpack_require__(201); - var transform = __webpack_require__(11); - var $ = __webpack_require__(1); - var Mousetrap = __webpack_require__(221); - var textFeature = __webpack_require__(218); - - /** - * @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 {object} [style] A `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} - */ - var annotationLayer = function (args) { - 'use strict'; - if (!(this instanceof annotationLayer)) { - return new annotationLayer(args); - } - featureLayer.call(this, args); - - var mapInteractor = __webpack_require__(222); - var timestamp = __webpack_require__(209); - var util = __webpack_require__(83); - - 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; - - 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. - */ - 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); - 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. - */ - 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. - */ - 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. - * @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. - */ - 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_mode) { - Mousetrap(mapNode[0]).bind('esc', function () { m_this.mode(null); }); - } else { - Mousetrap(mapNode[0]).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('jsonReader', {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. - */ - 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; - } - break; - case 'polygon': - position = feature.polygon()(data, data_idx); - if (!position || !position.outer || position.outer.length < 3) { - return; - } - position = position.outer; - 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. - */ - this._updateLabels = function (labels) { - if (!labels || !labels.length) { - m_this._removeLabelFeature(); - return m_this; - } - if (!m_labelFeature) { - var renderer = registry.rendererForFeatures(['text']); - if (renderer !== m_this.renderer().api()) { - 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 (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. - */ - 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 () { - 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; - - -/***/ }), -/* 220 */ -/***/ (function(module, exports, __webpack_require__) { - - var inherit = __webpack_require__(8); - var layer = __webpack_require__(210); - var geo_event = __webpack_require__(9); - var registry = __webpack_require__(201); - - /** - * 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 (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); - } - 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; - - -/***/ }), -/* 221 */ -/***/ (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.1 - * @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); - - -/***/ }), -/* 222 */ -/***/ (function(module, exports, __webpack_require__) { - - var inherit = __webpack_require__(8); - var object = __webpack_require__(203); - var util = __webpack_require__(83); - var Mousetrap = __webpack_require__(221); - - /** - * 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 (args) { - 'use strict'; - if (!(this instanceof mapInteractor)) { - return new mapInteractor(args); - } - object.call(this); - - var $ = __webpack_require__(1); - var geo_event = __webpack_require__(9); - var geo_action = __webpack_require__(10); - var throttle = __webpack_require__(83).throttle; - var debounce = __webpack_require__(83).debounce; - var actionMatch = __webpack_require__(83).actionMatch; - var quadFeature = __webpack_require__(223); - - 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 () {}; - - // 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 (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. - */ - 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. - */ - 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__(224); - } 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 () {}; - - 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. - */ - 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. - */ - 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). This is involed to work - * around modulo returning a signed value. */ - deltaTheta = ((theta - m_this.map().rotation()) % (Math.PI * 2) + - Math.PI * 3) % (Math.PI * 2) - Math.PI; - /* 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. - */ - 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. - */ - 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. - */ - 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 (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. - */ - 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 () { return options.keys; }, - stopPropagation: function () {}, - preventDefault: function () {}, - 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 () {}, - stopPropagation: function () {}, - stopImmediatePropagation: function () {} - } - } - ); - 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; - - -/***/ }), -/* 223 */ -/***/ (function(module, exports, __webpack_require__) { - - var $ = __webpack_require__(1); - var inherit = __webpack_require__(8); - var feature = __webpack_require__(207); - - /** - * Quad feature specification. - * - * @typedef {geo.feature.spec} geo.quadFeature.spec - * @param {geo.geoColor|Function} [color] Color for quads without images. - * Default is white ({r: 1, g: 1, b: 1}). - * @param {number|Function} [opacity=1] Opacity for the quads. - * @param {number|Function} [depth=0] Default z-coordinate for positions that - * don't explicitly specify one. - * @param {boolean|Function} [drawOnAsyncResourceLoaded=true] Redraw quads - * when images or videos are loaded after initial render. - * @param {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. - * @param {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. - * @param {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. - * @param {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. - * @param {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. - * @param {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. - * @param {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 geo.quadFeature - * @param {geo.quadFeature.spec} arg Options object. - * @extends geo.feature - * @returns {geo.quadFeature} - */ - var quadFeature = function (arg) { - 'use strict'; - - var transform = __webpack_require__(11); - var util = __webpack_require__(83); - - 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 (d) { return d.image; }, - video: function (d) { return d.video; }, - position: function (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; - - -/***/ }), -/* 224 */ -/***/ (function(module, exports) { - - if(typeof __WEBPACK_EXTERNAL_MODULE_224__ === 'undefined') {var e = new Error("Cannot find module \"hammerjs\""); e.code = 'MODULE_NOT_FOUND'; throw e;} - module.exports = __WEBPACK_EXTERNAL_MODULE_224__; - -/***/ }), -/* 225 */ -/***/ (function(module, exports, __webpack_require__) { - - var inherit = __webpack_require__(8); - var feature = __webpack_require__(207); - - /** - * Create a new instance of class choroplethFeature - * - * @class 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 (arg) { - 'use strict'; - if (!(this instanceof choroplethFeature)) { - return new choroplethFeature(arg); - } - arg = arg || {}; - feature.call(this, arg); - - var $ = __webpack_require__(1); - var ensureFunction = __webpack_require__(83).ensureFunction; - - /** - * @private - */ - var d3 = __webpack_require__(226).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 (geoFeature) { - return geoFeature.properties.GEO_ID; - }, - //accessor for ID on scalar element - scalarId: function (scalarElement) { - return scalarElement.id; - }, - //accessor for value on scalar element - scalarValue: function (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. - * - * @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; - - -/***/ }), -/* 226 */ -/***/ (function(module, exports, __webpack_require__) { - - var inherit = __webpack_require__(8); - var registerRenderer = __webpack_require__(201).registerRenderer; - var renderer = __webpack_require__(202); - - /** - * Create a new instance of class d3Renderer. - * - * @class 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 (arg) { - 'use strict'; - - var d3 = d3Renderer.d3; - var object = __webpack_require__(227); - var util = __webpack_require__(83); - var geo_event = __webpack_require__(9); - var d3Rescale = __webpack_require__(229); - - 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 {function|object} 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 {function|object} 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} - */ - 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); - - (function () { - 'use strict'; - - /** - * 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__(230); - } 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; - - -/***/ }), -/* 227 */ -/***/ (function(module, exports, __webpack_require__) { - - var inherit = __webpack_require__(8); - var sceneObject = __webpack_require__(208); - - /** - * 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 (arg) { - 'use strict'; - - var object = __webpack_require__(203); - var uniqueID = __webpack_require__(228); - - // 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 () { - 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; - - -/***/ }), -/* 228 */ -/***/ (function(module, exports) { - - var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz', - strLength = 8; - - /** - * Get a random string to use as a div ID - * @function geo.d3.uniqueID - * @returns {string} - */ - var uniqueID = function () { - 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; - - -/***/ }), -/* 229 */ -/***/ (function(module, exports) { - - module.exports = 'geo_d3_rescale'; - - -/***/ }), -/* 230 */ -/***/ (function(module, exports) { - - if(typeof __WEBPACK_EXTERNAL_MODULE_230__ === 'undefined') {var e = new Error("Cannot find module \"d3\""); e.code = 'MODULE_NOT_FOUND'; throw e;} - module.exports = __WEBPACK_EXTERNAL_MODULE_230__; - -/***/ }), -/* 231 */ -/***/ (function(module, exports, __webpack_require__) { - - var inherit = __webpack_require__(8); - var feature = __webpack_require__(207); - - /** - * Contour feature specification. - * - * @typedef {geo.feature.spec} geo.contourFeature.spec - * @property {object[]} [data=[]] An array of arbitrary objects used to - * construct the feature. - * @property {object} [style] An object that contains style values for the - * feature. - * @property {function|number} [style.opacity=1] The opacity on a scale of 0 to - * 1. - * @property {function|geo.geoPosition} [style.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 {function|number} [style.value=data.z] The contour value of each - * data element. This defaults `z` properties of the data element. If - * the value of a grid point is `null` or `undefined`, that point will not - * be included in the contour display. Since the values are on a grid, if - * this point is in the interior of the grid, this can remove up to four - * squares. - * @property {geo.contourFeature.contourSpec} [contour] The contour - * specification for the feature. - */ - - /** - * Contour specification. - * - * @typedef {object} geo.contourFeature.contourSpec - * @property {function|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` not `gridHeight` are specified, the square root of the - * number of data elements is used. If both are specified, some data - * could be unused. - * @property {function|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 square root of the - * number of data elements is used. If both are specified, some data - * could be unused. - * @property {function|number} [x0] The x coordinate of the 0th point in the - * `value` array. If `null` or `undefined`, the coordinate is taken from - * the `position` style. - * @property {function|number} [y0] The y coordinate of the 0th point in the - * `value` array. If `null` or `undefined`, the coordinate is taken from - * the `position` style. - * @property {function|number} [dx] The distance in the x direction between the - * 0th and 1st point in the `value` array. This may be positive or - * negative. If 0, `null`, or `undefined`, the coordinate is taken from - * the `position` style. - * @property {function|number} [dy] The distance in the y direction between the - * 0th and `gridWidth`th point in the `value` array. This may be positive - * or negative. If 0, `null`, or `undefined`, the coordinate is taken - * from the `position` style. - * @property {function|boolean} [wrapLongitude] If truthy and `position` is not - * used (`x0`, `y0`, `dx`, and `dy` are all set appropriately), 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 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. This is ignored if - * `position` is used. - * @property {function|number} [min] Minimum contour value. If unspecified, - * taken from the computed minimum of the `value` style. - * @property {function|number} [max] Maximum contour value. If unspecified, - * taken from the computed maxi,um of the `value` style. - * @property {function|geo.geoColor} [minColor='black'] Color used for any - * value below the minimum. - * @property {function|number} [minOpacity=0] Opacity used for any value below - * the minimum. - * @property {function|geo.geoColor} [maxColor='black'] Color used for any - * value above the maximum. - * @property {function|number} [maxOpacity=0] Opacity used for any value above - * the maximum. - * @property {function|boolean} [stepped] If falsy but not `undefined`, smooth - * transitions between colors. - * @property {function|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 {function|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 {function|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 {object} geo.contourFeature.contourInfo - * @property {number[]} elements An array of 0-based indices into the values - * array. Each set of the three values forms a triangle that should be - * rendered. If no contour data can be used, this will be a zero-length - * array and other properties may not be set. - * @property {number[]} pos An flat array of coordinates for the vertices in - * the triangular mesh. The array is in the order x0, y0, z0, x1, y1, z1, - * x2, ..., and is always three times as long as the number of vertices. - * @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.feature - * - * @param {geo.contourFeature.spec} arg - * @returns {geo.contourFeature} - */ - var contourFeature = function (arg) { - 'use strict'; - if (!(this instanceof contourFeature)) { - return new contourFeature(arg); - } - - var $ = __webpack_require__(1); - var util = __webpack_require__(83); - - arg = arg || {}; - feature.call(this, arg); - - /** - * @private - */ - var m_this = this, - m_contour = {}, - s_init = this._init; - - if (arg.contour === undefined) { - m_contour = function (d) { - return d; - }; - } else { - m_contour = arg.contour; - } - - /** - * Get/Set contour accessor. - * - * @param {string|geo.contourFeature.contourSpec} [specOrProperty] If - * `undefined`, return the current contour specification. If a string is - * specified, either get or set the named contour property. If an object - * is given, set or update the contour 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.contourFeature.contourSpec|object|this} The current contour - * specification, the value of a named contour property, or this contour - * feature. - */ - this.contour = function (specOrProperty, value) { - if (specOrProperty === undefined) { - return m_contour; - } - if (typeof specOrProperty === 'string' && value === undefined) { - return m_contour[specOrProperty]; - } - if (value === undefined) { - var contour = $.extend( - {}, - { - gridWidth: function () { - if (specOrProperty.gridHeight) { - return Math.floor(m_this.data().length / specOrProperty.gridHeight); - } - return Math.floor(Math.sqrt(m_this.data().length)); - }, - gridHeight: function () { - if (specOrProperty.gridWidth) { - return Math.floor(m_this.data().length / specOrProperty.gridWidth); - } - return Math.floor(Math.sqrt(m_this.data().length)); - }, - 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} - ] - }, - m_contour, - specOrProperty - ); - m_contour = contour; - } else { - m_contour[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 contour values as an object. - * - * @param {string|undefined} key The name of the contour key or `undefined` - * to return an object with all keys as functions. - * @returns {function|object} A function related to the key, or an object - * with all contour keys, each of which is a function. - */ - this.contour.get = function (key) { - if (key === undefined) { - var all = {}, k; - for (k in m_contour) { - if (m_contour.hasOwnProperty(k)) { - all[k] = m_this.contour.get(k); - } - } - return all; - } - return util.ensureFunction(m_contour[key]); - }; - - /** - * Get/Set position accessor. This is identical to getting or setting the - * `position` style. - * - * @param {function|array} [val] If specified, set the position style. If - * `undefined`, return the current value. - * @returns {function|array|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, 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 i, i3, j, idx, k, val, numPts, usedPts = 0, usePos, item, - idxMap = {}, - minval, maxval, range, - contour = m_this.contour, - data = m_this.data(), - posFunc = m_this.position(), posVal, - gridW = contour.get('gridWidth')(), - gridH = contour.get('gridHeight')(), - x0 = contour.get('x0')(), - y0 = contour.get('y0')(), - dx = contour.get('dx')(), - dy = contour.get('dy')(), - opacityFunc = m_this.style.get('opacity'), - opacityRange = contour.get('opacityRange')(), - rangeValues = contour.get('rangeValues')(), - valueFunc = m_this.style.get('value'), values = [], - stepped = contour.get('stepped')(), - wrapLong = contour.get('wrapLongitude')(), - calcX, skipColumn, x, origI, /* used for wrapping */ - gridWorig = gridW, /* can be different when wrapping */ - result = { - minValue: contour.get('min')(), - maxValue: contour.get('max')(), - stepped: stepped === undefined || stepped ? true : false, - wrapLongitude: wrapLong === undefined || wrapLong ? true : false, - colorMap: [], - elements: [] - }; - /* Create the min/max colors and the color array */ - result.minColor = $.extend({a: contour.get('minOpacity')() || 0}, - util.convertColor(contour.get('minColor')())); - result.maxColor = $.extend({a: contour.get('maxOpacity')() || 0}, - util.convertColor(contour.get('maxColor')())); - contour.get('colorRange')().forEach(function (clr, idx) { - result.colorMap.push($.extend({ - a: opacityRange && opacityRange[idx] !== undefined ? opacityRange[idx] : 1 - }, util.convertColor(clr))); - }); - /* Determine which values are usable */ - 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 = (x0 === null || x0 === undefined || y0 === null || - y0 === undefined || !dx || !dy); - if (!usePos && result.wrapLongitude && (x0 < -180 || x0 > 180 || - x0 + dx * (gridW - 1) < -180 || x0 + dx * (gridW - 1) > 180) && - dx > -180 && dx < 180) { - 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; - for (i = 0; i < numPts; i += 1) { - if (skipColumn === undefined) { - val = parseFloat(valueFunc(data[i], i)); - } else { - j = Math.floor(i / gridW); - origI = i - j * gridW; - origI += (origI > skipColumn ? -2 : 0); - if (origI >= gridWorig) { - origI -= gridWorig; - } - origI += j * gridWorig; - val = parseFloat(valueFunc(data[origI], origI)); - } - values[i] = isNaN(val) ? null : val; - if (values[i] !== null) { - idxMap[i] = usedPts; - usedPts += 1; - if (minval === undefined) { - minval = maxval = values[i]; - } - if (values[i] < minval) { - minval = values[i]; - } - if (values[i] > maxval) { - maxval = values[i]; - } - } - } - if (!usedPts) { - return result; - } - if (!$.isNumeric(result.minValue)) { - result.minValue = minval; - } - if (!$.isNumeric(result.maxValue)) { - result.maxValue = maxval; - } - 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] > rangeValues[k + 1]) { - 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; - /* Create triangles */ - for (j = idx = 0; j < gridH - 1; j += 1, idx += 1) { - for (i = 0; i < gridW - 1; i += 1, idx += 1) { - if (values[idx] !== null && values[idx + 1] !== null && - values[idx + gridW] !== null && - values[idx + gridW + 1] !== null && i !== skipColumn) { - result.elements.push(idxMap[idx]); - result.elements.push(idxMap[idx + 1]); - result.elements.push(idxMap[idx + gridW]); - result.elements.push(idxMap[idx + gridW + 1]); - result.elements.push(idxMap[idx + gridW]); - result.elements.push(idxMap[idx + 1]); - } - } - } - /* Only locate the points that are in use. */ - result.pos = new Array(usedPts * 3); - result.value = new Array(usedPts); - result.opacity = new Array(usedPts); - for (j = i = i3 = 0; j < numPts; j += 1) { - val = values[j]; - if (val !== null) { - item = data[j]; - if (usePos) { - posVal = posFunc(item); - 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 * (j % gridW); - } else { - result.pos[i3] = calcX[j % gridW]; - } - result.pos[i3 + 1] = y0 + dy * Math.floor(j / gridW); - result.pos[i3 + 2] = 0; - } - result.opacity[i] = opacityFunc(item, j); - 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; - } - i += 1; - i3 += 3; - } - } - return result; - }; - - /** - * 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, - position: function (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; - }, - value: function (d) { - return m_this.position()(d).z; - } - }, - arg.style === undefined ? {} : arg.style - ); - - m_this.style(defaultStyle); - - if (m_contour) { - m_this.dataTime().modified(); - } - }; - - this._init(arg); - return this; - }; - - inherit(contourFeature, feature); - 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>] - }) - */ - - -/***/ }), -/* 232 */ -/***/ (function(module, exports, __webpack_require__) { - - var inherit = __webpack_require__(8); - var renderer = __webpack_require__(202); - var registerRenderer = __webpack_require__(201).registerRenderer; - - /** - * Create a new instance of class domRenderer. - * - * @class 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 (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; - - -/***/ }), -/* 233 */ -/***/ (function(module, exports, __webpack_require__) { - - module.exports = (function () { - 'use strict'; - - var $ = __webpack_require__(1); - - /** - * 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 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 (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. - */ - Object.defineProperty(this, 'size', { - get: function () { return this._size; }, - set: function (n) { - this._size = n; - this.next_item(); - } - }); - - /** - * Get the current queue size. - */ - Object.defineProperty(this, 'length', { - get: function () { return this._queue.length; } - }); - - /** - * Get the current number of processing items. - */ - Object.defineProperty(this, 'processing', { - get: function () { return this._processing; } - }); - - /** - * Remove all items from the queue. - */ - this.clear = function () { - this._queue = []; - this._processing = 0; - return this; - }; - - /** - * Add a Deferred object to the queue. - * @param {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 false, 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 true, always add the item to the end of the queue. - */ - 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 {Deferred} defer Deferred object to add to the queue. - * @param {boolean} atEnd if false, 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 true, 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 {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 {Deferred} defer Deferred object to add to the queue. - * @returns {bool} 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 true to start a new batch, false to turn off - * using batches. Undefined to return the current - * state of batches. - * @return {Number|boolean|Object} the current batch state or this object. - */ - 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 there if there are not too many - * deferred objects being processed. 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; - }; - - return fetchQueue; - })(); - - -/***/ }), -/* 234 */ -/***/ (function(module, exports, __webpack_require__) { - - var inherit = __webpack_require__(8); - var featureLayer = __webpack_require__(220); - var object = __webpack_require__(203); - - /** - * Create a new instance of class fileReader - * - * @class geo.fileReader - * @extends geo.object - * @returns {geo.fileReader} - */ - var fileReader = function (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 - */ - this.layer = function () { - return m_layer; - }; - - /** - * Tells the caller if it can handle the given file by returning a boolean. - */ - this.canRead = function () { - return false; - }; - - /** - * Reads the file object and calls the done function when finished. As an - * argument to done, provides a boolean that reports if the read was a - * success. Possibly, it can call done with an object containing details - * of the read operation. - */ - this.read = function (file, done) { - done(false); - }; - - /** - * Return a FileReader with handlers attached. - */ - function newFileReader(done, progress) { - var reader = new FileReader(); - if (progress) { - reader.onprogress = progress; - } - reader.onloadend = function () { - if (!reader.result) { - done(reader.error); - } - done(reader.result); - }; - return reader; - } - - /** - * Private method for reading a file object as a string. Calls done with - * the string content when finished or an error object if unsuccessful. - * Optionally, the caller can provide a progress method that is called - * after reading each slice. - */ - this._getString = function (file, done, progress) { - var reader = newFileReader(done, progress); - reader.readAsText(file); - }; - - /** - * Like _getString, but returns an ArrayBuffer object. - */ - this._getArrayBuffer = function (file, done, progress) { - var reader = newFileReader(done, progress); - reader.readAsText(file); - }; - - return this; - }; - - inherit(fileReader, object); - module.exports = fileReader; - - -/***/ }), -/* 235 */ -/***/ (function(module, exports, __webpack_require__) { - - var inherit = __webpack_require__(8); - var feature = __webpack_require__(207); - - /** - * Create a new instance of class graphFeature - * - * @class geo.graphFeature - * @extends geo.feature - * @returns {geo.graphFeature} - */ - var graphFeature = function (arg) { - 'use strict'; - - if (!(this instanceof graphFeature)) { - return new graphFeature(arg); - } - arg = arg || {}; - feature.call(this, arg); - - var $ = __webpack_require__(1); - var util = __webpack_require__(83); - var registry = __webpack_require__(201); - - /** - * @private - */ - var m_this = this, - s_draw = this.draw, - s_style = this.style, - m_nodes = null, - m_points = null, - m_children = function (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; - - -/***/ }), -/* 236 */ -/***/ (function(module, exports, __webpack_require__) { - - var $ = __webpack_require__(1); - var inherit = __webpack_require__(8); - var feature = __webpack_require__(207); - var transform = __webpack_require__(11); - - /** - * Create a new instance of class heatmapFeature - * - * @class geo.heatmapFeature - * @param {Object} arg Options object - * @extends geo.feature - * @param {Object|Function} [position] Position of the data. Default is - * (data). - * @param {Object|Function} [intensity] Scalar value of each data point. Scalar - * value must be a positive real number and will be 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 will be used to normalize all - * intensities with a dataset. If no value is given, then a it will - * be computed. - * @param {number} [minIntensity=null] Minimum intensity of the data. Minimum - * intensity must be a positive real number will be used to normalize all - * intensities with a dataset. If no value is given, then a it will - * be 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 heatpmap. If false, 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|string|Function} [style.color] Color transfer function that. - * will be used to evaluate color of each pixel using normalized intensity - * as the look up value. - * @param {Object|Function} [style.radius=10] Radius of a point in terms of - * number of pixels. - * @param {Object|Function} [style.blurRadius=10] Blur radius for each point in - * terms of number of pixels. - * @param {boolean} [style.gaussian=true] If true, 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. - * @returns {geo.heatmapFeature} - */ - - var heatmapFeature = function (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 - * - * @returns {geo.heatmap} - */ - 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 maxIntensity - * - * @returns {geo.heatmap} - */ - 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 - * - * @returns {geo.heatmap} - */ - this.updateDelay = function (val) { - if (val === undefined) { - return m_updateDelay; - } else { - m_updateDelay = parseInt(val, 10); - } - return m_this; - }; - - /** - * Get/Set binned - * - * @returns {geo.heatmap} - */ - 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 - * - * @returns {geo.heatmap} - */ - 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 - * - * @returns {geo.heatmap} - */ - 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 - */ - 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 - * @override - */ - 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) { - position.push(m_this.position()(d)); - if (setMax || setMin) { - intensity = m_this.intensity()(d); - 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; - - -/***/ }), -/* 237 */ -/***/ (function(module, exports, __webpack_require__) { - - var inherit = __webpack_require__(8); - var tile = __webpack_require__(238); - - module.exports = (function () { - 'use strict'; - - /** - * 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 fetch from the url, but - * subclasses may define additional rendering steps to produce the images - * before passing them off to the handlers. - * - * @class geo.imageTile - * @param {object} spec The tile specification object - * - * @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 (spec) { - if (!(this instanceof imageTile)) { - return new imageTile(spec); - } - - var $ = __webpack_require__(1); - - 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 gaurantee that the - * image data is available. Use the promise interface - * to add asyncronous handlers. - * @returns {Image} - */ - Object.defineProperty(this, 'image', { - get: function () { return this._image; } - }); - - /** - * Initiate the image request. - * - * @returns {this} The current tile class instance. - */ - 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} chainable - */ - 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); - return imageTile; - })(); - - -/***/ }), -/* 238 */ -/***/ (function(module, exports, __webpack_require__) { - - module.exports = (function () { - 'use strict'; - - var $ = __webpack_require__(1); - - /** - * 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. For example, - * - * tile.then(function (data) {...}).catch(function (data) {...}); - * - * @class geo.tile - * @param {Object} spec The tile specification object - * - * @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] - */ - var tile = function (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. - */ - Object.defineProperty(this, 'index', { - get: - function () { return this._index; } - }); - - /** - * Return the tile sizes. - */ - Object.defineProperty(this, 'size', { - get: function () { return this._size; } - }); - - /** - * Return the tile overlap sizes. - */ - Object.defineProperty(this, 'overlap', { - get: function () { 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. - */ - 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} Supports chained calling - * - */ - 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} Supports chained calling - * - */ - 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 - */ - 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. - * @returns {number} - */ - Object.defineProperty(this, 'bottom', { - get: function () { - return this.size.y * (this.index.y + 1) + this.overlap.y; - } - }); - - /** - * Computes the global coordinates of the top edge. - * @returns {number} - */ - Object.defineProperty(this, 'top', { - get: function () { - return this.size.y * this.index.y - this.overlap.y; - } - }); - - /** - * Computes the global coordinates of the left edge. - * @returns {number} - */ - Object.defineProperty(this, 'left', { - get: function () { - return this.size.x * this.index.x - this.overlap.x; - } - }); - - /** - * Computes the global coordinates of the right edge. - * @returns {number} - */ - Object.defineProperty(this, 'right', { - get: function () { - return this.size.x * (this.index.x + 1) + this.overlap.x; - } - }); - - /** - * Returns the global image size at this level. - * @returns {number} - */ - Object.defineProperty(this, 'levelSize', { - value: { - width: Math.pow(2, this.index.level || 0) * this.size.x, - height: Math.pow(2, this.index.level || 0) * this.size.y - } - }); - - /** - * 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} chainable - */ - this.fadeIn = function (duration) { - $.noop(duration); - return this; - }; - }; - return tile; - })(); - - -/***/ }), -/* 239 */ -/***/ (function(module, exports, __webpack_require__) { - - var inherit = __webpack_require__(8); - var registerFileReader = __webpack_require__(201).registerFileReader; - var fileReader = __webpack_require__(234); - - /** - * Create a new instance of class jsonReader - * - * @class geo.jsonReader - * @extends geo.fileReader - * @returns {geo.jsonReader} - */ - var jsonReader = function (arg) { - 'use strict'; - if (!(this instanceof jsonReader)) { - return new jsonReader(arg); - } - - var $ = __webpack_require__(1); - var convertColor = __webpack_require__(83).convertColor; - - var m_this = this; - - fileReader.call(this, arg); - - this.canRead = function (file) { - if (file instanceof File) { - return (file.type === 'application/json' || 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; - }; - - this._readObject = function (file, done, progress) { - var object; - function onDone(fileString) { - if (typeof fileString !== 'string') { - done(false); - } - - // We have two possibilities here: - // 1) fileString is a JSON string or is - // a URL. - try { - object = JSON.parse(fileString); - done(object); - } catch (e) { - if (!object) { - $.ajax({ - type: 'GET', - url: fileString, - dataType: 'text' - }).done(function (data) { - object = JSON.parse(data); - done(object); - }).fail(function () { - done(false); - }); - } - } - } - - if (file instanceof File) { - m_this._getString(file, onDone, progress); - } else if (typeof file === 'string') { - onDone(file); - } else { - done(file); - } - }; - - /** - * Return an array of normalized geojson features. This - * will do the following: - * - * 1. Turn bare geometries into features - * 2. Turn multi-geometry features into single geometry features - * - * Returns an array of Point, LineString, or Polygon features. - * @protected - */ - 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. - */ - 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. - */ - 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. - * - * @protected - * @param {string} prop The property name - * @param {object} default The default value - * @param {object} [spec] The argument containing the main property object - * @param {function} [convert] An optional conversion function - */ - this._style = function (prop, _default, spec, convert) { - convert = convert || function (d) { return d; }; - _default = convert(_default); - return function (d, i, e, j) { - var p; - if (spec && j !== undefined && spec[j] !== undefined) { - p = spec[j].properties; - } else { - p = d.properties; - } - if (p !== undefined && p.hasOwnProperty(prop)) { - return convert(p[prop]); - } - return _default; - }; - }; - - this.read = function (file, done, progress) { - - function _done(object) { - var features, allFeatures = [], points, lines, polygons; - - features = m_this._featureArray(object); - - // process points - points = features.filter(function (f) { return f.geometry.type === 'Point'; }); - if (points.length) { - allFeatures.push( - m_this.layer().createFeature('point') - .data(points) - .position(function (d) { - return m_this._position(d.geometry.coordinates); - }) - .style({ - fill: m_this._style('fill', true), - fillColor: m_this._style('fillColor', '#ff7800', null, convertColor), - fillOpacity: m_this._style('fillOpacity', 0.8), - stroke: m_this._style('stroke', true), - strokeColor: m_this._style('strokeColor', '#000000', null, convertColor), - strokeWidth: m_this._style('strokeWidth', 1), - strokeOpacity: m_this._style('strokeOpacity', 1), - radius: m_this._style('radius', 8) - }) - ); - } - - // process lines - lines = features.filter(function (f) { return f.geometry.type === 'LineString'; }); - if (lines.length) { - allFeatures.push( - m_this.layer().createFeature('line') - .data(lines) - .line(function (d) { - return d.geometry.coordinates; - }) - .position(m_this._position) - .style({ - strokeColor: m_this._style('strokeColor', '#ff7800', lines, convertColor), - strokeWidth: m_this._style('strokeWidth', 4, lines), - strokeOpacity: m_this._style('strokeOpacity', 0.5, lines), - strokeOffset: m_this._style('strokeOffset', 0, lines), - lineCap: m_this._style('lineCap', 'butt', lines), - lineJoin: m_this._style('lineCap', 'miter', lines), - closed: m_this._style('closed', false, lines) - }) - ); - } - - // process polygons - polygons = features.filter(function (f) { return f.geometry.type === 'Polygon'; }); - if (polygons.length) { - allFeatures.push( - m_this.layer().createFeature('polygon') - .data(polygons) - .polygon(function (d, i) { - return { - outer: d.geometry.coordinates[0], - inner: d.geometry.coordinates.slice(1) - }; - }) - .position(m_this._position) - .style({ - fill: m_this._style('fill', true), - fillColor: m_this._style('fillColor', '#b0de5c', polygons, convertColor), - fillOpacity: m_this._style('fillOpacity', 0.8, polygons), - stroke: m_this._style('stroke', true), - strokeColor: m_this._style('strokeColor', '#999999', polygons, convertColor), - strokeWidth: m_this._style('strokeWidth', 2, polygons), - strokeOpacity: m_this._style('strokeOpacity', 1, polygons) - }) - ); - } - if (done) { - done(allFeatures); - } - } - - m_this._readObject(file, _done, progress); - }; - }; - - inherit(jsonReader, fileReader); - registerFileReader('jsonReader', jsonReader); - module.exports = jsonReader; - - -/***/ }), -/* 240 */ -/***/ (function(module, exports, __webpack_require__) { - - var $ = __webpack_require__(1); - var vgl = __webpack_require__(86); - var inherit = __webpack_require__(8); - var sceneObject = __webpack_require__(208); - - /** - * 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 (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__(211); - var transform = __webpack_require__(11); - var util = __webpack_require__(83); - var registry = __webpack_require__(201); - var geo_event = __webpack_require__(9); - var mapInteractor = __webpack_require__(222); - var uiLayer = __webpack_require__(241); - - /** - * 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 {@linkcode 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 {@linkcode 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) { - 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. - */ - 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 (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 {@linkcode 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: - * - * * geo.event.layerAdd - * * 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. - */ - 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 () { - 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) { - queue[i].apply(this, arguments); - } - } - - /* - * 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__(210); - - /* 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; - - -/***/ }), -/* 241 */ -/***/ (function(module, exports, __webpack_require__) { - - var inherit = __webpack_require__(8); - var registerLayer = __webpack_require__(201).registerLayer; - var layer = __webpack_require__(210); - - /** - * 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 (arg) { - 'use strict'; - - var createWidget = __webpack_require__(201).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; - - -/***/ }), -/* 242 */ -/***/ (function(module, exports, __webpack_require__) { - - module.exports = (function () { - 'use strict'; - - var $ = __webpack_require__(1); - var inherit = __webpack_require__(8); - var tileLayer = __webpack_require__(243); - var registry = __webpack_require__(201); - var quadFeature = __webpack_require__(223); - - /** - * Create a new instance of osmLayer. - * - * @class geo.osmLayer - * @extends geo.featureLayer - * - * @param {object} arg - arg can contain following keys: baseUrl, - * imageFormat (such as png or jpeg), and displayLast - * (to decide whether or not render tiles from last zoom level). - */ - var osmLayer = function (arg) { - - var imageTile = __webpack_require__(237); - - 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, { - minLevel: 0, - maxLevel: 18, - tileOverlap: 0, - tileWidth: 256, - tileHeight: 256, - tileOffset : function (level) { - var s = Math.pow(2, level - 1) * 256; - return {x: s, y: s}; - }, - wrapX: true, - wrapY: false, - url: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', - attribution: 'Tile data © <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]); - return osmLayer; - })(); - - -/***/ }), -/* 243 */ -/***/ (function(module, exports, __webpack_require__) { - - module.exports = (function () { - 'use strict'; - - var inherit = __webpack_require__(8); - var featureLayer = __webpack_require__(220); - - /** - * 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, which is an abstract class defining a - * layer divided into tiles of arbitrary data. Notably, this class provides - * the core functionality of the osmLayer, but hooks exist to render tiles - * dynamically from vector data, or to display arbitrary grids of images - * in a custom coordinate system. 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. - * - * Although not currently supported, this class is intended to extend to - * 3D grids of tiles as well where 1 tile is covered by a 2x2x2 grid of - * tiles at the next level. The tiles are assumed to be rectangular, - * identically sized, and aligned with x/y axis of the underlying - * coordinate system. The local coordinate system is in pixels relative - * to the current zoom level and changes when the zoom level crosses an - * integer threshold. - * - * The constructor takes a number of optional parameters that customize - * the display of the tiles. The default values of these options are - * stored as the `defaults` attribution on the constructor. Supporting - * alternate tiling protocols often only requires adjusting these - * defaults. - * - * @class 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 {number} [options.tileOverlap=0] - * Number of pixels of overlap between tiles - * @param {number} [options.tileWidth=256] - * The tile width as displayed without overlap - * @param {number} [options.tileHeight=256] - * The tile height as displayed 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] - * 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 {function|string} [options.url=null] - * A function taking the current tile indices 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. - * @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. **This - * option is currently buggy because old tiles will purge before the animation - * is complete.** - * @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, in units of - * pixels, the top, left, right, and bottom maximum value for which tiles - * should be drawn at the given zoom level relative to the bottom left - * corner of the domain. This can be used to crop tiles at the edges of - * tile layer. 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 false, this inverts the gcs y-coordinate when calculating - * local coordinates. - * @returns {geo.tileLayer} - */ - var tileLayer = function (options) { - 'use strict'; - if (!(this instanceof tileLayer)) { - return new tileLayer(options); - } - featureLayer.call(this, options); - - var $ = __webpack_require__(1); - var geo_event = __webpack_require__(9); - var transform = __webpack_require__(11); - var tileCache = __webpack_require__(244); - var fetchQueue = __webpack_require__(233); - var adjustLayerForRenderer = __webpack_require__(201).adjustLayerForRenderer; - var Tile = __webpack_require__(238); - - 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; - - // 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 - */ - Object.defineProperty(this, 'options', {get: function () { - return $.extend({}, this._options); - }}); - - /** - * Readonly accessor to the tile cache object. - */ - Object.defineProperty(this, 'cache', {get: function () { - return this._cache; - }}); - - /** - * Readonly accessor to the active tile mapping. This is an object containing - * all currently drawn tiles (hash(tile) => tile). - */ - Object.defineProperty(this, 'activeTiles', {get: function () { - 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 {{x: nx, y: ny}} The number of tiles in each axis - */ - 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} {x: width, y: height} The maximum tile bounds in - * pixels for the specified level, or null if none specified. - */ - 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 {geo.tile} - */ - 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} {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. - */ - 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.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. - */ - 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. - * @protected - */ - 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 {object} 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} Chainable. - */ - 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. - * @param {number|undefined} zoom If unspecified, use the map zoom. - * @returns {object} Local coordinates. - */ - 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. - * @param {number|undefined} zoom If unspecified, use the map zoom. - * @returns {object} Local coordinates. - */ - 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} Chainable. - */ - 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 - */ - 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 - * @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 - * @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 - * @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. - */ - 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: 0, - tileWidth: 256, - tileHeight: 256, - wrapX: true, - wrapY: false, - url: null, - subdomains: 'abc', - tileOffset: function (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); - return tileLayer; - })(); - - -/***/ }), -/* 244 */ -/***/ (function(module, exports) { - - module.exports = (function () { - '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 geo.tileCache - * - * @param {object?} [options] A configuratoin object for the cache - * @param {number} [options.size=64] The maximum number of tiles to store - */ - var tileCache = function (options) { - if (!(this instanceof tileCache)) { - return new tileCache(options); - } - options = options || {}; - this._size = options.size || 64; - - /** - * Get/set the maximum cache size. - */ - Object.defineProperty(this, 'size', { - get: function () { return this._size; }, - set: function (n) { - while (this._atime.length > n) { - this.remove(this._atime[this._atime.length - 1]); - } - this._size = n; - } - }); - - /** - * Get the current cache size. - */ - Object.defineProperty(this, 'length', { - get: function () { 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 {bool} 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. - */ - 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 true, 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 - * @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; - }; - return tileCache; - })(); - - -/***/ }), -/* 245 */ -/***/ (function(module, exports, __webpack_require__) { - - var $ = __webpack_require__(1); - var inherit = __webpack_require__(8); - var feature = __webpack_require__(207); - - /** - * Create a new instance of class pathFeature - * - * @class geo.pathFeature - * @extends geo.feature - * @returns {geo.pathFeature} - */ - var pathFeature = function (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 () { return 1; }, - 'strokeColor': function () { 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; - - -/***/ }), -/* 246 */ -/***/ (function(module, exports, __webpack_require__) { - - var $ = __webpack_require__(1); - var inherit = __webpack_require__(8); - var feature = __webpack_require__(207); - var geo_event = __webpack_require__(9); - var util = __webpack_require__(83); - - /** - * Create a new instance of class imagemapFeature - * - * @class 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 (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. - */ - 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 (d, idx) { - return { - r: (idx & 0xFF) / 255, - g: ((idx >> 8) & 0xFF) / 255, - b: ((idx >> 16) & 0xFF) / 255, - a: 1 - }; - }, - position: function (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; - - -/***/ }), -/* 247 */ -/***/ (function(module, exports) { - - /* - * 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. - */ - - -/***/ }), -/* 248 */ -/***/ (function(module, exports, __webpack_require__) { - - var inherit = __webpack_require__(8); - var feature = __webpack_require__(207); - - /** - * Create a new instance of class vectorFeature - * - * @class geo.vectorFeature - * @extends geo.feature - * @returns {geo.vectorFeature} - */ - var vectorFeature = function (arg) { - 'use strict'; - if (!(this instanceof vectorFeature)) { - return new vectorFeature(arg); - } - - var $ = __webpack_require__(1); - - 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 (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; - - -/***/ }), -/* 249 */ -/***/ (function(module, exports, __webpack_require__) { - - module.exports = ("0.16.0"); - - -/***/ }), -/* 250 */ -/***/ (function(module, exports, __webpack_require__) { - - module.exports = ("644aa5f8e129f78eb97c81b3e4838c5bc33637c2"); - - -/***/ }), -/* 251 */ -/***/ (function(module, exports, __webpack_require__) { - - var geo_event = __webpack_require__(9); - geo_event.d3 = { - rescale: __webpack_require__(229) - }; - - /** - * @namespace geo.d3 - */ - module.exports = { - graphFeature: __webpack_require__(252), - lineFeature: __webpack_require__(253), - object: __webpack_require__(227), - pathFeature: __webpack_require__(254), - pointFeature: __webpack_require__(255), - quadFeature: __webpack_require__(256), - renderer: __webpack_require__(226), - tileLayer: __webpack_require__(257), - uniqueID: __webpack_require__(228), - vectorFeature: __webpack_require__(258) - }; - - -/***/ }), -/* 252 */ -/***/ (function(module, exports, __webpack_require__) { - - var inherit = __webpack_require__(8); - var registerFeature = __webpack_require__(201).registerFeature; - var graphFeature = __webpack_require__(235); - - /** - * @class geo.d3.graphFeature - * @extends geo.graphFeature - */ - var d3_graphFeature = function (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; - - -/***/ }), -/* 253 */ -/***/ (function(module, exports, __webpack_require__) { - - var inherit = __webpack_require__(8); - var registerFeature = __webpack_require__(201).registerFeature; - var lineFeature = __webpack_require__(206); - - /** - * Create a new instance of class lineFeature. - * - * @class geo.d3.lineFeature - * @extends geo.lineFeature - * @param {geo.lineFeature.spec} arg - * @returns {geo.d3.lineFeature} - */ - var d3_lineFeature = function (arg) { - 'use strict'; - if (!(this instanceof d3_lineFeature)) { - return new d3_lineFeature(arg); - } - - var d3 = __webpack_require__(226).d3; - var object = __webpack_require__(227); - var timestamp = __webpack_require__(209); - var util = __webpack_require__(83); - - 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; - - -/***/ }), -/* 254 */ -/***/ (function(module, exports, __webpack_require__) { - - var inherit = __webpack_require__(8); - var registerFeature = __webpack_require__(201).registerFeature; - var pathFeature = __webpack_require__(245); - - /** - * Create a new instance of class pathFeature - * - * @class geo.d3.pathFeature - * @extends geo.pathFeature - * @extends geo.d3.object - * @returns {geo.d3.pathFeature} - */ - var d3_pathFeature = function (arg) { - 'use strict'; - if (!(this instanceof d3_pathFeature)) { - return new d3_pathFeature(arg); - } - - var $ = __webpack_require__(1); - var d3 = __webpack_require__(226).d3; - var object = __webpack_require__(227); - var timestamp = __webpack_require__(209); - - 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 (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 () { return false; }, - 'fillColor': function () { 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; - - -/***/ }), -/* 255 */ -/***/ (function(module, exports, __webpack_require__) { - - var inherit = __webpack_require__(8); - var registerFeature = __webpack_require__(201).registerFeature; - var pointFeature = __webpack_require__(212); - - /** - * - * Create a new instance of pointFeature - * - * @class geo.d3.pointFeature - * @extends geo.pointFeature - * @extends geo.d3.object - * @returns {geo.d3.pointFeature} - */ - var d3_pointFeature = function (arg) { - 'use strict'; - if (!(this instanceof d3_pointFeature)) { - return new d3_pointFeature(arg); - } - - var d3_object = __webpack_require__(227); - var timestamp = __webpack_require__(209); - - 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 - */ - 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.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 (d) { - return m_this.featureGcsToDisplay(pos_func(d)).x; - }, - cy: function (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 - * - * @override - */ - 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; - - -/***/ }), -/* 256 */ -/***/ (function(module, exports, __webpack_require__) { - - var inherit = __webpack_require__(8); - var registerFeature = __webpack_require__(201).registerFeature; - var quadFeature = __webpack_require__(223); - - /** - * Create a new instance of class quadFeature. - * - * @class geo.d3.quadFeature - * @param {geo.quadFeature.spec} arg Options object. - * @extends geo.quadFeature - * @returns {geo.d3.quadFeature} - */ - var d3_quadFeature = function (arg) { - 'use strict'; - if (!(this instanceof d3_quadFeature)) { - return new d3_quadFeature(arg); - } - - var $ = __webpack_require__(1); - var d3 = __webpack_require__(226).d3; - var object = __webpack_require__(227); - - 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 (d) { - return d.quad.quadId; - }, - append: function (d) { - var ns = this.namespaceURI, - element = d.type === 'clr' ? 'polygon' : 'image'; - return (ns ? document.createElementNS(ns, element) : - document.createElement(element)); - }, - attributes: { - fill: function (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 (d) { - return d.type === 'clr' ? undefined : 1; - }, - points: function (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 (d) { - return d.type === 'clr' ? undefined : 'none'; - }, - reference: function (d) { - return d.quad.reference; - }, - stroke: false, - transform: function (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 (d) { - return d.type === 'clr' ? undefined : 1; - }, - x: function (d) { - return d.type === 'clr' ? undefined : 0; - }, - 'xlink:href': function (d) { - return ((d.type === 'clr' || !d.quad.image) ? undefined : - d.quad.image.src); - }, - y: function (d) { - return d.type === 'clr' ? undefined : 0; - } - }, - style: { - fillOpacity: function (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; - - -/***/ }), -/* 257 */ -/***/ (function(module, exports, __webpack_require__) { - - var registerLayerAdjustment = __webpack_require__(201).registerLayerAdjustment; - - var d3_tileLayer = function () { - '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 * 1e-5; - 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; - - -/***/ }), -/* 258 */ -/***/ (function(module, exports, __webpack_require__) { - - var inherit = __webpack_require__(8); - var registerFeature = __webpack_require__(201).registerFeature; - var vectorFeature = __webpack_require__(248); - - /** - * Create a new instance of vectorFeature - * - * @class geo.d3.vectorFeature - * @extends geo.vectorFeature - * @extends geo.d3.object - * @returns {geo.d3.vectorFeature} - */ - var d3_vectorFeature = function (arg) { - 'use strict'; - if (!(this instanceof d3_vectorFeature)) { - return new d3_vectorFeature(arg); - } - - var object = __webpack_require__(227); - var timestamp = __webpack_require__(209); - var d3 = __webpack_require__(226).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 (d, i) { - return cache[i].x1; - }, - y1: function (d, i) { - return cache[i].y1; - }, - x2: function (d, i) { - return cache[i].x1 + getScale() * cache[i].dx; - }, - y2: function (d, i) { - return cache[i].y1 + getScale() * cache[i].dy; - }, - 'marker-start': function (d, i) { - return 'url(#' + markerID(d, i, 'tail') + ')'; - }, - 'marker-end': function (d, i) { - return 'url(#' + markerID(d, i, 'head') + ')'; - } - }; - m_style.style = { - stroke: function () { 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; - - -/***/ }), -/* 259 */ -/***/ (function(module, exports, __webpack_require__) { - - /** - * @namespace geo.gl - */ - module.exports = { - choroplethFeature: __webpack_require__(260), - contourFeature: __webpack_require__(261), - lineFeature: __webpack_require__(263), - pointFeature: __webpack_require__(264), - polygonFeature: __webpack_require__(265), - quadFeature: __webpack_require__(267), - tileLayer: __webpack_require__(268), - vglRenderer: __webpack_require__(200) - }; - - -/***/ }), -/* 260 */ -/***/ (function(module, exports, __webpack_require__) { - - var inherit = __webpack_require__(8); - var registerFeature = __webpack_require__(201).registerFeature; - var choroplethFeature = __webpack_require__(225); - - /** - * Create a new instance of choroplethFeature - * - * @class geo.gl.choroplethFeature - * @extends geo.choroplethFeature - * @returns {geo.gl.choroplethFeature} - */ - var gl_choroplethFeature = function (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; - - -/***/ }), -/* 261 */ -/***/ (function(module, exports, __webpack_require__) { - - var inherit = __webpack_require__(8); - var registerFeature = __webpack_require__(201).registerFeature; - var contourFeature = __webpack_require__(231); - - /** - * Create a new instance of contourFeature - * - * @class geo.gl.contourFeature - * @extends geo.contourFeature - * @returns {geo.gl.contourFeature} - */ - var gl_contourFeature = function (arg) { - 'use strict'; - - if (!(this instanceof gl_contourFeature)) { - return new gl_contourFeature(arg); - } - arg = arg || {}; - contourFeature.call(this, arg); - - var vgl = __webpack_require__(86); - var transform = __webpack_require__(11); - var util = __webpack_require__(83); - var object = __webpack_require__(262); - - 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;', - ' }', - ' clr = texture2D(sampler2d, vec2(step / steps, 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); - for (i = 0; i < contour.colorMap.length; i += 1) { - colorTable.push(contour.colorMap[i].r * 255); - colorTable.push(contour.colorMap[i].g * 255); - colorTable.push(contour.colorMap[i].b * 255); - colorTable.push(contour.colorMap[i].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 - */ - 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 - * - * @override - */ - 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 - * - * @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._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; - - -/***/ }), -/* 262 */ -/***/ (function(module, exports, __webpack_require__) { - - /** - * 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 (arg) { - 'use strict'; - - var object = __webpack_require__(203); - - // 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 () { - m_this._update({mayDelay: true}); - m_this.renderer()._render(); - s_draw(); - return m_this; - }; - - return this; - }; - - module.exports = gl_object; - - -/***/ }), -/* 263 */ -/***/ (function(module, exports, __webpack_require__) { - - var inherit = __webpack_require__(8); - var registerFeature = __webpack_require__(201).registerFeature; - var lineFeature = __webpack_require__(206); - - 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 geo.gl.lineFeature - * @extends geo.lineFeature - * @param {geo.lineFeature.spec} arg - * @returns {geo.gl.lineFeature} - */ - var gl_lineFeature = function (arg) { - 'use strict'; - if (!(this instanceof gl_lineFeature)) { - return new gl_lineFeature(arg); - } - arg = arg || {}; - lineFeature.call(this, arg); - - var vgl = __webpack_require__(86); - var transform = __webpack_require__(11); - var util = __webpack_require__(83); - var object = __webpack_require__(262); - - 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);', - ' if (result.w != 0.0) result = result / result.w;', - ' return result;', - '}', - - '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, 2, 0, 1);', - ' 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, 2, 0, 1);', - ' return;', - ' }', - ' float angleCB = atan(deltaCB.y / aspect, deltaCB.x);', - // 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, sinABC, cosBCD, sinBCD;', // of half angles - // handle near end - ' if (nearMode >= 4) {', - ' float angleBA = atan((B.y - A.y) / aspect, B.x - A.x);', - ' 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 + 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.0 || 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 = atan((D.y - C.y) / aspect, D.x - C.x);', - ' 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 + 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.0 || 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);', - // 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', - ' precision highp float;', - '#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(info.x);', - ' int farMode = int(info.y);', - ' 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(), - 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 * order.length; - 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 * order.length; - 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) { - for (k = 0; k < order.length; 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] = 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] = position[v.prev + 2]; - nextBuf[dest3] = position[v.next]; - nextBuf[dest3 + 1] = position[v.next + 1]; - nextBuf[dest3 + 2] = position[v.next + 2]; - farBuf[dest3] = position[v2.next]; - farBuf[dest3 + 1] = position[v2.next + 1]; - farBuf[dest3 + 2] = 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] = position[v.next + 2]; - nextBuf[dest3] = position[v.prev]; - nextBuf[dest3 + 1] = position[v.prev + 1]; - nextBuf[dest3 + 2] = position[v.prev + 2]; - farBuf[dest3] = position[v2.prev]; - farBuf[dest3 + 1] = position[v2.prev + 1]; - farBuf[dest3 + 2] = 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; - - -/***/ }), -/* 264 */ -/***/ (function(module, exports, __webpack_require__) { - - var $ = __webpack_require__(1); - var inherit = __webpack_require__(8); - var registerFeature = __webpack_require__(201).registerFeature; - var pointFeature = __webpack_require__(212); - - /** - * Create a new instance of pointFeature - * - * @class geo.gl.pointFeature - * @extends geo.pointFeature - * @returns {geo.gl.pointFeature} - */ - var gl_pointFeature = function (arg) { - 'use strict'; - if (!(this instanceof gl_pointFeature)) { - return new gl_pointFeature(arg); - } - arg = arg || {}; - pointFeature.call(this, arg); - - var vgl = __webpack_require__(86); - var transform = __webpack_require__(11); - var util = __webpack_require__(83); - var object = __webpack_require__(262); - - 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'); - - function createVertexShader() { - var shader = new vgl.shader(vgl.GL.VERTEX_SHADER); - shader.setShaderSource(vertexShaderSource); - return shader; - } - - function createFragmentShader() { - var shader = new vgl.shader(vgl.GL.FRAGMENT_SHADER); - shader.setShaderSource(fragmentShaderSource); - return shader; - } - - 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; - } - - 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 actors - * - * @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; - }; - - 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(); - } - }; - - /** - * 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 - * - * @override - */ - 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(); - }; - - /** - * Update - * - * @override - */ - 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(); - }; - - /** - * Destroy - */ - 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; - - -/***/ }), -/* 265 */ -/***/ (function(module, exports, __webpack_require__) { - - var inherit = __webpack_require__(8); - var registerFeature = __webpack_require__(201).registerFeature; - var polygonFeature = __webpack_require__(217); - - /** - * 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 (arg) { - 'use strict'; - if (!(this instanceof gl_polygonFeature)) { - return new gl_polygonFeature(arg); - } - arg = arg || {}; - polygonFeature.call(this, arg); - - var vgl = __webpack_require__(86); - var earcut = __webpack_require__(266); - var transform = __webpack_require__(11); - var util = __webpack_require__(83); - var object = __webpack_require__(262); - - 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 (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 () { - 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; - - -/***/ }), -/* 266 */ -/***/ (function(module, exports) { - - '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); - - // first look for points inside the triangle in increasing z-order - var p = ear.nextZ; - - while (p && p.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.nextZ; - } - - // then look for points in decreasing z-order - p = ear.prevZ; - - 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; - } - - 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; - }; - - -/***/ }), -/* 267 */ -/***/ (function(module, exports, __webpack_require__) { - - var inherit = __webpack_require__(8); - var registerFeature = __webpack_require__(201).registerFeature; - var quadFeature = __webpack_require__(223); - - /** - * Create a new instance of class quadFeature. - * - * @class geo.gl.quadFeature - * @param {geo.quadFeature.spec} arg Options object. - * @extends geo.quadFeature - * @returns {geo.gl.quadFeature} - */ - var gl_quadFeature = function (arg) { - 'use strict'; - if (!(this instanceof gl_quadFeature)) { - return new gl_quadFeature(arg); - } - quadFeature.call(this, arg); - - var $ = __webpack_require__(1); - var vgl = __webpack_require__(86); - var object = __webpack_require__(262); - - 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; - - -/***/ }), -/* 268 */ -/***/ (function(module, exports, __webpack_require__) { - - var registerLayerAdjustment = __webpack_require__(201).registerLayerAdjustment; - - var gl_tileLayer = function () { - '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); - quad.ul.z = quad.ll.z = quad.ur.z = quad.lr.z = level * 1e-5; - 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; - - -/***/ }), -/* 269 */ -/***/ (function(module, exports, __webpack_require__) { - - /** - * @namespace geo.canvas - */ - module.exports = { - canvasRenderer: __webpack_require__(270), - heatmapFeature: __webpack_require__(271), - lineFeature: __webpack_require__(273), - pixelmapFeature: __webpack_require__(274), - quadFeature: __webpack_require__(275), - textFeature: __webpack_require__(276), - tileLayer: __webpack_require__(296) - }; - - -/***/ }), -/* 270 */ -/***/ (function(module, exports, __webpack_require__) { - - var inherit = __webpack_require__(8); - var registerRenderer = __webpack_require__(201).registerRenderer; - var renderer = __webpack_require__(202); - - /** - * Create a new instance of class canvasRenderer. - * - * @class 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 (arg) { - 'use strict'; - - var $ = __webpack_require__(1); - - 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) { - m_this.canvas().attr('width', w); - m_this.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); - - (function () { - 'use strict'; - - 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; - - -/***/ }), -/* 271 */ -/***/ (function(module, exports, __webpack_require__) { - - var inherit = __webpack_require__(8); - var registerFeature = __webpack_require__(201).registerFeature; - var heatmapFeature = __webpack_require__(236); - var timestamp = __webpack_require__(209); - - /** - * Create a new instance of class heatmapFeature - * Inspired from - * https://github.com/mourner/simpleheat/blob/gh-pages/simpleheat.js - * - * @class geo.canvas.heatmapFeature - * @param {Object} arg Options object - * @extends geo.heatmapFeature - * @returns {canvas_heatmapFeature} - */ - var canvas_heatmapFeature = function (arg) { - 'use strict'; - - if (!(this instanceof canvas_heatmapFeature)) { - return new canvas_heatmapFeature(arg); - } - heatmapFeature.call(this, arg); - var object = __webpack_require__(272); - - object.call(this); - - /** - * @private - */ - var geo_event = __webpack_require__(9); - - var m_this = this, - m_typedBuffer, - m_typedClampedBuffer, - m_typedBufferData, - m_heatMapPosition, - m_heatMapTransform, - s_exit = this._exit, - s_init = this._init, - s_update = this._update, - m_renderTime = timestamp(); - - /** - * Meta functions for converting from geojs styles to canvas. - * @private - */ - this._convertColor = function (c) { - var color; - if (c.hasOwnProperty('r') && - c.hasOwnProperty('g') && - c.hasOwnProperty('b') && - c.hasOwnProperty('a')) { - color = 'rgba(' + 255 * c.r + ',' + 255 * c.g + ',' - + 255 * c.b + ',' + c.a + ')'; - } - return color; - }; - - /** - * Compute gradient (color lookup table) - * @protected - */ - 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, m_this._convertColor(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 - * @protected - */ - 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 - * @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. - * @protected - * @param {object} context2d the canvas context to draw in. - * @param {object} 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]) - 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. - * @protected - * @param {object} context2d the canvas context to draw in. - * @param {object} 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]) - 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. - * @protected - * @param {object} context2d the canvas context to draw in. - * @param {object} map the parent map object. - */ - 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 - * @protected - */ - this._init = function () { - s_init.call(m_this, arg); - - m_this.geoOn(geo_event.pan, m_this._animatePan); - - return m_this; - }; - - /** - * Update - * @protected - */ - 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. - * @protected - */ - 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) - * @protected - */ - this._animatePan = function (e) { - - 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()); - } - }; - - /** - * Destroy - * @protected - */ - this._exit = function () { - s_exit.call(m_this); - }; - - m_this._init(arg); - return this; - }; - - inherit(canvas_heatmapFeature, heatmapFeature); - - // Now register it - registerFeature('canvas', 'heatmap', canvas_heatmapFeature); - module.exports = canvas_heatmapFeature; - - -/***/ }), -/* 272 */ -/***/ (function(module, exports, __webpack_require__) { - - var inherit = __webpack_require__(8); - var sceneObject = __webpack_require__(208); - - /** - * 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 (arg) { - 'use strict'; - - var object = __webpack_require__(203); - - // 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 () { - m_this._update(); - m_this.renderer()._render(); - s_draw(); - return m_this; - }; - - return this; - }; - - inherit(canvas_object, sceneObject); - module.exports = canvas_object; - - -/***/ }), -/* 273 */ -/***/ (function(module, exports, __webpack_require__) { - - var inherit = __webpack_require__(8); - var registerFeature = __webpack_require__(201).registerFeature; - var lineFeature = __webpack_require__(206); - - /** - * Create a new instance of class lineFeature. - * - * @class geo.canvas.lineFeature - * @extends geo.lineFeature - * @param {geo.lineFeature.spec} arg - * @returns {geo.canvas.lineFeature} - */ - var canvas_lineFeature = function (arg) { - 'use strict'; - if (!(this instanceof canvas_lineFeature)) { - return new canvas_lineFeature(arg); - } - - var object = __webpack_require__(272); - - 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; - - -/***/ }), -/* 274 */ -/***/ (function(module, exports, __webpack_require__) { - - var inherit = __webpack_require__(8); - var registerFeature = __webpack_require__(201).registerFeature; - var pixelmapFeature = __webpack_require__(246); - - /** - * Create a new instance of class pixelmapFeature - * - * @class geo.canvas.pixelmapFeature - * @param {Object} arg Options object - * @extends geo.pixelmapFeature - * @returns {canvas_pixelmapFeature} - */ - var canvas_pixelmapFeature = function (arg) { - 'use strict'; - - if (!(this instanceof canvas_pixelmapFeature)) { - return new canvas_pixelmapFeature(arg); - } - pixelmapFeature.call(this, arg); - - var object = __webpack_require__(272); - object.call(this); - - this._init(arg); - return this; - }; - - inherit(canvas_pixelmapFeature, pixelmapFeature); - - // Now register it - registerFeature('canvas', 'pixelmap', canvas_pixelmapFeature); - module.exports = canvas_pixelmapFeature; - - -/***/ }), -/* 275 */ -/***/ (function(module, exports, __webpack_require__) { - - var inherit = __webpack_require__(8); - var registerFeature = __webpack_require__(201).registerFeature; - var quadFeature = __webpack_require__(223); - var util = __webpack_require__(83); - - /** - * Create a new instance of class quadFeature. - * - * @class geo.canvas.quadFeature - * @param {geo.quadFeature.spec} arg Options object. - * @extends geo.quadFeature - * @returns {geo.canvas.quadFeature} - */ - var canvas_quadFeature = function (arg) { - 'use strict'; - - if (!(this instanceof canvas_quadFeature)) { - return new canvas_quadFeature(arg); - } - quadFeature.call(this, arg); - - var object = __webpack_require__(272); - object.call(this); - - var $ = __webpack_require__(1); - - 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 { - context2d.drawImage(src, 0, 0, quad.crop.x, quad.crop.y, 0, 0, - quad.crop.x, quad.crop.y); - } - }); - }); - 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; - - -/***/ }), -/* 276 */ -/***/ (function(module, exports, __webpack_require__) { - - var inherit = __webpack_require__(8); - var registerFeature = __webpack_require__(201).registerFeature; - var textFeature = __webpack_require__(218); - var util = __webpack_require__(83); - var mat3 = __webpack_require__(277); - var vec3 = __webpack_require__(138); - - /** - * 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 (arg) { - 'use strict'; - if (!(this instanceof canvas_textFeature)) { - return new canvas_textFeature(arg); - } - - var object = __webpack_require__(272); - - 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'), - textFunc = m_this.style.get('text'), - mapRotation = map.rotation(), - mapZoom = map.zoom(), - fontFromSubValues, text, 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(); - data.forEach(function (d, i) { - 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)); - // TODO: get the position position without transform. If it is outside - // of the map to an extent that there is no chance of text showing, - // skip further processing. - pos = m_this.featureGcsToDisplay(posFunc(d, i)); - 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; - - -/***/ }), -/* 277 */ -/***/ (function(module, exports, __webpack_require__) { - - module.exports = { - adjoint: __webpack_require__(278) - , clone: __webpack_require__(279) - , copy: __webpack_require__(280) - , create: __webpack_require__(281) - , determinant: __webpack_require__(282) - , frob: __webpack_require__(283) - , fromMat2: __webpack_require__(284) - , fromMat4: __webpack_require__(285) - , fromQuat: __webpack_require__(286) - , identity: __webpack_require__(287) - , invert: __webpack_require__(288) - , multiply: __webpack_require__(289) - , normalFromMat4: __webpack_require__(290) - , rotate: __webpack_require__(291) - , scale: __webpack_require__(292) - , str: __webpack_require__(293) - , translate: __webpack_require__(294) - , transpose: __webpack_require__(295) - } - - -/***/ }), -/* 278 */ -/***/ (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 - } - - -/***/ }), -/* 279 */ -/***/ (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 - } - - -/***/ }), -/* 280 */ -/***/ (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 - } - - -/***/ }), -/* 281 */ -/***/ (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 - } - - -/***/ }), -/* 282 */ -/***/ (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) - } - - -/***/ }), -/* 283 */ -/***/ (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] - ) - } - - -/***/ }), -/* 284 */ -/***/ (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 - } - - -/***/ }), -/* 285 */ -/***/ (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 - } - - -/***/ }), -/* 286 */ -/***/ (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 - } - - -/***/ }), -/* 287 */ -/***/ (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 - } - - -/***/ }), -/* 288 */ -/***/ (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 - } - - -/***/ }), -/* 289 */ -/***/ (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 - } - - -/***/ }), -/* 290 */ -/***/ (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 - } - - -/***/ }), -/* 291 */ -/***/ (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 - } - - -/***/ }), -/* 292 */ -/***/ (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 - } - - -/***/ }), -/* 293 */ -/***/ (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] + ')' - } - - -/***/ }), -/* 294 */ -/***/ (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 - } - - -/***/ }), -/* 295 */ -/***/ (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 - } - - -/***/ }), -/* 296 */ -/***/ (function(module, exports, __webpack_require__) { - - var registerLayerAdjustment = __webpack_require__(201).registerLayerAdjustment; - - var canvas_tileLayer = function () { - '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 * 1e-5; - 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; - - -/***/ }), -/* 297 */ -/***/ (function(module, exports, __webpack_require__) { - - /** - * @namespace geo.gui - */ - module.exports = { - colorLegendWidget: __webpack_require__(298), - domWidget: __webpack_require__(299), - legendWidget: __webpack_require__(303), - scaleWidget: __webpack_require__(305), - sliderWidget: __webpack_require__(308), - svgWidget: __webpack_require__(304), - uiLayer: __webpack_require__(241), - widget: __webpack_require__(300) - }; - - -/***/ }), -/* 298 */ -/***/ (function(module, exports, __webpack_require__) { - - var d3 = __webpack_require__(226).d3; - var domWidget = __webpack_require__(299); - var inherit = __webpack_require__(8); - var registerWidget = __webpack_require__(201).registerWidget; - var util = __webpack_require__(83); - var uniqueID = __webpack_require__(228); - - __webpack_require__(301); - - /** - * @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. - */ - - /** - * 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 (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(axisScale.domain().length / m_ticks); - return axisScale.domain() - .filter(function (d, i) { return i % skip === 0; }); - }); - m_this._renderAxis(svg, axis); - - } 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]; })); - - ticks = steps.slice(); - ticks.push(category.colors.length); - axisScale = d3.scale.ordinal() - .domain(axisDomain) - .rangePoints([0, width]); - axis = createAxis(axisScale); - m_this._renderAxis(svg, axis); - - } 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); - 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; - if (['linear', 'log', 'sqrt', 'pow'].indexOf(category.scale) === -1) { - throw new Error('unsupported scale'); - } - axisScale = d3.scale[category.scale]().domain(category.domain).range([0, width]).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]); - gradient.append('stop') - .attr('offset', '100%') - .attr('stop-color', category.colors[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'); - - 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. - * @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; - - -/***/ }), -/* 299 */ -/***/ (function(module, exports, __webpack_require__) { - - var widget = __webpack_require__(300); - var inherit = __webpack_require__(8); - var registerWidget = __webpack_require__(201).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 (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; - - -/***/ }), -/* 300 */ -/***/ (function(module, exports, __webpack_require__) { - - var inherit = __webpack_require__(8); - var sceneObject = __webpack_require__(208); - var $ = __webpack_require__(1); - - /** - * @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 (arg) { - 'use strict'; - if (!(this instanceof widget)) { - return new widget(arg); - } - arg = arg || {}; - sceneObject.call(this, arg); - - var geo_event = __webpack_require__(9); - - 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])) { - // tris 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; - - -/***/ }), -/* 301 */ -/***/ (function(module, exports, __webpack_require__) { - - // style-loader: Adds some css to the DOM by adding a <style> tag - - // load the styles - var content = __webpack_require__(302); - if(typeof content === 'string') content = [[module.id, content, '']]; - // add the styles to the DOM - var update = __webpack_require__(6)(content, {}); - if(content.locals) module.exports = content.locals; - // Hot Module Replacement - if(false) { - // When the styles change, update the <style> tags - if(!content.locals) { - 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, '']]; - update(newContent); - }); - } - // When the module is disposed, remove the <style> tags - module.hot.dispose(function() { update(); }); - } - -/***/ }), -/* 302 */ -/***/ (function(module, exports, __webpack_require__) { - - exports = module.exports = __webpack_require__(5)(); - // imports - - - // module - exports.push([module.id, ".color-legend-container{display:none;padding:10px;border:1.5px solid #000;border-radius:3px;transition:background .25s linear;background-color:hsla(0,0%,100%,.75)}.color-legend-container:hover{background-color:#fff}.color-legend-container .legends .legend{margin-bottom:10px}.color-legend-container .legends .legend .title{text-align:center}.color-legend-container .legends .legend svg.svg{display:block}.color-legend-container .legends .legend svg.svg .axis.x line,.color-legend-container .legends .legend svg.svg .axis.x path.domain{fill:none;stroke:#000;stroke-width:.7}.color-legend-container .legends .legend svg.svg .axis.x text{font-size:12px}.color-legend-container .color-legend-popup{position:absolute;background:#fff;height:22px;font-size:14px;border:1px solid #000;padding:0 5px;pointer-events:none;white-space:nowrap;z-index:100000;opacity:0}", ""]); - - // exports - - -/***/ }), -/* 303 */ -/***/ (function(module, exports, __webpack_require__) { - - var svgWidget = __webpack_require__(304); - var inherit = __webpack_require__(8); - var registerWidget = __webpack_require__(201).registerWidget; - - /** - * Create a new instance of class legendWidget - * - * @class geo.gui.legendWidget - * @extends geo.gui.svgWidget - * @returns {geo.gui.legendWidget} - */ - var legendWidget = function (arg) { - 'use strict'; - if (!(this instanceof legendWidget)) { - return new legendWidget(arg); - } - svgWidget.call(this, arg); - - var d3 = __webpack_require__(226).d3; - var geo_event = __webpack_require__(9); - - /** @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; - - -/***/ }), -/* 304 */ -/***/ (function(module, exports, __webpack_require__) { - - var widget = __webpack_require__(300); - var inherit = __webpack_require__(8); - var registerWidget = __webpack_require__(201).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 (arg) { - 'use strict'; - if (!(this instanceof svgWidget)) { - return new svgWidget(arg); - } - - widget.call(this, arg); - - var d3Renderer = __webpack_require__(226); - - 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; - - -/***/ }), -/* 305 */ -/***/ (function(module, exports, __webpack_require__) { - - var $ = __webpack_require__(1); - var inherit = __webpack_require__(8); - var svgWidget = __webpack_require__(304); - var registerWidget = __webpack_require__(201).registerWidget; - - __webpack_require__(306); - - /** - * Scale widget specification. - * - * @typedef {object} geo.gui.scaleWidget.spec - * @param {number} [scale=1] A scale applied to the map gcs units to convert to - * the scale units. - * @param {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. - * @param {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. - * @param {string} [orientation='bottom'] One of `left`, `right`, `top`, or - * `bottom`. The scale text is placed in that location relative to the scale - * bar. - * @param {number} [strokeWidth=2] The width of the ticks and scale bar in - * pixels. - * @param {number} [tickLength=10] The length of the end ticks in pixels. - * @param {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. - * @param {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 - * @param {string} unit Display name for the unit. - * @param {number} scale Scale for 1 unit in the current system. - * @param {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}]`) - * @param {number} [basis=10] The basis for the multiples value. - * @param {object[]} [multiples] A list of objects in ascending value order that - * determine what round values are displayed. - * @param {number} multiples.multiple The value that is selected for display. - * @param {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 (arg) { - 'use strict'; - if (!(this instanceof scaleWidget)) { - return new scaleWidget(arg); - } - svgWidget.call(this, arg); - - var geo_event = __webpack_require__(9); - var transform = __webpack_require__(11); - var util = __webpack_require__(83); - var d3 = __webpack_require__(226).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 (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', - 'alignment-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', - 'alignment-baseline': 'baseline' - }); - 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', - 'alignment-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', - 'alignment-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: 'μ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; - - -/***/ }), -/* 306 */ -/***/ (function(module, exports, __webpack_require__) { - - // style-loader: Adds some css to the DOM by adding a <style> tag - - // load the styles - var content = __webpack_require__(307); - if(typeof content === 'string') content = [[module.id, content, '']]; - // add the styles to the DOM - var update = __webpack_require__(6)(content, {}); - if(content.locals) module.exports = content.locals; - // Hot Module Replacement - if(false) { - // When the styles change, update the <style> tags - if(!content.locals) { - 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, '']]; - update(newContent); - }); - } - // When the module is disposed, remove the <style> tags - module.hot.dispose(function() { update(); }); - } - -/***/ }), -/* 307 */ -/***/ (function(module, exports, __webpack_require__) { - - exports = module.exports = __webpack_require__(5)(); - // imports - - - // module - exports.push([module.id, ".geojs-scale-widget-bar{stroke:#000}.geojs-scale-widget-text{font-weight:700;font-size:16px;font-family:serif}", ""]); - - // exports - - -/***/ }), -/* 308 */ -/***/ (function(module, exports, __webpack_require__) { - - var svgWidget = __webpack_require__(304); - var inherit = __webpack_require__(8); - var registerWidget = __webpack_require__(201).registerWidget; - - /** - * Create a new instance of class sliderWidget - * - * @class geo.gui.sliderWidget - * @extends {geo.gui.svgWidget} - * @returns {geo.gui.sliderWidget} - */ - var sliderWidget = function (arg) { - 'use strict'; - if (!(this instanceof sliderWidget)) { - return new sliderWidget(arg); - } - svgWidget.call(this, arg); - - var d3 = __webpack_require__(226).d3; - var geo_event = __webpack_require__(9); - - 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 () { - 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 () { - 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; - - -/***/ }) -/******/ ]) -}); -; \ No newline at end of file