From c63bd1901c90b404776aeb901b7c39590f46ddba Mon Sep 17 00:00:00 2001 From: Amanda Ghassaei <amandaghassaei@gmail.com> Date: Fri, 23 Jan 2015 15:04:50 -0500 Subject: [PATCH] lattice structure coming in --- ...6_00_000_octa_turret_folded_corner-1-1.STL | Bin 0 -> 58184 bytes ...6_00_000_octa_turret_folded_corner-2-1.STL | Bin 0 -> 58184 bytes data/triangle.stl | Bin 0 -> 58184 bytes data/triangleRot.stl | Bin 0 -> 58184 bytes data/triangleScaled.stl | Bin 0 -> 58184 bytes dependencies/main.html | 128 + dependencies/main_files/OrbitControls.js | 675 + dependencies/main_files/STLLoader.js | 446 + dependencies/main_files/THREE2STL.js | 86 + dependencies/main_files/analytics.js | 11 + dependencies/main_files/backbone.js | 1608 + dependencies/main_files/bootstrap-slider.css | 164 + dependencies/main_files/bootstrap-slider.js | 1210 + dependencies/main_files/bootstrap.css | 5 + dependencies/main_files/dmaCell.js | 28 + dependencies/main_files/dmaNode.js | 55 + dependencies/main_files/dmaPart.js | 63 + dependencies/main_files/exportMenu.js | 21 + dependencies/main_files/fillGeometry.js | 67 + dependencies/main_files/fillGeometryView.js | 35 + dependencies/main_files/flat-ui.css | 6200 +++ dependencies/main_files/flat-ui.js | 12728 ++++++ dependencies/main_files/importView.js | 146 + dependencies/main_files/jquery-2.js | 9205 ++++ dependencies/main_files/lattice.js | 110 + dependencies/main_files/latticeView.js | 68 + dependencies/main_files/main.css | 79 + dependencies/main_files/main.js | 43 + dependencies/main_files/meshHandle.js | 37 + dependencies/main_files/navbar.js | 52 + dependencies/main_files/numeric-1.js | 4424 ++ dependencies/main_files/persistentWorkers.js | 108 + dependencies/main_files/pushPullMeshView.js | 97 + dependencies/main_files/three.js | 34545 ++++++++++++++++ dependencies/main_files/threeModel.js | 97 + dependencies/main_files/threeView.js | 92 + dependencies/main_files/underscore.js | 1416 + dependencies/main_files/worker.js | 58 + js/fea/dmaNode.js | 10 + js/fea/dmaPart.js | 20 +- js/main.js | 4 +- js/models/lattice.js | 65 +- 42 files changed, 74201 insertions(+), 5 deletions(-) create mode 100755 data/Assem1 - OT6_00_000_octa_turret_folded_corner-1-1.STL create mode 100755 data/Assem1 - OT6_00_000_octa_turret_folded_corner-2-1.STL create mode 100755 data/triangle.stl create mode 100644 data/triangleRot.stl create mode 100644 data/triangleScaled.stl create mode 100644 dependencies/main.html create mode 100644 dependencies/main_files/OrbitControls.js create mode 100644 dependencies/main_files/STLLoader.js create mode 100644 dependencies/main_files/THREE2STL.js create mode 100644 dependencies/main_files/analytics.js create mode 100644 dependencies/main_files/backbone.js create mode 100644 dependencies/main_files/bootstrap-slider.css create mode 100644 dependencies/main_files/bootstrap-slider.js create mode 100644 dependencies/main_files/bootstrap.css create mode 100644 dependencies/main_files/dmaCell.js create mode 100644 dependencies/main_files/dmaNode.js create mode 100644 dependencies/main_files/dmaPart.js create mode 100644 dependencies/main_files/exportMenu.js create mode 100644 dependencies/main_files/fillGeometry.js create mode 100644 dependencies/main_files/fillGeometryView.js create mode 100644 dependencies/main_files/flat-ui.css create mode 100644 dependencies/main_files/flat-ui.js create mode 100644 dependencies/main_files/importView.js create mode 100644 dependencies/main_files/jquery-2.js create mode 100644 dependencies/main_files/lattice.js create mode 100644 dependencies/main_files/latticeView.js create mode 100644 dependencies/main_files/main.css create mode 100644 dependencies/main_files/main.js create mode 100644 dependencies/main_files/meshHandle.js create mode 100644 dependencies/main_files/navbar.js create mode 100644 dependencies/main_files/numeric-1.js create mode 100644 dependencies/main_files/persistentWorkers.js create mode 100644 dependencies/main_files/pushPullMeshView.js create mode 100644 dependencies/main_files/three.js create mode 100644 dependencies/main_files/threeModel.js create mode 100644 dependencies/main_files/threeView.js create mode 100644 dependencies/main_files/underscore.js create mode 100644 dependencies/main_files/worker.js diff --git a/data/Assem1 - OT6_00_000_octa_turret_folded_corner-1-1.STL b/data/Assem1 - OT6_00_000_octa_turret_folded_corner-1-1.STL new file mode 100755 index 0000000000000000000000000000000000000000..99392315eb289536616dacfadfd2bb480c60638e GIT binary patch literal 58184 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 UAnQOr)fKl6WL3y#v@$RN08&#CT>t<8 literal 0 HcmV?d00001 diff --git a/data/Assem1 - OT6_00_000_octa_turret_folded_corner-2-1.STL b/data/Assem1 - OT6_00_000_octa_turret_folded_corner-2-1.STL new file mode 100755 index 0000000000000000000000000000000000000000..49b5920fc970cc6e3499e5a0ee73f73d17418ba7 GIT binary patch literal 58184 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 WU4<-ztONN}SKK;~RUwzb3=9CA20lOl literal 0 HcmV?d00001 diff --git a/data/triangle.stl b/data/triangle.stl new file mode 100755 index 0000000000000000000000000000000000000000..beeb31818b6c60de08e89bce06728278f16d63c4 GIT binary patch literal 58184 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 literal 0 HcmV?d00001 diff --git a/data/triangleRot.stl b/data/triangleRot.stl new file mode 100644 index 0000000000000000000000000000000000000000..4ca435b41180448f1f3971749e78ddd9f0178b31 GIT binary patch literal 58184 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 literal 0 HcmV?d00001 diff --git a/data/triangleScaled.stl b/data/triangleScaled.stl new file mode 100644 index 0000000000000000000000000000000000000000..abcfa76dab8ded85363d5651358ed5b6013fb753 GIT binary patch literal 58184 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 literal 0 HcmV?d00001 diff --git a/dependencies/main.html b/dependencies/main.html new file mode 100644 index 00000000..7a901763 --- /dev/null +++ b/dependencies/main.html @@ -0,0 +1,128 @@ +<!DOCTYPE html> +<html><head> +<meta http-equiv="content-type" content="text/html; charset=UTF-8"> + + <title></title> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <script src="main_files/analytics_002.js" async=""></script><script src="main_files/analytics.js"></script> + + <!--UI--> + <script src="main_files/jquery-2.js"></script> + <script src="main_files/underscore.js"></script> + + <!--backbone model/view framework--> + <script src="main_files/backbone.js"></script> + + <!--flatUI bootstrap theme--> + <link href="main_files/bootstrap.css" rel="stylesheet"><!-- Loading Bootstrap --> + <link href="main_files/flat-ui.css" rel="stylesheet"><!-- Loading Flat UI --> + <script src="main_files/flat-ui.js"></script> + <!--<script src="dependencies/flatUI/js/vendor/video.js"></script>--> + + <!--bootstrap slider--> + <link href="main_files/bootstrap-slider.css" rel="stylesheet"> + <script src="main_files/bootstrap-slider.js"></script> + + <!--threeJS--> + <script src="main_files/three.js"></script> + <script src="main_files/OrbitControls.js"></script> + + <!--stl import/export--> + <script src="main_files/THREE2STL.js"></script> + <script src="main_files/STLLoader.js"></script> + + <!--numerical javascript--> + <script src="main_files/numeric-1.js"></script> + + <!--multi-threading--> + <script src="main_files/worker.js"></script> + <script src="main_files/persistentWorkers.js"></script><!--global workers--> + + <!--fea stuff--> + <script src="main_files/dmaCell.js"></script> + <script src="main_files/dmaPart.js"></script> + <!--<script src="js/fea/dmaBeam.js"></script>--> + <script src="main_files/dmaNode.js"></script> + + <!--models--> + <script src="main_files/threeModel.js"></script> + <script src="main_files/fillGeometry.js"></script> + <script src="main_files/lattice.js"></script> + + <!--views--> + <script src="main_files/meshHandle.js"></script> + <script src="main_files/threeView.js"></script> + <script src="main_files/pushPullMeshView.js"></script> + <script src="main_files/importView.js"></script> + <script src="main_files/fillGeometryView.js"></script> + <script src="main_files/latticeView.js"></script> + + <script src="main_files/main.js"></script> + + + <!--<script src="js/views/elementMenu.js"></script>--> + <script src="main_files/exportMenu.js"></script> + <script src="main_files/navbar.js"></script> + <link rel="stylesheet" type="text/css" href="main_files/main.css"> + +</head> +<body> + +<nav class="navbar navbar-inverse navbar-embossed" role="navigation"> + <div class="navbar-header"> + <!--<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#navbar-collapse-01">--> + <!--<span class="sr-only">Toggle navigation</span>--> + <!--</button>--> + <a id="mainNavLink" class="navbar-brand" href="http://cba.mit.edu/">CBA.MIT.EDU</a> + </div> + <div class="collapse navbar-collapse" id="navbar-collapse-01"> + <ul class="nav navbar-nav navbar-left"> + <li><a data-menu-id="importGeometry" class="menuHoverControls" href="#">Import</a></li> + <li><a data-menu-id="element" class="menuHoverControls" href="#">Element</a></li> + + <!--<li><a data-menu-id="controls1Menu" class="menuHoverControls" href="#">Controls1</a></li>--> + <!--<li><a data-menu-id="controls2Menu" class="menuHoverControls" href="#">Controls2</a></li>--> + <!--<li><a data-menu-id="controls3Menu" class="menuHoverControls" href="#">Glyphs</a></li>--> + <!--<li><a data-menu-id="controls4Menu" class="menuHoverControls" href="#">Buttons</a></li>--> + <!--<li><a data-menu-id="controls5Menu" class="menuHoverControls" href="#">Inputs</a></li>--> + <li class="dropdown navDropdown"> + <a href="#" class="dropdown-toggle" data-toggle="dropdown">Export <b class="caret"></b></a> + <span class="dropdown-arrow"></span> + <ul class="dropdown-menu"> + <li><a id="exportSTL" href="#">Export STL</a></li> + <li><a href="#">Another action</a></li> + <li><a href="#">Something else here</a></li> + <li class="divider"></li> + <li><a href="#">Separated link</a></li> + </ul> + </li> + <li><a data-menu-id="about" class="menuHoverControls" href="#">About</a></li> + </ul> + <ul class="nav navbar-nav navbar-right"> + <li><a id="clearAll" class="menuHoverControls" href="#">Clear All</a></li> + </ul> + <!--<a href="#fakelink" id="clearAll" class=" btn btn-lg btn-primary">Clear All</a>--> + </div><!-- /.navbar-collapse --> + </nav> +<div style="display: none;" id="importGeometry" class="row navMenu"> +</div> +<div style="display: none;" id="element" class="row navMenu"> + <div class="row demo-row"> + <div class="col-xs-3"> + <a id="elementCube" href="#">Cube</a> + </div> + </div> +</div> +<div style="display: none;" id="about" class="row navMenu"> + <div class="row demo-row"> + <div class="col-xs-3"> + Latest code available on github + </div> + </div> +</div> + +<div id="threeContainer"><canvas style="width: 1145px; height: 947px;" height="947" width="1145"></canvas></div> + + +</body></html> \ No newline at end of file diff --git a/dependencies/main_files/OrbitControls.js b/dependencies/main_files/OrbitControls.js new file mode 100644 index 00000000..4af88f78 --- /dev/null +++ b/dependencies/main_files/OrbitControls.js @@ -0,0 +1,675 @@ +/** + * @author qiao / https://github.com/qiao + * @author mrdoob / http://mrdoob.com + * @author alteredq / http://alteredqualia.com/ + * @author WestLangley / http://github.com/WestLangley + * @author erich666 / http://erichaines.com + */ +/*global THREE, console */ + +// This set of controls performs orbiting, dollying (zooming), and panning. It maintains +// the "up" direction as +Y, unlike the TrackballControls. Touch on tablet and phones is +// supported. +// +// Orbit - left mouse / touch: one finger move +// Zoom - middle mouse, or mousewheel / touch: two finger spread or squish +// Pan - right mouse, or arrow keys / touch: three finter swipe +// +// This is a drop-in replacement for (most) TrackballControls used in examples. +// That is, include this js file and wherever you see: +// controls = new THREE.TrackballControls( camera ); +// controls.target.z = 150; +// Simple substitute "OrbitControls" and the control should work as-is. + +THREE.OrbitControls = function ( object, domElement ) { + + this.object = object; + this.domElement = ( domElement !== undefined ) ? domElement : document; + + // API + + // Set to false to disable this control + this.enabled = true; + + // "target" sets the location of focus, where the control orbits around + // and where it pans with respect to. + this.target = new THREE.Vector3(); + + // center is old, deprecated; use "target" instead + this.center = this.target; + + // This option actually enables dollying in and out; left as "zoom" for + // backwards compatibility + this.noZoom = false; + this.zoomSpeed = 1.0; + + // Limits to how far you can dolly in and out + this.minDistance = 0; + this.maxDistance = Infinity; + + // Set to true to disable this control + this.noRotate = false; + this.rotateSpeed = 1.0; + + // Set to true to disable this control + this.noPan = false; + this.keyPanSpeed = 7.0; // pixels moved per arrow key push + + // Set to true to automatically rotate around the target + this.autoRotate = false; + this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60 + + // How far you can orbit vertically, upper and lower limits. + // Range is 0 to Math.PI radians. + this.minPolarAngle = 0; // radians + this.maxPolarAngle = Math.PI; // radians + + // How far you can orbit horizontally, upper and lower limits. + // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ]. + this.minAzimuthAngle = - Infinity; // radians + this.maxAzimuthAngle = Infinity; // radians + + // Set to true to disable use of the keys + this.noKeys = false; + + // The four arrow keys + this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 }; + + // Mouse buttons + this.mouseButtons = { ORBIT: THREE.MOUSE.LEFT, ZOOM: THREE.MOUSE.MIDDLE, PAN: THREE.MOUSE.RIGHT }; + + //////////// + // internals + + var scope = this; + + var EPS = 0.000001; + + var rotateStart = new THREE.Vector2(); + var rotateEnd = new THREE.Vector2(); + var rotateDelta = new THREE.Vector2(); + + var panStart = new THREE.Vector2(); + var panEnd = new THREE.Vector2(); + var panDelta = new THREE.Vector2(); + var panOffset = new THREE.Vector3(); + + var offset = new THREE.Vector3(); + + var dollyStart = new THREE.Vector2(); + var dollyEnd = new THREE.Vector2(); + var dollyDelta = new THREE.Vector2(); + + var theta; + var phi; + var phiDelta = 0; + var thetaDelta = 0; + var scale = 1; + var pan = new THREE.Vector3(); + + var lastPosition = new THREE.Vector3(); + var lastQuaternion = new THREE.Quaternion(); + + var STATE = { NONE : -1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 }; + + var state = STATE.NONE; + + // for reset + + this.target0 = this.target.clone(); + this.position0 = this.object.position.clone(); + + // so camera.up is the orbit axis + + var quat = new THREE.Quaternion().setFromUnitVectors( object.up, new THREE.Vector3( 0, 1, 0 ) ); + var quatInverse = quat.clone().inverse(); + + // events + + var changeEvent = { type: 'change' }; + var startEvent = { type: 'start'}; + var endEvent = { type: 'end'}; + + this.rotateLeft = function ( angle ) { + + if ( angle === undefined ) { + + angle = getAutoRotationAngle(); + + } + + thetaDelta -= angle; + + }; + + this.rotateUp = function ( angle ) { + + if ( angle === undefined ) { + + angle = getAutoRotationAngle(); + + } + + phiDelta -= angle; + + }; + + // pass in distance in world space to move left + this.panLeft = function ( distance ) { + + var te = this.object.matrix.elements; + + // get X column of matrix + panOffset.set( te[ 0 ], te[ 1 ], te[ 2 ] ); + panOffset.multiplyScalar( - distance ); + + pan.add( panOffset ); + + }; + + // pass in distance in world space to move up + this.panUp = function ( distance ) { + + var te = this.object.matrix.elements; + + // get Y column of matrix + panOffset.set( te[ 4 ], te[ 5 ], te[ 6 ] ); + panOffset.multiplyScalar( distance ); + + pan.add( panOffset ); + + }; + + // pass in x,y of change desired in pixel space, + // right and down are positive + this.pan = function ( deltaX, deltaY ) { + + var element = scope.domElement === document ? scope.domElement.body : scope.domElement; + + if ( scope.object.fov !== undefined ) { + + // perspective + var position = scope.object.position; + var offset = position.clone().sub( scope.target ); + var targetDistance = offset.length(); + + // half of the fov is center to top of screen + targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 ); + + // we actually don't use screenWidth, since perspective camera is fixed to screen height + scope.panLeft( 2 * deltaX * targetDistance / element.clientHeight ); + scope.panUp( 2 * deltaY * targetDistance / element.clientHeight ); + + } else if ( scope.object.top !== undefined ) { + + // orthographic + scope.panLeft( deltaX * (scope.object.right - scope.object.left) / element.clientWidth ); + scope.panUp( deltaY * (scope.object.top - scope.object.bottom) / element.clientHeight ); + + } else { + + // camera neither orthographic or perspective + console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' ); + + } + + }; + + this.dollyIn = function ( dollyScale ) { + + if ( dollyScale === undefined ) { + + dollyScale = getZoomScale(); + + } + + scale /= dollyScale; + + }; + + this.dollyOut = function ( dollyScale ) { + + if ( dollyScale === undefined ) { + + dollyScale = getZoomScale(); + + } + + scale *= dollyScale; + + }; + + this.update = function () { + + var position = this.object.position; + + offset.copy( position ).sub( this.target ); + + // rotate offset to "y-axis-is-up" space + offset.applyQuaternion( quat ); + + // angle from z-axis around y-axis + + theta = Math.atan2( offset.x, offset.z ); + + // angle from y-axis + + phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y ); + + if ( this.autoRotate && state === STATE.NONE ) { + + this.rotateLeft( getAutoRotationAngle() ); + + } + + theta += thetaDelta; + phi += phiDelta; + + // restrict theta to be between desired limits + theta = Math.max( this.minAzimuthAngle, Math.min( this.maxAzimuthAngle, theta ) ); + + // restrict phi to be between desired limits + phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, phi ) ); + + // restrict phi to be betwee EPS and PI-EPS + phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) ); + + var radius = offset.length() * scale; + + // restrict radius to be between desired limits + radius = Math.max( this.minDistance, Math.min( this.maxDistance, radius ) ); + + // move target to panned location + this.target.add( pan ); + + offset.x = radius * Math.sin( phi ) * Math.sin( theta ); + offset.y = radius * Math.cos( phi ); + offset.z = radius * Math.sin( phi ) * Math.cos( theta ); + + // rotate offset back to "camera-up-vector-is-up" space + offset.applyQuaternion( quatInverse ); + + position.copy( this.target ).add( offset ); + + this.object.lookAt( this.target ); + + thetaDelta = 0; + phiDelta = 0; + scale = 1; + pan.set( 0, 0, 0 ); + + // update condition is: + // min(camera displacement, camera rotation in radians)^2 > EPS + // using small-angle approximation cos(x/2) = 1 - x^2 / 8 + + if ( lastPosition.distanceToSquared( this.object.position ) > EPS + || 8 * (1 - lastQuaternion.dot(this.object.quaternion)) > EPS ) { + + this.dispatchEvent( changeEvent ); + + lastPosition.copy( this.object.position ); + lastQuaternion.copy (this.object.quaternion ); + + } + + }; + + + this.reset = function () { + + state = STATE.NONE; + + this.target.copy( this.target0 ); + this.object.position.copy( this.position0 ); + + this.update(); + + }; + + this.getPolarAngle = function () { + + return phi; + + }; + + this.getAzimuthalAngle = function () { + + return theta + + }; + + function getAutoRotationAngle() { + + return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed; + + } + + function getZoomScale() { + + return Math.pow( 0.95, scope.zoomSpeed ); + + } + + function onMouseDown( event ) { + + if ( scope.enabled === false ) return; + event.preventDefault(); + + if ( event.button === scope.mouseButtons.ORBIT ) { + if ( scope.noRotate === true ) return; + + state = STATE.ROTATE; + + rotateStart.set( event.clientX, event.clientY ); + + } else if ( event.button === scope.mouseButtons.ZOOM ) { + if ( scope.noZoom === true ) return; + + state = STATE.DOLLY; + + dollyStart.set( event.clientX, event.clientY ); + + } else if ( event.button === scope.mouseButtons.PAN ) { + if ( scope.noPan === true ) return; + + state = STATE.PAN; + + panStart.set( event.clientX, event.clientY ); + + } + + if ( state !== STATE.NONE ) { + document.addEventListener( 'mousemove', onMouseMove, false ); + document.addEventListener( 'mouseup', onMouseUp, false ); + scope.dispatchEvent( startEvent ); + } + + } + + function onMouseMove( event ) { + + if ( scope.enabled === false ) return; + + event.preventDefault(); + + var element = scope.domElement === document ? scope.domElement.body : scope.domElement; + + if ( state === STATE.ROTATE ) { + + if ( scope.noRotate === true ) return; + + rotateEnd.set( event.clientX, event.clientY ); + rotateDelta.subVectors( rotateEnd, rotateStart ); + + // rotating across whole screen goes 360 degrees around + scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed ); + + // rotating up and down along whole screen attempts to go 360, but limited to 180 + scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); + + rotateStart.copy( rotateEnd ); + + } else if ( state === STATE.DOLLY ) { + + if ( scope.noZoom === true ) return; + + dollyEnd.set( event.clientX, event.clientY ); + dollyDelta.subVectors( dollyEnd, dollyStart ); + + if ( dollyDelta.y > 0 ) { + + scope.dollyIn(); + + } else { + + scope.dollyOut(); + + } + + dollyStart.copy( dollyEnd ); + + } else if ( state === STATE.PAN ) { + + if ( scope.noPan === true ) return; + + panEnd.set( event.clientX, event.clientY ); + panDelta.subVectors( panEnd, panStart ); + + scope.pan( panDelta.x, panDelta.y ); + + panStart.copy( panEnd ); + + } + + if ( state !== STATE.NONE ) scope.update(); + + } + + function onMouseUp( /* event */ ) { + + if ( scope.enabled === false ) return; + + document.removeEventListener( 'mousemove', onMouseMove, false ); + document.removeEventListener( 'mouseup', onMouseUp, false ); + scope.dispatchEvent( endEvent ); + state = STATE.NONE; + + } + + function onMouseWheel( event ) { + + if ( scope.enabled === false || scope.noZoom === true || state !== STATE.NONE ) return; + + event.preventDefault(); + event.stopPropagation(); + + var delta = 0; + + if ( event.wheelDelta !== undefined ) { // WebKit / Opera / Explorer 9 + + delta = event.wheelDelta; + + } else if ( event.detail !== undefined ) { // Firefox + + delta = - event.detail; + + } + + if ( delta > 0 ) { + + scope.dollyOut(); + + } else { + + scope.dollyIn(); + + } + + scope.update(); + scope.dispatchEvent( startEvent ); + scope.dispatchEvent( endEvent ); + + } + + function onKeyDown( event ) { + + if ( scope.enabled === false || scope.noKeys === true || scope.noPan === true ) return; + + switch ( event.keyCode ) { + + case scope.keys.UP: + scope.pan( 0, scope.keyPanSpeed ); + scope.update(); + break; + + case scope.keys.BOTTOM: + scope.pan( 0, - scope.keyPanSpeed ); + scope.update(); + break; + + case scope.keys.LEFT: + scope.pan( scope.keyPanSpeed, 0 ); + scope.update(); + break; + + case scope.keys.RIGHT: + scope.pan( - scope.keyPanSpeed, 0 ); + scope.update(); + break; + + } + + } + + function touchstart( event ) { + + if ( scope.enabled === false ) return; + + switch ( event.touches.length ) { + + case 1: // one-fingered touch: rotate + + if ( scope.noRotate === true ) return; + + state = STATE.TOUCH_ROTATE; + + rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); + break; + + case 2: // two-fingered touch: dolly + + if ( scope.noZoom === true ) return; + + state = STATE.TOUCH_DOLLY; + + var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; + var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; + var distance = Math.sqrt( dx * dx + dy * dy ); + dollyStart.set( 0, distance ); + break; + + case 3: // three-fingered touch: pan + + if ( scope.noPan === true ) return; + + state = STATE.TOUCH_PAN; + + panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); + break; + + default: + + state = STATE.NONE; + + } + + if ( state !== STATE.NONE ) scope.dispatchEvent( startEvent ); + + } + + function touchmove( event ) { + + if ( scope.enabled === false ) return; + + event.preventDefault(); + event.stopPropagation(); + + var element = scope.domElement === document ? scope.domElement.body : scope.domElement; + + switch ( event.touches.length ) { + + case 1: // one-fingered touch: rotate + + if ( scope.noRotate === true ) return; + if ( state !== STATE.TOUCH_ROTATE ) return; + + rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); + rotateDelta.subVectors( rotateEnd, rotateStart ); + + // rotating across whole screen goes 360 degrees around + scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed ); + // rotating up and down along whole screen attempts to go 360, but limited to 180 + scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); + + rotateStart.copy( rotateEnd ); + + scope.update(); + break; + + case 2: // two-fingered touch: dolly + + if ( scope.noZoom === true ) return; + if ( state !== STATE.TOUCH_DOLLY ) return; + + var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; + var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; + var distance = Math.sqrt( dx * dx + dy * dy ); + + dollyEnd.set( 0, distance ); + dollyDelta.subVectors( dollyEnd, dollyStart ); + + if ( dollyDelta.y > 0 ) { + + scope.dollyOut(); + + } else { + + scope.dollyIn(); + + } + + dollyStart.copy( dollyEnd ); + + scope.update(); + break; + + case 3: // three-fingered touch: pan + + if ( scope.noPan === true ) return; + if ( state !== STATE.TOUCH_PAN ) return; + + panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); + panDelta.subVectors( panEnd, panStart ); + + scope.pan( panDelta.x, panDelta.y ); + + panStart.copy( panEnd ); + + scope.update(); + break; + + default: + + state = STATE.NONE; + + } + + } + + function touchend( /* event */ ) { + + if ( scope.enabled === false ) return; + + scope.dispatchEvent( endEvent ); + state = STATE.NONE; + + } + + this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false ); + this.domElement.addEventListener( 'mousedown', onMouseDown, false ); + this.domElement.addEventListener( 'mousewheel', onMouseWheel, false ); + this.domElement.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox + + this.domElement.addEventListener( 'touchstart', touchstart, false ); + this.domElement.addEventListener( 'touchend', touchend, false ); + this.domElement.addEventListener( 'touchmove', touchmove, false ); + + window.addEventListener( 'keydown', onKeyDown, false ); + + // force an update at start + this.update(); + +}; + +THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype ); +THREE.OrbitControls.prototype.constructor = THREE.OrbitControls; diff --git a/dependencies/main_files/STLLoader.js b/dependencies/main_files/STLLoader.js new file mode 100644 index 00000000..4e5f426c --- /dev/null +++ b/dependencies/main_files/STLLoader.js @@ -0,0 +1,446 @@ +/** + * @author aleeper / http://adamleeper.com/ + * @author mrdoob / http://mrdoob.com/ + * @author gero3 / https://github.com/gero3 + * + * Description: A THREE loader for STL ASCII files, as created by Solidworks and other CAD programs. + * + * Supports both binary and ASCII encoded files, with automatic detection of type. + * + * Limitations: + * Binary decoding supports "Magics" color format (http://en.wikipedia.org/wiki/STL_(file_format)#Color_in_binary_STL). + * There is perhaps some question as to how valid it is to always assume little-endian-ness. + * ASCII decoding assumes file is UTF-8. Seems to work for the examples... + * + * Usage: + * var loader = new THREE.STLLoader(); + * loader.addEventListener( 'load', function ( event ) { + * + * var geometry = event.content; + * scene.add( new THREE.Mesh( geometry ) ); + * + * } ); + * loader.load( './models/stl/slotted_disk.stl' ); + * + * For binary STLs geometry might contain colors for vertices. To use it: + * ... // use the same code to load STL as above + * var geometry = event.content; + * if (geometry.hasColors) { + * material = new THREE.MeshPhongMaterial({ opacity: geometry.alpha, vertexColors: THREE.VertexColors }); + * } else { .... } + * var mesh = new THREE.Mesh( geometry, material ); + */ + + +THREE.STLLoader = function () {}; + +THREE.STLLoader.prototype = { + + constructor: THREE.STLLoader + +}; + +THREE.STLLoader.prototype.load = function ( url, callback ) { + + var scope = this; + + var xhr = new XMLHttpRequest(); + + function onloaded( event ) { + + if ( event.target.status === 200 || event.target.status === 0 ) { + + var geometry = scope.parse( event.target.response || event.target.responseText ); + + scope.dispatchEvent( { type: 'load', content: geometry } ); + + if ( callback ) callback( geometry ); + + } else { + + scope.dispatchEvent( { type: 'error', message: 'Couldn\'t load URL [' + url + ']', response: event.target.statusText } ); + + } + + } + + xhr.addEventListener( 'load', onloaded, false ); + + xhr.addEventListener( 'progress', function ( event ) { + + scope.dispatchEvent( { type: 'progress', loaded: event.loaded, total: event.total } ); + + }, false ); + + xhr.addEventListener( 'error', function () { + + scope.dispatchEvent( { type: 'error', message: 'Couldn\'t load URL [' + url + ']' } ); + + }, false ); + + if ( xhr.overrideMimeType ) xhr.overrideMimeType( 'text/plain; charset=x-user-defined' ); + xhr.open( 'GET', url, true ); + xhr.responseType = 'arraybuffer'; + xhr.send( null ); + +}; + +THREE.STLLoader.prototype.parse = function ( data ) { + + + var isBinary = function () { + + var expect, face_size, n_faces, reader; + reader = new DataView( binData ); + face_size = (32 / 8 * 3) + ((32 / 8 * 3) * 3) + (16 / 8); + n_faces = reader.getUint32(80,true); + expect = 80 + (32 / 8) + (n_faces * face_size); + return expect === reader.byteLength; + + }; + + var binData = this.ensureBinary( data ); + + return isBinary() + ? this.parseBinary( binData ) + : this.parseASCII( this.ensureString( data ) ); + +}; + +THREE.STLLoader.prototype.parseBinary = function ( data ) { + + var reader = new DataView( data ); + var faces = reader.getUint32( 80, true ); + + var r, g, b, hasColors = false, colors; + var defaultR, defaultG, defaultB, alpha; + + // process STL header + // check for default color in header ("COLOR=rgba" sequence). + for (var index = 0; index < 80 - 10; index++) { + + if ((reader.getUint32(index, false) == 0x434F4C4F /*COLO*/) && + (reader.getUint8(index + 4) == 0x52 /*'R'*/) && + (reader.getUint8(index + 5) == 0x3D /*'='*/)) { + + hasColors = true; + colors = new Float32Array( faces * 3 * 3); + + defaultR = reader.getUint8(index + 6) / 255; + defaultG = reader.getUint8(index + 7) / 255; + defaultB = reader.getUint8(index + 8) / 255; + alpha = reader.getUint8(index + 9) / 255; + } + } + + var dataOffset = 84; + var faceLength = 12 * 4 + 2; + + var offset = 0; + + var geometry = new THREE.BufferGeometry(); + + var vertices = new Float32Array( faces * 3 * 3 ); + var normals = new Float32Array( faces * 3 * 3 ); + + for ( var face = 0; face < faces; face ++ ) { + + var start = dataOffset + face * faceLength; + var normalX = reader.getFloat32(start, true); + var normalY = reader.getFloat32(start + 4, true); + var normalZ = reader.getFloat32(start + 8, true); + + if (hasColors) { + + var packedColor = reader.getUint16(start + 48, true); + + if ((packedColor & 0x8000) === 0) { // facet has its own unique color + + r = (packedColor & 0x1F) / 31; + g = ((packedColor >> 5) & 0x1F) / 31; + b = ((packedColor >> 10) & 0x1F) / 31; + } else { + + r = defaultR; + g = defaultG; + b = defaultB; + } + } + + for ( var i = 1; i <= 3; i ++ ) { + + var vertexstart = start + i * 12; + + vertices[ offset ] = reader.getFloat32( vertexstart, true ); + vertices[ offset + 1 ] = reader.getFloat32( vertexstart + 4, true ); + vertices[ offset + 2 ] = reader.getFloat32( vertexstart + 8, true ); + + normals[ offset ] = normalX; + normals[ offset + 1 ] = normalY; + normals[ offset + 2 ] = normalZ; + + if (hasColors) { + colors[ offset ] = r; + colors[ offset + 1 ] = g; + colors[ offset + 2 ] = b; + } + + offset += 3; + + } + + } + + geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) ); + geometry.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) ); + + if (hasColors) { + geometry.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ) ); + geometry.hasColors = true; + geometry.alpha = alpha; + } + + return geometry; + +}; + +THREE.STLLoader.prototype.parseASCII = function (data) { + + var geometry, length, normal, patternFace, patternNormal, patternVertex, result, text; + geometry = new THREE.Geometry(); + patternFace = /facet([\s\S]*?)endfacet/g; + + while ( ( result = patternFace.exec( data ) ) !== null ) { + + text = result[0]; + patternNormal = /normal[\s]+([\-+]?[0-9]+\.?[0-9]*([eE][\-+]?[0-9]+)?)+[\s]+([\-+]?[0-9]*\.?[0-9]+([eE][\-+]?[0-9]+)?)+[\s]+([\-+]?[0-9]*\.?[0-9]+([eE][\-+]?[0-9]+)?)+/g; + + while ( ( result = patternNormal.exec( text ) ) !== null ) { + + normal = new THREE.Vector3( parseFloat( result[ 1 ] ), parseFloat( result[ 3 ] ), parseFloat( result[ 5 ] ) ); + + } + + patternVertex = /vertex[\s]+([\-+]?[0-9]+\.?[0-9]*([eE][\-+]?[0-9]+)?)+[\s]+([\-+]?[0-9]*\.?[0-9]+([eE][\-+]?[0-9]+)?)+[\s]+([\-+]?[0-9]*\.?[0-9]+([eE][\-+]?[0-9]+)?)+/g; + + while ( ( result = patternVertex.exec( text ) ) !== null ) { + + geometry.vertices.push( new THREE.Vector3( parseFloat( result[ 1 ] ), parseFloat( result[ 3 ] ), parseFloat( result[ 5 ] ) ) ); + + } + + length = geometry.vertices.length; + + geometry.faces.push( new THREE.Face3( length - 3, length - 2, length - 1, normal ) ); + + } + + geometry.computeBoundingBox(); + geometry.computeBoundingSphere(); + + return geometry; + +}; + +THREE.STLLoader.prototype.ensureString = function (buf) { + + if (typeof buf !== "string"){ + var array_buffer = new Uint8Array(buf); + var str = ''; + for(var i = 0; i < buf.byteLength; i++) { + str += String.fromCharCode(array_buffer[i]); // implicitly assumes little-endian + } + return str; + } else { + return buf; + } + +}; + +THREE.STLLoader.prototype.ensureBinary = function (buf) { + + if (typeof buf === "string"){ + var array_buffer = new Uint8Array(buf.length); + for(var i = 0; i < buf.length; i++) { + array_buffer[i] = buf.charCodeAt(i) & 0xff; // implicitly assumes little-endian + } + return array_buffer.buffer || array_buffer; + } else { + return buf; + } + +}; + +THREE.EventDispatcher.prototype.apply( THREE.STLLoader.prototype ); + +if ( typeof DataView === 'undefined'){ + + DataView = function(buffer, byteOffset, byteLength){ + + this.buffer = buffer; + this.byteOffset = byteOffset || 0; + this.byteLength = byteLength || buffer.byteLength || buffer.length; + this._isString = typeof buffer === "string"; + + } + + DataView.prototype = { + + _getCharCodes:function(buffer,start,length){ + start = start || 0; + length = length || buffer.length; + var end = start + length; + var codes = []; + for (var i = start; i < end; i++) { + codes.push(buffer.charCodeAt(i) & 0xff); + } + return codes; + }, + + _getBytes: function (length, byteOffset, littleEndian) { + + var result; + + // Handle the lack of endianness + if (littleEndian === undefined) { + + littleEndian = this._littleEndian; + + } + + // Handle the lack of byteOffset + if (byteOffset === undefined) { + + byteOffset = this.byteOffset; + + } else { + + byteOffset = this.byteOffset + byteOffset; + + } + + if (length === undefined) { + + length = this.byteLength - byteOffset; + + } + + // Error Checking + if (typeof byteOffset !== 'number') { + + throw new TypeError('DataView byteOffset is not a number'); + + } + + if (length < 0 || byteOffset + length > this.byteLength) { + + throw new Error('DataView length or (byteOffset+length) value is out of bounds'); + + } + + if (this.isString){ + + result = this._getCharCodes(this.buffer, byteOffset, byteOffset + length); + + } else { + + result = this.buffer.slice(byteOffset, byteOffset + length); + + } + + if (!littleEndian && length > 1) { + + if (!(result instanceof Array)) { + + result = Array.prototype.slice.call(result); + + } + + result.reverse(); + } + + return result; + + }, + + // Compatibility functions on a String Buffer + + getFloat64: function (byteOffset, littleEndian) { + + var b = this._getBytes(8, byteOffset, littleEndian), + + sign = 1 - (2 * (b[7] >> 7)), + exponent = ((((b[7] << 1) & 0xff) << 3) | (b[6] >> 4)) - ((1 << 10) - 1), + + // Binary operators such as | and << operate on 32 bit values, using + and Math.pow(2) instead + mantissa = ((b[6] & 0x0f) * Math.pow(2, 48)) + (b[5] * Math.pow(2, 40)) + (b[4] * Math.pow(2, 32)) + + (b[3] * Math.pow(2, 24)) + (b[2] * Math.pow(2, 16)) + (b[1] * Math.pow(2, 8)) + b[0]; + + if (exponent === 1024) { + if (mantissa !== 0) { + return NaN; + } else { + return sign * Infinity; + } + } + + if (exponent === -1023) { // Denormalized + return sign * mantissa * Math.pow(2, -1022 - 52); + } + + return sign * (1 + mantissa * Math.pow(2, -52)) * Math.pow(2, exponent); + + }, + + getFloat32: function (byteOffset, littleEndian) { + + var b = this._getBytes(4, byteOffset, littleEndian), + + sign = 1 - (2 * (b[3] >> 7)), + exponent = (((b[3] << 1) & 0xff) | (b[2] >> 7)) - 127, + mantissa = ((b[2] & 0x7f) << 16) | (b[1] << 8) | b[0]; + + if (exponent === 128) { + if (mantissa !== 0) { + return NaN; + } else { + return sign * Infinity; + } + } + + if (exponent === -127) { // Denormalized + return sign * mantissa * Math.pow(2, -126 - 23); + } + + return sign * (1 + mantissa * Math.pow(2, -23)) * Math.pow(2, exponent); + }, + + getInt32: function (byteOffset, littleEndian) { + var b = this._getBytes(4, byteOffset, littleEndian); + return (b[3] << 24) | (b[2] << 16) | (b[1] << 8) | b[0]; + }, + + getUint32: function (byteOffset, littleEndian) { + return this.getInt32(byteOffset, littleEndian) >>> 0; + }, + + getInt16: function (byteOffset, littleEndian) { + return (this.getUint16(byteOffset, littleEndian) << 16) >> 16; + }, + + getUint16: function (byteOffset, littleEndian) { + var b = this._getBytes(2, byteOffset, littleEndian); + return (b[1] << 8) | b[0]; + }, + + getInt8: function (byteOffset) { + return (this.getUint8(byteOffset) << 24) >> 24; + }, + + getUint8: function (byteOffset) { + return this._getBytes(1, byteOffset)[0]; + } + + }; + +} diff --git a/dependencies/main_files/THREE2STL.js b/dependencies/main_files/THREE2STL.js new file mode 100644 index 00000000..3bb79094 --- /dev/null +++ b/dependencies/main_files/THREE2STL.js @@ -0,0 +1,86 @@ +function stlFromGeometry( geometry, options ) { + + // calculate the faces and normals if they are not yet present + geometry.computeFaceNormals() + + var addX = 0 + var addY = 0 + var addZ = 0 + var download = false + + if ( options ) { + if ( options.useObjectPosition ) { + addX = geometry.mesh.position.x + addY = geometry.mesh.position.y + addZ = geometry.mesh.position.z + } + + if ( options.download ) { + download = true + } + } + + + var facetToStl = function( verts, normal ) { + var faceStl = '' + faceStl += 'facet normal ' + normal.x + ' ' + normal.y + ' ' + normal.z + '\n' + faceStl += 'outer loop\n' + + for ( var j = 0; j < 3; j++ ) { + var vert = verts[j] + faceStl += 'vertex ' + (vert.x+addX) + ' ' + (vert.y+addY) + ' ' + (vert.z+addZ) + '\n' + } + + faceStl += 'endloop\n' + faceStl += 'endfacet\n' + + return faceStl + } + + // start bulding the STL string + var stl = '' + stl += 'solid\n' + + for ( var i = 0; i < geometry.faces.length; i++ ) { + var face = geometry.faces[i] + + // if we have just a griangle, that's easy. just write them to the file + if ( face.d === undefined ) { + var verts = [ + geometry.vertices[ face.a ], + geometry.vertices[ face.b ], + geometry.vertices[ face.c ] + ] + + stl += facetToStl( verts, face.normal ) + + } else { + // if it's a quad, we need to triangulate it first + // split the quad into two triangles: abd and bcd + var verts = [] + verts[0] = [ + geometry.vertices[ face.a ], + geometry.vertices[ face.b ], + geometry.vertices[ face.d ] + ] + verts[1] = [ + geometry.vertices[ face.b ], + geometry.vertices[ face.c ], + geometry.vertices[ face.d ] + ] + + for ( var k = 0; k<2; k++ ) { + stl += facetToStl( verts[k], face.normal ) + } + + } + } + + stl += 'endsolid' + + if ( download ) { + document.location = 'data:Application/octet-stream, ' + encodeURIComponent( stl ) + } + + return stl +} diff --git a/dependencies/main_files/analytics.js b/dependencies/main_files/analytics.js new file mode 100644 index 00000000..3f501588 --- /dev/null +++ b/dependencies/main_files/analytics.js @@ -0,0 +1,11 @@ +/** + * Created by aghassaei on 1/8/15. + */ + + (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ + (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), + m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) + })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); + + ga('create', 'UA-58379815-1', 'auto'); + ga('send', 'pageview'); diff --git a/dependencies/main_files/backbone.js b/dependencies/main_files/backbone.js new file mode 100644 index 00000000..24a550a0 --- /dev/null +++ b/dependencies/main_files/backbone.js @@ -0,0 +1,1608 @@ +// Backbone.js 1.1.2 + +// (c) 2010-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors +// Backbone may be freely distributed under the MIT license. +// For all details and documentation: +// http://backbonejs.org + +(function(root, factory) { + + // Set up Backbone appropriately for the environment. Start with AMD. + if (typeof define === 'function' && define.amd) { + define(['underscore', 'jquery', 'exports'], function(_, $, exports) { + // Export global even in AMD case in case this script is loaded with + // others that may still expect a global Backbone. + root.Backbone = factory(root, exports, _, $); + }); + + // Next for Node.js or CommonJS. jQuery may not be needed as a module. + } else if (typeof exports !== 'undefined') { + var _ = require('underscore'); + factory(root, exports, _); + + // Finally, as a browser global. + } else { + root.Backbone = factory(root, {}, root._, (root.jQuery || root.Zepto || root.ender || root.$)); + } + +}(this, function(root, Backbone, _, $) { + + // Initial Setup + // ------------- + + // Save the previous value of the `Backbone` variable, so that it can be + // restored later on, if `noConflict` is used. + var previousBackbone = root.Backbone; + + // Create local references to array methods we'll want to use later. + var array = []; + var push = array.push; + var slice = array.slice; + var splice = array.splice; + + // Current version of the library. Keep in sync with `package.json`. + Backbone.VERSION = '1.1.2'; + + // For Backbone's purposes, jQuery, Zepto, Ender, or My Library (kidding) owns + // the `$` variable. + Backbone.$ = $; + + // Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable + // to its previous owner. Returns a reference to this Backbone object. + Backbone.noConflict = function() { + root.Backbone = previousBackbone; + return this; + }; + + // Turn on `emulateHTTP` to support legacy HTTP servers. Setting this option + // will fake `"PATCH"`, `"PUT"` and `"DELETE"` requests via the `_method` parameter and + // set a `X-Http-Method-Override` header. + Backbone.emulateHTTP = false; + + // Turn on `emulateJSON` to support legacy servers that can't deal with direct + // `application/json` requests ... will encode the body as + // `application/x-www-form-urlencoded` instead and will send the model in a + // form param named `model`. + Backbone.emulateJSON = false; + + // Backbone.Events + // --------------- + + // A module that can be mixed in to *any object* in order to provide it with + // custom events. You may bind with `on` or remove with `off` callback + // functions to an event; `trigger`-ing an event fires all callbacks in + // succession. + // + // var object = {}; + // _.extend(object, Backbone.Events); + // object.on('expand', function(){ alert('expanded'); }); + // object.trigger('expand'); + // + var Events = Backbone.Events = { + + // Bind an event to a `callback` function. Passing `"all"` will bind + // the callback to all events fired. + on: function(name, callback, context) { + if (!eventsApi(this, 'on', name, [callback, context]) || !callback) return this; + this._events || (this._events = {}); + var events = this._events[name] || (this._events[name] = []); + events.push({callback: callback, context: context, ctx: context || this}); + return this; + }, + + // Bind an event to only be triggered a single time. After the first time + // the callback is invoked, it will be removed. + once: function(name, callback, context) { + if (!eventsApi(this, 'once', name, [callback, context]) || !callback) return this; + var self = this; + var once = _.once(function() { + self.off(name, once); + callback.apply(this, arguments); + }); + once._callback = callback; + return this.on(name, once, context); + }, + + // Remove one or many callbacks. If `context` is null, removes all + // callbacks with that function. If `callback` is null, removes all + // callbacks for the event. If `name` is null, removes all bound + // callbacks for all events. + off: function(name, callback, context) { + var retain, ev, events, names, i, l, j, k; + if (!this._events || !eventsApi(this, 'off', name, [callback, context])) return this; + if (!name && !callback && !context) { + this._events = void 0; + return this; + } + names = name ? [name] : _.keys(this._events); + for (i = 0, l = names.length; i < l; i++) { + name = names[i]; + if (events = this._events[name]) { + this._events[name] = retain = []; + if (callback || context) { + for (j = 0, k = events.length; j < k; j++) { + ev = events[j]; + if ((callback && callback !== ev.callback && callback !== ev.callback._callback) || + (context && context !== ev.context)) { + retain.push(ev); + } + } + } + if (!retain.length) delete this._events[name]; + } + } + + return this; + }, + + // Trigger one or many events, firing all bound callbacks. Callbacks are + // passed the same arguments as `trigger` is, apart from the event name + // (unless you're listening on `"all"`, which will cause your callback to + // receive the true name of the event as the first argument). + trigger: function(name) { + if (!this._events) return this; + var args = slice.call(arguments, 1); + if (!eventsApi(this, 'trigger', name, args)) return this; + var events = this._events[name]; + var allEvents = this._events.all; + if (events) triggerEvents(events, args); + if (allEvents) triggerEvents(allEvents, arguments); + return this; + }, + + // Tell this object to stop listening to either specific events ... or + // to every object it's currently listening to. + stopListening: function(obj, name, callback) { + var listeningTo = this._listeningTo; + if (!listeningTo) return this; + var remove = !name && !callback; + if (!callback && typeof name === 'object') callback = this; + if (obj) (listeningTo = {})[obj._listenId] = obj; + for (var id in listeningTo) { + obj = listeningTo[id]; + obj.off(name, callback, this); + if (remove || _.isEmpty(obj._events)) delete this._listeningTo[id]; + } + return this; + } + + }; + + // Regular expression used to split event strings. + var eventSplitter = /\s+/; + + // Implement fancy features of the Events API such as multiple event + // names `"change blur"` and jQuery-style event maps `{change: action}` + // in terms of the existing API. + var eventsApi = function(obj, action, name, rest) { + if (!name) return true; + + // Handle event maps. + if (typeof name === 'object') { + for (var key in name) { + obj[action].apply(obj, [key, name[key]].concat(rest)); + } + return false; + } + + // Handle space separated event names. + if (eventSplitter.test(name)) { + var names = name.split(eventSplitter); + for (var i = 0, l = names.length; i < l; i++) { + obj[action].apply(obj, [names[i]].concat(rest)); + } + return false; + } + + return true; + }; + + // A difficult-to-believe, but optimized internal dispatch function for + // triggering events. Tries to keep the usual cases speedy (most internal + // Backbone events have 3 arguments). + var triggerEvents = function(events, args) { + var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2]; + switch (args.length) { + case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx); return; + case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1); return; + case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2); return; + case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return; + default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); return; + } + }; + + var listenMethods = {listenTo: 'on', listenToOnce: 'once'}; + + // Inversion-of-control versions of `on` and `once`. Tell *this* object to + // listen to an event in another object ... keeping track of what it's + // listening to. + _.each(listenMethods, function(implementation, method) { + Events[method] = function(obj, name, callback) { + var listeningTo = this._listeningTo || (this._listeningTo = {}); + var id = obj._listenId || (obj._listenId = _.uniqueId('l')); + listeningTo[id] = obj; + if (!callback && typeof name === 'object') callback = this; + obj[implementation](name, callback, this); + return this; + }; + }); + + // Aliases for backwards compatibility. + Events.bind = Events.on; + Events.unbind = Events.off; + + // Allow the `Backbone` object to serve as a global event bus, for folks who + // want global "pubsub" in a convenient place. + _.extend(Backbone, Events); + + // Backbone.Model + // -------------- + + // Backbone **Models** are the basic data object in the framework -- + // frequently representing a row in a table in a database on your server. + // A discrete chunk of data and a bunch of useful, related methods for + // performing computations and transformations on that data. + + // Create a new model with the specified attributes. A client id (`cid`) + // is automatically generated and assigned for you. + var Model = Backbone.Model = function(attributes, options) { + var attrs = attributes || {}; + options || (options = {}); + this.cid = _.uniqueId('c'); + this.attributes = {}; + if (options.collection) this.collection = options.collection; + if (options.parse) attrs = this.parse(attrs, options) || {}; + attrs = _.defaults({}, attrs, _.result(this, 'defaults')); + this.set(attrs, options); + this.changed = {}; + this.initialize.apply(this, arguments); + }; + + // Attach all inheritable methods to the Model prototype. + _.extend(Model.prototype, Events, { + + // A hash of attributes whose current and previous value differ. + changed: null, + + // The value returned during the last failed validation. + validationError: null, + + // The default name for the JSON `id` attribute is `"id"`. MongoDB and + // CouchDB users may want to set this to `"_id"`. + idAttribute: 'id', + + // Initialize is an empty function by default. Override it with your own + // initialization logic. + initialize: function(){}, + + // Return a copy of the model's `attributes` object. + toJSON: function(options) { + return _.clone(this.attributes); + }, + + // Proxy `Backbone.sync` by default -- but override this if you need + // custom syncing semantics for *this* particular model. + sync: function() { + return Backbone.sync.apply(this, arguments); + }, + + // Get the value of an attribute. + get: function(attr) { + return this.attributes[attr]; + }, + + // Get the HTML-escaped value of an attribute. + escape: function(attr) { + return _.escape(this.get(attr)); + }, + + // Returns `true` if the attribute contains a value that is not null + // or undefined. + has: function(attr) { + return this.get(attr) != null; + }, + + // Set a hash of model attributes on the object, firing `"change"`. This is + // the core primitive operation of a model, updating the data and notifying + // anyone who needs to know about the change in state. The heart of the beast. + set: function(key, val, options) { + var attr, attrs, unset, changes, silent, changing, prev, current; + if (key == null) return this; + + // Handle both `"key", value` and `{key: value}` -style arguments. + if (typeof key === 'object') { + attrs = key; + options = val; + } else { + (attrs = {})[key] = val; + } + + options || (options = {}); + + // Run validation. + if (!this._validate(attrs, options)) return false; + + // Extract attributes and options. + unset = options.unset; + silent = options.silent; + changes = []; + changing = this._changing; + this._changing = true; + + if (!changing) { + this._previousAttributes = _.clone(this.attributes); + this.changed = {}; + } + current = this.attributes, prev = this._previousAttributes; + + // Check for changes of `id`. + if (this.idAttribute in attrs) this.id = attrs[this.idAttribute]; + + // For each `set` attribute, update or delete the current value. + for (attr in attrs) { + val = attrs[attr]; + if (!_.isEqual(current[attr], val)) changes.push(attr); + if (!_.isEqual(prev[attr], val)) { + this.changed[attr] = val; + } else { + delete this.changed[attr]; + } + unset ? delete current[attr] : current[attr] = val; + } + + // Trigger all relevant attribute changes. + if (!silent) { + if (changes.length) this._pending = options; + for (var i = 0, l = changes.length; i < l; i++) { + this.trigger('change:' + changes[i], this, current[changes[i]], options); + } + } + + // You might be wondering why there's a `while` loop here. Changes can + // be recursively nested within `"change"` events. + if (changing) return this; + if (!silent) { + while (this._pending) { + options = this._pending; + this._pending = false; + this.trigger('change', this, options); + } + } + this._pending = false; + this._changing = false; + return this; + }, + + // Remove an attribute from the model, firing `"change"`. `unset` is a noop + // if the attribute doesn't exist. + unset: function(attr, options) { + return this.set(attr, void 0, _.extend({}, options, {unset: true})); + }, + + // Clear all attributes on the model, firing `"change"`. + clear: function(options) { + var attrs = {}; + for (var key in this.attributes) attrs[key] = void 0; + return this.set(attrs, _.extend({}, options, {unset: true})); + }, + + // Determine if the model has changed since the last `"change"` event. + // If you specify an attribute name, determine if that attribute has changed. + hasChanged: function(attr) { + if (attr == null) return !_.isEmpty(this.changed); + return _.has(this.changed, attr); + }, + + // Return an object containing all the attributes that have changed, or + // false if there are no changed attributes. Useful for determining what + // parts of a view need to be updated and/or what attributes need to be + // persisted to the server. Unset attributes will be set to undefined. + // You can also pass an attributes object to diff against the model, + // determining if there *would be* a change. + changedAttributes: function(diff) { + if (!diff) return this.hasChanged() ? _.clone(this.changed) : false; + var val, changed = false; + var old = this._changing ? this._previousAttributes : this.attributes; + for (var attr in diff) { + if (_.isEqual(old[attr], (val = diff[attr]))) continue; + (changed || (changed = {}))[attr] = val; + } + return changed; + }, + + // Get the previous value of an attribute, recorded at the time the last + // `"change"` event was fired. + previous: function(attr) { + if (attr == null || !this._previousAttributes) return null; + return this._previousAttributes[attr]; + }, + + // Get all of the attributes of the model at the time of the previous + // `"change"` event. + previousAttributes: function() { + return _.clone(this._previousAttributes); + }, + + // Fetch the model from the server. If the server's representation of the + // model differs from its current attributes, they will be overridden, + // triggering a `"change"` event. + fetch: function(options) { + options = options ? _.clone(options) : {}; + if (options.parse === void 0) options.parse = true; + var model = this; + var success = options.success; + options.success = function(resp) { + if (!model.set(model.parse(resp, options), options)) return false; + if (success) success(model, resp, options); + model.trigger('sync', model, resp, options); + }; + wrapError(this, options); + return this.sync('read', this, options); + }, + + // Set a hash of model attributes, and sync the model to the server. + // If the server returns an attributes hash that differs, the model's + // state will be `set` again. + save: function(key, val, options) { + var attrs, method, xhr, attributes = this.attributes; + + // Handle both `"key", value` and `{key: value}` -style arguments. + if (key == null || typeof key === 'object') { + attrs = key; + options = val; + } else { + (attrs = {})[key] = val; + } + + options = _.extend({validate: true}, options); + + // If we're not waiting and attributes exist, save acts as + // `set(attr).save(null, opts)` with validation. Otherwise, check if + // the model will be valid when the attributes, if any, are set. + if (attrs && !options.wait) { + if (!this.set(attrs, options)) return false; + } else { + if (!this._validate(attrs, options)) return false; + } + + // Set temporary attributes if `{wait: true}`. + if (attrs && options.wait) { + this.attributes = _.extend({}, attributes, attrs); + } + + // After a successful server-side save, the client is (optionally) + // updated with the server-side state. + if (options.parse === void 0) options.parse = true; + var model = this; + var success = options.success; + options.success = function(resp) { + // Ensure attributes are restored during synchronous saves. + model.attributes = attributes; + var serverAttrs = model.parse(resp, options); + if (options.wait) serverAttrs = _.extend(attrs || {}, serverAttrs); + if (_.isObject(serverAttrs) && !model.set(serverAttrs, options)) { + return false; + } + if (success) success(model, resp, options); + model.trigger('sync', model, resp, options); + }; + wrapError(this, options); + + method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update'); + if (method === 'patch') options.attrs = attrs; + xhr = this.sync(method, this, options); + + // Restore attributes. + if (attrs && options.wait) this.attributes = attributes; + + return xhr; + }, + + // Destroy this model on the server if it was already persisted. + // Optimistically removes the model from its collection, if it has one. + // If `wait: true` is passed, waits for the server to respond before removal. + destroy: function(options) { + options = options ? _.clone(options) : {}; + var model = this; + var success = options.success; + + var destroy = function() { + model.trigger('destroy', model, model.collection, options); + }; + + options.success = function(resp) { + if (options.wait || model.isNew()) destroy(); + if (success) success(model, resp, options); + if (!model.isNew()) model.trigger('sync', model, resp, options); + }; + + if (this.isNew()) { + options.success(); + return false; + } + wrapError(this, options); + + var xhr = this.sync('delete', this, options); + if (!options.wait) destroy(); + return xhr; + }, + + // Default URL for the model's representation on the server -- if you're + // using Backbone's restful methods, override this to change the endpoint + // that will be called. + url: function() { + var base = + _.result(this, 'urlRoot') || + _.result(this.collection, 'url') || + urlError(); + if (this.isNew()) return base; + return base.replace(/([^\/])$/, '$1/') + encodeURIComponent(this.id); + }, + + // **parse** converts a response into the hash of attributes to be `set` on + // the model. The default implementation is just to pass the response along. + parse: function(resp, options) { + return resp; + }, + + // Create a new model with identical attributes to this one. + clone: function() { + return new this.constructor(this.attributes); + }, + + // A model is new if it has never been saved to the server, and lacks an id. + isNew: function() { + return !this.has(this.idAttribute); + }, + + // Check if the model is currently in a valid state. + isValid: function(options) { + return this._validate({}, _.extend(options || {}, { validate: true })); + }, + + // Run validation against the next complete set of model attributes, + // returning `true` if all is well. Otherwise, fire an `"invalid"` event. + _validate: function(attrs, options) { + if (!options.validate || !this.validate) return true; + attrs = _.extend({}, this.attributes, attrs); + var error = this.validationError = this.validate(attrs, options) || null; + if (!error) return true; + this.trigger('invalid', this, error, _.extend(options, {validationError: error})); + return false; + } + + }); + + // Underscore methods that we want to implement on the Model. + var modelMethods = ['keys', 'values', 'pairs', 'invert', 'pick', 'omit']; + + // Mix in each Underscore method as a proxy to `Model#attributes`. + _.each(modelMethods, function(method) { + Model.prototype[method] = function() { + var args = slice.call(arguments); + args.unshift(this.attributes); + return _[method].apply(_, args); + }; + }); + + // Backbone.Collection + // ------------------- + + // If models tend to represent a single row of data, a Backbone Collection is + // more analagous to a table full of data ... or a small slice or page of that + // table, or a collection of rows that belong together for a particular reason + // -- all of the messages in this particular folder, all of the documents + // belonging to this particular author, and so on. Collections maintain + // indexes of their models, both in order, and for lookup by `id`. + + // Create a new **Collection**, perhaps to contain a specific type of `model`. + // If a `comparator` is specified, the Collection will maintain + // its models in sort order, as they're added and removed. + var Collection = Backbone.Collection = function(models, options) { + options || (options = {}); + if (options.model) this.model = options.model; + if (options.comparator !== void 0) this.comparator = options.comparator; + this._reset(); + this.initialize.apply(this, arguments); + if (models) this.reset(models, _.extend({silent: true}, options)); + }; + + // Default options for `Collection#set`. + var setOptions = {add: true, remove: true, merge: true}; + var addOptions = {add: true, remove: false}; + + // Define the Collection's inheritable methods. + _.extend(Collection.prototype, Events, { + + // The default model for a collection is just a **Backbone.Model**. + // This should be overridden in most cases. + model: Model, + + // Initialize is an empty function by default. Override it with your own + // initialization logic. + initialize: function(){}, + + // The JSON representation of a Collection is an array of the + // models' attributes. + toJSON: function(options) { + return this.map(function(model){ return model.toJSON(options); }); + }, + + // Proxy `Backbone.sync` by default. + sync: function() { + return Backbone.sync.apply(this, arguments); + }, + + // Add a model, or list of models to the set. + add: function(models, options) { + return this.set(models, _.extend({merge: false}, options, addOptions)); + }, + + // Remove a model, or a list of models from the set. + remove: function(models, options) { + var singular = !_.isArray(models); + models = singular ? [models] : _.clone(models); + options || (options = {}); + var i, l, index, model; + for (i = 0, l = models.length; i < l; i++) { + model = models[i] = this.get(models[i]); + if (!model) continue; + delete this._byId[model.id]; + delete this._byId[model.cid]; + index = this.indexOf(model); + this.models.splice(index, 1); + this.length--; + if (!options.silent) { + options.index = index; + model.trigger('remove', model, this, options); + } + this._removeReference(model, options); + } + return singular ? models[0] : models; + }, + + // Update a collection by `set`-ing a new list of models, adding new ones, + // removing models that are no longer present, and merging models that + // already exist in the collection, as necessary. Similar to **Model#set**, + // the core operation for updating the data contained by the collection. + set: function(models, options) { + options = _.defaults({}, options, setOptions); + if (options.parse) models = this.parse(models, options); + var singular = !_.isArray(models); + models = singular ? (models ? [models] : []) : _.clone(models); + var i, l, id, model, attrs, existing, sort; + var at = options.at; + var targetModel = this.model; + var sortable = this.comparator && (at == null) && options.sort !== false; + var sortAttr = _.isString(this.comparator) ? this.comparator : null; + var toAdd = [], toRemove = [], modelMap = {}; + var add = options.add, merge = options.merge, remove = options.remove; + var order = !sortable && add && remove ? [] : false; + + // Turn bare objects into model references, and prevent invalid models + // from being added. + for (i = 0, l = models.length; i < l; i++) { + attrs = models[i] || {}; + if (attrs instanceof Model) { + id = model = attrs; + } else { + id = attrs[targetModel.prototype.idAttribute || 'id']; + } + + // If a duplicate is found, prevent it from being added and + // optionally merge it into the existing model. + if (existing = this.get(id)) { + if (remove) modelMap[existing.cid] = true; + if (merge) { + attrs = attrs === model ? model.attributes : attrs; + if (options.parse) attrs = existing.parse(attrs, options); + existing.set(attrs, options); + if (sortable && !sort && existing.hasChanged(sortAttr)) sort = true; + } + models[i] = existing; + + // If this is a new, valid model, push it to the `toAdd` list. + } else if (add) { + model = models[i] = this._prepareModel(attrs, options); + if (!model) continue; + toAdd.push(model); + this._addReference(model, options); + } + + // Do not add multiple models with the same `id`. + model = existing || model; + if (order && (model.isNew() || !modelMap[model.id])) order.push(model); + modelMap[model.id] = true; + } + + // Remove nonexistent models if appropriate. + if (remove) { + for (i = 0, l = this.length; i < l; ++i) { + if (!modelMap[(model = this.models[i]).cid]) toRemove.push(model); + } + if (toRemove.length) this.remove(toRemove, options); + } + + // See if sorting is needed, update `length` and splice in new models. + if (toAdd.length || (order && order.length)) { + if (sortable) sort = true; + this.length += toAdd.length; + if (at != null) { + for (i = 0, l = toAdd.length; i < l; i++) { + this.models.splice(at + i, 0, toAdd[i]); + } + } else { + if (order) this.models.length = 0; + var orderedModels = order || toAdd; + for (i = 0, l = orderedModels.length; i < l; i++) { + this.models.push(orderedModels[i]); + } + } + } + + // Silently sort the collection if appropriate. + if (sort) this.sort({silent: true}); + + // Unless silenced, it's time to fire all appropriate add/sort events. + if (!options.silent) { + for (i = 0, l = toAdd.length; i < l; i++) { + (model = toAdd[i]).trigger('add', model, this, options); + } + if (sort || (order && order.length)) this.trigger('sort', this, options); + } + + // Return the added (or merged) model (or models). + return singular ? models[0] : models; + }, + + // When you have more items than you want to add or remove individually, + // you can reset the entire set with a new list of models, without firing + // any granular `add` or `remove` events. Fires `reset` when finished. + // Useful for bulk operations and optimizations. + reset: function(models, options) { + options || (options = {}); + for (var i = 0, l = this.models.length; i < l; i++) { + this._removeReference(this.models[i], options); + } + options.previousModels = this.models; + this._reset(); + models = this.add(models, _.extend({silent: true}, options)); + if (!options.silent) this.trigger('reset', this, options); + return models; + }, + + // Add a model to the end of the collection. + push: function(model, options) { + return this.add(model, _.extend({at: this.length}, options)); + }, + + // Remove a model from the end of the collection. + pop: function(options) { + var model = this.at(this.length - 1); + this.remove(model, options); + return model; + }, + + // Add a model to the beginning of the collection. + unshift: function(model, options) { + return this.add(model, _.extend({at: 0}, options)); + }, + + // Remove a model from the beginning of the collection. + shift: function(options) { + var model = this.at(0); + this.remove(model, options); + return model; + }, + + // Slice out a sub-array of models from the collection. + slice: function() { + return slice.apply(this.models, arguments); + }, + + // Get a model from the set by id. + get: function(obj) { + if (obj == null) return void 0; + return this._byId[obj] || this._byId[obj.id] || this._byId[obj.cid]; + }, + + // Get the model at the given index. + at: function(index) { + return this.models[index]; + }, + + // Return models with matching attributes. Useful for simple cases of + // `filter`. + where: function(attrs, first) { + if (_.isEmpty(attrs)) return first ? void 0 : []; + return this[first ? 'find' : 'filter'](function(model) { + for (var key in attrs) { + if (attrs[key] !== model.get(key)) return false; + } + return true; + }); + }, + + // Return the first model with matching attributes. Useful for simple cases + // of `find`. + findWhere: function(attrs) { + return this.where(attrs, true); + }, + + // Force the collection to re-sort itself. You don't need to call this under + // normal circumstances, as the set will maintain sort order as each item + // is added. + sort: function(options) { + if (!this.comparator) throw new Error('Cannot sort a set without a comparator'); + options || (options = {}); + + // Run sort based on type of `comparator`. + if (_.isString(this.comparator) || this.comparator.length === 1) { + this.models = this.sortBy(this.comparator, this); + } else { + this.models.sort(_.bind(this.comparator, this)); + } + + if (!options.silent) this.trigger('sort', this, options); + return this; + }, + + // Pluck an attribute from each model in the collection. + pluck: function(attr) { + return _.invoke(this.models, 'get', attr); + }, + + // Fetch the default set of models for this collection, resetting the + // collection when they arrive. If `reset: true` is passed, the response + // data will be passed through the `reset` method instead of `set`. + fetch: function(options) { + options = options ? _.clone(options) : {}; + if (options.parse === void 0) options.parse = true; + var success = options.success; + var collection = this; + options.success = function(resp) { + var method = options.reset ? 'reset' : 'set'; + collection[method](resp, options); + if (success) success(collection, resp, options); + collection.trigger('sync', collection, resp, options); + }; + wrapError(this, options); + return this.sync('read', this, options); + }, + + // Create a new instance of a model in this collection. Add the model to the + // collection immediately, unless `wait: true` is passed, in which case we + // wait for the server to agree. + create: function(model, options) { + options = options ? _.clone(options) : {}; + if (!(model = this._prepareModel(model, options))) return false; + if (!options.wait) this.add(model, options); + var collection = this; + var success = options.success; + options.success = function(model, resp) { + if (options.wait) collection.add(model, options); + if (success) success(model, resp, options); + }; + model.save(null, options); + return model; + }, + + // **parse** converts a response into a list of models to be added to the + // collection. The default implementation is just to pass it through. + parse: function(resp, options) { + return resp; + }, + + // Create a new collection with an identical list of models as this one. + clone: function() { + return new this.constructor(this.models); + }, + + // Private method to reset all internal state. Called when the collection + // is first initialized or reset. + _reset: function() { + this.length = 0; + this.models = []; + this._byId = {}; + }, + + // Prepare a hash of attributes (or other model) to be added to this + // collection. + _prepareModel: function(attrs, options) { + if (attrs instanceof Model) return attrs; + options = options ? _.clone(options) : {}; + options.collection = this; + var model = new this.model(attrs, options); + if (!model.validationError) return model; + this.trigger('invalid', this, model.validationError, options); + return false; + }, + + // Internal method to create a model's ties to a collection. + _addReference: function(model, options) { + this._byId[model.cid] = model; + if (model.id != null) this._byId[model.id] = model; + if (!model.collection) model.collection = this; + model.on('all', this._onModelEvent, this); + }, + + // Internal method to sever a model's ties to a collection. + _removeReference: function(model, options) { + if (this === model.collection) delete model.collection; + model.off('all', this._onModelEvent, this); + }, + + // Internal method called every time a model in the set fires an event. + // Sets need to update their indexes when models change ids. All other + // events simply proxy through. "add" and "remove" events that originate + // in other collections are ignored. + _onModelEvent: function(event, model, collection, options) { + if ((event === 'add' || event === 'remove') && collection !== this) return; + if (event === 'destroy') this.remove(model, options); + if (model && event === 'change:' + model.idAttribute) { + delete this._byId[model.previous(model.idAttribute)]; + if (model.id != null) this._byId[model.id] = model; + } + this.trigger.apply(this, arguments); + } + + }); + + // Underscore methods that we want to implement on the Collection. + // 90% of the core usefulness of Backbone Collections is actually implemented + // right here: + var methods = ['forEach', 'each', 'map', 'collect', 'reduce', 'foldl', + 'inject', 'reduceRight', 'foldr', 'find', 'detect', 'filter', 'select', + 'reject', 'every', 'all', 'some', 'any', 'include', 'contains', 'invoke', + 'max', 'min', 'toArray', 'size', 'first', 'head', 'take', 'initial', 'rest', + 'tail', 'drop', 'last', 'without', 'difference', 'indexOf', 'shuffle', + 'lastIndexOf', 'isEmpty', 'chain', 'sample']; + + // Mix in each Underscore method as a proxy to `Collection#models`. + _.each(methods, function(method) { + Collection.prototype[method] = function() { + var args = slice.call(arguments); + args.unshift(this.models); + return _[method].apply(_, args); + }; + }); + + // Underscore methods that take a property name as an argument. + var attributeMethods = ['groupBy', 'countBy', 'sortBy', 'indexBy']; + + // Use attributes instead of properties. + _.each(attributeMethods, function(method) { + Collection.prototype[method] = function(value, context) { + var iterator = _.isFunction(value) ? value : function(model) { + return model.get(value); + }; + return _[method](this.models, iterator, context); + }; + }); + + // Backbone.View + // ------------- + + // Backbone Views are almost more convention than they are actual code. A View + // is simply a JavaScript object that represents a logical chunk of UI in the + // DOM. This might be a single item, an entire list, a sidebar or panel, or + // even the surrounding frame which wraps your whole app. Defining a chunk of + // UI as a **View** allows you to define your DOM events declaratively, without + // having to worry about render order ... and makes it easy for the view to + // react to specific changes in the state of your models. + + // Creating a Backbone.View creates its initial element outside of the DOM, + // if an existing element is not provided... + var View = Backbone.View = function(options) { + this.cid = _.uniqueId('view'); + options || (options = {}); + _.extend(this, _.pick(options, viewOptions)); + this._ensureElement(); + this.initialize.apply(this, arguments); + this.delegateEvents(); + }; + + // Cached regex to split keys for `delegate`. + var delegateEventSplitter = /^(\S+)\s*(.*)$/; + + // List of view options to be merged as properties. + var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events']; + + // Set up all inheritable **Backbone.View** properties and methods. + _.extend(View.prototype, Events, { + + // The default `tagName` of a View's element is `"div"`. + tagName: 'div', + + // jQuery delegate for element lookup, scoped to DOM elements within the + // current view. This should be preferred to global lookups where possible. + $: function(selector) { + return this.$el.find(selector); + }, + + // Initialize is an empty function by default. Override it with your own + // initialization logic. + initialize: function(){}, + + // **render** is the core function that your view should override, in order + // to populate its element (`this.el`), with the appropriate HTML. The + // convention is for **render** to always return `this`. + render: function() { + return this; + }, + + // Remove this view by taking the element out of the DOM, and removing any + // applicable Backbone.Events listeners. + remove: function() { + this.$el.remove(); + this.stopListening(); + return this; + }, + + // Change the view's element (`this.el` property), including event + // re-delegation. + setElement: function(element, delegate) { + if (this.$el) this.undelegateEvents(); + this.$el = element instanceof Backbone.$ ? element : Backbone.$(element); + this.el = this.$el[0]; + if (delegate !== false) this.delegateEvents(); + return this; + }, + + // Set callbacks, where `this.events` is a hash of + // + // *{"event selector": "callback"}* + // + // { + // 'mousedown .title': 'edit', + // 'click .button': 'save', + // 'click .open': function(e) { ... } + // } + // + // pairs. Callbacks will be bound to the view, with `this` set properly. + // Uses event delegation for efficiency. + // Omitting the selector binds the event to `this.el`. + // This only works for delegate-able events: not `focus`, `blur`, and + // not `change`, `submit`, and `reset` in Internet Explorer. + delegateEvents: function(events) { + if (!(events || (events = _.result(this, 'events')))) return this; + this.undelegateEvents(); + for (var key in events) { + var method = events[key]; + if (!_.isFunction(method)) method = this[events[key]]; + if (!method) continue; + + var match = key.match(delegateEventSplitter); + var eventName = match[1], selector = match[2]; + method = _.bind(method, this); + eventName += '.delegateEvents' + this.cid; + if (selector === '') { + this.$el.on(eventName, method); + } else { + this.$el.on(eventName, selector, method); + } + } + return this; + }, + + // Clears all callbacks previously bound to the view with `delegateEvents`. + // You usually don't need to use this, but may wish to if you have multiple + // Backbone views attached to the same DOM element. + undelegateEvents: function() { + this.$el.off('.delegateEvents' + this.cid); + return this; + }, + + // Ensure that the View has a DOM element to render into. + // If `this.el` is a string, pass it through `$()`, take the first + // matching element, and re-assign it to `el`. Otherwise, create + // an element from the `id`, `className` and `tagName` properties. + _ensureElement: function() { + if (!this.el) { + var attrs = _.extend({}, _.result(this, 'attributes')); + if (this.id) attrs.id = _.result(this, 'id'); + if (this.className) attrs['class'] = _.result(this, 'className'); + var $el = Backbone.$('<' + _.result(this, 'tagName') + '>').attr(attrs); + this.setElement($el, false); + } else { + this.setElement(_.result(this, 'el'), false); + } + } + + }); + + // Backbone.sync + // ------------- + + // Override this function to change the manner in which Backbone persists + // models to the server. You will be passed the type of request, and the + // model in question. By default, makes a RESTful Ajax request + // to the model's `url()`. Some possible customizations could be: + // + // * Use `setTimeout` to batch rapid-fire updates into a single request. + // * Send up the models as XML instead of JSON. + // * Persist models via WebSockets instead of Ajax. + // + // Turn on `Backbone.emulateHTTP` in order to send `PUT` and `DELETE` requests + // as `POST`, with a `_method` parameter containing the true HTTP method, + // as well as all requests with the body as `application/x-www-form-urlencoded` + // instead of `application/json` with the model in a param named `model`. + // Useful when interfacing with server-side languages like **PHP** that make + // it difficult to read the body of `PUT` requests. + Backbone.sync = function(method, model, options) { + var type = methodMap[method]; + + // Default options, unless specified. + _.defaults(options || (options = {}), { + emulateHTTP: Backbone.emulateHTTP, + emulateJSON: Backbone.emulateJSON + }); + + // Default JSON-request options. + var params = {type: type, dataType: 'json'}; + + // Ensure that we have a URL. + if (!options.url) { + params.url = _.result(model, 'url') || urlError(); + } + + // Ensure that we have the appropriate request data. + if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) { + params.contentType = 'application/json'; + params.data = JSON.stringify(options.attrs || model.toJSON(options)); + } + + // For older servers, emulate JSON by encoding the request into an HTML-form. + if (options.emulateJSON) { + params.contentType = 'application/x-www-form-urlencoded'; + params.data = params.data ? {model: params.data} : {}; + } + + // For older servers, emulate HTTP by mimicking the HTTP method with `_method` + // And an `X-HTTP-Method-Override` header. + if (options.emulateHTTP && (type === 'PUT' || type === 'DELETE' || type === 'PATCH')) { + params.type = 'POST'; + if (options.emulateJSON) params.data._method = type; + var beforeSend = options.beforeSend; + options.beforeSend = function(xhr) { + xhr.setRequestHeader('X-HTTP-Method-Override', type); + if (beforeSend) return beforeSend.apply(this, arguments); + }; + } + + // Don't process data on a non-GET request. + if (params.type !== 'GET' && !options.emulateJSON) { + params.processData = false; + } + + // If we're sending a `PATCH` request, and we're in an old Internet Explorer + // that still has ActiveX enabled by default, override jQuery to use that + // for XHR instead. Remove this line when jQuery supports `PATCH` on IE8. + if (params.type === 'PATCH' && noXhrPatch) { + params.xhr = function() { + return new ActiveXObject("Microsoft.XMLHTTP"); + }; + } + + // Make the request, allowing the user to override any Ajax options. + var xhr = options.xhr = Backbone.ajax(_.extend(params, options)); + model.trigger('request', model, xhr, options); + return xhr; + }; + + var noXhrPatch = + typeof window !== 'undefined' && !!window.ActiveXObject && + !(window.XMLHttpRequest && (new XMLHttpRequest).dispatchEvent); + + // Map from CRUD to HTTP for our default `Backbone.sync` implementation. + var methodMap = { + 'create': 'POST', + 'update': 'PUT', + 'patch': 'PATCH', + 'delete': 'DELETE', + 'read': 'GET' + }; + + // Set the default implementation of `Backbone.ajax` to proxy through to `$`. + // Override this if you'd like to use a different library. + Backbone.ajax = function() { + return Backbone.$.ajax.apply(Backbone.$, arguments); + }; + + // Backbone.Router + // --------------- + + // Routers map faux-URLs to actions, and fire events when routes are + // matched. Creating a new one sets its `routes` hash, if not set statically. + var Router = Backbone.Router = function(options) { + options || (options = {}); + if (options.routes) this.routes = options.routes; + this._bindRoutes(); + this.initialize.apply(this, arguments); + }; + + // Cached regular expressions for matching named param parts and splatted + // parts of route strings. + var optionalParam = /\((.*?)\)/g; + var namedParam = /(\(\?)?:\w+/g; + var splatParam = /\*\w+/g; + var escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g; + + // Set up all inheritable **Backbone.Router** properties and methods. + _.extend(Router.prototype, Events, { + + // Initialize is an empty function by default. Override it with your own + // initialization logic. + initialize: function(){}, + + // Manually bind a single named route to a callback. For example: + // + // this.route('search/:query/p:num', 'search', function(query, num) { + // ... + // }); + // + route: function(route, name, callback) { + if (!_.isRegExp(route)) route = this._routeToRegExp(route); + if (_.isFunction(name)) { + callback = name; + name = ''; + } + if (!callback) callback = this[name]; + var router = this; + Backbone.history.route(route, function(fragment) { + var args = router._extractParameters(route, fragment); + router.execute(callback, args); + router.trigger.apply(router, ['route:' + name].concat(args)); + router.trigger('route', name, args); + Backbone.history.trigger('route', router, name, args); + }); + return this; + }, + + // Execute a route handler with the provided parameters. This is an + // excellent place to do pre-route setup or post-route cleanup. + execute: function(callback, args) { + if (callback) callback.apply(this, args); + }, + + // Simple proxy to `Backbone.history` to save a fragment into the history. + navigate: function(fragment, options) { + Backbone.history.navigate(fragment, options); + return this; + }, + + // Bind all defined routes to `Backbone.history`. We have to reverse the + // order of the routes here to support behavior where the most general + // routes can be defined at the bottom of the route map. + _bindRoutes: function() { + if (!this.routes) return; + this.routes = _.result(this, 'routes'); + var route, routes = _.keys(this.routes); + while ((route = routes.pop()) != null) { + this.route(route, this.routes[route]); + } + }, + + // Convert a route string into a regular expression, suitable for matching + // against the current location hash. + _routeToRegExp: function(route) { + route = route.replace(escapeRegExp, '\\$&') + .replace(optionalParam, '(?:$1)?') + .replace(namedParam, function(match, optional) { + return optional ? match : '([^/?]+)'; + }) + .replace(splatParam, '([^?]*?)'); + return new RegExp('^' + route + '(?:\\?([\\s\\S]*))?$'); + }, + + // Given a route, and a URL fragment that it matches, return the array of + // extracted decoded parameters. Empty or unmatched parameters will be + // treated as `null` to normalize cross-browser behavior. + _extractParameters: function(route, fragment) { + var params = route.exec(fragment).slice(1); + return _.map(params, function(param, i) { + // Don't decode the search params. + if (i === params.length - 1) return param || null; + return param ? decodeURIComponent(param) : null; + }); + } + + }); + + // Backbone.History + // ---------------- + + // Handles cross-browser history management, based on either + // [pushState](http://diveintohtml5.info/history.html) and real URLs, or + // [onhashchange](https://developer.mozilla.org/en-US/docs/DOM/window.onhashchange) + // and URL fragments. If the browser supports neither (old IE, natch), + // falls back to polling. + var History = Backbone.History = function() { + this.handlers = []; + _.bindAll(this, 'checkUrl'); + + // Ensure that `History` can be used outside of the browser. + if (typeof window !== 'undefined') { + this.location = window.location; + this.history = window.history; + } + }; + + // Cached regex for stripping a leading hash/slash and trailing space. + var routeStripper = /^[#\/]|\s+$/g; + + // Cached regex for stripping leading and trailing slashes. + var rootStripper = /^\/+|\/+$/g; + + // Cached regex for detecting MSIE. + var isExplorer = /msie [\w.]+/; + + // Cached regex for removing a trailing slash. + var trailingSlash = /\/$/; + + // Cached regex for stripping urls of hash. + var pathStripper = /#.*$/; + + // Has the history handling already been started? + History.started = false; + + // Set up all inheritable **Backbone.History** properties and methods. + _.extend(History.prototype, Events, { + + // The default interval to poll for hash changes, if necessary, is + // twenty times a second. + interval: 50, + + // Are we at the app root? + atRoot: function() { + return this.location.pathname.replace(/[^\/]$/, '$&/') === this.root; + }, + + // Gets the true hash value. Cannot use location.hash directly due to bug + // in Firefox where location.hash will always be decoded. + getHash: function(window) { + var match = (window || this).location.href.match(/#(.*)$/); + return match ? match[1] : ''; + }, + + // Get the cross-browser normalized URL fragment, either from the URL, + // the hash, or the override. + getFragment: function(fragment, forcePushState) { + if (fragment == null) { + if (this._hasPushState || !this._wantsHashChange || forcePushState) { + fragment = decodeURI(this.location.pathname + this.location.search); + var root = this.root.replace(trailingSlash, ''); + if (!fragment.indexOf(root)) fragment = fragment.slice(root.length); + } else { + fragment = this.getHash(); + } + } + return fragment.replace(routeStripper, ''); + }, + + // Start the hash change handling, returning `true` if the current URL matches + // an existing route, and `false` otherwise. + start: function(options) { + if (History.started) throw new Error("Backbone.history has already been started"); + History.started = true; + + // Figure out the initial configuration. Do we need an iframe? + // Is pushState desired ... is it available? + this.options = _.extend({root: '/'}, this.options, options); + this.root = this.options.root; + this._wantsHashChange = this.options.hashChange !== false; + this._wantsPushState = !!this.options.pushState; + this._hasPushState = !!(this.options.pushState && this.history && this.history.pushState); + var fragment = this.getFragment(); + var docMode = document.documentMode; + var oldIE = (isExplorer.exec(navigator.userAgent.toLowerCase()) && (!docMode || docMode <= 7)); + + // Normalize root to always include a leading and trailing slash. + this.root = ('/' + this.root + '/').replace(rootStripper, '/'); + + if (oldIE && this._wantsHashChange) { + var frame = Backbone.$('<iframe src="javascript:0" tabindex="-1">'); + this.iframe = frame.hide().appendTo('body')[0].contentWindow; + this.navigate(fragment); + } + + // Depending on whether we're using pushState or hashes, and whether + // 'onhashchange' is supported, determine how we check the URL state. + if (this._hasPushState) { + Backbone.$(window).on('popstate', this.checkUrl); + } else if (this._wantsHashChange && ('onhashchange' in window) && !oldIE) { + Backbone.$(window).on('hashchange', this.checkUrl); + } else if (this._wantsHashChange) { + this._checkUrlInterval = setInterval(this.checkUrl, this.interval); + } + + // Determine if we need to change the base url, for a pushState link + // opened by a non-pushState browser. + this.fragment = fragment; + var loc = this.location; + + // Transition from hashChange to pushState or vice versa if both are + // requested. + if (this._wantsHashChange && this._wantsPushState) { + + // If we've started off with a route from a `pushState`-enabled + // browser, but we're currently in a browser that doesn't support it... + if (!this._hasPushState && !this.atRoot()) { + this.fragment = this.getFragment(null, true); + this.location.replace(this.root + '#' + this.fragment); + // Return immediately as browser will do redirect to new url + return true; + + // Or if we've started out with a hash-based route, but we're currently + // in a browser where it could be `pushState`-based instead... + } else if (this._hasPushState && this.atRoot() && loc.hash) { + this.fragment = this.getHash().replace(routeStripper, ''); + this.history.replaceState({}, document.title, this.root + this.fragment); + } + + } + + if (!this.options.silent) return this.loadUrl(); + }, + + // Disable Backbone.history, perhaps temporarily. Not useful in a real app, + // but possibly useful for unit testing Routers. + stop: function() { + Backbone.$(window).off('popstate', this.checkUrl).off('hashchange', this.checkUrl); + if (this._checkUrlInterval) clearInterval(this._checkUrlInterval); + History.started = false; + }, + + // Add a route to be tested when the fragment changes. Routes added later + // may override previous routes. + route: function(route, callback) { + this.handlers.unshift({route: route, callback: callback}); + }, + + // Checks the current URL to see if it has changed, and if it has, + // calls `loadUrl`, normalizing across the hidden iframe. + checkUrl: function(e) { + var current = this.getFragment(); + if (current === this.fragment && this.iframe) { + current = this.getFragment(this.getHash(this.iframe)); + } + if (current === this.fragment) return false; + if (this.iframe) this.navigate(current); + this.loadUrl(); + }, + + // Attempt to load the current URL fragment. If a route succeeds with a + // match, returns `true`. If no defined routes matches the fragment, + // returns `false`. + loadUrl: function(fragment) { + fragment = this.fragment = this.getFragment(fragment); + return _.any(this.handlers, function(handler) { + if (handler.route.test(fragment)) { + handler.callback(fragment); + return true; + } + }); + }, + + // Save a fragment into the hash history, or replace the URL state if the + // 'replace' option is passed. You are responsible for properly URL-encoding + // the fragment in advance. + // + // The options object can contain `trigger: true` if you wish to have the + // route callback be fired (not usually desirable), or `replace: true`, if + // you wish to modify the current URL without adding an entry to the history. + navigate: function(fragment, options) { + if (!History.started) return false; + if (!options || options === true) options = {trigger: !!options}; + + var url = this.root + (fragment = this.getFragment(fragment || '')); + + // Strip the hash for matching. + fragment = fragment.replace(pathStripper, ''); + + if (this.fragment === fragment) return; + this.fragment = fragment; + + // Don't include a trailing slash on the root. + if (fragment === '' && url !== '/') url = url.slice(0, -1); + + // If pushState is available, we use it to set the fragment as a real URL. + if (this._hasPushState) { + this.history[options.replace ? 'replaceState' : 'pushState']({}, document.title, url); + + // If hash changes haven't been explicitly disabled, update the hash + // fragment to store history. + } else if (this._wantsHashChange) { + this._updateHash(this.location, fragment, options.replace); + if (this.iframe && (fragment !== this.getFragment(this.getHash(this.iframe)))) { + // Opening and closing the iframe tricks IE7 and earlier to push a + // history entry on hash-tag change. When replace is true, we don't + // want this. + if(!options.replace) this.iframe.document.open().close(); + this._updateHash(this.iframe.location, fragment, options.replace); + } + + // If you've told us that you explicitly don't want fallback hashchange- + // based history, then `navigate` becomes a page refresh. + } else { + return this.location.assign(url); + } + if (options.trigger) return this.loadUrl(fragment); + }, + + // Update the hash location, either replacing the current entry, or adding + // a new one to the browser history. + _updateHash: function(location, fragment, replace) { + if (replace) { + var href = location.href.replace(/(javascript:|#).*$/, ''); + location.replace(href + '#' + fragment); + } else { + // Some browsers require that `hash` contains a leading #. + location.hash = '#' + fragment; + } + } + + }); + + // Create the default Backbone.history. + Backbone.history = new History; + + // Helpers + // ------- + + // Helper function to correctly set up the prototype chain, for subclasses. + // Similar to `goog.inherits`, but uses a hash of prototype properties and + // class properties to be extended. + var extend = function(protoProps, staticProps) { + var parent = this; + var child; + + // The constructor function for the new subclass is either defined by you + // (the "constructor" property in your `extend` definition), or defaulted + // by us to simply call the parent's constructor. + if (protoProps && _.has(protoProps, 'constructor')) { + child = protoProps.constructor; + } else { + child = function(){ return parent.apply(this, arguments); }; + } + + // Add static properties to the constructor function, if supplied. + _.extend(child, parent, staticProps); + + // Set the prototype chain to inherit from `parent`, without calling + // `parent`'s constructor function. + var Surrogate = function(){ this.constructor = child; }; + Surrogate.prototype = parent.prototype; + child.prototype = new Surrogate; + + // Add prototype properties (instance properties) to the subclass, + // if supplied. + if (protoProps) _.extend(child.prototype, protoProps); + + // Set a convenience property in case the parent's prototype is needed + // later. + child.__super__ = parent.prototype; + + return child; + }; + + // Set up inheritance for the model, collection, router, view and history. + Model.extend = Collection.extend = Router.extend = View.extend = History.extend = extend; + + // Throw an error when a URL is needed, and none is supplied. + var urlError = function() { + throw new Error('A "url" property or function must be specified'); + }; + + // Wrap an optional error callback with a fallback error event. + var wrapError = function(model, options) { + var error = options.error; + options.error = function(resp) { + if (error) error(model, resp, options); + model.trigger('error', model, resp, options); + }; + }; + + return Backbone; + +})); diff --git a/dependencies/main_files/bootstrap-slider.css b/dependencies/main_files/bootstrap-slider.css new file mode 100644 index 00000000..043a2341 --- /dev/null +++ b/dependencies/main_files/bootstrap-slider.css @@ -0,0 +1,164 @@ +/*! ========================================================= + * bootstrap-slider.js + * + * Maintainers: + * Kyle Kemp + * - Twitter: @seiyria + * - Github: seiyria + * Rohit Kalkur + * - Twitter: @Rovolutionary + * - Github: rovolution + * + * ========================================================= + * + * 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. + * ========================================================= */ +.slider { + display: inline-block; + vertical-align: middle; + position: relative; +} +.slider.slider-horizontal { + width: 210px; + height: 20px; +} +.slider.slider-horizontal .slider-track { + height: 10px; + width: 100%; + margin-top: -5px; + top: 50%; + left: 0; +} +.slider.slider-horizontal .slider-selection { + height: 100%; + top: 0; + bottom: 0; +} +.slider.slider-horizontal .slider-handle { + margin-left: -10px; + margin-top: -5px; +} +.slider.slider-horizontal .slider-handle.triangle { + border-width: 0 10px 10px 10px; + width: 0; + height: 0; + border-bottom-color: #1abc9c; + margin-top: 0; +} +.slider.slider-vertical { + height: 210px; + width: 20px; +} +.slider.slider-vertical .slider-track { + width: 10px; + height: 100%; + margin-left: -5px; + left: 50%; + top: 0; +} +.slider.slider-vertical .slider-selection { + width: 100%; + left: 0; + top: 0; + bottom: 0; +} +.slider.slider-vertical .slider-handle { + margin-left: -5px; + margin-top: -10px; +} +.slider.slider-vertical .slider-handle.triangle { + border-width: 10px 0 10px 10px; + width: 1px; + height: 1px; + border-left-color: #1abc9c; + margin-left: 0; +} +.slider.slider-disabled .slider-handle { + background-image: -webkit-linear-gradient(top, #dfdfdf 0%, #bebebe 100%); + background-image: linear-gradient(to bottom, #dfdfdf 0%, #bebebe 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdfdfdf', endColorstr='#ffbebebe', GradientType=0); +} +.slider.slider-disabled .slider-track { + background-image: -webkit-linear-gradient(top, #e5e5e5 0%, #e9e9e9 100%); + background-image: linear-gradient(to bottom, #e5e5e5 0%, #e9e9e9 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe5e5e5', endColorstr='#ffe9e9e9', GradientType=0); + cursor: not-allowed; +} +.slider input { + display: none; +} +.slider .tooltip.top { + margin-top: -36px; +} +.slider .tooltip-inner { + white-space: nowrap; +} +.slider .hide { + display: none; +} +.slider-track { + position: absolute; + cursor: pointer; + background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #f9f9f9 100%); + background-image: linear-gradient(to bottom, #f5f5f5 0%, #f9f9f9 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff9f9f9', GradientType=0); + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + border-radius: 4px; +} +.slider-selection { + position: absolute; + background-image: -webkit-linear-gradient(top, #f9f9f9 0%, #f5f5f5 100%); + background-image: linear-gradient(to bottom, #f9f9f9 0%, #f5f5f5 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff9f9f9', endColorstr='#fff5f5f5', GradientType=0); + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + border-radius: 4px; +} +.slider-handle { + position: absolute; + width: 20px; + height: 20px; + background-color: #3a94a5; + background-image: -webkit-linear-gradient(top, #1abc9c 0%, #1abc9c 100%); + background-image: linear-gradient(to bottom, #1abc9c 0%, #1abc9c 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf', endColorstr='#ff0480be', GradientType=0); + filter: none; + -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); + box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); + opacity: 0.8; + border: 0px solid transparent; +} +.slider-handle.round { + border-radius: 50%; +} +.slider-handle.triangle { + background: transparent none; +} +.slider-handle.custom { + background: transparent none; +} +.slider-handle.custom::before { + line-height: 20px; + font-size: 20px; + content: '\2605'; + color: #726204; +} diff --git a/dependencies/main_files/bootstrap-slider.js b/dependencies/main_files/bootstrap-slider.js new file mode 100644 index 00000000..ccb3c420 --- /dev/null +++ b/dependencies/main_files/bootstrap-slider.js @@ -0,0 +1,1210 @@ +/*! ========================================================= + * bootstrap-slider.js + * + * Maintainers: + * Kyle Kemp + * - Twitter: @seiyria + * - Github: seiyria + * Rohit Kalkur + * - Twitter: @Rovolutionary + * - Github: rovolution + * + * ========================================================= + * + * 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. + * ========================================================= */ + + +/** + * Bridget makes jQuery widgets + * v1.0.1 + * MIT license + */ + +(function(root, factory) { + if(typeof define === "function" && define.amd) { + define(["jquery"], factory); + } else if(typeof module === "object" && module.exports) { + var jQuery; + try { + jQuery = require("jquery"); + } catch (err) { + jQuery = null; + } + module.exports = factory(jQuery); + } else { + root.Slider = factory(root.jQuery); + } +}(this, function($) { + // Reference to Slider constructor + var Slider; + + + (function( $ ) { + + 'use strict'; + + // -------------------------- utils -------------------------- // + + var slice = Array.prototype.slice; + + function noop() {} + + // -------------------------- definition -------------------------- // + + function defineBridget( $ ) { + + // bail if no jQuery + if ( !$ ) { + return; + } + + // -------------------------- addOptionMethod -------------------------- // + + /** + * adds option method -> $().plugin('option', {...}) + * @param {Function} PluginClass - constructor class + */ + function addOptionMethod( PluginClass ) { + // don't overwrite original option method + if ( PluginClass.prototype.option ) { + return; + } + + // option setter + PluginClass.prototype.option = function( opts ) { + // bail out if not an object + if ( !$.isPlainObject( opts ) ){ + return; + } + this.options = $.extend( true, this.options, opts ); + }; + } + + + // -------------------------- plugin bridge -------------------------- // + + // helper function for logging errors + // $.error breaks jQuery chaining + var logError = typeof console === 'undefined' ? noop : + function( message ) { + console.error( message ); + }; + + /** + * jQuery plugin bridge, access methods like $elem.plugin('method') + * @param {String} namespace - plugin name + * @param {Function} PluginClass - constructor class + */ + function bridge( namespace, PluginClass ) { + // add to jQuery fn namespace + $.fn[ namespace ] = function( options ) { + if ( typeof options === 'string' ) { + // call plugin method when first argument is a string + // get arguments for method + var args = slice.call( arguments, 1 ); + + for ( var i=0, len = this.length; i < len; i++ ) { + var elem = this[i]; + var instance = $.data( elem, namespace ); + if ( !instance ) { + logError( "cannot call methods on " + namespace + " prior to initialization; " + + "attempted to call '" + options + "'" ); + continue; + } + if ( !$.isFunction( instance[options] ) || options.charAt(0) === '_' ) { + logError( "no such method '" + options + "' for " + namespace + " instance" ); + continue; + } + + // trigger method with arguments + var returnValue = instance[ options ].apply( instance, args); + + // break look and return first value if provided + if ( returnValue !== undefined && returnValue !== instance) { + return returnValue; + } + } + // return this if no return value + return this; + } else { + var objects = this.map( function() { + var instance = $.data( this, namespace ); + if ( instance ) { + // apply options & init + instance.option( options ); + instance._init(); + } else { + // initialize new instance + instance = new PluginClass( this, options ); + $.data( this, namespace, instance ); + } + return $(this); + }); + + if(!objects || objects.length > 1) { + return objects; + } else { + return objects[0]; + } + } + }; + + } + + // -------------------------- bridget -------------------------- // + + /** + * converts a Prototypical class into a proper jQuery plugin + * the class must have a ._init method + * @param {String} namespace - plugin name, used in $().pluginName + * @param {Function} PluginClass - constructor class + */ + $.bridget = function( namespace, PluginClass ) { + addOptionMethod( PluginClass ); + bridge( namespace, PluginClass ); + }; + + return $.bridget; + + } + + // get jquery from browser global + defineBridget( $ ); + + })( $ ); + + + /************************************************* + + BOOTSTRAP-SLIDER SOURCE CODE + + **************************************************/ + + (function($) { + + var ErrorMsgs = { + formatInvalidInputErrorMsg : function(input) { + return "Invalid input value '" + input + "' passed in"; + }, + callingContextNotSliderInstance : "Calling context element does not have instance of Slider bound to it. Check your code to make sure the JQuery object returned from the call to the slider() initializer is calling the method" + }; + + + + /************************************************* + + CONSTRUCTOR + + **************************************************/ + Slider = function(element, options) { + createNewSlider.call(this, element, options); + return this; + }; + + function createNewSlider(element, options) { + /************************************************* + + Create Markup + + **************************************************/ + if(typeof element === "string") { + this.element = document.querySelector(element); + } else if(element instanceof HTMLElement) { + this.element = element; + } + + var origWidth = this.element.style.width; + var updateSlider = false; + var parent = this.element.parentNode; + var sliderTrackSelection; + var sliderMinHandle; + var sliderMaxHandle; + + if (this.sliderElem) { + updateSlider = true; + } else { + /* Create elements needed for slider */ + this.sliderElem = document.createElement("div"); + this.sliderElem.className = "slider"; + + /* Create slider track elements */ + var sliderTrack = document.createElement("div"); + sliderTrack.className = "slider-track"; + + sliderTrackSelection = document.createElement("div"); + sliderTrackSelection.className = "slider-selection"; + + sliderMinHandle = document.createElement("div"); + sliderMinHandle.className = "slider-handle min-slider-handle"; + + sliderMaxHandle = document.createElement("div"); + sliderMaxHandle.className = "slider-handle max-slider-handle"; + + sliderTrack.appendChild(sliderTrackSelection); + sliderTrack.appendChild(sliderMinHandle); + sliderTrack.appendChild(sliderMaxHandle); + + var createAndAppendTooltipSubElements = function(tooltipElem) { + var arrow = document.createElement("div"); + arrow.className = "tooltip-arrow"; + + var inner = document.createElement("div"); + inner.className = "tooltip-inner"; + + tooltipElem.appendChild(arrow); + tooltipElem.appendChild(inner); + }; + + /* Create tooltip elements */ + var sliderTooltip = document.createElement("div"); + sliderTooltip.className = "tooltip tooltip-main"; + createAndAppendTooltipSubElements(sliderTooltip); + + var sliderTooltipMin = document.createElement("div"); + sliderTooltipMin.className = "tooltip tooltip-min"; + createAndAppendTooltipSubElements(sliderTooltipMin); + + var sliderTooltipMax = document.createElement("div"); + sliderTooltipMax.className = "tooltip tooltip-max"; + createAndAppendTooltipSubElements(sliderTooltipMax); + + + /* Append components to sliderElem */ + this.sliderElem.appendChild(sliderTrack); + this.sliderElem.appendChild(sliderTooltip); + this.sliderElem.appendChild(sliderTooltipMin); + this.sliderElem.appendChild(sliderTooltipMax); + + /* Append slider element to parent container, right before the original <input> element */ + parent.insertBefore(this.sliderElem, this.element); + + /* Hide original <input> element */ + this.element.style.display = "none"; + } + /* If JQuery exists, cache JQ references */ + if($) { + this.$element = $(this.element); + this.$sliderElem = $(this.sliderElem); + } + + /************************************************* + + Process Options + + **************************************************/ + options = options ? options : {}; + var optionTypes = Object.keys(this.defaultOptions); + + for(var i = 0; i < optionTypes.length; i++) { + var optName = optionTypes[i]; + + // First check if an option was passed in via the constructor + var val = options[optName]; + // If no data attrib, then check data atrributes + val = (typeof val !== 'undefined') ? val : getDataAttrib(this.element, optName); + // Finally, if nothing was specified, use the defaults + val = (val !== null) ? val : this.defaultOptions[optName]; + + // Set all options on the instance of the Slider + if(!this.options) { + this.options = {}; + } + this.options[optName] = val; + } + + function getDataAttrib(element, optName) { + var dataName = "data-slider-" + optName; + var dataValString = element.getAttribute(dataName); + + try { + return JSON.parse(dataValString); + } + catch(err) { + return dataValString; + } + } + + /************************************************* + + Setup + + **************************************************/ + this.eventToCallbackMap = {}; + this.sliderElem.id = this.options.id; + + this.touchCapable = 'ontouchstart' in window || (window.DocumentTouch && document instanceof window.DocumentTouch); + + this.tooltip = this.sliderElem.querySelector('.tooltip-main'); + this.tooltipInner = this.tooltip.querySelector('.tooltip-inner'); + + this.tooltip_min = this.sliderElem.querySelector('.tooltip-min'); + this.tooltipInner_min = this.tooltip_min.querySelector('.tooltip-inner'); + + this.tooltip_max = this.sliderElem.querySelector('.tooltip-max'); + this.tooltipInner_max= this.tooltip_max.querySelector('.tooltip-inner'); + + if (updateSlider === true) { + // Reset classes + this._removeClass(this.sliderElem, 'slider-horizontal'); + this._removeClass(this.sliderElem, 'slider-vertical'); + this._removeClass(this.tooltip, 'hide'); + this._removeClass(this.tooltip_min, 'hide'); + this._removeClass(this.tooltip_max, 'hide'); + + // Undo existing inline styles for track + ["left", "top", "width", "height"].forEach(function(prop) { + this._removeProperty(this.trackSelection, prop); + }, this); + + // Undo inline styles on handles + [this.handle1, this.handle2].forEach(function(handle) { + this._removeProperty(handle, 'left'); + this._removeProperty(handle, 'top'); + }, this); + + // Undo inline styles and classes on tooltips + [this.tooltip, this.tooltip_min, this.tooltip_max].forEach(function(tooltip) { + this._removeProperty(tooltip, 'left'); + this._removeProperty(tooltip, 'top'); + this._removeProperty(tooltip, 'margin-left'); + this._removeProperty(tooltip, 'margin-top'); + + this._removeClass(tooltip, 'right'); + this._removeClass(tooltip, 'top'); + }, this); + } + + if(this.options.orientation === 'vertical') { + this._addClass(this.sliderElem,'slider-vertical'); + + this.stylePos = 'top'; + this.mousePos = 'pageY'; + this.sizePos = 'offsetHeight'; + + this._addClass(this.tooltip, 'right'); + this.tooltip.style.left = '100%'; + + this._addClass(this.tooltip_min, 'right'); + this.tooltip_min.style.left = '100%'; + + this._addClass(this.tooltip_max, 'right'); + this.tooltip_max.style.left = '100%'; + } else { + this._addClass(this.sliderElem, 'slider-horizontal'); + this._addClass(this.sliderElem, 'fullWidth'); + this.sliderElem.style.width = origWidth; + + this.options.orientation = 'horizontal'; + this.stylePos = 'left'; + this.mousePos = 'pageX'; + this.sizePos = 'offsetWidth'; + + this._addClass(this.tooltip, 'top'); + this.tooltip.style.top = -this.tooltip.outerHeight - 14 + 'px'; + + this._addClass(this.tooltip_min, 'top'); + this.tooltip_min.style.top = -this.tooltip_min.outerHeight - 14 + 'px'; + + this._addClass(this.tooltip_max, 'top'); + this.tooltip_max.style.top = -this.tooltip_max.outerHeight - 14 + 'px'; + } + + if (this.options.value instanceof Array) { + this.options.range = true; + } else if (this.options.range) { + // User wants a range, but value is not an array + this.options.value = [this.options.value, this.options.max]; + } + + this.trackSelection = sliderTrackSelection || this.trackSelection; + if (this.options.selection === 'none') { + this._addClass(this.trackSelection, 'hide'); + } + + this.handle1 = sliderMinHandle || this.handle1; + this.handle2 = sliderMaxHandle || this.handle2; + + if (updateSlider === true) { + // Reset classes + this._removeClass(this.handle1, 'round triangle'); + this._removeClass(this.handle2, 'round triangle hide'); + } + + var availableHandleModifiers = ['round', 'triangle', 'custom']; + var isValidHandleType = availableHandleModifiers.indexOf(this.options.handle) !== -1; + if (isValidHandleType) { + this._addClass(this.handle1, this.options.handle); + this._addClass(this.handle2, this.options.handle); + } + + this.offset = this._offset(this.sliderElem); + this.size = this.sliderElem[this.sizePos]; + this.setValue(this.options.value); + + /****************************************** + + Bind Event Listeners + + ******************************************/ + + // Bind keyboard handlers + this.handle1Keydown = this._keydown.bind(this, 0); + this.handle1.addEventListener("keydown", this.handle1Keydown, false); + + this.handle2Keydown = this._keydown.bind(this, 1); + this.handle2.addEventListener("keydown", this.handle2Keydown, false); + + if (this.touchCapable) { + // Bind touch handlers + this.mousedown = this._mousedown.bind(this); + this.sliderElem.addEventListener("touchstart", this.mousedown, false); + } else { + // Bind mouse handlers + this.mousedown = this._mousedown.bind(this); + this.sliderElem.addEventListener("mousedown", this.mousedown, false); + } + + // Bind tooltip-related handlers + if(this.options.tooltip === 'hide') { + this._addClass(this.tooltip, 'hide'); + this._addClass(this.tooltip_min, 'hide'); + this._addClass(this.tooltip_max, 'hide'); + } else if(this.options.tooltip === 'always') { + this._showTooltip(); + this._alwaysShowTooltip = true; + } else { + this.showTooltip = this._showTooltip.bind(this); + this.hideTooltip = this._hideTooltip.bind(this); + + this.sliderElem.addEventListener("mouseenter", this.showTooltip, false); + this.sliderElem.addEventListener("mouseleave", this.hideTooltip, false); + + this.handle1.addEventListener("focus", this.showTooltip, false); + this.handle1.addEventListener("blur", this.hideTooltip, false); + + this.handle2.addEventListener("focus", this.showTooltip, false); + this.handle2.addEventListener("blur", this.hideTooltip, false); + } + + if(this.options.enabled) { + this.enable(); + } else { + this.disable(); + } + } + + /************************************************* + + INSTANCE PROPERTIES/METHODS + + - Any methods bound to the prototype are considered + part of the plugin's `public` interface + + **************************************************/ + Slider.prototype = { + _init: function() {}, // NOTE: Must exist to support bridget + + constructor: Slider, + + defaultOptions: { + id: "", + min: 0, + max: 10, + step: 1, + precision: 0, + orientation: 'horizontal', + value: 5, + range: false, + selection: 'before', + tooltip: 'show', + tooltip_split: false, + handle: 'round', + reversed: false, + enabled: true, + formatter: function(val) { + if(val instanceof Array) { + return val[0] + " : " + val[1]; + } else { + return val; + } + }, + natural_arrow_keys: false + }, + + over: false, + + inDrag: false, + + getValue: function() { + if (this.options.range) { + return this.options.value; + } + return this.options.value[0]; + }, + + setValue: function(val, triggerSlideEvent) { + if (!val) { + val = 0; + } + var oldValue = this.getValue(); + this.options.value = this._validateInputValue(val); + var applyPrecision = this._applyPrecision.bind(this); + + if (this.options.range) { + this.options.value[0] = applyPrecision(this.options.value[0]); + this.options.value[1] = applyPrecision(this.options.value[1]); + + this.options.value[0] = Math.max(this.options.min, Math.min(this.options.max, this.options.value[0])); + this.options.value[1] = Math.max(this.options.min, Math.min(this.options.max, this.options.value[1])); + } else { + this.options.value = applyPrecision(this.options.value); + this.options.value = [ Math.max(this.options.min, Math.min(this.options.max, this.options.value))]; + this._addClass(this.handle2, 'hide'); + if (this.options.selection === 'after') { + this.options.value[1] = this.options.max; + } else { + this.options.value[1] = this.options.min; + } + } + + this.diff = this.options.max - this.options.min; + if (this.diff > 0) { + this.percentage = [ + (this.options.value[0] - this.options.min) * 100 / this.diff, + (this.options.value[1] - this.options.min) * 100 / this.diff, + this.options.step * 100 / this.diff + ]; + } else { + this.percentage = [0, 0, 100]; + } + + this._layout(); + var newValue = this.options.range ? this.options.value : this.options.value[0]; + + if(triggerSlideEvent === true) { + this._trigger('slide', newValue); + } + if(oldValue !== newValue) { + this._trigger('change', { + oldValue: oldValue, + newValue: newValue + }); + } + this._setDataVal(newValue); + + return this; + }, + + destroy: function(){ + // Remove event handlers on slider elements + this._removeSliderEventHandlers(); + + // Remove the slider from the DOM + this.sliderElem.parentNode.removeChild(this.sliderElem); + /* Show original <input> element */ + this.element.style.display = ""; + + // Clear out custom event bindings + this._cleanUpEventCallbacksMap(); + + // Remove data values + this.element.removeAttribute("data"); + + // Remove JQuery handlers/data + if($) { + this._unbindJQueryEventHandlers(); + this.$element.removeData('slider'); + } + }, + + disable: function() { + this.options.enabled = false; + this.handle1.removeAttribute("tabindex"); + this.handle2.removeAttribute("tabindex"); + this._addClass(this.sliderElem, 'slider-disabled'); + this._trigger('slideDisabled'); + + return this; + }, + + enable: function() { + this.options.enabled = true; + this.handle1.setAttribute("tabindex", 0); + this.handle2.setAttribute("tabindex", 0); + this._removeClass(this.sliderElem, 'slider-disabled'); + this._trigger('slideEnabled'); + + return this; + }, + + toggle: function() { + if(this.options.enabled) { + this.disable(); + } else { + this.enable(); + } + + return this; + }, + + isEnabled: function() { + return this.options.enabled; + }, + + on: function(evt, callback) { + if($) { + this.$element.on(evt, callback); + this.$sliderElem.on(evt, callback); + } else { + this._bindNonQueryEventHandler(evt, callback); + } + return this; + }, + + getAttribute: function(attribute) { + if(attribute) { + return this.options[attribute]; + } else { + return this.options; + } + }, + + setAttribute: function(attribute, value) { + this.options[attribute] = value; + return this; + }, + + refresh: function() { + this._removeSliderEventHandlers(); + createNewSlider.call(this, this.element, this.options); + if($) { + // Bind new instance of slider to the element + $.data(this.element, 'slider', this); + } + return this; + }, + + relayout: function() { + this._layout(); + return this; + }, + + /******************************+ + + HELPERS + + - Any method that is not part of the public interface. + - Place it underneath this comment block and write its signature like so: + + _fnName : function() {...} + + ********************************/ + _removeSliderEventHandlers: function() { + // Remove event listeners from handle1 + this.handle1.removeEventListener("keydown", this.handle1Keydown, false); + this.handle1.removeEventListener("focus", this.showTooltip, false); + this.handle1.removeEventListener("blur", this.hideTooltip, false); + + // Remove event listeners from handle2 + this.handle2.removeEventListener("keydown", this.handle2Keydown, false); + this.handle2.removeEventListener("focus", this.handle2Keydown, false); + this.handle2.removeEventListener("blur", this.handle2Keydown, false); + + // Remove event listeners from sliderElem + this.sliderElem.removeEventListener("mouseenter", this.showTooltip, false); + this.sliderElem.removeEventListener("mouseleave", this.hideTooltip, false); + this.sliderElem.removeEventListener("touchstart", this.mousedown, false); + this.sliderElem.removeEventListener("mousedown", this.mousedown, false); + }, + _bindNonQueryEventHandler: function(evt, callback) { + if(this.eventToCallbackMap[evt]===undefined) { + this.eventToCallbackMap[evt] = []; + } + this.eventToCallbackMap[evt].push(callback); + }, + _cleanUpEventCallbacksMap: function() { + var eventNames = Object.keys(this.eventToCallbackMap); + for(var i = 0; i < eventNames.length; i++) { + var eventName = eventNames[i]; + this.eventToCallbackMap[eventName] = null; + } + }, + _showTooltip: function() { + if (this.options.tooltip_split === false ){ + this._addClass(this.tooltip, 'in'); + } else { + this._addClass(this.tooltip_min, 'in'); + this._addClass(this.tooltip_max, 'in'); + } + this.over = true; + }, + _hideTooltip: function() { + if (this.inDrag === false && this.alwaysShowTooltip !== true) { + this._removeClass(this.tooltip, 'in'); + this._removeClass(this.tooltip_min, 'in'); + this._removeClass(this.tooltip_max, 'in'); + } + this.over = false; + }, + _layout: function() { + var positionPercentages; + + if(this.options.reversed) { + positionPercentages = [ 100 - this.percentage[0], this.percentage[1] ]; + } else { + positionPercentages = [ this.percentage[0], this.percentage[1] ]; + } + + this.handle1.style[this.stylePos] = positionPercentages[0]+'%'; + this.handle2.style[this.stylePos] = positionPercentages[1]+'%'; + + if (this.options.orientation === 'vertical') { + this.trackSelection.style.top = Math.min(positionPercentages[0], positionPercentages[1]) +'%'; + this.trackSelection.style.height = Math.abs(positionPercentages[0] - positionPercentages[1]) +'%'; + } else { + this.trackSelection.style.left = Math.min(positionPercentages[0], positionPercentages[1]) +'%'; + this.trackSelection.style.width = Math.abs(positionPercentages[0] - positionPercentages[1]) +'%'; + + var offset_min = this.tooltip_min.getBoundingClientRect(); + var offset_max = this.tooltip_max.getBoundingClientRect(); + + if (offset_min.right > offset_max.left) { + this._removeClass(this.tooltip_max, 'top'); + this._addClass(this.tooltip_max, 'bottom'); + this.tooltip_max.style.top = 18 + 'px'; + } else { + this._removeClass(this.tooltip_max, 'bottom'); + this._addClass(this.tooltip_max, 'top'); + this.tooltip_max.style.top = -30 + 'px'; + } + } + + var formattedTooltipVal; + + if (this.options.range) { + formattedTooltipVal = this.options.formatter(this.options.value); + this._setText(this.tooltipInner, formattedTooltipVal); + this.tooltip.style[this.stylePos] = (positionPercentages[1] + positionPercentages[0])/2 + '%'; + + if (this.options.orientation === 'vertical') { + this._css(this.tooltip, 'margin-top', -this.tooltip.offsetHeight / 2 + 'px'); + } else { + this._css(this.tooltip, 'margin-left', -this.tooltip.offsetWidth / 2 + 'px'); + } + + if (this.options.orientation === 'vertical') { + this._css(this.tooltip, 'margin-top', -this.tooltip.offsetHeight / 2 + 'px'); + } else { + this._css(this.tooltip, 'margin-left', -this.tooltip.offsetWidth / 2 + 'px'); + } + + var innerTooltipMinText = this.options.formatter(this.options.value[0]); + this._setText(this.tooltipInner_min, innerTooltipMinText); + + var innerTooltipMaxText = this.options.formatter(this.options.value[1]); + this._setText(this.tooltipInner_max, innerTooltipMaxText); + + this.tooltip_min.style[this.stylePos] = positionPercentages[0] + '%'; + + if (this.options.orientation === 'vertical') { + this._css(this.tooltip_min, 'margin-top', -this.tooltip_min.offsetHeight / 2 + 'px'); + } else { + this._css(this.tooltip_min, 'margin-left', -this.tooltip_min.offsetWidth / 2 + 'px'); + } + + this.tooltip_max.style[this.stylePos] = positionPercentages[1] + '%'; + + if (this.options.orientation === 'vertical') { + this._css(this.tooltip_max, 'margin-top', -this.tooltip_max.offsetHeight / 2 + 'px'); + } else { + this._css(this.tooltip_max, 'margin-left', -this.tooltip_max.offsetWidth / 2 + 'px'); + } + } else { + formattedTooltipVal = this.options.formatter(this.options.value[0]); + this._setText(this.tooltipInner, formattedTooltipVal); + + this.tooltip.style[this.stylePos] = positionPercentages[0] + '%'; + if (this.options.orientation === 'vertical') { + this._css(this.tooltip, 'margin-top', -this.tooltip.offsetHeight / 2 + 'px'); + } else { + this._css(this.tooltip, 'margin-left', -this.tooltip.offsetWidth / 2 + 'px'); + } + } + }, + _removeProperty: function(element, prop) { + if (element.style.removeProperty) { + element.style.removeProperty(prop); + } else { + element.style.removeAttribute(prop); + } + }, + _mousedown: function(ev) { + if(!this.options.enabled) { + return false; + } + + this._triggerFocusOnHandle(); + + this.offset = this._offset(this.sliderElem); + this.size = this.sliderElem[this.sizePos]; + + var percentage = this._getPercentage(ev); + + if (this.options.range) { + var diff1 = Math.abs(this.percentage[0] - percentage); + var diff2 = Math.abs(this.percentage[1] - percentage); + this.dragged = (diff1 < diff2) ? 0 : 1; + } else { + this.dragged = 0; + } + + this.percentage[this.dragged] = this.options.reversed ? 100 - percentage : percentage; + this._layout(); + + if (this.touchCapable) { + document.removeEventListener("touchmove", this.mousemove, false); + document.removeEventListener("touchend", this.mouseup, false); + } + + if(this.mousemove){ + document.removeEventListener("mousemove", this.mousemove, false); + } + if(this.mouseup){ + document.removeEventListener("mouseup", this.mouseup, false); + } + + this.mousemove = this._mousemove.bind(this); + this.mouseup = this._mouseup.bind(this); + + if (this.touchCapable) { + // Touch: Bind touch events: + document.addEventListener("touchmove", this.mousemove, false); + document.addEventListener("touchend", this.mouseup, false); + } + // Bind mouse events: + document.addEventListener("mousemove", this.mousemove, false); + document.addEventListener("mouseup", this.mouseup, false); + + this.inDrag = true; + var newValue = this._calculateValue(); + + this._trigger('slideStart', newValue); + + this._setDataVal(newValue); + this.setValue(newValue); + + this._pauseEvent(ev); + + return true; + }, + _triggerFocusOnHandle: function(handleIdx) { + if(handleIdx === 0) { + this.handle1.focus(); + } + if(handleIdx === 1) { + this.handle2.focus(); + } + }, + _keydown: function(handleIdx, ev) { + if(!this.options.enabled) { + return false; + } + + var dir; + switch (ev.keyCode) { + case 37: // left + case 40: // down + dir = -1; + break; + case 39: // right + case 38: // up + dir = 1; + break; + } + if (!dir) { + return; + } + + // use natural arrow keys instead of from min to max + if (this.options.natural_arrow_keys) { + var ifVerticalAndNotReversed = (this.options.orientation === 'vertical' && !this.options.reversed); + var ifHorizontalAndReversed = (this.options.orientation === 'horizontal' && this.options.reversed); + + if (ifVerticalAndNotReversed || ifHorizontalAndReversed) { + dir = dir * -1; + } + } + + var oneStepValuePercentageChange = dir * this.percentage[2]; + var percentage = this.percentage[handleIdx] + oneStepValuePercentageChange; + + if (percentage > 100) { + percentage = 100; + } else if (percentage < 0) { + percentage = 0; + } + + this.dragged = handleIdx; + this._adjustPercentageForRangeSliders(percentage); + this.percentage[this.dragged] = percentage; + this._layout(); + + var val = this._calculateValue(); + + this._trigger('slideStart', val); + this._setDataVal(val); + this.setValue(val, true); + + this._trigger('slideStop', val); + this._setDataVal(val); + + this._pauseEvent(ev); + + return false; + }, + _pauseEvent: function(ev) { + if(ev.stopPropagation) { + ev.stopPropagation(); + } + if(ev.preventDefault) { + ev.preventDefault(); + } + ev.cancelBubble=true; + ev.returnValue=false; + }, + _mousemove: function(ev) { + if(!this.options.enabled) { + return false; + } + + var percentage = this._getPercentage(ev); + this._adjustPercentageForRangeSliders(percentage); + this.percentage[this.dragged] = this.options.reversed ? 100 - percentage : percentage; + this._layout(); + + var val = this._calculateValue(); + this.setValue(val, true); + + return false; + }, + _adjustPercentageForRangeSliders: function(percentage) { + if (this.options.range) { + if (this.dragged === 0 && this.percentage[1] < percentage) { + this.percentage[0] = this.percentage[1]; + this.dragged = 1; + } else if (this.dragged === 1 && this.percentage[0] > percentage) { + this.percentage[1] = this.percentage[0]; + this.dragged = 0; + } + } + }, + _mouseup: function() { + if(!this.options.enabled) { + return false; + } + if (this.touchCapable) { + // Touch: Unbind touch event handlers: + document.removeEventListener("touchmove", this.mousemove, false); + document.removeEventListener("touchend", this.mouseup, false); + } + // Unbind mouse event handlers: + document.removeEventListener("mousemove", this.mousemove, false); + document.removeEventListener("mouseup", this.mouseup, false); + + this.inDrag = false; + if (this.over === false) { + this._hideTooltip(); + } + var val = this._calculateValue(); + + this._layout(); + this._trigger('slideStop', val); + this._setDataVal(val); + + return false; + }, + _calculateValue: function() { + var val; + if (this.options.range) { + val = [this.options.min,this.options.max]; + if (this.percentage[0] !== 0){ + val[0] = (Math.max(this.options.min, this.options.min + Math.round((this.diff * this.percentage[0]/100)/this.options.step)*this.options.step)); + val[0] = this._applyPrecision(val[0]); + } + if (this.percentage[1] !== 100){ + val[1] = (Math.min(this.options.max, this.options.min + Math.round((this.diff * this.percentage[1]/100)/this.options.step)*this.options.step)); + val[1] = this._applyPrecision(val[1]); + } + } else { + val = (this.options.min + Math.round((this.diff * this.percentage[0]/100)/this.options.step)*this.options.step); + if (val < this.options.min) { + val = this.options.min; + } + else if (val > this.options.max) { + val = this.options.max; + } + val = parseFloat(val); + val = this._applyPrecision(val); + } + return val; + }, + _applyPrecision: function(val) { + var precision = this.options.precision || this._getNumDigitsAfterDecimalPlace(this.options.step); + return this._applyToFixedAndParseFloat(val, precision); + }, + _getNumDigitsAfterDecimalPlace: function(num) { + var match = (''+num).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/); + if (!match) { return 0; } + return Math.max(0, (match[1] ? match[1].length : 0) - (match[2] ? +match[2] : 0)); + }, + _applyToFixedAndParseFloat: function(num, toFixedInput) { + var truncatedNum = num.toFixed(toFixedInput); + return parseFloat(truncatedNum); + }, + /* + Credits to Mike Samuel for the following method! + Source: http://stackoverflow.com/questions/10454518/javascript-how-to-retrieve-the-number-of-decimals-of-a-string-number + */ + _getPercentage: function(ev) { + if (this.touchCapable && (ev.type === 'touchstart' || ev.type === 'touchmove')) { + ev = ev.touches[0]; + } + var percentage = (ev[this.mousePos] - this.offset[this.stylePos])*100/this.size; + percentage = Math.round(percentage/this.percentage[2])*this.percentage[2]; + return Math.max(0, Math.min(100, percentage)); + }, + _validateInputValue: function(val) { + if(typeof val === 'number') { + return val; + } else if(val instanceof Array) { + this._validateArray(val); + return val; + } else { + throw new Error( ErrorMsgs.formatInvalidInputErrorMsg(val) ); + } + }, + _validateArray: function(val) { + for(var i = 0; i < val.length; i++) { + var input = val[i]; + if (typeof input !== 'number') { throw new Error( ErrorMsgs.formatInvalidInputErrorMsg(input) ); } + } + }, + _setDataVal: function(val) { + var value = "value: '" + val + "'"; + this.element.setAttribute('data', value); + this.element.setAttribute('value', val); + }, + _trigger: function(evt, val) { + val = (val || val === 0) ? val : undefined; + + var callbackFnArray = this.eventToCallbackMap[evt]; + if(callbackFnArray && callbackFnArray.length) { + for(var i = 0; i < callbackFnArray.length; i++) { + var callbackFn = callbackFnArray[i]; + callbackFn(val); + } + } + + /* If JQuery exists, trigger JQuery events */ + if($) { + this._triggerJQueryEvent(evt, val); + } + }, + _triggerJQueryEvent: function(evt, val) { + var eventData = { + type: evt, + value: val + }; + this.$element.trigger(eventData); + this.$sliderElem.trigger(eventData); + }, + _unbindJQueryEventHandlers: function() { + this.$element.off(); + this.$sliderElem.off(); + }, + _setText: function(element, text) { + if(typeof element.innerText !== "undefined") { + element.innerText = text; + } else if(typeof element.textContent !== "undefined") { + element.textContent = text; + } + }, + _removeClass: function(element, classString) { + var classes = classString.split(" "); + var newClasses = element.className; + + for(var i = 0; i < classes.length; i++) { + var classTag = classes[i]; + var regex = new RegExp("(?:\\s|^)" + classTag + "(?:\\s|$)"); + newClasses = newClasses.replace(regex, " "); + } + + element.className = newClasses.trim(); + }, + _addClass: function(element, classString) { + var classes = classString.split(" "); + var newClasses = element.className; + + for(var i = 0; i < classes.length; i++) { + var classTag = classes[i]; + var regex = new RegExp("(?:\\s|^)" + classTag + "(?:\\s|$)"); + var ifClassExists = regex.test(newClasses); + + if(!ifClassExists) { + newClasses += " " + classTag; + } + } + + element.className = newClasses.trim(); + }, + _offset: function (obj) { + var ol = 0; + var ot = 0; + if (obj.offsetParent) { + do { + ol += obj.offsetLeft; + ot += obj.offsetTop; + } while (obj = obj.offsetParent); + } + return { + left: ol, + top: ot + }; + }, + _css: function(elementRef, styleName, value) { + if ($) { + $.style(elementRef, styleName, value); + } else { + var style = styleName.replace(/^-ms-/, "ms-").replace(/-([\da-z])/gi, function (all, letter) { + return letter.toUpperCase(); + }); + elementRef.style[style] = value; + } + } + }; + + /********************************* + + Attach to global namespace + + *********************************/ + if($) { + var namespace = $.fn.slider ? 'bootstrapSlider' : 'slider'; + $.bridget(namespace, Slider); + } + + })( $ ); + + return Slider; +})); \ No newline at end of file diff --git a/dependencies/main_files/bootstrap.css b/dependencies/main_files/bootstrap.css new file mode 100644 index 00000000..a9f35cee --- /dev/null +++ b/dependencies/main_files/bootstrap.css @@ -0,0 +1,5 @@ +/*! + * Bootstrap v3.2.0 (http://getbootstrap.com) + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.1 | MIT License | git.io/normalize */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background:0 0}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}@media print{*{color:#000!important;text-shadow:none!important;background:transparent!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100%!important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}select{background:#fff!important}.navbar{display:none}.table td,.table th{background-color:#fff!important}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table-bordered th,.table-bordered td{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\2a"}.glyphicon-plus:before{content:"\2b"}.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:before,:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#428bca;text-decoration:none}a:hover,a:focus{color:#2a6496;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive,.thumbnail>img,.thumbnail a>img,.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;width:100% \9;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;width:100% \9;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:400;line-height:1;color:#777}h1,.h1,h2,.h2,h3,.h3{margin-top:20px;margin-bottom:10px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:10px;margin-bottom:10px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:36px}h2,.h2{font-size:30px}h3,.h3{font-size:24px}h4,.h4{font-size:18px}h5,.h5{font-size:14px}h6,.h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}small,.small{font-size:85%}cite{font-style:normal}mark,.mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#428bca}a.text-primary:hover{color:#3071a9}.text-success{color:#3c763d}a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#428bca}a.bg-primary:hover{background-color:#3071a9}.bg-success{background-color:#dff0d8}a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ul,ol{margin-top:0;margin-bottom:10px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dt,dd{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote footer:before,blockquote small:before,blockquote .small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:''}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:'\00A0 \2014'}blockquote:before,blockquote:after{content:""}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-xs-1,.col-sm-1,.col-md-1,.col-lg-1,.col-xs-2,.col-sm-2,.col-md-2,.col-lg-2,.col-xs-3,.col-sm-3,.col-md-3,.col-lg-3,.col-xs-4,.col-sm-4,.col-md-4,.col-lg-4,.col-xs-5,.col-sm-5,.col-md-5,.col-lg-5,.col-xs-6,.col-sm-6,.col-md-6,.col-lg-6,.col-xs-7,.col-sm-7,.col-md-7,.col-lg-7,.col-xs-8,.col-sm-8,.col-md-8,.col-lg-8,.col-xs-9,.col-sm-9,.col-md-9,.col-lg-9,.col-xs-10,.col-sm-10,.col-md-10,.col-lg-10,.col-xs-11,.col-sm-11,.col-md-11,.col-lg-11,.col-xs-12,.col-sm-12,.col-md-12,.col-lg-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-child(odd)>td,.table-striped>tbody>tr:nth-child(odd)>th{background-color:#f9f9f9}.table-hover>tbody>tr:hover>td,.table-hover>tbody>tr:hover>th{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#f5f5f5}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#dff0d8}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr.success:hover>th{background-color:#d0e9c6}.table>thead>tr>td.info,.table>tbody>tr>td.info,.table>tfoot>tr>td.info,.table>thead>tr>th.info,.table>tbody>tr>th.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>tbody>tr.info>td,.table>tfoot>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr.info>th,.table>tfoot>tr.info>th{background-color:#d9edf7}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr.info:hover>th{background-color:#c4e3f3}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#fcf8e3}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr.warning:hover>th{background-color:#faf2cc}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#f2dede}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background-color:#ebcccc}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=radio],input[type=checkbox]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=radio]:focus,input[type=checkbox]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#777;opacity:1}.form-control:-ms-input-placeholder{color:#777}.form-control::-webkit-input-placeholder{color:#777}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{cursor:not-allowed;background-color:#eee;opacity:1}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}input[type=date],input[type=time],input[type=datetime-local],input[type=month]{line-height:34px;line-height:1.42857143 \0}input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}.form-group{margin-bottom:15px}.radio,.checkbox{position:relative;display:block;min-height:20px;margin-top:10px;margin-bottom:10px}.radio label,.checkbox label{padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.radio input[type=radio],.radio-inline input[type=radio],.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox]{position:absolute;margin-top:4px \9;margin-left:-20px}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}input[type=radio][disabled],input[type=checkbox][disabled],input[type=radio].disabled,input[type=checkbox].disabled,fieldset[disabled] input[type=radio],fieldset[disabled] input[type=checkbox]{cursor:not-allowed}.radio-inline.disabled,.checkbox-inline.disabled,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.radio.disabled label,.checkbox.disabled label,fieldset[disabled] .radio label,fieldset[disabled] .checkbox label{cursor:not-allowed}.form-control-static{padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm,.form-horizontal .form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}textarea.input-sm,select[multiple].input-sm{height:auto}.input-lg,.form-horizontal .form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}select.input-lg{height:46px;line-height:46px}textarea.input-lg,select[multiple].input-lg{height:auto}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:25px;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center}.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn,.form-inline .input-group .form-control{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .radio label,.form-inline .checkbox label{padding-left:0}.form-inline .radio input[type=radio],.form-inline .checkbox input[type=checkbox]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .radio,.form-horizontal .checkbox{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{top:0;right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:14.3px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn:focus,.btn:active:focus,.btn.active:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus{color:#333;text-decoration:none}.btn:active,.btn.active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{pointer-events:none;cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default:hover,.btn-default:focus,.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled,.btn-default[disabled],fieldset[disabled] .btn-default,.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled:active,.btn-default[disabled]:active,fieldset[disabled] .btn-default:active,.btn-default.disabled.active,.btn-default[disabled].active,fieldset[disabled] .btn-default.active{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#428bca;border-color:#357ebd}.btn-primary:hover,.btn-primary:focus,.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#3071a9;border-color:#285e8e}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled,.btn-primary[disabled],fieldset[disabled] .btn-primary,.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled:active,.btn-primary[disabled]:active,fieldset[disabled] .btn-primary:active,.btn-primary.disabled.active,.btn-primary[disabled].active,fieldset[disabled] .btn-primary.active{background-color:#428bca;border-color:#357ebd}.btn-primary .badge{color:#428bca;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success:hover,.btn-success:focus,.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled,.btn-success[disabled],fieldset[disabled] .btn-success,.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled:active,.btn-success[disabled]:active,fieldset[disabled] .btn-success:active,.btn-success.disabled.active,.btn-success[disabled].active,fieldset[disabled] .btn-success.active{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info:hover,.btn-info:focus,.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled,.btn-info[disabled],fieldset[disabled] .btn-info,.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled:active,.btn-info[disabled]:active,fieldset[disabled] .btn-info:active,.btn-info.disabled.active,.btn-info[disabled].active,fieldset[disabled] .btn-info.active{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning:hover,.btn-warning:focus,.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-warning,.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled:active,.btn-warning[disabled]:active,fieldset[disabled] .btn-warning:active,.btn-warning.disabled.active,.btn-warning[disabled].active,fieldset[disabled] .btn-warning.active{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger:hover,.btn-danger:focus,.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled,.btn-danger[disabled],fieldset[disabled] .btn-danger,.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled:active,.btn-danger[disabled]:active,fieldset[disabled] .btn-danger:active,.btn-danger.disabled.active,.btn-danger[disabled].active,fieldset[disabled] .btn-danger.active{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#428bca;cursor:pointer;border-radius:0}.btn-link,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#2a6496;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#777;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}.btn-sm,.btn-group-sm>.btn{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=submit].btn-block,input[type=reset].btn-block,input[type=button].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition:height .35s ease;-o-transition:height .35s ease;transition:height .35s ease}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px solid;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#fff;text-decoration:none;background-color:#428bca;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#777}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px solid}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn.active{z-index:2}.btn-group>.btn:focus,.btn-group-vertical>.btn:focus{outline:0}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child>.btn:last-child,.btn-group>.btn-group:first-child>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn>input[type=radio],[data-toggle=buttons]>.btn>input[type=checkbox]{position:absolute;z-index:-1;filter:alpha(opacity=0);opacity:0}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn,select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn,select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn{height:auto}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=radio],.input-group-addon input[type=checkbox]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#eee;border-color:#428bca}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#fff;background-color:#428bca}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:340px}@media (max-width:480px) and (orientation:landscape){.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:200px}}.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;-webkit-transform:translate3d(0,0,0);-o-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}@media (min-width:768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}.navbar-nav.navbar-right:last-child{margin-right:-15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn,.navbar-form .input-group .form-control{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .radio label,.navbar-form .checkbox label{padding-left:0}.navbar-form .radio input[type=radio],.navbar-form .checkbox input[type=checkbox]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-form.navbar-right:last-child{margin-right:-15px}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}.navbar-text.navbar-right:last-child{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:hover,.navbar-default .btn-link:focus{color:#333}.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:hover,.navbar-default .btn-link[disabled]:focus,fieldset[disabled] .navbar-default .btn-link:focus{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#777}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#777}.navbar-inverse .navbar-nav>li>a{color:#777}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#777}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#777}.navbar-inverse .btn-link:hover,.navbar-inverse .btn-link:focus{color:#fff}.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:hover,.navbar-inverse .btn-link[disabled]:focus,fieldset[disabled] .navbar-inverse .btn-link:focus{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#428bca;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{color:#2a6496;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:2;color:#fff;cursor:default;background-color:#428bca;border-color:#428bca}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:hover,a.label:focus{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:hover,.label-default[href]:focus{background-color:#5e5e5e}.label-primary{background-color:#428bca}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#3071a9}.label-success{background-color:#5cb85c}.label-success[href]:hover,.label-success[href]:focus{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:hover,.label-info[href]:focus{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge{top:0;padding:1px 5px}a.badge:hover,a.badge:focus{color:#fff;text-decoration:none;cursor:pointer}a.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#428bca;background-color:#fff}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron h1,.jumbotron .h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron{border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron{padding-right:60px;padding-left:60px}.jumbotron h1,.jumbotron .h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.thumbnail>img,.thumbnail a>img{margin-right:auto;margin-left:auto}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#428bca}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#428bca;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-striped .progress-bar,.progress-bar-striped{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress.active .progress-bar,.progress-bar.active{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar[aria-valuenow="1"],.progress-bar[aria-valuenow="2"]{min-width:30px}.progress-bar[aria-valuenow="0"]{min-width:30px;color:#777;background-color:transparent;background-image:none;-webkit-box-shadow:none;box-shadow:none}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media,.media-body{overflow:hidden;zoom:1}.media,.media .media{margin-top:15px}.media:first-child{margin-top:0}.media-object{display:block}.media-heading{margin:0 0 5px}.media>.pull-left{margin-right:10px}.media>.pull-right{margin-left:10px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}a.list-group-item{color:#555}a.list-group-item .list-group-item-heading{color:#333}a.list-group-item:hover,a.list-group-item:focus{color:#555;text-decoration:none;background-color:#f5f5f5}.list-group-item.disabled,.list-group-item.disabled:hover,.list-group-item.disabled:focus{color:#777;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{z-index:2;color:#fff;background-color:#428bca;border-color:#428bca}.list-group-item.active .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>.small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:hover .list-group-item-text,.list-group-item.active:focus .list-group-item-text{color:#e1edf7}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,a.list-group-item-success:focus{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:hover,a.list-group-item-success.active:focus{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,a.list-group-item-info:focus{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:hover,a.list-group-item-info.active:focus{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,a.list-group-item-warning:focus{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,a.list-group-item-danger:focus{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group{margin-bottom:0}.panel>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.table,.panel>.table-responsive>.table,.panel>.panel-collapse>.table{margin-bottom:0}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#428bca}.panel-primary>.panel-heading{color:#fff;background-color:#428bca;border-color:#428bca}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#428bca}.panel-primary>.panel-heading .badge{color:#428bca;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#428bca}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive iframe,.embed-responsive embed,.embed-responsive object{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:hover,.close:focus{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate3d(0,-25%,0);-o-transform:translate3d(0,-25%,0);transform:translate3d(0,-25%,0)}.modal.in .modal-dialog{-webkit-transform:translate3d(0,0,0);-o-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{min-height:16.43px;padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-size:12px;line-height:1.4;visibility:visible;filter:alpha(opacity=0);opacity:0}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{bottom:0;left:5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{right:5px;bottom:0;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;left:5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;right:5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;text-align:left;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2)}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;font-weight:400;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{line-height:1}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:hover,.carousel-control:focus{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;z-index:5;display:inline-block}.carousel-control .icon-prev,.carousel-control .glyphicon-chevron-left{left:50%;margin-left:-10px}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%;margin-right:-10px}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;margin-top:-10px;font-family:serif}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000 \9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-15px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-15px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-15px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after,.dl-horizontal dd:before,.dl-horizontal dd:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after,.form-horizontal .form-group:before,.form-horizontal .form-group:after,.btn-toolbar:before,.btn-toolbar:after,.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after,.nav:before,.nav:after,.navbar:before,.navbar:after,.navbar-header:before,.navbar-header:after,.navbar-collapse:before,.navbar-collapse:after,.pager:before,.pager:after,.panel-body:before,.panel-body:after,.modal-footer:before,.modal-footer:after{display:table;content:" "}.clearfix:after,.dl-horizontal dd:after,.container:after,.container-fluid:after,.row:after,.form-horizontal .form-group:after,.btn-toolbar:after,.btn-group-vertical>.btn-group:after,.nav:after,.navbar:after,.navbar-header:after,.navbar-collapse:after,.pager:after,.panel-body:after,.modal-footer:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important;visibility:hidden!important}.affix{position:fixed;-webkit-transform:translate3d(0,0,0);-o-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none!important}.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table}tr.visible-xs{display:table-row!important}th.visible-xs,td.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table}tr.visible-sm{display:table-row!important}th.visible-sm,td.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table}tr.visible-md{display:table-row!important}th.visible-md,td.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table}tr.visible-lg{display:table-row!important}th.visible-lg,td.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table}tr.visible-print{display:table-row!important}th.visible-print,td.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} \ No newline at end of file diff --git a/dependencies/main_files/dmaCell.js b/dependencies/main_files/dmaCell.js new file mode 100644 index 00000000..aae9878e --- /dev/null +++ b/dependencies/main_files/dmaCell.js @@ -0,0 +1,28 @@ +/** + * Created by aghassaei on 1/14/15. + */ + + +//a Cell, a unit piece of the lattice + +function Cell(nodes, config) { + this.parts = this._createParts(nodes, config); +}; + +Cell.prototype._createParts = function(nodes, config){ + var parts = []; + for (var i=0;i<nodes.length;i++){ + parts.push(new Part(nodes[i], config[i])); + } + return parts; +}; + +Cell.prototype.render = function(scene){ +}; + + +Cell.prototype.translate = function(dx, dy, dz){ +}; + +Cell.prototype.rotate = function(rx, ry, rz){ +}; diff --git a/dependencies/main_files/dmaNode.js b/dependencies/main_files/dmaNode.js new file mode 100644 index 00000000..5547b6c0 --- /dev/null +++ b/dependencies/main_files/dmaNode.js @@ -0,0 +1,55 @@ +//a node, two for each dmaBeam, not to be confused with node.js + +function BeamNode(x, y, z) { + this._beams = [];//store all beams attached to this node, eventually this will be used to calc global stiffness K + this.x = x; + this.y = y; + this.z = z; + this.render(); +} + +BeamNode.prototype.addBeam = function(beam){ + this._beams.push(beam); +}; + +BeamNode.prototype.render = function(){ + + var geometry = new THREE.BoxGeometry(2,2,2); + geometry.applyMatrix( new THREE.Matrix4().makeTranslation(this.x, this.y, this.z) ); + var mesh = new THREE.Mesh(geometry); + + window.three.sceneAdd(mesh); +}; + + +BeamNode.prototype.deflect = function(dx, dy, dz){ +}; + +BeamNode.prototype.translate = function(dx, dy, dz){ + this.x += dx; + this.y += dy; + this.z += dz; +}; + +BeamNode.prototype.translateAbsolute = function(x, y, z){ + this.x = x; + this.y = y; + this.z = z; +}; + +BeamNode.prototype.rotate = function(rx, ry, rz){ +}; + +BeamNode.prototype.destroy = function(){ + this._beams = null;//be sure to remove cyclic reference +}; + +BeamNode.prototype.getMidPoint = function(node){ + + var x = (node.x+this.x)/2; + var y = (node.y+this.y)/2; + var z = (node.z+this.z)/2; + + return [x,y,z]; + +} diff --git a/dependencies/main_files/dmaPart.js b/dependencies/main_files/dmaPart.js new file mode 100644 index 00000000..c286d020 --- /dev/null +++ b/dependencies/main_files/dmaPart.js @@ -0,0 +1,63 @@ +/** + * Created by aghassaei on 1/14/15. + */ + + +//a part, element with a single material, handled by assembler + + + +function DmaPart(geometry, nodes) {//list of nodes, config tells how nodes are connected + this.nodes = nodes; +// this.beams = this._createBeams(nodes, config); + this.scale = 10; + this.geometry = geometry; + this.render(); +} + +DmaPart.prototype._createBeams = function(nodes, config){ + var beams = []; + _.each(config, function(pair){ + beams.push(new Beam(nodes[pair[0]], nodes[pair[2]])); + }); + return beams; +}; + +DmaPart.prototype.render = function(){ + //translate geo to nodes + var midpoint = this.nodes[0].getMidPoint(this.nodes[1]); +// this.geometry.applyMatrix(new THREE.Matrix4().makeTranslation()); + + + var mesh = new THREE.Mesh(this.geometry); + var scale = this.scale*3; + mesh.scale.set(scale, scale, scale); +// mesh.rotateZ(Math.PI); + + mesh.position.x = midpoint[0]; + mesh.position.y = midpoint[1]; + mesh.position.z = midpoint[2]; + + + window.three.sceneAdd(mesh); + window.three.render(); +}; + + +DmaPart.prototype.translate = function(dx, dy, dz){ +}; + +DmaPart.prototype.rotate = function(rx, ry, rz){ +}; + +////////////////////////////////////////////////////////////// +/////////////////SUBCLASSES/////////////////////////////////// +////////////////////////////////////////////////////////////// + + + +////matt's part +//function PartTriangle(){ +//} +// +//PartTriangle.prototype = new DmaPart(); \ No newline at end of file diff --git a/dependencies/main_files/exportMenu.js b/dependencies/main_files/exportMenu.js new file mode 100644 index 00000000..b538dbf3 --- /dev/null +++ b/dependencies/main_files/exportMenu.js @@ -0,0 +1,21 @@ +/** + * Created by aghassaei on 1/9/15. + */ + + +$(function(){ + + $("#exportSTL").click(function(e){ + e.preventDefault(); + + _.each(three.scene.children, function(object){ + if (object instanceof THREE.Mesh){ + console.log(object.geometry); + stlFromGeometry(object.geometry, {download:true}); + } + }); + + + }) + +}); \ No newline at end of file diff --git a/dependencies/main_files/fillGeometry.js b/dependencies/main_files/fillGeometry.js new file mode 100644 index 00000000..85b86453 --- /dev/null +++ b/dependencies/main_files/fillGeometry.js @@ -0,0 +1,67 @@ +/** + * Created by aghassaei on 1/16/15. +*/ + + +FillGeometry = Backbone.Model.extend({ + + defaults: { + material: new THREE.MeshBasicMaterial( + {color:0xf25536, + shading: THREE.FlatShading, + transparent:true, + opacity:0.2}), + geometry: new THREE.BoxGeometry(100, 100, 100), + filename: "Cube", + orientation: [0,0,0], + scale: [1.0,1.0,1.0] + }, + + initialize: function(){ + + //bind events + this.on("change:mesh", this.getBounds); + this.on("change:orientation change:scale", this.updateBoundingBox); + this.on("change:geometry", this.buildNewMesh); + + this.buildNewMesh(); + }, + + buildNewMesh:function(){ + this.set({orientation:this.defaults.orientation, scale:this.defaults.scale}, {silent:true});//restore defaults + var mesh = new THREE.Mesh(this.get("geometry"), this.get("material")); + this.makeBoundingBoxHelper(mesh); + this.set({mesh: mesh}); + + //send new geometry out to workers +// _.each(workers.allWorkers, function(worker){ +// worker.postMessage({model: this.toJSON}); +// }); + }, + + getBounds: function(){//bounds is the bounding box of the mesh geometry (before scaling) + this.get("mesh").geometry.computeBoundingBox(); + this.set("bounds", this.get("geometry").boundingBox.clone()); + }, + + makeBoundingBoxHelper: function(mesh){ + var helper = new THREE.BoundingBoxHelper(mesh, 0x000000); + helper.update(); + this.set("boundingBoxHelper", helper); + }, + + updateBoundingBox: function(){ + this.get("boundingBoxHelper").update(); + this.trigger("change:boundingBoxHelper"); + }, + + scale: function(scale){ + var currentScale = this.get("scale"); + for (var i=0;i<currentScale.length;i++){ + if (!scale[i]) scale[i] = currentScale[i]; + } + this.get("mesh").scale.set(scale[0], scale[1], scale[2]); + this.set("scale", scale); + } +}); + diff --git a/dependencies/main_files/fillGeometryView.js b/dependencies/main_files/fillGeometryView.js new file mode 100644 index 00000000..3d5f0a45 --- /dev/null +++ b/dependencies/main_files/fillGeometryView.js @@ -0,0 +1,35 @@ +/** + * Created by aghassaei on 1/16/15. + */ + + +FillGeometryView = PushPullMeshView.extend({ + + events: { + }, + + defaultColor: 0xf25536, + + initialize: function(options){ + + PushPullMeshView.prototype.initialize.apply(this, arguments); + + //bind events + this.listenTo(this.model, "change:geometry", this.newFillGeometry); + this.listenTo(this.model, "change:orientation", this.render); + + this.newFillGeometry(); + }, + + newFillGeometry: function(){ + if (this.model.previous("mesh")) this.three.sceneRemove(this.model.previous("mesh")); + this.three.sceneAdd(this.model.get("mesh")); + this.three.render(); + }, + + + + render: function(){ + this.three.render(); + } +}); \ No newline at end of file diff --git a/dependencies/main_files/flat-ui.css b/dependencies/main_files/flat-ui.css new file mode 100644 index 00000000..2452fab3 --- /dev/null +++ b/dependencies/main_files/flat-ui.css @@ -0,0 +1,6200 @@ +/*! + * Flat UI Free v2.2.2 (http://designmodo.github.io/Flat-UI/) + * Copyright 2013-2014 Designmodo, Inc. + */ + +@font-face { + font-family: 'Lato'; + font-style: normal; + font-weight: 900; + + src: url('../fonts/lato/lato-black.eot'); + src: url('../fonts/lato/lato-black.eot?#iefix') format('embedded-opentype'), url('../fonts/lato/lato-black.woff') format('woff'), url('../fonts/lato/lato-black.ttf') format('truetype'), url('../fonts/lato/lato-black.svg#latoblack') format('svg'); + } +@font-face { + font-family: 'Lato'; + font-style: normal; + font-weight: bold; + + src: url('../fonts/lato/lato-bold.eot'); + src: url('../fonts/lato/lato-bold.eot?#iefix') format('embedded-opentype'), url('../fonts/lato/lato-bold.woff') format('woff'), url('../fonts/lato/lato-bold.ttf') format('truetype'), url('../fonts/lato/lato-bold.svg#latobold') format('svg'); + } +@font-face { + font-family: 'Lato'; + font-style: italic; + font-weight: bold; + + src: url('../fonts/lato/lato-bolditalic.eot'); + src: url('../fonts/lato/lato-bolditalic.eot?#iefix') format('embedded-opentype'), url('../fonts/lato/lato-bolditalic.woff') format('woff'), url('../fonts/lato/lato-bolditalic.ttf') format('truetype'), url('../fonts/lato/lato-bolditalic.svg#latobold-italic') format('svg'); + } +@font-face { + font-family: 'Lato'; + font-style: italic; + font-weight: normal; + + src: url('../fonts/lato/lato-italic.eot'); + src: url('../fonts/lato/lato-italic.eot?#iefix') format('embedded-opentype'), url('../fonts/lato/lato-italic.woff') format('woff'), url('../fonts/lato/lato-italic.ttf') format('truetype'), url('../fonts/lato/lato-italic.svg#latoitalic') format('svg'); + } +@font-face { + font-family: 'Lato'; + font-style: normal; + font-weight: 300; + + src: url('../fonts/lato/lato-light.eot'); + src: url('../fonts/lato/lato-light.eot?#iefix') format('embedded-opentype'), url('../fonts/lato/lato-light.woff') format('woff'), url('../fonts/lato/lato-light.ttf') format('truetype'), url('../fonts/lato/lato-light.svg#latolight') format('svg'); + } +@font-face { + font-family: 'Lato'; + font-style: normal; + font-weight: normal; + + src: url('../fonts/lato/lato-regular.eot'); + src: url('../fonts/lato/lato-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/lato/lato-regular.woff') format('woff'), url('../fonts/lato/lato-regular.ttf') format('truetype'), url('../fonts/lato/lato-regular.svg#latoregular') format('svg'); + } +@font-face { + font-family: 'Flat-UI-Icons'; + + src: url('../fonts/glyphicons/flat-ui-icons-regular.eot'); + src: url('../fonts/glyphicons/flat-ui-icons-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons/flat-ui-icons-regular.woff') format('woff'), url('../fonts/glyphicons/flat-ui-icons-regular.ttf') format('truetype'), url('../fonts/glyphicons/flat-ui-icons-regular.svg#flat-ui-icons-regular') format('svg'); + } +[class^="fui-"], +[class*="fui-"] { + font-family: 'Flat-UI-Icons'; + font-style: normal; + font-weight: normal; + font-variant: normal; + text-transform: none; + + speak: none; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + } +.fui-triangle-up:before { + content: "\e600"; + } +.fui-triangle-down:before { + content: "\e601"; + } +.fui-triangle-up-small:before { + content: "\e602"; + } +.fui-triangle-down-small:before { + content: "\e603"; + } +.fui-triangle-left-large:before { + content: "\e604"; + } +.fui-triangle-right-large:before { + content: "\e605"; + } +.fui-arrow-left:before { + content: "\e606"; + } +.fui-arrow-right:before { + content: "\e607"; + } +.fui-plus:before { + content: "\e608"; + } +.fui-cross:before { + content: "\e609"; + } +.fui-check:before { + content: "\e60a"; + } +.fui-radio-unchecked:before { + content: "\e60b"; + } +.fui-radio-checked:before { + content: "\e60c"; + } +.fui-checkbox-unchecked:before { + content: "\e60d"; + } +.fui-checkbox-checked:before { + content: "\e60e"; + } +.fui-info-circle:before { + content: "\e60f"; + } +.fui-alert-circle:before { + content: "\e610"; + } +.fui-question-circle:before { + content: "\e611"; + } +.fui-check-circle:before { + content: "\e612"; + } +.fui-cross-circle:before { + content: "\e613"; + } +.fui-plus-circle:before { + content: "\e614"; + } +.fui-pause:before { + content: "\e615"; + } +.fui-play:before { + content: "\e616"; + } +.fui-volume:before { + content: "\e617"; + } +.fui-mute:before { + content: "\e618"; + } +.fui-resize:before { + content: "\e619"; + } +.fui-list:before { + content: "\e61a"; + } +.fui-list-thumbnailed:before { + content: "\e61b"; + } +.fui-list-small-thumbnails:before { + content: "\e61c"; + } +.fui-list-large-thumbnails:before { + content: "\e61d"; + } +.fui-list-numbered:before { + content: "\e61e"; + } +.fui-list-columned:before { + content: "\e61f"; + } +.fui-list-bulleted:before { + content: "\e620"; + } +.fui-window:before { + content: "\e621"; + } +.fui-windows:before { + content: "\e622"; + } +.fui-loop:before { + content: "\e623"; + } +.fui-cmd:before { + content: "\e624"; + } +.fui-mic:before { + content: "\e625"; + } +.fui-heart:before { + content: "\e626"; + } +.fui-location:before { + content: "\e627"; + } +.fui-new:before { + content: "\e628"; + } +.fui-video:before { + content: "\e629"; + } +.fui-photo:before { + content: "\e62a"; + } +.fui-time:before { + content: "\e62b"; + } +.fui-eye:before { + content: "\e62c"; + } +.fui-chat:before { + content: "\e62d"; + } +.fui-home:before { + content: "\e62e"; + } +.fui-upload:before { + content: "\e62f"; + } +.fui-search:before { + content: "\e630"; + } +.fui-user:before { + content: "\e631"; + } +.fui-mail:before { + content: "\e632"; + } +.fui-lock:before { + content: "\e633"; + } +.fui-power:before { + content: "\e634"; + } +.fui-calendar:before { + content: "\e635"; + } +.fui-gear:before { + content: "\e636"; + } +.fui-bookmark:before { + content: "\e637"; + } +.fui-exit:before { + content: "\e638"; + } +.fui-trash:before { + content: "\e639"; + } +.fui-folder:before { + content: "\e63a"; + } +.fui-bubble:before { + content: "\e63b"; + } +.fui-export:before { + content: "\e63c"; + } +.fui-calendar-solid:before { + content: "\e63d"; + } +.fui-star:before { + content: "\e63e"; + } +.fui-star-2:before { + content: "\e63f"; + } +.fui-credit-card:before { + content: "\e640"; + } +.fui-clip:before { + content: "\e641"; + } +.fui-link:before { + content: "\e642"; + } +.fui-tag:before { + content: "\e643"; + } +.fui-document:before { + content: "\e644"; + } +.fui-image:before { + content: "\e645"; + } +.fui-facebook:before { + content: "\e646"; + } +.fui-youtube:before { + content: "\e647"; + } +.fui-vimeo:before { + content: "\e648"; + } +.fui-twitter:before { + content: "\e649"; + } +.fui-spotify:before { + content: "\e64a"; + } +.fui-skype:before { + content: "\e64b"; + } +.fui-pinterest:before { + content: "\e64c"; + } +.fui-path:before { + content: "\e64d"; + } +.fui-linkedin:before { + content: "\e64e"; + } +.fui-google-plus:before { + content: "\e64f"; + } +.fui-dribbble:before { + content: "\e650"; + } +.fui-behance:before { + content: "\e651"; + } +.fui-stumbleupon:before { + content: "\e652"; + } +.fui-yelp:before { + content: "\e653"; + } +.fui-wordpress:before { + content: "\e654"; + } +.fui-windows-8:before { + content: "\e655"; + } +.fui-vine:before { + content: "\e656"; + } +.fui-tumblr:before { + content: "\e657"; + } +.fui-paypal:before { + content: "\e658"; + } +.fui-lastfm:before { + content: "\e659"; + } +.fui-instagram:before { + content: "\e65a"; + } +.fui-html5:before { + content: "\e65b"; + } +.fui-github:before { + content: "\e65c"; + } +.fui-foursquare:before { + content: "\e65d"; + } +.fui-dropbox:before { + content: "\e65e"; + } +.fui-android:before { + content: "\e65f"; + } +.fui-apple:before { + content: "\e660"; + } +body { + font-family: "Lato", Helvetica, Arial, sans-serif; + font-size: 18px; + line-height: 1.72222; + color: #34495e; + background-color: #fff; + } +a { + color: #16a085; + text-decoration: none; + -webkit-transition: .25s; + transition: .25s; + } +a:hover, +a:focus { + color: #1abc9c; + text-decoration: none; + } +a:focus { + outline: none; + } +.img-rounded { + border-radius: 6px; + } +.img-thumbnail { + display: inline-block; + max-width: 100%; + height: auto; + padding: 4px; + line-height: 1.72222; + background-color: #fff; + border: 2px solid #bdc3c7; + border-radius: 6px; + -webkit-transition: all .25s ease-in-out; + transition: all .25s ease-in-out; + } +.img-comment { + margin: 24px 0; + font-size: 15px; + font-style: italic; + line-height: 1.2; + } +h1, +h2, +h3, +h4, +h5, +h6, +.h1, +.h2, +.h3, +.h4, +.h5, +.h6 { + font-family: inherit; + font-weight: 700; + line-height: 1.1; + color: inherit; + } +h1 small, +h2 small, +h3 small, +h4 small, +h5 small, +h6 small, +.h1 small, +.h2 small, +.h3 small, +.h4 small, +.h5 small, +.h6 small { + color: #e7e9ec; + } +h1, +h2, +h3 { + margin-top: 30px; + margin-bottom: 15px; + } +h4, +h5, +h6 { + margin-top: 15px; + margin-bottom: 15px; + } +h6 { + font-weight: normal; + } +h1, +.h1 { + font-size: 61px; + } +h2, +.h2 { + font-size: 53px; + } +h3, +.h3 { + font-size: 40px; + } +h4, +.h4 { + font-size: 29px; + } +h5, +.h5 { + font-size: 28px; + } +h6, +.h6 { + font-size: 24px; + } +p { + margin: 0 0 15px; + font-size: 18px; + line-height: 1.72222; + } +.lead { + margin-bottom: 30px; + font-size: 28px; + font-weight: 300; + line-height: 1.46428571; + } +@media (min-width: 768px) { + .lead { + font-size: 30.006px; + } + } +small, +.small { + font-size: 83%; + line-height: 2.067; + } +.text-muted { + color: #bdc3c7; + } +.text-inverse { + color: #fff; + } +.text-primary { + color: #1abc9c; + } +a.text-primary:hover { + color: #148f77; + } +.text-warning { + color: #f1c40f; + } +a.text-warning:hover { + color: #c29d0b; + } +.text-danger { + color: #e74c3c; + } +a.text-danger:hover { + color: #d62c1a; + } +.text-success { + color: #2ecc71; + } +a.text-success:hover { + color: #25a25a; + } +.text-info { + color: #3498db; + } +a.text-info:hover { + color: #217dbb; + } +.bg-primary { + color: #fff; + background-color: #34495e; + } +a.bg-primary:hover { + background-color: #222f3d; + } +.bg-success { + background-color: #dff0d8; + } +a.bg-success:hover { + background-color: #c1e2b3; + } +.bg-info { + background-color: #d9edf7; + } +a.bg-info:hover { + background-color: #afd9ee; + } +.bg-warning { + background-color: #fcf8e3; + } +a.bg-warning:hover { + background-color: #f7ecb5; + } +.bg-danger { + background-color: #f2dede; + } +a.bg-danger:hover { + background-color: #e4b9b9; + } +.page-header { + padding-bottom: 14px; + margin: 60px 0 30px; + border-bottom: 2px solid #e7e9ec; + } +ul, +ol { + margin-bottom: 15px; + } +dl { + margin-bottom: 30px; + } +dt, +dd { + line-height: 1.72222; + } +@media (min-width: 768px) { + .dl-horizontal dt { + width: 160px; + } + .dl-horizontal dd { + margin-left: 180px; + } + } +abbr[title], +abbr[data-original-title] { + border-bottom: 1px dotted #bdc3c7; + } +blockquote { + padding: 0 0 0 16px; + margin: 0 0 30px; + border-left: 3px solid #e7e9ec; + } +blockquote p { + margin-bottom: .4em; + font-size: 20px; + font-weight: normal; + line-height: 1.55; + } +blockquote small, +blockquote .small { + font-size: 18px; + font-style: italic; + line-height: 1.72222; + color: inherit; + } +blockquote small:before, +blockquote .small:before { + content: ""; + } +blockquote.pull-right { + padding-right: 16px; + padding-left: 0; + border-right: 3px solid #e7e9ec; + border-left: 0; + } +blockquote.pull-right small:after { + content: ""; + } +address { + margin-bottom: 30px; + line-height: 1.72222; + } +sub, +sup { + font-size: 70%; + } +code, +kbd, +pre, +samp { + font-family: Monaco, Menlo, Consolas, "Courier New", monospace; + } +code { + padding: 2px 6px; + font-size: 85%; + color: #c7254e; + background-color: #f9f2f4; + border-radius: 4px; + } +kbd { + padding: 2px 6px; + font-size: 85%; + color: #fff; + background-color: #34495e; + border-radius: 4px; + box-shadow: none; + } +pre { + padding: 8px; + margin: 0 0 15px; + font-size: 13px; + line-height: 1.72222; + color: inherit; + white-space: pre; + background-color: #fff; + border: 2px solid #e7e9ec; + border-radius: 6px; + } +.pre-scrollable { + max-height: 340px; + } +.thumbnail { + display: block; + padding: 4px; + margin-bottom: 5px; + line-height: 1.72222; + background-color: #fff; + border: 2px solid #bdc3c7; + border-radius: 6px; + -webkit-transition: border .25s ease-in-out; + transition: border .25s ease-in-out; + } +.thumbnail > img, +.thumbnail a > img { + display: block; + max-width: 100%; + height: auto; + margin-right: auto; + margin-left: auto; + } +a.thumbnail:hover, +a.thumbnail:focus, +a.thumbnail.active { + border-color: #16a085; + } +.thumbnail .caption { + padding: 9px; + color: #34495e; + } +.btn { + padding: 10px 15px; + font-size: 15px; + font-weight: normal; + line-height: 1.4; + border: none; + border-radius: 4px; + -webkit-transition: border .25s linear, color .25s linear, background-color .25s linear; + transition: border .25s linear, color .25s linear, background-color .25s linear; + + -webkit-font-smoothing: subpixel-antialiased; + } +.btn:hover, +.btn:focus { + color: #fff; + outline: none; + } +.btn:active, +.btn.active { + outline: none; + box-shadow: none; + } +.btn:focus:active { + outline: none; + } +.btn.disabled, +.btn[disabled], +fieldset[disabled] .btn { + color: rgba(255, 255, 255, .75); + cursor: not-allowed; + background-color: #bdc3c7; + filter: alpha(opacity=70); + opacity: .7; + } +.btn [class^="fui-"] { + position: relative; + top: 1px; + margin: 0 1px; + line-height: 1; + } +.btn-xs.btn [class^="fui-"] { + top: 0; + font-size: 11px; + } +.btn-hg.btn [class^="fui-"] { + top: 2px; + } +.btn-default { + color: #fff; + background-color: #bdc3c7; + } +.btn-default:hover, +.btn-default.hover, +.btn-default:focus, +.btn-default:active, +.btn-default.active, +.open > .dropdown-toggle.btn-default { + color: #fff; + background-color: #cacfd2; + border-color: #cacfd2; + } +.btn-default:active, +.btn-default.active, +.open > .dropdown-toggle.btn-default { + background: #a1a6a9; + border-color: #a1a6a9; + } +.btn-default.disabled, +.btn-default[disabled], +fieldset[disabled] .btn-default, +.btn-default.disabled:hover, +.btn-default[disabled]:hover, +fieldset[disabled] .btn-default:hover, +.btn-default.disabled.hover, +.btn-default[disabled].hover, +fieldset[disabled] .btn-default.hover, +.btn-default.disabled:focus, +.btn-default[disabled]:focus, +fieldset[disabled] .btn-default:focus, +.btn-default.disabled:active, +.btn-default[disabled]:active, +fieldset[disabled] .btn-default:active, +.btn-default.disabled.active, +.btn-default[disabled].active, +fieldset[disabled] .btn-default.active { + background-color: #bdc3c7; + border-color: #bdc3c7; + } +.btn-default .badge { + color: #bdc3c7; + background-color: #fff; + } +.btn-primary { + color: #fff; + background-color: #1abc9c; + } +.btn-primary:hover, +.btn-primary.hover, +.btn-primary:focus, +.btn-primary:active, +.btn-primary.active, +.open > .dropdown-toggle.btn-primary { + color: #fff; + background-color: #48c9b0; + border-color: #48c9b0; + } +.btn-primary:active, +.btn-primary.active, +.open > .dropdown-toggle.btn-primary { + background: #16a085; + border-color: #16a085; + } +.btn-primary.disabled, +.btn-primary[disabled], +fieldset[disabled] .btn-primary, +.btn-primary.disabled:hover, +.btn-primary[disabled]:hover, +fieldset[disabled] .btn-primary:hover, +.btn-primary.disabled.hover, +.btn-primary[disabled].hover, +fieldset[disabled] .btn-primary.hover, +.btn-primary.disabled:focus, +.btn-primary[disabled]:focus, +fieldset[disabled] .btn-primary:focus, +.btn-primary.disabled:active, +.btn-primary[disabled]:active, +fieldset[disabled] .btn-primary:active, +.btn-primary.disabled.active, +.btn-primary[disabled].active, +fieldset[disabled] .btn-primary.active { + background-color: #bdc3c7; + border-color: #1abc9c; + } +.btn-primary .badge { + color: #1abc9c; + background-color: #fff; + } +.btn-info { + color: #fff; + background-color: #3498db; + } +.btn-info:hover, +.btn-info.hover, +.btn-info:focus, +.btn-info:active, +.btn-info.active, +.open > .dropdown-toggle.btn-info { + color: #fff; + background-color: #5dade2; + border-color: #5dade2; + } +.btn-info:active, +.btn-info.active, +.open > .dropdown-toggle.btn-info { + background: #2c81ba; + border-color: #2c81ba; + } +.btn-info.disabled, +.btn-info[disabled], +fieldset[disabled] .btn-info, +.btn-info.disabled:hover, +.btn-info[disabled]:hover, +fieldset[disabled] .btn-info:hover, +.btn-info.disabled.hover, +.btn-info[disabled].hover, +fieldset[disabled] .btn-info.hover, +.btn-info.disabled:focus, +.btn-info[disabled]:focus, +fieldset[disabled] .btn-info:focus, +.btn-info.disabled:active, +.btn-info[disabled]:active, +fieldset[disabled] .btn-info:active, +.btn-info.disabled.active, +.btn-info[disabled].active, +fieldset[disabled] .btn-info.active { + background-color: #bdc3c7; + border-color: #3498db; + } +.btn-info .badge { + color: #3498db; + background-color: #fff; + } +.btn-danger { + color: #fff; + background-color: #e74c3c; + } +.btn-danger:hover, +.btn-danger.hover, +.btn-danger:focus, +.btn-danger:active, +.btn-danger.active, +.open > .dropdown-toggle.btn-danger { + color: #fff; + background-color: #ec7063; + border-color: #ec7063; + } +.btn-danger:active, +.btn-danger.active, +.open > .dropdown-toggle.btn-danger { + background: #c44133; + border-color: #c44133; + } +.btn-danger.disabled, +.btn-danger[disabled], +fieldset[disabled] .btn-danger, +.btn-danger.disabled:hover, +.btn-danger[disabled]:hover, +fieldset[disabled] .btn-danger:hover, +.btn-danger.disabled.hover, +.btn-danger[disabled].hover, +fieldset[disabled] .btn-danger.hover, +.btn-danger.disabled:focus, +.btn-danger[disabled]:focus, +fieldset[disabled] .btn-danger:focus, +.btn-danger.disabled:active, +.btn-danger[disabled]:active, +fieldset[disabled] .btn-danger:active, +.btn-danger.disabled.active, +.btn-danger[disabled].active, +fieldset[disabled] .btn-danger.active { + background-color: #bdc3c7; + border-color: #e74c3c; + } +.btn-danger .badge { + color: #e74c3c; + background-color: #fff; + } +.btn-success { + color: #fff; + background-color: #2ecc71; + } +.btn-success:hover, +.btn-success.hover, +.btn-success:focus, +.btn-success:active, +.btn-success.active, +.open > .dropdown-toggle.btn-success { + color: #fff; + background-color: #58d68d; + border-color: #58d68d; + } +.btn-success:active, +.btn-success.active, +.open > .dropdown-toggle.btn-success { + background: #27ad60; + border-color: #27ad60; + } +.btn-success.disabled, +.btn-success[disabled], +fieldset[disabled] .btn-success, +.btn-success.disabled:hover, +.btn-success[disabled]:hover, +fieldset[disabled] .btn-success:hover, +.btn-success.disabled.hover, +.btn-success[disabled].hover, +fieldset[disabled] .btn-success.hover, +.btn-success.disabled:focus, +.btn-success[disabled]:focus, +fieldset[disabled] .btn-success:focus, +.btn-success.disabled:active, +.btn-success[disabled]:active, +fieldset[disabled] .btn-success:active, +.btn-success.disabled.active, +.btn-success[disabled].active, +fieldset[disabled] .btn-success.active { + background-color: #bdc3c7; + border-color: #2ecc71; + } +.btn-success .badge { + color: #2ecc71; + background-color: #fff; + } +.btn-warning { + color: #fff; + background-color: #f1c40f; + } +.btn-warning:hover, +.btn-warning.hover, +.btn-warning:focus, +.btn-warning:active, +.btn-warning.active, +.open > .dropdown-toggle.btn-warning { + color: #fff; + background-color: #f4d313; + border-color: #f4d313; + } +.btn-warning:active, +.btn-warning.active, +.open > .dropdown-toggle.btn-warning { + background: #cda70d; + border-color: #cda70d; + } +.btn-warning.disabled, +.btn-warning[disabled], +fieldset[disabled] .btn-warning, +.btn-warning.disabled:hover, +.btn-warning[disabled]:hover, +fieldset[disabled] .btn-warning:hover, +.btn-warning.disabled.hover, +.btn-warning[disabled].hover, +fieldset[disabled] .btn-warning.hover, +.btn-warning.disabled:focus, +.btn-warning[disabled]:focus, +fieldset[disabled] .btn-warning:focus, +.btn-warning.disabled:active, +.btn-warning[disabled]:active, +fieldset[disabled] .btn-warning:active, +.btn-warning.disabled.active, +.btn-warning[disabled].active, +fieldset[disabled] .btn-warning.active { + background-color: #bdc3c7; + border-color: #f1c40f; + } +.btn-warning .badge { + color: #f1c40f; + background-color: #fff; + } +.btn-inverse { + color: #fff; + background-color: #34495e; + } +.btn-inverse:hover, +.btn-inverse.hover, +.btn-inverse:focus, +.btn-inverse:active, +.btn-inverse.active, +.open > .dropdown-toggle.btn-inverse { + color: #fff; + background-color: #415b76; + border-color: #415b76; + } +.btn-inverse:active, +.btn-inverse.active, +.open > .dropdown-toggle.btn-inverse { + background: #2c3e50; + border-color: #2c3e50; + } +.btn-inverse.disabled, +.btn-inverse[disabled], +fieldset[disabled] .btn-inverse, +.btn-inverse.disabled:hover, +.btn-inverse[disabled]:hover, +fieldset[disabled] .btn-inverse:hover, +.btn-inverse.disabled.hover, +.btn-inverse[disabled].hover, +fieldset[disabled] .btn-inverse.hover, +.btn-inverse.disabled:focus, +.btn-inverse[disabled]:focus, +fieldset[disabled] .btn-inverse:focus, +.btn-inverse.disabled:active, +.btn-inverse[disabled]:active, +fieldset[disabled] .btn-inverse:active, +.btn-inverse.disabled.active, +.btn-inverse[disabled].active, +fieldset[disabled] .btn-inverse.active { + background-color: #bdc3c7; + border-color: #34495e; + } +.btn-inverse .badge { + color: #34495e; + background-color: #fff; + } +.btn-embossed { + box-shadow: inset 0 -2px 0 rgba(0, 0, 0, .15); + } +.btn-embossed.active, +.btn-embossed:active { + box-shadow: inset 0 2px 0 rgba(0, 0, 0, .15); + } +.btn-wide { + min-width: 140px; + padding-right: 30px; + padding-left: 30px; + } +.btn-link { + color: #16a085; + } +.btn-link:hover, +.btn-link:focus { + color: #1abc9c; + text-decoration: underline; + background-color: transparent; + } +.btn-link[disabled]:hover, +fieldset[disabled] .btn-link:hover, +.btn-link[disabled]:focus, +fieldset[disabled] .btn-link:focus { + color: #bdc3c7; + text-decoration: none; + } +.btn-hg, +.btn-group-hg > .btn { + padding: 13px 20px; + font-size: 22px; + line-height: 1.227; + border-radius: 6px; + } +.btn-lg, +.btn-group-lg > .btn { + padding: 10px 19px; + font-size: 17px; + line-height: 1.471; + border-radius: 6px; + } +.btn-sm, +.btn-group-sm > .btn { + padding: 9px 13px; + font-size: 13px; + line-height: 1.385; + border-radius: 4px; + } +.btn-xs, +.btn-group-xs > .btn { + padding: 6px 9px; + font-size: 12px; + line-height: 1.083; + border-radius: 3px; + } +.btn-tip { + padding-left: 10px; + font-size: 92%; + font-weight: 300; + } +.btn-block { + white-space: normal; + } +[class*="btn-social-"] { + padding: 10px 15px; + font-size: 13px; + line-height: 1.077; + border-radius: 4px; + } +.btn-social-pinterest { + color: #fff; + background-color: #cb2028; + } +.btn-social-pinterest:hover, +.btn-social-pinterest:focus { + background-color: #d54d53; + } +.btn-social-pinterest:active, +.btn-social-pinterest.active { + background-color: #ad1b22; + } +.btn-social-linkedin { + color: #fff; + background-color: #0072b5; + } +.btn-social-linkedin:hover, +.btn-social-linkedin:focus { + background-color: #338ec4; + } +.btn-social-linkedin:active, +.btn-social-linkedin.active { + background-color: #00619a; + } +.btn-social-stumbleupon { + color: #fff; + background-color: #ed4a13; + } +.btn-social-stumbleupon:hover, +.btn-social-stumbleupon:focus { + background-color: #f16e42; + } +.btn-social-stumbleupon:active, +.btn-social-stumbleupon.active { + background-color: #c93f10; + } +.btn-social-googleplus { + color: #fff; + background-color: #2d2d2d; + } +.btn-social-googleplus:hover, +.btn-social-googleplus:focus { + background-color: #575757; + } +.btn-social-googleplus:active, +.btn-social-googleplus.active { + background-color: #262626; + } +.btn-social-facebook { + color: #fff; + background-color: #2f4b93; + } +.btn-social-facebook:hover, +.btn-social-facebook:focus { + background-color: #596fa9; + } +.btn-social-facebook:active, +.btn-social-facebook.active { + background-color: #28407d; + } +.btn-social-twitter { + color: #fff; + background-color: #00bdef; + } +.btn-social-twitter:hover, +.btn-social-twitter:focus { + background-color: #33caf2; + } +.btn-social-twitter:active, +.btn-social-twitter.active { + background-color: #00a1cb; + } +.btn-group > .btn + .btn { + margin-left: 0; + } +.btn-group > .btn + .dropdown-toggle { + padding: 10px 12px; + border-left: 2px solid rgba(52, 73, 94, .15); + } +.btn-group > .btn + .dropdown-toggle .caret { + margin-right: 3px; + margin-left: 3px; + } +.btn-group > .btn.btn-gh + .dropdown-toggle .caret { + margin-right: 7px; + margin-left: 7px; + } +.btn-group > .btn.btn-sm + .dropdown-toggle .caret { + margin-right: 0; + margin-left: 0; + } +.dropdown-toggle .caret { + margin-left: 8px; + } +.btn-group-xs > .btn + .dropdown-toggle { + padding: 6px 9px; + } +.btn-group-sm > .btn + .dropdown-toggle { + padding: 9px 13px; + } +.btn-group-lg > .btn + .dropdown-toggle { + padding: 10px 19px; + } +.btn-group-hg > .btn + .dropdown-toggle { + padding: 13px 20px; + } +.btn-xs .caret { + border-width: 6px 4px 0; + border-bottom-width: 0; + } +.btn-lg .caret { + border-width: 8px 6px 0; + border-bottom-width: 0; + } +.dropup .btn-lg .caret { + border-width: 0 6px 8px; + } +.dropup .btn-xs .caret { + border-width: 0 4px 6px; + } +.btn-group > .btn, +.btn-group > .dropdown-menu, +.btn-group > .popover { + font-weight: 400; + } +.btn-group:focus .dropdown-toggle { + outline: none; + -webkit-transition: .25s; + transition: .25s; + } +.btn-group.open .dropdown-toggle { + color: rgba(255, 255, 255, .75); + box-shadow: none; + } +.btn-toolbar .btn.active { + color: #fff; + } +.btn-toolbar .btn > [class^="fui-"] { + margin: 0 1px; + font-size: 16px; + } +legend { + display: block; + width: 100%; + padding: 0; + margin-bottom: 30px / 2; + font-size: 24px; + line-height: inherit; + color: inherit; + border-bottom: none; + } +textarea { + padding: 5px 11px; + font-size: 20px; + line-height: 24px; + } +input[type="search"] { + -webkit-appearance: none !important; + } +label { + font-size: 15px; + font-weight: normal; + line-height: 2.3; + } +.form-control::-moz-placeholder, +.select2-search input[type="text"]::-moz-placeholder { + color: #b2bcc5; + opacity: 1; + } +.form-control:-ms-input-placeholder, +.select2-search input[type="text"]:-ms-input-placeholder { + color: #b2bcc5; + } +.form-control::-webkit-input-placeholder, +.select2-search input[type="text"]::-webkit-input-placeholder { + color: #b2bcc5; + } +.form-control, +.select2-search input[type="text"] { + height: 42px; + padding: 8px 12px; + font-family: "Lato", Helvetica, Arial, sans-serif; + font-size: 15px; + line-height: 1.467; + color: #34495e; + border: 2px solid #bdc3c7; + border-radius: 6px; + box-shadow: none; + -webkit-transition: border .25s linear, color .25s linear, background-color .25s linear; + transition: border .25s linear, color .25s linear, background-color .25s linear; + } +.form-group.focus .form-control, +.form-control:focus, +.form-group.focus .select2-search input[type="text"], +.select2-search input[type="text"]:focus { + border-color: #1abc9c; + outline: 0; + box-shadow: none; + } +.form-control[disabled], +.form-control[readonly], +fieldset[disabled] .form-control, +.select2-search input[type="text"][disabled], +.select2-search input[type="text"][readonly], +fieldset[disabled] .select2-search input[type="text"] { + color: #d5dbdb; + cursor: default; + background-color: #f4f6f6; + filter: alpha(opacity=70); + border-color: #d5dbdb; + opacity: .7; + } +.form-control.flat, +.select2-search input[type="text"].flat { + border-color: transparent; + } +.form-control.flat:hover, +.select2-search input[type="text"].flat:hover { + border-color: #bdc3c7; + } +.form-control.flat:focus, +.select2-search input[type="text"].flat:focus { + border-color: #1abc9c; + } +.input-sm, +.form-group-sm .form-control, +.form-group-sm .select2-search input[type="text"], +.select2-search input[type="text"] { + height: 35px; + padding: 6px 10px; + font-size: 13px; + line-height: 1.462; + border-radius: 6px; + } +select.input-sm, +select.form-group-sm .form-control, +select.form-group-sm .select2-search input[type="text"], +select.select2-search input[type="text"] { + height: 35px; + line-height: 35px; + } +textarea.input-sm, +textarea.form-group-sm .form-control, +select[multiple].input-sm, +select[multiple].form-group-sm .form-control, +textarea.form-group-sm .select2-search input[type="text"], +select[multiple].form-group-sm .select2-search input[type="text"], +textarea.select2-search input[type="text"], +select[multiple].select2-search input[type="text"] { + height: auto; + } +.input-lg, +.form-group-lg .form-control, +.form-group-lg .select2-search input[type="text"] { + height: 45px; + padding: 10px 15px; + font-size: 17px; + line-height: 1.235; + border-radius: 6px; + } +select.input-lg, +select.form-group-lg .form-control, +select.form-group-lg .select2-search input[type="text"] { + height: 45px; + line-height: 45px; + } +textarea.input-lg, +textarea.form-group-lg .form-control, +select[multiple].input-lg, +select[multiple].form-group-lg .form-control, +textarea.form-group-lg .select2-search input[type="text"], +select[multiple].form-group-lg .select2-search input[type="text"] { + height: auto; + } +.input-hg, +.form-group-hg .form-control, +.form-horizontal .form-group-hg .form-control, +.form-group-hg .select2-search input[type="text"], +.form-horizontal .form-group-hg .select2-search input[type="text"] { + height: 53px; + padding: 10px 16px; + font-size: 22px; + line-height: 1.318; + border-radius: 6px; + } +select.input-hg, +select.form-group-hg .form-control, +select.form-group-hg .select2-search input[type="text"] { + height: 53px; + line-height: 53px; + } +textarea.input-hg, +textarea.form-group-hg .form-control, +select[multiple].input-hg, +select[multiple].form-group-hg .form-control, +textarea.form-group-hg .select2-search input[type="text"], +select[multiple].form-group-hg .select2-search input[type="text"] { + height: auto; + } +.form-control-feedback { + position: absolute; + top: 2px; + right: 2px; + padding: 0 12px 0 0; + margin-top: 1px; + font-size: 17px; + line-height: 36px; + color: #b2bcc5; + pointer-events: none; + background-color: transparent; + border-radius: 6px; + } +.input-hg + .form-control-feedback, +.control-feedback-hg { + width: auto; + height: 48px; + padding-right: 16px; + font-size: 20px; + line-height: 48px; + } +.input-lg + .form-control-feedback, +.control-feedback-lg { + width: auto; + height: 40px; + padding-right: 15px; + font-size: 18px; + line-height: 40px; + } +.input-sm + .form-control-feedback, +.control-feedback-sm, +.select2-search input[type="text"] + .form-control-feedback { + width: auto; + height: 29px; + padding-right: 10px; + line-height: 29px; + } +.has-success .help-block, +.has-success .control-label, +.has-success .radio, +.has-success .checkbox, +.has-success .radio-inline, +.has-success .checkbox-inline { + color: #2ecc71; + } +.has-success .form-control, +.has-success .select2-search input[type="text"] { + color: #2ecc71; + border-color: #2ecc71; + box-shadow: none; + } +.has-success .form-control::-moz-placeholder, +.has-success .select2-search input[type="text"]::-moz-placeholder { + color: #2ecc71; + opacity: 1; + } +.has-success .form-control:-ms-input-placeholder, +.has-success .select2-search input[type="text"]:-ms-input-placeholder { + color: #2ecc71; + } +.has-success .form-control::-webkit-input-placeholder, +.has-success .select2-search input[type="text"]::-webkit-input-placeholder { + color: #2ecc71; + } +.has-success .form-control:focus, +.has-success .select2-search input[type="text"]:focus { + border-color: #2ecc71; + box-shadow: none; + } +.has-success .input-group-addon { + color: #2ecc71; + background-color: #fff; + border-color: #2ecc71; + } +.has-success .form-control-feedback { + color: #2ecc71; + } +.has-warning .help-block, +.has-warning .control-label, +.has-warning .radio, +.has-warning .checkbox, +.has-warning .radio-inline, +.has-warning .checkbox-inline { + color: #f1c40f; + } +.has-warning .form-control, +.has-warning .select2-search input[type="text"] { + color: #f1c40f; + border-color: #f1c40f; + box-shadow: none; + } +.has-warning .form-control::-moz-placeholder, +.has-warning .select2-search input[type="text"]::-moz-placeholder { + color: #f1c40f; + opacity: 1; + } +.has-warning .form-control:-ms-input-placeholder, +.has-warning .select2-search input[type="text"]:-ms-input-placeholder { + color: #f1c40f; + } +.has-warning .form-control::-webkit-input-placeholder, +.has-warning .select2-search input[type="text"]::-webkit-input-placeholder { + color: #f1c40f; + } +.has-warning .form-control:focus, +.has-warning .select2-search input[type="text"]:focus { + border-color: #f1c40f; + box-shadow: none; + } +.has-warning .input-group-addon { + color: #f1c40f; + background-color: #fff; + border-color: #f1c40f; + } +.has-warning .form-control-feedback { + color: #f1c40f; + } +.has-error .help-block, +.has-error .control-label, +.has-error .radio, +.has-error .checkbox, +.has-error .radio-inline, +.has-error .checkbox-inline { + color: #e74c3c; + } +.has-error .form-control, +.has-error .select2-search input[type="text"] { + color: #e74c3c; + border-color: #e74c3c; + box-shadow: none; + } +.has-error .form-control::-moz-placeholder, +.has-error .select2-search input[type="text"]::-moz-placeholder { + color: #e74c3c; + opacity: 1; + } +.has-error .form-control:-ms-input-placeholder, +.has-error .select2-search input[type="text"]:-ms-input-placeholder { + color: #e74c3c; + } +.has-error .form-control::-webkit-input-placeholder, +.has-error .select2-search input[type="text"]::-webkit-input-placeholder { + color: #e74c3c; + } +.has-error .form-control:focus, +.has-error .select2-search input[type="text"]:focus { + border-color: #e74c3c; + box-shadow: none; + } +.has-error .input-group-addon { + color: #e74c3c; + background-color: #fff; + border-color: #e74c3c; + } +.has-error .form-control-feedback { + color: #e74c3c; + } +.form-control[disabled] + .form-control-feedback, +.form-control[readonly] + .form-control-feedback, +fieldset[disabled] .form-control + .form-control-feedback, +.form-control.disabled + .form-control-feedback, +.select2-search input[type="text"][disabled] + .form-control-feedback, +.select2-search input[type="text"][readonly] + .form-control-feedback, +fieldset[disabled] .select2-search input[type="text"] + .form-control-feedback, +.select2-search input[type="text"].disabled + .form-control-feedback { + color: #d5dbdb; + cursor: not-allowed; + background-color: transparent; + filter: alpha(opacity=70); + opacity: .7; + } +.help-block { + margin-bottom: 5px; + font-size: 14px; + color: #6b7a88; + } +.form-group { + position: relative; + margin-bottom: 20px; + } +.form-horizontal .radio, +.form-horizontal .checkbox, +.form-horizontal .radio-inline, +.form-horizontal .checkbox-inline { + padding-top: 0; + margin-top: 0; + margin-bottom: 0; + } +@media (min-width: 768px) { + .form-horizontal .control-label { + padding-top: 3px; + padding-bottom: 3px; + } + } +.form-horizontal .form-group { + margin-right: -15px; + margin-left: -15px; + } +.form-horizontal .form-control-static { + padding-top: 6px; + padding-bottom: 6px; + } +@media (min-width: 768px) { + .form-horizontal .form-group-hg .control-label { + padding-top: 2px; + padding-bottom: 0; + font-size: 22px; + } + } +@media (min-width: 768px) { + .form-horizontal .form-group-lg .control-label { + padding-top: 3px; + padding-bottom: 2px; + font-size: 17px; + } + } +@media (min-width: 768px) { + .form-horizontal .form-group-sm .control-label { + padding-top: 2px; + padding-bottom: 2px; + font-size: 13px; + } + } +.input-group .form-control, +.input-group .select2-search input[type="text"] { + position: static; + } +.input-group-hg > .form-control, +.input-group-hg > .input-group-addon, +.input-group-hg > .input-group-btn > .btn, +.input-group-hg > .select2-search input[type="text"] { + height: 53px; + padding: 10px 16px; + font-size: 22px; + line-height: 1.318; + border-radius: 6px; + } +select.input-group-hg > .form-control, +select.input-group-hg > .input-group-addon, +select.input-group-hg > .input-group-btn > .btn, +select.input-group-hg > .select2-search input[type="text"] { + height: 53px; + line-height: 53px; + } +textarea.input-group-hg > .form-control, +textarea.input-group-hg > .input-group-addon, +textarea.input-group-hg > .input-group-btn > .btn, +select[multiple].input-group-hg > .form-control, +select[multiple].input-group-hg > .input-group-addon, +select[multiple].input-group-hg > .input-group-btn > .btn, +textarea.input-group-hg > .select2-search input[type="text"], +select[multiple].input-group-hg > .select2-search input[type="text"] { + height: auto; + } +.input-group-lg > .form-control, +.input-group-lg > .input-group-addon, +.input-group-lg > .input-group-btn > .btn, +.input-group-lg > .select2-search input[type="text"] { + height: 45px; + padding: 10px 15px; + font-size: 17px; + line-height: 1.235; + border-radius: 6px; + } +select.input-group-lg > .form-control, +select.input-group-lg > .input-group-addon, +select.input-group-lg > .input-group-btn > .btn, +select.input-group-lg > .select2-search input[type="text"] { + height: 45px; + line-height: 45px; + } +textarea.input-group-lg > .form-control, +textarea.input-group-lg > .input-group-addon, +textarea.input-group-lg > .input-group-btn > .btn, +select[multiple].input-group-lg > .form-control, +select[multiple].input-group-lg > .input-group-addon, +select[multiple].input-group-lg > .input-group-btn > .btn, +textarea.input-group-lg > .select2-search input[type="text"], +select[multiple].input-group-lg > .select2-search input[type="text"] { + height: auto; + } +.input-group-sm > .form-control, +.input-group-sm > .input-group-addon, +.input-group-sm > .input-group-btn > .btn, +.input-group-sm > .select2-search input[type="text"] { + height: 35px; + padding: 6px 10px; + font-size: 13px; + line-height: 1.462; + border-radius: 6px; + } +select.input-group-sm > .form-control, +select.input-group-sm > .input-group-addon, +select.input-group-sm > .input-group-btn > .btn, +select.input-group-sm > .select2-search input[type="text"] { + height: 35px; + line-height: 35px; + } +textarea.input-group-sm > .form-control, +textarea.input-group-sm > .input-group-addon, +textarea.input-group-sm > .input-group-btn > .btn, +select[multiple].input-group-sm > .form-control, +select[multiple].input-group-sm > .input-group-addon, +select[multiple].input-group-sm > .input-group-btn > .btn, +textarea.input-group-sm > .select2-search input[type="text"], +select[multiple].input-group-sm > .select2-search input[type="text"] { + height: auto; + } +.input-group-addon { + padding: 10px 12px; + font-size: 15px; + color: #fff; + text-align: center; + background-color: #bdc3c7; + border: 2px solid #bdc3c7; + border-radius: 6px; + -webkit-transition: border .25s linear, color .25s linear, background-color .25s linear; + transition: border .25s linear, color .25s linear, background-color .25s linear; + } +.input-group-hg .input-group-addon, +.input-group-lg .input-group-addon, +.input-group-sm .input-group-addon { + line-height: 1; + } +.input-group .form-control:first-child, +.input-group-addon:first-child, +.input-group-btn:first-child > .btn, +.input-group-btn:first-child > .dropdown-toggle, +.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), +.input-group .select2-search input[type="text"]:first-child { + border-top-right-radius: 0; + border-bottom-right-radius: 0; + } +.input-group .form-control:last-child, +.input-group-addon:last-child, +.input-group-btn:last-child > .btn, +.input-group-btn:last-child > .dropdown-toggle, +.input-group-btn:first-child > .btn:not(:first-child), +.input-group .select2-search input[type="text"]:last-child { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } +.form-group.focus .input-group-addon, +.input-group.focus .input-group-addon { + background-color: #1abc9c; + border-color: #1abc9c; + } +.form-group.focus .input-group-btn > .btn-default + .btn-default, +.input-group.focus .input-group-btn > .btn-default + .btn-default { + border-left-color: #16a085; + } +.form-group.focus .input-group-btn .btn, +.input-group.focus .input-group-btn .btn { + color: #1abc9c; + background-color: #fff; + border-color: #1abc9c; + } +.form-group.focus .input-group-btn .btn-default, +.input-group.focus .input-group-btn .btn-default { + color: #fff; + background-color: #1abc9c; + } +.form-group.focus .input-group-btn .btn-default:hover, +.input-group.focus .input-group-btn .btn-default:hover, +.form-group.focus .input-group-btn .btn-default.hover, +.input-group.focus .input-group-btn .btn-default.hover, +.form-group.focus .input-group-btn .btn-default:focus, +.input-group.focus .input-group-btn .btn-default:focus, +.form-group.focus .input-group-btn .btn-default:active, +.input-group.focus .input-group-btn .btn-default:active, +.form-group.focus .input-group-btn .btn-default.active, +.input-group.focus .input-group-btn .btn-default.active, +.open > .dropdown-toggle.form-group.focus .input-group-btn .btn-default, +.open > .dropdown-toggle.input-group.focus .input-group-btn .btn-default { + color: #fff; + background-color: #48c9b0; + border-color: #48c9b0; + } +.form-group.focus .input-group-btn .btn-default:active, +.input-group.focus .input-group-btn .btn-default:active, +.form-group.focus .input-group-btn .btn-default.active, +.input-group.focus .input-group-btn .btn-default.active, +.open > .dropdown-toggle.form-group.focus .input-group-btn .btn-default, +.open > .dropdown-toggle.input-group.focus .input-group-btn .btn-default { + background: #16a085; + border-color: #16a085; + } +.form-group.focus .input-group-btn .btn-default.disabled, +.input-group.focus .input-group-btn .btn-default.disabled, +.form-group.focus .input-group-btn .btn-default[disabled], +.input-group.focus .input-group-btn .btn-default[disabled], +fieldset[disabled] .form-group.focus .input-group-btn .btn-default, +fieldset[disabled] .input-group.focus .input-group-btn .btn-default, +.form-group.focus .input-group-btn .btn-default.disabled:hover, +.input-group.focus .input-group-btn .btn-default.disabled:hover, +.form-group.focus .input-group-btn .btn-default[disabled]:hover, +.input-group.focus .input-group-btn .btn-default[disabled]:hover, +fieldset[disabled] .form-group.focus .input-group-btn .btn-default:hover, +fieldset[disabled] .input-group.focus .input-group-btn .btn-default:hover, +.form-group.focus .input-group-btn .btn-default.disabled.hover, +.input-group.focus .input-group-btn .btn-default.disabled.hover, +.form-group.focus .input-group-btn .btn-default[disabled].hover, +.input-group.focus .input-group-btn .btn-default[disabled].hover, +fieldset[disabled] .form-group.focus .input-group-btn .btn-default.hover, +fieldset[disabled] .input-group.focus .input-group-btn .btn-default.hover, +.form-group.focus .input-group-btn .btn-default.disabled:focus, +.input-group.focus .input-group-btn .btn-default.disabled:focus, +.form-group.focus .input-group-btn .btn-default[disabled]:focus, +.input-group.focus .input-group-btn .btn-default[disabled]:focus, +fieldset[disabled] .form-group.focus .input-group-btn .btn-default:focus, +fieldset[disabled] .input-group.focus .input-group-btn .btn-default:focus, +.form-group.focus .input-group-btn .btn-default.disabled:active, +.input-group.focus .input-group-btn .btn-default.disabled:active, +.form-group.focus .input-group-btn .btn-default[disabled]:active, +.input-group.focus .input-group-btn .btn-default[disabled]:active, +fieldset[disabled] .form-group.focus .input-group-btn .btn-default:active, +fieldset[disabled] .input-group.focus .input-group-btn .btn-default:active, +.form-group.focus .input-group-btn .btn-default.disabled.active, +.input-group.focus .input-group-btn .btn-default.disabled.active, +.form-group.focus .input-group-btn .btn-default[disabled].active, +.input-group.focus .input-group-btn .btn-default[disabled].active, +fieldset[disabled] .form-group.focus .input-group-btn .btn-default.active, +fieldset[disabled] .input-group.focus .input-group-btn .btn-default.active { + background-color: #bdc3c7; + border-color: #1abc9c; + } +.form-group.focus .input-group-btn .btn-default .badge, +.input-group.focus .input-group-btn .btn-default .badge { + color: #1abc9c; + background-color: #fff; + } +.input-group-btn .btn { + height: 42px; + line-height: 18px; + color: #bdc3c7; + background-color: #fff; + border: 2px solid #bdc3c7; + } +.input-group-btn .btn-default { + color: #fff; + background-color: #bdc3c7; + } +.input-group-btn .btn-default:hover, +.input-group-btn .btn-default.hover, +.input-group-btn .btn-default:focus, +.input-group-btn .btn-default:active, +.input-group-btn .btn-default.active, +.open > .dropdown-toggle.input-group-btn .btn-default { + color: #fff; + background-color: #cacfd2; + border-color: #cacfd2; + } +.input-group-btn .btn-default:active, +.input-group-btn .btn-default.active, +.open > .dropdown-toggle.input-group-btn .btn-default { + background: #a1a6a9; + border-color: #a1a6a9; + } +.input-group-btn .btn-default.disabled, +.input-group-btn .btn-default[disabled], +fieldset[disabled] .input-group-btn .btn-default, +.input-group-btn .btn-default.disabled:hover, +.input-group-btn .btn-default[disabled]:hover, +fieldset[disabled] .input-group-btn .btn-default:hover, +.input-group-btn .btn-default.disabled.hover, +.input-group-btn .btn-default[disabled].hover, +fieldset[disabled] .input-group-btn .btn-default.hover, +.input-group-btn .btn-default.disabled:focus, +.input-group-btn .btn-default[disabled]:focus, +fieldset[disabled] .input-group-btn .btn-default:focus, +.input-group-btn .btn-default.disabled:active, +.input-group-btn .btn-default[disabled]:active, +fieldset[disabled] .input-group-btn .btn-default:active, +.input-group-btn .btn-default.disabled.active, +.input-group-btn .btn-default[disabled].active, +fieldset[disabled] .input-group-btn .btn-default.active { + background-color: #bdc3c7; + border-color: #bdc3c7; + } +.input-group-btn .btn-default .badge { + color: #bdc3c7; + background-color: #fff; + } +.input-group-hg .input-group-btn .btn { + line-height: 31px; + } +.input-group-lg .input-group-btn .btn { + line-height: 21px; + } +.input-group-sm .input-group-btn .btn { + line-height: 19px; + } +.input-group-btn:first-child > .btn { + margin-right: -3px; + border-right-width: 0; + } +.input-group-btn:last-child > .btn { + margin-left: -3px; + border-left-width: 0; + } +.input-group-btn > .btn-default + .btn-default { + border-left: 2px solid #bdc3c7; + } +.input-group-btn > .btn:first-child + .btn .caret { + margin-left: 0; + } +.input-group-rounded .input-group-btn + .form-control, +.input-group-rounded .input-group-btn:last-child .btn, +.input-group-rounded .input-group-btn + .select2-search input[type="text"] { + border-top-right-radius: 20px; + border-bottom-right-radius: 20px; + } +.input-group-hg.input-group-rounded .input-group-btn + .form-control, +.input-group-hg.input-group-rounded .input-group-btn:last-child .btn, +.input-group-hg.input-group-rounded .input-group-btn + .select2-search input[type="text"] { + border-top-right-radius: 27px; + border-bottom-right-radius: 27px; + } +.input-group-lg.input-group-rounded .input-group-btn + .form-control, +.input-group-lg.input-group-rounded .input-group-btn:last-child .btn, +.input-group-lg.input-group-rounded .input-group-btn + .select2-search input[type="text"] { + border-top-right-radius: 25px; + border-bottom-right-radius: 25px; + } +.input-group-rounded .form-control:first-child, +.input-group-rounded .input-group-btn:first-child .btn, +.input-group-rounded .select2-search input[type="text"]:first-child { + border-top-left-radius: 20px; + border-bottom-left-radius: 20px; + } +.input-group-hg.input-group-rounded .form-control:first-child, +.input-group-hg.input-group-rounded .input-group-btn:first-child .btn, +.input-group-hg.input-group-rounded .select2-search input[type="text"]:first-child { + border-top-left-radius: 27px; + border-bottom-left-radius: 27px; + } +.input-group-lg.input-group-rounded .form-control:first-child, +.input-group-lg.input-group-rounded .input-group-btn:first-child .btn, +.input-group-lg.input-group-rounded .select2-search input[type="text"]:first-child { + border-top-left-radius: 25px; + border-bottom-left-radius: 25px; + } +.input-group-rounded .input-group-btn + .form-control, +.input-group-rounded .input-group-btn + .select2-search input[type="text"] { + padding-left: 0; + } +.checkbox, +.radio { + position: relative; + padding-left: 32px; + margin-bottom: 12px; + font-size: 14px; + line-height: 1.5; + -webkit-transition: color .25s linear; + transition: color .25s linear; + } +.checkbox .icons, +.radio .icons { + position: absolute; + top: 0; + left: 0; + display: block; + width: 20px; + height: 20px; + font-size: 20px; + line-height: 20px; + color: #bdc3c7; + text-align: center; + cursor: pointer; + } +.checkbox .icons .icon-checked, +.radio .icons .icon-checked { + filter: alpha(opacity=0); + opacity: 0; + } +.checkbox .icon-checked, +.radio .icon-checked, +.checkbox .icon-unchecked, +.radio .icon-unchecked { + position: absolute; + top: 0; + left: 0; + display: inline-table; + margin: 0; + background-color: transparent; + filter: none; + opacity: 1; + -webkit-transition: color .25s linear; + transition: color .25s linear; + + -webkit-filter: none; + } +.checkbox .icon-checked:before, +.radio .icon-checked:before, +.checkbox .icon-unchecked:before, +.radio .icon-unchecked:before { + font-family: 'Flat-UI-Icons'; + font-style: normal; + font-weight: normal; + font-variant: normal; + text-transform: none; + + speak: none; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + } +.checkbox .icon-checked:before { + content: "\e60e"; + } +.checkbox .icon-unchecked:before { + content: "\e60d"; + } +.radio .icon-checked:before { + content: "\e60c"; + } +.radio .icon-unchecked:before { + content: "\e60b"; + } +.checkbox input[type="checkbox"].custom-checkbox, +.radio input[type="checkbox"].custom-checkbox, +.checkbox input[type="radio"].custom-radio, +.radio input[type="radio"].custom-radio { + position: absolute; + top: 0; + left: 0; + width: 20px; + height: 20px; + padding: 0; + margin: 0; + outline: none !important; + opacity: 0; + } +.checkbox input[type="checkbox"].custom-checkbox:hover:not(.nohover):not(:disabled) + .icons .icon-unchecked, +.radio input[type="checkbox"].custom-checkbox:hover:not(.nohover):not(:disabled) + .icons .icon-unchecked, +.checkbox input[type="radio"].custom-radio:hover:not(.nohover):not(:disabled) + .icons .icon-unchecked, +.radio input[type="radio"].custom-radio:hover:not(.nohover):not(:disabled) + .icons .icon-unchecked { + filter: alpha(opacity=0); + opacity: 0; + } +.checkbox input[type="checkbox"].custom-checkbox:hover:not(.nohover):not(:disabled) + .icons .icon-checked, +.radio input[type="checkbox"].custom-checkbox:hover:not(.nohover):not(:disabled) + .icons .icon-checked, +.checkbox input[type="radio"].custom-radio:hover:not(.nohover):not(:disabled) + .icons .icon-checked, +.radio input[type="radio"].custom-radio:hover:not(.nohover):not(:disabled) + .icons .icon-checked { + filter: none; + opacity: 1; + + -webkit-filter: none; + } +.checkbox input[type="checkbox"].custom-checkbox:checked + .icons, +.radio input[type="checkbox"].custom-checkbox:checked + .icons, +.checkbox input[type="radio"].custom-radio:checked + .icons, +.radio input[type="radio"].custom-radio:checked + .icons { + color: #1abc9c; + } +.checkbox input[type="checkbox"].custom-checkbox:checked + .icons .icon-unchecked, +.radio input[type="checkbox"].custom-checkbox:checked + .icons .icon-unchecked, +.checkbox input[type="radio"].custom-radio:checked + .icons .icon-unchecked, +.radio input[type="radio"].custom-radio:checked + .icons .icon-unchecked { + filter: alpha(opacity=0); + opacity: 0; + } +.checkbox input[type="checkbox"].custom-checkbox:checked + .icons .icon-checked, +.radio input[type="checkbox"].custom-checkbox:checked + .icons .icon-checked, +.checkbox input[type="radio"].custom-radio:checked + .icons .icon-checked, +.radio input[type="radio"].custom-radio:checked + .icons .icon-checked { + color: #1abc9c; + filter: none; + opacity: 1; + + -webkit-filter: none; + } +.checkbox input[type="checkbox"].custom-checkbox:disabled + .icons, +.radio input[type="checkbox"].custom-checkbox:disabled + .icons, +.checkbox input[type="radio"].custom-radio:disabled + .icons, +.radio input[type="radio"].custom-radio:disabled + .icons { + color: #e6e8ea; + cursor: default; + } +.checkbox input[type="checkbox"].custom-checkbox:disabled + .icons .icon-unchecked, +.radio input[type="checkbox"].custom-checkbox:disabled + .icons .icon-unchecked, +.checkbox input[type="radio"].custom-radio:disabled + .icons .icon-unchecked, +.radio input[type="radio"].custom-radio:disabled + .icons .icon-unchecked { + filter: none; + opacity: 1; + + -webkit-filter: none; + } +.checkbox input[type="checkbox"].custom-checkbox:disabled + .icons .icon-checked, +.radio input[type="checkbox"].custom-checkbox:disabled + .icons .icon-checked, +.checkbox input[type="radio"].custom-radio:disabled + .icons .icon-checked, +.radio input[type="radio"].custom-radio:disabled + .icons .icon-checked { + filter: alpha(opacity=0); + opacity: 0; + } +.checkbox input[type="checkbox"].custom-checkbox:disabled:checked + .icons, +.radio input[type="checkbox"].custom-checkbox:disabled:checked + .icons, +.checkbox input[type="radio"].custom-radio:disabled:checked + .icons, +.radio input[type="radio"].custom-radio:disabled:checked + .icons { + color: #e6e8ea; + } +.checkbox input[type="checkbox"].custom-checkbox:disabled:checked + .icons .icon-unchecked, +.radio input[type="checkbox"].custom-checkbox:disabled:checked + .icons .icon-unchecked, +.checkbox input[type="radio"].custom-radio:disabled:checked + .icons .icon-unchecked, +.radio input[type="radio"].custom-radio:disabled:checked + .icons .icon-unchecked { + filter: alpha(opacity=0); + opacity: 0; + } +.checkbox input[type="checkbox"].custom-checkbox:disabled:checked + .icons .icon-checked, +.radio input[type="checkbox"].custom-checkbox:disabled:checked + .icons .icon-checked, +.checkbox input[type="radio"].custom-radio:disabled:checked + .icons .icon-checked, +.radio input[type="radio"].custom-radio:disabled:checked + .icons .icon-checked { + color: #e6e8ea; + filter: none; + opacity: 1; + + -webkit-filter: none; + } +.checkbox input[type="checkbox"].custom-checkbox:indeterminate + .icons, +.radio input[type="checkbox"].custom-checkbox:indeterminate + .icons, +.checkbox input[type="radio"].custom-radio:indeterminate + .icons, +.radio input[type="radio"].custom-radio:indeterminate + .icons { + color: #bdc3c7; + } +.checkbox input[type="checkbox"].custom-checkbox:indeterminate + .icons .icon-unchecked, +.radio input[type="checkbox"].custom-checkbox:indeterminate + .icons .icon-unchecked, +.checkbox input[type="radio"].custom-radio:indeterminate + .icons .icon-unchecked, +.radio input[type="radio"].custom-radio:indeterminate + .icons .icon-unchecked { + filter: none; + opacity: 1; + + -webkit-filter: none; + } +.checkbox input[type="checkbox"].custom-checkbox:indeterminate + .icons .icon-checked, +.radio input[type="checkbox"].custom-checkbox:indeterminate + .icons .icon-checked, +.checkbox input[type="radio"].custom-radio:indeterminate + .icons .icon-checked, +.radio input[type="radio"].custom-radio:indeterminate + .icons .icon-checked { + filter: alpha(opacity=0); + opacity: 0; + } +.checkbox input[type="checkbox"].custom-checkbox:indeterminate + .icons:before, +.radio input[type="checkbox"].custom-checkbox:indeterminate + .icons:before, +.checkbox input[type="radio"].custom-radio:indeterminate + .icons:before, +.radio input[type="radio"].custom-radio:indeterminate + .icons:before { + position: absolute; + top: 0; + left: 0; + z-index: 10; + width: 20px; + font-size: 22px; + line-height: 20px; + color: #fff; + text-align: center; + content: "\2013"; + } +.checkbox.primary input[type="checkbox"].custom-checkbox + .icons, +.radio.primary input[type="checkbox"].custom-checkbox + .icons, +.checkbox.primary input[type="radio"].custom-radio + .icons, +.radio.primary input[type="radio"].custom-radio + .icons { + color: #34495e; + } +.checkbox.primary input[type="checkbox"].custom-checkbox:checked + .icons, +.radio.primary input[type="checkbox"].custom-checkbox:checked + .icons, +.checkbox.primary input[type="radio"].custom-radio:checked + .icons, +.radio.primary input[type="radio"].custom-radio:checked + .icons { + color: #1abc9c; + } +.checkbox.primary input[type="checkbox"].custom-checkbox:disabled + .icons, +.radio.primary input[type="checkbox"].custom-checkbox:disabled + .icons, +.checkbox.primary input[type="radio"].custom-radio:disabled + .icons, +.radio.primary input[type="radio"].custom-radio:disabled + .icons { + color: #bdc3c7; + cursor: default; + } +.checkbox.primary input[type="checkbox"].custom-checkbox:disabled + .icons.checked, +.radio.primary input[type="checkbox"].custom-checkbox:disabled + .icons.checked, +.checkbox.primary input[type="radio"].custom-radio:disabled + .icons.checked, +.radio.primary input[type="radio"].custom-radio:disabled + .icons.checked { + color: #bdc3c7; + } +.checkbox.primary input[type="checkbox"].custom-checkbox:indeterminate + .icons, +.radio.primary input[type="checkbox"].custom-checkbox:indeterminate + .icons, +.checkbox.primary input[type="radio"].custom-radio:indeterminate + .icons, +.radio.primary input[type="radio"].custom-radio:indeterminate + .icons { + color: #34495e; + } +.input-group-addon .radio, +.input-group-addon .checkbox { + padding-left: 20px; + margin: -2px 0; + } +.input-group-addon .radio .icons, +.input-group-addon .checkbox .icons { + color: #e6e8ea; + } +.input-group-addon .radio input[type="checkbox"].custom-checkbox:checked + .icons, +.input-group-addon .checkbox input[type="checkbox"].custom-checkbox:checked + .icons, +.input-group-addon .radio input[type="radio"].custom-radio:checked + .icons, +.input-group-addon .checkbox input[type="radio"].custom-radio:checked + .icons { + color: #fff; + } +.input-group-addon .radio input[type="checkbox"].custom-checkbox:checked + .icons .icon-checked, +.input-group-addon .checkbox input[type="checkbox"].custom-checkbox:checked + .icons .icon-checked, +.input-group-addon .radio input[type="radio"].custom-radio:checked + .icons .icon-checked, +.input-group-addon .checkbox input[type="radio"].custom-radio:checked + .icons .icon-checked { + color: #fff; + } +.input-group-addon .radio input[type="checkbox"].custom-checkbox:disabled + .icons, +.input-group-addon .checkbox input[type="checkbox"].custom-checkbox:disabled + .icons, +.input-group-addon .radio input[type="radio"].custom-radio:disabled + .icons, +.input-group-addon .checkbox input[type="radio"].custom-radio:disabled + .icons { + color: rgba(230, 232, 234, .6); + } +.input-group-addon .radio input[type="checkbox"].custom-checkbox:disabled:checked + .icons, +.input-group-addon .checkbox input[type="checkbox"].custom-checkbox:disabled:checked + .icons, +.input-group-addon .radio input[type="radio"].custom-radio:disabled:checked + .icons, +.input-group-addon .checkbox input[type="radio"].custom-radio:disabled:checked + .icons { + color: rgba(230, 232, 234, .6); + } +.input-group-addon .radio input[type="checkbox"].custom-checkbox:disabled:checked + .icons .icon-checked, +.input-group-addon .checkbox input[type="checkbox"].custom-checkbox:disabled:checked + .icons .icon-checked, +.input-group-addon .radio input[type="radio"].custom-radio:disabled:checked + .icons .icon-checked, +.input-group-addon .checkbox input[type="radio"].custom-radio:disabled:checked + .icons .icon-checked { + color: rgba(230, 232, 234, .6); + } +.radio + .radio, +.checkbox + .checkbox { + margin-top: 10px; + } +.form-inline .checkbox, +.form-inline .radio { + padding-left: 32px; + } +.bootstrap-tagsinput { + padding: 6px 1px 1px 6px; + margin-bottom: 18px; + font-size: 0; + text-align: left; + background-color: #fff; + border: 2px solid #ebedef; + border-radius: 6px; + } +.bootstrap-tagsinput .tag { + position: relative; + display: inline-block; + height: 27px; + padding: 6px 21px; + margin: 0 5px 5px 0; + overflow: hidden; + font-size: 13px; + line-height: 15px; + color: #7b8996; + vertical-align: middle; + cursor: pointer; + background-color: #ebedef; + border-radius: 4px; + -webkit-transition: .25s linear; + transition: .25s linear; + } +.bootstrap-tagsinput .tag > span { + position: absolute; + top: 0; + right: 0; + bottom: 0; + z-index: 2; + width: 100%; + padding: 0 10px 0 0; + font-size: 12px; + color: #fff; + text-align: right; + text-decoration: none; + cursor: pointer; + filter: alpha(opacity=0); + opacity: 0; + -webkit-transition: opacity .25s linear; + transition: opacity .25s linear; + } +.bootstrap-tagsinput .tag > span:after { + font-family: "Flat-UI-Icons"; + line-height: 27px; + content: "\e609"; + + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + } +.bootstrap-tagsinput .tag:hover { + padding-right: 28px; + padding-left: 14px; + color: #fff; + background-color: #16a085; + } +.bootstrap-tagsinput .tag:hover > span { + filter: none; + opacity: 1; + + -webkit-filter: none; + } +.bootstrap-tagsinput input[type="text"] { + width: auto !important; + min-width: 80px; + max-width: inherit; + height: 29px; + padding: 0; + margin: 0; + font-size: 14px; + color: #34495e; + vertical-align: top; + background-color: transparent; + border: none; + outline: none; + box-shadow: none; + } +.bootstrap-tagsinput input[type="text"]:first-child { + height: 23px; + margin: 3px 0 8px; + } +.tags_clear { + width: 100%; + height: 0; + clear: both; + } +.not_valid { + margin-left: 5px !important; + color: #90111a !important; + background: #fbd8db !important; + } +.tagsinput-primary { + margin-bottom: 18px; + } +.tagsinput-primary .bootstrap-tagsinput { + margin-bottom: 0; + border-color: #1abc9c; + } +.tagsinput-primary .tag { + color: #fff; + background-color: #1abc9c; + } +.tagsinput-primary .tag:hover { + color: #fff; + background-color: #16a085; + } +.bootstrap-tagsinput .twitter-typeahead { + width: auto; + vertical-align: top; + } +.bootstrap-tagsinput .twitter-typeahead .tt-input { + min-width: 200px; + } +.bootstrap-tagsinput .twitter-typeahead .tt-dropdown-menu { + width: auto; + min-width: 120px; + margin-top: 11px; + } +.twitter-typeahead { + width: 100%; + } +.twitter-typeahead .tt-dropdown-menu { + width: 100%; + padding: 5px 0; + margin-top: 5px; + background-color: #fff; + border: 2px solid #1abc9c; + border-radius: 6px; + } +.twitter-typeahead .tt-suggestion p { + padding: 6px 14px; + margin: 0; + font-size: 14px; + line-height: 1.429; + } +.twitter-typeahead .tt-suggestion:first-child p, +.twitter-typeahead .tt-suggestion:last-child p { + padding: 6px 14px; + } +.twitter-typeahead .tt-suggestion.tt-is-under-cursor, +.twitter-typeahead .tt-suggestion.tt-cursor { + color: #fff; + cursor: pointer; + background-color: #16a085; + } +.progress { + height: 12px; + background: #ebedef; + border-radius: 32px; + box-shadow: none; + } +.progress-bar { + line-height: 12px; + background: #1abc9c; + box-shadow: none; + } +.progress-bar-success { + background-color: #2ecc71; + } +.progress-bar-warning { + background-color: #f1c40f; + } +.progress-bar-danger { + background-color: #e74c3c; + } +.progress-bar-info { + background-color: #3498db; + } +/*.ui-slider {*/ + /*position: relative;*/ + /*height: 12px;*/ + /*margin-bottom: 20px;*/ + /*cursor: pointer;*/ + /*background: #ebedef;*/ + /*border-radius: 32px;*/ + /*box-shadow: none;*/ + /*}*/ +/*.ui-slider-handle {*/ + /*position: absolute;*/ + /*z-index: 2;*/ + /*width: 18px;*/ + /*height: 18px;*/ + /*cursor: pointer;*/ + /*background-color: #16a085;*/ + /*border-radius: 50%;*/ + /*-webkit-transition: background .25s;*/ + /*transition: background .25s;*/ + /*}*/ +/*.ui-slider-handle:hover,*/ +/*.ui-slider-handle:focus {*/ + /*background-color: #48c9b0;*/ + /*outline: none;*/ + /*}*/ +/*.ui-slider-handle:active {*/ + /*background-color: #16a085;*/ + /*}*/ +/*.ui-slider-range {*/ + /*position: absolute;*/ + /*z-index: 1;*/ + /*display: block;*/ + /*height: 100%;*/ + /*background-color: #1abc9c;*/ + /*}*/ +/*.ui-slider-segment {*/ + /*width: 6px;*/ + /*height: 6px;*/ + /*background-color: #d9dbdd;*/ + /*border-radius: 50%;*/ + /*}*/ +/*.ui-slider-value {*/ + /*float: right;*/ + /*margin-top: 12px;*/ + /*font-size: 13px;*/ + /*}*/ +/*.ui-slider-value.first {*/ + /*float: left;*/ + /*clear: left;*/ + /*}*/ +/*.ui-slider-horizontal .ui-slider-handle {*/ + /*top: -3px;*/ + /*margin-left: -9px;*/ + /*}*/ +/*.ui-slider-horizontal .ui-slider-handle[style*="100"] {*/ + /*margin-left: -15px;*/ + /*}*/ +/*.ui-slider-horizontal .ui-slider-range {*/ + /*border-radius: 30px 0 0 30px;*/ + /*}*/ +/*.ui-slider-horizontal .ui-slider-segment {*/ + /*float: left;*/ + /*margin: 3px -6px 0 0;*/ + /*}*/ +/*.ui-slider-vertical {*/ + /*width: 12px;*/ + /*}*/ +/*.ui-slider-vertical .ui-slider-handle {*/ + /*top: auto;*/ + /*margin-bottom: -11px;*/ + /*margin-left: -3px;*/ + /*}*/ +/*.ui-slider-vertical .ui-slider-range {*/ + /*bottom: 0;*/ + /*width: 100%;*/ + /*border-radius: 0 0 30px 30px;*/ + /*}*/ +/*.ui-slider-vertical .ui-slider-segment {*/ + /*position: absolute;*/ + /*right: 3px;*/ + /*}*/ +.pager { + display: inline-block; + font-size: 16px; + font-weight: 700; + color: #fff; + background-color: #34495e; + border-radius: 6px; + } +.pager li:first-child > a, +.pager li:first-child > span { + border-left: none; + border-radius: 6px 0 0 6px; + } +.pager li > a, +.pager li > span { + padding: 9px 15px 10px; + line-height: 1.313; + color: #fff; + text-decoration: none; + white-space: nowrap; + background: none; + border: none; + border-left: 2px solid #2c3e50; + border-radius: 0 6px 6px 0; + } +.pager li > a:hover, +.pager li > span:hover, +.pager li > a:focus, +.pager li > span:focus { + background-color: #2c3e50; + } +.pager li > a:active, +.pager li > span:active { + background-color: #2c3e50; + } +.pager li > a [class*="fui-"] + span, +.pager li > span [class*="fui-"] + span { + margin-left: 8px; + } +.pager li > a span + [class*="fui-"], +.pager li > span span + [class*="fui-"] { + margin-left: 8px; + } +.pagination { + position: relative; + display: block; + } +@media (min-width: 768px) { + .pagination { + display: inline-block; + } + } +.pagination > ul { + display: inline-block; + padding: 0; + margin: 0; + color: #fff; + word-spacing: -.5px; + background: #d6dbdf; + border-radius: 6px; + } +@media (max-width: 767px) { + .pagination > ul { + height: 41px; + padding: 0 55px 0 52px; + overflow: auto; + white-space: nowrap; + border-radius: 6px; + } + } +.pagination li { + display: inline-block; + margin-right: -3px; + word-spacing: normal; + vertical-align: middle; + } +.pagination li.active > a, +.pagination li.active > span { + color: #fff; + background-color: #1abc9c; + border-color: #dfe2e5; + } +.pagination li.active.previous > a, +.pagination li.active.next > a, +.pagination li.active.previous > span, +.pagination li.active.next > span { + margin: 0; + } +.pagination li.active.previous > a, +.pagination li.active.next > a, +.pagination li.active.previous > span, +.pagination li.active.next > span, +.pagination li.active.previous > a:hover, +.pagination li.active.next > a:hover, +.pagination li.active.previous > span:hover, +.pagination li.active.next > span:hover, +.pagination li.active.previous > a:focus, +.pagination li.active.next > a:focus, +.pagination li.active.previous > span:focus, +.pagination li.active.next > span:focus { + color: #fff; + background-color: #1abc9c; + } +.pagination li:first-child > a, +.pagination li:first-child > span { + border-left: none; + border-radius: 6px 0 0 6px; + } +.pagination li:first-child.previous + li > a, +.pagination li:first-child.previous + li > span { + border-left-width: 0; + } +.pagination li:last-child { + margin-right: 0; + } +.pagination li:last-child > a, +.pagination li:last-child > span, +.pagination li:last-child > a:hover, +.pagination li:last-child > span:hover, +.pagination li:last-child > a:focus, +.pagination li:last-child > span:focus { + border-radius: 0 6px 6px 0; + } +.pagination li.previous > a, +.pagination li.next > a, +.pagination li.previous > span, +.pagination li.next > span { + min-width: auto; + padding: 12px 17px; + font-size: 16px; + background-color: transparent; + border-right: 2px solid #e4e7ea; + } +.pagination li.next > a, +.pagination li.next > span { + border-right: none; + } +.pagination li.disabled > a, +.pagination li.disabled > span { + color: #fff; + cursor: not-allowed; + background-color: rgba(255, 255, 255, .3); + border-right-color: #dfe2e5; + } +.pagination li.disabled > a:hover, +.pagination li.disabled > span:hover, +.pagination li.disabled > a:focus, +.pagination li.disabled > span:focus, +.pagination li.disabled > a:active, +.pagination li.disabled > span:active { + color: #fff; + background-color: rgba(255, 255, 255, .4); + } +@media (max-width: 767px) { + .pagination li.next, + .pagination li.previous { + position: absolute; + top: 0; + right: 0; + z-index: 10; + background-color: #d6dbdf; + border-radius: 0 6px 6px 0; + } + .pagination li.previous { + right: auto; + left: 0; + border-radius: 6px 0 0 6px; + } + } +.pagination li > a, +.pagination li > span { + display: inline-block; + min-width: 41px; + min-height: 41px; + padding: 12px 10px; + font-size: 14px; + line-height: 16px; + color: #fff; + text-align: center; + background: transparent; + border: none; + border-left: 2px solid #e4e7ea; + outline: none; + -webkit-transition: .25s ease-out; + transition: .25s ease-out; + } +.pagination li > a:hover, +.pagination li > span:hover, +.pagination li > a:focus, +.pagination li > span:focus { + color: #fff; + background-color: #1abc9c; + } +.pagination li > a:active, +.pagination li > span:active { + color: #fff; + background-color: #1abc9c; + } +.pagination > .btn.previous, +.pagination > .btn.next { + padding-right: 23px; + padding-left: 23px; + margin-right: 8px; + font-size: 14px; + line-height: 1.429; + } +.pagination > .btn.previous [class*="fui-"], +.pagination > .btn.next [class*="fui-"] { + margin-top: -2px; + margin-left: -2px; + font-size: 16px; + } +.pagination > .btn.next { + margin-right: 0; + margin-left: 8px; + } +.pagination > .btn.next [class*="fui-"] { + margin-right: -2px; + margin-left: 4px; + } +@media (max-width: 767px) { + .pagination > .btn { + display: block; + width: 50%; + margin: 0; + } + .pagination > .btn:first-child { + border-bottom: 2px solid #dfe2e5; + border-radius: 6px 0 0; + } + .pagination > .btn:first-child.btn-primary { + border-bottom-color: #48c9b0; + } + .pagination > .btn:first-child.btn-danger { + border-bottom-color: #ec7063; + } + .pagination > .btn:first-child.btn-warning { + border-bottom-color: #f4d03f; + } + .pagination > .btn:first-child.btn-success { + border-bottom-color: #58d68d; + } + .pagination > .btn:first-child.btn-info { + border-bottom-color: #5dade2; + } + .pagination > .btn:first-child.btn-inverse { + border-bottom-color: #5d6d7e; + } + .pagination > .btn:first-child > [class*="fui"] { + margin-left: -20px; + } + .pagination > .btn + ul { + padding: 0; + text-align: center; + border-radius: 0 0 6px 6px; + } + .pagination > .btn + ul + .btn { + position: absolute; + top: 0; + right: 0; + border-bottom: 2px solid #dfe2e5; + border-radius: 0 6px 0 0; + } + .pagination > .btn + ul + .btn.btn-primary { + border-bottom-color: #48c9b0; + } + .pagination > .btn + ul + .btn.btn-danger { + border-bottom-color: #ec7063; + } + .pagination > .btn + ul + .btn.btn-warning { + border-bottom-color: #f4d03f; + } + .pagination > .btn + ul + .btn.btn-success { + border-bottom-color: #58d68d; + } + .pagination > .btn + ul + .btn.btn-info { + border-bottom-color: #5dade2; + } + .pagination > .btn + ul + .btn.btn-inverse { + border-bottom-color: #5d6d7e; + } + .pagination > .btn + ul + .btn > [class*="fui"] { + margin-right: -20px; + } + .pagination ul { + display: block; + } + .pagination ul > li > a { + border-radius: 0; + } + } +.pagination-danger ul { + background-color: #e74c3c; + } +.pagination-danger ul li.previous > a { + border-right-color: #ef897e; + } +.pagination-danger ul li > a, +.pagination-danger ul li > span { + border-left-color: #ef897e; + } +.pagination-danger ul li > a:hover, +.pagination-danger ul li > span:hover, +.pagination-danger ul li > a:focus, +.pagination-danger ul li > span:focus { + background-color: #ec7063; + } +.pagination-danger ul li > a:active, +.pagination-danger ul li > span:active { + background-color: #c44133; + } +.pagination-danger ul li.active > a, +.pagination-danger ul li.active > span { + background-color: #c44133; + } +.pagination-success ul { + background-color: #2ecc71; + } +.pagination-success ul li.previous > a { + border-right-color: #75dda1; + } +.pagination-success ul li > a, +.pagination-success ul li > span { + border-left-color: #75dda1; + } +.pagination-success ul li > a:hover, +.pagination-success ul li > span:hover, +.pagination-success ul li > a:focus, +.pagination-success ul li > span:focus { + background-color: #58d68d; + } +.pagination-success ul li > a:active, +.pagination-success ul li > span:active { + background-color: #27ad60; + } +.pagination-success ul li.active > a, +.pagination-success ul li.active > span { + background-color: #27ad60; + } +.pagination-warning ul { + background-color: #f1c40f; + } +.pagination-warning ul li.previous > a { + border-right-color: #f6d861; + } +.pagination-warning ul li > a, +.pagination-warning ul li > span { + border-left-color: #f6d861; + } +.pagination-warning ul li > a:hover, +.pagination-warning ul li > span:hover, +.pagination-warning ul li > a:focus, +.pagination-warning ul li > span:focus { + background-color: #f4d313; + } +.pagination-warning ul li > a:active, +.pagination-warning ul li > span:active { + background-color: #cda70d; + } +.pagination-warning ul li.active > a, +.pagination-warning ul li.active > span { + background-color: #cda70d; + } +.pagination-info ul { + background-color: #3498db; + } +.pagination-info ul li.previous > a { + border-right-color: #79bbe7; + } +.pagination-info ul li > a, +.pagination-info ul li > span { + border-left-color: #79bbe7; + } +.pagination-info ul li > a:hover, +.pagination-info ul li > span:hover, +.pagination-info ul li > a:focus, +.pagination-info ul li > span:focus { + background-color: #5dade2; + } +.pagination-info ul li > a:active, +.pagination-info ul li > span:active { + background-color: #2c81ba; + } +.pagination-info ul li.active > a, +.pagination-info ul li.active > span { + background-color: #2c81ba; + } +.pagination-inverse ul { + background-color: #34495e; + } +.pagination-inverse ul li.previous > a { + border-right-color: #798795; + } +.pagination-inverse ul li > a, +.pagination-inverse ul li > span { + border-left-color: #798795; + } +.pagination-inverse ul li > a:hover, +.pagination-inverse ul li > span:hover, +.pagination-inverse ul li > a:focus, +.pagination-inverse ul li > span:focus { + background-color: #415b76; + } +.pagination-inverse ul li > a:active, +.pagination-inverse ul li > span:active { + background-color: #2c3e50; + } +.pagination-inverse ul li.active > a, +.pagination-inverse ul li.active > span { + background-color: #2c3e50; + } +.pagination-minimal > ul > li:first-child { + border-radius: 6px 0 0 6px; + } +.pagination-minimal > ul > li:first-child.previous + li > a, +.pagination-minimal > ul > li:first-child.previous + li > span { + border-left-width: 5px; + } +.pagination-minimal > ul > li:last-child { + border-radius: 0 6px 6px 0; + } +.pagination-minimal > ul > li.previous > a, +.pagination-minimal > ul > li.next > a, +.pagination-minimal > ul > li.previous > span, +.pagination-minimal > ul > li.next > span { + padding: 12px 17px; + margin: 0 9px 0 0; + background: transparent; + border: none; + border-right: 2px solid #e4e7ea; + border-radius: 6px 0 0 6px; + } +.pagination-minimal > ul > li.previous > a, +.pagination-minimal > ul > li.next > a, +.pagination-minimal > ul > li.previous > span, +.pagination-minimal > ul > li.next > span, +.pagination-minimal > ul > li.previous > a:hover, +.pagination-minimal > ul > li.next > a:hover, +.pagination-minimal > ul > li.previous > span:hover, +.pagination-minimal > ul > li.next > span:hover, +.pagination-minimal > ul > li.previous > a:focus, +.pagination-minimal > ul > li.next > a:focus, +.pagination-minimal > ul > li.previous > span:focus, +.pagination-minimal > ul > li.next > span:focus { + border-color: #e4e7ea !important; + } +@media (max-width: 767px) { + .pagination-minimal > ul > li.previous > a, + .pagination-minimal > ul > li.next > a, + .pagination-minimal > ul > li.previous > span, + .pagination-minimal > ul > li.next > span { + margin-right: 0; + } + } +.pagination-minimal > ul > li.next { + margin-left: 9px; + } +.pagination-minimal > ul > li.next > a, +.pagination-minimal > ul > li.next > span { + margin: 0; + border-right: none; + border-left: 2px solid #e4e7ea; + border-radius: 0 6px 6px 0; + } +.pagination-minimal > ul > li.active > a, +.pagination-minimal > ul > li.active > span { + margin: 10px 5px 9px; + color: #d6dbdf; + background-color: #fff; + border-color: #fff; + border-width: 2px !important; + } +.pagination-minimal > ul > li.active > a:hover, +.pagination-minimal > ul > li.active > span:hover, +.pagination-minimal > ul > li.active > a:focus, +.pagination-minimal > ul > li.active > span:focus { + color: #d6dbdf; + background-color: #fff; + border-color: #fff; + } +.pagination-minimal > ul > li.active.previous, +.pagination-minimal > ul > li.active.next { + border-color: #e4e7ea; + } +.pagination-minimal > ul > li.active.previous { + margin-right: 6px; + } +.pagination-minimal > ul > li > a, +.pagination-minimal > ul > li > span { + min-width: 0; + min-height: 16px; + padding: 0 4px; + margin: 7px 2px 6px; + line-height: 16px; + color: #fff; + background: #fff; + background-clip: padding-box; + border: 5px solid #d6dbdf; + border-radius: 50px; + -webkit-transition: background .2s ease-out, border-color 0s ease-out, color .2s ease-out; + transition: background .2s ease-out, border-color 0s ease-out, color .2s ease-out; + } +.pagination-minimal > ul > li > a:hover, +.pagination-minimal > ul > li > span:hover, +.pagination-minimal > ul > li > a:focus, +.pagination-minimal > ul > li > span:focus { + color: #fff; + background-color: #1abc9c; + border-color: #1abc9c; + -webkit-transition: background .2s ease-out, border-color .2s ease-out, color .2s ease-out; + transition: background .2s ease-out, border-color .2s ease-out, color .2s ease-out; + } +.pagination-minimal > ul > li > a:active, +.pagination-minimal > ul > li > span:active { + background-color: #16a085; + border-color: #16a085; + } +.pagination-plain { + height: 57px; + padding: 0; + margin: 0 0 20px; + font-size: 16px; + font-weight: 700; + list-style-type: none; + } +.pagination-plain > li { + display: inline; + } +.pagination-plain > li.previous { + padding-right: 23px; + } +.pagination-plain > li.next { + padding-left: 20px; + } +.pagination-plain > li.active > a { + color: #d3d7da; + } +.pagination-plain > li > a { + padding: 0 5px; + } +@media (max-width: 480px) { + .pagination-plain { + overflow: hidden; + text-align: center; + } + .pagination-plain > li.previous { + display: block; + width: 50%; + margin-bottom: 10px; + text-align: left; + } + .pagination-plain > li.next { + float: right; + width: 50%; + margin-top: -64px; + text-align: right; + } + } +@media (min-width: 768px) { + .pagination-plain { + height: auto; + } + } +.pagination-dropdown ul { + left: 50%; + width: auto; + min-width: 67px; + margin-left: -34px; + } +.pagination-dropdown ul li { + display: block; + margin-right: 0; + } +.pagination-dropdown ul li:first-child > a, +.pagination-dropdown ul li:first-child > span { + border-radius: 6px 6px 0 0; + } +.pagination-dropdown ul li:last-child > a, +.pagination-dropdown ul li:last-child > span { + border-radius: 0 0 6px 6px !important; + } +.pagination-dropdown ul li > a, +.pagination-dropdown ul li > span { + display: block; + float: none; + min-height: 0; + padding: 8px 10px 7px; + text-align: center; + border-left: none; + } +.pagination-dropdown.dropup { + position: relative; + } +/*.tooltip {*/ + /*z-index: 1070;*/ + /*font-size: 14px;*/ + /*line-height: 1.286;*/ + /*}*/ +/*.tooltip.in {*/ + /*filter: alpha(opacity=100);*/ + /*opacity: 1;*/ + /*}*/ +/*.tooltip.top {*/ + /*padding: 9px 0;*/ + /*margin-top: -5px;*/ + /*}*/ +/*.tooltip.right {*/ + /*padding: 0 9px;*/ + /*margin-left: 5px;*/ + /*}*/ +/*.tooltip.bottom {*/ + /*padding: 9px 0;*/ + /*margin-top: 5px;*/ + /*}*/ +/*.tooltip.left {*/ + /*padding: 0 9px;*/ + /*margin-left: -5px;*/ + /*}*/ +/*.tooltip-inner {*/ + /*max-width: 183px;*/ + /*padding: 12px 12px;*/ + /*line-height: 1.286;*/ + /*color: #fff;*/ + /*background-color: #34495e;*/ + /*border-radius: 6px;*/ + /*}*/ +/*.tooltip.top .tooltip-arrow {*/ + /*margin-left: -9px;*/ + /*border-width: 9px 9px 0;*/ + /*border-top-color: #34495e;*/ + /*}*/ +/*.tooltip.right .tooltip-arrow {*/ + /*margin-top: -9px;*/ + /*border-width: 9px 9px 9px 0;*/ + /*border-right-color: #34495e;*/ + /*}*/ +/*.tooltip.left .tooltip-arrow {*/ + /*margin-top: -9px;*/ + /*border-width: 9px 0 9px 9px;*/ + /*border-left-color: #34495e;*/ + /*}*/ +/*.tooltip.bottom .tooltip-arrow {*/ + /*margin-left: -9px;*/ + /*border-width: 0 9px 9px;*/ + /*border-bottom-color: #34495e;*/ + /*}*/ +.caret { + display: inline-block; + width: 0; + height: 0; + margin-left: 5px; + vertical-align: middle; + border-top: 8px solid; + border-right: 6px solid transparent; + border-left: 6px solid transparent; + -webkit-transition: border-color .25s, color .25s; + transition: border-color .25s, color .25s; + } +.dropdown-menu, +.select2-drop { + z-index: 1000; + min-width: 220px; + padding: 0; + margin-top: 9px; + font-size: 14px; + background-color: #f3f4f5; + border: none; + border-radius: 4px; + box-shadow: none; + } +.dropdown-menu .divider { + height: 2px; + margin: 3px 0; + overflow: hidden; + background-color: rgba(202, 206, 209, .5); + } +.dropdown-menu > li > a { + padding: 8px 16px; + line-height: 1.429; + color: #606d7a; + } +.dropdown-menu > li:first-child > a:first-child { + border-top-left-radius: 4px; + border-top-right-radius: 4px; + } +.dropdown-menu > li:last-child > a:first-child { + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; + } +.dropdown-menu.typeahead { + display: none; + width: auto; + padding: 5px 0; + margin-top: 5px; + background-color: #fff; + border: 2px solid #1abc9c; + border-radius: 6px; + } +.dropdown-menu.typeahead li a { + padding: 6px 14px; + } +.dropdown-menu.typeahead li:first-child a, +.dropdown-menu.typeahead li:last-child a { + padding: 6px 14px; + border-radius: 0; + } +.dropdown-menu > li > a:hover, +.dropdown-menu > li > a:focus { + color: #55606c; + background-color: rgba(202, 206, 209, .5); + } +.dropdown-menu > .active > a, +.dropdown-menu > .active > a:hover, +.dropdown-menu > .active > a:focus { + color: #fff; + background-color: #1abc9c; + } +.dropdown-menu > .disabled > a, +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + color: #bdc3c7; + cursor: not-allowed; + background-color: transparent; + } +.dropdown-menu-right { + right: 0; + left: auto; + } +.dropdown-menu-left { + right: auto; + left: 0; + } +.dropdown-header { + padding: 8px 16px; + font-size: 13px; + line-height: 1.538; + color: rgba(52, 73, 94, .6); + text-transform: uppercase; + } +.dropdown-header:first-child { + margin-top: 3px; + } +.dropdown-backdrop { + z-index: 990; + } +.dropup .caret, +.navbar-fixed-bottom .dropdown .caret { + margin-bottom: .25em; + border-bottom: 8px solid; + } +.dropup .dropdown-menu, +.navbar-fixed-bottom .dropdown .dropdown-menu { + margin-top: 0; + margin-bottom: 9px; + } +.dropdown-menu-inverse { + background-color: #34495e; + } +.dropdown-menu-inverse .divider { + height: 2px; + margin: 3px 0; + overflow: hidden; + background-color: rgba(43, 60, 78, .5); + } +.dropdown-menu-inverse > li > a { + color: rgba(255, 255, 255, .85); + } +.dropdown-menu-inverse > li > a:hover, +.dropdown-menu-inverse > li > a:focus { + color: rgba(255, 255, 255, .85); + background-color: rgba(43, 60, 78, .5); + } +.dropdown-menu-inverse > .active > a, +.dropdown-menu-inverse > .active > a:hover, +.dropdown-menu-inverse > .active > a:focus { + color: rgba(255, 255, 255, .85); + background-color: #1abc9c; + } +.dropdown-menu-inverse > .disabled > a, +.dropdown-menu-inverse > .disabled > a:hover, +.dropdown-menu-inverse > .disabled > a:focus { + color: rgba(255, 255, 255, .5); + } +.dropdown-menu-inverse > .disabled > a:hover, +.dropdown-menu-inverse > .disabled > a:focus { + background-color: transparent; + } +.dropdown-menu-inverse .dropdown-header { + color: rgba(255, 255, 255, .4); + } +@media (min-width: 768px) { + .navbar-right .dropdown-menu { + right: 0; + left: auto; + } + .navbar-right .dropdown-menu-left { + right: auto; + left: 0; + } + } +.select { + position: relative; + display: inline-block; + width: auto; + min-width: 220px; + vertical-align: top; + } +.form-group .select { + width: 100%; + } +.form-group .select > .select2-choice { + width: 100%; + } +.select.form-control, +.select.select2-search input[type="text"] { + height: auto; + padding: 0; + border: none; + } +.select2-choice { + position: relative; + display: inline-block; + width: 100%; + padding: 10px 39px 10px 15px; + font-size: 15px; + font-weight: normal; + line-height: 1.4; + border: none; + border-radius: 4px; + -webkit-transition: border .25s linear, color .25s linear, background-color .25s linear; + transition: border .25s linear, color .25s linear, background-color .25s linear; + } +.select2-choice:hover, +.select2-choice:focus { + outline: none; + } +.select2-choice:active { + outline: none; + box-shadow: none; + } +.select2-container-disabled .select2-choice { + filter: alpha(opacity=70); + opacity: .7; + } +.select2-chosen { + overflow: hidden; + text-align: left; + } +.select2-arrow { + position: absolute; + top: 42%; + right: 16px; + display: inline-block; + border-color: #34495e transparent; + border-style: solid; + border-width: 8px 6px; + border-bottom-style: none; + -webkit-transform: scale(1.001); + -ms-transform: scale(1.001); + transform: scale(1.001); + } +.select2-arrow b { + display: none; + } +.btn-lg .select2-arrow { + border-top-width: 8px; + border-right-width: 6px; + border-left-width: 6px; + } +.select-default .select2-choice { + color: #fff; + background-color: #bdc3c7; + } +.select-default .select2-choice:hover, +.select-default .select2-choice.hover, +.select-default .select2-choice:focus, +.select-default .select2-choice:active { + color: #fff; + background-color: #cacfd2; + border-color: #cacfd2; + } +.select-default .select2-choice:active { + background: #a1a6a9; + border-color: #a1a6a9; + } +.select2-container-disabled.select-default .select2-choice, +.select2-container-disabled.select-default .select2-choice:hover, +.select2-container-disabled.select-default .select2-choice:focus, +.select2-container-disabled.select-default .select2-choice:active { + background-color: #bdc3c7; + border-color: #bdc3c7; + } +.select-default .select2-choice .select2-arrow { + border-top-color: #fff; + } +.select-primary .select2-choice { + color: #fff; + background-color: #1abc9c; + } +.select-primary .select2-choice:hover, +.select-primary .select2-choice.hover, +.select-primary .select2-choice:focus, +.select-primary .select2-choice:active { + color: #fff; + background-color: #48c9b0; + border-color: #48c9b0; + } +.select-primary .select2-choice:active { + background: #16a085; + border-color: #16a085; + } +.select2-container-disabled.select-primary .select2-choice, +.select2-container-disabled.select-primary .select2-choice:hover, +.select2-container-disabled.select-primary .select2-choice:focus, +.select2-container-disabled.select-primary .select2-choice:active { + background-color: #bdc3c7; + border-color: #1abc9c; + } +.select-primary .select2-choice .select2-arrow { + border-top-color: #fff; + } +.select-info .select2-choice { + color: #fff; + background-color: #3498db; + } +.select-info .select2-choice:hover, +.select-info .select2-choice.hover, +.select-info .select2-choice:focus, +.select-info .select2-choice:active { + color: #fff; + background-color: #5dade2; + border-color: #5dade2; + } +.select-info .select2-choice:active { + background: #2c81ba; + border-color: #2c81ba; + } +.select2-container-disabled.select-info .select2-choice, +.select2-container-disabled.select-info .select2-choice:hover, +.select2-container-disabled.select-info .select2-choice:focus, +.select2-container-disabled.select-info .select2-choice:active { + background-color: #bdc3c7; + border-color: #3498db; + } +.select-info .select2-choice .select2-arrow { + border-top-color: #fff; + } +.select-danger .select2-choice { + color: #fff; + background-color: #e74c3c; + } +.select-danger .select2-choice:hover, +.select-danger .select2-choice.hover, +.select-danger .select2-choice:focus, +.select-danger .select2-choice:active { + color: #fff; + background-color: #ec7063; + border-color: #ec7063; + } +.select-danger .select2-choice:active { + background: #c44133; + border-color: #c44133; + } +.select2-container-disabled.select-danger .select2-choice, +.select2-container-disabled.select-danger .select2-choice:hover, +.select2-container-disabled.select-danger .select2-choice:focus, +.select2-container-disabled.select-danger .select2-choice:active { + background-color: #bdc3c7; + border-color: #e74c3c; + } +.select-danger .select2-choice .select2-arrow { + border-top-color: #fff; + } +.select-success .select2-choice { + color: #fff; + background-color: #2ecc71; + } +.select-success .select2-choice:hover, +.select-success .select2-choice.hover, +.select-success .select2-choice:focus, +.select-success .select2-choice:active { + color: #fff; + background-color: #58d68d; + border-color: #58d68d; + } +.select-success .select2-choice:active { + background: #27ad60; + border-color: #27ad60; + } +.select2-container-disabled.select-success .select2-choice, +.select2-container-disabled.select-success .select2-choice:hover, +.select2-container-disabled.select-success .select2-choice:focus, +.select2-container-disabled.select-success .select2-choice:active { + background-color: #bdc3c7; + border-color: #2ecc71; + } +.select-success .select2-choice .select2-arrow { + border-top-color: #fff; + } +.select-warning .select2-choice { + color: #fff; + background-color: #f1c40f; + } +.select-warning .select2-choice:hover, +.select-warning .select2-choice.hover, +.select-warning .select2-choice:focus, +.select-warning .select2-choice:active { + color: #fff; + background-color: #f4d313; + border-color: #f4d313; + } +.select-warning .select2-choice:active { + background: #cda70d; + border-color: #cda70d; + } +.select2-container-disabled.select-warning .select2-choice, +.select2-container-disabled.select-warning .select2-choice:hover, +.select2-container-disabled.select-warning .select2-choice:focus, +.select2-container-disabled.select-warning .select2-choice:active { + background-color: #bdc3c7; + border-color: #f1c40f; + } +.select-warning .select2-choice .select2-arrow { + border-top-color: #fff; + } +.select-inverse .select2-choice { + color: #fff; + background-color: #34495e; + } +.select-inverse .select2-choice:hover, +.select-inverse .select2-choice.hover, +.select-inverse .select2-choice:focus, +.select-inverse .select2-choice:active { + color: #fff; + background-color: #415b76; + border-color: #415b76; + } +.select-inverse .select2-choice:active { + background: #2c3e50; + border-color: #2c3e50; + } +.select2-container-disabled.select-inverse .select2-choice, +.select2-container-disabled.select-inverse .select2-choice:hover, +.select2-container-disabled.select-inverse .select2-choice:focus, +.select2-container-disabled.select-inverse .select2-choice:active { + background-color: #bdc3c7; + border-color: #34495e; + } +.select-inverse .select2-choice .select2-arrow { + border-top-color: #fff; + } +.select2-container.select-hg > .select2-choice { + min-height: 53px; + padding: 13px 20px; + padding-right: 49px; + font-size: 22px; + line-height: 1.227; + border-radius: 6px; + } +.select2-container.select-hg > .select2-choice .filter-option { + top: 13px; + right: 40px; + left: 20px; + } +.select2-container.select-hg > .select2-choice .select2-arrow { + right: 20px; + } +.select2-container.select-hg > .select2-choice > [class^="fui-"] { + top: 2px; + } +.select2-container.select-lg > .select2-choice { + min-height: 45px; + padding: 10px 19px; + padding-right: 47px; + font-size: 17px; + line-height: 1.471; + border-radius: 6px; + } +.select2-container.select-lg > .select2-choice .filter-option { + right: 38px; + left: 18px; + } +.select2-container.select-sm > .select2-choice { + min-height: 36px; + padding: 9px 13px; + padding-right: 35px; + font-size: 13px; + line-height: 1.385; + border-radius: 4px; + } +.select2-container.select-sm > .select2-choice .filter-option { + right: 33px; + left: 13px; + } +.select2-container.select-sm > .select2-choice .select2-arrow { + right: 13px; + } +.multiselect { + position: relative; + display: inline-block; + width: auto; + min-width: 220px; + max-width: none; + font-size: 0; + text-align: left; + vertical-align: top; + background-color: #fff; + border-radius: 6px; + } +.form-group .multiselect { + width: 100%; + } +.form-group .multiselect > .select2-choice { + width: 100%; + } +.multiselect.form-control, +.multiselect.select2-search input[type="text"] { + height: auto; + padding: 6px 1px 1px 6px; + border: 2px solid #ebedef; + } +.select2-choices { + position: relative; + min-height: 26px; + padding: 0; + margin: 0; + overflow: hidden; + cursor: text; + } +.select2-choices li { + float: left; + list-style: none; + } +.select2-search-choice { + position: relative; + display: inline-block; + height: 27px; + padding: 6px 21px; + margin: 0 5px 4px 0; + overflow: hidden; + font-size: 13px; + line-height: 15px; + color: #fff; + vertical-align: middle; + cursor: pointer; + border-radius: 4px; + -webkit-transition: .25s linear; + transition: .25s linear; + } +.select2-search-choice:hover { + padding-right: 28px; + padding-left: 14px; + color: #fff; + } +.select2-search-choice:hover .select2-search-choice-close { + color: inherit; + filter: none; + opacity: 1; + + -webkit-filter: none; + } +.select2-search-choice .select2-search-choice-close { + position: absolute; + top: 0; + right: 0; + bottom: 0; + z-index: 2; + width: 100%; + padding-right: 10px; + font-size: 12px; + color: #fff; + text-align: right; + text-decoration: none; + cursor: pointer; + filter: alpha(opacity=0); + opacity: 0; + -webkit-transition: opacity .25s linear; + transition: opacity .25s linear; + } +.select2-search-choice .select2-search-choice-close:after { + font-family: "Flat-UI-Icons"; + line-height: 27px; + content: "\e609"; + + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + } +.select2-search-field input[type="text"] { + width: auto; + min-width: 80px; + max-width: inherit; + height: 29px; + padding: 0; + margin: 0; + font-size: 14px; + color: #34495e; + vertical-align: top; + background-color: transparent; + border: none; + outline: none; + box-shadow: none; + } +.select2-search-field:first-child input[type="text"] { + height: 23px; + margin: 3px 0 5px; + } +.select2-container-multi.multiselect-default { + border-color: #bdc3c7; + } +.select2-container-multi.multiselect-default .select2-search-choice { + background-color: #bdc3c7; + } +.select2-container-multi.multiselect-default .select2-search-choice:hover { + background-color: #cacfd2; + } +.select2-container-multi.multiselect-primary { + border-color: #1abc9c; + } +.select2-container-multi.multiselect-primary .select2-search-choice { + background-color: #1abc9c; + } +.select2-container-multi.multiselect-primary .select2-search-choice:hover { + background-color: #48c9b0; + } +.select2-container-multi.multiselect-info { + border-color: #3498db; + } +.select2-container-multi.multiselect-info .select2-search-choice { + background-color: #3498db; + } +.select2-container-multi.multiselect-info .select2-search-choice:hover { + background-color: #5dade2; + } +.select2-container-multi.multiselect-danger { + border-color: #e74c3c; + } +.select2-container-multi.multiselect-danger .select2-search-choice { + background-color: #e74c3c; + } +.select2-container-multi.multiselect-danger .select2-search-choice:hover { + background-color: #ec7063; + } +.select2-container-multi.multiselect-success { + border-color: #2ecc71; + } +.select2-container-multi.multiselect-success .select2-search-choice { + background-color: #2ecc71; + } +.select2-container-multi.multiselect-success .select2-search-choice:hover { + background-color: #58d68d; + } +.select2-container-multi.multiselect-warning { + border-color: #f1c40f; + } +.select2-container-multi.multiselect-warning .select2-search-choice { + background-color: #f1c40f; + } +.select2-container-multi.multiselect-warning .select2-search-choice:hover { + background-color: #f4d313; + } +.select2-container-multi.multiselect-inverse { + border-color: #34495e; + } +.select2-container-multi.multiselect-inverse .select2-search-choice { + background-color: #34495e; + } +.select2-container-multi.multiselect-inverse .select2-search-choice:hover { + background-color: #415b76; + } +.select2-drop { + position: absolute; + top: 100%; + z-index: 9999; + min-width: 220px; + margin-top: 9px; + font-size: 14px; + visibility: visible; + filter: none; + border-radius: 4px; + opacity: 1; + -webkit-transition: none; + transition: none; + + -webkit-filter: none; + } +.select2-drop.select2-drop-above { + margin-top: -9px; + } +.select2-drop.select2-drop-auto-width { + width: auto; + } +.select2-drop.show-select-search .select2-search { + display: block; + } +.select2-drop.show-select-search .select2-search + .select2-results > li:first-child .select2-result-label { + border-radius: 0; + } +.select2-drop .select2-results { + padding: 0; + margin: 0; + list-style: none; + } +.select2-drop .select2-results > li:first-child > .select2-result-label { + border-top-left-radius: 4px; + border-top-right-radius: 4px; + } +.select2-drop .select2-results > li:last-child > .select2-result-label { + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; + } +.select2-drop .select2-result-sub { + padding: 0; + margin: 0; + list-style: none; + } +.select2-drop .select2-result-sub > li:last-child > .select2-result-label { + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; + } +.select2-drop .select2-no-results { + padding: 8px 15px; + } +.select2-drop .select2-result-label { + padding: 8px 16px; + line-height: 1.429; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -webkit-transition: background-color .25s, color .25s; + transition: background-color .25s, color .25s; + } +.select2-drop .select2-result-selectable .select2-result-label { + color: rgba(52, 73, 94, .85); + cursor: pointer; + } +.select2-drop .select2-result-selectable .select2-result-label:focus, +.select2-drop .select2-result-selectable .select2-result-label:hover, +.select2-drop .select2-result-selectable .select2-result-label:active { + color: inherit; + background-color: #e1e4e7; + outline: none; + } +.select2-drop .select2-disabled { + color: rgba(52, 73, 94, .95); + cursor: default; + filter: alpha(opacity=40); + opacity: .4; + } +.select2-drop .select2-disabled:focus, +.select2-drop .select2-disabled:hover, +.select2-drop .select2-disabled:active { + background: none !important; + } +.select2-drop .select2-highlighted > .select2-result-label { + color: #fff; + background: #1abc9c; + } +.select2-drop .select2-result-with-children > .select2-result-label { + margin-top: 5px; + font-size: 13px; + color: rgba(52, 73, 94, .6); + text-transform: uppercase; + } +.select2-drop .select2-result-with-children + .select2-result-with-children > .select2-result-label { + margin-top: 11px; + } +.select2-results { + position: relative; + max-height: 200px; + overflow-x: hidden; + overflow-y: auto; + + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); + } +.select2-search { + display: none; + width: 100%; + padding: 8px 6px; + } +.select2-search input[type="text"] { + width: 100%; + height: auto !important; + } +.select-inverse-dropdown { + color: rgba(255, 255, 255, .75); + background-color: #34495e; + } +.select-inverse-dropdown .select2-results .select2-result-label { + color: #fff; + } +.select-inverse-dropdown .select2-results .select2-result-label:focus, +.select-inverse-dropdown .select2-results .select2-result-label:hover, +.select-inverse-dropdown .select2-results .select2-result-label:active { + background: #2c3e50; + } +.select-inverse-dropdown .select2-results.select2-disabled .select2-result-label:hover { + color: #fff; + } +.select-inverse-dropdown .select2-result-with-children > .select2-result-label { + color: rgba(255, 255, 255, .6); + } +.select-inverse-dropdown .select2-result-with-children > .select2-result-label:hover { + color: #fff; + background: none !important; + } +.select2-drop-multi { + border-radius: 6px; + } +.select2-drop-multi .select2-results { + padding: 2px 0; + } +.select2-drop-multi .select2-result { + padding: 2px 4px; + } +.select2-drop-multi .select2-result-label { + border-radius: 4px; + } +.select2-drop-multi .select2-selected { + display: none; + } +.select2-offscreen, +.select2-offscreen:focus { + position: absolute !important; + top: 0 !important; + left: 0 !important; + width: 1px !important; + height: 1px !important; + padding: 0 !important; + margin: 0 !important; + overflow: hidden !important; + clip: rect(0 0 0 0) !important; + border: 0 !important; + outline: 0 !important; + } +.select2-hidden-accessible { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0 0 0 0); + border: 0; + } +.select2-offscreen, +.select2-offscreen:focus { + position: absolute !important; + top: 0 !important; + left: 0 !important; + width: 1px !important; + height: 1px !important; + padding: 0 !important; + margin: 0 !important; + overflow: hidden !important; + clip: rect(0 0 0 0) !important; + border: 0 !important; + outline: 0 !important; + } +.select2-display-none { + display: none; + } +.select2-measure-scrollbar { + position: absolute; + top: -10000px; + left: -10000px; + width: 100px; + height: 100px; + overflow: scroll; + } +.select2-drop-mask { + position: fixed; + top: 0; + left: 0; + z-index: 9998; + width: auto; + min-width: 100%; + height: auto; + min-height: 100%; + padding: 0; + margin: 0; + /* styles required for IE to work */ + background-color: #fff; + filter: alpha(opacity=0); + border: 0; + opacity: 0; + } +.tile { + position: relative; + padding: 14px; + margin-bottom: 20px; + text-align: center; + background-color: #eff0f2; + border-radius: 6px; + } +.tile .tile-hot-ribbon { + position: absolute; + top: -4px; + right: -4px; + display: block; + width: 82px; + } +.tile p { + margin-bottom: 33px; + font-size: 15px; + } +.tile-image { + height: 100px; + margin: 31px 0 27px; + vertical-align: bottom; + } +.tile-image.big-illustration { + width: 112px; + height: 111px; + margin-top: 20px; + } +.tile-title { + margin: 0; + font-size: 20px; + } +.navbar { + min-height: 53px; + margin-bottom: 30px; + font-size: 16px; + border: none; + border-radius: 6px; + } +@media (min-width: 768px) { + .navbar-header { + float: left; + } + } +.navbar-collapse { + padding-right: 21px; + padding-left: 21px; + box-shadow: none; + } +.navbar-collapse .navbar-form:first-child { + border: none; + } +@media (min-width: 768px) { + .navbar-collapse .navbar-nav.navbar-left:first-child { + margin-left: -21px; + } + .navbar-collapse .navbar-nav.navbar-left:first-child > li:first-child a { + border-top-left-radius: 6px; + border-bottom-left-radius: 6px; + } + .navbar-collapse .navbar-nav.navbar-right:last-child { + margin-right: -21px; + } + .navbar-collapse .navbar-nav.navbar-right:last-child > .dropdown:last-child > a { + border-radius: 0 6px 6px 0; + } + .navbar-fixed-top .navbar-collapse .navbar-form.navbar-right:last-child, + .navbar-fixed-bottom .navbar-collapse .navbar-form.navbar-right:last-child { + margin-right: 0; + } + } +@media (max-width: 767px) { + .navbar-collapse .navbar-nav.navbar-right:last-child { + margin-bottom: 3px; + } + } +.navbar .container, +.navbar .container-fluid { + padding-right: 21px; + padding-left: 21px; + } +.navbar .container > .navbar-header, +.navbar .container-fluid > .navbar-header, +.navbar .container > .navbar-collapse, +.navbar .container-fluid > .navbar-collapse { + margin-right: -21px; + margin-left: -21px; + } +@media (min-width: 768px) { + .navbar .container > .navbar-header, + .navbar .container-fluid > .navbar-header, + .navbar .container > .navbar-collapse, + .navbar .container-fluid > .navbar-collapse { + margin-right: 0; + margin-left: 0; + } + } +.navbar-static-top { + z-index: 1000; + border-width: 0; + border-radius: 0; + } +.navbar-fixed-top, +.navbar-fixed-bottom { + z-index: 1030; + border-radius: 0; + } +.navbar-fixed-top { + border-width: 0; + } +.navbar-fixed-bottom { + margin-bottom: 0; + border-width: 0; + } +.navbar-brand { + height: 53px; + padding: 14px 21px; + font-size: 24px; + font-weight: 700; + line-height: 1.042; + } +.navbar-brand > [class*="fui-"] { + font-size: 19px; + line-height: 1.263; + vertical-align: top; + } +@media (min-width: 768px) { + .navbar > .container .navbar-brand, + .navbar > .container-fluid .navbar-brand { + margin-left: -21px; + } + } +.navbar-toggle { + height: 53px; + padding: 0 21px; + margin: 0 0 0 21px; + line-height: 53px; + color: #34495e; + border: none; + } +.navbar-toggle:before { + font-family: "Flat-UI-Icons"; + font-size: 22px; + font-style: normal; + font-weight: normal; + color: #16a085; + content: "\e61a"; + -webkit-transition: color .25s linear; + transition: color .25s linear; + + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + } +.navbar-toggle:hover, +.navbar-toggle:focus { + outline: none; + } +.navbar-toggle:hover:before, +.navbar-toggle:focus:before { + color: #1abc9c; + } +.navbar-toggle .icon-bar { + display: none; + } +@media (min-width: 768px) { + .navbar-toggle { + display: none; + } + } +.navbar-nav { + margin: 0; + } +.navbar-nav > li > a { + padding: 15px 21px; + font-size: 16px; + font-weight: 700; + line-height: 23px; + } +.navbar-nav > li > a:hover, +.navbar-nav > li > a:focus, +.navbar-nav .open > a:focus, +.navbar-nav .open > a:hover { + background-color: transparent; + } +.navbar-nav [class^="fui-"] { + position: relative; + top: 1px; + line-height: 20px; + } +.navbar-nav .visible-sm > [class^="fui-"], +.navbar-nav .visible-xs > [class^="fui-"] { + margin-left: 12px; + } +@media (max-width: 767px) { + .navbar-nav { + margin: 0 -21px; + } + .navbar-nav .open .dropdown-menu > li > a, + .navbar-nav .open .dropdown-menu .dropdown-header { + padding: 7px 15px 7px 31px !important; + } + .navbar-nav .open .dropdown-menu > li > a { + line-height: 23px; + } + .navbar-nav > li > a { + padding-top: 7px; + padding-bottom: 7px; + } + } +.navbar-input { + height: 35px; + padding: 5px 10px; + font-size: 13px; + line-height: 1.4; + border-radius: 6px; + } +select.navbar-input { + height: 35px; + line-height: 35px; + } +textarea.navbar-input, +select[multiple].navbar-input { + height: auto; + } +.navbar-form { + padding-top: 9px; + padding-right: 19px; + padding-bottom: 9px; + padding-left: 19px; + margin-top: 0; + margin-bottom: 0; + box-shadow: none; + } +@media (max-width: 767px) { + .navbar-form { + width: auto; + margin: 3px -21px; + } + } +.navbar-form .form-control, +.navbar-form .input-group-addon, +.navbar-form .btn, +.navbar-form .select2-search input[type="text"] { + height: 35px; + padding: 5px 10px; + font-size: 13px; + line-height: 1.4; + border-radius: 6px; + } +select.navbar-form .form-control, +select.navbar-form .input-group-addon, +select.navbar-form .btn, +select.navbar-form .select2-search input[type="text"] { + height: 35px; + line-height: 35px; + } +textarea.navbar-form .form-control, +textarea.navbar-form .input-group-addon, +textarea.navbar-form .btn, +select[multiple].navbar-form .form-control, +select[multiple].navbar-form .input-group-addon, +select[multiple].navbar-form .btn, +textarea.navbar-form .select2-search input[type="text"], +select[multiple].navbar-form .select2-search input[type="text"] { + height: auto; + } +.navbar-form .btn { + margin: 0; + } +.navbar-form .input-group .form-control:first-child, +.navbar-form .input-group-addon:first-child, +.navbar-form .input-group-btn:first-child > .btn, +.navbar-form .input-group-btn:first-child > .dropdown-toggle, +.navbar-form .input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), +.navbar-form .input-group .select2-search input[type="text"]:first-child { + border-top-right-radius: 0; + border-bottom-right-radius: 0; + } +.navbar-form .input-group .form-control:last-child, +.navbar-form .input-group-addon:last-child, +.navbar-form .input-group-btn:last-child > .btn, +.navbar-form .input-group-btn:last-child > .dropdown-toggle, +.navbar-form .input-group-btn:first-child > .btn:not(:first-child), +.navbar-form .input-group .select2-search input[type="text"]:last-child { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } +.navbar-form .form-control, +.navbar-form .select2-search input[type="text"] { + display: table-cell; + font-size: 15px; + border-radius: 5px; + } +.navbar-form .form-group ~ .btn { + margin-left: 5px; + font-size: 15px; + border-radius: 5px; + } +.navbar-form .form-group + .btn { + margin-right: 5px; + } +@media (min-width: 768px) { + .navbar-form .input-group { + width: 195px; + } + } +@media (max-width: 767px) { + .navbar-form .form-group { + margin-bottom: 7px; + } + .navbar-form .form-group:last-child { + margin-bottom: 0; + } + .navbar-form .form-group + .btn { + margin-left: 0; + } + } +.navbar-nav > li > .dropdown-menu { + min-width: 100%; + margin-top: 9px; + border-radius: 4px; + } +@media (max-width: 767px) { + .navbar-nav > li.open > .dropdown-menu { + margin-top: 0 !important; + } + } +.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; + } +.navbar-nav > .open > .dropdown-toggle, +.navbar-nav > .open > .dropdown-toggle:focus, +.navbar-nav > .open > .dropdown-toggle:hover { + background-color: transparent; + } +.navbar-text { + padding-top: 15px; + padding-bottom: 15px; + margin-top: 0; + margin-bottom: 0; + font-size: 16px; + line-height: 1.438; + color: #34495e; + } +@media (min-width: 768px) { + .navbar-text { + margin-right: 21px; + margin-left: 21px; + } + .navbar-text.navbar-right:last-child { + margin-right: 0; + } + } +.navbar-btn { + margin-top: 6px; + margin-bottom: 6px; + } +.navbar-btn.btn-sm { + margin-top: 9px; + margin-bottom: 8px; + } +.navbar-btn.btn-xs { + margin-top: 14px; + margin-bottom: 14px; + } +.navbar-unread, +.navbar-new { + position: absolute; + top: 35%; + right: 12px; + z-index: 10; + width: 6px; + height: 6px; + font-family: "Lato", Helvetica, Arial, sans-serif; + font-size: 0; + font-weight: 700; + line-height: 1; + color: #fff; + text-align: center; + background-color: #1abc9c; + border-radius: 50%; + } +@media (max-width: 768px) { + .navbar-unread, + .navbar-new { + position: static; + float: right; + margin: 0 0 0 10px; + } + } +.active .navbar-unread, +.active .navbar-new { + display: none; + background-color: #fff; + } +.navbar-new { + width: auto; + min-width: 18px; + height: 18px; + padding: 0 1px; + margin: -6px -10px; + font-size: 12px; + line-height: 17px; + background-color: #e74c3c; + + -webkit-font-smoothing: subpixel-antialiased; + } +.navbar-default { + background-color: #ecf0f1; + } +.navbar-default .navbar-brand { + color: #34495e; + } +.navbar-default .navbar-brand:hover, +.navbar-default .navbar-brand:focus { + color: #1abc9c; + background-color: transparent; + } +.navbar-default .navbar-toggle:before { + color: #34495e; + } +.navbar-default .navbar-toggle:hover, +.navbar-default .navbar-toggle:focus { + background-color: transparent; + } +.navbar-default .navbar-toggle:hover:before, +.navbar-default .navbar-toggle:focus:before { + color: #1abc9c; + } +.navbar-default .navbar-collapse, +.navbar-default .navbar-form { + border-color: #e5e9ea; + border-width: 2px; + } +.navbar-default .navbar-nav > li > a { + color: #34495e; + } +.navbar-default .navbar-nav > li > a:hover, +.navbar-default .navbar-nav > li > a:focus { + color: #1abc9c; + background-color: transparent; + } +.navbar-default .navbar-nav > .active > a, +.navbar-default .navbar-nav > .active > a:hover, +.navbar-default .navbar-nav > .active > a:focus { + color: #1abc9c; + background-color: transparent; + } +.navbar-default .navbar-nav > .disabled > a, +.navbar-default .navbar-nav > .disabled > a:hover, +.navbar-default .navbar-nav > .disabled > a:focus { + color: #ccc; + background-color: transparent; + } +.navbar-default .navbar-nav > .dropdown > a .caret { + border-top-color: #34495e; + border-bottom-color: #34495e; + } +.navbar-default .navbar-nav > .active > a .caret { + border-top-color: #1abc9c; + border-bottom-color: #1abc9c; + } +.navbar-default .navbar-nav > .dropdown > a:hover .caret, +.navbar-default .navbar-nav > .dropdown > a:focus .caret { + border-top-color: #1abc9c; + border-bottom-color: #1abc9c; + } +.navbar-default .navbar-nav > .open > a, +.navbar-default .navbar-nav > .open > a:hover, +.navbar-default .navbar-nav > .open > a:focus { + color: #1abc9c; + background-color: transparent; + } +.navbar-default .navbar-nav > .open > a .caret, +.navbar-default .navbar-nav > .open > a:hover .caret, +.navbar-default .navbar-nav > .open > a:focus .caret { + border-top-color: #1abc9c; + border-bottom-color: #1abc9c; + } +@media (max-width: 767px) { + .navbar-default .navbar-nav .open .dropdown-menu > li > a { + color: #34495e; + } + .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { + color: #1abc9c; + background-color: transparent; + } + .navbar-default .navbar-nav .open .dropdown-menu > .active > a, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #1abc9c; + background-color: transparent; + } + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #ccc; + background-color: transparent; + } + } +.navbar-default .navbar-form .form-control, +.navbar-default .navbar-form .select2-search input[type="text"] { + border-color: transparent; + } +.navbar-default .navbar-form .form-control::-moz-placeholder, +.navbar-default .navbar-form .select2-search input[type="text"]::-moz-placeholder { + color: #aeb6bf; + opacity: 1; + } +.navbar-default .navbar-form .form-control:-ms-input-placeholder, +.navbar-default .navbar-form .select2-search input[type="text"]:-ms-input-placeholder { + color: #aeb6bf; + } +.navbar-default .navbar-form .form-control::-webkit-input-placeholder, +.navbar-default .navbar-form .select2-search input[type="text"]::-webkit-input-placeholder { + color: #aeb6bf; + } +.navbar-default .navbar-form .form-control:focus, +.navbar-default .navbar-form .select2-search input[type="text"]:focus { + color: #1abc9c; + border-color: #1abc9c; + } +.navbar-default .navbar-form .input-group-btn .btn { + color: #919ba4; + border-color: transparent; + } +.navbar-default .navbar-form .input-group.focus .form-control, +.navbar-default .navbar-form .input-group.focus .input-group-btn .btn, +.navbar-default .navbar-form .input-group.focus .select2-search input[type="text"] { + color: #1abc9c; + border-color: #1abc9c; + } +.navbar-default .navbar-text { + color: #34495e; + } +.navbar-default .navbar-link { + color: #34495e; + } +.navbar-default .navbar-link:hover { + color: #1abc9c; + } +.navbar-default .btn-link { + color: #34495e; + } +.navbar-default .btn-link:hover, +.navbar-default .btn-link:focus { + color: #1abc9c; + } +.navbar-default .btn-link[disabled]:hover, +fieldset[disabled] .navbar-default .btn-link:hover, +.navbar-default .btn-link[disabled]:focus, +fieldset[disabled] .navbar-default .btn-link:focus { + color: #ccc; + } +.navbar-inverse { + background-color: #34495e; + } +.navbar-inverse .navbar-brand { + color: #fff; + } +.navbar-inverse .navbar-brand:hover, +.navbar-inverse .navbar-brand:focus { + color: #1abc9c; + background-color: transparent; + } +.navbar-inverse .navbar-toggle:before { + color: #fff; + } +.navbar-inverse .navbar-toggle:hover, +.navbar-inverse .navbar-toggle:focus { + background-color: transparent; + } +.navbar-inverse .navbar-toggle:hover:before, +.navbar-inverse .navbar-toggle:focus:before { + color: #1abc9c; + } +.navbar-inverse .navbar-collapse { + border-color: #2f4154; + border-width: 2px; + } +.navbar-inverse .navbar-nav > li > a { + color: #fff; + } +.navbar-inverse .navbar-nav > li > a:hover, +.navbar-inverse .navbar-nav > li > a:focus { + color: #1abc9c; + background-color: transparent; + } +.navbar-inverse .navbar-nav > .active > a, +.navbar-inverse .navbar-nav > .active > a:hover, +.navbar-inverse .navbar-nav > .active > a:focus { + color: #fff; + background-color: #1abc9c; + } +.navbar-inverse .navbar-nav > .disabled > a, +.navbar-inverse .navbar-nav > .disabled > a:hover, +.navbar-inverse .navbar-nav > .disabled > a:focus { + color: #444; + background-color: transparent; + } +.navbar-inverse .navbar-nav > .dropdown > a:hover .caret, +.navbar-inverse .navbar-nav > .dropdown > a:focus .caret { + border-top-color: #1abc9c; + border-bottom-color: #1abc9c; + } +.navbar-inverse .navbar-nav > .open > a, +.navbar-inverse .navbar-nav > .open > a:hover, +.navbar-inverse .navbar-nav > .open > a:focus { + color: #fff; + background-color: #1abc9c; + border-left-color: transparent; + } +.navbar-inverse .navbar-nav > .open > a .caret, +.navbar-inverse .navbar-nav > .open > a:hover .caret, +.navbar-inverse .navbar-nav > .open > a:focus .caret { + border-top-color: #fff; + border-bottom-color: #fff; + } +.navbar-inverse .navbar-nav > .dropdown > a .caret { + border-top-color: #4b6075; + border-bottom-color: #4b6075; + } +.navbar-inverse .navbar-nav > .open > .dropdown-menu { + padding: 3px 4px; + background-color: #34495e; + } +.navbar-inverse .navbar-nav > .open > .dropdown-menu > li > a { + padding: 6px 9px; + color: #e1e4e7; + border-radius: 4px; + } +.navbar-inverse .navbar-nav > .open > .dropdown-menu > li > a:hover, +.navbar-inverse .navbar-nav > .open > .dropdown-menu > li > a:focus { + color: #fff; + background-color: #1abc9c; + } +.navbar-inverse .navbar-nav > .open > .dropdown-menu > .divider { + height: 2px; + margin-right: -4px; + margin-left: -4px; + background-color: #2f4154; + } +@media (max-width: 767px) { + .navbar-inverse .navbar-nav > li > a { + border-left-width: 0; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a { + color: #fff; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { + color: #1abc9c; + background-color: transparent; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a, + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #fff; + background-color: #1abc9c; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #444; + background-color: transparent; + } + .navbar-inverse .navbar-nav .dropdown-menu .divider { + background-color: #2f4154; + } + } +.navbar-inverse .navbar-form .form-control, +.navbar-inverse .navbar-form .select2-search input[type="text"] { + color: #536a81; + background-color: #293a4a; + border-color: transparent; + } +.navbar-inverse .navbar-form .form-control::-moz-placeholder, +.navbar-inverse .navbar-form .select2-search input[type="text"]::-moz-placeholder { + color: #536a81; + opacity: 1; + } +.navbar-inverse .navbar-form .form-control:-ms-input-placeholder, +.navbar-inverse .navbar-form .select2-search input[type="text"]:-ms-input-placeholder { + color: #536a81; + } +.navbar-inverse .navbar-form .form-control::-webkit-input-placeholder, +.navbar-inverse .navbar-form .select2-search input[type="text"]::-webkit-input-placeholder { + color: #536a81; + } +.navbar-inverse .navbar-form .form-control:focus, +.navbar-inverse .navbar-form .select2-search input[type="text"]:focus { + color: #1abc9c; + border-color: #1abc9c; + } +.navbar-inverse .navbar-form .btn { + color: #fff; + background-color: #1abc9c; + } +.navbar-inverse .navbar-form .btn:hover, +.navbar-inverse .navbar-form .btn.hover, +.navbar-inverse .navbar-form .btn:focus, +.navbar-inverse .navbar-form .btn:active, +.navbar-inverse .navbar-form .btn.active, +.open > .dropdown-toggle.navbar-inverse .navbar-form .btn { + color: #fff; + background-color: #48c9b0; + border-color: #48c9b0; + } +.navbar-inverse .navbar-form .btn:active, +.navbar-inverse .navbar-form .btn.active, +.open > .dropdown-toggle.navbar-inverse .navbar-form .btn { + background: #16a085; + border-color: #16a085; + } +.navbar-inverse .navbar-form .btn.disabled, +.navbar-inverse .navbar-form .btn[disabled], +fieldset[disabled] .navbar-inverse .navbar-form .btn, +.navbar-inverse .navbar-form .btn.disabled:hover, +.navbar-inverse .navbar-form .btn[disabled]:hover, +fieldset[disabled] .navbar-inverse .navbar-form .btn:hover, +.navbar-inverse .navbar-form .btn.disabled.hover, +.navbar-inverse .navbar-form .btn[disabled].hover, +fieldset[disabled] .navbar-inverse .navbar-form .btn.hover, +.navbar-inverse .navbar-form .btn.disabled:focus, +.navbar-inverse .navbar-form .btn[disabled]:focus, +fieldset[disabled] .navbar-inverse .navbar-form .btn:focus, +.navbar-inverse .navbar-form .btn.disabled:active, +.navbar-inverse .navbar-form .btn[disabled]:active, +fieldset[disabled] .navbar-inverse .navbar-form .btn:active, +.navbar-inverse .navbar-form .btn.disabled.active, +.navbar-inverse .navbar-form .btn[disabled].active, +fieldset[disabled] .navbar-inverse .navbar-form .btn.active { + background-color: #bdc3c7; + border-color: #1abc9c; + } +.navbar-inverse .navbar-form .btn .badge { + color: #1abc9c; + background-color: #fff; + } +.navbar-inverse .navbar-form .input-group-btn .btn { + color: #526a82; + background-color: #293a4a; + border-color: transparent; + } +.navbar-inverse .navbar-form .input-group.focus .form-control, +.navbar-inverse .navbar-form .input-group.focus .input-group-btn .btn, +.navbar-inverse .navbar-form .input-group.focus .select2-search input[type="text"] { + color: #1abc9c; + border-color: #1abc9c; + } +@media (max-width: 767px) { + .navbar-inverse .navbar-form { + border-color: #2f4154; + border-width: 2px 0; + } + } +.navbar-inverse .navbar-text { + color: #fff; + } +.navbar-inverse .navbar-text a { + color: #fff; + } +.navbar-inverse .navbar-text a:hover, +.navbar-inverse .navbar-text a:focus { + color: #1abc9c; + } +.navbar-inverse .navbar-btn { + color: #fff; + background-color: #1abc9c; + } +.navbar-inverse .navbar-btn:hover, +.navbar-inverse .navbar-btn.hover, +.navbar-inverse .navbar-btn:focus, +.navbar-inverse .navbar-btn:active, +.navbar-inverse .navbar-btn.active, +.open > .dropdown-toggle.navbar-inverse .navbar-btn { + color: #fff; + background-color: #48c9b0; + border-color: #48c9b0; + } +.navbar-inverse .navbar-btn:active, +.navbar-inverse .navbar-btn.active, +.open > .dropdown-toggle.navbar-inverse .navbar-btn { + background: #16a085; + border-color: #16a085; + } +.navbar-inverse .navbar-btn.disabled, +.navbar-inverse .navbar-btn[disabled], +fieldset[disabled] .navbar-inverse .navbar-btn, +.navbar-inverse .navbar-btn.disabled:hover, +.navbar-inverse .navbar-btn[disabled]:hover, +fieldset[disabled] .navbar-inverse .navbar-btn:hover, +.navbar-inverse .navbar-btn.disabled.hover, +.navbar-inverse .navbar-btn[disabled].hover, +fieldset[disabled] .navbar-inverse .navbar-btn.hover, +.navbar-inverse .navbar-btn.disabled:focus, +.navbar-inverse .navbar-btn[disabled]:focus, +fieldset[disabled] .navbar-inverse .navbar-btn:focus, +.navbar-inverse .navbar-btn.disabled:active, +.navbar-inverse .navbar-btn[disabled]:active, +fieldset[disabled] .navbar-inverse .navbar-btn:active, +.navbar-inverse .navbar-btn.disabled.active, +.navbar-inverse .navbar-btn[disabled].active, +fieldset[disabled] .navbar-inverse .navbar-btn.active { + background-color: #bdc3c7; + border-color: #1abc9c; + } +.navbar-inverse .navbar-btn .badge { + color: #1abc9c; + background-color: #fff; + } +@media (min-width: 768px) { + .navbar-embossed > .navbar-collapse { + border-radius: 6px; + box-shadow: inset 0 -2px 0 rgba(0, 0, 0, .15); + } + .navbar-embossed.navbar-inverse .navbar-nav .active > a, + .navbar-embossed.navbar-inverse .navbar-nav .open > a { + box-shadow: inset 0 -2px 0 rgba(0, 0, 0, .15); + } + } +.navbar-lg { + min-height: 76px; + } +.navbar-lg .navbar-brand { + height: 76px; + padding-top: 26px; + padding-bottom: 26px; + line-height: 1; + } +.navbar-lg .navbar-brand > [class*="fui-"] { + font-size: 24px; + line-height: 1; + } +.navbar-lg .navbar-nav > li > a { + font-size: 15px; + line-height: 1.6; + } +@media (min-width: 768px) { + .navbar-lg .navbar-nav > li > a { + padding-top: 26px; + padding-bottom: 26px; + } + } +.navbar-lg .navbar-toggle { + height: 76px; + line-height: 76px; + } +.navbar-lg .navbar-form { + padding-top: 20.5px; + padding-bottom: 20.5px; + } +.navbar-lg .navbar-text { + padding-top: 26.5px; + padding-bottom: 26.5px; + } +.navbar-lg .navbar-btn { + margin-top: 17.5px; + margin-bottom: 17.5px; + } +.navbar-lg .navbar-btn.btn-sm { + margin-top: 20.5px; + margin-bottom: 20.5px; + } +.navbar-lg .navbar-btn.btn-xs { + margin-top: 25.5px; + margin-bottom: 25.5px; + } +.bootstrap-switch { + position: relative; + display: inline-block; + width: 80px; + height: 29px; + overflow: hidden; + font-size: 15px; + line-height: 29px; + text-align: left; + vertical-align: middle; + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + border-radius: 30px; + + -webkit-mask-box-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNy4xLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB2aWV3Qm94PSIwIDAgODAgMjkiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDgwIDI5IiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxwYXRoIGQ9Ik04MCwxNC41YzAsOC02LjUsMTQuNS0xNC41LDE0LjVoLTUxQzYuNSwyOSwwLDIyLjUsMCwxNC41bDAsMEMwLDYuNSw2LjUsMCwxNC41LDBoNTFDNzMuNSwwLDgwLDYuNSw4MCwxNC41TDgwLDE0LjV6Ii8+DQo8L3N2Zz4NCg==) 0 0 stretch; + } +.bootstrap-switch > div { + display: inline-block; + width: 132px; + border-radius: 30px; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +.bootstrap-switch > div > span { + z-index: 1; + display: inline-block; + width: 66px; + height: 100%; + padding-top: 5px; + padding-bottom: 5px; + font-weight: 700; + line-height: 19px; + text-align: center; + cursor: pointer; + -webkit-transition: box-shadow .25s ease-out; + transition: box-shadow .25s ease-out; + } +.bootstrap-switch > div > span > [class^="fui-"] { + text-indent: 0; + } +.bootstrap-switch > div > label { + position: absolute; + top: 0; + left: 0; + z-index: 200; + display: block; + width: 100%; + height: 100%; + margin: 0; + font-size: 0; + text-indent: -9999px; + cursor: pointer; + filter: alpha(opacity=0); + opacity: 0; + } +.bootstrap-switch input[type="radio"], +.bootstrap-switch input[type="checkbox"] { + position: absolute !important; + top: 0; + left: 0; + z-index: -1; + margin: 0; + filter: alpha(opacity=0); + opacity: 0; + } +.bootstrap-switch-handle-on { + border-top-left-radius: 30px; + border-bottom-left-radius: 30px; + } +.bootstrap-switch-off .bootstrap-switch-handle-on ~ .bootstrap-switch-handle-off.bootstrap-switch-default { + box-shadow: inset 0 0 transparent, -16px 0 0 #bdc3c7; + } +.bootstrap-switch-off .bootstrap-switch-handle-on ~ .bootstrap-switch-handle-off.bootstrap-switch-default:before { + background-color: #7f8c9a; + border-color: #bdc3c7; + } +.bootstrap-switch-off .bootstrap-switch-handle-on ~ .bootstrap-switch-handle-off.bootstrap-switch-primary { + box-shadow: inset 0 0 transparent, -16px 0 0 #34495e; + } +.bootstrap-switch-off .bootstrap-switch-handle-on ~ .bootstrap-switch-handle-off.bootstrap-switch-primary:before { + background-color: #1abc9c; + border-color: #34495e; + } +.bootstrap-switch-off .bootstrap-switch-handle-on ~ .bootstrap-switch-handle-off.bootstrap-switch-success { + box-shadow: inset 0 0 transparent, -16px 0 0 #2ecc71; + } +.bootstrap-switch-off .bootstrap-switch-handle-on ~ .bootstrap-switch-handle-off.bootstrap-switch-success:before { + background-color: #fff; + border-color: #2ecc71; + } +.bootstrap-switch-off .bootstrap-switch-handle-on ~ .bootstrap-switch-handle-off.bootstrap-switch-warning { + box-shadow: inset 0 0 transparent, -16px 0 0 #f1c40f; + } +.bootstrap-switch-off .bootstrap-switch-handle-on ~ .bootstrap-switch-handle-off.bootstrap-switch-warning:before { + background-color: #fff; + border-color: #f1c40f; + } +.bootstrap-switch-off .bootstrap-switch-handle-on ~ .bootstrap-switch-handle-off.bootstrap-switch-info { + box-shadow: inset 0 0 transparent, -16px 0 0 #3498db; + } +.bootstrap-switch-off .bootstrap-switch-handle-on ~ .bootstrap-switch-handle-off.bootstrap-switch-info:before { + background-color: #fff; + border-color: #3498db; + } +.bootstrap-switch-off .bootstrap-switch-handle-on ~ .bootstrap-switch-handle-off.bootstrap-switch-danger { + box-shadow: inset 0 0 transparent, -16px 0 0 #e74c3c; + } +.bootstrap-switch-off .bootstrap-switch-handle-on ~ .bootstrap-switch-handle-off.bootstrap-switch-danger:before { + background-color: #fff; + border-color: #e74c3c; + } +.bootstrap-switch-handle-off { + border-top-right-radius: 30px; + border-bottom-right-radius: 30px; + } +.bootstrap-switch-handle-off:before { + position: absolute; + top: 0; + left: 51px; + z-index: 100; + display: inline-block; + width: 29px; + height: 29px; + padding: 0; + text-align: center; + vertical-align: top; + content: " "; + background-clip: padding-box; + border: 4px solid transparent; + border-radius: 50%; + -webkit-transition: border-color .25s ease-out, background-color .25s ease-out; + transition: border-color .25s ease-out, background-color .25s ease-out; + } +.bootstrap-switch-animate > div { + -webkit-transition: margin-left .25s ease-out; + transition: margin-left .25s ease-out; + } +.bootstrap-switch-on > div { + margin-left: 0; + } +.bootstrap-switch-off > div { + margin-left: -51px; + } +.bootstrap-switch-disabled, +.bootstrap-switch-readonly { + cursor: default; + filter: alpha(opacity=50); + opacity: .5; + } +.bootstrap-switch-disabled > div > span, +.bootstrap-switch-readonly > div > span, +.bootstrap-switch-disabled > div > label, +.bootstrap-switch-readonly > div > label { + cursor: default !important; + } +.bootstrap-switch-focused { + outline: 0; + } +.bootstrap-switch-default { + color: #fff; + background-color: #bdc3c7; + } +.bootstrap-switch-default ~ .bootstrap-switch-handle-off:before { + background-color: #7f8c9a; + border-color: #bdc3c7; + } +.bootstrap-switch-on .bootstrap-switch-default ~ .bootstrap-switch-handle-off { + box-shadow: inset 16px 0 0 #bdc3c7; + } +.bootstrap-switch-primary { + color: #1abc9c; + background-color: #34495e; + } +.bootstrap-switch-primary ~ .bootstrap-switch-handle-off:before { + background-color: #1abc9c; + border-color: #34495e; + } +.bootstrap-switch-on .bootstrap-switch-primary ~ .bootstrap-switch-handle-off { + box-shadow: inset 16px 0 0 #34495e; + } +.bootstrap-switch-info { + color: #fff; + background-color: #3498db; + } +.bootstrap-switch-info ~ .bootstrap-switch-handle-off:before { + background-color: #fff; + border-color: #3498db; + } +.bootstrap-switch-on .bootstrap-switch-info ~ .bootstrap-switch-handle-off { + box-shadow: inset 16px 0 0 #3498db; + } +.bootstrap-switch-success { + color: #fff; + background-color: #2ecc71; + } +.bootstrap-switch-success ~ .bootstrap-switch-handle-off:before { + background-color: #fff; + border-color: #2ecc71; + } +.bootstrap-switch-on .bootstrap-switch-success ~ .bootstrap-switch-handle-off { + box-shadow: inset 16px 0 0 #2ecc71; + } +.bootstrap-switch-warning { + color: #fff; + background-color: #f1c40f; + } +.bootstrap-switch-warning ~ .bootstrap-switch-handle-off:before { + background-color: #fff; + border-color: #f1c40f; + } +.bootstrap-switch-on .bootstrap-switch-warning ~ .bootstrap-switch-handle-off { + box-shadow: inset 16px 0 0 #f1c40f; + } +.bootstrap-switch-danger { + color: #fff; + background-color: #e74c3c; + } +.bootstrap-switch-danger ~ .bootstrap-switch-handle-off:before { + background-color: #fff; + border-color: #e74c3c; + } +.bootstrap-switch-on .bootstrap-switch-danger ~ .bootstrap-switch-handle-off { + box-shadow: inset 16px 0 0 #e74c3c; + } +.bootstrap-switch-square .bootstrap-switch { + border-radius: 4px; + + -webkit-mask-box-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNy4xLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB2aWV3Qm94PSIwIDAgODAgMjkiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDgwIDI5IiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxwYXRoIGQ9Ik04MCwyNWMwLDIuMi0xLjgsNC00LDRINGMtMi4yLDAtNC0xLjgtNC00VjRjMC0yLjIsMS44LTQsNC00aDcyYzIuMiwwLDQsMS44LDQsNFYyNXoiLz4NCjwvc3ZnPg0K) 0 0 stretch; + } +.bootstrap-switch-square .bootstrap-switch > div { + border-radius: 4px; + } +.bootstrap-switch-square .bootstrap-switch .bootstrap-switch-handle-on { + text-indent: -15px; + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; + } +.bootstrap-switch-square .bootstrap-switch .bootstrap-switch-handle-off { + text-indent: 15px; + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; + } +.bootstrap-switch-square .bootstrap-switch .bootstrap-switch-handle-off:before { + border: none; + border-top-left-radius: 0; + border-top-right-radius: 2px; + border-bottom-right-radius: 2px; + border-bottom-left-radius: 0; + } +.bootstrap-switch-square .bootstrap-switch-off .bootstrap-switch-handle-off:before { + border-top-left-radius: 2px; + border-top-right-radius: 0; + border-bottom-right-radius: 0; + border-bottom-left-radius: 2px; + } +.share { + position: relative; + background-color: #eff0f2; + border-radius: 6px; + } +.share ul { + padding: 15px; + margin: 0; + list-style-type: none; + } +.share li { + padding-top: 11px; + font-size: 15px; + line-height: 1.4; + } +.share li:before, +.share li:after { + display: table; + content: " "; + } +.share li:after { + clear: both; + } +.share li:first-child { + padding-top: 0; + } +.share .toggle { + float: right; + margin: 0; + } +.share .btn { + border-top-left-radius: 0; + border-top-right-radius: 0; + } +.share-label { + float: left; + width: 50%; + padding-top: 5px; + font-size: 15px; + line-height: 1.4; + } +.video-js { + position: relative; + width: 100% !important; + height: auto !important; + padding-bottom: 47px; + overflow: hidden; + font-size: 0; + vertical-align: middle; + background-color: transparent; + + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + border-top-radius: 6px; + } +.video-js .vjs-tech { + display: block; + width: 100%; + height: 100%; + } +.video-js::-moz-full-screen { + position: absolute; + } +.video-js::-webkit-full-screen { + width: 100% !important; + height: 100% !important; + } +.vjs-fullscreen { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 10000; + width: 100% !important; + height: 100% !important; + overflow: hidden; + + border-top-radius: 0; + } +.vjs-fullscreen .vjs-control-bar { + margin-top: 0; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; + } +.vjs-fullscreen .vjs-tech { + background-color: #000; + } +.vjs-poster { + position: relative; + width: 100%; + max-height: 100%; + padding: 0; + margin: 0 auto; + cursor: pointer; + + border-top-radius: 6px; + } +.vjs-control-bar { + position: relative; + height: 47px; + margin-top: -1px; + color: #fff; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + background: #2c3e50; + border-bottom-right-radius: 6px; + border-bottom-left-radius: 6px; + } +.vjs-control-bar.vjs-fade-out { + visibility: visible !important; + opacity: 1 !important; + } +.vjs-text-track-display { + position: absolute; + right: 1em; + bottom: 4em; + left: 1em; + font-family: "Lato", Helvetica, Arial, sans-serif; + text-align: center; + } +.vjs-text-track { + display: none; + margin-bottom: .1em; + font-size: 1.4em; + color: #fff; + text-align: center; + background-color: rgba(0, 0, 0, .5); + } +.vjs-subtitles { + color: #fff; + } +.vjs-captions { + color: #fc6; + } +.vjs-tt-cue { + display: block; + } +.vjs-fade-in { + visibility: visible !important; + opacity: 1 !important; + -webkit-transition: visibility 0s linear 0s, opacity .3s linear; + transition: visibility 0s linear 0s, opacity .3s linear; + } +.vjs-fade-out { + visibility: hidden !important; + opacity: 0 !important; + -webkit-transition: visibility 0s linear 1.5s, opacity 1.5s linear; + transition: visibility 0s linear 1.5s, opacity 1.5s linear; + } +.vjs-control { + position: relative; + display: inline-block; + width: 18px; + height: 18px; + text-align: center; + vertical-align: middle; + background-repeat: no-repeat; + background-position: center; + } +.vjs-control:focus { + outline: 0; + } +.vjs-control > div { + background-repeat: no-repeat; + background-position: center; + } +.vjs-control-text { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0 0 0 0); + border: 0; + } +.vjs-play-control { + width: 58px; + height: 47px; + cursor: pointer; + } +.vjs-play-control > div { + position: relative; + height: 47px; + } +.vjs-play-control > div:before, +.vjs-play-control > div:after { + position: absolute; + top: 38%; + left: 50%; + margin: -.5em 0 0 -.5em; + font-family: "Flat-UI-Icons"; + font-size: 16px; + color: #1abc9c; + -webkit-transition: color .25s, opacity .25s; + transition: color .25s, opacity .25s; + + -webkit-font-smoothing: antialiased; + } +.vjs-play-control > div:after { + content: "\e615"; + } +.vjs-play-control > div:before { + content: "\e616"; + } +.vjs-paused .vjs-play-control:hover > div:before { + color: #16a085; + } +.vjs-paused .vjs-play-control > div:after { + filter: alpha(opacity=0); + opacity: 0; + } +.vjs-paused .vjs-play-control > div:before { + filter: none; + opacity: 1; + + -webkit-filter: none; + } +.vjs-playing .vjs-play-control:hover > div:after { + color: #16a085; + } +.vjs-playing .vjs-play-control > div:after { + filter: none; + opacity: 1; + + -webkit-filter: none; + } +.vjs-playing .vjs-play-control > div:before { + filter: alpha(opacity=0); + opacity: 0; + } +.vjs-rewind-control { + width: 5em; + cursor: pointer !important; + } +.vjs-rewind-control > div { + width: 19px; + height: 16px; + margin: .5em auto 0; + background: none transparent; + } +.vjs-mute-control { + float: right; + margin: 14px 0; + cursor: pointer !important; + } +.vjs-mute-control:hover > div, +.vjs-mute-control:focus > div { + color: #57718b; + } +.vjs-mute-control > div { + height: 18px; + color: #475d72; + } +.vjs-mute-control > div:after, +.vjs-mute-control > div:before { + position: absolute; + left: 50%; + margin: 0 0 0 -.5em; + font-family: "Flat-UI-Icons"; + font-size: 16px; + line-height: 18px; + -webkit-transition: color .25s, opacity .25s; + transition: color .25s, opacity .25s; + + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + } +.vjs-mute-control > div:after { + content: "\e617"; + } +.vjs-mute-control > div:before { + content: "\e618"; + filter: alpha(opacity=0); + opacity: 0; + } +.vjs-mute-control.vjs-vol-0 > div:after { + filter: alpha(opacity=0); + opacity: 0; + } +.vjs-mute-control.vjs-vol-0 > div:before { + filter: none; + opacity: 1; + + -webkit-filter: none; + } +.vjs-volume-control, +.vjs-volume-level, +.vjs-volume-handle, +.vjs-volume-bar { + display: none; + } +.vjs-progress-control { + position: absolute; + top: 18px; + right: 160px; + left: 60px; + width: auto; + height: 12px; + background: #425669; + border-radius: 32px; + } +.vjs-progress-holder { + position: relative; + height: 12px; + padding: 0; + margin: 0; + cursor: pointer !important; + } +.vjs-play-progress, +.vjs-load-progress { + display: block; + height: 12px; + padding: 0; + margin: 0; + border-radius: 32px; + } +.vjs-play-progress { + position: absolute; + top: 0; + left: -1px; + background: #1abc9c; + border-top-right-radius: 0; + border-bottom-right-radius: 0; + } +.vjs-load-progress { + background: #d6dbdf; + } +.vjs-load-progress[style*="100%"], +.vjs-load-progress[style*="99%"] { + border-radius: 32px; + } +.vjs-seek-handle { + position: absolute; + top: 0; + width: 18px; + height: 18px; + margin: -3px 0 0 -3px; + background-color: #16a085; + border-radius: 50%; + -webkit-transition: background-color .25s; + transition: background-color .25s; + } +.vjs-seek-handle[style*="95."] { + margin-left: 3px; + } +.vjs-seek-handle[style="left: 0%;"] { + margin-left: -2px; + } +.vjs-seek-handle:hover, +.vjs-seek-handle:focus { + background-color: #148d75; + } +.vjs-seek-handle:active { + background-color: #117a65; + } +.vjs-time-controls { + position: absolute; + width: auto; + height: auto; + font-family: "Lato", Helvetica, Arial, sans-serif; + font-size: 13px; + font-weight: 300; + line-height: normal; + } +.vjs-time-divider { + position: absolute; + top: 11px; + right: 114px; + font-size: 14px; + color: #5d6d7e; + } +.vjs-remaining-time { + display: none; + } +.vjs-current-time { + top: 16px; + right: 122px; + } +.vjs-duration { + top: 16px; + right: 85px; + color: #5d6d7e; + } +.vjs-fullscreen-control { + float: right; + margin: 14px 15px; + cursor: pointer; + } +.vjs-fullscreen-control:hover > div, +.vjs-fullscreen-control:focus > div { + color: #57718b; + } +.vjs-fullscreen-control > div { + height: 18px; + color: #475d72; + } +.vjs-fullscreen-control > div:before { + position: absolute; + left: 50%; + margin: 0 0 0 -.5em; + font-family: "Flat-UI-Icons"; + font-size: 16px; + line-height: 18px; + content: "\e619"; + -webkit-transition: color .25s, opacity .25s; + transition: color .25s, opacity .25s; + + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + } +.vjs-menu-button { + display: none !important; + } +.vjs-loading-spinner { + position: absolute; + top: 50%; + left: 50%; + display: none; + width: 16px; + height: 16px; + margin: -8px 0 0 -8px; + background: #ebedee; + border-radius: 10px; + -webkit-animation: sharp 2s ease infinite; + animation: sharp 2s ease infinite; + } +@-webkit-keyframes sharp { + 0% { + background-color: #e74c3c; + border-radius: 10px; + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 50% { + background-color: #ebedee; + border-radius: 0; + -webkit-transform: rotate(180deg); + transform: rotate(180deg); + } + 100% { + background-color: #e74c3c; + border-radius: 10px; + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } + } +@keyframes sharp { + 0% { + background-color: #e74c3c; + border-radius: 10px; + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 50% { + background-color: #ebedee; + border-radius: 0; + -webkit-transform: rotate(180deg); + transform: rotate(180deg); + } + 100% { + background-color: #e74c3c; + border-radius: 10px; + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } + } +.todo { + margin-bottom: 20px; + color: #798795; + border-radius: 6px; + } +.todo ul { + padding: 0; + margin: 0; + list-style-type: none; + background-color: #2c3e50; + border-radius: 0 0 6px 6px; + } +.todo li { + position: relative; + padding: 18px 42px 21px 25px; + margin-top: 2px; + font-size: 14px; + line-height: 1.214; + cursor: pointer; + background: #34495e; + background-size: 20px 20px; + -webkit-transition: .25s; + transition: .25s; + } +.todo li:first-child { + margin-top: 0; + } +.todo li:last-child { + padding-bottom: 21px; + border-radius: 0 0 6px 6px; + } +.todo li.todo-done { + color: #1abc9c; + background: transparent; + } +.todo li.todo-done .todo-name { + color: #1abc9c; + } +.todo li:after { + position: absolute; + top: 50%; + right: 22px; + display: block; + width: 20px; + height: 20px; + margin-top: -10px; + content: " "; + background: #fff; + border-radius: 50%; + } +.todo li.todo-done:after { + font-family: 'Flat-UI-Icons'; + font-size: 12px; + font-style: normal; + font-weight: normal; + font-variant: normal; + line-height: 21px; + color: #2c3e50; + text-align: center; + text-transform: none; + content: "\e60a"; + background: #1abc9c; + + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + } +.todo-search { + position: relative; + padding: 19px 25px 20px; + color: #34495e; + background: #1abc9c; + background-size: 16px 16px; + border-radius: 6px 6px 0 0; + } +.todo-search:before { + position: absolute; + top: 50%; + left: 92%; + display: inline-block; + margin: -.5em 0 0 -1em; + font-family: 'Flat-UI-Icons'; + font-size: 16px; + line-height: 17px; + content: "\e630"; + } +.todo-search-field { + padding: 5px 0; + margin: 0; + font-size: 19px; + font-weight: 700; + line-height: 23px; + color: #34495e; + text-indent: 0; + background: none; + border: none; + outline: none; + box-shadow: none; + } +.todo-search-field::-moz-placeholder { + color: #34495e; + opacity: 1; + } +.todo-search-field:-ms-input-placeholder { + color: #34495e; + } +.todo-search-field::-webkit-input-placeholder { + color: #34495e; + } +.todo-icon { + float: left; + padding: 11px 22px 0 0; + font-size: 24px; + } +.todo-content { + padding-top: 1px; + overflow: hidden; + } +.todo-name { + margin: 1px 0 3px; + font-size: 17px; + color: #fff; + } +.pallete-item { + float: left; + width: 140px; + margin: 0 0 20px 20px; + } +.palette { + padding: 15px; + margin: 0; + font-size: 14px; + line-height: 1.214; + color: #fff; + text-transform: uppercase; + } +.palette dt, +.palette dd { + line-height: 1.429; + } +.palette dt { + display: block; + font-weight: bold; + opacity: .8; + } +.palette dd { + margin-left: 0; + font-weight: 300; + opacity: .8; + + -webkit-font-smoothing: subpixel-antialiased; + } +.palette-turquoise { + background-color: #1abc9c; + } +.palette-green-sea { + background-color: #16a085; + } +.palette-emerald { + background-color: #2ecc71; + } +.palette-nephritis { + background-color: #27ae60; + } +.palette-peter-river { + background-color: #3498db; + } +.palette-belize-hole { + background-color: #2980b9; + } +.palette-amethyst { + background-color: #9b59b6; + } +.palette-wisteria { + background-color: #8e44ad; + } +.palette-wet-asphalt { + background-color: #34495e; + } +.palette-midnight-blue { + background-color: #2c3e50; + } +.palette-sun-flower { + background-color: #f1c40f; + } +.palette-orange { + background-color: #f39c12; + } +.palette-carrot { + background-color: #e67e22; + } +.palette-pumpkin { + background-color: #d35400; + } +.palette-alizarin { + background-color: #e74c3c; + } +.palette-pomegranate { + background-color: #c0392b; + } +.palette-clouds { + background-color: #ecf0f1; + } +.palette-silver { + background-color: #bdc3c7; + } +.palette-concrete { + background-color: #95a5a6; + } +.palette-asbestos { + background-color: #7f8c8d; + } +.palette-clouds { + color: #bdc3c7; + } +.palette-paragraph { + font-size: 12px; + line-height: 17px; + color: #7f8c8d; + } +.palette-paragraph span { + color: #bdc3c7; + } +.palette-headline { + margin-top: -3px; + font-size: 13px; + font-weight: 700; + color: #7f8c8d; + } +.login { + position: relative; + padding: 38px 38px 267px; + margin-bottom: 77px; + color: #fff; + background: url(../img/login/imac.png) 0 0 no-repeat; + background-size: 940px 778px; + } +.login-screen { + min-height: 473px; + padding: 123px 199px 33px 306px; + background-color: #1abc9c; + } +.login-icon { + position: absolute; + top: 160px; + left: 200px; + width: 96px; + } +.login-icon > img { + display: block; + width: 100%; + margin-bottom: 6px; + } +.login-icon > h4 { + font-size: 17px; + font-weight: 300; + line-height: 34px; + opacity: .95; + } +.login-icon > h4 small { + display: block; + font-size: inherit; + font-weight: 700; + color: inherit; + } +.login-form { + position: relative; + padding: 24px 23px 20px; + background-color: #edeff1; + border-radius: 6px; + } +.login-form .control-group { + position: relative; + margin-bottom: 6px; + } +.login-form .login-field { + font-size: 17px; + text-indent: 3px; + border-color: transparent; + } +.login-form .login-field:focus { + border-color: #1abc9c; + } +.login-form .login-field:focus + .login-field-icon { + color: #1abc9c; + } +.login-form .login-field-icon { + position: absolute; + top: 3px; + right: 15px; + font-size: 16px; + color: #bfc9ca; + -webkit-transition: all .25s; + transition: all .25s; + } +.login-link { + display: block; + margin-top: 15px; + font-size: 13px; + color: #bfc9ca; + text-align: center; + } +@media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (-moz-min-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 3/2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (-moz-min-device-pixel-ratio: 1.5), only screen and (min-device-pixel-ratio: 1.5), only screen and (min-device-pixel-ratio: 2) { + .login { + background-image: url(../img/login/imac-2x.png); + } + } +footer { + padding: 0; + font-size: 15px; + color: #bac1c8; + background-color: #edeff1; + } +footer a { + font-weight: 700; + color: #9aa4af; + } +footer p { + margin-bottom: 10px; + font-size: 15px; + line-height: 20px; + } +.footer-title { + padding-top: 21px; + margin: 0 0 22px; + font-size: 24px; + line-height: 40px; + } +.footer-brand { + display: block; + width: 220px; + margin-bottom: 26px; + } +.footer-brand img { + width: 216px; + } +.footer-banner { + min-height: 316px; + padding: 0 30px 30px; + margin-left: 42px; + color: #d1f2eb; + background-color: #1abc9c; + } +.footer-banner .footer-title { + color: #fff; + } +.footer-banner a { + color: #b7f5e9; + text-decoration: underline; + } +.footer-banner a:hover { + text-decoration: none; + } +.footer-banner ul { + padding: 0; + margin: 0 0 26px; + list-style-type: none; + } +.footer-banner ul li { + padding: 6px 0; + line-height: 19px; + border-top: 1px solid #1bc5a3; + } +.footer-banner ul li:first-child { + padding-top: 1px; + border-top: none; + } +.last-col { + overflow: hidden; + } +.ptn, +.pvn, +.pan { + padding-top: 0; + } +.ptx, +.pvx, +.pax { + padding-top: 3px; + } +.pts, +.pvs, +.pas { + padding-top: 5px; + } +.ptm, +.pvm, +.pam { + padding-top: 10px; + } +.ptl, +.pvl, +.pal { + padding-top: 20px; + } +.prn, +.phn, +.pan { + padding-right: 0; + } +.prx, +.phx, +.pax { + padding-right: 3px; + } +.prs, +.phs, +.pas { + padding-right: 5px; + } +.prm, +.phm, +.pam { + padding-right: 10px; + } +.prl, +.phl, +.pal { + padding-right: 20px; + } +.pbn, +.pvn, +.pan { + padding-bottom: 0; + } +.pbx, +.pvx, +.pax { + padding-bottom: 3px; + } +.pbs, +.pvs, +.pas { + padding-bottom: 5px; + } +.pbm, +.pvm, +.pam { + padding-bottom: 10px; + } +.pbl, +.pvl, +.pal { + padding-bottom: 20px; + } +.pln, +.phn, +.pan { + padding-left: 0; + } +.plx, +.phx, +.pax { + padding-left: 3px; + } +.pls, +.phs, +.pas { + padding-left: 5px; + } +.plm, +.phm, +.pam { + padding-left: 10px; + } +.pll, +.phl, +.pal { + padding-left: 20px; + } +.mtn, +.mvn, +.man { + margin-top: 0; + } +.mtx, +.mvx, +.max { + margin-top: 3px; + } +.mts, +.mvs, +.mas { + margin-top: 5px; + } +.mtm, +.mvm, +.mam { + margin-top: 10px; + } +.mtl, +.mvl, +.mal { + margin-top: 20px; + } +.mrn, +.mhn, +.man { + margin-right: 0; + } +.mrx, +.mhx, +.max { + margin-right: 3px; + } +.mrs, +.mhs, +.mas { + margin-right: 5px; + } +.mrm, +.mhm, +.mam { + margin-right: 10px; + } +.mrl, +.mhl, +.mal { + margin-right: 20px; + } +.mbn, +.mvn, +.man { + margin-bottom: 0; + } +.mbx, +.mvx, +.max { + margin-bottom: 3px; + } +.mbs, +.mvs, +.mas { + margin-bottom: 5px; + } +.mbm, +.mvm, +.mam { + margin-bottom: 10px; + } +.mbl, +.mvl, +.mal { + margin-bottom: 20px; + } +.mln, +.mhn, +.man { + margin-left: 0; + } +.mlx, +.mhx, +.max { + margin-left: 3px; + } +.mls, +.mhs, +.mas { + margin-left: 5px; + } +.mlm, +.mhm, +.mam { + margin-left: 10px; + } +.mll, +.mhl, +.mal { + margin-left: 20px; + } +/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */ +@media print { + .btn { + border-style: solid; + border-width: 2px; + } + .dropdown-menu, + .select2-drop { + background: #fff !important; + border: 2px solid #ddd; + } + .input-group-rounded .input-group-btn + .form-control, + .input-group-rounded .input-group-btn + .select2-search input[type="text"], + .input-group-rounded .input-group-btn + .select2-search input[type="text"] { + padding-left: 10px; + } + .form-control, + .select2-search input[type="text"] { + border: 2px solid #ddd !important; + } + .bootstrap-switch { + width: 84px; + height: 33px; + border: 2px solid #bdc3c7; + } + /*.tooltip {*/ + /*border: 2px solid #bdc3c7;*/ + /*}*/ + /*.progress,*/ + /*.ui-slider {*/ + /*background: #ddd !important;*/ + /*}*/ + /*.progress-bar,*/ + /*.ui-slider-range,*/ + /*.ui-slider-handle {*/ + /*background: #bdc3c7 !important;*/ + /*}*/ + } +/*# sourceMappingURL=flat-ui.css.map */ diff --git a/dependencies/main_files/flat-ui.js b/dependencies/main_files/flat-ui.js new file mode 100644 index 00000000..cdbc1679 --- /dev/null +++ b/dependencies/main_files/flat-ui.js @@ -0,0 +1,12728 @@ +/*! + * Flat UI Free v2.2.2 (http://designmodo.github.io/Flat-UI/) + * Copyright 2013-2014 Designmodo, Inc. + */ +/*! + * jQuery UI Core 1.10.4 + * http://jqueryui.com + * + * Copyright 2014 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/category/ui-core/ + */ +(function( $, undefined ) { + +var uuid = 0, + runiqueId = /^ui-id-\d+$/; + +// $.ui might exist from components with no dependencies, e.g., $.ui.position +$.ui = $.ui || {}; + +$.extend( $.ui, { + version: "1.10.4", + + keyCode: { + BACKSPACE: 8, + COMMA: 188, + DELETE: 46, + DOWN: 40, + END: 35, + ENTER: 13, + ESCAPE: 27, + HOME: 36, + LEFT: 37, + NUMPAD_ADD: 107, + NUMPAD_DECIMAL: 110, + NUMPAD_DIVIDE: 111, + NUMPAD_ENTER: 108, + NUMPAD_MULTIPLY: 106, + NUMPAD_SUBTRACT: 109, + PAGE_DOWN: 34, + PAGE_UP: 33, + PERIOD: 190, + RIGHT: 39, + SPACE: 32, + TAB: 9, + UP: 38 + } +}); + +// plugins +$.fn.extend({ + focus: (function( orig ) { + return function( delay, fn ) { + return typeof delay === "number" ? + this.each(function() { + var elem = this; + setTimeout(function() { + $( elem ).focus(); + if ( fn ) { + fn.call( elem ); + } + }, delay ); + }) : + orig.apply( this, arguments ); + }; + })( $.fn.focus ), + + scrollParent: function() { + var scrollParent; + if (($.ui.ie && (/(static|relative)/).test(this.css("position"))) || (/absolute/).test(this.css("position"))) { + scrollParent = this.parents().filter(function() { + return (/(relative|absolute|fixed)/).test($.css(this,"position")) && (/(auto|scroll)/).test($.css(this,"overflow")+$.css(this,"overflow-y")+$.css(this,"overflow-x")); + }).eq(0); + } else { + scrollParent = this.parents().filter(function() { + return (/(auto|scroll)/).test($.css(this,"overflow")+$.css(this,"overflow-y")+$.css(this,"overflow-x")); + }).eq(0); + } + + return (/fixed/).test(this.css("position")) || !scrollParent.length ? $(document) : scrollParent; + }, + + zIndex: function( zIndex ) { + if ( zIndex !== undefined ) { + return this.css( "zIndex", zIndex ); + } + + if ( this.length ) { + var elem = $( this[ 0 ] ), position, value; + while ( elem.length && elem[ 0 ] !== document ) { + // Ignore z-index if position is set to a value where z-index is ignored by the browser + // This makes behavior of this function consistent across browsers + // WebKit always returns auto if the element is positioned + position = elem.css( "position" ); + if ( position === "absolute" || position === "relative" || position === "fixed" ) { + // IE returns 0 when zIndex is not specified + // other browsers return a string + // we ignore the case of nested elements with an explicit value of 0 + // <div style="z-index: -10;"><div style="z-index: 0;"></div></div> + value = parseInt( elem.css( "zIndex" ), 10 ); + if ( !isNaN( value ) && value !== 0 ) { + return value; + } + } + elem = elem.parent(); + } + } + + return 0; + }, + + uniqueId: function() { + return this.each(function() { + if ( !this.id ) { + this.id = "ui-id-" + (++uuid); + } + }); + }, + + removeUniqueId: function() { + return this.each(function() { + if ( runiqueId.test( this.id ) ) { + $( this ).removeAttr( "id" ); + } + }); + } +}); + +// selectors +function focusable( element, isTabIndexNotNaN ) { + var map, mapName, img, + nodeName = element.nodeName.toLowerCase(); + if ( "area" === nodeName ) { + map = element.parentNode; + mapName = map.name; + if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) { + return false; + } + img = $( "img[usemap=#" + mapName + "]" )[0]; + return !!img && visible( img ); + } + return ( /input|select|textarea|button|object/.test( nodeName ) ? + !element.disabled : + "a" === nodeName ? + element.href || isTabIndexNotNaN : + isTabIndexNotNaN) && + // the element and all of its ancestors must be visible + visible( element ); +} + +function visible( element ) { + return $.expr.filters.visible( element ) && + !$( element ).parents().addBack().filter(function() { + return $.css( this, "visibility" ) === "hidden"; + }).length; +} + +$.extend( $.expr[ ":" ], { + data: $.expr.createPseudo ? + $.expr.createPseudo(function( dataName ) { + return function( elem ) { + return !!$.data( elem, dataName ); + }; + }) : + // support: jQuery <1.8 + function( elem, i, match ) { + return !!$.data( elem, match[ 3 ] ); + }, + + focusable: function( element ) { + return focusable( element, !isNaN( $.attr( element, "tabindex" ) ) ); + }, + + tabbable: function( element ) { + var tabIndex = $.attr( element, "tabindex" ), + isTabIndexNaN = isNaN( tabIndex ); + return ( isTabIndexNaN || tabIndex >= 0 ) && focusable( element, !isTabIndexNaN ); + } +}); + +// support: jQuery <1.8 +if ( !$( "<a>" ).outerWidth( 1 ).jquery ) { + $.each( [ "Width", "Height" ], function( i, name ) { + var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ], + type = name.toLowerCase(), + orig = { + innerWidth: $.fn.innerWidth, + innerHeight: $.fn.innerHeight, + outerWidth: $.fn.outerWidth, + outerHeight: $.fn.outerHeight + }; + + function reduce( elem, size, border, margin ) { + $.each( side, function() { + size -= parseFloat( $.css( elem, "padding" + this ) ) || 0; + if ( border ) { + size -= parseFloat( $.css( elem, "border" + this + "Width" ) ) || 0; + } + if ( margin ) { + size -= parseFloat( $.css( elem, "margin" + this ) ) || 0; + } + }); + return size; + } + + $.fn[ "inner" + name ] = function( size ) { + if ( size === undefined ) { + return orig[ "inner" + name ].call( this ); + } + + return this.each(function() { + $( this ).css( type, reduce( this, size ) + "px" ); + }); + }; + + $.fn[ "outer" + name] = function( size, margin ) { + if ( typeof size !== "number" ) { + return orig[ "outer" + name ].call( this, size ); + } + + return this.each(function() { + $( this).css( type, reduce( this, size, true, margin ) + "px" ); + }); + }; + }); +} + +// support: jQuery <1.8 +if ( !$.fn.addBack ) { + $.fn.addBack = function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter( selector ) + ); + }; +} + +// support: jQuery 1.6.1, 1.6.2 (http://bugs.jquery.com/ticket/9413) +if ( $( "<a>" ).data( "a-b", "a" ).removeData( "a-b" ).data( "a-b" ) ) { + $.fn.removeData = (function( removeData ) { + return function( key ) { + if ( arguments.length ) { + return removeData.call( this, $.camelCase( key ) ); + } else { + return removeData.call( this ); + } + }; + })( $.fn.removeData ); +} + + + + + +// deprecated +$.ui.ie = !!/msie [\w.]+/.exec( navigator.userAgent.toLowerCase() ); + +$.support.selectstart = "onselectstart" in document.createElement( "div" ); +$.fn.extend({ + disableSelection: function() { + return this.bind( ( $.support.selectstart ? "selectstart" : "mousedown" ) + + ".ui-disableSelection", function( event ) { + event.preventDefault(); + }); + }, + + enableSelection: function() { + return this.unbind( ".ui-disableSelection" ); + } +}); + +$.extend( $.ui, { + // $.ui.plugin is deprecated. Use $.widget() extensions instead. + plugin: { + add: function( module, option, set ) { + var i, + proto = $.ui[ module ].prototype; + for ( i in set ) { + proto.plugins[ i ] = proto.plugins[ i ] || []; + proto.plugins[ i ].push( [ option, set[ i ] ] ); + } + }, + call: function( instance, name, args ) { + var i, + set = instance.plugins[ name ]; + if ( !set || !instance.element[ 0 ].parentNode || instance.element[ 0 ].parentNode.nodeType === 11 ) { + return; + } + + for ( i = 0; i < set.length; i++ ) { + if ( instance.options[ set[ i ][ 0 ] ] ) { + set[ i ][ 1 ].apply( instance.element, args ); + } + } + } + }, + + // only used by resizable + hasScroll: function( el, a ) { + + //If overflow is hidden, the element might have extra content, but the user wants to hide it + if ( $( el ).css( "overflow" ) === "hidden") { + return false; + } + + var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop", + has = false; + + if ( el[ scroll ] > 0 ) { + return true; + } + + // TODO: determine which cases actually cause this to happen + // if the element doesn't have the scroll set, see if it's possible to + // set the scroll + el[ scroll ] = 1; + has = ( el[ scroll ] > 0 ); + el[ scroll ] = 0; + return has; + } +}); + +})( jQuery ); + +/*! + * jQuery UI Widget 1.10.4 + * http://jqueryui.com + * + * Copyright 2014 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/jQuery.widget/ + */ +(function( $, undefined ) { + +var uuid = 0, + slice = Array.prototype.slice, + _cleanData = $.cleanData; +$.cleanData = function( elems ) { + for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) { + try { + $( elem ).triggerHandler( "remove" ); + // http://bugs.jquery.com/ticket/8235 + } catch( e ) {} + } + _cleanData( elems ); +}; + +$.widget = function( name, base, prototype ) { + var fullName, existingConstructor, constructor, basePrototype, + // proxiedPrototype allows the provided prototype to remain unmodified + // so that it can be used as a mixin for multiple widgets (#8876) + proxiedPrototype = {}, + namespace = name.split( "." )[ 0 ]; + + name = name.split( "." )[ 1 ]; + fullName = namespace + "-" + name; + + if ( !prototype ) { + prototype = base; + base = $.Widget; + } + + // create selector for plugin + $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) { + return !!$.data( elem, fullName ); + }; + + $[ namespace ] = $[ namespace ] || {}; + existingConstructor = $[ namespace ][ name ]; + constructor = $[ namespace ][ name ] = function( options, element ) { + // allow instantiation without "new" keyword + if ( !this._createWidget ) { + return new constructor( options, element ); + } + + // allow instantiation without initializing for simple inheritance + // must use "new" keyword (the code above always passes args) + if ( arguments.length ) { + this._createWidget( options, element ); + } + }; + // extend with the existing constructor to carry over any static properties + $.extend( constructor, existingConstructor, { + version: prototype.version, + // copy the object used to create the prototype in case we need to + // redefine the widget later + _proto: $.extend( {}, prototype ), + // track widgets that inherit from this widget in case this widget is + // redefined after a widget inherits from it + _childConstructors: [] + }); + + basePrototype = new base(); + // we need to make the options hash a property directly on the new instance + // otherwise we'll modify the options hash on the prototype that we're + // inheriting from + basePrototype.options = $.widget.extend( {}, basePrototype.options ); + $.each( prototype, function( prop, value ) { + if ( !$.isFunction( value ) ) { + proxiedPrototype[ prop ] = value; + return; + } + proxiedPrototype[ prop ] = (function() { + var _super = function() { + return base.prototype[ prop ].apply( this, arguments ); + }, + _superApply = function( args ) { + return base.prototype[ prop ].apply( this, args ); + }; + return function() { + var __super = this._super, + __superApply = this._superApply, + returnValue; + + this._super = _super; + this._superApply = _superApply; + + returnValue = value.apply( this, arguments ); + + this._super = __super; + this._superApply = __superApply; + + return returnValue; + }; + })(); + }); + constructor.prototype = $.widget.extend( basePrototype, { + // TODO: remove support for widgetEventPrefix + // always use the name + a colon as the prefix, e.g., draggable:start + // don't prefix for widgets that aren't DOM-based + widgetEventPrefix: existingConstructor ? (basePrototype.widgetEventPrefix || name) : name + }, proxiedPrototype, { + constructor: constructor, + namespace: namespace, + widgetName: name, + widgetFullName: fullName + }); + + // If this widget is being redefined then we need to find all widgets that + // are inheriting from it and redefine all of them so that they inherit from + // the new version of this widget. We're essentially trying to replace one + // level in the prototype chain. + if ( existingConstructor ) { + $.each( existingConstructor._childConstructors, function( i, child ) { + var childPrototype = child.prototype; + + // redefine the child widget using the same prototype that was + // originally used, but inherit from the new version of the base + $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto ); + }); + // remove the list of existing child constructors from the old constructor + // so the old child constructors can be garbage collected + delete existingConstructor._childConstructors; + } else { + base._childConstructors.push( constructor ); + } + + $.widget.bridge( name, constructor ); +}; + +$.widget.extend = function( target ) { + var input = slice.call( arguments, 1 ), + inputIndex = 0, + inputLength = input.length, + key, + value; + for ( ; inputIndex < inputLength; inputIndex++ ) { + for ( key in input[ inputIndex ] ) { + value = input[ inputIndex ][ key ]; + if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) { + // Clone objects + if ( $.isPlainObject( value ) ) { + target[ key ] = $.isPlainObject( target[ key ] ) ? + $.widget.extend( {}, target[ key ], value ) : + // Don't extend strings, arrays, etc. with objects + $.widget.extend( {}, value ); + // Copy everything else by reference + } else { + target[ key ] = value; + } + } + } + } + return target; +}; + +$.widget.bridge = function( name, object ) { + var fullName = object.prototype.widgetFullName || name; + $.fn[ name ] = function( options ) { + var isMethodCall = typeof options === "string", + args = slice.call( arguments, 1 ), + returnValue = this; + + // allow multiple hashes to be passed on init + options = !isMethodCall && args.length ? + $.widget.extend.apply( null, [ options ].concat(args) ) : + options; + + if ( isMethodCall ) { + this.each(function() { + var methodValue, + instance = $.data( this, fullName ); + if ( !instance ) { + return $.error( "cannot call methods on " + name + " prior to initialization; " + + "attempted to call method '" + options + "'" ); + } + if ( !$.isFunction( instance[options] ) || options.charAt( 0 ) === "_" ) { + return $.error( "no such method '" + options + "' for " + name + " widget instance" ); + } + methodValue = instance[ options ].apply( instance, args ); + if ( methodValue !== instance && methodValue !== undefined ) { + returnValue = methodValue && methodValue.jquery ? + returnValue.pushStack( methodValue.get() ) : + methodValue; + return false; + } + }); + } else { + this.each(function() { + var instance = $.data( this, fullName ); + if ( instance ) { + instance.option( options || {} )._init(); + } else { + $.data( this, fullName, new object( options, this ) ); + } + }); + } + + return returnValue; + }; +}; + +$.Widget = function( /* options, element */ ) {}; +$.Widget._childConstructors = []; + +$.Widget.prototype = { + widgetName: "widget", + widgetEventPrefix: "", + defaultElement: "<div>", + options: { + disabled: false, + + // callbacks + create: null + }, + _createWidget: function( options, element ) { + element = $( element || this.defaultElement || this )[ 0 ]; + this.element = $( element ); + this.uuid = uuid++; + this.eventNamespace = "." + this.widgetName + this.uuid; + this.options = $.widget.extend( {}, + this.options, + this._getCreateOptions(), + options ); + + this.bindings = $(); + this.hoverable = $(); + this.focusable = $(); + + if ( element !== this ) { + $.data( element, this.widgetFullName, this ); + this._on( true, this.element, { + remove: function( event ) { + if ( event.target === element ) { + this.destroy(); + } + } + }); + this.document = $( element.style ? + // element within the document + element.ownerDocument : + // element is window or document + element.document || element ); + this.window = $( this.document[0].defaultView || this.document[0].parentWindow ); + } + + this._create(); + this._trigger( "create", null, this._getCreateEventData() ); + this._init(); + }, + _getCreateOptions: $.noop, + _getCreateEventData: $.noop, + _create: $.noop, + _init: $.noop, + + destroy: function() { + this._destroy(); + // we can probably remove the unbind calls in 2.0 + // all event bindings should go through this._on() + this.element + .unbind( this.eventNamespace ) + // 1.9 BC for #7810 + // TODO remove dual storage + .removeData( this.widgetName ) + .removeData( this.widgetFullName ) + // support: jquery <1.6.3 + // http://bugs.jquery.com/ticket/9413 + .removeData( $.camelCase( this.widgetFullName ) ); + this.widget() + .unbind( this.eventNamespace ) + .removeAttr( "aria-disabled" ) + .removeClass( + this.widgetFullName + "-disabled " + + "ui-state-disabled" ); + + // clean up events and states + this.bindings.unbind( this.eventNamespace ); + this.hoverable.removeClass( "ui-state-hover" ); + this.focusable.removeClass( "ui-state-focus" ); + }, + _destroy: $.noop, + + widget: function() { + return this.element; + }, + + option: function( key, value ) { + var options = key, + parts, + curOption, + i; + + if ( arguments.length === 0 ) { + // don't return a reference to the internal hash + return $.widget.extend( {}, this.options ); + } + + if ( typeof key === "string" ) { + // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } } + options = {}; + parts = key.split( "." ); + key = parts.shift(); + if ( parts.length ) { + curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] ); + for ( i = 0; i < parts.length - 1; i++ ) { + curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {}; + curOption = curOption[ parts[ i ] ]; + } + key = parts.pop(); + if ( arguments.length === 1 ) { + return curOption[ key ] === undefined ? null : curOption[ key ]; + } + curOption[ key ] = value; + } else { + if ( arguments.length === 1 ) { + return this.options[ key ] === undefined ? null : this.options[ key ]; + } + options[ key ] = value; + } + } + + this._setOptions( options ); + + return this; + }, + _setOptions: function( options ) { + var key; + + for ( key in options ) { + this._setOption( key, options[ key ] ); + } + + return this; + }, + _setOption: function( key, value ) { + this.options[ key ] = value; + + if ( key === "disabled" ) { + this.widget() + .toggleClass( this.widgetFullName + "-disabled ui-state-disabled", !!value ) + .attr( "aria-disabled", value ); + this.hoverable.removeClass( "ui-state-hover" ); + this.focusable.removeClass( "ui-state-focus" ); + } + + return this; + }, + + enable: function() { + return this._setOption( "disabled", false ); + }, + disable: function() { + return this._setOption( "disabled", true ); + }, + + _on: function( suppressDisabledCheck, element, handlers ) { + var delegateElement, + instance = this; + + // no suppressDisabledCheck flag, shuffle arguments + if ( typeof suppressDisabledCheck !== "boolean" ) { + handlers = element; + element = suppressDisabledCheck; + suppressDisabledCheck = false; + } + + // no element argument, shuffle and use this.element + if ( !handlers ) { + handlers = element; + element = this.element; + delegateElement = this.widget(); + } else { + // accept selectors, DOM elements + element = delegateElement = $( element ); + this.bindings = this.bindings.add( element ); + } + + $.each( handlers, function( event, handler ) { + function handlerProxy() { + // allow widgets to customize the disabled handling + // - disabled as an array instead of boolean + // - disabled class as method for disabling individual parts + if ( !suppressDisabledCheck && + ( instance.options.disabled === true || + $( this ).hasClass( "ui-state-disabled" ) ) ) { + return; + } + return ( typeof handler === "string" ? instance[ handler ] : handler ) + .apply( instance, arguments ); + } + + // copy the guid so direct unbinding works + if ( typeof handler !== "string" ) { + handlerProxy.guid = handler.guid = + handler.guid || handlerProxy.guid || $.guid++; + } + + var match = event.match( /^(\w+)\s*(.*)$/ ), + eventName = match[1] + instance.eventNamespace, + selector = match[2]; + if ( selector ) { + delegateElement.delegate( selector, eventName, handlerProxy ); + } else { + element.bind( eventName, handlerProxy ); + } + }); + }, + + _off: function( element, eventName ) { + eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) + this.eventNamespace; + element.unbind( eventName ).undelegate( eventName ); + }, + + _delay: function( handler, delay ) { + function handlerProxy() { + return ( typeof handler === "string" ? instance[ handler ] : handler ) + .apply( instance, arguments ); + } + var instance = this; + return setTimeout( handlerProxy, delay || 0 ); + }, + + _hoverable: function( element ) { + this.hoverable = this.hoverable.add( element ); + this._on( element, { + mouseenter: function( event ) { + $( event.currentTarget ).addClass( "ui-state-hover" ); + }, + mouseleave: function( event ) { + $( event.currentTarget ).removeClass( "ui-state-hover" ); + } + }); + }, + + _focusable: function( element ) { + this.focusable = this.focusable.add( element ); + this._on( element, { + focusin: function( event ) { + $( event.currentTarget ).addClass( "ui-state-focus" ); + }, + focusout: function( event ) { + $( event.currentTarget ).removeClass( "ui-state-focus" ); + } + }); + }, + + _trigger: function( type, event, data ) { + var prop, orig, + callback = this.options[ type ]; + + data = data || {}; + event = $.Event( event ); + event.type = ( type === this.widgetEventPrefix ? + type : + this.widgetEventPrefix + type ).toLowerCase(); + // the original event may come from any element + // so we need to reset the target on the new event + event.target = this.element[ 0 ]; + + // copy original event properties over to the new event + orig = event.originalEvent; + if ( orig ) { + for ( prop in orig ) { + if ( !( prop in event ) ) { + event[ prop ] = orig[ prop ]; + } + } + } + + this.element.trigger( event, data ); + return !( $.isFunction( callback ) && + callback.apply( this.element[0], [ event ].concat( data ) ) === false || + event.isDefaultPrevented() ); + } +}; + +$.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) { + $.Widget.prototype[ "_" + method ] = function( element, options, callback ) { + if ( typeof options === "string" ) { + options = { effect: options }; + } + var hasOptions, + effectName = !options ? + method : + options === true || typeof options === "number" ? + defaultEffect : + options.effect || defaultEffect; + options = options || {}; + if ( typeof options === "number" ) { + options = { duration: options }; + } + hasOptions = !$.isEmptyObject( options ); + options.complete = callback; + if ( options.delay ) { + element.delay( options.delay ); + } + if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) { + element[ method ]( options ); + } else if ( effectName !== method && element[ effectName ] ) { + element[ effectName ]( options.duration, options.easing, callback ); + } else { + element.queue(function( next ) { + $( this )[ method ](); + if ( callback ) { + callback.call( element[ 0 ] ); + } + next(); + }); + } + }; +}); + +})( jQuery ); + +/*! + * jQuery UI Mouse 1.10.4 + * http://jqueryui.com + * + * Copyright 2014 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/mouse/ + * + * Depends: + * jquery.ui.widget.js + */ +(function( $, undefined ) { + +var mouseHandled = false; +$( document ).mouseup( function() { + mouseHandled = false; +}); + +$.widget("ui.mouse", { + version: "1.10.4", + options: { + cancel: "input,textarea,button,select,option", + distance: 1, + delay: 0 + }, + _mouseInit: function() { + var that = this; + + this.element + .bind("mousedown."+this.widgetName, function(event) { + return that._mouseDown(event); + }) + .bind("click."+this.widgetName, function(event) { + if (true === $.data(event.target, that.widgetName + ".preventClickEvent")) { + $.removeData(event.target, that.widgetName + ".preventClickEvent"); + event.stopImmediatePropagation(); + return false; + } + }); + + this.started = false; + }, + + // TODO: make sure destroying one instance of mouse doesn't mess with + // other instances of mouse + _mouseDestroy: function() { + this.element.unbind("."+this.widgetName); + if ( this._mouseMoveDelegate ) { + $(document) + .unbind("mousemove."+this.widgetName, this._mouseMoveDelegate) + .unbind("mouseup."+this.widgetName, this._mouseUpDelegate); + } + }, + + _mouseDown: function(event) { + // don't let more than one widget handle mouseStart + if( mouseHandled ) { return; } + + // we may have missed mouseup (out of window) + (this._mouseStarted && this._mouseUp(event)); + + this._mouseDownEvent = event; + + var that = this, + btnIsLeft = (event.which === 1), + // event.target.nodeName works around a bug in IE 8 with + // disabled inputs (#7620) + elIsCancel = (typeof this.options.cancel === "string" && event.target.nodeName ? $(event.target).closest(this.options.cancel).length : false); + if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) { + return true; + } + + this.mouseDelayMet = !this.options.delay; + if (!this.mouseDelayMet) { + this._mouseDelayTimer = setTimeout(function() { + that.mouseDelayMet = true; + }, this.options.delay); + } + + if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) { + this._mouseStarted = (this._mouseStart(event) !== false); + if (!this._mouseStarted) { + event.preventDefault(); + return true; + } + } + + // Click event may never have fired (Gecko & Opera) + if (true === $.data(event.target, this.widgetName + ".preventClickEvent")) { + $.removeData(event.target, this.widgetName + ".preventClickEvent"); + } + + // these delegates are required to keep context + this._mouseMoveDelegate = function(event) { + return that._mouseMove(event); + }; + this._mouseUpDelegate = function(event) { + return that._mouseUp(event); + }; + $(document) + .bind("mousemove."+this.widgetName, this._mouseMoveDelegate) + .bind("mouseup."+this.widgetName, this._mouseUpDelegate); + + event.preventDefault(); + + mouseHandled = true; + return true; + }, + + _mouseMove: function(event) { + // IE mouseup check - mouseup happened when mouse was out of window + if ($.ui.ie && ( !document.documentMode || document.documentMode < 9 ) && !event.button) { + return this._mouseUp(event); + } + + if (this._mouseStarted) { + this._mouseDrag(event); + return event.preventDefault(); + } + + if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) { + this._mouseStarted = + (this._mouseStart(this._mouseDownEvent, event) !== false); + (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event)); + } + + return !this._mouseStarted; + }, + + _mouseUp: function(event) { + $(document) + .unbind("mousemove."+this.widgetName, this._mouseMoveDelegate) + .unbind("mouseup."+this.widgetName, this._mouseUpDelegate); + + if (this._mouseStarted) { + this._mouseStarted = false; + + if (event.target === this._mouseDownEvent.target) { + $.data(event.target, this.widgetName + ".preventClickEvent", true); + } + + this._mouseStop(event); + } + + return false; + }, + + _mouseDistanceMet: function(event) { + return (Math.max( + Math.abs(this._mouseDownEvent.pageX - event.pageX), + Math.abs(this._mouseDownEvent.pageY - event.pageY) + ) >= this.options.distance + ); + }, + + _mouseDelayMet: function(/* event */) { + return this.mouseDelayMet; + }, + + // These are placeholder methods, to be overriden by extending plugin + _mouseStart: function(/* event */) {}, + _mouseDrag: function(/* event */) {}, + _mouseStop: function(/* event */) {}, + _mouseCapture: function(/* event */) { return true; } +}); + +})(jQuery); + +/*! + * jQuery UI Position 1.10.4 + * http://jqueryui.com + * + * Copyright 2014 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/position/ + */ +(function( $, undefined ) { + +$.ui = $.ui || {}; + +var cachedScrollbarWidth, + max = Math.max, + abs = Math.abs, + round = Math.round, + rhorizontal = /left|center|right/, + rvertical = /top|center|bottom/, + roffset = /[\+\-]\d+(\.[\d]+)?%?/, + rposition = /^\w+/, + rpercent = /%$/, + _position = $.fn.position; + +function getOffsets( offsets, width, height ) { + return [ + parseFloat( offsets[ 0 ] ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ), + parseFloat( offsets[ 1 ] ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 ) + ]; +} + +function parseCss( element, property ) { + return parseInt( $.css( element, property ), 10 ) || 0; +} + +function getDimensions( elem ) { + var raw = elem[0]; + if ( raw.nodeType === 9 ) { + return { + width: elem.width(), + height: elem.height(), + offset: { top: 0, left: 0 } + }; + } + if ( $.isWindow( raw ) ) { + return { + width: elem.width(), + height: elem.height(), + offset: { top: elem.scrollTop(), left: elem.scrollLeft() } + }; + } + if ( raw.preventDefault ) { + return { + width: 0, + height: 0, + offset: { top: raw.pageY, left: raw.pageX } + }; + } + return { + width: elem.outerWidth(), + height: elem.outerHeight(), + offset: elem.offset() + }; +} + +$.position = { + scrollbarWidth: function() { + if ( cachedScrollbarWidth !== undefined ) { + return cachedScrollbarWidth; + } + var w1, w2, + div = $( "<div style='display:block;position:absolute;width:50px;height:50px;overflow:hidden;'><div style='height:100px;width:auto;'></div></div>" ), + innerDiv = div.children()[0]; + + $( "body" ).append( div ); + w1 = innerDiv.offsetWidth; + div.css( "overflow", "scroll" ); + + w2 = innerDiv.offsetWidth; + + if ( w1 === w2 ) { + w2 = div[0].clientWidth; + } + + div.remove(); + + return (cachedScrollbarWidth = w1 - w2); + }, + getScrollInfo: function( within ) { + var overflowX = within.isWindow || within.isDocument ? "" : + within.element.css( "overflow-x" ), + overflowY = within.isWindow || within.isDocument ? "" : + within.element.css( "overflow-y" ), + hasOverflowX = overflowX === "scroll" || + ( overflowX === "auto" && within.width < within.element[0].scrollWidth ), + hasOverflowY = overflowY === "scroll" || + ( overflowY === "auto" && within.height < within.element[0].scrollHeight ); + return { + width: hasOverflowY ? $.position.scrollbarWidth() : 0, + height: hasOverflowX ? $.position.scrollbarWidth() : 0 + }; + }, + getWithinInfo: function( element ) { + var withinElement = $( element || window ), + isWindow = $.isWindow( withinElement[0] ), + isDocument = !!withinElement[ 0 ] && withinElement[ 0 ].nodeType === 9; + return { + element: withinElement, + isWindow: isWindow, + isDocument: isDocument, + offset: withinElement.offset() || { left: 0, top: 0 }, + scrollLeft: withinElement.scrollLeft(), + scrollTop: withinElement.scrollTop(), + width: isWindow ? withinElement.width() : withinElement.outerWidth(), + height: isWindow ? withinElement.height() : withinElement.outerHeight() + }; + } +}; + +$.fn.position = function( options ) { + if ( !options || !options.of ) { + return _position.apply( this, arguments ); + } + + // make a copy, we don't want to modify arguments + options = $.extend( {}, options ); + + var atOffset, targetWidth, targetHeight, targetOffset, basePosition, dimensions, + target = $( options.of ), + within = $.position.getWithinInfo( options.within ), + scrollInfo = $.position.getScrollInfo( within ), + collision = ( options.collision || "flip" ).split( " " ), + offsets = {}; + + dimensions = getDimensions( target ); + if ( target[0].preventDefault ) { + // force left top to allow flipping + options.at = "left top"; + } + targetWidth = dimensions.width; + targetHeight = dimensions.height; + targetOffset = dimensions.offset; + // clone to reuse original targetOffset later + basePosition = $.extend( {}, targetOffset ); + + // force my and at to have valid horizontal and vertical positions + // if a value is missing or invalid, it will be converted to center + $.each( [ "my", "at" ], function() { + var pos = ( options[ this ] || "" ).split( " " ), + horizontalOffset, + verticalOffset; + + if ( pos.length === 1) { + pos = rhorizontal.test( pos[ 0 ] ) ? + pos.concat( [ "center" ] ) : + rvertical.test( pos[ 0 ] ) ? + [ "center" ].concat( pos ) : + [ "center", "center" ]; + } + pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : "center"; + pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : "center"; + + // calculate offsets + horizontalOffset = roffset.exec( pos[ 0 ] ); + verticalOffset = roffset.exec( pos[ 1 ] ); + offsets[ this ] = [ + horizontalOffset ? horizontalOffset[ 0 ] : 0, + verticalOffset ? verticalOffset[ 0 ] : 0 + ]; + + // reduce to just the positions without the offsets + options[ this ] = [ + rposition.exec( pos[ 0 ] )[ 0 ], + rposition.exec( pos[ 1 ] )[ 0 ] + ]; + }); + + // normalize collision option + if ( collision.length === 1 ) { + collision[ 1 ] = collision[ 0 ]; + } + + if ( options.at[ 0 ] === "right" ) { + basePosition.left += targetWidth; + } else if ( options.at[ 0 ] === "center" ) { + basePosition.left += targetWidth / 2; + } + + if ( options.at[ 1 ] === "bottom" ) { + basePosition.top += targetHeight; + } else if ( options.at[ 1 ] === "center" ) { + basePosition.top += targetHeight / 2; + } + + atOffset = getOffsets( offsets.at, targetWidth, targetHeight ); + basePosition.left += atOffset[ 0 ]; + basePosition.top += atOffset[ 1 ]; + + return this.each(function() { + var collisionPosition, using, + elem = $( this ), + elemWidth = elem.outerWidth(), + elemHeight = elem.outerHeight(), + marginLeft = parseCss( this, "marginLeft" ), + marginTop = parseCss( this, "marginTop" ), + collisionWidth = elemWidth + marginLeft + parseCss( this, "marginRight" ) + scrollInfo.width, + collisionHeight = elemHeight + marginTop + parseCss( this, "marginBottom" ) + scrollInfo.height, + position = $.extend( {}, basePosition ), + myOffset = getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() ); + + if ( options.my[ 0 ] === "right" ) { + position.left -= elemWidth; + } else if ( options.my[ 0 ] === "center" ) { + position.left -= elemWidth / 2; + } + + if ( options.my[ 1 ] === "bottom" ) { + position.top -= elemHeight; + } else if ( options.my[ 1 ] === "center" ) { + position.top -= elemHeight / 2; + } + + position.left += myOffset[ 0 ]; + position.top += myOffset[ 1 ]; + + // if the browser doesn't support fractions, then round for consistent results + if ( !$.support.offsetFractions ) { + position.left = round( position.left ); + position.top = round( position.top ); + } + + collisionPosition = { + marginLeft: marginLeft, + marginTop: marginTop + }; + + $.each( [ "left", "top" ], function( i, dir ) { + if ( $.ui.position[ collision[ i ] ] ) { + $.ui.position[ collision[ i ] ][ dir ]( position, { + targetWidth: targetWidth, + targetHeight: targetHeight, + elemWidth: elemWidth, + elemHeight: elemHeight, + collisionPosition: collisionPosition, + collisionWidth: collisionWidth, + collisionHeight: collisionHeight, + offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ], + my: options.my, + at: options.at, + within: within, + elem : elem + }); + } + }); + + if ( options.using ) { + // adds feedback as second argument to using callback, if present + using = function( props ) { + var left = targetOffset.left - position.left, + right = left + targetWidth - elemWidth, + top = targetOffset.top - position.top, + bottom = top + targetHeight - elemHeight, + feedback = { + target: { + element: target, + left: targetOffset.left, + top: targetOffset.top, + width: targetWidth, + height: targetHeight + }, + element: { + element: elem, + left: position.left, + top: position.top, + width: elemWidth, + height: elemHeight + }, + horizontal: right < 0 ? "left" : left > 0 ? "right" : "center", + vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle" + }; + if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) { + feedback.horizontal = "center"; + } + if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) { + feedback.vertical = "middle"; + } + if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) { + feedback.important = "horizontal"; + } else { + feedback.important = "vertical"; + } + options.using.call( this, props, feedback ); + }; + } + + elem.offset( $.extend( position, { using: using } ) ); + }); +}; + +$.ui.position = { + fit: { + left: function( position, data ) { + var within = data.within, + withinOffset = within.isWindow ? within.scrollLeft : within.offset.left, + outerWidth = within.width, + collisionPosLeft = position.left - data.collisionPosition.marginLeft, + overLeft = withinOffset - collisionPosLeft, + overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset, + newOverRight; + + // element is wider than within + if ( data.collisionWidth > outerWidth ) { + // element is initially over the left side of within + if ( overLeft > 0 && overRight <= 0 ) { + newOverRight = position.left + overLeft + data.collisionWidth - outerWidth - withinOffset; + position.left += overLeft - newOverRight; + // element is initially over right side of within + } else if ( overRight > 0 && overLeft <= 0 ) { + position.left = withinOffset; + // element is initially over both left and right sides of within + } else { + if ( overLeft > overRight ) { + position.left = withinOffset + outerWidth - data.collisionWidth; + } else { + position.left = withinOffset; + } + } + // too far left -> align with left edge + } else if ( overLeft > 0 ) { + position.left += overLeft; + // too far right -> align with right edge + } else if ( overRight > 0 ) { + position.left -= overRight; + // adjust based on position and margin + } else { + position.left = max( position.left - collisionPosLeft, position.left ); + } + }, + top: function( position, data ) { + var within = data.within, + withinOffset = within.isWindow ? within.scrollTop : within.offset.top, + outerHeight = data.within.height, + collisionPosTop = position.top - data.collisionPosition.marginTop, + overTop = withinOffset - collisionPosTop, + overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset, + newOverBottom; + + // element is taller than within + if ( data.collisionHeight > outerHeight ) { + // element is initially over the top of within + if ( overTop > 0 && overBottom <= 0 ) { + newOverBottom = position.top + overTop + data.collisionHeight - outerHeight - withinOffset; + position.top += overTop - newOverBottom; + // element is initially over bottom of within + } else if ( overBottom > 0 && overTop <= 0 ) { + position.top = withinOffset; + // element is initially over both top and bottom of within + } else { + if ( overTop > overBottom ) { + position.top = withinOffset + outerHeight - data.collisionHeight; + } else { + position.top = withinOffset; + } + } + // too far up -> align with top + } else if ( overTop > 0 ) { + position.top += overTop; + // too far down -> align with bottom edge + } else if ( overBottom > 0 ) { + position.top -= overBottom; + // adjust based on position and margin + } else { + position.top = max( position.top - collisionPosTop, position.top ); + } + } + }, + flip: { + left: function( position, data ) { + var within = data.within, + withinOffset = within.offset.left + within.scrollLeft, + outerWidth = within.width, + offsetLeft = within.isWindow ? within.scrollLeft : within.offset.left, + collisionPosLeft = position.left - data.collisionPosition.marginLeft, + overLeft = collisionPosLeft - offsetLeft, + overRight = collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft, + myOffset = data.my[ 0 ] === "left" ? + -data.elemWidth : + data.my[ 0 ] === "right" ? + data.elemWidth : + 0, + atOffset = data.at[ 0 ] === "left" ? + data.targetWidth : + data.at[ 0 ] === "right" ? + -data.targetWidth : + 0, + offset = -2 * data.offset[ 0 ], + newOverRight, + newOverLeft; + + if ( overLeft < 0 ) { + newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth - outerWidth - withinOffset; + if ( newOverRight < 0 || newOverRight < abs( overLeft ) ) { + position.left += myOffset + atOffset + offset; + } + } + else if ( overRight > 0 ) { + newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset + atOffset + offset - offsetLeft; + if ( newOverLeft > 0 || abs( newOverLeft ) < overRight ) { + position.left += myOffset + atOffset + offset; + } + } + }, + top: function( position, data ) { + var within = data.within, + withinOffset = within.offset.top + within.scrollTop, + outerHeight = within.height, + offsetTop = within.isWindow ? within.scrollTop : within.offset.top, + collisionPosTop = position.top - data.collisionPosition.marginTop, + overTop = collisionPosTop - offsetTop, + overBottom = collisionPosTop + data.collisionHeight - outerHeight - offsetTop, + top = data.my[ 1 ] === "top", + myOffset = top ? + -data.elemHeight : + data.my[ 1 ] === "bottom" ? + data.elemHeight : + 0, + atOffset = data.at[ 1 ] === "top" ? + data.targetHeight : + data.at[ 1 ] === "bottom" ? + -data.targetHeight : + 0, + offset = -2 * data.offset[ 1 ], + newOverTop, + newOverBottom; + if ( overTop < 0 ) { + newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight - outerHeight - withinOffset; + if ( ( position.top + myOffset + atOffset + offset) > overTop && ( newOverBottom < 0 || newOverBottom < abs( overTop ) ) ) { + position.top += myOffset + atOffset + offset; + } + } + else if ( overBottom > 0 ) { + newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset + offset - offsetTop; + if ( ( position.top + myOffset + atOffset + offset) > overBottom && ( newOverTop > 0 || abs( newOverTop ) < overBottom ) ) { + position.top += myOffset + atOffset + offset; + } + } + } + }, + flipfit: { + left: function() { + $.ui.position.flip.left.apply( this, arguments ); + $.ui.position.fit.left.apply( this, arguments ); + }, + top: function() { + $.ui.position.flip.top.apply( this, arguments ); + $.ui.position.fit.top.apply( this, arguments ); + } + } +}; + +// fraction support test +(function () { + var testElement, testElementParent, testElementStyle, offsetLeft, i, + body = document.getElementsByTagName( "body" )[ 0 ], + div = document.createElement( "div" ); + + //Create a "fake body" for testing based on method used in jQuery.support + testElement = document.createElement( body ? "div" : "body" ); + testElementStyle = { + visibility: "hidden", + width: 0, + height: 0, + border: 0, + margin: 0, + background: "none" + }; + if ( body ) { + $.extend( testElementStyle, { + position: "absolute", + left: "-1000px", + top: "-1000px" + }); + } + for ( i in testElementStyle ) { + testElement.style[ i ] = testElementStyle[ i ]; + } + testElement.appendChild( div ); + testElementParent = body || document.documentElement; + testElementParent.insertBefore( testElement, testElementParent.firstChild ); + + div.style.cssText = "position: absolute; left: 10.7432222px;"; + + offsetLeft = $( div ).offset().left; + $.support.offsetFractions = offsetLeft > 10 && offsetLeft < 11; + + testElement.innerHTML = ""; + testElementParent.removeChild( testElement ); +})(); + +}( jQuery ) ); + +/*! + * jQuery UI Button 1.10.4 + * http://jqueryui.com + * + * Copyright 2014 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/button/ + * + * Depends: + * jquery.ui.core.js + * jquery.ui.widget.js + */ +(function( $, undefined ) { + +var lastActive, + baseClasses = "ui-button ui-widget ui-state-default ui-corner-all", + typeClasses = "ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only", + formResetHandler = function() { + var form = $( this ); + setTimeout(function() { + form.find( ":ui-button" ).button( "refresh" ); + }, 1 ); + }, + radioGroup = function( radio ) { + var name = radio.name, + form = radio.form, + radios = $( [] ); + if ( name ) { + name = name.replace( /'/g, "\\'" ); + if ( form ) { + radios = $( form ).find( "[name='" + name + "']" ); + } else { + radios = $( "[name='" + name + "']", radio.ownerDocument ) + .filter(function() { + return !this.form; + }); + } + } + return radios; + }; + +$.widget( "ui.button", { + version: "1.10.4", + defaultElement: "<button>", + options: { + disabled: null, + text: true, + label: null, + icons: { + primary: null, + secondary: null + } + }, + _create: function() { + this.element.closest( "form" ) + .unbind( "reset" + this.eventNamespace ) + .bind( "reset" + this.eventNamespace, formResetHandler ); + + if ( typeof this.options.disabled !== "boolean" ) { + this.options.disabled = !!this.element.prop( "disabled" ); + } else { + this.element.prop( "disabled", this.options.disabled ); + } + + this._determineButtonType(); + this.hasTitle = !!this.buttonElement.attr( "title" ); + + var that = this, + options = this.options, + toggleButton = this.type === "checkbox" || this.type === "radio", + activeClass = !toggleButton ? "ui-state-active" : ""; + + if ( options.label === null ) { + options.label = (this.type === "input" ? this.buttonElement.val() : this.buttonElement.html()); + } + + this._hoverable( this.buttonElement ); + + this.buttonElement + .addClass( baseClasses ) + .attr( "role", "button" ) + .bind( "mouseenter" + this.eventNamespace, function() { + if ( options.disabled ) { + return; + } + if ( this === lastActive ) { + $( this ).addClass( "ui-state-active" ); + } + }) + .bind( "mouseleave" + this.eventNamespace, function() { + if ( options.disabled ) { + return; + } + $( this ).removeClass( activeClass ); + }) + .bind( "click" + this.eventNamespace, function( event ) { + if ( options.disabled ) { + event.preventDefault(); + event.stopImmediatePropagation(); + } + }); + + // Can't use _focusable() because the element that receives focus + // and the element that gets the ui-state-focus class are different + this._on({ + focus: function() { + this.buttonElement.addClass( "ui-state-focus" ); + }, + blur: function() { + this.buttonElement.removeClass( "ui-state-focus" ); + } + }); + + if ( toggleButton ) { + this.element.bind( "change" + this.eventNamespace, function() { + that.refresh(); + }); + } + + if ( this.type === "checkbox" ) { + this.buttonElement.bind( "click" + this.eventNamespace, function() { + if ( options.disabled ) { + return false; + } + }); + } else if ( this.type === "radio" ) { + this.buttonElement.bind( "click" + this.eventNamespace, function() { + if ( options.disabled ) { + return false; + } + $( this ).addClass( "ui-state-active" ); + that.buttonElement.attr( "aria-pressed", "true" ); + + var radio = that.element[ 0 ]; + radioGroup( radio ) + .not( radio ) + .map(function() { + return $( this ).button( "widget" )[ 0 ]; + }) + .removeClass( "ui-state-active" ) + .attr( "aria-pressed", "false" ); + }); + } else { + this.buttonElement + .bind( "mousedown" + this.eventNamespace, function() { + if ( options.disabled ) { + return false; + } + $( this ).addClass( "ui-state-active" ); + lastActive = this; + that.document.one( "mouseup", function() { + lastActive = null; + }); + }) + .bind( "mouseup" + this.eventNamespace, function() { + if ( options.disabled ) { + return false; + } + $( this ).removeClass( "ui-state-active" ); + }) + .bind( "keydown" + this.eventNamespace, function(event) { + if ( options.disabled ) { + return false; + } + if ( event.keyCode === $.ui.keyCode.SPACE || event.keyCode === $.ui.keyCode.ENTER ) { + $( this ).addClass( "ui-state-active" ); + } + }) + // see #8559, we bind to blur here in case the button element loses + // focus between keydown and keyup, it would be left in an "active" state + .bind( "keyup" + this.eventNamespace + " blur" + this.eventNamespace, function() { + $( this ).removeClass( "ui-state-active" ); + }); + + if ( this.buttonElement.is("a") ) { + this.buttonElement.keyup(function(event) { + if ( event.keyCode === $.ui.keyCode.SPACE ) { + // TODO pass through original event correctly (just as 2nd argument doesn't work) + $( this ).click(); + } + }); + } + } + + // TODO: pull out $.Widget's handling for the disabled option into + // $.Widget.prototype._setOptionDisabled so it's easy to proxy and can + // be overridden by individual plugins + this._setOption( "disabled", options.disabled ); + this._resetButton(); + }, + + _determineButtonType: function() { + var ancestor, labelSelector, checked; + + if ( this.element.is("[type=checkbox]") ) { + this.type = "checkbox"; + } else if ( this.element.is("[type=radio]") ) { + this.type = "radio"; + } else if ( this.element.is("input") ) { + this.type = "input"; + } else { + this.type = "button"; + } + + if ( this.type === "checkbox" || this.type === "radio" ) { + // we don't search against the document in case the element + // is disconnected from the DOM + ancestor = this.element.parents().last(); + labelSelector = "label[for='" + this.element.attr("id") + "']"; + this.buttonElement = ancestor.find( labelSelector ); + if ( !this.buttonElement.length ) { + ancestor = ancestor.length ? ancestor.siblings() : this.element.siblings(); + this.buttonElement = ancestor.filter( labelSelector ); + if ( !this.buttonElement.length ) { + this.buttonElement = ancestor.find( labelSelector ); + } + } + this.element.addClass( "ui-helper-hidden-accessible" ); + + checked = this.element.is( ":checked" ); + if ( checked ) { + this.buttonElement.addClass( "ui-state-active" ); + } + this.buttonElement.prop( "aria-pressed", checked ); + } else { + this.buttonElement = this.element; + } + }, + + widget: function() { + return this.buttonElement; + }, + + _destroy: function() { + this.element + .removeClass( "ui-helper-hidden-accessible" ); + this.buttonElement + .removeClass( baseClasses + " ui-state-active " + typeClasses ) + .removeAttr( "role" ) + .removeAttr( "aria-pressed" ) + .html( this.buttonElement.find(".ui-button-text").html() ); + + if ( !this.hasTitle ) { + this.buttonElement.removeAttr( "title" ); + } + }, + + _setOption: function( key, value ) { + this._super( key, value ); + if ( key === "disabled" ) { + this.element.prop( "disabled", !!value ); + if ( value ) { + this.buttonElement.removeClass( "ui-state-focus" ); + } + return; + } + this._resetButton(); + }, + + refresh: function() { + //See #8237 & #8828 + var isDisabled = this.element.is( "input, button" ) ? this.element.is( ":disabled" ) : this.element.hasClass( "ui-button-disabled" ); + + if ( isDisabled !== this.options.disabled ) { + this._setOption( "disabled", isDisabled ); + } + if ( this.type === "radio" ) { + radioGroup( this.element[0] ).each(function() { + if ( $( this ).is( ":checked" ) ) { + $( this ).button( "widget" ) + .addClass( "ui-state-active" ) + .attr( "aria-pressed", "true" ); + } else { + $( this ).button( "widget" ) + .removeClass( "ui-state-active" ) + .attr( "aria-pressed", "false" ); + } + }); + } else if ( this.type === "checkbox" ) { + if ( this.element.is( ":checked" ) ) { + this.buttonElement + .addClass( "ui-state-active" ) + .attr( "aria-pressed", "true" ); + } else { + this.buttonElement + .removeClass( "ui-state-active" ) + .attr( "aria-pressed", "false" ); + } + } + }, + + _resetButton: function() { + if ( this.type === "input" ) { + if ( this.options.label ) { + this.element.val( this.options.label ); + } + return; + } + var buttonElement = this.buttonElement.removeClass( typeClasses ), + buttonText = $( "<span></span>", this.document[0] ) + .addClass( "ui-button-text" ) + .html( this.options.label ) + .appendTo( buttonElement.empty() ) + .text(), + icons = this.options.icons, + multipleIcons = icons.primary && icons.secondary, + buttonClasses = []; + + if ( icons.primary || icons.secondary ) { + if ( this.options.text ) { + buttonClasses.push( "ui-button-text-icon" + ( multipleIcons ? "s" : ( icons.primary ? "-primary" : "-secondary" ) ) ); + } + + if ( icons.primary ) { + buttonElement.prepend( "<span class='ui-button-icon-primary ui-icon " + icons.primary + "'></span>" ); + } + + if ( icons.secondary ) { + buttonElement.append( "<span class='ui-button-icon-secondary ui-icon " + icons.secondary + "'></span>" ); + } + + if ( !this.options.text ) { + buttonClasses.push( multipleIcons ? "ui-button-icons-only" : "ui-button-icon-only" ); + + if ( !this.hasTitle ) { + buttonElement.attr( "title", $.trim( buttonText ) ); + } + } + } else { + buttonClasses.push( "ui-button-text-only" ); + } + buttonElement.addClass( buttonClasses.join( " " ) ); + } +}); + +$.widget( "ui.buttonset", { + version: "1.10.4", + options: { + items: "button, input[type=button], input[type=submit], input[type=reset], input[type=checkbox], input[type=radio], a, :data(ui-button)" + }, + + _create: function() { + this.element.addClass( "ui-buttonset" ); + }, + + _init: function() { + this.refresh(); + }, + + _setOption: function( key, value ) { + if ( key === "disabled" ) { + this.buttons.button( "option", key, value ); + } + + this._super( key, value ); + }, + + refresh: function() { + var rtl = this.element.css( "direction" ) === "rtl"; + + this.buttons = this.element.find( this.options.items ) + .filter( ":ui-button" ) + .button( "refresh" ) + .end() + .not( ":ui-button" ) + .button() + .end() + .map(function() { + return $( this ).button( "widget" )[ 0 ]; + }) + .removeClass( "ui-corner-all ui-corner-left ui-corner-right" ) + .filter( ":first" ) + .addClass( rtl ? "ui-corner-right" : "ui-corner-left" ) + .end() + .filter( ":last" ) + .addClass( rtl ? "ui-corner-left" : "ui-corner-right" ) + .end() + .end(); + }, + + _destroy: function() { + this.element.removeClass( "ui-buttonset" ); + this.buttons + .map(function() { + return $( this ).button( "widget" )[ 0 ]; + }) + .removeClass( "ui-corner-left ui-corner-right" ) + .end() + .button( "destroy" ); + } +}); + +}( jQuery ) ); + +/*! + * jQuery UI Slider 1.10.4 + * http://jqueryui.com + * + * Copyright 2014 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/slider/ + * + * Depends: + * jquery.ui.core.js + * jquery.ui.mouse.js + * jquery.ui.widget.js + */ +//(function( $, undefined ) { +// +//// number of pages in a slider +//// (how many times can you page up/down to go through the whole range) +//var numPages = 5; +// +//$.widget( "ui.slider", $.ui.mouse, { +// version: "1.10.4", +// widgetEventPrefix: "slide", +// +// options: { +// animate: false, +// distance: 0, +// max: 100, +// min: 0, +// orientation: "horizontal", +// range: false, +// step: 1, +// value: 0, +// values: null, +// +// // callbacks +// change: null, +// slide: null, +// start: null, +// stop: null +// }, +// +// _create: function() { +// this._keySliding = false; +// this._mouseSliding = false; +// this._animateOff = true; +// this._handleIndex = null; +// this._detectOrientation(); +// this._mouseInit(); +// +// this.element +// .addClass( "ui-slider" + +// " ui-slider-" + this.orientation + +// " ui-widget" + +// " ui-widget-content" + +// " ui-corner-all"); +// +// this._refresh(); +// this._setOption( "disabled", this.options.disabled ); +// +// this._animateOff = false; +// }, +// +// _refresh: function() { +// this._createRange(); +// this._createHandles(); +// this._setupEvents(); +// this._refreshValue(); +// }, +// +// _createHandles: function() { +// var i, handleCount, +// options = this.options, +// existingHandles = this.element.find( ".ui-slider-handle" ).addClass( "ui-state-default ui-corner-all" ), +// handle = "<a class='ui-slider-handle ui-state-default ui-corner-all' href='#'></a>", +// handles = []; +// +// handleCount = ( options.values && options.values.length ) || 1; +// +// if ( existingHandles.length > handleCount ) { +// existingHandles.slice( handleCount ).remove(); +// existingHandles = existingHandles.slice( 0, handleCount ); +// } +// +// for ( i = existingHandles.length; i < handleCount; i++ ) { +// handles.push( handle ); +// } +// +// this.handles = existingHandles.add( $( handles.join( "" ) ).appendTo( this.element ) ); +// +// this.handle = this.handles.eq( 0 ); +// +// this.handles.each(function( i ) { +// $( this ).data( "ui-slider-handle-index", i ); +// }); +// }, +// +// _createRange: function() { +// var options = this.options, +// classes = ""; +// +// if ( options.range ) { +// if ( options.range === true ) { +// if ( !options.values ) { +// options.values = [ this._valueMin(), this._valueMin() ]; +// } else if ( options.values.length && options.values.length !== 2 ) { +// options.values = [ options.values[0], options.values[0] ]; +// } else if ( $.isArray( options.values ) ) { +// options.values = options.values.slice(0); +// } +// } +// +// if ( !this.range || !this.range.length ) { +// this.range = $( "<div></div>" ) +// .appendTo( this.element ); +// +// classes = "ui-slider-range" + +// // note: this isn't the most fittingly semantic framework class for this element, +// // but worked best visually with a variety of themes +// " ui-widget-header ui-corner-all"; +// } else { +// this.range.removeClass( "ui-slider-range-min ui-slider-range-max" ) +// // Handle range switching from true to min/max +// .css({ +// "left": "", +// "bottom": "" +// }); +// } +// +// this.range.addClass( classes + +// ( ( options.range === "min" || options.range === "max" ) ? " ui-slider-range-" + options.range : "" ) ); +// } else { +// if ( this.range ) { +// this.range.remove(); +// } +// this.range = null; +// } +// }, +// +// _setupEvents: function() { +// var elements = this.handles.add( this.range ).filter( "a" ); +// this._off( elements ); +// this._on( elements, this._handleEvents ); +// this._hoverable( elements ); +// this._focusable( elements ); +// }, +// +// _destroy: function() { +// this.handles.remove(); +// if ( this.range ) { +// this.range.remove(); +// } +// +// this.element +// .removeClass( "ui-slider" + +// " ui-slider-horizontal" + +// " ui-slider-vertical" + +// " ui-widget" + +// " ui-widget-content" + +// " ui-corner-all" ); +// +// this._mouseDestroy(); +// }, +// +// _mouseCapture: function( event ) { +// var position, normValue, distance, closestHandle, index, allowed, offset, mouseOverHandle, +// that = this, +// o = this.options; +// +// if ( o.disabled ) { +// return false; +// } +// +// this.elementSize = { +// width: this.element.outerWidth(), +// height: this.element.outerHeight() +// }; +// this.elementOffset = this.element.offset(); +// +// position = { x: event.pageX, y: event.pageY }; +// normValue = this._normValueFromMouse( position ); +// distance = this._valueMax() - this._valueMin() + 1; +// this.handles.each(function( i ) { +// var thisDistance = Math.abs( normValue - that.values(i) ); +// if (( distance > thisDistance ) || +// ( distance === thisDistance && +// (i === that._lastChangedValue || that.values(i) === o.min ))) { +// distance = thisDistance; +// closestHandle = $( this ); +// index = i; +// } +// }); +// +// allowed = this._start( event, index ); +// if ( allowed === false ) { +// return false; +// } +// this._mouseSliding = true; +// +// this._handleIndex = index; +// +// closestHandle +// .addClass( "ui-state-active" ) +// .focus(); +// +// offset = closestHandle.offset(); +// mouseOverHandle = !$( event.target ).parents().addBack().is( ".ui-slider-handle" ); +// this._clickOffset = mouseOverHandle ? { left: 0, top: 0 } : { +// left: event.pageX - offset.left - ( closestHandle.width() / 2 ), +// top: event.pageY - offset.top - +// ( closestHandle.height() / 2 ) - +// ( parseInt( closestHandle.css("borderTopWidth"), 10 ) || 0 ) - +// ( parseInt( closestHandle.css("borderBottomWidth"), 10 ) || 0) + +// ( parseInt( closestHandle.css("marginTop"), 10 ) || 0) +// }; +// +// if ( !this.handles.hasClass( "ui-state-hover" ) ) { +// this._slide( event, index, normValue ); +// } +// this._animateOff = true; +// return true; +// }, +// +// _mouseStart: function() { +// return true; +// }, +// +// _mouseDrag: function( event ) { +// var position = { x: event.pageX, y: event.pageY }, +// normValue = this._normValueFromMouse( position ); +// +// this._slide( event, this._handleIndex, normValue ); +// +// return false; +// }, +// +// _mouseStop: function( event ) { +// this.handles.removeClass( "ui-state-active" ); +// this._mouseSliding = false; +// +// this._stop( event, this._handleIndex ); +// this._change( event, this._handleIndex ); +// +// this._handleIndex = null; +// this._clickOffset = null; +// this._animateOff = false; +// +// return false; +// }, +// +// _detectOrientation: function() { +// this.orientation = ( this.options.orientation === "vertical" ) ? "vertical" : "horizontal"; +// }, +// +// _normValueFromMouse: function( position ) { +// var pixelTotal, +// pixelMouse, +// percentMouse, +// valueTotal, +// valueMouse; +// +// if ( this.orientation === "horizontal" ) { +// pixelTotal = this.elementSize.width; +// pixelMouse = position.x - this.elementOffset.left - ( this._clickOffset ? this._clickOffset.left : 0 ); +// } else { +// pixelTotal = this.elementSize.height; +// pixelMouse = position.y - this.elementOffset.top - ( this._clickOffset ? this._clickOffset.top : 0 ); +// } +// +// percentMouse = ( pixelMouse / pixelTotal ); +// if ( percentMouse > 1 ) { +// percentMouse = 1; +// } +// if ( percentMouse < 0 ) { +// percentMouse = 0; +// } +// if ( this.orientation === "vertical" ) { +// percentMouse = 1 - percentMouse; +// } +// +// valueTotal = this._valueMax() - this._valueMin(); +// valueMouse = this._valueMin() + percentMouse * valueTotal; +// +// return this._trimAlignValue( valueMouse ); +// }, +// +// _start: function( event, index ) { +// var uiHash = { +// handle: this.handles[ index ], +// value: this.value() +// }; +// if ( this.options.values && this.options.values.length ) { +// uiHash.value = this.values( index ); +// uiHash.values = this.values(); +// } +// return this._trigger( "start", event, uiHash ); +// }, +// +// _slide: function( event, index, newVal ) { +// var otherVal, +// newValues, +// allowed; +// +// if ( this.options.values && this.options.values.length ) { +// otherVal = this.values( index ? 0 : 1 ); +// +// if ( ( this.options.values.length === 2 && this.options.range === true ) && +// ( ( index === 0 && newVal > otherVal) || ( index === 1 && newVal < otherVal ) ) +// ) { +// newVal = otherVal; +// } +// +// if ( newVal !== this.values( index ) ) { +// newValues = this.values(); +// newValues[ index ] = newVal; +// // A slide can be canceled by returning false from the slide callback +// allowed = this._trigger( "slide", event, { +// handle: this.handles[ index ], +// value: newVal, +// values: newValues +// } ); +// otherVal = this.values( index ? 0 : 1 ); +// if ( allowed !== false ) { +// this.values( index, newVal ); +// } +// } +// } else { +// if ( newVal !== this.value() ) { +// // A slide can be canceled by returning false from the slide callback +// allowed = this._trigger( "slide", event, { +// handle: this.handles[ index ], +// value: newVal +// } ); +// if ( allowed !== false ) { +// this.value( newVal ); +// } +// } +// } +// }, +// +// _stop: function( event, index ) { +// var uiHash = { +// handle: this.handles[ index ], +// value: this.value() +// }; +// if ( this.options.values && this.options.values.length ) { +// uiHash.value = this.values( index ); +// uiHash.values = this.values(); +// } +// +// this._trigger( "stop", event, uiHash ); +// }, +// +// _change: function( event, index ) { +// if ( !this._keySliding && !this._mouseSliding ) { +// var uiHash = { +// handle: this.handles[ index ], +// value: this.value() +// }; +// if ( this.options.values && this.options.values.length ) { +// uiHash.value = this.values( index ); +// uiHash.values = this.values(); +// } +// +// //store the last changed value index for reference when handles overlap +// this._lastChangedValue = index; +// +// this._trigger( "change", event, uiHash ); +// } +// }, +// +// value: function( newValue ) { +// if ( arguments.length ) { +// this.options.value = this._trimAlignValue( newValue ); +// this._refreshValue(); +// this._change( null, 0 ); +// return; +// } +// +// return this._value(); +// }, +// +// values: function( index, newValue ) { +// var vals, +// newValues, +// i; +// +// if ( arguments.length > 1 ) { +// this.options.values[ index ] = this._trimAlignValue( newValue ); +// this._refreshValue(); +// this._change( null, index ); +// return; +// } +// +// if ( arguments.length ) { +// if ( $.isArray( arguments[ 0 ] ) ) { +// vals = this.options.values; +// newValues = arguments[ 0 ]; +// for ( i = 0; i < vals.length; i += 1 ) { +// vals[ i ] = this._trimAlignValue( newValues[ i ] ); +// this._change( null, i ); +// } +// this._refreshValue(); +// } else { +// if ( this.options.values && this.options.values.length ) { +// return this._values( index ); +// } else { +// return this.value(); +// } +// } +// } else { +// return this._values(); +// } +// }, +// +// _setOption: function( key, value ) { +// var i, +// valsLength = 0; +// +// if ( key === "range" && this.options.range === true ) { +// if ( value === "min" ) { +// this.options.value = this._values( 0 ); +// this.options.values = null; +// } else if ( value === "max" ) { +// this.options.value = this._values( this.options.values.length-1 ); +// this.options.values = null; +// } +// } +// +// if ( $.isArray( this.options.values ) ) { +// valsLength = this.options.values.length; +// } +// +// $.Widget.prototype._setOption.apply( this, arguments ); +// +// switch ( key ) { +// case "orientation": +// this._detectOrientation(); +// this.element +// .removeClass( "ui-slider-horizontal ui-slider-vertical" ) +// .addClass( "ui-slider-" + this.orientation ); +// this._refreshValue(); +// break; +// case "value": +// this._animateOff = true; +// this._refreshValue(); +// this._change( null, 0 ); +// this._animateOff = false; +// break; +// case "values": +// this._animateOff = true; +// this._refreshValue(); +// for ( i = 0; i < valsLength; i += 1 ) { +// this._change( null, i ); +// } +// this._animateOff = false; +// break; +// case "min": +// case "max": +// this._animateOff = true; +// this._refreshValue(); +// this._animateOff = false; +// break; +// case "range": +// this._animateOff = true; +// this._refresh(); +// this._animateOff = false; +// break; +// } +// }, +// +// //internal value getter +// // _value() returns value trimmed by min and max, aligned by step +// _value: function() { +// var val = this.options.value; +// val = this._trimAlignValue( val ); +// +// return val; +// }, +// +// //internal values getter +// // _values() returns array of values trimmed by min and max, aligned by step +// // _values( index ) returns single value trimmed by min and max, aligned by step +// _values: function( index ) { +// var val, +// vals, +// i; +// +// if ( arguments.length ) { +// val = this.options.values[ index ]; +// val = this._trimAlignValue( val ); +// +// return val; +// } else if ( this.options.values && this.options.values.length ) { +// // .slice() creates a copy of the array +// // this copy gets trimmed by min and max and then returned +// vals = this.options.values.slice(); +// for ( i = 0; i < vals.length; i+= 1) { +// vals[ i ] = this._trimAlignValue( vals[ i ] ); +// } +// +// return vals; +// } else { +// return []; +// } +// }, +// +// // returns the step-aligned value that val is closest to, between (inclusive) min and max +// _trimAlignValue: function( val ) { +// if ( val <= this._valueMin() ) { +// return this._valueMin(); +// } +// if ( val >= this._valueMax() ) { +// return this._valueMax(); +// } +// var step = ( this.options.step > 0 ) ? this.options.step : 1, +// valModStep = (val - this._valueMin()) % step, +// alignValue = val - valModStep; +// +// if ( Math.abs(valModStep) * 2 >= step ) { +// alignValue += ( valModStep > 0 ) ? step : ( -step ); +// } +// +// // Since JavaScript has problems with large floats, round +// // the final value to 5 digits after the decimal point (see #4124) +// return parseFloat( alignValue.toFixed(5) ); +// }, +// +// _valueMin: function() { +// return this.options.min; +// }, +// +// _valueMax: function() { +// return this.options.max; +// }, +// +// _refreshValue: function() { +// var lastValPercent, valPercent, value, valueMin, valueMax, +// oRange = this.options.range, +// o = this.options, +// that = this, +// animate = ( !this._animateOff ) ? o.animate : false, +// _set = {}; +// +// if ( this.options.values && this.options.values.length ) { +// this.handles.each(function( i ) { +// valPercent = ( that.values(i) - that._valueMin() ) / ( that._valueMax() - that._valueMin() ) * 100; +// _set[ that.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%"; +// $( this ).stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate ); +// if ( that.options.range === true ) { +// if ( that.orientation === "horizontal" ) { +// if ( i === 0 ) { +// that.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { left: valPercent + "%" }, o.animate ); +// } +// if ( i === 1 ) { +// that.range[ animate ? "animate" : "css" ]( { width: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } ); +// } +// } else { +// if ( i === 0 ) { +// that.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { bottom: ( valPercent ) + "%" }, o.animate ); +// } +// if ( i === 1 ) { +// that.range[ animate ? "animate" : "css" ]( { height: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } ); +// } +// } +// } +// lastValPercent = valPercent; +// }); +// } else { +// value = this.value(); +// valueMin = this._valueMin(); +// valueMax = this._valueMax(); +// valPercent = ( valueMax !== valueMin ) ? +// ( value - valueMin ) / ( valueMax - valueMin ) * 100 : +// 0; +// _set[ this.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%"; +// this.handle.stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate ); +// +// if ( oRange === "min" && this.orientation === "horizontal" ) { +// this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { width: valPercent + "%" }, o.animate ); +// } +// if ( oRange === "max" && this.orientation === "horizontal" ) { +// this.range[ animate ? "animate" : "css" ]( { width: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } ); +// } +// if ( oRange === "min" && this.orientation === "vertical" ) { +// this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { height: valPercent + "%" }, o.animate ); +// } +// if ( oRange === "max" && this.orientation === "vertical" ) { +// this.range[ animate ? "animate" : "css" ]( { height: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } ); +// } +// } +// }, +// +// _handleEvents: { +// keydown: function( event ) { +// var allowed, curVal, newVal, step, +// index = $( event.target ).data( "ui-slider-handle-index" ); +// +// switch ( event.keyCode ) { +// case $.ui.keyCode.HOME: +// case $.ui.keyCode.END: +// case $.ui.keyCode.PAGE_UP: +// case $.ui.keyCode.PAGE_DOWN: +// case $.ui.keyCode.UP: +// case $.ui.keyCode.RIGHT: +// case $.ui.keyCode.DOWN: +// case $.ui.keyCode.LEFT: +// event.preventDefault(); +// if ( !this._keySliding ) { +// this._keySliding = true; +// $( event.target ).addClass( "ui-state-active" ); +// allowed = this._start( event, index ); +// if ( allowed === false ) { +// return; +// } +// } +// break; +// } +// +// step = this.options.step; +// if ( this.options.values && this.options.values.length ) { +// curVal = newVal = this.values( index ); +// } else { +// curVal = newVal = this.value(); +// } +// +// switch ( event.keyCode ) { +// case $.ui.keyCode.HOME: +// newVal = this._valueMin(); +// break; +// case $.ui.keyCode.END: +// newVal = this._valueMax(); +// break; +// case $.ui.keyCode.PAGE_UP: +// newVal = this._trimAlignValue( curVal + ( (this._valueMax() - this._valueMin()) / numPages ) ); +// break; +// case $.ui.keyCode.PAGE_DOWN: +// newVal = this._trimAlignValue( curVal - ( (this._valueMax() - this._valueMin()) / numPages ) ); +// break; +// case $.ui.keyCode.UP: +// case $.ui.keyCode.RIGHT: +// if ( curVal === this._valueMax() ) { +// return; +// } +// newVal = this._trimAlignValue( curVal + step ); +// break; +// case $.ui.keyCode.DOWN: +// case $.ui.keyCode.LEFT: +// if ( curVal === this._valueMin() ) { +// return; +// } +// newVal = this._trimAlignValue( curVal - step ); +// break; +// } +// +// this._slide( event, index, newVal ); +// }, +// click: function( event ) { +// event.preventDefault(); +// }, +// keyup: function( event ) { +// var index = $( event.target ).data( "ui-slider-handle-index" ); +// +// if ( this._keySliding ) { +// this._keySliding = false; +// this._stop( event, index ); +// this._change( event, index ); +// $( event.target ).removeClass( "ui-state-active" ); +// } +// } +// } +// +//}); +// +//}(jQuery)); + +/*! + * jQuery UI Effects 1.10.4 + * http://jqueryui.com + * + * Copyright 2014 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/category/effects-core/ + */ +(function($, undefined) { + +var dataSpace = "ui-effects-"; + +$.effects = { + effect: {} +}; + +/*! + * jQuery Color Animations v2.1.2 + * https://github.com/jquery/jquery-color + * + * Copyright 2013 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * Date: Wed Jan 16 08:47:09 2013 -0600 + */ +(function( jQuery, undefined ) { + + var stepHooks = "backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor", + + // plusequals test for += 100 -= 100 + rplusequals = /^([\-+])=\s*(\d+\.?\d*)/, + // a set of RE's that can match strings and generate color tuples. + stringParsers = [{ + re: /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/, + parse: function( execResult ) { + return [ + execResult[ 1 ], + execResult[ 2 ], + execResult[ 3 ], + execResult[ 4 ] + ]; + } + }, { + re: /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/, + parse: function( execResult ) { + return [ + execResult[ 1 ] * 2.55, + execResult[ 2 ] * 2.55, + execResult[ 3 ] * 2.55, + execResult[ 4 ] + ]; + } + }, { + // this regex ignores A-F because it's compared against an already lowercased string + re: /#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/, + parse: function( execResult ) { + return [ + parseInt( execResult[ 1 ], 16 ), + parseInt( execResult[ 2 ], 16 ), + parseInt( execResult[ 3 ], 16 ) + ]; + } + }, { + // this regex ignores A-F because it's compared against an already lowercased string + re: /#([a-f0-9])([a-f0-9])([a-f0-9])/, + parse: function( execResult ) { + return [ + parseInt( execResult[ 1 ] + execResult[ 1 ], 16 ), + parseInt( execResult[ 2 ] + execResult[ 2 ], 16 ), + parseInt( execResult[ 3 ] + execResult[ 3 ], 16 ) + ]; + } + }, { + re: /hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/, + space: "hsla", + parse: function( execResult ) { + return [ + execResult[ 1 ], + execResult[ 2 ] / 100, + execResult[ 3 ] / 100, + execResult[ 4 ] + ]; + } + }], + + // jQuery.Color( ) + color = jQuery.Color = function( color, green, blue, alpha ) { + return new jQuery.Color.fn.parse( color, green, blue, alpha ); + }, + spaces = { + rgba: { + props: { + red: { + idx: 0, + type: "byte" + }, + green: { + idx: 1, + type: "byte" + }, + blue: { + idx: 2, + type: "byte" + } + } + }, + + hsla: { + props: { + hue: { + idx: 0, + type: "degrees" + }, + saturation: { + idx: 1, + type: "percent" + }, + lightness: { + idx: 2, + type: "percent" + } + } + } + }, + propTypes = { + "byte": { + floor: true, + max: 255 + }, + "percent": { + max: 1 + }, + "degrees": { + mod: 360, + floor: true + } + }, + support = color.support = {}, + + // element for support tests + supportElem = jQuery( "<p>" )[ 0 ], + + // colors = jQuery.Color.names + colors, + + // local aliases of functions called often + each = jQuery.each; + +// determine rgba support immediately +supportElem.style.cssText = "background-color:rgba(1,1,1,.5)"; +support.rgba = supportElem.style.backgroundColor.indexOf( "rgba" ) > -1; + +// define cache name and alpha properties +// for rgba and hsla spaces +each( spaces, function( spaceName, space ) { + space.cache = "_" + spaceName; + space.props.alpha = { + idx: 3, + type: "percent", + def: 1 + }; +}); + +function clamp( value, prop, allowEmpty ) { + var type = propTypes[ prop.type ] || {}; + + if ( value == null ) { + return (allowEmpty || !prop.def) ? null : prop.def; + } + + // ~~ is an short way of doing floor for positive numbers + value = type.floor ? ~~value : parseFloat( value ); + + // IE will pass in empty strings as value for alpha, + // which will hit this case + if ( isNaN( value ) ) { + return prop.def; + } + + if ( type.mod ) { + // we add mod before modding to make sure that negatives values + // get converted properly: -10 -> 350 + return (value + type.mod) % type.mod; + } + + // for now all property types without mod have min and max + return 0 > value ? 0 : type.max < value ? type.max : value; +} + +function stringParse( string ) { + var inst = color(), + rgba = inst._rgba = []; + + string = string.toLowerCase(); + + each( stringParsers, function( i, parser ) { + var parsed, + match = parser.re.exec( string ), + values = match && parser.parse( match ), + spaceName = parser.space || "rgba"; + + if ( values ) { + parsed = inst[ spaceName ]( values ); + + // if this was an rgba parse the assignment might happen twice + // oh well.... + inst[ spaces[ spaceName ].cache ] = parsed[ spaces[ spaceName ].cache ]; + rgba = inst._rgba = parsed._rgba; + + // exit each( stringParsers ) here because we matched + return false; + } + }); + + // Found a stringParser that handled it + if ( rgba.length ) { + + // if this came from a parsed string, force "transparent" when alpha is 0 + // chrome, (and maybe others) return "transparent" as rgba(0,0,0,0) + if ( rgba.join() === "0,0,0,0" ) { + jQuery.extend( rgba, colors.transparent ); + } + return inst; + } + + // named colors + return colors[ string ]; +} + +color.fn = jQuery.extend( color.prototype, { + parse: function( red, green, blue, alpha ) { + if ( red === undefined ) { + this._rgba = [ null, null, null, null ]; + return this; + } + if ( red.jquery || red.nodeType ) { + red = jQuery( red ).css( green ); + green = undefined; + } + + var inst = this, + type = jQuery.type( red ), + rgba = this._rgba = []; + + // more than 1 argument specified - assume ( red, green, blue, alpha ) + if ( green !== undefined ) { + red = [ red, green, blue, alpha ]; + type = "array"; + } + + if ( type === "string" ) { + return this.parse( stringParse( red ) || colors._default ); + } + + if ( type === "array" ) { + each( spaces.rgba.props, function( key, prop ) { + rgba[ prop.idx ] = clamp( red[ prop.idx ], prop ); + }); + return this; + } + + if ( type === "object" ) { + if ( red instanceof color ) { + each( spaces, function( spaceName, space ) { + if ( red[ space.cache ] ) { + inst[ space.cache ] = red[ space.cache ].slice(); + } + }); + } else { + each( spaces, function( spaceName, space ) { + var cache = space.cache; + each( space.props, function( key, prop ) { + + // if the cache doesn't exist, and we know how to convert + if ( !inst[ cache ] && space.to ) { + + // if the value was null, we don't need to copy it + // if the key was alpha, we don't need to copy it either + if ( key === "alpha" || red[ key ] == null ) { + return; + } + inst[ cache ] = space.to( inst._rgba ); + } + + // this is the only case where we allow nulls for ALL properties. + // call clamp with alwaysAllowEmpty + inst[ cache ][ prop.idx ] = clamp( red[ key ], prop, true ); + }); + + // everything defined but alpha? + if ( inst[ cache ] && jQuery.inArray( null, inst[ cache ].slice( 0, 3 ) ) < 0 ) { + // use the default of 1 + inst[ cache ][ 3 ] = 1; + if ( space.from ) { + inst._rgba = space.from( inst[ cache ] ); + } + } + }); + } + return this; + } + }, + is: function( compare ) { + var is = color( compare ), + same = true, + inst = this; + + each( spaces, function( _, space ) { + var localCache, + isCache = is[ space.cache ]; + if (isCache) { + localCache = inst[ space.cache ] || space.to && space.to( inst._rgba ) || []; + each( space.props, function( _, prop ) { + if ( isCache[ prop.idx ] != null ) { + same = ( isCache[ prop.idx ] === localCache[ prop.idx ] ); + return same; + } + }); + } + return same; + }); + return same; + }, + _space: function() { + var used = [], + inst = this; + each( spaces, function( spaceName, space ) { + if ( inst[ space.cache ] ) { + used.push( spaceName ); + } + }); + return used.pop(); + }, + transition: function( other, distance ) { + var end = color( other ), + spaceName = end._space(), + space = spaces[ spaceName ], + startColor = this.alpha() === 0 ? color( "transparent" ) : this, + start = startColor[ space.cache ] || space.to( startColor._rgba ), + result = start.slice(); + + end = end[ space.cache ]; + each( space.props, function( key, prop ) { + var index = prop.idx, + startValue = start[ index ], + endValue = end[ index ], + type = propTypes[ prop.type ] || {}; + + // if null, don't override start value + if ( endValue === null ) { + return; + } + // if null - use end + if ( startValue === null ) { + result[ index ] = endValue; + } else { + if ( type.mod ) { + if ( endValue - startValue > type.mod / 2 ) { + startValue += type.mod; + } else if ( startValue - endValue > type.mod / 2 ) { + startValue -= type.mod; + } + } + result[ index ] = clamp( ( endValue - startValue ) * distance + startValue, prop ); + } + }); + return this[ spaceName ]( result ); + }, + blend: function( opaque ) { + // if we are already opaque - return ourself + if ( this._rgba[ 3 ] === 1 ) { + return this; + } + + var rgb = this._rgba.slice(), + a = rgb.pop(), + blend = color( opaque )._rgba; + + return color( jQuery.map( rgb, function( v, i ) { + return ( 1 - a ) * blend[ i ] + a * v; + })); + }, + toRgbaString: function() { + var prefix = "rgba(", + rgba = jQuery.map( this._rgba, function( v, i ) { + return v == null ? ( i > 2 ? 1 : 0 ) : v; + }); + + if ( rgba[ 3 ] === 1 ) { + rgba.pop(); + prefix = "rgb("; + } + + return prefix + rgba.join() + ")"; + }, + toHslaString: function() { + var prefix = "hsla(", + hsla = jQuery.map( this.hsla(), function( v, i ) { + if ( v == null ) { + v = i > 2 ? 1 : 0; + } + + // catch 1 and 2 + if ( i && i < 3 ) { + v = Math.round( v * 100 ) + "%"; + } + return v; + }); + + if ( hsla[ 3 ] === 1 ) { + hsla.pop(); + prefix = "hsl("; + } + return prefix + hsla.join() + ")"; + }, + toHexString: function( includeAlpha ) { + var rgba = this._rgba.slice(), + alpha = rgba.pop(); + + if ( includeAlpha ) { + rgba.push( ~~( alpha * 255 ) ); + } + + return "#" + jQuery.map( rgba, function( v ) { + + // default to 0 when nulls exist + v = ( v || 0 ).toString( 16 ); + return v.length === 1 ? "0" + v : v; + }).join(""); + }, + toString: function() { + return this._rgba[ 3 ] === 0 ? "transparent" : this.toRgbaString(); + } +}); +color.fn.parse.prototype = color.fn; + +// hsla conversions adapted from: +// https://code.google.com/p/maashaack/source/browse/packages/graphics/trunk/src/graphics/colors/HUE2RGB.as?r=5021 + +function hue2rgb( p, q, h ) { + h = ( h + 1 ) % 1; + if ( h * 6 < 1 ) { + return p + (q - p) * h * 6; + } + if ( h * 2 < 1) { + return q; + } + if ( h * 3 < 2 ) { + return p + (q - p) * ((2/3) - h) * 6; + } + return p; +} + +spaces.hsla.to = function ( rgba ) { + if ( rgba[ 0 ] == null || rgba[ 1 ] == null || rgba[ 2 ] == null ) { + return [ null, null, null, rgba[ 3 ] ]; + } + var r = rgba[ 0 ] / 255, + g = rgba[ 1 ] / 255, + b = rgba[ 2 ] / 255, + a = rgba[ 3 ], + max = Math.max( r, g, b ), + min = Math.min( r, g, b ), + diff = max - min, + add = max + min, + l = add * 0.5, + h, s; + + if ( min === max ) { + h = 0; + } else if ( r === max ) { + h = ( 60 * ( g - b ) / diff ) + 360; + } else if ( g === max ) { + h = ( 60 * ( b - r ) / diff ) + 120; + } else { + h = ( 60 * ( r - g ) / diff ) + 240; + } + + // chroma (diff) == 0 means greyscale which, by definition, saturation = 0% + // otherwise, saturation is based on the ratio of chroma (diff) to lightness (add) + if ( diff === 0 ) { + s = 0; + } else if ( l <= 0.5 ) { + s = diff / add; + } else { + s = diff / ( 2 - add ); + } + return [ Math.round(h) % 360, s, l, a == null ? 1 : a ]; +}; + +spaces.hsla.from = function ( hsla ) { + if ( hsla[ 0 ] == null || hsla[ 1 ] == null || hsla[ 2 ] == null ) { + return [ null, null, null, hsla[ 3 ] ]; + } + var h = hsla[ 0 ] / 360, + s = hsla[ 1 ], + l = hsla[ 2 ], + a = hsla[ 3 ], + q = l <= 0.5 ? l * ( 1 + s ) : l + s - l * s, + p = 2 * l - q; + + return [ + Math.round( hue2rgb( p, q, h + ( 1 / 3 ) ) * 255 ), + Math.round( hue2rgb( p, q, h ) * 255 ), + Math.round( hue2rgb( p, q, h - ( 1 / 3 ) ) * 255 ), + a + ]; +}; + + +each( spaces, function( spaceName, space ) { + var props = space.props, + cache = space.cache, + to = space.to, + from = space.from; + + // makes rgba() and hsla() + color.fn[ spaceName ] = function( value ) { + + // generate a cache for this space if it doesn't exist + if ( to && !this[ cache ] ) { + this[ cache ] = to( this._rgba ); + } + if ( value === undefined ) { + return this[ cache ].slice(); + } + + var ret, + type = jQuery.type( value ), + arr = ( type === "array" || type === "object" ) ? value : arguments, + local = this[ cache ].slice(); + + each( props, function( key, prop ) { + var val = arr[ type === "object" ? key : prop.idx ]; + if ( val == null ) { + val = local[ prop.idx ]; + } + local[ prop.idx ] = clamp( val, prop ); + }); + + if ( from ) { + ret = color( from( local ) ); + ret[ cache ] = local; + return ret; + } else { + return color( local ); + } + }; + + // makes red() green() blue() alpha() hue() saturation() lightness() + each( props, function( key, prop ) { + // alpha is included in more than one space + if ( color.fn[ key ] ) { + return; + } + color.fn[ key ] = function( value ) { + var vtype = jQuery.type( value ), + fn = ( key === "alpha" ? ( this._hsla ? "hsla" : "rgba" ) : spaceName ), + local = this[ fn ](), + cur = local[ prop.idx ], + match; + + if ( vtype === "undefined" ) { + return cur; + } + + if ( vtype === "function" ) { + value = value.call( this, cur ); + vtype = jQuery.type( value ); + } + if ( value == null && prop.empty ) { + return this; + } + if ( vtype === "string" ) { + match = rplusequals.exec( value ); + if ( match ) { + value = cur + parseFloat( match[ 2 ] ) * ( match[ 1 ] === "+" ? 1 : -1 ); + } + } + local[ prop.idx ] = value; + return this[ fn ]( local ); + }; + }); +}); + +// add cssHook and .fx.step function for each named hook. +// accept a space separated string of properties +color.hook = function( hook ) { + var hooks = hook.split( " " ); + each( hooks, function( i, hook ) { + jQuery.cssHooks[ hook ] = { + set: function( elem, value ) { + var parsed, curElem, + backgroundColor = ""; + + if ( value !== "transparent" && ( jQuery.type( value ) !== "string" || ( parsed = stringParse( value ) ) ) ) { + value = color( parsed || value ); + if ( !support.rgba && value._rgba[ 3 ] !== 1 ) { + curElem = hook === "backgroundColor" ? elem.parentNode : elem; + while ( + (backgroundColor === "" || backgroundColor === "transparent") && + curElem && curElem.style + ) { + try { + backgroundColor = jQuery.css( curElem, "backgroundColor" ); + curElem = curElem.parentNode; + } catch ( e ) { + } + } + + value = value.blend( backgroundColor && backgroundColor !== "transparent" ? + backgroundColor : + "_default" ); + } + + value = value.toRgbaString(); + } + try { + elem.style[ hook ] = value; + } catch( e ) { + // wrapped to prevent IE from throwing errors on "invalid" values like 'auto' or 'inherit' + } + } + }; + jQuery.fx.step[ hook ] = function( fx ) { + if ( !fx.colorInit ) { + fx.start = color( fx.elem, hook ); + fx.end = color( fx.end ); + fx.colorInit = true; + } + jQuery.cssHooks[ hook ].set( fx.elem, fx.start.transition( fx.end, fx.pos ) ); + }; + }); + +}; + +color.hook( stepHooks ); + +jQuery.cssHooks.borderColor = { + expand: function( value ) { + var expanded = {}; + + each( [ "Top", "Right", "Bottom", "Left" ], function( i, part ) { + expanded[ "border" + part + "Color" ] = value; + }); + return expanded; + } +}; + +// Basic color names only. +// Usage of any of the other color names requires adding yourself or including +// jquery.color.svg-names.js. +colors = jQuery.Color.names = { + // 4.1. Basic color keywords + aqua: "#00ffff", + black: "#000000", + blue: "#0000ff", + fuchsia: "#ff00ff", + gray: "#808080", + green: "#008000", + lime: "#00ff00", + maroon: "#800000", + navy: "#000080", + olive: "#808000", + purple: "#800080", + red: "#ff0000", + silver: "#c0c0c0", + teal: "#008080", + white: "#ffffff", + yellow: "#ffff00", + + // 4.2.3. "transparent" color keyword + transparent: [ null, null, null, 0 ], + + _default: "#ffffff" +}; + +})( jQuery ); + + +/******************************************************************************/ +/****************************** CLASS ANIMATIONS ******************************/ +/******************************************************************************/ +(function() { + +var classAnimationActions = [ "add", "remove", "toggle" ], + shorthandStyles = { + border: 1, + borderBottom: 1, + borderColor: 1, + borderLeft: 1, + borderRight: 1, + borderTop: 1, + borderWidth: 1, + margin: 1, + padding: 1 + }; + +$.each([ "borderLeftStyle", "borderRightStyle", "borderBottomStyle", "borderTopStyle" ], function( _, prop ) { + $.fx.step[ prop ] = function( fx ) { + if ( fx.end !== "none" && !fx.setAttr || fx.pos === 1 && !fx.setAttr ) { + jQuery.style( fx.elem, prop, fx.end ); + fx.setAttr = true; + } + }; +}); + +function getElementStyles( elem ) { + var key, len, + style = elem.ownerDocument.defaultView ? + elem.ownerDocument.defaultView.getComputedStyle( elem, null ) : + elem.currentStyle, + styles = {}; + + if ( style && style.length && style[ 0 ] && style[ style[ 0 ] ] ) { + len = style.length; + while ( len-- ) { + key = style[ len ]; + if ( typeof style[ key ] === "string" ) { + styles[ $.camelCase( key ) ] = style[ key ]; + } + } + // support: Opera, IE <9 + } else { + for ( key in style ) { + if ( typeof style[ key ] === "string" ) { + styles[ key ] = style[ key ]; + } + } + } + + return styles; +} + + +function styleDifference( oldStyle, newStyle ) { + var diff = {}, + name, value; + + for ( name in newStyle ) { + value = newStyle[ name ]; + if ( oldStyle[ name ] !== value ) { + if ( !shorthandStyles[ name ] ) { + if ( $.fx.step[ name ] || !isNaN( parseFloat( value ) ) ) { + diff[ name ] = value; + } + } + } + } + + return diff; +} + +// support: jQuery <1.8 +if ( !$.fn.addBack ) { + $.fn.addBack = function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter( selector ) + ); + }; +} + +$.effects.animateClass = function( value, duration, easing, callback ) { + var o = $.speed( duration, easing, callback ); + + return this.queue( function() { + var animated = $( this ), + baseClass = animated.attr( "class" ) || "", + applyClassChange, + allAnimations = o.children ? animated.find( "*" ).addBack() : animated; + + // map the animated objects to store the original styles. + allAnimations = allAnimations.map(function() { + var el = $( this ); + return { + el: el, + start: getElementStyles( this ) + }; + }); + + // apply class change + applyClassChange = function() { + $.each( classAnimationActions, function(i, action) { + if ( value[ action ] ) { + animated[ action + "Class" ]( value[ action ] ); + } + }); + }; + applyClassChange(); + + // map all animated objects again - calculate new styles and diff + allAnimations = allAnimations.map(function() { + this.end = getElementStyles( this.el[ 0 ] ); + this.diff = styleDifference( this.start, this.end ); + return this; + }); + + // apply original class + animated.attr( "class", baseClass ); + + // map all animated objects again - this time collecting a promise + allAnimations = allAnimations.map(function() { + var styleInfo = this, + dfd = $.Deferred(), + opts = $.extend({}, o, { + queue: false, + complete: function() { + dfd.resolve( styleInfo ); + } + }); + + this.el.animate( this.diff, opts ); + return dfd.promise(); + }); + + // once all animations have completed: + $.when.apply( $, allAnimations.get() ).done(function() { + + // set the final class + applyClassChange(); + + // for each animated element, + // clear all css properties that were animated + $.each( arguments, function() { + var el = this.el; + $.each( this.diff, function(key) { + el.css( key, "" ); + }); + }); + + // this is guarnteed to be there if you use jQuery.speed() + // it also handles dequeuing the next anim... + o.complete.call( animated[ 0 ] ); + }); + }); +}; + +$.fn.extend({ + addClass: (function( orig ) { + return function( classNames, speed, easing, callback ) { + return speed ? + $.effects.animateClass.call( this, + { add: classNames }, speed, easing, callback ) : + orig.apply( this, arguments ); + }; + })( $.fn.addClass ), + + removeClass: (function( orig ) { + return function( classNames, speed, easing, callback ) { + return arguments.length > 1 ? + $.effects.animateClass.call( this, + { remove: classNames }, speed, easing, callback ) : + orig.apply( this, arguments ); + }; + })( $.fn.removeClass ), + + toggleClass: (function( orig ) { + return function( classNames, force, speed, easing, callback ) { + if ( typeof force === "boolean" || force === undefined ) { + if ( !speed ) { + // without speed parameter + return orig.apply( this, arguments ); + } else { + return $.effects.animateClass.call( this, + (force ? { add: classNames } : { remove: classNames }), + speed, easing, callback ); + } + } else { + // without force parameter + return $.effects.animateClass.call( this, + { toggle: classNames }, force, speed, easing ); + } + }; + })( $.fn.toggleClass ), + + switchClass: function( remove, add, speed, easing, callback) { + return $.effects.animateClass.call( this, { + add: add, + remove: remove + }, speed, easing, callback ); + } +}); + +})(); + +/******************************************************************************/ +/*********************************** EFFECTS **********************************/ +/******************************************************************************/ + +(function() { + +$.extend( $.effects, { + version: "1.10.4", + + // Saves a set of properties in a data storage + save: function( element, set ) { + for( var i=0; i < set.length; i++ ) { + if ( set[ i ] !== null ) { + element.data( dataSpace + set[ i ], element[ 0 ].style[ set[ i ] ] ); + } + } + }, + + // Restores a set of previously saved properties from a data storage + restore: function( element, set ) { + var val, i; + for( i=0; i < set.length; i++ ) { + if ( set[ i ] !== null ) { + val = element.data( dataSpace + set[ i ] ); + // support: jQuery 1.6.2 + // http://bugs.jquery.com/ticket/9917 + // jQuery 1.6.2 incorrectly returns undefined for any falsy value. + // We can't differentiate between "" and 0 here, so we just assume + // empty string since it's likely to be a more common value... + if ( val === undefined ) { + val = ""; + } + element.css( set[ i ], val ); + } + } + }, + + setMode: function( el, mode ) { + if (mode === "toggle") { + mode = el.is( ":hidden" ) ? "show" : "hide"; + } + return mode; + }, + + // Translates a [top,left] array into a baseline value + // this should be a little more flexible in the future to handle a string & hash + getBaseline: function( origin, original ) { + var y, x; + switch ( origin[ 0 ] ) { + case "top": y = 0; break; + case "middle": y = 0.5; break; + case "bottom": y = 1; break; + default: y = origin[ 0 ] / original.height; + } + switch ( origin[ 1 ] ) { + case "left": x = 0; break; + case "center": x = 0.5; break; + case "right": x = 1; break; + default: x = origin[ 1 ] / original.width; + } + return { + x: x, + y: y + }; + }, + + // Wraps the element around a wrapper that copies position properties + createWrapper: function( element ) { + + // if the element is already wrapped, return it + if ( element.parent().is( ".ui-effects-wrapper" )) { + return element.parent(); + } + + // wrap the element + var props = { + width: element.outerWidth(true), + height: element.outerHeight(true), + "float": element.css( "float" ) + }, + wrapper = $( "<div></div>" ) + .addClass( "ui-effects-wrapper" ) + .css({ + fontSize: "100%", + background: "transparent", + border: "none", + margin: 0, + padding: 0 + }), + // Store the size in case width/height are defined in % - Fixes #5245 + size = { + width: element.width(), + height: element.height() + }, + active = document.activeElement; + + // support: Firefox + // Firefox incorrectly exposes anonymous content + // https://bugzilla.mozilla.org/show_bug.cgi?id=561664 + try { + active.id; + } catch( e ) { + active = document.body; + } + + element.wrap( wrapper ); + + // Fixes #7595 - Elements lose focus when wrapped. + if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) { + $( active ).focus(); + } + + wrapper = element.parent(); //Hotfix for jQuery 1.4 since some change in wrap() seems to actually lose the reference to the wrapped element + + // transfer positioning properties to the wrapper + if ( element.css( "position" ) === "static" ) { + wrapper.css({ position: "relative" }); + element.css({ position: "relative" }); + } else { + $.extend( props, { + position: element.css( "position" ), + zIndex: element.css( "z-index" ) + }); + $.each([ "top", "left", "bottom", "right" ], function(i, pos) { + props[ pos ] = element.css( pos ); + if ( isNaN( parseInt( props[ pos ], 10 ) ) ) { + props[ pos ] = "auto"; + } + }); + element.css({ + position: "relative", + top: 0, + left: 0, + right: "auto", + bottom: "auto" + }); + } + element.css(size); + + return wrapper.css( props ).show(); + }, + + removeWrapper: function( element ) { + var active = document.activeElement; + + if ( element.parent().is( ".ui-effects-wrapper" ) ) { + element.parent().replaceWith( element ); + + // Fixes #7595 - Elements lose focus when wrapped. + if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) { + $( active ).focus(); + } + } + + + return element; + }, + + setTransition: function( element, list, factor, value ) { + value = value || {}; + $.each( list, function( i, x ) { + var unit = element.cssUnit( x ); + if ( unit[ 0 ] > 0 ) { + value[ x ] = unit[ 0 ] * factor + unit[ 1 ]; + } + }); + return value; + } +}); + +// return an effect options object for the given parameters: +function _normalizeArguments( effect, options, speed, callback ) { + + // allow passing all options as the first parameter + if ( $.isPlainObject( effect ) ) { + options = effect; + effect = effect.effect; + } + + // convert to an object + effect = { effect: effect }; + + // catch (effect, null, ...) + if ( options == null ) { + options = {}; + } + + // catch (effect, callback) + if ( $.isFunction( options ) ) { + callback = options; + speed = null; + options = {}; + } + + // catch (effect, speed, ?) + if ( typeof options === "number" || $.fx.speeds[ options ] ) { + callback = speed; + speed = options; + options = {}; + } + + // catch (effect, options, callback) + if ( $.isFunction( speed ) ) { + callback = speed; + speed = null; + } + + // add options to effect + if ( options ) { + $.extend( effect, options ); + } + + speed = speed || options.duration; + effect.duration = $.fx.off ? 0 : + typeof speed === "number" ? speed : + speed in $.fx.speeds ? $.fx.speeds[ speed ] : + $.fx.speeds._default; + + effect.complete = callback || options.complete; + + return effect; +} + +function standardAnimationOption( option ) { + // Valid standard speeds (nothing, number, named speed) + if ( !option || typeof option === "number" || $.fx.speeds[ option ] ) { + return true; + } + + // Invalid strings - treat as "normal" speed + if ( typeof option === "string" && !$.effects.effect[ option ] ) { + return true; + } + + // Complete callback + if ( $.isFunction( option ) ) { + return true; + } + + // Options hash (but not naming an effect) + if ( typeof option === "object" && !option.effect ) { + return true; + } + + // Didn't match any standard API + return false; +} + +$.fn.extend({ + effect: function( /* effect, options, speed, callback */ ) { + var args = _normalizeArguments.apply( this, arguments ), + mode = args.mode, + queue = args.queue, + effectMethod = $.effects.effect[ args.effect ]; + + if ( $.fx.off || !effectMethod ) { + // delegate to the original method (e.g., .show()) if possible + if ( mode ) { + return this[ mode ]( args.duration, args.complete ); + } else { + return this.each( function() { + if ( args.complete ) { + args.complete.call( this ); + } + }); + } + } + + function run( next ) { + var elem = $( this ), + complete = args.complete, + mode = args.mode; + + function done() { + if ( $.isFunction( complete ) ) { + complete.call( elem[0] ); + } + if ( $.isFunction( next ) ) { + next(); + } + } + + // If the element already has the correct final state, delegate to + // the core methods so the internal tracking of "olddisplay" works. + if ( elem.is( ":hidden" ) ? mode === "hide" : mode === "show" ) { + elem[ mode ](); + done(); + } else { + effectMethod.call( elem[0], args, done ); + } + } + + return queue === false ? this.each( run ) : this.queue( queue || "fx", run ); + }, + + show: (function( orig ) { + return function( option ) { + if ( standardAnimationOption( option ) ) { + return orig.apply( this, arguments ); + } else { + var args = _normalizeArguments.apply( this, arguments ); + args.mode = "show"; + return this.effect.call( this, args ); + } + }; + })( $.fn.show ), + + hide: (function( orig ) { + return function( option ) { + if ( standardAnimationOption( option ) ) { + return orig.apply( this, arguments ); + } else { + var args = _normalizeArguments.apply( this, arguments ); + args.mode = "hide"; + return this.effect.call( this, args ); + } + }; + })( $.fn.hide ), + + toggle: (function( orig ) { + return function( option ) { + if ( standardAnimationOption( option ) || typeof option === "boolean" ) { + return orig.apply( this, arguments ); + } else { + var args = _normalizeArguments.apply( this, arguments ); + args.mode = "toggle"; + return this.effect.call( this, args ); + } + }; + })( $.fn.toggle ), + + // helper functions + cssUnit: function(key) { + var style = this.css( key ), + val = []; + + $.each( [ "em", "px", "%", "pt" ], function( i, unit ) { + if ( style.indexOf( unit ) > 0 ) { + val = [ parseFloat( style ), unit ]; + } + }); + return val; + } +}); + +})(); + +/******************************************************************************/ +/*********************************** EASING ***********************************/ +/******************************************************************************/ + +(function() { + +// based on easing equations from Robert Penner (http://www.robertpenner.com/easing) + +var baseEasings = {}; + +$.each( [ "Quad", "Cubic", "Quart", "Quint", "Expo" ], function( i, name ) { + baseEasings[ name ] = function( p ) { + return Math.pow( p, i + 2 ); + }; +}); + +$.extend( baseEasings, { + Sine: function ( p ) { + return 1 - Math.cos( p * Math.PI / 2 ); + }, + Circ: function ( p ) { + return 1 - Math.sqrt( 1 - p * p ); + }, + Elastic: function( p ) { + return p === 0 || p === 1 ? p : + -Math.pow( 2, 8 * (p - 1) ) * Math.sin( ( (p - 1) * 80 - 7.5 ) * Math.PI / 15 ); + }, + Back: function( p ) { + return p * p * ( 3 * p - 2 ); + }, + Bounce: function ( p ) { + var pow2, + bounce = 4; + + while ( p < ( ( pow2 = Math.pow( 2, --bounce ) ) - 1 ) / 11 ) {} + return 1 / Math.pow( 4, 3 - bounce ) - 7.5625 * Math.pow( ( pow2 * 3 - 2 ) / 22 - p, 2 ); + } +}); + +$.each( baseEasings, function( name, easeIn ) { + $.easing[ "easeIn" + name ] = easeIn; + $.easing[ "easeOut" + name ] = function( p ) { + return 1 - easeIn( 1 - p ); + }; + $.easing[ "easeInOut" + name ] = function( p ) { + return p < 0.5 ? + easeIn( p * 2 ) / 2 : + 1 - easeIn( p * -2 + 2 ) / 2; + }; +}); + +})(); + +})(jQuery); + +/*! + * jQuery UI Touch Punch 0.2.2 + * + * Copyright 2011, Dave Furfero + * Dual licensed under the MIT or GPL Version 2 licenses. + * + * Depends: + * jquery.ui.widget.js + * jquery.ui.mouse.js + */ +(function ($) { + + // Detect touch support + $.support.touch = 'ontouchend' in document; + + // Ignore browsers without touch support + if (!$.support.touch) { + return; + } + + var mouseProto = $.ui.mouse.prototype, + _mouseInit = mouseProto._mouseInit, + touchHandled; + + /** + * Simulate a mouse event based on a corresponding touch event + * @param {Object} event A touch event + * @param {String} simulatedType The corresponding mouse event + */ + function simulateMouseEvent (event, simulatedType) { + + // Ignore multi-touch events + if (event.originalEvent.touches.length > 1) { + return; + } + + event.preventDefault(); + + var touch = event.originalEvent.changedTouches[0], + simulatedEvent = document.createEvent('MouseEvents'); + + // Initialize the simulated mouse event using the touch event's coordinates + simulatedEvent.initMouseEvent( + simulatedType, // type + true, // bubbles + true, // cancelable + window, // view + 1, // detail + touch.screenX, // screenX + touch.screenY, // screenY + touch.clientX, // clientX + touch.clientY, // clientY + false, // ctrlKey + false, // altKey + false, // shiftKey + false, // metaKey + 0, // button + null // relatedTarget + ); + + // Dispatch the simulated event to the target element + event.target.dispatchEvent(simulatedEvent); + } + + /** + * Handle the jQuery UI widget's touchstart events + * @param {Object} event The widget element's touchstart event + */ + mouseProto._touchStart = function (event) { + + var self = this; + + // Ignore the event if another widget is already being handled + if (touchHandled || !self._mouseCapture(event.originalEvent.changedTouches[0])) { + return; + } + + // Set the flag to prevent other widgets from inheriting the touch event + touchHandled = true; + + // Track movement to determine if interaction was a click + self._touchMoved = false; + + // Simulate the mouseover event + simulateMouseEvent(event, 'mouseover'); + + // Simulate the mousemove event + simulateMouseEvent(event, 'mousemove'); + + // Simulate the mousedown event + simulateMouseEvent(event, 'mousedown'); + }; + + /** + * Handle the jQuery UI widget's touchmove events + * @param {Object} event The document's touchmove event + */ + mouseProto._touchMove = function (event) { + + // Ignore event if not handled + if (!touchHandled) { + return; + } + + // Interaction was not a click + this._touchMoved = true; + + // Simulate the mousemove event + simulateMouseEvent(event, 'mousemove'); + }; + + /** + * Handle the jQuery UI widget's touchend events + * @param {Object} event The document's touchend event + */ + mouseProto._touchEnd = function (event) { + + // Ignore event if not handled + if (!touchHandled) { + return; + } + + // Simulate the mouseup event + simulateMouseEvent(event, 'mouseup'); + + // Simulate the mouseout event + simulateMouseEvent(event, 'mouseout'); + + // If the touch interaction did not move, it should trigger a click + if (!this._touchMoved) { + + // Simulate the click event + simulateMouseEvent(event, 'click'); + } + + // Unset the flag to allow other widgets to inherit the touch event + touchHandled = false; + }; + + /** + * A duck punch of the $.ui.mouse _mouseInit method to support touch events. + * This method extends the widget with bound touch event handlers that + * translate touch events to mouse events and pass them to the widget's + * original mouse event handling methods. + */ + mouseProto._mouseInit = function () { + + var self = this; + + // Delegate the touch handlers to the widget's element + self.element + .bind('touchstart', $.proxy(self, '_touchStart')) + .bind('touchmove', $.proxy(self, '_touchMove')) + .bind('touchend', $.proxy(self, '_touchEnd')); + + // Call the original $.ui.mouse init method + _mouseInit.call(self); + }; + +})(jQuery); +/*! + * Bootstrap v3.2.0 (http://getbootstrap.com) + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ + +if (typeof jQuery === 'undefined') { throw new Error('Bootstrap\'s JavaScript requires jQuery') } + +/* ======================================================================== + * Bootstrap: transition.js v3.2.0 + * http://getbootstrap.com/javascript/#transitions + * ======================================================================== + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/) + // ============================================================ + + function transitionEnd() { + var el = document.createElement('bootstrap') + + var transEndEventNames = { + WebkitTransition : 'webkitTransitionEnd', + MozTransition : 'transitionend', + OTransition : 'oTransitionEnd otransitionend', + transition : 'transitionend' + } + + for (var name in transEndEventNames) { + if (el.style[name] !== undefined) { + return { end: transEndEventNames[name] } + } + } + + return false // explicit for ie8 ( ._.) + } + + // http://blog.alexmaccaw.com/css-transitions + $.fn.emulateTransitionEnd = function (duration) { + var called = false + var $el = this + $(this).one('bsTransitionEnd', function () { called = true }) + var callback = function () { if (!called) $($el).trigger($.support.transition.end) } + setTimeout(callback, duration) + return this + } + + $(function () { + $.support.transition = transitionEnd() + + if (!$.support.transition) return + + $.event.special.bsTransitionEnd = { + bindType: $.support.transition.end, + delegateType: $.support.transition.end, + handle: function (e) { + if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments) + } + } + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: alert.js v3.2.0 + * http://getbootstrap.com/javascript/#alerts + * ======================================================================== + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // ALERT CLASS DEFINITION + // ====================== + + var dismiss = '[data-dismiss="alert"]' + var Alert = function (el) { + $(el).on('click', dismiss, this.close) + } + + Alert.VERSION = '3.2.0' + + Alert.prototype.close = function (e) { + var $this = $(this) + var selector = $this.attr('data-target') + + if (!selector) { + selector = $this.attr('href') + selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 + } + + var $parent = $(selector) + + if (e) e.preventDefault() + + if (!$parent.length) { + $parent = $this.hasClass('alert') ? $this : $this.parent() + } + + $parent.trigger(e = $.Event('close.bs.alert')) + + if (e.isDefaultPrevented()) return + + $parent.removeClass('in') + + function removeElement() { + // detach from parent, fire event then clean up data + $parent.detach().trigger('closed.bs.alert').remove() + } + + $.support.transition && $parent.hasClass('fade') ? + $parent + .one('bsTransitionEnd', removeElement) + .emulateTransitionEnd(150) : + removeElement() + } + + + // ALERT PLUGIN DEFINITION + // ======================= + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.alert') + + if (!data) $this.data('bs.alert', (data = new Alert(this))) + if (typeof option == 'string') data[option].call($this) + }) + } + + var old = $.fn.alert + + $.fn.alert = Plugin + $.fn.alert.Constructor = Alert + + + // ALERT NO CONFLICT + // ================= + + $.fn.alert.noConflict = function () { + $.fn.alert = old + return this + } + + + // ALERT DATA-API + // ============== + + $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: button.js v3.2.0 + * http://getbootstrap.com/javascript/#buttons + * ======================================================================== + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // BUTTON PUBLIC CLASS DEFINITION + // ============================== + + var Button = function (element, options) { + this.$element = $(element) + this.options = $.extend({}, Button.DEFAULTS, options) + this.isLoading = false + } + + Button.VERSION = '3.2.0' + + Button.DEFAULTS = { + loadingText: 'loading...' + } + + Button.prototype.setState = function (state) { + var d = 'disabled' + var $el = this.$element + var val = $el.is('input') ? 'val' : 'html' + var data = $el.data() + + state = state + 'Text' + + if (data.resetText == null) $el.data('resetText', $el[val]()) + + $el[val](data[state] == null ? this.options[state] : data[state]) + + // push to event loop to allow forms to submit + setTimeout($.proxy(function () { + if (state == 'loadingText') { + this.isLoading = true + $el.addClass(d).attr(d, d) + } else if (this.isLoading) { + this.isLoading = false + $el.removeClass(d).removeAttr(d) + } + }, this), 0) + } + + Button.prototype.toggle = function () { + var changed = true + var $parent = this.$element.closest('[data-toggle="buttons"]') + + if ($parent.length) { + var $input = this.$element.find('input') + if ($input.prop('type') == 'radio') { + if ($input.prop('checked') && this.$element.hasClass('active')) changed = false + else $parent.find('.active').removeClass('active') + } + if (changed) $input.prop('checked', !this.$element.hasClass('active')).trigger('change') + } + + if (changed) this.$element.toggleClass('active') + } + + + // BUTTON PLUGIN DEFINITION + // ======================== + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.button') + var options = typeof option == 'object' && option + + if (!data) $this.data('bs.button', (data = new Button(this, options))) + + if (option == 'toggle') data.toggle() + else if (option) data.setState(option) + }) + } + + var old = $.fn.button + + $.fn.button = Plugin + $.fn.button.Constructor = Button + + + // BUTTON NO CONFLICT + // ================== + + $.fn.button.noConflict = function () { + $.fn.button = old + return this + } + + + // BUTTON DATA-API + // =============== + + $(document).on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) { + var $btn = $(e.target) + if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') + Plugin.call($btn, 'toggle') + e.preventDefault() + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: carousel.js v3.2.0 + * http://getbootstrap.com/javascript/#carousel + * ======================================================================== + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // CAROUSEL CLASS DEFINITION + // ========================= + + var Carousel = function (element, options) { + this.$element = $(element).on('keydown.bs.carousel', $.proxy(this.keydown, this)) + this.$indicators = this.$element.find('.carousel-indicators') + this.options = options + this.paused = + this.sliding = + this.interval = + this.$active = + this.$items = null + + this.options.pause == 'hover' && this.$element + .on('mouseenter.bs.carousel', $.proxy(this.pause, this)) + .on('mouseleave.bs.carousel', $.proxy(this.cycle, this)) + } + + Carousel.VERSION = '3.2.0' + + Carousel.DEFAULTS = { + interval: 5000, + pause: 'hover', + wrap: true + } + + Carousel.prototype.keydown = function (e) { + switch (e.which) { + case 37: this.prev(); break + case 39: this.next(); break + default: return + } + + e.preventDefault() + } + + Carousel.prototype.cycle = function (e) { + e || (this.paused = false) + + this.interval && clearInterval(this.interval) + + this.options.interval + && !this.paused + && (this.interval = setInterval($.proxy(this.next, this), this.options.interval)) + + return this + } + + Carousel.prototype.getItemIndex = function (item) { + this.$items = item.parent().children('.item') + return this.$items.index(item || this.$active) + } + + Carousel.prototype.to = function (pos) { + var that = this + var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active')) + + if (pos > (this.$items.length - 1) || pos < 0) return + + if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, "slid" + if (activeIndex == pos) return this.pause().cycle() + + return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos])) + } + + Carousel.prototype.pause = function (e) { + e || (this.paused = true) + + if (this.$element.find('.next, .prev').length && $.support.transition) { + this.$element.trigger($.support.transition.end) + this.cycle(true) + } + + this.interval = clearInterval(this.interval) + + return this + } + + Carousel.prototype.next = function () { + if (this.sliding) return + return this.slide('next') + } + + Carousel.prototype.prev = function () { + if (this.sliding) return + return this.slide('prev') + } + + Carousel.prototype.slide = function (type, next) { + var $active = this.$element.find('.item.active') + var $next = next || $active[type]() + var isCycling = this.interval + var direction = type == 'next' ? 'left' : 'right' + var fallback = type == 'next' ? 'first' : 'last' + var that = this + + if (!$next.length) { + if (!this.options.wrap) return + $next = this.$element.find('.item')[fallback]() + } + + if ($next.hasClass('active')) return (this.sliding = false) + + var relatedTarget = $next[0] + var slideEvent = $.Event('slide.bs.carousel', { + relatedTarget: relatedTarget, + direction: direction + }) + this.$element.trigger(slideEvent) + if (slideEvent.isDefaultPrevented()) return + + this.sliding = true + + isCycling && this.pause() + + if (this.$indicators.length) { + this.$indicators.find('.active').removeClass('active') + var $nextIndicator = $(this.$indicators.children()[this.getItemIndex($next)]) + $nextIndicator && $nextIndicator.addClass('active') + } + + var slidEvent = $.Event('slid.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) // yes, "slid" + if ($.support.transition && this.$element.hasClass('slide')) { + $next.addClass(type) + $next[0].offsetWidth // force reflow + $active.addClass(direction) + $next.addClass(direction) + $active + .one('bsTransitionEnd', function () { + $next.removeClass([type, direction].join(' ')).addClass('active') + $active.removeClass(['active', direction].join(' ')) + that.sliding = false + setTimeout(function () { + that.$element.trigger(slidEvent) + }, 0) + }) + .emulateTransitionEnd($active.css('transition-duration').slice(0, -1) * 1000) + } else { + $active.removeClass('active') + $next.addClass('active') + this.sliding = false + this.$element.trigger(slidEvent) + } + + isCycling && this.cycle() + + return this + } + + + // CAROUSEL PLUGIN DEFINITION + // ========================== + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.carousel') + var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option) + var action = typeof option == 'string' ? option : options.slide + + if (!data) $this.data('bs.carousel', (data = new Carousel(this, options))) + if (typeof option == 'number') data.to(option) + else if (action) data[action]() + else if (options.interval) data.pause().cycle() + }) + } + + var old = $.fn.carousel + + $.fn.carousel = Plugin + $.fn.carousel.Constructor = Carousel + + + // CAROUSEL NO CONFLICT + // ==================== + + $.fn.carousel.noConflict = function () { + $.fn.carousel = old + return this + } + + + // CAROUSEL DATA-API + // ================= + + $(document).on('click.bs.carousel.data-api', '[data-slide], [data-slide-to]', function (e) { + var href + var $this = $(this) + var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7 + if (!$target.hasClass('carousel')) return + var options = $.extend({}, $target.data(), $this.data()) + var slideIndex = $this.attr('data-slide-to') + if (slideIndex) options.interval = false + + Plugin.call($target, options) + + if (slideIndex) { + $target.data('bs.carousel').to(slideIndex) + } + + e.preventDefault() + }) + + $(window).on('load', function () { + $('[data-ride="carousel"]').each(function () { + var $carousel = $(this) + Plugin.call($carousel, $carousel.data()) + }) + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: collapse.js v3.2.0 + * http://getbootstrap.com/javascript/#collapse + * ======================================================================== + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // COLLAPSE PUBLIC CLASS DEFINITION + // ================================ + + var Collapse = function (element, options) { + this.$element = $(element) + this.options = $.extend({}, Collapse.DEFAULTS, options) + this.transitioning = null + + if (this.options.parent) this.$parent = $(this.options.parent) + if (this.options.toggle) this.toggle() + } + + Collapse.VERSION = '3.2.0' + + Collapse.DEFAULTS = { + toggle: true + } + + Collapse.prototype.dimension = function () { + var hasWidth = this.$element.hasClass('width') + return hasWidth ? 'width' : 'height' + } + + Collapse.prototype.show = function () { + if (this.transitioning || this.$element.hasClass('in')) return + + var startEvent = $.Event('show.bs.collapse') + this.$element.trigger(startEvent) + if (startEvent.isDefaultPrevented()) return + + var actives = this.$parent && this.$parent.find('> .panel > .in') + + if (actives && actives.length) { + var hasData = actives.data('bs.collapse') + if (hasData && hasData.transitioning) return + Plugin.call(actives, 'hide') + hasData || actives.data('bs.collapse', null) + } + + var dimension = this.dimension() + + this.$element + .removeClass('collapse') + .addClass('collapsing')[dimension](0) + + this.transitioning = 1 + + var complete = function () { + this.$element + .removeClass('collapsing') + .addClass('collapse in')[dimension]('') + this.transitioning = 0 + this.$element + .trigger('shown.bs.collapse') + } + + if (!$.support.transition) return complete.call(this) + + var scrollSize = $.camelCase(['scroll', dimension].join('-')) + + this.$element + .one('bsTransitionEnd', $.proxy(complete, this)) + .emulateTransitionEnd(350)[dimension](this.$element[0][scrollSize]) + } + + Collapse.prototype.hide = function () { + if (this.transitioning || !this.$element.hasClass('in')) return + + var startEvent = $.Event('hide.bs.collapse') + this.$element.trigger(startEvent) + if (startEvent.isDefaultPrevented()) return + + var dimension = this.dimension() + + this.$element[dimension](this.$element[dimension]())[0].offsetHeight + + this.$element + .addClass('collapsing') + .removeClass('collapse') + .removeClass('in') + + this.transitioning = 1 + + var complete = function () { + this.transitioning = 0 + this.$element + .trigger('hidden.bs.collapse') + .removeClass('collapsing') + .addClass('collapse') + } + + if (!$.support.transition) return complete.call(this) + + this.$element + [dimension](0) + .one('bsTransitionEnd', $.proxy(complete, this)) + .emulateTransitionEnd(350) + } + + Collapse.prototype.toggle = function () { + this[this.$element.hasClass('in') ? 'hide' : 'show']() + } + + + // COLLAPSE PLUGIN DEFINITION + // ========================== + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.collapse') + var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option) + + if (!data && options.toggle && option == 'show') option = !option + if (!data) $this.data('bs.collapse', (data = new Collapse(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + var old = $.fn.collapse + + $.fn.collapse = Plugin + $.fn.collapse.Constructor = Collapse + + + // COLLAPSE NO CONFLICT + // ==================== + + $.fn.collapse.noConflict = function () { + $.fn.collapse = old + return this + } + + + // COLLAPSE DATA-API + // ================= + + $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) { + var href + var $this = $(this) + var target = $this.attr('data-target') + || e.preventDefault() + || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7 + var $target = $(target) + var data = $target.data('bs.collapse') + var option = data ? 'toggle' : $this.data() + var parent = $this.attr('data-parent') + var $parent = parent && $(parent) + + if (!data || !data.transitioning) { + if ($parent) $parent.find('[data-toggle="collapse"][data-parent="' + parent + '"]').not($this).addClass('collapsed') + $this[$target.hasClass('in') ? 'addClass' : 'removeClass']('collapsed') + } + + Plugin.call($target, option) + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: dropdown.js v3.2.0 + * http://getbootstrap.com/javascript/#dropdowns + * ======================================================================== + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // DROPDOWN CLASS DEFINITION + // ========================= + + var backdrop = '.dropdown-backdrop' + var toggle = '[data-toggle="dropdown"]' + var Dropdown = function (element) { + $(element).on('click.bs.dropdown', this.toggle) + } + + Dropdown.VERSION = '3.2.0' + + Dropdown.prototype.toggle = function (e) { + var $this = $(this) + + if ($this.is('.disabled, :disabled')) return + + var $parent = getParent($this) + var isActive = $parent.hasClass('open') + + clearMenus() + + if (!isActive) { + if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) { + // if mobile we use a backdrop because click events don't delegate + $('<div class="dropdown-backdrop"/>').insertAfter($(this)).on('click', clearMenus) + } + + var relatedTarget = { relatedTarget: this } + $parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget)) + + if (e.isDefaultPrevented()) return + + $this.trigger('focus') + + $parent + .toggleClass('open') + .trigger('shown.bs.dropdown', relatedTarget) + } + + return false + } + + Dropdown.prototype.keydown = function (e) { + if (!/(38|40|27)/.test(e.keyCode)) return + + var $this = $(this) + + e.preventDefault() + e.stopPropagation() + + if ($this.is('.disabled, :disabled')) return + + var $parent = getParent($this) + var isActive = $parent.hasClass('open') + + if (!isActive || (isActive && e.keyCode == 27)) { + if (e.which == 27) $parent.find(toggle).trigger('focus') + return $this.trigger('click') + } + + var desc = ' li:not(.divider):visible a' + var $items = $parent.find('[role="menu"]' + desc + ', [role="listbox"]' + desc) + + if (!$items.length) return + + var index = $items.index($items.filter(':focus')) + + if (e.keyCode == 38 && index > 0) index-- // up + if (e.keyCode == 40 && index < $items.length - 1) index++ // down + if (!~index) index = 0 + + $items.eq(index).trigger('focus') + } + + function clearMenus(e) { + if (e && e.which === 3) return + $(backdrop).remove() + $(toggle).each(function () { + var $parent = getParent($(this)) + var relatedTarget = { relatedTarget: this } + if (!$parent.hasClass('open')) return + $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget)) + if (e.isDefaultPrevented()) return + $parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget) + }) + } + + function getParent($this) { + var selector = $this.attr('data-target') + + if (!selector) { + selector = $this.attr('href') + selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 + } + + var $parent = selector && $(selector) + + return $parent && $parent.length ? $parent : $this.parent() + } + + + // DROPDOWN PLUGIN DEFINITION + // ========================== + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.dropdown') + + if (!data) $this.data('bs.dropdown', (data = new Dropdown(this))) + if (typeof option == 'string') data[option].call($this) + }) + } + + var old = $.fn.dropdown + + $.fn.dropdown = Plugin + $.fn.dropdown.Constructor = Dropdown + + + // DROPDOWN NO CONFLICT + // ==================== + + $.fn.dropdown.noConflict = function () { + $.fn.dropdown = old + return this + } + + + // APPLY TO STANDARD DROPDOWN ELEMENTS + // =================================== + + $(document) + .on('click.bs.dropdown.data-api', clearMenus) + .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() }) + .on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle) + .on('keydown.bs.dropdown.data-api', toggle + ', [role="menu"], [role="listbox"]', Dropdown.prototype.keydown) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: modal.js v3.2.0 + * http://getbootstrap.com/javascript/#modals + * ======================================================================== + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // MODAL CLASS DEFINITION + // ====================== + + var Modal = function (element, options) { + this.options = options + this.$body = $(document.body) + this.$element = $(element) + this.$backdrop = + this.isShown = null + this.scrollbarWidth = 0 + + if (this.options.remote) { + this.$element + .find('.modal-content') + .load(this.options.remote, $.proxy(function () { + this.$element.trigger('loaded.bs.modal') + }, this)) + } + } + + Modal.VERSION = '3.2.0' + + Modal.DEFAULTS = { + backdrop: true, + keyboard: true, + show: true + } + + Modal.prototype.toggle = function (_relatedTarget) { + return this.isShown ? this.hide() : this.show(_relatedTarget) + } + + Modal.prototype.show = function (_relatedTarget) { + var that = this + var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget }) + + this.$element.trigger(e) + + if (this.isShown || e.isDefaultPrevented()) return + + this.isShown = true + + this.checkScrollbar() + this.$body.addClass('modal-open') + + this.setScrollbar() + this.escape() + + this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this)) + + this.backdrop(function () { + var transition = $.support.transition && that.$element.hasClass('fade') + + if (!that.$element.parent().length) { + that.$element.appendTo(that.$body) // don't move modals dom position + } + + that.$element + .show() + .scrollTop(0) + + if (transition) { + that.$element[0].offsetWidth // force reflow + } + + that.$element + .addClass('in') + .attr('aria-hidden', false) + + that.enforceFocus() + + var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget }) + + transition ? + that.$element.find('.modal-dialog') // wait for modal to slide in + .one('bsTransitionEnd', function () { + that.$element.trigger('focus').trigger(e) + }) + .emulateTransitionEnd(300) : + that.$element.trigger('focus').trigger(e) + }) + } + + Modal.prototype.hide = function (e) { + if (e) e.preventDefault() + + e = $.Event('hide.bs.modal') + + this.$element.trigger(e) + + if (!this.isShown || e.isDefaultPrevented()) return + + this.isShown = false + + this.$body.removeClass('modal-open') + + this.resetScrollbar() + this.escape() + + $(document).off('focusin.bs.modal') + + this.$element + .removeClass('in') + .attr('aria-hidden', true) + .off('click.dismiss.bs.modal') + + $.support.transition && this.$element.hasClass('fade') ? + this.$element + .one('bsTransitionEnd', $.proxy(this.hideModal, this)) + .emulateTransitionEnd(300) : + this.hideModal() + } + + Modal.prototype.enforceFocus = function () { + $(document) + .off('focusin.bs.modal') // guard against infinite focus loop + .on('focusin.bs.modal', $.proxy(function (e) { + if (this.$element[0] !== e.target && !this.$element.has(e.target).length) { + this.$element.trigger('focus') + } + }, this)) + } + + Modal.prototype.escape = function () { + if (this.isShown && this.options.keyboard) { + this.$element.on('keyup.dismiss.bs.modal', $.proxy(function (e) { + e.which == 27 && this.hide() + }, this)) + } else if (!this.isShown) { + this.$element.off('keyup.dismiss.bs.modal') + } + } + + Modal.prototype.hideModal = function () { + var that = this + this.$element.hide() + this.backdrop(function () { + that.$element.trigger('hidden.bs.modal') + }) + } + + Modal.prototype.removeBackdrop = function () { + this.$backdrop && this.$backdrop.remove() + this.$backdrop = null + } + + Modal.prototype.backdrop = function (callback) { + var that = this + var animate = this.$element.hasClass('fade') ? 'fade' : '' + + if (this.isShown && this.options.backdrop) { + var doAnimate = $.support.transition && animate + + this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />') + .appendTo(this.$body) + + this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) { + if (e.target !== e.currentTarget) return + this.options.backdrop == 'static' + ? this.$element[0].focus.call(this.$element[0]) + : this.hide.call(this) + }, this)) + + if (doAnimate) this.$backdrop[0].offsetWidth // force reflow + + this.$backdrop.addClass('in') + + if (!callback) return + + doAnimate ? + this.$backdrop + .one('bsTransitionEnd', callback) + .emulateTransitionEnd(150) : + callback() + + } else if (!this.isShown && this.$backdrop) { + this.$backdrop.removeClass('in') + + var callbackRemove = function () { + that.removeBackdrop() + callback && callback() + } + $.support.transition && this.$element.hasClass('fade') ? + this.$backdrop + .one('bsTransitionEnd', callbackRemove) + .emulateTransitionEnd(150) : + callbackRemove() + + } else if (callback) { + callback() + } + } + + Modal.prototype.checkScrollbar = function () { + if (document.body.clientWidth >= window.innerWidth) return + this.scrollbarWidth = this.scrollbarWidth || this.measureScrollbar() + } + + Modal.prototype.setScrollbar = function () { + var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10) + if (this.scrollbarWidth) this.$body.css('padding-right', bodyPad + this.scrollbarWidth) + } + + Modal.prototype.resetScrollbar = function () { + this.$body.css('padding-right', '') + } + + Modal.prototype.measureScrollbar = function () { // thx walsh + var scrollDiv = document.createElement('div') + scrollDiv.className = 'modal-scrollbar-measure' + this.$body.append(scrollDiv) + var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth + this.$body[0].removeChild(scrollDiv) + return scrollbarWidth + } + + + // MODAL PLUGIN DEFINITION + // ======================= + + function Plugin(option, _relatedTarget) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.modal') + var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option) + + if (!data) $this.data('bs.modal', (data = new Modal(this, options))) + if (typeof option == 'string') data[option](_relatedTarget) + else if (options.show) data.show(_relatedTarget) + }) + } + + var old = $.fn.modal + + $.fn.modal = Plugin + $.fn.modal.Constructor = Modal + + + // MODAL NO CONFLICT + // ================= + + $.fn.modal.noConflict = function () { + $.fn.modal = old + return this + } + + + // MODAL DATA-API + // ============== + + $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) { + var $this = $(this) + var href = $this.attr('href') + var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) // strip for ie7 + var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data()) + + if ($this.is('a')) e.preventDefault() + + $target.one('show.bs.modal', function (showEvent) { + if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown + $target.one('hidden.bs.modal', function () { + $this.is(':visible') && $this.trigger('focus') + }) + }) + Plugin.call($target, option, this) + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: tooltip.js v3.2.0 + * http://getbootstrap.com/javascript/#tooltip + * Inspired by the original jQuery.tipsy by Jason Frame + * ======================================================================== + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + +//+function ($) { +// 'use strict'; +// +// // TOOLTIP PUBLIC CLASS DEFINITION +// // =============================== +// +// var Tooltip = function (element, options) { +// this.type = +// this.options = +// this.enabled = +// this.timeout = +// this.hoverState = +// this.$element = null +// +// this.init('tooltip', element, options) +// } +// +// Tooltip.VERSION = '3.2.0' +// +// Tooltip.DEFAULTS = { +// animation: true, +// placement: 'top', +// selector: false, +// template: '<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>', +// trigger: 'hover focus', +// title: '', +// delay: 0, +// html: false, +// container: false, +// viewport: { +// selector: 'body', +// padding: 0 +// } +// } +// +// Tooltip.prototype.init = function (type, element, options) { +// this.enabled = true +// this.type = type +// this.$element = $(element) +// this.options = this.getOptions(options) +// this.$viewport = this.options.viewport && $(this.options.viewport.selector || this.options.viewport) +// +// var triggers = this.options.trigger.split(' ') +// +// for (var i = triggers.length; i--;) { +// var trigger = triggers[i] +// +// if (trigger == 'click') { +// this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this)) +// } else if (trigger != 'manual') { +// var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin' +// var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout' +// +// this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this)) +// this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this)) +// } +// } +// +// this.options.selector ? +// (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) : +// this.fixTitle() +// } +// +// Tooltip.prototype.getDefaults = function () { +// return Tooltip.DEFAULTS +// } +// +// Tooltip.prototype.getOptions = function (options) { +// options = $.extend({}, this.getDefaults(), this.$element.data(), options) +// +// if (options.delay && typeof options.delay == 'number') { +// options.delay = { +// show: options.delay, +// hide: options.delay +// } +// } +// +// return options +// } +// +// Tooltip.prototype.getDelegateOptions = function () { +// var options = {} +// var defaults = this.getDefaults() +// +// this._options && $.each(this._options, function (key, value) { +// if (defaults[key] != value) options[key] = value +// }) +// +// return options +// } +// +// Tooltip.prototype.enter = function (obj) { +// var self = obj instanceof this.constructor ? +// obj : $(obj.currentTarget).data('bs.' + this.type) +// +// if (!self) { +// self = new this.constructor(obj.currentTarget, this.getDelegateOptions()) +// $(obj.currentTarget).data('bs.' + this.type, self) +// } +// +// clearTimeout(self.timeout) +// +// self.hoverState = 'in' +// +// if (!self.options.delay || !self.options.delay.show) return self.show() +// +// self.timeout = setTimeout(function () { +// if (self.hoverState == 'in') self.show() +// }, self.options.delay.show) +// } +// +// Tooltip.prototype.leave = function (obj) { +// var self = obj instanceof this.constructor ? +// obj : $(obj.currentTarget).data('bs.' + this.type) +// +// if (!self) { +// self = new this.constructor(obj.currentTarget, this.getDelegateOptions()) +// $(obj.currentTarget).data('bs.' + this.type, self) +// } +// +// clearTimeout(self.timeout) +// +// self.hoverState = 'out' +// +// if (!self.options.delay || !self.options.delay.hide) return self.hide() +// +// self.timeout = setTimeout(function () { +// if (self.hoverState == 'out') self.hide() +// }, self.options.delay.hide) +// } +// +// Tooltip.prototype.show = function () { +// var e = $.Event('show.bs.' + this.type) +// +// if (this.hasContent() && this.enabled) { +// this.$element.trigger(e) +// +// var inDom = $.contains(document.documentElement, this.$element[0]) +// if (e.isDefaultPrevented() || !inDom) return +// var that = this +// +// var $tip = this.tip() +// +// var tipId = this.getUID(this.type) +// +// this.setContent() +// $tip.attr('id', tipId) +// this.$element.attr('aria-describedby', tipId) +// +// if (this.options.animation) $tip.addClass('fade') +// +// var placement = typeof this.options.placement == 'function' ? +// this.options.placement.call(this, $tip[0], this.$element[0]) : +// this.options.placement +// +// var autoToken = /\s?auto?\s?/i +// var autoPlace = autoToken.test(placement) +// if (autoPlace) placement = placement.replace(autoToken, '') || 'top' +// +// $tip +// .detach() +// .css({ top: 0, left: 0, display: 'block' }) +// .addClass(placement) +// .data('bs.' + this.type, this) +// +// this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element) +// +// var pos = this.getPosition() +// var actualWidth = $tip[0].offsetWidth +// var actualHeight = $tip[0].offsetHeight +// +// if (autoPlace) { +// var orgPlacement = placement +// var $parent = this.$element.parent() +// var parentDim = this.getPosition($parent) +// +// placement = placement == 'bottom' && pos.top + pos.height + actualHeight - parentDim.scroll > parentDim.height ? 'top' : +// placement == 'top' && pos.top - parentDim.scroll - actualHeight < 0 ? 'bottom' : +// placement == 'right' && pos.right + actualWidth > parentDim.width ? 'left' : +// placement == 'left' && pos.left - actualWidth < parentDim.left ? 'right' : +// placement +// +// $tip +// .removeClass(orgPlacement) +// .addClass(placement) +// } +// +// var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight) +// +// this.applyPlacement(calculatedOffset, placement) +// +// var complete = function () { +// that.$element.trigger('shown.bs.' + that.type) +// that.hoverState = null +// } +// +// $.support.transition && this.$tip.hasClass('fade') ? +// $tip +// .one('bsTransitionEnd', complete) +// .emulateTransitionEnd(150) : +// complete() +// } +// } +// +// Tooltip.prototype.applyPlacement = function (offset, placement) { +// var $tip = this.tip() +// var width = $tip[0].offsetWidth +// var height = $tip[0].offsetHeight +// +// // manually read margins because getBoundingClientRect includes difference +// var marginTop = parseInt($tip.css('margin-top'), 10) +// var marginLeft = parseInt($tip.css('margin-left'), 10) +// +// // we must check for NaN for ie 8/9 +// if (isNaN(marginTop)) marginTop = 0 +// if (isNaN(marginLeft)) marginLeft = 0 +// +// offset.top = offset.top + marginTop +// offset.left = offset.left + marginLeft +// +// // $.fn.offset doesn't round pixel values +// // so we use setOffset directly with our own function B-0 +// $.offset.setOffset($tip[0], $.extend({ +// using: function (props) { +// $tip.css({ +// top: Math.round(props.top), +// left: Math.round(props.left) +// }) +// } +// }, offset), 0) +// +// $tip.addClass('in') +// +// // check to see if placing tip in new offset caused the tip to resize itself +// var actualWidth = $tip[0].offsetWidth +// var actualHeight = $tip[0].offsetHeight +// +// if (placement == 'top' && actualHeight != height) { +// offset.top = offset.top + height - actualHeight +// } +// +// var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight) +// +// if (delta.left) offset.left += delta.left +// else offset.top += delta.top +// +// var arrowDelta = delta.left ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight +// var arrowPosition = delta.left ? 'left' : 'top' +// var arrowOffsetPosition = delta.left ? 'offsetWidth' : 'offsetHeight' +// +// $tip.offset(offset) +// this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], arrowPosition) +// } +// +// Tooltip.prototype.replaceArrow = function (delta, dimension, position) { +// this.arrow().css(position, delta ? (50 * (1 - delta / dimension) + '%') : '') +// } +// +// Tooltip.prototype.setContent = function () { +// var $tip = this.tip() +// var title = this.getTitle() +// +// $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title) +// $tip.removeClass('fade in top bottom left right') +// } +// +// Tooltip.prototype.hide = function () { +// var that = this +// var $tip = this.tip() +// var e = $.Event('hide.bs.' + this.type) +// +// this.$element.removeAttr('aria-describedby') +// +// function complete() { +// if (that.hoverState != 'in') $tip.detach() +// that.$element.trigger('hidden.bs.' + that.type) +// } +// +// this.$element.trigger(e) +// +// if (e.isDefaultPrevented()) return +// +// $tip.removeClass('in') +// +// $.support.transition && this.$tip.hasClass('fade') ? +// $tip +// .one('bsTransitionEnd', complete) +// .emulateTransitionEnd(150) : +// complete() +// +// this.hoverState = null +// +// return this +// } +// +// Tooltip.prototype.fixTitle = function () { +// var $e = this.$element +// if ($e.attr('title') || typeof ($e.attr('data-original-title')) != 'string') { +// $e.attr('data-original-title', $e.attr('title') || '').attr('title', '') +// } +// } +// +// Tooltip.prototype.hasContent = function () { +// return this.getTitle() +// } +// +// Tooltip.prototype.getPosition = function ($element) { +// $element = $element || this.$element +// var el = $element[0] +// var isBody = el.tagName == 'BODY' +// return $.extend({}, (typeof el.getBoundingClientRect == 'function') ? el.getBoundingClientRect() : null, { +// scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop(), +// width: isBody ? $(window).width() : $element.outerWidth(), +// height: isBody ? $(window).height() : $element.outerHeight() +// }, isBody ? { top: 0, left: 0 } : $element.offset()) +// } +// +// Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) { +// return placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } : +// placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } : +// placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } : +// /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width } +// +// } +// +// Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) { +// var delta = { top: 0, left: 0 } +// if (!this.$viewport) return delta +// +// var viewportPadding = this.options.viewport && this.options.viewport.padding || 0 +// var viewportDimensions = this.getPosition(this.$viewport) +// +// if (/right|left/.test(placement)) { +// var topEdgeOffset = pos.top - viewportPadding - viewportDimensions.scroll +// var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight +// if (topEdgeOffset < viewportDimensions.top) { // top overflow +// delta.top = viewportDimensions.top - topEdgeOffset +// } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow +// delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset +// } +// } else { +// var leftEdgeOffset = pos.left - viewportPadding +// var rightEdgeOffset = pos.left + viewportPadding + actualWidth +// if (leftEdgeOffset < viewportDimensions.left) { // left overflow +// delta.left = viewportDimensions.left - leftEdgeOffset +// } else if (rightEdgeOffset > viewportDimensions.width) { // right overflow +// delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset +// } +// } +// +// return delta +// } +// +// Tooltip.prototype.getTitle = function () { +// var title +// var $e = this.$element +// var o = this.options +// +// title = $e.attr('data-original-title') +// || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title) +// +// return title +// } +// +// Tooltip.prototype.getUID = function (prefix) { +// do prefix += ~~(Math.random() * 1000000) +// while (document.getElementById(prefix)) +// return prefix +// } +// +// Tooltip.prototype.tip = function () { +// return (this.$tip = this.$tip || $(this.options.template)) +// } +// +// Tooltip.prototype.arrow = function () { +// return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow')) +// } +// +// Tooltip.prototype.validate = function () { +// if (!this.$element[0].parentNode) { +// this.hide() +// this.$element = null +// this.options = null +// } +// } +// +// Tooltip.prototype.enable = function () { +// this.enabled = true +// } +// +// Tooltip.prototype.disable = function () { +// this.enabled = false +// } +// +// Tooltip.prototype.toggleEnabled = function () { +// this.enabled = !this.enabled +// } +// +// Tooltip.prototype.toggle = function (e) { +// var self = this +// if (e) { +// self = $(e.currentTarget).data('bs.' + this.type) +// if (!self) { +// self = new this.constructor(e.currentTarget, this.getDelegateOptions()) +// $(e.currentTarget).data('bs.' + this.type, self) +// } +// } +// +// self.tip().hasClass('in') ? self.leave(self) : self.enter(self) +// } +// +// Tooltip.prototype.destroy = function () { +// clearTimeout(this.timeout) +// this.hide().$element.off('.' + this.type).removeData('bs.' + this.type) +// } +// +// +// // TOOLTIP PLUGIN DEFINITION +// // ========================= +// +// function Plugin(option) { +// return this.each(function () { +// var $this = $(this) +// var data = $this.data('bs.tooltip') +// var options = typeof option == 'object' && option +// +// if (!data && option == 'destroy') return +// if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options))) +// if (typeof option == 'string') data[option]() +// }) +// } +// +// var old = $.fn.tooltip +// +// $.fn.tooltip = Plugin +// $.fn.tooltip.Constructor = Tooltip +// +// +// // TOOLTIP NO CONFLICT +// // =================== +// +// $.fn.tooltip.noConflict = function () { +// $.fn.tooltip = old +// return this +// } +// +//}(jQuery); + +/* ======================================================================== + * Bootstrap: popover.js v3.2.0 + * http://getbootstrap.com/javascript/#popovers + * ======================================================================== + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + +//+function ($) { +// 'use strict'; +// +// // POPOVER PUBLIC CLASS DEFINITION +// // =============================== +// +// var Popover = function (element, options) { +// this.init('popover', element, options) +// } +// +// if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js') +// +// Popover.VERSION = '3.2.0' +// +// Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, { +// placement: 'right', +// trigger: 'click', +// content: '', +// template: '<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>' +// }) +// +// +// // NOTE: POPOVER EXTENDS tooltip.js +// // ================================ +// +// Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype) +// +// Popover.prototype.constructor = Popover +// +// Popover.prototype.getDefaults = function () { +// return Popover.DEFAULTS +// } +// +// Popover.prototype.setContent = function () { +// var $tip = this.tip() +// var title = this.getTitle() +// var content = this.getContent() +// +// $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title) +// $tip.find('.popover-content').empty()[ // we use append for html objects to maintain js events +// this.options.html ? (typeof content == 'string' ? 'html' : 'append') : 'text' +// ](content) +// +// $tip.removeClass('fade top bottom left right in') +// +// // IE8 doesn't accept hiding via the `:empty` pseudo selector, we have to do +// // this manually by checking the contents. +// if (!$tip.find('.popover-title').html()) $tip.find('.popover-title').hide() +// } +// +// Popover.prototype.hasContent = function () { +// return this.getTitle() || this.getContent() +// } +// +// Popover.prototype.getContent = function () { +// var $e = this.$element +// var o = this.options +// +// return $e.attr('data-content') +// || (typeof o.content == 'function' ? +// o.content.call($e[0]) : +// o.content) +// } +// +// Popover.prototype.arrow = function () { +// return (this.$arrow = this.$arrow || this.tip().find('.arrow')) +// } +// +// Popover.prototype.tip = function () { +// if (!this.$tip) this.$tip = $(this.options.template) +// return this.$tip +// } +// +// +// // POPOVER PLUGIN DEFINITION +// // ========================= +// +// function Plugin(option) { +// return this.each(function () { +// var $this = $(this) +// var data = $this.data('bs.popover') +// var options = typeof option == 'object' && option +// +// if (!data && option == 'destroy') return +// if (!data) $this.data('bs.popover', (data = new Popover(this, options))) +// if (typeof option == 'string') data[option]() +// }) +// } +// +// var old = $.fn.popover +// +// $.fn.popover = Plugin +// $.fn.popover.Constructor = Popover +// +// +// // POPOVER NO CONFLICT +// // =================== +// +// $.fn.popover.noConflict = function () { +// $.fn.popover = old +// return this +// } +// +//}(jQuery); + +/* ======================================================================== + * Bootstrap: scrollspy.js v3.2.0 + * http://getbootstrap.com/javascript/#scrollspy + * ======================================================================== + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // SCROLLSPY CLASS DEFINITION + // ========================== + + function ScrollSpy(element, options) { + var process = $.proxy(this.process, this) + + this.$body = $('body') + this.$scrollElement = $(element).is('body') ? $(window) : $(element) + this.options = $.extend({}, ScrollSpy.DEFAULTS, options) + this.selector = (this.options.target || '') + ' .nav li > a' + this.offsets = [] + this.targets = [] + this.activeTarget = null + this.scrollHeight = 0 + + this.$scrollElement.on('scroll.bs.scrollspy', process) + this.refresh() + this.process() + } + + ScrollSpy.VERSION = '3.2.0' + + ScrollSpy.DEFAULTS = { + offset: 10 + } + + ScrollSpy.prototype.getScrollHeight = function () { + return this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight) + } + + ScrollSpy.prototype.refresh = function () { + var offsetMethod = 'offset' + var offsetBase = 0 + + if (!$.isWindow(this.$scrollElement[0])) { + offsetMethod = 'position' + offsetBase = this.$scrollElement.scrollTop() + } + + this.offsets = [] + this.targets = [] + this.scrollHeight = this.getScrollHeight() + + var self = this + + this.$body + .find(this.selector) + .map(function () { + var $el = $(this) + var href = $el.data('target') || $el.attr('href') + var $href = /^#./.test(href) && $(href) + + return ($href + && $href.length + && $href.is(':visible') + && [[$href[offsetMethod]().top + offsetBase, href]]) || null + }) + .sort(function (a, b) { return a[0] - b[0] }) + .each(function () { + self.offsets.push(this[0]) + self.targets.push(this[1]) + }) + } + + ScrollSpy.prototype.process = function () { + var scrollTop = this.$scrollElement.scrollTop() + this.options.offset + var scrollHeight = this.getScrollHeight() + var maxScroll = this.options.offset + scrollHeight - this.$scrollElement.height() + var offsets = this.offsets + var targets = this.targets + var activeTarget = this.activeTarget + var i + + if (this.scrollHeight != scrollHeight) { + this.refresh() + } + + if (scrollTop >= maxScroll) { + return activeTarget != (i = targets[targets.length - 1]) && this.activate(i) + } + + if (activeTarget && scrollTop <= offsets[0]) { + return activeTarget != (i = targets[0]) && this.activate(i) + } + + for (i = offsets.length; i--;) { + activeTarget != targets[i] + && scrollTop >= offsets[i] + && (!offsets[i + 1] || scrollTop <= offsets[i + 1]) + && this.activate(targets[i]) + } + } + + ScrollSpy.prototype.activate = function (target) { + this.activeTarget = target + + $(this.selector) + .parentsUntil(this.options.target, '.active') + .removeClass('active') + + var selector = this.selector + + '[data-target="' + target + '"],' + + this.selector + '[href="' + target + '"]' + + var active = $(selector) + .parents('li') + .addClass('active') + + if (active.parent('.dropdown-menu').length) { + active = active + .closest('li.dropdown') + .addClass('active') + } + + active.trigger('activate.bs.scrollspy') + } + + + // SCROLLSPY PLUGIN DEFINITION + // =========================== + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.scrollspy') + var options = typeof option == 'object' && option + + if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + var old = $.fn.scrollspy + + $.fn.scrollspy = Plugin + $.fn.scrollspy.Constructor = ScrollSpy + + + // SCROLLSPY NO CONFLICT + // ===================== + + $.fn.scrollspy.noConflict = function () { + $.fn.scrollspy = old + return this + } + + + // SCROLLSPY DATA-API + // ================== + + $(window).on('load.bs.scrollspy.data-api', function () { + $('[data-spy="scroll"]').each(function () { + var $spy = $(this) + Plugin.call($spy, $spy.data()) + }) + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: tab.js v3.2.0 + * http://getbootstrap.com/javascript/#tabs + * ======================================================================== + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // TAB CLASS DEFINITION + // ==================== + + var Tab = function (element) { + this.element = $(element) + } + + Tab.VERSION = '3.2.0' + + Tab.prototype.show = function () { + var $this = this.element + var $ul = $this.closest('ul:not(.dropdown-menu)') + var selector = $this.data('target') + + if (!selector) { + selector = $this.attr('href') + selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 + } + + if ($this.parent('li').hasClass('active')) return + + var previous = $ul.find('.active:last a')[0] + var e = $.Event('show.bs.tab', { + relatedTarget: previous + }) + + $this.trigger(e) + + if (e.isDefaultPrevented()) return + + var $target = $(selector) + + this.activate($this.closest('li'), $ul) + this.activate($target, $target.parent(), function () { + $this.trigger({ + type: 'shown.bs.tab', + relatedTarget: previous + }) + }) + } + + Tab.prototype.activate = function (element, container, callback) { + var $active = container.find('> .active') + var transition = callback + && $.support.transition + && $active.hasClass('fade') + + function next() { + $active + .removeClass('active') + .find('> .dropdown-menu > .active') + .removeClass('active') + + element.addClass('active') + + if (transition) { + element[0].offsetWidth // reflow for transition + element.addClass('in') + } else { + element.removeClass('fade') + } + + if (element.parent('.dropdown-menu')) { + element.closest('li.dropdown').addClass('active') + } + + callback && callback() + } + + transition ? + $active + .one('bsTransitionEnd', next) + .emulateTransitionEnd(150) : + next() + + $active.removeClass('in') + } + + + // TAB PLUGIN DEFINITION + // ===================== + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.tab') + + if (!data) $this.data('bs.tab', (data = new Tab(this))) + if (typeof option == 'string') data[option]() + }) + } + + var old = $.fn.tab + + $.fn.tab = Plugin + $.fn.tab.Constructor = Tab + + + // TAB NO CONFLICT + // =============== + + $.fn.tab.noConflict = function () { + $.fn.tab = old + return this + } + + + // TAB DATA-API + // ============ + + $(document).on('click.bs.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) { + e.preventDefault() + Plugin.call($(this), 'show') + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: affix.js v3.2.0 + * http://getbootstrap.com/javascript/#affix + * ======================================================================== + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // AFFIX CLASS DEFINITION + // ====================== + + var Affix = function (element, options) { + this.options = $.extend({}, Affix.DEFAULTS, options) + + this.$target = $(this.options.target) + .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this)) + .on('click.bs.affix.data-api', $.proxy(this.checkPositionWithEventLoop, this)) + + this.$element = $(element) + this.affixed = + this.unpin = + this.pinnedOffset = null + + this.checkPosition() + } + + Affix.VERSION = '3.2.0' + + Affix.RESET = 'affix affix-top affix-bottom' + + Affix.DEFAULTS = { + offset: 0, + target: window + } + + Affix.prototype.getPinnedOffset = function () { + if (this.pinnedOffset) return this.pinnedOffset + this.$element.removeClass(Affix.RESET).addClass('affix') + var scrollTop = this.$target.scrollTop() + var position = this.$element.offset() + return (this.pinnedOffset = position.top - scrollTop) + } + + Affix.prototype.checkPositionWithEventLoop = function () { + setTimeout($.proxy(this.checkPosition, this), 1) + } + + Affix.prototype.checkPosition = function () { + if (!this.$element.is(':visible')) return + + var scrollHeight = $(document).height() + var scrollTop = this.$target.scrollTop() + var position = this.$element.offset() + var offset = this.options.offset + var offsetTop = offset.top + var offsetBottom = offset.bottom + + if (typeof offset != 'object') offsetBottom = offsetTop = offset + if (typeof offsetTop == 'function') offsetTop = offset.top(this.$element) + if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element) + + var affix = this.unpin != null && (scrollTop + this.unpin <= position.top) ? false : + offsetBottom != null && (position.top + this.$element.height() >= scrollHeight - offsetBottom) ? 'bottom' : + offsetTop != null && (scrollTop <= offsetTop) ? 'top' : false + + if (this.affixed === affix) return + if (this.unpin != null) this.$element.css('top', '') + + var affixType = 'affix' + (affix ? '-' + affix : '') + var e = $.Event(affixType + '.bs.affix') + + this.$element.trigger(e) + + if (e.isDefaultPrevented()) return + + this.affixed = affix + this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null + + this.$element + .removeClass(Affix.RESET) + .addClass(affixType) + .trigger($.Event(affixType.replace('affix', 'affixed'))) + + if (affix == 'bottom') { + this.$element.offset({ + top: scrollHeight - this.$element.height() - offsetBottom + }) + } + } + + + // AFFIX PLUGIN DEFINITION + // ======================= + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.affix') + var options = typeof option == 'object' && option + + if (!data) $this.data('bs.affix', (data = new Affix(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + var old = $.fn.affix + + $.fn.affix = Plugin + $.fn.affix.Constructor = Affix + + + // AFFIX NO CONFLICT + // ================= + + $.fn.affix.noConflict = function () { + $.fn.affix = old + return this + } + + + // AFFIX DATA-API + // ============== + + $(window).on('load', function () { + $('[data-spy="affix"]').each(function () { + var $spy = $(this) + var data = $spy.data() + + data.offset = data.offset || {} + + if (data.offsetBottom) data.offset.bottom = data.offsetBottom + if (data.offsetTop) data.offset.top = data.offsetTop + + Plugin.call($spy, data) + }) + }) + +}(jQuery); + +/* ======================================================================== + * bootstrap-switch - v3.0.2 + * http://www.bootstrap-switch.org + * ======================================================================== + * Copyright 2012-2013 Mattia Larentis + * + * ======================================================================== + * 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. + * ======================================================================== + */ + +(function() { + var __slice = [].slice; + + (function($, window) { + "use strict"; + var BootstrapSwitch; + BootstrapSwitch = (function() { + function BootstrapSwitch(element, options) { + if (options == null) { + options = {}; + } + this.$element = $(element); + this.options = $.extend({}, $.fn.bootstrapSwitch.defaults, { + state: this.$element.is(":checked"), + size: this.$element.data("size"), + animate: this.$element.data("animate"), + disabled: this.$element.is(":disabled"), + readonly: this.$element.is("[readonly]"), + indeterminate: this.$element.data("indeterminate"), + onColor: this.$element.data("on-color"), + offColor: this.$element.data("off-color"), + onText: this.$element.data("on-text"), + offText: this.$element.data("off-text"), + labelText: this.$element.data("label-text"), + baseClass: this.$element.data("base-class"), + wrapperClass: this.$element.data("wrapper-class"), + radioAllOff: this.$element.data("radio-all-off") + }, options); + this.$wrapper = $("<div>", { + "class": (function(_this) { + return function() { + var classes; + classes = ["" + _this.options.baseClass].concat(_this._getClasses(_this.options.wrapperClass)); + classes.push(_this.options.state ? "" + _this.options.baseClass + "-on" : "" + _this.options.baseClass + "-off"); + if (_this.options.size != null) { + classes.push("" + _this.options.baseClass + "-" + _this.options.size); + } + if (_this.options.animate) { + classes.push("" + _this.options.baseClass + "-animate"); + } + if (_this.options.disabled) { + classes.push("" + _this.options.baseClass + "-disabled"); + } + if (_this.options.readonly) { + classes.push("" + _this.options.baseClass + "-readonly"); + } + if (_this.options.indeterminate) { + classes.push("" + _this.options.baseClass + "-indeterminate"); + } + if (_this.$element.attr("id")) { + classes.push("" + _this.options.baseClass + "-id-" + (_this.$element.attr("id"))); + } + return classes.join(" "); + }; + })(this)() + }); + this.$container = $("<div>", { + "class": "" + this.options.baseClass + "-container" + }); + this.$on = $("<span>", { + html: this.options.onText, + "class": "" + this.options.baseClass + "-handle-on " + this.options.baseClass + "-" + this.options.onColor + }); + this.$off = $("<span>", { + html: this.options.offText, + "class": "" + this.options.baseClass + "-handle-off " + this.options.baseClass + "-" + this.options.offColor + }); + this.$label = $("<label>", { + html: this.options.labelText, + "class": "" + this.options.baseClass + "-label" + }); + if (this.options.indeterminate) { + this.$element.prop("indeterminate", true); + } + this.$element.on("init.bootstrapSwitch", (function(_this) { + return function() { + return _this.options.onInit.apply(element, arguments); + }; + })(this)); + this.$element.on("switchChange.bootstrapSwitch", (function(_this) { + return function() { + return _this.options.onSwitchChange.apply(element, arguments); + }; + })(this)); + this.$container = this.$element.wrap(this.$container).parent(); + this.$wrapper = this.$container.wrap(this.$wrapper).parent(); + this.$element.before(this.$on).before(this.$label).before(this.$off).trigger("init.bootstrapSwitch"); + this._elementHandlers(); + this._handleHandlers(); + this._labelHandlers(); + this._formHandler(); + } + + BootstrapSwitch.prototype._constructor = BootstrapSwitch; + + BootstrapSwitch.prototype.state = function(value, skip) { + if (typeof value === "undefined") { + return this.options.state; + } + if (this.options.disabled || this.options.readonly || this.options.indeterminate) { + return this.$element; + } + if (this.options.state && !this.options.radioAllOff && this.$element.is(':radio')) { + return this.$element; + } + value = !!value; + this.$element.prop("checked", value).trigger("change.bootstrapSwitch", skip); + return this.$element; + }; + + BootstrapSwitch.prototype.toggleState = function(skip) { + if (this.options.disabled || this.options.readonly || this.options.indeterminate) { + return this.$element; + } + return this.$element.prop("checked", !this.options.state).trigger("change.bootstrapSwitch", skip); + }; + + BootstrapSwitch.prototype.size = function(value) { + if (typeof value === "undefined") { + return this.options.size; + } + if (this.options.size != null) { + this.$wrapper.removeClass("" + this.options.baseClass + "-" + this.options.size); + } + if (value) { + this.$wrapper.addClass("" + this.options.baseClass + "-" + value); + } + this.options.size = value; + return this.$element; + }; + + BootstrapSwitch.prototype.animate = function(value) { + if (typeof value === "undefined") { + return this.options.animate; + } + value = !!value; + this.$wrapper[value ? "addClass" : "removeClass"]("" + this.options.baseClass + "-animate"); + this.options.animate = value; + return this.$element; + }; + + BootstrapSwitch.prototype.disabled = function(value) { + if (typeof value === "undefined") { + return this.options.disabled; + } + value = !!value; + this.$wrapper[value ? "addClass" : "removeClass"]("" + this.options.baseClass + "-disabled"); + this.$element.prop("disabled", value); + this.options.disabled = value; + return this.$element; + }; + + BootstrapSwitch.prototype.toggleDisabled = function() { + this.$element.prop("disabled", !this.options.disabled); + this.$wrapper.toggleClass("" + this.options.baseClass + "-disabled"); + this.options.disabled = !this.options.disabled; + return this.$element; + }; + + BootstrapSwitch.prototype.readonly = function(value) { + if (typeof value === "undefined") { + return this.options.readonly; + } + value = !!value; + this.$wrapper[value ? "addClass" : "removeClass"]("" + this.options.baseClass + "-readonly"); + this.$element.prop("readonly", value); + this.options.readonly = value; + return this.$element; + }; + + BootstrapSwitch.prototype.toggleReadonly = function() { + this.$element.prop("readonly", !this.options.readonly); + this.$wrapper.toggleClass("" + this.options.baseClass + "-readonly"); + this.options.readonly = !this.options.readonly; + return this.$element; + }; + + BootstrapSwitch.prototype.indeterminate = function(value) { + if (typeof value === "undefined") { + return this.options.indeterminate; + } + value = !!value; + this.$wrapper[value ? "addClass" : "removeClass"]("" + this.options.baseClass + "-indeterminate"); + this.$element.prop("indeterminate", value); + this.options.indeterminate = value; + return this.$element; + }; + + BootstrapSwitch.prototype.toggleIndeterminate = function() { + this.$element.prop("indeterminate", !this.options.indeterminate); + this.$wrapper.toggleClass("" + this.options.baseClass + "-indeterminate"); + this.options.indeterminate = !this.options.indeterminate; + return this.$element; + }; + + BootstrapSwitch.prototype.onColor = function(value) { + var color; + color = this.options.onColor; + if (typeof value === "undefined") { + return color; + } + if (color != null) { + this.$on.removeClass("" + this.options.baseClass + "-" + color); + } + this.$on.addClass("" + this.options.baseClass + "-" + value); + this.options.onColor = value; + return this.$element; + }; + + BootstrapSwitch.prototype.offColor = function(value) { + var color; + color = this.options.offColor; + if (typeof value === "undefined") { + return color; + } + if (color != null) { + this.$off.removeClass("" + this.options.baseClass + "-" + color); + } + this.$off.addClass("" + this.options.baseClass + "-" + value); + this.options.offColor = value; + return this.$element; + }; + + BootstrapSwitch.prototype.onText = function(value) { + if (typeof value === "undefined") { + return this.options.onText; + } + this.$on.html(value); + this.options.onText = value; + return this.$element; + }; + + BootstrapSwitch.prototype.offText = function(value) { + if (typeof value === "undefined") { + return this.options.offText; + } + this.$off.html(value); + this.options.offText = value; + return this.$element; + }; + + BootstrapSwitch.prototype.labelText = function(value) { + if (typeof value === "undefined") { + return this.options.labelText; + } + this.$label.html(value); + this.options.labelText = value; + return this.$element; + }; + + BootstrapSwitch.prototype.baseClass = function(value) { + return this.options.baseClass; + }; + + BootstrapSwitch.prototype.wrapperClass = function(value) { + if (typeof value === "undefined") { + return this.options.wrapperClass; + } + if (!value) { + value = $.fn.bootstrapSwitch.defaults.wrapperClass; + } + this.$wrapper.removeClass(this._getClasses(this.options.wrapperClass).join(" ")); + this.$wrapper.addClass(this._getClasses(value).join(" ")); + this.options.wrapperClass = value; + return this.$element; + }; + + BootstrapSwitch.prototype.radioAllOff = function(value) { + if (typeof value === "undefined") { + return this.options.radioAllOff; + } + this.options.radioAllOff = value; + return this.$element; + }; + + BootstrapSwitch.prototype.onInit = function(value) { + if (typeof value === "undefined") { + return this.options.onInit; + } + if (!value) { + value = $.fn.bootstrapSwitch.defaults.onInit; + } + this.options.onInit = value; + return this.$element; + }; + + BootstrapSwitch.prototype.onSwitchChange = function(value) { + if (typeof value === "undefined") { + return this.options.onSwitchChange; + } + if (!value) { + value = $.fn.bootstrapSwitch.defaults.onSwitchChange; + } + this.options.onSwitchChange = value; + return this.$element; + }; + + BootstrapSwitch.prototype.destroy = function() { + var $form; + $form = this.$element.closest("form"); + if ($form.length) { + $form.off("reset.bootstrapSwitch").removeData("bootstrap-switch"); + } + this.$container.children().not(this.$element).remove(); + this.$element.unwrap().unwrap().off(".bootstrapSwitch").removeData("bootstrap-switch"); + return this.$element; + }; + + BootstrapSwitch.prototype._elementHandlers = function() { + return this.$element.on({ + "change.bootstrapSwitch": (function(_this) { + return function(e, skip) { + var checked; + e.preventDefault(); + e.stopImmediatePropagation(); + checked = _this.$element.is(":checked"); + if (checked === _this.options.state) { + return; + } + _this.options.state = checked; + _this.$wrapper.removeClass(checked ? "" + _this.options.baseClass + "-off" : "" + _this.options.baseClass + "-on").addClass(checked ? "" + _this.options.baseClass + "-on" : "" + _this.options.baseClass + "-off"); + if (!skip) { + if (_this.$element.is(":radio")) { + $("[name='" + (_this.$element.attr('name')) + "']").not(_this.$element).prop("checked", false).trigger("change.bootstrapSwitch", true); + } + return _this.$element.trigger("switchChange.bootstrapSwitch", [checked]); + } + }; + })(this), + "focus.bootstrapSwitch": (function(_this) { + return function(e) { + e.preventDefault(); + return _this.$wrapper.addClass("" + _this.options.baseClass + "-focused"); + }; + })(this), + "blur.bootstrapSwitch": (function(_this) { + return function(e) { + e.preventDefault(); + return _this.$wrapper.removeClass("" + _this.options.baseClass + "-focused"); + }; + })(this), + "keydown.bootstrapSwitch": (function(_this) { + return function(e) { + if (!e.which || _this.options.disabled || _this.options.readonly || _this.options.indeterminate) { + return; + } + switch (e.which) { + case 37: + e.preventDefault(); + e.stopImmediatePropagation(); + return _this.state(false); + case 39: + e.preventDefault(); + e.stopImmediatePropagation(); + return _this.state(true); + } + }; + })(this) + }); + }; + + BootstrapSwitch.prototype._handleHandlers = function() { + this.$on.on("click.bootstrapSwitch", (function(_this) { + return function(e) { + _this.state(false); + return _this.$element.trigger("focus.bootstrapSwitch"); + }; + })(this)); + return this.$off.on("click.bootstrapSwitch", (function(_this) { + return function(e) { + _this.state(true); + return _this.$element.trigger("focus.bootstrapSwitch"); + }; + })(this)); + }; + + BootstrapSwitch.prototype._labelHandlers = function() { + return this.$label.on({ + "mousemove.bootstrapSwitch touchmove.bootstrapSwitch": (function(_this) { + return function(e) { + var left, pageX, percent, right; + if (!_this.isLabelDragging) { + return; + } + e.preventDefault(); + _this.isLabelDragged = true; + pageX = e.pageX || e.originalEvent.touches[0].pageX; + percent = ((pageX - _this.$wrapper.offset().left) / _this.$wrapper.width()) * 100; + left = 25; + right = 75; + if (_this.options.animate) { + _this.$wrapper.removeClass("" + _this.options.baseClass + "-animate"); + } + if (percent < left) { + percent = left; + } else if (percent > right) { + percent = right; + } + _this.$container.css("margin-left", "" + (percent - right) + "%"); + return _this.$element.trigger("focus.bootstrapSwitch"); + }; + })(this), + "mousedown.bootstrapSwitch touchstart.bootstrapSwitch": (function(_this) { + return function(e) { + if (_this.isLabelDragging || _this.options.disabled || _this.options.readonly || _this.options.indeterminate) { + return; + } + e.preventDefault(); + _this.isLabelDragging = true; + return _this.$element.trigger("focus.bootstrapSwitch"); + }; + })(this), + "mouseup.bootstrapSwitch touchend.bootstrapSwitch": (function(_this) { + return function(e) { + if (!_this.isLabelDragging) { + return; + } + e.preventDefault(); + if (_this.isLabelDragged) { + _this.isLabelDragged = false; + _this.state(parseInt(_this.$container.css("margin-left"), 10) > -(_this.$container.width() / 6)); + if (_this.options.animate) { + _this.$wrapper.addClass("" + _this.options.baseClass + "-animate"); + } + _this.$container.css("margin-left", ""); + } else { + _this.state(!_this.options.state); + } + return _this.isLabelDragging = false; + }; + })(this), + "mouseleave.bootstrapSwitch": (function(_this) { + return function(e) { + return _this.$label.trigger("mouseup.bootstrapSwitch"); + }; + })(this) + }); + }; + + BootstrapSwitch.prototype._formHandler = function() { + var $form; + $form = this.$element.closest("form"); + if ($form.data("bootstrap-switch")) { + return; + } + return $form.on("reset.bootstrapSwitch", function() { + return window.setTimeout(function() { + return $form.find("input").filter(function() { + return $(this).data("bootstrap-switch"); + }).each(function() { + return $(this).bootstrapSwitch("state", this.checked); + }); + }, 1); + }).data("bootstrap-switch", true); + }; + + BootstrapSwitch.prototype._getClasses = function(classes) { + var c, cls, _i, _len; + if (!$.isArray(classes)) { + return ["" + this.options.baseClass + "-" + classes]; + } + cls = []; + for (_i = 0, _len = classes.length; _i < _len; _i++) { + c = classes[_i]; + cls.push("" + this.options.baseClass + "-" + c); + } + return cls; + }; + + return BootstrapSwitch; + + })(); + $.fn.bootstrapSwitch = function() { + var args, option, ret; + option = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; + ret = this; + this.each(function() { + var $this, data; + $this = $(this); + data = $this.data("bootstrap-switch"); + if (!data) { + $this.data("bootstrap-switch", data = new BootstrapSwitch(this, option)); + } + if (typeof option === "string") { + return ret = data[option].apply(data, args); + } + }); + return ret; + }; + $.fn.bootstrapSwitch.Constructor = BootstrapSwitch; + return $.fn.bootstrapSwitch.defaults = { + state: true, + size: null, + animate: true, + disabled: false, + readonly: false, + indeterminate: false, + onColor: "primary", + offColor: "default", + onText: "ON", + offText: "OFF", + labelText: " ", + baseClass: "bootstrap-switch", + wrapperClass: "wrapper", + radioAllOff: false, + onInit: function() {}, + onSwitchChange: function() {} + }; + })(window.jQuery, window); + +}).call(this); + +(function ($) { + "use strict"; + + var defaultOptions = { + tagClass: function(item) { + return 'label label-info'; + }, + itemValue: function(item) { + return item ? item.toString() : item; + }, + itemText: function(item) { + return this.itemValue(item); + }, + freeInput: true, + addOnBlur: true, + maxTags: undefined, + maxChars: undefined, + confirmKeys: [13, 44], + onTagExists: function(item, $tag) { + $tag.hide().fadeIn(); + }, + trimValue: false, + allowDuplicates: false + }; + + /** + * Constructor function + */ + function TagsInput(element, options) { + this.itemsArray = []; + + this.$element = $(element); + this.$element.hide(); + + this.isSelect = (element.tagName === 'SELECT'); + this.multiple = (this.isSelect && element.hasAttribute('multiple')); + this.objectItems = options && options.itemValue; + this.placeholderText = element.hasAttribute('placeholder') ? this.$element.attr('placeholder') : ''; + this.inputSize = Math.max(1, this.placeholderText.length); + + this.$container = $('<div class="bootstrap-tagsinput"></div>'); + this.$input = $('<input type="text" placeholder="' + this.placeholderText + '"/>').appendTo(this.$container); + + this.$element.after(this.$container); + + var inputWidth = (this.inputSize < 3 ? 3 : this.inputSize) + "em"; + this.$input.get(0).style.cssText = "width: " + inputWidth + " !important;"; + this.build(options); + } + + TagsInput.prototype = { + constructor: TagsInput, + + /** + * Adds the given item as a new tag. Pass true to dontPushVal to prevent + * updating the elements val() + */ + add: function(item, dontPushVal) { + var self = this; + + if (self.options.maxTags && self.itemsArray.length >= self.options.maxTags) + return; + + // Ignore falsey values, except false + if (item !== false && !item) + return; + + // Trim value + if (typeof item === "string" && self.options.trimValue) { + item = $.trim(item); + } + + // Throw an error when trying to add an object while the itemValue option was not set + if (typeof item === "object" && !self.objectItems) + throw("Can't add objects when itemValue option is not set"); + + // Ignore strings only containg whitespace + if (item.toString().match(/^\s*$/)) + return; + + // If SELECT but not multiple, remove current tag + if (self.isSelect && !self.multiple && self.itemsArray.length > 0) + self.remove(self.itemsArray[0]); + + if (typeof item === "string" && this.$element[0].tagName === 'INPUT') { + var items = item.split(','); + if (items.length > 1) { + for (var i = 0; i < items.length; i++) { + this.add(items[i], true); + } + + if (!dontPushVal) + self.pushVal(); + return; + } + } + + var itemValue = self.options.itemValue(item), + itemText = self.options.itemText(item), + tagClass = self.options.tagClass(item); + + // Ignore items allready added + var existing = $.grep(self.itemsArray, function(item) { return self.options.itemValue(item) === itemValue; } )[0]; + if (existing && !self.options.allowDuplicates) { + // Invoke onTagExists + if (self.options.onTagExists) { + var $existingTag = $(".tag", self.$container).filter(function() { return $(this).data("item") === existing; }); + self.options.onTagExists(item, $existingTag); + } + return; + } + + // if length greater than limit + if (self.items().toString().length + item.length + 1 > self.options.maxInputLength) + return; + + // raise beforeItemAdd arg + var beforeItemAddEvent = $.Event('beforeItemAdd', { item: item, cancel: false }); + self.$element.trigger(beforeItemAddEvent); + if (beforeItemAddEvent.cancel) + return; + + // register item in internal array and map + self.itemsArray.push(item); + + // add a tag element + var $tag = $('<span class="tag ' + htmlEncode(tagClass) + '">' + htmlEncode(itemText) + '<span data-role="remove"></span></span>'); + $tag.data('item', item); + self.findInputWrapper().before($tag); + $tag.after(' '); + + // add <option /> if item represents a value not present in one of the <select />'s options + if (self.isSelect && !$('option[value="' + encodeURIComponent(itemValue) + '"]',self.$element)[0]) { + var $option = $('<option selected>' + htmlEncode(itemText) + '</option>'); + $option.data('item', item); + $option.attr('value', itemValue); + self.$element.append($option); + } + + if (!dontPushVal) + self.pushVal(); + + // Add class when reached maxTags + if (self.options.maxTags === self.itemsArray.length || self.items().toString().length === self.options.maxInputLength) + self.$container.addClass('bootstrap-tagsinput-max'); + + self.$element.trigger($.Event('itemAdded', { item: item })); + }, + + /** + * Removes the given item. Pass true to dontPushVal to prevent updating the + * elements val() + */ + remove: function(item, dontPushVal) { + var self = this; + + if (self.objectItems) { + if (typeof item === "object") + item = $.grep(self.itemsArray, function(other) { return self.options.itemValue(other) == self.options.itemValue(item); } ); + else + item = $.grep(self.itemsArray, function(other) { return self.options.itemValue(other) == item; } ); + + item = item[item.length-1]; + } + + if (item) { + var beforeItemRemoveEvent = $.Event('beforeItemRemove', { item: item, cancel: false }); + self.$element.trigger(beforeItemRemoveEvent); + if (beforeItemRemoveEvent.cancel) + return; + + $('.tag', self.$container).filter(function() { return $(this).data('item') === item; }).remove(); + $('option', self.$element).filter(function() { return $(this).data('item') === item; }).remove(); + if($.inArray(item, self.itemsArray) !== -1) + self.itemsArray.splice($.inArray(item, self.itemsArray), 1); + } + + if (!dontPushVal) + self.pushVal(); + + // Remove class when reached maxTags + if (self.options.maxTags > self.itemsArray.length) + self.$container.removeClass('bootstrap-tagsinput-max'); + + self.$element.trigger($.Event('itemRemoved', { item: item })); + }, + + /** + * Removes all items + */ + removeAll: function() { + var self = this; + + $('.tag', self.$container).remove(); + $('option', self.$element).remove(); + + while(self.itemsArray.length > 0) + self.itemsArray.pop(); + + self.pushVal(); + }, + + /** + * Refreshes the tags so they match the text/value of their corresponding + * item. + */ + refresh: function() { + var self = this; + $('.tag', self.$container).each(function() { + var $tag = $(this), + item = $tag.data('item'), + itemValue = self.options.itemValue(item), + itemText = self.options.itemText(item), + tagClass = self.options.tagClass(item); + + // Update tag's class and inner text + $tag.attr('class', null); + $tag.addClass('tag ' + htmlEncode(tagClass)); + $tag.contents().filter(function() { + return this.nodeType == 3; + })[0].nodeValue = htmlEncode(itemText); + + if (self.isSelect) { + var option = $('option', self.$element).filter(function() { return $(this).data('item') === item; }); + option.attr('value', itemValue); + } + }); + }, + + /** + * Returns the items added as tags + */ + items: function() { + return this.itemsArray; + }, + + /** + * Assembly value by retrieving the value of each item, and set it on the + * element. + */ + pushVal: function() { + var self = this, + val = $.map(self.items(), function(item) { + return self.options.itemValue(item).toString(); + }); + + self.$element.val(val, true).trigger('change'); + }, + + /** + * Initializes the tags input behaviour on the element + */ + build: function(options) { + var self = this; + + self.options = $.extend({}, defaultOptions, options); + // When itemValue is set, freeInput should always be false + if (self.objectItems) + self.options.freeInput = false; + + makeOptionItemFunction(self.options, 'itemValue'); + makeOptionItemFunction(self.options, 'itemText'); + makeOptionFunction(self.options, 'tagClass'); + + // Typeahead Bootstrap version 2.3.2 + if (self.options.typeahead) { + var typeahead = self.options.typeahead || {}; + + makeOptionFunction(typeahead, 'source'); + + self.$input.typeahead($.extend({}, typeahead, { + source: function (query, process) { + function processItems(items) { + var texts = []; + + for (var i = 0; i < items.length; i++) { + var text = self.options.itemText(items[i]); + map[text] = items[i]; + texts.push(text); + } + process(texts); + } + + this.map = {}; + var map = this.map, + data = typeahead.source(query); + + if ($.isFunction(data.success)) { + // support for Angular callbacks + data.success(processItems); + } else if ($.isFunction(data.then)) { + // support for Angular promises + data.then(processItems); + } else { + // support for functions and jquery promises + $.when(data) + .then(processItems); + } + }, + updater: function (text) { + self.add(this.map[text]); + }, + matcher: function (text) { + return (text.toLowerCase().indexOf(this.query.trim().toLowerCase()) !== -1); + }, + sorter: function (texts) { + return texts.sort(); + }, + highlighter: function (text) { + var regex = new RegExp( '(' + this.query + ')', 'gi' ); + return text.replace( regex, "<strong>$1</strong>" ); + } + })); + } + + // typeahead.js + if (self.options.typeaheadjs) { + var typeaheadjs = self.options.typeaheadjs || {}; + + self.$input.typeahead(null, typeaheadjs).on('typeahead:selected', $.proxy(function (obj, datum) { + if (typeaheadjs.valueKey) + self.add(datum[typeaheadjs.valueKey]); + else + self.add(datum); + self.$input.typeahead('val', ''); + }, self)); + } + + self.$container.on('click', $.proxy(function(event) { + if (! self.$element.attr('disabled')) { + self.$input.removeAttr('disabled'); + } + self.$input.focus(); + }, self)); + + if (self.options.addOnBlur && self.options.freeInput) { + self.$input.on('focusout', $.proxy(function(event) { + // HACK: only process on focusout when no typeahead opened, to + // avoid adding the typeahead text as tag + if ($('.typeahead, .twitter-typeahead', self.$container).length === 0) { + self.add(self.$input.val()); + self.$input.val(''); + } + }, self)); + } + + + self.$container.on('keydown', 'input', $.proxy(function(event) { + var $input = $(event.target), + $inputWrapper = self.findInputWrapper(); + + if (self.$element.attr('disabled')) { + self.$input.attr('disabled', 'disabled'); + return; + } + + switch (event.which) { + // BACKSPACE + case 8: + if (doGetCaretPosition($input[0]) === 0) { + var prev = $inputWrapper.prev(); + if (prev) { + self.remove(prev.data('item')); + } + } + break; + + // DELETE + case 46: + if (doGetCaretPosition($input[0]) === 0) { + var next = $inputWrapper.next(); + if (next) { + self.remove(next.data('item')); + } + } + break; + + // LEFT ARROW + case 37: + // Try to move the input before the previous tag + var $prevTag = $inputWrapper.prev(); + if ($input.val().length === 0 && $prevTag[0]) { + $prevTag.before($inputWrapper); + $input.focus(); + } + break; + // RIGHT ARROW + case 39: + // Try to move the input after the next tag + var $nextTag = $inputWrapper.next(); + if ($input.val().length === 0 && $nextTag[0]) { + $nextTag.after($inputWrapper); + $input.focus(); + } + break; + default: + // ignore + } + + // Reset internal input's size + var textLength = $input.val().length, + wordSpace = Math.ceil(textLength / 5), + size = textLength + wordSpace + 1; + $input.attr('size', Math.max(this.inputSize, $input.val().length)); + }, self)); + + self.$container.on('keypress', 'input', $.proxy(function(event) { + var $input = $(event.target); + + if (self.$element.attr('disabled')) { + self.$input.attr('disabled', 'disabled'); + return; + } + + var text = $input.val(), + maxLengthReached = self.options.maxChars && text.length >= self.options.maxChars; + if (self.options.freeInput && (keyCombinationInList(event, self.options.confirmKeys) || maxLengthReached)) { + self.add(maxLengthReached ? text.substr(0, self.options.maxChars) : text); + $input.val(''); + event.preventDefault(); + } + + // Reset internal input's size + var textLength = $input.val().length, + wordSpace = Math.ceil(textLength / 5), + size = textLength + wordSpace + 1; + $input.attr('size', Math.max(this.inputSize, $input.val().length)); + }, self)); + + // Remove icon clicked + self.$container.on('click', '[data-role=remove]', $.proxy(function(event) { + if (self.$element.attr('disabled')) { + return; + } + self.remove($(event.target).closest('.tag').data('item')); + }, self)); + + // Only add existing value as tags when using strings as tags + if (self.options.itemValue === defaultOptions.itemValue) { + if (self.$element[0].tagName === 'INPUT') { + self.add(self.$element.val()); + } else { + $('option', self.$element).each(function() { + self.add($(this).attr('value'), true); + }); + } + } + }, + + /** + * Removes all tagsinput behaviour and unregsiter all event handlers + */ + destroy: function() { + var self = this; + + // Unbind events + self.$container.off('keypress', 'input'); + self.$container.off('click', '[role=remove]'); + + self.$container.remove(); + self.$element.removeData('tagsinput'); + self.$element.show(); + }, + + /** + * Sets focus on the tagsinput + */ + focus: function() { + this.$input.focus(); + }, + + /** + * Returns the internal input element + */ + input: function() { + return this.$input; + }, + + /** + * Returns the element which is wrapped around the internal input. This + * is normally the $container, but typeahead.js moves the $input element. + */ + findInputWrapper: function() { + var elt = this.$input[0], + container = this.$container[0]; + while(elt && elt.parentNode !== container) + elt = elt.parentNode; + + return $(elt); + } + }; + + /** + * Register JQuery plugin + */ + $.fn.tagsinput = function(arg1, arg2) { + var results = []; + + this.each(function() { + var tagsinput = $(this).data('tagsinput'); + // Initialize a new tags input + if (!tagsinput) { + tagsinput = new TagsInput(this, arg1); + $(this).data('tagsinput', tagsinput); + results.push(tagsinput); + + if (this.tagName === 'SELECT') { + $('option', $(this)).attr('selected', 'selected'); + } + + // Init tags from $(this).val() + $(this).val($(this).val()); + } else if (!arg1 && !arg2) { + // tagsinput already exists + // no function, trying to init + results.push(tagsinput); + } else if(tagsinput[arg1] !== undefined) { + // Invoke function on existing tags input + var retVal = tagsinput[arg1](arg2); + if (retVal !== undefined) + results.push(retVal); + } + }); + + if ( typeof arg1 == 'string') { + // Return the results from the invoked function calls + return results.length > 1 ? results : results[0]; + } else { + return results; + } + }; + + $.fn.tagsinput.Constructor = TagsInput; + + /** + * Most options support both a string or number as well as a function as + * option value. This function makes sure that the option with the given + * key in the given options is wrapped in a function + */ + function makeOptionItemFunction(options, key) { + if (typeof options[key] !== 'function') { + var propertyName = options[key]; + options[key] = function(item) { return item[propertyName]; }; + } + } + function makeOptionFunction(options, key) { + if (typeof options[key] !== 'function') { + var value = options[key]; + options[key] = function() { return value; }; + } + } + /** + * HtmlEncodes the given value + */ + var htmlEncodeContainer = $('<div />'); + function htmlEncode(value) { + if (value) { + return htmlEncodeContainer.text(value).html(); + } else { + return ''; + } + } + + /** + * Returns the position of the caret in the given input field + * http://flightschool.acylt.com/devnotes/caret-position-woes/ + */ + function doGetCaretPosition(oField) { + var iCaretPos = 0; + if (document.selection) { + oField.focus (); + var oSel = document.selection.createRange(); + oSel.moveStart ('character', -oField.value.length); + iCaretPos = oSel.text.length; + } else if (oField.selectionStart || oField.selectionStart == '0') { + iCaretPos = oField.selectionStart; + } + return (iCaretPos); + } + + /** + * Returns boolean indicates whether user has pressed an expected key combination. + * @param object keyPressEvent: JavaScript event object, refer + * http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html + * @param object lookupList: expected key combinations, as in: + * [13, {which: 188, shiftKey: true}] + */ + function keyCombinationInList(keyPressEvent, lookupList) { + var found = false; + $.each(lookupList, function (index, keyCombination) { + if (typeof (keyCombination) === 'number' && keyPressEvent.which === keyCombination) { + found = true; + return false; + } + + if (keyPressEvent.which === keyCombination.which) { + var alt = !keyCombination.hasOwnProperty('altKey') || keyPressEvent.altKey === keyCombination.altKey, + shift = !keyCombination.hasOwnProperty('shiftKey') || keyPressEvent.shiftKey === keyCombination.shiftKey, + ctrl = !keyCombination.hasOwnProperty('ctrlKey') || keyPressEvent.ctrlKey === keyCombination.ctrlKey; + if (alt && shift && ctrl) { + found = true; + return false; + } + } + }); + + return found; + } + + /** + * Initialize tagsinput behaviour on inputs and selects which have + * data-role=tagsinput + */ + $(function() { + $("input[data-role=tagsinput], select[multiple][data-role=tagsinput]").tagsinput(); + }); +})(window.jQuery); + +/*! + +Holder - client side image placeholders +Version 2.4.0+bxlim +© 2014 Ivan Malopinsky - http://imsky.co + +Site: http://imsky.github.io/holder +Issues: https://github.com/imsky/holder/issues +License: http://opensource.org/licenses/MIT + +*/ +!function(e,t,r){t[e]=r}("onDomReady",this,function(e){"use strict";function t(e){if(!b){if(!a.body)return i(t);for(b=!0;e=S.shift();)i(e)}}function r(e){(y||e.type===s||a[c]===u)&&(n(),t())}function n(){y?(a[x](m,r,d),e[x](s,r,d)):(a[g](v,r),e[g](h,r))}function i(e,t){setTimeout(e,+t>=0?t:1)}function o(e){b?i(e):S.push(e)}null==document.readyState&&document.addEventListener&&(document.addEventListener("DOMContentLoaded",function E(){document.removeEventListener("DOMContentLoaded",E,!1),document.readyState="complete"},!1),document.readyState="loading");var a=e.document,l=a.documentElement,s="load",d=!1,h="on"+s,u="complete",c="readyState",f="attachEvent",g="detachEvent",p="addEventListener",m="DOMContentLoaded",v="onreadystatechange",x="removeEventListener",y=p in a,w=d,b=d,S=[];if(a[c]===u)i(t);else if(y)a[p](m,r,d),e[p](s,r,d);else{a[f](v,r),e[f](h,r);try{w=null==e.frameElement&&l}catch(C){}w&&w.doScroll&&!function k(){if(!b){try{w.doScroll("left")}catch(e){return i(k,50)}n(),t()}}()}return o.version="1.4.0",o.isReady=function(){return b},o}(this)),document.querySelectorAll||(document.querySelectorAll=function(e){var t,r=document.createElement("style"),n=[];for(document.documentElement.firstChild.appendChild(r),document._qsa=[],r.styleSheet.cssText=e+"{x-qsa:expression(document._qsa && document._qsa.push(this))}",window.scrollBy(0,0),r.parentNode.removeChild(r);document._qsa.length;)t=document._qsa.shift(),t.style.removeAttribute("x-qsa"),n.push(t);return document._qsa=null,n}),document.querySelector||(document.querySelector=function(e){var t=document.querySelectorAll(e);return t.length?t[0]:null}),document.getElementsByClassName||(document.getElementsByClassName=function(e){return e=String(e).replace(/^|\s+/g,"."),document.querySelectorAll(e)}),Object.keys||(Object.keys=function(e){if(e!==Object(e))throw TypeError("Object.keys called on non-object");var t,r=[];for(t in e)Object.prototype.hasOwnProperty.call(e,t)&&r.push(t);return r}),function(e){var t="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";e.atob=e.atob||function(e){e=String(e);var r,n=0,i=[],o=0,a=0;if(e=e.replace(/\s/g,""),e.length%4===0&&(e=e.replace(/=+$/,"")),e.length%4===1)throw Error("InvalidCharacterError");if(/[^+/0-9A-Za-z]/.test(e))throw Error("InvalidCharacterError");for(;n<e.length;)r=t.indexOf(e.charAt(n)),o=o<<6|r,a+=6,24===a&&(i.push(String.fromCharCode(o>>16&255)),i.push(String.fromCharCode(o>>8&255)),i.push(String.fromCharCode(255&o)),a=0,o=0),n+=1;return 12===a?(o>>=4,i.push(String.fromCharCode(255&o))):18===a&&(o>>=2,i.push(String.fromCharCode(o>>8&255)),i.push(String.fromCharCode(255&o))),i.join("")},e.btoa=e.btoa||function(e){e=String(e);var r,n,i,o,a,l,s,d=0,h=[];if(/[^\x00-\xFF]/.test(e))throw Error("InvalidCharacterError");for(;d<e.length;)r=e.charCodeAt(d++),n=e.charCodeAt(d++),i=e.charCodeAt(d++),o=r>>2,a=(3&r)<<4|n>>4,l=(15&n)<<2|i>>6,s=63&i,d===e.length+2?(l=64,s=64):d===e.length+1&&(s=64),h.push(t.charAt(o),t.charAt(a),t.charAt(l),t.charAt(s));return h.join("")}}(this),function(){function e(t,r,n){t.document;var i,o=t.currentStyle[r].match(/([\d\.]+)(%|cm|em|in|mm|pc|pt|)/)||[0,0,""],a=o[1],l=o[2];return n=n?/%|em/.test(l)&&t.parentElement?e(t.parentElement,"fontSize",null):16:n,i="fontSize"==r?n:/width/i.test(r)?t.clientWidth:t.clientHeight,"%"==l?a/100*i:"cm"==l?.3937*a*96:"em"==l?a*n:"in"==l?96*a:"mm"==l?.3937*a*96/10:"pc"==l?12*a*96/72:"pt"==l?96*a/72:a}function t(e,t){var r="border"==t?"Width":"",n=t+"Top"+r,i=t+"Right"+r,o=t+"Bottom"+r,a=t+"Left"+r;e[t]=(e[n]==e[i]&&e[n]==e[o]&&e[n]==e[a]?[e[n]]:e[n]==e[o]&&e[a]==e[i]?[e[n],e[i]]:e[a]==e[i]?[e[n],e[i],e[o]]:[e[n],e[i],e[o],e[a]]).join(" ")}function r(r){var n,i=this,o=r.currentStyle,a=e(r,"fontSize"),l=function(e){return"-"+e.toLowerCase()};for(n in o)if(Array.prototype.push.call(i,"styleFloat"==n?"float":n.replace(/[A-Z]/,l)),"width"==n)i[n]=r.offsetWidth+"px";else if("height"==n)i[n]=r.offsetHeight+"px";else if("styleFloat"==n)i.float=o[n];else if(/margin.|padding.|border.+W/.test(n)&&"auto"!=i[n])i[n]=Math.round(e(r,n,a))+"px";else if(/^outline/.test(n))try{i[n]=o[n]}catch(s){i.outlineColor=o.color,i.outlineStyle=i.outlineStyle||"none",i.outlineWidth=i.outlineWidth||"0px",i.outline=[i.outlineColor,i.outlineWidth,i.outlineStyle].join(" ")}else i[n]=o[n];t(i,"margin"),t(i,"padding"),t(i,"border"),i.fontSize=Math.round(a)+"px"}window.getComputedStyle||(r.prototype={constructor:r,getPropertyPriority:function(){throw new Error("NotSupportedError: DOM Exception 9")},getPropertyValue:function(e){return this[e.replace(/-\w/g,function(e){return e[1].toUpperCase()})]},item:function(e){return this[e]},removeProperty:function(){throw new Error("NoModificationAllowedError: DOM Exception 7")},setProperty:function(){throw new Error("NoModificationAllowedError: DOM Exception 7")},getPropertyCSSValue:function(){throw new Error("NotSupportedError: DOM Exception 9")}},window.getComputedStyle=Window.prototype.getComputedStyle=function(e){return new r(e)})}(),Object.prototype.hasOwnProperty||(Object.prototype.hasOwnProperty=function(e){var t=this.__proto__||this.constructor.prototype;return e in this&&(!(e in t)||t[e]!==this[e])}),function(e,t){e.augment=t()}(this,function(){"use strict";var e=function(){},t=Array.prototype.slice,r=function(r,n){var i=e.prototype="function"==typeof r?r.prototype:r,o=new e,a=n.apply(o,t.call(arguments,2).concat(i));if("object"==typeof a)for(var l in a)o[l]=a[l];if(!o.hasOwnProperty("constructor"))return o;var s=o.constructor;return s.prototype=o,s};return r.defclass=function(e){var t=e.constructor;return t.prototype=e,t},r.extend=function(e,t){return r(e,function(e){return this.uber=e,t})},r}),function(e,t){function r(e,t,r,o){var a=n(r.substr(r.lastIndexOf(e.domain)),e);a&&i(null,o,a,t)}function n(e,t){for(var r={theme:p(A.settings.themes.gray,null),stylesheets:t.stylesheets,holderURL:[]},n=!1,i=String.fromCharCode(11),o=e.replace(/([^\\])\//g,"$1"+i).split(i),a=/%[0-9a-f]{2}/gi,l=o.length,s=0;l>s;s++){var d=o[s];if(d.match(a))try{d=decodeURIComponent(d)}catch(h){d=o[s]}var u=!1;if(A.flags.dimensions.match(d))n=!0,r.dimensions=A.flags.dimensions.output(d),u=!0;else if(A.flags.fluid.match(d))n=!0,r.dimensions=A.flags.fluid.output(d),r.fluid=!0,u=!0;else if(A.flags.textmode.match(d))r.textmode=A.flags.textmode.output(d),u=!0;else if(A.flags.colors.match(d)){var c=A.flags.colors.output(d);r.theme=p(r.theme,c),u=!0}else if(t.themes[d])t.themes.hasOwnProperty(d)&&(r.theme=p(t.themes[d],null)),u=!0;else if(A.flags.font.match(d))r.font=A.flags.font.output(d),u=!0;else if(A.flags.auto.match(d))r.auto=!0,u=!0;else if(A.flags.text.match(d))r.text=A.flags.text.output(d),u=!0;else if(A.flags.random.match(d)){null==A.vars.cache.themeKeys&&(A.vars.cache.themeKeys=Object.keys(t.themes));var f=A.vars.cache.themeKeys[0|Math.random()*A.vars.cache.themeKeys.length];r.theme=p(t.themes[f],null),u=!0}u&&r.holderURL.push(d)}return r.holderURL.unshift(t.domain),r.holderURL=r.holderURL.join("/"),n?r:!1}function i(e,t,r,n){var i=r.dimensions,a=r.theme,l=i.width+"x"+i.height;if(e=null==e?r.fluid?"fluid":"image":e,null!=r.text&&(a.text=r.text,"object"===t.nodeName.toLowerCase())){for(var d=a.text.split("\\n"),u=0;u<d.length;u++)d[u]=b(d[u]);a.text=d.join("\\n")}var f=r.holderURL,g=p(n,null);r.font&&(a.font=r.font,!g.noFontFallback&&"img"===t.nodeName.toLowerCase()&&A.setup.supportsCanvas&&"svg"===g.renderer&&(g=p(g,{renderer:"canvas"}))),r.font&&"canvas"==g.renderer&&(g.reRender=!0),"background"==e?null==t.getAttribute("data-background-src")&&c(t,{"data-background-src":f}):c(t,{"data-src":f}),r.theme=a,t.holderData={flags:r,renderSettings:g},("image"==e||"fluid"==e)&&c(t,{alt:a.text?(a.text.length>16?a.text.substring(0,16)+"…":a.text)+" ["+l+"]":l}),"image"==e?("html"!=g.renderer&&r.auto||(t.style.width=i.width+"px",t.style.height=i.height+"px"),"html"==g.renderer?t.style.backgroundColor=a.background:(o(e,{dimensions:i,theme:a,flags:r},t,g),r.textmode&&"exact"==r.textmode&&(A.vars.resizableImages.push(t),s(t)))):"background"==e&&"html"!=g.renderer?o(e,{dimensions:i,theme:a,flags:r},t,g):"fluid"==e&&("%"==i.height.slice(-1)?t.style.height=i.height:null!=r.auto&&r.auto||(t.style.height=i.height+"px"),"%"==i.width.slice(-1)?t.style.width=i.width:null!=r.auto&&r.auto||(t.style.width=i.width+"px"),("inline"==t.style.display||""===t.style.display||"none"==t.style.display)&&(t.style.display="block"),h(t),"html"==g.renderer?t.style.backgroundColor=a.background:(A.vars.resizableImages.push(t),s(t)))}function o(e,t,r,n){function i(){var e=null;switch(n.renderer){case"canvas":e=L(s);break;case"svg":e=O(s,n);break;default:throw"Holder: invalid renderer: "+n.renderer}return e}var o=null;switch(n.renderer){case"svg":if(!A.setup.supportsSVG)return;break;case"canvas":if(!A.setup.supportsCanvas)return;break;default:return}{var l={width:t.dimensions.width,height:t.dimensions.height,theme:t.theme,flags:t.flags},s=a(l);({text:l.text,width:l.width,height:l.height,textHeight:l.font.size,font:l.font.family,fontWeight:l.font.weight,template:l.theme})}if(o=i(),null==o)throw"Holder: couldn't render placeholder";"background"==e?(r.style.backgroundImage="url("+o+")",r.style.backgroundSize=l.width+"px "+l.height+"px"):("img"===r.nodeName.toLowerCase()?c(r,{src:o}):"object"===r.nodeName.toLowerCase()&&(c(r,{data:o}),c(r,{type:"image/svg+xml"})),n.reRender&&setTimeout(function(){var e=i();if(null==e)throw"Holder: couldn't render placeholder";"img"===r.nodeName.toLowerCase()?c(r,{src:e}):"object"===r.nodeName.toLowerCase()&&(c(r,{data:e}),c(r,{type:"image/svg+xml"}))},100)),c(r,{"data-holder-rendered":!0})}function a(e){function t(e,t,r,n){t.width=r,t.height=n,e.width=Math.max(e.width,t.width),e.height+=t.height,e.add(t)}switch(e.font={family:e.theme.font?e.theme.font:"Arial, Helvetica, Open Sans, sans-serif",size:l(e.width,e.height,e.theme.size?e.theme.size:12),weight:e.theme.fontweight?e.theme.fontweight:"bold"},e.text=e.theme.text?e.theme.text:Math.floor(e.width)+"x"+Math.floor(e.height),e.flags.textmode){case"literal":e.text=e.flags.dimensions.width+"x"+e.flags.dimensions.height;break;case"exact":if(!e.flags.exactDimensions)break;e.text=Math.floor(e.flags.exactDimensions.width)+"x"+Math.floor(e.flags.exactDimensions.height)}var r=new F({width:e.width,height:e.height}),n=r.Shape,i=new n.Rect("holderBg",{fill:e.theme.background});i.resize(e.width,e.height),r.root.add(i);var o=new n.Group("holderTextGroup",{text:e.text,align:"center",font:e.font,fill:e.theme.foreground});o.moveTo(null,null,1),r.root.add(o);var a=o.textPositionData=T(r);if(!a)throw"Holder: staging fallback not supported yet.";o.properties.leading=a.boundingBox.height;var s=null,d=null;if(a.lineCount>1){var h=0,u=0,c=e.width*A.setup.lineWrapRatio,f=0;d=new n.Group("line"+f);for(var g=0;g<a.words.length;g++){var p=a.words[g];s=new n.Text(p.text);var m="\\n"==p.text;(h+p.width>=c||m===!0)&&(t(o,d,h,o.properties.leading),h=0,u+=o.properties.leading,f+=1,d=new n.Group("line"+f),d.y=u),m!==!0&&(s.moveTo(h,0),h+=a.spaceWidth+p.width,d.add(s))}t(o,d,h,o.properties.leading);for(var v in o.children)d=o.children[v],d.moveTo((o.width-d.width)/2,null,null);o.moveTo((e.width-o.width)/2,(e.height-o.height)/2,null),(e.height-o.height)/2<0&&o.moveTo(null,0,null)}else s=new n.Text(e.text),d=new n.Group("line0"),d.add(s),o.add(d),o.moveTo((e.width-a.boundingBox.width)/2,(e.height-a.boundingBox.height)/2,null);return r}function l(e,t,r){t=parseInt(t,10),e=parseInt(e,10);var n=Math.max(t,e),i=Math.min(t,e),o=1/12,a=Math.min(.75*i,.75*n*o);return Math.round(Math.max(r,a))}function s(e){var t;t=null==e||null==e.nodeType?A.vars.resizableImages:[e];for(var r in t)if(t.hasOwnProperty(r)){var n=t[r];if(n.holderData){var i=n.holderData.flags,a=d(n,k.invisibleErrorFn(s));if(a){if(i.fluid&&i.auto){var l=n.holderData.fluidConfig;switch(l.mode){case"width":a.height=a.width/l.ratio;break;case"height":a.width=a.height*l.ratio}}var h={dimensions:a,theme:i.theme,flags:i};i.textmode&&"exact"==i.textmode&&(i.exactDimensions=a,h.dimensions=i.dimensions),o("image",h,n,n.holderData.renderSettings)}}}}function d(e,t){var r={height:e.clientHeight,width:e.clientWidth};return r.height||r.width?(e.removeAttribute("data-holder-invisible"),r):(c(e,{"data-holder-invisible":!0}),void t.call(this,e))}function h(e){if(e.holderData){var t=d(e,k.invisibleErrorFn(h));if(t){var r=e.holderData.flags,n={fluidHeight:"%"==r.dimensions.height.slice(-1),fluidWidth:"%"==r.dimensions.width.slice(-1),mode:null,initialDimensions:t};n.fluidWidth&&!n.fluidHeight?(n.mode="width",n.ratio=n.initialDimensions.width/parseFloat(r.dimensions.height)):!n.fluidWidth&&n.fluidHeight&&(n.mode="height",n.ratio=parseFloat(r.dimensions.width)/n.initialDimensions.height),e.holderData.fluidConfig=n}}}function u(e,t){return null==t?E.createElement(e):E.createElementNS(t,e)}function c(e,t){for(var r in t)e.setAttribute(r,t[r])}function f(e,t,r){if(null==e){e=u("svg",C);var n=u("defs",C);e.appendChild(n)}return e.webkitMatchesSelector&&e.setAttribute("xmlns",C),c(e,{width:t,height:r,viewBox:"0 0 "+t+" "+r,preserveAspectRatio:"none"}),e}function g(e,r){if(t.XMLSerializer){{var n=new XMLSerializer,i="",o=r.stylesheets;e.querySelector("defs")}if(r.svgXMLStylesheet){for(var a=(new DOMParser).parseFromString("<xml />","application/xml"),l=o.length-1;l>=0;l--){var s=a.createProcessingInstruction("xml-stylesheet",'href="'+o[l]+'" rel="stylesheet"');a.insertBefore(s,a.firstChild)}var d=a.createProcessingInstruction("xml",'version="1.0" encoding="UTF-8" standalone="yes"');a.insertBefore(d,a.firstChild),a.removeChild(a.documentElement),i=n.serializeToString(a)}var h=n.serializeToString(e);return h=h.replace(/\&(\#[0-9]{2,}\;)/g,"&$1"),i+h}}function p(e,t){var r={};for(var n in e)e.hasOwnProperty(n)&&(r[n]=e[n]);if(null!=t)for(var i in t)t.hasOwnProperty(i)&&(r[i]=t[i]);return r}function m(e){var t=[];for(var r in e)e.hasOwnProperty(r)&&t.push(r+":"+e[r]);return t.join(";")}function v(e){A.vars.debounceTimer||e.call(this),A.vars.debounceTimer&&clearTimeout(A.vars.debounceTimer),A.vars.debounceTimer=setTimeout(function(){A.vars.debounceTimer=null,e.call(this)},A.setup.debounce)}function x(){v(function(){s(null)})}function y(e){var r=null;return"string"==typeof e?r=E.querySelectorAll(e):t.NodeList&&e instanceof t.NodeList?r=e:t.Node&&e instanceof t.Node?r=[e]:t.HTMLCollection&&e instanceof t.HTMLCollection?r=e:null===e&&(r=[]),r}function w(e,t){var r=new Image;r.onerror=function(){t.call(this,!1,e)},r.onload=function(){t.call(this,!0,e)},r.src=e.src}function b(e){for(var t=[],r=0,n=e.length-1;n>=0;n--)r=e[n].charCodeAt(),t.unshift(r>128?["&#",r,";"].join(""):e[n]);return t.join("")}function S(e){return e.replace(/&#(\d+);/g,function(e,t){return String.fromCharCode(t)})}var C="http://www.w3.org/2000/svg",E=t.document,k={addTheme:function(e,t){return null!=e&&null!=t&&(A.settings.themes[e]=t),delete A.vars.cache.themeKeys,this},addImage:function(e,t){var r=E.querySelectorAll(t);if(r.length)for(var n=0,i=r.length;i>n;n++){var o=u("img");c(o,{"data-src":e}),r[n].appendChild(o)}return this},run:function(e){e=e||{};var o={};A.vars.preempted=!0;var a=p(A.settings,e);o.renderer=a.renderer?a.renderer:A.setup.renderer,-1===A.setup.renderers.join(",").indexOf(o.renderer)&&(o.renderer=A.setup.supportsSVG?"svg":A.setup.supportsCanvas?"canvas":"html"),a.use_canvas?o.renderer="canvas":a.use_svg&&(o.renderer="svg");var l=y(a.images),s=y(a.bgnodes),d=y(a.stylenodes),h=y(a.objects);o.stylesheets=[],o.svgXMLStylesheet=!0,o.noFontFallback=a.noFontFallback?a.noFontFallback:!1;for(var c=0;c<d.length;c++){var f=d[c];if(f.attributes.rel&&f.attributes.href&&"stylesheet"==f.attributes.rel.value){var g=f.attributes.href.value,m=u("a");m.href=g;var v=m.protocol+"//"+m.host+m.pathname+m.search;o.stylesheets.push(v)}}for(c=0;c<s.length;c++){var x=t.getComputedStyle(s[c],null).getPropertyValue("background-image"),b=s[c].getAttribute("data-background-src"),S=null;S=null==b?x:b;var C=null,E="?"+a.domain+"/";if(0===S.indexOf(E))C=S.slice(1);else if(-1!=S.indexOf(E)){var k=S.substr(S.indexOf(E)).slice(1),T=k.match(/([^\"]*)"?\)/);null!=T&&(C=T[1])}if(null!=C){var L=n(C,a);L&&i("background",s[c],L,o)}}for(c=0;c<h.length;c++){var O=h[c],F={};try{F.data=O.getAttribute("data"),F.dataSrc=O.getAttribute("data-src")}catch(z){}var M=null!=F.data&&0===F.data.indexOf(a.domain),D=null!=F.dataSrc&&0===F.dataSrc.indexOf(a.domain);M?r(a,o,F.data,O):D&&r(a,o,F.dataSrc,O)}for(c=0;c<l.length;c++){var R=l[c],j={};try{j.src=R.getAttribute("src"),j.dataSrc=R.getAttribute("data-src"),j.rendered=R.getAttribute("data-holder-rendered")}catch(z){}var B=null!=j.src,P=null!=j.dataSrc&&0===j.dataSrc.indexOf(a.domain),N=null!=j.rendered&&"true"==j.rendered;B?0===j.src.indexOf(a.domain)?r(a,o,j.src,R):P&&(N?r(a,o,j.dataSrc,R):w({src:j.src,options:a,renderSettings:o,dataSrc:j.dataSrc,image:R},function(e,t){e||r(t.options,t.renderSettings,t.dataSrc,t.image)})):P&&r(a,o,j.dataSrc,R)}return this},invisibleErrorFn:function(){return function(e){if(e.hasAttribute("data-holder-invisible"))throw"Holder: invisible placeholder"}}};k.add_theme=k.addTheme,k.add_image=k.addImage,k.invisible_error_fn=k.invisibleErrorFn;var A={settings:{domain:"holder.js",images:"img",objects:"object",bgnodes:"body .holderjs",stylenodes:"head link.holderjs",stylesheets:[],themes:{gray:{background:"#EEEEEE",foreground:"#AAAAAA"},social:{background:"#3a5a97",foreground:"#FFFFFF"},industrial:{background:"#434A52",foreground:"#C2F200"},sky:{background:"#0D8FDB",foreground:"#FFFFFF"},vine:{background:"#39DBAC",foreground:"#1E292C"},lava:{background:"#F8591A",foreground:"#1C2846",size:12}}},flags:{dimensions:{regex:/^(\d+)x(\d+)$/,output:function(e){var t=this.regex.exec(e);return{width:+t[1],height:+t[2]}}},fluid:{regex:/^([0-9]+%?)x([0-9]+%?)$/,output:function(e){var t=this.regex.exec(e);return{width:t[1],height:t[2]}}},colors:{regex:/(?:#|\^)([0-9a-f]{3,})\:(?:#|\^)([0-9a-f]{3,})/i,output:function(e){var t=this.regex.exec(e);return{foreground:"#"+t[2],background:"#"+t[1]}}},text:{regex:/text\:(.*)/,output:function(e){return this.regex.exec(e)[1].replace("\\/","/")}},font:{regex:/font\:(.*)/,output:function(e){return this.regex.exec(e)[1]}},auto:{regex:/^auto$/},textmode:{regex:/textmode\:(.*)/,output:function(e){return this.regex.exec(e)[1]}},random:{regex:/^random$/}}},T=function(){var e=null,t=null,r=null;return function(n){var i=n.root;if(A.setup.supportsSVG){var o=!1,a=function(e){return E.createTextNode(e)};null==e&&(o=!0),e=f(e,i.properties.width,i.properties.height),o&&(t=u("text",C),r=a(null),c(t,{x:0}),t.appendChild(r),e.appendChild(t),E.body.appendChild(e),e.style.visibility="hidden",e.style.position="absolute",e.style.top="-100%",e.style.left="-100%");var l=i.children.holderTextGroup,s=l.properties;c(t,{y:s.font.size,style:m({"font-weight":s.font.weight,"font-size":s.font.size+"px","font-family":s.font.family,"dominant-baseline":"middle"})}),r.nodeValue=s.text;var d=t.getBBox(),h=Math.ceil(d.width/(i.properties.width*A.setup.lineWrapRatio)),g=s.text.split(" "),p=s.text.match(/\\n/g);h+=null==p?0:p.length,r.nodeValue=s.text.replace(/[ ]+/g,"");var v=t.getComputedTextLength(),x=d.width-v,y=Math.round(x/Math.max(1,g.length-1)),w=[];if(h>1){r.nodeValue="";for(var b=0;b<g.length;b++)if(0!==g[b].length){r.nodeValue=S(g[b]);var k=t.getBBox();w.push({text:g[b],width:k.width})}}return{spaceWidth:y,lineCount:h,boundingBox:d,words:w}}return!1}}(),L=function(){var e=u("canvas"),t=null;return function(r){null==t&&(t=e.getContext("2d"));var n=r.root;e.width=A.dpr(n.properties.width),e.height=A.dpr(n.properties.height),t.textBaseline="middle",t.fillStyle=n.children.holderBg.properties.fill,t.fillRect(0,0,A.dpr(n.children.holderBg.width),A.dpr(n.children.holderBg.height));{var i=n.children.holderTextGroup;i.properties}t.font=i.properties.font.weight+" "+A.dpr(i.properties.font.size)+"px "+i.properties.font.family+", monospace",t.fillStyle=i.properties.fill;for(var o in i.children){var a=i.children[o];for(var l in a.children){var s=a.children[l],d=A.dpr(i.x+a.x+s.x),h=A.dpr(i.y+a.y+s.y+i.properties.leading/2);t.fillText(s.properties.text,d,h)}}return e.toDataURL("image/png")}}(),O=function(){if(t.XMLSerializer){var e=f(null,0,0),r=u("rect",C);return e.appendChild(r),function(t,n){var i=t.root;f(e,i.properties.width,i.properties.height);for(var o=e.querySelectorAll("g"),a=0;a<o.length;a++)o[a].parentNode.removeChild(o[a]);c(r,{width:i.children.holderBg.width,height:i.children.holderBg.height,fill:i.children.holderBg.properties.fill});var l=i.children.holderTextGroup,s=l.properties,d=u("g",C);e.appendChild(d);for(var h in l.children){var p=l.children[h];for(var v in p.children){var x=p.children[v],y=l.x+p.x+x.x,w=l.y+p.y+x.y+l.properties.leading/2,b=u("text",C),S=E.createTextNode(null);c(b,{x:y,y:w,style:m({fill:s.fill,"font-weight":s.font.weight,"font-family":s.font.family+", monospace","font-size":s.font.size+"px","dominant-baseline":"central"})}),S.nodeValue=x.properties.text,b.appendChild(S),d.appendChild(b)}}var k="data:image/svg+xml;base64,"+btoa(unescape(encodeURIComponent(g(e,n))));return k}}}(),F=function(e){function t(e,t){for(var r in t)e[r]=t[r];return e}var r=1,n=augment.defclass({constructor:function(e){r++,this.parent=null,this.children={},this.id=r,this.name="n"+r,null!=e&&(this.name=e),this.x=0,this.y=0,this.z=0,this.width=0,this.height=0},resize:function(e,t){null!=e&&(this.width=e),null!=t&&(this.height=t)},moveTo:function(e,t,r){this.x=null!=e?e:this.x,this.y=null!=t?t:this.y,this.z=null!=r?r:this.z},add:function(e){var t=e.name;if(null!=this.children[t])throw"SceneGraph: child with that name already exists: "+t;this.children[t]=e,e.parent=this}}),i=augment(n,function(t){this.constructor=function(){t.constructor.call(this,"root"),this.properties=e}}),o=augment(n,function(e){function r(r,n){if(e.constructor.call(this,r),this.properties={fill:"#000"},null!=n)t(this.properties,n);else if(null!=r&&"string"!=typeof r)throw"SceneGraph: invalid node name"}this.Group=augment.extend(this,{constructor:r,type:"group"}),this.Rect=augment.extend(this,{constructor:r,type:"rect"}),this.Text=augment.extend(this,{constructor:function(e){r.call(this),this.properties.text=e},type:"text"})}),a=new i;return this.Shape=o,this.root=a,this};for(var z in A.flags)A.flags.hasOwnProperty(z)&&(A.flags[z].match=function(e){return e.match(this.regex)});A.setup={renderer:"html",debounce:100,ratio:1,supportsCanvas:!1,supportsSVG:!1,lineWrapRatio:.9,renderers:["html","canvas","svg"]},A.dpr=function(e){return e*A.setup.ratio},A.vars={preempted:!1,resizableImages:[],debounceTimer:null,cache:{}},function(){var e=1,r=1,n=u("canvas"),i=null;n.getContext&&-1!=n.toDataURL("image/png").indexOf("data:image/png")&&(A.setup.renderer="canvas",i=n.getContext("2d"),A.setup.supportsCanvas=!0),A.setup.supportsCanvas&&(e=t.devicePixelRatio||1,r=i.webkitBackingStorePixelRatio||i.mozBackingStorePixelRatio||i.msBackingStorePixelRatio||i.oBackingStorePixelRatio||i.backingStorePixelRatio||1),A.setup.ratio=e/r,E.createElementNS&&E.createElementNS(C,"svg").createSVGRect&&(A.setup.renderer="svg",A.setup.supportsSVG=!0)}(),e(k,"Holder",t),t.onDomReady&&t.onDomReady(function(){A.vars.preempted||k.run(),t.addEventListener?(t.addEventListener("resize",x,!1),t.addEventListener("orientationchange",x,!1)):t.attachEvent("onresize",x),"object"==typeof t.Turbolinks&&t.document.addEventListener("page:change",function(){k.run()})})}(function(e,t,r){var n="function"==typeof define&&define.amd;n?define(e):r[t]=e},this); +/*! + * typeahead.js 0.10.5 + * https://github.com/twitter/typeahead.js + * Copyright 2013-2014 Twitter, Inc. and other contributors; Licensed MIT + */ + +(function($) { + var _ = function() { + "use strict"; + return { + isMsie: function() { + return /(msie|trident)/i.test(navigator.userAgent) ? navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2] : false; + }, + isBlankString: function(str) { + return !str || /^\s*$/.test(str); + }, + escapeRegExChars: function(str) { + return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); + }, + isString: function(obj) { + return typeof obj === "string"; + }, + isNumber: function(obj) { + return typeof obj === "number"; + }, + isArray: $.isArray, + isFunction: $.isFunction, + isObject: $.isPlainObject, + isUndefined: function(obj) { + return typeof obj === "undefined"; + }, + toStr: function toStr(s) { + return _.isUndefined(s) || s === null ? "" : s + ""; + }, + bind: $.proxy, + each: function(collection, cb) { + $.each(collection, reverseArgs); + function reverseArgs(index, value) { + return cb(value, index); + } + }, + map: $.map, + filter: $.grep, + every: function(obj, test) { + var result = true; + if (!obj) { + return result; + } + $.each(obj, function(key, val) { + if (!(result = test.call(null, val, key, obj))) { + return false; + } + }); + return !!result; + }, + some: function(obj, test) { + var result = false; + if (!obj) { + return result; + } + $.each(obj, function(key, val) { + if (result = test.call(null, val, key, obj)) { + return false; + } + }); + return !!result; + }, + mixin: $.extend, + getUniqueId: function() { + var counter = 0; + return function() { + return counter++; + }; + }(), + templatify: function templatify(obj) { + return $.isFunction(obj) ? obj : template; + function template() { + return String(obj); + } + }, + defer: function(fn) { + setTimeout(fn, 0); + }, + debounce: function(func, wait, immediate) { + var timeout, result; + return function() { + var context = this, args = arguments, later, callNow; + later = function() { + timeout = null; + if (!immediate) { + result = func.apply(context, args); + } + }; + callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) { + result = func.apply(context, args); + } + return result; + }; + }, + throttle: function(func, wait) { + var context, args, timeout, result, previous, later; + previous = 0; + later = function() { + previous = new Date(); + timeout = null; + result = func.apply(context, args); + }; + return function() { + var now = new Date(), remaining = wait - (now - previous); + context = this; + args = arguments; + if (remaining <= 0) { + clearTimeout(timeout); + timeout = null; + previous = now; + result = func.apply(context, args); + } else if (!timeout) { + timeout = setTimeout(later, remaining); + } + return result; + }; + }, + noop: function() {} + }; + }(); + var VERSION = "0.10.5"; + var tokenizers = function() { + "use strict"; + return { + nonword: nonword, + whitespace: whitespace, + obj: { + nonword: getObjTokenizer(nonword), + whitespace: getObjTokenizer(whitespace) + } + }; + function whitespace(str) { + str = _.toStr(str); + return str ? str.split(/\s+/) : []; + } + function nonword(str) { + str = _.toStr(str); + return str ? str.split(/\W+/) : []; + } + function getObjTokenizer(tokenizer) { + return function setKey() { + var args = [].slice.call(arguments, 0); + return function tokenize(o) { + var tokens = []; + _.each(args, function(k) { + tokens = tokens.concat(tokenizer(_.toStr(o[k]))); + }); + return tokens; + }; + }; + } + }(); + var LruCache = function() { + "use strict"; + function LruCache(maxSize) { + this.maxSize = _.isNumber(maxSize) ? maxSize : 100; + this.reset(); + if (this.maxSize <= 0) { + this.set = this.get = $.noop; + } + } + _.mixin(LruCache.prototype, { + set: function set(key, val) { + var tailItem = this.list.tail, node; + if (this.size >= this.maxSize) { + this.list.remove(tailItem); + delete this.hash[tailItem.key]; + } + if (node = this.hash[key]) { + node.val = val; + this.list.moveToFront(node); + } else { + node = new Node(key, val); + this.list.add(node); + this.hash[key] = node; + this.size++; + } + }, + get: function get(key) { + var node = this.hash[key]; + if (node) { + this.list.moveToFront(node); + return node.val; + } + }, + reset: function reset() { + this.size = 0; + this.hash = {}; + this.list = new List(); + } + }); + function List() { + this.head = this.tail = null; + } + _.mixin(List.prototype, { + add: function add(node) { + if (this.head) { + node.next = this.head; + this.head.prev = node; + } + this.head = node; + this.tail = this.tail || node; + }, + remove: function remove(node) { + node.prev ? node.prev.next = node.next : this.head = node.next; + node.next ? node.next.prev = node.prev : this.tail = node.prev; + }, + moveToFront: function(node) { + this.remove(node); + this.add(node); + } + }); + function Node(key, val) { + this.key = key; + this.val = val; + this.prev = this.next = null; + } + return LruCache; + }(); + var PersistentStorage = function() { + "use strict"; + var ls, methods; + try { + ls = window.localStorage; + ls.setItem("~~~", "!"); + ls.removeItem("~~~"); + } catch (err) { + ls = null; + } + function PersistentStorage(namespace) { + this.prefix = [ "__", namespace, "__" ].join(""); + this.ttlKey = "__ttl__"; + this.keyMatcher = new RegExp("^" + _.escapeRegExChars(this.prefix)); + } + if (ls && window.JSON) { + methods = { + _prefix: function(key) { + return this.prefix + key; + }, + _ttlKey: function(key) { + return this._prefix(key) + this.ttlKey; + }, + get: function(key) { + if (this.isExpired(key)) { + this.remove(key); + } + return decode(ls.getItem(this._prefix(key))); + }, + set: function(key, val, ttl) { + if (_.isNumber(ttl)) { + ls.setItem(this._ttlKey(key), encode(now() + ttl)); + } else { + ls.removeItem(this._ttlKey(key)); + } + return ls.setItem(this._prefix(key), encode(val)); + }, + remove: function(key) { + ls.removeItem(this._ttlKey(key)); + ls.removeItem(this._prefix(key)); + return this; + }, + clear: function() { + var i, key, keys = [], len = ls.length; + for (i = 0; i < len; i++) { + if ((key = ls.key(i)).match(this.keyMatcher)) { + keys.push(key.replace(this.keyMatcher, "")); + } + } + for (i = keys.length; i--; ) { + this.remove(keys[i]); + } + return this; + }, + isExpired: function(key) { + var ttl = decode(ls.getItem(this._ttlKey(key))); + return _.isNumber(ttl) && now() > ttl ? true : false; + } + }; + } else { + methods = { + get: _.noop, + set: _.noop, + remove: _.noop, + clear: _.noop, + isExpired: _.noop + }; + } + _.mixin(PersistentStorage.prototype, methods); + return PersistentStorage; + function now() { + return new Date().getTime(); + } + function encode(val) { + return JSON.stringify(_.isUndefined(val) ? null : val); + } + function decode(val) { + return JSON.parse(val); + } + }(); + var Transport = function() { + "use strict"; + var pendingRequestsCount = 0, pendingRequests = {}, maxPendingRequests = 6, sharedCache = new LruCache(10); + function Transport(o) { + o = o || {}; + this.cancelled = false; + this.lastUrl = null; + this._send = o.transport ? callbackToDeferred(o.transport) : $.ajax; + this._get = o.rateLimiter ? o.rateLimiter(this._get) : this._get; + this._cache = o.cache === false ? new LruCache(0) : sharedCache; + } + Transport.setMaxPendingRequests = function setMaxPendingRequests(num) { + maxPendingRequests = num; + }; + Transport.resetCache = function resetCache() { + sharedCache.reset(); + }; + _.mixin(Transport.prototype, { + _get: function(url, o, cb) { + var that = this, jqXhr; + if (this.cancelled || url !== this.lastUrl) { + return; + } + if (jqXhr = pendingRequests[url]) { + jqXhr.done(done).fail(fail); + } else if (pendingRequestsCount < maxPendingRequests) { + pendingRequestsCount++; + pendingRequests[url] = this._send(url, o).done(done).fail(fail).always(always); + } else { + this.onDeckRequestArgs = [].slice.call(arguments, 0); + } + function done(resp) { + cb && cb(null, resp); + that._cache.set(url, resp); + } + function fail() { + cb && cb(true); + } + function always() { + pendingRequestsCount--; + delete pendingRequests[url]; + if (that.onDeckRequestArgs) { + that._get.apply(that, that.onDeckRequestArgs); + that.onDeckRequestArgs = null; + } + } + }, + get: function(url, o, cb) { + var resp; + if (_.isFunction(o)) { + cb = o; + o = {}; + } + this.cancelled = false; + this.lastUrl = url; + if (resp = this._cache.get(url)) { + _.defer(function() { + cb && cb(null, resp); + }); + } else { + this._get(url, o, cb); + } + return !!resp; + }, + cancel: function() { + this.cancelled = true; + } + }); + return Transport; + function callbackToDeferred(fn) { + return function customSendWrapper(url, o) { + var deferred = $.Deferred(); + fn(url, o, onSuccess, onError); + return deferred; + function onSuccess(resp) { + _.defer(function() { + deferred.resolve(resp); + }); + } + function onError(err) { + _.defer(function() { + deferred.reject(err); + }); + } + }; + } + }(); + var SearchIndex = function() { + "use strict"; + function SearchIndex(o) { + o = o || {}; + if (!o.datumTokenizer || !o.queryTokenizer) { + $.error("datumTokenizer and queryTokenizer are both required"); + } + this.datumTokenizer = o.datumTokenizer; + this.queryTokenizer = o.queryTokenizer; + this.reset(); + } + _.mixin(SearchIndex.prototype, { + bootstrap: function bootstrap(o) { + this.datums = o.datums; + this.trie = o.trie; + }, + add: function(data) { + var that = this; + data = _.isArray(data) ? data : [ data ]; + _.each(data, function(datum) { + var id, tokens; + id = that.datums.push(datum) - 1; + tokens = normalizeTokens(that.datumTokenizer(datum)); + _.each(tokens, function(token) { + var node, chars, ch; + node = that.trie; + chars = token.split(""); + while (ch = chars.shift()) { + node = node.children[ch] || (node.children[ch] = newNode()); + node.ids.push(id); + } + }); + }); + }, + get: function get(query) { + var that = this, tokens, matches; + tokens = normalizeTokens(this.queryTokenizer(query)); + _.each(tokens, function(token) { + var node, chars, ch, ids; + if (matches && matches.length === 0) { + return false; + } + node = that.trie; + chars = token.split(""); + while (node && (ch = chars.shift())) { + node = node.children[ch]; + } + if (node && chars.length === 0) { + ids = node.ids.slice(0); + matches = matches ? getIntersection(matches, ids) : ids; + } else { + matches = []; + return false; + } + }); + return matches ? _.map(unique(matches), function(id) { + return that.datums[id]; + }) : []; + }, + reset: function reset() { + this.datums = []; + this.trie = newNode(); + }, + serialize: function serialize() { + return { + datums: this.datums, + trie: this.trie + }; + } + }); + return SearchIndex; + function normalizeTokens(tokens) { + tokens = _.filter(tokens, function(token) { + return !!token; + }); + tokens = _.map(tokens, function(token) { + return token.toLowerCase(); + }); + return tokens; + } + function newNode() { + return { + ids: [], + children: {} + }; + } + function unique(array) { + var seen = {}, uniques = []; + for (var i = 0, len = array.length; i < len; i++) { + if (!seen[array[i]]) { + seen[array[i]] = true; + uniques.push(array[i]); + } + } + return uniques; + } + function getIntersection(arrayA, arrayB) { + var ai = 0, bi = 0, intersection = []; + arrayA = arrayA.sort(compare); + arrayB = arrayB.sort(compare); + var lenArrayA = arrayA.length, lenArrayB = arrayB.length; + while (ai < lenArrayA && bi < lenArrayB) { + if (arrayA[ai] < arrayB[bi]) { + ai++; + } else if (arrayA[ai] > arrayB[bi]) { + bi++; + } else { + intersection.push(arrayA[ai]); + ai++; + bi++; + } + } + return intersection; + function compare(a, b) { + return a - b; + } + } + }(); + var oParser = function() { + "use strict"; + return { + local: getLocal, + prefetch: getPrefetch, + remote: getRemote + }; + function getLocal(o) { + return o.local || null; + } + function getPrefetch(o) { + var prefetch, defaults; + defaults = { + url: null, + thumbprint: "", + ttl: 24 * 60 * 60 * 1e3, + filter: null, + ajax: {} + }; + if (prefetch = o.prefetch || null) { + prefetch = _.isString(prefetch) ? { + url: prefetch + } : prefetch; + prefetch = _.mixin(defaults, prefetch); + prefetch.thumbprint = VERSION + prefetch.thumbprint; + prefetch.ajax.type = prefetch.ajax.type || "GET"; + prefetch.ajax.dataType = prefetch.ajax.dataType || "json"; + !prefetch.url && $.error("prefetch requires url to be set"); + } + return prefetch; + } + function getRemote(o) { + var remote, defaults; + defaults = { + url: null, + cache: true, + wildcard: "%QUERY", + replace: null, + rateLimitBy: "debounce", + rateLimitWait: 300, + send: null, + filter: null, + ajax: {} + }; + if (remote = o.remote || null) { + remote = _.isString(remote) ? { + url: remote + } : remote; + remote = _.mixin(defaults, remote); + remote.rateLimiter = /^throttle$/i.test(remote.rateLimitBy) ? byThrottle(remote.rateLimitWait) : byDebounce(remote.rateLimitWait); + remote.ajax.type = remote.ajax.type || "GET"; + remote.ajax.dataType = remote.ajax.dataType || "json"; + delete remote.rateLimitBy; + delete remote.rateLimitWait; + !remote.url && $.error("remote requires url to be set"); + } + return remote; + function byDebounce(wait) { + return function(fn) { + return _.debounce(fn, wait); + }; + } + function byThrottle(wait) { + return function(fn) { + return _.throttle(fn, wait); + }; + } + } + }(); + (function(root) { + "use strict"; + var old, keys; + old = root.Bloodhound; + keys = { + data: "data", + protocol: "protocol", + thumbprint: "thumbprint" + }; + root.Bloodhound = Bloodhound; + function Bloodhound(o) { + if (!o || !o.local && !o.prefetch && !o.remote) { + $.error("one of local, prefetch, or remote is required"); + } + this.limit = o.limit || 5; + this.sorter = getSorter(o.sorter); + this.dupDetector = o.dupDetector || ignoreDuplicates; + this.local = oParser.local(o); + this.prefetch = oParser.prefetch(o); + this.remote = oParser.remote(o); + this.cacheKey = this.prefetch ? this.prefetch.cacheKey || this.prefetch.url : null; + this.index = new SearchIndex({ + datumTokenizer: o.datumTokenizer, + queryTokenizer: o.queryTokenizer + }); + this.storage = this.cacheKey ? new PersistentStorage(this.cacheKey) : null; + } + Bloodhound.noConflict = function noConflict() { + root.Bloodhound = old; + return Bloodhound; + }; + Bloodhound.tokenizers = tokenizers; + _.mixin(Bloodhound.prototype, { + _loadPrefetch: function loadPrefetch(o) { + var that = this, serialized, deferred; + if (serialized = this._readFromStorage(o.thumbprint)) { + this.index.bootstrap(serialized); + deferred = $.Deferred().resolve(); + } else { + deferred = $.ajax(o.url, o.ajax).done(handlePrefetchResponse); + } + return deferred; + function handlePrefetchResponse(resp) { + that.clear(); + that.add(o.filter ? o.filter(resp) : resp); + that._saveToStorage(that.index.serialize(), o.thumbprint, o.ttl); + } + }, + _getFromRemote: function getFromRemote(query, cb) { + var that = this, url, uriEncodedQuery; + if (!this.transport) { + return; + } + query = query || ""; + uriEncodedQuery = encodeURIComponent(query); + url = this.remote.replace ? this.remote.replace(this.remote.url, query) : this.remote.url.replace(this.remote.wildcard, uriEncodedQuery); + return this.transport.get(url, this.remote.ajax, handleRemoteResponse); + function handleRemoteResponse(err, resp) { + err ? cb([]) : cb(that.remote.filter ? that.remote.filter(resp) : resp); + } + }, + _cancelLastRemoteRequest: function cancelLastRemoteRequest() { + this.transport && this.transport.cancel(); + }, + _saveToStorage: function saveToStorage(data, thumbprint, ttl) { + if (this.storage) { + this.storage.set(keys.data, data, ttl); + this.storage.set(keys.protocol, location.protocol, ttl); + this.storage.set(keys.thumbprint, thumbprint, ttl); + } + }, + _readFromStorage: function readFromStorage(thumbprint) { + var stored = {}, isExpired; + if (this.storage) { + stored.data = this.storage.get(keys.data); + stored.protocol = this.storage.get(keys.protocol); + stored.thumbprint = this.storage.get(keys.thumbprint); + } + isExpired = stored.thumbprint !== thumbprint || stored.protocol !== location.protocol; + return stored.data && !isExpired ? stored.data : null; + }, + _initialize: function initialize() { + var that = this, local = this.local, deferred; + deferred = this.prefetch ? this._loadPrefetch(this.prefetch) : $.Deferred().resolve(); + local && deferred.done(addLocalToIndex); + this.transport = this.remote ? new Transport(this.remote) : null; + return this.initPromise = deferred.promise(); + function addLocalToIndex() { + that.add(_.isFunction(local) ? local() : local); + } + }, + initialize: function initialize(force) { + return !this.initPromise || force ? this._initialize() : this.initPromise; + }, + add: function add(data) { + this.index.add(data); + }, + get: function get(query, cb) { + var that = this, matches = [], cacheHit = false; + matches = this.index.get(query); + matches = this.sorter(matches).slice(0, this.limit); + matches.length < this.limit ? cacheHit = this._getFromRemote(query, returnRemoteMatches) : this._cancelLastRemoteRequest(); + if (!cacheHit) { + (matches.length > 0 || !this.transport) && cb && cb(matches); + } + function returnRemoteMatches(remoteMatches) { + var matchesWithBackfill = matches.slice(0); + _.each(remoteMatches, function(remoteMatch) { + var isDuplicate; + isDuplicate = _.some(matchesWithBackfill, function(match) { + return that.dupDetector(remoteMatch, match); + }); + !isDuplicate && matchesWithBackfill.push(remoteMatch); + return matchesWithBackfill.length < that.limit; + }); + cb && cb(that.sorter(matchesWithBackfill)); + } + }, + clear: function clear() { + this.index.reset(); + }, + clearPrefetchCache: function clearPrefetchCache() { + this.storage && this.storage.clear(); + }, + clearRemoteCache: function clearRemoteCache() { + this.transport && Transport.resetCache(); + }, + ttAdapter: function ttAdapter() { + return _.bind(this.get, this); + } + }); + return Bloodhound; + function getSorter(sortFn) { + return _.isFunction(sortFn) ? sort : noSort; + function sort(array) { + return array.sort(sortFn); + } + function noSort(array) { + return array; + } + } + function ignoreDuplicates() { + return false; + } + })(this); + var html = function() { + return { + wrapper: '<span class="twitter-typeahead"></span>', + dropdown: '<span class="tt-dropdown-menu"></span>', + dataset: '<div class="tt-dataset-%CLASS%"></div>', + suggestions: '<span class="tt-suggestions"></span>', + suggestion: '<div class="tt-suggestion"></div>' + }; + }(); + var css = function() { + "use strict"; + var css = { + wrapper: { + position: "relative", + display: "inline-block" + }, + hint: { + position: "absolute", + top: "0", + left: "0", + borderColor: "transparent", + boxShadow: "none", + opacity: "1" + }, + input: { + position: "relative", + verticalAlign: "top", + backgroundColor: "transparent" + }, + inputWithNoHint: { + position: "relative", + verticalAlign: "top" + }, + dropdown: { + position: "absolute", + top: "100%", + left: "0", + zIndex: "100", + display: "none" + }, + suggestions: { + display: "block" + }, + suggestion: { + whiteSpace: "nowrap", + cursor: "pointer" + }, + suggestionChild: { + whiteSpace: "normal" + }, + ltr: { + left: "0", + right: "auto" + }, + rtl: { + left: "auto", + right: " 0" + } + }; + if (_.isMsie()) { + _.mixin(css.input, { + backgroundImage: "url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)" + }); + } + if (_.isMsie() && _.isMsie() <= 7) { + _.mixin(css.input, { + marginTop: "-1px" + }); + } + return css; + }(); + var EventBus = function() { + "use strict"; + var namespace = "typeahead:"; + function EventBus(o) { + if (!o || !o.el) { + $.error("EventBus initialized without el"); + } + this.$el = $(o.el); + } + _.mixin(EventBus.prototype, { + trigger: function(type) { + var args = [].slice.call(arguments, 1); + this.$el.trigger(namespace + type, args); + } + }); + return EventBus; + }(); + var EventEmitter = function() { + "use strict"; + var splitter = /\s+/, nextTick = getNextTick(); + return { + onSync: onSync, + onAsync: onAsync, + off: off, + trigger: trigger + }; + function on(method, types, cb, context) { + var type; + if (!cb) { + return this; + } + types = types.split(splitter); + cb = context ? bindContext(cb, context) : cb; + this._callbacks = this._callbacks || {}; + while (type = types.shift()) { + this._callbacks[type] = this._callbacks[type] || { + sync: [], + async: [] + }; + this._callbacks[type][method].push(cb); + } + return this; + } + function onAsync(types, cb, context) { + return on.call(this, "async", types, cb, context); + } + function onSync(types, cb, context) { + return on.call(this, "sync", types, cb, context); + } + function off(types) { + var type; + if (!this._callbacks) { + return this; + } + types = types.split(splitter); + while (type = types.shift()) { + delete this._callbacks[type]; + } + return this; + } + function trigger(types) { + var type, callbacks, args, syncFlush, asyncFlush; + if (!this._callbacks) { + return this; + } + types = types.split(splitter); + args = [].slice.call(arguments, 1); + while ((type = types.shift()) && (callbacks = this._callbacks[type])) { + syncFlush = getFlush(callbacks.sync, this, [ type ].concat(args)); + asyncFlush = getFlush(callbacks.async, this, [ type ].concat(args)); + syncFlush() && nextTick(asyncFlush); + } + return this; + } + function getFlush(callbacks, context, args) { + return flush; + function flush() { + var cancelled; + for (var i = 0, len = callbacks.length; !cancelled && i < len; i += 1) { + cancelled = callbacks[i].apply(context, args) === false; + } + return !cancelled; + } + } + function getNextTick() { + var nextTickFn; + if (window.setImmediate) { + nextTickFn = function nextTickSetImmediate(fn) { + setImmediate(function() { + fn(); + }); + }; + } else { + nextTickFn = function nextTickSetTimeout(fn) { + setTimeout(function() { + fn(); + }, 0); + }; + } + return nextTickFn; + } + function bindContext(fn, context) { + return fn.bind ? fn.bind(context) : function() { + fn.apply(context, [].slice.call(arguments, 0)); + }; + } + }(); + var highlight = function(doc) { + "use strict"; + var defaults = { + node: null, + pattern: null, + tagName: "strong", + className: null, + wordsOnly: false, + caseSensitive: false + }; + return function hightlight(o) { + var regex; + o = _.mixin({}, defaults, o); + if (!o.node || !o.pattern) { + return; + } + o.pattern = _.isArray(o.pattern) ? o.pattern : [ o.pattern ]; + regex = getRegex(o.pattern, o.caseSensitive, o.wordsOnly); + traverse(o.node, hightlightTextNode); + function hightlightTextNode(textNode) { + var match, patternNode, wrapperNode; + if (match = regex.exec(textNode.data)) { + wrapperNode = doc.createElement(o.tagName); + o.className && (wrapperNode.className = o.className); + patternNode = textNode.splitText(match.index); + patternNode.splitText(match[0].length); + wrapperNode.appendChild(patternNode.cloneNode(true)); + textNode.parentNode.replaceChild(wrapperNode, patternNode); + } + return !!match; + } + function traverse(el, hightlightTextNode) { + var childNode, TEXT_NODE_TYPE = 3; + for (var i = 0; i < el.childNodes.length; i++) { + childNode = el.childNodes[i]; + if (childNode.nodeType === TEXT_NODE_TYPE) { + i += hightlightTextNode(childNode) ? 1 : 0; + } else { + traverse(childNode, hightlightTextNode); + } + } + } + }; + function getRegex(patterns, caseSensitive, wordsOnly) { + var escapedPatterns = [], regexStr; + for (var i = 0, len = patterns.length; i < len; i++) { + escapedPatterns.push(_.escapeRegExChars(patterns[i])); + } + regexStr = wordsOnly ? "\\b(" + escapedPatterns.join("|") + ")\\b" : "(" + escapedPatterns.join("|") + ")"; + return caseSensitive ? new RegExp(regexStr) : new RegExp(regexStr, "i"); + } + }(window.document); + var Input = function() { + "use strict"; + var specialKeyCodeMap; + specialKeyCodeMap = { + 9: "tab", + 27: "esc", + 37: "left", + 39: "right", + 13: "enter", + 38: "up", + 40: "down" + }; + function Input(o) { + var that = this, onBlur, onFocus, onKeydown, onInput; + o = o || {}; + if (!o.input) { + $.error("input is missing"); + } + onBlur = _.bind(this._onBlur, this); + onFocus = _.bind(this._onFocus, this); + onKeydown = _.bind(this._onKeydown, this); + onInput = _.bind(this._onInput, this); + this.$hint = $(o.hint); + this.$input = $(o.input).on("blur.tt", onBlur).on("focus.tt", onFocus).on("keydown.tt", onKeydown); + if (this.$hint.length === 0) { + this.setHint = this.getHint = this.clearHint = this.clearHintIfInvalid = _.noop; + } + if (!_.isMsie()) { + this.$input.on("input.tt", onInput); + } else { + this.$input.on("keydown.tt keypress.tt cut.tt paste.tt", function($e) { + if (specialKeyCodeMap[$e.which || $e.keyCode]) { + return; + } + _.defer(_.bind(that._onInput, that, $e)); + }); + } + this.query = this.$input.val(); + this.$overflowHelper = buildOverflowHelper(this.$input); + } + Input.normalizeQuery = function(str) { + return (str || "").replace(/^\s*/g, "").replace(/\s{2,}/g, " "); + }; + _.mixin(Input.prototype, EventEmitter, { + _onBlur: function onBlur() { + this.resetInputValue(); + this.trigger("blurred"); + }, + _onFocus: function onFocus() { + this.trigger("focused"); + }, + _onKeydown: function onKeydown($e) { + var keyName = specialKeyCodeMap[$e.which || $e.keyCode]; + this._managePreventDefault(keyName, $e); + if (keyName && this._shouldTrigger(keyName, $e)) { + this.trigger(keyName + "Keyed", $e); + } + }, + _onInput: function onInput() { + this._checkInputValue(); + }, + _managePreventDefault: function managePreventDefault(keyName, $e) { + var preventDefault, hintValue, inputValue; + switch (keyName) { + case "tab": + hintValue = this.getHint(); + inputValue = this.getInputValue(); + preventDefault = hintValue && hintValue !== inputValue && !withModifier($e); + break; + + case "up": + case "down": + preventDefault = !withModifier($e); + break; + + default: + preventDefault = false; + } + preventDefault && $e.preventDefault(); + }, + _shouldTrigger: function shouldTrigger(keyName, $e) { + var trigger; + switch (keyName) { + case "tab": + trigger = !withModifier($e); + break; + + default: + trigger = true; + } + return trigger; + }, + _checkInputValue: function checkInputValue() { + var inputValue, areEquivalent, hasDifferentWhitespace; + inputValue = this.getInputValue(); + areEquivalent = areQueriesEquivalent(inputValue, this.query); + hasDifferentWhitespace = areEquivalent ? this.query.length !== inputValue.length : false; + this.query = inputValue; + if (!areEquivalent) { + this.trigger("queryChanged", this.query); + } else if (hasDifferentWhitespace) { + this.trigger("whitespaceChanged", this.query); + } + }, + focus: function focus() { + this.$input.focus(); + }, + blur: function blur() { + this.$input.blur(); + }, + getQuery: function getQuery() { + return this.query; + }, + setQuery: function setQuery(query) { + this.query = query; + }, + getInputValue: function getInputValue() { + return this.$input.val(); + }, + setInputValue: function setInputValue(value, silent) { + this.$input.val(value); + silent ? this.clearHint() : this._checkInputValue(); + }, + resetInputValue: function resetInputValue() { + this.setInputValue(this.query, true); + }, + getHint: function getHint() { + return this.$hint.val(); + }, + setHint: function setHint(value) { + this.$hint.val(value); + }, + clearHint: function clearHint() { + this.setHint(""); + }, + clearHintIfInvalid: function clearHintIfInvalid() { + var val, hint, valIsPrefixOfHint, isValid; + val = this.getInputValue(); + hint = this.getHint(); + valIsPrefixOfHint = val !== hint && hint.indexOf(val) === 0; + isValid = val !== "" && valIsPrefixOfHint && !this.hasOverflow(); + !isValid && this.clearHint(); + }, + getLanguageDirection: function getLanguageDirection() { + return (this.$input.css("direction") || "ltr").toLowerCase(); + }, + hasOverflow: function hasOverflow() { + var constraint = this.$input.width() - 2; + this.$overflowHelper.text(this.getInputValue()); + return this.$overflowHelper.width() >= constraint; + }, + isCursorAtEnd: function() { + var valueLength, selectionStart, range; + valueLength = this.$input.val().length; + selectionStart = this.$input[0].selectionStart; + if (_.isNumber(selectionStart)) { + return selectionStart === valueLength; + } else if (document.selection) { + range = document.selection.createRange(); + range.moveStart("character", -valueLength); + return valueLength === range.text.length; + } + return true; + }, + destroy: function destroy() { + this.$hint.off(".tt"); + this.$input.off(".tt"); + this.$hint = this.$input = this.$overflowHelper = null; + } + }); + return Input; + function buildOverflowHelper($input) { + return $('<pre aria-hidden="true"></pre>').css({ + position: "absolute", + visibility: "hidden", + whiteSpace: "pre", + fontFamily: $input.css("font-family"), + fontSize: $input.css("font-size"), + fontStyle: $input.css("font-style"), + fontVariant: $input.css("font-variant"), + fontWeight: $input.css("font-weight"), + wordSpacing: $input.css("word-spacing"), + letterSpacing: $input.css("letter-spacing"), + textIndent: $input.css("text-indent"), + textRendering: $input.css("text-rendering"), + textTransform: $input.css("text-transform") + }).insertAfter($input); + } + function areQueriesEquivalent(a, b) { + return Input.normalizeQuery(a) === Input.normalizeQuery(b); + } + function withModifier($e) { + return $e.altKey || $e.ctrlKey || $e.metaKey || $e.shiftKey; + } + }(); + var Dataset = function() { + "use strict"; + var datasetKey = "ttDataset", valueKey = "ttValue", datumKey = "ttDatum"; + function Dataset(o) { + o = o || {}; + o.templates = o.templates || {}; + if (!o.source) { + $.error("missing source"); + } + if (o.name && !isValidName(o.name)) { + $.error("invalid dataset name: " + o.name); + } + this.query = null; + this.highlight = !!o.highlight; + this.name = o.name || _.getUniqueId(); + this.source = o.source; + this.displayFn = getDisplayFn(o.display || o.displayKey); + this.templates = getTemplates(o.templates, this.displayFn); + this.$el = $(html.dataset.replace("%CLASS%", this.name)); + } + Dataset.extractDatasetName = function extractDatasetName(el) { + return $(el).data(datasetKey); + }; + Dataset.extractValue = function extractDatum(el) { + return $(el).data(valueKey); + }; + Dataset.extractDatum = function extractDatum(el) { + return $(el).data(datumKey); + }; + _.mixin(Dataset.prototype, EventEmitter, { + _render: function render(query, suggestions) { + if (!this.$el) { + return; + } + var that = this, hasSuggestions; + this.$el.empty(); + hasSuggestions = suggestions && suggestions.length; + if (!hasSuggestions && this.templates.empty) { + this.$el.html(getEmptyHtml()).prepend(that.templates.header ? getHeaderHtml() : null).append(that.templates.footer ? getFooterHtml() : null); + } else if (hasSuggestions) { + this.$el.html(getSuggestionsHtml()).prepend(that.templates.header ? getHeaderHtml() : null).append(that.templates.footer ? getFooterHtml() : null); + } + this.trigger("rendered"); + function getEmptyHtml() { + return that.templates.empty({ + query: query, + isEmpty: true + }); + } + function getSuggestionsHtml() { + var $suggestions, nodes; + $suggestions = $(html.suggestions).css(css.suggestions); + nodes = _.map(suggestions, getSuggestionNode); + $suggestions.append.apply($suggestions, nodes); + that.highlight && highlight({ + className: "tt-highlight", + node: $suggestions[0], + pattern: query + }); + return $suggestions; + function getSuggestionNode(suggestion) { + var $el; + $el = $(html.suggestion).append(that.templates.suggestion(suggestion)).data(datasetKey, that.name).data(valueKey, that.displayFn(suggestion)).data(datumKey, suggestion); + $el.children().each(function() { + $(this).css(css.suggestionChild); + }); + return $el; + } + } + function getHeaderHtml() { + return that.templates.header({ + query: query, + isEmpty: !hasSuggestions + }); + } + function getFooterHtml() { + return that.templates.footer({ + query: query, + isEmpty: !hasSuggestions + }); + } + }, + getRoot: function getRoot() { + return this.$el; + }, + update: function update(query) { + var that = this; + this.query = query; + this.canceled = false; + this.source(query, render); + function render(suggestions) { + if (!that.canceled && query === that.query) { + that._render(query, suggestions); + } + } + }, + cancel: function cancel() { + this.canceled = true; + }, + clear: function clear() { + this.cancel(); + this.$el.empty(); + this.trigger("rendered"); + }, + isEmpty: function isEmpty() { + return this.$el.is(":empty"); + }, + destroy: function destroy() { + this.$el = null; + } + }); + return Dataset; + function getDisplayFn(display) { + display = display || "value"; + return _.isFunction(display) ? display : displayFn; + function displayFn(obj) { + return obj[display]; + } + } + function getTemplates(templates, displayFn) { + return { + empty: templates.empty && _.templatify(templates.empty), + header: templates.header && _.templatify(templates.header), + footer: templates.footer && _.templatify(templates.footer), + suggestion: templates.suggestion || suggestionTemplate + }; + function suggestionTemplate(context) { + return "<p>" + displayFn(context) + "</p>"; + } + } + function isValidName(str) { + return /^[_a-zA-Z0-9-]+$/.test(str); + } + }(); + var Dropdown = function() { + "use strict"; + function Dropdown(o) { + var that = this, onSuggestionClick, onSuggestionMouseEnter, onSuggestionMouseLeave; + o = o || {}; + if (!o.menu) { + $.error("menu is required"); + } + this.isOpen = false; + this.isEmpty = true; + this.datasets = _.map(o.datasets, initializeDataset); + onSuggestionClick = _.bind(this._onSuggestionClick, this); + onSuggestionMouseEnter = _.bind(this._onSuggestionMouseEnter, this); + onSuggestionMouseLeave = _.bind(this._onSuggestionMouseLeave, this); + this.$menu = $(o.menu).on("click.tt", ".tt-suggestion", onSuggestionClick).on("mouseenter.tt", ".tt-suggestion", onSuggestionMouseEnter).on("mouseleave.tt", ".tt-suggestion", onSuggestionMouseLeave); + _.each(this.datasets, function(dataset) { + that.$menu.append(dataset.getRoot()); + dataset.onSync("rendered", that._onRendered, that); + }); + } + _.mixin(Dropdown.prototype, EventEmitter, { + _onSuggestionClick: function onSuggestionClick($e) { + this.trigger("suggestionClicked", $($e.currentTarget)); + }, + _onSuggestionMouseEnter: function onSuggestionMouseEnter($e) { + this._removeCursor(); + this._setCursor($($e.currentTarget), true); + }, + _onSuggestionMouseLeave: function onSuggestionMouseLeave() { + this._removeCursor(); + }, + _onRendered: function onRendered() { + this.isEmpty = _.every(this.datasets, isDatasetEmpty); + this.isEmpty ? this._hide() : this.isOpen && this._show(); + this.trigger("datasetRendered"); + function isDatasetEmpty(dataset) { + return dataset.isEmpty(); + } + }, + _hide: function() { + this.$menu.hide(); + }, + _show: function() { + this.$menu.css("display", "block"); + }, + _getSuggestions: function getSuggestions() { + return this.$menu.find(".tt-suggestion"); + }, + _getCursor: function getCursor() { + return this.$menu.find(".tt-cursor").first(); + }, + _setCursor: function setCursor($el, silent) { + $el.first().addClass("tt-cursor"); + !silent && this.trigger("cursorMoved"); + }, + _removeCursor: function removeCursor() { + this._getCursor().removeClass("tt-cursor"); + }, + _moveCursor: function moveCursor(increment) { + var $suggestions, $oldCursor, newCursorIndex, $newCursor; + if (!this.isOpen) { + return; + } + $oldCursor = this._getCursor(); + $suggestions = this._getSuggestions(); + this._removeCursor(); + newCursorIndex = $suggestions.index($oldCursor) + increment; + newCursorIndex = (newCursorIndex + 1) % ($suggestions.length + 1) - 1; + if (newCursorIndex === -1) { + this.trigger("cursorRemoved"); + return; + } else if (newCursorIndex < -1) { + newCursorIndex = $suggestions.length - 1; + } + this._setCursor($newCursor = $suggestions.eq(newCursorIndex)); + this._ensureVisible($newCursor); + }, + _ensureVisible: function ensureVisible($el) { + var elTop, elBottom, menuScrollTop, menuHeight; + elTop = $el.position().top; + elBottom = elTop + $el.outerHeight(true); + menuScrollTop = this.$menu.scrollTop(); + menuHeight = this.$menu.height() + parseInt(this.$menu.css("paddingTop"), 10) + parseInt(this.$menu.css("paddingBottom"), 10); + if (elTop < 0) { + this.$menu.scrollTop(menuScrollTop + elTop); + } else if (menuHeight < elBottom) { + this.$menu.scrollTop(menuScrollTop + (elBottom - menuHeight)); + } + }, + close: function close() { + if (this.isOpen) { + this.isOpen = false; + this._removeCursor(); + this._hide(); + this.trigger("closed"); + } + }, + open: function open() { + if (!this.isOpen) { + this.isOpen = true; + !this.isEmpty && this._show(); + this.trigger("opened"); + } + }, + setLanguageDirection: function setLanguageDirection(dir) { + this.$menu.css(dir === "ltr" ? css.ltr : css.rtl); + }, + moveCursorUp: function moveCursorUp() { + this._moveCursor(-1); + }, + moveCursorDown: function moveCursorDown() { + this._moveCursor(+1); + }, + getDatumForSuggestion: function getDatumForSuggestion($el) { + var datum = null; + if ($el.length) { + datum = { + raw: Dataset.extractDatum($el), + value: Dataset.extractValue($el), + datasetName: Dataset.extractDatasetName($el) + }; + } + return datum; + }, + getDatumForCursor: function getDatumForCursor() { + return this.getDatumForSuggestion(this._getCursor().first()); + }, + getDatumForTopSuggestion: function getDatumForTopSuggestion() { + return this.getDatumForSuggestion(this._getSuggestions().first()); + }, + update: function update(query) { + _.each(this.datasets, updateDataset); + function updateDataset(dataset) { + dataset.update(query); + } + }, + empty: function empty() { + _.each(this.datasets, clearDataset); + this.isEmpty = true; + function clearDataset(dataset) { + dataset.clear(); + } + }, + isVisible: function isVisible() { + return this.isOpen && !this.isEmpty; + }, + destroy: function destroy() { + this.$menu.off(".tt"); + this.$menu = null; + _.each(this.datasets, destroyDataset); + function destroyDataset(dataset) { + dataset.destroy(); + } + } + }); + return Dropdown; + function initializeDataset(oDataset) { + return new Dataset(oDataset); + } + }(); + var Typeahead = function() { + "use strict"; + var attrsKey = "ttAttrs"; + function Typeahead(o) { + var $menu, $input, $hint; + o = o || {}; + if (!o.input) { + $.error("missing input"); + } + this.isActivated = false; + this.autoselect = !!o.autoselect; + this.minLength = _.isNumber(o.minLength) ? o.minLength : 1; + this.$node = buildDom(o.input, o.withHint); + $menu = this.$node.find(".tt-dropdown-menu"); + $input = this.$node.find(".tt-input"); + $hint = this.$node.find(".tt-hint"); + $input.on("blur.tt", function($e) { + var active, isActive, hasActive; + active = document.activeElement; + isActive = $menu.is(active); + hasActive = $menu.has(active).length > 0; + if (_.isMsie() && (isActive || hasActive)) { + $e.preventDefault(); + $e.stopImmediatePropagation(); + _.defer(function() { + $input.focus(); + }); + } + }); + $menu.on("mousedown.tt", function($e) { + $e.preventDefault(); + }); + this.eventBus = o.eventBus || new EventBus({ + el: $input + }); + this.dropdown = new Dropdown({ + menu: $menu, + datasets: o.datasets + }).onSync("suggestionClicked", this._onSuggestionClicked, this).onSync("cursorMoved", this._onCursorMoved, this).onSync("cursorRemoved", this._onCursorRemoved, this).onSync("opened", this._onOpened, this).onSync("closed", this._onClosed, this).onAsync("datasetRendered", this._onDatasetRendered, this); + this.input = new Input({ + input: $input, + hint: $hint + }).onSync("focused", this._onFocused, this).onSync("blurred", this._onBlurred, this).onSync("enterKeyed", this._onEnterKeyed, this).onSync("tabKeyed", this._onTabKeyed, this).onSync("escKeyed", this._onEscKeyed, this).onSync("upKeyed", this._onUpKeyed, this).onSync("downKeyed", this._onDownKeyed, this).onSync("leftKeyed", this._onLeftKeyed, this).onSync("rightKeyed", this._onRightKeyed, this).onSync("queryChanged", this._onQueryChanged, this).onSync("whitespaceChanged", this._onWhitespaceChanged, this); + this._setLanguageDirection(); + } + _.mixin(Typeahead.prototype, { + _onSuggestionClicked: function onSuggestionClicked(type, $el) { + var datum; + if (datum = this.dropdown.getDatumForSuggestion($el)) { + this._select(datum); + } + }, + _onCursorMoved: function onCursorMoved() { + var datum = this.dropdown.getDatumForCursor(); + this.input.setInputValue(datum.value, true); + this.eventBus.trigger("cursorchanged", datum.raw, datum.datasetName); + }, + _onCursorRemoved: function onCursorRemoved() { + this.input.resetInputValue(); + this._updateHint(); + }, + _onDatasetRendered: function onDatasetRendered() { + this._updateHint(); + }, + _onOpened: function onOpened() { + this._updateHint(); + this.eventBus.trigger("opened"); + }, + _onClosed: function onClosed() { + this.input.clearHint(); + this.eventBus.trigger("closed"); + }, + _onFocused: function onFocused() { + this.isActivated = true; + this.dropdown.open(); + }, + _onBlurred: function onBlurred() { + this.isActivated = false; + this.dropdown.empty(); + this.dropdown.close(); + }, + _onEnterKeyed: function onEnterKeyed(type, $e) { + var cursorDatum, topSuggestionDatum; + cursorDatum = this.dropdown.getDatumForCursor(); + topSuggestionDatum = this.dropdown.getDatumForTopSuggestion(); + if (cursorDatum) { + this._select(cursorDatum); + $e.preventDefault(); + } else if (this.autoselect && topSuggestionDatum) { + this._select(topSuggestionDatum); + $e.preventDefault(); + } + }, + _onTabKeyed: function onTabKeyed(type, $e) { + var datum; + if (datum = this.dropdown.getDatumForCursor()) { + this._select(datum); + $e.preventDefault(); + } else { + this._autocomplete(true); + } + }, + _onEscKeyed: function onEscKeyed() { + this.dropdown.close(); + this.input.resetInputValue(); + }, + _onUpKeyed: function onUpKeyed() { + var query = this.input.getQuery(); + this.dropdown.isEmpty && query.length >= this.minLength ? this.dropdown.update(query) : this.dropdown.moveCursorUp(); + this.dropdown.open(); + }, + _onDownKeyed: function onDownKeyed() { + var query = this.input.getQuery(); + this.dropdown.isEmpty && query.length >= this.minLength ? this.dropdown.update(query) : this.dropdown.moveCursorDown(); + this.dropdown.open(); + }, + _onLeftKeyed: function onLeftKeyed() { + this.dir === "rtl" && this._autocomplete(); + }, + _onRightKeyed: function onRightKeyed() { + this.dir === "ltr" && this._autocomplete(); + }, + _onQueryChanged: function onQueryChanged(e, query) { + this.input.clearHintIfInvalid(); + query.length >= this.minLength ? this.dropdown.update(query) : this.dropdown.empty(); + this.dropdown.open(); + this._setLanguageDirection(); + }, + _onWhitespaceChanged: function onWhitespaceChanged() { + this._updateHint(); + this.dropdown.open(); + }, + _setLanguageDirection: function setLanguageDirection() { + var dir; + if (this.dir !== (dir = this.input.getLanguageDirection())) { + this.dir = dir; + this.$node.css("direction", dir); + this.dropdown.setLanguageDirection(dir); + } + }, + _updateHint: function updateHint() { + var datum, val, query, escapedQuery, frontMatchRegEx, match; + datum = this.dropdown.getDatumForTopSuggestion(); + if (datum && this.dropdown.isVisible() && !this.input.hasOverflow()) { + val = this.input.getInputValue(); + query = Input.normalizeQuery(val); + escapedQuery = _.escapeRegExChars(query); + frontMatchRegEx = new RegExp("^(?:" + escapedQuery + ")(.+$)", "i"); + match = frontMatchRegEx.exec(datum.value); + match ? this.input.setHint(val + match[1]) : this.input.clearHint(); + } else { + this.input.clearHint(); + } + }, + _autocomplete: function autocomplete(laxCursor) { + var hint, query, isCursorAtEnd, datum; + hint = this.input.getHint(); + query = this.input.getQuery(); + isCursorAtEnd = laxCursor || this.input.isCursorAtEnd(); + if (hint && query !== hint && isCursorAtEnd) { + datum = this.dropdown.getDatumForTopSuggestion(); + datum && this.input.setInputValue(datum.value); + this.eventBus.trigger("autocompleted", datum.raw, datum.datasetName); + } + }, + _select: function select(datum) { + this.input.setQuery(datum.value); + this.input.setInputValue(datum.value, true); + this._setLanguageDirection(); + this.eventBus.trigger("selected", datum.raw, datum.datasetName); + this.dropdown.close(); + _.defer(_.bind(this.dropdown.empty, this.dropdown)); + }, + open: function open() { + this.dropdown.open(); + }, + close: function close() { + this.dropdown.close(); + }, + setVal: function setVal(val) { + val = _.toStr(val); + if (this.isActivated) { + this.input.setInputValue(val); + } else { + this.input.setQuery(val); + this.input.setInputValue(val, true); + } + this._setLanguageDirection(); + }, + getVal: function getVal() { + return this.input.getQuery(); + }, + destroy: function destroy() { + this.input.destroy(); + this.dropdown.destroy(); + destroyDomStructure(this.$node); + this.$node = null; + } + }); + return Typeahead; + function buildDom(input, withHint) { + var $input, $wrapper, $dropdown, $hint; + $input = $(input); + $wrapper = $(html.wrapper).css(css.wrapper); + $dropdown = $(html.dropdown).css(css.dropdown); + $hint = $input.clone().css(css.hint).css(getBackgroundStyles($input)); + $hint.val("").removeData().addClass("tt-hint").removeAttr("id name placeholder required").prop("readonly", true).attr({ + autocomplete: "off", + spellcheck: "false", + tabindex: -1 + }); + $input.data(attrsKey, { + dir: $input.attr("dir"), + autocomplete: $input.attr("autocomplete"), + spellcheck: $input.attr("spellcheck"), + style: $input.attr("style") + }); + $input.addClass("tt-input").attr({ + autocomplete: "off", + spellcheck: false + }).css(withHint ? css.input : css.inputWithNoHint); + try { + !$input.attr("dir") && $input.attr("dir", "auto"); + } catch (e) {} + return $input.wrap($wrapper).parent().prepend(withHint ? $hint : null).append($dropdown); + } + function getBackgroundStyles($el) { + return { + backgroundAttachment: $el.css("background-attachment"), + backgroundClip: $el.css("background-clip"), + backgroundColor: $el.css("background-color"), + backgroundImage: $el.css("background-image"), + backgroundOrigin: $el.css("background-origin"), + backgroundPosition: $el.css("background-position"), + backgroundRepeat: $el.css("background-repeat"), + backgroundSize: $el.css("background-size") + }; + } + function destroyDomStructure($node) { + var $input = $node.find(".tt-input"); + _.each($input.data(attrsKey), function(val, key) { + _.isUndefined(val) ? $input.removeAttr(key) : $input.attr(key, val); + }); + $input.detach().removeData(attrsKey).removeClass("tt-input").insertAfter($node); + $node.remove(); + } + }(); + (function() { + "use strict"; + var old, typeaheadKey, methods; + old = $.fn.typeahead; + typeaheadKey = "ttTypeahead"; + methods = { + initialize: function initialize(o, datasets) { + datasets = _.isArray(datasets) ? datasets : [].slice.call(arguments, 1); + o = o || {}; + return this.each(attach); + function attach() { + var $input = $(this), eventBus, typeahead; + _.each(datasets, function(d) { + d.highlight = !!o.highlight; + }); + typeahead = new Typeahead({ + input: $input, + eventBus: eventBus = new EventBus({ + el: $input + }), + withHint: _.isUndefined(o.hint) ? true : !!o.hint, + minLength: o.minLength, + autoselect: o.autoselect, + datasets: datasets + }); + $input.data(typeaheadKey, typeahead); + } + }, + open: function open() { + return this.each(openTypeahead); + function openTypeahead() { + var $input = $(this), typeahead; + if (typeahead = $input.data(typeaheadKey)) { + typeahead.open(); + } + } + }, + close: function close() { + return this.each(closeTypeahead); + function closeTypeahead() { + var $input = $(this), typeahead; + if (typeahead = $input.data(typeaheadKey)) { + typeahead.close(); + } + } + }, + val: function val(newVal) { + return !arguments.length ? getVal(this.first()) : this.each(setVal); + function setVal() { + var $input = $(this), typeahead; + if (typeahead = $input.data(typeaheadKey)) { + typeahead.setVal(newVal); + } + } + function getVal($input) { + var typeahead, query; + if (typeahead = $input.data(typeaheadKey)) { + query = typeahead.getVal(); + } + return query; + } + }, + destroy: function destroy() { + return this.each(unattach); + function unattach() { + var $input = $(this), typeahead; + if (typeahead = $input.data(typeaheadKey)) { + typeahead.destroy(); + $input.removeData(typeaheadKey); + } + } + } + }; + $.fn.typeahead = function(method) { + var tts; + if (methods[method] && method !== "initialize") { + tts = this.filter(function() { + return !!$(this).data(typeaheadKey); + }); + return methods[method].apply(tts, [].slice.call(arguments, 1)); + } else { + return methods.initialize.apply(this, arguments); + } + }; + $.fn.typeahead.noConflict = function noConflict() { + $.fn.typeahead = old; + return this; + }; + })(); +})(window.jQuery); +/* +Copyright 2012 Igor Vaynberg + +Version: 3.5.1 Timestamp: Tue Jul 22 18:58:56 EDT 2014 + +This software is licensed under the Apache License, Version 2.0 (the "Apache License") or the GNU +General Public License version 2 (the "GPL License"). You may choose either license to govern your +use of this software only upon the condition that you accept all of the terms of either the Apache +License or the GPL License. + +You may obtain a copy of the Apache License and the GPL License at: + + http://www.apache.org/licenses/LICENSE-2.0 + http://www.gnu.org/licenses/gpl-2.0.html + +Unless required by applicable law or agreed to in writing, software distributed under the +Apache License or the GPL License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the Apache License and the GPL License for +the specific language governing permissions and limitations under the Apache License and the GPL License. +*/ +(function ($) { + if(typeof $.fn.each2 == "undefined") { + $.extend($.fn, { + /* + * 4-10 times faster .each replacement + * use it carefully, as it overrides jQuery context of element on each iteration + */ + each2 : function (c) { + var j = $([0]), i = -1, l = this.length; + while ( + ++i < l + && (j.context = j[0] = this[i]) + && c.call(j[0], i, j) !== false //"this"=DOM, i=index, j=jQuery object + ); + return this; + } + }); + } +})(jQuery); + +(function ($, undefined) { + "use strict"; + /*global document, window, jQuery, console */ + + if (window.Select2 !== undefined) { + return; + } + + var KEY, AbstractSelect2, SingleSelect2, MultiSelect2, nextUid, sizer, + lastMousePosition={x:0,y:0}, $document, scrollBarDimensions, + + KEY = { + TAB: 9, + ENTER: 13, + ESC: 27, + SPACE: 32, + LEFT: 37, + UP: 38, + RIGHT: 39, + DOWN: 40, + SHIFT: 16, + CTRL: 17, + ALT: 18, + PAGE_UP: 33, + PAGE_DOWN: 34, + HOME: 36, + END: 35, + BACKSPACE: 8, + DELETE: 46, + isArrow: function (k) { + k = k.which ? k.which : k; + switch (k) { + case KEY.LEFT: + case KEY.RIGHT: + case KEY.UP: + case KEY.DOWN: + return true; + } + return false; + }, + isControl: function (e) { + var k = e.which; + switch (k) { + case KEY.SHIFT: + case KEY.CTRL: + case KEY.ALT: + return true; + } + + if (e.metaKey) return true; + + return false; + }, + isFunctionKey: function (k) { + k = k.which ? k.which : k; + return k >= 112 && k <= 123; + } + }, + MEASURE_SCROLLBAR_TEMPLATE = "<div class='select2-measure-scrollbar'></div>", + + DIACRITICS = {"\u24B6":"A","\uFF21":"A","\u00C0":"A","\u00C1":"A","\u00C2":"A","\u1EA6":"A","\u1EA4":"A","\u1EAA":"A","\u1EA8":"A","\u00C3":"A","\u0100":"A","\u0102":"A","\u1EB0":"A","\u1EAE":"A","\u1EB4":"A","\u1EB2":"A","\u0226":"A","\u01E0":"A","\u00C4":"A","\u01DE":"A","\u1EA2":"A","\u00C5":"A","\u01FA":"A","\u01CD":"A","\u0200":"A","\u0202":"A","\u1EA0":"A","\u1EAC":"A","\u1EB6":"A","\u1E00":"A","\u0104":"A","\u023A":"A","\u2C6F":"A","\uA732":"AA","\u00C6":"AE","\u01FC":"AE","\u01E2":"AE","\uA734":"AO","\uA736":"AU","\uA738":"AV","\uA73A":"AV","\uA73C":"AY","\u24B7":"B","\uFF22":"B","\u1E02":"B","\u1E04":"B","\u1E06":"B","\u0243":"B","\u0182":"B","\u0181":"B","\u24B8":"C","\uFF23":"C","\u0106":"C","\u0108":"C","\u010A":"C","\u010C":"C","\u00C7":"C","\u1E08":"C","\u0187":"C","\u023B":"C","\uA73E":"C","\u24B9":"D","\uFF24":"D","\u1E0A":"D","\u010E":"D","\u1E0C":"D","\u1E10":"D","\u1E12":"D","\u1E0E":"D","\u0110":"D","\u018B":"D","\u018A":"D","\u0189":"D","\uA779":"D","\u01F1":"DZ","\u01C4":"DZ","\u01F2":"Dz","\u01C5":"Dz","\u24BA":"E","\uFF25":"E","\u00C8":"E","\u00C9":"E","\u00CA":"E","\u1EC0":"E","\u1EBE":"E","\u1EC4":"E","\u1EC2":"E","\u1EBC":"E","\u0112":"E","\u1E14":"E","\u1E16":"E","\u0114":"E","\u0116":"E","\u00CB":"E","\u1EBA":"E","\u011A":"E","\u0204":"E","\u0206":"E","\u1EB8":"E","\u1EC6":"E","\u0228":"E","\u1E1C":"E","\u0118":"E","\u1E18":"E","\u1E1A":"E","\u0190":"E","\u018E":"E","\u24BB":"F","\uFF26":"F","\u1E1E":"F","\u0191":"F","\uA77B":"F","\u24BC":"G","\uFF27":"G","\u01F4":"G","\u011C":"G","\u1E20":"G","\u011E":"G","\u0120":"G","\u01E6":"G","\u0122":"G","\u01E4":"G","\u0193":"G","\uA7A0":"G","\uA77D":"G","\uA77E":"G","\u24BD":"H","\uFF28":"H","\u0124":"H","\u1E22":"H","\u1E26":"H","\u021E":"H","\u1E24":"H","\u1E28":"H","\u1E2A":"H","\u0126":"H","\u2C67":"H","\u2C75":"H","\uA78D":"H","\u24BE":"I","\uFF29":"I","\u00CC":"I","\u00CD":"I","\u00CE":"I","\u0128":"I","\u012A":"I","\u012C":"I","\u0130":"I","\u00CF":"I","\u1E2E":"I","\u1EC8":"I","\u01CF":"I","\u0208":"I","\u020A":"I","\u1ECA":"I","\u012E":"I","\u1E2C":"I","\u0197":"I","\u24BF":"J","\uFF2A":"J","\u0134":"J","\u0248":"J","\u24C0":"K","\uFF2B":"K","\u1E30":"K","\u01E8":"K","\u1E32":"K","\u0136":"K","\u1E34":"K","\u0198":"K","\u2C69":"K","\uA740":"K","\uA742":"K","\uA744":"K","\uA7A2":"K","\u24C1":"L","\uFF2C":"L","\u013F":"L","\u0139":"L","\u013D":"L","\u1E36":"L","\u1E38":"L","\u013B":"L","\u1E3C":"L","\u1E3A":"L","\u0141":"L","\u023D":"L","\u2C62":"L","\u2C60":"L","\uA748":"L","\uA746":"L","\uA780":"L","\u01C7":"LJ","\u01C8":"Lj","\u24C2":"M","\uFF2D":"M","\u1E3E":"M","\u1E40":"M","\u1E42":"M","\u2C6E":"M","\u019C":"M","\u24C3":"N","\uFF2E":"N","\u01F8":"N","\u0143":"N","\u00D1":"N","\u1E44":"N","\u0147":"N","\u1E46":"N","\u0145":"N","\u1E4A":"N","\u1E48":"N","\u0220":"N","\u019D":"N","\uA790":"N","\uA7A4":"N","\u01CA":"NJ","\u01CB":"Nj","\u24C4":"O","\uFF2F":"O","\u00D2":"O","\u00D3":"O","\u00D4":"O","\u1ED2":"O","\u1ED0":"O","\u1ED6":"O","\u1ED4":"O","\u00D5":"O","\u1E4C":"O","\u022C":"O","\u1E4E":"O","\u014C":"O","\u1E50":"O","\u1E52":"O","\u014E":"O","\u022E":"O","\u0230":"O","\u00D6":"O","\u022A":"O","\u1ECE":"O","\u0150":"O","\u01D1":"O","\u020C":"O","\u020E":"O","\u01A0":"O","\u1EDC":"O","\u1EDA":"O","\u1EE0":"O","\u1EDE":"O","\u1EE2":"O","\u1ECC":"O","\u1ED8":"O","\u01EA":"O","\u01EC":"O","\u00D8":"O","\u01FE":"O","\u0186":"O","\u019F":"O","\uA74A":"O","\uA74C":"O","\u01A2":"OI","\uA74E":"OO","\u0222":"OU","\u24C5":"P","\uFF30":"P","\u1E54":"P","\u1E56":"P","\u01A4":"P","\u2C63":"P","\uA750":"P","\uA752":"P","\uA754":"P","\u24C6":"Q","\uFF31":"Q","\uA756":"Q","\uA758":"Q","\u024A":"Q","\u24C7":"R","\uFF32":"R","\u0154":"R","\u1E58":"R","\u0158":"R","\u0210":"R","\u0212":"R","\u1E5A":"R","\u1E5C":"R","\u0156":"R","\u1E5E":"R","\u024C":"R","\u2C64":"R","\uA75A":"R","\uA7A6":"R","\uA782":"R","\u24C8":"S","\uFF33":"S","\u1E9E":"S","\u015A":"S","\u1E64":"S","\u015C":"S","\u1E60":"S","\u0160":"S","\u1E66":"S","\u1E62":"S","\u1E68":"S","\u0218":"S","\u015E":"S","\u2C7E":"S","\uA7A8":"S","\uA784":"S","\u24C9":"T","\uFF34":"T","\u1E6A":"T","\u0164":"T","\u1E6C":"T","\u021A":"T","\u0162":"T","\u1E70":"T","\u1E6E":"T","\u0166":"T","\u01AC":"T","\u01AE":"T","\u023E":"T","\uA786":"T","\uA728":"TZ","\u24CA":"U","\uFF35":"U","\u00D9":"U","\u00DA":"U","\u00DB":"U","\u0168":"U","\u1E78":"U","\u016A":"U","\u1E7A":"U","\u016C":"U","\u00DC":"U","\u01DB":"U","\u01D7":"U","\u01D5":"U","\u01D9":"U","\u1EE6":"U","\u016E":"U","\u0170":"U","\u01D3":"U","\u0214":"U","\u0216":"U","\u01AF":"U","\u1EEA":"U","\u1EE8":"U","\u1EEE":"U","\u1EEC":"U","\u1EF0":"U","\u1EE4":"U","\u1E72":"U","\u0172":"U","\u1E76":"U","\u1E74":"U","\u0244":"U","\u24CB":"V","\uFF36":"V","\u1E7C":"V","\u1E7E":"V","\u01B2":"V","\uA75E":"V","\u0245":"V","\uA760":"VY","\u24CC":"W","\uFF37":"W","\u1E80":"W","\u1E82":"W","\u0174":"W","\u1E86":"W","\u1E84":"W","\u1E88":"W","\u2C72":"W","\u24CD":"X","\uFF38":"X","\u1E8A":"X","\u1E8C":"X","\u24CE":"Y","\uFF39":"Y","\u1EF2":"Y","\u00DD":"Y","\u0176":"Y","\u1EF8":"Y","\u0232":"Y","\u1E8E":"Y","\u0178":"Y","\u1EF6":"Y","\u1EF4":"Y","\u01B3":"Y","\u024E":"Y","\u1EFE":"Y","\u24CF":"Z","\uFF3A":"Z","\u0179":"Z","\u1E90":"Z","\u017B":"Z","\u017D":"Z","\u1E92":"Z","\u1E94":"Z","\u01B5":"Z","\u0224":"Z","\u2C7F":"Z","\u2C6B":"Z","\uA762":"Z","\u24D0":"a","\uFF41":"a","\u1E9A":"a","\u00E0":"a","\u00E1":"a","\u00E2":"a","\u1EA7":"a","\u1EA5":"a","\u1EAB":"a","\u1EA9":"a","\u00E3":"a","\u0101":"a","\u0103":"a","\u1EB1":"a","\u1EAF":"a","\u1EB5":"a","\u1EB3":"a","\u0227":"a","\u01E1":"a","\u00E4":"a","\u01DF":"a","\u1EA3":"a","\u00E5":"a","\u01FB":"a","\u01CE":"a","\u0201":"a","\u0203":"a","\u1EA1":"a","\u1EAD":"a","\u1EB7":"a","\u1E01":"a","\u0105":"a","\u2C65":"a","\u0250":"a","\uA733":"aa","\u00E6":"ae","\u01FD":"ae","\u01E3":"ae","\uA735":"ao","\uA737":"au","\uA739":"av","\uA73B":"av","\uA73D":"ay","\u24D1":"b","\uFF42":"b","\u1E03":"b","\u1E05":"b","\u1E07":"b","\u0180":"b","\u0183":"b","\u0253":"b","\u24D2":"c","\uFF43":"c","\u0107":"c","\u0109":"c","\u010B":"c","\u010D":"c","\u00E7":"c","\u1E09":"c","\u0188":"c","\u023C":"c","\uA73F":"c","\u2184":"c","\u24D3":"d","\uFF44":"d","\u1E0B":"d","\u010F":"d","\u1E0D":"d","\u1E11":"d","\u1E13":"d","\u1E0F":"d","\u0111":"d","\u018C":"d","\u0256":"d","\u0257":"d","\uA77A":"d","\u01F3":"dz","\u01C6":"dz","\u24D4":"e","\uFF45":"e","\u00E8":"e","\u00E9":"e","\u00EA":"e","\u1EC1":"e","\u1EBF":"e","\u1EC5":"e","\u1EC3":"e","\u1EBD":"e","\u0113":"e","\u1E15":"e","\u1E17":"e","\u0115":"e","\u0117":"e","\u00EB":"e","\u1EBB":"e","\u011B":"e","\u0205":"e","\u0207":"e","\u1EB9":"e","\u1EC7":"e","\u0229":"e","\u1E1D":"e","\u0119":"e","\u1E19":"e","\u1E1B":"e","\u0247":"e","\u025B":"e","\u01DD":"e","\u24D5":"f","\uFF46":"f","\u1E1F":"f","\u0192":"f","\uA77C":"f","\u24D6":"g","\uFF47":"g","\u01F5":"g","\u011D":"g","\u1E21":"g","\u011F":"g","\u0121":"g","\u01E7":"g","\u0123":"g","\u01E5":"g","\u0260":"g","\uA7A1":"g","\u1D79":"g","\uA77F":"g","\u24D7":"h","\uFF48":"h","\u0125":"h","\u1E23":"h","\u1E27":"h","\u021F":"h","\u1E25":"h","\u1E29":"h","\u1E2B":"h","\u1E96":"h","\u0127":"h","\u2C68":"h","\u2C76":"h","\u0265":"h","\u0195":"hv","\u24D8":"i","\uFF49":"i","\u00EC":"i","\u00ED":"i","\u00EE":"i","\u0129":"i","\u012B":"i","\u012D":"i","\u00EF":"i","\u1E2F":"i","\u1EC9":"i","\u01D0":"i","\u0209":"i","\u020B":"i","\u1ECB":"i","\u012F":"i","\u1E2D":"i","\u0268":"i","\u0131":"i","\u24D9":"j","\uFF4A":"j","\u0135":"j","\u01F0":"j","\u0249":"j","\u24DA":"k","\uFF4B":"k","\u1E31":"k","\u01E9":"k","\u1E33":"k","\u0137":"k","\u1E35":"k","\u0199":"k","\u2C6A":"k","\uA741":"k","\uA743":"k","\uA745":"k","\uA7A3":"k","\u24DB":"l","\uFF4C":"l","\u0140":"l","\u013A":"l","\u013E":"l","\u1E37":"l","\u1E39":"l","\u013C":"l","\u1E3D":"l","\u1E3B":"l","\u017F":"l","\u0142":"l","\u019A":"l","\u026B":"l","\u2C61":"l","\uA749":"l","\uA781":"l","\uA747":"l","\u01C9":"lj","\u24DC":"m","\uFF4D":"m","\u1E3F":"m","\u1E41":"m","\u1E43":"m","\u0271":"m","\u026F":"m","\u24DD":"n","\uFF4E":"n","\u01F9":"n","\u0144":"n","\u00F1":"n","\u1E45":"n","\u0148":"n","\u1E47":"n","\u0146":"n","\u1E4B":"n","\u1E49":"n","\u019E":"n","\u0272":"n","\u0149":"n","\uA791":"n","\uA7A5":"n","\u01CC":"nj","\u24DE":"o","\uFF4F":"o","\u00F2":"o","\u00F3":"o","\u00F4":"o","\u1ED3":"o","\u1ED1":"o","\u1ED7":"o","\u1ED5":"o","\u00F5":"o","\u1E4D":"o","\u022D":"o","\u1E4F":"o","\u014D":"o","\u1E51":"o","\u1E53":"o","\u014F":"o","\u022F":"o","\u0231":"o","\u00F6":"o","\u022B":"o","\u1ECF":"o","\u0151":"o","\u01D2":"o","\u020D":"o","\u020F":"o","\u01A1":"o","\u1EDD":"o","\u1EDB":"o","\u1EE1":"o","\u1EDF":"o","\u1EE3":"o","\u1ECD":"o","\u1ED9":"o","\u01EB":"o","\u01ED":"o","\u00F8":"o","\u01FF":"o","\u0254":"o","\uA74B":"o","\uA74D":"o","\u0275":"o","\u01A3":"oi","\u0223":"ou","\uA74F":"oo","\u24DF":"p","\uFF50":"p","\u1E55":"p","\u1E57":"p","\u01A5":"p","\u1D7D":"p","\uA751":"p","\uA753":"p","\uA755":"p","\u24E0":"q","\uFF51":"q","\u024B":"q","\uA757":"q","\uA759":"q","\u24E1":"r","\uFF52":"r","\u0155":"r","\u1E59":"r","\u0159":"r","\u0211":"r","\u0213":"r","\u1E5B":"r","\u1E5D":"r","\u0157":"r","\u1E5F":"r","\u024D":"r","\u027D":"r","\uA75B":"r","\uA7A7":"r","\uA783":"r","\u24E2":"s","\uFF53":"s","\u00DF":"s","\u015B":"s","\u1E65":"s","\u015D":"s","\u1E61":"s","\u0161":"s","\u1E67":"s","\u1E63":"s","\u1E69":"s","\u0219":"s","\u015F":"s","\u023F":"s","\uA7A9":"s","\uA785":"s","\u1E9B":"s","\u24E3":"t","\uFF54":"t","\u1E6B":"t","\u1E97":"t","\u0165":"t","\u1E6D":"t","\u021B":"t","\u0163":"t","\u1E71":"t","\u1E6F":"t","\u0167":"t","\u01AD":"t","\u0288":"t","\u2C66":"t","\uA787":"t","\uA729":"tz","\u24E4":"u","\uFF55":"u","\u00F9":"u","\u00FA":"u","\u00FB":"u","\u0169":"u","\u1E79":"u","\u016B":"u","\u1E7B":"u","\u016D":"u","\u00FC":"u","\u01DC":"u","\u01D8":"u","\u01D6":"u","\u01DA":"u","\u1EE7":"u","\u016F":"u","\u0171":"u","\u01D4":"u","\u0215":"u","\u0217":"u","\u01B0":"u","\u1EEB":"u","\u1EE9":"u","\u1EEF":"u","\u1EED":"u","\u1EF1":"u","\u1EE5":"u","\u1E73":"u","\u0173":"u","\u1E77":"u","\u1E75":"u","\u0289":"u","\u24E5":"v","\uFF56":"v","\u1E7D":"v","\u1E7F":"v","\u028B":"v","\uA75F":"v","\u028C":"v","\uA761":"vy","\u24E6":"w","\uFF57":"w","\u1E81":"w","\u1E83":"w","\u0175":"w","\u1E87":"w","\u1E85":"w","\u1E98":"w","\u1E89":"w","\u2C73":"w","\u24E7":"x","\uFF58":"x","\u1E8B":"x","\u1E8D":"x","\u24E8":"y","\uFF59":"y","\u1EF3":"y","\u00FD":"y","\u0177":"y","\u1EF9":"y","\u0233":"y","\u1E8F":"y","\u00FF":"y","\u1EF7":"y","\u1E99":"y","\u1EF5":"y","\u01B4":"y","\u024F":"y","\u1EFF":"y","\u24E9":"z","\uFF5A":"z","\u017A":"z","\u1E91":"z","\u017C":"z","\u017E":"z","\u1E93":"z","\u1E95":"z","\u01B6":"z","\u0225":"z","\u0240":"z","\u2C6C":"z","\uA763":"z","\u0386":"\u0391","\u0388":"\u0395","\u0389":"\u0397","\u038A":"\u0399","\u03AA":"\u0399","\u038C":"\u039F","\u038E":"\u03A5","\u03AB":"\u03A5","\u038F":"\u03A9","\u03AC":"\u03B1","\u03AD":"\u03B5","\u03AE":"\u03B7","\u03AF":"\u03B9","\u03CA":"\u03B9","\u0390":"\u03B9","\u03CC":"\u03BF","\u03CD":"\u03C5","\u03CB":"\u03C5","\u03B0":"\u03C5","\u03C9":"\u03C9","\u03C2":"\u03C3"}; + + $document = $(document); + + nextUid=(function() { var counter=1; return function() { return counter++; }; }()); + + + function reinsertElement(element) { + var placeholder = $(document.createTextNode('')); + + element.before(placeholder); + placeholder.before(element); + placeholder.remove(); + } + + function stripDiacritics(str) { + // Used 'uni range + named function' from http://jsperf.com/diacritics/18 + function match(a) { + return DIACRITICS[a] || a; + } + + return str.replace(/[^\u0000-\u007E]/g, match); + } + + function indexOf(value, array) { + var i = 0, l = array.length; + for (; i < l; i = i + 1) { + if (equal(value, array[i])) return i; + } + return -1; + } + + function measureScrollbar () { + var $template = $( MEASURE_SCROLLBAR_TEMPLATE ); + $template.appendTo('body'); + + var dim = { + width: $template.width() - $template[0].clientWidth, + height: $template.height() - $template[0].clientHeight + }; + $template.remove(); + + return dim; + } + + /** + * Compares equality of a and b + * @param a + * @param b + */ + function equal(a, b) { + if (a === b) return true; + if (a === undefined || b === undefined) return false; + if (a === null || b === null) return false; + // Check whether 'a' or 'b' is a string (primitive or object). + // The concatenation of an empty string (+'') converts its argument to a string's primitive. + if (a.constructor === String) return a+'' === b+''; // a+'' - in case 'a' is a String object + if (b.constructor === String) return b+'' === a+''; // b+'' - in case 'b' is a String object + return false; + } + + /** + * Splits the string into an array of values, trimming each value. An empty array is returned for nulls or empty + * strings + * @param string + * @param separator + */ + function splitVal(string, separator) { + var val, i, l; + if (string === null || string.length < 1) return []; + val = string.split(separator); + for (i = 0, l = val.length; i < l; i = i + 1) val[i] = $.trim(val[i]); + return val; + } + + function getSideBorderPadding(element) { + return element.outerWidth(false) - element.width(); + } + + function installKeyUpChangeEvent(element) { + var key="keyup-change-value"; + element.on("keydown", function () { + if ($.data(element, key) === undefined) { + $.data(element, key, element.val()); + } + }); + element.on("keyup", function () { + var val= $.data(element, key); + if (val !== undefined && element.val() !== val) { + $.removeData(element, key); + element.trigger("keyup-change"); + } + }); + } + + + /** + * filters mouse events so an event is fired only if the mouse moved. + * + * filters out mouse events that occur when mouse is stationary but + * the elements under the pointer are scrolled. + */ + function installFilteredMouseMove(element) { + element.on("mousemove", function (e) { + var lastpos = lastMousePosition; + if (lastpos === undefined || lastpos.x !== e.pageX || lastpos.y !== e.pageY) { + $(e.target).trigger("mousemove-filtered", e); + } + }); + } + + /** + * Debounces a function. Returns a function that calls the original fn function only if no invocations have been made + * within the last quietMillis milliseconds. + * + * @param quietMillis number of milliseconds to wait before invoking fn + * @param fn function to be debounced + * @param ctx object to be used as this reference within fn + * @return debounced version of fn + */ + function debounce(quietMillis, fn, ctx) { + ctx = ctx || undefined; + var timeout; + return function () { + var args = arguments; + window.clearTimeout(timeout); + timeout = window.setTimeout(function() { + fn.apply(ctx, args); + }, quietMillis); + }; + } + + function installDebouncedScroll(threshold, element) { + var notify = debounce(threshold, function (e) { element.trigger("scroll-debounced", e);}); + element.on("scroll", function (e) { + if (indexOf(e.target, element.get()) >= 0) notify(e); + }); + } + + function focus($el) { + if ($el[0] === document.activeElement) return; + + /* set the focus in a 0 timeout - that way the focus is set after the processing + of the current event has finished - which seems like the only reliable way + to set focus */ + window.setTimeout(function() { + var el=$el[0], pos=$el.val().length, range; + + $el.focus(); + + /* make sure el received focus so we do not error out when trying to manipulate the caret. + sometimes modals or others listeners may steal it after its set */ + var isVisible = (el.offsetWidth > 0 || el.offsetHeight > 0); + if (isVisible && el === document.activeElement) { + + /* after the focus is set move the caret to the end, necessary when we val() + just before setting focus */ + if(el.setSelectionRange) + { + el.setSelectionRange(pos, pos); + } + else if (el.createTextRange) { + range = el.createTextRange(); + range.collapse(false); + range.select(); + } + } + }, 0); + } + + function getCursorInfo(el) { + el = $(el)[0]; + var offset = 0; + var length = 0; + if ('selectionStart' in el) { + offset = el.selectionStart; + length = el.selectionEnd - offset; + } else if ('selection' in document) { + el.focus(); + var sel = document.selection.createRange(); + length = document.selection.createRange().text.length; + sel.moveStart('character', -el.value.length); + offset = sel.text.length - length; + } + return { offset: offset, length: length }; + } + + function killEvent(event) { + event.preventDefault(); + event.stopPropagation(); + } + function killEventImmediately(event) { + event.preventDefault(); + event.stopImmediatePropagation(); + } + + function measureTextWidth(e) { + if (!sizer){ + var style = e[0].currentStyle || window.getComputedStyle(e[0], null); + sizer = $(document.createElement("div")).css({ + position: "absolute", + left: "-10000px", + top: "-10000px", + display: "none", + fontSize: style.fontSize, + fontFamily: style.fontFamily, + fontStyle: style.fontStyle, + fontWeight: style.fontWeight, + letterSpacing: style.letterSpacing, + textTransform: style.textTransform, + whiteSpace: "nowrap" + }); + sizer.attr("class","select2-sizer"); + $("body").append(sizer); + } + sizer.text(e.val()); + return sizer.width(); + } + + function syncCssClasses(dest, src, adapter) { + var classes, replacements = [], adapted; + + classes = $.trim(dest.attr("class")); + + if (classes) { + classes = '' + classes; // for IE which returns object + + $(classes.split(/\s+/)).each2(function() { + if (this.indexOf("select2-") === 0) { + replacements.push(this); + } + }); + } + + classes = $.trim(src.attr("class")); + + if (classes) { + classes = '' + classes; // for IE which returns object + + $(classes.split(/\s+/)).each2(function() { + if (this.indexOf("select2-") !== 0) { + adapted = adapter(this); + + if (adapted) { + replacements.push(adapted); + } + } + }); + } + + dest.attr("class", replacements.join(" ")); + } + + + function markMatch(text, term, markup, escapeMarkup) { + var match=stripDiacritics(text.toUpperCase()).indexOf(stripDiacritics(term.toUpperCase())), + tl=term.length; + + if (match<0) { + markup.push(escapeMarkup(text)); + return; + } + + markup.push(escapeMarkup(text.substring(0, match))); + markup.push("<span class='select2-match'>"); + markup.push(escapeMarkup(text.substring(match, match + tl))); + markup.push("</span>"); + markup.push(escapeMarkup(text.substring(match + tl, text.length))); + } + + function defaultEscapeMarkup(markup) { + var replace_map = { + '\\': '\', + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''', + "/": '/' + }; + + return String(markup).replace(/[&<>"'\/\\]/g, function (match) { + return replace_map[match]; + }); + } + + /** + * Produces an ajax-based query function + * + * @param options object containing configuration parameters + * @param options.params parameter map for the transport ajax call, can contain such options as cache, jsonpCallback, etc. see $.ajax + * @param options.transport function that will be used to execute the ajax request. must be compatible with parameters supported by $.ajax + * @param options.url url for the data + * @param options.data a function(searchTerm, pageNumber, context) that should return an object containing query string parameters for the above url. + * @param options.dataType request data type: ajax, jsonp, other datatypes supported by jQuery's $.ajax function or the transport function if specified + * @param options.quietMillis (optional) milliseconds to wait before making the ajaxRequest, helps debounce the ajax function if invoked too often + * @param options.results a function(remoteData, pageNumber, query) that converts data returned form the remote request to the format expected by Select2. + * The expected format is an object containing the following keys: + * results array of objects that will be used as choices + * more (optional) boolean indicating whether there are more results available + * Example: {results:[{id:1, text:'Red'},{id:2, text:'Blue'}], more:true} + */ + function ajax(options) { + var timeout, // current scheduled but not yet executed request + handler = null, + quietMillis = options.quietMillis || 100, + ajaxUrl = options.url, + self = this; + + return function (query) { + window.clearTimeout(timeout); + timeout = window.setTimeout(function () { + var data = options.data, // ajax data function + url = ajaxUrl, // ajax url string or function + transport = options.transport || $.fn.select2.ajaxDefaults.transport, + // deprecated - to be removed in 4.0 - use params instead + deprecated = { + type: options.type || 'GET', // set type of request (GET or POST) + cache: options.cache || false, + jsonpCallback: options.jsonpCallback||undefined, + dataType: options.dataType||"json" + }, + params = $.extend({}, $.fn.select2.ajaxDefaults.params, deprecated); + + data = data ? data.call(self, query.term, query.page, query.context) : null; + url = (typeof url === 'function') ? url.call(self, query.term, query.page, query.context) : url; + + if (handler && typeof handler.abort === "function") { handler.abort(); } + + if (options.params) { + if ($.isFunction(options.params)) { + $.extend(params, options.params.call(self)); + } else { + $.extend(params, options.params); + } + } + + $.extend(params, { + url: url, + dataType: options.dataType, + data: data, + success: function (data) { + // TODO - replace query.page with query so users have access to term, page, etc. + // added query as third paramter to keep backwards compatibility + var results = options.results(data, query.page, query); + query.callback(results); + }, + error: function(jqXHR, textStatus, errorThrown){ + var results = { + hasError: true, + jqXHR: jqXHR, + textStatus: textStatus, + errorThrown: errorThrown, + }; + + query.callback(results); + } + }); + handler = transport.call(self, params); + }, quietMillis); + }; + } + + /** + * Produces a query function that works with a local array + * + * @param options object containing configuration parameters. The options parameter can either be an array or an + * object. + * + * If the array form is used it is assumed that it contains objects with 'id' and 'text' keys. + * + * If the object form is used it is assumed that it contains 'data' and 'text' keys. The 'data' key should contain + * an array of objects that will be used as choices. These objects must contain at least an 'id' key. The 'text' + * key can either be a String in which case it is expected that each element in the 'data' array has a key with the + * value of 'text' which will be used to match choices. Alternatively, text can be a function(item) that can extract + * the text. + */ + function local(options) { + var data = options, // data elements + dataText, + tmp, + text = function (item) { return ""+item.text; }; // function used to retrieve the text portion of a data item that is matched against the search + + if ($.isArray(data)) { + tmp = data; + data = { results: tmp }; + } + + if ($.isFunction(data) === false) { + tmp = data; + data = function() { return tmp; }; + } + + var dataItem = data(); + if (dataItem.text) { + text = dataItem.text; + // if text is not a function we assume it to be a key name + if (!$.isFunction(text)) { + dataText = dataItem.text; // we need to store this in a separate variable because in the next step data gets reset and data.text is no longer available + text = function (item) { return item[dataText]; }; + } + } + + return function (query) { + var t = query.term, filtered = { results: [] }, process; + if (t === "") { + query.callback(data()); + return; + } + + process = function(datum, collection) { + var group, attr; + datum = datum[0]; + if (datum.children) { + group = {}; + for (attr in datum) { + if (datum.hasOwnProperty(attr)) group[attr]=datum[attr]; + } + group.children=[]; + $(datum.children).each2(function(i, childDatum) { process(childDatum, group.children); }); + if (group.children.length || query.matcher(t, text(group), datum)) { + collection.push(group); + } + } else { + if (query.matcher(t, text(datum), datum)) { + collection.push(datum); + } + } + }; + + $(data().results).each2(function(i, datum) { process(datum, filtered.results); }); + query.callback(filtered); + }; + } + + // TODO javadoc + function tags(data) { + var isFunc = $.isFunction(data); + return function (query) { + var t = query.term, filtered = {results: []}; + var result = isFunc ? data(query) : data; + if ($.isArray(result)) { + $(result).each(function () { + var isObject = this.text !== undefined, + text = isObject ? this.text : this; + if (t === "" || query.matcher(t, text)) { + filtered.results.push(isObject ? this : {id: this, text: this}); + } + }); + query.callback(filtered); + } + }; + } + + /** + * Checks if the formatter function should be used. + * + * Throws an error if it is not a function. Returns true if it should be used, + * false if no formatting should be performed. + * + * @param formatter + */ + function checkFormatter(formatter, formatterName) { + if ($.isFunction(formatter)) return true; + if (!formatter) return false; + if (typeof(formatter) === 'string') return true; + throw new Error(formatterName +" must be a string, function, or falsy value"); + } + + /** + * Returns a given value + * If given a function, returns its output + * + * @param val string|function + * @param context value of "this" to be passed to function + * @returns {*} + */ + function evaluate(val, context) { + if ($.isFunction(val)) { + var args = Array.prototype.slice.call(arguments, 2); + return val.apply(context, args); + } + return val; + } + + function countResults(results) { + var count = 0; + $.each(results, function(i, item) { + if (item.children) { + count += countResults(item.children); + } else { + count++; + } + }); + return count; + } + + /** + * Default tokenizer. This function uses breaks the input on substring match of any string from the + * opts.tokenSeparators array and uses opts.createSearchChoice to create the choice object. Both of those + * two options have to be defined in order for the tokenizer to work. + * + * @param input text user has typed so far or pasted into the search field + * @param selection currently selected choices + * @param selectCallback function(choice) callback tho add the choice to selection + * @param opts select2's opts + * @return undefined/null to leave the current input unchanged, or a string to change the input to the returned value + */ + function defaultTokenizer(input, selection, selectCallback, opts) { + var original = input, // store the original so we can compare and know if we need to tell the search to update its text + dupe = false, // check for whether a token we extracted represents a duplicate selected choice + token, // token + index, // position at which the separator was found + i, l, // looping variables + separator; // the matched separator + + if (!opts.createSearchChoice || !opts.tokenSeparators || opts.tokenSeparators.length < 1) return undefined; + + while (true) { + index = -1; + + for (i = 0, l = opts.tokenSeparators.length; i < l; i++) { + separator = opts.tokenSeparators[i]; + index = input.indexOf(separator); + if (index >= 0) break; + } + + if (index < 0) break; // did not find any token separator in the input string, bail + + token = input.substring(0, index); + input = input.substring(index + separator.length); + + if (token.length > 0) { + token = opts.createSearchChoice.call(this, token, selection); + if (token !== undefined && token !== null && opts.id(token) !== undefined && opts.id(token) !== null) { + dupe = false; + for (i = 0, l = selection.length; i < l; i++) { + if (equal(opts.id(token), opts.id(selection[i]))) { + dupe = true; break; + } + } + + if (!dupe) selectCallback(token); + } + } + } + + if (original!==input) return input; + } + + function cleanupJQueryElements() { + var self = this; + + $.each(arguments, function (i, element) { + self[element].remove(); + self[element] = null; + }); + } + + /** + * Creates a new class + * + * @param superClass + * @param methods + */ + function clazz(SuperClass, methods) { + var constructor = function () {}; + constructor.prototype = new SuperClass; + constructor.prototype.constructor = constructor; + constructor.prototype.parent = SuperClass.prototype; + constructor.prototype = $.extend(constructor.prototype, methods); + return constructor; + } + + AbstractSelect2 = clazz(Object, { + + // abstract + bind: function (func) { + var self = this; + return function () { + func.apply(self, arguments); + }; + }, + + // abstract + init: function (opts) { + var results, search, resultsSelector = ".select2-results"; + + // prepare options + this.opts = opts = this.prepareOpts(opts); + + this.id=opts.id; + + // destroy if called on an existing component + if (opts.element.data("select2") !== undefined && + opts.element.data("select2") !== null) { + opts.element.data("select2").destroy(); + } + + this.container = this.createContainer(); + + this.liveRegion = $("<span>", { + role: "status", + "aria-live": "polite" + }) + .addClass("select2-hidden-accessible") + .appendTo(document.body); + + this.containerId="s2id_"+(opts.element.attr("id") || "autogen"+nextUid()); + this.containerEventName= this.containerId + .replace(/([.])/g, '_') + .replace(/([;&,\-\.\+\*\~':"\!\^#$%@\[\]\(\)=>\|])/g, '\\$1'); + this.container.attr("id", this.containerId); + + this.container.attr("title", opts.element.attr("title")); + + this.body = $("body"); + + syncCssClasses(this.container, this.opts.element, this.opts.adaptContainerCssClass); + + this.container.attr("style", opts.element.attr("style")); + this.container.css(evaluate(opts.containerCss, this.opts.element)); + this.container.addClass(evaluate(opts.containerCssClass, this.opts.element)); + + this.elementTabIndex = this.opts.element.attr("tabindex"); + + // swap container for the element + this.opts.element + .data("select2", this) + .attr("tabindex", "-1") + .before(this.container) + .on("click.select2", killEvent); // do not leak click events + + this.container.data("select2", this); + + this.dropdown = this.container.find(".select2-drop"); + + syncCssClasses(this.dropdown, this.opts.element, this.opts.adaptDropdownCssClass); + + this.dropdown.addClass(evaluate(opts.dropdownCssClass, this.opts.element)); + this.dropdown.data("select2", this); + this.dropdown.on("click", killEvent); + + this.results = results = this.container.find(resultsSelector); + this.search = search = this.container.find("input.select2-input"); + + this.queryCount = 0; + this.resultsPage = 0; + this.context = null; + + // initialize the container + this.initContainer(); + + this.container.on("click", killEvent); + + installFilteredMouseMove(this.results); + + this.dropdown.on("mousemove-filtered", resultsSelector, this.bind(this.highlightUnderEvent)); + this.dropdown.on("touchstart touchmove touchend", resultsSelector, this.bind(function (event) { + this._touchEvent = true; + this.highlightUnderEvent(event); + })); + this.dropdown.on("touchmove", resultsSelector, this.bind(this.touchMoved)); + this.dropdown.on("touchstart touchend", resultsSelector, this.bind(this.clearTouchMoved)); + + // Waiting for a click event on touch devices to select option and hide dropdown + // otherwise click will be triggered on an underlying element + this.dropdown.on('click', this.bind(function (event) { + if (this._touchEvent) { + this._touchEvent = false; + this.selectHighlighted(); + } + })); + + installDebouncedScroll(80, this.results); + this.dropdown.on("scroll-debounced", resultsSelector, this.bind(this.loadMoreIfNeeded)); + + // do not propagate change event from the search field out of the component + $(this.container).on("change", ".select2-input", function(e) {e.stopPropagation();}); + $(this.dropdown).on("change", ".select2-input", function(e) {e.stopPropagation();}); + + // if jquery.mousewheel plugin is installed we can prevent out-of-bounds scrolling of results via mousewheel + if ($.fn.mousewheel) { + results.mousewheel(function (e, delta, deltaX, deltaY) { + var top = results.scrollTop(); + if (deltaY > 0 && top - deltaY <= 0) { + results.scrollTop(0); + killEvent(e); + } else if (deltaY < 0 && results.get(0).scrollHeight - results.scrollTop() + deltaY <= results.height()) { + results.scrollTop(results.get(0).scrollHeight - results.height()); + killEvent(e); + } + }); + } + + installKeyUpChangeEvent(search); + search.on("keyup-change input paste", this.bind(this.updateResults)); + search.on("focus", function () { search.addClass("select2-focused"); }); + search.on("blur", function () { search.removeClass("select2-focused");}); + + this.dropdown.on("mouseup", resultsSelector, this.bind(function (e) { + if ($(e.target).closest(".select2-result-selectable").length > 0) { + this.highlightUnderEvent(e); + this.selectHighlighted(e); + } + })); + + // trap all mouse events from leaving the dropdown. sometimes there may be a modal that is listening + // for mouse events outside of itself so it can close itself. since the dropdown is now outside the select2's + // dom it will trigger the popup close, which is not what we want + // focusin can cause focus wars between modals and select2 since the dropdown is outside the modal. + this.dropdown.on("click mouseup mousedown touchstart touchend focusin", function (e) { e.stopPropagation(); }); + + this.nextSearchTerm = undefined; + + if ($.isFunction(this.opts.initSelection)) { + // initialize selection based on the current value of the source element + this.initSelection(); + + // if the user has provided a function that can set selection based on the value of the source element + // we monitor the change event on the element and trigger it, allowing for two way synchronization + this.monitorSource(); + } + + if (opts.maximumInputLength !== null) { + this.search.attr("maxlength", opts.maximumInputLength); + } + + var disabled = opts.element.prop("disabled"); + if (disabled === undefined) disabled = false; + this.enable(!disabled); + + var readonly = opts.element.prop("readonly"); + if (readonly === undefined) readonly = false; + this.readonly(readonly); + + // Calculate size of scrollbar + scrollBarDimensions = scrollBarDimensions || measureScrollbar(); + + this.autofocus = opts.element.prop("autofocus"); + opts.element.prop("autofocus", false); + if (this.autofocus) this.focus(); + + this.search.attr("placeholder", opts.searchInputPlaceholder); + }, + + // abstract + destroy: function () { + var element=this.opts.element, select2 = element.data("select2"), self = this; + + this.close(); + + if (element.length && element[0].detachEvent) { + element.each(function () { + this.detachEvent("onpropertychange", self._sync); + }); + } + if (this.propertyObserver) { + this.propertyObserver.disconnect(); + this.propertyObserver = null; + } + this._sync = null; + + if (select2 !== undefined) { + select2.container.remove(); + select2.liveRegion.remove(); + select2.dropdown.remove(); + element + .removeClass("select2-offscreen") + .removeData("select2") + .off(".select2") + .prop("autofocus", this.autofocus || false); + if (this.elementTabIndex) { + element.attr({tabindex: this.elementTabIndex}); + } else { + element.removeAttr("tabindex"); + } + element.show(); + } + + cleanupJQueryElements.call(this, + "container", + "liveRegion", + "dropdown", + "results", + "search" + ); + }, + + // abstract + optionToData: function(element) { + if (element.is("option")) { + return { + id:element.prop("value"), + text:element.text(), + element: element.get(), + css: element.attr("class"), + disabled: element.prop("disabled"), + locked: equal(element.attr("locked"), "locked") || equal(element.data("locked"), true) + }; + } else if (element.is("optgroup")) { + return { + text:element.attr("label"), + children:[], + element: element.get(), + css: element.attr("class") + }; + } + }, + + // abstract + prepareOpts: function (opts) { + var element, select, idKey, ajaxUrl, self = this; + + element = opts.element; + + if (element.get(0).tagName.toLowerCase() === "select") { + this.select = select = opts.element; + } + + if (select) { + // these options are not allowed when attached to a select because they are picked up off the element itself + $.each(["id", "multiple", "ajax", "query", "createSearchChoice", "initSelection", "data", "tags"], function () { + if (this in opts) { + throw new Error("Option '" + this + "' is not allowed for Select2 when attached to a <select> element."); + } + }); + } + + opts = $.extend({}, { + populateResults: function(container, results, query) { + var populate, id=this.opts.id, liveRegion=this.liveRegion; + + populate=function(results, container, depth) { + + var i, l, result, selectable, disabled, compound, node, label, innerContainer, formatted; + + results = opts.sortResults(results, container, query); + + // collect the created nodes for bulk append + var nodes = []; + for (i = 0, l = results.length; i < l; i = i + 1) { + + result=results[i]; + + disabled = (result.disabled === true); + selectable = (!disabled) && (id(result) !== undefined); + + compound=result.children && result.children.length > 0; + + node=$("<li></li>"); + node.addClass("select2-results-dept-"+depth); + node.addClass("select2-result"); + node.addClass(selectable ? "select2-result-selectable" : "select2-result-unselectable"); + if (disabled) { node.addClass("select2-disabled"); } + if (compound) { node.addClass("select2-result-with-children"); } + node.addClass(self.opts.formatResultCssClass(result)); + node.attr("role", "presentation"); + + label=$(document.createElement("div")); + label.addClass("select2-result-label"); + label.attr("id", "select2-result-label-" + nextUid()); + label.attr("role", "option"); + + formatted=opts.formatResult(result, label, query, self.opts.escapeMarkup); + if (formatted!==undefined) { + label.html(formatted); + node.append(label); + } + + + if (compound) { + + innerContainer=$("<ul></ul>"); + innerContainer.addClass("select2-result-sub"); + populate(result.children, innerContainer, depth+1); + node.append(innerContainer); + } + + node.data("select2-data", result); + nodes.push(node[0]); + } + + // bulk append the created nodes + container.append(nodes); + liveRegion.text(opts.formatMatches(results.length)); + }; + + populate(results, container, 0); + } + }, $.fn.select2.defaults, opts); + + if (typeof(opts.id) !== "function") { + idKey = opts.id; + opts.id = function (e) { return e[idKey]; }; + } + + if ($.isArray(opts.element.data("select2Tags"))) { + if ("tags" in opts) { + throw "tags specified as both an attribute 'data-select2-tags' and in options of Select2 " + opts.element.attr("id"); + } + opts.tags=opts.element.data("select2Tags"); + } + + if (select) { + opts.query = this.bind(function (query) { + var data = { results: [], more: false }, + term = query.term, + children, placeholderOption, process; + + process=function(element, collection) { + var group; + if (element.is("option")) { + if (query.matcher(term, element.text(), element)) { + collection.push(self.optionToData(element)); + } + } else if (element.is("optgroup")) { + group=self.optionToData(element); + element.children().each2(function(i, elm) { process(elm, group.children); }); + if (group.children.length>0) { + collection.push(group); + } + } + }; + + children=element.children(); + + // ignore the placeholder option if there is one + if (this.getPlaceholder() !== undefined && children.length > 0) { + placeholderOption = this.getPlaceholderOption(); + if (placeholderOption) { + children=children.not(placeholderOption); + } + } + + children.each2(function(i, elm) { process(elm, data.results); }); + + query.callback(data); + }); + // this is needed because inside val() we construct choices from options and their id is hardcoded + opts.id=function(e) { return e.id; }; + } else { + if (!("query" in opts)) { + + if ("ajax" in opts) { + ajaxUrl = opts.element.data("ajax-url"); + if (ajaxUrl && ajaxUrl.length > 0) { + opts.ajax.url = ajaxUrl; + } + opts.query = ajax.call(opts.element, opts.ajax); + } else if ("data" in opts) { + opts.query = local(opts.data); + } else if ("tags" in opts) { + opts.query = tags(opts.tags); + if (opts.createSearchChoice === undefined) { + opts.createSearchChoice = function (term) { return {id: $.trim(term), text: $.trim(term)}; }; + } + if (opts.initSelection === undefined) { + opts.initSelection = function (element, callback) { + var data = []; + $(splitVal(element.val(), opts.separator)).each(function () { + var obj = { id: this, text: this }, + tags = opts.tags; + if ($.isFunction(tags)) tags=tags(); + $(tags).each(function() { if (equal(this.id, obj.id)) { obj = this; return false; } }); + data.push(obj); + }); + + callback(data); + }; + } + } + } + } + if (typeof(opts.query) !== "function") { + throw "query function not defined for Select2 " + opts.element.attr("id"); + } + + if (opts.createSearchChoicePosition === 'top') { + opts.createSearchChoicePosition = function(list, item) { list.unshift(item); }; + } + else if (opts.createSearchChoicePosition === 'bottom') { + opts.createSearchChoicePosition = function(list, item) { list.push(item); }; + } + else if (typeof(opts.createSearchChoicePosition) !== "function") { + throw "invalid createSearchChoicePosition option must be 'top', 'bottom' or a custom function"; + } + + return opts; + }, + + /** + * Monitor the original element for changes and update select2 accordingly + */ + // abstract + monitorSource: function () { + var el = this.opts.element, observer, self = this; + + el.on("change.select2", this.bind(function (e) { + if (this.opts.element.data("select2-change-triggered") !== true) { + this.initSelection(); + } + })); + + this._sync = this.bind(function () { + + // sync enabled state + var disabled = el.prop("disabled"); + if (disabled === undefined) disabled = false; + this.enable(!disabled); + + var readonly = el.prop("readonly"); + if (readonly === undefined) readonly = false; + this.readonly(readonly); + + syncCssClasses(this.container, this.opts.element, this.opts.adaptContainerCssClass); + this.container.addClass(evaluate(this.opts.containerCssClass, this.opts.element)); + + syncCssClasses(this.dropdown, this.opts.element, this.opts.adaptDropdownCssClass); + this.dropdown.addClass(evaluate(this.opts.dropdownCssClass, this.opts.element)); + + }); + + // IE8-10 (IE9/10 won't fire propertyChange via attachEventListener) + if (el.length && el[0].attachEvent) { + el.each(function() { + this.attachEvent("onpropertychange", self._sync); + }); + } + + // safari, chrome, firefox, IE11 + observer = window.MutationObserver || window.WebKitMutationObserver|| window.MozMutationObserver; + if (observer !== undefined) { + if (this.propertyObserver) { delete this.propertyObserver; this.propertyObserver = null; } + this.propertyObserver = new observer(function (mutations) { + $.each(mutations, self._sync); + }); + this.propertyObserver.observe(el.get(0), { attributes:true, subtree:false }); + } + }, + + // abstract + triggerSelect: function(data) { + var evt = $.Event("select2-selecting", { val: this.id(data), object: data, choice: data }); + this.opts.element.trigger(evt); + return !evt.isDefaultPrevented(); + }, + + /** + * Triggers the change event on the source element + */ + // abstract + triggerChange: function (details) { + + details = details || {}; + details= $.extend({}, details, { type: "change", val: this.val() }); + // prevents recursive triggering + this.opts.element.data("select2-change-triggered", true); + this.opts.element.trigger(details); + this.opts.element.data("select2-change-triggered", false); + + // some validation frameworks ignore the change event and listen instead to keyup, click for selects + // so here we trigger the click event manually + this.opts.element.click(); + + // ValidationEngine ignores the change event and listens instead to blur + // so here we trigger the blur event manually if so desired + if (this.opts.blurOnChange) + this.opts.element.blur(); + }, + + //abstract + isInterfaceEnabled: function() + { + return this.enabledInterface === true; + }, + + // abstract + enableInterface: function() { + var enabled = this._enabled && !this._readonly, + disabled = !enabled; + + if (enabled === this.enabledInterface) return false; + + this.container.toggleClass("select2-container-disabled", disabled); + this.close(); + this.enabledInterface = enabled; + + return true; + }, + + // abstract + enable: function(enabled) { + if (enabled === undefined) enabled = true; + if (this._enabled === enabled) return; + this._enabled = enabled; + + this.opts.element.prop("disabled", !enabled); + this.enableInterface(); + }, + + // abstract + disable: function() { + this.enable(false); + }, + + // abstract + readonly: function(enabled) { + if (enabled === undefined) enabled = false; + if (this._readonly === enabled) return; + this._readonly = enabled; + + this.opts.element.prop("readonly", enabled); + this.enableInterface(); + }, + + // abstract + opened: function () { + return (this.container) ? this.container.hasClass("select2-dropdown-open") : false; + }, + + // abstract + positionDropdown: function() { + var $dropdown = this.dropdown, + offset = this.container.offset(), + height = this.container.outerHeight(false), + width = this.container.outerWidth(false), + dropHeight = $dropdown.outerHeight(false), + $window = $(window), + windowWidth = $window.width(), + windowHeight = $window.height(), + viewPortRight = $window.scrollLeft() + windowWidth, + viewportBottom = $window.scrollTop() + windowHeight, + dropTop = offset.top + height, + dropLeft = offset.left, + enoughRoomBelow = dropTop + dropHeight <= viewportBottom, + enoughRoomAbove = (offset.top - dropHeight) >= $window.scrollTop(), + dropWidth = $dropdown.outerWidth(false), + enoughRoomOnRight = dropLeft + dropWidth <= viewPortRight, + aboveNow = $dropdown.hasClass("select2-drop-above"), + bodyOffset, + above, + changeDirection, + css, + resultsListNode; + + // always prefer the current above/below alignment, unless there is not enough room + if (aboveNow) { + above = true; + if (!enoughRoomAbove && enoughRoomBelow) { + changeDirection = true; + above = false; + } + } else { + above = false; + if (!enoughRoomBelow && enoughRoomAbove) { + changeDirection = true; + above = true; + } + } + + //if we are changing direction we need to get positions when dropdown is hidden; + if (changeDirection) { + $dropdown.hide(); + offset = this.container.offset(); + height = this.container.outerHeight(false); + width = this.container.outerWidth(false); + dropHeight = $dropdown.outerHeight(false); + viewPortRight = $window.scrollLeft() + windowWidth; + viewportBottom = $window.scrollTop() + windowHeight; + dropTop = offset.top + height; + dropLeft = offset.left; + dropWidth = $dropdown.outerWidth(false); + enoughRoomOnRight = dropLeft + dropWidth <= viewPortRight; + $dropdown.show(); + + // fix so the cursor does not move to the left within the search-textbox in IE + this.focusSearch(); + } + + if (this.opts.dropdownAutoWidth) { + resultsListNode = $('.select2-results', $dropdown)[0]; + $dropdown.addClass('select2-drop-auto-width'); + $dropdown.css('width', ''); + // Add scrollbar width to dropdown if vertical scrollbar is present + dropWidth = $dropdown.outerWidth(false) + (resultsListNode.scrollHeight === resultsListNode.clientHeight ? 0 : scrollBarDimensions.width); + dropWidth > width ? width = dropWidth : dropWidth = width; + dropHeight = $dropdown.outerHeight(false); + enoughRoomOnRight = dropLeft + dropWidth <= viewPortRight; + } + else { + this.container.removeClass('select2-drop-auto-width'); + } + + //console.log("below/ droptop:", dropTop, "dropHeight", dropHeight, "sum", (dropTop+dropHeight)+" viewport bottom", viewportBottom, "enough?", enoughRoomBelow); + //console.log("above/ offset.top", offset.top, "dropHeight", dropHeight, "top", (offset.top-dropHeight), "scrollTop", this.body.scrollTop(), "enough?", enoughRoomAbove); + + // fix positioning when body has an offset and is not position: static + if (this.body.css('position') !== 'static') { + bodyOffset = this.body.offset(); + dropTop -= bodyOffset.top; + dropLeft -= bodyOffset.left; + } + + if (!enoughRoomOnRight) { + dropLeft = offset.left + this.container.outerWidth(false) - dropWidth; + } + + css = { + left: dropLeft, + width: width + }; + + if (above) { + css.top = offset.top - dropHeight; + css.bottom = 'auto'; + this.container.addClass("select2-drop-above"); + $dropdown.addClass("select2-drop-above"); + } + else { + css.top = dropTop; + css.bottom = 'auto'; + this.container.removeClass("select2-drop-above"); + $dropdown.removeClass("select2-drop-above"); + } + css = $.extend(css, evaluate(this.opts.dropdownCss, this.opts.element)); + + $dropdown.css(css); + }, + + // abstract + shouldOpen: function() { + var event; + + if (this.opened()) return false; + + if (this._enabled === false || this._readonly === true) return false; + + event = $.Event("select2-opening"); + this.opts.element.trigger(event); + return !event.isDefaultPrevented(); + }, + + // abstract + clearDropdownAlignmentPreference: function() { + // clear the classes used to figure out the preference of where the dropdown should be opened + this.container.removeClass("select2-drop-above"); + this.dropdown.removeClass("select2-drop-above"); + }, + + /** + * Opens the dropdown + * + * @return {Boolean} whether or not dropdown was opened. This method will return false if, for example, + * the dropdown is already open, or if the 'open' event listener on the element called preventDefault(). + */ + // abstract + open: function () { + + if (!this.shouldOpen()) return false; + + this.opening(); + + // Only bind the document mousemove when the dropdown is visible + $document.on("mousemove.select2Event", function (e) { + lastMousePosition.x = e.pageX; + lastMousePosition.y = e.pageY; + }); + + return true; + }, + + /** + * Performs the opening of the dropdown + */ + // abstract + opening: function() { + var cid = this.containerEventName, + scroll = "scroll." + cid, + resize = "resize."+cid, + orient = "orientationchange."+cid, + mask; + + this.container.addClass("select2-dropdown-open").addClass("select2-container-active"); + + this.clearDropdownAlignmentPreference(); + + if(this.dropdown[0] !== this.body.children().last()[0]) { + this.dropdown.detach().appendTo(this.body); + } + + // create the dropdown mask if doesn't already exist + mask = $("#select2-drop-mask"); + if (mask.length == 0) { + mask = $(document.createElement("div")); + mask.attr("id","select2-drop-mask").attr("class","select2-drop-mask"); + mask.hide(); + mask.appendTo(this.body); + mask.on("mousedown touchstart click", function (e) { + // Prevent IE from generating a click event on the body + reinsertElement(mask); + + var dropdown = $("#select2-drop"), self; + if (dropdown.length > 0) { + self=dropdown.data("select2"); + if (self.opts.selectOnBlur) { + self.selectHighlighted({noFocus: true}); + } + self.close(); + e.preventDefault(); + e.stopPropagation(); + } + }); + } + + // ensure the mask is always right before the dropdown + if (this.dropdown.prev()[0] !== mask[0]) { + this.dropdown.before(mask); + } + + // move the global id to the correct dropdown + $("#select2-drop").removeAttr("id"); + this.dropdown.attr("id", "select2-drop"); + + // show the elements + mask.show(); + + this.positionDropdown(); + this.dropdown.show(); + this.positionDropdown(); + + this.dropdown.addClass("select2-drop-active"); + + // attach listeners to events that can change the position of the container and thus require + // the position of the dropdown to be updated as well so it does not come unglued from the container + var that = this; + this.container.parents().add(window).each(function () { + $(this).on(resize+" "+scroll+" "+orient, function (e) { + if (that.opened()) that.positionDropdown(); + }); + }); + + + }, + + // abstract + close: function () { + if (!this.opened()) return; + + var cid = this.containerEventName, + scroll = "scroll." + cid, + resize = "resize."+cid, + orient = "orientationchange."+cid; + + // unbind event listeners + this.container.parents().add(window).each(function () { $(this).off(scroll).off(resize).off(orient); }); + + this.clearDropdownAlignmentPreference(); + + $("#select2-drop-mask").hide(); + this.dropdown.removeAttr("id"); // only the active dropdown has the select2-drop id + this.dropdown.hide(); + this.container.removeClass("select2-dropdown-open").removeClass("select2-container-active"); + this.results.empty(); + + // Now that the dropdown is closed, unbind the global document mousemove event + $document.off("mousemove.select2Event"); + + this.clearSearch(); + this.search.removeClass("select2-active"); + this.opts.element.trigger($.Event("select2-close")); + }, + + /** + * Opens control, sets input value, and updates results. + */ + // abstract + externalSearch: function (term) { + this.open(); + this.search.val(term); + this.updateResults(false); + }, + + // abstract + clearSearch: function () { + + }, + + //abstract + getMaximumSelectionSize: function() { + return evaluate(this.opts.maximumSelectionSize, this.opts.element); + }, + + // abstract + ensureHighlightVisible: function () { + var results = this.results, children, index, child, hb, rb, y, more, topOffset; + + index = this.highlight(); + + if (index < 0) return; + + if (index == 0) { + + // if the first element is highlighted scroll all the way to the top, + // that way any unselectable headers above it will also be scrolled + // into view + + results.scrollTop(0); + return; + } + + children = this.findHighlightableChoices().find('.select2-result-label'); + + child = $(children[index]); + + topOffset = (child.offset() || {}).top || 0; + + hb = topOffset + child.outerHeight(true); + + // if this is the last child lets also make sure select2-more-results is visible + if (index === children.length - 1) { + more = results.find("li.select2-more-results"); + if (more.length > 0) { + hb = more.offset().top + more.outerHeight(true); + } + } + + rb = results.offset().top + results.outerHeight(true); + if (hb > rb) { + results.scrollTop(results.scrollTop() + (hb - rb)); + } + y = topOffset - results.offset().top; + + // make sure the top of the element is visible + if (y < 0 && child.css('display') != 'none' ) { + results.scrollTop(results.scrollTop() + y); // y is negative + } + }, + + // abstract + findHighlightableChoices: function() { + return this.results.find(".select2-result-selectable:not(.select2-disabled):not(.select2-selected)"); + }, + + // abstract + moveHighlight: function (delta) { + var choices = this.findHighlightableChoices(), + index = this.highlight(); + + while (index > -1 && index < choices.length) { + index += delta; + var choice = $(choices[index]); + if (choice.hasClass("select2-result-selectable") && !choice.hasClass("select2-disabled") && !choice.hasClass("select2-selected")) { + this.highlight(index); + break; + } + } + }, + + // abstract + highlight: function (index) { + var choices = this.findHighlightableChoices(), + choice, + data; + + if (arguments.length === 0) { + return indexOf(choices.filter(".select2-highlighted")[0], choices.get()); + } + + if (index >= choices.length) index = choices.length - 1; + if (index < 0) index = 0; + + this.removeHighlight(); + + choice = $(choices[index]); + choice.addClass("select2-highlighted"); + + // ensure assistive technology can determine the active choice + this.search.attr("aria-activedescendant", choice.find(".select2-result-label").attr("id")); + + this.ensureHighlightVisible(); + + this.liveRegion.text(choice.text()); + + data = choice.data("select2-data"); + if (data) { + this.opts.element.trigger({ type: "select2-highlight", val: this.id(data), choice: data }); + } + }, + + removeHighlight: function() { + this.results.find(".select2-highlighted").removeClass("select2-highlighted"); + }, + + touchMoved: function() { + this._touchMoved = true; + }, + + clearTouchMoved: function() { + this._touchMoved = false; + }, + + // abstract + countSelectableResults: function() { + return this.findHighlightableChoices().length; + }, + + // abstract + highlightUnderEvent: function (event) { + var el = $(event.target).closest(".select2-result-selectable"); + if (el.length > 0 && !el.is(".select2-highlighted")) { + var choices = this.findHighlightableChoices(); + this.highlight(choices.index(el)); + } else if (el.length == 0) { + // if we are over an unselectable item remove all highlights + this.removeHighlight(); + } + }, + + // abstract + loadMoreIfNeeded: function () { + var results = this.results, + more = results.find("li.select2-more-results"), + below, // pixels the element is below the scroll fold, below==0 is when the element is starting to be visible + page = this.resultsPage + 1, + self=this, + term=this.search.val(), + context=this.context; + + if (more.length === 0) return; + below = more.offset().top - results.offset().top - results.height(); + + if (below <= this.opts.loadMorePadding) { + more.addClass("select2-active"); + this.opts.query({ + element: this.opts.element, + term: term, + page: page, + context: context, + matcher: this.opts.matcher, + callback: this.bind(function (data) { + + // ignore a response if the select2 has been closed before it was received + if (!self.opened()) return; + + + self.opts.populateResults.call(this, results, data.results, {term: term, page: page, context:context}); + self.postprocessResults(data, false, false); + + if (data.more===true) { + more.detach().appendTo(results).text(evaluate(self.opts.formatLoadMore, self.opts.element, page+1)); + window.setTimeout(function() { self.loadMoreIfNeeded(); }, 10); + } else { + more.remove(); + } + self.positionDropdown(); + self.resultsPage = page; + self.context = data.context; + this.opts.element.trigger({ type: "select2-loaded", items: data }); + })}); + } + }, + + /** + * Default tokenizer function which does nothing + */ + tokenize: function() { + + }, + + /** + * @param initial whether or not this is the call to this method right after the dropdown has been opened + */ + // abstract + updateResults: function (initial) { + var search = this.search, + results = this.results, + opts = this.opts, + data, + self = this, + input, + term = search.val(), + lastTerm = $.data(this.container, "select2-last-term"), + // sequence number used to drop out-of-order responses + queryNumber; + + // prevent duplicate queries against the same term + if (initial !== true && lastTerm && equal(term, lastTerm)) return; + + $.data(this.container, "select2-last-term", term); + + // if the search is currently hidden we do not alter the results + if (initial !== true && (this.showSearchInput === false || !this.opened())) { + return; + } + + function postRender() { + search.removeClass("select2-active"); + self.positionDropdown(); + if (results.find('.select2-no-results,.select2-selection-limit,.select2-searching').length) { + self.liveRegion.text(results.text()); + } + else { + self.liveRegion.text(self.opts.formatMatches(results.find('.select2-result-selectable').length)); + } + } + + function render(html) { + results.html(html); + postRender(); + } + + queryNumber = ++this.queryCount; + + var maxSelSize = this.getMaximumSelectionSize(); + if (maxSelSize >=1) { + data = this.data(); + if ($.isArray(data) && data.length >= maxSelSize && checkFormatter(opts.formatSelectionTooBig, "formatSelectionTooBig")) { + render("<li class='select2-selection-limit'>" + evaluate(opts.formatSelectionTooBig, opts.element, maxSelSize) + "</li>"); + return; + } + } + + if (search.val().length < opts.minimumInputLength) { + if (checkFormatter(opts.formatInputTooShort, "formatInputTooShort")) { + render("<li class='select2-no-results'>" + evaluate(opts.formatInputTooShort, opts.element, search.val(), opts.minimumInputLength) + "</li>"); + } else { + render(""); + } + if (initial && this.showSearch) this.showSearch(true); + return; + } + + if (opts.maximumInputLength && search.val().length > opts.maximumInputLength) { + if (checkFormatter(opts.formatInputTooLong, "formatInputTooLong")) { + render("<li class='select2-no-results'>" + evaluate(opts.formatInputTooLong, opts.element, search.val(), opts.maximumInputLength) + "</li>"); + } else { + render(""); + } + return; + } + + if (opts.formatSearching && this.findHighlightableChoices().length === 0) { + render("<li class='select2-searching'>" + evaluate(opts.formatSearching, opts.element) + "</li>"); + } + + search.addClass("select2-active"); + + this.removeHighlight(); + + // give the tokenizer a chance to pre-process the input + input = this.tokenize(); + if (input != undefined && input != null) { + search.val(input); + } + + this.resultsPage = 1; + + opts.query({ + element: opts.element, + term: search.val(), + page: this.resultsPage, + context: null, + matcher: opts.matcher, + callback: this.bind(function (data) { + var def; // default choice + + // ignore old responses + if (queryNumber != this.queryCount) { + return; + } + + // ignore a response if the select2 has been closed before it was received + if (!this.opened()) { + this.search.removeClass("select2-active"); + return; + } + + // handle ajax error + if(data.hasError !== undefined && checkFormatter(opts.formatAjaxError, "formatAjaxError")) { + render("<li class='select2-ajax-error'>" + evaluate(opts.formatAjaxError, opts.element, data.jqXHR, data.textStatus, data.errorThrown) + "</li>"); + return; + } + + // save context, if any + this.context = (data.context===undefined) ? null : data.context; + // create a default choice and prepend it to the list + if (this.opts.createSearchChoice && search.val() !== "") { + def = this.opts.createSearchChoice.call(self, search.val(), data.results); + if (def !== undefined && def !== null && self.id(def) !== undefined && self.id(def) !== null) { + if ($(data.results).filter( + function () { + return equal(self.id(this), self.id(def)); + }).length === 0) { + this.opts.createSearchChoicePosition(data.results, def); + } + } + } + + if (data.results.length === 0 && checkFormatter(opts.formatNoMatches, "formatNoMatches")) { + render("<li class='select2-no-results'>" + evaluate(opts.formatNoMatches, opts.element, search.val()) + "</li>"); + return; + } + + results.empty(); + self.opts.populateResults.call(this, results, data.results, {term: search.val(), page: this.resultsPage, context:null}); + + if (data.more === true && checkFormatter(opts.formatLoadMore, "formatLoadMore")) { + results.append("<li class='select2-more-results'>" + opts.escapeMarkup(evaluate(opts.formatLoadMore, opts.element, this.resultsPage)) + "</li>"); + window.setTimeout(function() { self.loadMoreIfNeeded(); }, 10); + } + + this.postprocessResults(data, initial); + + postRender(); + + this.opts.element.trigger({ type: "select2-loaded", items: data }); + })}); + }, + + // abstract + cancel: function () { + this.close(); + }, + + // abstract + blur: function () { + // if selectOnBlur == true, select the currently highlighted option + if (this.opts.selectOnBlur) + this.selectHighlighted({noFocus: true}); + + this.close(); + this.container.removeClass("select2-container-active"); + // synonymous to .is(':focus'), which is available in jquery >= 1.6 + if (this.search[0] === document.activeElement) { this.search.blur(); } + this.clearSearch(); + this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus"); + }, + + // abstract + focusSearch: function () { + focus(this.search); + }, + + // abstract + selectHighlighted: function (options) { + if (this._touchMoved) { + this.clearTouchMoved(); + return; + } + var index=this.highlight(), + highlighted=this.results.find(".select2-highlighted"), + data = highlighted.closest('.select2-result').data("select2-data"); + + if (data) { + this.highlight(index); + this.onSelect(data, options); + } else if (options && options.noFocus) { + this.close(); + } + }, + + // abstract + getPlaceholder: function () { + var placeholderOption; + return this.opts.element.attr("placeholder") || + this.opts.element.attr("data-placeholder") || // jquery 1.4 compat + this.opts.element.data("placeholder") || + this.opts.placeholder || + ((placeholderOption = this.getPlaceholderOption()) !== undefined ? placeholderOption.text() : undefined); + }, + + // abstract + getPlaceholderOption: function() { + if (this.select) { + var firstOption = this.select.children('option').first(); + if (this.opts.placeholderOption !== undefined ) { + //Determine the placeholder option based on the specified placeholderOption setting + return (this.opts.placeholderOption === "first" && firstOption) || + (typeof this.opts.placeholderOption === "function" && this.opts.placeholderOption(this.select)); + } else if ($.trim(firstOption.text()) === "" && firstOption.val() === "") { + //No explicit placeholder option specified, use the first if it's blank + return firstOption; + } + } + }, + + /** + * Get the desired width for the container element. This is + * derived first from option `width` passed to select2, then + * the inline 'style' on the original element, and finally + * falls back to the jQuery calculated element width. + */ + // abstract + initContainerWidth: function () { + function resolveContainerWidth() { + var style, attrs, matches, i, l, attr; + + if (this.opts.width === "off") { + return null; + } else if (this.opts.width === "element"){ + return this.opts.element.outerWidth(false) === 0 ? 'auto' : this.opts.element.outerWidth(false) + 'px'; + } else if (this.opts.width === "copy" || this.opts.width === "resolve") { + // check if there is inline style on the element that contains width + style = this.opts.element.attr('style'); + if (style !== undefined) { + attrs = style.split(';'); + for (i = 0, l = attrs.length; i < l; i = i + 1) { + attr = attrs[i].replace(/\s/g, ''); + matches = attr.match(/^width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/i); + if (matches !== null && matches.length >= 1) + return matches[1]; + } + } + + if (this.opts.width === "resolve") { + // next check if css('width') can resolve a width that is percent based, this is sometimes possible + // when attached to input type=hidden or elements hidden via css + style = this.opts.element.css('width'); + if (style.indexOf("%") > 0) return style; + + // finally, fallback on the calculated width of the element + return (this.opts.element.outerWidth(false) === 0 ? 'auto' : this.opts.element.outerWidth(false) + 'px'); + } + + return null; + } else if ($.isFunction(this.opts.width)) { + return this.opts.width(); + } else { + return this.opts.width; + } + }; + + var width = resolveContainerWidth.call(this); + if (width !== null) { + this.container.css("width", width); + } + } + }); + + SingleSelect2 = clazz(AbstractSelect2, { + + // single + + createContainer: function () { + var container = $(document.createElement("div")).attr({ + "class": "select2-container" + }).html([ + "<a href='javascript:void(0)' class='select2-choice' tabindex='-1'>", + " <span class='select2-chosen'> </span><abbr class='select2-search-choice-close'></abbr>", + " <span class='select2-arrow' role='presentation'><b role='presentation'></b></span>", + "</a>", + "<label for='' class='select2-offscreen'></label>", + "<input class='select2-focusser select2-offscreen' type='text' aria-haspopup='true' role='button' />", + "<div class='select2-drop select2-display-none'>", + " <div class='select2-search'>", + " <label for='' class='select2-offscreen'></label>", + " <input type='text' autocomplete='off' autocorrect='off' autocapitalize='off' spellcheck='false' class='select2-input' role='combobox' aria-expanded='true'", + " aria-autocomplete='list' />", + " </div>", + " <ul class='select2-results' role='listbox'>", + " </ul>", + "</div>"].join("")); + return container; + }, + + // single + enableInterface: function() { + if (this.parent.enableInterface.apply(this, arguments)) { + this.focusser.prop("disabled", !this.isInterfaceEnabled()); + } + }, + + // single + opening: function () { + var el, range, len; + + if (this.opts.minimumResultsForSearch >= 0) { + this.showSearch(true); + } + + this.parent.opening.apply(this, arguments); + + if (this.showSearchInput !== false) { + // IE appends focusser.val() at the end of field :/ so we manually insert it at the beginning using a range + // all other browsers handle this just fine + + this.search.val(this.focusser.val()); + } + if (this.opts.shouldFocusInput(this)) { + this.search.focus(); + // move the cursor to the end after focussing, otherwise it will be at the beginning and + // new text will appear *before* focusser.val() + el = this.search.get(0); + if (el.createTextRange) { + range = el.createTextRange(); + range.collapse(false); + range.select(); + } else if (el.setSelectionRange) { + len = this.search.val().length; + el.setSelectionRange(len, len); + } + } + + // initializes search's value with nextSearchTerm (if defined by user) + // ignore nextSearchTerm if the dropdown is opened by the user pressing a letter + if(this.search.val() === "") { + if(this.nextSearchTerm != undefined){ + this.search.val(this.nextSearchTerm); + this.search.select(); + } + } + + this.focusser.prop("disabled", true).val(""); + this.updateResults(true); + this.opts.element.trigger($.Event("select2-open")); + }, + + // single + close: function () { + if (!this.opened()) return; + this.parent.close.apply(this, arguments); + + this.focusser.prop("disabled", false); + + if (this.opts.shouldFocusInput(this)) { + this.focusser.focus(); + } + }, + + // single + focus: function () { + if (this.opened()) { + this.close(); + } else { + this.focusser.prop("disabled", false); + if (this.opts.shouldFocusInput(this)) { + this.focusser.focus(); + } + } + }, + + // single + isFocused: function () { + return this.container.hasClass("select2-container-active"); + }, + + // single + cancel: function () { + this.parent.cancel.apply(this, arguments); + this.focusser.prop("disabled", false); + + if (this.opts.shouldFocusInput(this)) { + this.focusser.focus(); + } + }, + + // single + destroy: function() { + $("label[for='" + this.focusser.attr('id') + "']") + .attr('for', this.opts.element.attr("id")); + this.parent.destroy.apply(this, arguments); + + cleanupJQueryElements.call(this, + "selection", + "focusser" + ); + }, + + // single + initContainer: function () { + + var selection, + container = this.container, + dropdown = this.dropdown, + idSuffix = nextUid(), + elementLabel; + + if (this.opts.minimumResultsForSearch < 0) { + this.showSearch(false); + } else { + this.showSearch(true); + } + + this.selection = selection = container.find(".select2-choice"); + + this.focusser = container.find(".select2-focusser"); + + // add aria associations + selection.find(".select2-chosen").attr("id", "select2-chosen-"+idSuffix); + this.focusser.attr("aria-labelledby", "select2-chosen-"+idSuffix); + this.results.attr("id", "select2-results-"+idSuffix); + this.search.attr("aria-owns", "select2-results-"+idSuffix); + + // rewrite labels from original element to focusser + this.focusser.attr("id", "s2id_autogen"+idSuffix); + + elementLabel = $("label[for='" + this.opts.element.attr("id") + "']"); + + this.focusser.prev() + .text(elementLabel.text()) + .attr('for', this.focusser.attr('id')); + + // Ensure the original element retains an accessible name + var originalTitle = this.opts.element.attr("title"); + this.opts.element.attr("title", (originalTitle || elementLabel.text())); + + this.focusser.attr("tabindex", this.elementTabIndex); + + // write label for search field using the label from the focusser element + this.search.attr("id", this.focusser.attr('id') + '_search'); + + this.search.prev() + .text($("label[for='" + this.focusser.attr('id') + "']").text()) + .attr('for', this.search.attr('id')); + + this.search.on("keydown", this.bind(function (e) { + if (!this.isInterfaceEnabled()) return; + + // filter 229 keyCodes (input method editor is processing key input) + if (229 == e.keyCode) return; + + if (e.which === KEY.PAGE_UP || e.which === KEY.PAGE_DOWN) { + // prevent the page from scrolling + killEvent(e); + return; + } + + switch (e.which) { + case KEY.UP: + case KEY.DOWN: + this.moveHighlight((e.which === KEY.UP) ? -1 : 1); + killEvent(e); + return; + case KEY.ENTER: + this.selectHighlighted(); + killEvent(e); + return; + case KEY.TAB: + this.selectHighlighted({noFocus: true}); + return; + case KEY.ESC: + this.cancel(e); + killEvent(e); + return; + } + })); + + this.search.on("blur", this.bind(function(e) { + // a workaround for chrome to keep the search field focussed when the scroll bar is used to scroll the dropdown. + // without this the search field loses focus which is annoying + if (document.activeElement === this.body.get(0)) { + window.setTimeout(this.bind(function() { + if (this.opened()) { + this.search.focus(); + } + }), 0); + } + })); + + this.focusser.on("keydown", this.bind(function (e) { + if (!this.isInterfaceEnabled()) return; + + if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC) { + return; + } + + if (this.opts.openOnEnter === false && e.which === KEY.ENTER) { + killEvent(e); + return; + } + + if (e.which == KEY.DOWN || e.which == KEY.UP + || (e.which == KEY.ENTER && this.opts.openOnEnter)) { + + if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) return; + + this.open(); + killEvent(e); + return; + } + + if (e.which == KEY.DELETE || e.which == KEY.BACKSPACE) { + if (this.opts.allowClear) { + this.clear(); + } + killEvent(e); + return; + } + })); + + + installKeyUpChangeEvent(this.focusser); + this.focusser.on("keyup-change input", this.bind(function(e) { + if (this.opts.minimumResultsForSearch >= 0) { + e.stopPropagation(); + if (this.opened()) return; + this.open(); + } + })); + + selection.on("mousedown touchstart", "abbr", this.bind(function (e) { + if (!this.isInterfaceEnabled()) return; + this.clear(); + killEventImmediately(e); + this.close(); + this.selection.focus(); + })); + + selection.on("mousedown touchstart", this.bind(function (e) { + // Prevent IE from generating a click event on the body + reinsertElement(selection); + + if (!this.container.hasClass("select2-container-active")) { + this.opts.element.trigger($.Event("select2-focus")); + } + + if (this.opened()) { + this.close(); + } else if (this.isInterfaceEnabled()) { + this.open(); + } + + killEvent(e); + })); + + dropdown.on("mousedown touchstart", this.bind(function() { + if (this.opts.shouldFocusInput(this)) { + this.search.focus(); + } + })); + + selection.on("focus", this.bind(function(e) { + killEvent(e); + })); + + this.focusser.on("focus", this.bind(function(){ + if (!this.container.hasClass("select2-container-active")) { + this.opts.element.trigger($.Event("select2-focus")); + } + this.container.addClass("select2-container-active"); + })).on("blur", this.bind(function() { + if (!this.opened()) { + this.container.removeClass("select2-container-active"); + this.opts.element.trigger($.Event("select2-blur")); + } + })); + this.search.on("focus", this.bind(function(){ + if (!this.container.hasClass("select2-container-active")) { + this.opts.element.trigger($.Event("select2-focus")); + } + this.container.addClass("select2-container-active"); + })); + + this.initContainerWidth(); + this.opts.element.addClass("select2-offscreen"); + this.setPlaceholder(); + + }, + + // single + clear: function(triggerChange) { + var data=this.selection.data("select2-data"); + if (data) { // guard against queued quick consecutive clicks + var evt = $.Event("select2-clearing"); + this.opts.element.trigger(evt); + if (evt.isDefaultPrevented()) { + return; + } + var placeholderOption = this.getPlaceholderOption(); + this.opts.element.val(placeholderOption ? placeholderOption.val() : ""); + this.selection.find(".select2-chosen").empty(); + this.selection.removeData("select2-data"); + this.setPlaceholder(); + + if (triggerChange !== false){ + this.opts.element.trigger({ type: "select2-removed", val: this.id(data), choice: data }); + this.triggerChange({removed:data}); + } + } + }, + + /** + * Sets selection based on source element's value + */ + // single + initSelection: function () { + var selected; + if (this.isPlaceholderOptionSelected()) { + this.updateSelection(null); + this.close(); + this.setPlaceholder(); + } else { + var self = this; + this.opts.initSelection.call(null, this.opts.element, function(selected){ + if (selected !== undefined && selected !== null) { + self.updateSelection(selected); + self.close(); + self.setPlaceholder(); + self.nextSearchTerm = self.opts.nextSearchTerm(selected, self.search.val()); + } + }); + } + }, + + isPlaceholderOptionSelected: function() { + var placeholderOption; + if (this.getPlaceholder() === undefined) return false; // no placeholder specified so no option should be considered + return ((placeholderOption = this.getPlaceholderOption()) !== undefined && placeholderOption.prop("selected")) + || (this.opts.element.val() === "") + || (this.opts.element.val() === undefined) + || (this.opts.element.val() === null); + }, + + // single + prepareOpts: function () { + var opts = this.parent.prepareOpts.apply(this, arguments), + self=this; + + if (opts.element.get(0).tagName.toLowerCase() === "select") { + // install the selection initializer + opts.initSelection = function (element, callback) { + var selected = element.find("option").filter(function() { return this.selected && !this.disabled }); + // a single select box always has a value, no need to null check 'selected' + callback(self.optionToData(selected)); + }; + } else if ("data" in opts) { + // install default initSelection when applied to hidden input and data is local + opts.initSelection = opts.initSelection || function (element, callback) { + var id = element.val(); + //search in data by id, storing the actual matching item + var match = null; + opts.query({ + matcher: function(term, text, el){ + var is_match = equal(id, opts.id(el)); + if (is_match) { + match = el; + } + return is_match; + }, + callback: !$.isFunction(callback) ? $.noop : function() { + callback(match); + } + }); + }; + } + + return opts; + }, + + // single + getPlaceholder: function() { + // if a placeholder is specified on a single select without a valid placeholder option ignore it + if (this.select) { + if (this.getPlaceholderOption() === undefined) { + return undefined; + } + } + + return this.parent.getPlaceholder.apply(this, arguments); + }, + + // single + setPlaceholder: function () { + var placeholder = this.getPlaceholder(); + + if (this.isPlaceholderOptionSelected() && placeholder !== undefined) { + + // check for a placeholder option if attached to a select + if (this.select && this.getPlaceholderOption() === undefined) return; + + this.selection.find(".select2-chosen").html(this.opts.escapeMarkup(placeholder)); + + this.selection.addClass("select2-default"); + + this.container.removeClass("select2-allowclear"); + } + }, + + // single + postprocessResults: function (data, initial, noHighlightUpdate) { + var selected = 0, self = this, showSearchInput = true; + + // find the selected element in the result list + + this.findHighlightableChoices().each2(function (i, elm) { + if (equal(self.id(elm.data("select2-data")), self.opts.element.val())) { + selected = i; + return false; + } + }); + + // and highlight it + if (noHighlightUpdate !== false) { + if (initial === true && selected >= 0) { + this.highlight(selected); + } else { + this.highlight(0); + } + } + + // hide the search box if this is the first we got the results and there are enough of them for search + + if (initial === true) { + var min = this.opts.minimumResultsForSearch; + if (min >= 0) { + this.showSearch(countResults(data.results) >= min); + } + } + }, + + // single + showSearch: function(showSearchInput) { + if (this.showSearchInput === showSearchInput) return; + + this.showSearchInput = showSearchInput; + + this.dropdown.find(".select2-search").toggleClass("select2-search-hidden", !showSearchInput); + this.dropdown.find(".select2-search").toggleClass("select2-offscreen", !showSearchInput); + //add "select2-with-searchbox" to the container if search box is shown + $(this.dropdown, this.container).toggleClass("select2-with-searchbox", showSearchInput); + }, + + // single + onSelect: function (data, options) { + + if (!this.triggerSelect(data)) { return; } + + var old = this.opts.element.val(), + oldData = this.data(); + + this.opts.element.val(this.id(data)); + this.updateSelection(data); + + this.opts.element.trigger({ type: "select2-selected", val: this.id(data), choice: data }); + + this.nextSearchTerm = this.opts.nextSearchTerm(data, this.search.val()); + this.close(); + + if ((!options || !options.noFocus) && this.opts.shouldFocusInput(this)) { + this.focusser.focus(); + } + + if (!equal(old, this.id(data))) { + this.triggerChange({ added: data, removed: oldData }); + } + }, + + // single + updateSelection: function (data) { + + var container=this.selection.find(".select2-chosen"), formatted, cssClass; + + this.selection.data("select2-data", data); + + container.empty(); + if (data !== null) { + formatted=this.opts.formatSelection(data, container, this.opts.escapeMarkup); + } + if (formatted !== undefined) { + container.append(formatted); + } + cssClass=this.opts.formatSelectionCssClass(data, container); + if (cssClass !== undefined) { + container.addClass(cssClass); + } + + this.selection.removeClass("select2-default"); + + if (this.opts.allowClear && this.getPlaceholder() !== undefined) { + this.container.addClass("select2-allowclear"); + } + }, + + // single + val: function () { + var val, + triggerChange = false, + data = null, + self = this, + oldData = this.data(); + + if (arguments.length === 0) { + return this.opts.element.val(); + } + + val = arguments[0]; + + if (arguments.length > 1) { + triggerChange = arguments[1]; + } + + if (this.select) { + this.select + .val(val) + .find("option").filter(function() { return this.selected }).each2(function (i, elm) { + data = self.optionToData(elm); + return false; + }); + this.updateSelection(data); + this.setPlaceholder(); + if (triggerChange) { + this.triggerChange({added: data, removed:oldData}); + } + } else { + // val is an id. !val is true for [undefined,null,'',0] - 0 is legal + if (!val && val !== 0) { + this.clear(triggerChange); + return; + } + if (this.opts.initSelection === undefined) { + throw new Error("cannot call val() if initSelection() is not defined"); + } + this.opts.element.val(val); + this.opts.initSelection(this.opts.element, function(data){ + self.opts.element.val(!data ? "" : self.id(data)); + self.updateSelection(data); + self.setPlaceholder(); + if (triggerChange) { + self.triggerChange({added: data, removed:oldData}); + } + }); + } + }, + + // single + clearSearch: function () { + this.search.val(""); + this.focusser.val(""); + }, + + // single + data: function(value) { + var data, + triggerChange = false; + + if (arguments.length === 0) { + data = this.selection.data("select2-data"); + if (data == undefined) data = null; + return data; + } else { + if (arguments.length > 1) { + triggerChange = arguments[1]; + } + if (!value) { + this.clear(triggerChange); + } else { + data = this.data(); + this.opts.element.val(!value ? "" : this.id(value)); + this.updateSelection(value); + if (triggerChange) { + this.triggerChange({added: value, removed:data}); + } + } + } + } + }); + + MultiSelect2 = clazz(AbstractSelect2, { + + // multi + createContainer: function () { + var container = $(document.createElement("div")).attr({ + "class": "select2-container select2-container-multi" + }).html([ + "<ul class='select2-choices'>", + " <li class='select2-search-field'>", + " <label for='' class='select2-offscreen'></label>", + " <input type='text' autocomplete='off' autocorrect='off' autocapitalize='off' spellcheck='false' class='select2-input'>", + " </li>", + "</ul>", + "<div class='select2-drop select2-drop-multi select2-display-none'>", + " <ul class='select2-results'>", + " </ul>", + "</div>"].join("")); + return container; + }, + + // multi + prepareOpts: function () { + var opts = this.parent.prepareOpts.apply(this, arguments), + self=this; + + // TODO validate placeholder is a string if specified + + if (opts.element.get(0).tagName.toLowerCase() === "select") { + // install the selection initializer + opts.initSelection = function (element, callback) { + + var data = []; + + element.find("option").filter(function() { return this.selected && !this.disabled }).each2(function (i, elm) { + data.push(self.optionToData(elm)); + }); + callback(data); + }; + } else if ("data" in opts) { + // install default initSelection when applied to hidden input and data is local + opts.initSelection = opts.initSelection || function (element, callback) { + var ids = splitVal(element.val(), opts.separator); + //search in data by array of ids, storing matching items in a list + var matches = []; + opts.query({ + matcher: function(term, text, el){ + var is_match = $.grep(ids, function(id) { + return equal(id, opts.id(el)); + }).length; + if (is_match) { + matches.push(el); + } + return is_match; + }, + callback: !$.isFunction(callback) ? $.noop : function() { + // reorder matches based on the order they appear in the ids array because right now + // they are in the order in which they appear in data array + var ordered = []; + for (var i = 0; i < ids.length; i++) { + var id = ids[i]; + for (var j = 0; j < matches.length; j++) { + var match = matches[j]; + if (equal(id, opts.id(match))) { + ordered.push(match); + matches.splice(j, 1); + break; + } + } + } + callback(ordered); + } + }); + }; + } + + return opts; + }, + + // multi + selectChoice: function (choice) { + + var selected = this.container.find(".select2-search-choice-focus"); + if (selected.length && choice && choice[0] == selected[0]) { + + } else { + if (selected.length) { + this.opts.element.trigger("choice-deselected", selected); + } + selected.removeClass("select2-search-choice-focus"); + if (choice && choice.length) { + this.close(); + choice.addClass("select2-search-choice-focus"); + this.opts.element.trigger("choice-selected", choice); + } + } + }, + + // multi + destroy: function() { + $("label[for='" + this.search.attr('id') + "']") + .attr('for', this.opts.element.attr("id")); + this.parent.destroy.apply(this, arguments); + + cleanupJQueryElements.call(this, + "searchContainer", + "selection" + ); + }, + + // multi + initContainer: function () { + + var selector = ".select2-choices", selection; + + this.searchContainer = this.container.find(".select2-search-field"); + this.selection = selection = this.container.find(selector); + + var _this = this; + this.selection.on("click", ".select2-search-choice:not(.select2-locked)", function (e) { + //killEvent(e); + _this.search[0].focus(); + _this.selectChoice($(this)); + }); + + // rewrite labels from original element to focusser + this.search.attr("id", "s2id_autogen"+nextUid()); + + this.search.prev() + .text($("label[for='" + this.opts.element.attr("id") + "']").text()) + .attr('for', this.search.attr('id')); + + this.search.on("input paste", this.bind(function() { + if (this.search.attr('placeholder') && this.search.val().length == 0) return; + if (!this.isInterfaceEnabled()) return; + if (!this.opened()) { + this.open(); + } + })); + + this.search.attr("tabindex", this.elementTabIndex); + + this.keydowns = 0; + this.search.on("keydown", this.bind(function (e) { + if (!this.isInterfaceEnabled()) return; + + ++this.keydowns; + var selected = selection.find(".select2-search-choice-focus"); + var prev = selected.prev(".select2-search-choice:not(.select2-locked)"); + var next = selected.next(".select2-search-choice:not(.select2-locked)"); + var pos = getCursorInfo(this.search); + + if (selected.length && + (e.which == KEY.LEFT || e.which == KEY.RIGHT || e.which == KEY.BACKSPACE || e.which == KEY.DELETE || e.which == KEY.ENTER)) { + var selectedChoice = selected; + if (e.which == KEY.LEFT && prev.length) { + selectedChoice = prev; + } + else if (e.which == KEY.RIGHT) { + selectedChoice = next.length ? next : null; + } + else if (e.which === KEY.BACKSPACE) { + if (this.unselect(selected.first())) { + this.search.width(10); + selectedChoice = prev.length ? prev : next; + } + } else if (e.which == KEY.DELETE) { + if (this.unselect(selected.first())) { + this.search.width(10); + selectedChoice = next.length ? next : null; + } + } else if (e.which == KEY.ENTER) { + selectedChoice = null; + } + + this.selectChoice(selectedChoice); + killEvent(e); + if (!selectedChoice || !selectedChoice.length) { + this.open(); + } + return; + } else if (((e.which === KEY.BACKSPACE && this.keydowns == 1) + || e.which == KEY.LEFT) && (pos.offset == 0 && !pos.length)) { + + this.selectChoice(selection.find(".select2-search-choice:not(.select2-locked)").last()); + killEvent(e); + return; + } else { + this.selectChoice(null); + } + + if (this.opened()) { + switch (e.which) { + case KEY.UP: + case KEY.DOWN: + this.moveHighlight((e.which === KEY.UP) ? -1 : 1); + killEvent(e); + return; + case KEY.ENTER: + this.selectHighlighted(); + killEvent(e); + return; + case KEY.TAB: + this.selectHighlighted({noFocus:true}); + this.close(); + return; + case KEY.ESC: + this.cancel(e); + killEvent(e); + return; + } + } + + if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) + || e.which === KEY.BACKSPACE || e.which === KEY.ESC) { + return; + } + + if (e.which === KEY.ENTER) { + if (this.opts.openOnEnter === false) { + return; + } else if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) { + return; + } + } + + this.open(); + + if (e.which === KEY.PAGE_UP || e.which === KEY.PAGE_DOWN) { + // prevent the page from scrolling + killEvent(e); + } + + if (e.which === KEY.ENTER) { + // prevent form from being submitted + killEvent(e); + } + + })); + + this.search.on("keyup", this.bind(function (e) { + this.keydowns = 0; + this.resizeSearch(); + }) + ); + + this.search.on("blur", this.bind(function(e) { + this.container.removeClass("select2-container-active"); + this.search.removeClass("select2-focused"); + this.selectChoice(null); + if (!this.opened()) this.clearSearch(); + e.stopImmediatePropagation(); + this.opts.element.trigger($.Event("select2-blur")); + })); + + this.container.on("click", selector, this.bind(function (e) { + if (!this.isInterfaceEnabled()) return; + if ($(e.target).closest(".select2-search-choice").length > 0) { + // clicked inside a select2 search choice, do not open + return; + } + this.selectChoice(null); + this.clearPlaceholder(); + if (!this.container.hasClass("select2-container-active")) { + this.opts.element.trigger($.Event("select2-focus")); + } + this.open(); + this.focusSearch(); + e.preventDefault(); + })); + + this.container.on("focus", selector, this.bind(function () { + if (!this.isInterfaceEnabled()) return; + if (!this.container.hasClass("select2-container-active")) { + this.opts.element.trigger($.Event("select2-focus")); + } + this.container.addClass("select2-container-active"); + this.dropdown.addClass("select2-drop-active"); + this.clearPlaceholder(); + })); + + this.initContainerWidth(); + this.opts.element.addClass("select2-offscreen"); + + // set the placeholder if necessary + this.clearSearch(); + }, + + // multi + enableInterface: function() { + if (this.parent.enableInterface.apply(this, arguments)) { + this.search.prop("disabled", !this.isInterfaceEnabled()); + } + }, + + // multi + initSelection: function () { + var data; + if (this.opts.element.val() === "" && this.opts.element.text() === "") { + this.updateSelection([]); + this.close(); + // set the placeholder if necessary + this.clearSearch(); + } + if (this.select || this.opts.element.val() !== "") { + var self = this; + this.opts.initSelection.call(null, this.opts.element, function(data){ + if (data !== undefined && data !== null) { + self.updateSelection(data); + self.close(); + // set the placeholder if necessary + self.clearSearch(); + } + }); + } + }, + + // multi + clearSearch: function () { + var placeholder = this.getPlaceholder(), + maxWidth = this.getMaxSearchWidth(); + + if (placeholder !== undefined && this.getVal().length === 0 && this.search.hasClass("select2-focused") === false) { + this.search.val(placeholder).addClass("select2-default"); + // stretch the search box to full width of the container so as much of the placeholder is visible as possible + // we could call this.resizeSearch(), but we do not because that requires a sizer and we do not want to create one so early because of a firefox bug, see #944 + this.search.width(maxWidth > 0 ? maxWidth : this.container.css("width")); + } else { + this.search.val("").width(10); + } + }, + + // multi + clearPlaceholder: function () { + if (this.search.hasClass("select2-default")) { + this.search.val("").removeClass("select2-default"); + } + }, + + // multi + opening: function () { + this.clearPlaceholder(); // should be done before super so placeholder is not used to search + this.resizeSearch(); + + this.parent.opening.apply(this, arguments); + + this.focusSearch(); + + // initializes search's value with nextSearchTerm (if defined by user) + // ignore nextSearchTerm if the dropdown is opened by the user pressing a letter + if(this.search.val() === "") { + if(this.nextSearchTerm != undefined){ + this.search.val(this.nextSearchTerm); + this.search.select(); + } + } + + this.updateResults(true); + if (this.opts.shouldFocusInput(this)) { + this.search.focus(); + } + this.opts.element.trigger($.Event("select2-open")); + }, + + // multi + close: function () { + if (!this.opened()) return; + this.parent.close.apply(this, arguments); + }, + + // multi + focus: function () { + this.close(); + this.search.focus(); + }, + + // multi + isFocused: function () { + return this.search.hasClass("select2-focused"); + }, + + // multi + updateSelection: function (data) { + var ids = [], filtered = [], self = this; + + // filter out duplicates + $(data).each(function () { + if (indexOf(self.id(this), ids) < 0) { + ids.push(self.id(this)); + filtered.push(this); + } + }); + data = filtered; + + this.selection.find(".select2-search-choice").remove(); + $(data).each(function () { + self.addSelectedChoice(this); + }); + self.postprocessResults(); + }, + + // multi + tokenize: function() { + var input = this.search.val(); + input = this.opts.tokenizer.call(this, input, this.data(), this.bind(this.onSelect), this.opts); + if (input != null && input != undefined) { + this.search.val(input); + if (input.length > 0) { + this.open(); + } + } + + }, + + // multi + onSelect: function (data, options) { + + if (!this.triggerSelect(data) || data.text === "") { return; } + + this.addSelectedChoice(data); + + this.opts.element.trigger({ type: "selected", val: this.id(data), choice: data }); + + // keep track of the search's value before it gets cleared + this.nextSearchTerm = this.opts.nextSearchTerm(data, this.search.val()); + + this.clearSearch(); + this.updateResults(); + + if (this.select || !this.opts.closeOnSelect) this.postprocessResults(data, false, this.opts.closeOnSelect===true); + + if (this.opts.closeOnSelect) { + this.close(); + this.search.width(10); + } else { + if (this.countSelectableResults()>0) { + this.search.width(10); + this.resizeSearch(); + if (this.getMaximumSelectionSize() > 0 && this.val().length >= this.getMaximumSelectionSize()) { + // if we reached max selection size repaint the results so choices + // are replaced with the max selection reached message + this.updateResults(true); + } else { + // initializes search's value with nextSearchTerm and update search result + if(this.nextSearchTerm != undefined){ + this.search.val(this.nextSearchTerm); + this.updateResults(); + this.search.select(); + } + } + this.positionDropdown(); + } else { + // if nothing left to select close + this.close(); + this.search.width(10); + } + } + + // since its not possible to select an element that has already been + // added we do not need to check if this is a new element before firing change + this.triggerChange({ added: data }); + + if (!options || !options.noFocus) + this.focusSearch(); + }, + + // multi + cancel: function () { + this.close(); + this.focusSearch(); + }, + + addSelectedChoice: function (data) { + var enableChoice = !data.locked, + enabledItem = $( + "<li class='select2-search-choice'>" + + " <div></div>" + + " <a href='#' class='select2-search-choice-close' tabindex='-1'></a>" + + "</li>"), + disabledItem = $( + "<li class='select2-search-choice select2-locked'>" + + "<div></div>" + + "</li>"); + var choice = enableChoice ? enabledItem : disabledItem, + id = this.id(data), + val = this.getVal(), + formatted, + cssClass; + + formatted=this.opts.formatSelection(data, choice.find("div"), this.opts.escapeMarkup); + if (formatted != undefined) { + choice.find("div").replaceWith("<div>"+formatted+"</div>"); + } + cssClass=this.opts.formatSelectionCssClass(data, choice.find("div")); + if (cssClass != undefined) { + choice.addClass(cssClass); + } + + if(enableChoice){ + choice.find(".select2-search-choice-close") + .on("mousedown", killEvent) + .on("click dblclick", this.bind(function (e) { + if (!this.isInterfaceEnabled()) return; + + this.unselect($(e.target)); + this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus"); + killEvent(e); + this.close(); + this.focusSearch(); + })).on("focus", this.bind(function () { + if (!this.isInterfaceEnabled()) return; + this.container.addClass("select2-container-active"); + this.dropdown.addClass("select2-drop-active"); + })); + } + + choice.data("select2-data", data); + choice.insertBefore(this.searchContainer); + + val.push(id); + this.setVal(val); + }, + + // multi + unselect: function (selected) { + var val = this.getVal(), + data, + index; + selected = selected.closest(".select2-search-choice"); + + if (selected.length === 0) { + throw "Invalid argument: " + selected + ". Must be .select2-search-choice"; + } + + data = selected.data("select2-data"); + + if (!data) { + // prevent a race condition when the 'x' is clicked really fast repeatedly the event can be queued + // and invoked on an element already removed + return; + } + + var evt = $.Event("select2-removing"); + evt.val = this.id(data); + evt.choice = data; + this.opts.element.trigger(evt); + + if (evt.isDefaultPrevented()) { + return false; + } + + while((index = indexOf(this.id(data), val)) >= 0) { + val.splice(index, 1); + this.setVal(val); + if (this.select) this.postprocessResults(); + } + + selected.remove(); + + this.opts.element.trigger({ type: "select2-removed", val: this.id(data), choice: data }); + this.triggerChange({ removed: data }); + + return true; + }, + + // multi + postprocessResults: function (data, initial, noHighlightUpdate) { + var val = this.getVal(), + choices = this.results.find(".select2-result"), + compound = this.results.find(".select2-result-with-children"), + self = this; + + choices.each2(function (i, choice) { + var id = self.id(choice.data("select2-data")); + if (indexOf(id, val) >= 0) { + choice.addClass("select2-selected"); + // mark all children of the selected parent as selected + choice.find(".select2-result-selectable").addClass("select2-selected"); + } + }); + + compound.each2(function(i, choice) { + // hide an optgroup if it doesn't have any selectable children + if (!choice.is('.select2-result-selectable') + && choice.find(".select2-result-selectable:not(.select2-selected)").length === 0) { + choice.addClass("select2-selected"); + } + }); + + if (this.highlight() == -1 && noHighlightUpdate !== false){ + self.highlight(0); + } + + //If all results are chosen render formatNoMatches + if(!this.opts.createSearchChoice && !choices.filter('.select2-result:not(.select2-selected)').length > 0){ + if(!data || data && !data.more && this.results.find(".select2-no-results").length === 0) { + if (checkFormatter(self.opts.formatNoMatches, "formatNoMatches")) { + this.results.append("<li class='select2-no-results'>" + evaluate(self.opts.formatNoMatches, self.opts.element, self.search.val()) + "</li>"); + } + } + } + + }, + + // multi + getMaxSearchWidth: function() { + return this.selection.width() - getSideBorderPadding(this.search); + }, + + // multi + resizeSearch: function () { + var minimumWidth, left, maxWidth, containerLeft, searchWidth, + sideBorderPadding = getSideBorderPadding(this.search); + + minimumWidth = measureTextWidth(this.search) + 10; + + left = this.search.offset().left; + + maxWidth = this.selection.width(); + containerLeft = this.selection.offset().left; + + searchWidth = maxWidth - (left - containerLeft) - sideBorderPadding; + + if (searchWidth < minimumWidth) { + searchWidth = maxWidth - sideBorderPadding; + } + + if (searchWidth < 40) { + searchWidth = maxWidth - sideBorderPadding; + } + + if (searchWidth <= 0) { + searchWidth = minimumWidth; + } + + this.search.width(Math.floor(searchWidth)); + }, + + // multi + getVal: function () { + var val; + if (this.select) { + val = this.select.val(); + return val === null ? [] : val; + } else { + val = this.opts.element.val(); + return splitVal(val, this.opts.separator); + } + }, + + // multi + setVal: function (val) { + var unique; + if (this.select) { + this.select.val(val); + } else { + unique = []; + // filter out duplicates + $(val).each(function () { + if (indexOf(this, unique) < 0) unique.push(this); + }); + this.opts.element.val(unique.length === 0 ? "" : unique.join(this.opts.separator)); + } + }, + + // multi + buildChangeDetails: function (old, current) { + var current = current.slice(0), + old = old.slice(0); + + // remove intersection from each array + for (var i = 0; i < current.length; i++) { + for (var j = 0; j < old.length; j++) { + if (equal(this.opts.id(current[i]), this.opts.id(old[j]))) { + current.splice(i, 1); + if(i>0){ + i--; + } + old.splice(j, 1); + j--; + } + } + } + + return {added: current, removed: old}; + }, + + + // multi + val: function (val, triggerChange) { + var oldData, self=this; + + if (arguments.length === 0) { + return this.getVal(); + } + + oldData=this.data(); + if (!oldData.length) oldData=[]; + + // val is an id. !val is true for [undefined,null,'',0] - 0 is legal + if (!val && val !== 0) { + this.opts.element.val(""); + this.updateSelection([]); + this.clearSearch(); + if (triggerChange) { + this.triggerChange({added: this.data(), removed: oldData}); + } + return; + } + + // val is a list of ids + this.setVal(val); + + if (this.select) { + this.opts.initSelection(this.select, this.bind(this.updateSelection)); + if (triggerChange) { + this.triggerChange(this.buildChangeDetails(oldData, this.data())); + } + } else { + if (this.opts.initSelection === undefined) { + throw new Error("val() cannot be called if initSelection() is not defined"); + } + + this.opts.initSelection(this.opts.element, function(data){ + var ids=$.map(data, self.id); + self.setVal(ids); + self.updateSelection(data); + self.clearSearch(); + if (triggerChange) { + self.triggerChange(self.buildChangeDetails(oldData, self.data())); + } + }); + } + this.clearSearch(); + }, + + // multi + onSortStart: function() { + if (this.select) { + throw new Error("Sorting of elements is not supported when attached to <select>. Attach to <input type='hidden'/> instead."); + } + + // collapse search field into 0 width so its container can be collapsed as well + this.search.width(0); + // hide the container + this.searchContainer.hide(); + }, + + // multi + onSortEnd:function() { + + var val=[], self=this; + + // show search and move it to the end of the list + this.searchContainer.show(); + // make sure the search container is the last item in the list + this.searchContainer.appendTo(this.searchContainer.parent()); + // since we collapsed the width in dragStarted, we resize it here + this.resizeSearch(); + + // update selection + this.selection.find(".select2-search-choice").each(function() { + val.push(self.opts.id($(this).data("select2-data"))); + }); + this.setVal(val); + this.triggerChange(); + }, + + // multi + data: function(values, triggerChange) { + var self=this, ids, old; + if (arguments.length === 0) { + return this.selection + .children(".select2-search-choice") + .map(function() { return $(this).data("select2-data"); }) + .get(); + } else { + old = this.data(); + if (!values) { values = []; } + ids = $.map(values, function(e) { return self.opts.id(e); }); + this.setVal(ids); + this.updateSelection(values); + this.clearSearch(); + if (triggerChange) { + this.triggerChange(this.buildChangeDetails(old, this.data())); + } + } + } + }); + + $.fn.select2 = function () { + + var args = Array.prototype.slice.call(arguments, 0), + opts, + select2, + method, value, multiple, + allowedMethods = ["val", "destroy", "opened", "open", "close", "focus", "isFocused", "container", "dropdown", "onSortStart", "onSortEnd", "enable", "disable", "readonly", "positionDropdown", "data", "search"], + valueMethods = ["opened", "isFocused", "container", "dropdown"], + propertyMethods = ["val", "data"], + methodsMap = { search: "externalSearch" }; + + this.each(function () { + if (args.length === 0 || typeof(args[0]) === "object") { + opts = args.length === 0 ? {} : $.extend({}, args[0]); + opts.element = $(this); + + if (opts.element.get(0).tagName.toLowerCase() === "select") { + multiple = opts.element.prop("multiple"); + } else { + multiple = opts.multiple || false; + if ("tags" in opts) {opts.multiple = multiple = true;} + } + + select2 = multiple ? new window.Select2["class"].multi() : new window.Select2["class"].single(); + select2.init(opts); + } else if (typeof(args[0]) === "string") { + + if (indexOf(args[0], allowedMethods) < 0) { + throw "Unknown method: " + args[0]; + } + + value = undefined; + select2 = $(this).data("select2"); + if (select2 === undefined) return; + + method=args[0]; + + if (method === "container") { + value = select2.container; + } else if (method === "dropdown") { + value = select2.dropdown; + } else { + if (methodsMap[method]) method = methodsMap[method]; + + value = select2[method].apply(select2, args.slice(1)); + } + if (indexOf(args[0], valueMethods) >= 0 + || (indexOf(args[0], propertyMethods) >= 0 && args.length == 1)) { + return false; // abort the iteration, ready to return first matched value + } + } else { + throw "Invalid arguments to select2 plugin: " + args; + } + }); + return (value === undefined) ? this : value; + }; + + // plugin defaults, accessible to users + $.fn.select2.defaults = { + width: "copy", + loadMorePadding: 0, + closeOnSelect: true, + openOnEnter: true, + containerCss: {}, + dropdownCss: {}, + containerCssClass: "", + dropdownCssClass: "", + formatResult: function(result, container, query, escapeMarkup) { + var markup=[]; + markMatch(result.text, query.term, markup, escapeMarkup); + return markup.join(""); + }, + formatSelection: function (data, container, escapeMarkup) { + return data ? escapeMarkup(data.text) : undefined; + }, + sortResults: function (results, container, query) { + return results; + }, + formatResultCssClass: function(data) {return data.css;}, + formatSelectionCssClass: function(data, container) {return undefined;}, + minimumResultsForSearch: 0, + minimumInputLength: 0, + maximumInputLength: null, + maximumSelectionSize: 0, + id: function (e) { return e == undefined ? null : e.id; }, + matcher: function(term, text) { + return stripDiacritics(''+text).toUpperCase().indexOf(stripDiacritics(''+term).toUpperCase()) >= 0; + }, + separator: ",", + tokenSeparators: [], + tokenizer: defaultTokenizer, + escapeMarkup: defaultEscapeMarkup, + blurOnChange: false, + selectOnBlur: false, + adaptContainerCssClass: function(c) { return c; }, + adaptDropdownCssClass: function(c) { return null; }, + nextSearchTerm: function(selectedObject, currentSearchTerm) { return undefined; }, + searchInputPlaceholder: '', + createSearchChoicePosition: 'top', + shouldFocusInput: function (instance) { + // Attempt to detect touch devices + var supportsTouchEvents = (('ontouchstart' in window) || + (navigator.msMaxTouchPoints > 0)); + + // Only devices which support touch events should be special cased + if (!supportsTouchEvents) { + return true; + } + + // Never focus the input if search is disabled + if (instance.opts.minimumResultsForSearch < 0) { + return false; + } + + return true; + } + }; + + $.fn.select2.locales = []; + + $.fn.select2.locales['en'] = { + formatMatches: function (matches) { if (matches === 1) { return "One result is available, press enter to select it."; } return matches + " results are available, use up and down arrow keys to navigate."; }, + formatNoMatches: function () { return "No matches found"; }, + formatAjaxError: function (jqXHR, textStatus, errorThrown) { return "Loading failed"; }, + formatInputTooShort: function (input, min) { var n = min - input.length; return "Please enter " + n + " or more character" + (n == 1 ? "" : "s"); }, + formatInputTooLong: function (input, max) { var n = input.length - max; return "Please delete " + n + " character" + (n == 1 ? "" : "s"); }, + formatSelectionTooBig: function (limit) { return "You can only select " + limit + " item" + (limit == 1 ? "" : "s"); }, + formatLoadMore: function (pageNumber) { return "Loading more results…"; }, + formatSearching: function () { return "Searching…"; }, + }; + + $.extend($.fn.select2.defaults, $.fn.select2.locales['en']); + + $.fn.select2.ajaxDefaults = { + transport: $.ajax, + params: { + type: "GET", + cache: false, + dataType: "json" + } + }; + + // exports + window.Select2 = { + query: { + ajax: ajax, + local: local, + tags: tags + }, util: { + debounce: debounce, + markMatch: markMatch, + escapeMarkup: defaultEscapeMarkup, + stripDiacritics: stripDiacritics + }, "class": { + "abstract": AbstractSelect2, + "single": SingleSelect2, + "multi": MultiSelect2 + } + }; + +}(jQuery)); + +/* ============================================================ + * flatui-radiocheck v0.1.0 + * ============================================================ */ + ++function (global, $) { + 'use strict'; + + var Radiocheck = function (element, options) { + this.init('radiocheck', element, options); + }; + + Radiocheck.DEFAULTS = { + checkboxClass: 'custom-checkbox', + radioClass: 'custom-radio', + checkboxTemplate: '<span class="icons"><span class="icon-unchecked"></span><span class="icon-checked"></span></span>', + radioTemplate: '<span class="icons"><span class="icon-unchecked"></span><span class="icon-checked"></span></span>' + }; + + Radiocheck.prototype.init = function (type, element, options) { + this.$element = $(element); + this.options = $.extend({}, Radiocheck.DEFAULTS, this.$element.data(), options); + if (this.$element.attr('type') == 'checkbox') { + this.$element.addClass(this.options.checkboxClass); + this.$element.after(this.options.checkboxTemplate); + } else if (this.$element.attr('type') == 'radio') { + this.$element.addClass(this.options.radioClass); + this.$element.after(this.options.radioTemplate); + } + }; + + Radiocheck.prototype.check = function () { + this.$element.prop('checked', true); + this.$element.trigger('change.radiocheck').trigger('checked.radiocheck'); + }, + + Radiocheck.prototype.uncheck = function () { + this.$element.prop('checked', false); + this.$element.trigger('change.radiocheck').trigger('unchecked.radiocheck'); + }, + + Radiocheck.prototype.toggle = function () { + this.$element.prop('checked', function (i, value) { + return !value; + }); + this.$element.trigger('change.radiocheck').trigger('toggled.radiocheck'); + }, + + Radiocheck.prototype.indeterminate = function () { + this.$element.prop('indeterminate', true); + this.$element.trigger('change.radiocheck').trigger('indeterminated.radiocheck'); + }, + + Radiocheck.prototype.determinate = function () { + this.$element.prop('indeterminate', false); + this.$element.trigger('change.radiocheck').trigger('determinated.radiocheck'); + }, + + Radiocheck.prototype.disable = function () { + this.$element.prop('disabled', true); + this.$element.trigger('change.radiocheck').trigger('disabled.radiocheck'); + }, + + Radiocheck.prototype.enable = function () { + this.$element.prop('disabled', false); + this.$element.trigger('change.radiocheck').trigger('enabled.radiocheck'); + }, + + Radiocheck.prototype.destroy = function () { + this.$element.removeData().removeClass(this.options.checkboxClass + ' ' + this.options.radioClass).next('.icons').remove(); + this.$element.trigger('destroyed.radiocheck'); + }; + + // RADIOCHECK PLUGIN DEFINITION + // ============================ + + function Plugin(option) { + return this.each(function () { + var $this = $(this); + var data = $this.data('radiocheck'); + var options = typeof option == 'object' && option; + + if (!data && option == 'destroy') { return; } + if (!data) { + $this.data('radiocheck', (data = new Radiocheck(this, options))); + } + if (typeof option == 'string') { + data[option](); + } + + // Adding 'nohover' class for mobile devices + + var mobile = /mobile|tablet|phone|ip(ad|od)|android|silk|webos/i.test(global.navigator.userAgent); + + if (mobile === true) { + $this.parent().hover(function () { + $this.addClass('nohover'); + }, function () { + $this.removeClass('nohover'); + }); + } + }); + } + + var old = $.fn.radiocheck; + + $.fn.radiocheck = Plugin; + $.fn.radiocheck.Constructor = Radiocheck; + + // RADIOCHECK NO CONFLICT + // ====================== + + $.fn.radiocheck.noConflict = function () { + $.fn.radiocheck = old; + return this; + }; + +}(this, jQuery); diff --git a/dependencies/main_files/importView.js b/dependencies/main_files/importView.js new file mode 100644 index 00000000..81b0e29d --- /dev/null +++ b/dependencies/main_files/importView.js @@ -0,0 +1,146 @@ +/** + * Created by aghassaei on 1/16/15. + */ + + +ImportView = Backbone.View.extend({ + + el: "#importGeometry", + + events: { + "change #uploadMesh": "uploadMesh", + "click .selectMesh": "selectMesh", + "fileselect .btn-file :file": "readDataURL", + "click .stlRotate": "rotate", + "change .dimension": "scale" + }, + + initialize: function(){ + + _.bindAll(this, "render", "onMeshLoad"); + this.listenTo(this.model, "change:filename change:boundingBoxHelper", this.render);//boundingBoxHelper covers orientation + + this.render(); + }, + + selectMesh: function(e){//select mesh from dropdown list + e.preventDefault(); + var filename = $(e.target).data("file"); + this.loadMeshFromURL('data/' + filename); + this.model.set("filename", filename); + }, + + uploadMesh: function(e){//select a mesh to upload + var input = $(e.target), + numFiles = input.get(0).files ? input.get(0).files.length : 1, + label = input.val().replace(/\\/g, '/').replace(/.*\//, ''); + input.trigger('fileselect', [numFiles, label, input.get(0).files]); + input.val(""); + }, + + readDataURL: function(event, numFiles, filename, files){ + if (files.length>1) console.log("too many files selected"); + var reader = new FileReader(); + reader.readAsDataURL(files[0]); + var self = this; + reader.onload = (function() { + return function(e) { + self.loadMeshFromURL(e.target.result); + self.model.set("filename", filename); + } + })(); + }, + + loadMeshFromURL: function(url){ + var loader = new THREE.STLLoader(); + loader.addEventListener('load', this.onMeshLoad); + loader.load(url); + }, + + onMeshLoad: function(e){ + this.model.set("geometry", e.content); + }, + + makeDimensionString: function(){ + var bounds = this.model.get("boundingBoxHelper").box; + return (bounds.max.x - bounds.min.x).toFixed(1) + " x " + + (bounds.max.y - bounds.min.y).toFixed(1) + " x " + (bounds.max.z - bounds.min.z).toFixed(1); + }, + + getScale: function(){ + var scale = this.model.get("scale"); + var dimensions = {}; + dimensions.xScale = scale[0]; + dimensions.yScale = scale[1]; + dimensions.zScale = scale[2]; + dimensions.dimensions = this.makeDimensionString(); + return dimensions; + }, + + scale: function(e){ + + this.model.scale([this.getDimScale($(".xScale").val()), this.getDimScale($(".yScale").val()), this.getDimScale($(".zScale").val())]); + }, + + getDimScale: function(val){ + if (val == "") return null; + return parseFloat(val); + }, + + rotate: function(e){ + e.preventDefault(); + var axis = $(e.target).data("axis"); + this.model.rotate(axis); + }, + + render: function(){ + this.$el.html(this.template(_.extend(this.model.attributes, this.getScale()))); + }, + + template: _.template( + '<div class="row demo-row">\ + <div class="col-xs-3">\ + <div>\ + <span class="btn btn-default btn-file fullWidth">\ + Upload STL<input id="uploadMesh" type="file">\ + </span>\ + </div>\ + <div class="text-center">\ + OR\ + </div>\ + <div>\ + <div class="btn-group fullWidth">\ + <button data-toggle="dropdown" class="btn btn-primary dropdown-toggle fullWidth" type="button">Select Model <span class="caret"></span></button>\ + <ul role="menu" class="dropdown-menu">\ + <li><a class="selectMesh" data-file="Airbus_A300-600.stl" href="#">Plane</a></li>\ + </ul>\ + </div><!-- /btn-group -->\ + </div>\ + </div> <!-- /.col-xs-3 -->\ + <div class="col-xs-9">\ + <div>Geometry: <%= filename %><br/>\ + <div>Dimensions: <%= dimensions %></div>\ + <div class="col-xs-2">\ + <input type="text" value placeholder="<%= xScale %>" class="xScale dimension form-control"></input>\ + </div> \ + <div class="col-xs-2">\ + <input type="text" value placeholder="<%= yScale %>" class="yScale dimension form-control"></input>\ + </div>\ + <div class="col-xs-2">\ + <input type="text" value placeholder="<%= zScale %>" class="zScale dimension form-control"></input>\ + </div><br/><br/>\ + Units: </div></br>\ + </div>\ + </div>') +}); + + +// <div class="col-xs-4">\ +// <a href="#" data-axis="z" class="stlRotate btn btn-block btn-lg btn-default">Rotate X</a>\ +// </div>\ +// <div class="col-xs-4">\ +// <a href="#" data-axis="y" class="stlRotate btn btn-block btn-lg btn-default">Rotate Y</a>\ +// </div>\ +// <div class="col-xs-4">\ +// <a href="#" data-axis="x" class="stlRotate btn btn-block btn-lg btn-default">Rotate Z</a>\ +// </div>\ \ No newline at end of file diff --git a/dependencies/main_files/jquery-2.js b/dependencies/main_files/jquery-2.js new file mode 100644 index 00000000..79d631ff --- /dev/null +++ b/dependencies/main_files/jquery-2.js @@ -0,0 +1,9205 @@ +/*! + * jQuery JavaScript Library v2.1.3 + * http://jquery.com/ + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * + * Copyright 2005, 2014 jQuery Foundation, Inc. and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2014-12-18T15:11Z + */ + +(function( global, factory ) { + + 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 ) { + +// Support: Firefox 18+ +// Can't be in strict mode, several libs including ASP.NET trace +// the stack via arguments.caller.callee and Firefox dies if +// you try to trace through "use strict" call chains. (#13335) +// + +var arr = []; + +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 support = {}; + + + +var + // Use the correct document accordingly with window argument (sandbox) + document = window.document, + + version = "2.1.3", + + // 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.1 + // Make sure we trim BOM and NBSP + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, + + // Matches dashed string for camelizing + rmsPrefix = /^-ms-/, + rdashAlpha = /-([\da-z])/gi, + + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return letter.toUpperCase(); + }; + +jQuery.fn = jQuery.prototype = { + // The current version of jQuery being used + jquery: version, + + constructor: jQuery, + + // Start with an empty selector + selector: "", + + // 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 num != null ? + + // Return just the one element from the set + ( num < 0 ? this[ num + this.length ] : this[ num ] ) : + + // Return all the elements in a clean array + slice.call( this ); + }, + + // 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; + ret.context = this.context; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + }, + + 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(null); + }, + + // 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" && !jQuery.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 = jQuery.isArray(copy)) ) ) { + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.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() {}, + + isFunction: function( obj ) { + return jQuery.type(obj) === "function"; + }, + + isArray: Array.isArray, + + isWindow: function( obj ) { + return obj != null && obj === obj.window; + }, + + isNumeric: function( obj ) { + // parseFloat NaNs numeric-cast false positives (null|true|false|"") + // ...but misinterprets leading-number strings, particularly hex literals ("0x...") + // subtraction forces infinities to NaN + // adding 1 corrects loss of precision from parseFloat (#15100) + return !jQuery.isArray( obj ) && (obj - parseFloat( obj ) + 1) >= 0; + }, + + isPlainObject: function( obj ) { + // Not plain objects: + // - Any object or value whose internal [[Class]] property is not "[object Object]" + // - DOM nodes + // - window + if ( jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + return false; + } + + if ( obj.constructor && + !hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) { + return false; + } + + // If the function hasn't returned already, we're confident that + // |obj| is a plain object, created by {} or constructed with new Object + return true; + }, + + isEmptyObject: function( obj ) { + var name; + for ( name in obj ) { + return false; + } + return true; + }, + + type: function( obj ) { + if ( obj == null ) { + return obj + ""; + } + // Support: Android<4.0, iOS<6 (functionish RegExp) + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call(obj) ] || "object" : + typeof obj; + }, + + // Evaluates a script in a global context + globalEval: function( code ) { + var script, + indirect = eval; + + code = jQuery.trim( code ); + + if ( code ) { + // If the code includes a valid, prologue position + // strict mode pragma, execute code by injecting a + // script tag into the document. + if ( code.indexOf("use strict") === 1 ) { + script = document.createElement("script"); + script.text = code; + document.head.appendChild( script ).parentNode.removeChild( script ); + } else { + // Otherwise, avoid the DOM node creation, insertion + // and removal by using an indirect global eval + indirect( code ); + } + } + }, + + // Convert dashed to camelCase; used by the css and data modules + // Support: IE9-11+ + // Microsoft forgot to hump their vendor prefix (#9572) + camelCase: function( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + }, + + // args is for internal usage only + each: function( obj, callback, args ) { + var value, + i = 0, + length = obj.length, + isArray = isArraylike( obj ); + + if ( args ) { + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback.apply( obj[ i ], args ); + + if ( value === false ) { + break; + } + } + } else { + for ( i in obj ) { + value = callback.apply( obj[ i ], args ); + + if ( value === false ) { + break; + } + } + } + + // A special, fast, case for the most common use of each + } else { + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback.call( obj[ i ], i, obj[ i ] ); + + if ( value === false ) { + break; + } + } + } else { + for ( i in obj ) { + value = callback.call( obj[ i ], i, obj[ i ] ); + + if ( value === false ) { + break; + } + } + } + } + + return obj; + }, + + // Support: Android<4.1 + 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 ); + }, + + 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 value, + i = 0, + length = elems.length, + isArray = isArraylike( elems ), + ret = []; + + // Go through the array, translating each of the items to their new values + if ( isArray ) { + 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, + + // Bind a function to a context, optionally partially applying any + // arguments. + 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 ( !jQuery.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; + }, + + now: Date.now, + + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support +}); + +// Populate the class2type map +jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +}); + +function isArraylike( obj ) { + var length = obj.length, + type = jQuery.type( obj ); + + if ( type === "function" || jQuery.isWindow( obj ) ) { + return false; + } + + if ( obj.nodeType === 1 && length ) { + return true; + } + + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; +} +var Sizzle = +/*! + * Sizzle CSS Selector Engine v2.2.0-pre + * http://sizzlejs.com/ + * + * Copyright 2008, 2014 jQuery Foundation, Inc. and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2014-12-16 + */ +(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; + }, + + // General-purpose constants + MAX_NEGATIVE = 1 << 31, + + // 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 + // http://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 + + // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + // http://www.w3.org/TR/css3-syntax/#characters + characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", + + // Loosely modeled on CSS identifier characters + // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors + // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = characterEncoding.replace( "w", "w#" ), + + // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + characterEncoding + ")(?:" + whitespace + + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + + // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + + "*\\]", + + pseudos = ":(" + characterEncoding + ")(?:\\((" + + // 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( "^#(" + characterEncoding + ")" ), + "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), + "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), + "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 = /[+~]/, + rescape = /'|\\/g, + + // 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 ); + }, + + // Used for iframes + // See setDocument() + // Removing the function wrapper causes a "Permission Denied" + // error in IE + unloadHandler = function() { + setDocument(); + }; + +// 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 match, elem, m, nodeType, + // QSA vars + i, groups, old, nid, newContext, newSelector; + + if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { + setDocument( context ); + } + + context = context || document; + results = results || []; + nodeType = context.nodeType; + + if ( typeof selector !== "string" || !selector || + nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { + + return results; + } + + if ( !seed && documentIsHTML ) { + + // Try to shortcut find operations when possible (e.g., not under DocumentFragment) + if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) { + // Speed-up: Sizzle("#ID") + if ( (m = match[1]) ) { + if ( nodeType === 9 ) { + elem = context.getElementById( m ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document (jQuery #6963) + if ( elem && elem.parentNode ) { + // Handle the case where IE, Opera, and Webkit return items + // by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + } else { + // Context is not a document + if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && + contains( context, elem ) && elem.id === m ) { + results.push( elem ); + return results; + } + } + + // Speed-up: Sizzle("TAG") + } else if ( match[2] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Speed-up: Sizzle(".CLASS") + } else if ( (m = match[3]) && support.getElementsByClassName ) { + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // QSA path + if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { + nid = old = expando; + newContext = context; + newSelector = nodeType !== 1 && selector; + + // qSA works strangely on Element-rooted queries + // We can work around this by specifying an extra ID on the root + // and working up from there (Thanks to Andrew Dupont for the technique) + // IE 8 doesn't work on object elements + if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { + groups = tokenize( selector ); + + if ( (old = context.getAttribute("id")) ) { + nid = old.replace( rescape, "\\$&" ); + } else { + context.setAttribute( "id", nid ); + } + nid = "[id='" + nid + "'] "; + + i = groups.length; + while ( i-- ) { + groups[i] = nid + toSelector( groups[i] ); + } + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context; + newSelector = groups.join(","); + } + + if ( newSelector ) { + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch(qsaError) { + } finally { + if ( !old ) { + 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 div and expects a boolean result + */ +function assert( fn ) { + var div = document.createElement("div"); + + try { + return !!fn( div ); + } catch (e) { + return false; + } finally { + // Remove from its parent by default + if ( div.parentNode ) { + div.parentNode.removeChild( div ); + } + // release memory in IE + div = 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 = attrs.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 && + ( ~b.sourceIndex || MAX_NEGATIVE ) - + ( ~a.sourceIndex || MAX_NEGATIVE ); + + // 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 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, parent, + doc = node ? node.ownerDocument || node : preferredDoc; + + // If no document and documentElement is available, return + if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Set our document + document = doc; + docElem = doc.documentElement; + parent = doc.defaultView; + + // Support: IE>8 + // If iframe document is assigned to "document" variable and if iframe has been reloaded, + // IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936 + // IE6-8 do not support the defaultView property so parent will be undefined + if ( parent && parent !== parent.top ) { + // IE11 does not have attachEvent, so all must suffer + if ( parent.addEventListener ) { + parent.addEventListener( "unload", unloadHandler, false ); + } else if ( parent.attachEvent ) { + parent.attachEvent( "onunload", unloadHandler ); + } + } + + /* Support tests + ---------------------------------------------------------------------- */ + documentIsHTML = !isXML( doc ); + + /* Attributes + ---------------------------------------------------------------------- */ + + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties + // (excepting IE8 booleans) + support.attributes = assert(function( div ) { + div.className = "i"; + return !div.getAttribute("className"); + }); + + /* getElement(s)By* + ---------------------------------------------------------------------- */ + + // Check if getElementsByTagName("*") returns only elements + support.getElementsByTagName = assert(function( div ) { + div.appendChild( doc.createComment("") ); + return !div.getElementsByTagName("*").length; + }); + + // Support: IE<9 + support.getElementsByClassName = rnative.test( doc.getElementsByClassName ); + + // Support: IE<10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert(function( div ) { + docElem.appendChild( div ).id = expando; + return !doc.getElementsByName || !doc.getElementsByName( expando ).length; + }); + + // ID find and filter + if ( support.getById ) { + Expr.find["ID"] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var m = context.getElementById( id ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + return m && m.parentNode ? [ m ] : []; + } + }; + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute("id") === attrId; + }; + }; + } else { + // Support: IE6/7 + // getElementById is not reliable as a find shortcut + delete Expr.find["ID"]; + + 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; + }; + }; + } + + // 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 ( 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 http://bugs.jquery.com/ticket/13378 + rbuggyQSA = []; + + if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) { + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert(function( div ) { + // 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 + // http://bugs.jquery.com/ticket/12359 + docElem.appendChild( div ).innerHTML = "<a id='" + expando + "'></a>" + + "<select id='" + expando + "-\f]' 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 + // http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section + if ( div.querySelectorAll("[msallowcapture^='']").length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } + + // Support: IE8 + // Boolean attributes and "value" are not treated correctly + if ( !div.querySelectorAll("[selected]").length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } + + // Support: Chrome<29, Android<4.2+, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.7+ + if ( !div.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 ( !div.querySelectorAll(":checked").length ) { + rbuggyQSA.push(":checked"); + } + + // Support: Safari 8+, iOS 8+ + // https://bugs.webkit.org/show_bug.cgi?id=136851 + // In-page `selector#id sibing-combinator selector` fails + if ( !div.querySelectorAll( "a#" + expando + "+*" ).length ) { + rbuggyQSA.push(".#.+[+~]"); + } + }); + + assert(function( div ) { + // Support: Windows 8 Native Apps + // The type and name attributes are restricted during .innerHTML assignment + var input = doc.createElement("input"); + input.setAttribute( "type", "hidden" ); + div.appendChild( input ).setAttribute( "name", "D" ); + + // Support: IE8 + // Enforce case-sensitivity of name attribute + if ( div.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 ( !div.querySelectorAll(":enabled").length ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Opera 10-11 does not throw on post-comma invalid pseudos + div.querySelectorAll("*,:x"); + rbuggyQSA.push(",.*:"); + }); + } + + if ( (support.matchesSelector = rnative.test( (matches = docElem.matches || + docElem.webkitMatchesSelector || + docElem.mozMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector) )) ) { + + assert(function( div ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( div, "div" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( div, "[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 does not implement inclusive descendent + // 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 === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { + return -1; + } + if ( b === doc || 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 === doc ? -1 : + b === doc ? 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 doc; +}; + +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 && + ( !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.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, outerCache, node, diff, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType; + + 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 + outerCache = parent[ expando ] || (parent[ expando ] = {}); + cache = outerCache[ type ] || []; + nodeIndex = cache[0] === dirruns && cache[1]; + diff = cache[0] === dirruns && 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 ) { + outerCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + // Use previously-cached element index if available + } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) { + diff = cache[1]; + + // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...) + } else { + // 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 ) { + (node[ expando ] || (node[ expando ] = {}))[ 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": function( elem ) { + return elem.disabled === false; + }, + + "disabled": function( elem ) { + return elem.disabled === 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, + checkNonElements = base && dir === "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 ); + } + } + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + var oldCache, outerCache, + newCache = [ dirruns, doneName ]; + + // We can't set arbitrary data on XML nodes, so they don't benefit from dir 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 ] = {}); + if ( (oldCache = outerCache[ dir ]) && + 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 + outerCache[ dir ] = newCache; + + // A match means we're done; a fail means we have to keep checking + if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) { + return true; + } + } + } + } + } + }; +} + +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; + } + + // Add elements passing elementMatchers directly to results + // Keep `i` a string if there are no elements so `matchedCount` will be "00" below + // 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; + while ( (matcher = elementMatchers[j++]) ) { + if ( matcher( elem, context, 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 ); + } + } + } + + // Apply set filters to unmatched elements + matchedCount += i; + 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 no seed and only one group + if ( match.length === 1 ) { + + // Take a shortcut and set the context if the root selector is an ID + tokens = match[0] = match[0].slice( 0 ); + if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && + support.getById && 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, + 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( div1 ) { + // Should return 1, but returns 4 (following) + return div1.compareDocumentPosition( document.createElement("div") ) & 1; +}); + +// Support: IE<8 +// Prevent attribute/property "interpolation" +// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !assert(function( div ) { + div.innerHTML = "<a href='#'></a>"; + return div.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( div ) { + div.innerHTML = "<input/>"; + div.firstChild.setAttribute( "value", "" ); + return div.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( div ) { + return div.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; +jQuery.expr[":"] = jQuery.expr.pseudos; +jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; + + + +var rneedsContext = jQuery.expr.match.needsContext; + +var rsingleTag = (/^<(\w+)\s*\/?>(?:<\/\1>|)$/); + + + +var risSimple = /^.[^:#\[\.,]*$/; + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, not ) { + if ( jQuery.isFunction( qualifier ) ) { + return jQuery.grep( elements, function( elem, i ) { + /* jshint -W018 */ + return !!qualifier.call( elem, i, elem ) !== not; + }); + + } + + if ( qualifier.nodeType ) { + return jQuery.grep( elements, function( elem ) { + return ( elem === qualifier ) !== not; + }); + + } + + if ( typeof qualifier === "string" ) { + if ( risSimple.test( qualifier ) ) { + return jQuery.filter( qualifier, elements, not ); + } + + qualifier = jQuery.filter( qualifier, elements ); + } + + return jQuery.grep( elements, function( elem ) { + return ( indexOf.call( qualifier, elem ) >= 0 ) !== not; + }); +} + +jQuery.filter = function( expr, elems, not ) { + var elem = elems[ 0 ]; + + if ( not ) { + expr = ":not(" + expr + ")"; + } + + return elems.length === 1 && elem.nodeType === 1 ? + jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] : + jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { + return elem.nodeType === 1; + })); +}; + +jQuery.fn.extend({ + find: function( selector ) { + var i, + len = this.length, + ret = [], + 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; + } + } + }) ); + } + + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, self[ i ], ret ); + } + + // Needed because $( selector, context ) becomes $( context ).find( selector ) + ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret ); + ret.selector = this.selector ? this.selector + " " + selector : selector; + return 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 <) + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/, + + init = jQuery.fn.init = function( selector, context ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // 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 ( jQuery.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] ); + + // Support: Blackberry 4.6 + // gEBID returns nodes no longer in the document (#6963) + if ( elem && elem.parentNode ) { + // Inject the element directly into the jQuery object + this.length = 1; + this[0] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || rootjQuery ).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.context = this[0] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return typeof rootjQuery.ready !== "undefined" ? + rootjQuery.ready( selector ) : + // Execute immediately if ready is not present + selector( jQuery ); + } + + if ( selector.selector !== undefined ) { + this.selector = selector.selector; + this.context = selector.context; + } + + 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.extend({ + 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; + }, + + sibling: function( n, elem ) { + var matched = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + matched.push( n ); + } + } + + return matched; + } +}); + +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 = [], + pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ? + jQuery( selectors, context || this.context ) : + 0; + + for ( ; i < l; i++ ) { + for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) { + // Always skip document fragments + if ( cur.nodeType < 11 && (pos ? + pos.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.unique( 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.unique( + 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 jQuery.dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return jQuery.dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return jQuery.dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return jQuery.dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return jQuery.dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return jQuery.dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return jQuery.sibling( elem.firstChild ); + }, + contents: function( elem ) { + return elem.contentDocument || 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.unique( matched ); + } + + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + matched.reverse(); + } + } + + return this.pushStack( matched ); + }; +}); +var rnotwhite = (/\S+/g); + + + +// String to Object options format cache +var optionsCache = {}; + +// Convert String-formatted options into Object-formatted ones and store in cache +function createOptions( options ) { + var object = optionsCache[ options ] = {}; + jQuery.each( options.match( rnotwhite ) || [], 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" ? + ( optionsCache[ options ] || createOptions( options ) ) : + jQuery.extend( {}, options ); + + var // Last fire value (for non-forgettable lists) + memory, + // Flag to know if list was already fired + fired, + // Flag to know if list is currently firing + firing, + // First callback to fire (used internally by add and fireWith) + firingStart, + // End of the loop when firing + firingLength, + // Index of currently firing callback (modified by remove if needed) + firingIndex, + // Actual callback list + list = [], + // Stack of fire calls for repeatable lists + stack = !options.once && [], + // Fire callbacks + fire = function( data ) { + memory = options.memory && data; + fired = true; + firingIndex = firingStart || 0; + firingStart = 0; + firingLength = list.length; + firing = true; + for ( ; list && firingIndex < firingLength; firingIndex++ ) { + if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { + memory = false; // To prevent further calls using add + break; + } + } + firing = false; + if ( list ) { + if ( stack ) { + if ( stack.length ) { + fire( stack.shift() ); + } + } else if ( memory ) { + list = []; + } else { + self.disable(); + } + } + }, + // Actual Callbacks object + self = { + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + // First, we save the current length + var start = list.length; + (function add( args ) { + jQuery.each( args, function( _, arg ) { + var type = jQuery.type( arg ); + if ( type === "function" ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && type !== "string" ) { + // Inspect recursively + add( arg ); + } + }); + })( arguments ); + // Do we need to add the callbacks to the + // current firing batch? + if ( firing ) { + firingLength = list.length; + // With memory, if we're not firing then + // we should call right away + } else if ( memory ) { + firingStart = start; + fire( memory ); + } + } + return this; + }, + // Remove a callback from the list + remove: function() { + if ( list ) { + jQuery.each( arguments, function( _, arg ) { + var index; + while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + // Handle firing indexes + if ( firing ) { + if ( index <= firingLength ) { + firingLength--; + } + 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 && list.length ); + }, + // Remove all callbacks from the list + empty: function() { + list = []; + firingLength = 0; + return this; + }, + // Have the list do nothing anymore + disable: function() { + list = stack = memory = undefined; + return this; + }, + // Is it disabled? + disabled: function() { + return !list; + }, + // Lock the list in its current state + lock: function() { + stack = undefined; + if ( !memory ) { + self.disable(); + } + return this; + }, + // Is it locked? + locked: function() { + return !stack; + }, + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( list && ( !fired || stack ) ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + if ( firing ) { + stack.push( args ); + } else { + fire( args ); + } + } + 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; +}; + + +jQuery.extend({ + + Deferred: function( func ) { + var tuples = [ + // action, add listener, listener list, final state + [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], + [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], + [ "notify", "progress", jQuery.Callbacks("memory") ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + then: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + return jQuery.Deferred(function( newDefer ) { + jQuery.each( tuples, function( i, tuple ) { + var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; + // deferred[ done | fail | progress ] for forwarding actions to newDefer + deferred[ tuple[1] ](function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && jQuery.isFunction( returned.promise ) ) { + returned.promise() + .done( newDefer.resolve ) + .fail( newDefer.reject ) + .progress( newDefer.notify ); + } else { + newDefer[ tuple[ 0 ] + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments ); + } + }); + }); + fns = null; + }).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 = {}; + + // Keep pipe for back-compat + promise.pipe = promise.then; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 3 ]; + + // promise[ done | fail | progress ] = list.add + promise[ tuple[1] ] = list.add; + + // Handle state + if ( stateString ) { + list.add(function() { + // state = [ resolved | rejected ] + state = stateString; + + // [ reject_list | resolve_list ].disable; progress_list.lock + }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); + } + + // deferred[ resolve | reject | notify ] + deferred[ tuple[0] ] = function() { + deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments ); + return this; + }; + 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( subordinate /* , ..., subordinateN */ ) { + var i = 0, + resolveValues = slice.call( arguments ), + length = resolveValues.length, + + // the count of uncompleted subordinates + remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, + + // the master Deferred. If resolveValues consist of only a single Deferred, just use that. + deferred = remaining === 1 ? subordinate : jQuery.Deferred(), + + // Update function for both resolve and progress values + updateFunc = function( i, contexts, values ) { + return function( value ) { + contexts[ i ] = this; + values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; + if ( values === progressValues ) { + deferred.notifyWith( contexts, values ); + } else if ( !( --remaining ) ) { + deferred.resolveWith( contexts, values ); + } + }; + }, + + progressValues, progressContexts, resolveContexts; + + // Add listeners to Deferred subordinates; treat others as resolved + if ( length > 1 ) { + progressValues = new Array( length ); + progressContexts = new Array( length ); + resolveContexts = new Array( length ); + for ( ; i < length; i++ ) { + if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { + resolveValues[ i ].promise() + .done( updateFunc( i, resolveContexts, resolveValues ) ) + .fail( deferred.reject ) + .progress( updateFunc( i, progressContexts, progressValues ) ); + } else { + --remaining; + } + } + } + + // If we're not waiting on anything, resolve the master + if ( !remaining ) { + deferred.resolveWith( resolveContexts, resolveValues ); + } + + return deferred.promise(); + } +}); + + +// The deferred used on DOM ready +var readyList; + +jQuery.fn.ready = function( fn ) { + // Add the callback + jQuery.ready.promise().done( fn ); + + 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, + + // Hold (or release) the ready event + holdReady: function( hold ) { + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } + }, + + // 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 ] ); + + // Trigger any bound ready events + if ( jQuery.fn.triggerHandler ) { + jQuery( document ).triggerHandler( "ready" ); + jQuery( document ).off( "ready" ); + } + } +}); + +/** + * The ready event handler and self cleanup method + */ +function completed() { + document.removeEventListener( "DOMContentLoaded", completed, false ); + window.removeEventListener( "load", completed, false ); + jQuery.ready(); +} + +jQuery.ready.promise = function( obj ) { + if ( !readyList ) { + + readyList = jQuery.Deferred(); + + // Catch cases where $(document).ready() is called after the browser event has already occurred. + // We once tried to use readyState "interactive" here, but it caused issues like the one + // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 + if ( document.readyState === "complete" ) { + // Handle it asynchronously to allow scripts the opportunity to delay ready + setTimeout( jQuery.ready ); + + } else { + + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed, false ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed, false ); + } + } + return readyList.promise( obj ); +}; + +// Kick off the DOM ready check even if the user does not +jQuery.ready.promise(); + + + + +// Multifunctional method to get and set values of a collection +// The value/s can optionally be executed if it's a function +var access = jQuery.access = function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + len = elems.length, + bulk = key == null; + + // Sets many values + if ( jQuery.type( key ) === "object" ) { + chainable = true; + for ( i in key ) { + jQuery.access( elems, fn, i, key[i], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !jQuery.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 ) ) ); + } + } + } + + return chainable ? + elems : + + // Gets + bulk ? + fn.call( elems ) : + len ? fn( elems[0], key ) : emptyGet; +}; + + +/** + * Determines whether an object can have data + */ +jQuery.acceptData = function( owner ) { + // Accepts only: + // - Node + // - Node.ELEMENT_NODE + // - Node.DOCUMENT_NODE + // - Object + // - Any + /* jshint -W018 */ + return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); +}; + + +function Data() { + // Support: Android<4, + // Old WebKit does not have Object.preventExtensions/freeze method, + // return new empty object instead with no [[set]] accessor + Object.defineProperty( this.cache = {}, 0, { + get: function() { + return {}; + } + }); + + this.expando = jQuery.expando + Data.uid++; +} + +Data.uid = 1; +Data.accepts = jQuery.acceptData; + +Data.prototype = { + key: function( owner ) { + // We can accept data for non-element nodes in modern browsers, + // but we should not, see #8335. + // Always return the key for a frozen object. + if ( !Data.accepts( owner ) ) { + return 0; + } + + var descriptor = {}, + // Check if the owner object already has a cache key + unlock = owner[ this.expando ]; + + // If not, create one + if ( !unlock ) { + unlock = Data.uid++; + + // Secure it in a non-enumerable, non-writable property + try { + descriptor[ this.expando ] = { value: unlock }; + Object.defineProperties( owner, descriptor ); + + // Support: Android<4 + // Fallback to a less secure definition + } catch ( e ) { + descriptor[ this.expando ] = unlock; + jQuery.extend( owner, descriptor ); + } + } + + // Ensure the cache object + if ( !this.cache[ unlock ] ) { + this.cache[ unlock ] = {}; + } + + return unlock; + }, + set: function( owner, data, value ) { + var prop, + // There may be an unlock assigned to this node, + // if there is no entry for this "owner", create one inline + // and set the unlock as though an owner entry had always existed + unlock = this.key( owner ), + cache = this.cache[ unlock ]; + + // Handle: [ owner, key, value ] args + if ( typeof data === "string" ) { + cache[ data ] = value; + + // Handle: [ owner, { properties } ] args + } else { + // Fresh assignments by object are shallow copied + if ( jQuery.isEmptyObject( cache ) ) { + jQuery.extend( this.cache[ unlock ], data ); + // Otherwise, copy the properties one-by-one to the cache object + } else { + for ( prop in data ) { + cache[ prop ] = data[ prop ]; + } + } + } + return cache; + }, + get: function( owner, key ) { + // Either a valid cache is found, or will be created. + // New caches will be created and the unlock returned, + // allowing direct access to the newly created + // empty data object. A valid owner object must be provided. + var cache = this.cache[ this.key( owner ) ]; + + return key === undefined ? + cache : cache[ key ]; + }, + access: function( owner, key, value ) { + var stored; + // 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) ) { + + stored = this.get( owner, key ); + + return stored !== undefined ? + stored : this.get( owner, jQuery.camelCase(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, name, camel, + unlock = this.key( owner ), + cache = this.cache[ unlock ]; + + if ( key === undefined ) { + this.cache[ unlock ] = {}; + + } else { + // Support array or space separated string of keys + if ( jQuery.isArray( key ) ) { + // If "name" is an array of keys... + // When data is initially created, via ("key", "val") signature, + // keys will be converted to camelCase. + // Since there is no way to tell _how_ a key was added, remove + // both plain key and camelCase key. #12786 + // This will only penalize the array argument path. + name = key.concat( key.map( jQuery.camelCase ) ); + } else { + camel = jQuery.camelCase( key ); + // Try the string as a key before any manipulation + if ( key in cache ) { + name = [ key, camel ]; + } else { + // If a key with the spaces exists, use it. + // Otherwise, create an array by matching non-whitespace + name = camel; + name = name in cache ? + [ name ] : ( name.match( rnotwhite ) || [] ); + } + } + + i = name.length; + while ( i-- ) { + delete cache[ name[ i ] ]; + } + } + }, + hasData: function( owner ) { + return !jQuery.isEmptyObject( + this.cache[ owner[ this.expando ] ] || {} + ); + }, + discard: function( owner ) { + if ( owner[ this.expando ] ) { + delete this.cache[ owner[ this.expando ] ]; + } + } +}; +var data_priv = new Data(); + +var data_user = 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 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, "-$1" ).toLowerCase(); + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : + // Only convert to a number if it doesn't change the string + +data + "" === data ? +data : + rbrace.test( data ) ? jQuery.parseJSON( data ) : + data; + } catch( e ) {} + + // Make sure we set the data so it isn't changed later + data_user.set( elem, key, data ); + } else { + data = undefined; + } + } + return data; +} + +jQuery.extend({ + hasData: function( elem ) { + return data_user.hasData( elem ) || data_priv.hasData( elem ); + }, + + data: function( elem, name, data ) { + return data_user.access( elem, name, data ); + }, + + removeData: function( elem, name ) { + data_user.remove( elem, name ); + }, + + // TODO: Now that all calls to _data and _removeData have been replaced + // with direct calls to data_priv methods, these can be deprecated. + _data: function( elem, name, data ) { + return data_priv.access( elem, name, data ); + }, + + _removeData: function( elem, name ) { + data_priv.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 = data_user.get( elem ); + + if ( elem.nodeType === 1 && !data_priv.get( elem, "hasDataAttrs" ) ) { + i = attrs.length; + while ( i-- ) { + + // Support: IE11+ + // The attrs elements can be null (#14894) + if ( attrs[ i ] ) { + name = attrs[ i ].name; + if ( name.indexOf( "data-" ) === 0 ) { + name = jQuery.camelCase( name.slice(5) ); + dataAttr( elem, name, data[ name ] ); + } + } + } + data_priv.set( elem, "hasDataAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each(function() { + data_user.set( this, key ); + }); + } + + return access( this, function( value ) { + var data, + camelKey = jQuery.camelCase( key ); + + // 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 + // with the key as-is + data = data_user.get( elem, key ); + if ( data !== undefined ) { + return data; + } + + // Attempt to get data from the cache + // with the key camelized + data = data_user.get( elem, camelKey ); + if ( data !== undefined ) { + return data; + } + + // Attempt to "discover" the data in + // HTML5 custom data-* attrs + data = dataAttr( elem, camelKey, undefined ); + if ( data !== undefined ) { + return data; + } + + // We tried really hard, but the data doesn't exist. + return; + } + + // Set the data... + this.each(function() { + // First, attempt to store a copy or reference of any + // data that might've been store with a camelCased key. + var data = data_user.get( this, camelKey ); + + // For HTML5 data-* attribute interop, we have to + // store property names with dashes in a camelCase form. + // This might not apply to all properties...* + data_user.set( this, camelKey, value ); + + // *... In the case of properties that might _actually_ + // have dashes, we need to also store a copy of that + // unchanged property. + if ( key.indexOf("-") !== -1 && data !== undefined ) { + data_user.set( this, key, value ); + } + }); + }, null, value, arguments.length > 1, null, true ); + }, + + removeData: function( key ) { + return this.each(function() { + data_user.remove( this, key ); + }); + } +}); + + +jQuery.extend({ + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = data_priv.get( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || jQuery.isArray( data ) ) { + queue = data_priv.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 data_priv.get( elem, key ) || data_priv.access( elem, key, { + empty: jQuery.Callbacks("once memory").add(function() { + data_priv.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 = data_priv.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 cssExpand = [ "Top", "Right", "Bottom", "Left" ]; + +var isHidden = function( elem, el ) { + // isHidden might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem ); + }; + +var rcheckableType = (/^(?:checkbox|radio)$/i); + + + +(function() { + var fragment = document.createDocumentFragment(), + div = fragment.appendChild( document.createElement( "div" ) ), + input = document.createElement( "input" ); + + // Support: Safari<=5.1 + // 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: Safari<=5.1, Android<4.2 + // Older WebKit doesn't clone checked state correctly in fragments + support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Support: IE<=11+ + // Make sure textarea (and checkbox) defaultValue is properly cloned + div.innerHTML = "<textarea>x</textarea>"; + support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; +})(); +var strundefined = typeof undefined; + + + +support.focusinBubbles = "onfocusin" in window; + + +var + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|pointer|contextmenu)|click/, + rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)$/; + +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + +function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } +} + +/* + * 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 = data_priv.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; + } + + // 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 !== strundefined && jQuery.event.triggered !== e.type ? + jQuery.event.dispatch.apply( elem, arguments ) : undefined; + }; + } + + // Handle multiple events separated by a space + types = ( types || "" ).match( rnotwhite ) || [ "" ]; + 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, false ); + } + } + } + + 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 = data_priv.hasData( elem ) && data_priv.get( elem ); + + if ( !elemData || !(events = elemData.events) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( rnotwhite ) || [ "" ]; + 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 the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + delete elemData.handle; + data_priv.remove( elem, "events" ); + } + }, + + trigger: function( event, data, elem, onlyHandlers ) { + + var i, cur, tmp, bubbleType, ontype, handle, special, + eventPath = [ elem || document ], + type = hasOwn.call( event, "type" ) ? event.type : event, + namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : []; + + cur = 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(".") >= 0 ) { + // 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.namespace_re = 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 && !jQuery.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() ) { + + event.type = i > 1 ? + bubbleType : + special.bindType || type; + + // jQuery handler + handle = ( data_priv.get( cur, "events" ) || {} )[ event.type ] && data_priv.get( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + + // Native handler + handle = ontype && cur[ ontype ]; + if ( handle && handle.apply && jQuery.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) && + jQuery.acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name name as the event. + // Don't do default actions on window, that's where global variables be (#6170) + if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.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; + elem[ type ](); + jQuery.event.triggered = undefined; + + if ( tmp ) { + elem[ ontype ] = tmp; + } + } + } + } + + return event.result; + }, + + dispatch: function( event ) { + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( event ); + + var i, j, ret, matched, handleObj, + handlerQueue = [], + args = slice.call( arguments ), + handlers = ( data_priv.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; + 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.namespace_re || event.namespace_re.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, matches, sel, handleObj, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Find delegate handlers + // Black-hole SVG <use> instance trees (#13180) + // Avoid non-left-click bubbling in Firefox (#3861) + if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) { + + for ( ; cur !== this; cur = cur.parentNode || this ) { + + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.disabled !== true || event.type !== "click" ) { + matches = []; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matches[ sel ] === undefined ) { + matches[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) >= 0 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matches[ sel ] ) { + matches.push( handleObj ); + } + } + if ( matches.length ) { + handlerQueue.push({ elem: cur, handlers: matches }); + } + } + } + } + + // Add the remaining (directly-bound) handlers + if ( delegateCount < handlers.length ) { + handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) }); + } + + return handlerQueue; + }, + + // Includes some event props shared by KeyEvent and MouseEvent + props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), + + fixHooks: {}, + + keyHooks: { + props: "char charCode key keyCode".split(" "), + filter: function( event, original ) { + + // Add which for key events + if ( event.which == null ) { + event.which = original.charCode != null ? original.charCode : original.keyCode; + } + + return event; + } + }, + + mouseHooks: { + props: "button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement".split(" "), + filter: function( event, original ) { + var eventDoc, doc, body, + button = original.button; + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && original.clientX != null ) { + eventDoc = event.target.ownerDocument || document; + doc = eventDoc.documentElement; + body = eventDoc.body; + + event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); + event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && button !== undefined ) { + event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); + } + + return event; + } + }, + + fix: function( event ) { + if ( event[ jQuery.expando ] ) { + return event; + } + + // Create a writable copy of the event object and normalize some properties + var i, prop, copy, + type = event.type, + originalEvent = event, + fixHook = this.fixHooks[ type ]; + + if ( !fixHook ) { + this.fixHooks[ type ] = fixHook = + rmouseEvent.test( type ) ? this.mouseHooks : + rkeyEvent.test( type ) ? this.keyHooks : + {}; + } + copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; + + event = new jQuery.Event( originalEvent ); + + i = copy.length; + while ( i-- ) { + prop = copy[ i ]; + event[ prop ] = originalEvent[ prop ]; + } + + // Support: Cordova 2.5 (WebKit) (#13255) + // All events should have a target; Cordova deviceready doesn't + if ( !event.target ) { + event.target = document; + } + + // Support: Safari 6.0+, Chrome<28 + // Target should not be a text node (#504, #13143) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; + } + + return fixHook.filter ? fixHook.filter( event, originalEvent ) : event; + }, + + 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 && jQuery.nodeName( this, "input" ) ) { + this.click(); + return false; + } + }, + + // For cross-browser consistency, don't fire native .click() on links + _default: function( event ) { + return jQuery.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; + } + } + } + }, + + simulate: function( type, elem, event, bubble ) { + // Piggyback on a donor event to simulate a different one. + // Fake originalEvent to avoid donor's stopPropagation, but if the + // simulated event prevents default then we do the same on the donor. + var e = jQuery.extend( + new jQuery.Event(), + event, + { + type: type, + isSimulated: true, + originalEvent: {} + } + ); + if ( bubble ) { + jQuery.event.trigger( e, null, elem ); + } else { + jQuery.event.dispatch.call( elem, e ); + } + if ( e.isDefaultPrevented() ) { + event.preventDefault(); + } + } +}; + +jQuery.removeEvent = function( elem, type, handle ) { + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle, false ); + } +}; + +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<4.0 + src.returnValue === false ? + returnTrue : + returnFalse; + + // 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 || jQuery.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + + if ( e && e.preventDefault ) { + e.preventDefault(); + } + }, + stopPropagation: function() { + var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + + if ( e && e.stopPropagation ) { + e.stopPropagation(); + } + }, + stopImmediatePropagation: function() { + var e = this.originalEvent; + + this.isImmediatePropagationStopped = returnTrue; + + if ( e && e.stopImmediatePropagation ) { + e.stopImmediatePropagation(); + } + + this.stopPropagation(); + } +}; + +// Create mouseenter/leave events using mouseover/out and event-time checks +// Support: Chrome 15+ +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 mousenter/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; + } + }; +}); + +// Support: Firefox, Chrome, Safari +// Create "bubbling" focus and blur events +if ( !support.focusinBubbles ) { + 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 ), true ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + var doc = this.ownerDocument || this, + attaches = data_priv.access( doc, fix ); + + if ( !attaches ) { + doc.addEventListener( orig, handler, true ); + } + data_priv.access( doc, fix, ( attaches || 0 ) + 1 ); + }, + teardown: function() { + var doc = this.ownerDocument || this, + attaches = data_priv.access( doc, fix ) - 1; + + if ( !attaches ) { + doc.removeEventListener( orig, handler, true ); + data_priv.remove( doc, fix ); + + } else { + data_priv.access( doc, fix, attaches ); + } + } + }; + }); +} + +jQuery.fn.extend({ + + on: function( types, selector, data, fn, /*INTERNAL*/ 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 ) { + this.on( type, selector, data, types[ type ], one ); + } + return this; + } + + 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 this; + } + + 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 this.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + }); + }, + one: function( types, selector, data, fn ) { + return this.on( 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 ); + }); + }, + + 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 ); + } + } +}); + + +var + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, + rtagName = /<([\w:]+)/, + rhtml = /<|&#?\w+;/, + rnoInnerhtml = /<(?:script|style|link)/i, + // checked="checked" or checked + rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, + rscriptType = /^$|\/(?:java|ecma)script/i, + rscriptTypeMasked = /^true\/(.*)/, + rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g, + + // We have to close these tags to support XHTML (#13200) + wrapMap = { + + // Support: IE9 + option: [ 1, "<select multiple='multiple'>", "</select>" ], + + 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: IE9 +wrapMap.optgroup = wrapMap.option; + +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +// Support: 1.x compatibility +// Manipulating tables requires a tbody +function manipulationTarget( elem, content ) { + return jQuery.nodeName( elem, "table" ) && + jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ? + + elem.getElementsByTagName("tbody")[0] || + elem.appendChild( elem.ownerDocument.createElement("tbody") ) : + 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 ) { + var match = rscriptTypeMasked.exec( elem.type ); + + if ( match ) { + elem.type = match[ 1 ]; + } else { + elem.removeAttribute("type"); + } + + return elem; +} + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + data_priv.set( + elems[ i ], "globalEval", !refElements || data_priv.get( refElements[ i ], "globalEval" ) + ); + } +} + +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 ( data_priv.hasData( src ) ) { + pdataOld = data_priv.access( src ); + pdataCur = data_priv.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 ( data_user.hasData( src ) ) { + udataOld = data_user.access( src ); + udataCur = jQuery.extend( {}, udataOld ); + + data_user.set( dest, udataCur ); + } +} + +function getAll( context, tag ) { + var ret = context.getElementsByTagName ? context.getElementsByTagName( tag || "*" ) : + context.querySelectorAll ? context.querySelectorAll( tag || "*" ) : + []; + + return tag === undefined || tag && jQuery.nodeName( context, tag ) ? + jQuery.merge( [ context ], ret ) : + ret; +} + +// 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; + } +} + +jQuery.extend({ + 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: http://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; + }, + + buildFragment: function( elems, context, scripts, selection ) { + 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 ( jQuery.type( elem ) === "object" ) { + // Support: QtWebKit, PhantomJS + // 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 ] + elem.replace( rxhtmlTag, "<$1></$2>" ) + wrap[ 2 ]; + + // Descend through wrappers to the right content + j = wrap[ 0 ]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Support: QtWebKit, PhantomJS + // 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++ ]) ) { + + // #4087 - If origin and destination elements are the same, and this is + // that element, do not do anything + if ( selection && jQuery.inArray( elem, selection ) !== -1 ) { + 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; + }, + + cleanData: function( elems ) { + var data, elem, type, key, + special = jQuery.event.special, + i = 0; + + for ( ; (elem = elems[ i ]) !== undefined; i++ ) { + if ( jQuery.acceptData( elem ) ) { + key = elem[ data_priv.expando ]; + + if ( key && (data = data_priv.cache[ key ]) ) { + 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 ); + } + } + } + if ( data_priv.cache[ key ] ) { + // Discard any remaining `private` data + delete data_priv.cache[ key ]; + } + } + } + // Discard any remaining `user` data + delete data_user.cache[ elem[ data_user.expando ] ]; + } + } +}); + +jQuery.fn.extend({ + 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 this.domManip( arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.appendChild( elem ); + } + }); + }, + + prepend: function() { + return this.domManip( 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 this.domManip( arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + }); + }, + + after: function() { + return this.domManip( arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + }); + }, + + remove: function( selector, keepData /* Internal Use Only */ ) { + var elem, + elems = selector ? jQuery.filter( selector, this ) : this, + i = 0; + + for ( ; (elem = elems[i]) != null; i++ ) { + if ( !keepData && elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem ) ); + } + + if ( elem.parentNode ) { + if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) { + setGlobalEval( getAll( elem, "script" ) ); + } + elem.parentNode.removeChild( elem ); + } + } + + return this; + }, + + 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 = value.replace( rxhtmlTag, "<$1></$2>" ); + + 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 arg = arguments[ 0 ]; + + // Make the changes, replacing each context element with the new content + this.domManip( arguments, function( elem ) { + arg = this.parentNode; + + jQuery.cleanData( getAll( this ) ); + + if ( arg ) { + arg.replaceChild( elem, this ); + } + }); + + // Force removal if there was no new content (e.g., from empty arguments) + return arg && (arg.length || arg.nodeType) ? this : this.remove(); + }, + + detach: function( selector ) { + return this.remove( selector, true ); + }, + + domManip: function( args, callback ) { + + // Flatten any nested arrays + args = concat.apply( [], args ); + + var fragment, first, scripts, hasScripts, node, doc, + i = 0, + l = this.length, + set = this, + iNoClone = l - 1, + value = args[ 0 ], + isFunction = jQuery.isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( isFunction || + ( l > 1 && typeof value === "string" && + !support.checkClone && rchecked.test( value ) ) ) { + return this.each(function( index ) { + var self = set.eq( index ); + if ( isFunction ) { + args[ 0 ] = value.call( this, index, self.html() ); + } + self.domManip( args, callback ); + }); + } + + if ( l ) { + fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + if ( first ) { + 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: QtWebKit + // jQuery.merge because push.apply(_, arraylike) throws + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( this[ 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 || "" ) && + !data_priv.access( node, "globalEval" ) && jQuery.contains( doc, node ) ) { + + if ( node.src ) { + // Optional AJAX dependency, but won't run scripts if not present + if ( jQuery._evalUrl ) { + jQuery._evalUrl( node.src ); + } + } else { + jQuery.globalEval( node.textContent.replace( rcleanScript, "" ) ); + } + } + } + } + } + } + + return this; + } +}); + +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: QtWebKit + // .get() because push.apply(_, arraylike) throws + push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +}); + + +var iframe, + elemdisplay = {}; + +/** + * Retrieve the actual display of a element + * @param {String} name nodeName of the element + * @param {Object} doc Document object + */ +// Called only from within defaultDisplay +function actualDisplay( name, doc ) { + var style, + elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ), + + // getDefaultComputedStyle might be reliably used only on attached element + display = window.getDefaultComputedStyle && ( style = window.getDefaultComputedStyle( elem[ 0 ] ) ) ? + + // Use of this method is a temporary fix (more like optimization) until something better comes along, + // since it was removed from specification and supported only in FF + style.display : jQuery.css( elem[ 0 ], "display" ); + + // We don't have any data stored on the element, + // so use "detach" method as fast way to get rid of the element + elem.detach(); + + return display; +} + +/** + * Try to determine the default display value of an element + * @param {String} nodeName + */ +function defaultDisplay( nodeName ) { + var doc = document, + display = elemdisplay[ nodeName ]; + + if ( !display ) { + display = actualDisplay( nodeName, doc ); + + // If the simple way fails, read from inside an iframe + if ( display === "none" || !display ) { + + // Use the already-created iframe if possible + iframe = (iframe || jQuery( "<iframe frameborder='0' width='0' height='0'/>" )).appendTo( doc.documentElement ); + + // Always write a new HTML skeleton so Webkit and Firefox don't choke on reuse + doc = iframe[ 0 ].contentDocument; + + // Support: IE + doc.write(); + doc.close(); + + display = actualDisplay( nodeName, doc ); + iframe.detach(); + } + + // Store the correct default display + elemdisplay[ nodeName ] = display; + } + + return display; +} +var rmargin = (/^margin/); + +var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ); + +var getStyles = function( elem ) { + // Support: IE<=11+, Firefox<=30+ (#15098, #14150) + // IE throws on elements created in popups + // FF meanwhile throws on frame elements through "defaultView.getComputedStyle" + if ( elem.ownerDocument.defaultView.opener ) { + return elem.ownerDocument.defaultView.getComputedStyle( elem, null ); + } + + return window.getComputedStyle( elem, null ); + }; + + + +function curCSS( elem, name, computed ) { + var width, minWidth, maxWidth, ret, + style = elem.style; + + computed = computed || getStyles( elem ); + + // Support: IE9 + // getPropertyValue is only needed for .css('filter') (#12537) + if ( computed ) { + ret = computed.getPropertyValue( name ) || computed[ name ]; + } + + if ( computed ) { + + if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) { + ret = jQuery.style( elem, name ); + } + + // Support: iOS < 6 + // A tribute to the "awesome hack by Dean Edwards" + // iOS < 6 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels + // this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values + if ( rnumnonpx.test( ret ) && rmargin.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 + // 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 ); + } + }; +} + + +(function() { + var pixelPositionVal, boxSizingReliableVal, + docElem = document.documentElement, + container = document.createElement( "div" ), + div = document.createElement( "div" ); + + if ( !div.style ) { + return; + } + + // Support: IE9-11+ + // 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"; + + container.style.cssText = "border:0;width:0;height:0;top:0;left:-9999px;margin-top:1px;" + + "position:absolute"; + container.appendChild( div ); + + // Executing both pixelPosition & boxSizingReliable tests require only one layout + // so they're executed at the same time to save the second computation. + function computePixelPositionAndBoxSizingReliable() { + div.style.cssText = + // Support: Firefox<29, Android 2.3 + // Vendor-prefix box-sizing + "-webkit-box-sizing:border-box;-moz-box-sizing:border-box;" + + "box-sizing:border-box;display:block;margin-top:1%;top:1%;" + + "border:1px;padding:1px;width:4px;position:absolute"; + div.innerHTML = ""; + docElem.appendChild( container ); + + var divStyle = window.getComputedStyle( div, null ); + pixelPositionVal = divStyle.top !== "1%"; + boxSizingReliableVal = divStyle.width === "4px"; + + docElem.removeChild( container ); + } + + // Support: node.js jsdom + // Don't assume that getComputedStyle is a property of the global object + if ( window.getComputedStyle ) { + jQuery.extend( support, { + pixelPosition: function() { + + // This test is executed only once but we still do memoizing + // since we can use the boxSizingReliable pre-computing. + // No need to check if the test was already performed, though. + computePixelPositionAndBoxSizingReliable(); + return pixelPositionVal; + }, + boxSizingReliable: function() { + if ( boxSizingReliableVal == null ) { + computePixelPositionAndBoxSizingReliable(); + } + return boxSizingReliableVal; + }, + reliableMarginRight: function() { + + // Support: Android 2.3 + // Check if div with explicit width and no margin-right incorrectly + // gets computed margin-right based on width of container. (#3333) + // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right + // This support function is only executed once so no memoizing is needed. + var ret, + marginDiv = div.appendChild( document.createElement( "div" ) ); + + // Reset CSS: box-sizing; display; margin; border; padding + marginDiv.style.cssText = div.style.cssText = + // Support: Firefox<29, Android 2.3 + // Vendor-prefix box-sizing + "-webkit-box-sizing:content-box;-moz-box-sizing:content-box;" + + "box-sizing:content-box;display:block;margin:0;border:0;padding:0"; + marginDiv.style.marginRight = marginDiv.style.width = "0"; + div.style.width = "1px"; + docElem.appendChild( container ); + + ret = !parseFloat( window.getComputedStyle( marginDiv, null ).marginRight ); + + docElem.removeChild( container ); + div.removeChild( marginDiv ); + + return ret; + } + }); + } +})(); + + +// A method for quickly swapping in/out CSS properties to get correct calculations. +jQuery.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; +}; + + +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]).+)/, + rnumsplit = new RegExp( "^(" + pnum + ")(.*)$", "i" ), + rrelNum = new RegExp( "^([+-])=(" + pnum + ")", "i" ), + + cssShow = { position: "absolute", visibility: "hidden", display: "block" }, + cssNormalTransform = { + letterSpacing: "0", + fontWeight: "400" + }, + + cssPrefixes = [ "Webkit", "O", "Moz", "ms" ]; + +// Return a css property mapped to a potentially vendor prefixed property +function vendorPropName( style, name ) { + + // Shortcut for names that are not vendor prefixed + if ( name in style ) { + return name; + } + + // Check for vendor prefixed names + var capName = name[0].toUpperCase() + name.slice(1), + origName = name, + i = cssPrefixes.length; + + while ( i-- ) { + name = cssPrefixes[ i ] + capName; + if ( name in style ) { + return name; + } + } + + return origName; +} + +function setPositiveNumber( elem, value, subtract ) { + var matches = rnumsplit.exec( value ); + return matches ? + // Guard against undefined "subtract", e.g., when used as in cssHooks + Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) : + value; +} + +function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) { + var i = extra === ( isBorderBox ? "border" : "content" ) ? + // If we already have the right measurement, avoid augmentation + 4 : + // Otherwise initialize for horizontal or vertical properties + name === "width" ? 1 : 0, + + val = 0; + + for ( ; i < 4; i += 2 ) { + // Both box models exclude margin, so add it if we want it + if ( extra === "margin" ) { + val += jQuery.css( elem, extra + cssExpand[ i ], true, styles ); + } + + if ( isBorderBox ) { + // border-box includes padding, so remove it if we want content + if ( extra === "content" ) { + val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + } + + // At this point, extra isn't border nor margin, so remove border + if ( extra !== "margin" ) { + val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } else { + // At this point, extra isn't content, so add padding + val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + + // At this point, extra isn't content nor padding, so add border + if ( extra !== "padding" ) { + val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } + } + + return val; +} + +function getWidthOrHeight( elem, name, extra ) { + + // Start with offset property, which is equivalent to the border-box value + var valueIsBorderBox = true, + val = name === "width" ? elem.offsetWidth : elem.offsetHeight, + styles = getStyles( elem ), + isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; + + // Some non-html elements return undefined for offsetWidth, so check for null/undefined + // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285 + // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668 + if ( val <= 0 || val == null ) { + // Fall back to computed then uncomputed css if necessary + val = curCSS( elem, name, styles ); + if ( val < 0 || val == null ) { + val = elem.style[ name ]; + } + + // Computed unit is not pixels. Stop here and return. + if ( rnumnonpx.test(val) ) { + return val; + } + + // Check for style in case a browser which returns unreliable values + // for getComputedStyle silently falls back to the reliable elem.style + valueIsBorderBox = isBorderBox && + ( support.boxSizingReliable() || val === elem.style[ name ] ); + + // Normalize "", auto, and prepare for extra + val = parseFloat( val ) || 0; + } + + // Use the active box-sizing model to add/subtract irrelevant styles + return ( val + + augmentWidthOrHeight( + elem, + name, + extra || ( isBorderBox ? "border" : "content" ), + valueIsBorderBox, + styles + ) + ) + "px"; +} + +function showHide( elements, show ) { + var display, elem, hidden, + values = [], + index = 0, + length = elements.length; + + for ( ; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + + values[ index ] = data_priv.get( elem, "olddisplay" ); + display = elem.style.display; + if ( show ) { + // Reset the inline display of this element to learn if it is + // being hidden by cascaded rules or not + if ( !values[ index ] && display === "none" ) { + elem.style.display = ""; + } + + // Set elements which have been overridden with display: none + // in a stylesheet to whatever the default browser style is + // for such an element + if ( elem.style.display === "" && isHidden( elem ) ) { + values[ index ] = data_priv.access( elem, "olddisplay", defaultDisplay(elem.nodeName) ); + } + } else { + hidden = isHidden( elem ); + + if ( display !== "none" || !hidden ) { + data_priv.set( elem, "olddisplay", hidden ? display : jQuery.css( elem, "display" ) ); + } + } + } + + // Set the display of most of the elements in a second loop + // to avoid the constant reflow + for ( index = 0; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + if ( !show || elem.style.display === "none" || elem.style.display === "" ) { + elem.style.display = show ? values[ index ] || "" : "none"; + } + } + + return elements; +} + +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: { + "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: { + "float": "cssFloat" + }, + + // 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 = jQuery.camelCase( name ), + style = elem.style; + + name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, 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 = rrelNum.exec( value )) ) { + value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) ); + // 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, add 'px' to the (except for certain CSS properties) + if ( type === "number" && !jQuery.cssNumber[ origName ] ) { + value += "px"; + } + + // Support: IE9-11+ + // 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 ) { + 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 = jQuery.camelCase( name ); + + // Make sure that we're working with the right name + name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, 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 || jQuery.isNumeric( num ) ? num || 0 : val; + } + return val; + } +}); + +jQuery.each([ "height", "width" ], function( i, name ) { + jQuery.cssHooks[ name ] = { + 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" ) ) && elem.offsetWidth === 0 ? + jQuery.swap( elem, cssShow, function() { + return getWidthOrHeight( elem, name, extra ); + }) : + getWidthOrHeight( elem, name, extra ); + } + }, + + set: function( elem, value, extra ) { + var styles = extra && getStyles( elem ); + return setPositiveNumber( elem, value, extra ? + augmentWidthOrHeight( + elem, + name, + extra, + jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + styles + ) : 0 + ); + } + }; +}); + +// Support: Android 2.3 +jQuery.cssHooks.marginRight = addGetHookIf( support.reliableMarginRight, + function( elem, computed ) { + if ( computed ) { + return jQuery.swap( elem, { "display": "inline-block" }, + curCSS, [ elem, "marginRight" ] ); + } + } +); + +// 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 ( !rmargin.test( prefix ) ) { + 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 ( jQuery.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 ); + }, + 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 ( isHidden( this ) ) { + jQuery( this ).show(); + } else { + jQuery( this ).hide(); + } + }); + } +}); + + +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 || "swing"; + 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; + + if ( tween.elem[ tween.prop ] != null && + (!tween.elem.style || 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.style && ( 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: IE9 +// 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; + } +}; + +jQuery.fx = Tween.prototype.init; + +// Back Compat <1.8 extension point +jQuery.fx.step = {}; + + + + +var + fxNow, timerId, + rfxtypes = /^(?:toggle|show|hide)$/, + rfxnum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ), + rrun = /queueHooks$/, + animationPrefilters = [ defaultPrefilter ], + tweeners = { + "*": [ function( prop, value ) { + var tween = this.createTween( prop, value ), + target = tween.cur(), + parts = rfxnum.exec( value ), + unit = parts && parts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), + + // Starting value computation is required for potential unit mismatches + start = ( jQuery.cssNumber[ prop ] || unit !== "px" && +target ) && + rfxnum.exec( jQuery.css( tween.elem, prop ) ), + scale = 1, + maxIterations = 20; + + if ( start && start[ 3 ] !== unit ) { + // Trust units reported by jQuery.css + unit = unit || start[ 3 ]; + + // Make sure we update the tween properties later on + parts = parts || []; + + // Iteratively approximate from a nonzero starting point + start = +target || 1; + + do { + // If previous iteration zeroed out, double until we get *something*. + // Use string for doubling so we don't accidentally see scale as unchanged below + scale = scale || ".5"; + + // Adjust and apply + start = start / scale; + jQuery.style( tween.elem, prop, start + unit ); + + // Update scale, tolerating zero or NaN from tween.cur(), + // break the loop if scale is unchanged or perfect, or if we've just had enough + } while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations ); + } + + // Update tween properties + if ( parts ) { + start = tween.start = +start || +target || 0; + tween.unit = unit; + // If a +=/-= token was provided, we're doing a relative animation + tween.end = parts[ 1 ] ? + start + ( parts[ 1 ] + 1 ) * parts[ 2 ] : + +parts[ 2 ]; + } + + return tween; + } ] + }; + +// Animations created synchronously will run synchronously +function createFxNow() { + setTimeout(function() { + fxNow = undefined; + }); + return ( fxNow = jQuery.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 = ( tweeners[ prop ] || [] ).concat( 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 ) { + /* jshint validthis: true */ + var prop, value, toggle, tween, hooks, oldfire, display, checkDisplay, + anim = this, + orig = {}, + style = elem.style, + hidden = elem.nodeType && isHidden( elem ), + dataShow = data_priv.get( elem, "fxshow" ); + + // Handle queue: false promises + 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(); + } + }); + }); + } + + // Height/width overflow pass + if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) { + // Make sure that nothing sneaks out + // Record all 3 overflow attributes because IE9-10 do not + // change the overflow attribute when overflowX and + // overflowY are set to the same value + opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; + + // Set display property to inline-block for height/width + // animations on inline elements that are having width/height animated + display = jQuery.css( elem, "display" ); + + // Test default display if display is currently "none" + checkDisplay = display === "none" ? + data_priv.get( elem, "olddisplay" ) || defaultDisplay( elem.nodeName ) : display; + + if ( checkDisplay === "inline" && jQuery.css( elem, "float" ) === "none" ) { + 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 ]; + }); + } + + // show/hide pass + for ( prop in props ) { + value = props[ prop ]; + if ( rfxtypes.exec( value ) ) { + delete props[ prop ]; + toggle = toggle || value === "toggle"; + if ( value === ( hidden ? "hide" : "show" ) ) { + + // If there is dataShow left over from a stopped hide or show and we are going to proceed with show, we should pretend to be hidden + if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) { + hidden = true; + } else { + continue; + } + } + orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop ); + + // Any non-fx value stops us from restoring the original display value + } else { + display = undefined; + } + } + + if ( !jQuery.isEmptyObject( orig ) ) { + if ( dataShow ) { + if ( "hidden" in dataShow ) { + hidden = dataShow.hidden; + } + } else { + dataShow = data_priv.access( elem, "fxshow", {} ); + } + + // Store state if its toggle - enables .stop().toggle() to "reverse" + if ( toggle ) { + dataShow.hidden = !hidden; + } + if ( hidden ) { + jQuery( elem ).show(); + } else { + anim.done(function() { + jQuery( elem ).hide(); + }); + } + anim.done(function() { + var prop; + + data_priv.remove( elem, "fxshow" ); + for ( prop in orig ) { + jQuery.style( elem, prop, orig[ prop ] ); + } + }); + for ( prop in orig ) { + tween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim ); + + if ( !( prop in dataShow ) ) { + dataShow[ prop ] = tween.start; + if ( hidden ) { + tween.end = tween.start; + tween.start = prop === "width" || prop === "height" ? 1 : 0; + } + } + } + + // If this is a noop like .hide().hide(), restore an overwritten display value + } else if ( (display === "none" ? defaultDisplay( elem.nodeName ) : display) === "inline" ) { + style.display = display; + } +} + +function propFilter( props, specialEasing ) { + var index, name, easing, value, hooks; + + // camelCase, specialEasing and expand cssHook pass + for ( index in props ) { + name = jQuery.camelCase( index ); + easing = specialEasing[ name ]; + value = props[ index ]; + if ( jQuery.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 = animationPrefilters.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 + // 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 ( percent < 1 && length ) { + return remaining; + } else { + deferred.resolveWith( elem, [ animation ] ); + return false; + } + }, + animation = deferred.promise({ + elem: elem, + props: jQuery.extend( {}, properties ), + opts: jQuery.extend( true, { specialEasing: {} }, 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.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 = animationPrefilters[ index ].call( animation, elem, props, animation.opts ); + if ( result ) { + return result; + } + } + + jQuery.map( props, createTween, animation ); + + if ( jQuery.isFunction( animation.opts.start ) ) { + animation.opts.start.call( elem, animation ); + } + + jQuery.fx.timer( + jQuery.extend( tick, { + elem: elem, + anim: animation, + queue: animation.opts.queue + }) + ); + + // attach callbacks from options + return animation.progress( animation.opts.progress ) + .done( animation.opts.done, animation.opts.complete ) + .fail( animation.opts.fail ) + .always( animation.opts.always ); +} + +jQuery.Animation = jQuery.extend( Animation, { + + tweener: function( props, callback ) { + if ( jQuery.isFunction( props ) ) { + callback = props; + props = [ "*" ]; + } else { + props = props.split(" "); + } + + var prop, + index = 0, + length = props.length; + + for ( ; index < length ; index++ ) { + prop = props[ index ]; + tweeners[ prop ] = tweeners[ prop ] || []; + tweeners[ prop ].unshift( callback ); + } + }, + + prefilter: function( callback, prepend ) { + if ( prepend ) { + animationPrefilters.unshift( callback ); + } else { + animationPrefilters.push( callback ); + } + } +}); + +jQuery.speed = function( speed, easing, fn ) { + var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { + complete: fn || !fn && easing || + jQuery.isFunction( speed ) && speed, + duration: speed, + easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing + }; + + opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration : + opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ 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 ( jQuery.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( isHidden ).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 || data_priv.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 = data_priv.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 = data_priv.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 = jQuery.now(); + + for ( ; i < timers.length; i++ ) { + timer = timers[ i ]; + // Checks the timer has not already been removed + 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 ); + if ( timer() ) { + jQuery.fx.start(); + } else { + jQuery.timers.pop(); + } +}; + +jQuery.fx.interval = 13; + +jQuery.fx.start = function() { + if ( !timerId ) { + timerId = setInterval( jQuery.fx.tick, jQuery.fx.interval ); + } +}; + +jQuery.fx.stop = function() { + clearInterval( timerId ); + timerId = null; +}; + +jQuery.fx.speeds = { + slow: 600, + fast: 200, + // Default speed + _default: 400 +}; + + +// Based off of the plugin by Clint Helfers, with permission. +// 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 = setTimeout( next, time ); + hooks.stop = function() { + clearTimeout( timeout ); + }; + }); +}; + + +(function() { + var input = document.createElement( "input" ), + select = document.createElement( "select" ), + opt = select.appendChild( document.createElement( "option" ) ); + + input.type = "checkbox"; + + // Support: iOS<=5.1, Android<=4.2+ + // Default value for a checkbox should be "on" + support.checkOn = input.value !== ""; + + // Support: IE<=11+ + // Must access selectedIndex to make default options select + support.optSelected = opt.selected; + + // Support: Android<=2.3 + // Options inside disabled selects are incorrectly marked as disabled + select.disabled = true; + support.optDisabled = !opt.disabled; + + // Support: IE<=11+ + // 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 nodeHook, 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 hooks, ret, + nType = elem.nodeType; + + // don't get/set attributes on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === strundefined ) { + return jQuery.prop( elem, name, value ); + } + + // All attributes are lowercase + // Grab necessary hook if one is defined + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + name = name.toLowerCase(); + hooks = jQuery.attrHooks[ name ] || + ( jQuery.expr.match.bool.test( name ) ? boolHook : nodeHook ); + } + + if ( value !== undefined ) { + + if ( value === null ) { + jQuery.removeAttr( elem, name ); + + } else if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + elem.setAttribute( name, value + "" ); + return value; + } + + } else if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { + return ret; + + } else { + ret = jQuery.find.attr( elem, name ); + + // Non-existent attributes return null, we normalize to undefined + return ret == null ? + undefined : + ret; + } + }, + + removeAttr: function( elem, value ) { + var name, propName, + i = 0, + attrNames = value && value.match( rnotwhite ); + + if ( attrNames && elem.nodeType === 1 ) { + while ( (name = attrNames[i++]) ) { + propName = jQuery.propFix[ name ] || name; + + // Boolean attributes get special treatment (#10870) + if ( jQuery.expr.match.bool.test( name ) ) { + // Set corresponding property to false + elem[ propName ] = false; + } + + elem.removeAttribute( name ); + } + } + }, + + attrHooks: { + type: { + set: function( elem, value ) { + if ( !support.radioValue && value === "radio" && + jQuery.nodeName( elem, "input" ) ) { + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + } + } +}); + +// 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; + if ( !isXML ) { + // Avoid an infinite loop by temporarily removing this function from the getter + handle = attrHandle[ name ]; + attrHandle[ name ] = ret; + ret = getter( elem, name, isXML ) != null ? + name.toLowerCase() : + null; + attrHandle[ name ] = handle; + } + return ret; + }; +}); + + + + +var rfocusable = /^(?:input|select|textarea|button)$/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({ + propFix: { + "for": "htmlFor", + "class": "className" + }, + + prop: function( elem, name, value ) { + var ret, hooks, notxml, + nType = elem.nodeType; + + // Don't get/set properties on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + if ( notxml ) { + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + return hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ? + ret : + ( elem[ name ] = value ); + + } else { + return hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ? + ret : + elem[ name ]; + } + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + return elem.hasAttribute( "tabindex" ) || rfocusable.test( elem.nodeName ) || elem.href ? + elem.tabIndex : + -1; + } + } + } +}); + +if ( !support.optSelected ) { + jQuery.propHooks.selected = { + get: function( elem ) { + var parent = elem.parentNode; + if ( parent && parent.parentNode ) { + parent.parentNode.selectedIndex; + } + return null; + } + }; +} + +jQuery.each([ + "tabIndex", + "readOnly", + "maxLength", + "cellSpacing", + "cellPadding", + "rowSpan", + "colSpan", + "useMap", + "frameBorder", + "contentEditable" +], function() { + jQuery.propFix[ this.toLowerCase() ] = this; +}); + + + + +var rclass = /[\t\r\n\f]/g; + +jQuery.fn.extend({ + addClass: function( value ) { + var classes, elem, cur, clazz, j, finalValue, + proceed = typeof value === "string" && value, + i = 0, + len = this.length; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).addClass( value.call( this, j, this.className ) ); + }); + } + + if ( proceed ) { + // The disjunction here is for better compressibility (see removeClass) + classes = ( value || "" ).match( rnotwhite ) || []; + + for ( ; i < len; i++ ) { + elem = this[ i ]; + cur = elem.nodeType === 1 && ( elem.className ? + ( " " + elem.className + " " ).replace( rclass, " " ) : + " " + ); + + if ( cur ) { + j = 0; + while ( (clazz = classes[j++]) ) { + if ( cur.indexOf( " " + clazz + " " ) < 0 ) { + cur += clazz + " "; + } + } + + // only assign if different to avoid unneeded rendering. + finalValue = jQuery.trim( cur ); + if ( elem.className !== finalValue ) { + elem.className = finalValue; + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classes, elem, cur, clazz, j, finalValue, + proceed = arguments.length === 0 || typeof value === "string" && value, + i = 0, + len = this.length; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).removeClass( value.call( this, j, this.className ) ); + }); + } + if ( proceed ) { + classes = ( value || "" ).match( rnotwhite ) || []; + + for ( ; i < len; i++ ) { + elem = this[ i ]; + // This expression is here for better compressibility (see addClass) + cur = elem.nodeType === 1 && ( elem.className ? + ( " " + elem.className + " " ).replace( rclass, " " ) : + "" + ); + + if ( cur ) { + j = 0; + while ( (clazz = classes[j++]) ) { + // Remove *all* instances + while ( cur.indexOf( " " + clazz + " " ) >= 0 ) { + cur = cur.replace( " " + clazz + " ", " " ); + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = value ? jQuery.trim( cur ) : ""; + if ( elem.className !== finalValue ) { + elem.className = finalValue; + } + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value; + + if ( typeof stateVal === "boolean" && type === "string" ) { + return stateVal ? this.addClass( value ) : this.removeClass( value ); + } + + if ( jQuery.isFunction( value ) ) { + return this.each(function( i ) { + jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal ); + }); + } + + return this.each(function() { + if ( type === "string" ) { + // Toggle individual class names + var className, + i = 0, + self = jQuery( this ), + classNames = value.match( rnotwhite ) || []; + + 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 ( type === strundefined || type === "boolean" ) { + if ( this.className ) { + // store className if set + data_priv.set( this, "__className__", this.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. + this.className = this.className || value === false ? "" : data_priv.get( this, "__className__" ) || ""; + } + }); + }, + + hasClass: function( selector ) { + var className = " " + selector + " ", + i = 0, + l = this.length; + for ( ; i < l; i++ ) { + if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) { + return true; + } + } + + return false; + } +}); + + + + +var rreturn = /\r/g; + +jQuery.fn.extend({ + val: function( value ) { + var hooks, ret, isFunction, + 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; + + return typeof ret === "string" ? + // Handle most common string cases + ret.replace(rreturn, "") : + // Handle cases where value is null/undef or number + ret == null ? "" : ret; + } + + return; + } + + isFunction = jQuery.isFunction( value ); + + return this.each(function( i ) { + var val; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( isFunction ) { + 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 ( jQuery.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: IE10-11+ + // option.text throws exceptions (#14686, #14858) + jQuery.trim( jQuery.text( elem ) ); + } + }, + select: { + get: function( elem ) { + var value, option, + options = elem.options, + index = elem.selectedIndex, + one = elem.type === "select-one" || index < 0, + values = one ? null : [], + max = one ? index + 1 : options.length, + i = index < 0 ? + max : + one ? index : 0; + + // Loop through all the selected options + for ( ; i < max; i++ ) { + option = options[ i ]; + + // IE6-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 + ( support.optDisabled ? !option.disabled : option.getAttribute( "disabled" ) === null ) && + ( !option.parentNode.disabled || !jQuery.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 ]; + if ( (option.selected = jQuery.inArray( option.value, values ) >= 0) ) { + optionSet = true; + } + } + + // 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 ( jQuery.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 ); + } + } + }; + if ( !support.checkOn ) { + jQuery.valHooks[ this ].get = function( elem ) { + return elem.getAttribute("value") === null ? "on" : elem.value; + }; + } +}); + + + + +// Return jQuery for attributes-only inclusion + + +jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " + + "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + + "change select submit keydown keypress keyup error 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 ); + }, + + 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 ); + } +}); + + +var nonce = jQuery.now(); + +var rquery = (/\?/); + + + +// Support: Android 2.3 +// Workaround failure to string-cast null input +jQuery.parseJSON = function( data ) { + return JSON.parse( data + "" ); +}; + + +// Cross-browser xml parsing +jQuery.parseXML = function( data ) { + var xml, tmp; + if ( !data || typeof data !== "string" ) { + return null; + } + + // Support: IE9 + try { + tmp = new DOMParser(); + xml = tmp.parseFromString( data, "text/xml" ); + } catch ( e ) { + xml = undefined; + } + + if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) { + jQuery.error( "Invalid XML: " + data ); + } + return xml; +}; + + +var + rhash = /#.*$/, + rts = /([?&])_=[^&]*/, + rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg, + // #7653, #8125, #8152: local protocol detection + rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, + rnoContent = /^(?:GET|HEAD)$/, + rprotocol = /^\/\//, + rurl = /^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/, + + /* 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( "*" ), + + // Document location + ajaxLocation = window.location.href, + + // Segment location into parts + ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || []; + +// 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( rnotwhite ) || []; + + if ( jQuery.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: ajaxLocation, + type: "GET", + isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ), + 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: /xml/, + html: /html/, + json: /json/ + }, + + 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": jQuery.parseJSON, + + // 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, + // Cross-domain detection vars + parts, + // To know if global events are to be dispatched + fireGlobals, + // Loop variable + i, + // 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 = {}, + // The jqXHR state + state = 0, + // Default abort message + strAbort = "canceled", + // Fake xhr + jqXHR = { + readyState: 0, + + // Builds headers hashtable if needed + getResponseHeader: function( key ) { + var match; + if ( state === 2 ) { + 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 state === 2 ? responseHeadersString : null; + }, + + // Caches the header + setRequestHeader: function( name, value ) { + var lname = name.toLowerCase(); + if ( !state ) { + name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name; + requestHeaders[ name ] = value; + } + return this; + }, + + // Overrides response content-type header + overrideMimeType: function( type ) { + if ( !state ) { + s.mimeType = type; + } + return this; + }, + + // Status-dependent callbacks + statusCode: function( map ) { + var code; + if ( map ) { + if ( state < 2 ) { + for ( code in map ) { + // Lazy-add the new callback in a way that preserves old ones + statusCode[ code ] = [ statusCode[ code ], map[ code ] ]; + } + } else { + // Execute the appropriate callbacks + jqXHR.always( map[ jqXHR.status ] ); + } + } + 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 ).complete = completeDeferred.add; + jqXHR.success = jqXHR.done; + jqXHR.error = jqXHR.fail; + + // Remove hash character (#7531: and string promotion) + // 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 || ajaxLocation ) + "" ).replace( rhash, "" ) + .replace( rprotocol, ajaxLocParts[ 1 ] + "//" ); + + // Alias method option to type as per ticket #12004 + s.type = options.method || options.type || s.method || s.type; + + // Extract dataTypes list + s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().match( rnotwhite ) || [ "" ]; + + // A cross-domain request is in order when we have a protocol:host:port mismatch + if ( s.crossDomain == null ) { + parts = rurl.exec( s.url.toLowerCase() ); + s.crossDomain = !!( parts && + ( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] || + ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? "80" : "443" ) ) !== + ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? "80" : "443" ) ) ) + ); + } + + // 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 ( state === 2 ) { + 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 + cacheURL = s.url; + + // More options handling for requests with no content + if ( !s.hasContent ) { + + // If data is available, append data to url + if ( s.data ) { + cacheURL = ( s.url += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data ); + // #9682: remove data so that it's not used in an eventual retry + delete s.data; + } + + // Add anti-cache in url if needed + if ( s.cache === false ) { + s.url = rts.test( cacheURL ) ? + + // If there is already a '_' parameter, set its value + cacheURL.replace( rts, "$1_=" + nonce++ ) : + + // Otherwise add one to the end + cacheURL + ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + nonce++; + } + } + + // 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 || state === 2 ) ) { + // Abort if not done already and return + return jqXHR.abort(); + } + + // Aborting is no longer a cancellation + strAbort = "abort"; + + // Install callbacks on deferreds + for ( i in { success: 1, error: 1, complete: 1 } ) { + jqXHR[ i ]( s[ i ] ); + } + + // 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 ] ); + } + // Timeout + if ( s.async && s.timeout > 0 ) { + timeoutTimer = setTimeout(function() { + jqXHR.abort("timeout"); + }, s.timeout ); + } + + try { + state = 1; + transport.send( requestHeaders, done ); + } catch ( e ) { + // Propagate exception as error if not done + if ( state < 2 ) { + done( -1, e ); + // Simply rethrow otherwise + } else { + throw e; + } + } + } + + // Callback for when everything is done + function done( status, nativeStatusText, responses, headers ) { + var isSuccess, success, error, response, modified, + statusText = nativeStatusText; + + // Called once + if ( state === 2 ) { + return; + } + + // State is "done" now + state = 2; + + // Clear timeout if it exists + if ( timeoutTimer ) { + 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 ( jQuery.isFunction( data ) ) { + type = type || callback; + callback = data; + data = undefined; + } + + return jQuery.ajax({ + url: url, + type: method, + dataType: type, + data: data, + success: callback + }); + }; +}); + + +jQuery._evalUrl = function( url ) { + return jQuery.ajax({ + url: url, + type: "GET", + dataType: "script", + async: false, + global: false, + "throws": true + }); +}; + + +jQuery.fn.extend({ + wrapAll: function( html ) { + var wrap; + + if ( jQuery.isFunction( html ) ) { + return this.each(function( i ) { + jQuery( this ).wrapAll( html.call(this, i) ); + }); + } + + if ( 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 ( jQuery.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 isFunction = jQuery.isFunction( html ); + + return this.each(function( i ) { + jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html ); + }); + }, + + unwrap: function() { + return this.parent().each(function() { + if ( !jQuery.nodeName( this, "body" ) ) { + jQuery( this ).replaceWith( this.childNodes ); + } + }).end(); + } +}); + + +jQuery.expr.filters.hidden = function( elem ) { + // Support: Opera <= 12.12 + // Opera reports offsetWidths and offsetHeights less than zero on some elements + return elem.offsetWidth <= 0 && elem.offsetHeight <= 0; +}; +jQuery.expr.filters.visible = function( elem ) { + return !jQuery.expr.filters.hidden( elem ); +}; + + + + +var r20 = /%20/g, + 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 ( jQuery.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" ? i : "" ) + "]", v, traditional, add ); + } + }); + + } else if ( !traditional && jQuery.type( 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, value ) { + // If value is a function, invoke it and return its value + value = jQuery.isFunction( value ) ? value() : ( value == null ? "" : value ); + s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value ); + }; + + // Set traditional to true for jQuery <= 1.3.2 behavior. + if ( traditional === undefined ) { + traditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional; + } + + // If an array was passed in, assume that it is an array of form elements. + if ( jQuery.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( "&" ).replace( r20, "+" ); +}; + +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(); + + return val == null ? + null : + jQuery.isArray( val ) ? + jQuery.map( val, function( val ) { + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + }) : + { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + }).get(); + } +}); + + +jQuery.ajaxSettings.xhr = function() { + try { + return new XMLHttpRequest(); + } catch( e ) {} +}; + +var xhrId = 0, + xhrCallbacks = {}, + xhrSuccessStatus = { + // file protocol always yields status code 0, assume 200 + 0: 200, + // Support: IE9 + // #1450: sometimes IE returns 1223 when it should be 204 + 1223: 204 + }, + xhrSupported = jQuery.ajaxSettings.xhr(); + +// Support: IE9 +// Open requests must be manually aborted on unload (#5280) +// See https://support.microsoft.com/kb/2856746 for more info +if ( window.attachEvent ) { + window.attachEvent( "onunload", function() { + for ( var key in xhrCallbacks ) { + xhrCallbacks[ key ](); + } + }); +} + +support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported ); +support.ajax = xhrSupported = !!xhrSupported; + +jQuery.ajaxTransport(function( options ) { + var callback; + + // Cross domain only allowed if supported through XMLHttpRequest + if ( support.cors || xhrSupported && !options.crossDomain ) { + return { + send: function( headers, complete ) { + var i, + xhr = options.xhr(), + id = ++xhrId; + + 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 ) { + delete xhrCallbacks[ id ]; + callback = xhr.onload = xhr.onerror = null; + + if ( type === "abort" ) { + xhr.abort(); + } else if ( type === "error" ) { + complete( + // file: protocol always yields status 0; see #8605, #14207 + xhr.status, + xhr.statusText + ); + } else { + complete( + xhrSuccessStatus[ xhr.status ] || xhr.status, + xhr.statusText, + // Support: IE9 + // Accessing binary-data responseText throws an exception + // (#11426) + typeof xhr.responseText === "string" ? { + text: xhr.responseText + } : undefined, + xhr.getAllResponseHeaders() + ); + } + } + }; + }; + + // Listen to events + xhr.onload = callback(); + xhr.onerror = callback("error"); + + // Create the abort callback + callback = xhrCallbacks[ id ] = 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(); + } + } + }; + } +}); + + + + +// Install script dataType +jQuery.ajaxSetup({ + accepts: { + script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript" + }, + contents: { + script: /(?:java|ecma)script/ + }, + 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({ + async: true, + 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 ); + } + } + ); + 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") && 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 = jQuery.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() { + // Restore preexisting value + 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 && jQuery.isFunction( overwritten ) ) { + overwritten( responseContainer[ 0 ] ); + } + + responseContainer = overwritten = undefined; + }); + + // Delegate to script + return "script"; + } +}); + + + + +// data: 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 ( !data || typeof data !== "string" ) { + return null; + } + if ( typeof context === "boolean" ) { + keepScripts = context; + context = false; + } + context = context || document; + + var parsed = rsingleTag.exec( data ), + scripts = !keepScripts && []; + + // Single tag + if ( parsed ) { + return [ context.createElement( parsed[1] ) ]; + } + + parsed = jQuery.buildFragment( [ data ], context, scripts ); + + if ( scripts && scripts.length ) { + jQuery( scripts ).remove(); + } + + return jQuery.merge( [], parsed.childNodes ); +}; + + +// Keep a copy of the old load method +var _load = jQuery.fn.load; + +/** + * Load a url into a page + */ +jQuery.fn.load = function( url, params, callback ) { + if ( typeof url !== "string" && _load ) { + return _load.apply( this, arguments ); + } + + var selector, type, response, + self = this, + off = url.indexOf(" "); + + if ( off >= 0 ) { + selector = jQuery.trim( url.slice( off ) ); + url = url.slice( 0, off ); + } + + // If it's a function + if ( jQuery.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 + type: type, + 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 ); + + }).complete( callback && function( jqXHR, status ) { + self.each( callback, 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.filters.animated = function( elem ) { + return jQuery.grep(jQuery.timers, function( fn ) { + return elem === fn.elem; + }).length; +}; + + + + +var docElem = window.document.documentElement; + +/** + * Gets a window from an element + */ +function getWindow( elem ) { + return jQuery.isWindow( elem ) ? elem : elem.nodeType === 9 && elem.defaultView; +} + +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 ( jQuery.isFunction( options ) ) { + options = options.call( elem, i, 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: function( options ) { + if ( arguments.length ) { + return options === undefined ? + this : + this.each(function( i ) { + jQuery.offset.setOffset( this, options, i ); + }); + } + + var docElem, win, + elem = this[ 0 ], + box = { top: 0, left: 0 }, + doc = elem && elem.ownerDocument; + + if ( !doc ) { + return; + } + + docElem = doc.documentElement; + + // Make sure it's not a disconnected DOM node + if ( !jQuery.contains( docElem, elem ) ) { + return box; + } + + // Support: BlackBerry 5, iOS 3 (original iPhone) + // If we don't have gBCR, just use 0,0 rather than error + if ( typeof elem.getBoundingClientRect !== strundefined ) { + box = elem.getBoundingClientRect(); + } + win = getWindow( doc ); + return { + top: box.top + win.pageYOffset - docElem.clientTop, + left: box.left + win.pageXOffset - docElem.clientLeft + }; + }, + + position: function() { + if ( !this[ 0 ] ) { + return; + } + + var offsetParent, offset, + elem = this[ 0 ], + parentOffset = { top: 0, left: 0 }; + + // Fixed elements are offset from window (parentOffset = {top:0, left: 0}, because it is its only offset parent + if ( jQuery.css( elem, "position" ) === "fixed" ) { + // Assume getBoundingClientRect is there when computed position is fixed + offset = elem.getBoundingClientRect(); + + } else { + // Get *real* offsetParent + offsetParent = this.offsetParent(); + + // Get correct offsets + offset = this.offset(); + if ( !jQuery.nodeName( offsetParent[ 0 ], "html" ) ) { + parentOffset = offsetParent.offset(); + } + + // Add offsetParent borders + parentOffset.top += jQuery.css( offsetParent[ 0 ], "borderTopWidth", true ); + parentOffset.left += jQuery.css( offsetParent[ 0 ], "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 ) + }; + }, + + offsetParent: function() { + return this.map(function() { + var offsetParent = this.offsetParent || docElem; + + while ( offsetParent && ( !jQuery.nodeName( offsetParent, "html" ) && jQuery.css( offsetParent, "position" ) === "static" ) ) { + offsetParent = offsetParent.offsetParent; + } + + return offsetParent || docElem; + }); + } +}); + +// 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 ) { + var win = getWindow( elem ); + + if ( val === undefined ) { + return win ? win[ prop ] : elem[ method ]; + } + + if ( win ) { + win.scrollTo( + !top ? val : window.pageXOffset, + top ? val : window.pageYOffset + ); + + } else { + elem[ method ] = val; + } + }, method, val, arguments.length, null ); + }; +}); + +// Support: Safari<7+, Chrome<37+ +// Add the top/left cssHooks using jQuery.fn.position +// Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084 +// Blink bug: https://code.google.com/p/chromium/issues/detail?id=229280 +// 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 ( jQuery.isWindow( elem ) ) { + // As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there + // isn't a whole lot we can do. See pull request at this URL for discussion: + // https://github.com/jquery/jquery/pull/764 + return 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, null ); + }; + }); +}); + + +// The number of elements contained in the matched element set +jQuery.fn.size = function() { + return this.length; +}; + +jQuery.fn.andSelf = jQuery.fn.addBack; + + + + +// 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 ( typeof define === "function" && define.amd ) { + define( "jquery", [], function() { + return jQuery; + }); +} + + + + +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 ( typeof noGlobal === strundefined ) { + window.jQuery = window.$ = jQuery; +} + + + + +return jQuery; + +})); diff --git a/dependencies/main_files/lattice.js b/dependencies/main_files/lattice.js new file mode 100644 index 00000000..e918bc91 --- /dev/null +++ b/dependencies/main_files/lattice.js @@ -0,0 +1,110 @@ +/** + * Created by aghassaei on 1/16/15. + */ + + +Lattice = Backbone.Model.extend({ + + defaults: { + scale: 30.0, + translation: new THREE.Vector3(0,0,0), + rotation: new THREE.Vector3(0,0,0), + type: "octagonFace", + nodes: [], + min: new THREE.Vector3(0,0,0), + max: new THREE.Vector3(0,0,0) + }, + + //pass in fillGeometry + + initialize: function(){ + + //bind events + + this.layoutNodes(); + + }, + + layoutNodes: function(type){ + type = type || this.get("type"); + var boundingBox = this.get("fillGeometry").get("boundingBoxHelper").box; + var scale = this.get("scale"); + + var nodes; + + if (type == "octagonFace"){ + nodes = this.faceConnectedOctahedra(boundingBox, scale); + + + } else { + console.log("no type"); + } + this.set("nodes", nodes); + }, + + faceConnectedOctahedra: function(boundingBox, scale){ + + var scaleSqrt3 = scale/2*Math.sqrt(3); + var dimX = Math.round((boundingBox.max.x-boundingBox.min.x)/scale); + var dimY = Math.round((boundingBox.max.y-boundingBox.min.y)/scaleSqrt3); + var dimZ = Math.round((boundingBox.max.z-boundingBox.min.z)/scaleSqrt3); + + + var nodes = []; + + for (var x=0; x<=dimX; x++){ + if (nodes.length<=x) nodes[x] = []; + for (var y=0; y<=dimY; y++){ + if (nodes[x].length<=y) nodes[x][y] = []; + for (var z=0; z<=dimZ; z++){ + + var xOffset = 0; + if (y%2 == 1) xOffset = scale/2; + var yOffset = 0; + if (z%2 == 1) yOffset = scaleSqrt3/2; + + nodes[x][y][z] = new BeamNode(boundingBox.min.x+ xOffset + scale*x, + boundingBox.min.y + yOffset + scaleSqrt3*y, + boundingBox.min.z + z*scaleSqrt3); + } + } + } + return nodes; + }, + + fillWithParts: function(geometry){ + geometry.applyMatrix(new THREE.Matrix4().makeTranslation(-2.3580117225646973,-0.2165864259004593,0)); +// var scale = this.scale*3; +// geometry.makeScale(scale, scale, scale); + + var nodes = this.get("nodes"); + if (!this.nodesFilled(nodes)) return; + + var y=0; + var z=1; +// for (var z=1;z<=nodes[0][0].length;z++){ +// for (var y= 0;y<nodes[0].length;y++){ + for (var x=0;x<nodes.length-1;x++){ + console.log("here"); + new DmaPart(geometry, [nodes[x][y][z], nodes[x+1][y][z], nodes[x][y][z-1]]); + + } +// } +// } + + + + + + + + }, + + nodesFilled: function(nodes){ + if (!nodes) return false; + if (nodes.length == 0) return false; + if (nodes[0].length == 0) return false; + return (nodes[0][0].length > 0); + } + +}); \ No newline at end of file diff --git a/dependencies/main_files/latticeView.js b/dependencies/main_files/latticeView.js new file mode 100644 index 00000000..04c91590 --- /dev/null +++ b/dependencies/main_files/latticeView.js @@ -0,0 +1,68 @@ +/** + * Created by aghassaei on 1/16/15. + */ + +LatticeView = Backbone.View.extend({ + + events: { + }, + + el: "#threeContainer", + + controls: null, + + initialize: function(options){ + + this.three = options.three; + this.fillGeometry = options.fillGeometry; + + + //bind events + this.listenTo(this.model, "change:bounds", this.updateBuildPlanes()); + + this.buildPlanes(); + this.render(); + + }, + + updateBuildPlanes: function(){ + }, + + buildPlanes: function(){ + var xyPlaneGeo = new THREE.Geometry(); + var xzPlaneGeo = new THREE.Geometry(); + var yzPlaneGeo = new THREE.Geometry(); + + var size=50, step = 5; + for (var i=-size;i<=size;i+=step){ + xyPlaneGeo.vertices.push(new THREE.Vector3(-size, i, -size)); + xyPlaneGeo.vertices.push(new THREE.Vector3(size, i, -size)); + xyPlaneGeo.vertices.push(new THREE.Vector3(i, -size, -size)); + xyPlaneGeo.vertices.push(new THREE.Vector3(i, size, -size)); + + xzPlaneGeo.vertices.push(new THREE.Vector3(-size, i, -size)); + xzPlaneGeo.vertices.push(new THREE.Vector3(-size, i, size)); + xzPlaneGeo.vertices.push(new THREE.Vector3(-size, -size, i)); + xzPlaneGeo.vertices.push(new THREE.Vector3(-size, size, i)); + + yzPlaneGeo.vertices.push(new THREE.Vector3(-size, -size, i)); + yzPlaneGeo.vertices.push(new THREE.Vector3(size, -size, i)); + yzPlaneGeo.vertices.push(new THREE.Vector3(i, -size, -size)); + yzPlaneGeo.vertices.push(new THREE.Vector3(i, -size, size)); + } + + this.addPlane(xyPlaneGeo); +// this.addPlane(yzPlaneGeo); +// this.addPlane(xzPlaneGeo); + + }, + + addPlane: function(planeGeo){ + var plane = new THREE.Line(planeGeo, new THREE.LineBasicMaterial({color:0x000000, opacity:0.3}), THREE.LinePieces); + this.three.sceneAdd(plane); + }, + + render: function(){ + this.three.render(); + } +}); \ No newline at end of file diff --git a/dependencies/main_files/main.css b/dependencies/main_files/main.css new file mode 100644 index 00000000..9266bd8c --- /dev/null +++ b/dependencies/main_files/main.css @@ -0,0 +1,79 @@ + + +body { + overflow:hidden;/*hide scroll bar*/ +} + +.navbar { + margin-bottom:0; + z-index: 2; + border-radius: 0; +} + +.navMenu { + display: none; + z-index: 1; + position:absolute; + /*opacity:0.8;*/ + width: 100%; + left: 15px; + background-color: rgba(255,255,255,0.9); + padding-bottom: 20px; +} + +.nav li a{ + border-radius: 0!important; +} + +.navbar-header { + padding-right: 20px; +} + +.demo-row{ + padding:30px; +} + +.dropdown-menu { + margin-top: 0!important; + border-top-left-radius: 0!important; + border-top-right-radius: 0!important; +} + +nav .btn { + margin-top: 5px; +} + +.btn-file input[type=file] { + position: absolute; + top: 0; + right: 0; + min-width: 100%; + min-height: 100%; + font-size: 100px; + text-align: right; + filter: alpha(opacity=0); + opacity: 0; + outline: none; + background: white; + cursor: inherit; + display: block; +} + +.btn-file { + position: relative; + overflow: hidden; +} + +.fullWidth { + width: 100%!important; +} + +/*import menu*/ + +.slider-selection { + background: #BABABA; +} + +#STLImportStats { + display: none; +} \ No newline at end of file diff --git a/dependencies/main_files/main.js b/dependencies/main_files/main.js new file mode 100644 index 00000000..7474451b --- /dev/null +++ b/dependencies/main_files/main.js @@ -0,0 +1,43 @@ +/** + * Created by aghassaei on 1/7/15. + */ + + +$(function(){ + + window.workers = persistentWorkers(8); + + + //init threeJS + var threeModel = new ThreeModel(); + window.three = threeModel; + + //backbone models and views + var fillGeometry = new FillGeometry();//singleton, mesh to fill with lattice +// new ImportView({model: fillGeometry}); +// var fillGeometryView = new FillGeometryView({model: fillGeometry, three:threeModel}); + + + var lattice = new Lattice({fillGeometry:fillGeometry}); + var latticeView = new LatticeView({model:lattice, three:threeModel}); + + var highlightTargets = [latticeView]; + + var threeView = new ThreeView({model:threeModel, highlightTargets:highlightTargets}); + + + //first, pre load the stl + part_loadSTL(); + + function part_loadSTL(){ + var loader = new THREE.STLLoader(); + loader.addEventListener('load', part_onMeshLoad); + loader.load('data/triangleRot.stl'); + } + + function part_onMeshLoad(e){ + lattice.fillWithParts(e.content); + } + + setupNavBar(threeModel); +}); diff --git a/dependencies/main_files/meshHandle.js b/dependencies/main_files/meshHandle.js new file mode 100644 index 00000000..8fa49ce7 --- /dev/null +++ b/dependencies/main_files/meshHandle.js @@ -0,0 +1,37 @@ +/** + * Created by aghassaei on 1/18/15. + */ + +//a draggable vector that moves a mesh face, singleton for now + +function MeshHandle(three){ + //init invisible arrow and add to scene + this.arrow = new THREE.ArrowHelper(new THREE.Vector3(0,0,1), new THREE.Vector3(0,0,0), 10, 0x000000); +// this.arrow.visibility = false; +// three.sceneAdd(this.arrow); +} + +MeshHandle.prototype.updatePosition = function(origin, normal){ + this.arrow.position.set(origin); + this.arrow.setDirection(normal); +}; + +MeshHandle.prototype.setForFaces = function(faces, vertices){ + if (!faces || faces.length<1) console.warn("no faces passed in to mesh handle"); + + var mutuallyExclusiveVertices = [faces[1].a, faces[1].b, faces[1].c]; + _.each([faces[0].a, faces[0].b, faces[0].c], function(vertex){ + if (mutuallyExclusiveVertices.indexOf(vertex) == -1) mutuallyExclusiveVertices.push(vertex); + else mutuallyExclusiveVertices.remove(vertex); + }); + if (mutuallyExclusiveVertices.length != 2) console.warn("not a square face"); + var origin = numeric.mul(numeric.add(vertices[mutuallyExclusiveVertices[0]], vertices[mutuallyExclusiveVertices[1]]), 0.5); + + this.arrow.position.set(origin); + this.arrow.setDirection(faces[0].normal); + this.arrow.visibility = true; +}; + +MeshHandle.prototype.hide = function(){ + this.arrow.visibility = false; +}; \ No newline at end of file diff --git a/dependencies/main_files/navbar.js b/dependencies/main_files/navbar.js new file mode 100644 index 00000000..a97bc86a --- /dev/null +++ b/dependencies/main_files/navbar.js @@ -0,0 +1,52 @@ +/** + * Created by aghassaei on 1/7/15. + */ + + +function setupNavBar(three){ + + var allMenus = $(".navMenu"); + var allNavMenuLinks = $(".menuHoverControls"); + var allNavDropdownMenus = $(".navDropdown"); + var allNavTitles = $(".navbar-nav li a"); + + allNavMenuLinks.mouseover(function(){ + hideAllMenus(); + $(this).parent().addClass("open");//highlight + var menuId = "#" + $(this).data("menuId"); + $(menuId).show(); + }); + + var clearButton = $("#clearAll"); + clearButton.mouseout(function(){ + $(this).parent().removeClass("open"); + }); + + //clear canvas + clearButton.click(three.clearAll); + + function hideAllMenus(){ + allMenus.hide(); + allNavMenuLinks.parent().removeClass("open");//no highlight + allNavDropdownMenus.removeClass("open");//no highlight + allNavTitles.blur(); + } + + $("#threeContainer").mousedown(function(){ + hideAllMenus(); + }); + $("#mainNavLink").mouseover(function(){ + hideAllMenus(); + }); + allNavDropdownMenus.mouseover(function(){ + hideAllMenus(); + $(this).addClass("open"); + }); + + //remove nav text coloring on click + allNavTitles.click(function(e){ + e.preventDefault(); + return false; + }); + +} \ No newline at end of file diff --git a/dependencies/main_files/numeric-1.js b/dependencies/main_files/numeric-1.js new file mode 100644 index 00000000..44edb9b8 --- /dev/null +++ b/dependencies/main_files/numeric-1.js @@ -0,0 +1,4424 @@ +"use strict"; + +var numeric = (typeof exports === "undefined")?(function numeric() {}):(exports); +if(typeof global !== "undefined") { global.numeric = numeric; } + +numeric.version = "1.2.6"; + +// 1. Utility functions +numeric.bench = function bench (f,interval) { + var t1,t2,n,i; + if(typeof interval === "undefined") { interval = 15; } + n = 0.5; + t1 = new Date(); + while(1) { + n*=2; + for(i=n;i>3;i-=4) { f(); f(); f(); f(); } + while(i>0) { f(); i--; } + t2 = new Date(); + if(t2-t1 > interval) break; + } + for(i=n;i>3;i-=4) { f(); f(); f(); f(); } + while(i>0) { f(); i--; } + t2 = new Date(); + return 1000*(3*n-1)/(t2-t1); +} + +numeric._myIndexOf = (function _myIndexOf(w) { + var n = this.length,k; + for(k=0;k<n;++k) if(this[k]===w) return k; + return -1; +}); +numeric.myIndexOf = (Array.prototype.indexOf)?Array.prototype.indexOf:numeric._myIndexOf; + +numeric.Function = Function; +numeric.precision = 4; +numeric.largeArray = 50; + +numeric.prettyPrint = function prettyPrint(x) { + function fmtnum(x) { + if(x === 0) { return '0'; } + if(isNaN(x)) { return 'NaN'; } + if(x<0) { return '-'+fmtnum(-x); } + if(isFinite(x)) { + var scale = Math.floor(Math.log(x) / Math.log(10)); + var normalized = x / Math.pow(10,scale); + var basic = normalized.toPrecision(numeric.precision); + if(parseFloat(basic) === 10) { scale++; normalized = 1; basic = normalized.toPrecision(numeric.precision); } + return parseFloat(basic).toString()+'e'+scale.toString(); + } + return 'Infinity'; + } + var ret = []; + function foo(x) { + var k; + if(typeof x === "undefined") { ret.push(Array(numeric.precision+8).join(' ')); return false; } + if(typeof x === "string") { ret.push('"'+x+'"'); return false; } + if(typeof x === "boolean") { ret.push(x.toString()); return false; } + if(typeof x === "number") { + var a = fmtnum(x); + var b = x.toPrecision(numeric.precision); + var c = parseFloat(x.toString()).toString(); + var d = [a,b,c,parseFloat(b).toString(),parseFloat(c).toString()]; + for(k=1;k<d.length;k++) { if(d[k].length < a.length) a = d[k]; } + ret.push(Array(numeric.precision+8-a.length).join(' ')+a); + return false; + } + if(x === null) { ret.push("null"); return false; } + if(typeof x === "function") { + ret.push(x.toString()); + var flag = false; + for(k in x) { if(x.hasOwnProperty(k)) { + if(flag) ret.push(',\n'); + else ret.push('\n{'); + flag = true; + ret.push(k); + ret.push(': \n'); + foo(x[k]); + } } + if(flag) ret.push('}\n'); + return true; + } + if(x instanceof Array) { + if(x.length > numeric.largeArray) { ret.push('...Large Array...'); return true; } + var flag = false; + ret.push('['); + for(k=0;k<x.length;k++) { if(k>0) { ret.push(','); if(flag) ret.push('\n '); } flag = foo(x[k]); } + ret.push(']'); + return true; + } + ret.push('{'); + var flag = false; + for(k in x) { if(x.hasOwnProperty(k)) { if(flag) ret.push(',\n'); flag = true; ret.push(k); ret.push(': \n'); foo(x[k]); } } + ret.push('}'); + return true; + } + foo(x); + return ret.join(''); +} + +numeric.parseDate = function parseDate(d) { + function foo(d) { + if(typeof d === 'string') { return Date.parse(d.replace(/-/g,'/')); } + if(!(d instanceof Array)) { throw new Error("parseDate: parameter must be arrays of strings"); } + var ret = [],k; + for(k=0;k<d.length;k++) { ret[k] = foo(d[k]); } + return ret; + } + return foo(d); +} + +numeric.parseFloat = function parseFloat_(d) { + function foo(d) { + if(typeof d === 'string') { return parseFloat(d); } + if(!(d instanceof Array)) { throw new Error("parseFloat: parameter must be arrays of strings"); } + var ret = [],k; + for(k=0;k<d.length;k++) { ret[k] = foo(d[k]); } + return ret; + } + return foo(d); +} + +numeric.parseCSV = function parseCSV(t) { + var foo = t.split('\n'); + var j,k; + var ret = []; + var pat = /(([^'",]*)|('[^']*')|("[^"]*")),/g; + var patnum = /^\s*(([+-]?[0-9]+(\.[0-9]*)?(e[+-]?[0-9]+)?)|([+-]?[0-9]*(\.[0-9]+)?(e[+-]?[0-9]+)?))\s*$/; + var stripper = function(n) { return n.substr(0,n.length-1); } + var count = 0; + for(k=0;k<foo.length;k++) { + var bar = (foo[k]+",").match(pat),baz; + if(bar.length>0) { + ret[count] = []; + for(j=0;j<bar.length;j++) { + baz = stripper(bar[j]); + if(patnum.test(baz)) { ret[count][j] = parseFloat(baz); } + else ret[count][j] = baz; + } + count++; + } + } + return ret; +} + +numeric.toCSV = function toCSV(A) { + var s = numeric.dim(A); + var i,j,m,n,row,ret; + m = s[0]; + n = s[1]; + ret = []; + for(i=0;i<m;i++) { + row = []; + for(j=0;j<m;j++) { row[j] = A[i][j].toString(); } + ret[i] = row.join(', '); + } + return ret.join('\n')+'\n'; +} + +numeric.getURL = function getURL(url) { + var client = new XMLHttpRequest(); + client.open("GET",url,false); + client.send(); + return client; +} + +numeric.imageURL = function imageURL(img) { + function base64(A) { + var n = A.length, i,x,y,z,p,q,r,s; + var key = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + var ret = ""; + for(i=0;i<n;i+=3) { + x = A[i]; + y = A[i+1]; + z = A[i+2]; + p = x >> 2; + q = ((x & 3) << 4) + (y >> 4); + r = ((y & 15) << 2) + (z >> 6); + s = z & 63; + if(i+1>=n) { r = s = 64; } + else if(i+2>=n) { s = 64; } + ret += key.charAt(p) + key.charAt(q) + key.charAt(r) + key.charAt(s); + } + return ret; + } + function crc32Array (a,from,to) { + if(typeof from === "undefined") { from = 0; } + if(typeof to === "undefined") { to = a.length; } + var table = [0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D]; + + var crc = -1, y = 0, n = a.length,i; + + for (i = from; i < to; i++) { + y = (crc ^ a[i]) & 0xFF; + crc = (crc >>> 8) ^ table[y]; + } + + return crc ^ (-1); + } + + var h = img[0].length, w = img[0][0].length, s1, s2, next,k,length,a,b,i,j,adler32,crc32; + var stream = [ + 137, 80, 78, 71, 13, 10, 26, 10, // 0: PNG signature + 0,0,0,13, // 8: IHDR Chunk length + 73, 72, 68, 82, // 12: "IHDR" + (w >> 24) & 255, (w >> 16) & 255, (w >> 8) & 255, w&255, // 16: Width + (h >> 24) & 255, (h >> 16) & 255, (h >> 8) & 255, h&255, // 20: Height + 8, // 24: bit depth + 2, // 25: RGB + 0, // 26: deflate + 0, // 27: no filter + 0, // 28: no interlace + -1,-2,-3,-4, // 29: CRC + -5,-6,-7,-8, // 33: IDAT Chunk length + 73, 68, 65, 84, // 37: "IDAT" + // RFC 1950 header starts here + 8, // 41: RFC1950 CMF + 29 // 42: RFC1950 FLG + ]; + crc32 = crc32Array(stream,12,29); + stream[29] = (crc32>>24)&255; + stream[30] = (crc32>>16)&255; + stream[31] = (crc32>>8)&255; + stream[32] = (crc32)&255; + s1 = 1; + s2 = 0; + for(i=0;i<h;i++) { + if(i<h-1) { stream.push(0); } + else { stream.push(1); } + a = (3*w+1+(i===0))&255; b = ((3*w+1+(i===0))>>8)&255; + stream.push(a); stream.push(b); + stream.push((~a)&255); stream.push((~b)&255); + if(i===0) stream.push(0); + for(j=0;j<w;j++) { + for(k=0;k<3;k++) { + a = img[k][i][j]; + if(a>255) a = 255; + else if(a<0) a=0; + else a = Math.round(a); + s1 = (s1 + a )%65521; + s2 = (s2 + s1)%65521; + stream.push(a); + } + } + stream.push(0); + } + adler32 = (s2<<16)+s1; + stream.push((adler32>>24)&255); + stream.push((adler32>>16)&255); + stream.push((adler32>>8)&255); + stream.push((adler32)&255); + length = stream.length - 41; + stream[33] = (length>>24)&255; + stream[34] = (length>>16)&255; + stream[35] = (length>>8)&255; + stream[36] = (length)&255; + crc32 = crc32Array(stream,37); + stream.push((crc32>>24)&255); + stream.push((crc32>>16)&255); + stream.push((crc32>>8)&255); + stream.push((crc32)&255); + stream.push(0); + stream.push(0); + stream.push(0); + stream.push(0); +// a = stream.length; + stream.push(73); // I + stream.push(69); // E + stream.push(78); // N + stream.push(68); // D + stream.push(174); // CRC1 + stream.push(66); // CRC2 + stream.push(96); // CRC3 + stream.push(130); // CRC4 + return 'data:image/png;base64,'+base64(stream); +} + +// 2. Linear algebra with Arrays. +numeric._dim = function _dim(x) { + var ret = []; + while(typeof x === "object") { ret.push(x.length); x = x[0]; } + return ret; +} + +numeric.dim = function dim(x) { + var y,z; + if(typeof x === "object") { + y = x[0]; + if(typeof y === "object") { + z = y[0]; + if(typeof z === "object") { + return numeric._dim(x); + } + return [x.length,y.length]; + } + return [x.length]; + } + return []; +} + +numeric.mapreduce = function mapreduce(body,init) { + return Function('x','accum','_s','_k', + 'if(typeof accum === "undefined") accum = '+init+';\n'+ + 'if(typeof x === "number") { var xi = x; '+body+'; return accum; }\n'+ + 'if(typeof _s === "undefined") _s = numeric.dim(x);\n'+ + 'if(typeof _k === "undefined") _k = 0;\n'+ + 'var _n = _s[_k];\n'+ + 'var i,xi;\n'+ + 'if(_k < _s.length-1) {\n'+ + ' for(i=_n-1;i>=0;i--) {\n'+ + ' accum = arguments.callee(x[i],accum,_s,_k+1);\n'+ + ' }'+ + ' return accum;\n'+ + '}\n'+ + 'for(i=_n-1;i>=1;i-=2) { \n'+ + ' xi = x[i];\n'+ + ' '+body+';\n'+ + ' xi = x[i-1];\n'+ + ' '+body+';\n'+ + '}\n'+ + 'if(i === 0) {\n'+ + ' xi = x[i];\n'+ + ' '+body+'\n'+ + '}\n'+ + 'return accum;' + ); +} +numeric.mapreduce2 = function mapreduce2(body,setup) { + return Function('x', + 'var n = x.length;\n'+ + 'var i,xi;\n'+setup+';\n'+ + 'for(i=n-1;i!==-1;--i) { \n'+ + ' xi = x[i];\n'+ + ' '+body+';\n'+ + '}\n'+ + 'return accum;' + ); +} + + +numeric.same = function same(x,y) { + var i,n; + if(!(x instanceof Array) || !(y instanceof Array)) { return false; } + n = x.length; + if(n !== y.length) { return false; } + for(i=0;i<n;i++) { + if(x[i] === y[i]) { continue; } + if(typeof x[i] === "object") { if(!same(x[i],y[i])) return false; } + else { return false; } + } + return true; +} + +numeric.rep = function rep(s,v,k) { + if(typeof k === "undefined") { k=0; } + var n = s[k], ret = Array(n), i; + if(k === s.length-1) { + for(i=n-2;i>=0;i-=2) { ret[i+1] = v; ret[i] = v; } + if(i===-1) { ret[0] = v; } + return ret; + } + for(i=n-1;i>=0;i--) { ret[i] = numeric.rep(s,v,k+1); } + return ret; +} + + +numeric.dotMMsmall = function dotMMsmall(x,y) { + var i,j,k,p,q,r,ret,foo,bar,woo,i0,k0,p0,r0; + p = x.length; q = y.length; r = y[0].length; + ret = Array(p); + for(i=p-1;i>=0;i--) { + foo = Array(r); + bar = x[i]; + for(k=r-1;k>=0;k--) { + woo = bar[q-1]*y[q-1][k]; + for(j=q-2;j>=1;j-=2) { + i0 = j-1; + woo += bar[j]*y[j][k] + bar[i0]*y[i0][k]; + } + if(j===0) { woo += bar[0]*y[0][k]; } + foo[k] = woo; + } + ret[i] = foo; + } + return ret; +} +numeric._getCol = function _getCol(A,j,x) { + var n = A.length, i; + for(i=n-1;i>0;--i) { + x[i] = A[i][j]; + --i; + x[i] = A[i][j]; + } + if(i===0) x[0] = A[0][j]; +} +numeric.dotMMbig = function dotMMbig(x,y){ + var gc = numeric._getCol, p = y.length, v = Array(p); + var m = x.length, n = y[0].length, A = new Array(m), xj; + var VV = numeric.dotVV; + var i,j,k,z; + --p; + --m; + for(i=m;i!==-1;--i) A[i] = Array(n); + --n; + for(i=n;i!==-1;--i) { + gc(y,i,v); + for(j=m;j!==-1;--j) { + z=0; + xj = x[j]; + A[j][i] = VV(xj,v); + } + } + return A; +} + +numeric.dotMV = function dotMV(x,y) { + var p = x.length, q = y.length,i; + var ret = Array(p), dotVV = numeric.dotVV; + for(i=p-1;i>=0;i--) { ret[i] = dotVV(x[i],y); } + return ret; +} + +numeric.dotVM = function dotVM(x,y) { + var i,j,k,p,q,r,ret,foo,bar,woo,i0,k0,p0,r0,s1,s2,s3,baz,accum; + p = x.length; q = y[0].length; + ret = Array(q); + for(k=q-1;k>=0;k--) { + woo = x[p-1]*y[p-1][k]; + for(j=p-2;j>=1;j-=2) { + i0 = j-1; + woo += x[j]*y[j][k] + x[i0]*y[i0][k]; + } + if(j===0) { woo += x[0]*y[0][k]; } + ret[k] = woo; + } + return ret; +} + +numeric.dotVV = function dotVV(x,y) { + var i,n=x.length,i1,ret = x[n-1]*y[n-1]; + for(i=n-2;i>=1;i-=2) { + i1 = i-1; + ret += x[i]*y[i] + x[i1]*y[i1]; + } + if(i===0) { ret += x[0]*y[0]; } + return ret; +} + +numeric.dot = function dot(x,y) { + var d = numeric.dim; + switch(d(x).length*1000+d(y).length) { + case 2002: + if(y.length < 10) return numeric.dotMMsmall(x,y); + else return numeric.dotMMbig(x,y); + case 2001: return numeric.dotMV(x,y); + case 1002: return numeric.dotVM(x,y); + case 1001: return numeric.dotVV(x,y); + case 1000: return numeric.mulVS(x,y); + case 1: return numeric.mulSV(x,y); + case 0: return x*y; + default: throw new Error('numeric.dot only works on vectors and matrices'); + } +} + +numeric.diag = function diag(d) { + var i,i1,j,n = d.length, A = Array(n), Ai; + for(i=n-1;i>=0;i--) { + Ai = Array(n); + i1 = i+2; + for(j=n-1;j>=i1;j-=2) { + Ai[j] = 0; + Ai[j-1] = 0; + } + if(j>i) { Ai[j] = 0; } + Ai[i] = d[i]; + for(j=i-1;j>=1;j-=2) { + Ai[j] = 0; + Ai[j-1] = 0; + } + if(j===0) { Ai[0] = 0; } + A[i] = Ai; + } + return A; +} +numeric.getDiag = function(A) { + var n = Math.min(A.length,A[0].length),i,ret = Array(n); + for(i=n-1;i>=1;--i) { + ret[i] = A[i][i]; + --i; + ret[i] = A[i][i]; + } + if(i===0) { + ret[0] = A[0][0]; + } + return ret; +} + +numeric.identity = function identity(n) { return numeric.diag(numeric.rep([n],1)); } +numeric.pointwise = function pointwise(params,body,setup) { + if(typeof setup === "undefined") { setup = ""; } + var fun = []; + var k; + var avec = /\[i\]$/,p,thevec = ''; + var haveret = false; + for(k=0;k<params.length;k++) { + if(avec.test(params[k])) { + p = params[k].substring(0,params[k].length-3); + thevec = p; + } else { p = params[k]; } + if(p==='ret') haveret = true; + fun.push(p); + } + fun[params.length] = '_s'; + fun[params.length+1] = '_k'; + fun[params.length+2] = ( + 'if(typeof _s === "undefined") _s = numeric.dim('+thevec+');\n'+ + 'if(typeof _k === "undefined") _k = 0;\n'+ + 'var _n = _s[_k];\n'+ + 'var i'+(haveret?'':', ret = Array(_n)')+';\n'+ + 'if(_k < _s.length-1) {\n'+ + ' for(i=_n-1;i>=0;i--) ret[i] = arguments.callee('+params.join(',')+',_s,_k+1);\n'+ + ' return ret;\n'+ + '}\n'+ + setup+'\n'+ + 'for(i=_n-1;i!==-1;--i) {\n'+ + ' '+body+'\n'+ + '}\n'+ + 'return ret;' + ); + return Function.apply(null,fun); +} +numeric.pointwise2 = function pointwise2(params,body,setup) { + if(typeof setup === "undefined") { setup = ""; } + var fun = []; + var k; + var avec = /\[i\]$/,p,thevec = ''; + var haveret = false; + for(k=0;k<params.length;k++) { + if(avec.test(params[k])) { + p = params[k].substring(0,params[k].length-3); + thevec = p; + } else { p = params[k]; } + if(p==='ret') haveret = true; + fun.push(p); + } + fun[params.length] = ( + 'var _n = '+thevec+'.length;\n'+ + 'var i'+(haveret?'':', ret = Array(_n)')+';\n'+ + setup+'\n'+ + 'for(i=_n-1;i!==-1;--i) {\n'+ + body+'\n'+ + '}\n'+ + 'return ret;' + ); + return Function.apply(null,fun); +} +numeric._biforeach = (function _biforeach(x,y,s,k,f) { + if(k === s.length-1) { f(x,y); return; } + var i,n=s[k]; + for(i=n-1;i>=0;i--) { _biforeach(typeof x==="object"?x[i]:x,typeof y==="object"?y[i]:y,s,k+1,f); } +}); +numeric._biforeach2 = (function _biforeach2(x,y,s,k,f) { + if(k === s.length-1) { return f(x,y); } + var i,n=s[k],ret = Array(n); + for(i=n-1;i>=0;--i) { ret[i] = _biforeach2(typeof x==="object"?x[i]:x,typeof y==="object"?y[i]:y,s,k+1,f); } + return ret; +}); +numeric._foreach = (function _foreach(x,s,k,f) { + if(k === s.length-1) { f(x); return; } + var i,n=s[k]; + for(i=n-1;i>=0;i--) { _foreach(x[i],s,k+1,f); } +}); +numeric._foreach2 = (function _foreach2(x,s,k,f) { + if(k === s.length-1) { return f(x); } + var i,n=s[k], ret = Array(n); + for(i=n-1;i>=0;i--) { ret[i] = _foreach2(x[i],s,k+1,f); } + return ret; +}); + +/*numeric.anyV = numeric.mapreduce('if(xi) return true;','false'); +numeric.allV = numeric.mapreduce('if(!xi) return false;','true'); +numeric.any = function(x) { if(typeof x.length === "undefined") return x; return numeric.anyV(x); } +numeric.all = function(x) { if(typeof x.length === "undefined") return x; return numeric.allV(x); }*/ + +numeric.ops2 = { + add: '+', + sub: '-', + mul: '*', + div: '/', + mod: '%', + and: '&&', + or: '||', + eq: '===', + neq: '!==', + lt: '<', + gt: '>', + leq: '<=', + geq: '>=', + band: '&', + bor: '|', + bxor: '^', + lshift: '<<', + rshift: '>>', + rrshift: '>>>' +}; +numeric.opseq = { + addeq: '+=', + subeq: '-=', + muleq: '*=', + diveq: '/=', + modeq: '%=', + lshifteq: '<<=', + rshifteq: '>>=', + rrshifteq: '>>>=', + bandeq: '&=', + boreq: '|=', + bxoreq: '^=' +}; +numeric.mathfuns = ['abs','acos','asin','atan','ceil','cos', + 'exp','floor','log','round','sin','sqrt','tan', + 'isNaN','isFinite']; +numeric.mathfuns2 = ['atan2','pow','max','min']; +numeric.ops1 = { + neg: '-', + not: '!', + bnot: '~', + clone: '' +}; +numeric.mapreducers = { + any: ['if(xi) return true;','var accum = false;'], + all: ['if(!xi) return false;','var accum = true;'], + sum: ['accum += xi;','var accum = 0;'], + prod: ['accum *= xi;','var accum = 1;'], + norm2Squared: ['accum += xi*xi;','var accum = 0;'], + norminf: ['accum = max(accum,abs(xi));','var accum = 0, max = Math.max, abs = Math.abs;'], + norm1: ['accum += abs(xi)','var accum = 0, abs = Math.abs;'], + sup: ['accum = max(accum,xi);','var accum = -Infinity, max = Math.max;'], + inf: ['accum = min(accum,xi);','var accum = Infinity, min = Math.min;'] +}; + +(function () { + var i,o; + for(i=0;i<numeric.mathfuns2.length;++i) { + o = numeric.mathfuns2[i]; + numeric.ops2[o] = o; + } + for(i in numeric.ops2) { + if(numeric.ops2.hasOwnProperty(i)) { + o = numeric.ops2[i]; + var code, codeeq, setup = ''; + if(numeric.myIndexOf.call(numeric.mathfuns2,i)!==-1) { + setup = 'var '+o+' = Math.'+o+';\n'; + code = function(r,x,y) { return r+' = '+o+'('+x+','+y+')'; }; + codeeq = function(x,y) { return x+' = '+o+'('+x+','+y+')'; }; + } else { + code = function(r,x,y) { return r+' = '+x+' '+o+' '+y; }; + if(numeric.opseq.hasOwnProperty(i+'eq')) { + codeeq = function(x,y) { return x+' '+o+'= '+y; }; + } else { + codeeq = function(x,y) { return x+' = '+x+' '+o+' '+y; }; + } + } + numeric[i+'VV'] = numeric.pointwise2(['x[i]','y[i]'],code('ret[i]','x[i]','y[i]'),setup); + numeric[i+'SV'] = numeric.pointwise2(['x','y[i]'],code('ret[i]','x','y[i]'),setup); + numeric[i+'VS'] = numeric.pointwise2(['x[i]','y'],code('ret[i]','x[i]','y'),setup); + numeric[i] = Function( + 'var n = arguments.length, i, x = arguments[0], y;\n'+ + 'var VV = numeric.'+i+'VV, VS = numeric.'+i+'VS, SV = numeric.'+i+'SV;\n'+ + 'var dim = numeric.dim;\n'+ + 'for(i=1;i!==n;++i) { \n'+ + ' y = arguments[i];\n'+ + ' if(typeof x === "object") {\n'+ + ' if(typeof y === "object") x = numeric._biforeach2(x,y,dim(x),0,VV);\n'+ + ' else x = numeric._biforeach2(x,y,dim(x),0,VS);\n'+ + ' } else if(typeof y === "object") x = numeric._biforeach2(x,y,dim(y),0,SV);\n'+ + ' else '+codeeq('x','y')+'\n'+ + '}\nreturn x;\n'); + numeric[o] = numeric[i]; + numeric[i+'eqV'] = numeric.pointwise2(['ret[i]','x[i]'], codeeq('ret[i]','x[i]'),setup); + numeric[i+'eqS'] = numeric.pointwise2(['ret[i]','x'], codeeq('ret[i]','x'),setup); + numeric[i+'eq'] = Function( + 'var n = arguments.length, i, x = arguments[0], y;\n'+ + 'var V = numeric.'+i+'eqV, S = numeric.'+i+'eqS\n'+ + 'var s = numeric.dim(x);\n'+ + 'for(i=1;i!==n;++i) { \n'+ + ' y = arguments[i];\n'+ + ' if(typeof y === "object") numeric._biforeach(x,y,s,0,V);\n'+ + ' else numeric._biforeach(x,y,s,0,S);\n'+ + '}\nreturn x;\n'); + } + } + for(i=0;i<numeric.mathfuns2.length;++i) { + o = numeric.mathfuns2[i]; + delete numeric.ops2[o]; + } + for(i=0;i<numeric.mathfuns.length;++i) { + o = numeric.mathfuns[i]; + numeric.ops1[o] = o; + } + for(i in numeric.ops1) { + if(numeric.ops1.hasOwnProperty(i)) { + setup = ''; + o = numeric.ops1[i]; + if(numeric.myIndexOf.call(numeric.mathfuns,i)!==-1) { + if(Math.hasOwnProperty(o)) setup = 'var '+o+' = Math.'+o+';\n'; + } + numeric[i+'eqV'] = numeric.pointwise2(['ret[i]'],'ret[i] = '+o+'(ret[i]);',setup); + numeric[i+'eq'] = Function('x', + 'if(typeof x !== "object") return '+o+'x\n'+ + 'var i;\n'+ + 'var V = numeric.'+i+'eqV;\n'+ + 'var s = numeric.dim(x);\n'+ + 'numeric._foreach(x,s,0,V);\n'+ + 'return x;\n'); + numeric[i+'V'] = numeric.pointwise2(['x[i]'],'ret[i] = '+o+'(x[i]);',setup); + numeric[i] = Function('x', + 'if(typeof x !== "object") return '+o+'(x)\n'+ + 'var i;\n'+ + 'var V = numeric.'+i+'V;\n'+ + 'var s = numeric.dim(x);\n'+ + 'return numeric._foreach2(x,s,0,V);\n'); + } + } + for(i=0;i<numeric.mathfuns.length;++i) { + o = numeric.mathfuns[i]; + delete numeric.ops1[o]; + } + for(i in numeric.mapreducers) { + if(numeric.mapreducers.hasOwnProperty(i)) { + o = numeric.mapreducers[i]; + numeric[i+'V'] = numeric.mapreduce2(o[0],o[1]); + numeric[i] = Function('x','s','k', + o[1]+ + 'if(typeof x !== "object") {'+ + ' xi = x;\n'+ + o[0]+';\n'+ + ' return accum;\n'+ + '}'+ + 'if(typeof s === "undefined") s = numeric.dim(x);\n'+ + 'if(typeof k === "undefined") k = 0;\n'+ + 'if(k === s.length-1) return numeric.'+i+'V(x);\n'+ + 'var xi;\n'+ + 'var n = x.length, i;\n'+ + 'for(i=n-1;i!==-1;--i) {\n'+ + ' xi = arguments.callee(x[i]);\n'+ + o[0]+';\n'+ + '}\n'+ + 'return accum;\n'); + } + } +}()); + +numeric.truncVV = numeric.pointwise(['x[i]','y[i]'],'ret[i] = round(x[i]/y[i])*y[i];','var round = Math.round;'); +numeric.truncVS = numeric.pointwise(['x[i]','y'],'ret[i] = round(x[i]/y)*y;','var round = Math.round;'); +numeric.truncSV = numeric.pointwise(['x','y[i]'],'ret[i] = round(x/y[i])*y[i];','var round = Math.round;'); +numeric.trunc = function trunc(x,y) { + if(typeof x === "object") { + if(typeof y === "object") return numeric.truncVV(x,y); + return numeric.truncVS(x,y); + } + if (typeof y === "object") return numeric.truncSV(x,y); + return Math.round(x/y)*y; +} + +numeric.inv = function inv(x) { + var s = numeric.dim(x), abs = Math.abs, m = s[0], n = s[1]; + var A = numeric.clone(x), Ai, Aj; + var I = numeric.identity(m), Ii, Ij; + var i,j,k,x; + for(j=0;j<n;++j) { + var i0 = -1; + var v0 = -1; + for(i=j;i!==m;++i) { k = abs(A[i][j]); if(k>v0) { i0 = i; v0 = k; } } + Aj = A[i0]; A[i0] = A[j]; A[j] = Aj; + Ij = I[i0]; I[i0] = I[j]; I[j] = Ij; + x = Aj[j]; + for(k=j;k!==n;++k) Aj[k] /= x; + for(k=n-1;k!==-1;--k) Ij[k] /= x; + for(i=m-1;i!==-1;--i) { + if(i!==j) { + Ai = A[i]; + Ii = I[i]; + x = Ai[j]; + for(k=j+1;k!==n;++k) Ai[k] -= Aj[k]*x; + for(k=n-1;k>0;--k) { Ii[k] -= Ij[k]*x; --k; Ii[k] -= Ij[k]*x; } + if(k===0) Ii[0] -= Ij[0]*x; + } + } + } + return I; +} + +numeric.det = function det(x) { + var s = numeric.dim(x); + if(s.length !== 2 || s[0] !== s[1]) { throw new Error('numeric: det() only works on square matrices'); } + var n = s[0], ret = 1,i,j,k,A = numeric.clone(x),Aj,Ai,alpha,temp,k1,k2,k3; + for(j=0;j<n-1;j++) { + k=j; + for(i=j+1;i<n;i++) { if(Math.abs(A[i][j]) > Math.abs(A[k][j])) { k = i; } } + if(k !== j) { + temp = A[k]; A[k] = A[j]; A[j] = temp; + ret *= -1; + } + Aj = A[j]; + for(i=j+1;i<n;i++) { + Ai = A[i]; + alpha = Ai[j]/Aj[j]; + for(k=j+1;k<n-1;k+=2) { + k1 = k+1; + Ai[k] -= Aj[k]*alpha; + Ai[k1] -= Aj[k1]*alpha; + } + if(k!==n) { Ai[k] -= Aj[k]*alpha; } + } + if(Aj[j] === 0) { return 0; } + ret *= Aj[j]; + } + return ret*A[j][j]; +} + +numeric.transpose = function transpose(x) { + var i,j,m = x.length,n = x[0].length, ret=Array(n),A0,A1,Bj; + for(j=0;j<n;j++) ret[j] = Array(m); + for(i=m-1;i>=1;i-=2) { + A1 = x[i]; + A0 = x[i-1]; + for(j=n-1;j>=1;--j) { + Bj = ret[j]; Bj[i] = A1[j]; Bj[i-1] = A0[j]; + --j; + Bj = ret[j]; Bj[i] = A1[j]; Bj[i-1] = A0[j]; + } + if(j===0) { + Bj = ret[0]; Bj[i] = A1[0]; Bj[i-1] = A0[0]; + } + } + if(i===0) { + A0 = x[0]; + for(j=n-1;j>=1;--j) { + ret[j][0] = A0[j]; + --j; + ret[j][0] = A0[j]; + } + if(j===0) { ret[0][0] = A0[0]; } + } + return ret; +} +numeric.negtranspose = function negtranspose(x) { + var i,j,m = x.length,n = x[0].length, ret=Array(n),A0,A1,Bj; + for(j=0;j<n;j++) ret[j] = Array(m); + for(i=m-1;i>=1;i-=2) { + A1 = x[i]; + A0 = x[i-1]; + for(j=n-1;j>=1;--j) { + Bj = ret[j]; Bj[i] = -A1[j]; Bj[i-1] = -A0[j]; + --j; + Bj = ret[j]; Bj[i] = -A1[j]; Bj[i-1] = -A0[j]; + } + if(j===0) { + Bj = ret[0]; Bj[i] = -A1[0]; Bj[i-1] = -A0[0]; + } + } + if(i===0) { + A0 = x[0]; + for(j=n-1;j>=1;--j) { + ret[j][0] = -A0[j]; + --j; + ret[j][0] = -A0[j]; + } + if(j===0) { ret[0][0] = -A0[0]; } + } + return ret; +} + +numeric._random = function _random(s,k) { + var i,n=s[k],ret=Array(n), rnd; + if(k === s.length-1) { + rnd = Math.random; + for(i=n-1;i>=1;i-=2) { + ret[i] = rnd(); + ret[i-1] = rnd(); + } + if(i===0) { ret[0] = rnd(); } + return ret; + } + for(i=n-1;i>=0;i--) ret[i] = _random(s,k+1); + return ret; +} +numeric.random = function random(s) { return numeric._random(s,0); } + +numeric.norm2 = function norm2(x) { return Math.sqrt(numeric.norm2Squared(x)); } + +numeric.linspace = function linspace(a,b,n) { + if(typeof n === "undefined") n = Math.max(Math.round(b-a)+1,1); + if(n<2) { return n===1?[a]:[]; } + var i,ret = Array(n); + n--; + for(i=n;i>=0;i--) { ret[i] = (i*b+(n-i)*a)/n; } + return ret; +} + +numeric.getBlock = function getBlock(x,from,to) { + var s = numeric.dim(x); + function foo(x,k) { + var i,a = from[k], n = to[k]-a, ret = Array(n); + if(k === s.length-1) { + for(i=n;i>=0;i--) { ret[i] = x[i+a]; } + return ret; + } + for(i=n;i>=0;i--) { ret[i] = foo(x[i+a],k+1); } + return ret; + } + return foo(x,0); +} + +numeric.setBlock = function setBlock(x,from,to,B) { + var s = numeric.dim(x); + function foo(x,y,k) { + var i,a = from[k], n = to[k]-a; + if(k === s.length-1) { for(i=n;i>=0;i--) { x[i+a] = y[i]; } } + for(i=n;i>=0;i--) { foo(x[i+a],y[i],k+1); } + } + foo(x,B,0); + return x; +} + +numeric.getRange = function getRange(A,I,J) { + var m = I.length, n = J.length; + var i,j; + var B = Array(m), Bi, AI; + for(i=m-1;i!==-1;--i) { + B[i] = Array(n); + Bi = B[i]; + AI = A[I[i]]; + for(j=n-1;j!==-1;--j) Bi[j] = AI[J[j]]; + } + return B; +} + +numeric.blockMatrix = function blockMatrix(X) { + var s = numeric.dim(X); + if(s.length<4) return numeric.blockMatrix([X]); + var m=s[0],n=s[1],M,N,i,j,Xij; + M = 0; N = 0; + for(i=0;i<m;++i) M+=X[i][0].length; + for(j=0;j<n;++j) N+=X[0][j][0].length; + var Z = Array(M); + for(i=0;i<M;++i) Z[i] = Array(N); + var I=0,J,ZI,k,l,Xijk; + for(i=0;i<m;++i) { + J=N; + for(j=n-1;j!==-1;--j) { + Xij = X[i][j]; + J -= Xij[0].length; + for(k=Xij.length-1;k!==-1;--k) { + Xijk = Xij[k]; + ZI = Z[I+k]; + for(l = Xijk.length-1;l!==-1;--l) ZI[J+l] = Xijk[l]; + } + } + I += X[i][0].length; + } + return Z; +} + +numeric.tensor = function tensor(x,y) { + if(typeof x === "number" || typeof y === "number") return numeric.mul(x,y); + var s1 = numeric.dim(x), s2 = numeric.dim(y); + if(s1.length !== 1 || s2.length !== 1) { + throw new Error('numeric: tensor product is only defined for vectors'); + } + var m = s1[0], n = s2[0], A = Array(m), Ai, i,j,xi; + for(i=m-1;i>=0;i--) { + Ai = Array(n); + xi = x[i]; + for(j=n-1;j>=3;--j) { + Ai[j] = xi * y[j]; + --j; + Ai[j] = xi * y[j]; + --j; + Ai[j] = xi * y[j]; + --j; + Ai[j] = xi * y[j]; + } + while(j>=0) { Ai[j] = xi * y[j]; --j; } + A[i] = Ai; + } + return A; +} + +// 3. The Tensor type T +numeric.T = function T(x,y) { this.x = x; this.y = y; } +numeric.t = function t(x,y) { return new numeric.T(x,y); } + +numeric.Tbinop = function Tbinop(rr,rc,cr,cc,setup) { + var io = numeric.indexOf; + if(typeof setup !== "string") { + var k; + setup = ''; + for(k in numeric) { + if(numeric.hasOwnProperty(k) && (rr.indexOf(k)>=0 || rc.indexOf(k)>=0 || cr.indexOf(k)>=0 || cc.indexOf(k)>=0) && k.length>1) { + setup += 'var '+k+' = numeric.'+k+';\n'; + } + } + } + return Function(['y'], + 'var x = this;\n'+ + 'if(!(y instanceof numeric.T)) { y = new numeric.T(y); }\n'+ + setup+'\n'+ + 'if(x.y) {'+ + ' if(y.y) {'+ + ' return new numeric.T('+cc+');\n'+ + ' }\n'+ + ' return new numeric.T('+cr+');\n'+ + '}\n'+ + 'if(y.y) {\n'+ + ' return new numeric.T('+rc+');\n'+ + '}\n'+ + 'return new numeric.T('+rr+');\n' + ); +} + +numeric.T.prototype.add = numeric.Tbinop( + 'add(x.x,y.x)', + 'add(x.x,y.x),y.y', + 'add(x.x,y.x),x.y', + 'add(x.x,y.x),add(x.y,y.y)'); +numeric.T.prototype.sub = numeric.Tbinop( + 'sub(x.x,y.x)', + 'sub(x.x,y.x),neg(y.y)', + 'sub(x.x,y.x),x.y', + 'sub(x.x,y.x),sub(x.y,y.y)'); +numeric.T.prototype.mul = numeric.Tbinop( + 'mul(x.x,y.x)', + 'mul(x.x,y.x),mul(x.x,y.y)', + 'mul(x.x,y.x),mul(x.y,y.x)', + 'sub(mul(x.x,y.x),mul(x.y,y.y)),add(mul(x.x,y.y),mul(x.y,y.x))'); + +numeric.T.prototype.reciprocal = function reciprocal() { + var mul = numeric.mul, div = numeric.div; + if(this.y) { + var d = numeric.add(mul(this.x,this.x),mul(this.y,this.y)); + return new numeric.T(div(this.x,d),div(numeric.neg(this.y),d)); + } + return new T(div(1,this.x)); +} +numeric.T.prototype.div = function div(y) { + if(!(y instanceof numeric.T)) y = new numeric.T(y); + if(y.y) { return this.mul(y.reciprocal()); } + var div = numeric.div; + if(this.y) { return new numeric.T(div(this.x,y.x),div(this.y,y.x)); } + return new numeric.T(div(this.x,y.x)); +} +numeric.T.prototype.dot = numeric.Tbinop( + 'dot(x.x,y.x)', + 'dot(x.x,y.x),dot(x.x,y.y)', + 'dot(x.x,y.x),dot(x.y,y.x)', + 'sub(dot(x.x,y.x),dot(x.y,y.y)),add(dot(x.x,y.y),dot(x.y,y.x))' + ); +numeric.T.prototype.transpose = function transpose() { + var t = numeric.transpose, x = this.x, y = this.y; + if(y) { return new numeric.T(t(x),t(y)); } + return new numeric.T(t(x)); +} +numeric.T.prototype.transjugate = function transjugate() { + var t = numeric.transpose, x = this.x, y = this.y; + if(y) { return new numeric.T(t(x),numeric.negtranspose(y)); } + return new numeric.T(t(x)); +} +numeric.Tunop = function Tunop(r,c,s) { + if(typeof s !== "string") { s = ''; } + return Function( + 'var x = this;\n'+ + s+'\n'+ + 'if(x.y) {'+ + ' '+c+';\n'+ + '}\n'+ + r+';\n' + ); +} + +numeric.T.prototype.exp = numeric.Tunop( + 'return new numeric.T(ex)', + 'return new numeric.T(mul(cos(x.y),ex),mul(sin(x.y),ex))', + 'var ex = numeric.exp(x.x), cos = numeric.cos, sin = numeric.sin, mul = numeric.mul;'); +numeric.T.prototype.conj = numeric.Tunop( + 'return new numeric.T(x.x);', + 'return new numeric.T(x.x,numeric.neg(x.y));'); +numeric.T.prototype.neg = numeric.Tunop( + 'return new numeric.T(neg(x.x));', + 'return new numeric.T(neg(x.x),neg(x.y));', + 'var neg = numeric.neg;'); +numeric.T.prototype.sin = numeric.Tunop( + 'return new numeric.T(numeric.sin(x.x))', + 'return x.exp().sub(x.neg().exp()).div(new numeric.T(0,2));'); +numeric.T.prototype.cos = numeric.Tunop( + 'return new numeric.T(numeric.cos(x.x))', + 'return x.exp().add(x.neg().exp()).div(2);'); +numeric.T.prototype.abs = numeric.Tunop( + 'return new numeric.T(numeric.abs(x.x));', + 'return new numeric.T(numeric.sqrt(numeric.add(mul(x.x,x.x),mul(x.y,x.y))));', + 'var mul = numeric.mul;'); +numeric.T.prototype.log = numeric.Tunop( + 'return new numeric.T(numeric.log(x.x));', + 'var theta = new numeric.T(numeric.atan2(x.y,x.x)), r = x.abs();\n'+ + 'return new numeric.T(numeric.log(r.x),theta.x);'); +numeric.T.prototype.norm2 = numeric.Tunop( + 'return numeric.norm2(x.x);', + 'var f = numeric.norm2Squared;\n'+ + 'return Math.sqrt(f(x.x)+f(x.y));'); +numeric.T.prototype.inv = function inv() { + var A = this; + if(typeof A.y === "undefined") { return new numeric.T(numeric.inv(A.x)); } + var n = A.x.length, i, j, k; + var Rx = numeric.identity(n),Ry = numeric.rep([n,n],0); + var Ax = numeric.clone(A.x), Ay = numeric.clone(A.y); + var Aix, Aiy, Ajx, Ajy, Rix, Riy, Rjx, Rjy; + var i,j,k,d,d1,ax,ay,bx,by,temp; + for(i=0;i<n;i++) { + ax = Ax[i][i]; ay = Ay[i][i]; + d = ax*ax+ay*ay; + k = i; + for(j=i+1;j<n;j++) { + ax = Ax[j][i]; ay = Ay[j][i]; + d1 = ax*ax+ay*ay; + if(d1 > d) { k=j; d = d1; } + } + if(k!==i) { + temp = Ax[i]; Ax[i] = Ax[k]; Ax[k] = temp; + temp = Ay[i]; Ay[i] = Ay[k]; Ay[k] = temp; + temp = Rx[i]; Rx[i] = Rx[k]; Rx[k] = temp; + temp = Ry[i]; Ry[i] = Ry[k]; Ry[k] = temp; + } + Aix = Ax[i]; Aiy = Ay[i]; + Rix = Rx[i]; Riy = Ry[i]; + ax = Aix[i]; ay = Aiy[i]; + for(j=i+1;j<n;j++) { + bx = Aix[j]; by = Aiy[j]; + Aix[j] = (bx*ax+by*ay)/d; + Aiy[j] = (by*ax-bx*ay)/d; + } + for(j=0;j<n;j++) { + bx = Rix[j]; by = Riy[j]; + Rix[j] = (bx*ax+by*ay)/d; + Riy[j] = (by*ax-bx*ay)/d; + } + for(j=i+1;j<n;j++) { + Ajx = Ax[j]; Ajy = Ay[j]; + Rjx = Rx[j]; Rjy = Ry[j]; + ax = Ajx[i]; ay = Ajy[i]; + for(k=i+1;k<n;k++) { + bx = Aix[k]; by = Aiy[k]; + Ajx[k] -= bx*ax-by*ay; + Ajy[k] -= by*ax+bx*ay; + } + for(k=0;k<n;k++) { + bx = Rix[k]; by = Riy[k]; + Rjx[k] -= bx*ax-by*ay; + Rjy[k] -= by*ax+bx*ay; + } + } + } + for(i=n-1;i>0;i--) { + Rix = Rx[i]; Riy = Ry[i]; + for(j=i-1;j>=0;j--) { + Rjx = Rx[j]; Rjy = Ry[j]; + ax = Ax[j][i]; ay = Ay[j][i]; + for(k=n-1;k>=0;k--) { + bx = Rix[k]; by = Riy[k]; + Rjx[k] -= ax*bx - ay*by; + Rjy[k] -= ax*by + ay*bx; + } + } + } + return new numeric.T(Rx,Ry); +} +numeric.T.prototype.get = function get(i) { + var x = this.x, y = this.y, k = 0, ik, n = i.length; + if(y) { + while(k<n) { + ik = i[k]; + x = x[ik]; + y = y[ik]; + k++; + } + return new numeric.T(x,y); + } + while(k<n) { + ik = i[k]; + x = x[ik]; + k++; + } + return new numeric.T(x); +} +numeric.T.prototype.set = function set(i,v) { + var x = this.x, y = this.y, k = 0, ik, n = i.length, vx = v.x, vy = v.y; + if(n===0) { + if(vy) { this.y = vy; } + else if(y) { this.y = undefined; } + this.x = x; + return this; + } + if(vy) { + if(y) { /* ok */ } + else { + y = numeric.rep(numeric.dim(x),0); + this.y = y; + } + while(k<n-1) { + ik = i[k]; + x = x[ik]; + y = y[ik]; + k++; + } + ik = i[k]; + x[ik] = vx; + y[ik] = vy; + return this; + } + if(y) { + while(k<n-1) { + ik = i[k]; + x = x[ik]; + y = y[ik]; + k++; + } + ik = i[k]; + x[ik] = vx; + if(vx instanceof Array) y[ik] = numeric.rep(numeric.dim(vx),0); + else y[ik] = 0; + return this; + } + while(k<n-1) { + ik = i[k]; + x = x[ik]; + k++; + } + ik = i[k]; + x[ik] = vx; + return this; +} +numeric.T.prototype.getRows = function getRows(i0,i1) { + var n = i1-i0+1, j; + var rx = Array(n), ry, x = this.x, y = this.y; + for(j=i0;j<=i1;j++) { rx[j-i0] = x[j]; } + if(y) { + ry = Array(n); + for(j=i0;j<=i1;j++) { ry[j-i0] = y[j]; } + return new numeric.T(rx,ry); + } + return new numeric.T(rx); +} +numeric.T.prototype.setRows = function setRows(i0,i1,A) { + var j; + var rx = this.x, ry = this.y, x = A.x, y = A.y; + for(j=i0;j<=i1;j++) { rx[j] = x[j-i0]; } + if(y) { + if(!ry) { ry = numeric.rep(numeric.dim(rx),0); this.y = ry; } + for(j=i0;j<=i1;j++) { ry[j] = y[j-i0]; } + } else if(ry) { + for(j=i0;j<=i1;j++) { ry[j] = numeric.rep([x[j-i0].length],0); } + } + return this; +} +numeric.T.prototype.getRow = function getRow(k) { + var x = this.x, y = this.y; + if(y) { return new numeric.T(x[k],y[k]); } + return new numeric.T(x[k]); +} +numeric.T.prototype.setRow = function setRow(i,v) { + var rx = this.x, ry = this.y, x = v.x, y = v.y; + rx[i] = x; + if(y) { + if(!ry) { ry = numeric.rep(numeric.dim(rx),0); this.y = ry; } + ry[i] = y; + } else if(ry) { + ry = numeric.rep([x.length],0); + } + return this; +} + +numeric.T.prototype.getBlock = function getBlock(from,to) { + var x = this.x, y = this.y, b = numeric.getBlock; + if(y) { return new numeric.T(b(x,from,to),b(y,from,to)); } + return new numeric.T(b(x,from,to)); +} +numeric.T.prototype.setBlock = function setBlock(from,to,A) { + if(!(A instanceof numeric.T)) A = new numeric.T(A); + var x = this.x, y = this.y, b = numeric.setBlock, Ax = A.x, Ay = A.y; + if(Ay) { + if(!y) { this.y = numeric.rep(numeric.dim(this),0); y = this.y; } + b(x,from,to,Ax); + b(y,from,to,Ay); + return this; + } + b(x,from,to,Ax); + if(y) b(y,from,to,numeric.rep(numeric.dim(Ax),0)); +} +numeric.T.rep = function rep(s,v) { + var T = numeric.T; + if(!(v instanceof T)) v = new T(v); + var x = v.x, y = v.y, r = numeric.rep; + if(y) return new T(r(s,x),r(s,y)); + return new T(r(s,x)); +} +numeric.T.diag = function diag(d) { + if(!(d instanceof numeric.T)) d = new numeric.T(d); + var x = d.x, y = d.y, diag = numeric.diag; + if(y) return new numeric.T(diag(x),diag(y)); + return new numeric.T(diag(x)); +} +numeric.T.eig = function eig() { + if(this.y) { throw new Error('eig: not implemented for complex matrices.'); } + return numeric.eig(this.x); +} +numeric.T.identity = function identity(n) { return new numeric.T(numeric.identity(n)); } +numeric.T.prototype.getDiag = function getDiag() { + var n = numeric; + var x = this.x, y = this.y; + if(y) { return new n.T(n.getDiag(x),n.getDiag(y)); } + return new n.T(n.getDiag(x)); +} + +// 4. Eigenvalues of real matrices + +numeric.house = function house(x) { + var v = numeric.clone(x); + var s = x[0] >= 0 ? 1 : -1; + var alpha = s*numeric.norm2(x); + v[0] += alpha; + var foo = numeric.norm2(v); + if(foo === 0) { /* this should not happen */ throw new Error('eig: internal error'); } + return numeric.div(v,foo); +} + +numeric.toUpperHessenberg = function toUpperHessenberg(me) { + var s = numeric.dim(me); + if(s.length !== 2 || s[0] !== s[1]) { throw new Error('numeric: toUpperHessenberg() only works on square matrices'); } + var m = s[0], i,j,k,x,v,A = numeric.clone(me),B,C,Ai,Ci,Q = numeric.identity(m),Qi; + for(j=0;j<m-2;j++) { + x = Array(m-j-1); + for(i=j+1;i<m;i++) { x[i-j-1] = A[i][j]; } + if(numeric.norm2(x)>0) { + v = numeric.house(x); + B = numeric.getBlock(A,[j+1,j],[m-1,m-1]); + C = numeric.tensor(v,numeric.dot(v,B)); + for(i=j+1;i<m;i++) { Ai = A[i]; Ci = C[i-j-1]; for(k=j;k<m;k++) Ai[k] -= 2*Ci[k-j]; } + B = numeric.getBlock(A,[0,j+1],[m-1,m-1]); + C = numeric.tensor(numeric.dot(B,v),v); + for(i=0;i<m;i++) { Ai = A[i]; Ci = C[i]; for(k=j+1;k<m;k++) Ai[k] -= 2*Ci[k-j-1]; } + B = Array(m-j-1); + for(i=j+1;i<m;i++) B[i-j-1] = Q[i]; + C = numeric.tensor(v,numeric.dot(v,B)); + for(i=j+1;i<m;i++) { Qi = Q[i]; Ci = C[i-j-1]; for(k=0;k<m;k++) Qi[k] -= 2*Ci[k]; } + } + } + return {H:A, Q:Q}; +} + +numeric.epsilon = 2.220446049250313e-16; + +numeric.QRFrancis = function(H,maxiter) { + if(typeof maxiter === "undefined") { maxiter = 10000; } + H = numeric.clone(H); + var H0 = numeric.clone(H); + var s = numeric.dim(H),m=s[0],x,v,a,b,c,d,det,tr, Hloc, Q = numeric.identity(m), Qi, Hi, B, C, Ci,i,j,k,iter; + if(m<3) { return {Q:Q, B:[ [0,m-1] ]}; } + var epsilon = numeric.epsilon; + for(iter=0;iter<maxiter;iter++) { + for(j=0;j<m-1;j++) { + if(Math.abs(H[j+1][j]) < epsilon*(Math.abs(H[j][j])+Math.abs(H[j+1][j+1]))) { + var QH1 = numeric.QRFrancis(numeric.getBlock(H,[0,0],[j,j]),maxiter); + var QH2 = numeric.QRFrancis(numeric.getBlock(H,[j+1,j+1],[m-1,m-1]),maxiter); + B = Array(j+1); + for(i=0;i<=j;i++) { B[i] = Q[i]; } + C = numeric.dot(QH1.Q,B); + for(i=0;i<=j;i++) { Q[i] = C[i]; } + B = Array(m-j-1); + for(i=j+1;i<m;i++) { B[i-j-1] = Q[i]; } + C = numeric.dot(QH2.Q,B); + for(i=j+1;i<m;i++) { Q[i] = C[i-j-1]; } + return {Q:Q,B:QH1.B.concat(numeric.add(QH2.B,j+1))}; + } + } + a = H[m-2][m-2]; b = H[m-2][m-1]; + c = H[m-1][m-2]; d = H[m-1][m-1]; + tr = a+d; + det = (a*d-b*c); + Hloc = numeric.getBlock(H, [0,0], [2,2]); + if(tr*tr>=4*det) { + var s1,s2; + s1 = 0.5*(tr+Math.sqrt(tr*tr-4*det)); + s2 = 0.5*(tr-Math.sqrt(tr*tr-4*det)); + Hloc = numeric.add(numeric.sub(numeric.dot(Hloc,Hloc), + numeric.mul(Hloc,s1+s2)), + numeric.diag(numeric.rep([3],s1*s2))); + } else { + Hloc = numeric.add(numeric.sub(numeric.dot(Hloc,Hloc), + numeric.mul(Hloc,tr)), + numeric.diag(numeric.rep([3],det))); + } + x = [Hloc[0][0],Hloc[1][0],Hloc[2][0]]; + v = numeric.house(x); + B = [H[0],H[1],H[2]]; + C = numeric.tensor(v,numeric.dot(v,B)); + for(i=0;i<3;i++) { Hi = H[i]; Ci = C[i]; for(k=0;k<m;k++) Hi[k] -= 2*Ci[k]; } + B = numeric.getBlock(H, [0,0],[m-1,2]); + C = numeric.tensor(numeric.dot(B,v),v); + for(i=0;i<m;i++) { Hi = H[i]; Ci = C[i]; for(k=0;k<3;k++) Hi[k] -= 2*Ci[k]; } + B = [Q[0],Q[1],Q[2]]; + C = numeric.tensor(v,numeric.dot(v,B)); + for(i=0;i<3;i++) { Qi = Q[i]; Ci = C[i]; for(k=0;k<m;k++) Qi[k] -= 2*Ci[k]; } + var J; + for(j=0;j<m-2;j++) { + for(k=j;k<=j+1;k++) { + if(Math.abs(H[k+1][k]) < epsilon*(Math.abs(H[k][k])+Math.abs(H[k+1][k+1]))) { + var QH1 = numeric.QRFrancis(numeric.getBlock(H,[0,0],[k,k]),maxiter); + var QH2 = numeric.QRFrancis(numeric.getBlock(H,[k+1,k+1],[m-1,m-1]),maxiter); + B = Array(k+1); + for(i=0;i<=k;i++) { B[i] = Q[i]; } + C = numeric.dot(QH1.Q,B); + for(i=0;i<=k;i++) { Q[i] = C[i]; } + B = Array(m-k-1); + for(i=k+1;i<m;i++) { B[i-k-1] = Q[i]; } + C = numeric.dot(QH2.Q,B); + for(i=k+1;i<m;i++) { Q[i] = C[i-k-1]; } + return {Q:Q,B:QH1.B.concat(numeric.add(QH2.B,k+1))}; + } + } + J = Math.min(m-1,j+3); + x = Array(J-j); + for(i=j+1;i<=J;i++) { x[i-j-1] = H[i][j]; } + v = numeric.house(x); + B = numeric.getBlock(H, [j+1,j],[J,m-1]); + C = numeric.tensor(v,numeric.dot(v,B)); + for(i=j+1;i<=J;i++) { Hi = H[i]; Ci = C[i-j-1]; for(k=j;k<m;k++) Hi[k] -= 2*Ci[k-j]; } + B = numeric.getBlock(H, [0,j+1],[m-1,J]); + C = numeric.tensor(numeric.dot(B,v),v); + for(i=0;i<m;i++) { Hi = H[i]; Ci = C[i]; for(k=j+1;k<=J;k++) Hi[k] -= 2*Ci[k-j-1]; } + B = Array(J-j); + for(i=j+1;i<=J;i++) B[i-j-1] = Q[i]; + C = numeric.tensor(v,numeric.dot(v,B)); + for(i=j+1;i<=J;i++) { Qi = Q[i]; Ci = C[i-j-1]; for(k=0;k<m;k++) Qi[k] -= 2*Ci[k]; } + } + } + throw new Error('numeric: eigenvalue iteration does not converge -- increase maxiter?'); +} + +numeric.eig = function eig(A,maxiter) { + var QH = numeric.toUpperHessenberg(A); + var QB = numeric.QRFrancis(QH.H,maxiter); + var T = numeric.T; + var n = A.length,i,k,flag = false,B = QB.B,H = numeric.dot(QB.Q,numeric.dot(QH.H,numeric.transpose(QB.Q))); + var Q = new T(numeric.dot(QB.Q,QH.Q)),Q0; + var m = B.length,j; + var a,b,c,d,p1,p2,disc,x,y,p,q,n1,n2; + var sqrt = Math.sqrt; + for(k=0;k<m;k++) { + i = B[k][0]; + if(i === B[k][1]) { + // nothing + } else { + j = i+1; + a = H[i][i]; + b = H[i][j]; + c = H[j][i]; + d = H[j][j]; + if(b === 0 && c === 0) continue; + p1 = -a-d; + p2 = a*d-b*c; + disc = p1*p1-4*p2; + if(disc>=0) { + if(p1<0) x = -0.5*(p1-sqrt(disc)); + else x = -0.5*(p1+sqrt(disc)); + n1 = (a-x)*(a-x)+b*b; + n2 = c*c+(d-x)*(d-x); + if(n1>n2) { + n1 = sqrt(n1); + p = (a-x)/n1; + q = b/n1; + } else { + n2 = sqrt(n2); + p = c/n2; + q = (d-x)/n2; + } + Q0 = new T([[q,-p],[p,q]]); + Q.setRows(i,j,Q0.dot(Q.getRows(i,j))); + } else { + x = -0.5*p1; + y = 0.5*sqrt(-disc); + n1 = (a-x)*(a-x)+b*b; + n2 = c*c+(d-x)*(d-x); + if(n1>n2) { + n1 = sqrt(n1+y*y); + p = (a-x)/n1; + q = b/n1; + x = 0; + y /= n1; + } else { + n2 = sqrt(n2+y*y); + p = c/n2; + q = (d-x)/n2; + x = y/n2; + y = 0; + } + Q0 = new T([[q,-p],[p,q]],[[x,y],[y,-x]]); + Q.setRows(i,j,Q0.dot(Q.getRows(i,j))); + } + } + } + var R = Q.dot(A).dot(Q.transjugate()), n = A.length, E = numeric.T.identity(n); + for(j=0;j<n;j++) { + if(j>0) { + for(k=j-1;k>=0;k--) { + var Rk = R.get([k,k]), Rj = R.get([j,j]); + if(numeric.neq(Rk.x,Rj.x) || numeric.neq(Rk.y,Rj.y)) { + x = R.getRow(k).getBlock([k],[j-1]); + y = E.getRow(j).getBlock([k],[j-1]); + E.set([j,k],(R.get([k,j]).neg().sub(x.dot(y))).div(Rk.sub(Rj))); + } else { + E.setRow(j,E.getRow(k)); + continue; + } + } + } + } + for(j=0;j<n;j++) { + x = E.getRow(j); + E.setRow(j,x.div(x.norm2())); + } + E = E.transpose(); + E = Q.transjugate().dot(E); + return { lambda:R.getDiag(), E:E }; +}; + +// 5. Compressed Column Storage matrices +numeric.ccsSparse = function ccsSparse(A) { + var m = A.length,n,foo, i,j, counts = []; + for(i=m-1;i!==-1;--i) { + foo = A[i]; + for(j in foo) { + j = parseInt(j); + while(j>=counts.length) counts[counts.length] = 0; + if(foo[j]!==0) counts[j]++; + } + } + var n = counts.length; + var Ai = Array(n+1); + Ai[0] = 0; + for(i=0;i<n;++i) Ai[i+1] = Ai[i] + counts[i]; + var Aj = Array(Ai[n]), Av = Array(Ai[n]); + for(i=m-1;i!==-1;--i) { + foo = A[i]; + for(j in foo) { + if(foo[j]!==0) { + counts[j]--; + Aj[Ai[j]+counts[j]] = i; + Av[Ai[j]+counts[j]] = foo[j]; + } + } + } + return [Ai,Aj,Av]; +} +numeric.ccsFull = function ccsFull(A) { + var Ai = A[0], Aj = A[1], Av = A[2], s = numeric.ccsDim(A), m = s[0], n = s[1], i,j,j0,j1,k; + var B = numeric.rep([m,n],0); + for(i=0;i<n;i++) { + j0 = Ai[i]; + j1 = Ai[i+1]; + for(j=j0;j<j1;++j) { B[Aj[j]][i] = Av[j]; } + } + return B; +} +numeric.ccsTSolve = function ccsTSolve(A,b,x,bj,xj) { + var Ai = A[0], Aj = A[1], Av = A[2],m = Ai.length-1, max = Math.max,n=0; + if(typeof bj === "undefined") x = numeric.rep([m],0); + if(typeof bj === "undefined") bj = numeric.linspace(0,x.length-1); + if(typeof xj === "undefined") xj = []; + function dfs(j) { + var k; + if(x[j] !== 0) return; + x[j] = 1; + for(k=Ai[j];k<Ai[j+1];++k) dfs(Aj[k]); + xj[n] = j; + ++n; + } + var i,j,j0,j1,k,l,l0,l1,a; + for(i=bj.length-1;i!==-1;--i) { dfs(bj[i]); } + xj.length = n; + for(i=xj.length-1;i!==-1;--i) { x[xj[i]] = 0; } + for(i=bj.length-1;i!==-1;--i) { j = bj[i]; x[j] = b[j]; } + for(i=xj.length-1;i!==-1;--i) { + j = xj[i]; + j0 = Ai[j]; + j1 = max(Ai[j+1],j0); + for(k=j0;k!==j1;++k) { if(Aj[k] === j) { x[j] /= Av[k]; break; } } + a = x[j]; + for(k=j0;k!==j1;++k) { + l = Aj[k]; + if(l !== j) x[l] -= a*Av[k]; + } + } + return x; +} +numeric.ccsDFS = function ccsDFS(n) { + this.k = Array(n); + this.k1 = Array(n); + this.j = Array(n); +} +numeric.ccsDFS.prototype.dfs = function dfs(J,Ai,Aj,x,xj,Pinv) { + var m = 0,foo,n=xj.length; + var k = this.k, k1 = this.k1, j = this.j,km,k11; + if(x[J]!==0) return; + x[J] = 1; + j[0] = J; + k[0] = km = Ai[J]; + k1[0] = k11 = Ai[J+1]; + while(1) { + if(km >= k11) { + xj[n] = j[m]; + if(m===0) return; + ++n; + --m; + km = k[m]; + k11 = k1[m]; + } else { + foo = Pinv[Aj[km]]; + if(x[foo] === 0) { + x[foo] = 1; + k[m] = km; + ++m; + j[m] = foo; + km = Ai[foo]; + k1[m] = k11 = Ai[foo+1]; + } else ++km; + } + } +} +numeric.ccsLPSolve = function ccsLPSolve(A,B,x,xj,I,Pinv,dfs) { + var Ai = A[0], Aj = A[1], Av = A[2],m = Ai.length-1, n=0; + var Bi = B[0], Bj = B[1], Bv = B[2]; + + var i,i0,i1,j,J,j0,j1,k,l,l0,l1,a; + i0 = Bi[I]; + i1 = Bi[I+1]; + xj.length = 0; + for(i=i0;i<i1;++i) { dfs.dfs(Pinv[Bj[i]],Ai,Aj,x,xj,Pinv); } + for(i=xj.length-1;i!==-1;--i) { x[xj[i]] = 0; } + for(i=i0;i!==i1;++i) { j = Pinv[Bj[i]]; x[j] = Bv[i]; } + for(i=xj.length-1;i!==-1;--i) { + j = xj[i]; + j0 = Ai[j]; + j1 = Ai[j+1]; + for(k=j0;k<j1;++k) { if(Pinv[Aj[k]] === j) { x[j] /= Av[k]; break; } } + a = x[j]; + for(k=j0;k<j1;++k) { + l = Pinv[Aj[k]]; + if(l !== j) x[l] -= a*Av[k]; + } + } + return x; +} +numeric.ccsLUP1 = function ccsLUP1(A,threshold) { + var m = A[0].length-1; + var L = [numeric.rep([m+1],0),[],[]], U = [numeric.rep([m+1], 0),[],[]]; + var Li = L[0], Lj = L[1], Lv = L[2], Ui = U[0], Uj = U[1], Uv = U[2]; + var x = numeric.rep([m],0), xj = numeric.rep([m],0); + var i,j,k,j0,j1,a,e,c,d,K; + var sol = numeric.ccsLPSolve, max = Math.max, abs = Math.abs; + var P = numeric.linspace(0,m-1),Pinv = numeric.linspace(0,m-1); + var dfs = new numeric.ccsDFS(m); + if(typeof threshold === "undefined") { threshold = 1; } + for(i=0;i<m;++i) { + sol(L,A,x,xj,i,Pinv,dfs); + a = -1; + e = -1; + for(j=xj.length-1;j!==-1;--j) { + k = xj[j]; + if(k <= i) continue; + c = abs(x[k]); + if(c > a) { e = k; a = c; } + } + if(abs(x[i])<threshold*a) { + j = P[i]; + a = P[e]; + P[i] = a; Pinv[a] = i; + P[e] = j; Pinv[j] = e; + a = x[i]; x[i] = x[e]; x[e] = a; + } + a = Li[i]; + e = Ui[i]; + d = x[i]; + Lj[a] = P[i]; + Lv[a] = 1; + ++a; + for(j=xj.length-1;j!==-1;--j) { + k = xj[j]; + c = x[k]; + xj[j] = 0; + x[k] = 0; + if(k<=i) { Uj[e] = k; Uv[e] = c; ++e; } + else { Lj[a] = P[k]; Lv[a] = c/d; ++a; } + } + Li[i+1] = a; + Ui[i+1] = e; + } + for(j=Lj.length-1;j!==-1;--j) { Lj[j] = Pinv[Lj[j]]; } + return {L:L, U:U, P:P, Pinv:Pinv}; +} +numeric.ccsDFS0 = function ccsDFS0(n) { + this.k = Array(n); + this.k1 = Array(n); + this.j = Array(n); +} +numeric.ccsDFS0.prototype.dfs = function dfs(J,Ai,Aj,x,xj,Pinv,P) { + var m = 0,foo,n=xj.length; + var k = this.k, k1 = this.k1, j = this.j,km,k11; + if(x[J]!==0) return; + x[J] = 1; + j[0] = J; + k[0] = km = Ai[Pinv[J]]; + k1[0] = k11 = Ai[Pinv[J]+1]; + while(1) { + if(isNaN(km)) throw new Error("Ow!"); + if(km >= k11) { + xj[n] = Pinv[j[m]]; + if(m===0) return; + ++n; + --m; + km = k[m]; + k11 = k1[m]; + } else { + foo = Aj[km]; + if(x[foo] === 0) { + x[foo] = 1; + k[m] = km; + ++m; + j[m] = foo; + foo = Pinv[foo]; + km = Ai[foo]; + k1[m] = k11 = Ai[foo+1]; + } else ++km; + } + } +} +numeric.ccsLPSolve0 = function ccsLPSolve0(A,B,y,xj,I,Pinv,P,dfs) { + var Ai = A[0], Aj = A[1], Av = A[2],m = Ai.length-1, n=0; + var Bi = B[0], Bj = B[1], Bv = B[2]; + + var i,i0,i1,j,J,j0,j1,k,l,l0,l1,a; + i0 = Bi[I]; + i1 = Bi[I+1]; + xj.length = 0; + for(i=i0;i<i1;++i) { dfs.dfs(Bj[i],Ai,Aj,y,xj,Pinv,P); } + for(i=xj.length-1;i!==-1;--i) { j = xj[i]; y[P[j]] = 0; } + for(i=i0;i!==i1;++i) { j = Bj[i]; y[j] = Bv[i]; } + for(i=xj.length-1;i!==-1;--i) { + j = xj[i]; + l = P[j]; + j0 = Ai[j]; + j1 = Ai[j+1]; + for(k=j0;k<j1;++k) { if(Aj[k] === l) { y[l] /= Av[k]; break; } } + a = y[l]; + for(k=j0;k<j1;++k) y[Aj[k]] -= a*Av[k]; + y[l] = a; + } +} +numeric.ccsLUP0 = function ccsLUP0(A,threshold) { + var m = A[0].length-1; + var L = [numeric.rep([m+1],0),[],[]], U = [numeric.rep([m+1], 0),[],[]]; + var Li = L[0], Lj = L[1], Lv = L[2], Ui = U[0], Uj = U[1], Uv = U[2]; + var y = numeric.rep([m],0), xj = numeric.rep([m],0); + var i,j,k,j0,j1,a,e,c,d,K; + var sol = numeric.ccsLPSolve0, max = Math.max, abs = Math.abs; + var P = numeric.linspace(0,m-1),Pinv = numeric.linspace(0,m-1); + var dfs = new numeric.ccsDFS0(m); + if(typeof threshold === "undefined") { threshold = 1; } + for(i=0;i<m;++i) { + sol(L,A,y,xj,i,Pinv,P,dfs); + a = -1; + e = -1; + for(j=xj.length-1;j!==-1;--j) { + k = xj[j]; + if(k <= i) continue; + c = abs(y[P[k]]); + if(c > a) { e = k; a = c; } + } + if(abs(y[P[i]])<threshold*a) { + j = P[i]; + a = P[e]; + P[i] = a; Pinv[a] = i; + P[e] = j; Pinv[j] = e; + } + a = Li[i]; + e = Ui[i]; + d = y[P[i]]; + Lj[a] = P[i]; + Lv[a] = 1; + ++a; + for(j=xj.length-1;j!==-1;--j) { + k = xj[j]; + c = y[P[k]]; + xj[j] = 0; + y[P[k]] = 0; + if(k<=i) { Uj[e] = k; Uv[e] = c; ++e; } + else { Lj[a] = P[k]; Lv[a] = c/d; ++a; } + } + Li[i+1] = a; + Ui[i+1] = e; + } + for(j=Lj.length-1;j!==-1;--j) { Lj[j] = Pinv[Lj[j]]; } + return {L:L, U:U, P:P, Pinv:Pinv}; +} +numeric.ccsLUP = numeric.ccsLUP0; + +numeric.ccsDim = function ccsDim(A) { return [numeric.sup(A[1])+1,A[0].length-1]; } +numeric.ccsGetBlock = function ccsGetBlock(A,i,j) { + var s = numeric.ccsDim(A),m=s[0],n=s[1]; + if(typeof i === "undefined") { i = numeric.linspace(0,m-1); } + else if(typeof i === "number") { i = [i]; } + if(typeof j === "undefined") { j = numeric.linspace(0,n-1); } + else if(typeof j === "number") { j = [j]; } + var p,p0,p1,P = i.length,q,Q = j.length,r,jq,ip; + var Bi = numeric.rep([n],0), Bj=[], Bv=[], B = [Bi,Bj,Bv]; + var Ai = A[0], Aj = A[1], Av = A[2]; + var x = numeric.rep([m],0),count=0,flags = numeric.rep([m],0); + for(q=0;q<Q;++q) { + jq = j[q]; + var q0 = Ai[jq]; + var q1 = Ai[jq+1]; + for(p=q0;p<q1;++p) { + r = Aj[p]; + flags[r] = 1; + x[r] = Av[p]; + } + for(p=0;p<P;++p) { + ip = i[p]; + if(flags[ip]) { + Bj[count] = p; + Bv[count] = x[i[p]]; + ++count; + } + } + for(p=q0;p<q1;++p) { + r = Aj[p]; + flags[r] = 0; + } + Bi[q+1] = count; + } + return B; +} + +numeric.ccsDot = function ccsDot(A,B) { + var Ai = A[0], Aj = A[1], Av = A[2]; + var Bi = B[0], Bj = B[1], Bv = B[2]; + var sA = numeric.ccsDim(A), sB = numeric.ccsDim(B); + var m = sA[0], n = sA[1], o = sB[1]; + var x = numeric.rep([m],0), flags = numeric.rep([m],0), xj = Array(m); + var Ci = numeric.rep([o],0), Cj = [], Cv = [], C = [Ci,Cj,Cv]; + var i,j,k,j0,j1,i0,i1,l,p,a,b; + for(k=0;k!==o;++k) { + j0 = Bi[k]; + j1 = Bi[k+1]; + p = 0; + for(j=j0;j<j1;++j) { + a = Bj[j]; + b = Bv[j]; + i0 = Ai[a]; + i1 = Ai[a+1]; + for(i=i0;i<i1;++i) { + l = Aj[i]; + if(flags[l]===0) { + xj[p] = l; + flags[l] = 1; + p = p+1; + } + x[l] = x[l] + Av[i]*b; + } + } + j0 = Ci[k]; + j1 = j0+p; + Ci[k+1] = j1; + for(j=p-1;j!==-1;--j) { + b = j0+j; + i = xj[j]; + Cj[b] = i; + Cv[b] = x[i]; + flags[i] = 0; + x[i] = 0; + } + Ci[k+1] = Ci[k]+p; + } + return C; +} + +numeric.ccsLUPSolve = function ccsLUPSolve(LUP,B) { + var L = LUP.L, U = LUP.U, P = LUP.P; + var Bi = B[0]; + var flag = false; + if(typeof Bi !== "object") { B = [[0,B.length],numeric.linspace(0,B.length-1),B]; Bi = B[0]; flag = true; } + var Bj = B[1], Bv = B[2]; + var n = L[0].length-1, m = Bi.length-1; + var x = numeric.rep([n],0), xj = Array(n); + var b = numeric.rep([n],0), bj = Array(n); + var Xi = numeric.rep([m+1],0), Xj = [], Xv = []; + var sol = numeric.ccsTSolve; + var i,j,j0,j1,k,J,N=0; + for(i=0;i<m;++i) { + k = 0; + j0 = Bi[i]; + j1 = Bi[i+1]; + for(j=j0;j<j1;++j) { + J = LUP.Pinv[Bj[j]]; + bj[k] = J; + b[J] = Bv[j]; + ++k; + } + bj.length = k; + sol(L,b,x,bj,xj); + for(j=bj.length-1;j!==-1;--j) b[bj[j]] = 0; + sol(U,x,b,xj,bj); + if(flag) return b; + for(j=xj.length-1;j!==-1;--j) x[xj[j]] = 0; + for(j=bj.length-1;j!==-1;--j) { + J = bj[j]; + Xj[N] = J; + Xv[N] = b[J]; + b[J] = 0; + ++N; + } + Xi[i+1] = N; + } + return [Xi,Xj,Xv]; +} + +numeric.ccsbinop = function ccsbinop(body,setup) { + if(typeof setup === "undefined") setup=''; + return Function('X','Y', + 'var Xi = X[0], Xj = X[1], Xv = X[2];\n'+ + 'var Yi = Y[0], Yj = Y[1], Yv = Y[2];\n'+ + 'var n = Xi.length-1,m = Math.max(numeric.sup(Xj),numeric.sup(Yj))+1;\n'+ + 'var Zi = numeric.rep([n+1],0), Zj = [], Zv = [];\n'+ + 'var x = numeric.rep([m],0),y = numeric.rep([m],0);\n'+ + 'var xk,yk,zk;\n'+ + 'var i,j,j0,j1,k,p=0;\n'+ + setup+ + 'for(i=0;i<n;++i) {\n'+ + ' j0 = Xi[i]; j1 = Xi[i+1];\n'+ + ' for(j=j0;j!==j1;++j) {\n'+ + ' k = Xj[j];\n'+ + ' x[k] = 1;\n'+ + ' Zj[p] = k;\n'+ + ' ++p;\n'+ + ' }\n'+ + ' j0 = Yi[i]; j1 = Yi[i+1];\n'+ + ' for(j=j0;j!==j1;++j) {\n'+ + ' k = Yj[j];\n'+ + ' y[k] = Yv[j];\n'+ + ' if(x[k] === 0) {\n'+ + ' Zj[p] = k;\n'+ + ' ++p;\n'+ + ' }\n'+ + ' }\n'+ + ' Zi[i+1] = p;\n'+ + ' j0 = Xi[i]; j1 = Xi[i+1];\n'+ + ' for(j=j0;j!==j1;++j) x[Xj[j]] = Xv[j];\n'+ + ' j0 = Zi[i]; j1 = Zi[i+1];\n'+ + ' for(j=j0;j!==j1;++j) {\n'+ + ' k = Zj[j];\n'+ + ' xk = x[k];\n'+ + ' yk = y[k];\n'+ + body+'\n'+ + ' Zv[j] = zk;\n'+ + ' }\n'+ + ' j0 = Xi[i]; j1 = Xi[i+1];\n'+ + ' for(j=j0;j!==j1;++j) x[Xj[j]] = 0;\n'+ + ' j0 = Yi[i]; j1 = Yi[i+1];\n'+ + ' for(j=j0;j!==j1;++j) y[Yj[j]] = 0;\n'+ + '}\n'+ + 'return [Zi,Zj,Zv];' + ); +}; + +(function() { + var k,A,B,C; + for(k in numeric.ops2) { + if(isFinite(eval('1'+numeric.ops2[k]+'0'))) A = '[Y[0],Y[1],numeric.'+k+'(X,Y[2])]'; + else A = 'NaN'; + if(isFinite(eval('0'+numeric.ops2[k]+'1'))) B = '[X[0],X[1],numeric.'+k+'(X[2],Y)]'; + else B = 'NaN'; + if(isFinite(eval('1'+numeric.ops2[k]+'0')) && isFinite(eval('0'+numeric.ops2[k]+'1'))) C = 'numeric.ccs'+k+'MM(X,Y)'; + else C = 'NaN'; + numeric['ccs'+k+'MM'] = numeric.ccsbinop('zk = xk '+numeric.ops2[k]+'yk;'); + numeric['ccs'+k] = Function('X','Y', + 'if(typeof X === "number") return '+A+';\n'+ + 'if(typeof Y === "number") return '+B+';\n'+ + 'return '+C+';\n' + ); + } +}()); + +numeric.ccsScatter = function ccsScatter(A) { + var Ai = A[0], Aj = A[1], Av = A[2]; + var n = numeric.sup(Aj)+1,m=Ai.length; + var Ri = numeric.rep([n],0),Rj=Array(m), Rv = Array(m); + var counts = numeric.rep([n],0),i; + for(i=0;i<m;++i) counts[Aj[i]]++; + for(i=0;i<n;++i) Ri[i+1] = Ri[i] + counts[i]; + var ptr = Ri.slice(0),k,Aii; + for(i=0;i<m;++i) { + Aii = Aj[i]; + k = ptr[Aii]; + Rj[k] = Ai[i]; + Rv[k] = Av[i]; + ptr[Aii]=ptr[Aii]+1; + } + return [Ri,Rj,Rv]; +} + +numeric.ccsGather = function ccsGather(A) { + var Ai = A[0], Aj = A[1], Av = A[2]; + var n = Ai.length-1,m = Aj.length; + var Ri = Array(m), Rj = Array(m), Rv = Array(m); + var i,j,j0,j1,p; + p=0; + for(i=0;i<n;++i) { + j0 = Ai[i]; + j1 = Ai[i+1]; + for(j=j0;j!==j1;++j) { + Rj[p] = i; + Ri[p] = Aj[j]; + Rv[p] = Av[j]; + ++p; + } + } + return [Ri,Rj,Rv]; +} + +// The following sparse linear algebra routines are deprecated. + +numeric.sdim = function dim(A,ret,k) { + if(typeof ret === "undefined") { ret = []; } + if(typeof A !== "object") return ret; + if(typeof k === "undefined") { k=0; } + if(!(k in ret)) { ret[k] = 0; } + if(A.length > ret[k]) ret[k] = A.length; + var i; + for(i in A) { + if(A.hasOwnProperty(i)) dim(A[i],ret,k+1); + } + return ret; +}; + +numeric.sclone = function clone(A,k,n) { + if(typeof k === "undefined") { k=0; } + if(typeof n === "undefined") { n = numeric.sdim(A).length; } + var i,ret = Array(A.length); + if(k === n-1) { + for(i in A) { if(A.hasOwnProperty(i)) ret[i] = A[i]; } + return ret; + } + for(i in A) { + if(A.hasOwnProperty(i)) ret[i] = clone(A[i],k+1,n); + } + return ret; +} + +numeric.sdiag = function diag(d) { + var n = d.length,i,ret = Array(n),i1,i2,i3; + for(i=n-1;i>=1;i-=2) { + i1 = i-1; + ret[i] = []; ret[i][i] = d[i]; + ret[i1] = []; ret[i1][i1] = d[i1]; + } + if(i===0) { ret[0] = []; ret[0][0] = d[i]; } + return ret; +} + +numeric.sidentity = function identity(n) { return numeric.sdiag(numeric.rep([n],1)); } + +numeric.stranspose = function transpose(A) { + var ret = [], n = A.length, i,j,Ai; + for(i in A) { + if(!(A.hasOwnProperty(i))) continue; + Ai = A[i]; + for(j in Ai) { + if(!(Ai.hasOwnProperty(j))) continue; + if(typeof ret[j] !== "object") { ret[j] = []; } + ret[j][i] = Ai[j]; + } + } + return ret; +} + +numeric.sLUP = function LUP(A,tol) { + throw new Error("The function numeric.sLUP had a bug in it and has been removed. Please use the new numeric.ccsLUP function instead."); +}; + +numeric.sdotMM = function dotMM(A,B) { + var p = A.length, q = B.length, BT = numeric.stranspose(B), r = BT.length, Ai, BTk; + var i,j,k,accum; + var ret = Array(p),reti; + for(i=p-1;i>=0;i--) { + reti = []; + Ai = A[i]; + for(k=r-1;k>=0;k--) { + accum = 0; + BTk = BT[k]; + for(j in Ai) { + if(!(Ai.hasOwnProperty(j))) continue; + if(j in BTk) { accum += Ai[j]*BTk[j]; } + } + if(accum) reti[k] = accum; + } + ret[i] = reti; + } + return ret; +} + +numeric.sdotMV = function dotMV(A,x) { + var p = A.length, Ai, i,j; + var ret = Array(p), accum; + for(i=p-1;i>=0;i--) { + Ai = A[i]; + accum = 0; + for(j in Ai) { + if(!(Ai.hasOwnProperty(j))) continue; + if(x[j]) accum += Ai[j]*x[j]; + } + if(accum) ret[i] = accum; + } + return ret; +} + +numeric.sdotVM = function dotMV(x,A) { + var i,j,Ai,alpha; + var ret = [], accum; + for(i in x) { + if(!x.hasOwnProperty(i)) continue; + Ai = A[i]; + alpha = x[i]; + for(j in Ai) { + if(!Ai.hasOwnProperty(j)) continue; + if(!ret[j]) { ret[j] = 0; } + ret[j] += alpha*Ai[j]; + } + } + return ret; +} + +numeric.sdotVV = function dotVV(x,y) { + var i,ret=0; + for(i in x) { if(x[i] && y[i]) ret+= x[i]*y[i]; } + return ret; +} + +numeric.sdot = function dot(A,B) { + var m = numeric.sdim(A).length, n = numeric.sdim(B).length; + var k = m*1000+n; + switch(k) { + case 0: return A*B; + case 1001: return numeric.sdotVV(A,B); + case 2001: return numeric.sdotMV(A,B); + case 1002: return numeric.sdotVM(A,B); + case 2002: return numeric.sdotMM(A,B); + default: throw new Error('numeric.sdot not implemented for tensors of order '+m+' and '+n); + } +} + +numeric.sscatter = function scatter(V) { + var n = V[0].length, Vij, i, j, m = V.length, A = [], Aj; + for(i=n-1;i>=0;--i) { + if(!V[m-1][i]) continue; + Aj = A; + for(j=0;j<m-2;j++) { + Vij = V[j][i]; + if(!Aj[Vij]) Aj[Vij] = []; + Aj = Aj[Vij]; + } + Aj[V[j][i]] = V[j+1][i]; + } + return A; +} + +numeric.sgather = function gather(A,ret,k) { + if(typeof ret === "undefined") ret = []; + if(typeof k === "undefined") k = []; + var n,i,Ai; + n = k.length; + for(i in A) { + if(A.hasOwnProperty(i)) { + k[n] = parseInt(i); + Ai = A[i]; + if(typeof Ai === "number") { + if(Ai) { + if(ret.length === 0) { + for(i=n+1;i>=0;--i) ret[i] = []; + } + for(i=n;i>=0;--i) ret[i].push(k[i]); + ret[n+1].push(Ai); + } + } else gather(Ai,ret,k); + } + } + if(k.length>n) k.pop(); + return ret; +} + +// 6. Coordinate matrices +numeric.cLU = function LU(A) { + var I = A[0], J = A[1], V = A[2]; + var p = I.length, m=0, i,j,k,a,b,c; + for(i=0;i<p;i++) if(I[i]>m) m=I[i]; + m++; + var L = Array(m), U = Array(m), left = numeric.rep([m],Infinity), right = numeric.rep([m],-Infinity); + var Ui, Uj,alpha; + for(k=0;k<p;k++) { + i = I[k]; + j = J[k]; + if(j<left[i]) left[i] = j; + if(j>right[i]) right[i] = j; + } + for(i=0;i<m-1;i++) { if(right[i] > right[i+1]) right[i+1] = right[i]; } + for(i=m-1;i>=1;i--) { if(left[i]<left[i-1]) left[i-1] = left[i]; } + var countL = 0, countU = 0; + for(i=0;i<m;i++) { + U[i] = numeric.rep([right[i]-left[i]+1],0); + L[i] = numeric.rep([i-left[i]],0); + countL += i-left[i]+1; + countU += right[i]-i+1; + } + for(k=0;k<p;k++) { i = I[k]; U[i][J[k]-left[i]] = V[k]; } + for(i=0;i<m-1;i++) { + a = i-left[i]; + Ui = U[i]; + for(j=i+1;left[j]<=i && j<m;j++) { + b = i-left[j]; + c = right[i]-i; + Uj = U[j]; + alpha = Uj[b]/Ui[a]; + if(alpha) { + for(k=1;k<=c;k++) { Uj[k+b] -= alpha*Ui[k+a]; } + L[j][i-left[j]] = alpha; + } + } + } + var Ui = [], Uj = [], Uv = [], Li = [], Lj = [], Lv = []; + var p,q,foo; + p=0; q=0; + for(i=0;i<m;i++) { + a = left[i]; + b = right[i]; + foo = U[i]; + for(j=i;j<=b;j++) { + if(foo[j-a]) { + Ui[p] = i; + Uj[p] = j; + Uv[p] = foo[j-a]; + p++; + } + } + foo = L[i]; + for(j=a;j<i;j++) { + if(foo[j-a]) { + Li[q] = i; + Lj[q] = j; + Lv[q] = foo[j-a]; + q++; + } + } + Li[q] = i; + Lj[q] = i; + Lv[q] = 1; + q++; + } + return {U:[Ui,Uj,Uv], L:[Li,Lj,Lv]}; +}; + +numeric.cLUsolve = function LUsolve(lu,b) { + var L = lu.L, U = lu.U, ret = numeric.clone(b); + var Li = L[0], Lj = L[1], Lv = L[2]; + var Ui = U[0], Uj = U[1], Uv = U[2]; + var p = Ui.length, q = Li.length; + var m = ret.length,i,j,k; + k = 0; + for(i=0;i<m;i++) { + while(Lj[k] < i) { + ret[i] -= Lv[k]*ret[Lj[k]]; + k++; + } + k++; + } + k = p-1; + for(i=m-1;i>=0;i--) { + while(Uj[k] > i) { + ret[i] -= Uv[k]*ret[Uj[k]]; + k--; + } + ret[i] /= Uv[k]; + k--; + } + return ret; +}; + +numeric.cgrid = function grid(n,shape) { + if(typeof n === "number") n = [n,n]; + var ret = numeric.rep(n,-1); + var i,j,count; + if(typeof shape !== "function") { + switch(shape) { + case 'L': + shape = function(i,j) { return (i>=n[0]/2 || j<n[1]/2); } + break; + default: + shape = function(i,j) { return true; }; + break; + } + } + count=0; + for(i=1;i<n[0]-1;i++) for(j=1;j<n[1]-1;j++) + if(shape(i,j)) { + ret[i][j] = count; + count++; + } + return ret; +} + +numeric.cdelsq = function delsq(g) { + var dir = [[-1,0],[0,-1],[0,1],[1,0]]; + var s = numeric.dim(g), m = s[0], n = s[1], i,j,k,p,q; + var Li = [], Lj = [], Lv = []; + for(i=1;i<m-1;i++) for(j=1;j<n-1;j++) { + if(g[i][j]<0) continue; + for(k=0;k<4;k++) { + p = i+dir[k][0]; + q = j+dir[k][1]; + if(g[p][q]<0) continue; + Li.push(g[i][j]); + Lj.push(g[p][q]); + Lv.push(-1); + } + Li.push(g[i][j]); + Lj.push(g[i][j]); + Lv.push(4); + } + return [Li,Lj,Lv]; +} + +numeric.cdotMV = function dotMV(A,x) { + var ret, Ai = A[0], Aj = A[1], Av = A[2],k,p=Ai.length,N; + N=0; + for(k=0;k<p;k++) { if(Ai[k]>N) N = Ai[k]; } + N++; + ret = numeric.rep([N],0); + for(k=0;k<p;k++) { ret[Ai[k]]+=Av[k]*x[Aj[k]]; } + return ret; +} + +// 7. Splines + +numeric.Spline = function Spline(x,yl,yr,kl,kr) { this.x = x; this.yl = yl; this.yr = yr; this.kl = kl; this.kr = kr; } +numeric.Spline.prototype._at = function _at(x1,p) { + var x = this.x; + var yl = this.yl; + var yr = this.yr; + var kl = this.kl; + var kr = this.kr; + var x1,a,b,t; + var add = numeric.add, sub = numeric.sub, mul = numeric.mul; + a = sub(mul(kl[p],x[p+1]-x[p]),sub(yr[p+1],yl[p])); + b = add(mul(kr[p+1],x[p]-x[p+1]),sub(yr[p+1],yl[p])); + t = (x1-x[p])/(x[p+1]-x[p]); + var s = t*(1-t); + return add(add(add(mul(1-t,yl[p]),mul(t,yr[p+1])),mul(a,s*(1-t))),mul(b,s*t)); +} +numeric.Spline.prototype.at = function at(x0) { + if(typeof x0 === "number") { + var x = this.x; + var n = x.length; + var p,q,mid,floor = Math.floor,a,b,t; + p = 0; + q = n-1; + while(q-p>1) { + mid = floor((p+q)/2); + if(x[mid] <= x0) p = mid; + else q = mid; + } + return this._at(x0,p); + } + var n = x0.length, i, ret = Array(n); + for(i=n-1;i!==-1;--i) ret[i] = this.at(x0[i]); + return ret; +} +numeric.Spline.prototype.diff = function diff() { + var x = this.x; + var yl = this.yl; + var yr = this.yr; + var kl = this.kl; + var kr = this.kr; + var n = yl.length; + var i,dx,dy; + var zl = kl, zr = kr, pl = Array(n), pr = Array(n); + var add = numeric.add, mul = numeric.mul, div = numeric.div, sub = numeric.sub; + for(i=n-1;i!==-1;--i) { + dx = x[i+1]-x[i]; + dy = sub(yr[i+1],yl[i]); + pl[i] = div(add(mul(dy, 6),mul(kl[i],-4*dx),mul(kr[i+1],-2*dx)),dx*dx); + pr[i+1] = div(add(mul(dy,-6),mul(kl[i], 2*dx),mul(kr[i+1], 4*dx)),dx*dx); + } + return new numeric.Spline(x,zl,zr,pl,pr); +} +numeric.Spline.prototype.roots = function roots() { + function sqr(x) { return x*x; } + function heval(y0,y1,k0,k1,x) { + var A = k0*2-(y1-y0); + var B = -k1*2+(y1-y0); + var t = (x+1)*0.5; + var s = t*(1-t); + return (1-t)*y0+t*y1+A*s*(1-t)+B*s*t; + } + var ret = []; + var x = this.x, yl = this.yl, yr = this.yr, kl = this.kl, kr = this.kr; + if(typeof yl[0] === "number") { + yl = [yl]; + yr = [yr]; + kl = [kl]; + kr = [kr]; + } + var m = yl.length,n=x.length-1,i,j,k,y,s,t; + var ai,bi,ci,di, ret = Array(m),ri,k0,k1,y0,y1,A,B,D,dx,cx,stops,z0,z1,zm,t0,t1,tm; + var sqrt = Math.sqrt; + for(i=0;i!==m;++i) { + ai = yl[i]; + bi = yr[i]; + ci = kl[i]; + di = kr[i]; + ri = []; + for(j=0;j!==n;j++) { + if(j>0 && bi[j]*ai[j]<0) ri.push(x[j]); + dx = (x[j+1]-x[j]); + cx = x[j]; + y0 = ai[j]; + y1 = bi[j+1]; + k0 = ci[j]/dx; + k1 = di[j+1]/dx; + D = sqr(k0-k1+3*(y0-y1)) + 12*k1*y0; + A = k1+3*y0+2*k0-3*y1; + B = 3*(k1+k0+2*(y0-y1)); + if(D<=0) { + z0 = A/B; + if(z0>x[j] && z0<x[j+1]) stops = [x[j],z0,x[j+1]]; + else stops = [x[j],x[j+1]]; + } else { + z0 = (A-sqrt(D))/B; + z1 = (A+sqrt(D))/B; + stops = [x[j]]; + if(z0>x[j] && z0<x[j+1]) stops.push(z0); + if(z1>x[j] && z1<x[j+1]) stops.push(z1); + stops.push(x[j+1]); + } + t0 = stops[0]; + z0 = this._at(t0,j); + for(k=0;k<stops.length-1;k++) { + t1 = stops[k+1]; + z1 = this._at(t1,j); + if(z0 === 0) { + ri.push(t0); + t0 = t1; + z0 = z1; + continue; + } + if(z1 === 0 || z0*z1>0) { + t0 = t1; + z0 = z1; + continue; + } + var side = 0; + while(1) { + tm = (z0*t1-z1*t0)/(z0-z1); + if(tm <= t0 || tm >= t1) { break; } + zm = this._at(tm,j); + if(zm*z1>0) { + t1 = tm; + z1 = zm; + if(side === -1) z0*=0.5; + side = -1; + } else if(zm*z0>0) { + t0 = tm; + z0 = zm; + if(side === 1) z1*=0.5; + side = 1; + } else break; + } + ri.push(tm); + t0 = stops[k+1]; + z0 = this._at(t0, j); + } + if(z1 === 0) ri.push(t1); + } + ret[i] = ri; + } + if(typeof this.yl[0] === "number") return ret[0]; + return ret; +} +numeric.spline = function spline(x,y,k1,kn) { + var n = x.length, b = [], dx = [], dy = []; + var i; + var sub = numeric.sub,mul = numeric.mul,add = numeric.add; + for(i=n-2;i>=0;i--) { dx[i] = x[i+1]-x[i]; dy[i] = sub(y[i+1],y[i]); } + if(typeof k1 === "string" || typeof kn === "string") { + k1 = kn = "periodic"; + } + // Build sparse tridiagonal system + var T = [[],[],[]]; + switch(typeof k1) { + case "undefined": + b[0] = mul(3/(dx[0]*dx[0]),dy[0]); + T[0].push(0,0); + T[1].push(0,1); + T[2].push(2/dx[0],1/dx[0]); + break; + case "string": + b[0] = add(mul(3/(dx[n-2]*dx[n-2]),dy[n-2]),mul(3/(dx[0]*dx[0]),dy[0])); + T[0].push(0,0,0); + T[1].push(n-2,0,1); + T[2].push(1/dx[n-2],2/dx[n-2]+2/dx[0],1/dx[0]); + break; + default: + b[0] = k1; + T[0].push(0); + T[1].push(0); + T[2].push(1); + break; + } + for(i=1;i<n-1;i++) { + b[i] = add(mul(3/(dx[i-1]*dx[i-1]),dy[i-1]),mul(3/(dx[i]*dx[i]),dy[i])); + T[0].push(i,i,i); + T[1].push(i-1,i,i+1); + T[2].push(1/dx[i-1],2/dx[i-1]+2/dx[i],1/dx[i]); + } + switch(typeof kn) { + case "undefined": + b[n-1] = mul(3/(dx[n-2]*dx[n-2]),dy[n-2]); + T[0].push(n-1,n-1); + T[1].push(n-2,n-1); + T[2].push(1/dx[n-2],2/dx[n-2]); + break; + case "string": + T[1][T[1].length-1] = 0; + break; + default: + b[n-1] = kn; + T[0].push(n-1); + T[1].push(n-1); + T[2].push(1); + break; + } + if(typeof b[0] !== "number") b = numeric.transpose(b); + else b = [b]; + var k = Array(b.length); + if(typeof k1 === "string") { + for(i=k.length-1;i!==-1;--i) { + k[i] = numeric.ccsLUPSolve(numeric.ccsLUP(numeric.ccsScatter(T)),b[i]); + k[i][n-1] = k[i][0]; + } + } else { + for(i=k.length-1;i!==-1;--i) { + k[i] = numeric.cLUsolve(numeric.cLU(T),b[i]); + } + } + if(typeof y[0] === "number") k = k[0]; + else k = numeric.transpose(k); + return new numeric.Spline(x,y,y,k,k); +} + +// 8. FFT +numeric.fftpow2 = function fftpow2(x,y) { + var n = x.length; + if(n === 1) return; + var cos = Math.cos, sin = Math.sin, i,j; + var xe = Array(n/2), ye = Array(n/2), xo = Array(n/2), yo = Array(n/2); + j = n/2; + for(i=n-1;i!==-1;--i) { + --j; + xo[j] = x[i]; + yo[j] = y[i]; + --i; + xe[j] = x[i]; + ye[j] = y[i]; + } + fftpow2(xe,ye); + fftpow2(xo,yo); + j = n/2; + var t,k = (-6.2831853071795864769252867665590057683943387987502116419/n),ci,si; + for(i=n-1;i!==-1;--i) { + --j; + if(j === -1) j = n/2-1; + t = k*i; + ci = cos(t); + si = sin(t); + x[i] = xe[j] + ci*xo[j] - si*yo[j]; + y[i] = ye[j] + ci*yo[j] + si*xo[j]; + } +} +numeric._ifftpow2 = function _ifftpow2(x,y) { + var n = x.length; + if(n === 1) return; + var cos = Math.cos, sin = Math.sin, i,j; + var xe = Array(n/2), ye = Array(n/2), xo = Array(n/2), yo = Array(n/2); + j = n/2; + for(i=n-1;i!==-1;--i) { + --j; + xo[j] = x[i]; + yo[j] = y[i]; + --i; + xe[j] = x[i]; + ye[j] = y[i]; + } + _ifftpow2(xe,ye); + _ifftpow2(xo,yo); + j = n/2; + var t,k = (6.2831853071795864769252867665590057683943387987502116419/n),ci,si; + for(i=n-1;i!==-1;--i) { + --j; + if(j === -1) j = n/2-1; + t = k*i; + ci = cos(t); + si = sin(t); + x[i] = xe[j] + ci*xo[j] - si*yo[j]; + y[i] = ye[j] + ci*yo[j] + si*xo[j]; + } +} +numeric.ifftpow2 = function ifftpow2(x,y) { + numeric._ifftpow2(x,y); + numeric.diveq(x,x.length); + numeric.diveq(y,y.length); +} +numeric.convpow2 = function convpow2(ax,ay,bx,by) { + numeric.fftpow2(ax,ay); + numeric.fftpow2(bx,by); + var i,n = ax.length,axi,bxi,ayi,byi; + for(i=n-1;i!==-1;--i) { + axi = ax[i]; ayi = ay[i]; bxi = bx[i]; byi = by[i]; + ax[i] = axi*bxi-ayi*byi; + ay[i] = axi*byi+ayi*bxi; + } + numeric.ifftpow2(ax,ay); +} +numeric.T.prototype.fft = function fft() { + var x = this.x, y = this.y; + var n = x.length, log = Math.log, log2 = log(2), + p = Math.ceil(log(2*n-1)/log2), m = Math.pow(2,p); + var cx = numeric.rep([m],0), cy = numeric.rep([m],0), cos = Math.cos, sin = Math.sin; + var k, c = (-3.141592653589793238462643383279502884197169399375105820/n),t; + var a = numeric.rep([m],0), b = numeric.rep([m],0),nhalf = Math.floor(n/2); + for(k=0;k<n;k++) a[k] = x[k]; + if(typeof y !== "undefined") for(k=0;k<n;k++) b[k] = y[k]; + cx[0] = 1; + for(k=1;k<=m/2;k++) { + t = c*k*k; + cx[k] = cos(t); + cy[k] = sin(t); + cx[m-k] = cos(t); + cy[m-k] = sin(t) + } + var X = new numeric.T(a,b), Y = new numeric.T(cx,cy); + X = X.mul(Y); + numeric.convpow2(X.x,X.y,numeric.clone(Y.x),numeric.neg(Y.y)); + X = X.mul(Y); + X.x.length = n; + X.y.length = n; + return X; +} +numeric.T.prototype.ifft = function ifft() { + var x = this.x, y = this.y; + var n = x.length, log = Math.log, log2 = log(2), + p = Math.ceil(log(2*n-1)/log2), m = Math.pow(2,p); + var cx = numeric.rep([m],0), cy = numeric.rep([m],0), cos = Math.cos, sin = Math.sin; + var k, c = (3.141592653589793238462643383279502884197169399375105820/n),t; + var a = numeric.rep([m],0), b = numeric.rep([m],0),nhalf = Math.floor(n/2); + for(k=0;k<n;k++) a[k] = x[k]; + if(typeof y !== "undefined") for(k=0;k<n;k++) b[k] = y[k]; + cx[0] = 1; + for(k=1;k<=m/2;k++) { + t = c*k*k; + cx[k] = cos(t); + cy[k] = sin(t); + cx[m-k] = cos(t); + cy[m-k] = sin(t) + } + var X = new numeric.T(a,b), Y = new numeric.T(cx,cy); + X = X.mul(Y); + numeric.convpow2(X.x,X.y,numeric.clone(Y.x),numeric.neg(Y.y)); + X = X.mul(Y); + X.x.length = n; + X.y.length = n; + return X.div(n); +} + +//9. Unconstrained optimization +numeric.gradient = function gradient(f,x) { + var n = x.length; + var f0 = f(x); + if(isNaN(f0)) throw new Error('gradient: f(x) is a NaN!'); + var max = Math.max; + var i,x0 = numeric.clone(x),f1,f2, J = Array(n); + var div = numeric.div, sub = numeric.sub,errest,roundoff,max = Math.max,eps = 1e-3,abs = Math.abs, min = Math.min; + var t0,t1,t2,it=0,d1,d2,N; + for(i=0;i<n;i++) { + var h = max(1e-6*f0,1e-8); + while(1) { + ++it; + if(it>20) { throw new Error("Numerical gradient fails"); } + x0[i] = x[i]+h; + f1 = f(x0); + x0[i] = x[i]-h; + f2 = f(x0); + x0[i] = x[i]; + if(isNaN(f1) || isNaN(f2)) { h/=16; continue; } + J[i] = (f1-f2)/(2*h); + t0 = x[i]-h; + t1 = x[i]; + t2 = x[i]+h; + d1 = (f1-f0)/h; + d2 = (f0-f2)/h; + N = max(abs(J[i]),abs(f0),abs(f1),abs(f2),abs(t0),abs(t1),abs(t2),1e-8); + errest = min(max(abs(d1-J[i]),abs(d2-J[i]),abs(d1-d2))/N,h/N); + if(errest>eps) { h/=16; } + else break; + } + } + return J; +} + +numeric.uncmin = function uncmin(f,x0,tol,gradient,maxit,callback,options) { + var grad = numeric.gradient; + if(typeof options === "undefined") { options = {}; } + if(typeof tol === "undefined") { tol = 1e-8; } + if(typeof gradient === "undefined") { gradient = function(x) { return grad(f,x); }; } + if(typeof maxit === "undefined") maxit = 1000; + x0 = numeric.clone(x0); + var n = x0.length; + var f0 = f(x0),f1,df0; + if(isNaN(f0)) throw new Error('uncmin: f(x0) is a NaN!'); + var max = Math.max, norm2 = numeric.norm2; + tol = max(tol,numeric.epsilon); + var step,g0,g1,H1 = options.Hinv || numeric.identity(n); + var dot = numeric.dot, inv = numeric.inv, sub = numeric.sub, add = numeric.add, ten = numeric.tensor, div = numeric.div, mul = numeric.mul; + var all = numeric.all, isfinite = numeric.isFinite, neg = numeric.neg; + var it=0,i,s,x1,y,Hy,Hs,ys,i0,t,nstep,t1,t2; + var msg = ""; + g0 = gradient(x0); + while(it<maxit) { + if(typeof callback === "function") { if(callback(it,x0,f0,g0,H1)) { msg = "Callback returned true"; break; } } + if(!all(isfinite(g0))) { msg = "Gradient has Infinity or NaN"; break; } + step = neg(dot(H1,g0)); + if(!all(isfinite(step))) { msg = "Search direction has Infinity or NaN"; break; } + nstep = norm2(step); + if(nstep < tol) { msg="Newton step smaller than tol"; break; } + t = 1; + df0 = dot(g0,step); + // line search + x1 = x0; + while(it < maxit) { + if(t*nstep < tol) { break; } + s = mul(step,t); + x1 = add(x0,s); + f1 = f(x1); + if(f1-f0 >= 0.1*t*df0 || isNaN(f1)) { + t *= 0.5; + ++it; + continue; + } + break; + } + if(t*nstep < tol) { msg = "Line search step size smaller than tol"; break; } + if(it === maxit) { msg = "maxit reached during line search"; break; } + g1 = gradient(x1); + y = sub(g1,g0); + ys = dot(y,s); + Hy = dot(H1,y); + H1 = sub(add(H1, + mul( + (ys+dot(y,Hy))/(ys*ys), + ten(s,s) )), + div(add(ten(Hy,s),ten(s,Hy)),ys)); + x0 = x1; + f0 = f1; + g0 = g1; + ++it; + } + return {solution: x0, f: f0, gradient: g0, invHessian: H1, iterations:it, message: msg}; +} + +// 10. Ode solver (Dormand-Prince) +numeric.Dopri = function Dopri(x,y,f,ymid,iterations,msg,events) { + this.x = x; + this.y = y; + this.f = f; + this.ymid = ymid; + this.iterations = iterations; + this.events = events; + this.message = msg; +} +numeric.Dopri.prototype._at = function _at(xi,j) { + function sqr(x) { return x*x; } + var sol = this; + var xs = sol.x; + var ys = sol.y; + var k1 = sol.f; + var ymid = sol.ymid; + var n = xs.length; + var x0,x1,xh,y0,y1,yh,xi; + var floor = Math.floor,h; + var c = 0.5; + var add = numeric.add, mul = numeric.mul,sub = numeric.sub, p,q,w; + x0 = xs[j]; + x1 = xs[j+1]; + y0 = ys[j]; + y1 = ys[j+1]; + h = x1-x0; + xh = x0+c*h; + yh = ymid[j]; + p = sub(k1[j ],mul(y0,1/(x0-xh)+2/(x0-x1))); + q = sub(k1[j+1],mul(y1,1/(x1-xh)+2/(x1-x0))); + w = [sqr(xi - x1) * (xi - xh) / sqr(x0 - x1) / (x0 - xh), + sqr(xi - x0) * sqr(xi - x1) / sqr(x0 - xh) / sqr(x1 - xh), + sqr(xi - x0) * (xi - xh) / sqr(x1 - x0) / (x1 - xh), + (xi - x0) * sqr(xi - x1) * (xi - xh) / sqr(x0-x1) / (x0 - xh), + (xi - x1) * sqr(xi - x0) * (xi - xh) / sqr(x0-x1) / (x1 - xh)]; + return add(add(add(add(mul(y0,w[0]), + mul(yh,w[1])), + mul(y1,w[2])), + mul( p,w[3])), + mul( q,w[4])); +} +numeric.Dopri.prototype.at = function at(x) { + var i,j,k,floor = Math.floor; + if(typeof x !== "number") { + var n = x.length, ret = Array(n); + for(i=n-1;i!==-1;--i) { + ret[i] = this.at(x[i]); + } + return ret; + } + var x0 = this.x; + i = 0; j = x0.length-1; + while(j-i>1) { + k = floor(0.5*(i+j)); + if(x0[k] <= x) i = k; + else j = k; + } + return this._at(x,i); +} + +numeric.dopri = function dopri(x0,x1,y0,f,tol,maxit,event) { + if(typeof tol === "undefined") { tol = 1e-6; } + if(typeof maxit === "undefined") { maxit = 1000; } + var xs = [x0], ys = [y0], k1 = [f(x0,y0)], k2,k3,k4,k5,k6,k7, ymid = []; + var A2 = 1/5; + var A3 = [3/40,9/40]; + var A4 = [44/45,-56/15,32/9]; + var A5 = [19372/6561,-25360/2187,64448/6561,-212/729]; + var A6 = [9017/3168,-355/33,46732/5247,49/176,-5103/18656]; + var b = [35/384,0,500/1113,125/192,-2187/6784,11/84]; + var bm = [0.5*6025192743/30085553152, + 0, + 0.5*51252292925/65400821598, + 0.5*-2691868925/45128329728, + 0.5*187940372067/1594534317056, + 0.5*-1776094331/19743644256, + 0.5*11237099/235043384]; + var c = [1/5,3/10,4/5,8/9,1,1]; + var e = [-71/57600,0,71/16695,-71/1920,17253/339200,-22/525,1/40]; + var i = 0,er,j; + var h = (x1-x0)/10; + var it = 0; + var add = numeric.add, mul = numeric.mul, y1,erinf; + var max = Math.max, min = Math.min, abs = Math.abs, norminf = numeric.norminf,pow = Math.pow; + var any = numeric.any, lt = numeric.lt, and = numeric.and, sub = numeric.sub; + var e0, e1, ev; + var ret = new numeric.Dopri(xs,ys,k1,ymid,-1,""); + if(typeof event === "function") e0 = event(x0,y0); + while(x0<x1 && it<maxit) { + ++it; + if(x0+h>x1) h = x1-x0; + k2 = f(x0+c[0]*h, add(y0,mul( A2*h,k1[i]))); + k3 = f(x0+c[1]*h, add(add(y0,mul(A3[0]*h,k1[i])),mul(A3[1]*h,k2))); + k4 = f(x0+c[2]*h, add(add(add(y0,mul(A4[0]*h,k1[i])),mul(A4[1]*h,k2)),mul(A4[2]*h,k3))); + k5 = f(x0+c[3]*h, add(add(add(add(y0,mul(A5[0]*h,k1[i])),mul(A5[1]*h,k2)),mul(A5[2]*h,k3)),mul(A5[3]*h,k4))); + k6 = f(x0+c[4]*h,add(add(add(add(add(y0,mul(A6[0]*h,k1[i])),mul(A6[1]*h,k2)),mul(A6[2]*h,k3)),mul(A6[3]*h,k4)),mul(A6[4]*h,k5))); + y1 = add(add(add(add(add(y0,mul(k1[i],h*b[0])),mul(k3,h*b[2])),mul(k4,h*b[3])),mul(k5,h*b[4])),mul(k6,h*b[5])); + k7 = f(x0+h,y1); + er = add(add(add(add(add(mul(k1[i],h*e[0]),mul(k3,h*e[2])),mul(k4,h*e[3])),mul(k5,h*e[4])),mul(k6,h*e[5])),mul(k7,h*e[6])); + if(typeof er === "number") erinf = abs(er); + else erinf = norminf(er); + if(erinf > tol) { // reject + h = 0.2*h*pow(tol/erinf,0.25); + if(x0+h === x0) { + ret.msg = "Step size became too small"; + break; + } + continue; + } + ymid[i] = add(add(add(add(add(add(y0, + mul(k1[i],h*bm[0])), + mul(k3 ,h*bm[2])), + mul(k4 ,h*bm[3])), + mul(k5 ,h*bm[4])), + mul(k6 ,h*bm[5])), + mul(k7 ,h*bm[6])); + ++i; + xs[i] = x0+h; + ys[i] = y1; + k1[i] = k7; + if(typeof event === "function") { + var yi,xl = x0,xr = x0+0.5*h,xi; + e1 = event(xr,ymid[i-1]); + ev = and(lt(e0,0),lt(0,e1)); + if(!any(ev)) { xl = xr; xr = x0+h; e0 = e1; e1 = event(xr,y1); ev = and(lt(e0,0),lt(0,e1)); } + if(any(ev)) { + var xc, yc, en,ei; + var side=0, sl = 1.0, sr = 1.0; + while(1) { + if(typeof e0 === "number") xi = (sr*e1*xl-sl*e0*xr)/(sr*e1-sl*e0); + else { + xi = xr; + for(j=e0.length-1;j!==-1;--j) { + if(e0[j]<0 && e1[j]>0) xi = min(xi,(sr*e1[j]*xl-sl*e0[j]*xr)/(sr*e1[j]-sl*e0[j])); + } + } + if(xi <= xl || xi >= xr) break; + yi = ret._at(xi, i-1); + ei = event(xi,yi); + en = and(lt(e0,0),lt(0,ei)); + if(any(en)) { + xr = xi; + e1 = ei; + ev = en; + sr = 1.0; + if(side === -1) sl *= 0.5; + else sl = 1.0; + side = -1; + } else { + xl = xi; + e0 = ei; + sl = 1.0; + if(side === 1) sr *= 0.5; + else sr = 1.0; + side = 1; + } + } + y1 = ret._at(0.5*(x0+xi),i-1); + ret.f[i] = f(xi,yi); + ret.x[i] = xi; + ret.y[i] = yi; + ret.ymid[i-1] = y1; + ret.events = ev; + ret.iterations = it; + return ret; + } + } + x0 += h; + y0 = y1; + e0 = e1; + h = min(0.8*h*pow(tol/erinf,0.25),4*h); + } + ret.iterations = it; + return ret; +} + +// 11. Ax = b +numeric.LU = function(A, fast) { + fast = fast || false; + + var abs = Math.abs; + var i, j, k, absAjk, Akk, Ak, Pk, Ai; + var max; + var n = A.length, n1 = n-1; + var P = new Array(n); + if(!fast) A = numeric.clone(A); + + for (k = 0; k < n; ++k) { + Pk = k; + Ak = A[k]; + max = abs(Ak[k]); + for (j = k + 1; j < n; ++j) { + absAjk = abs(A[j][k]); + if (max < absAjk) { + max = absAjk; + Pk = j; + } + } + P[k] = Pk; + + if (Pk != k) { + A[k] = A[Pk]; + A[Pk] = Ak; + Ak = A[k]; + } + + Akk = Ak[k]; + + for (i = k + 1; i < n; ++i) { + A[i][k] /= Akk; + } + + for (i = k + 1; i < n; ++i) { + Ai = A[i]; + for (j = k + 1; j < n1; ++j) { + Ai[j] -= Ai[k] * Ak[j]; + ++j; + Ai[j] -= Ai[k] * Ak[j]; + } + if(j===n1) Ai[j] -= Ai[k] * Ak[j]; + } + } + + return { + LU: A, + P: P + }; +} + +numeric.LUsolve = function LUsolve(LUP, b) { + var i, j; + var LU = LUP.LU; + var n = LU.length; + var x = numeric.clone(b); + var P = LUP.P; + var Pi, LUi, LUii, tmp; + + for (i=n-1;i!==-1;--i) x[i] = b[i]; + for (i = 0; i < n; ++i) { + Pi = P[i]; + if (P[i] !== i) { + tmp = x[i]; + x[i] = x[Pi]; + x[Pi] = tmp; + } + + LUi = LU[i]; + for (j = 0; j < i; ++j) { + x[i] -= x[j] * LUi[j]; + } + } + + for (i = n - 1; i >= 0; --i) { + LUi = LU[i]; + for (j = i + 1; j < n; ++j) { + x[i] -= x[j] * LUi[j]; + } + + x[i] /= LUi[i]; + } + + return x; +} + +numeric.solve = function solve(A,b,fast) { return numeric.LUsolve(numeric.LU(A,fast), b); } + +// 12. Linear programming +numeric.echelonize = function echelonize(A) { + var s = numeric.dim(A), m = s[0], n = s[1]; + var I = numeric.identity(m); + var P = Array(m); + var i,j,k,l,Ai,Ii,Z,a; + var abs = Math.abs; + var diveq = numeric.diveq; + A = numeric.clone(A); + for(i=0;i<m;++i) { + k = 0; + Ai = A[i]; + Ii = I[i]; + for(j=1;j<n;++j) if(abs(Ai[k])<abs(Ai[j])) k=j; + P[i] = k; + diveq(Ii,Ai[k]); + diveq(Ai,Ai[k]); + for(j=0;j<m;++j) if(j!==i) { + Z = A[j]; a = Z[k]; + for(l=n-1;l!==-1;--l) Z[l] -= Ai[l]*a; + Z = I[j]; + for(l=m-1;l!==-1;--l) Z[l] -= Ii[l]*a; + } + } + return {I:I, A:A, P:P}; +} + +numeric.__solveLP = function __solveLP(c,A,b,tol,maxit,x,flag) { + var sum = numeric.sum, log = numeric.log, mul = numeric.mul, sub = numeric.sub, dot = numeric.dot, div = numeric.div, add = numeric.add; + var m = c.length, n = b.length,y; + var unbounded = false, cb,i0=0; + var alpha = 1.0; + var f0,df0,AT = numeric.transpose(A), svd = numeric.svd,transpose = numeric.transpose,leq = numeric.leq, sqrt = Math.sqrt, abs = Math.abs; + var muleq = numeric.muleq; + var norm = numeric.norminf, any = numeric.any,min = Math.min; + var all = numeric.all, gt = numeric.gt; + var p = Array(m), A0 = Array(n),e=numeric.rep([n],1), H; + var solve = numeric.solve, z = sub(b,dot(A,x)),count; + var dotcc = dot(c,c); + var g; + for(count=i0;count<maxit;++count) { + var i,j,d; + for(i=n-1;i!==-1;--i) A0[i] = div(A[i],z[i]); + var A1 = transpose(A0); + for(i=m-1;i!==-1;--i) p[i] = (/*x[i]+*/sum(A1[i])); + alpha = 0.25*abs(dotcc/dot(c,p)); + var a1 = 100*sqrt(dotcc/dot(p,p)); + if(!isFinite(alpha) || alpha>a1) alpha = a1; + g = add(c,mul(alpha,p)); + H = dot(A1,A0); + for(i=m-1;i!==-1;--i) H[i][i] += 1; + d = solve(H,div(g,alpha),true); + var t0 = div(z,dot(A,d)); + var t = 1.0; + for(i=n-1;i!==-1;--i) if(t0[i]<0) t = min(t,-0.999*t0[i]); + y = sub(x,mul(d,t)); + z = sub(b,dot(A,y)); + if(!all(gt(z,0))) return { solution: x, message: "", iterations: count }; + x = y; + if(alpha<tol) return { solution: y, message: "", iterations: count }; + if(flag) { + var s = dot(c,g), Ag = dot(A,g); + unbounded = true; + for(i=n-1;i!==-1;--i) if(s*Ag[i]<0) { unbounded = false; break; } + } else { + if(x[m-1]>=0) unbounded = false; + else unbounded = true; + } + if(unbounded) return { solution: y, message: "Unbounded", iterations: count }; + } + return { solution: x, message: "maximum iteration count exceeded", iterations:count }; +} + +numeric._solveLP = function _solveLP(c,A,b,tol,maxit) { + var m = c.length, n = b.length,y; + var sum = numeric.sum, log = numeric.log, mul = numeric.mul, sub = numeric.sub, dot = numeric.dot, div = numeric.div, add = numeric.add; + var c0 = numeric.rep([m],0).concat([1]); + var J = numeric.rep([n,1],-1); + var A0 = numeric.blockMatrix([[A , J ]]); + var b0 = b; + var y = numeric.rep([m],0).concat(Math.max(0,numeric.sup(numeric.neg(b)))+1); + var x0 = numeric.__solveLP(c0,A0,b0,tol,maxit,y,false); + var x = numeric.clone(x0.solution); + x.length = m; + var foo = numeric.inf(sub(b,dot(A,x))); + if(foo<0) { return { solution: NaN, message: "Infeasible", iterations: x0.iterations }; } + var ret = numeric.__solveLP(c, A, b, tol, maxit-x0.iterations, x, true); + ret.iterations += x0.iterations; + return ret; +}; + +numeric.solveLP = function solveLP(c,A,b,Aeq,beq,tol,maxit) { + if(typeof maxit === "undefined") maxit = 1000; + if(typeof tol === "undefined") tol = numeric.epsilon; + if(typeof Aeq === "undefined") return numeric._solveLP(c,A,b,tol,maxit); + var m = Aeq.length, n = Aeq[0].length, o = A.length; + var B = numeric.echelonize(Aeq); + var flags = numeric.rep([n],0); + var P = B.P; + var Q = []; + var i; + for(i=P.length-1;i!==-1;--i) flags[P[i]] = 1; + for(i=n-1;i!==-1;--i) if(flags[i]===0) Q.push(i); + var g = numeric.getRange; + var I = numeric.linspace(0,m-1), J = numeric.linspace(0,o-1); + var Aeq2 = g(Aeq,I,Q), A1 = g(A,J,P), A2 = g(A,J,Q), dot = numeric.dot, sub = numeric.sub; + var A3 = dot(A1,B.I); + var A4 = sub(A2,dot(A3,Aeq2)), b4 = sub(b,dot(A3,beq)); + var c1 = Array(P.length), c2 = Array(Q.length); + for(i=P.length-1;i!==-1;--i) c1[i] = c[P[i]]; + for(i=Q.length-1;i!==-1;--i) c2[i] = c[Q[i]]; + var c4 = sub(c2,dot(c1,dot(B.I,Aeq2))); + var S = numeric._solveLP(c4,A4,b4,tol,maxit); + var x2 = S.solution; + if(x2!==x2) return S; + var x1 = dot(B.I,sub(beq,dot(Aeq2,x2))); + var x = Array(c.length); + for(i=P.length-1;i!==-1;--i) x[P[i]] = x1[i]; + for(i=Q.length-1;i!==-1;--i) x[Q[i]] = x2[i]; + return { solution: x, message:S.message, iterations: S.iterations }; +} + +numeric.MPStoLP = function MPStoLP(MPS) { + if(MPS instanceof String) { MPS.split('\n'); } + var state = 0; + var states = ['Initial state','NAME','ROWS','COLUMNS','RHS','BOUNDS','ENDATA']; + var n = MPS.length; + var i,j,z,N=0,rows = {}, sign = [], rl = 0, vars = {}, nv = 0; + var name; + var c = [], A = [], b = []; + function err(e) { throw new Error('MPStoLP: '+e+'\nLine '+i+': '+MPS[i]+'\nCurrent state: '+states[state]+'\n'); } + for(i=0;i<n;++i) { + z = MPS[i]; + var w0 = z.match(/\S*/g); + var w = []; + for(j=0;j<w0.length;++j) if(w0[j]!=="") w.push(w0[j]); + if(w.length === 0) continue; + for(j=0;j<states.length;++j) if(z.substr(0,states[j].length) === states[j]) break; + if(j<states.length) { + state = j; + if(j===1) { name = w[1]; } + if(j===6) return { name:name, c:c, A:numeric.transpose(A), b:b, rows:rows, vars:vars }; + continue; + } + switch(state) { + case 0: case 1: err('Unexpected line'); + case 2: + switch(w[0]) { + case 'N': if(N===0) N = w[1]; else err('Two or more N rows'); break; + case 'L': rows[w[1]] = rl; sign[rl] = 1; b[rl] = 0; ++rl; break; + case 'G': rows[w[1]] = rl; sign[rl] = -1;b[rl] = 0; ++rl; break; + case 'E': rows[w[1]] = rl; sign[rl] = 0;b[rl] = 0; ++rl; break; + default: err('Parse error '+numeric.prettyPrint(w)); + } + break; + case 3: + if(!vars.hasOwnProperty(w[0])) { vars[w[0]] = nv; c[nv] = 0; A[nv] = numeric.rep([rl],0); ++nv; } + var p = vars[w[0]]; + for(j=1;j<w.length;j+=2) { + if(w[j] === N) { c[p] = parseFloat(w[j+1]); continue; } + var q = rows[w[j]]; + A[p][q] = (sign[q]<0?-1:1)*parseFloat(w[j+1]); + } + break; + case 4: + for(j=1;j<w.length;j+=2) b[rows[w[j]]] = (sign[rows[w[j]]]<0?-1:1)*parseFloat(w[j+1]); + break; + case 5: /*FIXME*/ break; + case 6: err('Internal error'); + } + } + err('Reached end of file without ENDATA'); +} +// seedrandom.js version 2.0. +// Author: David Bau 4/2/2011 +// +// Defines a method Math.seedrandom() that, when called, substitutes +// an explicitly seeded RC4-based algorithm for Math.random(). Also +// supports automatic seeding from local or network sources of entropy. +// +// Usage: +// +// <script src=http://davidbau.com/encode/seedrandom-min.js></script> +// +// Math.seedrandom('yipee'); Sets Math.random to a function that is +// initialized using the given explicit seed. +// +// Math.seedrandom(); Sets Math.random to a function that is +// seeded using the current time, dom state, +// and other accumulated local entropy. +// The generated seed string is returned. +// +// Math.seedrandom('yowza', true); +// Seeds using the given explicit seed mixed +// together with accumulated entropy. +// +// <script src="http://bit.ly/srandom-512"></script> +// Seeds using physical random bits downloaded +// from random.org. +// +// <script src="https://jsonlib.appspot.com/urandom?callback=Math.seedrandom"> +// </script> Seeds using urandom bits from call.jsonlib.com, +// which is faster than random.org. +// +// Examples: +// +// Math.seedrandom("hello"); // Use "hello" as the seed. +// document.write(Math.random()); // Always 0.5463663768140734 +// document.write(Math.random()); // Always 0.43973793770592234 +// var rng1 = Math.random; // Remember the current prng. +// +// var autoseed = Math.seedrandom(); // New prng with an automatic seed. +// document.write(Math.random()); // Pretty much unpredictable. +// +// Math.random = rng1; // Continue "hello" prng sequence. +// document.write(Math.random()); // Always 0.554769432473455 +// +// Math.seedrandom(autoseed); // Restart at the previous seed. +// document.write(Math.random()); // Repeat the 'unpredictable' value. +// +// Notes: +// +// Each time seedrandom('arg') is called, entropy from the passed seed +// is accumulated in a pool to help generate future seeds for the +// zero-argument form of Math.seedrandom, so entropy can be injected over +// time by calling seedrandom with explicit data repeatedly. +// +// On speed - This javascript implementation of Math.random() is about +// 3-10x slower than the built-in Math.random() because it is not native +// code, but this is typically fast enough anyway. Seeding is more expensive, +// especially if you use auto-seeding. Some details (timings on Chrome 4): +// +// Our Math.random() - avg less than 0.002 milliseconds per call +// seedrandom('explicit') - avg less than 0.5 milliseconds per call +// seedrandom('explicit', true) - avg less than 2 milliseconds per call +// seedrandom() - avg about 38 milliseconds per call +// +// LICENSE (BSD): +// +// Copyright 2010 David Bau, all rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of this module nor the names of its contributors may +// be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// 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 +// OWNER 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. +// +/** + * All code is in an anonymous closure to keep the global namespace clean. + * + * @param {number=} overflow + * @param {number=} startdenom + */ + +// Patched by Seb so that seedrandom.js does not pollute the Math object. +// My tests suggest that doing Math.trouble = 1 makes Math lookups about 5% +// slower. +numeric.seedrandom = { pow:Math.pow, random:Math.random }; + +(function (pool, math, width, chunks, significance, overflow, startdenom) { + + +// +// seedrandom() +// This is the seedrandom function described above. +// +math['seedrandom'] = function seedrandom(seed, use_entropy) { + var key = []; + var arc4; + + // Flatten the seed string or build one from local entropy if needed. + seed = mixkey(flatten( + use_entropy ? [seed, pool] : + arguments.length ? seed : + [new Date().getTime(), pool, window], 3), key); + + // Use the seed to initialize an ARC4 generator. + arc4 = new ARC4(key); + + // Mix the randomness into accumulated entropy. + mixkey(arc4.S, pool); + + // Override Math.random + + // This function returns a random double in [0, 1) that contains + // randomness in every bit of the mantissa of the IEEE 754 value. + + math['random'] = function random() { // Closure to return a random double: + var n = arc4.g(chunks); // Start with a numerator n < 2 ^ 48 + var d = startdenom; // and denominator d = 2 ^ 48. + var x = 0; // and no 'extra last byte'. + while (n < significance) { // Fill up all significant digits by + n = (n + x) * width; // shifting numerator and + d *= width; // denominator and generating a + x = arc4.g(1); // new least-significant-byte. + } + while (n >= overflow) { // To avoid rounding up, before adding + n /= 2; // last byte, shift everything + d /= 2; // right using integer math until + x >>>= 1; // we have exactly the desired bits. + } + return (n + x) / d; // Form the number within [0, 1). + }; + + // Return the seed that was used + return seed; +}; + +// +// ARC4 +// +// An ARC4 implementation. The constructor takes a key in the form of +// an array of at most (width) integers that should be 0 <= x < (width). +// +// The g(count) method returns a pseudorandom integer that concatenates +// the next (count) outputs from ARC4. Its return value is a number x +// that is in the range 0 <= x < (width ^ count). +// +/** @constructor */ +function ARC4(key) { + var t, u, me = this, keylen = key.length; + var i = 0, j = me.i = me.j = me.m = 0; + me.S = []; + me.c = []; + + // The empty key [] is treated as [0]. + if (!keylen) { key = [keylen++]; } + + // Set up S using the standard key scheduling algorithm. + while (i < width) { me.S[i] = i++; } + for (i = 0; i < width; i++) { + t = me.S[i]; + j = lowbits(j + t + key[i % keylen]); + u = me.S[j]; + me.S[i] = u; + me.S[j] = t; + } + + // The "g" method returns the next (count) outputs as one number. + me.g = function getnext(count) { + var s = me.S; + var i = lowbits(me.i + 1); var t = s[i]; + var j = lowbits(me.j + t); var u = s[j]; + s[i] = u; + s[j] = t; + var r = s[lowbits(t + u)]; + while (--count) { + i = lowbits(i + 1); t = s[i]; + j = lowbits(j + t); u = s[j]; + s[i] = u; + s[j] = t; + r = r * width + s[lowbits(t + u)]; + } + me.i = i; + me.j = j; + return r; + }; + // For robust unpredictability discard an initial batch of values. + // See http://www.rsa.com/rsalabs/node.asp?id=2009 + me.g(width); +} + +// +// flatten() +// Converts an object tree to nested arrays of strings. +// +/** @param {Object=} result + * @param {string=} prop + * @param {string=} typ */ +function flatten(obj, depth, result, prop, typ) { + result = []; + typ = typeof(obj); + if (depth && typ == 'object') { + for (prop in obj) { + if (prop.indexOf('S') < 5) { // Avoid FF3 bug (local/sessionStorage) + try { result.push(flatten(obj[prop], depth - 1)); } catch (e) {} + } + } + } + return (result.length ? result : obj + (typ != 'string' ? '\0' : '')); +} + +// +// mixkey() +// Mixes a string seed into a key that is an array of integers, and +// returns a shortened string seed that is equivalent to the result key. +// +/** @param {number=} smear + * @param {number=} j */ +function mixkey(seed, key, smear, j) { + seed += ''; // Ensure the seed is a string + smear = 0; + for (j = 0; j < seed.length; j++) { + key[lowbits(j)] = + lowbits((smear ^= key[lowbits(j)] * 19) + seed.charCodeAt(j)); + } + seed = ''; + for (j in key) { seed += String.fromCharCode(key[j]); } + return seed; +} + +// +// lowbits() +// A quick "n mod width" for width a power of 2. +// +function lowbits(n) { return n & (width - 1); } + +// +// The following constants are related to IEEE 754 limits. +// +startdenom = math.pow(width, chunks); +significance = math.pow(2, significance); +overflow = significance * 2; + +// +// When seedrandom.js is loaded, we immediately mix a few bits +// from the built-in RNG into the entropy pool. Because we do +// not want to intefere with determinstic PRNG state later, +// seedrandom will not call math.random on its own again after +// initialization. +// +mixkey(math.random(), pool); + +// End anonymous scope, and pass initial values. +}( + [], // pool: entropy pool starts empty + numeric.seedrandom, // math: package containing random, pow, and seedrandom + 256, // width: each RC4 output is 0 <= x < 256 + 6, // chunks: at least six RC4 outputs for each double + 52 // significance: there are 52 significant digits in a double + )); +/* This file is a slightly modified version of quadprog.js from Alberto Santini. + * It has been slightly modified by Sébastien Loisel to make sure that it handles + * 0-based Arrays instead of 1-based Arrays. + * License is in resources/LICENSE.quadprog */ +(function(exports) { + +function base0to1(A) { + if(typeof A !== "object") { return A; } + var ret = [], i,n=A.length; + for(i=0;i<n;i++) ret[i+1] = base0to1(A[i]); + return ret; +} +function base1to0(A) { + if(typeof A !== "object") { return A; } + var ret = [], i,n=A.length; + for(i=1;i<n;i++) ret[i-1] = base1to0(A[i]); + return ret; +} + +function dpori(a, lda, n) { + var i, j, k, kp1, t; + + for (k = 1; k <= n; k = k + 1) { + a[k][k] = 1 / a[k][k]; + t = -a[k][k]; + //~ dscal(k - 1, t, a[1][k], 1); + for (i = 1; i < k; i = i + 1) { + a[i][k] = t * a[i][k]; + } + + kp1 = k + 1; + if (n < kp1) { + break; + } + for (j = kp1; j <= n; j = j + 1) { + t = a[k][j]; + a[k][j] = 0; + //~ daxpy(k, t, a[1][k], 1, a[1][j], 1); + for (i = 1; i <= k; i = i + 1) { + a[i][j] = a[i][j] + (t * a[i][k]); + } + } + } + +} + +function dposl(a, lda, n, b) { + var i, k, kb, t; + + for (k = 1; k <= n; k = k + 1) { + //~ t = ddot(k - 1, a[1][k], 1, b[1], 1); + t = 0; + for (i = 1; i < k; i = i + 1) { + t = t + (a[i][k] * b[i]); + } + + b[k] = (b[k] - t) / a[k][k]; + } + + for (kb = 1; kb <= n; kb = kb + 1) { + k = n + 1 - kb; + b[k] = b[k] / a[k][k]; + t = -b[k]; + //~ daxpy(k - 1, t, a[1][k], 1, b[1], 1); + for (i = 1; i < k; i = i + 1) { + b[i] = b[i] + (t * a[i][k]); + } + } +} + +function dpofa(a, lda, n, info) { + var i, j, jm1, k, t, s; + + for (j = 1; j <= n; j = j + 1) { + info[1] = j; + s = 0; + jm1 = j - 1; + if (jm1 < 1) { + s = a[j][j] - s; + if (s <= 0) { + break; + } + a[j][j] = Math.sqrt(s); + } else { + for (k = 1; k <= jm1; k = k + 1) { + //~ t = a[k][j] - ddot(k - 1, a[1][k], 1, a[1][j], 1); + t = a[k][j]; + for (i = 1; i < k; i = i + 1) { + t = t - (a[i][j] * a[i][k]); + } + t = t / a[k][k]; + a[k][j] = t; + s = s + t * t; + } + s = a[j][j] - s; + if (s <= 0) { + break; + } + a[j][j] = Math.sqrt(s); + } + info[1] = 0; + } +} + +function qpgen2(dmat, dvec, fddmat, n, sol, crval, amat, + bvec, fdamat, q, meq, iact, nact, iter, work, ierr) { + + var i, j, l, l1, info, it1, iwzv, iwrv, iwrm, iwsv, iwuv, nvl, r, iwnbv, + temp, sum, t1, tt, gc, gs, nu, + t1inf, t2min, + vsmall, tmpa, tmpb, + go; + + r = Math.min(n, q); + l = 2 * n + (r * (r + 5)) / 2 + 2 * q + 1; + + vsmall = 1.0e-60; + do { + vsmall = vsmall + vsmall; + tmpa = 1 + 0.1 * vsmall; + tmpb = 1 + 0.2 * vsmall; + } while (tmpa <= 1 || tmpb <= 1); + + for (i = 1; i <= n; i = i + 1) { + work[i] = dvec[i]; + } + for (i = n + 1; i <= l; i = i + 1) { + work[i] = 0; + } + for (i = 1; i <= q; i = i + 1) { + iact[i] = 0; + } + + info = []; + + if (ierr[1] === 0) { + dpofa(dmat, fddmat, n, info); + if (info[1] !== 0) { + ierr[1] = 2; + return; + } + dposl(dmat, fddmat, n, dvec); + dpori(dmat, fddmat, n); + } else { + for (j = 1; j <= n; j = j + 1) { + sol[j] = 0; + for (i = 1; i <= j; i = i + 1) { + sol[j] = sol[j] + dmat[i][j] * dvec[i]; + } + } + for (j = 1; j <= n; j = j + 1) { + dvec[j] = 0; + for (i = j; i <= n; i = i + 1) { + dvec[j] = dvec[j] + dmat[j][i] * sol[i]; + } + } + } + + crval[1] = 0; + for (j = 1; j <= n; j = j + 1) { + sol[j] = dvec[j]; + crval[1] = crval[1] + work[j] * sol[j]; + work[j] = 0; + for (i = j + 1; i <= n; i = i + 1) { + dmat[i][j] = 0; + } + } + crval[1] = -crval[1] / 2; + ierr[1] = 0; + + iwzv = n; + iwrv = iwzv + n; + iwuv = iwrv + r; + iwrm = iwuv + r + 1; + iwsv = iwrm + (r * (r + 1)) / 2; + iwnbv = iwsv + q; + + for (i = 1; i <= q; i = i + 1) { + sum = 0; + for (j = 1; j <= n; j = j + 1) { + sum = sum + amat[j][i] * amat[j][i]; + } + work[iwnbv + i] = Math.sqrt(sum); + } + nact = 0; + iter[1] = 0; + iter[2] = 0; + + function fn_goto_50() { + iter[1] = iter[1] + 1; + + l = iwsv; + for (i = 1; i <= q; i = i + 1) { + l = l + 1; + sum = -bvec[i]; + for (j = 1; j <= n; j = j + 1) { + sum = sum + amat[j][i] * sol[j]; + } + if (Math.abs(sum) < vsmall) { + sum = 0; + } + if (i > meq) { + work[l] = sum; + } else { + work[l] = -Math.abs(sum); + if (sum > 0) { + for (j = 1; j <= n; j = j + 1) { + amat[j][i] = -amat[j][i]; + } + bvec[i] = -bvec[i]; + } + } + } + + for (i = 1; i <= nact; i = i + 1) { + work[iwsv + iact[i]] = 0; + } + + nvl = 0; + temp = 0; + for (i = 1; i <= q; i = i + 1) { + if (work[iwsv + i] < temp * work[iwnbv + i]) { + nvl = i; + temp = work[iwsv + i] / work[iwnbv + i]; + } + } + if (nvl === 0) { + return 999; + } + + return 0; + } + + function fn_goto_55() { + for (i = 1; i <= n; i = i + 1) { + sum = 0; + for (j = 1; j <= n; j = j + 1) { + sum = sum + dmat[j][i] * amat[j][nvl]; + } + work[i] = sum; + } + + l1 = iwzv; + for (i = 1; i <= n; i = i + 1) { + work[l1 + i] = 0; + } + for (j = nact + 1; j <= n; j = j + 1) { + for (i = 1; i <= n; i = i + 1) { + work[l1 + i] = work[l1 + i] + dmat[i][j] * work[j]; + } + } + + t1inf = true; + for (i = nact; i >= 1; i = i - 1) { + sum = work[i]; + l = iwrm + (i * (i + 3)) / 2; + l1 = l - i; + for (j = i + 1; j <= nact; j = j + 1) { + sum = sum - work[l] * work[iwrv + j]; + l = l + j; + } + sum = sum / work[l1]; + work[iwrv + i] = sum; + if (iact[i] < meq) { + // continue; + break; + } + if (sum < 0) { + // continue; + break; + } + t1inf = false; + it1 = i; + } + + if (!t1inf) { + t1 = work[iwuv + it1] / work[iwrv + it1]; + for (i = 1; i <= nact; i = i + 1) { + if (iact[i] < meq) { + // continue; + break; + } + if (work[iwrv + i] < 0) { + // continue; + break; + } + temp = work[iwuv + i] / work[iwrv + i]; + if (temp < t1) { + t1 = temp; + it1 = i; + } + } + } + + sum = 0; + for (i = iwzv + 1; i <= iwzv + n; i = i + 1) { + sum = sum + work[i] * work[i]; + } + if (Math.abs(sum) <= vsmall) { + if (t1inf) { + ierr[1] = 1; + // GOTO 999 + return 999; + } else { + for (i = 1; i <= nact; i = i + 1) { + work[iwuv + i] = work[iwuv + i] - t1 * work[iwrv + i]; + } + work[iwuv + nact + 1] = work[iwuv + nact + 1] + t1; + // GOTO 700 + return 700; + } + } else { + sum = 0; + for (i = 1; i <= n; i = i + 1) { + sum = sum + work[iwzv + i] * amat[i][nvl]; + } + tt = -work[iwsv + nvl] / sum; + t2min = true; + if (!t1inf) { + if (t1 < tt) { + tt = t1; + t2min = false; + } + } + + for (i = 1; i <= n; i = i + 1) { + sol[i] = sol[i] + tt * work[iwzv + i]; + if (Math.abs(sol[i]) < vsmall) { + sol[i] = 0; + } + } + + crval[1] = crval[1] + tt * sum * (tt / 2 + work[iwuv + nact + 1]); + for (i = 1; i <= nact; i = i + 1) { + work[iwuv + i] = work[iwuv + i] - tt * work[iwrv + i]; + } + work[iwuv + nact + 1] = work[iwuv + nact + 1] + tt; + + if (t2min) { + nact = nact + 1; + iact[nact] = nvl; + + l = iwrm + ((nact - 1) * nact) / 2 + 1; + for (i = 1; i <= nact - 1; i = i + 1) { + work[l] = work[i]; + l = l + 1; + } + + if (nact === n) { + work[l] = work[n]; + } else { + for (i = n; i >= nact + 1; i = i - 1) { + if (work[i] === 0) { + // continue; + break; + } + gc = Math.max(Math.abs(work[i - 1]), Math.abs(work[i])); + gs = Math.min(Math.abs(work[i - 1]), Math.abs(work[i])); + if (work[i - 1] >= 0) { + temp = Math.abs(gc * Math.sqrt(1 + gs * gs / (gc * gc))); + } else { + temp = -Math.abs(gc * Math.sqrt(1 + gs * gs / (gc * gc))); + } + gc = work[i - 1] / temp; + gs = work[i] / temp; + + if (gc === 1) { + // continue; + break; + } + if (gc === 0) { + work[i - 1] = gs * temp; + for (j = 1; j <= n; j = j + 1) { + temp = dmat[j][i - 1]; + dmat[j][i - 1] = dmat[j][i]; + dmat[j][i] = temp; + } + } else { + work[i - 1] = temp; + nu = gs / (1 + gc); + for (j = 1; j <= n; j = j + 1) { + temp = gc * dmat[j][i - 1] + gs * dmat[j][i]; + dmat[j][i] = nu * (dmat[j][i - 1] + temp) - dmat[j][i]; + dmat[j][i - 1] = temp; + + } + } + } + work[l] = work[nact]; + } + } else { + sum = -bvec[nvl]; + for (j = 1; j <= n; j = j + 1) { + sum = sum + sol[j] * amat[j][nvl]; + } + if (nvl > meq) { + work[iwsv + nvl] = sum; + } else { + work[iwsv + nvl] = -Math.abs(sum); + if (sum > 0) { + for (j = 1; j <= n; j = j + 1) { + amat[j][nvl] = -amat[j][nvl]; + } + bvec[nvl] = -bvec[nvl]; + } + } + // GOTO 700 + return 700; + } + } + + return 0; + } + + function fn_goto_797() { + l = iwrm + (it1 * (it1 + 1)) / 2 + 1; + l1 = l + it1; + if (work[l1] === 0) { + // GOTO 798 + return 798; + } + gc = Math.max(Math.abs(work[l1 - 1]), Math.abs(work[l1])); + gs = Math.min(Math.abs(work[l1 - 1]), Math.abs(work[l1])); + if (work[l1 - 1] >= 0) { + temp = Math.abs(gc * Math.sqrt(1 + gs * gs / (gc * gc))); + } else { + temp = -Math.abs(gc * Math.sqrt(1 + gs * gs / (gc * gc))); + } + gc = work[l1 - 1] / temp; + gs = work[l1] / temp; + + if (gc === 1) { + // GOTO 798 + return 798; + } + if (gc === 0) { + for (i = it1 + 1; i <= nact; i = i + 1) { + temp = work[l1 - 1]; + work[l1 - 1] = work[l1]; + work[l1] = temp; + l1 = l1 + i; + } + for (i = 1; i <= n; i = i + 1) { + temp = dmat[i][it1]; + dmat[i][it1] = dmat[i][it1 + 1]; + dmat[i][it1 + 1] = temp; + } + } else { + nu = gs / (1 + gc); + for (i = it1 + 1; i <= nact; i = i + 1) { + temp = gc * work[l1 - 1] + gs * work[l1]; + work[l1] = nu * (work[l1 - 1] + temp) - work[l1]; + work[l1 - 1] = temp; + l1 = l1 + i; + } + for (i = 1; i <= n; i = i + 1) { + temp = gc * dmat[i][it1] + gs * dmat[i][it1 + 1]; + dmat[i][it1 + 1] = nu * (dmat[i][it1] + temp) - dmat[i][it1 + 1]; + dmat[i][it1] = temp; + } + } + + return 0; + } + + function fn_goto_798() { + l1 = l - it1; + for (i = 1; i <= it1; i = i + 1) { + work[l1] = work[l]; + l = l + 1; + l1 = l1 + 1; + } + + work[iwuv + it1] = work[iwuv + it1 + 1]; + iact[it1] = iact[it1 + 1]; + it1 = it1 + 1; + if (it1 < nact) { + // GOTO 797 + return 797; + } + + return 0; + } + + function fn_goto_799() { + work[iwuv + nact] = work[iwuv + nact + 1]; + work[iwuv + nact + 1] = 0; + iact[nact] = 0; + nact = nact - 1; + iter[2] = iter[2] + 1; + + return 0; + } + + go = 0; + while (true) { + go = fn_goto_50(); + if (go === 999) { + return; + } + while (true) { + go = fn_goto_55(); + if (go === 0) { + break; + } + if (go === 999) { + return; + } + if (go === 700) { + if (it1 === nact) { + fn_goto_799(); + } else { + while (true) { + fn_goto_797(); + go = fn_goto_798(); + if (go !== 797) { + break; + } + } + fn_goto_799(); + } + } + } + } + +} + +function solveQP(Dmat, dvec, Amat, bvec, meq, factorized) { + Dmat = base0to1(Dmat); + dvec = base0to1(dvec); + Amat = base0to1(Amat); + var i, n, q, + nact, r, + crval = [], iact = [], sol = [], work = [], iter = [], + message; + + meq = meq || 0; + factorized = factorized ? base0to1(factorized) : [undefined, 0]; + bvec = bvec ? base0to1(bvec) : []; + + // In Fortran the array index starts from 1 + n = Dmat.length - 1; + q = Amat[1].length - 1; + + if (!bvec) { + for (i = 1; i <= q; i = i + 1) { + bvec[i] = 0; + } + } + for (i = 1; i <= q; i = i + 1) { + iact[i] = 0; + } + nact = 0; + r = Math.min(n, q); + for (i = 1; i <= n; i = i + 1) { + sol[i] = 0; + } + crval[1] = 0; + for (i = 1; i <= (2 * n + (r * (r + 5)) / 2 + 2 * q + 1); i = i + 1) { + work[i] = 0; + } + for (i = 1; i <= 2; i = i + 1) { + iter[i] = 0; + } + + qpgen2(Dmat, dvec, n, n, sol, crval, Amat, + bvec, n, q, meq, iact, nact, iter, work, factorized); + + message = ""; + if (factorized[1] === 1) { + message = "constraints are inconsistent, no solution!"; + } + if (factorized[1] === 2) { + message = "matrix D in quadratic function is not positive definite!"; + } + + return { + solution: base1to0(sol), + value: base1to0(crval), + unconstrained_solution: base1to0(dvec), + iterations: base1to0(iter), + iact: base1to0(iact), + message: message + }; +} +exports.solveQP = solveQP; +}(numeric)); +/* +Shanti Rao sent me this routine by private email. I had to modify it +slightly to work on Arrays instead of using a Matrix object. +It is apparently translated from http://stitchpanorama.sourceforge.net/Python/svd.py +*/ + +numeric.svd= function svd(A) { + var temp; +//Compute the thin SVD from G. H. Golub and C. Reinsch, Numer. Math. 14, 403-420 (1970) + var prec= numeric.epsilon; //Math.pow(2,-52) // assumes double prec + var tolerance= 1.e-64/prec; + var itmax= 50; + var c=0; + var i=0; + var j=0; + var k=0; + var l=0; + + var u= numeric.clone(A); + var m= u.length; + + var n= u[0].length; + + if (m < n) throw "Need more rows than columns" + + var e = new Array(n); + var q = new Array(n); + for (i=0; i<n; i++) e[i] = q[i] = 0.0; + var v = numeric.rep([n,n],0); +// v.zero(); + + function pythag(a,b) + { + a = Math.abs(a) + b = Math.abs(b) + if (a > b) + return a*Math.sqrt(1.0+(b*b/a/a)) + else if (b == 0.0) + return a + return b*Math.sqrt(1.0+(a*a/b/b)) + } + + //Householder's reduction to bidiagonal form + + var f= 0.0; + var g= 0.0; + var h= 0.0; + var x= 0.0; + var y= 0.0; + var z= 0.0; + var s= 0.0; + + for (i=0; i < n; i++) + { + e[i]= g; + s= 0.0; + l= i+1; + for (j=i; j < m; j++) + s += (u[j][i]*u[j][i]); + if (s <= tolerance) + g= 0.0; + else + { + f= u[i][i]; + g= Math.sqrt(s); + if (f >= 0.0) g= -g; + h= f*g-s + u[i][i]=f-g; + for (j=l; j < n; j++) + { + s= 0.0 + for (k=i; k < m; k++) + s += u[k][i]*u[k][j] + f= s/h + for (k=i; k < m; k++) + u[k][j]+=f*u[k][i] + } + } + q[i]= g + s= 0.0 + for (j=l; j < n; j++) + s= s + u[i][j]*u[i][j] + if (s <= tolerance) + g= 0.0 + else + { + f= u[i][i+1] + g= Math.sqrt(s) + if (f >= 0.0) g= -g + h= f*g - s + u[i][i+1] = f-g; + for (j=l; j < n; j++) e[j]= u[i][j]/h + for (j=l; j < m; j++) + { + s=0.0 + for (k=l; k < n; k++) + s += (u[j][k]*u[i][k]) + for (k=l; k < n; k++) + u[j][k]+=s*e[k] + } + } + y= Math.abs(q[i])+Math.abs(e[i]) + if (y>x) + x=y + } + + // accumulation of right hand gtransformations + for (i=n-1; i != -1; i+= -1) + { + if (g != 0.0) + { + h= g*u[i][i+1] + for (j=l; j < n; j++) + v[j][i]=u[i][j]/h + for (j=l; j < n; j++) + { + s=0.0 + for (k=l; k < n; k++) + s += u[i][k]*v[k][j] + for (k=l; k < n; k++) + v[k][j]+=(s*v[k][i]) + } + } + for (j=l; j < n; j++) + { + v[i][j] = 0; + v[j][i] = 0; + } + v[i][i] = 1; + g= e[i] + l= i + } + + // accumulation of left hand transformations + for (i=n-1; i != -1; i+= -1) + { + l= i+1 + g= q[i] + for (j=l; j < n; j++) + u[i][j] = 0; + if (g != 0.0) + { + h= u[i][i]*g + for (j=l; j < n; j++) + { + s=0.0 + for (k=l; k < m; k++) s += u[k][i]*u[k][j]; + f= s/h + for (k=i; k < m; k++) u[k][j]+=f*u[k][i]; + } + for (j=i; j < m; j++) u[j][i] = u[j][i]/g; + } + else + for (j=i; j < m; j++) u[j][i] = 0; + u[i][i] += 1; + } + + // diagonalization of the bidiagonal form + prec= prec*x + for (k=n-1; k != -1; k+= -1) + { + for (var iteration=0; iteration < itmax; iteration++) + { // test f splitting + var test_convergence = false + for (l=k; l != -1; l+= -1) + { + if (Math.abs(e[l]) <= prec) + { test_convergence= true + break + } + if (Math.abs(q[l-1]) <= prec) + break + } + if (!test_convergence) + { // cancellation of e[l] if l>0 + c= 0.0 + s= 1.0 + var l1= l-1 + for (i =l; i<k+1; i++) + { + f= s*e[i] + e[i]= c*e[i] + if (Math.abs(f) <= prec) + break + g= q[i] + h= pythag(f,g) + q[i]= h + c= g/h + s= -f/h + for (j=0; j < m; j++) + { + y= u[j][l1] + z= u[j][i] + u[j][l1] = y*c+(z*s) + u[j][i] = -y*s+(z*c) + } + } + } + // test f convergence + z= q[k] + if (l== k) + { //convergence + if (z<0.0) + { //q[k] is made non-negative + q[k]= -z + for (j=0; j < n; j++) + v[j][k] = -v[j][k] + } + break //break out of iteration loop and move on to next k value + } + if (iteration >= itmax-1) + throw 'Error: no convergence.' + // shift from bottom 2x2 minor + x= q[l] + y= q[k-1] + g= e[k-1] + h= e[k] + f= ((y-z)*(y+z)+(g-h)*(g+h))/(2.0*h*y) + g= pythag(f,1.0) + if (f < 0.0) + f= ((x-z)*(x+z)+h*(y/(f-g)-h))/x + else + f= ((x-z)*(x+z)+h*(y/(f+g)-h))/x + // next QR transformation + c= 1.0 + s= 1.0 + for (i=l+1; i< k+1; i++) + { + g= e[i] + y= q[i] + h= s*g + g= c*g + z= pythag(f,h) + e[i-1]= z + c= f/z + s= h/z + f= x*c+g*s + g= -x*s+g*c + h= y*s + y= y*c + for (j=0; j < n; j++) + { + x= v[j][i-1] + z= v[j][i] + v[j][i-1] = x*c+z*s + v[j][i] = -x*s+z*c + } + z= pythag(f,h) + q[i-1]= z + c= f/z + s= h/z + f= c*g+s*y + x= -s*g+c*y + for (j=0; j < m; j++) + { + y= u[j][i-1] + z= u[j][i] + u[j][i-1] = y*c+z*s + u[j][i] = -y*s+z*c + } + } + e[l]= 0.0 + e[k]= f + q[k]= x + } + } + + //vt= transpose(v) + //return (u,q,vt) + for (i=0;i<q.length; i++) + if (q[i] < prec) q[i] = 0 + + //sort eigenvalues + for (i=0; i< n; i++) + { + //writeln(q) + for (j=i-1; j >= 0; j--) + { + if (q[j] < q[i]) + { + // writeln(i,'-',j) + c = q[j] + q[j] = q[i] + q[i] = c + for(k=0;k<u.length;k++) { temp = u[k][i]; u[k][i] = u[k][j]; u[k][j] = temp; } + for(k=0;k<v.length;k++) { temp = v[k][i]; v[k][i] = v[k][j]; v[k][j] = temp; } +// u.swapCols(i,j) +// v.swapCols(i,j) + i = j + } + } + } + + return {U:u,S:q,V:v} +}; + diff --git a/dependencies/main_files/persistentWorkers.js b/dependencies/main_files/persistentWorkers.js new file mode 100644 index 00000000..23349ec1 --- /dev/null +++ b/dependencies/main_files/persistentWorkers.js @@ -0,0 +1,108 @@ +//global workers so they do not have to be reinstantiated + +function persistentWorkers(numWorkers){ + + //check compatibility + if (!(typeof window.Worker === "function")){ + console.log("workers not supported"); + return nil; + } + + //local variables + var allWorkers = []; + var mapQueue = []; + + //create array of workers + var URL = window.URL || window.webkitURL; + var workerURL = makeBlobURL(URL, myWorker); + for (var i=0;i<numWorkers;i++){ + var worker = new Worker(workerURL); + worker.onmessage = onMessage; + worker.postMessage({url: document.location.toString()}); + allWorkers.push(worker); + } + URL.revokeObjectURL(workerURL); + + function map(data, executable, env, incrCallback, finalCallback){ + + //save new task in map queue + mapQueue.push({data:data, + executable:prepareExeFunc(executable.toString()), + env:env, + index:0, + incrCallback:incrCallback, + finalCallback:finalCallback, + finished:false, + activeThreads:0}); + + for (var i=0;i<allWorkers.length;i++){ + allWorkers[i].postMessage({isWorking:true});//ask workers if they are busy + } + } + + function prepareExeFunc(string){ + var index = string.indexOf("(");//get index of first ( in function declaration + if (index == -1) { + console.log("exe function not formed properly for web workers " + string); + return null; + } + return "function executable" + string.substring(index); + } + + function onMessage(e){ + + if (e.data.result){//handle result first + + //get current work item off queue + if (mapQueue.length == 0) return; + var currentTask = mapQueue[0]; + currentTask.activeThreads--;//decrement active threads + + if (currentTask.incrCallback) currentTask.incrCallback(e.data.result);//incremental callback + if (currentTask.finished && currentTask.activeThreads == 0){ + if (currentTask.finalCallback) currentTask.finalCallback(); + mapQueue.shift();//remove first element + } + } + + var nextTask = getNextTask(mapQueue[0], 0); + if (!nextTask) return; + + var currentIndex = nextTask.index; + nextTask.index++; + //check that the index is not out of bounds + if (nextTask.data.length<=currentIndex){ + nextTask.finished = true; + + e.data.result = null;//remove result so it doesn't get handled twice + onMessage(e);//try again in case there is another item in the queue to start + return; + } + + if (e.data.isWorking === false){ + nextTask.activeThreads++; + e.target.postMessage({ + arg:nextTask.data[currentIndex], + localEnv:JSON.stringify(nextTask.env), + executable:nextTask.executable}); + } + } + + function getNextTask(currentTask, index){ + if (!currentTask) return null; + if (currentTask.finished){ + var nextIndex = index+1; + if (mapQueue.length<=nextIndex) return null; + return getNextTask(mapQueue[nextIndex]); + } else { + return currentTask; + } + } + + function makeBlobURL(URL, someFunction) { + var blob = new Blob(["(" + someFunction.toString() + ")()"], { type: "text/javascript" }); + return URL.createObjectURL(blob); + } + + return {map:map, allWorkers:allWorkers};//return all public methods and vars +} \ No newline at end of file diff --git a/dependencies/main_files/pushPullMeshView.js b/dependencies/main_files/pushPullMeshView.js new file mode 100644 index 00000000..06d1b4da --- /dev/null +++ b/dependencies/main_files/pushPullMeshView.js @@ -0,0 +1,97 @@ +/** + * Created by aghassaei on 1/17/15. + */ + +//this is a parent class for other threeJS VCs that allows push and pull scale changes in the threeJS scene + +PushPullMeshView = Backbone.View.extend({ + + el: "#threeContainer", + + events: { + }, + + boundsBox: null, + boxHelper: null, + currentHighlightedFaces:[], + shouldReceiveHighlight: true, + highlightPlane: null, + + initialize: function(options){ + + this.three = options.three; + + //bind events + this.listenTo(this.model, "change:bounds change:scale change:orientation", this.updateBounds); + + this.drawBounds(); +// this.createHighlightPlane(); + }, + + drawBounds: function(){ + this.boundsBox = new THREE.Mesh(new THREE.BoxGeometry(100, 100, 100), + new THREE.MeshLambertMaterial({color:0x0000ff, shading:THREE.FlatShading, transparent:true, opacity:0.0, vertexColors:THREE.FaceColors})); + this.boxHelper = new THREE.BoxHelper(this.boundsBox); + + this.boxHelper.material.color.set(this.defaultColor); + this.three.sceneAdd(this.boxHelper); +// this.three.sceneAdd(this.boundsBox); + this.updateBounds(); + }, + + updateBounds: function(){ + var bounds = this.model.get("bounds");//this has not been scaled or rotated, as is when model was first imported + var max = bounds.max.toArray(); + var min = bounds.min.toArray(); + var size = numeric.sub(max, min); + var translation = numeric.mul(numeric.add(max, min), 0.5); + var geometry = new THREE.BoxGeometry(size[0], size[1], size[2]); + geometry.applyMatrix(new THREE.Matrix4().makeTranslation(translation[0], translation[1], translation[2])); + this.boundsBox.geometry = geometry; + this.boxHelper.update(this.boundsBox); + this.render(); + }, + +// createHighlightPlane: function(){ +// var squareGeometry = new THREE.Geometry(); +// squareGeometry.vertices.push(new THREE.Vector3()); +// squareGeometry.vertices.push(new THREE.Vector3()); +// squareGeometry.vertices.push(new THREE.Vector3()); +// squareGeometry.vertices.push(new THREE.Vector3()); +// squareGeometry.faces.push(new THREE.Face3(0, 1, 2)); +// squareGeometry.faces.push(new THREE.Face3(0, 2, 3)); +// var squareMaterial = new THREE.MeshBasicMaterial({color:0xffffff, shading:THREE.FlatShading, transparent:true, opacity:0.0, vertexColors:THREE.FaceColors}); +// this.highlightPlane = new THREE.Mesh(squareGeometry, squareMaterial); +// this.three.sceneAdd(this.highlightPlane); +// }, + + checkHighlight: function(intersections){ + if (intersections.length>0){ + var face = intersections[0].face; + if (this.currentHighlightedFaces.indexOf(face) != -1) return;//stay the same + this.setHighlightColor(this.currentHighlightedFaces, 0x0000ff); + + var faceIndex = intersections[0].object.geometry.faces.indexOf(face); + var face2Index = faceIndex-1; + if (faceIndex%2==0) face2Index = faceIndex+1; + this.currentHighlightedFaces = [face, intersections[0].object.geometry.faces[face2Index]]; + this.setHighlightColor(this.currentHighlightedFaces, 0xffffff); + + this.render(); + } else if (this.currentHighlightedFaces.length > 0){ + this.setHighlightColor(this.currentHighlightedFaces, 0x0000ff); + this.currentHighlightedFaces = []; + this.render(); + } + }, + + setHighlightColor: function(faces, color){ + _.each(faces, function(face){ + face.color.setHex(color); + }); + this.boundsBox.geometry.colorsNeedUpdate = true; +// this.boundsBox.geometry.__dirtyColors = true +// this.boundsBox.geometry.dynamic = true + } + +}); \ No newline at end of file diff --git a/dependencies/main_files/three.js b/dependencies/main_files/three.js new file mode 100644 index 00000000..d5d219aa --- /dev/null +++ b/dependencies/main_files/three.js @@ -0,0 +1,34545 @@ +// File:src/Three.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +var THREE = { REVISION: '70' }; + +// browserify support + +if ( typeof module === 'object' ) { + + module.exports = THREE; + +} + +// polyfills + +if ( Math.sign === undefined ) { + + Math.sign = function ( x ) { + + return ( x < 0 ) ? - 1 : ( x > 0 ) ? 1 : 0; + + }; + +} + +// https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent.button + +THREE.MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2 }; + +// GL STATE CONSTANTS + +THREE.CullFaceNone = 0; +THREE.CullFaceBack = 1; +THREE.CullFaceFront = 2; +THREE.CullFaceFrontBack = 3; + +THREE.FrontFaceDirectionCW = 0; +THREE.FrontFaceDirectionCCW = 1; + +// SHADOWING TYPES + +THREE.BasicShadowMap = 0; +THREE.PCFShadowMap = 1; +THREE.PCFSoftShadowMap = 2; + +// MATERIAL CONSTANTS + +// side + +THREE.FrontSide = 0; +THREE.BackSide = 1; +THREE.DoubleSide = 2; + +// shading + +THREE.NoShading = 0; +THREE.FlatShading = 1; +THREE.SmoothShading = 2; + +// colors + +THREE.NoColors = 0; +THREE.FaceColors = 1; +THREE.VertexColors = 2; + +// blending modes + +THREE.NoBlending = 0; +THREE.NormalBlending = 1; +THREE.AdditiveBlending = 2; +THREE.SubtractiveBlending = 3; +THREE.MultiplyBlending = 4; +THREE.CustomBlending = 5; + +// custom blending equations +// (numbers start from 100 not to clash with other +// mappings to OpenGL constants defined in Texture.js) + +THREE.AddEquation = 100; +THREE.SubtractEquation = 101; +THREE.ReverseSubtractEquation = 102; +THREE.MinEquation = 103; +THREE.MaxEquation = 104; + +// custom blending destination factors + +THREE.ZeroFactor = 200; +THREE.OneFactor = 201; +THREE.SrcColorFactor = 202; +THREE.OneMinusSrcColorFactor = 203; +THREE.SrcAlphaFactor = 204; +THREE.OneMinusSrcAlphaFactor = 205; +THREE.DstAlphaFactor = 206; +THREE.OneMinusDstAlphaFactor = 207; + +// custom blending source factors + +//THREE.ZeroFactor = 200; +//THREE.OneFactor = 201; +//THREE.SrcAlphaFactor = 204; +//THREE.OneMinusSrcAlphaFactor = 205; +//THREE.DstAlphaFactor = 206; +//THREE.OneMinusDstAlphaFactor = 207; +THREE.DstColorFactor = 208; +THREE.OneMinusDstColorFactor = 209; +THREE.SrcAlphaSaturateFactor = 210; + + +// TEXTURE CONSTANTS + +THREE.MultiplyOperation = 0; +THREE.MixOperation = 1; +THREE.AddOperation = 2; + +// Mapping modes + +THREE.UVMapping = 300; + +THREE.CubeReflectionMapping = 301; +THREE.CubeRefractionMapping = 302; + +THREE.EquirectangularReflectionMapping = 303; +THREE.EquirectangularRefractionMapping = 304; + +THREE.SphericalReflectionMapping = 305; + +// Wrapping modes + +THREE.RepeatWrapping = 1000; +THREE.ClampToEdgeWrapping = 1001; +THREE.MirroredRepeatWrapping = 1002; + +// Filters + +THREE.NearestFilter = 1003; +THREE.NearestMipMapNearestFilter = 1004; +THREE.NearestMipMapLinearFilter = 1005; +THREE.LinearFilter = 1006; +THREE.LinearMipMapNearestFilter = 1007; +THREE.LinearMipMapLinearFilter = 1008; + +// Data types + +THREE.UnsignedByteType = 1009; +THREE.ByteType = 1010; +THREE.ShortType = 1011; +THREE.UnsignedShortType = 1012; +THREE.IntType = 1013; +THREE.UnsignedIntType = 1014; +THREE.FloatType = 1015; + +// Pixel types + +//THREE.UnsignedByteType = 1009; +THREE.UnsignedShort4444Type = 1016; +THREE.UnsignedShort5551Type = 1017; +THREE.UnsignedShort565Type = 1018; + +// Pixel formats + +THREE.AlphaFormat = 1019; +THREE.RGBFormat = 1020; +THREE.RGBAFormat = 1021; +THREE.LuminanceFormat = 1022; +THREE.LuminanceAlphaFormat = 1023; +// THREE.RGBEFormat handled as THREE.RGBAFormat in shaders +THREE.RGBEFormat = THREE.RGBAFormat; //1024; + +// DDS / ST3C Compressed texture formats + +THREE.RGB_S3TC_DXT1_Format = 2001; +THREE.RGBA_S3TC_DXT1_Format = 2002; +THREE.RGBA_S3TC_DXT3_Format = 2003; +THREE.RGBA_S3TC_DXT5_Format = 2004; + + +// PVRTC compressed texture formats + +THREE.RGB_PVRTC_4BPPV1_Format = 2100; +THREE.RGB_PVRTC_2BPPV1_Format = 2101; +THREE.RGBA_PVRTC_4BPPV1_Format = 2102; +THREE.RGBA_PVRTC_2BPPV1_Format = 2103; + + +// DEPRECATED + +THREE.Projector = function () { + + console.error( 'THREE.Projector has been moved to /examples/js/renderers/Projector.js.' ); + + this.projectVector = function ( vector, camera ) { + + console.warn( 'THREE.Projector: .projectVector() is now vector.project().' ); + vector.project( camera ); + + }; + + this.unprojectVector = function ( vector, camera ) { + + console.warn( 'THREE.Projector: .unprojectVector() is now vector.unproject().' ); + vector.unproject( camera ); + + }; + + this.pickingRay = function ( vector, camera ) { + + console.error( 'THREE.Projector: .pickingRay() is now raycaster.setFromCamera().' ); + + }; + +}; + +THREE.CanvasRenderer = function () { + + console.error( 'THREE.CanvasRenderer has been moved to /examples/js/renderers/CanvasRenderer.js' ); + + this.domElement = document.createElement( 'canvas' ); + this.clear = function () {}; + this.render = function () {}; + this.setClearColor = function () {}; + this.setSize = function () {}; + +}; + +// File:src/math/Color.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Color = function ( color ) { + + if ( arguments.length === 3 ) { + + return this.setRGB( arguments[ 0 ], arguments[ 1 ], arguments[ 2 ] ); + + } + + return this.set( color ) + +}; + +THREE.Color.prototype = { + + constructor: THREE.Color, + + r: 1, g: 1, b: 1, + + set: function ( value ) { + + if ( value instanceof THREE.Color ) { + + this.copy( value ); + + } else if ( typeof value === 'number' ) { + + this.setHex( value ); + + } else if ( typeof value === 'string' ) { + + this.setStyle( value ); + + } + + return this; + + }, + + setHex: function ( hex ) { + + hex = Math.floor( hex ); + + this.r = ( hex >> 16 & 255 ) / 255; + this.g = ( hex >> 8 & 255 ) / 255; + this.b = ( hex & 255 ) / 255; + + return this; + + }, + + setRGB: function ( r, g, b ) { + + this.r = r; + this.g = g; + this.b = b; + + return this; + + }, + + setHSL: function ( h, s, l ) { + + // h,s,l ranges are in 0.0 - 1.0 + + if ( s === 0 ) { + + this.r = this.g = this.b = l; + + } else { + + var hue2rgb = function ( p, q, t ) { + + if ( t < 0 ) t += 1; + if ( t > 1 ) t -= 1; + if ( t < 1 / 6 ) return p + ( q - p ) * 6 * t; + if ( t < 1 / 2 ) return q; + if ( t < 2 / 3 ) return p + ( q - p ) * 6 * ( 2 / 3 - t ); + return p; + + }; + + var p = l <= 0.5 ? l * ( 1 + s ) : l + s - ( l * s ); + var q = ( 2 * l ) - p; + + this.r = hue2rgb( q, p, h + 1 / 3 ); + this.g = hue2rgb( q, p, h ); + this.b = hue2rgb( q, p, h - 1 / 3 ); + + } + + return this; + + }, + + setStyle: function ( style ) { + + // rgb(255,0,0) + + if ( /^rgb\((\d+), ?(\d+), ?(\d+)\)$/i.test( style ) ) { + + var color = /^rgb\((\d+), ?(\d+), ?(\d+)\)$/i.exec( style ); + + this.r = Math.min( 255, parseInt( color[ 1 ], 10 ) ) / 255; + this.g = Math.min( 255, parseInt( color[ 2 ], 10 ) ) / 255; + this.b = Math.min( 255, parseInt( color[ 3 ], 10 ) ) / 255; + + return this; + + } + + // rgb(100%,0%,0%) + + if ( /^rgb\((\d+)\%, ?(\d+)\%, ?(\d+)\%\)$/i.test( style ) ) { + + var color = /^rgb\((\d+)\%, ?(\d+)\%, ?(\d+)\%\)$/i.exec( style ); + + this.r = Math.min( 100, parseInt( color[ 1 ], 10 ) ) / 100; + this.g = Math.min( 100, parseInt( color[ 2 ], 10 ) ) / 100; + this.b = Math.min( 100, parseInt( color[ 3 ], 10 ) ) / 100; + + return this; + + } + + // #ff0000 + + if ( /^\#([0-9a-f]{6})$/i.test( style ) ) { + + var color = /^\#([0-9a-f]{6})$/i.exec( style ); + + this.setHex( parseInt( color[ 1 ], 16 ) ); + + return this; + + } + + // #f00 + + if ( /^\#([0-9a-f])([0-9a-f])([0-9a-f])$/i.test( style ) ) { + + var color = /^\#([0-9a-f])([0-9a-f])([0-9a-f])$/i.exec( style ); + + this.setHex( parseInt( color[ 1 ] + color[ 1 ] + color[ 2 ] + color[ 2 ] + color[ 3 ] + color[ 3 ], 16 ) ); + + return this; + + } + + // red + + if ( /^(\w+)$/i.test( style ) ) { + + this.setHex( THREE.ColorKeywords[ style ] ); + + return this; + + } + + + }, + + copy: function ( color ) { + + this.r = color.r; + this.g = color.g; + this.b = color.b; + + return this; + + }, + + copyGammaToLinear: function ( color ) { + + this.r = color.r * color.r; + this.g = color.g * color.g; + this.b = color.b * color.b; + + return this; + + }, + + copyLinearToGamma: function ( color ) { + + this.r = Math.sqrt( color.r ); + this.g = Math.sqrt( color.g ); + this.b = Math.sqrt( color.b ); + + return this; + + }, + + convertGammaToLinear: function () { + + var r = this.r, g = this.g, b = this.b; + + this.r = r * r; + this.g = g * g; + this.b = b * b; + + return this; + + }, + + convertLinearToGamma: function () { + + this.r = Math.sqrt( this.r ); + this.g = Math.sqrt( this.g ); + this.b = Math.sqrt( this.b ); + + return this; + + }, + + getHex: function () { + + return ( this.r * 255 ) << 16 ^ ( this.g * 255 ) << 8 ^ ( this.b * 255 ) << 0; + + }, + + getHexString: function () { + + return ( '000000' + this.getHex().toString( 16 ) ).slice( - 6 ); + + }, + + getHSL: function ( optionalTarget ) { + + // h,s,l ranges are in 0.0 - 1.0 + + var hsl = optionalTarget || { h: 0, s: 0, l: 0 }; + + var r = this.r, g = this.g, b = this.b; + + var max = Math.max( r, g, b ); + var min = Math.min( r, g, b ); + + var hue, saturation; + var lightness = ( min + max ) / 2.0; + + if ( min === max ) { + + hue = 0; + saturation = 0; + + } else { + + var delta = max - min; + + saturation = lightness <= 0.5 ? delta / ( max + min ) : delta / ( 2 - max - min ); + + switch ( max ) { + + case r: hue = ( g - b ) / delta + ( g < b ? 6 : 0 ); break; + case g: hue = ( b - r ) / delta + 2; break; + case b: hue = ( r - g ) / delta + 4; break; + + } + + hue /= 6; + + } + + hsl.h = hue; + hsl.s = saturation; + hsl.l = lightness; + + return hsl; + + }, + + getStyle: function () { + + return 'rgb(' + ( ( this.r * 255 ) | 0 ) + ',' + ( ( this.g * 255 ) | 0 ) + ',' + ( ( this.b * 255 ) | 0 ) + ')'; + + }, + + offsetHSL: function ( h, s, l ) { + + var hsl = this.getHSL(); + + hsl.h += h; hsl.s += s; hsl.l += l; + + this.setHSL( hsl.h, hsl.s, hsl.l ); + + return this; + + }, + + add: function ( color ) { + + this.r += color.r; + this.g += color.g; + this.b += color.b; + + return this; + + }, + + addColors: function ( color1, color2 ) { + + this.r = color1.r + color2.r; + this.g = color1.g + color2.g; + this.b = color1.b + color2.b; + + return this; + + }, + + addScalar: function ( s ) { + + this.r += s; + this.g += s; + this.b += s; + + return this; + + }, + + multiply: function ( color ) { + + this.r *= color.r; + this.g *= color.g; + this.b *= color.b; + + return this; + + }, + + multiplyScalar: function ( s ) { + + this.r *= s; + this.g *= s; + this.b *= s; + + return this; + + }, + + lerp: function ( color, alpha ) { + + this.r += ( color.r - this.r ) * alpha; + this.g += ( color.g - this.g ) * alpha; + this.b += ( color.b - this.b ) * alpha; + + return this; + + }, + + equals: function ( c ) { + + return ( c.r === this.r ) && ( c.g === this.g ) && ( c.b === this.b ); + + }, + + fromArray: function ( array ) { + + this.r = array[ 0 ]; + this.g = array[ 1 ]; + this.b = array[ 2 ]; + + return this; + + }, + + toArray: function () { + + return [ this.r, this.g, this.b ]; + + }, + + clone: function () { + + return new THREE.Color().setRGB( this.r, this.g, this.b ); + + } + +}; + +THREE.ColorKeywords = { '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, '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 }; + +// File:src/math/Quaternion.js + +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + * @author WestLangley / http://github.com/WestLangley + * @author bhouston / http://exocortex.com + */ + +THREE.Quaternion = function ( x, y, z, w ) { + + this._x = x || 0; + this._y = y || 0; + this._z = z || 0; + this._w = ( w !== undefined ) ? w : 1; + +}; + +THREE.Quaternion.prototype = { + + constructor: THREE.Quaternion, + + _x: 0,_y: 0, _z: 0, _w: 0, + + get x () { + + return this._x; + + }, + + set x ( value ) { + + this._x = value; + this.onChangeCallback(); + + }, + + get y () { + + return this._y; + + }, + + set y ( value ) { + + this._y = value; + this.onChangeCallback(); + + }, + + get z () { + + return this._z; + + }, + + set z ( value ) { + + this._z = value; + this.onChangeCallback(); + + }, + + get w () { + + return this._w; + + }, + + set w ( value ) { + + this._w = value; + this.onChangeCallback(); + + }, + + set: function ( x, y, z, w ) { + + this._x = x; + this._y = y; + this._z = z; + this._w = w; + + this.onChangeCallback(); + + return this; + + }, + + copy: function ( quaternion ) { + + this._x = quaternion.x; + this._y = quaternion.y; + this._z = quaternion.z; + this._w = quaternion.w; + + this.onChangeCallback(); + + return this; + + }, + + setFromEuler: function ( euler, update ) { + + if ( euler instanceof THREE.Euler === false ) { + + throw new Error( 'THREE.Quaternion: .setFromEuler() now expects a Euler rotation rather than a Vector3 and order.' ); + } + + // http://www.mathworks.com/matlabcentral/fileexchange/ + // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/ + // content/SpinCalc.m + + var c1 = Math.cos( euler._x / 2 ); + var c2 = Math.cos( euler._y / 2 ); + var c3 = Math.cos( euler._z / 2 ); + var s1 = Math.sin( euler._x / 2 ); + var s2 = Math.sin( euler._y / 2 ); + var s3 = Math.sin( euler._z / 2 ); + + if ( euler.order === 'XYZ' ) { + + this._x = s1 * c2 * c3 + c1 * s2 * s3; + this._y = c1 * s2 * c3 - s1 * c2 * s3; + this._z = c1 * c2 * s3 + s1 * s2 * c3; + this._w = c1 * c2 * c3 - s1 * s2 * s3; + + } else if ( euler.order === 'YXZ' ) { + + this._x = s1 * c2 * c3 + c1 * s2 * s3; + this._y = c1 * s2 * c3 - s1 * c2 * s3; + this._z = c1 * c2 * s3 - s1 * s2 * c3; + this._w = c1 * c2 * c3 + s1 * s2 * s3; + + } else if ( euler.order === 'ZXY' ) { + + this._x = s1 * c2 * c3 - c1 * s2 * s3; + this._y = c1 * s2 * c3 + s1 * c2 * s3; + this._z = c1 * c2 * s3 + s1 * s2 * c3; + this._w = c1 * c2 * c3 - s1 * s2 * s3; + + } else if ( euler.order === 'ZYX' ) { + + this._x = s1 * c2 * c3 - c1 * s2 * s3; + this._y = c1 * s2 * c3 + s1 * c2 * s3; + this._z = c1 * c2 * s3 - s1 * s2 * c3; + this._w = c1 * c2 * c3 + s1 * s2 * s3; + + } else if ( euler.order === 'YZX' ) { + + this._x = s1 * c2 * c3 + c1 * s2 * s3; + this._y = c1 * s2 * c3 + s1 * c2 * s3; + this._z = c1 * c2 * s3 - s1 * s2 * c3; + this._w = c1 * c2 * c3 - s1 * s2 * s3; + + } else if ( euler.order === 'XZY' ) { + + this._x = s1 * c2 * c3 - c1 * s2 * s3; + this._y = c1 * s2 * c3 - s1 * c2 * s3; + this._z = c1 * c2 * s3 + s1 * s2 * c3; + this._w = c1 * c2 * c3 + s1 * s2 * s3; + + } + + if ( update !== false ) this.onChangeCallback(); + + return this; + + }, + + setFromAxisAngle: function ( axis, angle ) { + + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm + + // assumes axis is normalized + + var halfAngle = angle / 2, s = Math.sin( halfAngle ); + + this._x = axis.x * s; + this._y = axis.y * s; + this._z = axis.z * s; + this._w = Math.cos( halfAngle ); + + this.onChangeCallback(); + + return this; + + }, + + setFromRotationMatrix: function ( m ) { + + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm + + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + + var te = m.elements, + + m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], + m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], + m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ], + + trace = m11 + m22 + m33, + s; + + if ( trace > 0 ) { + + s = 0.5 / Math.sqrt( trace + 1.0 ); + + this._w = 0.25 / s; + this._x = ( m32 - m23 ) * s; + this._y = ( m13 - m31 ) * s; + this._z = ( m21 - m12 ) * s; + + } else if ( m11 > m22 && m11 > m33 ) { + + s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 ); + + this._w = ( m32 - m23 ) / s; + this._x = 0.25 * s; + this._y = ( m12 + m21 ) / s; + this._z = ( m13 + m31 ) / s; + + } else if ( m22 > m33 ) { + + s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 ); + + this._w = ( m13 - m31 ) / s; + this._x = ( m12 + m21 ) / s; + this._y = 0.25 * s; + this._z = ( m23 + m32 ) / s; + + } else { + + s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 ); + + this._w = ( m21 - m12 ) / s; + this._x = ( m13 + m31 ) / s; + this._y = ( m23 + m32 ) / s; + this._z = 0.25 * s; + + } + + this.onChangeCallback(); + + return this; + + }, + + setFromUnitVectors: function () { + + // http://lolengine.net/blog/2014/02/24/quaternion-from-two-vectors-final + + // assumes direction vectors vFrom and vTo are normalized + + var v1, r; + + var EPS = 0.000001; + + return function ( vFrom, vTo ) { + + if ( v1 === undefined ) v1 = new THREE.Vector3(); + + r = vFrom.dot( vTo ) + 1; + + if ( r < EPS ) { + + r = 0; + + if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) { + + v1.set( - vFrom.y, vFrom.x, 0 ); + + } else { + + v1.set( 0, - vFrom.z, vFrom.y ); + + } + + } else { + + v1.crossVectors( vFrom, vTo ); + + } + + this._x = v1.x; + this._y = v1.y; + this._z = v1.z; + this._w = r; + + this.normalize(); + + return this; + + } + + }(), + + inverse: function () { + + this.conjugate().normalize(); + + return this; + + }, + + conjugate: function () { + + this._x *= - 1; + this._y *= - 1; + this._z *= - 1; + + this.onChangeCallback(); + + return this; + + }, + + dot: function ( v ) { + + return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w; + + }, + + lengthSq: function () { + + return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w; + + }, + + length: function () { + + return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w ); + + }, + + normalize: function () { + + var l = this.length(); + + if ( l === 0 ) { + + this._x = 0; + this._y = 0; + this._z = 0; + this._w = 1; + + } else { + + l = 1 / l; + + this._x = this._x * l; + this._y = this._y * l; + this._z = this._z * l; + this._w = this._w * l; + + } + + this.onChangeCallback(); + + return this; + + }, + + multiply: function ( q, p ) { + + if ( p !== undefined ) { + + console.warn( 'THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' ); + return this.multiplyQuaternions( q, p ); + + } + + return this.multiplyQuaternions( this, q ); + + }, + + multiplyQuaternions: function ( a, b ) { + + // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm + + var qax = a._x, qay = a._y, qaz = a._z, qaw = a._w; + var qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w; + + this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; + this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; + this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; + this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; + + this.onChangeCallback(); + + return this; + + }, + + multiplyVector3: function ( vector ) { + + console.warn( 'THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.' ); + return vector.applyQuaternion( this ); + + }, + + slerp: function ( qb, t ) { + + if ( t === 0 ) return this; + if ( t === 1 ) return this.copy( qb ); + + var x = this._x, y = this._y, z = this._z, w = this._w; + + // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ + + var cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z; + + if ( cosHalfTheta < 0 ) { + + this._w = - qb._w; + this._x = - qb._x; + this._y = - qb._y; + this._z = - qb._z; + + cosHalfTheta = - cosHalfTheta; + + } else { + + this.copy( qb ); + + } + + if ( cosHalfTheta >= 1.0 ) { + + this._w = w; + this._x = x; + this._y = y; + this._z = z; + + return this; + + } + + var halfTheta = Math.acos( cosHalfTheta ); + var sinHalfTheta = Math.sqrt( 1.0 - cosHalfTheta * cosHalfTheta ); + + if ( Math.abs( sinHalfTheta ) < 0.001 ) { + + this._w = 0.5 * ( w + this._w ); + this._x = 0.5 * ( x + this._x ); + this._y = 0.5 * ( y + this._y ); + this._z = 0.5 * ( z + this._z ); + + return this; + + } + + var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta, + ratioB = Math.sin( t * halfTheta ) / sinHalfTheta; + + this._w = ( w * ratioA + this._w * ratioB ); + this._x = ( x * ratioA + this._x * ratioB ); + this._y = ( y * ratioA + this._y * ratioB ); + this._z = ( z * ratioA + this._z * ratioB ); + + this.onChangeCallback(); + + return this; + + }, + + equals: function ( quaternion ) { + + return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w ); + + }, + + fromArray: function ( array, offset ) { + + if ( offset === undefined ) offset = 0; + + this._x = array[ offset ]; + this._y = array[ offset + 1 ]; + this._z = array[ offset + 2 ]; + this._w = array[ offset + 3 ]; + + this.onChangeCallback(); + + return this; + + }, + + toArray: function ( array, offset ) { + + if ( array === undefined ) array = []; + if ( offset === undefined ) offset = 0; + + array[ offset ] = this._x; + array[ offset + 1 ] = this._y; + array[ offset + 2 ] = this._z; + array[ offset + 3 ] = this._w; + + return array; + + }, + + onChange: function ( callback ) { + + this.onChangeCallback = callback; + + return this; + + }, + + onChangeCallback: function () {}, + + clone: function () { + + return new THREE.Quaternion( this._x, this._y, this._z, this._w ); + + } + +}; + +THREE.Quaternion.slerp = function ( qa, qb, qm, t ) { + + return qm.copy( qa ).slerp( qb, t ); + +} + +// File:src/math/Vector2.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author philogb / http://blog.thejit.org/ + * @author egraether / http://egraether.com/ + * @author zz85 / http://www.lab4games.net/zz85/blog + */ + +THREE.Vector2 = function ( x, y ) { + + this.x = x || 0; + this.y = y || 0; + +}; + +THREE.Vector2.prototype = { + + constructor: THREE.Vector2, + + set: function ( x, y ) { + + this.x = x; + this.y = y; + + return this; + + }, + + setX: function ( x ) { + + this.x = x; + + return this; + + }, + + setY: function ( y ) { + + this.y = y; + + return this; + + }, + + setComponent: function ( index, value ) { + + switch ( index ) { + + case 0: this.x = value; break; + case 1: this.y = value; break; + default: throw new Error( 'index is out of range: ' + index ); + + } + + }, + + getComponent: function ( index ) { + + switch ( index ) { + + case 0: return this.x; + case 1: return this.y; + default: throw new Error( 'index is out of range: ' + index ); + + } + + }, + + copy: function ( v ) { + + this.x = v.x; + this.y = v.y; + + return this; + + }, + + add: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); + return this.addVectors( v, w ); + + } + + this.x += v.x; + this.y += v.y; + + return this; + + }, + + addVectors: function ( a, b ) { + + this.x = a.x + b.x; + this.y = a.y + b.y; + + return this; + + }, + + addScalar: function ( s ) { + + this.x += s; + this.y += s; + + return this; + + }, + + sub: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); + return this.subVectors( v, w ); + + } + + this.x -= v.x; + this.y -= v.y; + + return this; + + }, + + subVectors: function ( a, b ) { + + this.x = a.x - b.x; + this.y = a.y - b.y; + + return this; + + }, + + multiply: function ( v ) { + + this.x *= v.x; + this.y *= v.y; + + return this; + + }, + + multiplyScalar: function ( s ) { + + this.x *= s; + this.y *= s; + + return this; + + }, + + divide: function ( v ) { + + this.x /= v.x; + this.y /= v.y; + + return this; + + }, + + divideScalar: function ( scalar ) { + + if ( scalar !== 0 ) { + + var invScalar = 1 / scalar; + + this.x *= invScalar; + this.y *= invScalar; + + } else { + + this.x = 0; + this.y = 0; + + } + + return this; + + }, + + min: function ( v ) { + + if ( this.x > v.x ) { + + this.x = v.x; + + } + + if ( this.y > v.y ) { + + this.y = v.y; + + } + + return this; + + }, + + max: function ( v ) { + + if ( this.x < v.x ) { + + this.x = v.x; + + } + + if ( this.y < v.y ) { + + this.y = v.y; + + } + + return this; + + }, + + clamp: function ( min, max ) { + + // This function assumes min < max, if this assumption isn't true it will not operate correctly + + if ( this.x < min.x ) { + + this.x = min.x; + + } else if ( this.x > max.x ) { + + this.x = max.x; + + } + + if ( this.y < min.y ) { + + this.y = min.y; + + } else if ( this.y > max.y ) { + + this.y = max.y; + + } + + return this; + }, + + clampScalar: ( function () { + + var min, max; + + return function ( minVal, maxVal ) { + + if ( min === undefined ) { + + min = new THREE.Vector2(); + max = new THREE.Vector2(); + + } + + min.set( minVal, minVal ); + max.set( maxVal, maxVal ); + + return this.clamp( min, max ); + + }; + + } )(), + + floor: function () { + + this.x = Math.floor( this.x ); + this.y = Math.floor( this.y ); + + return this; + + }, + + ceil: function () { + + this.x = Math.ceil( this.x ); + this.y = Math.ceil( this.y ); + + return this; + + }, + + round: function () { + + this.x = Math.round( this.x ); + this.y = Math.round( this.y ); + + return this; + + }, + + roundToZero: function () { + + this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); + this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); + + return this; + + }, + + negate: function () { + + this.x = - this.x; + this.y = - this.y; + + return this; + + }, + + dot: function ( v ) { + + return this.x * v.x + this.y * v.y; + + }, + + lengthSq: function () { + + return this.x * this.x + this.y * this.y; + + }, + + length: function () { + + return Math.sqrt( this.x * this.x + this.y * this.y ); + + }, + + normalize: function () { + + return this.divideScalar( this.length() ); + + }, + + distanceTo: function ( v ) { + + return Math.sqrt( this.distanceToSquared( v ) ); + + }, + + distanceToSquared: function ( v ) { + + var dx = this.x - v.x, dy = this.y - v.y; + return dx * dx + dy * dy; + + }, + + setLength: function ( l ) { + + var oldLength = this.length(); + + if ( oldLength !== 0 && l !== oldLength ) { + + this.multiplyScalar( l / oldLength ); + } + + return this; + + }, + + lerp: function ( v, alpha ) { + + this.x += ( v.x - this.x ) * alpha; + this.y += ( v.y - this.y ) * alpha; + + return this; + + }, + + equals: function ( v ) { + + return ( ( v.x === this.x ) && ( v.y === this.y ) ); + + }, + + fromArray: function ( array, offset ) { + + if ( offset === undefined ) offset = 0; + + this.x = array[ offset ]; + this.y = array[ offset + 1 ]; + + return this; + + }, + + toArray: function ( array, offset ) { + + if ( array === undefined ) array = []; + if ( offset === undefined ) offset = 0; + + array[ offset ] = this.x; + array[ offset + 1 ] = this.y; + + return array; + + }, + + fromAttribute: function ( attribute, index, offset ) { + + if ( offset === undefined ) offset = 0; + + index = index * attribute.itemSize + offset; + + this.x = attribute.array[ index ]; + this.y = attribute.array[ index + 1 ]; + + return this; + + }, + + clone: function () { + + return new THREE.Vector2( this.x, this.y ); + + } + +}; + +// File:src/math/Vector3.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author *kile / http://kile.stravaganza.org/ + * @author philogb / http://blog.thejit.org/ + * @author mikael emtinger / http://gomo.se/ + * @author egraether / http://egraether.com/ + * @author WestLangley / http://github.com/WestLangley + */ + +THREE.Vector3 = function ( x, y, z ) { + + this.x = x || 0; + this.y = y || 0; + this.z = z || 0; + +}; + +THREE.Vector3.prototype = { + + constructor: THREE.Vector3, + + set: function ( x, y, z ) { + + this.x = x; + this.y = y; + this.z = z; + + return this; + + }, + + setX: function ( x ) { + + this.x = x; + + return this; + + }, + + setY: function ( y ) { + + this.y = y; + + return this; + + }, + + setZ: function ( z ) { + + this.z = z; + + return this; + + }, + + setComponent: function ( index, value ) { + + switch ( index ) { + + case 0: this.x = value; break; + case 1: this.y = value; break; + case 2: this.z = value; break; + default: throw new Error( 'index is out of range: ' + index ); + + } + + }, + + getComponent: function ( index ) { + + switch ( index ) { + + case 0: return this.x; + case 1: return this.y; + case 2: return this.z; + default: throw new Error( 'index is out of range: ' + index ); + + } + + }, + + copy: function ( v ) { + + this.x = v.x; + this.y = v.y; + this.z = v.z; + + return this; + + }, + + add: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); + return this.addVectors( v, w ); + + } + + this.x += v.x; + this.y += v.y; + this.z += v.z; + + return this; + + }, + + addScalar: function ( s ) { + + this.x += s; + this.y += s; + this.z += s; + + return this; + + }, + + addVectors: function ( a, b ) { + + this.x = a.x + b.x; + this.y = a.y + b.y; + this.z = a.z + b.z; + + return this; + + }, + + sub: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); + return this.subVectors( v, w ); + + } + + this.x -= v.x; + this.y -= v.y; + this.z -= v.z; + + return this; + + }, + + subVectors: function ( a, b ) { + + this.x = a.x - b.x; + this.y = a.y - b.y; + this.z = a.z - b.z; + + return this; + + }, + + multiply: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' ); + return this.multiplyVectors( v, w ); + + } + + this.x *= v.x; + this.y *= v.y; + this.z *= v.z; + + return this; + + }, + + multiplyScalar: function ( scalar ) { + + this.x *= scalar; + this.y *= scalar; + this.z *= scalar; + + return this; + + }, + + multiplyVectors: function ( a, b ) { + + this.x = a.x * b.x; + this.y = a.y * b.y; + this.z = a.z * b.z; + + return this; + + }, + + applyEuler: function () { + + var quaternion; + + return function ( euler ) { + + if ( euler instanceof THREE.Euler === false ) { + + console.error( 'THREE.Vector3: .applyEuler() now expects a Euler rotation rather than a Vector3 and order.' ); + + } + + if ( quaternion === undefined ) quaternion = new THREE.Quaternion(); + + this.applyQuaternion( quaternion.setFromEuler( euler ) ); + + return this; + + }; + + }(), + + applyAxisAngle: function () { + + var quaternion; + + return function ( axis, angle ) { + + if ( quaternion === undefined ) quaternion = new THREE.Quaternion(); + + this.applyQuaternion( quaternion.setFromAxisAngle( axis, angle ) ); + + return this; + + }; + + }(), + + applyMatrix3: function ( m ) { + + var x = this.x; + var y = this.y; + var z = this.z; + + var e = m.elements; + + this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z; + this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z; + this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z; + + return this; + + }, + + applyMatrix4: function ( m ) { + + // input: THREE.Matrix4 affine matrix + + var x = this.x, y = this.y, z = this.z; + + var e = m.elements; + + this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ]; + this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ]; + this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ]; + + return this; + + }, + + applyProjection: function ( m ) { + + // input: THREE.Matrix4 projection matrix + + var x = this.x, y = this.y, z = this.z; + + var e = m.elements; + var d = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] ); // perspective divide + + this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * d; + this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * d; + this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * d; + + return this; + + }, + + applyQuaternion: function ( q ) { + + var x = this.x; + var y = this.y; + var z = this.z; + + var qx = q.x; + var qy = q.y; + var qz = q.z; + var qw = q.w; + + // calculate quat * vector + + var ix = qw * x + qy * z - qz * y; + var iy = qw * y + qz * x - qx * z; + var iz = qw * z + qx * y - qy * x; + var iw = - qx * x - qy * y - qz * z; + + // calculate result * inverse quat + + this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy; + this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz; + this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx; + + return this; + + }, + + project: function () { + + var matrix; + + return function ( camera ) { + + if ( matrix === undefined ) matrix = new THREE.Matrix4(); + + matrix.multiplyMatrices( camera.projectionMatrix, matrix.getInverse( camera.matrixWorld ) ); + return this.applyProjection( matrix ); + + }; + + }(), + + unproject: function () { + + var matrix; + + return function ( camera ) { + + if ( matrix === undefined ) matrix = new THREE.Matrix4(); + + matrix.multiplyMatrices( camera.matrixWorld, matrix.getInverse( camera.projectionMatrix ) ); + return this.applyProjection( matrix ); + + }; + + }(), + + transformDirection: function ( m ) { + + // input: THREE.Matrix4 affine matrix + // vector interpreted as a direction + + var x = this.x, y = this.y, z = this.z; + + var e = m.elements; + + this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z; + this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z; + this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z; + + this.normalize(); + + return this; + + }, + + divide: function ( v ) { + + this.x /= v.x; + this.y /= v.y; + this.z /= v.z; + + return this; + + }, + + divideScalar: function ( scalar ) { + + if ( scalar !== 0 ) { + + var invScalar = 1 / scalar; + + this.x *= invScalar; + this.y *= invScalar; + this.z *= invScalar; + + } else { + + this.x = 0; + this.y = 0; + this.z = 0; + + } + + return this; + + }, + + min: function ( v ) { + + if ( this.x > v.x ) { + + this.x = v.x; + + } + + if ( this.y > v.y ) { + + this.y = v.y; + + } + + if ( this.z > v.z ) { + + this.z = v.z; + + } + + return this; + + }, + + max: function ( v ) { + + if ( this.x < v.x ) { + + this.x = v.x; + + } + + if ( this.y < v.y ) { + + this.y = v.y; + + } + + if ( this.z < v.z ) { + + this.z = v.z; + + } + + return this; + + }, + + clamp: function ( min, max ) { + + // This function assumes min < max, if this assumption isn't true it will not operate correctly + + if ( this.x < min.x ) { + + this.x = min.x; + + } else if ( this.x > max.x ) { + + this.x = max.x; + + } + + if ( this.y < min.y ) { + + this.y = min.y; + + } else if ( this.y > max.y ) { + + this.y = max.y; + + } + + if ( this.z < min.z ) { + + this.z = min.z; + + } else if ( this.z > max.z ) { + + this.z = max.z; + + } + + return this; + + }, + + clampScalar: ( function () { + + var min, max; + + return function ( minVal, maxVal ) { + + if ( min === undefined ) { + + min = new THREE.Vector3(); + max = new THREE.Vector3(); + + } + + min.set( minVal, minVal, minVal ); + max.set( maxVal, maxVal, maxVal ); + + return this.clamp( min, max ); + + }; + + } )(), + + floor: function () { + + this.x = Math.floor( this.x ); + this.y = Math.floor( this.y ); + this.z = Math.floor( this.z ); + + return this; + + }, + + ceil: function () { + + this.x = Math.ceil( this.x ); + this.y = Math.ceil( this.y ); + this.z = Math.ceil( this.z ); + + return this; + + }, + + round: function () { + + this.x = Math.round( this.x ); + this.y = Math.round( this.y ); + this.z = Math.round( this.z ); + + return this; + + }, + + roundToZero: function () { + + this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); + this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); + this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z ); + + return this; + + }, + + negate: function () { + + this.x = - this.x; + this.y = - this.y; + this.z = - this.z; + + return this; + + }, + + dot: function ( v ) { + + return this.x * v.x + this.y * v.y + this.z * v.z; + + }, + + lengthSq: function () { + + return this.x * this.x + this.y * this.y + this.z * this.z; + + }, + + length: function () { + + return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z ); + + }, + + lengthManhattan: function () { + + return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ); + + }, + + normalize: function () { + + return this.divideScalar( this.length() ); + + }, + + setLength: function ( l ) { + + var oldLength = this.length(); + + if ( oldLength !== 0 && l !== oldLength ) { + + this.multiplyScalar( l / oldLength ); + } + + return this; + + }, + + lerp: function ( v, alpha ) { + + this.x += ( v.x - this.x ) * alpha; + this.y += ( v.y - this.y ) * alpha; + this.z += ( v.z - this.z ) * alpha; + + return this; + + }, + + cross: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' ); + return this.crossVectors( v, w ); + + } + + var x = this.x, y = this.y, z = this.z; + + this.x = y * v.z - z * v.y; + this.y = z * v.x - x * v.z; + this.z = x * v.y - y * v.x; + + return this; + + }, + + crossVectors: function ( a, b ) { + + var ax = a.x, ay = a.y, az = a.z; + var bx = b.x, by = b.y, bz = b.z; + + this.x = ay * bz - az * by; + this.y = az * bx - ax * bz; + this.z = ax * by - ay * bx; + + return this; + + }, + + projectOnVector: function () { + + var v1, dot; + + return function ( vector ) { + + if ( v1 === undefined ) v1 = new THREE.Vector3(); + + v1.copy( vector ).normalize(); + + dot = this.dot( v1 ); + + return this.copy( v1 ).multiplyScalar( dot ); + + }; + + }(), + + projectOnPlane: function () { + + var v1; + + return function ( planeNormal ) { + + if ( v1 === undefined ) v1 = new THREE.Vector3(); + + v1.copy( this ).projectOnVector( planeNormal ); + + return this.sub( v1 ); + + } + + }(), + + reflect: function () { + + // reflect incident vector off plane orthogonal to normal + // normal is assumed to have unit length + + var v1; + + return function ( normal ) { + + if ( v1 === undefined ) v1 = new THREE.Vector3(); + + return this.sub( v1.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) ); + + } + + }(), + + angleTo: function ( v ) { + + var theta = this.dot( v ) / ( this.length() * v.length() ); + + // clamp, to handle numerical problems + + return Math.acos( THREE.Math.clamp( theta, - 1, 1 ) ); + + }, + + distanceTo: function ( v ) { + + return Math.sqrt( this.distanceToSquared( v ) ); + + }, + + distanceToSquared: function ( v ) { + + var dx = this.x - v.x; + var dy = this.y - v.y; + var dz = this.z - v.z; + + return dx * dx + dy * dy + dz * dz; + + }, + + setEulerFromRotationMatrix: function ( m, order ) { + + console.error( 'THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.' ); + + }, + + setEulerFromQuaternion: function ( q, order ) { + + console.error( 'THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.' ); + + }, + + getPositionFromMatrix: function ( m ) { + + console.warn( 'THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition().' ); + + return this.setFromMatrixPosition( m ); + + }, + + getScaleFromMatrix: function ( m ) { + + console.warn( 'THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale().' ); + + return this.setFromMatrixScale( m ); + }, + + getColumnFromMatrix: function ( index, matrix ) { + + console.warn( 'THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn().' ); + + return this.setFromMatrixColumn( index, matrix ); + + }, + + setFromMatrixPosition: function ( m ) { + + this.x = m.elements[ 12 ]; + this.y = m.elements[ 13 ]; + this.z = m.elements[ 14 ]; + + return this; + + }, + + setFromMatrixScale: function ( m ) { + + var sx = this.set( m.elements[ 0 ], m.elements[ 1 ], m.elements[ 2 ] ).length(); + var sy = this.set( m.elements[ 4 ], m.elements[ 5 ], m.elements[ 6 ] ).length(); + var sz = this.set( m.elements[ 8 ], m.elements[ 9 ], m.elements[ 10 ] ).length(); + + this.x = sx; + this.y = sy; + this.z = sz; + + return this; + }, + + setFromMatrixColumn: function ( index, matrix ) { + + var offset = index * 4; + + var me = matrix.elements; + + this.x = me[ offset ]; + this.y = me[ offset + 1 ]; + this.z = me[ offset + 2 ]; + + return this; + + }, + + equals: function ( v ) { + + return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) ); + + }, + + fromArray: function ( array, offset ) { + + if ( offset === undefined ) offset = 0; + + this.x = array[ offset ]; + this.y = array[ offset + 1 ]; + this.z = array[ offset + 2 ]; + + return this; + + }, + + toArray: function ( array, offset ) { + + if ( array === undefined ) array = []; + if ( offset === undefined ) offset = 0; + + array[ offset ] = this.x; + array[ offset + 1 ] = this.y; + array[ offset + 2 ] = this.z; + + return array; + + }, + + fromAttribute: function ( attribute, index, offset ) { + + if ( offset === undefined ) offset = 0; + + index = index * attribute.itemSize + offset; + + this.x = attribute.array[ index ]; + this.y = attribute.array[ index + 1 ]; + this.z = attribute.array[ index + 2 ]; + + return this; + + }, + + clone: function () { + + return new THREE.Vector3( this.x, this.y, this.z ); + + } + +}; + +// File:src/math/Vector4.js + +/** + * @author supereggbert / http://www.paulbrunt.co.uk/ + * @author philogb / http://blog.thejit.org/ + * @author mikael emtinger / http://gomo.se/ + * @author egraether / http://egraether.com/ + * @author WestLangley / http://github.com/WestLangley + */ + +THREE.Vector4 = function ( x, y, z, w ) { + + this.x = x || 0; + this.y = y || 0; + this.z = z || 0; + this.w = ( w !== undefined ) ? w : 1; + +}; + +THREE.Vector4.prototype = { + + constructor: THREE.Vector4, + + set: function ( x, y, z, w ) { + + this.x = x; + this.y = y; + this.z = z; + this.w = w; + + return this; + + }, + + setX: function ( x ) { + + this.x = x; + + return this; + + }, + + setY: function ( y ) { + + this.y = y; + + return this; + + }, + + setZ: function ( z ) { + + this.z = z; + + return this; + + }, + + setW: function ( w ) { + + this.w = w; + + return this; + + }, + + setComponent: function ( index, value ) { + + switch ( index ) { + + case 0: this.x = value; break; + case 1: this.y = value; break; + case 2: this.z = value; break; + case 3: this.w = value; break; + default: throw new Error( 'index is out of range: ' + index ); + + } + + }, + + getComponent: function ( index ) { + + switch ( index ) { + + case 0: return this.x; + case 1: return this.y; + case 2: return this.z; + case 3: return this.w; + default: throw new Error( 'index is out of range: ' + index ); + + } + + }, + + copy: function ( v ) { + + this.x = v.x; + this.y = v.y; + this.z = v.z; + this.w = ( v.w !== undefined ) ? v.w : 1; + + return this; + + }, + + add: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); + return this.addVectors( v, w ); + + } + + this.x += v.x; + this.y += v.y; + this.z += v.z; + this.w += v.w; + + return this; + + }, + + addScalar: function ( s ) { + + this.x += s; + this.y += s; + this.z += s; + this.w += s; + + return this; + + }, + + addVectors: function ( a, b ) { + + this.x = a.x + b.x; + this.y = a.y + b.y; + this.z = a.z + b.z; + this.w = a.w + b.w; + + return this; + + }, + + sub: function ( v, w ) { + + if ( w !== undefined ) { + + console.warn( 'THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); + return this.subVectors( v, w ); + + } + + this.x -= v.x; + this.y -= v.y; + this.z -= v.z; + this.w -= v.w; + + return this; + + }, + + subVectors: function ( a, b ) { + + this.x = a.x - b.x; + this.y = a.y - b.y; + this.z = a.z - b.z; + this.w = a.w - b.w; + + return this; + + }, + + multiplyScalar: function ( scalar ) { + + this.x *= scalar; + this.y *= scalar; + this.z *= scalar; + this.w *= scalar; + + return this; + + }, + + applyMatrix4: function ( m ) { + + var x = this.x; + var y = this.y; + var z = this.z; + var w = this.w; + + var e = m.elements; + + this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] * w; + this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] * w; + this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] * w; + this.w = e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] * w; + + return this; + + }, + + divideScalar: function ( scalar ) { + + if ( scalar !== 0 ) { + + var invScalar = 1 / scalar; + + this.x *= invScalar; + this.y *= invScalar; + this.z *= invScalar; + this.w *= invScalar; + + } else { + + this.x = 0; + this.y = 0; + this.z = 0; + this.w = 1; + + } + + return this; + + }, + + setAxisAngleFromQuaternion: function ( q ) { + + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm + + // q is assumed to be normalized + + this.w = 2 * Math.acos( q.w ); + + var s = Math.sqrt( 1 - q.w * q.w ); + + if ( s < 0.0001 ) { + + this.x = 1; + this.y = 0; + this.z = 0; + + } else { + + this.x = q.x / s; + this.y = q.y / s; + this.z = q.z / s; + + } + + return this; + + }, + + setAxisAngleFromRotationMatrix: function ( m ) { + + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm + + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + + var angle, x, y, z, // variables for result + epsilon = 0.01, // margin to allow for rounding errors + epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees + + te = m.elements, + + m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], + m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], + m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; + + if ( ( Math.abs( m12 - m21 ) < epsilon ) + && ( Math.abs( m13 - m31 ) < epsilon ) + && ( Math.abs( m23 - m32 ) < epsilon ) ) { + + // singularity found + // first check for identity matrix which must have +1 for all terms + // in leading diagonal and zero in other terms + + if ( ( Math.abs( m12 + m21 ) < epsilon2 ) + && ( Math.abs( m13 + m31 ) < epsilon2 ) + && ( Math.abs( m23 + m32 ) < epsilon2 ) + && ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) { + + // this singularity is identity matrix so angle = 0 + + this.set( 1, 0, 0, 0 ); + + return this; // zero angle, arbitrary axis + + } + + // otherwise this singularity is angle = 180 + + angle = Math.PI; + + var xx = ( m11 + 1 ) / 2; + var yy = ( m22 + 1 ) / 2; + var zz = ( m33 + 1 ) / 2; + var xy = ( m12 + m21 ) / 4; + var xz = ( m13 + m31 ) / 4; + var yz = ( m23 + m32 ) / 4; + + if ( ( xx > yy ) && ( xx > zz ) ) { // m11 is the largest diagonal term + + if ( xx < epsilon ) { + + x = 0; + y = 0.707106781; + z = 0.707106781; + + } else { + + x = Math.sqrt( xx ); + y = xy / x; + z = xz / x; + + } + + } else if ( yy > zz ) { // m22 is the largest diagonal term + + if ( yy < epsilon ) { + + x = 0.707106781; + y = 0; + z = 0.707106781; + + } else { + + y = Math.sqrt( yy ); + x = xy / y; + z = yz / y; + + } + + } else { // m33 is the largest diagonal term so base result on this + + if ( zz < epsilon ) { + + x = 0.707106781; + y = 0.707106781; + z = 0; + + } else { + + z = Math.sqrt( zz ); + x = xz / z; + y = yz / z; + + } + + } + + this.set( x, y, z, angle ); + + return this; // return 180 deg rotation + + } + + // as we have reached here there are no singularities so we can handle normally + + var s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 ) + + ( m13 - m31 ) * ( m13 - m31 ) + + ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize + + if ( Math.abs( s ) < 0.001 ) s = 1; + + // prevent divide by zero, should not happen if matrix is orthogonal and should be + // caught by singularity test above, but I've left it in just in case + + this.x = ( m32 - m23 ) / s; + this.y = ( m13 - m31 ) / s; + this.z = ( m21 - m12 ) / s; + this.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 ); + + return this; + + }, + + min: function ( v ) { + + if ( this.x > v.x ) { + + this.x = v.x; + + } + + if ( this.y > v.y ) { + + this.y = v.y; + + } + + if ( this.z > v.z ) { + + this.z = v.z; + + } + + if ( this.w > v.w ) { + + this.w = v.w; + + } + + return this; + + }, + + max: function ( v ) { + + if ( this.x < v.x ) { + + this.x = v.x; + + } + + if ( this.y < v.y ) { + + this.y = v.y; + + } + + if ( this.z < v.z ) { + + this.z = v.z; + + } + + if ( this.w < v.w ) { + + this.w = v.w; + + } + + return this; + + }, + + clamp: function ( min, max ) { + + // This function assumes min < max, if this assumption isn't true it will not operate correctly + + if ( this.x < min.x ) { + + this.x = min.x; + + } else if ( this.x > max.x ) { + + this.x = max.x; + + } + + if ( this.y < min.y ) { + + this.y = min.y; + + } else if ( this.y > max.y ) { + + this.y = max.y; + + } + + if ( this.z < min.z ) { + + this.z = min.z; + + } else if ( this.z > max.z ) { + + this.z = max.z; + + } + + if ( this.w < min.w ) { + + this.w = min.w; + + } else if ( this.w > max.w ) { + + this.w = max.w; + + } + + return this; + + }, + + clampScalar: ( function () { + + var min, max; + + return function ( minVal, maxVal ) { + + if ( min === undefined ) { + + min = new THREE.Vector4(); + max = new THREE.Vector4(); + + } + + min.set( minVal, minVal, minVal, minVal ); + max.set( maxVal, maxVal, maxVal, maxVal ); + + return this.clamp( min, max ); + + }; + + } )(), + + floor: function () { + + this.x = Math.floor( this.x ); + this.y = Math.floor( this.y ); + this.z = Math.floor( this.z ); + this.w = Math.floor( this.w ); + + return this; + + }, + + ceil: function () { + + this.x = Math.ceil( this.x ); + this.y = Math.ceil( this.y ); + this.z = Math.ceil( this.z ); + this.w = Math.ceil( this.w ); + + return this; + + }, + + round: function () { + + this.x = Math.round( this.x ); + this.y = Math.round( this.y ); + this.z = Math.round( this.z ); + this.w = Math.round( this.w ); + + return this; + + }, + + roundToZero: function () { + + this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); + this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); + this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z ); + this.w = ( this.w < 0 ) ? Math.ceil( this.w ) : Math.floor( this.w ); + + return this; + + }, + + negate: function () { + + this.x = - this.x; + this.y = - this.y; + this.z = - this.z; + this.w = - this.w; + + return this; + + }, + + dot: function ( v ) { + + return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w; + + }, + + lengthSq: function () { + + return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w; + + }, + + length: function () { + + return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w ); + + }, + + lengthManhattan: function () { + + return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ) + Math.abs( this.w ); + + }, + + normalize: function () { + + return this.divideScalar( this.length() ); + + }, + + setLength: function ( l ) { + + var oldLength = this.length(); + + if ( oldLength !== 0 && l !== oldLength ) { + + this.multiplyScalar( l / oldLength ); + + } + + return this; + + }, + + lerp: function ( v, alpha ) { + + this.x += ( v.x - this.x ) * alpha; + this.y += ( v.y - this.y ) * alpha; + this.z += ( v.z - this.z ) * alpha; + this.w += ( v.w - this.w ) * alpha; + + return this; + + }, + + equals: function ( v ) { + + return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) ); + + }, + + fromArray: function ( array, offset ) { + + if ( offset === undefined ) offset = 0; + + this.x = array[ offset ]; + this.y = array[ offset + 1 ]; + this.z = array[ offset + 2 ]; + this.w = array[ offset + 3 ]; + + return this; + + }, + + toArray: function ( array, offset ) { + + if ( array === undefined ) array = []; + if ( offset === undefined ) offset = 0; + + array[ offset ] = this.x; + array[ offset + 1 ] = this.y; + array[ offset + 2 ] = this.z; + array[ offset + 3 ] = this.w; + + return array; + + }, + + fromAttribute: function ( attribute, index, offset ) { + + if ( offset === undefined ) offset = 0; + + index = index * attribute.itemSize + offset; + + this.x = attribute.array[ index ]; + this.y = attribute.array[ index + 1 ]; + this.z = attribute.array[ index + 2 ]; + this.w = attribute.array[ index + 3 ]; + + return this; + + }, + + clone: function () { + + return new THREE.Vector4( this.x, this.y, this.z, this.w ); + + } + +}; + +// File:src/math/Euler.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author WestLangley / http://github.com/WestLangley + * @author bhouston / http://exocortex.com + */ + +THREE.Euler = function ( x, y, z, order ) { + + this._x = x || 0; + this._y = y || 0; + this._z = z || 0; + this._order = order || THREE.Euler.DefaultOrder; + +}; + +THREE.Euler.RotationOrders = [ 'XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX' ]; + +THREE.Euler.DefaultOrder = 'XYZ'; + +THREE.Euler.prototype = { + + constructor: THREE.Euler, + + _x: 0, _y: 0, _z: 0, _order: THREE.Euler.DefaultOrder, + + get x () { + + return this._x; + + }, + + set x ( value ) { + + this._x = value; + this.onChangeCallback(); + + }, + + get y () { + + return this._y; + + }, + + set y ( value ) { + + this._y = value; + this.onChangeCallback(); + + }, + + get z () { + + return this._z; + + }, + + set z ( value ) { + + this._z = value; + this.onChangeCallback(); + + }, + + get order () { + + return this._order; + + }, + + set order ( value ) { + + this._order = value; + this.onChangeCallback(); + + }, + + set: function ( x, y, z, order ) { + + this._x = x; + this._y = y; + this._z = z; + this._order = order || this._order; + + this.onChangeCallback(); + + return this; + + }, + + copy: function ( euler ) { + + this._x = euler._x; + this._y = euler._y; + this._z = euler._z; + this._order = euler._order; + + this.onChangeCallback(); + + return this; + + }, + + setFromRotationMatrix: function ( m, order, update ) { + + var clamp = THREE.Math.clamp; + + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + + var te = m.elements; + var m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ]; + var m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ]; + var m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; + + order = order || this._order; + + if ( order === 'XYZ' ) { + + this._y = Math.asin( clamp( m13, - 1, 1 ) ); + + if ( Math.abs( m13 ) < 0.99999 ) { + + this._x = Math.atan2( - m23, m33 ); + this._z = Math.atan2( - m12, m11 ); + + } else { + + this._x = Math.atan2( m32, m22 ); + this._z = 0; + + } + + } else if ( order === 'YXZ' ) { + + this._x = Math.asin( - clamp( m23, - 1, 1 ) ); + + if ( Math.abs( m23 ) < 0.99999 ) { + + this._y = Math.atan2( m13, m33 ); + this._z = Math.atan2( m21, m22 ); + + } else { + + this._y = Math.atan2( - m31, m11 ); + this._z = 0; + + } + + } else if ( order === 'ZXY' ) { + + this._x = Math.asin( clamp( m32, - 1, 1 ) ); + + if ( Math.abs( m32 ) < 0.99999 ) { + + this._y = Math.atan2( - m31, m33 ); + this._z = Math.atan2( - m12, m22 ); + + } else { + + this._y = 0; + this._z = Math.atan2( m21, m11 ); + + } + + } else if ( order === 'ZYX' ) { + + this._y = Math.asin( - clamp( m31, - 1, 1 ) ); + + if ( Math.abs( m31 ) < 0.99999 ) { + + this._x = Math.atan2( m32, m33 ); + this._z = Math.atan2( m21, m11 ); + + } else { + + this._x = 0; + this._z = Math.atan2( - m12, m22 ); + + } + + } else if ( order === 'YZX' ) { + + this._z = Math.asin( clamp( m21, - 1, 1 ) ); + + if ( Math.abs( m21 ) < 0.99999 ) { + + this._x = Math.atan2( - m23, m22 ); + this._y = Math.atan2( - m31, m11 ); + + } else { + + this._x = 0; + this._y = Math.atan2( m13, m33 ); + + } + + } else if ( order === 'XZY' ) { + + this._z = Math.asin( - clamp( m12, - 1, 1 ) ); + + if ( Math.abs( m12 ) < 0.99999 ) { + + this._x = Math.atan2( m32, m22 ); + this._y = Math.atan2( m13, m11 ); + + } else { + + this._x = Math.atan2( - m23, m33 ); + this._y = 0; + + } + + } else { + + console.warn( 'THREE.Euler: .setFromRotationMatrix() given unsupported order: ' + order ) + + } + + this._order = order; + + if ( update !== false ) this.onChangeCallback(); + + return this; + + }, + + setFromQuaternion: function () { + + var matrix; + + return function ( q, order, update ) { + + if ( matrix === undefined ) matrix = new THREE.Matrix4(); + matrix.makeRotationFromQuaternion( q ); + this.setFromRotationMatrix( matrix, order, update ); + + return this; + + }; + + }(), + + setFromVector3: function ( v, order ) { + + return this.set( v.x, v.y, v.z, order || this._order ); + + }, + + reorder: function () { + + // WARNING: this discards revolution information -bhouston + + var q = new THREE.Quaternion(); + + return function ( newOrder ) { + + q.setFromEuler( this ); + this.setFromQuaternion( q, newOrder ); + + }; + + }(), + + equals: function ( euler ) { + + return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order ); + + }, + + fromArray: function ( array ) { + + this._x = array[ 0 ]; + this._y = array[ 1 ]; + this._z = array[ 2 ]; + if ( array[ 3 ] !== undefined ) this._order = array[ 3 ]; + + this.onChangeCallback(); + + return this; + + }, + + toArray: function () { + + return [ this._x, this._y, this._z, this._order ]; + + }, + + toVector3: function ( optionalResult ) { + + if ( optionalResult ) { + + return optionalResult.set( this._x, this._y, this._z ); + + } else { + + return new THREE.Vector3( this._x, this._y, this._z ); + + } + + }, + + onChange: function ( callback ) { + + this.onChangeCallback = callback; + + return this; + + }, + + onChangeCallback: function () {}, + + clone: function () { + + return new THREE.Euler( this._x, this._y, this._z, this._order ); + + } + +}; + +// File:src/math/Line3.js + +/** + * @author bhouston / http://exocortex.com + */ + +THREE.Line3 = function ( start, end ) { + + this.start = ( start !== undefined ) ? start : new THREE.Vector3(); + this.end = ( end !== undefined ) ? end : new THREE.Vector3(); + +}; + +THREE.Line3.prototype = { + + constructor: THREE.Line3, + + set: function ( start, end ) { + + this.start.copy( start ); + this.end.copy( end ); + + return this; + + }, + + copy: function ( line ) { + + this.start.copy( line.start ); + this.end.copy( line.end ); + + return this; + + }, + + center: function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + return result.addVectors( this.start, this.end ).multiplyScalar( 0.5 ); + + }, + + delta: function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + return result.subVectors( this.end, this.start ); + + }, + + distanceSq: function () { + + return this.start.distanceToSquared( this.end ); + + }, + + distance: function () { + + return this.start.distanceTo( this.end ); + + }, + + at: function ( t, optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + + return this.delta( result ).multiplyScalar( t ).add( this.start ); + + }, + + closestPointToPointParameter: function () { + + var startP = new THREE.Vector3(); + var startEnd = new THREE.Vector3(); + + return function ( point, clampToLine ) { + + startP.subVectors( point, this.start ); + startEnd.subVectors( this.end, this.start ); + + var startEnd2 = startEnd.dot( startEnd ); + var startEnd_startP = startEnd.dot( startP ); + + var t = startEnd_startP / startEnd2; + + if ( clampToLine ) { + + t = THREE.Math.clamp( t, 0, 1 ); + + } + + return t; + + }; + + }(), + + closestPointToPoint: function ( point, clampToLine, optionalTarget ) { + + var t = this.closestPointToPointParameter( point, clampToLine ); + + var result = optionalTarget || new THREE.Vector3(); + + return this.delta( result ).multiplyScalar( t ).add( this.start ); + + }, + + applyMatrix4: function ( matrix ) { + + this.start.applyMatrix4( matrix ); + this.end.applyMatrix4( matrix ); + + return this; + + }, + + equals: function ( line ) { + + return line.start.equals( this.start ) && line.end.equals( this.end ); + + }, + + clone: function () { + + return new THREE.Line3().copy( this ); + + } + +}; + +// File:src/math/Box2.js + +/** + * @author bhouston / http://exocortex.com + */ + +THREE.Box2 = function ( min, max ) { + + this.min = ( min !== undefined ) ? min : new THREE.Vector2( Infinity, Infinity ); + this.max = ( max !== undefined ) ? max : new THREE.Vector2( - Infinity, - Infinity ); + +}; + +THREE.Box2.prototype = { + + constructor: THREE.Box2, + + set: function ( min, max ) { + + this.min.copy( min ); + this.max.copy( max ); + + return this; + + }, + + setFromPoints: function ( points ) { + + this.makeEmpty(); + + for ( var i = 0, il = points.length; i < il; i ++ ) { + + this.expandByPoint( points[ i ] ) + + } + + return this; + + }, + + setFromCenterAndSize: function () { + + var v1 = new THREE.Vector2(); + + return function ( center, size ) { + + var halfSize = v1.copy( size ).multiplyScalar( 0.5 ); + this.min.copy( center ).sub( halfSize ); + this.max.copy( center ).add( halfSize ); + + return this; + + }; + + }(), + + copy: function ( box ) { + + this.min.copy( box.min ); + this.max.copy( box.max ); + + return this; + + }, + + makeEmpty: function () { + + this.min.x = this.min.y = Infinity; + this.max.x = this.max.y = - Infinity; + + return this; + + }, + + empty: function () { + + // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes + + return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ); + + }, + + center: function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector2(); + return result.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); + + }, + + size: function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector2(); + return result.subVectors( this.max, this.min ); + + }, + + expandByPoint: function ( point ) { + + this.min.min( point ); + this.max.max( point ); + + return this; + }, + + expandByVector: function ( vector ) { + + this.min.sub( vector ); + this.max.add( vector ); + + return this; + }, + + expandByScalar: function ( scalar ) { + + this.min.addScalar( - scalar ); + this.max.addScalar( scalar ); + + return this; + }, + + containsPoint: function ( point ) { + + if ( point.x < this.min.x || point.x > this.max.x || + point.y < this.min.y || point.y > this.max.y ) { + + return false; + + } + + return true; + + }, + + containsBox: function ( box ) { + + if ( ( this.min.x <= box.min.x ) && ( box.max.x <= this.max.x ) && + ( this.min.y <= box.min.y ) && ( box.max.y <= this.max.y ) ) { + + return true; + + } + + return false; + + }, + + getParameter: function ( point, optionalTarget ) { + + // This can potentially have a divide by zero if the box + // has a size dimension of 0. + + var result = optionalTarget || new THREE.Vector2(); + + return result.set( + ( point.x - this.min.x ) / ( this.max.x - this.min.x ), + ( point.y - this.min.y ) / ( this.max.y - this.min.y ) + ); + + }, + + isIntersectionBox: function ( box ) { + + // using 6 splitting planes to rule out intersections. + + if ( box.max.x < this.min.x || box.min.x > this.max.x || + box.max.y < this.min.y || box.min.y > this.max.y ) { + + return false; + + } + + return true; + + }, + + clampPoint: function ( point, optionalTarget ) { + + var result = optionalTarget || new THREE.Vector2(); + return result.copy( point ).clamp( this.min, this.max ); + + }, + + distanceToPoint: function () { + + var v1 = new THREE.Vector2(); + + return function ( point ) { + + var clampedPoint = v1.copy( point ).clamp( this.min, this.max ); + return clampedPoint.sub( point ).length(); + + }; + + }(), + + intersect: function ( box ) { + + this.min.max( box.min ); + this.max.min( box.max ); + + return this; + + }, + + union: function ( box ) { + + this.min.min( box.min ); + this.max.max( box.max ); + + return this; + + }, + + translate: function ( offset ) { + + this.min.add( offset ); + this.max.add( offset ); + + return this; + + }, + + equals: function ( box ) { + + return box.min.equals( this.min ) && box.max.equals( this.max ); + + }, + + clone: function () { + + return new THREE.Box2().copy( this ); + + } + +}; + +// File:src/math/Box3.js + +/** + * @author bhouston / http://exocortex.com + * @author WestLangley / http://github.com/WestLangley + */ + +THREE.Box3 = function ( min, max ) { + + this.min = ( min !== undefined ) ? min : new THREE.Vector3( Infinity, Infinity, Infinity ); + this.max = ( max !== undefined ) ? max : new THREE.Vector3( - Infinity, - Infinity, - Infinity ); + +}; + +THREE.Box3.prototype = { + + constructor: THREE.Box3, + + set: function ( min, max ) { + + this.min.copy( min ); + this.max.copy( max ); + + return this; + + }, + + setFromPoints: function ( points ) { + + this.makeEmpty(); + + for ( var i = 0, il = points.length; i < il; i ++ ) { + + this.expandByPoint( points[ i ] ) + + } + + return this; + + }, + + setFromCenterAndSize: function () { + + var v1 = new THREE.Vector3(); + + return function ( center, size ) { + + var halfSize = v1.copy( size ).multiplyScalar( 0.5 ); + + this.min.copy( center ).sub( halfSize ); + this.max.copy( center ).add( halfSize ); + + return this; + + }; + + }(), + + setFromObject: function () { + + // Computes the world-axis-aligned bounding box of an object (including its children), + // accounting for both the object's, and childrens', world transforms + + var v1 = new THREE.Vector3(); + + return function ( object ) { + + var scope = this; + + object.updateMatrixWorld( true ); + + this.makeEmpty(); + + object.traverse( function ( node ) { + + var geometry = node.geometry; + + if ( geometry !== undefined ) { + + if ( geometry instanceof THREE.Geometry ) { + + var vertices = geometry.vertices; + + for ( var i = 0, il = vertices.length; i < il; i ++ ) { + + v1.copy( vertices[ i ] ); + + v1.applyMatrix4( node.matrixWorld ); + + scope.expandByPoint( v1 ); + + } + + } else if ( geometry instanceof THREE.BufferGeometry && geometry.attributes[ 'position' ] !== undefined ) { + + var positions = geometry.attributes[ 'position' ].array; + + for ( var i = 0, il = positions.length; i < il; i += 3 ) { + + v1.set( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ); + + v1.applyMatrix4( node.matrixWorld ); + + scope.expandByPoint( v1 ); + + } + + } + + } + + } ); + + return this; + + }; + + }(), + + copy: function ( box ) { + + this.min.copy( box.min ); + this.max.copy( box.max ); + + return this; + + }, + + makeEmpty: function () { + + this.min.x = this.min.y = this.min.z = Infinity; + this.max.x = this.max.y = this.max.z = - Infinity; + + return this; + + }, + + empty: function () { + + // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes + + return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ) || ( this.max.z < this.min.z ); + + }, + + center: function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + return result.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); + + }, + + size: function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + return result.subVectors( this.max, this.min ); + + }, + + expandByPoint: function ( point ) { + + this.min.min( point ); + this.max.max( point ); + + return this; + + }, + + expandByVector: function ( vector ) { + + this.min.sub( vector ); + this.max.add( vector ); + + return this; + + }, + + expandByScalar: function ( scalar ) { + + this.min.addScalar( - scalar ); + this.max.addScalar( scalar ); + + return this; + + }, + + containsPoint: function ( point ) { + + if ( point.x < this.min.x || point.x > this.max.x || + point.y < this.min.y || point.y > this.max.y || + point.z < this.min.z || point.z > this.max.z ) { + + return false; + + } + + return true; + + }, + + containsBox: function ( box ) { + + if ( ( this.min.x <= box.min.x ) && ( box.max.x <= this.max.x ) && + ( this.min.y <= box.min.y ) && ( box.max.y <= this.max.y ) && + ( this.min.z <= box.min.z ) && ( box.max.z <= this.max.z ) ) { + + return true; + + } + + return false; + + }, + + getParameter: function ( point, optionalTarget ) { + + // This can potentially have a divide by zero if the box + // has a size dimension of 0. + + var result = optionalTarget || new THREE.Vector3(); + + return result.set( + ( point.x - this.min.x ) / ( this.max.x - this.min.x ), + ( point.y - this.min.y ) / ( this.max.y - this.min.y ), + ( point.z - this.min.z ) / ( this.max.z - this.min.z ) + ); + + }, + + isIntersectionBox: function ( box ) { + + // using 6 splitting planes to rule out intersections. + + if ( box.max.x < this.min.x || box.min.x > this.max.x || + box.max.y < this.min.y || box.min.y > this.max.y || + box.max.z < this.min.z || box.min.z > this.max.z ) { + + return false; + + } + + return true; + + }, + + clampPoint: function ( point, optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + return result.copy( point ).clamp( this.min, this.max ); + + }, + + distanceToPoint: function () { + + var v1 = new THREE.Vector3(); + + return function ( point ) { + + var clampedPoint = v1.copy( point ).clamp( this.min, this.max ); + return clampedPoint.sub( point ).length(); + + }; + + }(), + + getBoundingSphere: function () { + + var v1 = new THREE.Vector3(); + + return function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Sphere(); + + result.center = this.center(); + result.radius = this.size( v1 ).length() * 0.5; + + return result; + + }; + + }(), + + intersect: function ( box ) { + + this.min.max( box.min ); + this.max.min( box.max ); + + return this; + + }, + + union: function ( box ) { + + this.min.min( box.min ); + this.max.max( box.max ); + + return this; + + }, + + applyMatrix4: function () { + + var points = [ + new THREE.Vector3(), + new THREE.Vector3(), + new THREE.Vector3(), + new THREE.Vector3(), + new THREE.Vector3(), + new THREE.Vector3(), + new THREE.Vector3(), + new THREE.Vector3() + ]; + + return function ( matrix ) { + + // NOTE: I am using a binary pattern to specify all 2^3 combinations below + points[ 0 ].set( this.min.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 000 + points[ 1 ].set( this.min.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 001 + points[ 2 ].set( this.min.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 010 + points[ 3 ].set( this.min.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 011 + points[ 4 ].set( this.max.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 100 + points[ 5 ].set( this.max.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 101 + points[ 6 ].set( this.max.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 110 + points[ 7 ].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 111 + + this.makeEmpty(); + this.setFromPoints( points ); + + return this; + + }; + + }(), + + translate: function ( offset ) { + + this.min.add( offset ); + this.max.add( offset ); + + return this; + + }, + + equals: function ( box ) { + + return box.min.equals( this.min ) && box.max.equals( this.max ); + + }, + + clone: function () { + + return new THREE.Box3().copy( this ); + + } + +}; + +// File:src/math/Matrix3.js + +/** + * @author alteredq / http://alteredqualia.com/ + * @author WestLangley / http://github.com/WestLangley + * @author bhouston / http://exocortex.com + */ + +THREE.Matrix3 = function () { + + this.elements = new Float32Array( [ + + 1, 0, 0, + 0, 1, 0, + 0, 0, 1 + + ] ); + + if ( arguments.length > 0 ) { + + console.error( 'THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.' ); + + } + +}; + +THREE.Matrix3.prototype = { + + constructor: THREE.Matrix3, + + set: function ( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) { + + var te = this.elements; + + te[ 0 ] = n11; te[ 3 ] = n12; te[ 6 ] = n13; + te[ 1 ] = n21; te[ 4 ] = n22; te[ 7 ] = n23; + te[ 2 ] = n31; te[ 5 ] = n32; te[ 8 ] = n33; + + return this; + + }, + + identity: function () { + + this.set( + + 1, 0, 0, + 0, 1, 0, + 0, 0, 1 + + ); + + return this; + + }, + + copy: function ( m ) { + + var me = m.elements; + + this.set( + + me[ 0 ], me[ 3 ], me[ 6 ], + me[ 1 ], me[ 4 ], me[ 7 ], + me[ 2 ], me[ 5 ], me[ 8 ] + + ); + + return this; + + }, + + multiplyVector3: function ( vector ) { + + console.warn( 'THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead.' ); + return vector.applyMatrix3( this ); + + }, + + multiplyVector3Array: function ( a ) { + + console.warn( 'THREE.Matrix3: .multiplyVector3Array() has been renamed. Use matrix.applyToVector3Array( array ) instead.' ); + return this.applyToVector3Array( a ); + + }, + + applyToVector3Array: function () { + + var v1 = new THREE.Vector3(); + + return function ( array, offset, length ) { + + if ( offset === undefined ) offset = 0; + if ( length === undefined ) length = array.length; + + for ( var i = 0, j = offset, il; i < length; i += 3, j += 3 ) { + + v1.x = array[ j ]; + v1.y = array[ j + 1 ]; + v1.z = array[ j + 2 ]; + + v1.applyMatrix3( this ); + + array[ j ] = v1.x; + array[ j + 1 ] = v1.y; + array[ j + 2 ] = v1.z; + + } + + return array; + + }; + + }(), + + multiplyScalar: function ( s ) { + + var te = this.elements; + + te[ 0 ] *= s; te[ 3 ] *= s; te[ 6 ] *= s; + te[ 1 ] *= s; te[ 4 ] *= s; te[ 7 ] *= s; + te[ 2 ] *= s; te[ 5 ] *= s; te[ 8 ] *= s; + + return this; + + }, + + determinant: function () { + + var te = this.elements; + + var a = te[ 0 ], b = te[ 1 ], c = te[ 2 ], + d = te[ 3 ], e = te[ 4 ], f = te[ 5 ], + g = te[ 6 ], h = te[ 7 ], i = te[ 8 ]; + + return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g; + + }, + + getInverse: function ( matrix, throwOnInvertible ) { + + // input: THREE.Matrix4 + // ( based on http://code.google.com/p/webgl-mjs/ ) + + var me = matrix.elements; + var te = this.elements; + + te[ 0 ] = me[ 10 ] * me[ 5 ] - me[ 6 ] * me[ 9 ]; + te[ 1 ] = - me[ 10 ] * me[ 1 ] + me[ 2 ] * me[ 9 ]; + te[ 2 ] = me[ 6 ] * me[ 1 ] - me[ 2 ] * me[ 5 ]; + te[ 3 ] = - me[ 10 ] * me[ 4 ] + me[ 6 ] * me[ 8 ]; + te[ 4 ] = me[ 10 ] * me[ 0 ] - me[ 2 ] * me[ 8 ]; + te[ 5 ] = - me[ 6 ] * me[ 0 ] + me[ 2 ] * me[ 4 ]; + te[ 6 ] = me[ 9 ] * me[ 4 ] - me[ 5 ] * me[ 8 ]; + te[ 7 ] = - me[ 9 ] * me[ 0 ] + me[ 1 ] * me[ 8 ]; + te[ 8 ] = me[ 5 ] * me[ 0 ] - me[ 1 ] * me[ 4 ]; + + var det = me[ 0 ] * te[ 0 ] + me[ 1 ] * te[ 3 ] + me[ 2 ] * te[ 6 ]; + + // no inverse + + if ( det === 0 ) { + + var msg = "Matrix3.getInverse(): can't invert matrix, determinant is 0"; + + if ( throwOnInvertible || false ) { + + throw new Error( msg ); + + } else { + + console.warn( msg ); + + } + + this.identity(); + + return this; + + } + + this.multiplyScalar( 1.0 / det ); + + return this; + + }, + + transpose: function () { + + var tmp, m = this.elements; + + tmp = m[ 1 ]; m[ 1 ] = m[ 3 ]; m[ 3 ] = tmp; + tmp = m[ 2 ]; m[ 2 ] = m[ 6 ]; m[ 6 ] = tmp; + tmp = m[ 5 ]; m[ 5 ] = m[ 7 ]; m[ 7 ] = tmp; + + return this; + + }, + + flattenToArrayOffset: function ( array, offset ) { + + var te = this.elements; + + array[ offset ] = te[ 0 ]; + array[ offset + 1 ] = te[ 1 ]; + array[ offset + 2 ] = te[ 2 ]; + + array[ offset + 3 ] = te[ 3 ]; + array[ offset + 4 ] = te[ 4 ]; + array[ offset + 5 ] = te[ 5 ]; + + array[ offset + 6 ] = te[ 6 ]; + array[ offset + 7 ] = te[ 7 ]; + array[ offset + 8 ] = te[ 8 ]; + + return array; + + }, + + getNormalMatrix: function ( m ) { + + // input: THREE.Matrix4 + + this.getInverse( m ).transpose(); + + return this; + + }, + + transposeIntoArray: function ( r ) { + + var m = this.elements; + + r[ 0 ] = m[ 0 ]; + r[ 1 ] = m[ 3 ]; + r[ 2 ] = m[ 6 ]; + r[ 3 ] = m[ 1 ]; + r[ 4 ] = m[ 4 ]; + r[ 5 ] = m[ 7 ]; + r[ 6 ] = m[ 2 ]; + r[ 7 ] = m[ 5 ]; + r[ 8 ] = m[ 8 ]; + + return this; + + }, + + fromArray: function ( array ) { + + this.elements.set( array ); + + return this; + + }, + + toArray: function () { + + var te = this.elements; + + return [ + te[ 0 ], te[ 1 ], te[ 2 ], + te[ 3 ], te[ 4 ], te[ 5 ], + te[ 6 ], te[ 7 ], te[ 8 ] + ]; + + }, + + clone: function () { + + return new THREE.Matrix3().fromArray( this.elements ); + + } + +}; + +// File:src/math/Matrix4.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author supereggbert / http://www.paulbrunt.co.uk/ + * @author philogb / http://blog.thejit.org/ + * @author jordi_ros / http://plattsoft.com + * @author D1plo1d / http://github.com/D1plo1d + * @author alteredq / http://alteredqualia.com/ + * @author mikael emtinger / http://gomo.se/ + * @author timknip / http://www.floorplanner.com/ + * @author bhouston / http://exocortex.com + * @author WestLangley / http://github.com/WestLangley + */ + +THREE.Matrix4 = function () { + + this.elements = new Float32Array( [ + + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + + ] ); + + if ( arguments.length > 0 ) { + + console.error( 'THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.' ); + + } + +}; + +THREE.Matrix4.prototype = { + + constructor: THREE.Matrix4, + + set: function ( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) { + + var te = this.elements; + + te[ 0 ] = n11; te[ 4 ] = n12; te[ 8 ] = n13; te[ 12 ] = n14; + te[ 1 ] = n21; te[ 5 ] = n22; te[ 9 ] = n23; te[ 13 ] = n24; + te[ 2 ] = n31; te[ 6 ] = n32; te[ 10 ] = n33; te[ 14 ] = n34; + te[ 3 ] = n41; te[ 7 ] = n42; te[ 11 ] = n43; te[ 15 ] = n44; + + return this; + + }, + + identity: function () { + + this.set( + + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + + ); + + return this; + + }, + + copy: function ( m ) { + + this.elements.set( m.elements ); + + return this; + + }, + + extractPosition: function ( m ) { + + console.warn( 'THREE.Matrix4: .extractPosition() has been renamed to .copyPosition().' ); + return this.copyPosition( m ); + + }, + + copyPosition: function ( m ) { + + var te = this.elements; + var me = m.elements; + + te[ 12 ] = me[ 12 ]; + te[ 13 ] = me[ 13 ]; + te[ 14 ] = me[ 14 ]; + + return this; + + }, + + extractBasis: function ( xAxis, yAxis, zAxis ) { + + var te = this.elements; + + xAxis.set( te[ 0 ], te[ 1 ], te[ 2 ] ); + yAxis.set( te[ 4 ], te[ 5 ], te[ 6 ] ); + zAxis.set( te[ 8 ], te[ 9 ], te[ 10 ] ); + + return this; + + }, + + makeBasis: function ( xAxis, yAxis, zAxis ) { + + this.set( + xAxis.x, yAxis.x, zAxis.x, 0, + xAxis.y, yAxis.y, zAxis.y, 0, + xAxis.z, yAxis.z, zAxis.z, 0, + 0, 0, 0, 1 + ); + + return this; + + }, + + extractRotation: function () { + + var v1 = new THREE.Vector3(); + + return function ( m ) { + + var te = this.elements; + var me = m.elements; + + var scaleX = 1 / v1.set( me[ 0 ], me[ 1 ], me[ 2 ] ).length(); + var scaleY = 1 / v1.set( me[ 4 ], me[ 5 ], me[ 6 ] ).length(); + var scaleZ = 1 / v1.set( me[ 8 ], me[ 9 ], me[ 10 ] ).length(); + + te[ 0 ] = me[ 0 ] * scaleX; + te[ 1 ] = me[ 1 ] * scaleX; + te[ 2 ] = me[ 2 ] * scaleX; + + te[ 4 ] = me[ 4 ] * scaleY; + te[ 5 ] = me[ 5 ] * scaleY; + te[ 6 ] = me[ 6 ] * scaleY; + + te[ 8 ] = me[ 8 ] * scaleZ; + te[ 9 ] = me[ 9 ] * scaleZ; + te[ 10 ] = me[ 10 ] * scaleZ; + + return this; + + }; + + }(), + + makeRotationFromEuler: function ( euler ) { + + if ( euler instanceof THREE.Euler === false ) { + + console.error( 'THREE.Matrix: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.' ); + + } + + var te = this.elements; + + var x = euler.x, y = euler.y, z = euler.z; + var a = Math.cos( x ), b = Math.sin( x ); + var c = Math.cos( y ), d = Math.sin( y ); + var e = Math.cos( z ), f = Math.sin( z ); + + if ( euler.order === 'XYZ' ) { + + var ae = a * e, af = a * f, be = b * e, bf = b * f; + + te[ 0 ] = c * e; + te[ 4 ] = - c * f; + te[ 8 ] = d; + + te[ 1 ] = af + be * d; + te[ 5 ] = ae - bf * d; + te[ 9 ] = - b * c; + + te[ 2 ] = bf - ae * d; + te[ 6 ] = be + af * d; + te[ 10 ] = a * c; + + } else if ( euler.order === 'YXZ' ) { + + var ce = c * e, cf = c * f, de = d * e, df = d * f; + + te[ 0 ] = ce + df * b; + te[ 4 ] = de * b - cf; + te[ 8 ] = a * d; + + te[ 1 ] = a * f; + te[ 5 ] = a * e; + te[ 9 ] = - b; + + te[ 2 ] = cf * b - de; + te[ 6 ] = df + ce * b; + te[ 10 ] = a * c; + + } else if ( euler.order === 'ZXY' ) { + + var ce = c * e, cf = c * f, de = d * e, df = d * f; + + te[ 0 ] = ce - df * b; + te[ 4 ] = - a * f; + te[ 8 ] = de + cf * b; + + te[ 1 ] = cf + de * b; + te[ 5 ] = a * e; + te[ 9 ] = df - ce * b; + + te[ 2 ] = - a * d; + te[ 6 ] = b; + te[ 10 ] = a * c; + + } else if ( euler.order === 'ZYX' ) { + + var ae = a * e, af = a * f, be = b * e, bf = b * f; + + te[ 0 ] = c * e; + te[ 4 ] = be * d - af; + te[ 8 ] = ae * d + bf; + + te[ 1 ] = c * f; + te[ 5 ] = bf * d + ae; + te[ 9 ] = af * d - be; + + te[ 2 ] = - d; + te[ 6 ] = b * c; + te[ 10 ] = a * c; + + } else if ( euler.order === 'YZX' ) { + + var ac = a * c, ad = a * d, bc = b * c, bd = b * d; + + te[ 0 ] = c * e; + te[ 4 ] = bd - ac * f; + te[ 8 ] = bc * f + ad; + + te[ 1 ] = f; + te[ 5 ] = a * e; + te[ 9 ] = - b * e; + + te[ 2 ] = - d * e; + te[ 6 ] = ad * f + bc; + te[ 10 ] = ac - bd * f; + + } else if ( euler.order === 'XZY' ) { + + var ac = a * c, ad = a * d, bc = b * c, bd = b * d; + + te[ 0 ] = c * e; + te[ 4 ] = - f; + te[ 8 ] = d * e; + + te[ 1 ] = ac * f + bd; + te[ 5 ] = a * e; + te[ 9 ] = ad * f - bc; + + te[ 2 ] = bc * f - ad; + te[ 6 ] = b * e; + te[ 10 ] = bd * f + ac; + + } + + // last column + te[ 3 ] = 0; + te[ 7 ] = 0; + te[ 11 ] = 0; + + // bottom row + te[ 12 ] = 0; + te[ 13 ] = 0; + te[ 14 ] = 0; + te[ 15 ] = 1; + + return this; + + }, + + setRotationFromQuaternion: function ( q ) { + + console.warn( 'THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion().' ); + + return this.makeRotationFromQuaternion( q ); + + }, + + makeRotationFromQuaternion: function ( q ) { + + var te = this.elements; + + var x = q.x, y = q.y, z = q.z, w = q.w; + var x2 = x + x, y2 = y + y, z2 = z + z; + var xx = x * x2, xy = x * y2, xz = x * z2; + var yy = y * y2, yz = y * z2, zz = z * z2; + var wx = w * x2, wy = w * y2, wz = w * z2; + + te[ 0 ] = 1 - ( yy + zz ); + te[ 4 ] = xy - wz; + te[ 8 ] = xz + wy; + + te[ 1 ] = xy + wz; + te[ 5 ] = 1 - ( xx + zz ); + te[ 9 ] = yz - wx; + + te[ 2 ] = xz - wy; + te[ 6 ] = yz + wx; + te[ 10 ] = 1 - ( xx + yy ); + + // last column + te[ 3 ] = 0; + te[ 7 ] = 0; + te[ 11 ] = 0; + + // bottom row + te[ 12 ] = 0; + te[ 13 ] = 0; + te[ 14 ] = 0; + te[ 15 ] = 1; + + return this; + + }, + + lookAt: function () { + + var x = new THREE.Vector3(); + var y = new THREE.Vector3(); + var z = new THREE.Vector3(); + + return function ( eye, target, up ) { + + var te = this.elements; + + z.subVectors( eye, target ).normalize(); + + if ( z.length() === 0 ) { + + z.z = 1; + + } + + x.crossVectors( up, z ).normalize(); + + if ( x.length() === 0 ) { + + z.x += 0.0001; + x.crossVectors( up, z ).normalize(); + + } + + y.crossVectors( z, x ); + + + te[ 0 ] = x.x; te[ 4 ] = y.x; te[ 8 ] = z.x; + te[ 1 ] = x.y; te[ 5 ] = y.y; te[ 9 ] = z.y; + te[ 2 ] = x.z; te[ 6 ] = y.z; te[ 10 ] = z.z; + + return this; + + }; + + }(), + + multiply: function ( m, n ) { + + if ( n !== undefined ) { + + console.warn( 'THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.' ); + return this.multiplyMatrices( m, n ); + + } + + return this.multiplyMatrices( this, m ); + + }, + + multiplyMatrices: function ( a, b ) { + + var ae = a.elements; + var be = b.elements; + var te = this.elements; + + var a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ]; + var a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ]; + var a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ]; + var a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ]; + + var b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ]; + var b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ]; + var b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ]; + var b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ]; + + te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41; + te[ 4 ] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42; + te[ 8 ] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43; + te[ 12 ] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44; + + te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41; + te[ 5 ] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42; + te[ 9 ] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43; + te[ 13 ] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44; + + te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41; + te[ 6 ] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42; + te[ 10 ] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43; + te[ 14 ] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44; + + te[ 3 ] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41; + te[ 7 ] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42; + te[ 11 ] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43; + te[ 15 ] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44; + + return this; + + }, + + multiplyToArray: function ( a, b, r ) { + + var te = this.elements; + + this.multiplyMatrices( a, b ); + + r[ 0 ] = te[ 0 ]; r[ 1 ] = te[ 1 ]; r[ 2 ] = te[ 2 ]; r[ 3 ] = te[ 3 ]; + r[ 4 ] = te[ 4 ]; r[ 5 ] = te[ 5 ]; r[ 6 ] = te[ 6 ]; r[ 7 ] = te[ 7 ]; + r[ 8 ] = te[ 8 ]; r[ 9 ] = te[ 9 ]; r[ 10 ] = te[ 10 ]; r[ 11 ] = te[ 11 ]; + r[ 12 ] = te[ 12 ]; r[ 13 ] = te[ 13 ]; r[ 14 ] = te[ 14 ]; r[ 15 ] = te[ 15 ]; + + return this; + + }, + + multiplyScalar: function ( s ) { + + var te = this.elements; + + te[ 0 ] *= s; te[ 4 ] *= s; te[ 8 ] *= s; te[ 12 ] *= s; + te[ 1 ] *= s; te[ 5 ] *= s; te[ 9 ] *= s; te[ 13 ] *= s; + te[ 2 ] *= s; te[ 6 ] *= s; te[ 10 ] *= s; te[ 14 ] *= s; + te[ 3 ] *= s; te[ 7 ] *= s; te[ 11 ] *= s; te[ 15 ] *= s; + + return this; + + }, + + multiplyVector3: function ( vector ) { + + console.warn( 'THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) or vector.applyProjection( matrix ) instead.' ); + return vector.applyProjection( this ); + + }, + + multiplyVector4: function ( vector ) { + + console.warn( 'THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); + return vector.applyMatrix4( this ); + + }, + + multiplyVector3Array: function ( a ) { + + console.warn( 'THREE.Matrix4: .multiplyVector3Array() has been renamed. Use matrix.applyToVector3Array( array ) instead.' ); + return this.applyToVector3Array( a ); + + }, + + applyToVector3Array: function () { + + var v1 = new THREE.Vector3(); + + return function ( array, offset, length ) { + + if ( offset === undefined ) offset = 0; + if ( length === undefined ) length = array.length; + + for ( var i = 0, j = offset, il; i < length; i += 3, j += 3 ) { + + v1.x = array[ j ]; + v1.y = array[ j + 1 ]; + v1.z = array[ j + 2 ]; + + v1.applyMatrix4( this ); + + array[ j ] = v1.x; + array[ j + 1 ] = v1.y; + array[ j + 2 ] = v1.z; + + } + + return array; + + }; + + }(), + + rotateAxis: function ( v ) { + + console.warn( 'THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead.' ); + + v.transformDirection( this ); + + }, + + crossVector: function ( vector ) { + + console.warn( 'THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); + return vector.applyMatrix4( this ); + + }, + + determinant: function () { + + var te = this.elements; + + var n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ]; + var n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ]; + var n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ]; + var n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ]; + + //TODO: make this more efficient + //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm ) + + return ( + n41 * ( + + n14 * n23 * n32 + - n13 * n24 * n32 + - n14 * n22 * n33 + + n12 * n24 * n33 + + n13 * n22 * n34 + - n12 * n23 * n34 + ) + + n42 * ( + + n11 * n23 * n34 + - n11 * n24 * n33 + + n14 * n21 * n33 + - n13 * n21 * n34 + + n13 * n24 * n31 + - n14 * n23 * n31 + ) + + n43 * ( + + n11 * n24 * n32 + - n11 * n22 * n34 + - n14 * n21 * n32 + + n12 * n21 * n34 + + n14 * n22 * n31 + - n12 * n24 * n31 + ) + + n44 * ( + - n13 * n22 * n31 + - n11 * n23 * n32 + + n11 * n22 * n33 + + n13 * n21 * n32 + - n12 * n21 * n33 + + n12 * n23 * n31 + ) + + ); + + }, + + transpose: function () { + + var te = this.elements; + var tmp; + + tmp = te[ 1 ]; te[ 1 ] = te[ 4 ]; te[ 4 ] = tmp; + tmp = te[ 2 ]; te[ 2 ] = te[ 8 ]; te[ 8 ] = tmp; + tmp = te[ 6 ]; te[ 6 ] = te[ 9 ]; te[ 9 ] = tmp; + + tmp = te[ 3 ]; te[ 3 ] = te[ 12 ]; te[ 12 ] = tmp; + tmp = te[ 7 ]; te[ 7 ] = te[ 13 ]; te[ 13 ] = tmp; + tmp = te[ 11 ]; te[ 11 ] = te[ 14 ]; te[ 14 ] = tmp; + + return this; + + }, + + flattenToArrayOffset: function ( array, offset ) { + + var te = this.elements; + + array[ offset ] = te[ 0 ]; + array[ offset + 1 ] = te[ 1 ]; + array[ offset + 2 ] = te[ 2 ]; + array[ offset + 3 ] = te[ 3 ]; + + array[ offset + 4 ] = te[ 4 ]; + array[ offset + 5 ] = te[ 5 ]; + array[ offset + 6 ] = te[ 6 ]; + array[ offset + 7 ] = te[ 7 ]; + + array[ offset + 8 ] = te[ 8 ]; + array[ offset + 9 ] = te[ 9 ]; + array[ offset + 10 ] = te[ 10 ]; + array[ offset + 11 ] = te[ 11 ]; + + array[ offset + 12 ] = te[ 12 ]; + array[ offset + 13 ] = te[ 13 ]; + array[ offset + 14 ] = te[ 14 ]; + array[ offset + 15 ] = te[ 15 ]; + + return array; + + }, + + getPosition: function () { + + var v1 = new THREE.Vector3(); + + return function () { + + console.warn( 'THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead.' ); + + var te = this.elements; + return v1.set( te[ 12 ], te[ 13 ], te[ 14 ] ); + + }; + + }(), + + setPosition: function ( v ) { + + var te = this.elements; + + te[ 12 ] = v.x; + te[ 13 ] = v.y; + te[ 14 ] = v.z; + + return this; + + }, + + getInverse: function ( m, throwOnInvertible ) { + + // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm + var te = this.elements; + var me = m.elements; + + var n11 = me[ 0 ], n12 = me[ 4 ], n13 = me[ 8 ], n14 = me[ 12 ]; + var n21 = me[ 1 ], n22 = me[ 5 ], n23 = me[ 9 ], n24 = me[ 13 ]; + var n31 = me[ 2 ], n32 = me[ 6 ], n33 = me[ 10 ], n34 = me[ 14 ]; + var n41 = me[ 3 ], n42 = me[ 7 ], n43 = me[ 11 ], n44 = me[ 15 ]; + + te[ 0 ] = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44; + te[ 4 ] = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44; + te[ 8 ] = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44; + te[ 12 ] = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34; + te[ 1 ] = n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44; + te[ 5 ] = n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44; + te[ 9 ] = n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44; + te[ 13 ] = n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34; + te[ 2 ] = n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44; + te[ 6 ] = n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44; + te[ 10 ] = n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44; + te[ 14 ] = n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34; + te[ 3 ] = n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43; + te[ 7 ] = n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43; + te[ 11 ] = n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43; + te[ 15 ] = n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33; + + var det = n11 * te[ 0 ] + n21 * te[ 4 ] + n31 * te[ 8 ] + n41 * te[ 12 ]; + + if ( det == 0 ) { + + var msg = "Matrix4.getInverse(): can't invert matrix, determinant is 0"; + + if ( throwOnInvertible || false ) { + + throw new Error( msg ); + + } else { + + console.warn( msg ); + + } + + this.identity(); + + return this; + } + + this.multiplyScalar( 1 / det ); + + return this; + + }, + + translate: function ( v ) { + + console.warn( 'THREE.Matrix4: .translate() has been removed.' ); + + }, + + rotateX: function ( angle ) { + + console.warn( 'THREE.Matrix4: .rotateX() has been removed.' ); + + }, + + rotateY: function ( angle ) { + + console.warn( 'THREE.Matrix4: .rotateY() has been removed.' ); + + }, + + rotateZ: function ( angle ) { + + console.warn( 'THREE.Matrix4: .rotateZ() has been removed.' ); + + }, + + rotateByAxis: function ( axis, angle ) { + + console.warn( 'THREE.Matrix4: .rotateByAxis() has been removed.' ); + + }, + + scale: function ( v ) { + + var te = this.elements; + var x = v.x, y = v.y, z = v.z; + + te[ 0 ] *= x; te[ 4 ] *= y; te[ 8 ] *= z; + te[ 1 ] *= x; te[ 5 ] *= y; te[ 9 ] *= z; + te[ 2 ] *= x; te[ 6 ] *= y; te[ 10 ] *= z; + te[ 3 ] *= x; te[ 7 ] *= y; te[ 11 ] *= z; + + return this; + + }, + + getMaxScaleOnAxis: function () { + + var te = this.elements; + + var scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ]; + var scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ]; + var scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ]; + + return Math.sqrt( Math.max( scaleXSq, Math.max( scaleYSq, scaleZSq ) ) ); + + }, + + makeTranslation: function ( x, y, z ) { + + this.set( + + 1, 0, 0, x, + 0, 1, 0, y, + 0, 0, 1, z, + 0, 0, 0, 1 + + ); + + return this; + + }, + + makeRotationX: function ( theta ) { + + var c = Math.cos( theta ), s = Math.sin( theta ); + + this.set( + + 1, 0, 0, 0, + 0, c, - s, 0, + 0, s, c, 0, + 0, 0, 0, 1 + + ); + + return this; + + }, + + makeRotationY: function ( theta ) { + + var c = Math.cos( theta ), s = Math.sin( theta ); + + this.set( + + c, 0, s, 0, + 0, 1, 0, 0, + - s, 0, c, 0, + 0, 0, 0, 1 + + ); + + return this; + + }, + + makeRotationZ: function ( theta ) { + + var c = Math.cos( theta ), s = Math.sin( theta ); + + this.set( + + c, - s, 0, 0, + s, c, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + + ); + + return this; + + }, + + makeRotationAxis: function ( axis, angle ) { + + // Based on http://www.gamedev.net/reference/articles/article1199.asp + + var c = Math.cos( angle ); + var s = Math.sin( angle ); + var t = 1 - c; + var x = axis.x, y = axis.y, z = axis.z; + var tx = t * x, ty = t * y; + + this.set( + + tx * x + c, tx * y - s * z, tx * z + s * y, 0, + tx * y + s * z, ty * y + c, ty * z - s * x, 0, + tx * z - s * y, ty * z + s * x, t * z * z + c, 0, + 0, 0, 0, 1 + + ); + + return this; + + }, + + makeScale: function ( x, y, z ) { + + this.set( + + x, 0, 0, 0, + 0, y, 0, 0, + 0, 0, z, 0, + 0, 0, 0, 1 + + ); + + return this; + + }, + + compose: function ( position, quaternion, scale ) { + + this.makeRotationFromQuaternion( quaternion ); + this.scale( scale ); + this.setPosition( position ); + + return this; + + }, + + decompose: function () { + + var vector = new THREE.Vector3(); + var matrix = new THREE.Matrix4(); + + return function ( position, quaternion, scale ) { + + var te = this.elements; + + var sx = vector.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length(); + var sy = vector.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length(); + var sz = vector.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length(); + + // if determine is negative, we need to invert one scale + var det = this.determinant(); + if ( det < 0 ) { + sx = - sx; + } + + position.x = te[ 12 ]; + position.y = te[ 13 ]; + position.z = te[ 14 ]; + + // scale the rotation part + + matrix.elements.set( this.elements ); // at this point matrix is incomplete so we can't use .copy() + + var invSX = 1 / sx; + var invSY = 1 / sy; + var invSZ = 1 / sz; + + matrix.elements[ 0 ] *= invSX; + matrix.elements[ 1 ] *= invSX; + matrix.elements[ 2 ] *= invSX; + + matrix.elements[ 4 ] *= invSY; + matrix.elements[ 5 ] *= invSY; + matrix.elements[ 6 ] *= invSY; + + matrix.elements[ 8 ] *= invSZ; + matrix.elements[ 9 ] *= invSZ; + matrix.elements[ 10 ] *= invSZ; + + quaternion.setFromRotationMatrix( matrix ); + + scale.x = sx; + scale.y = sy; + scale.z = sz; + + return this; + + }; + + }(), + + makeFrustum: function ( left, right, bottom, top, near, far ) { + + var te = this.elements; + var x = 2 * near / ( right - left ); + var y = 2 * near / ( top - bottom ); + + var a = ( right + left ) / ( right - left ); + var b = ( top + bottom ) / ( top - bottom ); + var c = - ( far + near ) / ( far - near ); + var d = - 2 * far * near / ( far - near ); + + te[ 0 ] = x; te[ 4 ] = 0; te[ 8 ] = a; te[ 12 ] = 0; + te[ 1 ] = 0; te[ 5 ] = y; te[ 9 ] = b; te[ 13 ] = 0; + te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = c; te[ 14 ] = d; + te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = - 1; te[ 15 ] = 0; + + return this; + + }, + + makePerspective: function ( fov, aspect, near, far ) { + + var ymax = near * Math.tan( THREE.Math.degToRad( fov * 0.5 ) ); + var ymin = - ymax; + var xmin = ymin * aspect; + var xmax = ymax * aspect; + + return this.makeFrustum( xmin, xmax, ymin, ymax, near, far ); + + }, + + makeOrthographic: function ( left, right, top, bottom, near, far ) { + + var te = this.elements; + var w = right - left; + var h = top - bottom; + var p = far - near; + + var x = ( right + left ) / w; + var y = ( top + bottom ) / h; + var z = ( far + near ) / p; + + te[ 0 ] = 2 / w; te[ 4 ] = 0; te[ 8 ] = 0; te[ 12 ] = - x; + te[ 1 ] = 0; te[ 5 ] = 2 / h; te[ 9 ] = 0; te[ 13 ] = - y; + te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = - 2 / p; te[ 14 ] = - z; + te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; te[ 15 ] = 1; + + return this; + + }, + + fromArray: function ( array ) { + + this.elements.set( array ); + + return this; + + }, + + toArray: function () { + + var te = this.elements; + + return [ + te[ 0 ], te[ 1 ], te[ 2 ], te[ 3 ], + te[ 4 ], te[ 5 ], te[ 6 ], te[ 7 ], + te[ 8 ], te[ 9 ], te[ 10 ], te[ 11 ], + te[ 12 ], te[ 13 ], te[ 14 ], te[ 15 ] + ]; + + }, + + clone: function () { + + return new THREE.Matrix4().fromArray( this.elements ); + + } + +}; + +// File:src/math/Ray.js + +/** + * @author bhouston / http://exocortex.com + */ + +THREE.Ray = function ( origin, direction ) { + + this.origin = ( origin !== undefined ) ? origin : new THREE.Vector3(); + this.direction = ( direction !== undefined ) ? direction : new THREE.Vector3(); + +}; + +THREE.Ray.prototype = { + + constructor: THREE.Ray, + + set: function ( origin, direction ) { + + this.origin.copy( origin ); + this.direction.copy( direction ); + + return this; + + }, + + copy: function ( ray ) { + + this.origin.copy( ray.origin ); + this.direction.copy( ray.direction ); + + return this; + + }, + + at: function ( t, optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + + return result.copy( this.direction ).multiplyScalar( t ).add( this.origin ); + + }, + + recast: function () { + + var v1 = new THREE.Vector3(); + + return function ( t ) { + + this.origin.copy( this.at( t, v1 ) ); + + return this; + + }; + + }(), + + closestPointToPoint: function ( point, optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + result.subVectors( point, this.origin ); + var directionDistance = result.dot( this.direction ); + + if ( directionDistance < 0 ) { + + return result.copy( this.origin ); + + } + + return result.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); + + }, + + distanceToPoint: function () { + + var v1 = new THREE.Vector3(); + + return function ( point ) { + + var directionDistance = v1.subVectors( point, this.origin ).dot( this.direction ); + + // point behind the ray + + if ( directionDistance < 0 ) { + + return this.origin.distanceTo( point ); + + } + + v1.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); + + return v1.distanceTo( point ); + + }; + + }(), + + distanceSqToSegment: function () { + + var segCenter = new THREE.Vector3(); + var segDir = new THREE.Vector3(); + var diff = new THREE.Vector3(); + + return function ( v0, v1, optionalPointOnRay, optionalPointOnSegment ) { + + // from http://www.geometrictools.com/LibMathematics/Distance/Wm5DistRay3Segment3.cpp + // It returns the min distance between the ray and the segment + // defined by v0 and v1 + // It can also set two optional targets : + // - The closest point on the ray + // - The closest point on the segment + + segCenter.copy( v0 ).add( v1 ).multiplyScalar( 0.5 ); + segDir.copy( v1 ).sub( v0 ).normalize(); + diff.copy( this.origin ).sub( segCenter ); + + var segExtent = v0.distanceTo( v1 ) * 0.5; + var a01 = - this.direction.dot( segDir ); + var b0 = diff.dot( this.direction ); + var b1 = - diff.dot( segDir ); + var c = diff.lengthSq(); + var det = Math.abs( 1 - a01 * a01 ); + var s0, s1, sqrDist, extDet; + + if ( det > 0 ) { + + // The ray and segment are not parallel. + + s0 = a01 * b1 - b0; + s1 = a01 * b0 - b1; + extDet = segExtent * det; + + if ( s0 >= 0 ) { + + if ( s1 >= - extDet ) { + + if ( s1 <= extDet ) { + + // region 0 + // Minimum at interior points of ray and segment. + + var invDet = 1 / det; + s0 *= invDet; + s1 *= invDet; + sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c; + + } else { + + // region 1 + + s1 = segExtent; + s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); + sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; + + } + + } else { + + // region 5 + + s1 = - segExtent; + s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); + sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; + + } + + } else { + + if ( s1 <= - extDet ) { + + // region 4 + + s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) ); + s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); + sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; + + } else if ( s1 <= extDet ) { + + // region 3 + + s0 = 0; + s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent ); + sqrDist = s1 * ( s1 + 2 * b1 ) + c; + + } else { + + // region 2 + + s0 = Math.max( 0, - ( a01 * segExtent + b0 ) ); + s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); + sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; + + } + + } + + } else { + + // Ray and segment are parallel. + + s1 = ( a01 > 0 ) ? - segExtent : segExtent; + s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); + sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; + + } + + if ( optionalPointOnRay ) { + + optionalPointOnRay.copy( this.direction ).multiplyScalar( s0 ).add( this.origin ); + + } + + if ( optionalPointOnSegment ) { + + optionalPointOnSegment.copy( segDir ).multiplyScalar( s1 ).add( segCenter ); + + } + + return sqrDist; + + }; + + }(), + + + isIntersectionSphere: function ( sphere ) { + + return this.distanceToPoint( sphere.center ) <= sphere.radius; + + }, + + intersectSphere: function () { + + // from http://www.scratchapixel.com/lessons/3d-basic-lessons/lesson-7-intersecting-simple-shapes/ray-sphere-intersection/ + + var v1 = new THREE.Vector3(); + + return function ( sphere, optionalTarget ) { + + v1.subVectors( sphere.center, this.origin ); + + var tca = v1.dot( this.direction ); + + var d2 = v1.dot( v1 ) - tca * tca; + + var radius2 = sphere.radius * sphere.radius; + + if ( d2 > radius2 ) return null; + + var thc = Math.sqrt( radius2 - d2 ); + + // t0 = first intersect point - entrance on front of sphere + var t0 = tca - thc; + + // t1 = second intersect point - exit point on back of sphere + var t1 = tca + thc; + + // test to see if both t0 and t1 are behind the ray - if so, return null + if ( t0 < 0 && t1 < 0 ) return null; + + // test to see if t0 is behind the ray: + // if it is, the ray is inside the sphere, so return the second exit point scaled by t1, + // in order to always return an intersect point that is in front of the ray. + if ( t0 < 0 ) return this.at( t1, optionalTarget ); + + // else t0 is in front of the ray, so return the first collision point scaled by t0 + return this.at( t0, optionalTarget ); + + } + + }(), + + isIntersectionPlane: function ( plane ) { + + // check if the ray lies on the plane first + + var distToPoint = plane.distanceToPoint( this.origin ); + + if ( distToPoint === 0 ) { + + return true; + + } + + var denominator = plane.normal.dot( this.direction ); + + if ( denominator * distToPoint < 0 ) { + + return true; + + } + + // ray origin is behind the plane (and is pointing behind it) + + return false; + + }, + + distanceToPlane: function ( plane ) { + + var denominator = plane.normal.dot( this.direction ); + if ( denominator == 0 ) { + + // line is coplanar, return origin + if ( plane.distanceToPoint( this.origin ) == 0 ) { + + return 0; + + } + + // Null is preferable to undefined since undefined means.... it is undefined + + return null; + + } + + var t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator; + + // Return if the ray never intersects the plane + + return t >= 0 ? t : null; + + }, + + intersectPlane: function ( plane, optionalTarget ) { + + var t = this.distanceToPlane( plane ); + + if ( t === null ) { + + return null; + } + + return this.at( t, optionalTarget ); + + }, + + isIntersectionBox: function () { + + var v = new THREE.Vector3(); + + return function ( box ) { + + return this.intersectBox( box, v ) !== null; + + }; + + }(), + + intersectBox: function ( box , optionalTarget ) { + + // http://www.scratchapixel.com/lessons/3d-basic-lessons/lesson-7-intersecting-simple-shapes/ray-box-intersection/ + + var tmin,tmax,tymin,tymax,tzmin,tzmax; + + var invdirx = 1 / this.direction.x, + invdiry = 1 / this.direction.y, + invdirz = 1 / this.direction.z; + + var origin = this.origin; + + if ( invdirx >= 0 ) { + + tmin = ( box.min.x - origin.x ) * invdirx; + tmax = ( box.max.x - origin.x ) * invdirx; + + } else { + + tmin = ( box.max.x - origin.x ) * invdirx; + tmax = ( box.min.x - origin.x ) * invdirx; + } + + if ( invdiry >= 0 ) { + + tymin = ( box.min.y - origin.y ) * invdiry; + tymax = ( box.max.y - origin.y ) * invdiry; + + } else { + + tymin = ( box.max.y - origin.y ) * invdiry; + tymax = ( box.min.y - origin.y ) * invdiry; + } + + if ( ( tmin > tymax ) || ( tymin > tmax ) ) return null; + + // These lines also handle the case where tmin or tmax is NaN + // (result of 0 * Infinity). x !== x returns true if x is NaN + + if ( tymin > tmin || tmin !== tmin ) tmin = tymin; + + if ( tymax < tmax || tmax !== tmax ) tmax = tymax; + + if ( invdirz >= 0 ) { + + tzmin = ( box.min.z - origin.z ) * invdirz; + tzmax = ( box.max.z - origin.z ) * invdirz; + + } else { + + tzmin = ( box.max.z - origin.z ) * invdirz; + tzmax = ( box.min.z - origin.z ) * invdirz; + } + + if ( ( tmin > tzmax ) || ( tzmin > tmax ) ) return null; + + if ( tzmin > tmin || tmin !== tmin ) tmin = tzmin; + + if ( tzmax < tmax || tmax !== tmax ) tmax = tzmax; + + //return point closest to the ray (positive side) + + if ( tmax < 0 ) return null; + + return this.at( tmin >= 0 ? tmin : tmax, optionalTarget ); + + }, + + intersectTriangle: function () { + + // Compute the offset origin, edges, and normal. + var diff = new THREE.Vector3(); + var edge1 = new THREE.Vector3(); + var edge2 = new THREE.Vector3(); + var normal = new THREE.Vector3(); + + return function ( a, b, c, backfaceCulling, optionalTarget ) { + + // from http://www.geometrictools.com/LibMathematics/Intersection/Wm5IntrRay3Triangle3.cpp + + edge1.subVectors( b, a ); + edge2.subVectors( c, a ); + normal.crossVectors( edge1, edge2 ); + + // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction, + // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by + // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2)) + // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q)) + // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N) + var DdN = this.direction.dot( normal ); + var sign; + + if ( DdN > 0 ) { + + if ( backfaceCulling ) return null; + sign = 1; + + } else if ( DdN < 0 ) { + + sign = - 1; + DdN = - DdN; + + } else { + + return null; + + } + + diff.subVectors( this.origin, a ); + var DdQxE2 = sign * this.direction.dot( edge2.crossVectors( diff, edge2 ) ); + + // b1 < 0, no intersection + if ( DdQxE2 < 0 ) { + + return null; + + } + + var DdE1xQ = sign * this.direction.dot( edge1.cross( diff ) ); + + // b2 < 0, no intersection + if ( DdE1xQ < 0 ) { + + return null; + + } + + // b1+b2 > 1, no intersection + if ( DdQxE2 + DdE1xQ > DdN ) { + + return null; + + } + + // Line intersects triangle, check if ray does. + var QdN = - sign * diff.dot( normal ); + + // t < 0, no intersection + if ( QdN < 0 ) { + + return null; + + } + + // Ray intersects triangle. + return this.at( QdN / DdN, optionalTarget ); + + }; + + }(), + + applyMatrix4: function ( matrix4 ) { + + this.direction.add( this.origin ).applyMatrix4( matrix4 ); + this.origin.applyMatrix4( matrix4 ); + this.direction.sub( this.origin ); + this.direction.normalize(); + + return this; + }, + + equals: function ( ray ) { + + return ray.origin.equals( this.origin ) && ray.direction.equals( this.direction ); + + }, + + clone: function () { + + return new THREE.Ray().copy( this ); + + } + +}; + +// File:src/math/Sphere.js + +/** + * @author bhouston / http://exocortex.com + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Sphere = function ( center, radius ) { + + this.center = ( center !== undefined ) ? center : new THREE.Vector3(); + this.radius = ( radius !== undefined ) ? radius : 0; + +}; + +THREE.Sphere.prototype = { + + constructor: THREE.Sphere, + + set: function ( center, radius ) { + + this.center.copy( center ); + this.radius = radius; + + return this; + }, + + setFromPoints: function () { + + var box = new THREE.Box3(); + + return function ( points, optionalCenter ) { + + var center = this.center; + + if ( optionalCenter !== undefined ) { + + center.copy( optionalCenter ); + + } else { + + box.setFromPoints( points ).center( center ); + + } + + var maxRadiusSq = 0; + + for ( var i = 0, il = points.length; i < il; i ++ ) { + + maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( points[ i ] ) ); + + } + + this.radius = Math.sqrt( maxRadiusSq ); + + return this; + + }; + + }(), + + copy: function ( sphere ) { + + this.center.copy( sphere.center ); + this.radius = sphere.radius; + + return this; + + }, + + empty: function () { + + return ( this.radius <= 0 ); + + }, + + containsPoint: function ( point ) { + + return ( point.distanceToSquared( this.center ) <= ( this.radius * this.radius ) ); + + }, + + distanceToPoint: function ( point ) { + + return ( point.distanceTo( this.center ) - this.radius ); + + }, + + intersectsSphere: function ( sphere ) { + + var radiusSum = this.radius + sphere.radius; + + return sphere.center.distanceToSquared( this.center ) <= ( radiusSum * radiusSum ); + + }, + + clampPoint: function ( point, optionalTarget ) { + + var deltaLengthSq = this.center.distanceToSquared( point ); + + var result = optionalTarget || new THREE.Vector3(); + result.copy( point ); + + if ( deltaLengthSq > ( this.radius * this.radius ) ) { + + result.sub( this.center ).normalize(); + result.multiplyScalar( this.radius ).add( this.center ); + + } + + return result; + + }, + + getBoundingBox: function ( optionalTarget ) { + + var box = optionalTarget || new THREE.Box3(); + + box.set( this.center, this.center ); + box.expandByScalar( this.radius ); + + return box; + + }, + + applyMatrix4: function ( matrix ) { + + this.center.applyMatrix4( matrix ); + this.radius = this.radius * matrix.getMaxScaleOnAxis(); + + return this; + + }, + + translate: function ( offset ) { + + this.center.add( offset ); + + return this; + + }, + + equals: function ( sphere ) { + + return sphere.center.equals( this.center ) && ( sphere.radius === this.radius ); + + }, + + clone: function () { + + return new THREE.Sphere().copy( this ); + + } + +}; + +// File:src/math/Frustum.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * @author bhouston / http://exocortex.com + */ + +THREE.Frustum = function ( p0, p1, p2, p3, p4, p5 ) { + + this.planes = [ + + ( p0 !== undefined ) ? p0 : new THREE.Plane(), + ( p1 !== undefined ) ? p1 : new THREE.Plane(), + ( p2 !== undefined ) ? p2 : new THREE.Plane(), + ( p3 !== undefined ) ? p3 : new THREE.Plane(), + ( p4 !== undefined ) ? p4 : new THREE.Plane(), + ( p5 !== undefined ) ? p5 : new THREE.Plane() + + ]; + +}; + +THREE.Frustum.prototype = { + + constructor: THREE.Frustum, + + set: function ( p0, p1, p2, p3, p4, p5 ) { + + var planes = this.planes; + + planes[ 0 ].copy( p0 ); + planes[ 1 ].copy( p1 ); + planes[ 2 ].copy( p2 ); + planes[ 3 ].copy( p3 ); + planes[ 4 ].copy( p4 ); + planes[ 5 ].copy( p5 ); + + return this; + + }, + + copy: function ( frustum ) { + + var planes = this.planes; + + for ( var i = 0; i < 6; i ++ ) { + + planes[ i ].copy( frustum.planes[ i ] ); + + } + + return this; + + }, + + setFromMatrix: function ( m ) { + + var planes = this.planes; + var me = m.elements; + var me0 = me[ 0 ], me1 = me[ 1 ], me2 = me[ 2 ], me3 = me[ 3 ]; + var me4 = me[ 4 ], me5 = me[ 5 ], me6 = me[ 6 ], me7 = me[ 7 ]; + var me8 = me[ 8 ], me9 = me[ 9 ], me10 = me[ 10 ], me11 = me[ 11 ]; + var me12 = me[ 12 ], me13 = me[ 13 ], me14 = me[ 14 ], me15 = me[ 15 ]; + + planes[ 0 ].setComponents( me3 - me0, me7 - me4, me11 - me8, me15 - me12 ).normalize(); + planes[ 1 ].setComponents( me3 + me0, me7 + me4, me11 + me8, me15 + me12 ).normalize(); + planes[ 2 ].setComponents( me3 + me1, me7 + me5, me11 + me9, me15 + me13 ).normalize(); + planes[ 3 ].setComponents( me3 - me1, me7 - me5, me11 - me9, me15 - me13 ).normalize(); + planes[ 4 ].setComponents( me3 - me2, me7 - me6, me11 - me10, me15 - me14 ).normalize(); + planes[ 5 ].setComponents( me3 + me2, me7 + me6, me11 + me10, me15 + me14 ).normalize(); + + return this; + + }, + + intersectsObject: function () { + + var sphere = new THREE.Sphere(); + + return function ( object ) { + + var geometry = object.geometry; + + if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); + + sphere.copy( geometry.boundingSphere ); + sphere.applyMatrix4( object.matrixWorld ); + + return this.intersectsSphere( sphere ); + + }; + + }(), + + intersectsSphere: function ( sphere ) { + + var planes = this.planes; + var center = sphere.center; + var negRadius = - sphere.radius; + + for ( var i = 0; i < 6; i ++ ) { + + var distance = planes[ i ].distanceToPoint( center ); + + if ( distance < negRadius ) { + + return false; + + } + + } + + return true; + + }, + + intersectsBox: function () { + + var p1 = new THREE.Vector3(), + p2 = new THREE.Vector3(); + + return function ( box ) { + + var planes = this.planes; + + for ( var i = 0; i < 6 ; i ++ ) { + + var plane = planes[ i ]; + + p1.x = plane.normal.x > 0 ? box.min.x : box.max.x; + p2.x = plane.normal.x > 0 ? box.max.x : box.min.x; + p1.y = plane.normal.y > 0 ? box.min.y : box.max.y; + p2.y = plane.normal.y > 0 ? box.max.y : box.min.y; + p1.z = plane.normal.z > 0 ? box.min.z : box.max.z; + p2.z = plane.normal.z > 0 ? box.max.z : box.min.z; + + var d1 = plane.distanceToPoint( p1 ); + var d2 = plane.distanceToPoint( p2 ); + + // if both outside plane, no intersection + + if ( d1 < 0 && d2 < 0 ) { + + return false; + + } + } + + return true; + }; + + }(), + + + containsPoint: function ( point ) { + + var planes = this.planes; + + for ( var i = 0; i < 6; i ++ ) { + + if ( planes[ i ].distanceToPoint( point ) < 0 ) { + + return false; + + } + + } + + return true; + + }, + + clone: function () { + + return new THREE.Frustum().copy( this ); + + } + +}; + +// File:src/math/Plane.js + +/** + * @author bhouston / http://exocortex.com + */ + +THREE.Plane = function ( normal, constant ) { + + this.normal = ( normal !== undefined ) ? normal : new THREE.Vector3( 1, 0, 0 ); + this.constant = ( constant !== undefined ) ? constant : 0; + +}; + +THREE.Plane.prototype = { + + constructor: THREE.Plane, + + set: function ( normal, constant ) { + + this.normal.copy( normal ); + this.constant = constant; + + return this; + + }, + + setComponents: function ( x, y, z, w ) { + + this.normal.set( x, y, z ); + this.constant = w; + + return this; + + }, + + setFromNormalAndCoplanarPoint: function ( normal, point ) { + + this.normal.copy( normal ); + this.constant = - point.dot( this.normal ); // must be this.normal, not normal, as this.normal is normalized + + return this; + + }, + + setFromCoplanarPoints: function () { + + var v1 = new THREE.Vector3(); + var v2 = new THREE.Vector3(); + + return function ( a, b, c ) { + + var normal = v1.subVectors( c, b ).cross( v2.subVectors( a, b ) ).normalize(); + + // Q: should an error be thrown if normal is zero (e.g. degenerate plane)? + + this.setFromNormalAndCoplanarPoint( normal, a ); + + return this; + + }; + + }(), + + + copy: function ( plane ) { + + this.normal.copy( plane.normal ); + this.constant = plane.constant; + + return this; + + }, + + normalize: function () { + + // Note: will lead to a divide by zero if the plane is invalid. + + var inverseNormalLength = 1.0 / this.normal.length(); + this.normal.multiplyScalar( inverseNormalLength ); + this.constant *= inverseNormalLength; + + return this; + + }, + + negate: function () { + + this.constant *= - 1; + this.normal.negate(); + + return this; + + }, + + distanceToPoint: function ( point ) { + + return this.normal.dot( point ) + this.constant; + + }, + + distanceToSphere: function ( sphere ) { + + return this.distanceToPoint( sphere.center ) - sphere.radius; + + }, + + projectPoint: function ( point, optionalTarget ) { + + return this.orthoPoint( point, optionalTarget ).sub( point ).negate(); + + }, + + orthoPoint: function ( point, optionalTarget ) { + + var perpendicularMagnitude = this.distanceToPoint( point ); + + var result = optionalTarget || new THREE.Vector3(); + return result.copy( this.normal ).multiplyScalar( perpendicularMagnitude ); + + }, + + isIntersectionLine: function ( line ) { + + // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it. + + var startSign = this.distanceToPoint( line.start ); + var endSign = this.distanceToPoint( line.end ); + + return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 ); + + }, + + intersectLine: function () { + + var v1 = new THREE.Vector3(); + + return function ( line, optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + + var direction = line.delta( v1 ); + + var denominator = this.normal.dot( direction ); + + if ( denominator == 0 ) { + + // line is coplanar, return origin + if ( this.distanceToPoint( line.start ) == 0 ) { + + return result.copy( line.start ); + + } + + // Unsure if this is the correct method to handle this case. + return undefined; + + } + + var t = - ( line.start.dot( this.normal ) + this.constant ) / denominator; + + if ( t < 0 || t > 1 ) { + + return undefined; + + } + + return result.copy( direction ).multiplyScalar( t ).add( line.start ); + + }; + + }(), + + + coplanarPoint: function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + return result.copy( this.normal ).multiplyScalar( - this.constant ); + + }, + + applyMatrix4: function () { + + var v1 = new THREE.Vector3(); + var v2 = new THREE.Vector3(); + var m1 = new THREE.Matrix3(); + + return function ( matrix, optionalNormalMatrix ) { + + // compute new normal based on theory here: + // http://www.songho.ca/opengl/gl_normaltransform.html + var normalMatrix = optionalNormalMatrix || m1.getNormalMatrix( matrix ); + var newNormal = v1.copy( this.normal ).applyMatrix3( normalMatrix ); + + var newCoplanarPoint = this.coplanarPoint( v2 ); + newCoplanarPoint.applyMatrix4( matrix ); + + this.setFromNormalAndCoplanarPoint( newNormal, newCoplanarPoint ); + + return this; + + }; + + }(), + + translate: function ( offset ) { + + this.constant = this.constant - offset.dot( this.normal ); + + return this; + + }, + + equals: function ( plane ) { + + return plane.normal.equals( this.normal ) && ( plane.constant == this.constant ); + + }, + + clone: function () { + + return new THREE.Plane().copy( this ); + + } + +}; + +// File:src/math/Math.js + +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Math = { + + generateUUID: function () { + + // http://www.broofa.com/Tools/Math.uuid.htm + + var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split( '' ); + var uuid = new Array( 36 ); + var rnd = 0, r; + + return function () { + + for ( var i = 0; i < 36; i ++ ) { + + if ( i == 8 || i == 13 || i == 18 || i == 23 ) { + + uuid[ i ] = '-'; + + } else if ( i == 14 ) { + + uuid[ i ] = '4'; + + } else { + + if ( rnd <= 0x02 ) rnd = 0x2000000 + ( Math.random() * 0x1000000 ) | 0; + r = rnd & 0xf; + rnd = rnd >> 4; + uuid[ i ] = chars[ ( i == 19 ) ? ( r & 0x3 ) | 0x8 : r ]; + + } + } + + return uuid.join( '' ); + + }; + + }(), + + // Clamp value to range <a, b> + + clamp: function ( x, a, b ) { + + return ( x < a ) ? a : ( ( x > b ) ? b : x ); + + }, + + // Clamp value to range <a, inf) + + clampBottom: function ( x, a ) { + + return x < a ? a : x; + + }, + + // Linear mapping from range <a1, a2> to range <b1, b2> + + mapLinear: function ( x, a1, a2, b1, b2 ) { + + return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 ); + + }, + + // http://en.wikipedia.org/wiki/Smoothstep + + smoothstep: function ( x, min, max ) { + + if ( x <= min ) return 0; + if ( x >= max ) return 1; + + x = ( x - min ) / ( max - min ); + + return x * x * ( 3 - 2 * x ); + + }, + + smootherstep: function ( x, min, max ) { + + if ( x <= min ) return 0; + if ( x >= max ) return 1; + + x = ( x - min ) / ( max - min ); + + return x * x * x * ( x * ( x * 6 - 15 ) + 10 ); + + }, + + // Random float from <0, 1> with 16 bits of randomness + // (standard Math.random() creates repetitive patterns when applied over larger space) + + random16: function () { + + return ( 65280 * Math.random() + 255 * Math.random() ) / 65535; + + }, + + // Random integer from <low, high> interval + + randInt: function ( low, high ) { + + return Math.floor( this.randFloat( low, high ) ); + + }, + + // Random float from <low, high> interval + + randFloat: function ( low, high ) { + + return low + Math.random() * ( high - low ); + + }, + + // Random float from <-range/2, range/2> interval + + randFloatSpread: function ( range ) { + + return range * ( 0.5 - Math.random() ); + + }, + + degToRad: function () { + + var degreeToRadiansFactor = Math.PI / 180; + + return function ( degrees ) { + + return degrees * degreeToRadiansFactor; + + }; + + }(), + + radToDeg: function () { + + var radianToDegreesFactor = 180 / Math.PI; + + return function ( radians ) { + + return radians * radianToDegreesFactor; + + }; + + }(), + + isPowerOfTwo: function ( value ) { + + return ( value & ( value - 1 ) ) === 0 && value !== 0; + + } + +}; + +// File:src/math/Spline.js + +/** + * Spline from Tween.js, slightly optimized (and trashed) + * http://sole.github.com/tween.js/examples/05_spline.html + * + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Spline = function ( points ) { + + this.points = points; + + var c = [], v3 = { x: 0, y: 0, z: 0 }, + point, intPoint, weight, w2, w3, + pa, pb, pc, pd; + + this.initFromArray = function ( a ) { + + this.points = []; + + for ( var i = 0; i < a.length; i ++ ) { + + this.points[ i ] = { x: a[ i ][ 0 ], y: a[ i ][ 1 ], z: a[ i ][ 2 ] }; + + } + + }; + + this.getPoint = function ( k ) { + + point = ( this.points.length - 1 ) * k; + intPoint = Math.floor( point ); + weight = point - intPoint; + + c[ 0 ] = intPoint === 0 ? intPoint : intPoint - 1; + c[ 1 ] = intPoint; + c[ 2 ] = intPoint > this.points.length - 2 ? this.points.length - 1 : intPoint + 1; + c[ 3 ] = intPoint > this.points.length - 3 ? this.points.length - 1 : intPoint + 2; + + pa = this.points[ c[ 0 ] ]; + pb = this.points[ c[ 1 ] ]; + pc = this.points[ c[ 2 ] ]; + pd = this.points[ c[ 3 ] ]; + + w2 = weight * weight; + w3 = weight * w2; + + v3.x = interpolate( pa.x, pb.x, pc.x, pd.x, weight, w2, w3 ); + v3.y = interpolate( pa.y, pb.y, pc.y, pd.y, weight, w2, w3 ); + v3.z = interpolate( pa.z, pb.z, pc.z, pd.z, weight, w2, w3 ); + + return v3; + + }; + + this.getControlPointsArray = function () { + + var i, p, l = this.points.length, + coords = []; + + for ( i = 0; i < l; i ++ ) { + + p = this.points[ i ]; + coords[ i ] = [ p.x, p.y, p.z ]; + + } + + return coords; + + }; + + // approximate length by summing linear segments + + this.getLength = function ( nSubDivisions ) { + + var i, index, nSamples, position, + point = 0, intPoint = 0, oldIntPoint = 0, + oldPosition = new THREE.Vector3(), + tmpVec = new THREE.Vector3(), + chunkLengths = [], + totalLength = 0; + + // first point has 0 length + + chunkLengths[ 0 ] = 0; + + if ( ! nSubDivisions ) nSubDivisions = 100; + + nSamples = this.points.length * nSubDivisions; + + oldPosition.copy( this.points[ 0 ] ); + + for ( i = 1; i < nSamples; i ++ ) { + + index = i / nSamples; + + position = this.getPoint( index ); + tmpVec.copy( position ); + + totalLength += tmpVec.distanceTo( oldPosition ); + + oldPosition.copy( position ); + + point = ( this.points.length - 1 ) * index; + intPoint = Math.floor( point ); + + if ( intPoint != oldIntPoint ) { + + chunkLengths[ intPoint ] = totalLength; + oldIntPoint = intPoint; + + } + + } + + // last point ends with total length + + chunkLengths[ chunkLengths.length ] = totalLength; + + return { chunks: chunkLengths, total: totalLength }; + + }; + + this.reparametrizeByArcLength = function ( samplingCoef ) { + + var i, j, + index, indexCurrent, indexNext, + linearDistance, realDistance, + sampling, position, + newpoints = [], + tmpVec = new THREE.Vector3(), + sl = this.getLength(); + + newpoints.push( tmpVec.copy( this.points[ 0 ] ).clone() ); + + for ( i = 1; i < this.points.length; i ++ ) { + + //tmpVec.copy( this.points[ i - 1 ] ); + //linearDistance = tmpVec.distanceTo( this.points[ i ] ); + + realDistance = sl.chunks[ i ] - sl.chunks[ i - 1 ]; + + sampling = Math.ceil( samplingCoef * realDistance / sl.total ); + + indexCurrent = ( i - 1 ) / ( this.points.length - 1 ); + indexNext = i / ( this.points.length - 1 ); + + for ( j = 1; j < sampling - 1; j ++ ) { + + index = indexCurrent + j * ( 1 / sampling ) * ( indexNext - indexCurrent ); + + position = this.getPoint( index ); + newpoints.push( tmpVec.copy( position ).clone() ); + + } + + newpoints.push( tmpVec.copy( this.points[ i ] ).clone() ); + + } + + this.points = newpoints; + + }; + + // Catmull-Rom + + function interpolate( p0, p1, p2, p3, t, t2, t3 ) { + + var v0 = ( p2 - p0 ) * 0.5, + v1 = ( p3 - p1 ) * 0.5; + + return ( 2 * ( p1 - p2 ) + v0 + v1 ) * t3 + ( - 3 * ( p1 - p2 ) - 2 * v0 - v1 ) * t2 + v0 * t + p1; + + }; + +}; + +// File:src/math/Triangle.js + +/** + * @author bhouston / http://exocortex.com + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Triangle = function ( a, b, c ) { + + this.a = ( a !== undefined ) ? a : new THREE.Vector3(); + this.b = ( b !== undefined ) ? b : new THREE.Vector3(); + this.c = ( c !== undefined ) ? c : new THREE.Vector3(); + +}; + +THREE.Triangle.normal = function () { + + var v0 = new THREE.Vector3(); + + return function ( a, b, c, optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + + result.subVectors( c, b ); + v0.subVectors( a, b ); + result.cross( v0 ); + + var resultLengthSq = result.lengthSq(); + if ( resultLengthSq > 0 ) { + + return result.multiplyScalar( 1 / Math.sqrt( resultLengthSq ) ); + + } + + return result.set( 0, 0, 0 ); + + }; + +}(); + +// static/instance method to calculate barycoordinates +// based on: http://www.blackpawn.com/texts/pointinpoly/default.html +THREE.Triangle.barycoordFromPoint = function () { + + var v0 = new THREE.Vector3(); + var v1 = new THREE.Vector3(); + var v2 = new THREE.Vector3(); + + return function ( point, a, b, c, optionalTarget ) { + + v0.subVectors( c, a ); + v1.subVectors( b, a ); + v2.subVectors( point, a ); + + var dot00 = v0.dot( v0 ); + var dot01 = v0.dot( v1 ); + var dot02 = v0.dot( v2 ); + var dot11 = v1.dot( v1 ); + var dot12 = v1.dot( v2 ); + + var denom = ( dot00 * dot11 - dot01 * dot01 ); + + var result = optionalTarget || new THREE.Vector3(); + + // colinear or singular triangle + if ( denom == 0 ) { + // arbitrary location outside of triangle? + // not sure if this is the best idea, maybe should be returning undefined + return result.set( - 2, - 1, - 1 ); + } + + var invDenom = 1 / denom; + var u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom; + var v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom; + + // barycoordinates must always sum to 1 + return result.set( 1 - u - v, v, u ); + + }; + +}(); + +THREE.Triangle.containsPoint = function () { + + var v1 = new THREE.Vector3(); + + return function ( point, a, b, c ) { + + var result = THREE.Triangle.barycoordFromPoint( point, a, b, c, v1 ); + + return ( result.x >= 0 ) && ( result.y >= 0 ) && ( ( result.x + result.y ) <= 1 ); + + }; + +}(); + +THREE.Triangle.prototype = { + + constructor: THREE.Triangle, + + set: function ( a, b, c ) { + + this.a.copy( a ); + this.b.copy( b ); + this.c.copy( c ); + + return this; + + }, + + setFromPointsAndIndices: function ( points, i0, i1, i2 ) { + + this.a.copy( points[ i0 ] ); + this.b.copy( points[ i1 ] ); + this.c.copy( points[ i2 ] ); + + return this; + + }, + + copy: function ( triangle ) { + + this.a.copy( triangle.a ); + this.b.copy( triangle.b ); + this.c.copy( triangle.c ); + + return this; + + }, + + area: function () { + + var v0 = new THREE.Vector3(); + var v1 = new THREE.Vector3(); + + return function () { + + v0.subVectors( this.c, this.b ); + v1.subVectors( this.a, this.b ); + + return v0.cross( v1 ).length() * 0.5; + + }; + + }(), + + midpoint: function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + return result.addVectors( this.a, this.b ).add( this.c ).multiplyScalar( 1 / 3 ); + + }, + + normal: function ( optionalTarget ) { + + return THREE.Triangle.normal( this.a, this.b, this.c, optionalTarget ); + + }, + + plane: function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Plane(); + + return result.setFromCoplanarPoints( this.a, this.b, this.c ); + + }, + + barycoordFromPoint: function ( point, optionalTarget ) { + + return THREE.Triangle.barycoordFromPoint( point, this.a, this.b, this.c, optionalTarget ); + + }, + + containsPoint: function ( point ) { + + return THREE.Triangle.containsPoint( point, this.a, this.b, this.c ); + + }, + + equals: function ( triangle ) { + + return triangle.a.equals( this.a ) && triangle.b.equals( this.b ) && triangle.c.equals( this.c ); + + }, + + clone: function () { + + return new THREE.Triangle().copy( this ); + + } + +}; + +// File:src/core/Clock.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Clock = function ( autoStart ) { + + this.autoStart = ( autoStart !== undefined ) ? autoStart : true; + + this.startTime = 0; + this.oldTime = 0; + this.elapsedTime = 0; + + this.running = false; + +}; + +THREE.Clock.prototype = { + + constructor: THREE.Clock, + + start: function () { + + this.startTime = self.performance !== undefined && self.performance.now !== undefined + ? self.performance.now() + : Date.now(); + + this.oldTime = this.startTime; + this.running = true; + }, + + stop: function () { + + this.getElapsedTime(); + this.running = false; + + }, + + getElapsedTime: function () { + + this.getDelta(); + return this.elapsedTime; + + }, + + getDelta: function () { + + var diff = 0; + + if ( this.autoStart && ! this.running ) { + + this.start(); + + } + + if ( this.running ) { + + var newTime = self.performance !== undefined && self.performance.now !== undefined + ? self.performance.now() + : Date.now(); + + diff = 0.001 * ( newTime - this.oldTime ); + this.oldTime = newTime; + + this.elapsedTime += diff; + + } + + return diff; + + } + +}; + +// File:src/core/EventDispatcher.js + +/** + * https://github.com/mrdoob/eventdispatcher.js/ + */ + +THREE.EventDispatcher = function () {} + +THREE.EventDispatcher.prototype = { + + constructor: THREE.EventDispatcher, + + apply: function ( object ) { + + object.addEventListener = THREE.EventDispatcher.prototype.addEventListener; + object.hasEventListener = THREE.EventDispatcher.prototype.hasEventListener; + object.removeEventListener = THREE.EventDispatcher.prototype.removeEventListener; + object.dispatchEvent = THREE.EventDispatcher.prototype.dispatchEvent; + + }, + + addEventListener: function ( type, listener ) { + + if ( this._listeners === undefined ) this._listeners = {}; + + var listeners = this._listeners; + + if ( listeners[ type ] === undefined ) { + + listeners[ type ] = []; + + } + + if ( listeners[ type ].indexOf( listener ) === - 1 ) { + + listeners[ type ].push( listener ); + + } + + }, + + hasEventListener: function ( type, listener ) { + + if ( this._listeners === undefined ) return false; + + var listeners = this._listeners; + + if ( listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1 ) { + + return true; + + } + + return false; + + }, + + removeEventListener: function ( type, listener ) { + + if ( this._listeners === undefined ) return; + + var listeners = this._listeners; + var listenerArray = listeners[ type ]; + + if ( listenerArray !== undefined ) { + + var index = listenerArray.indexOf( listener ); + + if ( index !== - 1 ) { + + listenerArray.splice( index, 1 ); + + } + + } + + }, + + dispatchEvent: function ( event ) { + + if ( this._listeners === undefined ) return; + + var listeners = this._listeners; + var listenerArray = listeners[ event.type ]; + + if ( listenerArray !== undefined ) { + + event.target = this; + + var array = []; + var length = listenerArray.length; + + for ( var i = 0; i < length; i ++ ) { + + array[ i ] = listenerArray[ i ]; + + } + + for ( var i = 0; i < length; i ++ ) { + + array[ i ].call( this, event ); + + } + + } + + } + +}; + +// File:src/core/Raycaster.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author bhouston / http://exocortex.com/ + * @author stephomi / http://stephaneginier.com/ + */ + +( function ( THREE ) { + + THREE.Raycaster = function ( origin, direction, near, far ) { + + this.ray = new THREE.Ray( origin, direction ); + // direction is assumed to be normalized (for accurate distance calculations) + + this.near = near || 0; + this.far = far || Infinity; + + this.params = { + Sprite: {}, + Mesh: {}, + PointCloud: { threshold: 1 }, + LOD: {}, + Line: {} + }; + + }; + + var descSort = function ( a, b ) { + + return a.distance - b.distance; + + }; + + var intersectObject = function ( object, raycaster, intersects, recursive ) { + + object.raycast( raycaster, intersects ); + + if ( recursive === true ) { + + var children = object.children; + + for ( var i = 0, l = children.length; i < l; i ++ ) { + + intersectObject( children[ i ], raycaster, intersects, true ); + + } + + } + + }; + + // + + THREE.Raycaster.prototype = { + + constructor: THREE.Raycaster, + + precision: 0.0001, + linePrecision: 1, + + set: function ( origin, direction ) { + + // direction is assumed to be normalized (for accurate distance calculations) + + this.ray.set( origin, direction ); + + }, + + setFromCamera: function ( coords, camera ) { + + // camera is assumed _not_ to be a child of a transformed object + + if ( camera instanceof THREE.PerspectiveCamera ) { + + this.ray.origin.copy( camera.position ); + this.ray.direction.set( coords.x, coords.y, 0.5 ).unproject( camera ).sub( camera.position ).normalize(); + + } else if ( camera instanceof THREE.OrthographicCamera ) { + + this.ray.origin.set( coords.x, coords.y, - 1 ).unproject( camera ); + this.ray.direction.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld ); + + } else { + + console.error( 'THREE.Raycaster: Unsupported camera type.' ); + + } + + }, + + intersectObject: function ( object, recursive ) { + + var intersects = []; + + intersectObject( object, this, intersects, recursive ); + + intersects.sort( descSort ); + + return intersects; + + }, + + intersectObjects: function ( objects, recursive ) { + + var intersects = []; + + if ( objects instanceof Array === false ) { + + console.log( 'THREE.Raycaster.intersectObjects: objects is not an Array.' ); + return intersects; + + } + + for ( var i = 0, l = objects.length; i < l; i ++ ) { + + intersectObject( objects[ i ], this, intersects, recursive ); + + } + + intersects.sort( descSort ); + + return intersects; + + } + + }; + +}( THREE ) ); + +// File:src/core/Object3D.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + * @author WestLangley / http://github.com/WestLangley + */ + +THREE.Object3D = function () { + + Object.defineProperty( this, 'id', { value: THREE.Object3DIdCount ++ } ); + + this.uuid = THREE.Math.generateUUID(); + + this.name = ''; + this.type = 'Object3D'; + + this.parent = undefined; + this.children = []; + + this.up = THREE.Object3D.DefaultUp.clone(); + + var scope = this; + + var position = new THREE.Vector3(); + var rotation = new THREE.Euler(); + var quaternion = new THREE.Quaternion(); + var scale = new THREE.Vector3( 1, 1, 1 ); + + var onRotationChange = function () { + quaternion.setFromEuler( rotation, false ); + }; + + var onQuaternionChange = function () { + rotation.setFromQuaternion( quaternion, undefined, false ); + }; + + rotation.onChange( onRotationChange ); + quaternion.onChange( onQuaternionChange ); + + Object.defineProperties( this, { + position: { + enumerable: true, + value: position + }, + rotation: { + enumerable: true, + value: rotation + }, + quaternion: { + enumerable: true, + value: quaternion + }, + scale: { + enumerable: true, + value: scale + }, + } ); + + this.rotationAutoUpdate = true; + + this.matrix = new THREE.Matrix4(); + this.matrixWorld = new THREE.Matrix4(); + + this.matrixAutoUpdate = true; + this.matrixWorldNeedsUpdate = false; + + this.visible = true; + + this.castShadow = false; + this.receiveShadow = false; + + this.frustumCulled = true; + + this.userData = {}; + +}; + +THREE.Object3D.DefaultUp = new THREE.Vector3( 0, 1, 0 ); + +THREE.Object3D.prototype = { + + constructor: THREE.Object3D, + + get eulerOrder () { + + console.warn( 'THREE.Object3D: .eulerOrder has been moved to .rotation.order.' ); + + return this.rotation.order; + + }, + + set eulerOrder ( value ) { + + console.warn( 'THREE.Object3D: .eulerOrder has been moved to .rotation.order.' ); + + this.rotation.order = value; + + }, + + get useQuaternion () { + + console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' ); + + }, + + set useQuaternion ( value ) { + + console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' ); + + }, + + applyMatrix: function ( matrix ) { + + this.matrix.multiplyMatrices( matrix, this.matrix ); + + this.matrix.decompose( this.position, this.quaternion, this.scale ); + + }, + + setRotationFromAxisAngle: function ( axis, angle ) { + + // assumes axis is normalized + + this.quaternion.setFromAxisAngle( axis, angle ); + + }, + + setRotationFromEuler: function ( euler ) { + + this.quaternion.setFromEuler( euler, true ); + + }, + + setRotationFromMatrix: function ( m ) { + + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + + this.quaternion.setFromRotationMatrix( m ); + + }, + + setRotationFromQuaternion: function ( q ) { + + // assumes q is normalized + + this.quaternion.copy( q ); + + }, + + rotateOnAxis: function () { + + // rotate object on axis in object space + // axis is assumed to be normalized + + var q1 = new THREE.Quaternion(); + + return function ( axis, angle ) { + + q1.setFromAxisAngle( axis, angle ); + + this.quaternion.multiply( q1 ); + + return this; + + } + + }(), + + rotateX: function () { + + var v1 = new THREE.Vector3( 1, 0, 0 ); + + return function ( angle ) { + + return this.rotateOnAxis( v1, angle ); + + }; + + }(), + + rotateY: function () { + + var v1 = new THREE.Vector3( 0, 1, 0 ); + + return function ( angle ) { + + return this.rotateOnAxis( v1, angle ); + + }; + + }(), + + rotateZ: function () { + + var v1 = new THREE.Vector3( 0, 0, 1 ); + + return function ( angle ) { + + return this.rotateOnAxis( v1, angle ); + + }; + + }(), + + translateOnAxis: function () { + + // translate object by distance along axis in object space + // axis is assumed to be normalized + + var v1 = new THREE.Vector3(); + + return function ( axis, distance ) { + + v1.copy( axis ).applyQuaternion( this.quaternion ); + + this.position.add( v1.multiplyScalar( distance ) ); + + return this; + + } + + }(), + + translate: function ( distance, axis ) { + + console.warn( 'THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead.' ); + return this.translateOnAxis( axis, distance ); + + }, + + translateX: function () { + + var v1 = new THREE.Vector3( 1, 0, 0 ); + + return function ( distance ) { + + return this.translateOnAxis( v1, distance ); + + }; + + }(), + + translateY: function () { + + var v1 = new THREE.Vector3( 0, 1, 0 ); + + return function ( distance ) { + + return this.translateOnAxis( v1, distance ); + + }; + + }(), + + translateZ: function () { + + var v1 = new THREE.Vector3( 0, 0, 1 ); + + return function ( distance ) { + + return this.translateOnAxis( v1, distance ); + + }; + + }(), + + localToWorld: function ( vector ) { + + return vector.applyMatrix4( this.matrixWorld ); + + }, + + worldToLocal: function () { + + var m1 = new THREE.Matrix4(); + + return function ( vector ) { + + return vector.applyMatrix4( m1.getInverse( this.matrixWorld ) ); + + }; + + }(), + + lookAt: function () { + + // This routine does not support objects with rotated and/or translated parent(s) + + var m1 = new THREE.Matrix4(); + + return function ( vector ) { + + m1.lookAt( vector, this.position, this.up ); + + this.quaternion.setFromRotationMatrix( m1 ); + + }; + + }(), + + add: function ( object ) { + + if ( arguments.length > 1 ) { + + for ( var i = 0; i < arguments.length; i++ ) { + + this.add( arguments[ i ] ); + + } + + return this; + + }; + + if ( object === this ) { + + console.error( "THREE.Object3D.add:", object, "can't be added as a child of itself." ); + return this; + + } + + if ( object instanceof THREE.Object3D ) { + + if ( object.parent !== undefined ) { + + object.parent.remove( object ); + + } + + object.parent = this; + object.dispatchEvent( { type: 'added' } ); + + this.children.push( object ); + + } else { + + console.error( "THREE.Object3D.add:", object, "is not an instance of THREE.Object3D." ); + + } + + return this; + + }, + + remove: function ( object ) { + + if ( arguments.length > 1 ) { + + for ( var i = 0; i < arguments.length; i++ ) { + + this.remove( arguments[ i ] ); + + } + + }; + + var index = this.children.indexOf( object ); + + if ( index !== - 1 ) { + + object.parent = undefined; + + object.dispatchEvent( { type: 'removed' } ); + + this.children.splice( index, 1 ); + + } + + }, + + getChildByName: function ( name, recursive ) { + + console.warn( 'THREE.Object3D: .getChildByName() has been renamed to .getObjectByName().' ); + return this.getObjectByName( name, recursive ); + + }, + + getObjectById: function ( id, recursive ) { + + return this.getObjectByProperty( 'id', id, recursive ); + + }, + + getObjectByName: function ( name, recursive ) { + + return this.getObjectByProperty( 'name', name, recursive ); + + }, + + getObjectByProperty: function ( name, value, recursive ) { + + if ( this[ name ] === value ) return this; + + for ( var i = 0, l = this.children.length; i < l; i ++ ) { + + var child = this.children[ i ]; + var object = child.getObjectByProperty( name, value, recursive ); + + if ( object !== undefined ) { + + return object; + + } + + } + + return undefined; + + }, + + getWorldPosition: function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + + this.updateMatrixWorld( true ); + + return result.setFromMatrixPosition( this.matrixWorld ); + + }, + + getWorldQuaternion: function () { + + var position = new THREE.Vector3(); + var scale = new THREE.Vector3(); + + return function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Quaternion(); + + this.updateMatrixWorld( true ); + + this.matrixWorld.decompose( position, result, scale ); + + return result; + + } + + }(), + + getWorldRotation: function () { + + var quaternion = new THREE.Quaternion(); + + return function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Euler(); + + this.getWorldQuaternion( quaternion ); + + return result.setFromQuaternion( quaternion, this.rotation.order, false ); + + } + + }(), + + getWorldScale: function () { + + var position = new THREE.Vector3(); + var quaternion = new THREE.Quaternion(); + + return function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + + this.updateMatrixWorld( true ); + + this.matrixWorld.decompose( position, quaternion, result ); + + return result; + + } + + }(), + + getWorldDirection: function () { + + var quaternion = new THREE.Quaternion(); + + return function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + + this.getWorldQuaternion( quaternion ); + + return result.set( 0, 0, 1 ).applyQuaternion( quaternion ); + + } + + }(), + + raycast: function () {}, + + traverse: function ( callback ) { + + callback( this ); + + for ( var i = 0, l = this.children.length; i < l; i ++ ) { + + this.children[ i ].traverse( callback ); + + } + + }, + + traverseVisible: function ( callback ) { + + if ( this.visible === false ) return; + + callback( this ); + + for ( var i = 0, l = this.children.length; i < l; i ++ ) { + + this.children[ i ].traverseVisible( callback ); + + } + + }, + + traverseAncestors: function ( callback ) { + + if ( this.parent ) { + + callback( this.parent ); + + this.parent.traverseAncestors( callback ); + + } + + }, + + updateMatrix: function () { + + this.matrix.compose( this.position, this.quaternion, this.scale ); + + this.matrixWorldNeedsUpdate = true; + + }, + + updateMatrixWorld: function ( force ) { + + if ( this.matrixAutoUpdate === true ) this.updateMatrix(); + + if ( this.matrixWorldNeedsUpdate === true || force === true ) { + + if ( this.parent === undefined ) { + + this.matrixWorld.copy( this.matrix ); + + } else { + + this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); + + } + + this.matrixWorldNeedsUpdate = false; + + force = true; + + } + + // update children + + for ( var i = 0, l = this.children.length; i < l; i ++ ) { + + this.children[ i ].updateMatrixWorld( force ); + + } + + }, + + toJSON: function () { + + var output = { + metadata: { + version: 4.3, + type: 'Object', + generator: 'ObjectExporter' + } + }; + + // + + var geometries = {}; + + var parseGeometry = function ( geometry ) { + + if ( output.geometries === undefined ) { + + output.geometries = []; + + } + + if ( geometries[ geometry.uuid ] === undefined ) { + + var json = geometry.toJSON(); + + delete json.metadata; + + geometries[ geometry.uuid ] = json; + + output.geometries.push( json ); + + } + + return geometry.uuid; + + }; + + // + + var materials = {}; + + var parseMaterial = function ( material ) { + + if ( output.materials === undefined ) { + + output.materials = []; + + } + + if ( materials[ material.uuid ] === undefined ) { + + var json = material.toJSON(); + + delete json.metadata; + + materials[ material.uuid ] = json; + + output.materials.push( json ); + + } + + return material.uuid; + + }; + + // + + var parseObject = function ( object ) { + + var data = {}; + + data.uuid = object.uuid; + data.type = object.type; + + if ( object.name !== '' ) data.name = object.name; + if ( JSON.stringify( object.userData ) !== '{}' ) data.userData = object.userData; + if ( object.visible !== true ) data.visible = object.visible; + + if ( object instanceof THREE.PerspectiveCamera ) { + + data.fov = object.fov; + data.aspect = object.aspect; + data.near = object.near; + data.far = object.far; + + } else if ( object instanceof THREE.OrthographicCamera ) { + + data.left = object.left; + data.right = object.right; + data.top = object.top; + data.bottom = object.bottom; + data.near = object.near; + data.far = object.far; + + } else if ( object instanceof THREE.AmbientLight ) { + + data.color = object.color.getHex(); + + } else if ( object instanceof THREE.DirectionalLight ) { + + data.color = object.color.getHex(); + data.intensity = object.intensity; + + } else if ( object instanceof THREE.PointLight ) { + + data.color = object.color.getHex(); + data.intensity = object.intensity; + data.distance = object.distance; + + } else if ( object instanceof THREE.SpotLight ) { + + data.color = object.color.getHex(); + data.intensity = object.intensity; + data.distance = object.distance; + data.angle = object.angle; + data.exponent = object.exponent; + + } else if ( object instanceof THREE.HemisphereLight ) { + + data.color = object.color.getHex(); + data.groundColor = object.groundColor.getHex(); + + } else if ( object instanceof THREE.Mesh ) { + + data.geometry = parseGeometry( object.geometry ); + data.material = parseMaterial( object.material ); + + } else if ( object instanceof THREE.Line ) { + + data.geometry = parseGeometry( object.geometry ); + data.material = parseMaterial( object.material ); + + } else if ( object instanceof THREE.Sprite ) { + + data.material = parseMaterial( object.material ); + + } + + data.matrix = object.matrix.toArray(); + + if ( object.children.length > 0 ) { + + data.children = []; + + for ( var i = 0; i < object.children.length; i ++ ) { + + data.children.push( parseObject( object.children[ i ] ) ); + + } + + } + + return data; + + } + + output.object = parseObject( this ); + + return output; + + }, + + clone: function ( object, recursive ) { + + if ( object === undefined ) object = new THREE.Object3D(); + if ( recursive === undefined ) recursive = true; + + object.name = this.name; + + object.up.copy( this.up ); + + object.position.copy( this.position ); + object.quaternion.copy( this.quaternion ); + object.scale.copy( this.scale ); + + object.rotationAutoUpdate = this.rotationAutoUpdate; + + object.matrix.copy( this.matrix ); + object.matrixWorld.copy( this.matrixWorld ); + + object.matrixAutoUpdate = this.matrixAutoUpdate; + object.matrixWorldNeedsUpdate = this.matrixWorldNeedsUpdate; + + object.visible = this.visible; + + object.castShadow = this.castShadow; + object.receiveShadow = this.receiveShadow; + + object.frustumCulled = this.frustumCulled; + + object.userData = JSON.parse( JSON.stringify( this.userData ) ); + + if ( recursive === true ) { + + for ( var i = 0; i < this.children.length; i ++ ) { + + var child = this.children[ i ]; + object.add( child.clone() ); + + } + + } + + return object; + + } + +}; + +THREE.EventDispatcher.prototype.apply( THREE.Object3D.prototype ); + +THREE.Object3DIdCount = 0; + +// File:src/core/Face3.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Face3 = function ( a, b, c, normal, color, materialIndex ) { + + this.a = a; + this.b = b; + this.c = c; + + this.normal = normal instanceof THREE.Vector3 ? normal : new THREE.Vector3(); + this.vertexNormals = normal instanceof Array ? normal : []; + + this.color = color instanceof THREE.Color ? color : new THREE.Color(); + this.vertexColors = color instanceof Array ? color : []; + + this.vertexTangents = []; + + this.materialIndex = materialIndex !== undefined ? materialIndex : 0; + +}; + +THREE.Face3.prototype = { + + constructor: THREE.Face3, + + clone: function () { + + var face = new THREE.Face3( this.a, this.b, this.c ); + + face.normal.copy( this.normal ); + face.color.copy( this.color ); + + face.materialIndex = this.materialIndex; + + for ( var i = 0, il = this.vertexNormals.length; i < il; i ++ ) { + + face.vertexNormals[ i ] = this.vertexNormals[ i ].clone(); + + } + + for ( var i = 0, il = this.vertexColors.length; i < il; i ++ ) { + + face.vertexColors[ i ] = this.vertexColors[ i ].clone(); + + } + + for ( var i = 0, il = this.vertexTangents.length; i < il; i ++ ) { + + face.vertexTangents[ i ] = this.vertexTangents[ i ].clone(); + + } + + return face; + + } + +}; + +// File:src/core/Face4.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Face4 = function ( a, b, c, d, normal, color, materialIndex ) { + + console.warn( 'THREE.Face4 has been removed. A THREE.Face3 will be created instead.' ) + return new THREE.Face3( a, b, c, normal, color, materialIndex ); + +}; + +// File:src/core/BufferAttribute.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.BufferAttribute = function ( array, itemSize ) { + + this.array = array; + this.itemSize = itemSize; + + this.needsUpdate = false; + +}; + +THREE.BufferAttribute.prototype = { + + constructor: THREE.BufferAttribute, + + get length () { + + return this.array.length; + + }, + + copyAt: function ( index1, attribute, index2 ) { + + index1 *= this.itemSize; + index2 *= attribute.itemSize; + + for ( var i = 0, l = this.itemSize; i < l; i ++ ) { + + this.array[ index1 + i ] = attribute.array[ index2 + i ]; + + } + + }, + + set: function ( value ) { + + this.array.set( value ); + + return this; + + }, + + setX: function ( index, x ) { + + this.array[ index * this.itemSize ] = x; + + return this; + + }, + + setY: function ( index, y ) { + + this.array[ index * this.itemSize + 1 ] = y; + + return this; + + }, + + setZ: function ( index, z ) { + + this.array[ index * this.itemSize + 2 ] = z; + + return this; + + }, + + setXY: function ( index, x, y ) { + + index *= this.itemSize; + + this.array[ index ] = x; + this.array[ index + 1 ] = y; + + return this; + + }, + + setXYZ: function ( index, x, y, z ) { + + index *= this.itemSize; + + this.array[ index ] = x; + this.array[ index + 1 ] = y; + this.array[ index + 2 ] = z; + + return this; + + }, + + setXYZW: function ( index, x, y, z, w ) { + + index *= this.itemSize; + + this.array[ index ] = x; + this.array[ index + 1 ] = y; + this.array[ index + 2 ] = z; + this.array[ index + 3 ] = w; + + return this; + + }, + + clone: function () { + + return new THREE.BufferAttribute( new this.array.constructor( this.array ), this.itemSize ); + + } + +}; + +// + +THREE.Int8Attribute = function ( data, itemSize ) { + + console.warn( 'THREE.Int8Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' ); + return new THREE.BufferAttribute( data, itemSize ); + +}; + +THREE.Uint8Attribute = function ( data, itemSize ) { + + console.warn( 'THREE.Uint8Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' ); + return new THREE.BufferAttribute( data, itemSize ); + +}; + +THREE.Uint8ClampedAttribute = function ( data, itemSize ) { + + console.warn( 'THREE.Uint8ClampedAttribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' ); + return new THREE.BufferAttribute( data, itemSize ); + + +}; + +THREE.Int16Attribute = function ( data, itemSize ) { + + console.warn( 'THREE.Int16Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' ); + return new THREE.BufferAttribute( data, itemSize ); + +}; + +THREE.Uint16Attribute = function ( data, itemSize ) { + + console.warn( 'THREE.Uint16Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' ); + return new THREE.BufferAttribute( data, itemSize ); + +}; + +THREE.Int32Attribute = function ( data, itemSize ) { + + console.warn( 'THREE.Int32Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' ); + return new THREE.BufferAttribute( data, itemSize ); + +}; + +THREE.Uint32Attribute = function ( data, itemSize ) { + + console.warn( 'THREE.Uint32Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' ); + return new THREE.BufferAttribute( data, itemSize ); + +}; + +THREE.Float32Attribute = function ( data, itemSize ) { + + console.warn( 'THREE.Float32Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' ); + return new THREE.BufferAttribute( data, itemSize ); + +}; + +THREE.Float64Attribute = function ( data, itemSize ) { + + console.warn( 'THREE.Float64Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' ); + return new THREE.BufferAttribute( data, itemSize ); + +}; + +// File:src/core/BufferGeometry.js + +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.BufferGeometry = function () { + + Object.defineProperty( this, 'id', { value: THREE.GeometryIdCount ++ } ); + + this.uuid = THREE.Math.generateUUID(); + + this.name = ''; + this.type = 'BufferGeometry'; + + this.attributes = {}; + this.attributesKeys = []; + + this.drawcalls = []; + this.offsets = this.drawcalls; // backwards compatibility + + this.boundingBox = null; + this.boundingSphere = null; + +}; + +THREE.BufferGeometry.prototype = { + + constructor: THREE.BufferGeometry, + + addAttribute: function ( name, attribute ) { + + if ( attribute instanceof THREE.BufferAttribute === false ) { + + console.warn( 'THREE.BufferGeometry: .addAttribute() now expects ( name, attribute ).' ); + + this.attributes[ name ] = { array: arguments[ 1 ], itemSize: arguments[ 2 ] }; + + return; + + } + + this.attributes[ name ] = attribute; + this.attributesKeys = Object.keys( this.attributes ); + + }, + + getAttribute: function ( name ) { + + return this.attributes[ name ]; + + }, + + addDrawCall: function ( start, count, indexOffset ) { + + this.drawcalls.push( { + + start: start, + count: count, + index: indexOffset !== undefined ? indexOffset : 0 + + } ); + + }, + + applyMatrix: function ( matrix ) { + + var position = this.attributes.position; + + if ( position !== undefined ) { + + matrix.applyToVector3Array( position.array ); + position.needsUpdate = true; + + } + + var normal = this.attributes.normal; + + if ( normal !== undefined ) { + + var normalMatrix = new THREE.Matrix3().getNormalMatrix( matrix ); + + normalMatrix.applyToVector3Array( normal.array ); + normal.needsUpdate = true; + + } + + }, + + center: function () { + + // TODO + + }, + + fromGeometry: function ( geometry, settings ) { + + settings = settings || { 'vertexColors': THREE.NoColors }; + + var vertices = geometry.vertices; + var faces = geometry.faces; + var faceVertexUvs = geometry.faceVertexUvs; + var vertexColors = settings.vertexColors; + var hasFaceVertexUv = faceVertexUvs[ 0 ].length > 0; + var hasFaceVertexNormals = faces[ 0 ].vertexNormals.length == 3; + + var positions = new Float32Array( faces.length * 3 * 3 ); + this.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) ); + + var normals = new Float32Array( faces.length * 3 * 3 ); + this.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) ); + + if ( vertexColors !== THREE.NoColors ) { + + var colors = new Float32Array( faces.length * 3 * 3 ); + this.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ) ); + + } + + if ( hasFaceVertexUv === true ) { + + var uvs = new Float32Array( faces.length * 3 * 2 ); + this.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) ); + + } + + for ( var i = 0, i2 = 0, i3 = 0; i < faces.length; i ++, i2 += 6, i3 += 9 ) { + + var face = faces[ i ]; + + var a = vertices[ face.a ]; + var b = vertices[ face.b ]; + var c = vertices[ face.c ]; + + positions[ i3 ] = a.x; + positions[ i3 + 1 ] = a.y; + positions[ i3 + 2 ] = a.z; + + positions[ i3 + 3 ] = b.x; + positions[ i3 + 4 ] = b.y; + positions[ i3 + 5 ] = b.z; + + positions[ i3 + 6 ] = c.x; + positions[ i3 + 7 ] = c.y; + positions[ i3 + 8 ] = c.z; + + if ( hasFaceVertexNormals === true ) { + + var na = face.vertexNormals[ 0 ]; + var nb = face.vertexNormals[ 1 ]; + var nc = face.vertexNormals[ 2 ]; + + normals[ i3 ] = na.x; + normals[ i3 + 1 ] = na.y; + normals[ i3 + 2 ] = na.z; + + normals[ i3 + 3 ] = nb.x; + normals[ i3 + 4 ] = nb.y; + normals[ i3 + 5 ] = nb.z; + + normals[ i3 + 6 ] = nc.x; + normals[ i3 + 7 ] = nc.y; + normals[ i3 + 8 ] = nc.z; + + } else { + + var n = face.normal; + + normals[ i3 ] = n.x; + normals[ i3 + 1 ] = n.y; + normals[ i3 + 2 ] = n.z; + + normals[ i3 + 3 ] = n.x; + normals[ i3 + 4 ] = n.y; + normals[ i3 + 5 ] = n.z; + + normals[ i3 + 6 ] = n.x; + normals[ i3 + 7 ] = n.y; + normals[ i3 + 8 ] = n.z; + + } + + if ( vertexColors === THREE.FaceColors ) { + + var fc = face.color; + + colors[ i3 ] = fc.r; + colors[ i3 + 1 ] = fc.g; + colors[ i3 + 2 ] = fc.b; + + colors[ i3 + 3 ] = fc.r; + colors[ i3 + 4 ] = fc.g; + colors[ i3 + 5 ] = fc.b; + + colors[ i3 + 6 ] = fc.r; + colors[ i3 + 7 ] = fc.g; + colors[ i3 + 8 ] = fc.b; + + } else if ( vertexColors === THREE.VertexColors ) { + + var vca = face.vertexColors[ 0 ]; + var vcb = face.vertexColors[ 1 ]; + var vcc = face.vertexColors[ 2 ]; + + colors[ i3 ] = vca.r; + colors[ i3 + 1 ] = vca.g; + colors[ i3 + 2 ] = vca.b; + + colors[ i3 + 3 ] = vcb.r; + colors[ i3 + 4 ] = vcb.g; + colors[ i3 + 5 ] = vcb.b; + + colors[ i3 + 6 ] = vcc.r; + colors[ i3 + 7 ] = vcc.g; + colors[ i3 + 8 ] = vcc.b; + + } + + if ( hasFaceVertexUv === true ) { + + var uva = faceVertexUvs[ 0 ][ i ][ 0 ]; + var uvb = faceVertexUvs[ 0 ][ i ][ 1 ]; + var uvc = faceVertexUvs[ 0 ][ i ][ 2 ]; + + uvs[ i2 ] = uva.x; + uvs[ i2 + 1 ] = uva.y; + + uvs[ i2 + 2 ] = uvb.x; + uvs[ i2 + 3 ] = uvb.y; + + uvs[ i2 + 4 ] = uvc.x; + uvs[ i2 + 5 ] = uvc.y; + + } + + } + + this.computeBoundingSphere() + + return this; + + }, + + computeBoundingBox: function () { + + var vector = new THREE.Vector3(); + + return function () { + + if ( this.boundingBox === null ) { + + this.boundingBox = new THREE.Box3(); + + } + + var positions = this.attributes.position.array; + + if ( positions ) { + + var bb = this.boundingBox; + bb.makeEmpty(); + + for ( var i = 0, il = positions.length; i < il; i += 3 ) { + + vector.set( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ); + bb.expandByPoint( vector ); + + } + + } + + if ( positions === undefined || positions.length === 0 ) { + + this.boundingBox.min.set( 0, 0, 0 ); + this.boundingBox.max.set( 0, 0, 0 ); + + } + + if ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) { + + console.error( 'THREE.BufferGeometry.computeBoundingBox: Computed min/max have NaN values. The "position" attribute is likely to have NaN values.' ); + + } + + } + + }(), + + computeBoundingSphere: function () { + + var box = new THREE.Box3(); + var vector = new THREE.Vector3(); + + return function () { + + if ( this.boundingSphere === null ) { + + this.boundingSphere = new THREE.Sphere(); + + } + + var positions = this.attributes.position.array; + + if ( positions ) { + + box.makeEmpty(); + + var center = this.boundingSphere.center; + + for ( var i = 0, il = positions.length; i < il; i += 3 ) { + + vector.set( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ); + box.expandByPoint( vector ); + + } + + box.center( center ); + + // hoping to find a boundingSphere with a radius smaller than the + // boundingSphere of the boundingBox: sqrt(3) smaller in the best case + + var maxRadiusSq = 0; + + for ( var i = 0, il = positions.length; i < il; i += 3 ) { + + vector.set( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ); + maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( vector ) ); + + } + + this.boundingSphere.radius = Math.sqrt( maxRadiusSq ); + + if ( isNaN( this.boundingSphere.radius ) ) { + + console.error( 'THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.' ); + + } + + } + + } + + }(), + + computeFaceNormals: function () { + + // backwards compatibility + + }, + + computeVertexNormals: function () { + + var attributes = this.attributes; + + if ( attributes.position ) { + + var positions = attributes.position.array; + + if ( attributes.normal === undefined ) { + + this.addAttribute( 'normal', new THREE.BufferAttribute( new Float32Array( positions.length ), 3 ) ); + + } else { + + // reset existing normals to zero + + var normals = attributes.normal.array; + + for ( var i = 0, il = normals.length; i < il; i ++ ) { + + normals[ i ] = 0; + + } + + } + + var normals = attributes.normal.array; + + var vA, vB, vC, + + pA = new THREE.Vector3(), + pB = new THREE.Vector3(), + pC = new THREE.Vector3(), + + cb = new THREE.Vector3(), + ab = new THREE.Vector3(); + + // indexed elements + + if ( attributes.index ) { + + var indices = attributes.index.array; + + var offsets = ( this.offsets.length > 0 ? this.offsets : [ { start: 0, count: indices.length, index: 0 } ] ); + + for ( var j = 0, jl = offsets.length; j < jl; ++ j ) { + + var start = offsets[ j ].start; + var count = offsets[ j ].count; + var index = offsets[ j ].index; + + for ( var i = start, il = start + count; i < il; i += 3 ) { + + vA = ( index + indices[ i ] ) * 3; + vB = ( index + indices[ i + 1 ] ) * 3; + vC = ( index + indices[ i + 2 ] ) * 3; + + pA.fromArray( positions, vA ); + pB.fromArray( positions, vB ); + pC.fromArray( positions, vC ); + + cb.subVectors( pC, pB ); + ab.subVectors( pA, pB ); + cb.cross( ab ); + + normals[ vA ] += cb.x; + normals[ vA + 1 ] += cb.y; + normals[ vA + 2 ] += cb.z; + + normals[ vB ] += cb.x; + normals[ vB + 1 ] += cb.y; + normals[ vB + 2 ] += cb.z; + + normals[ vC ] += cb.x; + normals[ vC + 1 ] += cb.y; + normals[ vC + 2 ] += cb.z; + + } + + } + + } else { + + // non-indexed elements (unconnected triangle soup) + + for ( var i = 0, il = positions.length; i < il; i += 9 ) { + + pA.fromArray( positions, i ); + pB.fromArray( positions, i + 3 ); + pC.fromArray( positions, i + 6 ); + + cb.subVectors( pC, pB ); + ab.subVectors( pA, pB ); + cb.cross( ab ); + + normals[ i ] = cb.x; + normals[ i + 1 ] = cb.y; + normals[ i + 2 ] = cb.z; + + normals[ i + 3 ] = cb.x; + normals[ i + 4 ] = cb.y; + normals[ i + 5 ] = cb.z; + + normals[ i + 6 ] = cb.x; + normals[ i + 7 ] = cb.y; + normals[ i + 8 ] = cb.z; + + } + + } + + this.normalizeNormals(); + + attributes.normal.needsUpdate = true; + + } + + }, + + computeTangents: function () { + + // based on http://www.terathon.com/code/tangent.html + // (per vertex tangents) + + if ( this.attributes.index === undefined || + this.attributes.position === undefined || + this.attributes.normal === undefined || + this.attributes.uv === undefined ) { + + console.warn( 'Missing required attributes (index, position, normal or uv) in BufferGeometry.computeTangents()' ); + return; + + } + + var indices = this.attributes.index.array; + var positions = this.attributes.position.array; + var normals = this.attributes.normal.array; + var uvs = this.attributes.uv.array; + + var nVertices = positions.length / 3; + + if ( this.attributes.tangent === undefined ) { + + this.addAttribute( 'tangent', new THREE.BufferAttribute( new Float32Array( 4 * nVertices ), 4 ) ); + + } + + var tangents = this.attributes.tangent.array; + + var tan1 = [], tan2 = []; + + for ( var k = 0; k < nVertices; k ++ ) { + + tan1[ k ] = new THREE.Vector3(); + tan2[ k ] = new THREE.Vector3(); + + } + + var vA = new THREE.Vector3(), + vB = new THREE.Vector3(), + vC = new THREE.Vector3(), + + uvA = new THREE.Vector2(), + uvB = new THREE.Vector2(), + uvC = new THREE.Vector2(), + + x1, x2, y1, y2, z1, z2, + s1, s2, t1, t2, r; + + var sdir = new THREE.Vector3(), tdir = new THREE.Vector3(); + + function handleTriangle( a, b, c ) { + + vA.fromArray( positions, a * 3 ); + vB.fromArray( positions, b * 3 ); + vC.fromArray( positions, c * 3 ); + + uvA.fromArray( uvs, a * 2 ); + uvB.fromArray( uvs, b * 2 ); + uvC.fromArray( uvs, c * 2 ); + + x1 = vB.x - vA.x; + x2 = vC.x - vA.x; + + y1 = vB.y - vA.y; + y2 = vC.y - vA.y; + + z1 = vB.z - vA.z; + z2 = vC.z - vA.z; + + s1 = uvB.x - uvA.x; + s2 = uvC.x - uvA.x; + + t1 = uvB.y - uvA.y; + t2 = uvC.y - uvA.y; + + r = 1.0 / ( s1 * t2 - s2 * t1 ); + + sdir.set( + ( t2 * x1 - t1 * x2 ) * r, + ( t2 * y1 - t1 * y2 ) * r, + ( t2 * z1 - t1 * z2 ) * r + ); + + tdir.set( + ( s1 * x2 - s2 * x1 ) * r, + ( s1 * y2 - s2 * y1 ) * r, + ( s1 * z2 - s2 * z1 ) * r + ); + + tan1[ a ].add( sdir ); + tan1[ b ].add( sdir ); + tan1[ c ].add( sdir ); + + tan2[ a ].add( tdir ); + tan2[ b ].add( tdir ); + tan2[ c ].add( tdir ); + + } + + var i, il; + var j, jl; + var iA, iB, iC; + + if ( this.drawcalls.length === 0 ) { + + this.addDrawCall( 0, indices.length, 0 ); + + } + + var drawcalls = this.drawcalls; + + for ( j = 0, jl = drawcalls.length; j < jl; ++ j ) { + + var start = drawcalls[ j ].start; + var count = drawcalls[ j ].count; + var index = drawcalls[ j ].index; + + for ( i = start, il = start + count; i < il; i += 3 ) { + + iA = index + indices[ i ]; + iB = index + indices[ i + 1 ]; + iC = index + indices[ i + 2 ]; + + handleTriangle( iA, iB, iC ); + + } + + } + + var tmp = new THREE.Vector3(), tmp2 = new THREE.Vector3(); + var n = new THREE.Vector3(), n2 = new THREE.Vector3(); + var w, t, test; + + function handleVertex( v ) { + + n.fromArray( normals, v * 3 ); + n2.copy( n ); + + t = tan1[ v ]; + + // Gram-Schmidt orthogonalize + + tmp.copy( t ); + tmp.sub( n.multiplyScalar( n.dot( t ) ) ).normalize(); + + // Calculate handedness + + tmp2.crossVectors( n2, t ); + test = tmp2.dot( tan2[ v ] ); + w = ( test < 0.0 ) ? - 1.0 : 1.0; + + tangents[ v * 4 ] = tmp.x; + tangents[ v * 4 + 1 ] = tmp.y; + tangents[ v * 4 + 2 ] = tmp.z; + tangents[ v * 4 + 3 ] = w; + + } + + for ( j = 0, jl = drawcalls.length; j < jl; ++ j ) { + + var start = drawcalls[ j ].start; + var count = drawcalls[ j ].count; + var index = drawcalls[ j ].index; + + for ( i = start, il = start + count; i < il; i += 3 ) { + + iA = index + indices[ i ]; + iB = index + indices[ i + 1 ]; + iC = index + indices[ i + 2 ]; + + handleVertex( iA ); + handleVertex( iB ); + handleVertex( iC ); + + } + + } + + }, + + /* + computeOffsets + Compute the draw offset for large models by chunking the index buffer into chunks of 65k addressable vertices. + This method will effectively rewrite the index buffer and remap all attributes to match the new indices. + WARNING: This method will also expand the vertex count to prevent sprawled triangles across draw offsets. + indexBufferSize - Defaults to 65535, but allows for larger or smaller chunks. + */ + computeOffsets: function ( indexBufferSize ) { + + var size = indexBufferSize; + if ( indexBufferSize === undefined ) + size = 65535; //WebGL limits type of index buffer values to 16-bit. + + var s = Date.now(); + + var indices = this.attributes.index.array; + var vertices = this.attributes.position.array; + + var verticesCount = ( vertices.length / 3 ); + var facesCount = ( indices.length / 3 ); + + /* + console.log("Computing buffers in offsets of "+size+" -> indices:"+indices.length+" vertices:"+vertices.length); + console.log("Faces to process: "+(indices.length/3)); + console.log("Reordering "+verticesCount+" vertices."); + */ + + var sortedIndices = new Uint16Array( indices.length ); //16-bit buffers + var indexPtr = 0; + var vertexPtr = 0; + + var offsets = [ { start:0, count:0, index:0 } ]; + var offset = offsets[ 0 ]; + + var duplicatedVertices = 0; + var newVerticeMaps = 0; + var faceVertices = new Int32Array( 6 ); + var vertexMap = new Int32Array( vertices.length ); + var revVertexMap = new Int32Array( vertices.length ); + for ( var j = 0; j < vertices.length; j ++ ) { vertexMap[ j ] = - 1; revVertexMap[ j ] = - 1; } + + /* + Traverse every face and reorder vertices in the proper offsets of 65k. + We can have more than 65k entries in the index buffer per offset, but only reference 65k values. + */ + for ( var findex = 0; findex < facesCount; findex ++ ) { + newVerticeMaps = 0; + + for ( var vo = 0; vo < 3; vo ++ ) { + var vid = indices[ findex * 3 + vo ]; + if ( vertexMap[ vid ] == - 1 ) { + //Unmapped vertice + faceVertices[ vo * 2 ] = vid; + faceVertices[ vo * 2 + 1 ] = - 1; + newVerticeMaps ++; + } else if ( vertexMap[ vid ] < offset.index ) { + //Reused vertices from previous block (duplicate) + faceVertices[ vo * 2 ] = vid; + faceVertices[ vo * 2 + 1 ] = - 1; + duplicatedVertices ++; + } else { + //Reused vertice in the current block + faceVertices[ vo * 2 ] = vid; + faceVertices[ vo * 2 + 1 ] = vertexMap[ vid ]; + } + } + + var faceMax = vertexPtr + newVerticeMaps; + if ( faceMax > ( offset.index + size ) ) { + var new_offset = { start:indexPtr, count:0, index:vertexPtr }; + offsets.push( new_offset ); + offset = new_offset; + + //Re-evaluate reused vertices in light of new offset. + for ( var v = 0; v < 6; v += 2 ) { + var new_vid = faceVertices[ v + 1 ]; + if ( new_vid > - 1 && new_vid < offset.index ) + faceVertices[ v + 1 ] = - 1; + } + } + + //Reindex the face. + for ( var v = 0; v < 6; v += 2 ) { + var vid = faceVertices[ v ]; + var new_vid = faceVertices[ v + 1 ]; + + if ( new_vid === - 1 ) + new_vid = vertexPtr ++; + + vertexMap[ vid ] = new_vid; + revVertexMap[ new_vid ] = vid; + sortedIndices[ indexPtr ++ ] = new_vid - offset.index; //XXX overflows at 16bit + offset.count ++; + } + } + + /* Move all attribute values to map to the new computed indices , also expand the vertice stack to match our new vertexPtr. */ + this.reorderBuffers( sortedIndices, revVertexMap, vertexPtr ); + this.offsets = offsets; + + /* + var orderTime = Date.now(); + console.log("Reorder time: "+(orderTime-s)+"ms"); + console.log("Duplicated "+duplicatedVertices+" vertices."); + console.log("Compute Buffers time: "+(Date.now()-s)+"ms"); + console.log("Draw offsets: "+offsets.length); + */ + + return offsets; + + }, + + merge: function ( geometry, offset ) { + + if ( geometry instanceof THREE.BufferGeometry === false ) { + + console.error( 'THREE.BufferGeometry.merge(): geometry not an instance of THREE.BufferGeometry.', geometry ); + return; + + } + + if ( offset === undefined ) offset = 0; + + var attributes = this.attributes; + + for ( var key in attributes ) { + + if ( geometry.attributes[ key ] === undefined ) continue; + + var attribute1 = attributes[ key ]; + var attributeArray1 = attribute1.array; + + var attribute2 = geometry.attributes[ key ]; + var attributeArray2 = attribute2.array; + + var attributeSize = attribute2.itemSize; + + for ( var i = 0, j = attributeSize * offset; i < attributeArray2.length; i ++, j ++ ) { + + attributeArray1[ j ] = attributeArray2[ i ]; + + } + + } + + return this; + + }, + + normalizeNormals: function () { + + var normals = this.attributes.normal.array; + + var x, y, z, n; + + for ( var i = 0, il = normals.length; i < il; i += 3 ) { + + x = normals[ i ]; + y = normals[ i + 1 ]; + z = normals[ i + 2 ]; + + n = 1.0 / Math.sqrt( x * x + y * y + z * z ); + + normals[ i ] *= n; + normals[ i + 1 ] *= n; + normals[ i + 2 ] *= n; + + } + + }, + + /* + reoderBuffers: + Reorder attributes based on a new indexBuffer and indexMap. + indexBuffer - Uint16Array of the new ordered indices. + indexMap - Int32Array where the position is the new vertex ID and the value the old vertex ID for each vertex. + vertexCount - Amount of total vertices considered in this reordering (in case you want to grow the vertice stack). + */ + reorderBuffers: function ( indexBuffer, indexMap, vertexCount ) { + + /* Create a copy of all attributes for reordering. */ + var sortedAttributes = {}; + for ( var attr in this.attributes ) { + if ( attr == 'index' ) + continue; + var sourceArray = this.attributes[ attr ].array; + sortedAttributes[ attr ] = new sourceArray.constructor( this.attributes[ attr ].itemSize * vertexCount ); + } + + /* Move attribute positions based on the new index map */ + for ( var new_vid = 0; new_vid < vertexCount; new_vid ++ ) { + var vid = indexMap[ new_vid ]; + for ( var attr in this.attributes ) { + if ( attr == 'index' ) + continue; + var attrArray = this.attributes[ attr ].array; + var attrSize = this.attributes[ attr ].itemSize; + var sortedAttr = sortedAttributes[ attr ]; + for ( var k = 0; k < attrSize; k ++ ) + sortedAttr[ new_vid * attrSize + k ] = attrArray[ vid * attrSize + k ]; + } + } + + /* Carry the new sorted buffers locally */ + this.attributes[ 'index' ].array = indexBuffer; + for ( var attr in this.attributes ) { + if ( attr == 'index' ) + continue; + this.attributes[ attr ].array = sortedAttributes[ attr ]; + this.attributes[ attr ].numItems = this.attributes[ attr ].itemSize * vertexCount; + } + }, + + toJSON: function () { + + var output = { + metadata: { + version: 4.0, + type: 'BufferGeometry', + generator: 'BufferGeometryExporter' + }, + uuid: this.uuid, + type: this.type, + data: { + attributes: {} + } + }; + + var attributes = this.attributes; + var offsets = this.offsets; + var boundingSphere = this.boundingSphere; + + for ( var key in attributes ) { + + var attribute = attributes[ key ]; + + var array = [], typeArray = attribute.array; + + for ( var i = 0, l = typeArray.length; i < l; i ++ ) { + + array[ i ] = typeArray[ i ]; + + } + + output.data.attributes[ key ] = { + itemSize: attribute.itemSize, + type: attribute.array.constructor.name, + array: array + } + + } + + if ( offsets.length > 0 ) { + + output.data.offsets = JSON.parse( JSON.stringify( offsets ) ); + + } + + if ( boundingSphere !== null ) { + + output.data.boundingSphere = { + center: boundingSphere.center.toArray(), + radius: boundingSphere.radius + } + + } + + return output; + + }, + + clone: function () { + + var geometry = new THREE.BufferGeometry(); + + for ( var attr in this.attributes ) { + + var sourceAttr = this.attributes[ attr ]; + geometry.addAttribute( attr, sourceAttr.clone() ); + + } + + for ( var i = 0, il = this.offsets.length; i < il; i ++ ) { + + var offset = this.offsets[ i ]; + + geometry.offsets.push( { + + start: offset.start, + index: offset.index, + count: offset.count + + } ); + + } + + return geometry; + + }, + + dispose: function () { + + this.dispatchEvent( { type: 'dispose' } ); + + } + +}; + +THREE.EventDispatcher.prototype.apply( THREE.BufferGeometry.prototype ); + +// File:src/core/Geometry.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author kile / http://kile.stravaganza.org/ + * @author alteredq / http://alteredqualia.com/ + * @author mikael emtinger / http://gomo.se/ + * @author zz85 / http://www.lab4games.net/zz85/blog + * @author bhouston / http://exocortex.com + */ + +THREE.Geometry = function () { + + Object.defineProperty( this, 'id', { value: THREE.GeometryIdCount ++ } ); + + this.uuid = THREE.Math.generateUUID(); + + this.name = ''; + this.type = 'Geometry'; + + this.vertices = []; + this.colors = []; // one-to-one vertex colors, used in Points and Line + + this.faces = []; + + this.faceVertexUvs = [ [] ]; + + this.morphTargets = []; + this.morphColors = []; + this.morphNormals = []; + + this.skinWeights = []; + this.skinIndices = []; + + this.lineDistances = []; + + this.boundingBox = null; + this.boundingSphere = null; + + this.hasTangents = false; + + this.dynamic = true; // the intermediate typed arrays will be deleted when set to false + + // update flags + + this.verticesNeedUpdate = false; + this.elementsNeedUpdate = false; + this.uvsNeedUpdate = false; + this.normalsNeedUpdate = false; + this.tangentsNeedUpdate = false; + this.colorsNeedUpdate = false; + this.lineDistancesNeedUpdate = false; + + this.groupsNeedUpdate = false; + +}; + +THREE.Geometry.prototype = { + + constructor: THREE.Geometry, + + applyMatrix: function ( matrix ) { + + var normalMatrix = new THREE.Matrix3().getNormalMatrix( matrix ); + + for ( var i = 0, il = this.vertices.length; i < il; i ++ ) { + + var vertex = this.vertices[ i ]; + vertex.applyMatrix4( matrix ); + + } + + for ( var i = 0, il = this.faces.length; i < il; i ++ ) { + + var face = this.faces[ i ]; + face.normal.applyMatrix3( normalMatrix ).normalize(); + + for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { + + face.vertexNormals[ j ].applyMatrix3( normalMatrix ).normalize(); + + } + + } + + if ( this.boundingBox instanceof THREE.Box3 ) { + + this.computeBoundingBox(); + + } + + if ( this.boundingSphere instanceof THREE.Sphere ) { + + this.computeBoundingSphere(); + + } + + }, + + fromBufferGeometry: function ( geometry ) { + + var scope = this; + + var attributes = geometry.attributes; + + var vertices = attributes.position.array; + var indices = attributes.index !== undefined ? attributes.index.array : undefined; + var normals = attributes.normal !== undefined ? attributes.normal.array : undefined; + var colors = attributes.color !== undefined ? attributes.color.array : undefined; + var uvs = attributes.uv !== undefined ? attributes.uv.array : undefined; + + var tempNormals = []; + var tempUVs = []; + + for ( var i = 0, j = 0; i < vertices.length; i += 3, j += 2 ) { + + scope.vertices.push( new THREE.Vector3( vertices[ i ], vertices[ i + 1 ], vertices[ i + 2 ] ) ); + + if ( normals !== undefined ) { + + tempNormals.push( new THREE.Vector3( normals[ i ], normals[ i + 1 ], normals[ i + 2 ] ) ); + + } + + if ( colors !== undefined ) { + + scope.colors.push( new THREE.Color( colors[ i ], colors[ i + 1 ], colors[ i + 2 ] ) ); + + } + + if ( uvs !== undefined ) { + + tempUVs.push( new THREE.Vector2( uvs[ j ], uvs[ j + 1 ] ) ); + + } + + } + + var addFace = function ( a, b, c ) { + + var vertexNormals = normals !== undefined ? [ tempNormals[ a ].clone(), tempNormals[ b ].clone(), tempNormals[ c ].clone() ] : []; + var vertexColors = colors !== undefined ? [ scope.colors[ a ].clone(), scope.colors[ b ].clone(), scope.colors[ c ].clone() ] : []; + + scope.faces.push( new THREE.Face3( a, b, c, vertexNormals, vertexColors ) ); + + if ( uvs !== undefined ) { + + scope.faceVertexUvs[ 0 ].push( [ tempUVs[ a ].clone(), tempUVs[ b ].clone(), tempUVs[ c ].clone() ] ); + + } + + }; + + if ( indices !== undefined ) { + + for ( var i = 0; i < indices.length; i += 3 ) { + + addFace( indices[ i ], indices[ i + 1 ], indices[ i + 2 ] ); + + } + + } else { + + for ( var i = 0; i < vertices.length / 3; i += 3 ) { + + addFace( i, i + 1, i + 2 ); + + } + + } + + this.computeFaceNormals(); + + if ( geometry.boundingBox !== null ) { + + this.boundingBox = geometry.boundingBox.clone(); + + } + + if ( geometry.boundingSphere !== null ) { + + this.boundingSphere = geometry.boundingSphere.clone(); + + } + + return this; + + }, + + center: function () { + + this.computeBoundingBox(); + + var offset = new THREE.Vector3(); + + offset.addVectors( this.boundingBox.min, this.boundingBox.max ); + offset.multiplyScalar( - 0.5 ); + + this.applyMatrix( new THREE.Matrix4().makeTranslation( offset.x, offset.y, offset.z ) ); + this.computeBoundingBox(); + + return offset; + + }, + + computeFaceNormals: function () { + + var cb = new THREE.Vector3(), ab = new THREE.Vector3(); + + for ( var f = 0, fl = this.faces.length; f < fl; f ++ ) { + + var face = this.faces[ f ]; + + var vA = this.vertices[ face.a ]; + var vB = this.vertices[ face.b ]; + var vC = this.vertices[ face.c ]; + + cb.subVectors( vC, vB ); + ab.subVectors( vA, vB ); + cb.cross( ab ); + + cb.normalize(); + + face.normal.copy( cb ); + + } + + }, + + computeVertexNormals: function ( areaWeighted ) { + + var v, vl, f, fl, face, vertices; + + vertices = new Array( this.vertices.length ); + + for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { + + vertices[ v ] = new THREE.Vector3(); + + } + + if ( areaWeighted ) { + + // vertex normals weighted by triangle areas + // http://www.iquilezles.org/www/articles/normals/normals.htm + + var vA, vB, vC, vD; + var cb = new THREE.Vector3(), ab = new THREE.Vector3(), + db = new THREE.Vector3(), dc = new THREE.Vector3(), bc = new THREE.Vector3(); + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + + vA = this.vertices[ face.a ]; + vB = this.vertices[ face.b ]; + vC = this.vertices[ face.c ]; + + cb.subVectors( vC, vB ); + ab.subVectors( vA, vB ); + cb.cross( ab ); + + vertices[ face.a ].add( cb ); + vertices[ face.b ].add( cb ); + vertices[ face.c ].add( cb ); + + } + + } else { + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + + vertices[ face.a ].add( face.normal ); + vertices[ face.b ].add( face.normal ); + vertices[ face.c ].add( face.normal ); + + } + + } + + for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { + + vertices[ v ].normalize(); + + } + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + + face.vertexNormals[ 0 ] = vertices[ face.a ].clone(); + face.vertexNormals[ 1 ] = vertices[ face.b ].clone(); + face.vertexNormals[ 2 ] = vertices[ face.c ].clone(); + + } + + }, + + computeMorphNormals: function () { + + var i, il, f, fl, face; + + // save original normals + // - create temp variables on first access + // otherwise just copy (for faster repeated calls) + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + + if ( ! face.__originalFaceNormal ) { + + face.__originalFaceNormal = face.normal.clone(); + + } else { + + face.__originalFaceNormal.copy( face.normal ); + + } + + if ( ! face.__originalVertexNormals ) face.__originalVertexNormals = []; + + for ( i = 0, il = face.vertexNormals.length; i < il; i ++ ) { + + if ( ! face.__originalVertexNormals[ i ] ) { + + face.__originalVertexNormals[ i ] = face.vertexNormals[ i ].clone(); + + } else { + + face.__originalVertexNormals[ i ].copy( face.vertexNormals[ i ] ); + + } + + } + + } + + // use temp geometry to compute face and vertex normals for each morph + + var tmpGeo = new THREE.Geometry(); + tmpGeo.faces = this.faces; + + for ( i = 0, il = this.morphTargets.length; i < il; i ++ ) { + + // create on first access + + if ( ! this.morphNormals[ i ] ) { + + this.morphNormals[ i ] = {}; + this.morphNormals[ i ].faceNormals = []; + this.morphNormals[ i ].vertexNormals = []; + + var dstNormalsFace = this.morphNormals[ i ].faceNormals; + var dstNormalsVertex = this.morphNormals[ i ].vertexNormals; + + var faceNormal, vertexNormals; + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + faceNormal = new THREE.Vector3(); + vertexNormals = { a: new THREE.Vector3(), b: new THREE.Vector3(), c: new THREE.Vector3() }; + + dstNormalsFace.push( faceNormal ); + dstNormalsVertex.push( vertexNormals ); + + } + + } + + var morphNormals = this.morphNormals[ i ]; + + // set vertices to morph target + + tmpGeo.vertices = this.morphTargets[ i ].vertices; + + // compute morph normals + + tmpGeo.computeFaceNormals(); + tmpGeo.computeVertexNormals(); + + // store morph normals + + var faceNormal, vertexNormals; + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + + faceNormal = morphNormals.faceNormals[ f ]; + vertexNormals = morphNormals.vertexNormals[ f ]; + + faceNormal.copy( face.normal ); + + vertexNormals.a.copy( face.vertexNormals[ 0 ] ); + vertexNormals.b.copy( face.vertexNormals[ 1 ] ); + vertexNormals.c.copy( face.vertexNormals[ 2 ] ); + + } + + } + + // restore original normals + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + + face.normal = face.__originalFaceNormal; + face.vertexNormals = face.__originalVertexNormals; + + } + + }, + + computeTangents: function () { + + // based on http://www.terathon.com/code/tangent.html + // tangents go to vertices + + var f, fl, v, vl, i, il, vertexIndex, + face, uv, vA, vB, vC, uvA, uvB, uvC, + x1, x2, y1, y2, z1, z2, + s1, s2, t1, t2, r, t, test, + tan1 = [], tan2 = [], + sdir = new THREE.Vector3(), tdir = new THREE.Vector3(), + tmp = new THREE.Vector3(), tmp2 = new THREE.Vector3(), + n = new THREE.Vector3(), w; + + for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { + + tan1[ v ] = new THREE.Vector3(); + tan2[ v ] = new THREE.Vector3(); + + } + + function handleTriangle( context, a, b, c, ua, ub, uc ) { + + vA = context.vertices[ a ]; + vB = context.vertices[ b ]; + vC = context.vertices[ c ]; + + uvA = uv[ ua ]; + uvB = uv[ ub ]; + uvC = uv[ uc ]; + + x1 = vB.x - vA.x; + x2 = vC.x - vA.x; + y1 = vB.y - vA.y; + y2 = vC.y - vA.y; + z1 = vB.z - vA.z; + z2 = vC.z - vA.z; + + s1 = uvB.x - uvA.x; + s2 = uvC.x - uvA.x; + t1 = uvB.y - uvA.y; + t2 = uvC.y - uvA.y; + + r = 1.0 / ( s1 * t2 - s2 * t1 ); + sdir.set( ( t2 * x1 - t1 * x2 ) * r, + ( t2 * y1 - t1 * y2 ) * r, + ( t2 * z1 - t1 * z2 ) * r ); + tdir.set( ( s1 * x2 - s2 * x1 ) * r, + ( s1 * y2 - s2 * y1 ) * r, + ( s1 * z2 - s2 * z1 ) * r ); + + tan1[ a ].add( sdir ); + tan1[ b ].add( sdir ); + tan1[ c ].add( sdir ); + + tan2[ a ].add( tdir ); + tan2[ b ].add( tdir ); + tan2[ c ].add( tdir ); + + } + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + uv = this.faceVertexUvs[ 0 ][ f ]; // use UV layer 0 for tangents + + handleTriangle( this, face.a, face.b, face.c, 0, 1, 2 ); + + } + + var faceIndex = [ 'a', 'b', 'c', 'd' ]; + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + + for ( i = 0; i < Math.min( face.vertexNormals.length, 3 ); i ++ ) { + + n.copy( face.vertexNormals[ i ] ); + + vertexIndex = face[ faceIndex[ i ] ]; + + t = tan1[ vertexIndex ]; + + // Gram-Schmidt orthogonalize + + tmp.copy( t ); + tmp.sub( n.multiplyScalar( n.dot( t ) ) ).normalize(); + + // Calculate handedness + + tmp2.crossVectors( face.vertexNormals[ i ], t ); + test = tmp2.dot( tan2[ vertexIndex ] ); + w = ( test < 0.0 ) ? - 1.0 : 1.0; + + face.vertexTangents[ i ] = new THREE.Vector4( tmp.x, tmp.y, tmp.z, w ); + + } + + } + + this.hasTangents = true; + + }, + + computeLineDistances: function () { + + var d = 0; + var vertices = this.vertices; + + for ( var i = 0, il = vertices.length; i < il; i ++ ) { + + if ( i > 0 ) { + + d += vertices[ i ].distanceTo( vertices[ i - 1 ] ); + + } + + this.lineDistances[ i ] = d; + + } + + }, + + computeBoundingBox: function () { + + if ( this.boundingBox === null ) { + + this.boundingBox = new THREE.Box3(); + + } + + this.boundingBox.setFromPoints( this.vertices ); + + }, + + computeBoundingSphere: function () { + + if ( this.boundingSphere === null ) { + + this.boundingSphere = new THREE.Sphere(); + + } + + this.boundingSphere.setFromPoints( this.vertices ); + + }, + + merge: function ( geometry, matrix, materialIndexOffset ) { + + if ( geometry instanceof THREE.Geometry === false ) { + + console.error( 'THREE.Geometry.merge(): geometry not an instance of THREE.Geometry.', geometry ); + return; + + } + + var normalMatrix, + vertexOffset = this.vertices.length, + vertices1 = this.vertices, + vertices2 = geometry.vertices, + faces1 = this.faces, + faces2 = geometry.faces, + uvs1 = this.faceVertexUvs[ 0 ], + uvs2 = geometry.faceVertexUvs[ 0 ]; + + if ( materialIndexOffset === undefined ) materialIndexOffset = 0; + + if ( matrix !== undefined ) { + + normalMatrix = new THREE.Matrix3().getNormalMatrix( matrix ); + + } + + // vertices + + for ( var i = 0, il = vertices2.length; i < il; i ++ ) { + + var vertex = vertices2[ i ]; + + var vertexCopy = vertex.clone(); + + if ( matrix !== undefined ) vertexCopy.applyMatrix4( matrix ); + + vertices1.push( vertexCopy ); + + } + + // faces + + for ( i = 0, il = faces2.length; i < il; i ++ ) { + + var face = faces2[ i ], faceCopy, normal, color, + faceVertexNormals = face.vertexNormals, + faceVertexColors = face.vertexColors; + + faceCopy = new THREE.Face3( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset ); + faceCopy.normal.copy( face.normal ); + + if ( normalMatrix !== undefined ) { + + faceCopy.normal.applyMatrix3( normalMatrix ).normalize(); + + } + + for ( var j = 0, jl = faceVertexNormals.length; j < jl; j ++ ) { + + normal = faceVertexNormals[ j ].clone(); + + if ( normalMatrix !== undefined ) { + + normal.applyMatrix3( normalMatrix ).normalize(); + + } + + faceCopy.vertexNormals.push( normal ); + + } + + faceCopy.color.copy( face.color ); + + for ( var j = 0, jl = faceVertexColors.length; j < jl; j ++ ) { + + color = faceVertexColors[ j ]; + faceCopy.vertexColors.push( color.clone() ); + + } + + faceCopy.materialIndex = face.materialIndex + materialIndexOffset; + + faces1.push( faceCopy ); + + } + + // uvs + + for ( i = 0, il = uvs2.length; i < il; i ++ ) { + + var uv = uvs2[ i ], uvCopy = []; + + if ( uv === undefined ) { + + continue; + + } + + for ( var j = 0, jl = uv.length; j < jl; j ++ ) { + + uvCopy.push( uv[ j ].clone() ); + + } + + uvs1.push( uvCopy ); + + } + + }, + + mergeMesh: function ( mesh ) { + + if ( mesh instanceof THREE.Mesh === false ) { + + console.error( 'THREE.Geometry.mergeMesh(): mesh not an instance of THREE.Mesh.', mesh ); + return; + + } + + mesh.matrixAutoUpdate && mesh.updateMatrix(); + + this.merge( mesh.geometry, mesh.matrix ); + + }, + + /* + * Checks for duplicate vertices with hashmap. + * Duplicated vertices are removed + * and faces' vertices are updated. + */ + + mergeVertices: function () { + + var verticesMap = {}; // Hashmap for looking up vertice by position coordinates (and making sure they are unique) + var unique = [], changes = []; + + var v, key; + var precisionPoints = 4; // number of decimal points, eg. 4 for epsilon of 0.0001 + var precision = Math.pow( 10, precisionPoints ); + var i,il, face; + var indices, k, j, jl, u; + + for ( i = 0, il = this.vertices.length; i < il; i ++ ) { + + v = this.vertices[ i ]; + key = Math.round( v.x * precision ) + '_' + Math.round( v.y * precision ) + '_' + Math.round( v.z * precision ); + + if ( verticesMap[ key ] === undefined ) { + + verticesMap[ key ] = i; + unique.push( this.vertices[ i ] ); + changes[ i ] = unique.length - 1; + + } else { + + //console.log('Duplicate vertex found. ', i, ' could be using ', verticesMap[key]); + changes[ i ] = changes[ verticesMap[ key ] ]; + + } + + }; + + + // if faces are completely degenerate after merging vertices, we + // have to remove them from the geometry. + var faceIndicesToRemove = []; + + for ( i = 0, il = this.faces.length; i < il; i ++ ) { + + face = this.faces[ i ]; + + face.a = changes[ face.a ]; + face.b = changes[ face.b ]; + face.c = changes[ face.c ]; + + indices = [ face.a, face.b, face.c ]; + + var dupIndex = - 1; + + // if any duplicate vertices are found in a Face3 + // we have to remove the face as nothing can be saved + for ( var n = 0; n < 3; n ++ ) { + if ( indices[ n ] == indices[ ( n + 1 ) % 3 ] ) { + + dupIndex = n; + faceIndicesToRemove.push( i ); + break; + + } + } + + } + + for ( i = faceIndicesToRemove.length - 1; i >= 0; i -- ) { + var idx = faceIndicesToRemove[ i ]; + + this.faces.splice( idx, 1 ); + + for ( j = 0, jl = this.faceVertexUvs.length; j < jl; j ++ ) { + + this.faceVertexUvs[ j ].splice( idx, 1 ); + + } + + } + + // Use unique set of vertices + + var diff = this.vertices.length - unique.length; + this.vertices = unique; + return diff; + + }, + + toJSON: function () { + + var output = { + metadata: { + version: 4.0, + type: 'BufferGeometry', + generator: 'BufferGeometryExporter' + }, + uuid: this.uuid, + type: this.type + }; + + if ( this.name !== "" ) output.name = this.name; + + if ( this.parameters !== undefined ) { + + var parameters = this.parameters; + + for ( var key in parameters ) { + + if ( parameters[ key ] !== undefined ) output[ key ] = parameters[ key ]; + + } + + return output; + + } + + var vertices = []; + + for ( var i = 0; i < this.vertices.length; i ++ ) { + + var vertex = this.vertices[ i ]; + vertices.push( vertex.x, vertex.y, vertex.z ); + + } + + var faces = []; + var normals = []; + var normalsHash = {}; + var colors = []; + var colorsHash = {}; + var uvs = []; + var uvsHash = {}; + + for ( var i = 0; i < this.faces.length; i ++ ) { + + var face = this.faces[ i ]; + + var hasMaterial = false; // face.materialIndex !== undefined; + var hasFaceUv = false; // deprecated + var hasFaceVertexUv = this.faceVertexUvs[ 0 ][ i ] !== undefined; + var hasFaceNormal = face.normal.length() > 0; + var hasFaceVertexNormal = face.vertexNormals.length > 0; + var hasFaceColor = face.color.r !== 1 || face.color.g !== 1 || face.color.b !== 1; + var hasFaceVertexColor = face.vertexColors.length > 0; + + var faceType = 0; + + faceType = setBit( faceType, 0, 0 ); + faceType = setBit( faceType, 1, hasMaterial ); + faceType = setBit( faceType, 2, hasFaceUv ); + faceType = setBit( faceType, 3, hasFaceVertexUv ); + faceType = setBit( faceType, 4, hasFaceNormal ); + faceType = setBit( faceType, 5, hasFaceVertexNormal ); + faceType = setBit( faceType, 6, hasFaceColor ); + faceType = setBit( faceType, 7, hasFaceVertexColor ); + + faces.push( faceType ); + faces.push( face.a, face.b, face.c ); + + + /* + if ( hasMaterial ) { + + faces.push( face.materialIndex ); + + } + */ + + if ( hasFaceVertexUv ) { + + var faceVertexUvs = this.faceVertexUvs[ 0 ][ i ]; + + faces.push( + getUvIndex( faceVertexUvs[ 0 ] ), + getUvIndex( faceVertexUvs[ 1 ] ), + getUvIndex( faceVertexUvs[ 2 ] ) + ); + + } + + if ( hasFaceNormal ) { + + faces.push( getNormalIndex( face.normal ) ); + + } + + if ( hasFaceVertexNormal ) { + + var vertexNormals = face.vertexNormals; + + faces.push( + getNormalIndex( vertexNormals[ 0 ] ), + getNormalIndex( vertexNormals[ 1 ] ), + getNormalIndex( vertexNormals[ 2 ] ) + ); + + } + + if ( hasFaceColor ) { + + faces.push( getColorIndex( face.color ) ); + + } + + if ( hasFaceVertexColor ) { + + var vertexColors = face.vertexColors; + + faces.push( + getColorIndex( vertexColors[ 0 ] ), + getColorIndex( vertexColors[ 1 ] ), + getColorIndex( vertexColors[ 2 ] ) + ); + + } + + } + + function setBit( value, position, enabled ) { + + return enabled ? value | ( 1 << position ) : value & ( ~ ( 1 << position) ); + + } + + function getNormalIndex( normal ) { + + var hash = normal.x.toString() + normal.y.toString() + normal.z.toString(); + + if ( normalsHash[ hash ] !== undefined ) { + + return normalsHash[ hash ]; + + } + + normalsHash[ hash ] = normals.length / 3; + normals.push( normal.x, normal.y, normal.z ); + + return normalsHash[ hash ]; + + } + + function getColorIndex( color ) { + + var hash = color.r.toString() + color.g.toString() + color.b.toString(); + + if ( colorsHash[ hash ] !== undefined ) { + + return colorsHash[ hash ]; + + } + + colorsHash[ hash ] = colors.length; + colors.push( color.getHex() ); + + return colorsHash[ hash ]; + + } + + function getUvIndex( uv ) { + + var hash = uv.x.toString() + uv.y.toString(); + + if ( uvsHash[ hash ] !== undefined ) { + + return uvsHash[ hash ]; + + } + + uvsHash[ hash ] = uvs.length / 2; + uvs.push( uv.x, uv.y ); + + return uvsHash[ hash ]; + + } + + output.data = {}; + + output.data.vertices = vertices; + output.data.normals = normals; + if ( colors.length > 0 ) output.data.colors = colors; + if ( uvs.length > 0 ) output.data.uvs = [ uvs ]; // temporal backward compatibility + output.data.faces = faces; + + // + + return output; + + }, + + clone: function () { + + var geometry = new THREE.Geometry(); + + var vertices = this.vertices; + + for ( var i = 0, il = vertices.length; i < il; i ++ ) { + + geometry.vertices.push( vertices[ i ].clone() ); + + } + + var faces = this.faces; + + for ( var i = 0, il = faces.length; i < il; i ++ ) { + + geometry.faces.push( faces[ i ].clone() ); + + } + + for ( var i = 0, il = this.faceVertexUvs.length; i < il; i ++ ) { + + var faceVertexUvs = this.faceVertexUvs[ i ]; + + if ( geometry.faceVertexUvs[ i ] === undefined ) { + + geometry.faceVertexUvs[ i ] = []; + + } + + for ( var j = 0, jl = faceVertexUvs.length; j < jl; j ++ ) { + + var uvs = faceVertexUvs[ j ], uvsCopy = []; + + for ( var k = 0, kl = uvs.length; k < kl; k ++ ) { + + var uv = uvs[ k ]; + + uvsCopy.push( uv.clone() ); + + } + + geometry.faceVertexUvs[ i ].push( uvsCopy ); + + } + + } + + return geometry; + + }, + + dispose: function () { + + this.dispatchEvent( { type: 'dispose' } ); + + } + +}; + +THREE.EventDispatcher.prototype.apply( THREE.Geometry.prototype ); + +THREE.GeometryIdCount = 0; + +// File:src/cameras/Camera.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author mikael emtinger / http://gomo.se/ + * @author WestLangley / http://github.com/WestLangley +*/ + +THREE.Camera = function () { + + THREE.Object3D.call( this ); + + this.type = 'Camera'; + + this.matrixWorldInverse = new THREE.Matrix4(); + this.projectionMatrix = new THREE.Matrix4(); + +}; + +THREE.Camera.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Camera.prototype.constructor = THREE.Camera; + +THREE.Camera.prototype.getWorldDirection = function () { + + var quaternion = new THREE.Quaternion(); + + return function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + + this.getWorldQuaternion( quaternion ); + + return result.set( 0, 0, - 1 ).applyQuaternion( quaternion ); + + } + +}(); + +THREE.Camera.prototype.lookAt = function () { + + // This routine does not support cameras with rotated and/or translated parent(s) + + var m1 = new THREE.Matrix4(); + + return function ( vector ) { + + m1.lookAt( this.position, vector, this.up ); + + this.quaternion.setFromRotationMatrix( m1 ); + + }; + +}(); + +THREE.Camera.prototype.clone = function ( camera ) { + + if ( camera === undefined ) camera = new THREE.Camera(); + + THREE.Object3D.prototype.clone.call( this, camera ); + + camera.matrixWorldInverse.copy( this.matrixWorldInverse ); + camera.projectionMatrix.copy( this.projectionMatrix ); + + return camera; +}; + +// File:src/cameras/CubeCamera.js + +/** + * Camera for rendering cube maps + * - renders scene into axis-aligned cube + * + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.CubeCamera = function ( near, far, cubeResolution ) { + + THREE.Object3D.call( this ); + + this.type = 'CubeCamera'; + + var fov = 90, aspect = 1; + + var cameraPX = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraPX.up.set( 0, - 1, 0 ); + cameraPX.lookAt( new THREE.Vector3( 1, 0, 0 ) ); + this.add( cameraPX ); + + var cameraNX = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraNX.up.set( 0, - 1, 0 ); + cameraNX.lookAt( new THREE.Vector3( - 1, 0, 0 ) ); + this.add( cameraNX ); + + var cameraPY = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraPY.up.set( 0, 0, 1 ); + cameraPY.lookAt( new THREE.Vector3( 0, 1, 0 ) ); + this.add( cameraPY ); + + var cameraNY = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraNY.up.set( 0, 0, - 1 ); + cameraNY.lookAt( new THREE.Vector3( 0, - 1, 0 ) ); + this.add( cameraNY ); + + var cameraPZ = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraPZ.up.set( 0, - 1, 0 ); + cameraPZ.lookAt( new THREE.Vector3( 0, 0, 1 ) ); + this.add( cameraPZ ); + + var cameraNZ = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraNZ.up.set( 0, - 1, 0 ); + cameraNZ.lookAt( new THREE.Vector3( 0, 0, - 1 ) ); + this.add( cameraNZ ); + + this.renderTarget = new THREE.WebGLRenderTargetCube( cubeResolution, cubeResolution, { format: THREE.RGBFormat, magFilter: THREE.LinearFilter, minFilter: THREE.LinearFilter } ); + + this.updateCubeMap = function ( renderer, scene ) { + + var renderTarget = this.renderTarget; + var generateMipmaps = renderTarget.generateMipmaps; + + renderTarget.generateMipmaps = false; + + renderTarget.activeCubeFace = 0; + renderer.render( scene, cameraPX, renderTarget ); + + renderTarget.activeCubeFace = 1; + renderer.render( scene, cameraNX, renderTarget ); + + renderTarget.activeCubeFace = 2; + renderer.render( scene, cameraPY, renderTarget ); + + renderTarget.activeCubeFace = 3; + renderer.render( scene, cameraNY, renderTarget ); + + renderTarget.activeCubeFace = 4; + renderer.render( scene, cameraPZ, renderTarget ); + + renderTarget.generateMipmaps = generateMipmaps; + + renderTarget.activeCubeFace = 5; + renderer.render( scene, cameraNZ, renderTarget ); + + }; + +}; + +THREE.CubeCamera.prototype = Object.create( THREE.Object3D.prototype ); +THREE.CubeCamera.prototype.constructor = THREE.CubeCamera; + +// File:src/cameras/OrthographicCamera.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.OrthographicCamera = function ( left, right, top, bottom, near, far ) { + + THREE.Camera.call( this ); + + this.type = 'OrthographicCamera'; + + this.zoom = 1; + + this.left = left; + this.right = right; + this.top = top; + this.bottom = bottom; + + this.near = ( near !== undefined ) ? near : 0.1; + this.far = ( far !== undefined ) ? far : 2000; + + this.updateProjectionMatrix(); + +}; + +THREE.OrthographicCamera.prototype = Object.create( THREE.Camera.prototype ); +THREE.OrthographicCamera.prototype.constructor = THREE.OrthographicCamera; + +THREE.OrthographicCamera.prototype.updateProjectionMatrix = function () { + + var dx = ( this.right - this.left ) / ( 2 * this.zoom ); + var dy = ( this.top - this.bottom ) / ( 2 * this.zoom ); + var cx = ( this.right + this.left ) / 2; + var cy = ( this.top + this.bottom ) / 2; + + this.projectionMatrix.makeOrthographic( cx - dx, cx + dx, cy + dy, cy - dy, this.near, this.far ); + +}; + +THREE.OrthographicCamera.prototype.clone = function () { + + var camera = new THREE.OrthographicCamera(); + + THREE.Camera.prototype.clone.call( this, camera ); + + camera.zoom = this.zoom; + + camera.left = this.left; + camera.right = this.right; + camera.top = this.top; + camera.bottom = this.bottom; + + camera.near = this.near; + camera.far = this.far; + + camera.projectionMatrix.copy( this.projectionMatrix ); + + return camera; +}; + +// File:src/cameras/PerspectiveCamera.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author greggman / http://games.greggman.com/ + * @author zz85 / http://www.lab4games.net/zz85/blog + */ + +THREE.PerspectiveCamera = function ( fov, aspect, near, far ) { + + THREE.Camera.call( this ); + + this.type = 'PerspectiveCamera'; + + this.zoom = 1; + + this.fov = fov !== undefined ? fov : 50; + this.aspect = aspect !== undefined ? aspect : 1; + this.near = near !== undefined ? near : 0.1; + this.far = far !== undefined ? far : 2000; + + this.updateProjectionMatrix(); + +}; + +THREE.PerspectiveCamera.prototype = Object.create( THREE.Camera.prototype ); +THREE.PerspectiveCamera.prototype.constructor = THREE.PerspectiveCamera; + + +/** + * Uses Focal Length (in mm) to estimate and set FOV + * 35mm (fullframe) camera is used if frame size is not specified; + * Formula based on http://www.bobatkins.com/photography/technical/field_of_view.html + */ + +THREE.PerspectiveCamera.prototype.setLens = function ( focalLength, frameHeight ) { + + if ( frameHeight === undefined ) frameHeight = 24; + + this.fov = 2 * THREE.Math.radToDeg( Math.atan( frameHeight / ( focalLength * 2 ) ) ); + this.updateProjectionMatrix(); + +} + + +/** + * Sets an offset in a larger frustum. This is useful for multi-window or + * multi-monitor/multi-machine setups. + * + * For example, if you have 3x2 monitors and each monitor is 1920x1080 and + * the monitors are in grid like this + * + * +---+---+---+ + * | A | B | C | + * +---+---+---+ + * | D | E | F | + * +---+---+---+ + * + * then for each monitor you would call it like this + * + * var w = 1920; + * var h = 1080; + * var fullWidth = w * 3; + * var fullHeight = h * 2; + * + * --A-- + * camera.setOffset( fullWidth, fullHeight, w * 0, h * 0, w, h ); + * --B-- + * camera.setOffset( fullWidth, fullHeight, w * 1, h * 0, w, h ); + * --C-- + * camera.setOffset( fullWidth, fullHeight, w * 2, h * 0, w, h ); + * --D-- + * camera.setOffset( fullWidth, fullHeight, w * 0, h * 1, w, h ); + * --E-- + * camera.setOffset( fullWidth, fullHeight, w * 1, h * 1, w, h ); + * --F-- + * camera.setOffset( fullWidth, fullHeight, w * 2, h * 1, w, h ); + * + * Note there is no reason monitors have to be the same size or in a grid. + */ + +THREE.PerspectiveCamera.prototype.setViewOffset = function ( fullWidth, fullHeight, x, y, width, height ) { + + this.fullWidth = fullWidth; + this.fullHeight = fullHeight; + this.x = x; + this.y = y; + this.width = width; + this.height = height; + + this.updateProjectionMatrix(); + +}; + + +THREE.PerspectiveCamera.prototype.updateProjectionMatrix = function () { + + var fov = THREE.Math.radToDeg( 2 * Math.atan( Math.tan( THREE.Math.degToRad( this.fov ) * 0.5 ) / this.zoom ) ); + + if ( this.fullWidth ) { + + var aspect = this.fullWidth / this.fullHeight; + var top = Math.tan( THREE.Math.degToRad( fov * 0.5 ) ) * this.near; + var bottom = - top; + var left = aspect * bottom; + var right = aspect * top; + var width = Math.abs( right - left ); + var height = Math.abs( top - bottom ); + + this.projectionMatrix.makeFrustum( + left + this.x * width / this.fullWidth, + left + ( this.x + this.width ) * width / this.fullWidth, + top - ( this.y + this.height ) * height / this.fullHeight, + top - this.y * height / this.fullHeight, + this.near, + this.far + ); + + } else { + + this.projectionMatrix.makePerspective( fov, this.aspect, this.near, this.far ); + + } + +}; + +THREE.PerspectiveCamera.prototype.clone = function () { + + var camera = new THREE.PerspectiveCamera(); + + THREE.Camera.prototype.clone.call( this, camera ); + + camera.zoom = this.zoom; + + camera.fov = this.fov; + camera.aspect = this.aspect; + camera.near = this.near; + camera.far = this.far; + + camera.projectionMatrix.copy( this.projectionMatrix ); + + return camera; + +}; + +// File:src/lights/Light.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Light = function ( color ) { + + THREE.Object3D.call( this ); + + this.type = 'Light'; + + this.color = new THREE.Color( color ); + +}; + +THREE.Light.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Light.prototype.constructor = THREE.Light; + +THREE.Light.prototype.clone = function ( light ) { + + if ( light === undefined ) light = new THREE.Light(); + + THREE.Object3D.prototype.clone.call( this, light ); + + light.color.copy( this.color ); + + return light; + +}; + +// File:src/lights/AmbientLight.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.AmbientLight = function ( color ) { + + THREE.Light.call( this, color ); + + this.type = 'AmbientLight'; + +}; + +THREE.AmbientLight.prototype = Object.create( THREE.Light.prototype ); +THREE.AmbientLight.prototype.constructor = THREE.AmbientLight; + +THREE.AmbientLight.prototype.clone = function () { + + var light = new THREE.AmbientLight(); + + THREE.Light.prototype.clone.call( this, light ); + + return light; + +}; + +// File:src/lights/AreaLight.js + +/** + * @author MPanknin / http://www.redplant.de/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.AreaLight = function ( color, intensity ) { + + THREE.Light.call( this, color ); + + this.type = 'AreaLight'; + + this.normal = new THREE.Vector3( 0, - 1, 0 ); + this.right = new THREE.Vector3( 1, 0, 0 ); + + this.intensity = ( intensity !== undefined ) ? intensity : 1; + + this.width = 1.0; + this.height = 1.0; + + this.constantAttenuation = 1.5; + this.linearAttenuation = 0.5; + this.quadraticAttenuation = 0.1; + +}; + +THREE.AreaLight.prototype = Object.create( THREE.Light.prototype ); +THREE.AreaLight.prototype.constructor = THREE.AreaLight; + + +// File:src/lights/DirectionalLight.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.DirectionalLight = function ( color, intensity ) { + + THREE.Light.call( this, color ); + + this.type = 'DirectionalLight'; + + this.position.set( 0, 1, 0 ); + this.target = new THREE.Object3D(); + + this.intensity = ( intensity !== undefined ) ? intensity : 1; + + this.castShadow = false; + this.onlyShadow = false; + + // + + this.shadowCameraNear = 50; + this.shadowCameraFar = 5000; + + this.shadowCameraLeft = - 500; + this.shadowCameraRight = 500; + this.shadowCameraTop = 500; + this.shadowCameraBottom = - 500; + + this.shadowCameraVisible = false; + + this.shadowBias = 0; + this.shadowDarkness = 0.5; + + this.shadowMapWidth = 512; + this.shadowMapHeight = 512; + + // + + this.shadowCascade = false; + + this.shadowCascadeOffset = new THREE.Vector3( 0, 0, - 1000 ); + this.shadowCascadeCount = 2; + + this.shadowCascadeBias = [ 0, 0, 0 ]; + this.shadowCascadeWidth = [ 512, 512, 512 ]; + this.shadowCascadeHeight = [ 512, 512, 512 ]; + + this.shadowCascadeNearZ = [ - 1.000, 0.990, 0.998 ]; + this.shadowCascadeFarZ = [ 0.990, 0.998, 1.000 ]; + + this.shadowCascadeArray = []; + + // + + this.shadowMap = null; + this.shadowMapSize = null; + this.shadowCamera = null; + this.shadowMatrix = null; + +}; + +THREE.DirectionalLight.prototype = Object.create( THREE.Light.prototype ); +THREE.DirectionalLight.prototype.constructor = THREE.DirectionalLight; + +THREE.DirectionalLight.prototype.clone = function () { + + var light = new THREE.DirectionalLight(); + + THREE.Light.prototype.clone.call( this, light ); + + light.target = this.target.clone(); + + light.intensity = this.intensity; + + light.castShadow = this.castShadow; + light.onlyShadow = this.onlyShadow; + + // + + light.shadowCameraNear = this.shadowCameraNear; + light.shadowCameraFar = this.shadowCameraFar; + + light.shadowCameraLeft = this.shadowCameraLeft; + light.shadowCameraRight = this.shadowCameraRight; + light.shadowCameraTop = this.shadowCameraTop; + light.shadowCameraBottom = this.shadowCameraBottom; + + light.shadowCameraVisible = this.shadowCameraVisible; + + light.shadowBias = this.shadowBias; + light.shadowDarkness = this.shadowDarkness; + + light.shadowMapWidth = this.shadowMapWidth; + light.shadowMapHeight = this.shadowMapHeight; + + // + + light.shadowCascade = this.shadowCascade; + + light.shadowCascadeOffset.copy( this.shadowCascadeOffset ); + light.shadowCascadeCount = this.shadowCascadeCount; + + light.shadowCascadeBias = this.shadowCascadeBias.slice( 0 ); + light.shadowCascadeWidth = this.shadowCascadeWidth.slice( 0 ); + light.shadowCascadeHeight = this.shadowCascadeHeight.slice( 0 ); + + light.shadowCascadeNearZ = this.shadowCascadeNearZ.slice( 0 ); + light.shadowCascadeFarZ = this.shadowCascadeFarZ.slice( 0 ); + + return light; + +}; + +// File:src/lights/HemisphereLight.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.HemisphereLight = function ( skyColor, groundColor, intensity ) { + + THREE.Light.call( this, skyColor ); + + this.type = 'HemisphereLight'; + + this.position.set( 0, 100, 0 ); + + this.groundColor = new THREE.Color( groundColor ); + this.intensity = ( intensity !== undefined ) ? intensity : 1; + +}; + +THREE.HemisphereLight.prototype = Object.create( THREE.Light.prototype ); +THREE.HemisphereLight.prototype.constructor = THREE.HemisphereLight; + +THREE.HemisphereLight.prototype.clone = function () { + + var light = new THREE.HemisphereLight(); + + THREE.Light.prototype.clone.call( this, light ); + + light.groundColor.copy( this.groundColor ); + light.intensity = this.intensity; + + return light; + +}; + +// File:src/lights/PointLight.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.PointLight = function ( color, intensity, distance ) { + + THREE.Light.call( this, color ); + + this.type = 'PointLight'; + + this.intensity = ( intensity !== undefined ) ? intensity : 1; + this.distance = ( distance !== undefined ) ? distance : 0; + +}; + +THREE.PointLight.prototype = Object.create( THREE.Light.prototype ); +THREE.PointLight.prototype.constructor = THREE.PointLight; + +THREE.PointLight.prototype.clone = function () { + + var light = new THREE.PointLight(); + + THREE.Light.prototype.clone.call( this, light ); + + light.intensity = this.intensity; + light.distance = this.distance; + + return light; + +}; + +// File:src/lights/SpotLight.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.SpotLight = function ( color, intensity, distance, angle, exponent ) { + + THREE.Light.call( this, color ); + + this.type = 'SpotLight'; + + this.position.set( 0, 1, 0 ); + this.target = new THREE.Object3D(); + + this.intensity = ( intensity !== undefined ) ? intensity : 1; + this.distance = ( distance !== undefined ) ? distance : 0; + this.angle = ( angle !== undefined ) ? angle : Math.PI / 3; + this.exponent = ( exponent !== undefined ) ? exponent : 10; + + this.castShadow = false; + this.onlyShadow = false; + + // + + this.shadowCameraNear = 50; + this.shadowCameraFar = 5000; + this.shadowCameraFov = 50; + + this.shadowCameraVisible = false; + + this.shadowBias = 0; + this.shadowDarkness = 0.5; + + this.shadowMapWidth = 512; + this.shadowMapHeight = 512; + + // + + this.shadowMap = null; + this.shadowMapSize = null; + this.shadowCamera = null; + this.shadowMatrix = null; + +}; + +THREE.SpotLight.prototype = Object.create( THREE.Light.prototype ); +THREE.SpotLight.prototype.constructor = THREE.SpotLight; + +THREE.SpotLight.prototype.clone = function () { + + var light = new THREE.SpotLight(); + + THREE.Light.prototype.clone.call( this, light ); + + light.target = this.target.clone(); + + light.intensity = this.intensity; + light.distance = this.distance; + light.angle = this.angle; + light.exponent = this.exponent; + + light.castShadow = this.castShadow; + light.onlyShadow = this.onlyShadow; + + // + + light.shadowCameraNear = this.shadowCameraNear; + light.shadowCameraFar = this.shadowCameraFar; + light.shadowCameraFov = this.shadowCameraFov; + + light.shadowCameraVisible = this.shadowCameraVisible; + + light.shadowBias = this.shadowBias; + light.shadowDarkness = this.shadowDarkness; + + light.shadowMapWidth = this.shadowMapWidth; + light.shadowMapHeight = this.shadowMapHeight; + + return light; + +}; + +// File:src/loaders/Cache.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Cache = function () { + + this.files = {}; + +}; + +THREE.Cache.prototype = { + + constructor: THREE.Cache, + + add: function ( key, file ) { + + // console.log( 'THREE.Cache', 'Adding key:', key ); + + this.files[ key ] = file; + + }, + + get: function ( key ) { + + // console.log( 'THREE.Cache', 'Checking key:', key ); + + return this.files[ key ]; + + }, + + remove: function ( key ) { + + delete this.files[ key ]; + + }, + + clear: function () { + + this.files = {} + + } + +}; + +// File:src/loaders/Loader.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Loader = function ( showStatus ) { + + this.showStatus = showStatus; + this.statusDomElement = showStatus ? THREE.Loader.prototype.addStatusElement() : null; + + this.imageLoader = new THREE.ImageLoader(); + + this.onLoadStart = function () {}; + this.onLoadProgress = function () {}; + this.onLoadComplete = function () {}; + +}; + +THREE.Loader.prototype = { + + constructor: THREE.Loader, + + crossOrigin: undefined, + + addStatusElement: function () { + + var e = document.createElement( 'div' ); + + e.style.position = 'absolute'; + e.style.right = '0px'; + e.style.top = '0px'; + e.style.fontSize = '0.8em'; + e.style.textAlign = 'left'; + e.style.background = 'rgba(0,0,0,0.25)'; + e.style.color = '#fff'; + e.style.width = '120px'; + e.style.padding = '0.5em 0.5em 0.5em 0.5em'; + e.style.zIndex = 1000; + + e.innerHTML = 'Loading ...'; + + return e; + + }, + + updateProgress: function ( progress ) { + + var message = 'Loaded '; + + if ( progress.total ) { + + message += ( 100 * progress.loaded / progress.total ).toFixed( 0 ) + '%'; + + + } else { + + message += ( progress.loaded / 1024 ).toFixed( 2 ) + ' KB'; + + } + + this.statusDomElement.innerHTML = message; + + }, + + extractUrlBase: function ( url ) { + + var parts = url.split( '/' ); + + if ( parts.length === 1 ) return './'; + + parts.pop(); + + return parts.join( '/' ) + '/'; + + }, + + initMaterials: function ( materials, texturePath ) { + + var array = []; + + for ( var i = 0; i < materials.length; ++ i ) { + + array[ i ] = this.createMaterial( materials[ i ], texturePath ); + + } + + return array; + + }, + + needsTangents: function ( materials ) { + + for ( var i = 0, il = materials.length; i < il; i ++ ) { + + var m = materials[ i ]; + + if ( m instanceof THREE.ShaderMaterial ) return true; + + } + + return false; + + }, + + createMaterial: function ( m, texturePath ) { + + var scope = this; + + function nearest_pow2( n ) { + + var l = Math.log( n ) / Math.LN2; + return Math.pow( 2, Math.round( l ) ); + + } + + function create_texture( where, name, sourceFile, repeat, offset, wrap, anisotropy ) { + + var fullPath = texturePath + sourceFile; + + var texture; + + var loader = THREE.Loader.Handlers.get( fullPath ); + + if ( loader !== null ) { + + texture = loader.load( fullPath ); + + } else { + + texture = new THREE.Texture(); + + loader = scope.imageLoader; + loader.crossOrigin = scope.crossOrigin; + loader.load( fullPath, function ( image ) { + + if ( THREE.Math.isPowerOfTwo( image.width ) === false || + THREE.Math.isPowerOfTwo( image.height ) === false ) { + + var width = nearest_pow2( image.width ); + var height = nearest_pow2( image.height ); + + var canvas = document.createElement( 'canvas' ); + canvas.width = width; + canvas.height = height; + + var context = canvas.getContext( '2d' ); + context.drawImage( image, 0, 0, width, height ); + + texture.image = canvas; + + } else { + + texture.image = image; + + } + + texture.needsUpdate = true; + + } ); + + } + + texture.sourceFile = sourceFile; + + if ( repeat ) { + + texture.repeat.set( repeat[ 0 ], repeat[ 1 ] ); + + if ( repeat[ 0 ] !== 1 ) texture.wrapS = THREE.RepeatWrapping; + if ( repeat[ 1 ] !== 1 ) texture.wrapT = THREE.RepeatWrapping; + + } + + if ( offset ) { + + texture.offset.set( offset[ 0 ], offset[ 1 ] ); + + } + + if ( wrap ) { + + var wrapMap = { + 'repeat': THREE.RepeatWrapping, + 'mirror': THREE.MirroredRepeatWrapping + } + + if ( wrapMap[ wrap[ 0 ] ] !== undefined ) texture.wrapS = wrapMap[ wrap[ 0 ] ]; + if ( wrapMap[ wrap[ 1 ] ] !== undefined ) texture.wrapT = wrapMap[ wrap[ 1 ] ]; + + } + + if ( anisotropy ) { + + texture.anisotropy = anisotropy; + + } + + where[ name ] = texture; + + } + + function rgb2hex( rgb ) { + + return ( rgb[ 0 ] * 255 << 16 ) + ( rgb[ 1 ] * 255 << 8 ) + rgb[ 2 ] * 255; + + } + + // defaults + + var mtype = 'MeshLambertMaterial'; + var mpars = { color: 0xeeeeee, opacity: 1.0, map: null, lightMap: null, normalMap: null, bumpMap: null, wireframe: false }; + + // parameters from model file + + if ( m.shading ) { + + var shading = m.shading.toLowerCase(); + + if ( shading === 'phong' ) mtype = 'MeshPhongMaterial'; + else if ( shading === 'basic' ) mtype = 'MeshBasicMaterial'; + + } + + if ( m.blending !== undefined && THREE[ m.blending ] !== undefined ) { + + mpars.blending = THREE[ m.blending ]; + + } + + if ( m.transparent !== undefined || m.opacity < 1.0 ) { + + mpars.transparent = m.transparent; + + } + + if ( m.depthTest !== undefined ) { + + mpars.depthTest = m.depthTest; + + } + + if ( m.depthWrite !== undefined ) { + + mpars.depthWrite = m.depthWrite; + + } + + if ( m.visible !== undefined ) { + + mpars.visible = m.visible; + + } + + if ( m.flipSided !== undefined ) { + + mpars.side = THREE.BackSide; + + } + + if ( m.doubleSided !== undefined ) { + + mpars.side = THREE.DoubleSide; + + } + + if ( m.wireframe !== undefined ) { + + mpars.wireframe = m.wireframe; + + } + + if ( m.vertexColors !== undefined ) { + + if ( m.vertexColors === 'face' ) { + + mpars.vertexColors = THREE.FaceColors; + + } else if ( m.vertexColors ) { + + mpars.vertexColors = THREE.VertexColors; + + } + + } + + // colors + + if ( m.colorDiffuse ) { + + mpars.color = rgb2hex( m.colorDiffuse ); + + } else if ( m.DbgColor ) { + + mpars.color = m.DbgColor; + + } + + if ( m.colorSpecular ) { + + mpars.specular = rgb2hex( m.colorSpecular ); + + } + + if ( m.colorAmbient ) { + + mpars.ambient = rgb2hex( m.colorAmbient ); + + } + + if ( m.colorEmissive ) { + + mpars.emissive = rgb2hex( m.colorEmissive ); + + } + + // modifiers + + if ( m.transparency ) { + + mpars.opacity = m.transparency; + + } + + if ( m.specularCoef ) { + + mpars.shininess = m.specularCoef; + + } + + // textures + + if ( m.mapDiffuse && texturePath ) { + + create_texture( mpars, 'map', m.mapDiffuse, m.mapDiffuseRepeat, m.mapDiffuseOffset, m.mapDiffuseWrap, m.mapDiffuseAnisotropy ); + + } + + if ( m.mapLight && texturePath ) { + + create_texture( mpars, 'lightMap', m.mapLight, m.mapLightRepeat, m.mapLightOffset, m.mapLightWrap, m.mapLightAnisotropy ); + + } + + if ( m.mapBump && texturePath ) { + + create_texture( mpars, 'bumpMap', m.mapBump, m.mapBumpRepeat, m.mapBumpOffset, m.mapBumpWrap, m.mapBumpAnisotropy ); + + } + + if ( m.mapNormal && texturePath ) { + + create_texture( mpars, 'normalMap', m.mapNormal, m.mapNormalRepeat, m.mapNormalOffset, m.mapNormalWrap, m.mapNormalAnisotropy ); + + } + + if ( m.mapSpecular && texturePath ) { + + create_texture( mpars, 'specularMap', m.mapSpecular, m.mapSpecularRepeat, m.mapSpecularOffset, m.mapSpecularWrap, m.mapSpecularAnisotropy ); + + } + + if ( m.mapAlpha && texturePath ) { + + create_texture( mpars, 'alphaMap', m.mapAlpha, m.mapAlphaRepeat, m.mapAlphaOffset, m.mapAlphaWrap, m.mapAlphaAnisotropy ); + + } + + // + + if ( m.mapBumpScale ) { + + mpars.bumpScale = m.mapBumpScale; + + } + + if ( m.mapNormalFactor ) { + + mpars.normalScale = new THREE.Vector2( m.mapNormalFactor, m.mapNormalFactor ); + + } + + var material = new THREE[ mtype ]( mpars ); + + if ( m.DbgName !== undefined ) material.name = m.DbgName; + + return material; + + } + +}; + +THREE.Loader.Handlers = { + + handlers: [], + + add: function ( regex, loader ) { + + this.handlers.push( regex, loader ); + + }, + + get: function ( file ) { + + for ( var i = 0, l = this.handlers.length; i < l; i += 2 ) { + + var regex = this.handlers[ i ]; + var loader = this.handlers[ i + 1 ]; + + if ( regex.test( file ) ) { + + return loader; + + } + + } + + return null; + + } + +}; + +// File:src/loaders/XHRLoader.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.XHRLoader = function ( manager ) { + + this.cache = new THREE.Cache(); + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.XHRLoader.prototype = { + + constructor: THREE.XHRLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var cached = scope.cache.get( url ); + + if ( cached !== undefined ) { + + if ( onLoad ) onLoad( cached ); + return; + + } + + var request = new XMLHttpRequest(); + request.open( 'GET', url, true ); + + request.addEventListener( 'load', function ( event ) { + + scope.cache.add( url, this.response ); + + if ( onLoad ) onLoad( this.response ); + + scope.manager.itemEnd( url ); + + }, false ); + + if ( onProgress !== undefined ) { + + request.addEventListener( 'progress', function ( event ) { + + onProgress( event ); + + }, false ); + + } + + if ( onError !== undefined ) { + + request.addEventListener( 'error', function ( event ) { + + onError( event ); + + }, false ); + + } + + if ( this.crossOrigin !== undefined ) request.crossOrigin = this.crossOrigin; + if ( this.responseType !== undefined ) request.responseType = this.responseType; + + request.send( null ); + + scope.manager.itemStart( url ); + + }, + + setResponseType: function ( value ) { + + this.responseType = value; + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + } + +}; + +// File:src/loaders/ImageLoader.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.ImageLoader = function ( manager ) { + + this.cache = new THREE.Cache(); + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.ImageLoader.prototype = { + + constructor: THREE.ImageLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var cached = scope.cache.get( url ); + + if ( cached !== undefined ) { + + onLoad( cached ); + return; + + } + + var image = document.createElement( 'img' ); + + if ( onLoad !== undefined ) { + + image.addEventListener( 'load', function ( event ) { + + scope.cache.add( url, this ); + + onLoad( this ); + scope.manager.itemEnd( url ); + + }, false ); + + } + + if ( onProgress !== undefined ) { + + image.addEventListener( 'progress', function ( event ) { + + onProgress( event ); + + }, false ); + + } + + if ( onError !== undefined ) { + + image.addEventListener( 'error', function ( event ) { + + onError( event ); + + }, false ); + + } + + if ( this.crossOrigin !== undefined ) image.crossOrigin = this.crossOrigin; + + image.src = url; + + scope.manager.itemStart( url ); + + return image; + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + } + +} + +// File:src/loaders/JSONLoader.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.JSONLoader = function ( showStatus ) { + + THREE.Loader.call( this, showStatus ); + + this.withCredentials = false; + +}; + +THREE.JSONLoader.prototype = Object.create( THREE.Loader.prototype ); +THREE.JSONLoader.prototype.constructor = THREE.JSONLoader; + +THREE.JSONLoader.prototype.load = function ( url, callback, texturePath ) { + + var scope = this; + + // todo: unify load API to for easier SceneLoader use + + texturePath = texturePath && ( typeof texturePath === 'string' ) ? texturePath : this.extractUrlBase( url ); + + this.onLoadStart(); + this.loadAjaxJSON( this, url, callback, texturePath ); + +}; + +THREE.JSONLoader.prototype.loadAjaxJSON = function ( context, url, callback, texturePath, callbackProgress ) { + + var xhr = new XMLHttpRequest(); + + var length = 0; + + xhr.onreadystatechange = function () { + + if ( xhr.readyState === xhr.DONE ) { + + if ( xhr.status === 200 || xhr.status === 0 ) { + + if ( xhr.responseText ) { + + var json = JSON.parse( xhr.responseText ); + + if ( json.metadata !== undefined && json.metadata.type === 'scene' ) { + + console.error( 'THREE.JSONLoader: "' + url + '" seems to be a Scene. Use THREE.SceneLoader instead.' ); + return; + + } + + var result = context.parse( json, texturePath ); + callback( result.geometry, result.materials ); + + } else { + + console.error( 'THREE.JSONLoader: "' + url + '" seems to be unreachable or the file is empty.' ); + + } + + // in context of more complex asset initialization + // do not block on single failed file + // maybe should go even one more level up + + context.onLoadComplete(); + + } else { + + console.error( 'THREE.JSONLoader: Couldn\'t load "' + url + '" (' + xhr.status + ')' ); + + } + + } else if ( xhr.readyState === xhr.LOADING ) { + + if ( callbackProgress ) { + + if ( length === 0 ) { + + length = xhr.getResponseHeader( 'Content-Length' ); + + } + + callbackProgress( { total: length, loaded: xhr.responseText.length } ); + + } + + } else if ( xhr.readyState === xhr.HEADERS_RECEIVED ) { + + if ( callbackProgress !== undefined ) { + + length = xhr.getResponseHeader( 'Content-Length' ); + + } + + } + + }; + + xhr.open( 'GET', url, true ); + xhr.withCredentials = this.withCredentials; + xhr.send( null ); + +}; + +THREE.JSONLoader.prototype.parse = function ( json, texturePath ) { + + var scope = this, + geometry = new THREE.Geometry(), + scale = ( json.scale !== undefined ) ? 1.0 / json.scale : 1.0; + + parseModel( scale ); + + parseSkin(); + parseMorphing( scale ); + + geometry.computeFaceNormals(); + geometry.computeBoundingSphere(); + + function parseModel( scale ) { + + function isBitSet( value, position ) { + + return value & ( 1 << position ); + + } + + var i, j, fi, + + offset, zLength, + + colorIndex, normalIndex, uvIndex, materialIndex, + + type, + isQuad, + hasMaterial, + hasFaceVertexUv, + hasFaceNormal, hasFaceVertexNormal, + hasFaceColor, hasFaceVertexColor, + + vertex, face, faceA, faceB, color, hex, normal, + + uvLayer, uv, u, v, + + faces = json.faces, + vertices = json.vertices, + normals = json.normals, + colors = json.colors, + + nUvLayers = 0; + + if ( json.uvs !== undefined ) { + + // disregard empty arrays + + for ( i = 0; i < json.uvs.length; i ++ ) { + + if ( json.uvs[ i ].length ) nUvLayers ++; + + } + + for ( i = 0; i < nUvLayers; i ++ ) { + + geometry.faceVertexUvs[ i ] = []; + + } + + } + + offset = 0; + zLength = vertices.length; + + while ( offset < zLength ) { + + vertex = new THREE.Vector3(); + + vertex.x = vertices[ offset ++ ] * scale; + vertex.y = vertices[ offset ++ ] * scale; + vertex.z = vertices[ offset ++ ] * scale; + + geometry.vertices.push( vertex ); + + } + + offset = 0; + zLength = faces.length; + + while ( offset < zLength ) { + + type = faces[ offset ++ ]; + + + isQuad = isBitSet( type, 0 ); + hasMaterial = isBitSet( type, 1 ); + hasFaceVertexUv = isBitSet( type, 3 ); + hasFaceNormal = isBitSet( type, 4 ); + hasFaceVertexNormal = isBitSet( type, 5 ); + hasFaceColor = isBitSet( type, 6 ); + hasFaceVertexColor = isBitSet( type, 7 ); + + // console.log("type", type, "bits", isQuad, hasMaterial, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor); + + if ( isQuad ) { + + faceA = new THREE.Face3(); + faceA.a = faces[ offset ]; + faceA.b = faces[ offset + 1 ]; + faceA.c = faces[ offset + 3 ]; + + faceB = new THREE.Face3(); + faceB.a = faces[ offset + 1 ]; + faceB.b = faces[ offset + 2 ]; + faceB.c = faces[ offset + 3 ]; + + offset += 4; + + if ( hasMaterial ) { + + materialIndex = faces[ offset ++ ]; + faceA.materialIndex = materialIndex; + faceB.materialIndex = materialIndex; + + } + + // to get face <=> uv index correspondence + + fi = geometry.faces.length; + + if ( hasFaceVertexUv ) { + + for ( i = 0; i < nUvLayers; i ++ ) { + + uvLayer = json.uvs[ i ]; + + geometry.faceVertexUvs[ i ][ fi ] = []; + geometry.faceVertexUvs[ i ][ fi + 1 ] = [] + + for ( j = 0; j < 4; j ++ ) { + + uvIndex = faces[ offset ++ ]; + + u = uvLayer[ uvIndex * 2 ]; + v = uvLayer[ uvIndex * 2 + 1 ]; + + uv = new THREE.Vector2( u, v ); + + if ( j !== 2 ) geometry.faceVertexUvs[ i ][ fi ].push( uv ); + if ( j !== 0 ) geometry.faceVertexUvs[ i ][ fi + 1 ].push( uv ); + + } + + } + + } + + if ( hasFaceNormal ) { + + normalIndex = faces[ offset ++ ] * 3; + + faceA.normal.set( + normals[ normalIndex ++ ], + normals[ normalIndex ++ ], + normals[ normalIndex ] + ); + + faceB.normal.copy( faceA.normal ); + + } + + if ( hasFaceVertexNormal ) { + + for ( i = 0; i < 4; i ++ ) { + + normalIndex = faces[ offset ++ ] * 3; + + normal = new THREE.Vector3( + normals[ normalIndex ++ ], + normals[ normalIndex ++ ], + normals[ normalIndex ] + ); + + + if ( i !== 2 ) faceA.vertexNormals.push( normal ); + if ( i !== 0 ) faceB.vertexNormals.push( normal ); + + } + + } + + + if ( hasFaceColor ) { + + colorIndex = faces[ offset ++ ]; + hex = colors[ colorIndex ]; + + faceA.color.setHex( hex ); + faceB.color.setHex( hex ); + + } + + + if ( hasFaceVertexColor ) { + + for ( i = 0; i < 4; i ++ ) { + + colorIndex = faces[ offset ++ ]; + hex = colors[ colorIndex ]; + + if ( i !== 2 ) faceA.vertexColors.push( new THREE.Color( hex ) ); + if ( i !== 0 ) faceB.vertexColors.push( new THREE.Color( hex ) ); + + } + + } + + geometry.faces.push( faceA ); + geometry.faces.push( faceB ); + + } else { + + face = new THREE.Face3(); + face.a = faces[ offset ++ ]; + face.b = faces[ offset ++ ]; + face.c = faces[ offset ++ ]; + + if ( hasMaterial ) { + + materialIndex = faces[ offset ++ ]; + face.materialIndex = materialIndex; + + } + + // to get face <=> uv index correspondence + + fi = geometry.faces.length; + + if ( hasFaceVertexUv ) { + + for ( i = 0; i < nUvLayers; i ++ ) { + + uvLayer = json.uvs[ i ]; + + geometry.faceVertexUvs[ i ][ fi ] = []; + + for ( j = 0; j < 3; j ++ ) { + + uvIndex = faces[ offset ++ ]; + + u = uvLayer[ uvIndex * 2 ]; + v = uvLayer[ uvIndex * 2 + 1 ]; + + uv = new THREE.Vector2( u, v ); + + geometry.faceVertexUvs[ i ][ fi ].push( uv ); + + } + + } + + } + + if ( hasFaceNormal ) { + + normalIndex = faces[ offset ++ ] * 3; + + face.normal.set( + normals[ normalIndex ++ ], + normals[ normalIndex ++ ], + normals[ normalIndex ] + ); + + } + + if ( hasFaceVertexNormal ) { + + for ( i = 0; i < 3; i ++ ) { + + normalIndex = faces[ offset ++ ] * 3; + + normal = new THREE.Vector3( + normals[ normalIndex ++ ], + normals[ normalIndex ++ ], + normals[ normalIndex ] + ); + + face.vertexNormals.push( normal ); + + } + + } + + + if ( hasFaceColor ) { + + colorIndex = faces[ offset ++ ]; + face.color.setHex( colors[ colorIndex ] ); + + } + + + if ( hasFaceVertexColor ) { + + for ( i = 0; i < 3; i ++ ) { + + colorIndex = faces[ offset ++ ]; + face.vertexColors.push( new THREE.Color( colors[ colorIndex ] ) ); + + } + + } + + geometry.faces.push( face ); + + } + + } + + }; + + function parseSkin() { + var influencesPerVertex = ( json.influencesPerVertex !== undefined ) ? json.influencesPerVertex : 2; + + if ( json.skinWeights ) { + + for ( var i = 0, l = json.skinWeights.length; i < l; i += influencesPerVertex ) { + + var x = json.skinWeights[ i ]; + var y = ( influencesPerVertex > 1 ) ? json.skinWeights[ i + 1 ] : 0; + var z = ( influencesPerVertex > 2 ) ? json.skinWeights[ i + 2 ] : 0; + var w = ( influencesPerVertex > 3 ) ? json.skinWeights[ i + 3 ] : 0; + + geometry.skinWeights.push( new THREE.Vector4( x, y, z, w ) ); + + } + + } + + if ( json.skinIndices ) { + + for ( var i = 0, l = json.skinIndices.length; i < l; i += influencesPerVertex ) { + + var a = json.skinIndices[ i ]; + var b = ( influencesPerVertex > 1 ) ? json.skinIndices[ i + 1 ] : 0; + var c = ( influencesPerVertex > 2 ) ? json.skinIndices[ i + 2 ] : 0; + var d = ( influencesPerVertex > 3 ) ? json.skinIndices[ i + 3 ] : 0; + + geometry.skinIndices.push( new THREE.Vector4( a, b, c, d ) ); + + } + + } + + geometry.bones = json.bones; + + if ( geometry.bones && geometry.bones.length > 0 && ( geometry.skinWeights.length !== geometry.skinIndices.length || geometry.skinIndices.length !== geometry.vertices.length ) ) { + + console.warn( 'When skinning, number of vertices (' + geometry.vertices.length + '), skinIndices (' + + geometry.skinIndices.length + '), and skinWeights (' + geometry.skinWeights.length + ') should match.' ); + + } + + + // could change this to json.animations[0] or remove completely + + geometry.animation = json.animation; + geometry.animations = json.animations; + + }; + + function parseMorphing( scale ) { + + if ( json.morphTargets !== undefined ) { + + var i, l, v, vl, dstVertices, srcVertices; + + for ( i = 0, l = json.morphTargets.length; i < l; i ++ ) { + + geometry.morphTargets[ i ] = {}; + geometry.morphTargets[ i ].name = json.morphTargets[ i ].name; + geometry.morphTargets[ i ].vertices = []; + + dstVertices = geometry.morphTargets[ i ].vertices; + srcVertices = json.morphTargets [ i ].vertices; + + for ( v = 0, vl = srcVertices.length; v < vl; v += 3 ) { + + var vertex = new THREE.Vector3(); + vertex.x = srcVertices[ v ] * scale; + vertex.y = srcVertices[ v + 1 ] * scale; + vertex.z = srcVertices[ v + 2 ] * scale; + + dstVertices.push( vertex ); + + } + + } + + } + + if ( json.morphColors !== undefined ) { + + var i, l, c, cl, dstColors, srcColors, color; + + for ( i = 0, l = json.morphColors.length; i < l; i ++ ) { + + geometry.morphColors[ i ] = {}; + geometry.morphColors[ i ].name = json.morphColors[ i ].name; + geometry.morphColors[ i ].colors = []; + + dstColors = geometry.morphColors[ i ].colors; + srcColors = json.morphColors [ i ].colors; + + for ( c = 0, cl = srcColors.length; c < cl; c += 3 ) { + + color = new THREE.Color( 0xffaa00 ); + color.setRGB( srcColors[ c ], srcColors[ c + 1 ], srcColors[ c + 2 ] ); + dstColors.push( color ); + + } + + } + + } + + }; + + if ( json.materials === undefined || json.materials.length === 0 ) { + + return { geometry: geometry }; + + } else { + + var materials = this.initMaterials( json.materials, texturePath ); + + if ( this.needsTangents( materials ) ) { + + geometry.computeTangents(); + + } + + return { geometry: geometry, materials: materials }; + + } + +}; + +// File:src/loaders/LoadingManager.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.LoadingManager = function ( onLoad, onProgress, onError ) { + + var scope = this; + + var loaded = 0, total = 0; + + this.onLoad = onLoad; + this.onProgress = onProgress; + this.onError = onError; + + this.itemStart = function ( url ) { + + total ++; + + }; + + this.itemEnd = function ( url ) { + + loaded ++; + + if ( scope.onProgress !== undefined ) { + + scope.onProgress( url, loaded, total ); + + } + + if ( loaded === total && scope.onLoad !== undefined ) { + + scope.onLoad(); + + } + + }; + +}; + +THREE.DefaultLoadingManager = new THREE.LoadingManager(); + +// File:src/loaders/BufferGeometryLoader.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.BufferGeometryLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.BufferGeometryLoader.prototype = { + + constructor: THREE.BufferGeometryLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new THREE.XHRLoader( scope.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.load( url, function ( text ) { + + onLoad( scope.parse( JSON.parse( text ) ) ); + + }, onProgress, onError ); + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + }, + + parse: function ( json ) { + + var geometry = new THREE.BufferGeometry(); + + var attributes = json.attributes; + + for ( var key in attributes ) { + + var attribute = attributes[ key ]; + var typedArray = new self[ attribute.type ]( attribute.array ); + + geometry.addAttribute( key, new THREE.BufferAttribute( typedArray, attribute.itemSize ) ); + + } + + var offsets = json.offsets; + + if ( offsets !== undefined ) { + + geometry.offsets = JSON.parse( JSON.stringify( offsets ) ); + + } + + var boundingSphere = json.boundingSphere; + + if ( boundingSphere !== undefined ) { + + var center = new THREE.Vector3(); + + if ( boundingSphere.center !== undefined ) { + + center.fromArray( boundingSphere.center ); + + } + + geometry.boundingSphere = new THREE.Sphere( center, boundingSphere.radius ); + + } + + return geometry; + + } + +}; + +// File:src/loaders/MaterialLoader.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.MaterialLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.MaterialLoader.prototype = { + + constructor: THREE.MaterialLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new THREE.XHRLoader( scope.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.load( url, function ( text ) { + + onLoad( scope.parse( JSON.parse( text ) ) ); + + }, onProgress, onError ); + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + }, + + parse: function ( json ) { + + var material = new THREE[ json.type ]; + + if ( json.color !== undefined ) material.color.setHex( json.color ); + if ( json.ambient !== undefined ) material.ambient.setHex( json.ambient ); + if ( json.emissive !== undefined ) material.emissive.setHex( json.emissive ); + if ( json.specular !== undefined ) material.specular.setHex( json.specular ); + if ( json.shininess !== undefined ) material.shininess = json.shininess; + if ( json.uniforms !== undefined ) material.uniforms = json.uniforms; + if ( json.vertexShader !== undefined ) material.vertexShader = json.vertexShader; + if ( json.fragmentShader !== undefined ) material.fragmentShader = json.fragmentShader; + if ( json.vertexColors !== undefined ) material.vertexColors = json.vertexColors; + if ( json.shading !== undefined ) material.shading = json.shading; + if ( json.blending !== undefined ) material.blending = json.blending; + if ( json.side !== undefined ) material.side = json.side; + if ( json.opacity !== undefined ) material.opacity = json.opacity; + if ( json.transparent !== undefined ) material.transparent = json.transparent; + if ( json.wireframe !== undefined ) material.wireframe = json.wireframe; + + if ( json.materials !== undefined ) { + + for ( var i = 0, l = json.materials.length; i < l; i ++ ) { + + material.materials.push( this.parse( json.materials[ i ] ) ); + + } + + } + + return material; + + } + +}; + +// File:src/loaders/ObjectLoader.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.ObjectLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.ObjectLoader.prototype = { + + constructor: THREE.ObjectLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new THREE.XHRLoader( scope.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.load( url, function ( text ) { + + onLoad( scope.parse( JSON.parse( text ) ) ); + + }, onProgress, onError ); + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + }, + + parse: function ( json ) { + + var geometries = this.parseGeometries( json.geometries ); + var materials = this.parseMaterials( json.materials ); + var object = this.parseObject( json.object, geometries, materials ); + + return object; + + }, + + parseGeometries: function ( json ) { + + var geometries = {}; + + if ( json !== undefined ) { + + var geometryLoader = new THREE.JSONLoader(); + var bufferGeometryLoader = new THREE.BufferGeometryLoader(); + + for ( var i = 0, l = json.length; i < l; i ++ ) { + + var geometry; + var data = json[ i ]; + + switch ( data.type ) { + + case 'PlaneGeometry': + + geometry = new THREE.PlaneGeometry( + data.width, + data.height, + data.widthSegments, + data.heightSegments + ); + + break; + + case 'BoxGeometry': + case 'CubeGeometry': // backwards compatible + + geometry = new THREE.BoxGeometry( + data.width, + data.height, + data.depth, + data.widthSegments, + data.heightSegments, + data.depthSegments + ); + + break; + + case 'CircleGeometry': + + geometry = new THREE.CircleGeometry( + data.radius, + data.segments + ); + + break; + + case 'CylinderGeometry': + + geometry = new THREE.CylinderGeometry( + data.radiusTop, + data.radiusBottom, + data.height, + data.radialSegments, + data.heightSegments, + data.openEnded + ); + + break; + + case 'SphereGeometry': + + geometry = new THREE.SphereGeometry( + data.radius, + data.widthSegments, + data.heightSegments, + data.phiStart, + data.phiLength, + data.thetaStart, + data.thetaLength + ); + + break; + + case 'IcosahedronGeometry': + + geometry = new THREE.IcosahedronGeometry( + data.radius, + data.detail + ); + + break; + + case 'TorusGeometry': + + geometry = new THREE.TorusGeometry( + data.radius, + data.tube, + data.radialSegments, + data.tubularSegments, + data.arc + ); + + break; + + case 'TorusKnotGeometry': + + geometry = new THREE.TorusKnotGeometry( + data.radius, + data.tube, + data.radialSegments, + data.tubularSegments, + data.p, + data.q, + data.heightScale + ); + + break; + + case 'BufferGeometry': + + geometry = bufferGeometryLoader.parse( data.data ); + + break; + + case 'Geometry': + + geometry = geometryLoader.parse( data.data ).geometry; + + break; + + } + + geometry.uuid = data.uuid; + + if ( data.name !== undefined ) geometry.name = data.name; + + geometries[ data.uuid ] = geometry; + + } + + } + + return geometries; + + }, + + parseMaterials: function ( json ) { + + var materials = {}; + + if ( json !== undefined ) { + + var loader = new THREE.MaterialLoader(); + + for ( var i = 0, l = json.length; i < l; i ++ ) { + + var data = json[ i ]; + var material = loader.parse( data ); + + material.uuid = data.uuid; + + if ( data.name !== undefined ) material.name = data.name; + + materials[ data.uuid ] = material; + + } + + } + + return materials; + + }, + + parseObject: function () { + + var matrix = new THREE.Matrix4(); + + return function ( data, geometries, materials ) { + + var object; + + switch ( data.type ) { + + case 'Scene': + + object = new THREE.Scene(); + + break; + + case 'PerspectiveCamera': + + object = new THREE.PerspectiveCamera( data.fov, data.aspect, data.near, data.far ); + + break; + + case 'OrthographicCamera': + + object = new THREE.OrthographicCamera( data.left, data.right, data.top, data.bottom, data.near, data.far ); + + break; + + case 'AmbientLight': + + object = new THREE.AmbientLight( data.color ); + + break; + + case 'DirectionalLight': + + object = new THREE.DirectionalLight( data.color, data.intensity ); + + break; + + case 'PointLight': + + object = new THREE.PointLight( data.color, data.intensity, data.distance ); + + break; + + case 'SpotLight': + + object = new THREE.SpotLight( data.color, data.intensity, data.distance, data.angle, data.exponent ); + + break; + + case 'HemisphereLight': + + object = new THREE.HemisphereLight( data.color, data.groundColor, data.intensity ); + + break; + + case 'Mesh': + + var geometry = geometries[ data.geometry ]; + var material = materials[ data.material ]; + + if ( geometry === undefined ) { + + console.warn( 'THREE.ObjectLoader: Undefined geometry', data.geometry ); + + } + + if ( material === undefined ) { + + console.warn( 'THREE.ObjectLoader: Undefined material', data.material ); + + } + + object = new THREE.Mesh( geometry, material ); + + break; + + case 'Line': + + var geometry = geometries[ data.geometry ]; + var material = materials[ data.material ]; + + if ( geometry === undefined ) { + + console.warn( 'THREE.ObjectLoader: Undefined geometry', data.geometry ); + + } + + if ( material === undefined ) { + + console.warn( 'THREE.ObjectLoader: Undefined material', data.material ); + + } + + object = new THREE.Line( geometry, material ); + + break; + + case 'Sprite': + + var material = materials[ data.material ]; + + if ( material === undefined ) { + + console.warn( 'THREE.ObjectLoader: Undefined material', data.material ); + + } + + object = new THREE.Sprite( material ); + + break; + + case 'Group': + + object = new THREE.Group(); + + break; + + default: + + object = new THREE.Object3D(); + + } + + object.uuid = data.uuid; + + if ( data.name !== undefined ) object.name = data.name; + if ( data.matrix !== undefined ) { + + matrix.fromArray( data.matrix ); + matrix.decompose( object.position, object.quaternion, object.scale ); + + } else { + + if ( data.position !== undefined ) object.position.fromArray( data.position ); + if ( data.rotation !== undefined ) object.rotation.fromArray( data.rotation ); + if ( data.scale !== undefined ) object.scale.fromArray( data.scale ); + + } + + if ( data.visible !== undefined ) object.visible = data.visible; + if ( data.userData !== undefined ) object.userData = data.userData; + + if ( data.children !== undefined ) { + + for ( var child in data.children ) { + + object.add( this.parseObject( data.children[ child ], geometries, materials ) ); + + } + + } + + return object; + + } + + }() + +}; + +// File:src/loaders/TextureLoader.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.TextureLoader = function ( manager ) { + + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + +}; + +THREE.TextureLoader.prototype = { + + constructor: THREE.TextureLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var loader = new THREE.ImageLoader( scope.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.load( url, function ( image ) { + + var texture = new THREE.Texture( image ); + texture.needsUpdate = true; + + if ( onLoad !== undefined ) { + + onLoad( texture ); + + } + + }, onProgress, onError ); + + }, + + setCrossOrigin: function ( value ) { + + this.crossOrigin = value; + + } + +}; + +// File:src/loaders/BinaryTextureLoader.js + +/** + * @author Nikos M. / https://github.com/foo123/ + * + * Abstract Base class to load generic binary textures formats (rgbe, hdr, ...) + */ + +THREE.DataTextureLoader = THREE.BinaryTextureLoader = function () { + + // override in sub classes + this._parser = null; + +}; + +THREE.BinaryTextureLoader.prototype = { + + constructor: THREE.BinaryTextureLoader, + + load: function ( url, onLoad, onProgress, onError ) { + + var scope = this; + + var texture = new THREE.DataTexture( ); + + var loader = new THREE.XHRLoader(); + loader.setResponseType( 'arraybuffer' ); + + loader.load( url, function ( buffer ) { + + var texData = scope._parser( buffer ); + + if ( !texData ) return; + + if ( undefined !== texData.image ) { + + texture.image = texData.image; + + } else if ( undefined !== texData.data ){ + + texture.image.width = texData.width; + texture.image.height = texData.height; + texture.image.data = texData.data; + + } + + texture.wrapS = undefined !== texData.wrapS ? texData.wrapS : THREE.ClampToEdgeWrapping; + texture.wrapT = undefined !== texData.wrapT ? texData.wrapT : THREE.ClampToEdgeWrapping; + + texture.magFilter = undefined !== texData.magFilter ? texData.magFilter : THREE.LinearFilter; + texture.minFilter = undefined !== texData.minFilter ? texData.minFilter : THREE.LinearMipMapLinearFilter; + + texture.anisotropy = undefined !== texData.anisotropy ? texData.anisotropy : 1; + + if ( undefined !== texData.format ) { + + texture.format = texData.format; + + } + if ( undefined !== texData.type ) { + + texture.type = texData.type; + + } + + if ( undefined !== texData.mipmaps ) { + + texture.mipmaps = texData.mipmaps; + + } + + if ( 1 === texData.mipmapCount ) { + + texture.minFilter = THREE.LinearFilter; + + } + + texture.needsUpdate = true; + + if ( onLoad ) onLoad( texture, texData ); + + }, onProgress, onError ); + + + return texture; + + } + +}; + +// File:src/loaders/CompressedTextureLoader.js + +/** + * @author mrdoob / http://mrdoob.com/ + * + * Abstract Base class to block based textures loader (dds, pvr, ...) + */ + +THREE.CompressedTextureLoader = function () { + + // override in sub classes + this._parser = null; + +}; + + +THREE.CompressedTextureLoader.prototype = { + + constructor: THREE.CompressedTextureLoader, + + load: function ( url, onLoad, onError ) { + + var scope = this; + + var images = []; + + var texture = new THREE.CompressedTexture(); + texture.image = images; + + var loader = new THREE.XHRLoader(); + loader.setResponseType( 'arraybuffer' ); + + if ( url instanceof Array ) { + + var loaded = 0; + + var loadTexture = function ( i ) { + + loader.load( url[ i ], function ( buffer ) { + + var texDatas = scope._parser( buffer, true ); + + images[ i ] = { + width: texDatas.width, + height: texDatas.height, + format: texDatas.format, + mipmaps: texDatas.mipmaps + }; + + loaded += 1; + + if ( loaded === 6 ) { + + if (texDatas.mipmapCount == 1) + texture.minFilter = THREE.LinearFilter; + + texture.format = texDatas.format; + texture.needsUpdate = true; + + if ( onLoad ) onLoad( texture ); + + } + + } ); + + }; + + for ( var i = 0, il = url.length; i < il; ++ i ) { + + loadTexture( i ); + + } + + } else { + + // compressed cubemap texture stored in a single DDS file + + loader.load( url, function ( buffer ) { + + var texDatas = scope._parser( buffer, true ); + + if ( texDatas.isCubemap ) { + + var faces = texDatas.mipmaps.length / texDatas.mipmapCount; + + for ( var f = 0; f < faces; f ++ ) { + + images[ f ] = { mipmaps : [] }; + + for ( var i = 0; i < texDatas.mipmapCount; i ++ ) { + + images[ f ].mipmaps.push( texDatas.mipmaps[ f * texDatas.mipmapCount + i ] ); + images[ f ].format = texDatas.format; + images[ f ].width = texDatas.width; + images[ f ].height = texDatas.height; + + } + + } + + } else { + + texture.image.width = texDatas.width; + texture.image.height = texDatas.height; + texture.mipmaps = texDatas.mipmaps; + + } + + if ( texDatas.mipmapCount === 1 ) { + + texture.minFilter = THREE.LinearFilter; + + } + + texture.format = texDatas.format; + texture.needsUpdate = true; + + if ( onLoad ) onLoad( texture ); + + } ); + + } + + return texture; + + } + +}; + +// File:src/materials/Material.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Material = function () { + + Object.defineProperty( this, 'id', { value: THREE.MaterialIdCount ++ } ); + + this.uuid = THREE.Math.generateUUID(); + + this.name = ''; + this.type = 'Material'; + + this.side = THREE.FrontSide; + + this.opacity = 1; + this.transparent = false; + + this.blending = THREE.NormalBlending; + + this.blendSrc = THREE.SrcAlphaFactor; + this.blendDst = THREE.OneMinusSrcAlphaFactor; + this.blendEquation = THREE.AddEquation; + + this.depthTest = true; + this.depthWrite = true; + + this.polygonOffset = false; + this.polygonOffsetFactor = 0; + this.polygonOffsetUnits = 0; + + this.alphaTest = 0; + + this.overdraw = 0; // Overdrawn pixels (typically between 0 and 1) for fixing antialiasing gaps in CanvasRenderer + + this.visible = true; + + this.needsUpdate = true; + +}; + +THREE.Material.prototype = { + + constructor: THREE.Material, + + setValues: function ( values ) { + + if ( values === undefined ) return; + + for ( var key in values ) { + + var newValue = values[ key ]; + + if ( newValue === undefined ) { + + console.warn( "THREE.Material: '" + key + "' parameter is undefined." ); + continue; + + } + + if ( key in this ) { + + var currentValue = this[ key ]; + + if ( currentValue instanceof THREE.Color ) { + + currentValue.set( newValue ); + + } else if ( currentValue instanceof THREE.Vector3 && newValue instanceof THREE.Vector3 ) { + + currentValue.copy( newValue ); + + } else if ( key == 'overdraw' ) { + + // ensure overdraw is backwards-compatable with legacy boolean type + this[ key ] = Number( newValue ); + + } else { + + this[ key ] = newValue; + + } + + } + + } + + }, + + toJSON: function () { + + var output = { + metadata: { + version: 4.2, + type: 'material', + generator: 'MaterialExporter' + }, + uuid: this.uuid, + type: this.type + }; + + if ( this.name !== "" ) output.name = this.name; + + if ( this instanceof THREE.MeshBasicMaterial ) { + + output.color = this.color.getHex(); + if ( this.vertexColors !== THREE.NoColors ) output.vertexColors = this.vertexColors; + if ( this.blending !== THREE.NormalBlending ) output.blending = this.blending; + if ( this.side !== THREE.FrontSide ) output.side = this.side; + + } else if ( this instanceof THREE.MeshLambertMaterial ) { + + output.color = this.color.getHex(); + output.ambient = this.ambient.getHex(); + output.emissive = this.emissive.getHex(); + if ( this.vertexColors !== THREE.NoColors ) output.vertexColors = this.vertexColors; + if ( this.blending !== THREE.NormalBlending ) output.blending = this.blending; + if ( this.side !== THREE.FrontSide ) output.side = this.side; + + } else if ( this instanceof THREE.MeshPhongMaterial ) { + + output.color = this.color.getHex(); + output.ambient = this.ambient.getHex(); + output.emissive = this.emissive.getHex(); + output.specular = this.specular.getHex(); + output.shininess = this.shininess; + if ( this.vertexColors !== THREE.NoColors ) output.vertexColors = this.vertexColors; + if ( this.blending !== THREE.NormalBlending ) output.blending = this.blending; + if ( this.side !== THREE.FrontSide ) output.side = this.side; + + } else if ( this instanceof THREE.MeshNormalMaterial ) { + + if ( this.shading !== THREE.FlatShading ) output.shading = this.shading; + if ( this.blending !== THREE.NormalBlending ) output.blending = this.blending; + if ( this.side !== THREE.FrontSide ) output.side = this.side; + + } else if ( this instanceof THREE.MeshDepthMaterial ) { + + if ( this.blending !== THREE.NormalBlending ) output.blending = this.blending; + if ( this.side !== THREE.FrontSide ) output.side = this.side; + + } else if ( this instanceof THREE.ShaderMaterial ) { + + output.uniforms = this.uniforms; + output.vertexShader = this.vertexShader; + output.fragmentShader = this.fragmentShader; + + } else if ( this instanceof THREE.SpriteMaterial ) { + + output.color = this.color.getHex(); + + } + + if ( this.opacity < 1 ) output.opacity = this.opacity; + if ( this.transparent !== false ) output.transparent = this.transparent; + if ( this.wireframe !== false ) output.wireframe = this.wireframe; + + return output; + + }, + + clone: function ( material ) { + + if ( material === undefined ) material = new THREE.Material(); + + material.name = this.name; + + material.side = this.side; + + material.opacity = this.opacity; + material.transparent = this.transparent; + + material.blending = this.blending; + + material.blendSrc = this.blendSrc; + material.blendDst = this.blendDst; + material.blendEquation = this.blendEquation; + + material.depthTest = this.depthTest; + material.depthWrite = this.depthWrite; + + material.polygonOffset = this.polygonOffset; + material.polygonOffsetFactor = this.polygonOffsetFactor; + material.polygonOffsetUnits = this.polygonOffsetUnits; + + material.alphaTest = this.alphaTest; + + material.overdraw = this.overdraw; + + material.visible = this.visible; + + return material; + + }, + + dispose: function () { + + this.dispatchEvent( { type: 'dispose' } ); + + } + +}; + +THREE.EventDispatcher.prototype.apply( THREE.Material.prototype ); + +THREE.MaterialIdCount = 0; + +// File:src/materials/LineBasicMaterial.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: <hex>, + * opacity: <float>, + * + * blending: THREE.NormalBlending, + * depthTest: <bool>, + * depthWrite: <bool>, + * + * linewidth: <float>, + * linecap: "round", + * linejoin: "round", + * + * vertexColors: <bool> + * + * fog: <bool> + * } + */ + +THREE.LineBasicMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.type = 'LineBasicMaterial'; + + this.color = new THREE.Color( 0xffffff ); + + this.linewidth = 1; + this.linecap = 'round'; + this.linejoin = 'round'; + + this.vertexColors = THREE.NoColors; + + this.fog = true; + + this.setValues( parameters ); + +}; + +THREE.LineBasicMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.LineBasicMaterial.prototype.constructor = THREE.LineBasicMaterial; + +THREE.LineBasicMaterial.prototype.clone = function () { + + var material = new THREE.LineBasicMaterial(); + + THREE.Material.prototype.clone.call( this, material ); + + material.color.copy( this.color ); + + material.linewidth = this.linewidth; + material.linecap = this.linecap; + material.linejoin = this.linejoin; + + material.vertexColors = this.vertexColors; + + material.fog = this.fog; + + return material; + +}; + +// File:src/materials/LineDashedMaterial.js + +/** + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: <hex>, + * opacity: <float>, + * + * blending: THREE.NormalBlending, + * depthTest: <bool>, + * depthWrite: <bool>, + * + * linewidth: <float>, + * + * scale: <float>, + * dashSize: <float>, + * gapSize: <float>, + * + * vertexColors: <bool> + * + * fog: <bool> + * } + */ + +THREE.LineDashedMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.type = 'LineDashedMaterial'; + + this.color = new THREE.Color( 0xffffff ); + + this.linewidth = 1; + + this.scale = 1; + this.dashSize = 3; + this.gapSize = 1; + + this.vertexColors = false; + + this.fog = true; + + this.setValues( parameters ); + +}; + +THREE.LineDashedMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.LineDashedMaterial.prototype.constructor = THREE.LineDashedMaterial; + +THREE.LineDashedMaterial.prototype.clone = function () { + + var material = new THREE.LineDashedMaterial(); + + THREE.Material.prototype.clone.call( this, material ); + + material.color.copy( this.color ); + + material.linewidth = this.linewidth; + + material.scale = this.scale; + material.dashSize = this.dashSize; + material.gapSize = this.gapSize; + + material.vertexColors = this.vertexColors; + + material.fog = this.fog; + + return material; + +}; + +// File:src/materials/MeshBasicMaterial.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: <hex>, + * opacity: <float>, + * map: new THREE.Texture( <Image> ), + * + * lightMap: new THREE.Texture( <Image> ), + * + * specularMap: new THREE.Texture( <Image> ), + * + * alphaMap: new THREE.Texture( <Image> ), + * + * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ), + * combine: THREE.Multiply, + * reflectivity: <float>, + * refractionRatio: <float>, + * + * shading: THREE.SmoothShading, + * blending: THREE.NormalBlending, + * depthTest: <bool>, + * depthWrite: <bool>, + * + * wireframe: <boolean>, + * wireframeLinewidth: <float>, + * + * vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors, + * + * skinning: <bool>, + * morphTargets: <bool>, + * + * fog: <bool> + * } + */ + +THREE.MeshBasicMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.type = 'MeshBasicMaterial'; + + this.color = new THREE.Color( 0xffffff ); // emissive + + this.map = null; + + this.lightMap = null; + + this.specularMap = null; + + this.alphaMap = null; + + this.envMap = null; + this.combine = THREE.MultiplyOperation; + this.reflectivity = 1; + this.refractionRatio = 0.98; + + this.fog = true; + + this.shading = THREE.SmoothShading; + + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; + + this.vertexColors = THREE.NoColors; + + this.skinning = false; + this.morphTargets = false; + + this.setValues( parameters ); + +}; + +THREE.MeshBasicMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.MeshBasicMaterial.prototype.constructor = THREE.MeshBasicMaterial; + +THREE.MeshBasicMaterial.prototype.clone = function () { + + var material = new THREE.MeshBasicMaterial(); + + THREE.Material.prototype.clone.call( this, material ); + + material.color.copy( this.color ); + + material.map = this.map; + + material.lightMap = this.lightMap; + + material.specularMap = this.specularMap; + + material.alphaMap = this.alphaMap; + + material.envMap = this.envMap; + material.combine = this.combine; + material.reflectivity = this.reflectivity; + material.refractionRatio = this.refractionRatio; + + material.fog = this.fog; + + material.shading = this.shading; + + material.wireframe = this.wireframe; + material.wireframeLinewidth = this.wireframeLinewidth; + material.wireframeLinecap = this.wireframeLinecap; + material.wireframeLinejoin = this.wireframeLinejoin; + + material.vertexColors = this.vertexColors; + + material.skinning = this.skinning; + material.morphTargets = this.morphTargets; + + return material; + +}; + +// File:src/materials/MeshLambertMaterial.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: <hex>, + * ambient: <hex>, + * emissive: <hex>, + * opacity: <float>, + * + * map: new THREE.Texture( <Image> ), + * + * lightMap: new THREE.Texture( <Image> ), + * + * specularMap: new THREE.Texture( <Image> ), + * + * alphaMap: new THREE.Texture( <Image> ), + * + * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ), + * combine: THREE.Multiply, + * reflectivity: <float>, + * refractionRatio: <float>, + * + * shading: THREE.SmoothShading, + * blending: THREE.NormalBlending, + * depthTest: <bool>, + * depthWrite: <bool>, + * + * wireframe: <boolean>, + * wireframeLinewidth: <float>, + * + * vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors, + * + * skinning: <bool>, + * morphTargets: <bool>, + * morphNormals: <bool>, + * + * fog: <bool> + * } + */ + +THREE.MeshLambertMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.type = 'MeshLambertMaterial'; + + this.color = new THREE.Color( 0xffffff ); // diffuse + this.ambient = new THREE.Color( 0xffffff ); + this.emissive = new THREE.Color( 0x000000 ); + + this.wrapAround = false; + this.wrapRGB = new THREE.Vector3( 1, 1, 1 ); + + this.map = null; + + this.lightMap = null; + + this.specularMap = null; + + this.alphaMap = null; + + this.envMap = null; + this.combine = THREE.MultiplyOperation; + this.reflectivity = 1; + this.refractionRatio = 0.98; + + this.fog = true; + + this.shading = THREE.SmoothShading; + + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; + + this.vertexColors = THREE.NoColors; + + this.skinning = false; + this.morphTargets = false; + this.morphNormals = false; + + this.setValues( parameters ); + +}; + +THREE.MeshLambertMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.MeshLambertMaterial.prototype.constructor = THREE.MeshLambertMaterial; + +THREE.MeshLambertMaterial.prototype.clone = function () { + + var material = new THREE.MeshLambertMaterial(); + + THREE.Material.prototype.clone.call( this, material ); + + material.color.copy( this.color ); + material.ambient.copy( this.ambient ); + material.emissive.copy( this.emissive ); + + material.wrapAround = this.wrapAround; + material.wrapRGB.copy( this.wrapRGB ); + + material.map = this.map; + + material.lightMap = this.lightMap; + + material.specularMap = this.specularMap; + + material.alphaMap = this.alphaMap; + + material.envMap = this.envMap; + material.combine = this.combine; + material.reflectivity = this.reflectivity; + material.refractionRatio = this.refractionRatio; + + material.fog = this.fog; + + material.shading = this.shading; + + material.wireframe = this.wireframe; + material.wireframeLinewidth = this.wireframeLinewidth; + material.wireframeLinecap = this.wireframeLinecap; + material.wireframeLinejoin = this.wireframeLinejoin; + + material.vertexColors = this.vertexColors; + + material.skinning = this.skinning; + material.morphTargets = this.morphTargets; + material.morphNormals = this.morphNormals; + + return material; + +}; + +// File:src/materials/MeshPhongMaterial.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: <hex>, + * ambient: <hex>, + * emissive: <hex>, + * specular: <hex>, + * shininess: <float>, + * opacity: <float>, + * + * map: new THREE.Texture( <Image> ), + * + * lightMap: new THREE.Texture( <Image> ), + * + * bumpMap: new THREE.Texture( <Image> ), + * bumpScale: <float>, + * + * normalMap: new THREE.Texture( <Image> ), + * normalScale: <Vector2>, + * + * specularMap: new THREE.Texture( <Image> ), + * + * alphaMap: new THREE.Texture( <Image> ), + * + * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ), + * combine: THREE.Multiply, + * reflectivity: <float>, + * refractionRatio: <float>, + * + * shading: THREE.SmoothShading, + * blending: THREE.NormalBlending, + * depthTest: <bool>, + * depthWrite: <bool>, + * + * wireframe: <boolean>, + * wireframeLinewidth: <float>, + * + * vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors, + * + * skinning: <bool>, + * morphTargets: <bool>, + * morphNormals: <bool>, + * + * fog: <bool> + * } + */ + +THREE.MeshPhongMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.type = 'MeshPhongMaterial'; + + this.color = new THREE.Color( 0xffffff ); // diffuse + this.ambient = new THREE.Color( 0xffffff ); + this.emissive = new THREE.Color( 0x000000 ); + this.specular = new THREE.Color( 0x111111 ); + this.shininess = 30; + + this.metal = false; + + this.wrapAround = false; + this.wrapRGB = new THREE.Vector3( 1, 1, 1 ); + + this.map = null; + + this.lightMap = null; + + this.bumpMap = null; + this.bumpScale = 1; + + this.normalMap = null; + this.normalScale = new THREE.Vector2( 1, 1 ); + + this.specularMap = null; + + this.alphaMap = null; + + this.envMap = null; + this.combine = THREE.MultiplyOperation; + this.reflectivity = 1; + this.refractionRatio = 0.98; + + this.fog = true; + + this.shading = THREE.SmoothShading; + + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; + + this.vertexColors = THREE.NoColors; + + this.skinning = false; + this.morphTargets = false; + this.morphNormals = false; + + this.setValues( parameters ); + +}; + +THREE.MeshPhongMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.MeshPhongMaterial.prototype.constructor = THREE.MeshPhongMaterial; + +THREE.MeshPhongMaterial.prototype.clone = function () { + + var material = new THREE.MeshPhongMaterial(); + + THREE.Material.prototype.clone.call( this, material ); + + material.color.copy( this.color ); + material.ambient.copy( this.ambient ); + material.emissive.copy( this.emissive ); + material.specular.copy( this.specular ); + material.shininess = this.shininess; + + material.metal = this.metal; + + material.wrapAround = this.wrapAround; + material.wrapRGB.copy( this.wrapRGB ); + + material.map = this.map; + + material.lightMap = this.lightMap; + + material.bumpMap = this.bumpMap; + material.bumpScale = this.bumpScale; + + material.normalMap = this.normalMap; + material.normalScale.copy( this.normalScale ); + + material.specularMap = this.specularMap; + + material.alphaMap = this.alphaMap; + + material.envMap = this.envMap; + material.combine = this.combine; + material.reflectivity = this.reflectivity; + material.refractionRatio = this.refractionRatio; + + material.fog = this.fog; + + material.shading = this.shading; + + material.wireframe = this.wireframe; + material.wireframeLinewidth = this.wireframeLinewidth; + material.wireframeLinecap = this.wireframeLinecap; + material.wireframeLinejoin = this.wireframeLinejoin; + + material.vertexColors = this.vertexColors; + + material.skinning = this.skinning; + material.morphTargets = this.morphTargets; + material.morphNormals = this.morphNormals; + + return material; + +}; + +// File:src/materials/MeshDepthMaterial.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * opacity: <float>, + * + * blending: THREE.NormalBlending, + * depthTest: <bool>, + * depthWrite: <bool>, + * + * wireframe: <boolean>, + * wireframeLinewidth: <float> + * } + */ + +THREE.MeshDepthMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.type = 'MeshDepthMaterial'; + + this.morphTargets = false; + this.wireframe = false; + this.wireframeLinewidth = 1; + + this.setValues( parameters ); + +}; + +THREE.MeshDepthMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.MeshDepthMaterial.prototype.constructor = THREE.MeshDepthMaterial; + +THREE.MeshDepthMaterial.prototype.clone = function () { + + var material = new THREE.MeshDepthMaterial(); + + THREE.Material.prototype.clone.call( this, material ); + + material.wireframe = this.wireframe; + material.wireframeLinewidth = this.wireframeLinewidth; + + return material; + +}; + +// File:src/materials/MeshNormalMaterial.js + +/** + * @author mrdoob / http://mrdoob.com/ + * + * parameters = { + * opacity: <float>, + * + * shading: THREE.FlatShading, + * blending: THREE.NormalBlending, + * depthTest: <bool>, + * depthWrite: <bool>, + * + * wireframe: <boolean>, + * wireframeLinewidth: <float> + * } + */ + +THREE.MeshNormalMaterial = function ( parameters ) { + + THREE.Material.call( this, parameters ); + + this.type = 'MeshNormalMaterial'; + + this.shading = THREE.FlatShading; + + this.wireframe = false; + this.wireframeLinewidth = 1; + + this.morphTargets = false; + + this.setValues( parameters ); + +}; + +THREE.MeshNormalMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.MeshNormalMaterial.prototype.constructor = THREE.MeshNormalMaterial; + +THREE.MeshNormalMaterial.prototype.clone = function () { + + var material = new THREE.MeshNormalMaterial(); + + THREE.Material.prototype.clone.call( this, material ); + + material.shading = this.shading; + + material.wireframe = this.wireframe; + material.wireframeLinewidth = this.wireframeLinewidth; + + return material; + +}; + +// File:src/materials/MeshFaceMaterial.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.MeshFaceMaterial = function ( materials ) { + + this.uuid = THREE.Math.generateUUID(); + + this.type = 'MeshFaceMaterial'; + + this.materials = materials instanceof Array ? materials : []; + +}; + +THREE.MeshFaceMaterial.prototype = { + + constructor: THREE.MeshFaceMaterial, + + toJSON: function () { + + var output = { + metadata: { + version: 4.2, + type: 'material', + generator: 'MaterialExporter' + }, + uuid: this.uuid, + type: this.type, + materials: [] + }; + + for ( var i = 0, l = this.materials.length; i < l; i ++ ) { + + output.materials.push( this.materials[ i ].toJSON() ); + + } + + return output; + + }, + + clone: function () { + + var material = new THREE.MeshFaceMaterial(); + + for ( var i = 0; i < this.materials.length; i ++ ) { + + material.materials.push( this.materials[ i ].clone() ); + + } + + return material; + + } + +}; + +// File:src/materials/PointCloudMaterial.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: <hex>, + * opacity: <float>, + * map: new THREE.Texture( <Image> ), + * + * size: <float>, + * sizeAttenuation: <bool>, + * + * blending: THREE.NormalBlending, + * depthTest: <bool>, + * depthWrite: <bool>, + * + * vertexColors: <bool>, + * + * fog: <bool> + * } + */ + +THREE.PointCloudMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.type = 'PointCloudMaterial'; + + this.color = new THREE.Color( 0xffffff ); + + this.map = null; + + this.size = 1; + this.sizeAttenuation = true; + + this.vertexColors = THREE.NoColors; + + this.fog = true; + + this.setValues( parameters ); + +}; + +THREE.PointCloudMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.PointCloudMaterial.prototype.constructor = THREE.PointCloudMaterial; + +THREE.PointCloudMaterial.prototype.clone = function () { + + var material = new THREE.PointCloudMaterial(); + + THREE.Material.prototype.clone.call( this, material ); + + material.color.copy( this.color ); + + material.map = this.map; + + material.size = this.size; + material.sizeAttenuation = this.sizeAttenuation; + + material.vertexColors = this.vertexColors; + + material.fog = this.fog; + + return material; + +}; + +// backwards compatibility + +THREE.ParticleBasicMaterial = function ( parameters ) { + + console.warn( 'THREE.ParticleBasicMaterial has been renamed to THREE.PointCloudMaterial.' ); + return new THREE.PointCloudMaterial( parameters ); + +}; + +THREE.ParticleSystemMaterial = function ( parameters ) { + + console.warn( 'THREE.ParticleSystemMaterial has been renamed to THREE.PointCloudMaterial.' ); + return new THREE.PointCloudMaterial( parameters ); + +}; + +// File:src/materials/ShaderMaterial.js + +/** + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * defines: { "label" : "value" }, + * uniforms: { "parameter1": { type: "f", value: 1.0 }, "parameter2": { type: "i" value2: 2 } }, + * + * fragmentShader: <string>, + * vertexShader: <string>, + * + * shading: THREE.SmoothShading, + * blending: THREE.NormalBlending, + * depthTest: <bool>, + * depthWrite: <bool>, + * + * wireframe: <boolean>, + * wireframeLinewidth: <float>, + * + * lights: <bool>, + * + * vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors, + * + * skinning: <bool>, + * morphTargets: <bool>, + * morphNormals: <bool>, + * + * fog: <bool> + * } + */ + +THREE.ShaderMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.type = 'ShaderMaterial'; + + this.defines = {}; + this.uniforms = {}; + this.attributes = null; + + this.vertexShader = 'void main() {\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}'; + this.fragmentShader = 'void main() {\n\tgl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );\n}'; + + this.shading = THREE.SmoothShading; + + this.linewidth = 1; + + this.wireframe = false; + this.wireframeLinewidth = 1; + + this.fog = false; // set to use scene fog + + this.lights = false; // set to use scene lights + + this.vertexColors = THREE.NoColors; // set to use "color" attribute stream + + this.skinning = false; // set to use skinning attribute streams + + this.morphTargets = false; // set to use morph targets + this.morphNormals = false; // set to use morph normals + + // When rendered geometry doesn't include these attributes but the material does, + // use these default values in WebGL. This avoids errors when buffer data is missing. + this.defaultAttributeValues = { + 'color': [ 1, 1, 1 ], + 'uv': [ 0, 0 ], + 'uv2': [ 0, 0 ] + }; + + this.index0AttributeName = undefined; + + this.setValues( parameters ); + +}; + +THREE.ShaderMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.ShaderMaterial.prototype.constructor = THREE.ShaderMaterial; + +THREE.ShaderMaterial.prototype.clone = function () { + + var material = new THREE.ShaderMaterial(); + + THREE.Material.prototype.clone.call( this, material ); + + material.fragmentShader = this.fragmentShader; + material.vertexShader = this.vertexShader; + + material.uniforms = THREE.UniformsUtils.clone( this.uniforms ); + + material.attributes = this.attributes; + material.defines = this.defines; + + material.shading = this.shading; + + material.wireframe = this.wireframe; + material.wireframeLinewidth = this.wireframeLinewidth; + + material.fog = this.fog; + + material.lights = this.lights; + + material.vertexColors = this.vertexColors; + + material.skinning = this.skinning; + + material.morphTargets = this.morphTargets; + material.morphNormals = this.morphNormals; + + return material; + +}; + +// File:src/materials/RawShaderMaterial.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.RawShaderMaterial = function ( parameters ) { + + THREE.ShaderMaterial.call( this, parameters ); + + this.type = 'RawShaderMaterial'; + +}; + +THREE.RawShaderMaterial.prototype = Object.create( THREE.ShaderMaterial.prototype ); +THREE.RawShaderMaterial.prototype.constructor = THREE.RawShaderMaterial; + +THREE.RawShaderMaterial.prototype.clone = function () { + + var material = new THREE.RawShaderMaterial(); + + THREE.ShaderMaterial.prototype.clone.call( this, material ); + + return material; + +}; + +// File:src/materials/SpriteMaterial.js + +/** + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: <hex>, + * opacity: <float>, + * map: new THREE.Texture( <Image> ), + * + * blending: THREE.NormalBlending, + * depthTest: <bool>, + * depthWrite: <bool>, + * + * uvOffset: new THREE.Vector2(), + * uvScale: new THREE.Vector2(), + * + * fog: <bool> + * } + */ + +THREE.SpriteMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.type = 'SpriteMaterial'; + + this.color = new THREE.Color( 0xffffff ); + this.map = null; + + this.rotation = 0; + + this.fog = false; + + // set parameters + + this.setValues( parameters ); + +}; + +THREE.SpriteMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.SpriteMaterial.prototype.constructor = THREE.SpriteMaterial; + +THREE.SpriteMaterial.prototype.clone = function () { + + var material = new THREE.SpriteMaterial(); + + THREE.Material.prototype.clone.call( this, material ); + + material.color.copy( this.color ); + material.map = this.map; + + material.rotation = this.rotation; + + material.fog = this.fog; + + return material; + +}; + +// File:src/textures/Texture.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * @author szimek / https://github.com/szimek/ + */ + +THREE.Texture = function ( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { + + Object.defineProperty( this, 'id', { value: THREE.TextureIdCount ++ } ); + + this.uuid = THREE.Math.generateUUID(); + + this.name = ''; + + this.image = image !== undefined ? image : THREE.Texture.DEFAULT_IMAGE; + this.mipmaps = []; + + this.mapping = mapping !== undefined ? mapping : THREE.Texture.DEFAULT_MAPPING; + + this.wrapS = wrapS !== undefined ? wrapS : THREE.ClampToEdgeWrapping; + this.wrapT = wrapT !== undefined ? wrapT : THREE.ClampToEdgeWrapping; + + this.magFilter = magFilter !== undefined ? magFilter : THREE.LinearFilter; + this.minFilter = minFilter !== undefined ? minFilter : THREE.LinearMipMapLinearFilter; + + this.anisotropy = anisotropy !== undefined ? anisotropy : 1; + + this.format = format !== undefined ? format : THREE.RGBAFormat; + this.type = type !== undefined ? type : THREE.UnsignedByteType; + + this.offset = new THREE.Vector2( 0, 0 ); + this.repeat = new THREE.Vector2( 1, 1 ); + + this.generateMipmaps = true; + this.premultiplyAlpha = false; + this.flipY = true; + this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) + + this._needsUpdate = false; + this.onUpdate = null; + +}; + +THREE.Texture.DEFAULT_IMAGE = undefined; +THREE.Texture.DEFAULT_MAPPING = THREE.UVMapping; + +THREE.Texture.prototype = { + + constructor: THREE.Texture, + + get needsUpdate () { + + return this._needsUpdate; + + }, + + set needsUpdate ( value ) { + + if ( value === true ) this.update(); + + this._needsUpdate = value; + + }, + + clone: function ( texture ) { + + if ( texture === undefined ) texture = new THREE.Texture(); + + texture.image = this.image; + texture.mipmaps = this.mipmaps.slice( 0 ); + + texture.mapping = this.mapping; + + texture.wrapS = this.wrapS; + texture.wrapT = this.wrapT; + + texture.magFilter = this.magFilter; + texture.minFilter = this.minFilter; + + texture.anisotropy = this.anisotropy; + + texture.format = this.format; + texture.type = this.type; + + texture.offset.copy( this.offset ); + texture.repeat.copy( this.repeat ); + + texture.generateMipmaps = this.generateMipmaps; + texture.premultiplyAlpha = this.premultiplyAlpha; + texture.flipY = this.flipY; + texture.unpackAlignment = this.unpackAlignment; + + return texture; + + }, + + update: function () { + + this.dispatchEvent( { type: 'update' } ); + + }, + + dispose: function () { + + this.dispatchEvent( { type: 'dispose' } ); + + } + +}; + +THREE.EventDispatcher.prototype.apply( THREE.Texture.prototype ); + +THREE.TextureIdCount = 0; + +// File:src/textures/CubeTexture.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.CubeTexture = function ( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { + + mapping = mapping !== undefined ? mapping : THREE.CubeReflectionMapping; + + THREE.Texture.call( this, images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); + + this.images = images; + +}; + +THREE.CubeTexture.prototype = Object.create( THREE.Texture.prototype ); +THREE.CubeTexture.prototype.constructor = THREE.CubeTexture; + +THREE.CubeTexture.clone = function ( texture ) { + + if ( texture === undefined ) texture = new THREE.CubeTexture(); + + THREE.Texture.prototype.clone.call( this, texture ); + + texture.images = this.images; + + return texture; + +}; + +// File:src/textures/CompressedTexture.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.CompressedTexture = function ( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy ) { + + THREE.Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); + + this.image = { width: width, height: height }; + this.mipmaps = mipmaps; + + // no flipping for cube textures + // (also flipping doesn't work for compressed textures ) + + this.flipY = false; + + // can't generate mipmaps for compressed textures + // mips must be embedded in DDS files + + this.generateMipmaps = false; + +}; + +THREE.CompressedTexture.prototype = Object.create( THREE.Texture.prototype ); +THREE.CompressedTexture.prototype.constructor = THREE.CompressedTexture; + +THREE.CompressedTexture.prototype.clone = function () { + + var texture = new THREE.CompressedTexture(); + + THREE.Texture.prototype.clone.call( this, texture ); + + return texture; + +}; + +// File:src/textures/DataTexture.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.DataTexture = function ( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy ) { + + THREE.Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); + + this.image = { data: data, width: width, height: height }; + +}; + +THREE.DataTexture.prototype = Object.create( THREE.Texture.prototype ); +THREE.DataTexture.prototype.constructor = THREE.DataTexture; + +THREE.DataTexture.prototype.clone = function () { + + var texture = new THREE.DataTexture(); + + THREE.Texture.prototype.clone.call( this, texture ); + + return texture; + +}; + +// File:src/textures/VideoTexture.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.VideoTexture = function ( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { + + THREE.Texture.call( this, video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); + + this.generateMipmaps = false; + + var scope = this; + + var update = function () { + + requestAnimationFrame( update ); + + if ( video.readyState === video.HAVE_ENOUGH_DATA ) { + + scope.needsUpdate = true; + + } + + }; + + update(); + +}; + +THREE.VideoTexture.prototype = Object.create( THREE.Texture.prototype ); +THREE.VideoTexture.prototype.constructor = THREE.VideoTexture; + +// File:src/objects/Group.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Group = function () { + + THREE.Object3D.call( this ); + + this.type = 'Group'; + +}; + +THREE.Group.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Group.prototype.constructor = THREE.Group; + +// File:src/objects/PointCloud.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.PointCloud = function ( geometry, material ) { + + THREE.Object3D.call( this ); + + this.type = 'PointCloud'; + + this.geometry = geometry !== undefined ? geometry : new THREE.Geometry(); + this.material = material !== undefined ? material : new THREE.PointCloudMaterial( { color: Math.random() * 0xffffff } ); + +}; + +THREE.PointCloud.prototype = Object.create( THREE.Object3D.prototype ); +THREE.PointCloud.prototype.constructor = THREE.PointCloud; + +THREE.PointCloud.prototype.raycast = ( function () { + + var inverseMatrix = new THREE.Matrix4(); + var ray = new THREE.Ray(); + + return function ( raycaster, intersects ) { + + var object = this; + var geometry = object.geometry; + var threshold = raycaster.params.PointCloud.threshold; + + inverseMatrix.getInverse( this.matrixWorld ); + ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); + + if ( geometry.boundingBox !== null ) { + + if ( ray.isIntersectionBox( geometry.boundingBox ) === false ) { + + return; + + } + + } + + var localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); + var position = new THREE.Vector3(); + + var testPoint = function ( point, index ) { + + var rayPointDistance = ray.distanceToPoint( point ); + + if ( rayPointDistance < localThreshold ) { + + var intersectPoint = ray.closestPointToPoint( point ); + intersectPoint.applyMatrix4( object.matrixWorld ); + + var distance = raycaster.ray.origin.distanceTo( intersectPoint ); + + intersects.push( { + + distance: distance, + distanceToRay: rayPointDistance, + point: intersectPoint.clone(), + index: index, + face: null, + object: object + + } ); + + } + + }; + + if ( geometry instanceof THREE.BufferGeometry ) { + + var attributes = geometry.attributes; + var positions = attributes.position.array; + + if ( attributes.index !== undefined ) { + + var indices = attributes.index.array; + var offsets = geometry.offsets; + + if ( offsets.length === 0 ) { + + var offset = { + start: 0, + count: indices.length, + index: 0 + }; + + offsets = [ offset ]; + + } + + for ( var oi = 0, ol = offsets.length; oi < ol; ++oi ) { + + var start = offsets[ oi ].start; + var count = offsets[ oi ].count; + var index = offsets[ oi ].index; + + for ( var i = start, il = start + count; i < il; i ++ ) { + + var a = index + indices[ i ]; + + position.fromArray( positions, a * 3 ); + + testPoint( position, a ); + + } + + } + + } else { + + var pointCount = positions.length / 3; + + for ( var i = 0; i < pointCount; i ++ ) { + + position.set( + positions[ 3 * i ], + positions[ 3 * i + 1 ], + positions[ 3 * i + 2 ] + ); + + testPoint( position, i ); + + } + + } + + } else { + + var vertices = this.geometry.vertices; + + for ( var i = 0; i < vertices.length; i ++ ) { + + testPoint( vertices[ i ], i ); + + } + + } + + }; + +}() ); + +THREE.PointCloud.prototype.clone = function ( object ) { + + if ( object === undefined ) object = new THREE.PointCloud( this.geometry, this.material ); + + THREE.Object3D.prototype.clone.call( this, object ); + + return object; + +}; + +// Backwards compatibility + +THREE.ParticleSystem = function ( geometry, material ) { + + console.warn( 'THREE.ParticleSystem has been renamed to THREE.PointCloud.' ); + return new THREE.PointCloud( geometry, material ); + +}; + +// File:src/objects/Line.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Line = function ( geometry, material, mode ) { + + THREE.Object3D.call( this ); + + this.type = 'Line'; + + this.geometry = geometry !== undefined ? geometry : new THREE.Geometry(); + this.material = material !== undefined ? material : new THREE.LineBasicMaterial( { color: Math.random() * 0xffffff } ); + + this.mode = ( mode !== undefined ) ? mode : THREE.LineStrip; + +}; + +THREE.LineStrip = 0; +THREE.LinePieces = 1; + +THREE.Line.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Line.prototype.constructor = THREE.Line; + +THREE.Line.prototype.raycast = ( function () { + + var inverseMatrix = new THREE.Matrix4(); + var ray = new THREE.Ray(); + var sphere = new THREE.Sphere(); + + return function ( raycaster, intersects ) { + + var precision = raycaster.linePrecision; + var precisionSq = precision * precision; + + var geometry = this.geometry; + + if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); + + // Checking boundingSphere distance to ray + + sphere.copy( geometry.boundingSphere ); + sphere.applyMatrix4( this.matrixWorld ); + + if ( raycaster.ray.isIntersectionSphere( sphere ) === false ) { + + return; + + } + + inverseMatrix.getInverse( this.matrixWorld ); + ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); + + var vStart = new THREE.Vector3(); + var vEnd = new THREE.Vector3(); + var interSegment = new THREE.Vector3(); + var interRay = new THREE.Vector3(); + var step = this.mode === THREE.LineStrip ? 1 : 2; + + if ( geometry instanceof THREE.BufferGeometry ) { + + var attributes = geometry.attributes; + + if ( attributes.index !== undefined ) { + + var indices = attributes.index.array; + var positions = attributes.position.array; + var offsets = geometry.offsets; + + if ( offsets.length === 0 ) { + + offsets = [ { start: 0, count: indices.length, index: 0 } ]; + + } + + for ( var oi = 0; oi < offsets.length; oi++){ + + var start = offsets[ oi ].start; + var count = offsets[ oi ].count; + var index = offsets[ oi ].index; + + for ( var i = start; i < start + count - 1; i += step ) { + + var a = index + indices[ i ]; + var b = index + indices[ i + 1 ]; + + vStart.fromArray( positions, a * 3 ); + vEnd.fromArray( positions, b * 3 ); + + var distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); + + if ( distSq > precisionSq ) continue; + + var distance = ray.origin.distanceTo( interRay ); + + if ( distance < raycaster.near || distance > raycaster.far ) continue; + + intersects.push( { + + distance: distance, + // What do we want? intersection point on the ray or on the segment?? + // point: raycaster.ray.at( distance ), + point: interSegment.clone().applyMatrix4( this.matrixWorld ), + face: null, + faceIndex: null, + object: this + + } ); + + } + + } + + } else { + + var positions = attributes.position.array; + + for ( var i = 0; i < positions.length / 3 - 1; i += step ) { + + vStart.fromArray( positions, 3 * i ); + vEnd.fromArray( positions, 3 * i + 3 ); + + var distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); + + if ( distSq > precisionSq ) continue; + + var distance = ray.origin.distanceTo( interRay ); + + if ( distance < raycaster.near || distance > raycaster.far ) continue; + + intersects.push( { + + distance: distance, + // What do we want? intersection point on the ray or on the segment?? + // point: raycaster.ray.at( distance ), + point: interSegment.clone().applyMatrix4( this.matrixWorld ), + face: null, + faceIndex: null, + object: this + + } ); + + } + + } + + } else if ( geometry instanceof THREE.Geometry ) { + + var vertices = geometry.vertices; + var nbVertices = vertices.length; + + for ( var i = 0; i < nbVertices - 1; i += step ) { + + var distSq = ray.distanceSqToSegment( vertices[ i ], vertices[ i + 1 ], interRay, interSegment ); + + if ( distSq > precisionSq ) continue; + + var distance = ray.origin.distanceTo( interRay ); + + if ( distance < raycaster.near || distance > raycaster.far ) continue; + + intersects.push( { + + distance: distance, + // What do we want? intersection point on the ray or on the segment?? + // point: raycaster.ray.at( distance ), + point: interSegment.clone().applyMatrix4( this.matrixWorld ), + face: null, + faceIndex: null, + object: this + + } ); + + } + + } + + }; + +}() ); + +THREE.Line.prototype.clone = function ( object ) { + + if ( object === undefined ) object = new THREE.Line( this.geometry, this.material, this.mode ); + + THREE.Object3D.prototype.clone.call( this, object ); + + return object; + +}; + +// File:src/objects/Mesh.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * @author mikael emtinger / http://gomo.se/ + * @author jonobr1 / http://jonobr1.com/ + */ + +THREE.Mesh = function ( geometry, material ) { + + THREE.Object3D.call( this ); + + this.type = 'Mesh'; + + this.geometry = geometry !== undefined ? geometry : new THREE.Geometry(); + this.material = material !== undefined ? material : new THREE.MeshBasicMaterial( { color: Math.random() * 0xffffff } ); + + this.updateMorphTargets(); + +}; + +THREE.Mesh.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Mesh.prototype.constructor = THREE.Mesh; + +THREE.Mesh.prototype.updateMorphTargets = function () { + + if ( this.geometry.morphTargets !== undefined && this.geometry.morphTargets.length > 0 ) { + + this.morphTargetBase = - 1; + this.morphTargetForcedOrder = []; + this.morphTargetInfluences = []; + this.morphTargetDictionary = {}; + + for ( var m = 0, ml = this.geometry.morphTargets.length; m < ml; m ++ ) { + + this.morphTargetInfluences.push( 0 ); + this.morphTargetDictionary[ this.geometry.morphTargets[ m ].name ] = m; + + } + + } + +}; + +THREE.Mesh.prototype.getMorphTargetIndexByName = function ( name ) { + + if ( this.morphTargetDictionary[ name ] !== undefined ) { + + return this.morphTargetDictionary[ name ]; + + } + + console.log( 'THREE.Mesh.getMorphTargetIndexByName: morph target ' + name + ' does not exist. Returning 0.' ); + + return 0; + +}; + + +THREE.Mesh.prototype.raycast = ( function () { + + var inverseMatrix = new THREE.Matrix4(); + var ray = new THREE.Ray(); + var sphere = new THREE.Sphere(); + + var vA = new THREE.Vector3(); + var vB = new THREE.Vector3(); + var vC = new THREE.Vector3(); + + return function ( raycaster, intersects ) { + + var geometry = this.geometry; + + // Checking boundingSphere distance to ray + + if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); + + sphere.copy( geometry.boundingSphere ); + sphere.applyMatrix4( this.matrixWorld ); + + if ( raycaster.ray.isIntersectionSphere( sphere ) === false ) { + + return; + + } + + // Check boundingBox before continuing + + inverseMatrix.getInverse( this.matrixWorld ); + ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); + + if ( geometry.boundingBox !== null ) { + + if ( ray.isIntersectionBox( geometry.boundingBox ) === false ) { + + return; + + } + + } + + if ( geometry instanceof THREE.BufferGeometry ) { + + var material = this.material; + + if ( material === undefined ) return; + + var attributes = geometry.attributes; + + var a, b, c; + var precision = raycaster.precision; + + if ( attributes.index !== undefined ) { + + var indices = attributes.index.array; + var positions = attributes.position.array; + var offsets = geometry.offsets; + + if ( offsets.length === 0 ) { + + offsets = [ { start: 0, count: indices.length, index: 0 } ]; + + } + + for ( var oi = 0, ol = offsets.length; oi < ol; ++oi ) { + + var start = offsets[ oi ].start; + var count = offsets[ oi ].count; + var index = offsets[ oi ].index; + + for ( var i = start, il = start + count; i < il; i += 3 ) { + + a = index + indices[ i ]; + b = index + indices[ i + 1 ]; + c = index + indices[ i + 2 ]; + + vA.fromArray( positions, a * 3 ); + vB.fromArray( positions, b * 3 ); + vC.fromArray( positions, c * 3 ); + + if ( material.side === THREE.BackSide ) { + + var intersectionPoint = ray.intersectTriangle( vC, vB, vA, true ); + + } else { + + var intersectionPoint = ray.intersectTriangle( vA, vB, vC, material.side !== THREE.DoubleSide ); + + } + + if ( intersectionPoint === null ) continue; + + intersectionPoint.applyMatrix4( this.matrixWorld ); + + var distance = raycaster.ray.origin.distanceTo( intersectionPoint ); + + if ( distance < precision || distance < raycaster.near || distance > raycaster.far ) continue; + + intersects.push( { + + distance: distance, + point: intersectionPoint, + face: new THREE.Face3( a, b, c, THREE.Triangle.normal( vA, vB, vC ) ), + faceIndex: null, + object: this + + } ); + + } + + } + + } else { + + var positions = attributes.position.array; + + for ( var i = 0, j = 0, il = positions.length; i < il; i += 3, j += 9 ) { + + a = i; + b = i + 1; + c = i + 2; + + vA.fromArray( positions, j ); + vB.fromArray( positions, j + 3 ); + vC.fromArray( positions, j + 6 ); + + if ( material.side === THREE.BackSide ) { + + var intersectionPoint = ray.intersectTriangle( vC, vB, vA, true ); + + } else { + + var intersectionPoint = ray.intersectTriangle( vA, vB, vC, material.side !== THREE.DoubleSide ); + + } + + if ( intersectionPoint === null ) continue; + + intersectionPoint.applyMatrix4( this.matrixWorld ); + + var distance = raycaster.ray.origin.distanceTo( intersectionPoint ); + + if ( distance < precision || distance < raycaster.near || distance > raycaster.far ) continue; + + intersects.push( { + + distance: distance, + point: intersectionPoint, + face: new THREE.Face3( a, b, c, THREE.Triangle.normal( vA, vB, vC ) ), + faceIndex: null, + object: this + + } ); + + } + + } + + } else if ( geometry instanceof THREE.Geometry ) { + + var isFaceMaterial = this.material instanceof THREE.MeshFaceMaterial; + var objectMaterials = isFaceMaterial === true ? this.material.materials : null; + + var a, b, c, d; + var precision = raycaster.precision; + + var vertices = geometry.vertices; + + for ( var f = 0, fl = geometry.faces.length; f < fl; f ++ ) { + + var face = geometry.faces[ f ]; + + var material = isFaceMaterial === true ? objectMaterials[ face.materialIndex ] : this.material; + + if ( material === undefined ) continue; + + a = vertices[ face.a ]; + b = vertices[ face.b ]; + c = vertices[ face.c ]; + + if ( material.morphTargets === true ) { + + var morphTargets = geometry.morphTargets; + var morphInfluences = this.morphTargetInfluences; + + vA.set( 0, 0, 0 ); + vB.set( 0, 0, 0 ); + vC.set( 0, 0, 0 ); + + for ( var t = 0, tl = morphTargets.length; t < tl; t ++ ) { + + var influence = morphInfluences[ t ]; + + if ( influence === 0 ) continue; + + var targets = morphTargets[ t ].vertices; + + vA.x += ( targets[ face.a ].x - a.x ) * influence; + vA.y += ( targets[ face.a ].y - a.y ) * influence; + vA.z += ( targets[ face.a ].z - a.z ) * influence; + + vB.x += ( targets[ face.b ].x - b.x ) * influence; + vB.y += ( targets[ face.b ].y - b.y ) * influence; + vB.z += ( targets[ face.b ].z - b.z ) * influence; + + vC.x += ( targets[ face.c ].x - c.x ) * influence; + vC.y += ( targets[ face.c ].y - c.y ) * influence; + vC.z += ( targets[ face.c ].z - c.z ) * influence; + + } + + vA.add( a ); + vB.add( b ); + vC.add( c ); + + a = vA; + b = vB; + c = vC; + + } + + if ( material.side === THREE.BackSide ) { + + var intersectionPoint = ray.intersectTriangle( c, b, a, true ); + + } else { + + var intersectionPoint = ray.intersectTriangle( a, b, c, material.side !== THREE.DoubleSide ); + + } + + if ( intersectionPoint === null ) continue; + + intersectionPoint.applyMatrix4( this.matrixWorld ); + + var distance = raycaster.ray.origin.distanceTo( intersectionPoint ); + + if ( distance < precision || distance < raycaster.near || distance > raycaster.far ) continue; + + intersects.push( { + + distance: distance, + point: intersectionPoint, + face: face, + faceIndex: f, + object: this + + } ); + + } + + } + + }; + +}() ); + +THREE.Mesh.prototype.clone = function ( object, recursive ) { + + if ( object === undefined ) object = new THREE.Mesh( this.geometry, this.material ); + + THREE.Object3D.prototype.clone.call( this, object, recursive ); + + return object; + +}; + +// File:src/objects/Bone.js + +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + * @author ikerr / http://verold.com + */ + +THREE.Bone = function ( belongsToSkin ) { + + THREE.Object3D.call( this ); + + this.skin = belongsToSkin; + +}; + +THREE.Bone.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Bone.prototype.constructor = THREE.Bone; + + +// File:src/objects/Skeleton.js + +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + * @author michael guerrero / http://realitymeltdown.com + * @author ikerr / http://verold.com + */ + +THREE.Skeleton = function ( bones, boneInverses, useVertexTexture ) { + + this.useVertexTexture = useVertexTexture !== undefined ? useVertexTexture : true; + + this.identityMatrix = new THREE.Matrix4(); + + // copy the bone array + + bones = bones || []; + + this.bones = bones.slice( 0 ); + + // create a bone texture or an array of floats + + if ( this.useVertexTexture ) { + + // layout (1 matrix = 4 pixels) + // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) + // with 8x8 pixel texture max 16 bones (8 * 8 / 4) + // 16x16 pixel texture max 64 bones (16 * 16 / 4) + // 32x32 pixel texture max 256 bones (32 * 32 / 4) + // 64x64 pixel texture max 1024 bones (64 * 64 / 4) + + var size; + + if ( this.bones.length > 256 ) + size = 64; + else if ( this.bones.length > 64 ) + size = 32; + else if ( this.bones.length > 16 ) + size = 16; + else + size = 8; + + this.boneTextureWidth = size; + this.boneTextureHeight = size; + + this.boneMatrices = new Float32Array( this.boneTextureWidth * this.boneTextureHeight * 4 ); // 4 floats per RGBA pixel + this.boneTexture = new THREE.DataTexture( this.boneMatrices, this.boneTextureWidth, this.boneTextureHeight, THREE.RGBAFormat, THREE.FloatType ); + this.boneTexture.minFilter = THREE.NearestFilter; + this.boneTexture.magFilter = THREE.NearestFilter; + this.boneTexture.generateMipmaps = false; + this.boneTexture.flipY = false; + + } else { + + this.boneMatrices = new Float32Array( 16 * this.bones.length ); + + } + + // use the supplied bone inverses or calculate the inverses + + if ( boneInverses === undefined ) { + + this.calculateInverses(); + + } else { + + if ( this.bones.length === boneInverses.length ) { + + this.boneInverses = boneInverses.slice( 0 ); + + } else { + + console.warn( 'THREE.Skeleton bonInverses is the wrong length.' ); + + this.boneInverses = []; + + for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { + + this.boneInverses.push( new THREE.Matrix4() ); + + } + + } + + } + +}; + +THREE.Skeleton.prototype.calculateInverses = function () { + + this.boneInverses = []; + + for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { + + var inverse = new THREE.Matrix4(); + + if ( this.bones[ b ] ) { + + inverse.getInverse( this.bones[ b ].matrixWorld ); + + } + + this.boneInverses.push( inverse ); + + } + +}; + +THREE.Skeleton.prototype.pose = function () { + + var bone; + + // recover the bind-time world matrices + + for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { + + bone = this.bones[ b ]; + + if ( bone ) { + + bone.matrixWorld.getInverse( this.boneInverses[ b ] ); + + } + + } + + // compute the local matrices, positions, rotations and scales + + for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { + + bone = this.bones[ b ]; + + if ( bone ) { + + if ( bone.parent ) { + + bone.matrix.getInverse( bone.parent.matrixWorld ); + bone.matrix.multiply( bone.matrixWorld ); + + } else { + + bone.matrix.copy( bone.matrixWorld ); + + } + + bone.matrix.decompose( bone.position, bone.quaternion, bone.scale ); + + } + + } + +}; + +THREE.Skeleton.prototype.update = ( function () { + + var offsetMatrix = new THREE.Matrix4(); + + return function () { + + // flatten bone matrices to array + + for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { + + // compute the offset between the current and the original transform + + var matrix = this.bones[ b ] ? this.bones[ b ].matrixWorld : this.identityMatrix; + + offsetMatrix.multiplyMatrices( matrix, this.boneInverses[ b ] ); + offsetMatrix.flattenToArrayOffset( this.boneMatrices, b * 16 ); + + } + + if ( this.useVertexTexture ) { + + this.boneTexture.needsUpdate = true; + + } + + }; + +} )(); + + +// File:src/objects/SkinnedMesh.js + +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + * @author ikerr / http://verold.com + */ + +THREE.SkinnedMesh = function ( geometry, material, useVertexTexture ) { + + THREE.Mesh.call( this, geometry, material ); + + this.type = 'SkinnedMesh'; + + this.bindMode = "attached"; + this.bindMatrix = new THREE.Matrix4(); + this.bindMatrixInverse = new THREE.Matrix4(); + + // init bones + + // TODO: remove bone creation as there is no reason (other than + // convenience) for THREE.SkinnedMesh to do this. + + var bones = []; + + if ( this.geometry && this.geometry.bones !== undefined ) { + + var bone, gbone, p, q, s; + + for ( var b = 0, bl = this.geometry.bones.length; b < bl; ++b ) { + + gbone = this.geometry.bones[ b ]; + + p = gbone.pos; + q = gbone.rotq; + s = gbone.scl; + + bone = new THREE.Bone( this ); + bones.push( bone ); + + bone.name = gbone.name; + bone.position.set( p[ 0 ], p[ 1 ], p[ 2 ] ); + bone.quaternion.set( q[ 0 ], q[ 1 ], q[ 2 ], q[ 3 ] ); + + if ( s !== undefined ) { + + bone.scale.set( s[ 0 ], s[ 1 ], s[ 2 ] ); + + } else { + + bone.scale.set( 1, 1, 1 ); + + } + + } + + for ( var b = 0, bl = this.geometry.bones.length; b < bl; ++b ) { + + gbone = this.geometry.bones[ b ]; + + if ( gbone.parent !== - 1 ) { + + bones[ gbone.parent ].add( bones[ b ] ); + + } else { + + this.add( bones[ b ] ); + + } + + } + + } + + this.normalizeSkinWeights(); + + this.updateMatrixWorld( true ); + this.bind( new THREE.Skeleton( bones, undefined, useVertexTexture ) ); + +}; + + +THREE.SkinnedMesh.prototype = Object.create( THREE.Mesh.prototype ); +THREE.SkinnedMesh.prototype.constructor = THREE.SkinnedMesh; + +THREE.SkinnedMesh.prototype.bind = function( skeleton, bindMatrix ) { + + this.skeleton = skeleton; + + if ( bindMatrix === undefined ) { + + this.updateMatrixWorld( true ); + + bindMatrix = this.matrixWorld; + + } + + this.bindMatrix.copy( bindMatrix ); + this.bindMatrixInverse.getInverse( bindMatrix ); + +}; + +THREE.SkinnedMesh.prototype.pose = function () { + + this.skeleton.pose(); + +}; + +THREE.SkinnedMesh.prototype.normalizeSkinWeights = function () { + + if ( this.geometry instanceof THREE.Geometry ) { + + for ( var i = 0; i < this.geometry.skinIndices.length; i ++ ) { + + var sw = this.geometry.skinWeights[ i ]; + + var scale = 1.0 / sw.lengthManhattan(); + + if ( scale !== Infinity ) { + + sw.multiplyScalar( scale ); + + } else { + + sw.set( 1 ); // this will be normalized by the shader anyway + + } + + } + + } else { + + // skinning weights assumed to be normalized for THREE.BufferGeometry + + } + +}; + +THREE.SkinnedMesh.prototype.updateMatrixWorld = function( force ) { + + THREE.Mesh.prototype.updateMatrixWorld.call( this, true ); + + if ( this.bindMode === "attached" ) { + + this.bindMatrixInverse.getInverse( this.matrixWorld ); + + } else if ( this.bindMode === "detached" ) { + + this.bindMatrixInverse.getInverse( this.bindMatrix ); + + } else { + + console.warn( 'THREE.SkinnedMesh unreckognized bindMode: ' + this.bindMode ); + + } + +}; + +THREE.SkinnedMesh.prototype.clone = function( object ) { + + if ( object === undefined ) { + + object = new THREE.SkinnedMesh( this.geometry, this.material, this.useVertexTexture ); + + } + + THREE.Mesh.prototype.clone.call( this, object ); + + return object; + +}; + + +// File:src/objects/MorphAnimMesh.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.MorphAnimMesh = function ( geometry, material ) { + + THREE.Mesh.call( this, geometry, material ); + + this.type = 'MorphAnimMesh'; + + // API + + this.duration = 1000; // milliseconds + this.mirroredLoop = false; + this.time = 0; + + // internals + + this.lastKeyframe = 0; + this.currentKeyframe = 0; + + this.direction = 1; + this.directionBackwards = false; + + this.setFrameRange( 0, this.geometry.morphTargets.length - 1 ); + +}; + +THREE.MorphAnimMesh.prototype = Object.create( THREE.Mesh.prototype ); +THREE.MorphAnimMesh.prototype.constructor = THREE.MorphAnimMesh; + +THREE.MorphAnimMesh.prototype.setFrameRange = function ( start, end ) { + + this.startKeyframe = start; + this.endKeyframe = end; + + this.length = this.endKeyframe - this.startKeyframe + 1; + +}; + +THREE.MorphAnimMesh.prototype.setDirectionForward = function () { + + this.direction = 1; + this.directionBackwards = false; + +}; + +THREE.MorphAnimMesh.prototype.setDirectionBackward = function () { + + this.direction = - 1; + this.directionBackwards = true; + +}; + +THREE.MorphAnimMesh.prototype.parseAnimations = function () { + + var geometry = this.geometry; + + if ( ! geometry.animations ) geometry.animations = {}; + + var firstAnimation, animations = geometry.animations; + + var pattern = /([a-z]+)_?(\d+)/; + + for ( var i = 0, il = geometry.morphTargets.length; i < il; i ++ ) { + + var morph = geometry.morphTargets[ i ]; + var parts = morph.name.match( pattern ); + + if ( parts && parts.length > 1 ) { + + var label = parts[ 1 ]; + var num = parts[ 2 ]; + + if ( ! animations[ label ] ) animations[ label ] = { start: Infinity, end: - Infinity }; + + var animation = animations[ label ]; + + if ( i < animation.start ) animation.start = i; + if ( i > animation.end ) animation.end = i; + + if ( ! firstAnimation ) firstAnimation = label; + + } + + } + + geometry.firstAnimation = firstAnimation; + +}; + +THREE.MorphAnimMesh.prototype.setAnimationLabel = function ( label, start, end ) { + + if ( ! this.geometry.animations ) this.geometry.animations = {}; + + this.geometry.animations[ label ] = { start: start, end: end }; + +}; + +THREE.MorphAnimMesh.prototype.playAnimation = function ( label, fps ) { + + var animation = this.geometry.animations[ label ]; + + if ( animation ) { + + this.setFrameRange( animation.start, animation.end ); + this.duration = 1000 * ( ( animation.end - animation.start ) / fps ); + this.time = 0; + + } else { + + console.warn( 'animation[' + label + '] undefined' ); + + } + +}; + +THREE.MorphAnimMesh.prototype.updateAnimation = function ( delta ) { + + var frameTime = this.duration / this.length; + + this.time += this.direction * delta; + + if ( this.mirroredLoop ) { + + if ( this.time > this.duration || this.time < 0 ) { + + this.direction *= - 1; + + if ( this.time > this.duration ) { + + this.time = this.duration; + this.directionBackwards = true; + + } + + if ( this.time < 0 ) { + + this.time = 0; + this.directionBackwards = false; + + } + + } + + } else { + + this.time = this.time % this.duration; + + if ( this.time < 0 ) this.time += this.duration; + + } + + var keyframe = this.startKeyframe + THREE.Math.clamp( Math.floor( this.time / frameTime ), 0, this.length - 1 ); + + if ( keyframe !== this.currentKeyframe ) { + + this.morphTargetInfluences[ this.lastKeyframe ] = 0; + this.morphTargetInfluences[ this.currentKeyframe ] = 1; + + this.morphTargetInfluences[ keyframe ] = 0; + + this.lastKeyframe = this.currentKeyframe; + this.currentKeyframe = keyframe; + + } + + var mix = ( this.time % frameTime ) / frameTime; + + if ( this.directionBackwards ) { + + mix = 1 - mix; + + } + + this.morphTargetInfluences[ this.currentKeyframe ] = mix; + this.morphTargetInfluences[ this.lastKeyframe ] = 1 - mix; + +}; + +THREE.MorphAnimMesh.prototype.interpolateTargets = function ( a, b, t ) { + + var influences = this.morphTargetInfluences; + + for ( var i = 0, l = influences.length; i < l; i ++ ) { + + influences[ i ] = 0; + + } + + if ( a > -1 ) influences[ a ] = 1 - t; + if ( b > -1 ) influences[ b ] = t; + +}; + +THREE.MorphAnimMesh.prototype.clone = function ( object ) { + + if ( object === undefined ) object = new THREE.MorphAnimMesh( this.geometry, this.material ); + + object.duration = this.duration; + object.mirroredLoop = this.mirroredLoop; + object.time = this.time; + + object.lastKeyframe = this.lastKeyframe; + object.currentKeyframe = this.currentKeyframe; + + object.direction = this.direction; + object.directionBackwards = this.directionBackwards; + + THREE.Mesh.prototype.clone.call( this, object ); + + return object; + +}; + +// File:src/objects/LOD.js + +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.LOD = function () { + + THREE.Object3D.call( this ); + + this.objects = []; + +}; + + +THREE.LOD.prototype = Object.create( THREE.Object3D.prototype ); +THREE.LOD.prototype.constructor = THREE.LOD; + +THREE.LOD.prototype.addLevel = function ( object, distance ) { + + if ( distance === undefined ) distance = 0; + + distance = Math.abs( distance ); + + for ( var l = 0; l < this.objects.length; l ++ ) { + + if ( distance < this.objects[ l ].distance ) { + + break; + + } + + } + + this.objects.splice( l, 0, { distance: distance, object: object } ); + this.add( object ); + +}; + +THREE.LOD.prototype.getObjectForDistance = function ( distance ) { + + for ( var i = 1, l = this.objects.length; i < l; i ++ ) { + + if ( distance < this.objects[ i ].distance ) { + + break; + + } + + } + + return this.objects[ i - 1 ].object; + +}; + +THREE.LOD.prototype.raycast = ( function () { + + var matrixPosition = new THREE.Vector3(); + + return function ( raycaster, intersects ) { + + matrixPosition.setFromMatrixPosition( this.matrixWorld ); + + var distance = raycaster.ray.origin.distanceTo( matrixPosition ); + + this.getObjectForDistance( distance ).raycast( raycaster, intersects ); + + }; + +}() ); + +THREE.LOD.prototype.update = function () { + + var v1 = new THREE.Vector3(); + var v2 = new THREE.Vector3(); + + return function ( camera ) { + + if ( this.objects.length > 1 ) { + + v1.setFromMatrixPosition( camera.matrixWorld ); + v2.setFromMatrixPosition( this.matrixWorld ); + + var distance = v1.distanceTo( v2 ); + + this.objects[ 0 ].object.visible = true; + + for ( var i = 1, l = this.objects.length; i < l; i ++ ) { + + if ( distance >= this.objects[ i ].distance ) { + + this.objects[ i - 1 ].object.visible = false; + this.objects[ i ].object.visible = true; + + } else { + + break; + + } + + } + + for ( ; i < l; i ++ ) { + + this.objects[ i ].object.visible = false; + + } + + } + + }; + +}(); + +THREE.LOD.prototype.clone = function ( object ) { + + if ( object === undefined ) object = new THREE.LOD(); + + THREE.Object3D.prototype.clone.call( this, object ); + + for ( var i = 0, l = this.objects.length; i < l; i ++ ) { + var x = this.objects[ i ].object.clone(); + x.visible = i === 0; + object.addLevel( x, this.objects[ i ].distance ); + } + + return object; + +}; + +// File:src/objects/Sprite.js + +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Sprite = ( function () { + + var indices = new Uint16Array( [ 0, 1, 2, 0, 2, 3 ] ); + var vertices = new Float32Array( [ - 0.5, - 0.5, 0, 0.5, - 0.5, 0, 0.5, 0.5, 0, - 0.5, 0.5, 0 ] ); + var uvs = new Float32Array( [ 0, 0, 1, 0, 1, 1, 0, 1 ] ); + + var geometry = new THREE.BufferGeometry(); + geometry.addAttribute( 'index', new THREE.BufferAttribute( indices, 1 ) ); + geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) ); + geometry.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) ); + + return function ( material ) { + + THREE.Object3D.call( this ); + + this.type = 'Sprite'; + + this.geometry = geometry; + this.material = ( material !== undefined ) ? material : new THREE.SpriteMaterial(); + + }; + +} )(); + +THREE.Sprite.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Sprite.prototype.constructor = THREE.Sprite; + +THREE.Sprite.prototype.raycast = ( function () { + + var matrixPosition = new THREE.Vector3(); + + return function ( raycaster, intersects ) { + + matrixPosition.setFromMatrixPosition( this.matrixWorld ); + + var distance = raycaster.ray.distanceToPoint( matrixPosition ); + + if ( distance > this.scale.x ) { + + return; + + } + + intersects.push( { + + distance: distance, + point: this.position, + face: null, + object: this + + } ); + + }; + +}() ); + +THREE.Sprite.prototype.clone = function ( object ) { + + if ( object === undefined ) object = new THREE.Sprite( this.material ); + + THREE.Object3D.prototype.clone.call( this, object ); + + return object; + +}; + +// Backwards compatibility + +THREE.Particle = THREE.Sprite; + +// File:src/objects/LensFlare.js + +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.LensFlare = function ( texture, size, distance, blending, color ) { + + THREE.Object3D.call( this ); + + this.lensFlares = []; + + this.positionScreen = new THREE.Vector3(); + this.customUpdateCallback = undefined; + + if( texture !== undefined ) { + + this.add( texture, size, distance, blending, color ); + + } + +}; + +THREE.LensFlare.prototype = Object.create( THREE.Object3D.prototype ); +THREE.LensFlare.prototype.constructor = THREE.LensFlare; + + +/* + * Add: adds another flare + */ + +THREE.LensFlare.prototype.add = function ( texture, size, distance, blending, color, opacity ) { + + if ( size === undefined ) size = - 1; + if ( distance === undefined ) distance = 0; + if ( opacity === undefined ) opacity = 1; + if ( color === undefined ) color = new THREE.Color( 0xffffff ); + if ( blending === undefined ) blending = THREE.NormalBlending; + + distance = Math.min( distance, Math.max( 0, distance ) ); + + this.lensFlares.push( { + texture: texture, // THREE.Texture + size: size, // size in pixels (-1 = use texture.width) + distance: distance, // distance (0-1) from light source (0=at light source) + x: 0, y: 0, z: 0, // screen position (-1 => 1) z = 0 is ontop z = 1 is back + scale: 1, // scale + rotation: 1, // rotation + opacity: opacity, // opacity + color: color, // color + blending: blending // blending + } ); + +}; + +/* + * Update lens flares update positions on all flares based on the screen position + * Set myLensFlare.customUpdateCallback to alter the flares in your project specific way. + */ + +THREE.LensFlare.prototype.updateLensFlares = function () { + + var f, fl = this.lensFlares.length; + var flare; + var vecX = - this.positionScreen.x * 2; + var vecY = - this.positionScreen.y * 2; + + for( f = 0; f < fl; f ++ ) { + + flare = this.lensFlares[ f ]; + + flare.x = this.positionScreen.x + vecX * flare.distance; + flare.y = this.positionScreen.y + vecY * flare.distance; + + flare.wantedRotation = flare.x * Math.PI * 0.25; + flare.rotation += ( flare.wantedRotation - flare.rotation ) * 0.25; + + } + +}; + + +// File:src/scenes/Scene.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Scene = function () { + + THREE.Object3D.call( this ); + + this.type = 'Scene'; + + this.fog = null; + this.overrideMaterial = null; + + this.autoUpdate = true; // checked by the renderer + +}; + +THREE.Scene.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Scene.prototype.constructor = THREE.Scene; + +THREE.Scene.prototype.clone = function ( object ) { + + if ( object === undefined ) object = new THREE.Scene(); + + THREE.Object3D.prototype.clone.call( this, object ); + + if ( this.fog !== null ) object.fog = this.fog.clone(); + if ( this.overrideMaterial !== null ) object.overrideMaterial = this.overrideMaterial.clone(); + + object.autoUpdate = this.autoUpdate; + object.matrixAutoUpdate = this.matrixAutoUpdate; + + return object; + +}; + +// File:src/scenes/Fog.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Fog = function ( color, near, far ) { + + this.name = ''; + + this.color = new THREE.Color( color ); + + this.near = ( near !== undefined ) ? near : 1; + this.far = ( far !== undefined ) ? far : 1000; + +}; + +THREE.Fog.prototype.clone = function () { + + return new THREE.Fog( this.color.getHex(), this.near, this.far ); + +}; + +// File:src/scenes/FogExp2.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.FogExp2 = function ( color, density ) { + + this.name = ''; + + this.color = new THREE.Color( color ); + this.density = ( density !== undefined ) ? density : 0.00025; + +}; + +THREE.FogExp2.prototype.clone = function () { + + return new THREE.FogExp2( this.color.getHex(), this.density ); + +}; + +// File:src/renderers/shaders/ShaderChunk.js + +THREE.ShaderChunk = {}; + +// File:src/renderers/shaders/ShaderChunk/alphatest_fragment.glsl + +THREE.ShaderChunk[ 'alphatest_fragment'] = "#ifdef ALPHATEST\n\n if ( gl_FragColor.a < ALPHATEST ) discard;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/lights_lambert_vertex.glsl + +THREE.ShaderChunk[ 'lights_lambert_vertex'] = "vLightFront = vec3( 0.0 );\n\n#ifdef DOUBLE_SIDED\n\n vLightBack = vec3( 0.0 );\n\n#endif\n\ntransformedNormal = normalize( transformedNormal );\n\n#if MAX_DIR_LIGHTS > 0\n\nfor( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\n\n vec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );\n vec3 dirVector = normalize( lDirection.xyz );\n\n float dotProduct = dot( transformedNormal, dirVector );\n vec3 directionalLightWeighting = vec3( max( dotProduct, 0.0 ) );\n\n #ifdef DOUBLE_SIDED\n\n vec3 directionalLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );\n\n #ifdef WRAP_AROUND\n\n vec3 directionalLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );\n\n #endif\n\n #endif\n\n #ifdef WRAP_AROUND\n\n vec3 directionalLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );\n directionalLightWeighting = mix( directionalLightWeighting, directionalLightWeightingHalf, wrapRGB );\n\n #ifdef DOUBLE_SIDED\n\n directionalLightWeightingBack = mix( directionalLightWeightingBack, directionalLightWeightingHalfBack, wrapRGB );\n\n #endif\n\n #endif\n\n vLightFront += directionalLightColor[ i ] * directionalLightWeighting;\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += directionalLightColor[ i ] * directionalLightWeightingBack;\n\n #endif\n\n}\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n for( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n\n vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );\n vec3 lVector = lPosition.xyz - mvPosition.xyz;\n\n float lDistance = 1.0;\n if ( pointLightDistance[ i ] > 0.0 )\n lDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );\n\n lVector = normalize( lVector );\n float dotProduct = dot( transformedNormal, lVector );\n\n vec3 pointLightWeighting = vec3( max( dotProduct, 0.0 ) );\n\n #ifdef DOUBLE_SIDED\n\n vec3 pointLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );\n\n #ifdef WRAP_AROUND\n\n vec3 pointLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );\n\n #endif\n\n #endif\n\n #ifdef WRAP_AROUND\n\n vec3 pointLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );\n pointLightWeighting = mix( pointLightWeighting, pointLightWeightingHalf, wrapRGB );\n\n #ifdef DOUBLE_SIDED\n\n pointLightWeightingBack = mix( pointLightWeightingBack, pointLightWeightingHalfBack, wrapRGB );\n\n #endif\n\n #endif\n\n vLightFront += pointLightColor[ i ] * pointLightWeighting * lDistance;\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += pointLightColor[ i ] * pointLightWeightingBack * lDistance;\n\n #endif\n\n }\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n for( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\n\n vec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );\n vec3 lVector = lPosition.xyz - mvPosition.xyz;\n\n float spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - worldPosition.xyz ) );\n\n if ( spotEffect > spotLightAngleCos[ i ] ) {\n\n spotEffect = max( pow( max( spotEffect, 0.0 ), spotLightExponent[ i ] ), 0.0 );\n\n float lDistance = 1.0;\n if ( spotLightDistance[ i ] > 0.0 )\n lDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );\n\n lVector = normalize( lVector );\n\n float dotProduct = dot( transformedNormal, lVector );\n vec3 spotLightWeighting = vec3( max( dotProduct, 0.0 ) );\n\n #ifdef DOUBLE_SIDED\n\n vec3 spotLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );\n\n #ifdef WRAP_AROUND\n\n vec3 spotLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );\n\n #endif\n\n #endif\n\n #ifdef WRAP_AROUND\n\n vec3 spotLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );\n spotLightWeighting = mix( spotLightWeighting, spotLightWeightingHalf, wrapRGB );\n\n #ifdef DOUBLE_SIDED\n\n spotLightWeightingBack = mix( spotLightWeightingBack, spotLightWeightingHalfBack, wrapRGB );\n\n #endif\n\n #endif\n\n vLightFront += spotLightColor[ i ] * spotLightWeighting * lDistance * spotEffect;\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += spotLightColor[ i ] * spotLightWeightingBack * lDistance * spotEffect;\n\n #endif\n\n }\n\n }\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n for( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\n\n vec4 lDirection = viewMatrix * vec4( hemisphereLightDirection[ i ], 0.0 );\n vec3 lVector = normalize( lDirection.xyz );\n\n float dotProduct = dot( transformedNormal, lVector );\n\n float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\n float hemiDiffuseWeightBack = -0.5 * dotProduct + 0.5;\n\n vLightFront += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeightBack );\n\n #endif\n\n }\n\n#endif\n\nvLightFront = vLightFront * diffuse + ambient * ambientLightColor + emissive;\n\n#ifdef DOUBLE_SIDED\n\n vLightBack = vLightBack * diffuse + ambient * ambientLightColor + emissive;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/map_particle_pars_fragment.glsl + +THREE.ShaderChunk[ 'map_particle_pars_fragment'] = "#ifdef USE_MAP\n\n uniform sampler2D map;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/default_vertex.glsl + +THREE.ShaderChunk[ 'default_vertex'] = "#ifdef USE_SKINNING\n\n vec4 mvPosition = modelViewMatrix * skinned;\n\n#elif defined( USE_MORPHTARGETS )\n\n vec4 mvPosition = modelViewMatrix * vec4( morphed, 1.0 );\n\n#else\n\n vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\n\n#endif\n\ngl_Position = projectionMatrix * mvPosition;\n"; + +// File:src/renderers/shaders/ShaderChunk/map_pars_fragment.glsl + +THREE.ShaderChunk[ 'map_pars_fragment'] = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP )\n\n varying vec2 vUv;\n\n#endif\n\n#ifdef USE_MAP\n\n uniform sampler2D map;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/skinnormal_vertex.glsl + +THREE.ShaderChunk[ 'skinnormal_vertex'] = "#ifdef USE_SKINNING\n\n mat4 skinMatrix = mat4( 0.0 );\n skinMatrix += skinWeight.x * boneMatX;\n skinMatrix += skinWeight.y * boneMatY;\n skinMatrix += skinWeight.z * boneMatZ;\n skinMatrix += skinWeight.w * boneMatW;\n skinMatrix = bindMatrixInverse * skinMatrix * bindMatrix;\n\n #ifdef USE_MORPHNORMALS\n\n vec4 skinnedNormal = skinMatrix * vec4( morphedNormal, 0.0 );\n\n #else\n\n vec4 skinnedNormal = skinMatrix * vec4( normal, 0.0 );\n\n #endif\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/logdepthbuf_pars_vertex.glsl + +THREE.ShaderChunk[ 'logdepthbuf_pars_vertex'] = "#ifdef USE_LOGDEPTHBUF\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n varying float vFragDepth;\n\n #endif\n\n uniform float logDepthBufFC;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/lightmap_pars_vertex.glsl + +THREE.ShaderChunk[ 'lightmap_pars_vertex'] = "#ifdef USE_LIGHTMAP\n\n varying vec2 vUv2;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/lights_phong_fragment.glsl + +THREE.ShaderChunk[ 'lights_phong_fragment'] = "vec3 normal = normalize( vNormal );\nvec3 viewPosition = normalize( vViewPosition );\n\n#ifdef DOUBLE_SIDED\n\n normal = normal * ( -1.0 + 2.0 * float( gl_FrontFacing ) );\n\n#endif\n\n#ifdef USE_NORMALMAP\n\n normal = perturbNormal2Arb( -vViewPosition, normal );\n\n#elif defined( USE_BUMPMAP )\n\n normal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n vec3 pointDiffuse = vec3( 0.0 );\n vec3 pointSpecular = vec3( 0.0 );\n\n for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n\n vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );\n vec3 lVector = lPosition.xyz + vViewPosition.xyz;\n\n float lDistance = 1.0;\n if ( pointLightDistance[ i ] > 0.0 )\n lDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );\n\n lVector = normalize( lVector );\n\n // diffuse\n\n float dotProduct = dot( normal, lVector );\n\n #ifdef WRAP_AROUND\n\n float pointDiffuseWeightFull = max( dotProduct, 0.0 );\n float pointDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );\n\n vec3 pointDiffuseWeight = mix( vec3( pointDiffuseWeightFull ), vec3( pointDiffuseWeightHalf ), wrapRGB );\n\n #else\n\n float pointDiffuseWeight = max( dotProduct, 0.0 );\n\n #endif\n\n pointDiffuse += diffuse * pointLightColor[ i ] * pointDiffuseWeight * lDistance;\n\n // specular\n\n vec3 pointHalfVector = normalize( lVector + viewPosition );\n float pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );\n float pointSpecularWeight = specularStrength * max( pow( pointDotNormalHalf, shininess ), 0.0 );\n\n float specularNormalization = ( shininess + 2.0 ) / 8.0;\n\n vec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVector, pointHalfVector ), 0.0 ), 5.0 );\n pointSpecular += schlick * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * lDistance * specularNormalization;\n\n }\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n vec3 spotDiffuse = vec3( 0.0 );\n vec3 spotSpecular = vec3( 0.0 );\n\n for ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\n\n vec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );\n vec3 lVector = lPosition.xyz + vViewPosition.xyz;\n\n float lDistance = 1.0;\n if ( spotLightDistance[ i ] > 0.0 )\n lDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );\n\n lVector = normalize( lVector );\n\n float spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - vWorldPosition ) );\n\n if ( spotEffect > spotLightAngleCos[ i ] ) {\n\n spotEffect = max( pow( max( spotEffect, 0.0 ), spotLightExponent[ i ] ), 0.0 );\n\n // diffuse\n\n float dotProduct = dot( normal, lVector );\n\n #ifdef WRAP_AROUND\n\n float spotDiffuseWeightFull = max( dotProduct, 0.0 );\n float spotDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );\n\n vec3 spotDiffuseWeight = mix( vec3( spotDiffuseWeightFull ), vec3( spotDiffuseWeightHalf ), wrapRGB );\n\n #else\n\n float spotDiffuseWeight = max( dotProduct, 0.0 );\n\n #endif\n\n spotDiffuse += diffuse * spotLightColor[ i ] * spotDiffuseWeight * lDistance * spotEffect;\n\n // specular\n\n vec3 spotHalfVector = normalize( lVector + viewPosition );\n float spotDotNormalHalf = max( dot( normal, spotHalfVector ), 0.0 );\n float spotSpecularWeight = specularStrength * max( pow( spotDotNormalHalf, shininess ), 0.0 );\n\n float specularNormalization = ( shininess + 2.0 ) / 8.0;\n\n vec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVector, spotHalfVector ), 0.0 ), 5.0 );\n spotSpecular += schlick * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * lDistance * specularNormalization * spotEffect;\n\n }\n\n }\n\n#endif\n\n#if MAX_DIR_LIGHTS > 0\n\n vec3 dirDiffuse = vec3( 0.0 );\n vec3 dirSpecular = vec3( 0.0 );\n\n for( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\n\n vec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );\n vec3 dirVector = normalize( lDirection.xyz );\n\n // diffuse\n\n float dotProduct = dot( normal, dirVector );\n\n #ifdef WRAP_AROUND\n\n float dirDiffuseWeightFull = max( dotProduct, 0.0 );\n float dirDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );\n\n vec3 dirDiffuseWeight = mix( vec3( dirDiffuseWeightFull ), vec3( dirDiffuseWeightHalf ), wrapRGB );\n\n #else\n\n float dirDiffuseWeight = max( dotProduct, 0.0 );\n\n #endif\n\n dirDiffuse += diffuse * directionalLightColor[ i ] * dirDiffuseWeight;\n\n // specular\n\n vec3 dirHalfVector = normalize( dirVector + viewPosition );\n float dirDotNormalHalf = max( dot( normal, dirHalfVector ), 0.0 );\n float dirSpecularWeight = specularStrength * max( pow( dirDotNormalHalf, shininess ), 0.0 );\n\n /*\n // fresnel term from skin shader\n const float F0 = 0.128;\n\n float base = 1.0 - dot( viewPosition, dirHalfVector );\n float exponential = pow( base, 5.0 );\n\n float fresnel = exponential + F0 * ( 1.0 - exponential );\n */\n\n /*\n // fresnel term from fresnel shader\n const float mFresnelBias = 0.08;\n const float mFresnelScale = 0.3;\n const float mFresnelPower = 5.0;\n\n float fresnel = mFresnelBias + mFresnelScale * pow( 1.0 + dot( normalize( -viewPosition ), normal ), mFresnelPower );\n */\n\n float specularNormalization = ( shininess + 2.0 ) / 8.0;\n\n // dirSpecular += specular * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization * fresnel;\n\n vec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( dirVector, dirHalfVector ), 0.0 ), 5.0 );\n dirSpecular += schlick * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization;\n\n\n }\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n vec3 hemiDiffuse = vec3( 0.0 );\n vec3 hemiSpecular = vec3( 0.0 );\n\n for( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\n\n vec4 lDirection = viewMatrix * vec4( hemisphereLightDirection[ i ], 0.0 );\n vec3 lVector = normalize( lDirection.xyz );\n\n // diffuse\n\n float dotProduct = dot( normal, lVector );\n float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\n\n vec3 hemiColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n\n hemiDiffuse += diffuse * hemiColor;\n\n // specular (sky light)\n\n vec3 hemiHalfVectorSky = normalize( lVector + viewPosition );\n float hemiDotNormalHalfSky = 0.5 * dot( normal, hemiHalfVectorSky ) + 0.5;\n float hemiSpecularWeightSky = specularStrength * max( pow( max( hemiDotNormalHalfSky, 0.0 ), shininess ), 0.0 );\n\n // specular (ground light)\n\n vec3 lVectorGround = -lVector;\n\n vec3 hemiHalfVectorGround = normalize( lVectorGround + viewPosition );\n float hemiDotNormalHalfGround = 0.5 * dot( normal, hemiHalfVectorGround ) + 0.5;\n float hemiSpecularWeightGround = specularStrength * max( pow( max( hemiDotNormalHalfGround, 0.0 ), shininess ), 0.0 );\n\n float dotProductGround = dot( normal, lVectorGround );\n\n float specularNormalization = ( shininess + 2.0 ) / 8.0;\n\n vec3 schlickSky = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVector, hemiHalfVectorSky ), 0.0 ), 5.0 );\n vec3 schlickGround = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVectorGround, hemiHalfVectorGround ), 0.0 ), 5.0 );\n hemiSpecular += hemiColor * specularNormalization * ( schlickSky * hemiSpecularWeightSky * max( dotProduct, 0.0 ) + schlickGround * hemiSpecularWeightGround * max( dotProductGround, 0.0 ) );\n\n }\n\n#endif\n\nvec3 totalDiffuse = vec3( 0.0 );\nvec3 totalSpecular = vec3( 0.0 );\n\n#if MAX_DIR_LIGHTS > 0\n\n totalDiffuse += dirDiffuse;\n totalSpecular += dirSpecular;\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n totalDiffuse += hemiDiffuse;\n totalSpecular += hemiSpecular;\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n totalDiffuse += pointDiffuse;\n totalSpecular += pointSpecular;\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n totalDiffuse += spotDiffuse;\n totalSpecular += spotSpecular;\n\n#endif\n\n#ifdef METAL\n\n gl_FragColor.xyz = gl_FragColor.xyz * ( emissive + totalDiffuse + ambientLightColor * ambient + totalSpecular );\n\n#else\n\n gl_FragColor.xyz = gl_FragColor.xyz * ( emissive + totalDiffuse + ambientLightColor * ambient ) + totalSpecular;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/fog_pars_fragment.glsl + +THREE.ShaderChunk[ 'fog_pars_fragment'] = "#ifdef USE_FOG\n\n uniform vec3 fogColor;\n\n #ifdef FOG_EXP2\n\n uniform float fogDensity;\n\n #else\n\n uniform float fogNear;\n uniform float fogFar;\n #endif\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/morphnormal_vertex.glsl + +THREE.ShaderChunk[ 'morphnormal_vertex'] = "#ifdef USE_MORPHNORMALS\n\n vec3 morphedNormal = vec3( 0.0 );\n\n morphedNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];\n morphedNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];\n morphedNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];\n morphedNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];\n\n morphedNormal += normal;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/envmap_pars_fragment.glsl + +THREE.ShaderChunk[ 'envmap_pars_fragment'] = "#ifdef USE_ENVMAP\n\n uniform float reflectivity;\n #ifdef ENVMAP_TYPE_CUBE\n uniform samplerCube envMap;\n #else\n uniform sampler2D envMap;\n #endif\n uniform float flipEnvMap;\n\n #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\n uniform float refractionRatio;\n\n #else\n\n varying vec3 vReflect;\n\n #endif\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/logdepthbuf_fragment.glsl + +THREE.ShaderChunk[ 'logdepthbuf_fragment'] = "#if defined(USE_LOGDEPTHBUF) && defined(USE_LOGDEPTHBUF_EXT)\n\n gl_FragDepthEXT = log2(vFragDepth) * logDepthBufFC * 0.5;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/normalmap_pars_fragment.glsl + +THREE.ShaderChunk[ 'normalmap_pars_fragment'] = "#ifdef USE_NORMALMAP\n\n uniform sampler2D normalMap;\n uniform vec2 normalScale;\n\n // Per-Pixel Tangent Space Normal Mapping\n // http://hacksoflife.blogspot.ch/2009/11/per-pixel-tangent-space-normal-mapping.html\n\n vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {\n\n vec3 q0 = dFdx( eye_pos.xyz );\n vec3 q1 = dFdy( eye_pos.xyz );\n vec2 st0 = dFdx( vUv.st );\n vec2 st1 = dFdy( vUv.st );\n\n vec3 S = normalize( q0 * st1.t - q1 * st0.t );\n vec3 T = normalize( -q0 * st1.s + q1 * st0.s );\n vec3 N = normalize( surf_norm );\n\n vec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n mapN.xy = normalScale * mapN.xy;\n mat3 tsn = mat3( S, T, N );\n return normalize( tsn * mapN );\n\n }\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/lights_phong_pars_vertex.glsl + +THREE.ShaderChunk[ 'lights_phong_pars_vertex'] = "#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP ) || defined( USE_ENVMAP )\n\n varying vec3 vWorldPosition;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/lightmap_pars_fragment.glsl + +THREE.ShaderChunk[ 'lightmap_pars_fragment'] = "#ifdef USE_LIGHTMAP\n\n varying vec2 vUv2;\n uniform sampler2D lightMap;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/shadowmap_vertex.glsl + +THREE.ShaderChunk[ 'shadowmap_vertex'] = "#ifdef USE_SHADOWMAP\n\n for( int i = 0; i < MAX_SHADOWS; i ++ ) {\n\n vShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;\n\n }\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/lights_phong_vertex.glsl + +THREE.ShaderChunk[ 'lights_phong_vertex'] = "#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP ) || defined( USE_ENVMAP )\n\n vWorldPosition = worldPosition.xyz;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/map_fragment.glsl + +THREE.ShaderChunk[ 'map_fragment'] = "#ifdef USE_MAP\n\n vec4 texelColor = texture2D( map, vUv );\n\n #ifdef GAMMA_INPUT\n\n texelColor.xyz *= texelColor.xyz;\n\n #endif\n\n gl_FragColor = gl_FragColor * texelColor;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/lightmap_vertex.glsl + +THREE.ShaderChunk[ 'lightmap_vertex'] = "#ifdef USE_LIGHTMAP\n\n vUv2 = uv2;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/map_particle_fragment.glsl + +THREE.ShaderChunk[ 'map_particle_fragment'] = "#ifdef USE_MAP\n\n gl_FragColor = gl_FragColor * texture2D( map, vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y ) );\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/color_pars_fragment.glsl + +THREE.ShaderChunk[ 'color_pars_fragment'] = "#ifdef USE_COLOR\n\n varying vec3 vColor;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/color_vertex.glsl + +THREE.ShaderChunk[ 'color_vertex'] = "#ifdef USE_COLOR\n\n #ifdef GAMMA_INPUT\n\n vColor = color * color;\n\n #else\n\n vColor = color;\n\n #endif\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/skinning_vertex.glsl + +THREE.ShaderChunk[ 'skinning_vertex'] = "#ifdef USE_SKINNING\n\n #ifdef USE_MORPHTARGETS\n\n vec4 skinVertex = bindMatrix * vec4( morphed, 1.0 );\n\n #else\n\n vec4 skinVertex = bindMatrix * vec4( position, 1.0 );\n\n #endif\n\n vec4 skinned = vec4( 0.0 );\n skinned += boneMatX * skinVertex * skinWeight.x;\n skinned += boneMatY * skinVertex * skinWeight.y;\n skinned += boneMatZ * skinVertex * skinWeight.z;\n skinned += boneMatW * skinVertex * skinWeight.w;\n skinned = bindMatrixInverse * skinned;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/envmap_pars_vertex.glsl + +THREE.ShaderChunk[ 'envmap_pars_vertex'] = "#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG )\n\n varying vec3 vReflect;\n\n uniform float refractionRatio;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/linear_to_gamma_fragment.glsl + +THREE.ShaderChunk[ 'linear_to_gamma_fragment'] = "#ifdef GAMMA_OUTPUT\n\n gl_FragColor.xyz = sqrt( gl_FragColor.xyz );\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/color_pars_vertex.glsl + +THREE.ShaderChunk[ 'color_pars_vertex'] = "#ifdef USE_COLOR\n\n varying vec3 vColor;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/lights_lambert_pars_vertex.glsl + +THREE.ShaderChunk[ 'lights_lambert_pars_vertex'] = "uniform vec3 ambient;\nuniform vec3 diffuse;\nuniform vec3 emissive;\n\nuniform vec3 ambientLightColor;\n\n#if MAX_DIR_LIGHTS > 0\n\n uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\n uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\n uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n uniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\n uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\n uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n\n#endif\n\n#ifdef WRAP_AROUND\n\n uniform vec3 wrapRGB;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/map_pars_vertex.glsl + +THREE.ShaderChunk[ 'map_pars_vertex'] = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP )\n\n varying vec2 vUv;\n uniform vec4 offsetRepeat;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/envmap_fragment.glsl + +THREE.ShaderChunk[ 'envmap_fragment'] = "#ifdef USE_ENVMAP\n\n #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\n vec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\n\n // http://en.wikibooks.org/wiki/GLSL_Programming/Applying_Matrix_Transformations\n // Transforming Normal Vectors with the Inverse Transformation\n\n vec3 worldNormal = normalize( vec3( vec4( normal, 0.0 ) * viewMatrix ) );\n\n #ifdef ENVMAP_MODE_REFLECTION\n\n vec3 reflectVec = reflect( cameraToVertex, worldNormal );\n\n #else\n\n vec3 reflectVec = refract( cameraToVertex, worldNormal, refractionRatio );\n\n #endif\n\n #else\n\n vec3 reflectVec = vReflect;\n\n #endif\n\n #ifdef DOUBLE_SIDED\n float flipNormal = ( -1.0 + 2.0 * float( gl_FrontFacing ) );\n #else\n float flipNormal = 1.0;\n #endif\n\n #ifdef ENVMAP_TYPE_CUBE\n vec4 envColor = textureCube( envMap, flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\n #elif defined( ENVMAP_TYPE_EQUIREC )\n vec2 sampleUV;\n sampleUV.y = clamp( flipNormal * reflectVec.y * 0.5 + 0.5, 0.0, 1.0);\n sampleUV.x = atan( flipNormal * reflectVec.z, flipNormal * reflectVec.x ) * 0.15915494309189533576888376337251 + 0.5; // reciprocal( 2 PI ) + 0.5\n vec4 envColor = texture2D( envMap, sampleUV );\n \n #elif defined( ENVMAP_TYPE_SPHERE )\n vec3 reflectView = flipNormal * normalize((viewMatrix * vec4( reflectVec, 0.0 )).xyz + vec3(0.0,0.0,1.0));\n vec4 envColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5 );\n #endif\n\n #ifdef GAMMA_INPUT\n\n envColor.xyz *= envColor.xyz;\n\n #endif\n\n #ifdef ENVMAP_BLENDING_MULTIPLY\n\n gl_FragColor.xyz = mix( gl_FragColor.xyz, gl_FragColor.xyz * envColor.xyz, specularStrength * reflectivity );\n\n #elif defined( ENVMAP_BLENDING_MIX )\n\n gl_FragColor.xyz = mix( gl_FragColor.xyz, envColor.xyz, specularStrength * reflectivity );\n\n #elif defined( ENVMAP_BLENDING_ADD )\n\n gl_FragColor.xyz += envColor.xyz * specularStrength * reflectivity;\n\n #endif\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/specularmap_pars_fragment.glsl + +THREE.ShaderChunk[ 'specularmap_pars_fragment'] = "#ifdef USE_SPECULARMAP\n\n uniform sampler2D specularMap;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/logdepthbuf_vertex.glsl + +THREE.ShaderChunk[ 'logdepthbuf_vertex'] = "#ifdef USE_LOGDEPTHBUF\n\n gl_Position.z = log2(max(1e-6, gl_Position.w + 1.0)) * logDepthBufFC;\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n vFragDepth = 1.0 + gl_Position.w;\n\n#else\n\n gl_Position.z = (gl_Position.z - 1.0) * gl_Position.w;\n\n #endif\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/morphtarget_pars_vertex.glsl + +THREE.ShaderChunk[ 'morphtarget_pars_vertex'] = "#ifdef USE_MORPHTARGETS\n\n #ifndef USE_MORPHNORMALS\n\n uniform float morphTargetInfluences[ 8 ];\n\n #else\n\n uniform float morphTargetInfluences[ 4 ];\n\n #endif\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/specularmap_fragment.glsl + +THREE.ShaderChunk[ 'specularmap_fragment'] = "float specularStrength;\n\n#ifdef USE_SPECULARMAP\n\n vec4 texelSpecular = texture2D( specularMap, vUv );\n specularStrength = texelSpecular.r;\n\n#else\n\n specularStrength = 1.0;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/fog_fragment.glsl + +THREE.ShaderChunk[ 'fog_fragment'] = "#ifdef USE_FOG\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n float depth = gl_FragDepthEXT / gl_FragCoord.w;\n\n #else\n\n float depth = gl_FragCoord.z / gl_FragCoord.w;\n\n #endif\n\n #ifdef FOG_EXP2\n\n const float LOG2 = 1.442695;\n float fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );\n fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );\n\n #else\n\n float fogFactor = smoothstep( fogNear, fogFar, depth );\n\n #endif\n \n gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/bumpmap_pars_fragment.glsl + +THREE.ShaderChunk[ 'bumpmap_pars_fragment'] = "#ifdef USE_BUMPMAP\n\n uniform sampler2D bumpMap;\n uniform float bumpScale;\n\n // Derivative maps - bump mapping unparametrized surfaces by Morten Mikkelsen\n // http://mmikkelsen3d.blogspot.sk/2011/07/derivative-maps.html\n\n // Evaluate the derivative of the height w.r.t. screen-space using forward differencing (listing 2)\n\n vec2 dHdxy_fwd() {\n\n vec2 dSTdx = dFdx( vUv );\n vec2 dSTdy = dFdy( vUv );\n\n float Hll = bumpScale * texture2D( bumpMap, vUv ).x;\n float dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\n float dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\n\n return vec2( dBx, dBy );\n\n }\n\n vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {\n\n vec3 vSigmaX = dFdx( surf_pos );\n vec3 vSigmaY = dFdy( surf_pos );\n vec3 vN = surf_norm; // normalized\n\n vec3 R1 = cross( vSigmaY, vN );\n vec3 R2 = cross( vN, vSigmaX );\n\n float fDet = dot( vSigmaX, R1 );\n\n vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\n return normalize( abs( fDet ) * surf_norm - vGrad );\n\n }\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/defaultnormal_vertex.glsl + +THREE.ShaderChunk[ 'defaultnormal_vertex'] = "#ifdef USE_SKINNING\n\n vec3 objectNormal = skinnedNormal.xyz;\n\n#elif defined( USE_MORPHNORMALS )\n\n vec3 objectNormal = morphedNormal;\n\n#else\n\n vec3 objectNormal = normal;\n\n#endif\n\n#ifdef FLIP_SIDED\n\n objectNormal = -objectNormal;\n\n#endif\n\nvec3 transformedNormal = normalMatrix * objectNormal;\n"; + +// File:src/renderers/shaders/ShaderChunk/lights_phong_pars_fragment.glsl + +THREE.ShaderChunk[ 'lights_phong_pars_fragment'] = "uniform vec3 ambientLightColor;\n\n#if MAX_DIR_LIGHTS > 0\n\n uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\n uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\n\n uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n uniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\n uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\n uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n\n uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP ) || defined( USE_ENVMAP )\n\n varying vec3 vWorldPosition;\n\n#endif\n\n#ifdef WRAP_AROUND\n\n uniform vec3 wrapRGB;\n\n#endif\n\nvarying vec3 vViewPosition;\nvarying vec3 vNormal;"; + +// File:src/renderers/shaders/ShaderChunk/skinbase_vertex.glsl + +THREE.ShaderChunk[ 'skinbase_vertex'] = "#ifdef USE_SKINNING\n\n mat4 boneMatX = getBoneMatrix( skinIndex.x );\n mat4 boneMatY = getBoneMatrix( skinIndex.y );\n mat4 boneMatZ = getBoneMatrix( skinIndex.z );\n mat4 boneMatW = getBoneMatrix( skinIndex.w );\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/map_vertex.glsl + +THREE.ShaderChunk[ 'map_vertex'] = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP )\n\n vUv = uv * offsetRepeat.zw + offsetRepeat.xy;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/lightmap_fragment.glsl + +THREE.ShaderChunk[ 'lightmap_fragment'] = "#ifdef USE_LIGHTMAP\n\n gl_FragColor = gl_FragColor * texture2D( lightMap, vUv2 );\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/shadowmap_pars_vertex.glsl + +THREE.ShaderChunk[ 'shadowmap_pars_vertex'] = "#ifdef USE_SHADOWMAP\n\n varying vec4 vShadowCoord[ MAX_SHADOWS ];\n uniform mat4 shadowMatrix[ MAX_SHADOWS ];\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/color_fragment.glsl + +THREE.ShaderChunk[ 'color_fragment'] = "#ifdef USE_COLOR\n\n gl_FragColor = gl_FragColor * vec4( vColor, 1.0 );\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/morphtarget_vertex.glsl + +THREE.ShaderChunk[ 'morphtarget_vertex'] = "#ifdef USE_MORPHTARGETS\n\n vec3 morphed = vec3( 0.0 );\n morphed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];\n morphed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];\n morphed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];\n morphed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];\n\n #ifndef USE_MORPHNORMALS\n\n morphed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];\n morphed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];\n morphed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];\n morphed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];\n\n #endif\n\n morphed += position;\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/envmap_vertex.glsl + +THREE.ShaderChunk[ 'envmap_vertex'] = "#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG )\n\n vec3 worldNormal = mat3( modelMatrix[ 0 ].xyz, modelMatrix[ 1 ].xyz, modelMatrix[ 2 ].xyz ) * objectNormal;\n worldNormal = normalize( worldNormal );\n\n vec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\n #ifdef ENVMAP_MODE_REFLECTION\n\n vReflect = reflect( cameraToVertex, worldNormal );\n\n #else\n\n vReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n\n #endif\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/shadowmap_fragment.glsl + +THREE.ShaderChunk[ 'shadowmap_fragment'] = "#ifdef USE_SHADOWMAP\n\n #ifdef SHADOWMAP_DEBUG\n\n vec3 frustumColors[3];\n frustumColors[0] = vec3( 1.0, 0.5, 0.0 );\n frustumColors[1] = vec3( 0.0, 1.0, 0.8 );\n frustumColors[2] = vec3( 0.0, 0.5, 1.0 );\n\n #endif\n\n #ifdef SHADOWMAP_CASCADE\n\n int inFrustumCount = 0;\n\n #endif\n\n float fDepth;\n vec3 shadowColor = vec3( 1.0 );\n\n for( int i = 0; i < MAX_SHADOWS; i ++ ) {\n\n vec3 shadowCoord = vShadowCoord[ i ].xyz / vShadowCoord[ i ].w;\n\n // if ( something && something ) breaks ATI OpenGL shader compiler\n // if ( all( something, something ) ) using this instead\n\n bvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\n bool inFrustum = all( inFrustumVec );\n\n // don't shadow pixels outside of light frustum\n // use just first frustum (for cascades)\n // don't shadow pixels behind far plane of light frustum\n\n #ifdef SHADOWMAP_CASCADE\n\n inFrustumCount += int( inFrustum );\n bvec3 frustumTestVec = bvec3( inFrustum, inFrustumCount == 1, shadowCoord.z <= 1.0 );\n\n #else\n\n bvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\n\n #endif\n\n bool frustumTest = all( frustumTestVec );\n\n if ( frustumTest ) {\n\n shadowCoord.z += shadowBias[ i ];\n\n #if defined( SHADOWMAP_TYPE_PCF )\n\n // Percentage-close filtering\n // (9 pixel kernel)\n // http://fabiensanglard.net/shadowmappingPCF/\n\n float shadow = 0.0;\n\n /*\n // nested loops breaks shader compiler / validator on some ATI cards when using OpenGL\n // must enroll loop manually\n\n for ( float y = -1.25; y <= 1.25; y += 1.25 )\n for ( float x = -1.25; x <= 1.25; x += 1.25 ) {\n\n vec4 rgbaDepth = texture2D( shadowMap[ i ], vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy );\n\n // doesn't seem to produce any noticeable visual difference compared to simple texture2D lookup\n //vec4 rgbaDepth = texture2DProj( shadowMap[ i ], vec4( vShadowCoord[ i ].w * ( vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy ), 0.05, vShadowCoord[ i ].w ) );\n\n float fDepth = unpackDepth( rgbaDepth );\n\n if ( fDepth < shadowCoord.z )\n shadow += 1.0;\n\n }\n\n shadow /= 9.0;\n\n */\n\n const float shadowDelta = 1.0 / 9.0;\n\n float xPixelOffset = 1.0 / shadowMapSize[ i ].x;\n float yPixelOffset = 1.0 / shadowMapSize[ i ].y;\n\n float dx0 = -1.25 * xPixelOffset;\n float dy0 = -1.25 * yPixelOffset;\n float dx1 = 1.25 * xPixelOffset;\n float dy1 = 1.25 * yPixelOffset;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );\n if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n shadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );\n\n #elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n\n // Percentage-close filtering\n // (9 pixel kernel)\n // http://fabiensanglard.net/shadowmappingPCF/\n\n float shadow = 0.0;\n\n float xPixelOffset = 1.0 / shadowMapSize[ i ].x;\n float yPixelOffset = 1.0 / shadowMapSize[ i ].y;\n\n float dx0 = -1.0 * xPixelOffset;\n float dy0 = -1.0 * yPixelOffset;\n float dx1 = 1.0 * xPixelOffset;\n float dy1 = 1.0 * yPixelOffset;\n\n mat3 shadowKernel;\n mat3 depthKernel;\n\n depthKernel[0][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );\n depthKernel[0][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );\n depthKernel[0][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );\n depthKernel[1][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );\n depthKernel[1][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );\n depthKernel[1][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );\n depthKernel[2][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );\n depthKernel[2][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );\n depthKernel[2][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );\n\n vec3 shadowZ = vec3( shadowCoord.z );\n shadowKernel[0] = vec3(lessThan(depthKernel[0], shadowZ ));\n shadowKernel[0] *= vec3(0.25);\n\n shadowKernel[1] = vec3(lessThan(depthKernel[1], shadowZ ));\n shadowKernel[1] *= vec3(0.25);\n\n shadowKernel[2] = vec3(lessThan(depthKernel[2], shadowZ ));\n shadowKernel[2] *= vec3(0.25);\n\n vec2 fractionalCoord = 1.0 - fract( shadowCoord.xy * shadowMapSize[i].xy );\n\n shadowKernel[0] = mix( shadowKernel[1], shadowKernel[0], fractionalCoord.x );\n shadowKernel[1] = mix( shadowKernel[2], shadowKernel[1], fractionalCoord.x );\n\n vec4 shadowValues;\n shadowValues.x = mix( shadowKernel[0][1], shadowKernel[0][0], fractionalCoord.y );\n shadowValues.y = mix( shadowKernel[0][2], shadowKernel[0][1], fractionalCoord.y );\n shadowValues.z = mix( shadowKernel[1][1], shadowKernel[1][0], fractionalCoord.y );\n shadowValues.w = mix( shadowKernel[1][2], shadowKernel[1][1], fractionalCoord.y );\n\n shadow = dot( shadowValues, vec4( 1.0 ) );\n\n shadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );\n\n #else\n\n vec4 rgbaDepth = texture2D( shadowMap[ i ], shadowCoord.xy );\n float fDepth = unpackDepth( rgbaDepth );\n\n if ( fDepth < shadowCoord.z )\n\n // spot with multiple shadows is darker\n\n shadowColor = shadowColor * vec3( 1.0 - shadowDarkness[ i ] );\n\n // spot with multiple shadows has the same color as single shadow spot\n\n // shadowColor = min( shadowColor, vec3( shadowDarkness[ i ] ) );\n\n #endif\n\n }\n\n\n #ifdef SHADOWMAP_DEBUG\n\n #ifdef SHADOWMAP_CASCADE\n\n if ( inFrustum && inFrustumCount == 1 ) gl_FragColor.xyz *= frustumColors[ i ];\n\n #else\n\n if ( inFrustum ) gl_FragColor.xyz *= frustumColors[ i ];\n\n #endif\n\n #endif\n\n }\n\n #ifdef GAMMA_OUTPUT\n\n shadowColor *= shadowColor;\n\n #endif\n\n gl_FragColor.xyz = gl_FragColor.xyz * shadowColor;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/worldpos_vertex.glsl + +THREE.ShaderChunk[ 'worldpos_vertex'] = "#if defined( USE_ENVMAP ) || defined( PHONG ) || defined( LAMBERT ) || defined ( USE_SHADOWMAP )\n\n #ifdef USE_SKINNING\n\n vec4 worldPosition = modelMatrix * skinned;\n\n #elif defined( USE_MORPHTARGETS )\n\n vec4 worldPosition = modelMatrix * vec4( morphed, 1.0 );\n\n #else\n\n vec4 worldPosition = modelMatrix * vec4( position, 1.0 );\n\n #endif\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl + +THREE.ShaderChunk[ 'shadowmap_pars_fragment'] = "#ifdef USE_SHADOWMAP\n\n uniform sampler2D shadowMap[ MAX_SHADOWS ];\n uniform vec2 shadowMapSize[ MAX_SHADOWS ];\n\n uniform float shadowDarkness[ MAX_SHADOWS ];\n uniform float shadowBias[ MAX_SHADOWS ];\n\n varying vec4 vShadowCoord[ MAX_SHADOWS ];\n\n float unpackDepth( const in vec4 rgba_depth ) {\n\n const vec4 bit_shift = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );\n float depth = dot( rgba_depth, bit_shift );\n return depth;\n\n }\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/skinning_pars_vertex.glsl + +THREE.ShaderChunk[ 'skinning_pars_vertex'] = "#ifdef USE_SKINNING\n\n uniform mat4 bindMatrix;\n uniform mat4 bindMatrixInverse;\n\n #ifdef BONE_TEXTURE\n\n uniform sampler2D boneTexture;\n uniform int boneTextureWidth;\n uniform int boneTextureHeight;\n\n mat4 getBoneMatrix( const in float i ) {\n\n float j = i * 4.0;\n float x = mod( j, float( boneTextureWidth ) );\n float y = floor( j / float( boneTextureWidth ) );\n\n float dx = 1.0 / float( boneTextureWidth );\n float dy = 1.0 / float( boneTextureHeight );\n\n y = dy * ( y + 0.5 );\n\n vec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n vec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n vec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n vec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n\n mat4 bone = mat4( v1, v2, v3, v4 );\n\n return bone;\n\n }\n\n #else\n\n uniform mat4 boneGlobalMatrices[ MAX_BONES ];\n\n mat4 getBoneMatrix( const in float i ) {\n\n mat4 bone = boneGlobalMatrices[ int(i) ];\n return bone;\n\n }\n\n #endif\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/logdepthbuf_pars_fragment.glsl + +THREE.ShaderChunk[ 'logdepthbuf_pars_fragment'] = "#ifdef USE_LOGDEPTHBUF\n\n uniform float logDepthBufFC;\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n #extension GL_EXT_frag_depth : enable\n varying float vFragDepth;\n\n #endif\n\n#endif"; + +// File:src/renderers/shaders/ShaderChunk/alphamap_fragment.glsl + +THREE.ShaderChunk[ 'alphamap_fragment'] = "#ifdef USE_ALPHAMAP\n\n gl_FragColor.a *= texture2D( alphaMap, vUv ).g;\n\n#endif\n"; + +// File:src/renderers/shaders/ShaderChunk/alphamap_pars_fragment.glsl + +THREE.ShaderChunk[ 'alphamap_pars_fragment'] = "#ifdef USE_ALPHAMAP\n\n uniform sampler2D alphaMap;\n\n#endif\n"; + +// File:src/renderers/shaders/UniformsUtils.js + +/** + * Uniform Utilities + */ + +THREE.UniformsUtils = { + + merge: function ( uniforms ) { + + var merged = {}; + + for ( var u = 0; u < uniforms.length; u ++ ) { + + var tmp = this.clone( uniforms[ u ] ); + + for ( var p in tmp ) { + + merged[ p ] = tmp[ p ]; + + } + + } + + return merged; + + }, + + clone: function ( uniforms_src ) { + + var uniforms_dst = {}; + + for ( var u in uniforms_src ) { + + uniforms_dst[ u ] = {}; + + for ( var p in uniforms_src[ u ] ) { + + var parameter_src = uniforms_src[ u ][ p ]; + + if ( parameter_src instanceof THREE.Color || + parameter_src instanceof THREE.Vector2 || + parameter_src instanceof THREE.Vector3 || + parameter_src instanceof THREE.Vector4 || + parameter_src instanceof THREE.Matrix4 || + parameter_src instanceof THREE.Texture ) { + + uniforms_dst[ u ][ p ] = parameter_src.clone(); + + } else if ( parameter_src instanceof Array ) { + + uniforms_dst[ u ][ p ] = parameter_src.slice(); + + } else { + + uniforms_dst[ u ][ p ] = parameter_src; + + } + + } + + } + + return uniforms_dst; + + } + +}; + +// File:src/renderers/shaders/UniformsLib.js + +/** + * Uniforms library for shared webgl shaders + */ + +THREE.UniformsLib = { + + common: { + + "diffuse" : { type: "c", value: new THREE.Color( 0xeeeeee ) }, + "opacity" : { type: "f", value: 1.0 }, + + "map" : { type: "t", value: null }, + "offsetRepeat" : { type: "v4", value: new THREE.Vector4( 0, 0, 1, 1 ) }, + + "lightMap" : { type: "t", value: null }, + "specularMap" : { type: "t", value: null }, + "alphaMap" : { type: "t", value: null }, + + "envMap" : { type: "t", value: null }, + "flipEnvMap" : { type: "f", value: - 1 }, + "reflectivity" : { type: "f", value: 1.0 }, + "refractionRatio" : { type: "f", value: 0.98 }, + + "morphTargetInfluences" : { type: "f", value: 0 } + + }, + + bump: { + + "bumpMap" : { type: "t", value: null }, + "bumpScale" : { type: "f", value: 1 } + + }, + + normalmap: { + + "normalMap" : { type: "t", value: null }, + "normalScale" : { type: "v2", value: new THREE.Vector2( 1, 1 ) } + }, + + fog : { + + "fogDensity" : { type: "f", value: 0.00025 }, + "fogNear" : { type: "f", value: 1 }, + "fogFar" : { type: "f", value: 2000 }, + "fogColor" : { type: "c", value: new THREE.Color( 0xffffff ) } + + }, + + lights: { + + "ambientLightColor" : { type: "fv", value: [] }, + + "directionalLightDirection" : { type: "fv", value: [] }, + "directionalLightColor" : { type: "fv", value: [] }, + + "hemisphereLightDirection" : { type: "fv", value: [] }, + "hemisphereLightSkyColor" : { type: "fv", value: [] }, + "hemisphereLightGroundColor" : { type: "fv", value: [] }, + + "pointLightColor" : { type: "fv", value: [] }, + "pointLightPosition" : { type: "fv", value: [] }, + "pointLightDistance" : { type: "fv1", value: [] }, + + "spotLightColor" : { type: "fv", value: [] }, + "spotLightPosition" : { type: "fv", value: [] }, + "spotLightDirection" : { type: "fv", value: [] }, + "spotLightDistance" : { type: "fv1", value: [] }, + "spotLightAngleCos" : { type: "fv1", value: [] }, + "spotLightExponent" : { type: "fv1", value: [] } + + }, + + particle: { + + "psColor" : { type: "c", value: new THREE.Color( 0xeeeeee ) }, + "opacity" : { type: "f", value: 1.0 }, + "size" : { type: "f", value: 1.0 }, + "scale" : { type: "f", value: 1.0 }, + "map" : { type: "t", value: null }, + + "fogDensity" : { type: "f", value: 0.00025 }, + "fogNear" : { type: "f", value: 1 }, + "fogFar" : { type: "f", value: 2000 }, + "fogColor" : { type: "c", value: new THREE.Color( 0xffffff ) } + + }, + + shadowmap: { + + "shadowMap": { type: "tv", value: [] }, + "shadowMapSize": { type: "v2v", value: [] }, + + "shadowBias" : { type: "fv1", value: [] }, + "shadowDarkness": { type: "fv1", value: [] }, + + "shadowMatrix" : { type: "m4v", value: [] } + + } + +}; + +// File:src/renderers/shaders/ShaderLib.js + +/** + * Webgl Shader Library for three.js + * + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + * @author mikael emtinger / http://gomo.se/ + */ + + +THREE.ShaderLib = { + + 'basic': { + + uniforms: THREE.UniformsUtils.merge( [ + + THREE.UniformsLib[ "common" ], + THREE.UniformsLib[ "fog" ], + THREE.UniformsLib[ "shadowmap" ] + + ] ), + + vertexShader: [ + + THREE.ShaderChunk[ "map_pars_vertex" ], + THREE.ShaderChunk[ "lightmap_pars_vertex" ], + THREE.ShaderChunk[ "envmap_pars_vertex" ], + THREE.ShaderChunk[ "color_pars_vertex" ], + THREE.ShaderChunk[ "morphtarget_pars_vertex" ], + THREE.ShaderChunk[ "skinning_pars_vertex" ], + THREE.ShaderChunk[ "shadowmap_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + + "void main() {", + + THREE.ShaderChunk[ "map_vertex" ], + THREE.ShaderChunk[ "lightmap_vertex" ], + THREE.ShaderChunk[ "color_vertex" ], + THREE.ShaderChunk[ "skinbase_vertex" ], + + " #ifdef USE_ENVMAP", + + THREE.ShaderChunk[ "morphnormal_vertex" ], + THREE.ShaderChunk[ "skinnormal_vertex" ], + THREE.ShaderChunk[ "defaultnormal_vertex" ], + + " #endif", + + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "skinning_vertex" ], + THREE.ShaderChunk[ "default_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_vertex" ], + + THREE.ShaderChunk[ "worldpos_vertex" ], + THREE.ShaderChunk[ "envmap_vertex" ], + THREE.ShaderChunk[ "shadowmap_vertex" ], + + "}" + + ].join("\n"), + + fragmentShader: [ + + "uniform vec3 diffuse;", + "uniform float opacity;", + + THREE.ShaderChunk[ "color_pars_fragment" ], + THREE.ShaderChunk[ "map_pars_fragment" ], + THREE.ShaderChunk[ "alphamap_pars_fragment" ], + THREE.ShaderChunk[ "lightmap_pars_fragment" ], + THREE.ShaderChunk[ "envmap_pars_fragment" ], + THREE.ShaderChunk[ "fog_pars_fragment" ], + THREE.ShaderChunk[ "shadowmap_pars_fragment" ], + THREE.ShaderChunk[ "specularmap_pars_fragment" ], + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + + "void main() {", + + " gl_FragColor = vec4( diffuse, opacity );", + + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + THREE.ShaderChunk[ "map_fragment" ], + THREE.ShaderChunk[ "alphamap_fragment" ], + THREE.ShaderChunk[ "alphatest_fragment" ], + THREE.ShaderChunk[ "specularmap_fragment" ], + THREE.ShaderChunk[ "lightmap_fragment" ], + THREE.ShaderChunk[ "color_fragment" ], + THREE.ShaderChunk[ "envmap_fragment" ], + THREE.ShaderChunk[ "shadowmap_fragment" ], + + THREE.ShaderChunk[ "linear_to_gamma_fragment" ], + + THREE.ShaderChunk[ "fog_fragment" ], + + "}" + + ].join("\n") + + }, + + 'lambert': { + + uniforms: THREE.UniformsUtils.merge( [ + + THREE.UniformsLib[ "common" ], + THREE.UniformsLib[ "fog" ], + THREE.UniformsLib[ "lights" ], + THREE.UniformsLib[ "shadowmap" ], + + { + "ambient" : { type: "c", value: new THREE.Color( 0xffffff ) }, + "emissive" : { type: "c", value: new THREE.Color( 0x000000 ) }, + "wrapRGB" : { type: "v3", value: new THREE.Vector3( 1, 1, 1 ) } + } + + ] ), + + vertexShader: [ + + "#define LAMBERT", + + "varying vec3 vLightFront;", + + "#ifdef DOUBLE_SIDED", + + " varying vec3 vLightBack;", + + "#endif", + + THREE.ShaderChunk[ "map_pars_vertex" ], + THREE.ShaderChunk[ "lightmap_pars_vertex" ], + THREE.ShaderChunk[ "envmap_pars_vertex" ], + THREE.ShaderChunk[ "lights_lambert_pars_vertex" ], + THREE.ShaderChunk[ "color_pars_vertex" ], + THREE.ShaderChunk[ "morphtarget_pars_vertex" ], + THREE.ShaderChunk[ "skinning_pars_vertex" ], + THREE.ShaderChunk[ "shadowmap_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + + "void main() {", + + THREE.ShaderChunk[ "map_vertex" ], + THREE.ShaderChunk[ "lightmap_vertex" ], + THREE.ShaderChunk[ "color_vertex" ], + + THREE.ShaderChunk[ "morphnormal_vertex" ], + THREE.ShaderChunk[ "skinbase_vertex" ], + THREE.ShaderChunk[ "skinnormal_vertex" ], + THREE.ShaderChunk[ "defaultnormal_vertex" ], + + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "skinning_vertex" ], + THREE.ShaderChunk[ "default_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_vertex" ], + + THREE.ShaderChunk[ "worldpos_vertex" ], + THREE.ShaderChunk[ "envmap_vertex" ], + THREE.ShaderChunk[ "lights_lambert_vertex" ], + THREE.ShaderChunk[ "shadowmap_vertex" ], + + "}" + + ].join("\n"), + + fragmentShader: [ + + "uniform float opacity;", + + "varying vec3 vLightFront;", + + "#ifdef DOUBLE_SIDED", + + " varying vec3 vLightBack;", + + "#endif", + + THREE.ShaderChunk[ "color_pars_fragment" ], + THREE.ShaderChunk[ "map_pars_fragment" ], + THREE.ShaderChunk[ "alphamap_pars_fragment" ], + THREE.ShaderChunk[ "lightmap_pars_fragment" ], + THREE.ShaderChunk[ "envmap_pars_fragment" ], + THREE.ShaderChunk[ "fog_pars_fragment" ], + THREE.ShaderChunk[ "shadowmap_pars_fragment" ], + THREE.ShaderChunk[ "specularmap_pars_fragment" ], + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + + "void main() {", + + " gl_FragColor = vec4( vec3( 1.0 ), opacity );", + + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + THREE.ShaderChunk[ "map_fragment" ], + THREE.ShaderChunk[ "alphamap_fragment" ], + THREE.ShaderChunk[ "alphatest_fragment" ], + THREE.ShaderChunk[ "specularmap_fragment" ], + + " #ifdef DOUBLE_SIDED", + + //"float isFront = float( gl_FrontFacing );", + //"gl_FragColor.xyz *= isFront * vLightFront + ( 1.0 - isFront ) * vLightBack;", + + " if ( gl_FrontFacing )", + " gl_FragColor.xyz *= vLightFront;", + " else", + " gl_FragColor.xyz *= vLightBack;", + + " #else", + + " gl_FragColor.xyz *= vLightFront;", + + " #endif", + + THREE.ShaderChunk[ "lightmap_fragment" ], + THREE.ShaderChunk[ "color_fragment" ], + THREE.ShaderChunk[ "envmap_fragment" ], + THREE.ShaderChunk[ "shadowmap_fragment" ], + + THREE.ShaderChunk[ "linear_to_gamma_fragment" ], + + THREE.ShaderChunk[ "fog_fragment" ], + + "}" + + ].join("\n") + + }, + + 'phong': { + + uniforms: THREE.UniformsUtils.merge( [ + + THREE.UniformsLib[ "common" ], + THREE.UniformsLib[ "bump" ], + THREE.UniformsLib[ "normalmap" ], + THREE.UniformsLib[ "fog" ], + THREE.UniformsLib[ "lights" ], + THREE.UniformsLib[ "shadowmap" ], + + { + "ambient" : { type: "c", value: new THREE.Color( 0xffffff ) }, + "emissive" : { type: "c", value: new THREE.Color( 0x000000 ) }, + "specular" : { type: "c", value: new THREE.Color( 0x111111 ) }, + "shininess": { type: "f", value: 30 }, + "wrapRGB" : { type: "v3", value: new THREE.Vector3( 1, 1, 1 ) } + } + + ] ), + + vertexShader: [ + + "#define PHONG", + + "varying vec3 vViewPosition;", + "varying vec3 vNormal;", + + THREE.ShaderChunk[ "map_pars_vertex" ], + THREE.ShaderChunk[ "lightmap_pars_vertex" ], + THREE.ShaderChunk[ "envmap_pars_vertex" ], + THREE.ShaderChunk[ "lights_phong_pars_vertex" ], + THREE.ShaderChunk[ "color_pars_vertex" ], + THREE.ShaderChunk[ "morphtarget_pars_vertex" ], + THREE.ShaderChunk[ "skinning_pars_vertex" ], + THREE.ShaderChunk[ "shadowmap_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + + "void main() {", + + THREE.ShaderChunk[ "map_vertex" ], + THREE.ShaderChunk[ "lightmap_vertex" ], + THREE.ShaderChunk[ "color_vertex" ], + + THREE.ShaderChunk[ "morphnormal_vertex" ], + THREE.ShaderChunk[ "skinbase_vertex" ], + THREE.ShaderChunk[ "skinnormal_vertex" ], + THREE.ShaderChunk[ "defaultnormal_vertex" ], + + " vNormal = normalize( transformedNormal );", + + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "skinning_vertex" ], + THREE.ShaderChunk[ "default_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_vertex" ], + + " vViewPosition = -mvPosition.xyz;", + + THREE.ShaderChunk[ "worldpos_vertex" ], + THREE.ShaderChunk[ "envmap_vertex" ], + THREE.ShaderChunk[ "lights_phong_vertex" ], + THREE.ShaderChunk[ "shadowmap_vertex" ], + + "}" + + ].join("\n"), + + fragmentShader: [ + + "#define PHONG", + + "uniform vec3 diffuse;", + "uniform float opacity;", + + "uniform vec3 ambient;", + "uniform vec3 emissive;", + "uniform vec3 specular;", + "uniform float shininess;", + + THREE.ShaderChunk[ "color_pars_fragment" ], + THREE.ShaderChunk[ "map_pars_fragment" ], + THREE.ShaderChunk[ "alphamap_pars_fragment" ], + THREE.ShaderChunk[ "lightmap_pars_fragment" ], + THREE.ShaderChunk[ "envmap_pars_fragment" ], + THREE.ShaderChunk[ "fog_pars_fragment" ], + THREE.ShaderChunk[ "lights_phong_pars_fragment" ], + THREE.ShaderChunk[ "shadowmap_pars_fragment" ], + THREE.ShaderChunk[ "bumpmap_pars_fragment" ], + THREE.ShaderChunk[ "normalmap_pars_fragment" ], + THREE.ShaderChunk[ "specularmap_pars_fragment" ], + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + + "void main() {", + + " gl_FragColor = vec4( vec3( 1.0 ), opacity );", + + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + THREE.ShaderChunk[ "map_fragment" ], + THREE.ShaderChunk[ "alphamap_fragment" ], + THREE.ShaderChunk[ "alphatest_fragment" ], + THREE.ShaderChunk[ "specularmap_fragment" ], + + THREE.ShaderChunk[ "lights_phong_fragment" ], + + THREE.ShaderChunk[ "lightmap_fragment" ], + THREE.ShaderChunk[ "color_fragment" ], + THREE.ShaderChunk[ "envmap_fragment" ], + THREE.ShaderChunk[ "shadowmap_fragment" ], + + THREE.ShaderChunk[ "linear_to_gamma_fragment" ], + + THREE.ShaderChunk[ "fog_fragment" ], + + "}" + + ].join("\n") + + }, + + 'particle_basic': { + + uniforms: THREE.UniformsUtils.merge( [ + + THREE.UniformsLib[ "particle" ], + THREE.UniformsLib[ "shadowmap" ] + + ] ), + + vertexShader: [ + + "uniform float size;", + "uniform float scale;", + + THREE.ShaderChunk[ "color_pars_vertex" ], + THREE.ShaderChunk[ "shadowmap_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + + "void main() {", + + THREE.ShaderChunk[ "color_vertex" ], + + " vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", + + " #ifdef USE_SIZEATTENUATION", + " gl_PointSize = size * ( scale / length( mvPosition.xyz ) );", + " #else", + " gl_PointSize = size;", + " #endif", + + " gl_Position = projectionMatrix * mvPosition;", + + THREE.ShaderChunk[ "logdepthbuf_vertex" ], + THREE.ShaderChunk[ "worldpos_vertex" ], + THREE.ShaderChunk[ "shadowmap_vertex" ], + + "}" + + ].join("\n"), + + fragmentShader: [ + + "uniform vec3 psColor;", + "uniform float opacity;", + + THREE.ShaderChunk[ "color_pars_fragment" ], + THREE.ShaderChunk[ "map_particle_pars_fragment" ], + THREE.ShaderChunk[ "fog_pars_fragment" ], + THREE.ShaderChunk[ "shadowmap_pars_fragment" ], + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + + "void main() {", + + " gl_FragColor = vec4( psColor, opacity );", + + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + THREE.ShaderChunk[ "map_particle_fragment" ], + THREE.ShaderChunk[ "alphatest_fragment" ], + THREE.ShaderChunk[ "color_fragment" ], + THREE.ShaderChunk[ "shadowmap_fragment" ], + THREE.ShaderChunk[ "fog_fragment" ], + + "}" + + ].join("\n") + + }, + + 'dashed': { + + uniforms: THREE.UniformsUtils.merge( [ + + THREE.UniformsLib[ "common" ], + THREE.UniformsLib[ "fog" ], + + { + "scale" : { type: "f", value: 1 }, + "dashSize" : { type: "f", value: 1 }, + "totalSize": { type: "f", value: 2 } + } + + ] ), + + vertexShader: [ + + "uniform float scale;", + "attribute float lineDistance;", + + "varying float vLineDistance;", + + THREE.ShaderChunk[ "color_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + + "void main() {", + + THREE.ShaderChunk[ "color_vertex" ], + + " vLineDistance = scale * lineDistance;", + + " vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", + " gl_Position = projectionMatrix * mvPosition;", + + THREE.ShaderChunk[ "logdepthbuf_vertex" ], + + "}" + + ].join("\n"), + + fragmentShader: [ + + "uniform vec3 diffuse;", + "uniform float opacity;", + + "uniform float dashSize;", + "uniform float totalSize;", + + "varying float vLineDistance;", + + THREE.ShaderChunk[ "color_pars_fragment" ], + THREE.ShaderChunk[ "fog_pars_fragment" ], + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + + "void main() {", + + " if ( mod( vLineDistance, totalSize ) > dashSize ) {", + + " discard;", + + " }", + + " gl_FragColor = vec4( diffuse, opacity );", + + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + THREE.ShaderChunk[ "color_fragment" ], + THREE.ShaderChunk[ "fog_fragment" ], + + "}" + + ].join("\n") + + }, + + 'depth': { + + uniforms: { + + "mNear": { type: "f", value: 1.0 }, + "mFar" : { type: "f", value: 2000.0 }, + "opacity" : { type: "f", value: 1.0 } + + }, + + vertexShader: [ + + THREE.ShaderChunk[ "morphtarget_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + + "void main() {", + + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "default_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_vertex" ], + + "}" + + ].join("\n"), + + fragmentShader: [ + + "uniform float mNear;", + "uniform float mFar;", + "uniform float opacity;", + + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + + "void main() {", + + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + + " #ifdef USE_LOGDEPTHBUF_EXT", + + " float depth = gl_FragDepthEXT / gl_FragCoord.w;", + + " #else", + + " float depth = gl_FragCoord.z / gl_FragCoord.w;", + + " #endif", + + " float color = 1.0 - smoothstep( mNear, mFar, depth );", + " gl_FragColor = vec4( vec3( color ), opacity );", + + "}" + + ].join("\n") + + }, + + 'normal': { + + uniforms: { + + "opacity" : { type: "f", value: 1.0 } + + }, + + vertexShader: [ + + "varying vec3 vNormal;", + + THREE.ShaderChunk[ "morphtarget_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + + "void main() {", + + " vNormal = normalize( normalMatrix * normal );", + + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "default_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_vertex" ], + + "}" + + ].join("\n"), + + fragmentShader: [ + + "uniform float opacity;", + "varying vec3 vNormal;", + + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + + "void main() {", + + " gl_FragColor = vec4( 0.5 * normalize( vNormal ) + 0.5, opacity );", + + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + + "}" + + ].join("\n") + + }, + + /* ------------------------------------------------------------------------- + // Cube map shader + ------------------------------------------------------------------------- */ + + 'cube': { + + uniforms: { "tCube": { type: "t", value: null }, + "tFlip": { type: "f", value: - 1 } }, + + vertexShader: [ + + "varying vec3 vWorldPosition;", + + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + + "void main() {", + + " vec4 worldPosition = modelMatrix * vec4( position, 1.0 );", + " vWorldPosition = worldPosition.xyz;", + + " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + THREE.ShaderChunk[ "logdepthbuf_vertex" ], + + "}" + + ].join("\n"), + + fragmentShader: [ + + "uniform samplerCube tCube;", + "uniform float tFlip;", + + "varying vec3 vWorldPosition;", + + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + + "void main() {", + + " gl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );", + + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + + "}" + + ].join("\n") + + }, + + /* ------------------------------------------------------------------------- + // Cube map shader + ------------------------------------------------------------------------- */ + + 'equirect': { + + uniforms: { "tEquirect": { type: "t", value: null }, + "tFlip": { type: "f", value: - 1 } }, + + vertexShader: [ + + "varying vec3 vWorldPosition;", + + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + + "void main() {", + + " vec4 worldPosition = modelMatrix * vec4( position, 1.0 );", + " vWorldPosition = worldPosition.xyz;", + + " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + THREE.ShaderChunk[ "logdepthbuf_vertex" ], + + "}" + + ].join("\n"), + + fragmentShader: [ + + "uniform sampler2D tEquirect;", + "uniform float tFlip;", + + "varying vec3 vWorldPosition;", + + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + + "void main() {", + + // " gl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );", + "vec3 direction = normalize( vWorldPosition );", + "vec2 sampleUV;", + "sampleUV.y = clamp( tFlip * direction.y * -0.5 + 0.5, 0.0, 1.0);", + "sampleUV.x = atan( direction.z, direction.x ) * 0.15915494309189533576888376337251 + 0.5;", // reciprocal( 2 PI ) + 0.5 + "gl_FragColor = texture2D( tEquirect, sampleUV );", + + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + + "}" + + ].join("\n") + + }, + + /* Depth encoding into RGBA texture + * + * based on SpiderGL shadow map example + * http://spidergl.org/example.php?id=6 + * + * originally from + * http://www.gamedev.net/topic/442138-packing-a-float-into-a-a8r8g8b8-texture-shader/page__whichpage__1%25EF%25BF%25BD + * + * see also + * http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/ + */ + + 'depthRGBA': { + + uniforms: {}, + + vertexShader: [ + + THREE.ShaderChunk[ "morphtarget_pars_vertex" ], + THREE.ShaderChunk[ "skinning_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + + "void main() {", + + THREE.ShaderChunk[ "skinbase_vertex" ], + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "skinning_vertex" ], + THREE.ShaderChunk[ "default_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_vertex" ], + + "}" + + ].join("\n"), + + fragmentShader: [ + + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + + "vec4 pack_depth( const in float depth ) {", + + " const vec4 bit_shift = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 );", + " const vec4 bit_mask = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 );", + " vec4 res = mod( depth * bit_shift * vec4( 255 ), vec4( 256 ) ) / vec4( 255 );", // " vec4 res = fract( depth * bit_shift );", + " res -= res.xxyz * bit_mask;", + " return res;", + + "}", + + "void main() {", + + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + + " #ifdef USE_LOGDEPTHBUF_EXT", + + " gl_FragData[ 0 ] = pack_depth( gl_FragDepthEXT );", + + " #else", + + " gl_FragData[ 0 ] = pack_depth( gl_FragCoord.z );", + + " #endif", + + //"gl_FragData[ 0 ] = pack_depth( gl_FragCoord.z / gl_FragCoord.w );", + //"float z = ( ( gl_FragCoord.z / gl_FragCoord.w ) - 3.0 ) / ( 4000.0 - 3.0 );", + //"gl_FragData[ 0 ] = pack_depth( z );", + //"gl_FragData[ 0 ] = vec4( z, z, z, 1.0 );", + + "}" + + ].join("\n") + + } + +}; + +// File:src/renderers/WebGLRenderer.js + +/** + * @author supereggbert / http://www.paulbrunt.co.uk/ + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * @author szimek / https://github.com/szimek/ + */ + +THREE.WebGLRenderer = function ( parameters ) { + + console.log( 'THREE.WebGLRenderer', THREE.REVISION ); + + parameters = parameters || {}; + + var _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElement( 'canvas' ), + _context = parameters.context !== undefined ? parameters.context : null, + + pixelRatio = 1, + + _precision = parameters.precision !== undefined ? parameters.precision : 'highp', + + _alpha = parameters.alpha !== undefined ? parameters.alpha : false, + _depth = parameters.depth !== undefined ? parameters.depth : true, + _stencil = parameters.stencil !== undefined ? parameters.stencil : true, + _antialias = parameters.antialias !== undefined ? parameters.antialias : false, + _premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true, + _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false, + _logarithmicDepthBuffer = parameters.logarithmicDepthBuffer !== undefined ? parameters.logarithmicDepthBuffer : false, + + _clearColor = new THREE.Color( 0x000000 ), + _clearAlpha = 0; + + var lights = []; + + var _webglObjects = {}; + var _webglObjectsImmediate = []; + + var opaqueObjects = []; + var transparentObjects = []; + + var sprites = []; + var lensFlares = []; + + // public properties + + this.domElement = _canvas; + this.context = null; + + // clearing + + this.autoClear = true; + this.autoClearColor = true; + this.autoClearDepth = true; + this.autoClearStencil = true; + + // scene graph + + this.sortObjects = true; + + // physically based shading + + this.gammaInput = false; + this.gammaOutput = false; + + // shadow map + + this.shadowMapEnabled = false; + this.shadowMapType = THREE.PCFShadowMap; + this.shadowMapCullFace = THREE.CullFaceFront; + this.shadowMapDebug = false; + this.shadowMapCascade = false; + + // morphs + + this.maxMorphTargets = 8; + this.maxMorphNormals = 4; + + // flags + + this.autoScaleCubemaps = true; + + // info + + this.info = { + + memory: { + + programs: 0, + geometries: 0, + textures: 0 + + }, + + render: { + + calls: 0, + vertices: 0, + faces: 0, + points: 0 + + } + + }; + + // internal properties + + var _this = this, + + _programs = [], + + // internal state cache + + _currentProgram = null, + _currentFramebuffer = null, + _currentMaterialId = - 1, + _currentGeometryProgram = '', + _currentCamera = null, + + _usedTextureUnits = 0, + + // GL state cache + + _oldDoubleSided = - 1, + _oldFlipSided = - 1, + + _oldBlending = - 1, + + _oldBlendEquation = - 1, + _oldBlendSrc = - 1, + _oldBlendDst = - 1, + + _oldDepthTest = - 1, + _oldDepthWrite = - 1, + + _oldPolygonOffset = null, + _oldPolygonOffsetFactor = null, + _oldPolygonOffsetUnits = null, + + _oldLineWidth = null, + + _viewportX = 0, + _viewportY = 0, + _viewportWidth = _canvas.width, + _viewportHeight = _canvas.height, + _currentWidth = 0, + _currentHeight = 0, + + _newAttributes = new Uint8Array( 16 ), + _enabledAttributes = new Uint8Array( 16 ), + + // frustum + + _frustum = new THREE.Frustum(), + + // camera matrices cache + + _projScreenMatrix = new THREE.Matrix4(), + _projScreenMatrixPS = new THREE.Matrix4(), + + _vector3 = new THREE.Vector3(), + + // light arrays cache + + _direction = new THREE.Vector3(), + + _lightsNeedUpdate = true, + + _lights = { + + ambient: [ 0, 0, 0 ], + directional: { length: 0, colors:[], positions: [] }, + point: { length: 0, colors: [], positions: [], distances: [] }, + spot: { length: 0, colors: [], positions: [], distances: [], directions: [], anglesCos: [], exponents: [] }, + hemi: { length: 0, skyColors: [], groundColors: [], positions: [] } + + }; + + // initialize + + var _gl; + + try { + + var attributes = { + alpha: _alpha, + depth: _depth, + stencil: _stencil, + antialias: _antialias, + premultipliedAlpha: _premultipliedAlpha, + preserveDrawingBuffer: _preserveDrawingBuffer + }; + + _gl = _context || _canvas.getContext( 'webgl', attributes ) || _canvas.getContext( 'experimental-webgl', attributes ); + + if ( _gl === null ) { + + if ( _canvas.getContext( 'webgl') !== null ) { + + throw 'Error creating WebGL context with your selected attributes.'; + + } else { + + throw 'Error creating WebGL context.'; + + } + + } + + _canvas.addEventListener( 'webglcontextlost', function ( event ) { + + event.preventDefault(); + + resetGLState(); + setDefaultGLState(); + + _webglObjects = {}; + + }, false); + + } catch ( error ) { + + console.error( error ); + + } + + if ( _gl.getShaderPrecisionFormat === undefined ) { + + _gl.getShaderPrecisionFormat = function () { + + return { + 'rangeMin': 1, + 'rangeMax': 1, + 'precision': 1 + }; + + } + + } + + var extensions = new THREE.WebGLExtensions( _gl ); + + extensions.get( 'OES_texture_float' ); + extensions.get( 'OES_texture_float_linear' ); + extensions.get( 'OES_standard_derivatives' ); + + if ( _logarithmicDepthBuffer ) { + + extensions.get( 'EXT_frag_depth' ); + + } + + // + + var setDefaultGLState = function () { + + _gl.clearColor( 0, 0, 0, 1 ); + _gl.clearDepth( 1 ); + _gl.clearStencil( 0 ); + + _gl.enable( _gl.DEPTH_TEST ); + _gl.depthFunc( _gl.LEQUAL ); + + _gl.frontFace( _gl.CCW ); + _gl.cullFace( _gl.BACK ); + _gl.enable( _gl.CULL_FACE ); + + _gl.enable( _gl.BLEND ); + _gl.blendEquation( _gl.FUNC_ADD ); + _gl.blendFunc( _gl.SRC_ALPHA, _gl.ONE_MINUS_SRC_ALPHA ); + + _gl.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight ); + + _gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); + + }; + + var resetGLState = function () { + + _currentProgram = null; + _currentCamera = null; + + _oldBlending = - 1; + _oldDepthTest = - 1; + _oldDepthWrite = - 1; + _oldDoubleSided = - 1; + _oldFlipSided = - 1; + _currentGeometryProgram = ''; + _currentMaterialId = - 1; + + _lightsNeedUpdate = true; + + for ( var i = 0; i < _enabledAttributes.length; i ++ ) { + + _enabledAttributes[ i ] = 0; + + } + + }; + + setDefaultGLState(); + + this.context = _gl; + + // GPU capabilities + + var _maxTextures = _gl.getParameter( _gl.MAX_TEXTURE_IMAGE_UNITS ); + var _maxVertexTextures = _gl.getParameter( _gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ); + var _maxTextureSize = _gl.getParameter( _gl.MAX_TEXTURE_SIZE ); + var _maxCubemapSize = _gl.getParameter( _gl.MAX_CUBE_MAP_TEXTURE_SIZE ); + + var _supportsVertexTextures = _maxVertexTextures > 0; + var _supportsBoneTextures = _supportsVertexTextures && extensions.get( 'OES_texture_float' ); + + // + + var _vertexShaderPrecisionHighpFloat = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.HIGH_FLOAT ); + var _vertexShaderPrecisionMediumpFloat = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.MEDIUM_FLOAT ); + var _vertexShaderPrecisionLowpFloat = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.LOW_FLOAT ); + + var _fragmentShaderPrecisionHighpFloat = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.HIGH_FLOAT ); + var _fragmentShaderPrecisionMediumpFloat = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.MEDIUM_FLOAT ); + var _fragmentShaderPrecisionLowpFloat = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.LOW_FLOAT ); + + var getCompressedTextureFormats = ( function () { + + var array; + + return function () { + + if ( array !== undefined ) { + + return array; + + } + + array = []; + + if ( extensions.get( 'WEBGL_compressed_texture_pvrtc' ) || extensions.get( 'WEBGL_compressed_texture_s3tc' ) ) { + + var formats = _gl.getParameter( _gl.COMPRESSED_TEXTURE_FORMATS ); + + for ( var i = 0; i < formats.length; i ++ ){ + + array.push( formats[ i ] ); + + } + + } + + return array; + + }; + + } )(); + + // clamp precision to maximum available + + var highpAvailable = _vertexShaderPrecisionHighpFloat.precision > 0 && _fragmentShaderPrecisionHighpFloat.precision > 0; + var mediumpAvailable = _vertexShaderPrecisionMediumpFloat.precision > 0 && _fragmentShaderPrecisionMediumpFloat.precision > 0; + + if ( _precision === 'highp' && ! highpAvailable ) { + + if ( mediumpAvailable ) { + + _precision = 'mediump'; + console.warn( 'THREE.WebGLRenderer: highp not supported, using mediump.' ); + + } else { + + _precision = 'lowp'; + console.warn( 'THREE.WebGLRenderer: highp and mediump not supported, using lowp.' ); + + } + + } + + if ( _precision === 'mediump' && ! mediumpAvailable ) { + + _precision = 'lowp'; + console.warn( 'THREE.WebGLRenderer: mediump not supported, using lowp.' ); + + } + + // Plugins + + var shadowMapPlugin = new THREE.ShadowMapPlugin( this, lights, _webglObjects, _webglObjectsImmediate ); + + var spritePlugin = new THREE.SpritePlugin( this, sprites ); + var lensFlarePlugin = new THREE.LensFlarePlugin( this, lensFlares ); + + // API + + this.getContext = function () { + + return _gl; + + }; + + this.forceContextLoss = function () { + + extensions.get( 'WEBGL_lose_context' ).loseContext(); + + }; + + this.supportsVertexTextures = function () { + + return _supportsVertexTextures; + + }; + + this.supportsFloatTextures = function () { + + return extensions.get( 'OES_texture_float' ); + + }; + + this.supportsStandardDerivatives = function () { + + return extensions.get( 'OES_standard_derivatives' ); + + }; + + this.supportsCompressedTextureS3TC = function () { + + return extensions.get( 'WEBGL_compressed_texture_s3tc' ); + + }; + + this.supportsCompressedTexturePVRTC = function () { + + return extensions.get( 'WEBGL_compressed_texture_pvrtc' ); + + }; + + this.supportsBlendMinMax = function () { + + return extensions.get( 'EXT_blend_minmax' ); + + }; + + this.getMaxAnisotropy = ( function () { + + var value; + + return function () { + + if ( value !== undefined ) { + + return value; + + } + + var extension = extensions.get( 'EXT_texture_filter_anisotropic' ); + + value = extension !== null ? _gl.getParameter( extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT ) : 0; + + return value; + + } + + } )(); + + this.getPrecision = function () { + + return _precision; + + }; + + this.getPixelRatio = function () { + + return pixelRatio; + + }; + + this.setPixelRatio = function ( value ) { + + pixelRatio = value; + + }; + + this.setSize = function ( width, height, updateStyle ) { + + _canvas.width = width * pixelRatio; + _canvas.height = height * pixelRatio; + + if ( updateStyle !== false ) { + + _canvas.style.width = width + 'px'; + _canvas.style.height = height + 'px'; + + } + + this.setViewport( 0, 0, width, height ); + + }; + + this.setViewport = function ( x, y, width, height ) { + + _viewportX = x * pixelRatio; + _viewportY = y * pixelRatio; + + _viewportWidth = width * pixelRatio; + _viewportHeight = height * pixelRatio; + + _gl.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight ); + + }; + + this.setScissor = function ( x, y, width, height ) { + + _gl.scissor( + x * pixelRatio, + y * pixelRatio, + width * pixelRatio, + height * pixelRatio + ); + + }; + + this.enableScissorTest = function ( enable ) { + + enable ? _gl.enable( _gl.SCISSOR_TEST ) : _gl.disable( _gl.SCISSOR_TEST ); + + }; + + // Clearing + + this.getClearColor = function () { + + return _clearColor; + + }; + + this.setClearColor = function ( color, alpha ) { + + _clearColor.set( color ); + _clearAlpha = alpha !== undefined ? alpha : 1; + + _gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); + + }; + + this.getClearAlpha = function () { + + return _clearAlpha; + + }; + + this.setClearAlpha = function ( alpha ) { + + _clearAlpha = alpha; + + _gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); + + }; + + this.clear = function ( color, depth, stencil ) { + + var bits = 0; + + if ( color === undefined || color ) bits |= _gl.COLOR_BUFFER_BIT; + if ( depth === undefined || depth ) bits |= _gl.DEPTH_BUFFER_BIT; + if ( stencil === undefined || stencil ) bits |= _gl.STENCIL_BUFFER_BIT; + + _gl.clear( bits ); + + }; + + this.clearColor = function () { + + _gl.clear( _gl.COLOR_BUFFER_BIT ); + + }; + + this.clearDepth = function () { + + _gl.clear( _gl.DEPTH_BUFFER_BIT ); + + }; + + this.clearStencil = function () { + + _gl.clear( _gl.STENCIL_BUFFER_BIT ); + + }; + + this.clearTarget = function ( renderTarget, color, depth, stencil ) { + + this.setRenderTarget( renderTarget ); + this.clear( color, depth, stencil ); + + }; + + // Reset + + this.resetGLState = resetGLState; + + // Buffer allocation + + function createParticleBuffers ( geometry ) { + + geometry.__webglVertexBuffer = _gl.createBuffer(); + geometry.__webglColorBuffer = _gl.createBuffer(); + + _this.info.memory.geometries ++; + + }; + + function createLineBuffers ( geometry ) { + + geometry.__webglVertexBuffer = _gl.createBuffer(); + geometry.__webglColorBuffer = _gl.createBuffer(); + geometry.__webglLineDistanceBuffer = _gl.createBuffer(); + + _this.info.memory.geometries ++; + + }; + + function createMeshBuffers ( geometryGroup ) { + + geometryGroup.__webglVertexBuffer = _gl.createBuffer(); + geometryGroup.__webglNormalBuffer = _gl.createBuffer(); + geometryGroup.__webglTangentBuffer = _gl.createBuffer(); + geometryGroup.__webglColorBuffer = _gl.createBuffer(); + geometryGroup.__webglUVBuffer = _gl.createBuffer(); + geometryGroup.__webglUV2Buffer = _gl.createBuffer(); + + geometryGroup.__webglSkinIndicesBuffer = _gl.createBuffer(); + geometryGroup.__webglSkinWeightsBuffer = _gl.createBuffer(); + + geometryGroup.__webglFaceBuffer = _gl.createBuffer(); + geometryGroup.__webglLineBuffer = _gl.createBuffer(); + + var numMorphTargets = geometryGroup.numMorphTargets; + + if ( numMorphTargets ) { + + geometryGroup.__webglMorphTargetsBuffers = []; + + for ( var m = 0, ml = numMorphTargets; m < ml; m ++ ) { + + geometryGroup.__webglMorphTargetsBuffers.push( _gl.createBuffer() ); + + } + + } + + var numMorphNormals = geometryGroup.numMorphNormals; + + if ( numMorphNormals ) { + + geometryGroup.__webglMorphNormalsBuffers = []; + + for ( var m = 0, ml = numMorphNormals; m < ml; m ++ ) { + + geometryGroup.__webglMorphNormalsBuffers.push( _gl.createBuffer() ); + + } + + } + + _this.info.memory.geometries ++; + + }; + + // Events + + var onObjectRemoved = function ( event ) { + + var object = event.target; + + object.traverse( function ( child ) { + + child.removeEventListener( 'remove', onObjectRemoved ); + + removeObject( child ); + + } ); + + }; + + var onGeometryDispose = function ( event ) { + + var geometry = event.target; + + geometry.removeEventListener( 'dispose', onGeometryDispose ); + + deallocateGeometry( geometry ); + + }; + + var onTextureDispose = function ( event ) { + + var texture = event.target; + + texture.removeEventListener( 'dispose', onTextureDispose ); + + deallocateTexture( texture ); + + _this.info.memory.textures --; + + + }; + + var onRenderTargetDispose = function ( event ) { + + var renderTarget = event.target; + + renderTarget.removeEventListener( 'dispose', onRenderTargetDispose ); + + deallocateRenderTarget( renderTarget ); + + _this.info.memory.textures --; + + }; + + var onMaterialDispose = function ( event ) { + + var material = event.target; + + material.removeEventListener( 'dispose', onMaterialDispose ); + + deallocateMaterial( material ); + + }; + + // Buffer deallocation + + var deleteBuffers = function ( geometry ) { + + var buffers = [ + '__webglVertexBuffer', + '__webglNormalBuffer', + '__webglTangentBuffer', + '__webglColorBuffer', + '__webglUVBuffer', + '__webglUV2Buffer', + + '__webglSkinIndicesBuffer', + '__webglSkinWeightsBuffer', + + '__webglFaceBuffer', + '__webglLineBuffer', + + '__webglLineDistanceBuffer' + ]; + + for ( var i = 0, l = buffers.length; i < l; i ++ ) { + + var name = buffers[ i ]; + + if ( geometry[ name ] !== undefined ) { + + _gl.deleteBuffer( geometry[ name ] ); + + delete geometry[ name ]; + + } + + } + + // custom attributes + + if ( geometry.__webglCustomAttributesList !== undefined ) { + + for ( var name in geometry.__webglCustomAttributesList ) { + + _gl.deleteBuffer( geometry.__webglCustomAttributesList[ name ].buffer ); + + } + + delete geometry.__webglCustomAttributesList; + + } + + _this.info.memory.geometries --; + + }; + + var deallocateGeometry = function ( geometry ) { + + delete geometry.__webglInit; + + if ( geometry instanceof THREE.BufferGeometry ) { + + for ( var name in geometry.attributes ) { + + var attribute = geometry.attributes[ name ]; + + if ( attribute.buffer !== undefined ) { + + _gl.deleteBuffer( attribute.buffer ); + + delete attribute.buffer; + + } + + } + + _this.info.memory.geometries --; + + } else { + + var geometryGroupsList = geometryGroups[ geometry.id ]; + + if ( geometryGroupsList !== undefined ) { + + for ( var i = 0, l = geometryGroupsList.length; i < l; i ++ ) { + + var geometryGroup = geometryGroupsList[ i ]; + + if ( geometryGroup.numMorphTargets !== undefined ) { + + for ( var m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) { + + _gl.deleteBuffer( geometryGroup.__webglMorphTargetsBuffers[ m ] ); + + } + + delete geometryGroup.__webglMorphTargetsBuffers; + + } + + if ( geometryGroup.numMorphNormals !== undefined ) { + + for ( var m = 0, ml = geometryGroup.numMorphNormals; m < ml; m ++ ) { + + _gl.deleteBuffer( geometryGroup.__webglMorphNormalsBuffers[ m ] ); + + } + + delete geometryGroup.__webglMorphNormalsBuffers; + + } + + deleteBuffers( geometryGroup ); + + } + + delete geometryGroups[ geometry.id ]; + + } else { + + deleteBuffers( geometry ); + + } + + } + + // TOFIX: Workaround for deleted geometry being currently bound + + _currentGeometryProgram = ''; + + }; + + var deallocateTexture = function ( texture ) { + + if ( texture.image && texture.image.__webglTextureCube ) { + + // cube texture + + _gl.deleteTexture( texture.image.__webglTextureCube ); + + delete texture.image.__webglTextureCube; + + } else { + + // 2D texture + + if ( texture.__webglInit === undefined ) return; + + _gl.deleteTexture( texture.__webglTexture ); + + delete texture.__webglTexture; + delete texture.__webglInit; + + } + + }; + + var deallocateRenderTarget = function ( renderTarget ) { + + if ( ! renderTarget || renderTarget.__webglTexture === undefined ) return; + + _gl.deleteTexture( renderTarget.__webglTexture ); + + delete renderTarget.__webglTexture; + + if ( renderTarget instanceof THREE.WebGLRenderTargetCube ) { + + for ( var i = 0; i < 6; i ++ ) { + + _gl.deleteFramebuffer( renderTarget.__webglFramebuffer[ i ] ); + _gl.deleteRenderbuffer( renderTarget.__webglRenderbuffer[ i ] ); + + } + + } else { + + _gl.deleteFramebuffer( renderTarget.__webglFramebuffer ); + _gl.deleteRenderbuffer( renderTarget.__webglRenderbuffer ); + + } + + delete renderTarget.__webglFramebuffer; + delete renderTarget.__webglRenderbuffer; + + }; + + var deallocateMaterial = function ( material ) { + + var program = material.program.program; + + if ( program === undefined ) return; + + material.program = undefined; + + // only deallocate GL program if this was the last use of shared program + // assumed there is only single copy of any program in the _programs list + // (that's how it's constructed) + + var i, il, programInfo; + var deleteProgram = false; + + for ( i = 0, il = _programs.length; i < il; i ++ ) { + + programInfo = _programs[ i ]; + + if ( programInfo.program === program ) { + + programInfo.usedTimes --; + + if ( programInfo.usedTimes === 0 ) { + + deleteProgram = true; + + } + + break; + + } + + } + + if ( deleteProgram === true ) { + + // avoid using array.splice, this is costlier than creating new array from scratch + + var newPrograms = []; + + for ( i = 0, il = _programs.length; i < il; i ++ ) { + + programInfo = _programs[ i ]; + + if ( programInfo.program !== program ) { + + newPrograms.push( programInfo ); + + } + + } + + _programs = newPrograms; + + _gl.deleteProgram( program ); + + _this.info.memory.programs --; + + } + + }; + + // Buffer initialization + + function initCustomAttributes ( object ) { + + var geometry = object.geometry; + var material = object.material; + + var nvertices = geometry.vertices.length; + + if ( material.attributes ) { + + if ( geometry.__webglCustomAttributesList === undefined ) { + + geometry.__webglCustomAttributesList = []; + + } + + for ( var name in material.attributes ) { + + var attribute = material.attributes[ name ]; + + if ( ! attribute.__webglInitialized || attribute.createUniqueBuffers ) { + + attribute.__webglInitialized = true; + + var size = 1; // "f" and "i" + + if ( attribute.type === 'v2' ) size = 2; + else if ( attribute.type === 'v3' ) size = 3; + else if ( attribute.type === 'v4' ) size = 4; + else if ( attribute.type === 'c' ) size = 3; + + attribute.size = size; + + attribute.array = new Float32Array( nvertices * size ); + + attribute.buffer = _gl.createBuffer(); + attribute.buffer.belongsToAttribute = name; + + attribute.needsUpdate = true; + + } + + geometry.__webglCustomAttributesList.push( attribute ); + + } + + } + + }; + + function initParticleBuffers ( geometry, object ) { + + var nvertices = geometry.vertices.length; + + geometry.__vertexArray = new Float32Array( nvertices * 3 ); + geometry.__colorArray = new Float32Array( nvertices * 3 ); + + geometry.__sortArray = []; + + geometry.__webglParticleCount = nvertices; + + initCustomAttributes( object ); + + }; + + function initLineBuffers ( geometry, object ) { + + var nvertices = geometry.vertices.length; + + geometry.__vertexArray = new Float32Array( nvertices * 3 ); + geometry.__colorArray = new Float32Array( nvertices * 3 ); + geometry.__lineDistanceArray = new Float32Array( nvertices * 1 ); + + geometry.__webglLineCount = nvertices; + + initCustomAttributes( object ); + + }; + + function initMeshBuffers ( geometryGroup, object ) { + + var geometry = object.geometry, + faces3 = geometryGroup.faces3, + + nvertices = faces3.length * 3, + ntris = faces3.length * 1, + nlines = faces3.length * 3, + + material = getBufferMaterial( object, geometryGroup ); + + geometryGroup.__vertexArray = new Float32Array( nvertices * 3 ); + geometryGroup.__normalArray = new Float32Array( nvertices * 3 ); + geometryGroup.__colorArray = new Float32Array( nvertices * 3 ); + geometryGroup.__uvArray = new Float32Array( nvertices * 2 ); + + if ( geometry.faceVertexUvs.length > 1 ) { + + geometryGroup.__uv2Array = new Float32Array( nvertices * 2 ); + + } + + if ( geometry.hasTangents ) { + + geometryGroup.__tangentArray = new Float32Array( nvertices * 4 ); + + } + + if ( object.geometry.skinWeights.length && object.geometry.skinIndices.length ) { + + geometryGroup.__skinIndexArray = new Float32Array( nvertices * 4 ); + geometryGroup.__skinWeightArray = new Float32Array( nvertices * 4 ); + + } + + var UintArray = extensions.get( 'OES_element_index_uint' ) !== null && ntris > 21845 ? Uint32Array : Uint16Array; // 65535 / 3 + + geometryGroup.__typeArray = UintArray; + geometryGroup.__faceArray = new UintArray( ntris * 3 ); + geometryGroup.__lineArray = new UintArray( nlines * 2 ); + + var numMorphTargets = geometryGroup.numMorphTargets; + + if ( numMorphTargets ) { + + geometryGroup.__morphTargetsArrays = []; + + for ( var m = 0, ml = numMorphTargets; m < ml; m ++ ) { + + geometryGroup.__morphTargetsArrays.push( new Float32Array( nvertices * 3 ) ); + + } + + } + + var numMorphNormals = geometryGroup.numMorphNormals; + + if ( numMorphNormals ) { + + geometryGroup.__morphNormalsArrays = []; + + for ( var m = 0, ml = numMorphNormals; m < ml; m ++ ) { + + geometryGroup.__morphNormalsArrays.push( new Float32Array( nvertices * 3 ) ); + + } + + } + + geometryGroup.__webglFaceCount = ntris * 3; + geometryGroup.__webglLineCount = nlines * 2; + + + // custom attributes + + if ( material.attributes ) { + + if ( geometryGroup.__webglCustomAttributesList === undefined ) { + + geometryGroup.__webglCustomAttributesList = []; + + } + + for ( var name in material.attributes ) { + + // Do a shallow copy of the attribute object so different geometryGroup chunks use different + // attribute buffers which are correctly indexed in the setMeshBuffers function + + var originalAttribute = material.attributes[ name ]; + + var attribute = {}; + + for ( var property in originalAttribute ) { + + attribute[ property ] = originalAttribute[ property ]; + + } + + if ( ! attribute.__webglInitialized || attribute.createUniqueBuffers ) { + + attribute.__webglInitialized = true; + + var size = 1; // "f" and "i" + + if ( attribute.type === 'v2' ) size = 2; + else if ( attribute.type === 'v3' ) size = 3; + else if ( attribute.type === 'v4' ) size = 4; + else if ( attribute.type === 'c' ) size = 3; + + attribute.size = size; + + attribute.array = new Float32Array( nvertices * size ); + + attribute.buffer = _gl.createBuffer(); + attribute.buffer.belongsToAttribute = name; + + originalAttribute.needsUpdate = true; + attribute.__original = originalAttribute; + + } + + geometryGroup.__webglCustomAttributesList.push( attribute ); + + } + + } + + geometryGroup.__inittedArrays = true; + + }; + + function getBufferMaterial( object, geometryGroup ) { + + return object.material instanceof THREE.MeshFaceMaterial + ? object.material.materials[ geometryGroup.materialIndex ] + : object.material; + + }; + + function materialNeedsSmoothNormals ( material ) { + + return material && material.shading !== undefined && material.shading === THREE.SmoothShading; + + }; + + // Buffer setting + + function setParticleBuffers ( geometry, hint, object ) { + + var v, c, vertex, offset, index, color, + + vertices = geometry.vertices, + vl = vertices.length, + + colors = geometry.colors, + cl = colors.length, + + vertexArray = geometry.__vertexArray, + colorArray = geometry.__colorArray, + + sortArray = geometry.__sortArray, + + dirtyVertices = geometry.verticesNeedUpdate, + dirtyElements = geometry.elementsNeedUpdate, + dirtyColors = geometry.colorsNeedUpdate, + + customAttributes = geometry.__webglCustomAttributesList, + i, il, + a, ca, cal, value, + customAttribute; + + if ( dirtyVertices ) { + + for ( v = 0; v < vl; v ++ ) { + + vertex = vertices[ v ]; + + offset = v * 3; + + vertexArray[ offset ] = vertex.x; + vertexArray[ offset + 1 ] = vertex.y; + vertexArray[ offset + 2 ] = vertex.z; + + } + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglVertexBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint ); + + } + + if ( dirtyColors ) { + + for ( c = 0; c < cl; c ++ ) { + + color = colors[ c ]; + + offset = c * 3; + + colorArray[ offset ] = color.r; + colorArray[ offset + 1 ] = color.g; + colorArray[ offset + 2 ] = color.b; + + } + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglColorBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint ); + + } + + if ( customAttributes ) { + + for ( i = 0, il = customAttributes.length; i < il; i ++ ) { + + customAttribute = customAttributes[ i ]; + + if ( customAttribute.needsUpdate && ( customAttribute.boundTo === undefined || customAttribute.boundTo === 'vertices' ) ) { + + cal = customAttribute.value.length; + + offset = 0; + + if ( customAttribute.size === 1 ) { + + for ( ca = 0; ca < cal; ca ++ ) { + + customAttribute.array[ ca ] = customAttribute.value[ ca ]; + + } + + } else if ( customAttribute.size === 2 ) { + + for ( ca = 0; ca < cal; ca ++ ) { + + value = customAttribute.value[ ca ]; + + customAttribute.array[ offset ] = value.x; + customAttribute.array[ offset + 1 ] = value.y; + + offset += 2; + + } + + } else if ( customAttribute.size === 3 ) { + + if ( customAttribute.type === 'c' ) { + + for ( ca = 0; ca < cal; ca ++ ) { + + value = customAttribute.value[ ca ]; + + customAttribute.array[ offset ] = value.r; + customAttribute.array[ offset + 1 ] = value.g; + customAttribute.array[ offset + 2 ] = value.b; + + offset += 3; + + } + + } else { + + for ( ca = 0; ca < cal; ca ++ ) { + + value = customAttribute.value[ ca ]; + + customAttribute.array[ offset ] = value.x; + customAttribute.array[ offset + 1 ] = value.y; + customAttribute.array[ offset + 2 ] = value.z; + + offset += 3; + + } + + } + + } else if ( customAttribute.size === 4 ) { + + for ( ca = 0; ca < cal; ca ++ ) { + + value = customAttribute.value[ ca ]; + + customAttribute.array[ offset ] = value.x; + customAttribute.array[ offset + 1 ] = value.y; + customAttribute.array[ offset + 2 ] = value.z; + customAttribute.array[ offset + 3 ] = value.w; + + offset += 4; + + } + + } + + } + + _gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint ); + + customAttribute.needsUpdate = false; + + } + + } + + } + + function setLineBuffers ( geometry, hint ) { + + var v, c, d, vertex, offset, color, + + vertices = geometry.vertices, + colors = geometry.colors, + lineDistances = geometry.lineDistances, + + vl = vertices.length, + cl = colors.length, + dl = lineDistances.length, + + vertexArray = geometry.__vertexArray, + colorArray = geometry.__colorArray, + lineDistanceArray = geometry.__lineDistanceArray, + + dirtyVertices = geometry.verticesNeedUpdate, + dirtyColors = geometry.colorsNeedUpdate, + dirtyLineDistances = geometry.lineDistancesNeedUpdate, + + customAttributes = geometry.__webglCustomAttributesList, + + i, il, + a, ca, cal, value, + customAttribute; + + if ( dirtyVertices ) { + + for ( v = 0; v < vl; v ++ ) { + + vertex = vertices[ v ]; + + offset = v * 3; + + vertexArray[ offset ] = vertex.x; + vertexArray[ offset + 1 ] = vertex.y; + vertexArray[ offset + 2 ] = vertex.z; + + } + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglVertexBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint ); + + } + + if ( dirtyColors ) { + + for ( c = 0; c < cl; c ++ ) { + + color = colors[ c ]; + + offset = c * 3; + + colorArray[ offset ] = color.r; + colorArray[ offset + 1 ] = color.g; + colorArray[ offset + 2 ] = color.b; + + } + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglColorBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint ); + + } + + if ( dirtyLineDistances ) { + + for ( d = 0; d < dl; d ++ ) { + + lineDistanceArray[ d ] = lineDistances[ d ]; + + } + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglLineDistanceBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, lineDistanceArray, hint ); + + } + + if ( customAttributes ) { + + for ( i = 0, il = customAttributes.length; i < il; i ++ ) { + + customAttribute = customAttributes[ i ]; + + if ( customAttribute.needsUpdate && ( customAttribute.boundTo === undefined || customAttribute.boundTo === 'vertices' ) ) { + + offset = 0; + + cal = customAttribute.value.length; + + if ( customAttribute.size === 1 ) { + + for ( ca = 0; ca < cal; ca ++ ) { + + customAttribute.array[ ca ] = customAttribute.value[ ca ]; + + } + + } else if ( customAttribute.size === 2 ) { + + for ( ca = 0; ca < cal; ca ++ ) { + + value = customAttribute.value[ ca ]; + + customAttribute.array[ offset ] = value.x; + customAttribute.array[ offset + 1 ] = value.y; + + offset += 2; + + } + + } else if ( customAttribute.size === 3 ) { + + if ( customAttribute.type === 'c' ) { + + for ( ca = 0; ca < cal; ca ++ ) { + + value = customAttribute.value[ ca ]; + + customAttribute.array[ offset ] = value.r; + customAttribute.array[ offset + 1 ] = value.g; + customAttribute.array[ offset + 2 ] = value.b; + + offset += 3; + + } + + } else { + + for ( ca = 0; ca < cal; ca ++ ) { + + value = customAttribute.value[ ca ]; + + customAttribute.array[ offset ] = value.x; + customAttribute.array[ offset + 1 ] = value.y; + customAttribute.array[ offset + 2 ] = value.z; + + offset += 3; + + } + + } + + } else if ( customAttribute.size === 4 ) { + + for ( ca = 0; ca < cal; ca ++ ) { + + value = customAttribute.value[ ca ]; + + customAttribute.array[ offset ] = value.x; + customAttribute.array[ offset + 1 ] = value.y; + customAttribute.array[ offset + 2 ] = value.z; + customAttribute.array[ offset + 3 ] = value.w; + + offset += 4; + + } + + } + + _gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint ); + + customAttribute.needsUpdate = false; + + } + + } + + } + + } + + function setMeshBuffers( geometryGroup, object, hint, dispose, material ) { + + if ( ! geometryGroup.__inittedArrays ) { + + return; + + } + + var needsSmoothNormals = materialNeedsSmoothNormals( material ); + + var f, fl, fi, face, + vertexNormals, faceNormal, normal, + vertexColors, faceColor, + vertexTangents, + uv, uv2, v1, v2, v3, v4, t1, t2, t3, t4, n1, n2, n3, n4, + c1, c2, c3, + sw1, sw2, sw3, sw4, + si1, si2, si3, si4, + sa1, sa2, sa3, sa4, + sb1, sb2, sb3, sb4, + m, ml, i, il, + vn, uvi, uv2i, + vk, vkl, vka, + nka, chf, faceVertexNormals, + a, + + vertexIndex = 0, + + offset = 0, + offset_uv = 0, + offset_uv2 = 0, + offset_face = 0, + offset_normal = 0, + offset_tangent = 0, + offset_line = 0, + offset_color = 0, + offset_skin = 0, + offset_morphTarget = 0, + offset_custom = 0, + offset_customSrc = 0, + + value, + + vertexArray = geometryGroup.__vertexArray, + uvArray = geometryGroup.__uvArray, + uv2Array = geometryGroup.__uv2Array, + normalArray = geometryGroup.__normalArray, + tangentArray = geometryGroup.__tangentArray, + colorArray = geometryGroup.__colorArray, + + skinIndexArray = geometryGroup.__skinIndexArray, + skinWeightArray = geometryGroup.__skinWeightArray, + + morphTargetsArrays = geometryGroup.__morphTargetsArrays, + morphNormalsArrays = geometryGroup.__morphNormalsArrays, + + customAttributes = geometryGroup.__webglCustomAttributesList, + customAttribute, + + faceArray = geometryGroup.__faceArray, + lineArray = geometryGroup.__lineArray, + + geometry = object.geometry, // this is shared for all chunks + + dirtyVertices = geometry.verticesNeedUpdate, + dirtyElements = geometry.elementsNeedUpdate, + dirtyUvs = geometry.uvsNeedUpdate, + dirtyNormals = geometry.normalsNeedUpdate, + dirtyTangents = geometry.tangentsNeedUpdate, + dirtyColors = geometry.colorsNeedUpdate, + dirtyMorphTargets = geometry.morphTargetsNeedUpdate, + + vertices = geometry.vertices, + chunk_faces3 = geometryGroup.faces3, + obj_faces = geometry.faces, + + obj_uvs = geometry.faceVertexUvs[ 0 ], + obj_uvs2 = geometry.faceVertexUvs[ 1 ], + + obj_colors = geometry.colors, + + obj_skinIndices = geometry.skinIndices, + obj_skinWeights = geometry.skinWeights, + + morphTargets = geometry.morphTargets, + morphNormals = geometry.morphNormals; + + if ( dirtyVertices ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + face = obj_faces[ chunk_faces3[ f ] ]; + + v1 = vertices[ face.a ]; + v2 = vertices[ face.b ]; + v3 = vertices[ face.c ]; + + vertexArray[ offset ] = v1.x; + vertexArray[ offset + 1 ] = v1.y; + vertexArray[ offset + 2 ] = v1.z; + + vertexArray[ offset + 3 ] = v2.x; + vertexArray[ offset + 4 ] = v2.y; + vertexArray[ offset + 5 ] = v2.z; + + vertexArray[ offset + 6 ] = v3.x; + vertexArray[ offset + 7 ] = v3.y; + vertexArray[ offset + 8 ] = v3.z; + + offset += 9; + + } + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint ); + + } + + if ( dirtyMorphTargets ) { + + for ( vk = 0, vkl = morphTargets.length; vk < vkl; vk ++ ) { + + offset_morphTarget = 0; + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + chf = chunk_faces3[ f ]; + face = obj_faces[ chf ]; + + // morph positions + + v1 = morphTargets[ vk ].vertices[ face.a ]; + v2 = morphTargets[ vk ].vertices[ face.b ]; + v3 = morphTargets[ vk ].vertices[ face.c ]; + + vka = morphTargetsArrays[ vk ]; + + vka[ offset_morphTarget ] = v1.x; + vka[ offset_morphTarget + 1 ] = v1.y; + vka[ offset_morphTarget + 2 ] = v1.z; + + vka[ offset_morphTarget + 3 ] = v2.x; + vka[ offset_morphTarget + 4 ] = v2.y; + vka[ offset_morphTarget + 5 ] = v2.z; + + vka[ offset_morphTarget + 6 ] = v3.x; + vka[ offset_morphTarget + 7 ] = v3.y; + vka[ offset_morphTarget + 8 ] = v3.z; + + // morph normals + + if ( material.morphNormals ) { + + if ( needsSmoothNormals ) { + + faceVertexNormals = morphNormals[ vk ].vertexNormals[ chf ]; + + n1 = faceVertexNormals.a; + n2 = faceVertexNormals.b; + n3 = faceVertexNormals.c; + + } else { + + n1 = morphNormals[ vk ].faceNormals[ chf ]; + n2 = n1; + n3 = n1; + + } + + nka = morphNormalsArrays[ vk ]; + + nka[ offset_morphTarget ] = n1.x; + nka[ offset_morphTarget + 1 ] = n1.y; + nka[ offset_morphTarget + 2 ] = n1.z; + + nka[ offset_morphTarget + 3 ] = n2.x; + nka[ offset_morphTarget + 4 ] = n2.y; + nka[ offset_morphTarget + 5 ] = n2.z; + + nka[ offset_morphTarget + 6 ] = n3.x; + nka[ offset_morphTarget + 7 ] = n3.y; + nka[ offset_morphTarget + 8 ] = n3.z; + + } + + // + + offset_morphTarget += 9; + + } + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ vk ] ); + _gl.bufferData( _gl.ARRAY_BUFFER, morphTargetsArrays[ vk ], hint ); + + if ( material.morphNormals ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphNormalsBuffers[ vk ] ); + _gl.bufferData( _gl.ARRAY_BUFFER, morphNormalsArrays[ vk ], hint ); + + } + + } + + } + + if ( obj_skinWeights.length ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + face = obj_faces[ chunk_faces3[ f ] ]; + + // weights + + sw1 = obj_skinWeights[ face.a ]; + sw2 = obj_skinWeights[ face.b ]; + sw3 = obj_skinWeights[ face.c ]; + + skinWeightArray[ offset_skin ] = sw1.x; + skinWeightArray[ offset_skin + 1 ] = sw1.y; + skinWeightArray[ offset_skin + 2 ] = sw1.z; + skinWeightArray[ offset_skin + 3 ] = sw1.w; + + skinWeightArray[ offset_skin + 4 ] = sw2.x; + skinWeightArray[ offset_skin + 5 ] = sw2.y; + skinWeightArray[ offset_skin + 6 ] = sw2.z; + skinWeightArray[ offset_skin + 7 ] = sw2.w; + + skinWeightArray[ offset_skin + 8 ] = sw3.x; + skinWeightArray[ offset_skin + 9 ] = sw3.y; + skinWeightArray[ offset_skin + 10 ] = sw3.z; + skinWeightArray[ offset_skin + 11 ] = sw3.w; + + // indices + + si1 = obj_skinIndices[ face.a ]; + si2 = obj_skinIndices[ face.b ]; + si3 = obj_skinIndices[ face.c ]; + + skinIndexArray[ offset_skin ] = si1.x; + skinIndexArray[ offset_skin + 1 ] = si1.y; + skinIndexArray[ offset_skin + 2 ] = si1.z; + skinIndexArray[ offset_skin + 3 ] = si1.w; + + skinIndexArray[ offset_skin + 4 ] = si2.x; + skinIndexArray[ offset_skin + 5 ] = si2.y; + skinIndexArray[ offset_skin + 6 ] = si2.z; + skinIndexArray[ offset_skin + 7 ] = si2.w; + + skinIndexArray[ offset_skin + 8 ] = si3.x; + skinIndexArray[ offset_skin + 9 ] = si3.y; + skinIndexArray[ offset_skin + 10 ] = si3.z; + skinIndexArray[ offset_skin + 11 ] = si3.w; + + offset_skin += 12; + + } + + if ( offset_skin > 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinIndicesBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, skinIndexArray, hint ); + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinWeightsBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, skinWeightArray, hint ); + + } + + } + + if ( dirtyColors ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + face = obj_faces[ chunk_faces3[ f ] ]; + + vertexColors = face.vertexColors; + faceColor = face.color; + + if ( vertexColors.length === 3 && material.vertexColors === THREE.VertexColors ) { + + c1 = vertexColors[ 0 ]; + c2 = vertexColors[ 1 ]; + c3 = vertexColors[ 2 ]; + + } else { + + c1 = faceColor; + c2 = faceColor; + c3 = faceColor; + + } + + colorArray[ offset_color ] = c1.r; + colorArray[ offset_color + 1 ] = c1.g; + colorArray[ offset_color + 2 ] = c1.b; + + colorArray[ offset_color + 3 ] = c2.r; + colorArray[ offset_color + 4 ] = c2.g; + colorArray[ offset_color + 5 ] = c2.b; + + colorArray[ offset_color + 6 ] = c3.r; + colorArray[ offset_color + 7 ] = c3.g; + colorArray[ offset_color + 8 ] = c3.b; + + offset_color += 9; + + } + + if ( offset_color > 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglColorBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint ); + + } + + } + + if ( dirtyTangents && geometry.hasTangents ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + face = obj_faces[ chunk_faces3[ f ] ]; + + vertexTangents = face.vertexTangents; + + t1 = vertexTangents[ 0 ]; + t2 = vertexTangents[ 1 ]; + t3 = vertexTangents[ 2 ]; + + tangentArray[ offset_tangent ] = t1.x; + tangentArray[ offset_tangent + 1 ] = t1.y; + tangentArray[ offset_tangent + 2 ] = t1.z; + tangentArray[ offset_tangent + 3 ] = t1.w; + + tangentArray[ offset_tangent + 4 ] = t2.x; + tangentArray[ offset_tangent + 5 ] = t2.y; + tangentArray[ offset_tangent + 6 ] = t2.z; + tangentArray[ offset_tangent + 7 ] = t2.w; + + tangentArray[ offset_tangent + 8 ] = t3.x; + tangentArray[ offset_tangent + 9 ] = t3.y; + tangentArray[ offset_tangent + 10 ] = t3.z; + tangentArray[ offset_tangent + 11 ] = t3.w; + + offset_tangent += 12; + + } + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglTangentBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, tangentArray, hint ); + + } + + if ( dirtyNormals ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + face = obj_faces[ chunk_faces3[ f ] ]; + + vertexNormals = face.vertexNormals; + faceNormal = face.normal; + + if ( vertexNormals.length === 3 && needsSmoothNormals ) { + + for ( i = 0; i < 3; i ++ ) { + + vn = vertexNormals[ i ]; + + normalArray[ offset_normal ] = vn.x; + normalArray[ offset_normal + 1 ] = vn.y; + normalArray[ offset_normal + 2 ] = vn.z; + + offset_normal += 3; + + } + + } else { + + for ( i = 0; i < 3; i ++ ) { + + normalArray[ offset_normal ] = faceNormal.x; + normalArray[ offset_normal + 1 ] = faceNormal.y; + normalArray[ offset_normal + 2 ] = faceNormal.z; + + offset_normal += 3; + + } + + } + + } + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglNormalBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, normalArray, hint ); + + } + + if ( dirtyUvs && obj_uvs ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + fi = chunk_faces3[ f ]; + + uv = obj_uvs[ fi ]; + + if ( uv === undefined ) continue; + + for ( i = 0; i < 3; i ++ ) { + + uvi = uv[ i ]; + + uvArray[ offset_uv ] = uvi.x; + uvArray[ offset_uv + 1 ] = uvi.y; + + offset_uv += 2; + + } + + } + + if ( offset_uv > 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUVBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, uvArray, hint ); + + } + + } + + if ( dirtyUvs && obj_uvs2 ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + fi = chunk_faces3[ f ]; + + uv2 = obj_uvs2[ fi ]; + + if ( uv2 === undefined ) continue; + + for ( i = 0; i < 3; i ++ ) { + + uv2i = uv2[ i ]; + + uv2Array[ offset_uv2 ] = uv2i.x; + uv2Array[ offset_uv2 + 1 ] = uv2i.y; + + offset_uv2 += 2; + + } + + } + + if ( offset_uv2 > 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUV2Buffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, uv2Array, hint ); + + } + + } + + if ( dirtyElements ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + faceArray[ offset_face ] = vertexIndex; + faceArray[ offset_face + 1 ] = vertexIndex + 1; + faceArray[ offset_face + 2 ] = vertexIndex + 2; + + offset_face += 3; + + lineArray[ offset_line ] = vertexIndex; + lineArray[ offset_line + 1 ] = vertexIndex + 1; + + lineArray[ offset_line + 2 ] = vertexIndex; + lineArray[ offset_line + 3 ] = vertexIndex + 2; + + lineArray[ offset_line + 4 ] = vertexIndex + 1; + lineArray[ offset_line + 5 ] = vertexIndex + 2; + + offset_line += 6; + + vertexIndex += 3; + + } + + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglFaceBuffer ); + _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, faceArray, hint ); + + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglLineBuffer ); + _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, lineArray, hint ); + + } + + if ( customAttributes ) { + + for ( i = 0, il = customAttributes.length; i < il; i ++ ) { + + customAttribute = customAttributes[ i ]; + + if ( ! customAttribute.__original.needsUpdate ) continue; + + offset_custom = 0; + offset_customSrc = 0; + + if ( customAttribute.size === 1 ) { + + if ( customAttribute.boundTo === undefined || customAttribute.boundTo === 'vertices' ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + face = obj_faces[ chunk_faces3[ f ] ]; + + customAttribute.array[ offset_custom ] = customAttribute.value[ face.a ]; + customAttribute.array[ offset_custom + 1 ] = customAttribute.value[ face.b ]; + customAttribute.array[ offset_custom + 2 ] = customAttribute.value[ face.c ]; + + offset_custom += 3; + + } + + } else if ( customAttribute.boundTo === 'faces' ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + value = customAttribute.value[ chunk_faces3[ f ] ]; + + customAttribute.array[ offset_custom ] = value; + customAttribute.array[ offset_custom + 1 ] = value; + customAttribute.array[ offset_custom + 2 ] = value; + + offset_custom += 3; + + } + + } + + } else if ( customAttribute.size === 2 ) { + + if ( customAttribute.boundTo === undefined || customAttribute.boundTo === 'vertices' ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + face = obj_faces[ chunk_faces3[ f ] ]; + + v1 = customAttribute.value[ face.a ]; + v2 = customAttribute.value[ face.b ]; + v3 = customAttribute.value[ face.c ]; + + customAttribute.array[ offset_custom ] = v1.x; + customAttribute.array[ offset_custom + 1 ] = v1.y; + + customAttribute.array[ offset_custom + 2 ] = v2.x; + customAttribute.array[ offset_custom + 3 ] = v2.y; + + customAttribute.array[ offset_custom + 4 ] = v3.x; + customAttribute.array[ offset_custom + 5 ] = v3.y; + + offset_custom += 6; + + } + + } else if ( customAttribute.boundTo === 'faces' ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + value = customAttribute.value[ chunk_faces3[ f ] ]; + + v1 = value; + v2 = value; + v3 = value; + + customAttribute.array[ offset_custom ] = v1.x; + customAttribute.array[ offset_custom + 1 ] = v1.y; + + customAttribute.array[ offset_custom + 2 ] = v2.x; + customAttribute.array[ offset_custom + 3 ] = v2.y; + + customAttribute.array[ offset_custom + 4 ] = v3.x; + customAttribute.array[ offset_custom + 5 ] = v3.y; + + offset_custom += 6; + + } + + } + + } else if ( customAttribute.size === 3 ) { + + var pp; + + if ( customAttribute.type === 'c' ) { + + pp = [ 'r', 'g', 'b' ]; + + } else { + + pp = [ 'x', 'y', 'z' ]; + + } + + if ( customAttribute.boundTo === undefined || customAttribute.boundTo === 'vertices' ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + face = obj_faces[ chunk_faces3[ f ] ]; + + v1 = customAttribute.value[ face.a ]; + v2 = customAttribute.value[ face.b ]; + v3 = customAttribute.value[ face.c ]; + + customAttribute.array[ offset_custom ] = v1[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ]; + + customAttribute.array[ offset_custom + 3 ] = v2[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 4 ] = v2[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 5 ] = v2[ pp[ 2 ] ]; + + customAttribute.array[ offset_custom + 6 ] = v3[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 7 ] = v3[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 8 ] = v3[ pp[ 2 ] ]; + + offset_custom += 9; + + } + + } else if ( customAttribute.boundTo === 'faces' ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + value = customAttribute.value[ chunk_faces3[ f ] ]; + + v1 = value; + v2 = value; + v3 = value; + + customAttribute.array[ offset_custom ] = v1[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ]; + + customAttribute.array[ offset_custom + 3 ] = v2[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 4 ] = v2[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 5 ] = v2[ pp[ 2 ] ]; + + customAttribute.array[ offset_custom + 6 ] = v3[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 7 ] = v3[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 8 ] = v3[ pp[ 2 ] ]; + + offset_custom += 9; + + } + + } else if ( customAttribute.boundTo === 'faceVertices' ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + value = customAttribute.value[ chunk_faces3[ f ] ]; + + v1 = value[ 0 ]; + v2 = value[ 1 ]; + v3 = value[ 2 ]; + + customAttribute.array[ offset_custom ] = v1[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ]; + + customAttribute.array[ offset_custom + 3 ] = v2[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 4 ] = v2[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 5 ] = v2[ pp[ 2 ] ]; + + customAttribute.array[ offset_custom + 6 ] = v3[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 7 ] = v3[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 8 ] = v3[ pp[ 2 ] ]; + + offset_custom += 9; + + } + + } + + } else if ( customAttribute.size === 4 ) { + + if ( customAttribute.boundTo === undefined || customAttribute.boundTo === 'vertices' ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + face = obj_faces[ chunk_faces3[ f ] ]; + + v1 = customAttribute.value[ face.a ]; + v2 = customAttribute.value[ face.b ]; + v3 = customAttribute.value[ face.c ]; + + customAttribute.array[ offset_custom ] = v1.x; + customAttribute.array[ offset_custom + 1 ] = v1.y; + customAttribute.array[ offset_custom + 2 ] = v1.z; + customAttribute.array[ offset_custom + 3 ] = v1.w; + + customAttribute.array[ offset_custom + 4 ] = v2.x; + customAttribute.array[ offset_custom + 5 ] = v2.y; + customAttribute.array[ offset_custom + 6 ] = v2.z; + customAttribute.array[ offset_custom + 7 ] = v2.w; + + customAttribute.array[ offset_custom + 8 ] = v3.x; + customAttribute.array[ offset_custom + 9 ] = v3.y; + customAttribute.array[ offset_custom + 10 ] = v3.z; + customAttribute.array[ offset_custom + 11 ] = v3.w; + + offset_custom += 12; + + } + + } else if ( customAttribute.boundTo === 'faces' ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + value = customAttribute.value[ chunk_faces3[ f ] ]; + + v1 = value; + v2 = value; + v3 = value; + + customAttribute.array[ offset_custom ] = v1.x; + customAttribute.array[ offset_custom + 1 ] = v1.y; + customAttribute.array[ offset_custom + 2 ] = v1.z; + customAttribute.array[ offset_custom + 3 ] = v1.w; + + customAttribute.array[ offset_custom + 4 ] = v2.x; + customAttribute.array[ offset_custom + 5 ] = v2.y; + customAttribute.array[ offset_custom + 6 ] = v2.z; + customAttribute.array[ offset_custom + 7 ] = v2.w; + + customAttribute.array[ offset_custom + 8 ] = v3.x; + customAttribute.array[ offset_custom + 9 ] = v3.y; + customAttribute.array[ offset_custom + 10 ] = v3.z; + customAttribute.array[ offset_custom + 11 ] = v3.w; + + offset_custom += 12; + + } + + } else if ( customAttribute.boundTo === 'faceVertices' ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + value = customAttribute.value[ chunk_faces3[ f ] ]; + + v1 = value[ 0 ]; + v2 = value[ 1 ]; + v3 = value[ 2 ]; + + customAttribute.array[ offset_custom ] = v1.x; + customAttribute.array[ offset_custom + 1 ] = v1.y; + customAttribute.array[ offset_custom + 2 ] = v1.z; + customAttribute.array[ offset_custom + 3 ] = v1.w; + + customAttribute.array[ offset_custom + 4 ] = v2.x; + customAttribute.array[ offset_custom + 5 ] = v2.y; + customAttribute.array[ offset_custom + 6 ] = v2.z; + customAttribute.array[ offset_custom + 7 ] = v2.w; + + customAttribute.array[ offset_custom + 8 ] = v3.x; + customAttribute.array[ offset_custom + 9 ] = v3.y; + customAttribute.array[ offset_custom + 10 ] = v3.z; + customAttribute.array[ offset_custom + 11 ] = v3.w; + + offset_custom += 12; + + } + + } + + } + + _gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint ); + + } + + } + + if ( dispose ) { + + delete geometryGroup.__inittedArrays; + delete geometryGroup.__colorArray; + delete geometryGroup.__normalArray; + delete geometryGroup.__tangentArray; + delete geometryGroup.__uvArray; + delete geometryGroup.__uv2Array; + delete geometryGroup.__faceArray; + delete geometryGroup.__vertexArray; + delete geometryGroup.__lineArray; + delete geometryGroup.__skinIndexArray; + delete geometryGroup.__skinWeightArray; + + } + + }; + + // Buffer rendering + + this.renderBufferImmediate = function ( object, program, material ) { + + initAttributes(); + + if ( object.hasPositions && ! object.__webglVertexBuffer ) object.__webglVertexBuffer = _gl.createBuffer(); + if ( object.hasNormals && ! object.__webglNormalBuffer ) object.__webglNormalBuffer = _gl.createBuffer(); + if ( object.hasUvs && ! object.__webglUvBuffer ) object.__webglUvBuffer = _gl.createBuffer(); + if ( object.hasColors && ! object.__webglColorBuffer ) object.__webglColorBuffer = _gl.createBuffer(); + + if ( object.hasPositions ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglVertexBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, object.positionArray, _gl.DYNAMIC_DRAW ); + enableAttribute( program.attributes.position ); + _gl.vertexAttribPointer( program.attributes.position, 3, _gl.FLOAT, false, 0, 0 ); + + } + + if ( object.hasNormals ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglNormalBuffer ); + + if ( material.shading === THREE.FlatShading ) { + + var nx, ny, nz, + nax, nbx, ncx, nay, nby, ncy, naz, nbz, ncz, + normalArray, + i, il = object.count * 3; + + for ( i = 0; i < il; i += 9 ) { + + normalArray = object.normalArray; + + nax = normalArray[ i ]; + nay = normalArray[ i + 1 ]; + naz = normalArray[ i + 2 ]; + + nbx = normalArray[ i + 3 ]; + nby = normalArray[ i + 4 ]; + nbz = normalArray[ i + 5 ]; + + ncx = normalArray[ i + 6 ]; + ncy = normalArray[ i + 7 ]; + ncz = normalArray[ i + 8 ]; + + nx = ( nax + nbx + ncx ) / 3; + ny = ( nay + nby + ncy ) / 3; + nz = ( naz + nbz + ncz ) / 3; + + normalArray[ i ] = nx; + normalArray[ i + 1 ] = ny; + normalArray[ i + 2 ] = nz; + + normalArray[ i + 3 ] = nx; + normalArray[ i + 4 ] = ny; + normalArray[ i + 5 ] = nz; + + normalArray[ i + 6 ] = nx; + normalArray[ i + 7 ] = ny; + normalArray[ i + 8 ] = nz; + + } + + } + + _gl.bufferData( _gl.ARRAY_BUFFER, object.normalArray, _gl.DYNAMIC_DRAW ); + enableAttribute( program.attributes.normal ); + _gl.vertexAttribPointer( program.attributes.normal, 3, _gl.FLOAT, false, 0, 0 ); + + } + + if ( object.hasUvs && material.map ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglUvBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, object.uvArray, _gl.DYNAMIC_DRAW ); + enableAttribute( program.attributes.uv ); + _gl.vertexAttribPointer( program.attributes.uv, 2, _gl.FLOAT, false, 0, 0 ); + + } + + if ( object.hasColors && material.vertexColors !== THREE.NoColors ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglColorBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, object.colorArray, _gl.DYNAMIC_DRAW ); + enableAttribute( program.attributes.color ); + _gl.vertexAttribPointer( program.attributes.color, 3, _gl.FLOAT, false, 0, 0 ); + + } + + disableUnusedAttributes(); + + _gl.drawArrays( _gl.TRIANGLES, 0, object.count ); + + object.count = 0; + + }; + + function setupVertexAttributes( material, program, geometry, startIndex ) { + + var geometryAttributes = geometry.attributes; + + var programAttributes = program.attributes; + var programAttributesKeys = program.attributesKeys; + + for ( var i = 0, l = programAttributesKeys.length; i < l; i ++ ) { + + var key = programAttributesKeys[ i ]; + var programAttribute = programAttributes[ key ]; + + if ( programAttribute >= 0 ) { + + var geometryAttribute = geometryAttributes[ key ]; + + if ( geometryAttribute !== undefined ) { + + var size = geometryAttribute.itemSize; + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryAttribute.buffer ); + + enableAttribute( programAttribute ); + + _gl.vertexAttribPointer( programAttribute, size, _gl.FLOAT, false, 0, startIndex * size * 4 ); // 4 bytes per Float32 + + } else if ( material.defaultAttributeValues !== undefined ) { + + if ( material.defaultAttributeValues[ key ].length === 2 ) { + + _gl.vertexAttrib2fv( programAttribute, material.defaultAttributeValues[ key ] ); + + } else if ( material.defaultAttributeValues[ key ].length === 3 ) { + + _gl.vertexAttrib3fv( programAttribute, material.defaultAttributeValues[ key ] ); + + } + + } + + } + + } + + disableUnusedAttributes(); + + } + + this.renderBufferDirect = function ( camera, lights, fog, material, geometry, object ) { + + if ( material.visible === false ) return; + + updateObject( object ); + + var program = setProgram( camera, lights, fog, material, object ); + + var updateBuffers = false, + wireframeBit = material.wireframe ? 1 : 0, + geometryProgram = 'direct_' + geometry.id + '_' + program.id + '_' + wireframeBit; + + if ( geometryProgram !== _currentGeometryProgram ) { + + _currentGeometryProgram = geometryProgram; + updateBuffers = true; + + } + + if ( updateBuffers ) { + + initAttributes(); + + } + + // render mesh + + if ( object instanceof THREE.Mesh ) { + + var mode = material.wireframe === true ? _gl.LINES : _gl.TRIANGLES; + + var index = geometry.attributes.index; + + if ( index ) { + + // indexed triangles + + var type, size; + + if ( index.array instanceof Uint32Array && extensions.get( 'OES_element_index_uint' ) ) { + + type = _gl.UNSIGNED_INT; + size = 4; + + } else { + + type = _gl.UNSIGNED_SHORT; + size = 2; + + } + + var offsets = geometry.offsets; + + if ( offsets.length === 0 ) { + + if ( updateBuffers ) { + + setupVertexAttributes( material, program, geometry, 0 ); + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer ); + + } + + _gl.drawElements( mode, index.array.length, type, 0 ); + + _this.info.render.calls ++; + _this.info.render.vertices += index.array.length; // not really true, here vertices can be shared + _this.info.render.faces += index.array.length / 3; + + } else { + + // if there is more than 1 chunk + // must set attribute pointers to use new offsets for each chunk + // even if geometry and materials didn't change + + updateBuffers = true; + + for ( var i = 0, il = offsets.length; i < il; i ++ ) { + + var startIndex = offsets[ i ].index; + + if ( updateBuffers ) { + + setupVertexAttributes( material, program, geometry, startIndex ); + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer ); + + } + + // render indexed triangles + + _gl.drawElements( mode, offsets[ i ].count, type, offsets[ i ].start * size ); + + _this.info.render.calls ++; + _this.info.render.vertices += offsets[ i ].count; // not really true, here vertices can be shared + _this.info.render.faces += offsets[ i ].count / 3; + + } + + } + + } else { + + // non-indexed triangles + + if ( updateBuffers ) { + + setupVertexAttributes( material, program, geometry, 0 ); + + } + + var position = geometry.attributes[ 'position' ]; + + // render non-indexed triangles + + _gl.drawArrays( mode, 0, position.array.length / 3 ); + + _this.info.render.calls ++; + _this.info.render.vertices += position.array.length / 3; + _this.info.render.faces += position.array.length / 9; + + } + + } else if ( object instanceof THREE.PointCloud ) { + + // render particles + + var mode = _gl.POINTS; + + var index = geometry.attributes.index; + + if ( index ) { + + // indexed points + + var type, size; + + if ( index.array instanceof Uint32Array && extensions.get( 'OES_element_index_uint' ) ) { + + type = _gl.UNSIGNED_INT; + size = 4; + + } else { + + type = _gl.UNSIGNED_SHORT; + size = 2; + + } + + var offsets = geometry.offsets; + + if ( offsets.length === 0 ) { + + if ( updateBuffers ) { + + setupVertexAttributes( material, program, geometry, 0 ); + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer ); + + } + + _gl.drawElements( mode, index.array.length, type, 0); + + _this.info.render.calls ++; + _this.info.render.points += index.array.length; + + } else { + + // if there is more than 1 chunk + // must set attribute pointers to use new offsets for each chunk + // even if geometry and materials didn't change + + if ( offsets.length > 1 ) updateBuffers = true; + + for ( var i = 0, il = offsets.length; i < il; i ++ ) { + + var startIndex = offsets[ i ].index; + + if ( updateBuffers ) { + + setupVertexAttributes( material, program, geometry, startIndex ); + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer ); + + } + + // render indexed points + + _gl.drawElements( mode, offsets[ i ].count, type, offsets[ i ].start * size ); + + _this.info.render.calls ++; + _this.info.render.points += offsets[ i ].count; + + } + + } + + } else { + + // non-indexed points + + if ( updateBuffers ) { + + setupVertexAttributes( material, program, geometry, 0 ); + + } + + var position = geometry.attributes.position; + var offsets = geometry.offsets; + + if ( offsets.length === 0 ) { + + _gl.drawArrays( mode, 0, position.array.length / 3 ); + + _this.info.render.calls ++; + _this.info.render.points += position.array.length / 3; + + } else { + + for ( var i = 0, il = offsets.length; i < il; i ++ ) { + + _gl.drawArrays( mode, offsets[ i ].index, offsets[ i ].count ); + + _this.info.render.calls ++; + _this.info.render.points += offsets[ i ].count; + + } + + } + + } + + } else if ( object instanceof THREE.Line ) { + + var mode = ( object.mode === THREE.LineStrip ) ? _gl.LINE_STRIP : _gl.LINES; + + setLineWidth( material.linewidth ); + + var index = geometry.attributes.index; + + if ( index ) { + + // indexed lines + + var type, size; + + if ( index.array instanceof Uint32Array ) { + + type = _gl.UNSIGNED_INT; + size = 4; + + } else { + + type = _gl.UNSIGNED_SHORT; + size = 2; + + } + + var offsets = geometry.offsets; + + if ( offsets.length === 0 ) { + + if ( updateBuffers ) { + + setupVertexAttributes( material, program, geometry, 0 ); + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer ); + + } + + _gl.drawElements( mode, index.array.length, type, 0 ); // 2 bytes per Uint16Array + + _this.info.render.calls ++; + _this.info.render.vertices += index.array.length; // not really true, here vertices can be shared + + } else { + + // if there is more than 1 chunk + // must set attribute pointers to use new offsets for each chunk + // even if geometry and materials didn't change + + if ( offsets.length > 1 ) updateBuffers = true; + + for ( var i = 0, il = offsets.length; i < il; i ++ ) { + + var startIndex = offsets[ i ].index; + + if ( updateBuffers ) { + + setupVertexAttributes( material, program, geometry, startIndex ); + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer ); + + } + + // render indexed lines + + _gl.drawElements( mode, offsets[ i ].count, type, offsets[ i ].start * size ); // 2 bytes per Uint16Array + + _this.info.render.calls ++; + _this.info.render.vertices += offsets[ i ].count; // not really true, here vertices can be shared + + } + + } + + } else { + + // non-indexed lines + + if ( updateBuffers ) { + + setupVertexAttributes( material, program, geometry, 0 ); + + } + + var position = geometry.attributes.position; + var offsets = geometry.offsets; + + if ( offsets.length === 0 ) { + + _gl.drawArrays( mode, 0, position.array.length / 3 ); + + _this.info.render.calls ++; + _this.info.render.vertices += position.array.length / 3; + + } else { + + for ( var i = 0, il = offsets.length; i < il; i ++ ) { + + _gl.drawArrays( mode, offsets[ i ].index, offsets[ i ].count ); + + _this.info.render.calls ++; + _this.info.render.vertices += offsets[ i ].count; + + } + + } + + } + + } + + }; + + this.renderBuffer = function ( camera, lights, fog, material, geometryGroup, object ) { + + if ( material.visible === false ) return; + + updateObject( object ); + + var program = setProgram( camera, lights, fog, material, object ); + + var attributes = program.attributes; + + var updateBuffers = false, + wireframeBit = material.wireframe ? 1 : 0, + geometryProgram = geometryGroup.id + '_' + program.id + '_' + wireframeBit; + + if ( geometryProgram !== _currentGeometryProgram ) { + + _currentGeometryProgram = geometryProgram; + updateBuffers = true; + + } + + if ( updateBuffers ) { + + initAttributes(); + + } + + // vertices + + if ( ! material.morphTargets && attributes.position >= 0 ) { + + if ( updateBuffers ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer ); + enableAttribute( attributes.position ); + _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 ); + + } + + } else { + + if ( object.morphTargetBase ) { + + setupMorphTargets( material, geometryGroup, object ); + + } + + } + + + if ( updateBuffers ) { + + // custom attributes + + // Use the per-geometryGroup custom attribute arrays which are setup in initMeshBuffers + + if ( geometryGroup.__webglCustomAttributesList ) { + + for ( var i = 0, il = geometryGroup.__webglCustomAttributesList.length; i < il; i ++ ) { + + var attribute = geometryGroup.__webglCustomAttributesList[ i ]; + + if ( attributes[ attribute.buffer.belongsToAttribute ] >= 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, attribute.buffer ); + enableAttribute( attributes[ attribute.buffer.belongsToAttribute ] ); + _gl.vertexAttribPointer( attributes[ attribute.buffer.belongsToAttribute ], attribute.size, _gl.FLOAT, false, 0, 0 ); + + } + + } + + } + + + // colors + + if ( attributes.color >= 0 ) { + + if ( object.geometry.colors.length > 0 || object.geometry.faces.length > 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglColorBuffer ); + enableAttribute( attributes.color ); + _gl.vertexAttribPointer( attributes.color, 3, _gl.FLOAT, false, 0, 0 ); + + } else if ( material.defaultAttributeValues !== undefined ) { + + + _gl.vertexAttrib3fv( attributes.color, material.defaultAttributeValues.color ); + + } + + } + + // normals + + if ( attributes.normal >= 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglNormalBuffer ); + enableAttribute( attributes.normal ); + _gl.vertexAttribPointer( attributes.normal, 3, _gl.FLOAT, false, 0, 0 ); + + } + + // tangents + + if ( attributes.tangent >= 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglTangentBuffer ); + enableAttribute( attributes.tangent ); + _gl.vertexAttribPointer( attributes.tangent, 4, _gl.FLOAT, false, 0, 0 ); + + } + + // uvs + + if ( attributes.uv >= 0 ) { + + if ( object.geometry.faceVertexUvs[ 0 ] ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUVBuffer ); + enableAttribute( attributes.uv ); + _gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 0, 0 ); + + } else if ( material.defaultAttributeValues !== undefined ) { + + + _gl.vertexAttrib2fv( attributes.uv, material.defaultAttributeValues.uv ); + + } + + } + + if ( attributes.uv2 >= 0 ) { + + if ( object.geometry.faceVertexUvs[ 1 ] ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUV2Buffer ); + enableAttribute( attributes.uv2 ); + _gl.vertexAttribPointer( attributes.uv2, 2, _gl.FLOAT, false, 0, 0 ); + + } else if ( material.defaultAttributeValues !== undefined ) { + + + _gl.vertexAttrib2fv( attributes.uv2, material.defaultAttributeValues.uv2 ); + + } + + } + + if ( material.skinning && + attributes.skinIndex >= 0 && attributes.skinWeight >= 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinIndicesBuffer ); + enableAttribute( attributes.skinIndex ); + _gl.vertexAttribPointer( attributes.skinIndex, 4, _gl.FLOAT, false, 0, 0 ); + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinWeightsBuffer ); + enableAttribute( attributes.skinWeight ); + _gl.vertexAttribPointer( attributes.skinWeight, 4, _gl.FLOAT, false, 0, 0 ); + + } + + // line distances + + if ( attributes.lineDistance >= 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglLineDistanceBuffer ); + enableAttribute( attributes.lineDistance ); + _gl.vertexAttribPointer( attributes.lineDistance, 1, _gl.FLOAT, false, 0, 0 ); + + } + + } + + disableUnusedAttributes(); + + // render mesh + + if ( object instanceof THREE.Mesh ) { + + var type = geometryGroup.__typeArray === Uint32Array ? _gl.UNSIGNED_INT : _gl.UNSIGNED_SHORT; + + // wireframe + + if ( material.wireframe ) { + + setLineWidth( material.wireframeLinewidth ); + if ( updateBuffers ) _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglLineBuffer ); + _gl.drawElements( _gl.LINES, geometryGroup.__webglLineCount, type, 0 ); + + // triangles + + } else { + + if ( updateBuffers ) _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglFaceBuffer ); + _gl.drawElements( _gl.TRIANGLES, geometryGroup.__webglFaceCount, type, 0 ); + + } + + _this.info.render.calls ++; + _this.info.render.vertices += geometryGroup.__webglFaceCount; + _this.info.render.faces += geometryGroup.__webglFaceCount / 3; + + // render lines + + } else if ( object instanceof THREE.Line ) { + + var mode = ( object.mode === THREE.LineStrip ) ? _gl.LINE_STRIP : _gl.LINES; + + setLineWidth( material.linewidth ); + + _gl.drawArrays( mode, 0, geometryGroup.__webglLineCount ); + + _this.info.render.calls ++; + + // render particles + + } else if ( object instanceof THREE.PointCloud ) { + + _gl.drawArrays( _gl.POINTS, 0, geometryGroup.__webglParticleCount ); + + _this.info.render.calls ++; + _this.info.render.points += geometryGroup.__webglParticleCount; + + } + + }; + + function initAttributes() { + + for ( var i = 0, l = _newAttributes.length; i < l; i ++ ) { + + _newAttributes[ i ] = 0; + + } + + } + + function enableAttribute( attribute ) { + + _newAttributes[ attribute ] = 1; + + if ( _enabledAttributes[ attribute ] === 0 ) { + + _gl.enableVertexAttribArray( attribute ); + _enabledAttributes[ attribute ] = 1; + + } + + } + + function disableUnusedAttributes() { + + for ( var i = 0, l = _enabledAttributes.length; i < l; i ++ ) { + + if ( _enabledAttributes[ i ] !== _newAttributes[ i ] ) { + + _gl.disableVertexAttribArray( i ); + _enabledAttributes[ i ] = 0; + + } + + } + + } + + function setupMorphTargets ( material, geometryGroup, object ) { + + // set base + + var attributes = material.program.attributes; + + if ( object.morphTargetBase !== - 1 && attributes.position >= 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ object.morphTargetBase ] ); + enableAttribute( attributes.position ); + _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 ); + + } else if ( attributes.position >= 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer ); + enableAttribute( attributes.position ); + _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 ); + + } + + if ( object.morphTargetForcedOrder.length ) { + + // set forced order + + var m = 0; + var order = object.morphTargetForcedOrder; + var influences = object.morphTargetInfluences; + + var attribute; + + while ( m < material.numSupportedMorphTargets && m < order.length ) { + + attribute = attributes[ 'morphTarget' + m ]; + + if ( attribute >= 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ order[ m ] ] ); + enableAttribute( attribute ); + _gl.vertexAttribPointer( attribute, 3, _gl.FLOAT, false, 0, 0 ); + + } + + attribute = attributes[ 'morphNormal' + m ]; + + if ( attribute >= 0 && material.morphNormals ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphNormalsBuffers[ order[ m ] ] ); + enableAttribute( attribute ); + _gl.vertexAttribPointer( attribute, 3, _gl.FLOAT, false, 0, 0 ); + + } + + object.__webglMorphTargetInfluences[ m ] = influences[ order[ m ] ]; + + m ++; + + } + + } else { + + // find the most influencing + + var activeInfluenceIndices = []; + var influences = object.morphTargetInfluences; + + for ( var i = 0, il = influences.length; i < il; i ++ ) { + + var influence = influences[ i ]; + + activeInfluenceIndices.push( [ influence, i ] ); + + } + + if ( activeInfluenceIndices.length > material.numSupportedMorphTargets ) { + + activeInfluenceIndices.sort( numericalSort ); + activeInfluenceIndices.length = material.numSupportedMorphTargets; + + } else if ( activeInfluenceIndices.length > material.numSupportedMorphNormals ) { + + activeInfluenceIndices.sort( numericalSort ); + + } else if ( activeInfluenceIndices.length === 0 ) { + + activeInfluenceIndices.push( [ 0, 0 ] ); + + } + + var attribute; + + for ( var m = 0, ml = material.numSupportedMorphTargets; m < ml; m ++ ) { + + if ( activeInfluenceIndices[ m ] ) { + + var influenceIndex = activeInfluenceIndices[ m ][ 1 ]; + + attribute = attributes[ 'morphTarget' + m ]; + + if ( attribute >= 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ influenceIndex ] ); + enableAttribute( attribute ); + _gl.vertexAttribPointer( attribute, 3, _gl.FLOAT, false, 0, 0 ); + + } + + attribute = attributes[ 'morphNormal' + m ]; + + if ( attribute >= 0 && material.morphNormals ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphNormalsBuffers[ influenceIndex ] ); + enableAttribute( attribute ); + _gl.vertexAttribPointer( attribute, 3, _gl.FLOAT, false, 0, 0 ); + + + } + + object.__webglMorphTargetInfluences[ m ] = influences[ influenceIndex ]; + + } else { + + /* + _gl.vertexAttribPointer( attributes[ "morphTarget" + m ], 3, _gl.FLOAT, false, 0, 0 ); + + if ( material.morphNormals ) { + + _gl.vertexAttribPointer( attributes[ "morphNormal" + m ], 3, _gl.FLOAT, false, 0, 0 ); + + } + */ + + object.__webglMorphTargetInfluences[ m ] = 0; + + } + + } + + } + + // load updated influences uniform + + if ( material.program.uniforms.morphTargetInfluences !== null ) { + + _gl.uniform1fv( material.program.uniforms.morphTargetInfluences, object.__webglMorphTargetInfluences ); + + } + + } + + // Sorting + + function painterSortStable ( a, b ) { + + if ( a.material.id !== b.material.id ) { + + return a.material.id - b.material.id; + + } else if ( a.z !== b.z ) { + + return a.z - b.z; + + } else { + + return a.id - b.id; + + } + + } + + function reversePainterSortStable ( a, b ) { + + if ( a.z !== b.z ) { + + return b.z - a.z; + + } else { + + return a.id - b.id; + + } + + } + + function numericalSort ( a, b ) { + + return b[ 0 ] - a[ 0 ]; + + } + + // Rendering + + this.render = function ( scene, camera, renderTarget, forceClear ) { + + if ( camera instanceof THREE.Camera === false ) { + + console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' ); + return; + + } + + var fog = scene.fog; + + // reset caching for this frame + + _currentGeometryProgram = ''; + _currentMaterialId = - 1; + _currentCamera = null; + _lightsNeedUpdate = true; + + // update scene graph + + if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); + + // update camera matrices and frustum + + if ( camera.parent === undefined ) camera.updateMatrixWorld(); + + // update Skeleton objects + + scene.traverse( function ( object ) { + + if ( object instanceof THREE.SkinnedMesh ) { + + object.skeleton.update(); + + } + + } ); + + camera.matrixWorldInverse.getInverse( camera.matrixWorld ); + + _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); + _frustum.setFromMatrix( _projScreenMatrix ); + + lights.length = 0; + opaqueObjects.length = 0; + transparentObjects.length = 0; + + sprites.length = 0; + lensFlares.length = 0; + + projectObject( scene ); + + if ( _this.sortObjects === true ) { + + opaqueObjects.sort( painterSortStable ); + transparentObjects.sort( reversePainterSortStable ); + + } + + // custom render plugins (pre pass) + + shadowMapPlugin.render( scene, camera ); + + // + + _this.info.render.calls = 0; + _this.info.render.vertices = 0; + _this.info.render.faces = 0; + _this.info.render.points = 0; + + this.setRenderTarget( renderTarget ); + + if ( this.autoClear || forceClear ) { + + this.clear( this.autoClearColor, this.autoClearDepth, this.autoClearStencil ); + + } + + // set matrices for immediate objects + + for ( var i = 0, il = _webglObjectsImmediate.length; i < il; i ++ ) { + + var webglObject = _webglObjectsImmediate[ i ]; + var object = webglObject.object; + + if ( object.visible ) { + + setupMatrices( object, camera ); + + unrollImmediateBufferMaterial( webglObject ); + + } + + } + + if ( scene.overrideMaterial ) { + + var material = scene.overrideMaterial; + + this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst ); + this.setDepthTest( material.depthTest ); + this.setDepthWrite( material.depthWrite ); + setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); + + renderObjects( opaqueObjects, camera, lights, fog, true, material ); + renderObjects( transparentObjects, camera, lights, fog, true, material ); + renderObjectsImmediate( _webglObjectsImmediate, '', camera, lights, fog, false, material ); + + } else { + + var material = null; + + // opaque pass (front-to-back order) + + this.setBlending( THREE.NoBlending ); + + renderObjects( opaqueObjects, camera, lights, fog, false, material ); + renderObjectsImmediate( _webglObjectsImmediate, 'opaque', camera, lights, fog, false, material ); + + // transparent pass (back-to-front order) + + renderObjects( transparentObjects, camera, lights, fog, true, material ); + renderObjectsImmediate( _webglObjectsImmediate, 'transparent', camera, lights, fog, true, material ); + + } + + // custom render plugins (post pass) + + spritePlugin.render( scene, camera ); + lensFlarePlugin.render( scene, camera, _currentWidth, _currentHeight ); + + // Generate mipmap if we're using any kind of mipmap filtering + + if ( renderTarget && renderTarget.generateMipmaps && renderTarget.minFilter !== THREE.NearestFilter && renderTarget.minFilter !== THREE.LinearFilter ) { + + updateRenderTargetMipmap( renderTarget ); + + } + + // Ensure depth buffer writing is enabled so it can be cleared on next render + + this.setDepthTest( true ); + this.setDepthWrite( true ); + + // _gl.finish(); + + }; + + function projectObject( object ) { + + if ( object.visible === false ) return; + + if ( object instanceof THREE.Scene || object instanceof THREE.Group ) { + + // skip + + } else { + + initObject( object ); + + if ( object instanceof THREE.Light ) { + + lights.push( object ); + + } else if ( object instanceof THREE.Sprite ) { + + sprites.push( object ); + + } else if ( object instanceof THREE.LensFlare ) { + + lensFlares.push( object ); + + } else { + + var webglObjects = _webglObjects[ object.id ]; + + if ( webglObjects && ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) ) { + + for ( var i = 0, l = webglObjects.length; i < l; i ++ ) { + + var webglObject = webglObjects[i]; + + unrollBufferMaterial( webglObject ); + + webglObject.render = true; + + if ( _this.sortObjects === true ) { + + _vector3.setFromMatrixPosition( object.matrixWorld ); + _vector3.applyProjection( _projScreenMatrix ); + + webglObject.z = _vector3.z; + + } + + } + + } + + } + + } + + for ( var i = 0, l = object.children.length; i < l; i ++ ) { + + projectObject( object.children[ i ] ); + + } + + } + + function renderObjects( renderList, camera, lights, fog, useBlending, overrideMaterial ) { + + var material; + + for ( var i = 0, l = renderList.length; i < l; i ++ ) { + + var webglObject = renderList[ i ]; + + var object = webglObject.object; + var buffer = webglObject.buffer; + + setupMatrices( object, camera ); + + if ( overrideMaterial ) { + + material = overrideMaterial; + + } else { + + material = webglObject.material; + + if ( ! material ) continue; + + if ( useBlending ) _this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst ); + + _this.setDepthTest( material.depthTest ); + _this.setDepthWrite( material.depthWrite ); + setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); + + } + + _this.setMaterialFaces( material ); + + if ( buffer instanceof THREE.BufferGeometry ) { + + _this.renderBufferDirect( camera, lights, fog, material, buffer, object ); + + } else { + + _this.renderBuffer( camera, lights, fog, material, buffer, object ); + + } + + } + + } + + function renderObjectsImmediate ( renderList, materialType, camera, lights, fog, useBlending, overrideMaterial ) { + + var material; + + for ( var i = 0, l = renderList.length; i < l; i ++ ) { + + var webglObject = renderList[ i ]; + var object = webglObject.object; + + if ( object.visible ) { + + if ( overrideMaterial ) { + + material = overrideMaterial; + + } else { + + material = webglObject[ materialType ]; + + if ( ! material ) continue; + + if ( useBlending ) _this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst ); + + _this.setDepthTest( material.depthTest ); + _this.setDepthWrite( material.depthWrite ); + setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); + + } + + _this.renderImmediateObject( camera, lights, fog, material, object ); + + } + + } + + } + + this.renderImmediateObject = function ( camera, lights, fog, material, object ) { + + var program = setProgram( camera, lights, fog, material, object ); + + _currentGeometryProgram = ''; + + _this.setMaterialFaces( material ); + + if ( object.immediateRenderCallback ) { + + object.immediateRenderCallback( program, _gl, _frustum ); + + } else { + + object.render( function ( object ) { _this.renderBufferImmediate( object, program, material ); } ); + + } + + }; + + function unrollImmediateBufferMaterial ( globject ) { + + var object = globject.object, + material = object.material; + + if ( material.transparent ) { + + globject.transparent = material; + globject.opaque = null; + + } else { + + globject.opaque = material; + globject.transparent = null; + + } + + } + + function unrollBufferMaterial ( globject ) { + + var object = globject.object; + var buffer = globject.buffer; + + var geometry = object.geometry; + var material = object.material; + + if ( material instanceof THREE.MeshFaceMaterial ) { + + var materialIndex = geometry instanceof THREE.BufferGeometry ? 0 : buffer.materialIndex; + + material = material.materials[ materialIndex ]; + + globject.material = material; + + if ( material.transparent ) { + + transparentObjects.push( globject ); + + } else { + + opaqueObjects.push( globject ); + + } + + } else if ( material ) { + + globject.material = material; + + if ( material.transparent ) { + + transparentObjects.push( globject ); + + } else { + + opaqueObjects.push( globject ); + + } + + } + + } + + function initObject( object ) { + + if ( object.__webglInit === undefined ) { + + object.__webglInit = true; + object._modelViewMatrix = new THREE.Matrix4(); + object._normalMatrix = new THREE.Matrix3(); + + object.addEventListener( 'removed', onObjectRemoved ); + + } + + var geometry = object.geometry; + + if ( geometry === undefined ) { + + // ImmediateRenderObject + + } else if ( geometry.__webglInit === undefined ) { + + geometry.__webglInit = true; + geometry.addEventListener( 'dispose', onGeometryDispose ); + + if ( geometry instanceof THREE.BufferGeometry ) { + + _this.info.memory.geometries ++; + + } else if ( object instanceof THREE.Mesh ) { + + initGeometryGroups( object, geometry ); + + } else if ( object instanceof THREE.Line ) { + + if ( geometry.__webglVertexBuffer === undefined ) { + + createLineBuffers( geometry ); + initLineBuffers( geometry, object ); + + geometry.verticesNeedUpdate = true; + geometry.colorsNeedUpdate = true; + geometry.lineDistancesNeedUpdate = true; + + } + + } else if ( object instanceof THREE.PointCloud ) { + + if ( geometry.__webglVertexBuffer === undefined ) { + + createParticleBuffers( geometry ); + initParticleBuffers( geometry, object ); + + geometry.verticesNeedUpdate = true; + geometry.colorsNeedUpdate = true; + + } + + } + + } + + if ( object.__webglActive === undefined) { + + object.__webglActive = true; + + if ( object instanceof THREE.Mesh ) { + + if ( geometry instanceof THREE.BufferGeometry ) { + + addBuffer( _webglObjects, geometry, object ); + + } else if ( geometry instanceof THREE.Geometry ) { + + var geometryGroupsList = geometryGroups[ geometry.id ]; + + for ( var i = 0,l = geometryGroupsList.length; i < l; i ++ ) { + + addBuffer( _webglObjects, geometryGroupsList[ i ], object ); + + } + + } + + } else if ( object instanceof THREE.Line || object instanceof THREE.PointCloud ) { + + addBuffer( _webglObjects, geometry, object ); + + } else if ( object instanceof THREE.ImmediateRenderObject || object.immediateRenderCallback ) { + + addBufferImmediate( _webglObjectsImmediate, object ); + + } + + } + + } + + // Geometry splitting + + var geometryGroups = {}; + var geometryGroupCounter = 0; + + function makeGroups( geometry, usesFaceMaterial ) { + + var maxVerticesInGroup = extensions.get( 'OES_element_index_uint' ) ? 4294967296 : 65535; + + var groupHash, hash_map = {}; + + var numMorphTargets = geometry.morphTargets.length; + var numMorphNormals = geometry.morphNormals.length; + + var group; + var groups = {}; + var groupsList = []; + + for ( var f = 0, fl = geometry.faces.length; f < fl; f ++ ) { + + var face = geometry.faces[ f ]; + var materialIndex = usesFaceMaterial ? face.materialIndex : 0; + + if ( ! ( materialIndex in hash_map ) ) { + + hash_map[ materialIndex ] = { hash: materialIndex, counter: 0 }; + + } + + groupHash = hash_map[ materialIndex ].hash + '_' + hash_map[ materialIndex ].counter; + + if ( ! ( groupHash in groups ) ) { + + group = { + id: geometryGroupCounter ++, + faces3: [], + materialIndex: materialIndex, + vertices: 0, + numMorphTargets: numMorphTargets, + numMorphNormals: numMorphNormals + }; + + groups[ groupHash ] = group; + groupsList.push( group ); + + } + + if ( groups[ groupHash ].vertices + 3 > maxVerticesInGroup ) { + + hash_map[ materialIndex ].counter += 1; + groupHash = hash_map[ materialIndex ].hash + '_' + hash_map[ materialIndex ].counter; + + if ( ! ( groupHash in groups ) ) { + + group = { + id: geometryGroupCounter ++, + faces3: [], + materialIndex: materialIndex, + vertices: 0, + numMorphTargets: numMorphTargets, + numMorphNormals: numMorphNormals + }; + + groups[ groupHash ] = group; + groupsList.push( group ); + + } + + } + + groups[ groupHash ].faces3.push( f ); + groups[ groupHash ].vertices += 3; + + } + + return groupsList; + + } + + function initGeometryGroups( object, geometry ) { + + var material = object.material, addBuffers = false; + + if ( geometryGroups[ geometry.id ] === undefined || geometry.groupsNeedUpdate === true ) { + + delete _webglObjects[ object.id ]; + + geometryGroups[ geometry.id ] = makeGroups( geometry, material instanceof THREE.MeshFaceMaterial ); + + geometry.groupsNeedUpdate = false; + + } + + var geometryGroupsList = geometryGroups[ geometry.id ]; + + // create separate VBOs per geometry chunk + + for ( var i = 0, il = geometryGroupsList.length; i < il; i ++ ) { + + var geometryGroup = geometryGroupsList[ i ]; + + // initialise VBO on the first access + + if ( geometryGroup.__webglVertexBuffer === undefined ) { + + createMeshBuffers( geometryGroup ); + initMeshBuffers( geometryGroup, object ); + + geometry.verticesNeedUpdate = true; + geometry.morphTargetsNeedUpdate = true; + geometry.elementsNeedUpdate = true; + geometry.uvsNeedUpdate = true; + geometry.normalsNeedUpdate = true; + geometry.tangentsNeedUpdate = true; + geometry.colorsNeedUpdate = true; + + addBuffers = true; + + } else { + + addBuffers = false; + + } + + if ( addBuffers || object.__webglActive === undefined ) { + + addBuffer( _webglObjects, geometryGroup, object ); + + } + + } + + object.__webglActive = true; + + } + + function addBuffer( objlist, buffer, object ) { + + var id = object.id; + objlist[id] = objlist[id] || []; + objlist[id].push( + { + id: id, + buffer: buffer, + object: object, + material: null, + z: 0 + } + ); + + }; + + function addBufferImmediate( objlist, object ) { + + objlist.push( + { + id: null, + object: object, + opaque: null, + transparent: null, + z: 0 + } + ); + + }; + + // Objects updates + + function updateObject( object ) { + + var geometry = object.geometry; + + if ( geometry instanceof THREE.BufferGeometry ) { + + var attributes = geometry.attributes; + var attributesKeys = geometry.attributesKeys; + + for ( var i = 0, l = attributesKeys.length; i < l; i ++ ) { + + var key = attributesKeys[ i ]; + var attribute = attributes[ key ]; + + if ( attribute.buffer === undefined ) { + + attribute.buffer = _gl.createBuffer(); + attribute.needsUpdate = true; + + } + + if ( attribute.needsUpdate === true ) { + + var bufferType = ( key === 'index' ) ? _gl.ELEMENT_ARRAY_BUFFER : _gl.ARRAY_BUFFER; + + _gl.bindBuffer( bufferType, attribute.buffer ); + _gl.bufferData( bufferType, attribute.array, _gl.STATIC_DRAW ); + + attribute.needsUpdate = false; + + } + + } + + } else if ( object instanceof THREE.Mesh ) { + + // check all geometry groups + + if ( geometry.groupsNeedUpdate === true ) { + + initGeometryGroups( object, geometry ); + + } + + var geometryGroupsList = geometryGroups[ geometry.id ]; + + for ( var i = 0, il = geometryGroupsList.length; i < il; i ++ ) { + + var geometryGroup = geometryGroupsList[ i ]; + var material = getBufferMaterial( object, geometryGroup ); + + if ( geometry.groupsNeedUpdate === true ) { + + initMeshBuffers( geometryGroup, object ); + + } + + var customAttributesDirty = material.attributes && areCustomAttributesDirty( material ); + + if ( geometry.verticesNeedUpdate || geometry.morphTargetsNeedUpdate || geometry.elementsNeedUpdate || + geometry.uvsNeedUpdate || geometry.normalsNeedUpdate || + geometry.colorsNeedUpdate || geometry.tangentsNeedUpdate || customAttributesDirty ) { + + setMeshBuffers( geometryGroup, object, _gl.DYNAMIC_DRAW, ! geometry.dynamic, material ); + + } + + } + + geometry.verticesNeedUpdate = false; + geometry.morphTargetsNeedUpdate = false; + geometry.elementsNeedUpdate = false; + geometry.uvsNeedUpdate = false; + geometry.normalsNeedUpdate = false; + geometry.colorsNeedUpdate = false; + geometry.tangentsNeedUpdate = false; + + material.attributes && clearCustomAttributes( material ); + + } else if ( object instanceof THREE.Line ) { + + var material = getBufferMaterial( object, geometry ); + var customAttributesDirty = material.attributes && areCustomAttributesDirty( material ); + + if ( geometry.verticesNeedUpdate || geometry.colorsNeedUpdate || geometry.lineDistancesNeedUpdate || customAttributesDirty ) { + + setLineBuffers( geometry, _gl.DYNAMIC_DRAW ); + + } + + geometry.verticesNeedUpdate = false; + geometry.colorsNeedUpdate = false; + geometry.lineDistancesNeedUpdate = false; + + material.attributes && clearCustomAttributes( material ); + + } else if ( object instanceof THREE.PointCloud ) { + + var material = getBufferMaterial( object, geometry ); + var customAttributesDirty = material.attributes && areCustomAttributesDirty( material ); + + if ( geometry.verticesNeedUpdate || geometry.colorsNeedUpdate || customAttributesDirty ) { + + setParticleBuffers( geometry, _gl.DYNAMIC_DRAW, object ); + + } + + geometry.verticesNeedUpdate = false; + geometry.colorsNeedUpdate = false; + + material.attributes && clearCustomAttributes( material ); + + } + + } + + // Objects updates - custom attributes check + + function areCustomAttributesDirty( material ) { + + for ( var name in material.attributes ) { + + if ( material.attributes[ name ].needsUpdate ) return true; + + } + + return false; + + } + + function clearCustomAttributes( material ) { + + for ( var name in material.attributes ) { + + material.attributes[ name ].needsUpdate = false; + + } + + } + + // Objects removal + + function removeObject( object ) { + + if ( object instanceof THREE.Mesh || + object instanceof THREE.PointCloud || + object instanceof THREE.Line ) { + + delete _webglObjects[ object.id ]; + + } else if ( object instanceof THREE.ImmediateRenderObject || object.immediateRenderCallback ) { + + removeInstances( _webglObjectsImmediate, object ); + + } + + delete object.__webglInit; + delete object._modelViewMatrix; + delete object._normalMatrix; + + delete object.__webglActive; + + } + + function removeInstances( objlist, object ) { + + for ( var o = objlist.length - 1; o >= 0; o -- ) { + + if ( objlist[ o ].object === object ) { + + objlist.splice( o, 1 ); + + } + + } + + } + + // Materials + + var shaderIDs = { + MeshDepthMaterial: 'depth', + MeshNormalMaterial: 'normal', + MeshBasicMaterial: 'basic', + MeshLambertMaterial: 'lambert', + MeshPhongMaterial: 'phong', + LineBasicMaterial: 'basic', + LineDashedMaterial: 'dashed', + PointCloudMaterial: 'particle_basic' + }; + + function initMaterial( material, lights, fog, object ) { + + material.addEventListener( 'dispose', onMaterialDispose ); + + var shaderID = shaderIDs[ material.type ]; + + if ( shaderID ) { + + var shader = THREE.ShaderLib[ shaderID ]; + + material.__webglShader = { + uniforms: THREE.UniformsUtils.clone( shader.uniforms ), + vertexShader: shader.vertexShader, + fragmentShader: shader.fragmentShader + } + + } else { + + material.__webglShader = { + uniforms: material.uniforms, + vertexShader: material.vertexShader, + fragmentShader: material.fragmentShader + } + + } + + // heuristics to create shader parameters according to lights in the scene + // (not to blow over maxLights budget) + + var maxLightCount = allocateLights( lights ); + var maxShadows = allocateShadows( lights ); + var maxBones = allocateBones( object ); + + var parameters = { + + precision: _precision, + supportsVertexTextures: _supportsVertexTextures, + + map: !! material.map, + envMap: !! material.envMap, + envMapMode: material.envMap && material.envMap.mapping, + lightMap: !! material.lightMap, + bumpMap: !! material.bumpMap, + normalMap: !! material.normalMap, + specularMap: !! material.specularMap, + alphaMap: !! material.alphaMap, + + combine: material.combine, + + vertexColors: material.vertexColors, + + fog: fog, + useFog: material.fog, + fogExp: fog instanceof THREE.FogExp2, + + sizeAttenuation: material.sizeAttenuation, + logarithmicDepthBuffer: _logarithmicDepthBuffer, + + skinning: material.skinning, + maxBones: maxBones, + useVertexTexture: _supportsBoneTextures && object && object.skeleton && object.skeleton.useVertexTexture, + + morphTargets: material.morphTargets, + morphNormals: material.morphNormals, + maxMorphTargets: _this.maxMorphTargets, + maxMorphNormals: _this.maxMorphNormals, + + maxDirLights: maxLightCount.directional, + maxPointLights: maxLightCount.point, + maxSpotLights: maxLightCount.spot, + maxHemiLights: maxLightCount.hemi, + + maxShadows: maxShadows, + shadowMapEnabled: _this.shadowMapEnabled && object.receiveShadow && maxShadows > 0, + shadowMapType: _this.shadowMapType, + shadowMapDebug: _this.shadowMapDebug, + shadowMapCascade: _this.shadowMapCascade, + + alphaTest: material.alphaTest, + metal: material.metal, + wrapAround: material.wrapAround, + doubleSided: material.side === THREE.DoubleSide, + flipSided: material.side === THREE.BackSide + + }; + + // Generate code + + var chunks = []; + + if ( shaderID ) { + + chunks.push( shaderID ); + + } else { + + chunks.push( material.fragmentShader ); + chunks.push( material.vertexShader ); + + } + + if ( material.defines !== undefined ) { + + for ( var name in material.defines ) { + + chunks.push( name ); + chunks.push( material.defines[ name ] ); + + } + + } + + for ( var name in parameters ) { + + chunks.push( name ); + chunks.push( parameters[ name ] ); + + } + + var code = chunks.join(); + + var program; + + // Check if code has been already compiled + + for ( var p = 0, pl = _programs.length; p < pl; p ++ ) { + + var programInfo = _programs[ p ]; + + if ( programInfo.code === code ) { + + program = programInfo; + program.usedTimes ++; + + break; + + } + + } + + if ( program === undefined ) { + + program = new THREE.WebGLProgram( _this, code, material, parameters ); + _programs.push( program ); + + _this.info.memory.programs = _programs.length; + + } + + material.program = program; + + var attributes = program.attributes; + + if ( material.morphTargets ) { + + material.numSupportedMorphTargets = 0; + + var id, base = 'morphTarget'; + + for ( var i = 0; i < _this.maxMorphTargets; i ++ ) { + + id = base + i; + + if ( attributes[ id ] >= 0 ) { + + material.numSupportedMorphTargets ++; + + } + + } + + } + + if ( material.morphNormals ) { + + material.numSupportedMorphNormals = 0; + + var id, base = 'morphNormal'; + + for ( i = 0; i < _this.maxMorphNormals; i ++ ) { + + id = base + i; + + if ( attributes[ id ] >= 0 ) { + + material.numSupportedMorphNormals ++; + + } + + } + + } + + material.uniformsList = []; + + for ( var u in material.__webglShader.uniforms ) { + + var location = material.program.uniforms[ u ]; + + if ( location ) { + material.uniformsList.push( [ material.__webglShader.uniforms[ u ], location ] ); + } + + } + + } + + function setProgram( camera, lights, fog, material, object ) { + + _usedTextureUnits = 0; + + if ( material.needsUpdate ) { + + if ( material.program ) deallocateMaterial( material ); + + initMaterial( material, lights, fog, object ); + material.needsUpdate = false; + + } + + if ( material.morphTargets ) { + + if ( ! object.__webglMorphTargetInfluences ) { + + object.__webglMorphTargetInfluences = new Float32Array( _this.maxMorphTargets ); + + } + + } + + var refreshProgram = false; + var refreshMaterial = false; + var refreshLights = false; + + var program = material.program, + p_uniforms = program.uniforms, + m_uniforms = material.__webglShader.uniforms; + + if ( program.id !== _currentProgram ) { + + _gl.useProgram( program.program ); + _currentProgram = program.id; + + refreshProgram = true; + refreshMaterial = true; + refreshLights = true; + + } + + if ( material.id !== _currentMaterialId ) { + + if ( _currentMaterialId === -1 ) refreshLights = true; + _currentMaterialId = material.id; + + refreshMaterial = true; + + } + + if ( refreshProgram || camera !== _currentCamera ) { + + _gl.uniformMatrix4fv( p_uniforms.projectionMatrix, false, camera.projectionMatrix.elements ); + + if ( _logarithmicDepthBuffer ) { + + _gl.uniform1f( p_uniforms.logDepthBufFC, 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) ); + + } + + + if ( camera !== _currentCamera ) _currentCamera = camera; + + // load material specific uniforms + // (shader material also gets them for the sake of genericity) + + if ( material instanceof THREE.ShaderMaterial || + material instanceof THREE.MeshPhongMaterial || + material.envMap ) { + + if ( p_uniforms.cameraPosition !== null ) { + + _vector3.setFromMatrixPosition( camera.matrixWorld ); + _gl.uniform3f( p_uniforms.cameraPosition, _vector3.x, _vector3.y, _vector3.z ); + + } + + } + + if ( material instanceof THREE.MeshPhongMaterial || + material instanceof THREE.MeshLambertMaterial || + material instanceof THREE.MeshBasicMaterial || + material instanceof THREE.ShaderMaterial || + material.skinning ) { + + if ( p_uniforms.viewMatrix !== null ) { + + _gl.uniformMatrix4fv( p_uniforms.viewMatrix, false, camera.matrixWorldInverse.elements ); + + } + + } + + } + + // skinning uniforms must be set even if material didn't change + // auto-setting of texture unit for bone texture must go before other textures + // not sure why, but otherwise weird things happen + + if ( material.skinning ) { + + if ( object.bindMatrix && p_uniforms.bindMatrix !== null ) { + + _gl.uniformMatrix4fv( p_uniforms.bindMatrix, false, object.bindMatrix.elements ); + + } + + if ( object.bindMatrixInverse && p_uniforms.bindMatrixInverse !== null ) { + + _gl.uniformMatrix4fv( p_uniforms.bindMatrixInverse, false, object.bindMatrixInverse.elements ); + + } + + if ( _supportsBoneTextures && object.skeleton && object.skeleton.useVertexTexture ) { + + if ( p_uniforms.boneTexture !== null ) { + + var textureUnit = getTextureUnit(); + + _gl.uniform1i( p_uniforms.boneTexture, textureUnit ); + _this.setTexture( object.skeleton.boneTexture, textureUnit ); + + } + + if ( p_uniforms.boneTextureWidth !== null ) { + + _gl.uniform1i( p_uniforms.boneTextureWidth, object.skeleton.boneTextureWidth ); + + } + + if ( p_uniforms.boneTextureHeight !== null ) { + + _gl.uniform1i( p_uniforms.boneTextureHeight, object.skeleton.boneTextureHeight ); + + } + + } else if ( object.skeleton && object.skeleton.boneMatrices ) { + + if ( p_uniforms.boneGlobalMatrices !== null ) { + + _gl.uniformMatrix4fv( p_uniforms.boneGlobalMatrices, false, object.skeleton.boneMatrices ); + + } + + } + + } + + if ( refreshMaterial ) { + + // refresh uniforms common to several materials + + if ( fog && material.fog ) { + + refreshUniformsFog( m_uniforms, fog ); + + } + + if ( material instanceof THREE.MeshPhongMaterial || + material instanceof THREE.MeshLambertMaterial || + material.lights ) { + + if ( _lightsNeedUpdate ) { + + refreshLights = true; + setupLights( lights ); + _lightsNeedUpdate = false; + } + + if ( refreshLights ) { + refreshUniformsLights( m_uniforms, _lights ); + markUniformsLightsNeedsUpdate( m_uniforms, true ); + } else { + markUniformsLightsNeedsUpdate( m_uniforms, false ); + } + + } + + if ( material instanceof THREE.MeshBasicMaterial || + material instanceof THREE.MeshLambertMaterial || + material instanceof THREE.MeshPhongMaterial ) { + + refreshUniformsCommon( m_uniforms, material ); + + } + + // refresh single material specific uniforms + + if ( material instanceof THREE.LineBasicMaterial ) { + + refreshUniformsLine( m_uniforms, material ); + + } else if ( material instanceof THREE.LineDashedMaterial ) { + + refreshUniformsLine( m_uniforms, material ); + refreshUniformsDash( m_uniforms, material ); + + } else if ( material instanceof THREE.PointCloudMaterial ) { + + refreshUniformsParticle( m_uniforms, material ); + + } else if ( material instanceof THREE.MeshPhongMaterial ) { + + refreshUniformsPhong( m_uniforms, material ); + + } else if ( material instanceof THREE.MeshLambertMaterial ) { + + refreshUniformsLambert( m_uniforms, material ); + + } else if ( material instanceof THREE.MeshDepthMaterial ) { + + m_uniforms.mNear.value = camera.near; + m_uniforms.mFar.value = camera.far; + m_uniforms.opacity.value = material.opacity; + + } else if ( material instanceof THREE.MeshNormalMaterial ) { + + m_uniforms.opacity.value = material.opacity; + + } + + if ( object.receiveShadow && ! material._shadowPass ) { + + refreshUniformsShadow( m_uniforms, lights ); + + } + + // load common uniforms + + loadUniformsGeneric( material.uniformsList ); + + } + + loadUniformsMatrices( p_uniforms, object ); + + if ( p_uniforms.modelMatrix !== null ) { + + _gl.uniformMatrix4fv( p_uniforms.modelMatrix, false, object.matrixWorld.elements ); + + } + + return program; + + } + + // Uniforms (refresh uniforms objects) + + function refreshUniformsCommon ( uniforms, material ) { + + uniforms.opacity.value = material.opacity; + + if ( _this.gammaInput ) { + + uniforms.diffuse.value.copyGammaToLinear( material.color ); + + } else { + + uniforms.diffuse.value = material.color; + + } + + uniforms.map.value = material.map; + uniforms.lightMap.value = material.lightMap; + uniforms.specularMap.value = material.specularMap; + uniforms.alphaMap.value = material.alphaMap; + + if ( material.bumpMap ) { + + uniforms.bumpMap.value = material.bumpMap; + uniforms.bumpScale.value = material.bumpScale; + + } + + if ( material.normalMap ) { + + uniforms.normalMap.value = material.normalMap; + uniforms.normalScale.value.copy( material.normalScale ); + + } + + // uv repeat and offset setting priorities + // 1. color map + // 2. specular map + // 3. normal map + // 4. bump map + // 5. alpha map + + var uvScaleMap; + + if ( material.map ) { + + uvScaleMap = material.map; + + } else if ( material.specularMap ) { + + uvScaleMap = material.specularMap; + + } else if ( material.normalMap ) { + + uvScaleMap = material.normalMap; + + } else if ( material.bumpMap ) { + + uvScaleMap = material.bumpMap; + + } else if ( material.alphaMap ) { + + uvScaleMap = material.alphaMap; + + } + + if ( uvScaleMap !== undefined ) { + + var offset = uvScaleMap.offset; + var repeat = uvScaleMap.repeat; + + uniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y ); + + } + + uniforms.envMap.value = material.envMap; + uniforms.flipEnvMap.value = ( material.envMap instanceof THREE.WebGLRenderTargetCube ) ? 1 : - 1; + + if ( _this.gammaInput ) { + + //uniforms.reflectivity.value = material.reflectivity * material.reflectivity; + uniforms.reflectivity.value = material.reflectivity; + + } else { + + uniforms.reflectivity.value = material.reflectivity; + + } + + uniforms.refractionRatio.value = material.refractionRatio; + + } + + function refreshUniformsLine ( uniforms, material ) { + + uniforms.diffuse.value = material.color; + uniforms.opacity.value = material.opacity; + + } + + function refreshUniformsDash ( uniforms, material ) { + + uniforms.dashSize.value = material.dashSize; + uniforms.totalSize.value = material.dashSize + material.gapSize; + uniforms.scale.value = material.scale; + + } + + function refreshUniformsParticle ( uniforms, material ) { + + uniforms.psColor.value = material.color; + uniforms.opacity.value = material.opacity; + uniforms.size.value = material.size; + uniforms.scale.value = _canvas.height / 2.0; // TODO: Cache this. + + uniforms.map.value = material.map; + + } + + function refreshUniformsFog ( uniforms, fog ) { + + uniforms.fogColor.value = fog.color; + + if ( fog instanceof THREE.Fog ) { + + uniforms.fogNear.value = fog.near; + uniforms.fogFar.value = fog.far; + + } else if ( fog instanceof THREE.FogExp2 ) { + + uniforms.fogDensity.value = fog.density; + + } + + } + + function refreshUniformsPhong ( uniforms, material ) { + + uniforms.shininess.value = material.shininess; + + if ( _this.gammaInput ) { + + uniforms.ambient.value.copyGammaToLinear( material.ambient ); + uniforms.emissive.value.copyGammaToLinear( material.emissive ); + uniforms.specular.value.copyGammaToLinear( material.specular ); + + } else { + + uniforms.ambient.value = material.ambient; + uniforms.emissive.value = material.emissive; + uniforms.specular.value = material.specular; + + } + + if ( material.wrapAround ) { + + uniforms.wrapRGB.value.copy( material.wrapRGB ); + + } + + } + + function refreshUniformsLambert ( uniforms, material ) { + + if ( _this.gammaInput ) { + + uniforms.ambient.value.copyGammaToLinear( material.ambient ); + uniforms.emissive.value.copyGammaToLinear( material.emissive ); + + } else { + + uniforms.ambient.value = material.ambient; + uniforms.emissive.value = material.emissive; + + } + + if ( material.wrapAround ) { + + uniforms.wrapRGB.value.copy( material.wrapRGB ); + + } + + } + + function refreshUniformsLights ( uniforms, lights ) { + + uniforms.ambientLightColor.value = lights.ambient; + + uniforms.directionalLightColor.value = lights.directional.colors; + uniforms.directionalLightDirection.value = lights.directional.positions; + + uniforms.pointLightColor.value = lights.point.colors; + uniforms.pointLightPosition.value = lights.point.positions; + uniforms.pointLightDistance.value = lights.point.distances; + + uniforms.spotLightColor.value = lights.spot.colors; + uniforms.spotLightPosition.value = lights.spot.positions; + uniforms.spotLightDistance.value = lights.spot.distances; + uniforms.spotLightDirection.value = lights.spot.directions; + uniforms.spotLightAngleCos.value = lights.spot.anglesCos; + uniforms.spotLightExponent.value = lights.spot.exponents; + + uniforms.hemisphereLightSkyColor.value = lights.hemi.skyColors; + uniforms.hemisphereLightGroundColor.value = lights.hemi.groundColors; + uniforms.hemisphereLightDirection.value = lights.hemi.positions; + + } + + // If uniforms are marked as clean, they don't need to be loaded to the GPU. + + function markUniformsLightsNeedsUpdate ( uniforms, boolean ) { + + uniforms.ambientLightColor.needsUpdate = boolean; + + uniforms.directionalLightColor.needsUpdate = boolean; + uniforms.directionalLightDirection.needsUpdate = boolean; + + uniforms.pointLightColor.needsUpdate = boolean; + uniforms.pointLightPosition.needsUpdate = boolean; + uniforms.pointLightDistance.needsUpdate = boolean; + + uniforms.spotLightColor.needsUpdate = boolean; + uniforms.spotLightPosition.needsUpdate = boolean; + uniforms.spotLightDistance.needsUpdate = boolean; + uniforms.spotLightDirection.needsUpdate = boolean; + uniforms.spotLightAngleCos.needsUpdate = boolean; + uniforms.spotLightExponent.needsUpdate = boolean; + + uniforms.hemisphereLightSkyColor.needsUpdate = boolean; + uniforms.hemisphereLightGroundColor.needsUpdate = boolean; + uniforms.hemisphereLightDirection.needsUpdate = boolean; + + } + + function refreshUniformsShadow ( uniforms, lights ) { + + if ( uniforms.shadowMatrix ) { + + var j = 0; + + for ( var i = 0, il = lights.length; i < il; i ++ ) { + + var light = lights[ i ]; + + if ( ! light.castShadow ) continue; + + if ( light instanceof THREE.SpotLight || ( light instanceof THREE.DirectionalLight && ! light.shadowCascade ) ) { + + uniforms.shadowMap.value[ j ] = light.shadowMap; + uniforms.shadowMapSize.value[ j ] = light.shadowMapSize; + + uniforms.shadowMatrix.value[ j ] = light.shadowMatrix; + + uniforms.shadowDarkness.value[ j ] = light.shadowDarkness; + uniforms.shadowBias.value[ j ] = light.shadowBias; + + j ++; + + } + + } + + } + + } + + // Uniforms (load to GPU) + + function loadUniformsMatrices ( uniforms, object ) { + + _gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, object._modelViewMatrix.elements ); + + if ( uniforms.normalMatrix ) { + + _gl.uniformMatrix3fv( uniforms.normalMatrix, false, object._normalMatrix.elements ); + + } + + } + + function getTextureUnit() { + + var textureUnit = _usedTextureUnits; + + if ( textureUnit >= _maxTextures ) { + + console.warn( 'WebGLRenderer: trying to use ' + textureUnit + ' texture units while this GPU supports only ' + _maxTextures ); + + } + + _usedTextureUnits += 1; + + return textureUnit; + + } + + function loadUniformsGeneric ( uniforms ) { + + var texture, textureUnit, offset; + + for ( var j = 0, jl = uniforms.length; j < jl; j ++ ) { + + var uniform = uniforms[ j ][ 0 ]; + + // needsUpdate property is not added to all uniforms. + if ( uniform.needsUpdate === false ) continue; + + var type = uniform.type; + var value = uniform.value; + var location = uniforms[ j ][ 1 ]; + + switch ( type ) { + + case '1i': + _gl.uniform1i( location, value ); + break; + + case '1f': + _gl.uniform1f( location, value ); + break; + + case '2f': + _gl.uniform2f( location, value[ 0 ], value[ 1 ] ); + break; + + case '3f': + _gl.uniform3f( location, value[ 0 ], value[ 1 ], value[ 2 ] ); + break; + + case '4f': + _gl.uniform4f( location, value[ 0 ], value[ 1 ], value[ 2 ], value[ 3 ] ); + break; + + case '1iv': + _gl.uniform1iv( location, value ); + break; + + case '3iv': + _gl.uniform3iv( location, value ); + break; + + case '1fv': + _gl.uniform1fv( location, value ); + break; + + case '2fv': + _gl.uniform2fv( location, value ); + break; + + case '3fv': + _gl.uniform3fv( location, value ); + break; + + case '4fv': + _gl.uniform4fv( location, value ); + break; + + case 'Matrix3fv': + _gl.uniformMatrix3fv( location, false, value ); + break; + + case 'Matrix4fv': + _gl.uniformMatrix4fv( location, false, value ); + break; + + // + + case 'i': + + // single integer + _gl.uniform1i( location, value ); + + break; + + case 'f': + + // single float + _gl.uniform1f( location, value ); + + break; + + case 'v2': + + // single THREE.Vector2 + _gl.uniform2f( location, value.x, value.y ); + + break; + + case 'v3': + + // single THREE.Vector3 + _gl.uniform3f( location, value.x, value.y, value.z ); + + break; + + case 'v4': + + // single THREE.Vector4 + _gl.uniform4f( location, value.x, value.y, value.z, value.w ); + + break; + + case 'c': + + // single THREE.Color + _gl.uniform3f( location, value.r, value.g, value.b ); + + break; + + case 'iv1': + + // flat array of integers (JS or typed array) + _gl.uniform1iv( location, value ); + + break; + + case 'iv': + + // flat array of integers with 3 x N size (JS or typed array) + _gl.uniform3iv( location, value ); + + break; + + case 'fv1': + + // flat array of floats (JS or typed array) + _gl.uniform1fv( location, value ); + + break; + + case 'fv': + + // flat array of floats with 3 x N size (JS or typed array) + _gl.uniform3fv( location, value ); + + break; + + case 'v2v': + + // array of THREE.Vector2 + + if ( uniform._array === undefined ) { + + uniform._array = new Float32Array( 2 * value.length ); + + } + + for ( var i = 0, il = value.length; i < il; i ++ ) { + + offset = i * 2; + + uniform._array[ offset ] = value[ i ].x; + uniform._array[ offset + 1 ] = value[ i ].y; + + } + + _gl.uniform2fv( location, uniform._array ); + + break; + + case 'v3v': + + // array of THREE.Vector3 + + if ( uniform._array === undefined ) { + + uniform._array = new Float32Array( 3 * value.length ); + + } + + for ( var i = 0, il = value.length; i < il; i ++ ) { + + offset = i * 3; + + uniform._array[ offset ] = value[ i ].x; + uniform._array[ offset + 1 ] = value[ i ].y; + uniform._array[ offset + 2 ] = value[ i ].z; + + } + + _gl.uniform3fv( location, uniform._array ); + + break; + + case 'v4v': + + // array of THREE.Vector4 + + if ( uniform._array === undefined ) { + + uniform._array = new Float32Array( 4 * value.length ); + + } + + for ( var i = 0, il = value.length; i < il; i ++ ) { + + offset = i * 4; + + uniform._array[ offset ] = value[ i ].x; + uniform._array[ offset + 1 ] = value[ i ].y; + uniform._array[ offset + 2 ] = value[ i ].z; + uniform._array[ offset + 3 ] = value[ i ].w; + + } + + _gl.uniform4fv( location, uniform._array ); + + break; + + case 'm3': + + // single THREE.Matrix3 + _gl.uniformMatrix3fv( location, false, value.elements ); + + break; + + case 'm3v': + + // array of THREE.Matrix3 + + if ( uniform._array === undefined ) { + + uniform._array = new Float32Array( 9 * value.length ); + + } + + for ( var i = 0, il = value.length; i < il; i ++ ) { + + value[ i ].flattenToArrayOffset( uniform._array, i * 9 ); + + } + + _gl.uniformMatrix3fv( location, false, uniform._array ); + + break; + + case 'm4': + + // single THREE.Matrix4 + _gl.uniformMatrix4fv( location, false, value.elements ); + + break; + + case 'm4v': + + // array of THREE.Matrix4 + + if ( uniform._array === undefined ) { + + uniform._array = new Float32Array( 16 * value.length ); + + } + + for ( var i = 0, il = value.length; i < il; i ++ ) { + + value[ i ].flattenToArrayOffset( uniform._array, i * 16 ); + + } + + _gl.uniformMatrix4fv( location, false, uniform._array ); + + break; + + case 't': + + // single THREE.Texture (2d or cube) + + texture = value; + textureUnit = getTextureUnit(); + + _gl.uniform1i( location, textureUnit ); + + if ( ! texture ) continue; + + if ( texture instanceof THREE.CubeTexture || + ( texture.image instanceof Array && texture.image.length === 6 ) ) { // CompressedTexture can have Array in image :/ + + setCubeTexture( texture, textureUnit ); + + } else if ( texture instanceof THREE.WebGLRenderTargetCube ) { + + setCubeTextureDynamic( texture, textureUnit ); + + } else { + + _this.setTexture( texture, textureUnit ); + + } + + break; + + case 'tv': + + // array of THREE.Texture (2d) + + if ( uniform._array === undefined ) { + + uniform._array = []; + + } + + for ( var i = 0, il = uniform.value.length; i < il; i ++ ) { + + uniform._array[ i ] = getTextureUnit(); + + } + + _gl.uniform1iv( location, uniform._array ); + + for ( var i = 0, il = uniform.value.length; i < il; i ++ ) { + + texture = uniform.value[ i ]; + textureUnit = uniform._array[ i ]; + + if ( ! texture ) continue; + + _this.setTexture( texture, textureUnit ); + + } + + break; + + default: + + console.warn( 'THREE.WebGLRenderer: Unknown uniform type: ' + type ); + + } + + } + + } + + function setupMatrices ( object, camera ) { + + object._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); + object._normalMatrix.getNormalMatrix( object._modelViewMatrix ); + + } + + // + + function setColorGamma( array, offset, color, intensitySq ) { + + array[ offset ] = color.r * color.r * intensitySq; + array[ offset + 1 ] = color.g * color.g * intensitySq; + array[ offset + 2 ] = color.b * color.b * intensitySq; + + } + + function setColorLinear( array, offset, color, intensity ) { + + array[ offset ] = color.r * intensity; + array[ offset + 1 ] = color.g * intensity; + array[ offset + 2 ] = color.b * intensity; + + } + + function setupLights ( lights ) { + + var l, ll, light, n, + r = 0, g = 0, b = 0, + color, skyColor, groundColor, + intensity, intensitySq, + position, + distance, + + zlights = _lights, + + dirColors = zlights.directional.colors, + dirPositions = zlights.directional.positions, + + pointColors = zlights.point.colors, + pointPositions = zlights.point.positions, + pointDistances = zlights.point.distances, + + spotColors = zlights.spot.colors, + spotPositions = zlights.spot.positions, + spotDistances = zlights.spot.distances, + spotDirections = zlights.spot.directions, + spotAnglesCos = zlights.spot.anglesCos, + spotExponents = zlights.spot.exponents, + + hemiSkyColors = zlights.hemi.skyColors, + hemiGroundColors = zlights.hemi.groundColors, + hemiPositions = zlights.hemi.positions, + + dirLength = 0, + pointLength = 0, + spotLength = 0, + hemiLength = 0, + + dirCount = 0, + pointCount = 0, + spotCount = 0, + hemiCount = 0, + + dirOffset = 0, + pointOffset = 0, + spotOffset = 0, + hemiOffset = 0; + + for ( l = 0, ll = lights.length; l < ll; l ++ ) { + + light = lights[ l ]; + + if ( light.onlyShadow ) continue; + + color = light.color; + intensity = light.intensity; + distance = light.distance; + + if ( light instanceof THREE.AmbientLight ) { + + if ( ! light.visible ) continue; + + if ( _this.gammaInput ) { + + r += color.r * color.r; + g += color.g * color.g; + b += color.b * color.b; + + } else { + + r += color.r; + g += color.g; + b += color.b; + + } + + } else if ( light instanceof THREE.DirectionalLight ) { + + dirCount += 1; + + if ( ! light.visible ) continue; + + _direction.setFromMatrixPosition( light.matrixWorld ); + _vector3.setFromMatrixPosition( light.target.matrixWorld ); + _direction.sub( _vector3 ); + _direction.normalize(); + + dirOffset = dirLength * 3; + + dirPositions[ dirOffset ] = _direction.x; + dirPositions[ dirOffset + 1 ] = _direction.y; + dirPositions[ dirOffset + 2 ] = _direction.z; + + if ( _this.gammaInput ) { + + setColorGamma( dirColors, dirOffset, color, intensity * intensity ); + + } else { + + setColorLinear( dirColors, dirOffset, color, intensity ); + + } + + dirLength += 1; + + } else if ( light instanceof THREE.PointLight ) { + + pointCount += 1; + + if ( ! light.visible ) continue; + + pointOffset = pointLength * 3; + + if ( _this.gammaInput ) { + + setColorGamma( pointColors, pointOffset, color, intensity * intensity ); + + } else { + + setColorLinear( pointColors, pointOffset, color, intensity ); + + } + + _vector3.setFromMatrixPosition( light.matrixWorld ); + + pointPositions[ pointOffset ] = _vector3.x; + pointPositions[ pointOffset + 1 ] = _vector3.y; + pointPositions[ pointOffset + 2 ] = _vector3.z; + + pointDistances[ pointLength ] = distance; + + pointLength += 1; + + } else if ( light instanceof THREE.SpotLight ) { + + spotCount += 1; + + if ( ! light.visible ) continue; + + spotOffset = spotLength * 3; + + if ( _this.gammaInput ) { + + setColorGamma( spotColors, spotOffset, color, intensity * intensity ); + + } else { + + setColorLinear( spotColors, spotOffset, color, intensity ); + + } + + _direction.setFromMatrixPosition( light.matrixWorld ); + + spotPositions[ spotOffset ] = _direction.x; + spotPositions[ spotOffset + 1 ] = _direction.y; + spotPositions[ spotOffset + 2 ] = _direction.z; + + spotDistances[ spotLength ] = distance; + + _vector3.setFromMatrixPosition( light.target.matrixWorld ); + _direction.sub( _vector3 ); + _direction.normalize(); + + spotDirections[ spotOffset ] = _direction.x; + spotDirections[ spotOffset + 1 ] = _direction.y; + spotDirections[ spotOffset + 2 ] = _direction.z; + + spotAnglesCos[ spotLength ] = Math.cos( light.angle ); + spotExponents[ spotLength ] = light.exponent; + + spotLength += 1; + + } else if ( light instanceof THREE.HemisphereLight ) { + + hemiCount += 1; + + if ( ! light.visible ) continue; + + _direction.setFromMatrixPosition( light.matrixWorld ); + _direction.normalize(); + + hemiOffset = hemiLength * 3; + + hemiPositions[ hemiOffset ] = _direction.x; + hemiPositions[ hemiOffset + 1 ] = _direction.y; + hemiPositions[ hemiOffset + 2 ] = _direction.z; + + skyColor = light.color; + groundColor = light.groundColor; + + if ( _this.gammaInput ) { + + intensitySq = intensity * intensity; + + setColorGamma( hemiSkyColors, hemiOffset, skyColor, intensitySq ); + setColorGamma( hemiGroundColors, hemiOffset, groundColor, intensitySq ); + + } else { + + setColorLinear( hemiSkyColors, hemiOffset, skyColor, intensity ); + setColorLinear( hemiGroundColors, hemiOffset, groundColor, intensity ); + + } + + hemiLength += 1; + + } + + } + + // null eventual remains from removed lights + // (this is to avoid if in shader) + + for ( l = dirLength * 3, ll = Math.max( dirColors.length, dirCount * 3 ); l < ll; l ++ ) dirColors[ l ] = 0.0; + for ( l = pointLength * 3, ll = Math.max( pointColors.length, pointCount * 3 ); l < ll; l ++ ) pointColors[ l ] = 0.0; + for ( l = spotLength * 3, ll = Math.max( spotColors.length, spotCount * 3 ); l < ll; l ++ ) spotColors[ l ] = 0.0; + for ( l = hemiLength * 3, ll = Math.max( hemiSkyColors.length, hemiCount * 3 ); l < ll; l ++ ) hemiSkyColors[ l ] = 0.0; + for ( l = hemiLength * 3, ll = Math.max( hemiGroundColors.length, hemiCount * 3 ); l < ll; l ++ ) hemiGroundColors[ l ] = 0.0; + + zlights.directional.length = dirLength; + zlights.point.length = pointLength; + zlights.spot.length = spotLength; + zlights.hemi.length = hemiLength; + + zlights.ambient[ 0 ] = r; + zlights.ambient[ 1 ] = g; + zlights.ambient[ 2 ] = b; + + } + + // GL state setting + + this.setFaceCulling = function ( cullFace, frontFaceDirection ) { + + if ( cullFace === THREE.CullFaceNone ) { + + _gl.disable( _gl.CULL_FACE ); + + } else { + + if ( frontFaceDirection === THREE.FrontFaceDirectionCW ) { + + _gl.frontFace( _gl.CW ); + + } else { + + _gl.frontFace( _gl.CCW ); + + } + + if ( cullFace === THREE.CullFaceBack ) { + + _gl.cullFace( _gl.BACK ); + + } else if ( cullFace === THREE.CullFaceFront ) { + + _gl.cullFace( _gl.FRONT ); + + } else { + + _gl.cullFace( _gl.FRONT_AND_BACK ); + + } + + _gl.enable( _gl.CULL_FACE ); + + } + + }; + + this.setMaterialFaces = function ( material ) { + + var doubleSided = material.side === THREE.DoubleSide; + var flipSided = material.side === THREE.BackSide; + + if ( _oldDoubleSided !== doubleSided ) { + + if ( doubleSided ) { + + _gl.disable( _gl.CULL_FACE ); + + } else { + + _gl.enable( _gl.CULL_FACE ); + + } + + _oldDoubleSided = doubleSided; + + } + + if ( _oldFlipSided !== flipSided ) { + + if ( flipSided ) { + + _gl.frontFace( _gl.CW ); + + } else { + + _gl.frontFace( _gl.CCW ); + + } + + _oldFlipSided = flipSided; + + } + + }; + + this.setDepthTest = function ( depthTest ) { + + if ( _oldDepthTest !== depthTest ) { + + if ( depthTest ) { + + _gl.enable( _gl.DEPTH_TEST ); + + } else { + + _gl.disable( _gl.DEPTH_TEST ); + + } + + _oldDepthTest = depthTest; + + } + + }; + + this.setDepthWrite = function ( depthWrite ) { + + if ( _oldDepthWrite !== depthWrite ) { + + _gl.depthMask( depthWrite ); + _oldDepthWrite = depthWrite; + + } + + }; + + function setLineWidth ( width ) { + + width *= pixelRatio; + + if ( width !== _oldLineWidth ) { + + _gl.lineWidth( width ); + + _oldLineWidth = width; + + } + + } + + function setPolygonOffset ( polygonoffset, factor, units ) { + + if ( _oldPolygonOffset !== polygonoffset ) { + + if ( polygonoffset ) { + + _gl.enable( _gl.POLYGON_OFFSET_FILL ); + + } else { + + _gl.disable( _gl.POLYGON_OFFSET_FILL ); + + } + + _oldPolygonOffset = polygonoffset; + + } + + if ( polygonoffset && ( _oldPolygonOffsetFactor !== factor || _oldPolygonOffsetUnits !== units ) ) { + + _gl.polygonOffset( factor, units ); + + _oldPolygonOffsetFactor = factor; + _oldPolygonOffsetUnits = units; + + } + + } + + this.setBlending = function ( blending, blendEquation, blendSrc, blendDst ) { + + if ( blending !== _oldBlending ) { + + if ( blending === THREE.NoBlending ) { + + _gl.disable( _gl.BLEND ); + + } else if ( blending === THREE.AdditiveBlending ) { + + _gl.enable( _gl.BLEND ); + _gl.blendEquation( _gl.FUNC_ADD ); + _gl.blendFunc( _gl.SRC_ALPHA, _gl.ONE ); + + } else if ( blending === THREE.SubtractiveBlending ) { + + // TODO: Find blendFuncSeparate() combination + _gl.enable( _gl.BLEND ); + _gl.blendEquation( _gl.FUNC_ADD ); + _gl.blendFunc( _gl.ZERO, _gl.ONE_MINUS_SRC_COLOR ); + + } else if ( blending === THREE.MultiplyBlending ) { + + // TODO: Find blendFuncSeparate() combination + _gl.enable( _gl.BLEND ); + _gl.blendEquation( _gl.FUNC_ADD ); + _gl.blendFunc( _gl.ZERO, _gl.SRC_COLOR ); + + } else if ( blending === THREE.CustomBlending ) { + + _gl.enable( _gl.BLEND ); + + } else { + + _gl.enable( _gl.BLEND ); + _gl.blendEquationSeparate( _gl.FUNC_ADD, _gl.FUNC_ADD ); + _gl.blendFuncSeparate( _gl.SRC_ALPHA, _gl.ONE_MINUS_SRC_ALPHA, _gl.ONE, _gl.ONE_MINUS_SRC_ALPHA ); + + } + + _oldBlending = blending; + + } + + if ( blending === THREE.CustomBlending ) { + + if ( blendEquation !== _oldBlendEquation ) { + + _gl.blendEquation( paramThreeToGL( blendEquation ) ); + + _oldBlendEquation = blendEquation; + + } + + if ( blendSrc !== _oldBlendSrc || blendDst !== _oldBlendDst ) { + + _gl.blendFunc( paramThreeToGL( blendSrc ), paramThreeToGL( blendDst ) ); + + _oldBlendSrc = blendSrc; + _oldBlendDst = blendDst; + + } + + } else { + + _oldBlendEquation = null; + _oldBlendSrc = null; + _oldBlendDst = null; + + } + + }; + + // Textures + + function setTextureParameters ( textureType, texture, isImagePowerOfTwo ) { + + var extension; + + if ( isImagePowerOfTwo ) { + + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, paramThreeToGL( texture.wrapS ) ); + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, paramThreeToGL( texture.wrapT ) ); + + _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, paramThreeToGL( texture.magFilter ) ); + _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, paramThreeToGL( texture.minFilter ) ); + + } else { + + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE ); + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE ); + + if ( texture.wrapS !== THREE.ClampToEdgeWrapping || texture.wrapT !== THREE.ClampToEdgeWrapping ) { + + console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT is set to THREE.ClampToEdgeWrapping. ( ' + texture.sourceFile + ' )' ); + + } + + _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterFallback( texture.magFilter ) ); + _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterFallback( texture.minFilter ) ); + + if ( texture.minFilter !== THREE.NearestFilter && texture.minFilter !== THREE.LinearFilter ) { + + console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter is set to THREE.LinearFilter or THREE.NearestFilter. ( ' + texture.sourceFile + ' )' ); + + } + + } + + extension = extensions.get( 'EXT_texture_filter_anisotropic' ); + + if ( extension && texture.type !== THREE.FloatType ) { + + if ( texture.anisotropy > 1 || texture.__oldAnisotropy ) { + + _gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, _this.getMaxAnisotropy() ) ); + texture.__oldAnisotropy = texture.anisotropy; + + } + + } + + } + + this.uploadTexture = function ( texture ) { + + if ( texture.__webglInit === undefined ) { + + texture.__webglInit = true; + + texture.addEventListener( 'dispose', onTextureDispose ); + + texture.__webglTexture = _gl.createTexture(); + + _this.info.memory.textures ++; + + } + + _gl.bindTexture( _gl.TEXTURE_2D, texture.__webglTexture ); + + _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); + _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha ); + _gl.pixelStorei( _gl.UNPACK_ALIGNMENT, texture.unpackAlignment ); + + texture.image = clampToMaxSize( texture.image, _maxTextureSize ); + + var image = texture.image, + isImagePowerOfTwo = THREE.Math.isPowerOfTwo( image.width ) && THREE.Math.isPowerOfTwo( image.height ), + glFormat = paramThreeToGL( texture.format ), + glType = paramThreeToGL( texture.type ); + + setTextureParameters( _gl.TEXTURE_2D, texture, isImagePowerOfTwo ); + + var mipmap, mipmaps = texture.mipmaps; + + if ( texture instanceof THREE.DataTexture ) { + + // use manually created mipmaps if available + // if there are no manual mipmaps + // set 0 level mipmap and then use GL to generate other mipmap levels + + if ( mipmaps.length > 0 && isImagePowerOfTwo ) { + + for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { + + mipmap = mipmaps[ i ]; + _gl.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); + + } + + texture.generateMipmaps = false; + + } else { + + _gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, image.width, image.height, 0, glFormat, glType, image.data ); + + } + + } else if ( texture instanceof THREE.CompressedTexture ) { + + for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { + + mipmap = mipmaps[ i ]; + + if ( texture.format !== THREE.RGBAFormat && texture.format !== THREE.RGBFormat ) { + + if ( getCompressedTextureFormats().indexOf( glFormat ) > -1 ) { + + _gl.compressedTexImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, mipmap.data ); + + } else { + + console.warn( "Attempt to load unsupported compressed texture format" ); + + } + + } else { + + _gl.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); + + } + + } + + } else { // regular Texture (image, video, canvas) + + // use manually created mipmaps if available + // if there are no manual mipmaps + // set 0 level mipmap and then use GL to generate other mipmap levels + + if ( mipmaps.length > 0 && isImagePowerOfTwo ) { + + for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { + + mipmap = mipmaps[ i ]; + _gl.texImage2D( _gl.TEXTURE_2D, i, glFormat, glFormat, glType, mipmap ); + + } + + texture.generateMipmaps = false; + + } else { + + _gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, glFormat, glType, texture.image ); + + } + + } + + if ( texture.generateMipmaps && isImagePowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D ); + + texture.needsUpdate = false; + + if ( texture.onUpdate ) texture.onUpdate(); + + }; + + this.setTexture = function ( texture, slot ) { + + _gl.activeTexture( _gl.TEXTURE0 + slot ); + + if ( texture.needsUpdate ) { + + _this.uploadTexture( texture ); + + } else { + + _gl.bindTexture( _gl.TEXTURE_2D, texture.__webglTexture ); + + } + + }; + + function clampToMaxSize ( image, maxSize ) { + + if ( image.width > maxSize || image.height > maxSize ) { + + // Warning: Scaling through the canvas will only work with images that use + // premultiplied alpha. + + var scale = maxSize / Math.max( image.width, image.height ); + + var canvas = document.createElement( 'canvas' ); + canvas.width = Math.floor( image.width * scale ); + canvas.height = Math.floor( image.height * scale ); + + var context = canvas.getContext( '2d' ); + context.drawImage( image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height ); + + console.log( 'THREE.WebGLRenderer:', image, 'is too big (' + image.width + 'x' + image.height + '). Resized to ' + canvas.width + 'x' + canvas.height + '.' ); + + return canvas; + + } + + return image; + + } + + function setCubeTexture ( texture, slot ) { + + if ( texture.image.length === 6 ) { + + if ( texture.needsUpdate ) { + + if ( ! texture.image.__webglTextureCube ) { + + texture.addEventListener( 'dispose', onTextureDispose ); + + texture.image.__webglTextureCube = _gl.createTexture(); + + _this.info.memory.textures ++; + + } + + _gl.activeTexture( _gl.TEXTURE0 + slot ); + _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, texture.image.__webglTextureCube ); + + _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); + + var isCompressed = texture instanceof THREE.CompressedTexture; + var isDataTexture = texture.image[ 0 ] instanceof THREE.DataTexture; + + var cubeImage = []; + + for ( var i = 0; i < 6; i ++ ) { + + if ( _this.autoScaleCubemaps && ! isCompressed && ! isDataTexture ) { + + cubeImage[ i ] = clampToMaxSize( texture.image[ i ], _maxCubemapSize ); + + } else { + + cubeImage[ i ] = isDataTexture ? texture.image[ i ].image : texture.image[ i ]; + + } + + } + + var image = cubeImage[ 0 ], + isImagePowerOfTwo = THREE.Math.isPowerOfTwo( image.width ) && THREE.Math.isPowerOfTwo( image.height ), + glFormat = paramThreeToGL( texture.format ), + glType = paramThreeToGL( texture.type ); + + setTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, isImagePowerOfTwo ); + + for ( var i = 0; i < 6; i ++ ) { + + if ( ! isCompressed ) { + + if ( isDataTexture ) { + + _gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data ); + + } else { + + _gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, glFormat, glType, cubeImage[ i ] ); + + } + + } else { + + var mipmap, mipmaps = cubeImage[ i ].mipmaps; + + for ( var j = 0, jl = mipmaps.length; j < jl; j ++ ) { + + mipmap = mipmaps[ j ]; + + if ( texture.format !== THREE.RGBAFormat && texture.format !== THREE.RGBFormat ) { + + if ( getCompressedTextureFormats().indexOf( glFormat ) > -1 ) { + + _gl.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, mipmap.data ); + + } else { + + console.warn( "Attempt to load unsupported compressed texture format" ); + + } + + } else { + + _gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); + + } + + } + + } + + } + + if ( texture.generateMipmaps && isImagePowerOfTwo ) { + + _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); + + } + + texture.needsUpdate = false; + + if ( texture.onUpdate ) texture.onUpdate(); + + } else { + + _gl.activeTexture( _gl.TEXTURE0 + slot ); + _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, texture.image.__webglTextureCube ); + + } + + } + + } + + function setCubeTextureDynamic ( texture, slot ) { + + _gl.activeTexture( _gl.TEXTURE0 + slot ); + _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, texture.__webglTexture ); + + } + + // Render targets + + function setupFrameBuffer ( framebuffer, renderTarget, textureTarget ) { + + _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); + _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, textureTarget, renderTarget.__webglTexture, 0 ); + + } + + function setupRenderBuffer ( renderbuffer, renderTarget ) { + + _gl.bindRenderbuffer( _gl.RENDERBUFFER, renderbuffer ); + + if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { + + _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_COMPONENT16, renderTarget.width, renderTarget.height ); + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); + + /* For some reason this is not working. Defaulting to RGBA4. + } else if ( ! renderTarget.depthBuffer && renderTarget.stencilBuffer ) { + + _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.STENCIL_INDEX8, renderTarget.width, renderTarget.height ); + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); + */ + } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { + + _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height ); + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); + + } else { + + _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.RGBA4, renderTarget.width, renderTarget.height ); + + } + + } + + this.setRenderTarget = function ( renderTarget ) { + + var isCube = ( renderTarget instanceof THREE.WebGLRenderTargetCube ); + + if ( renderTarget && renderTarget.__webglFramebuffer === undefined ) { + + if ( renderTarget.depthBuffer === undefined ) renderTarget.depthBuffer = true; + if ( renderTarget.stencilBuffer === undefined ) renderTarget.stencilBuffer = true; + + renderTarget.addEventListener( 'dispose', onRenderTargetDispose ); + + renderTarget.__webglTexture = _gl.createTexture(); + + _this.info.memory.textures ++; + + // Setup texture, create render and frame buffers + + var isTargetPowerOfTwo = THREE.Math.isPowerOfTwo( renderTarget.width ) && THREE.Math.isPowerOfTwo( renderTarget.height ), + glFormat = paramThreeToGL( renderTarget.format ), + glType = paramThreeToGL( renderTarget.type ); + + if ( isCube ) { + + renderTarget.__webglFramebuffer = []; + renderTarget.__webglRenderbuffer = []; + + _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, renderTarget.__webglTexture ); + setTextureParameters( _gl.TEXTURE_CUBE_MAP, renderTarget, isTargetPowerOfTwo ); + + for ( var i = 0; i < 6; i ++ ) { + + renderTarget.__webglFramebuffer[ i ] = _gl.createFramebuffer(); + renderTarget.__webglRenderbuffer[ i ] = _gl.createRenderbuffer(); + + _gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); + + setupFrameBuffer( renderTarget.__webglFramebuffer[ i ], renderTarget, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i ); + setupRenderBuffer( renderTarget.__webglRenderbuffer[ i ], renderTarget ); + + } + + if ( isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); + + } else { + + renderTarget.__webglFramebuffer = _gl.createFramebuffer(); + + if ( renderTarget.shareDepthFrom ) { + + renderTarget.__webglRenderbuffer = renderTarget.shareDepthFrom.__webglRenderbuffer; + + } else { + + renderTarget.__webglRenderbuffer = _gl.createRenderbuffer(); + + } + + _gl.bindTexture( _gl.TEXTURE_2D, renderTarget.__webglTexture ); + setTextureParameters( _gl.TEXTURE_2D, renderTarget, isTargetPowerOfTwo ); + + _gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); + + setupFrameBuffer( renderTarget.__webglFramebuffer, renderTarget, _gl.TEXTURE_2D ); + + if ( renderTarget.shareDepthFrom ) { + + if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { + + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderTarget.__webglRenderbuffer ); + + } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { + + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderTarget.__webglRenderbuffer ); + + } + + } else { + + setupRenderBuffer( renderTarget.__webglRenderbuffer, renderTarget ); + + } + + if ( isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D ); + + } + + // Release everything + + if ( isCube ) { + + _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, null ); + + } else { + + _gl.bindTexture( _gl.TEXTURE_2D, null ); + + } + + _gl.bindRenderbuffer( _gl.RENDERBUFFER, null ); + _gl.bindFramebuffer( _gl.FRAMEBUFFER, null ); + + } + + var framebuffer, width, height, vx, vy; + + if ( renderTarget ) { + + if ( isCube ) { + + framebuffer = renderTarget.__webglFramebuffer[ renderTarget.activeCubeFace ]; + + } else { + + framebuffer = renderTarget.__webglFramebuffer; + + } + + width = renderTarget.width; + height = renderTarget.height; + + vx = 0; + vy = 0; + + } else { + + framebuffer = null; + + width = _viewportWidth; + height = _viewportHeight; + + vx = _viewportX; + vy = _viewportY; + + } + + if ( framebuffer !== _currentFramebuffer ) { + + _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); + _gl.viewport( vx, vy, width, height ); + + _currentFramebuffer = framebuffer; + + } + + _currentWidth = width; + _currentHeight = height; + + }; + + function updateRenderTargetMipmap ( renderTarget ) { + + if ( renderTarget instanceof THREE.WebGLRenderTargetCube ) { + + _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, renderTarget.__webglTexture ); + _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); + _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, null ); + + } else { + + _gl.bindTexture( _gl.TEXTURE_2D, renderTarget.__webglTexture ); + _gl.generateMipmap( _gl.TEXTURE_2D ); + _gl.bindTexture( _gl.TEXTURE_2D, null ); + + } + + } + + // Fallback filters for non-power-of-2 textures + + function filterFallback ( f ) { + + if ( f === THREE.NearestFilter || f === THREE.NearestMipMapNearestFilter || f === THREE.NearestMipMapLinearFilter ) { + + return _gl.NEAREST; + + } + + return _gl.LINEAR; + + } + + // Map three.js constants to WebGL constants + + function paramThreeToGL ( p ) { + + var extension; + + if ( p === THREE.RepeatWrapping ) return _gl.REPEAT; + if ( p === THREE.ClampToEdgeWrapping ) return _gl.CLAMP_TO_EDGE; + if ( p === THREE.MirroredRepeatWrapping ) return _gl.MIRRORED_REPEAT; + + if ( p === THREE.NearestFilter ) return _gl.NEAREST; + if ( p === THREE.NearestMipMapNearestFilter ) return _gl.NEAREST_MIPMAP_NEAREST; + if ( p === THREE.NearestMipMapLinearFilter ) return _gl.NEAREST_MIPMAP_LINEAR; + + if ( p === THREE.LinearFilter ) return _gl.LINEAR; + if ( p === THREE.LinearMipMapNearestFilter ) return _gl.LINEAR_MIPMAP_NEAREST; + if ( p === THREE.LinearMipMapLinearFilter ) return _gl.LINEAR_MIPMAP_LINEAR; + + if ( p === THREE.UnsignedByteType ) return _gl.UNSIGNED_BYTE; + if ( p === THREE.UnsignedShort4444Type ) return _gl.UNSIGNED_SHORT_4_4_4_4; + if ( p === THREE.UnsignedShort5551Type ) return _gl.UNSIGNED_SHORT_5_5_5_1; + if ( p === THREE.UnsignedShort565Type ) return _gl.UNSIGNED_SHORT_5_6_5; + + if ( p === THREE.ByteType ) return _gl.BYTE; + if ( p === THREE.ShortType ) return _gl.SHORT; + if ( p === THREE.UnsignedShortType ) return _gl.UNSIGNED_SHORT; + if ( p === THREE.IntType ) return _gl.INT; + if ( p === THREE.UnsignedIntType ) return _gl.UNSIGNED_INT; + if ( p === THREE.FloatType ) return _gl.FLOAT; + + if ( p === THREE.AlphaFormat ) return _gl.ALPHA; + if ( p === THREE.RGBFormat ) return _gl.RGB; + if ( p === THREE.RGBAFormat ) return _gl.RGBA; + if ( p === THREE.LuminanceFormat ) return _gl.LUMINANCE; + if ( p === THREE.LuminanceAlphaFormat ) return _gl.LUMINANCE_ALPHA; + + if ( p === THREE.AddEquation ) return _gl.FUNC_ADD; + if ( p === THREE.SubtractEquation ) return _gl.FUNC_SUBTRACT; + if ( p === THREE.ReverseSubtractEquation ) return _gl.FUNC_REVERSE_SUBTRACT; + + if ( p === THREE.ZeroFactor ) return _gl.ZERO; + if ( p === THREE.OneFactor ) return _gl.ONE; + if ( p === THREE.SrcColorFactor ) return _gl.SRC_COLOR; + if ( p === THREE.OneMinusSrcColorFactor ) return _gl.ONE_MINUS_SRC_COLOR; + if ( p === THREE.SrcAlphaFactor ) return _gl.SRC_ALPHA; + if ( p === THREE.OneMinusSrcAlphaFactor ) return _gl.ONE_MINUS_SRC_ALPHA; + if ( p === THREE.DstAlphaFactor ) return _gl.DST_ALPHA; + if ( p === THREE.OneMinusDstAlphaFactor ) return _gl.ONE_MINUS_DST_ALPHA; + + if ( p === THREE.DstColorFactor ) return _gl.DST_COLOR; + if ( p === THREE.OneMinusDstColorFactor ) return _gl.ONE_MINUS_DST_COLOR; + if ( p === THREE.SrcAlphaSaturateFactor ) return _gl.SRC_ALPHA_SATURATE; + + extension = extensions.get( 'WEBGL_compressed_texture_s3tc' ); + + if ( extension !== null ) { + + if ( p === THREE.RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT; + if ( p === THREE.RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT; + if ( p === THREE.RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT; + if ( p === THREE.RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT; + + } + + extension = extensions.get( 'WEBGL_compressed_texture_pvrtc' ); + + if ( extension !== null ) { + + if ( p === THREE.RGB_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG; + if ( p === THREE.RGB_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG; + if ( p === THREE.RGBA_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; + if ( p === THREE.RGBA_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; + + } + + extension = extensions.get( 'EXT_blend_minmax' ); + + if ( extension !== null ) { + + if ( p === THREE.MinEquation ) return extension.MIN_EXT; + if ( p === THREE.MaxEquation ) return extension.MAX_EXT; + + } + + return 0; + + } + + // Allocations + + function allocateBones ( object ) { + + if ( _supportsBoneTextures && object && object.skeleton && object.skeleton.useVertexTexture ) { + + return 1024; + + } else { + + // default for when object is not specified + // ( for example when prebuilding shader + // to be used with multiple objects ) + // + // - leave some extra space for other uniforms + // - limit here is ANGLE's 254 max uniform vectors + // (up to 54 should be safe) + + var nVertexUniforms = _gl.getParameter( _gl.MAX_VERTEX_UNIFORM_VECTORS ); + var nVertexMatrices = Math.floor( ( nVertexUniforms - 20 ) / 4 ); + + var maxBones = nVertexMatrices; + + if ( object !== undefined && object instanceof THREE.SkinnedMesh ) { + + maxBones = Math.min( object.skeleton.bones.length, maxBones ); + + if ( maxBones < object.skeleton.bones.length ) { + + console.warn( 'WebGLRenderer: too many bones - ' + object.skeleton.bones.length + ', this GPU supports just ' + maxBones + ' (try OpenGL instead of ANGLE)' ); + + } + + } + + return maxBones; + + } + + } + + function allocateLights( lights ) { + + var dirLights = 0; + var pointLights = 0; + var spotLights = 0; + var hemiLights = 0; + + for ( var l = 0, ll = lights.length; l < ll; l ++ ) { + + var light = lights[ l ]; + + if ( light.onlyShadow || light.visible === false ) continue; + + if ( light instanceof THREE.DirectionalLight ) dirLights ++; + if ( light instanceof THREE.PointLight ) pointLights ++; + if ( light instanceof THREE.SpotLight ) spotLights ++; + if ( light instanceof THREE.HemisphereLight ) hemiLights ++; + + } + + return { 'directional': dirLights, 'point': pointLights, 'spot': spotLights, 'hemi': hemiLights }; + + } + + function allocateShadows( lights ) { + + var maxShadows = 0; + + for ( var l = 0, ll = lights.length; l < ll; l ++ ) { + + var light = lights[ l ]; + + if ( ! light.castShadow ) continue; + + if ( light instanceof THREE.SpotLight ) maxShadows ++; + if ( light instanceof THREE.DirectionalLight && ! light.shadowCascade ) maxShadows ++; + + } + + return maxShadows; + + } + + // DEPRECATED + + this.initMaterial = function () { + + console.warn( 'THREE.WebGLRenderer: .initMaterial() has been removed.' ); + + }; + + this.addPrePlugin = function () { + + console.warn( 'THREE.WebGLRenderer: .addPrePlugin() has been removed.' ); + + }; + + this.addPostPlugin = function () { + + console.warn( 'THREE.WebGLRenderer: .addPostPlugin() has been removed.' ); + + }; + + this.updateShadowMap = function () { + + console.warn( 'THREE.WebGLRenderer: .updateShadowMap() has been removed.' ); + + }; + +}; + +// File:src/renderers/WebGLRenderTarget.js + +/** + * @author szimek / https://github.com/szimek/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.WebGLRenderTarget = function ( width, height, options ) { + + this.width = width; + this.height = height; + + options = options || {}; + + this.wrapS = options.wrapS !== undefined ? options.wrapS : THREE.ClampToEdgeWrapping; + this.wrapT = options.wrapT !== undefined ? options.wrapT : THREE.ClampToEdgeWrapping; + + this.magFilter = options.magFilter !== undefined ? options.magFilter : THREE.LinearFilter; + this.minFilter = options.minFilter !== undefined ? options.minFilter : THREE.LinearMipMapLinearFilter; + + this.anisotropy = options.anisotropy !== undefined ? options.anisotropy : 1; + + this.offset = new THREE.Vector2( 0, 0 ); + this.repeat = new THREE.Vector2( 1, 1 ); + + this.format = options.format !== undefined ? options.format : THREE.RGBAFormat; + this.type = options.type !== undefined ? options.type : THREE.UnsignedByteType; + + this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true; + this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : true; + + this.generateMipmaps = true; + + this.shareDepthFrom = null; + +}; + +THREE.WebGLRenderTarget.prototype = { + + constructor: THREE.WebGLRenderTarget, + + setSize: function ( width, height ) { + + this.width = width; + this.height = height; + + }, + + clone: function () { + + var tmp = new THREE.WebGLRenderTarget( this.width, this.height ); + + tmp.wrapS = this.wrapS; + tmp.wrapT = this.wrapT; + + tmp.magFilter = this.magFilter; + tmp.minFilter = this.minFilter; + + tmp.anisotropy = this.anisotropy; + + tmp.offset.copy( this.offset ); + tmp.repeat.copy( this.repeat ); + + tmp.format = this.format; + tmp.type = this.type; + + tmp.depthBuffer = this.depthBuffer; + tmp.stencilBuffer = this.stencilBuffer; + + tmp.generateMipmaps = this.generateMipmaps; + + tmp.shareDepthFrom = this.shareDepthFrom; + + return tmp; + + }, + + dispose: function () { + + this.dispatchEvent( { type: 'dispose' } ); + + } + +}; + +THREE.EventDispatcher.prototype.apply( THREE.WebGLRenderTarget.prototype ); + +// File:src/renderers/WebGLRenderTargetCube.js + +/** + * @author alteredq / http://alteredqualia.com + */ + +THREE.WebGLRenderTargetCube = function ( width, height, options ) { + + THREE.WebGLRenderTarget.call( this, width, height, options ); + + this.activeCubeFace = 0; // PX 0, NX 1, PY 2, NY 3, PZ 4, NZ 5 + +}; + +THREE.WebGLRenderTargetCube.prototype = Object.create( THREE.WebGLRenderTarget.prototype ); +THREE.WebGLRenderTargetCube.prototype.constructor = THREE.WebGLRenderTargetCube; + +// File:src/renderers/webgl/WebGLExtensions.js + +/** +* @author mrdoob / http://mrdoob.com/ +*/ + +THREE.WebGLExtensions = function ( gl ) { + + var extensions = {}; + + this.get = function ( name ) { + + if ( extensions[ name ] !== undefined ) { + + return extensions[ name ]; + + } + + var extension; + + switch ( name ) { + + case 'EXT_texture_filter_anisotropic': + extension = gl.getExtension( 'EXT_texture_filter_anisotropic' ) || gl.getExtension( 'MOZ_EXT_texture_filter_anisotropic' ) || gl.getExtension( 'WEBKIT_EXT_texture_filter_anisotropic' ); + break; + + case 'WEBGL_compressed_texture_s3tc': + extension = gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' ); + break; + + case 'WEBGL_compressed_texture_pvrtc': + extension = gl.getExtension( 'WEBGL_compressed_texture_pvrtc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_pvrtc' ); + break; + + default: + extension = gl.getExtension( name ); + + } + + if ( extension === null ) { + + console.log( 'THREE.WebGLRenderer: ' + name + ' extension not supported.' ); + + } + + extensions[ name ] = extension; + + return extension; + + }; + +}; + +// File:src/renderers/webgl/WebGLProgram.js + +THREE.WebGLProgram = ( function () { + + var programIdCount = 0; + + var generateDefines = function ( defines ) { + + var value, chunk, chunks = []; + + for ( var d in defines ) { + + value = defines[ d ]; + if ( value === false ) continue; + + chunk = '#define ' + d + ' ' + value; + chunks.push( chunk ); + + } + + return chunks.join( '\n' ); + + }; + + var cacheUniformLocations = function ( gl, program, identifiers ) { + + var uniforms = {}; + + for ( var i = 0, l = identifiers.length; i < l; i ++ ) { + + var id = identifiers[ i ]; + uniforms[ id ] = gl.getUniformLocation( program, id ); + + } + + return uniforms; + + }; + + var cacheAttributeLocations = function ( gl, program, identifiers ) { + + var attributes = {}; + + for ( var i = 0, l = identifiers.length; i < l; i ++ ) { + + var id = identifiers[ i ]; + attributes[ id ] = gl.getAttribLocation( program, id ); + + } + + return attributes; + + }; + + return function ( renderer, code, material, parameters ) { + + var _this = renderer; + var _gl = _this.context; + + var defines = material.defines; + var uniforms = material.__webglShader.uniforms; + var attributes = material.attributes; + + var vertexShader = material.__webglShader.vertexShader; + var fragmentShader = material.__webglShader.fragmentShader; + + var index0AttributeName = material.index0AttributeName; + + if ( index0AttributeName === undefined && parameters.morphTargets === true ) { + + // programs with morphTargets displace position out of attribute 0 + + index0AttributeName = 'position'; + + } + + var shadowMapTypeDefine = 'SHADOWMAP_TYPE_BASIC'; + + if ( parameters.shadowMapType === THREE.PCFShadowMap ) { + + shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF'; + + } else if ( parameters.shadowMapType === THREE.PCFSoftShadowMap ) { + + shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF_SOFT'; + + } + + var envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; + var envMapModeDefine = 'ENVMAP_MODE_REFLECTION'; + var envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY'; + + if ( parameters.envMap ) { + + switch ( material.envMap.mapping ) { + + case THREE.CubeReflectionMapping: + case THREE.CubeRefractionMapping: + envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; + break; + + case THREE.EquirectangularReflectionMapping: + case THREE.EquirectangularRefractionMapping: + envMapTypeDefine = 'ENVMAP_TYPE_EQUIREC'; + break; + + case THREE.SphericalReflectionMapping: + envMapTypeDefine = 'ENVMAP_TYPE_SPHERE'; + break; + + } + + switch ( material.envMap.mapping ) { + + case THREE.CubeRefractionMapping: + case THREE.EquirectangularRefractionMapping: + envMapModeDefine = 'ENVMAP_MODE_REFRACTION'; + break; + + } + + switch ( material.combine ) { + + case THREE.MultiplyOperation: + envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY'; + break; + + case THREE.MixOperation: + envMapBlendingDefine = 'ENVMAP_BLENDING_MIX'; + break; + + case THREE.AddOperation: + envMapBlendingDefine = 'ENVMAP_BLENDING_ADD'; + break; + + } + + } + + // console.log( 'building new program ' ); + + // + + var customDefines = generateDefines( defines ); + + // + + var program = _gl.createProgram(); + + var prefix_vertex, prefix_fragment; + + if ( material instanceof THREE.RawShaderMaterial ) { + + prefix_vertex = ''; + prefix_fragment = ''; + + } else { + + prefix_vertex = [ + + 'precision ' + parameters.precision + ' float;', + 'precision ' + parameters.precision + ' int;', + + customDefines, + + parameters.supportsVertexTextures ? '#define VERTEX_TEXTURES' : '', + + _this.gammaInput ? '#define GAMMA_INPUT' : '', + _this.gammaOutput ? '#define GAMMA_OUTPUT' : '', + + '#define MAX_DIR_LIGHTS ' + parameters.maxDirLights, + '#define MAX_POINT_LIGHTS ' + parameters.maxPointLights, + '#define MAX_SPOT_LIGHTS ' + parameters.maxSpotLights, + '#define MAX_HEMI_LIGHTS ' + parameters.maxHemiLights, + + '#define MAX_SHADOWS ' + parameters.maxShadows, + + '#define MAX_BONES ' + parameters.maxBones, + + parameters.map ? '#define USE_MAP' : '', + parameters.envMap ? '#define USE_ENVMAP' : '', + parameters.envMap ? '#define ' + envMapModeDefine : '', + parameters.lightMap ? '#define USE_LIGHTMAP' : '', + parameters.bumpMap ? '#define USE_BUMPMAP' : '', + parameters.normalMap ? '#define USE_NORMALMAP' : '', + parameters.specularMap ? '#define USE_SPECULARMAP' : '', + parameters.alphaMap ? '#define USE_ALPHAMAP' : '', + parameters.vertexColors ? '#define USE_COLOR' : '', + + parameters.skinning ? '#define USE_SKINNING' : '', + parameters.useVertexTexture ? '#define BONE_TEXTURE' : '', + + parameters.morphTargets ? '#define USE_MORPHTARGETS' : '', + parameters.morphNormals ? '#define USE_MORPHNORMALS' : '', + parameters.wrapAround ? '#define WRAP_AROUND' : '', + parameters.doubleSided ? '#define DOUBLE_SIDED' : '', + parameters.flipSided ? '#define FLIP_SIDED' : '', + + parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', + parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', + parameters.shadowMapDebug ? '#define SHADOWMAP_DEBUG' : '', + parameters.shadowMapCascade ? '#define SHADOWMAP_CASCADE' : '', + + parameters.sizeAttenuation ? '#define USE_SIZEATTENUATION' : '', + + parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', + //_this._glExtensionFragDepth ? '#define USE_LOGDEPTHBUF_EXT' : '', + + + 'uniform mat4 modelMatrix;', + 'uniform mat4 modelViewMatrix;', + 'uniform mat4 projectionMatrix;', + 'uniform mat4 viewMatrix;', + 'uniform mat3 normalMatrix;', + 'uniform vec3 cameraPosition;', + + 'attribute vec3 position;', + 'attribute vec3 normal;', + 'attribute vec2 uv;', + 'attribute vec2 uv2;', + + '#ifdef USE_COLOR', + + ' attribute vec3 color;', + + '#endif', + + '#ifdef USE_MORPHTARGETS', + + ' attribute vec3 morphTarget0;', + ' attribute vec3 morphTarget1;', + ' attribute vec3 morphTarget2;', + ' attribute vec3 morphTarget3;', + + ' #ifdef USE_MORPHNORMALS', + + ' attribute vec3 morphNormal0;', + ' attribute vec3 morphNormal1;', + ' attribute vec3 morphNormal2;', + ' attribute vec3 morphNormal3;', + + ' #else', + + ' attribute vec3 morphTarget4;', + ' attribute vec3 morphTarget5;', + ' attribute vec3 morphTarget6;', + ' attribute vec3 morphTarget7;', + + ' #endif', + + '#endif', + + '#ifdef USE_SKINNING', + + ' attribute vec4 skinIndex;', + ' attribute vec4 skinWeight;', + + '#endif', + + '' + + ].join( '\n' ); + + prefix_fragment = [ + + 'precision ' + parameters.precision + ' float;', + 'precision ' + parameters.precision + ' int;', + + ( parameters.bumpMap || parameters.normalMap ) ? '#extension GL_OES_standard_derivatives : enable' : '', + + customDefines, + + '#define MAX_DIR_LIGHTS ' + parameters.maxDirLights, + '#define MAX_POINT_LIGHTS ' + parameters.maxPointLights, + '#define MAX_SPOT_LIGHTS ' + parameters.maxSpotLights, + '#define MAX_HEMI_LIGHTS ' + parameters.maxHemiLights, + + '#define MAX_SHADOWS ' + parameters.maxShadows, + + parameters.alphaTest ? '#define ALPHATEST ' + parameters.alphaTest: '', + + _this.gammaInput ? '#define GAMMA_INPUT' : '', + _this.gammaOutput ? '#define GAMMA_OUTPUT' : '', + + ( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '', + ( parameters.useFog && parameters.fogExp ) ? '#define FOG_EXP2' : '', + + parameters.map ? '#define USE_MAP' : '', + parameters.envMap ? '#define USE_ENVMAP' : '', + parameters.envMap ? '#define ' + envMapTypeDefine : '', + parameters.envMap ? '#define ' + envMapModeDefine : '', + parameters.envMap ? '#define ' + envMapBlendingDefine : '', + parameters.lightMap ? '#define USE_LIGHTMAP' : '', + parameters.bumpMap ? '#define USE_BUMPMAP' : '', + parameters.normalMap ? '#define USE_NORMALMAP' : '', + parameters.specularMap ? '#define USE_SPECULARMAP' : '', + parameters.alphaMap ? '#define USE_ALPHAMAP' : '', + parameters.vertexColors ? '#define USE_COLOR' : '', + + parameters.metal ? '#define METAL' : '', + parameters.wrapAround ? '#define WRAP_AROUND' : '', + parameters.doubleSided ? '#define DOUBLE_SIDED' : '', + parameters.flipSided ? '#define FLIP_SIDED' : '', + + parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', + parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', + parameters.shadowMapDebug ? '#define SHADOWMAP_DEBUG' : '', + parameters.shadowMapCascade ? '#define SHADOWMAP_CASCADE' : '', + + parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', + //_this._glExtensionFragDepth ? '#define USE_LOGDEPTHBUF_EXT' : '', + + 'uniform mat4 viewMatrix;', + 'uniform vec3 cameraPosition;', + '' + + ].join( '\n' ); + + } + + var glVertexShader = new THREE.WebGLShader( _gl, _gl.VERTEX_SHADER, prefix_vertex + vertexShader ); + var glFragmentShader = new THREE.WebGLShader( _gl, _gl.FRAGMENT_SHADER, prefix_fragment + fragmentShader ); + + _gl.attachShader( program, glVertexShader ); + _gl.attachShader( program, glFragmentShader ); + + if ( index0AttributeName !== undefined ) { + + // Force a particular attribute to index 0. + // because potentially expensive emulation is done by browser if attribute 0 is disabled. + // And, color, for example is often automatically bound to index 0 so disabling it + + _gl.bindAttribLocation( program, 0, index0AttributeName ); + + } + + _gl.linkProgram( program ); + + if ( _gl.getProgramParameter( program, _gl.LINK_STATUS ) === false ) { + + console.error( 'THREE.WebGLProgram: Could not initialise shader.' ); + console.error( 'gl.VALIDATE_STATUS', _gl.getProgramParameter( program, _gl.VALIDATE_STATUS ) ); + console.error( 'gl.getError()', _gl.getError() ); + + } + + if ( _gl.getProgramInfoLog( program ) !== '' ) { + + console.warn( 'THREE.WebGLProgram: gl.getProgramInfoLog()', _gl.getProgramInfoLog( program ) ); + // console.warn( _gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( glVertexShader ) ); + // console.warn( _gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( glFragmentShader ) ); + + } + + // clean up + + _gl.deleteShader( glVertexShader ); + _gl.deleteShader( glFragmentShader ); + + // cache uniform locations + + var identifiers = [ + + 'viewMatrix', + 'modelViewMatrix', + 'projectionMatrix', + 'normalMatrix', + 'modelMatrix', + 'cameraPosition', + 'morphTargetInfluences', + 'bindMatrix', + 'bindMatrixInverse' + + ]; + + if ( parameters.useVertexTexture ) { + + identifiers.push( 'boneTexture' ); + identifiers.push( 'boneTextureWidth' ); + identifiers.push( 'boneTextureHeight' ); + + } else { + + identifiers.push( 'boneGlobalMatrices' ); + + } + + if ( parameters.logarithmicDepthBuffer ) { + + identifiers.push('logDepthBufFC'); + + } + + + for ( var u in uniforms ) { + + identifiers.push( u ); + + } + + this.uniforms = cacheUniformLocations( _gl, program, identifiers ); + + // cache attributes locations + + identifiers = [ + + 'position', + 'normal', + 'uv', + 'uv2', + 'tangent', + 'color', + 'skinIndex', + 'skinWeight', + 'lineDistance' + + ]; + + for ( var i = 0; i < parameters.maxMorphTargets; i ++ ) { + + identifiers.push( 'morphTarget' + i ); + + } + + for ( var i = 0; i < parameters.maxMorphNormals; i ++ ) { + + identifiers.push( 'morphNormal' + i ); + + } + + for ( var a in attributes ) { + + identifiers.push( a ); + + } + + this.attributes = cacheAttributeLocations( _gl, program, identifiers ); + this.attributesKeys = Object.keys( this.attributes ); + + // + + this.id = programIdCount ++; + this.code = code; + this.usedTimes = 1; + this.program = program; + this.vertexShader = glVertexShader; + this.fragmentShader = glFragmentShader; + + return this; + + }; + +} )(); + +// File:src/renderers/webgl/WebGLShader.js + +THREE.WebGLShader = ( function () { + + var addLineNumbers = function ( string ) { + + var lines = string.split( '\n' ); + + for ( var i = 0; i < lines.length; i ++ ) { + + lines[ i ] = ( i + 1 ) + ': ' + lines[ i ]; + + } + + return lines.join( '\n' ); + + }; + + return function ( gl, type, string ) { + + var shader = gl.createShader( type ); + + gl.shaderSource( shader, string ); + gl.compileShader( shader ); + + if ( gl.getShaderParameter( shader, gl.COMPILE_STATUS ) === false ) { + + console.error( 'THREE.WebGLShader: Shader couldn\'t compile.' ); + + } + + if ( gl.getShaderInfoLog( shader ) !== '' ) { + + console.warn( 'THREE.WebGLShader: gl.getShaderInfoLog()', gl.getShaderInfoLog( shader ) ); + console.warn( addLineNumbers( string ) ); + + } + + // --enable-privileged-webgl-extension + // console.log( type, gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( shader ) ); + + return shader; + + }; + +} )(); + +// File:src/renderers/webgl/plugins/LensFlarePlugin.js + +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.LensFlarePlugin = function ( renderer, flares ) { + + var gl = renderer.context; + + var vertexBuffer, elementBuffer; + var program, attributes, uniforms; + var hasVertexTexture; + + var tempTexture, occlusionTexture; + + var init = function () { + + var vertices = new Float32Array( [ + -1, -1, 0, 0, + 1, -1, 1, 0, + 1, 1, 1, 1, + -1, 1, 0, 1 + ] ); + + var faces = new Uint16Array( [ + 0, 1, 2, + 0, 2, 3 + ] ); + + // buffers + + vertexBuffer = gl.createBuffer(); + elementBuffer = gl.createBuffer(); + + gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer ); + gl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW ); + + gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); + gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW ); + + // textures + + tempTexture = gl.createTexture(); + occlusionTexture = gl.createTexture(); + + gl.bindTexture( gl.TEXTURE_2D, tempTexture ); + gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGB, 16, 16, 0, gl.RGB, gl.UNSIGNED_BYTE, null ); + gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE ); + gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE ); + gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST ); + gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST ); + + gl.bindTexture( gl.TEXTURE_2D, occlusionTexture ); + gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, 16, 16, 0, gl.RGBA, gl.UNSIGNED_BYTE, null ); + gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE ); + gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE ); + gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST ); + gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST ); + + hasVertexTexture = gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ) > 0; + + var shader; + + if ( hasVertexTexture ) { + + shader = { + + vertexShader: [ + + "uniform lowp int renderType;", + + "uniform vec3 screenPosition;", + "uniform vec2 scale;", + "uniform float rotation;", + + "uniform sampler2D occlusionMap;", + + "attribute vec2 position;", + "attribute vec2 uv;", + + "varying vec2 vUV;", + "varying float vVisibility;", + + "void main() {", + + "vUV = uv;", + + "vec2 pos = position;", + + "if( renderType == 2 ) {", + + "vec4 visibility = texture2D( occlusionMap, vec2( 0.1, 0.1 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.5, 0.1 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.9, 0.1 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.9, 0.9 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.1, 0.9 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.5, 0.5 ) );", + + "vVisibility = visibility.r / 9.0;", + "vVisibility *= 1.0 - visibility.g / 9.0;", + "vVisibility *= visibility.b / 9.0;", + "vVisibility *= 1.0 - visibility.a / 9.0;", + + "pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;", + "pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;", + + "}", + + "gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "uniform lowp int renderType;", + + "uniform sampler2D map;", + "uniform float opacity;", + "uniform vec3 color;", + + "varying vec2 vUV;", + "varying float vVisibility;", + + "void main() {", + + // pink square + + "if( renderType == 0 ) {", + + "gl_FragColor = vec4( 1.0, 0.0, 1.0, 0.0 );", + + // restore + + "} else if( renderType == 1 ) {", + + "gl_FragColor = texture2D( map, vUV );", + + // flare + + "} else {", + + "vec4 texture = texture2D( map, vUV );", + "texture.a *= opacity * vVisibility;", + "gl_FragColor = texture;", + "gl_FragColor.rgb *= color;", + + "}", + + "}" + + ].join( "\n" ) + + }; + + } else { + + shader = { + + vertexShader: [ + + "uniform lowp int renderType;", + + "uniform vec3 screenPosition;", + "uniform vec2 scale;", + "uniform float rotation;", + + "attribute vec2 position;", + "attribute vec2 uv;", + + "varying vec2 vUV;", + + "void main() {", + + "vUV = uv;", + + "vec2 pos = position;", + + "if( renderType == 2 ) {", + + "pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;", + "pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;", + + "}", + + "gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "precision mediump float;", + + "uniform lowp int renderType;", + + "uniform sampler2D map;", + "uniform sampler2D occlusionMap;", + "uniform float opacity;", + "uniform vec3 color;", + + "varying vec2 vUV;", + + "void main() {", + + // pink square + + "if( renderType == 0 ) {", + + "gl_FragColor = vec4( texture2D( map, vUV ).rgb, 0.0 );", + + // restore + + "} else if( renderType == 1 ) {", + + "gl_FragColor = texture2D( map, vUV );", + + // flare + + "} else {", + + "float visibility = texture2D( occlusionMap, vec2( 0.5, 0.1 ) ).a;", + "visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) ).a;", + "visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) ).a;", + "visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) ).a;", + "visibility = ( 1.0 - visibility / 4.0 );", + + "vec4 texture = texture2D( map, vUV );", + "texture.a *= opacity * visibility;", + "gl_FragColor = texture;", + "gl_FragColor.rgb *= color;", + + "}", + + "}" + + ].join( "\n" ) + + }; + + } + + program = createProgram( shader ); + + attributes = { + vertex: gl.getAttribLocation ( program, "position" ), + uv: gl.getAttribLocation ( program, "uv" ) + } + + uniforms = { + renderType: gl.getUniformLocation( program, "renderType" ), + map: gl.getUniformLocation( program, "map" ), + occlusionMap: gl.getUniformLocation( program, "occlusionMap" ), + opacity: gl.getUniformLocation( program, "opacity" ), + color: gl.getUniformLocation( program, "color" ), + scale: gl.getUniformLocation( program, "scale" ), + rotation: gl.getUniformLocation( program, "rotation" ), + screenPosition: gl.getUniformLocation( program, "screenPosition" ) + }; + + }; + + /* + * Render lens flares + * Method: renders 16x16 0xff00ff-colored points scattered over the light source area, + * reads these back and calculates occlusion. + */ + + this.render = function ( scene, camera, viewportWidth, viewportHeight ) { + + if ( flares.length === 0 ) return; + + var tempPosition = new THREE.Vector3(); + + var invAspect = viewportHeight / viewportWidth, + halfViewportWidth = viewportWidth * 0.5, + halfViewportHeight = viewportHeight * 0.5; + + var size = 16 / viewportHeight, + scale = new THREE.Vector2( size * invAspect, size ); + + var screenPosition = new THREE.Vector3( 1, 1, 0 ), + screenPositionPixels = new THREE.Vector2( 1, 1 ); + + if ( program === undefined ) { + + init(); + + } + + gl.useProgram( program ); + + gl.enableVertexAttribArray( attributes.vertex ); + gl.enableVertexAttribArray( attributes.uv ); + + // loop through all lens flares to update their occlusion and positions + // setup gl and common used attribs/unforms + + gl.uniform1i( uniforms.occlusionMap, 0 ); + gl.uniform1i( uniforms.map, 1 ); + + gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer ); + gl.vertexAttribPointer( attributes.vertex, 2, gl.FLOAT, false, 2 * 8, 0 ); + gl.vertexAttribPointer( attributes.uv, 2, gl.FLOAT, false, 2 * 8, 8 ); + + gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); + + gl.disable( gl.CULL_FACE ); + gl.depthMask( false ); + + for ( var i = 0, l = flares.length; i < l; i ++ ) { + + size = 16 / viewportHeight; + scale.set( size * invAspect, size ); + + // calc object screen position + + var flare = flares[ i ]; + + tempPosition.set( flare.matrixWorld.elements[12], flare.matrixWorld.elements[13], flare.matrixWorld.elements[14] ); + + tempPosition.applyMatrix4( camera.matrixWorldInverse ); + tempPosition.applyProjection( camera.projectionMatrix ); + + // setup arrays for gl programs + + screenPosition.copy( tempPosition ) + + screenPositionPixels.x = screenPosition.x * halfViewportWidth + halfViewportWidth; + screenPositionPixels.y = screenPosition.y * halfViewportHeight + halfViewportHeight; + + // screen cull + + if ( hasVertexTexture || ( + screenPositionPixels.x > 0 && + screenPositionPixels.x < viewportWidth && + screenPositionPixels.y > 0 && + screenPositionPixels.y < viewportHeight ) ) { + + // save current RGB to temp texture + + gl.activeTexture( gl.TEXTURE1 ); + gl.bindTexture( gl.TEXTURE_2D, tempTexture ); + gl.copyTexImage2D( gl.TEXTURE_2D, 0, gl.RGB, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 ); + + + // render pink quad + + gl.uniform1i( uniforms.renderType, 0 ); + gl.uniform2f( uniforms.scale, scale.x, scale.y ); + gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z ); + + gl.disable( gl.BLEND ); + gl.enable( gl.DEPTH_TEST ); + + gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); + + + // copy result to occlusionMap + + gl.activeTexture( gl.TEXTURE0 ); + gl.bindTexture( gl.TEXTURE_2D, occlusionTexture ); + gl.copyTexImage2D( gl.TEXTURE_2D, 0, gl.RGBA, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 ); + + + // restore graphics + + gl.uniform1i( uniforms.renderType, 1 ); + gl.disable( gl.DEPTH_TEST ); + + gl.activeTexture( gl.TEXTURE1 ); + gl.bindTexture( gl.TEXTURE_2D, tempTexture ); + gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); + + + // update object positions + + flare.positionScreen.copy( screenPosition ) + + if ( flare.customUpdateCallback ) { + + flare.customUpdateCallback( flare ); + + } else { + + flare.updateLensFlares(); + + } + + // render flares + + gl.uniform1i( uniforms.renderType, 2 ); + gl.enable( gl.BLEND ); + + for ( var j = 0, jl = flare.lensFlares.length; j < jl; j ++ ) { + + var sprite = flare.lensFlares[ j ]; + + if ( sprite.opacity > 0.001 && sprite.scale > 0.001 ) { + + screenPosition.x = sprite.x; + screenPosition.y = sprite.y; + screenPosition.z = sprite.z; + + size = sprite.size * sprite.scale / viewportHeight; + + scale.x = size * invAspect; + scale.y = size; + + gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z ); + gl.uniform2f( uniforms.scale, scale.x, scale.y ); + gl.uniform1f( uniforms.rotation, sprite.rotation ); + + gl.uniform1f( uniforms.opacity, sprite.opacity ); + gl.uniform3f( uniforms.color, sprite.color.r, sprite.color.g, sprite.color.b ); + + renderer.setBlending( sprite.blending, sprite.blendEquation, sprite.blendSrc, sprite.blendDst ); + renderer.setTexture( sprite.texture, 1 ); + + gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); + + } + + } + + } + + } + + // restore gl + + gl.enable( gl.CULL_FACE ); + gl.enable( gl.DEPTH_TEST ); + gl.depthMask( true ); + + renderer.resetGLState(); + + }; + + function createProgram ( shader ) { + + var program = gl.createProgram(); + + var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER ); + var vertexShader = gl.createShader( gl.VERTEX_SHADER ); + + var prefix = "precision " + renderer.getPrecision() + " float;\n"; + + gl.shaderSource( fragmentShader, prefix + shader.fragmentShader ); + gl.shaderSource( vertexShader, prefix + shader.vertexShader ); + + gl.compileShader( fragmentShader ); + gl.compileShader( vertexShader ); + + gl.attachShader( program, fragmentShader ); + gl.attachShader( program, vertexShader ); + + gl.linkProgram( program ); + + return program; + + } + +}; + +// File:src/renderers/webgl/plugins/ShadowMapPlugin.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.ShadowMapPlugin = function ( _renderer, _lights, _webglObjects, _webglObjectsImmediate ) { + + var _gl = _renderer.context; + + var _depthMaterial, _depthMaterialMorph, _depthMaterialSkin, _depthMaterialMorphSkin, + + _frustum = new THREE.Frustum(), + _projScreenMatrix = new THREE.Matrix4(), + + _min = new THREE.Vector3(), + _max = new THREE.Vector3(), + + _matrixPosition = new THREE.Vector3(), + + _renderList = []; + + // init + + var depthShader = THREE.ShaderLib[ "depthRGBA" ]; + var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms ); + + _depthMaterial = new THREE.ShaderMaterial( { + uniforms: depthUniforms, + vertexShader: depthShader.vertexShader, + fragmentShader: depthShader.fragmentShader + } ); + + _depthMaterialMorph = new THREE.ShaderMaterial( { + uniforms: depthUniforms, + vertexShader: depthShader.vertexShader, + fragmentShader: depthShader.fragmentShader, + morphTargets: true + } ); + + _depthMaterialSkin = new THREE.ShaderMaterial( { + uniforms: depthUniforms, + vertexShader: depthShader.vertexShader, + fragmentShader: depthShader.fragmentShader, + skinning: true + } ); + + _depthMaterialMorphSkin = new THREE.ShaderMaterial( { + uniforms: depthUniforms, + vertexShader: depthShader.vertexShader, + fragmentShader: depthShader.fragmentShader, + morphTargets: true, + skinning: true + } ); + + _depthMaterial._shadowPass = true; + _depthMaterialMorph._shadowPass = true; + _depthMaterialSkin._shadowPass = true; + _depthMaterialMorphSkin._shadowPass = true; + + this.render = function ( scene, camera ) { + + if ( _renderer.shadowMapEnabled === false ) return; + + var i, il, j, jl, n, + + shadowMap, shadowMatrix, shadowCamera, + program, buffer, material, + webglObject, object, light, + + lights = [], + k = 0, + + fog = null; + + // set GL state for depth map + + _gl.clearColor( 1, 1, 1, 1 ); + _gl.disable( _gl.BLEND ); + + _gl.enable( _gl.CULL_FACE ); + _gl.frontFace( _gl.CCW ); + + if ( _renderer.shadowMapCullFace === THREE.CullFaceFront ) { + + _gl.cullFace( _gl.FRONT ); + + } else { + + _gl.cullFace( _gl.BACK ); + + } + + _renderer.setDepthTest( true ); + + // preprocess lights + // - skip lights that are not casting shadows + // - create virtual lights for cascaded shadow maps + + for ( i = 0, il = _lights.length; i < il; i ++ ) { + + light = _lights[ i ]; + + if ( ! light.castShadow ) continue; + + if ( ( light instanceof THREE.DirectionalLight ) && light.shadowCascade ) { + + for ( n = 0; n < light.shadowCascadeCount; n ++ ) { + + var virtualLight; + + if ( ! light.shadowCascadeArray[ n ] ) { + + virtualLight = createVirtualLight( light, n ); + virtualLight.originalCamera = camera; + + var gyro = new THREE.Gyroscope(); + gyro.position.copy( light.shadowCascadeOffset ); + + gyro.add( virtualLight ); + gyro.add( virtualLight.target ); + + camera.add( gyro ); + + light.shadowCascadeArray[ n ] = virtualLight; + + console.log( "Created virtualLight", virtualLight ); + + } else { + + virtualLight = light.shadowCascadeArray[ n ]; + + } + + updateVirtualLight( light, n ); + + lights[ k ] = virtualLight; + k ++; + + } + + } else { + + lights[ k ] = light; + k ++; + + } + + } + + // render depth map + + for ( i = 0, il = lights.length; i < il; i ++ ) { + + light = lights[ i ]; + + if ( ! light.shadowMap ) { + + var shadowFilter = THREE.LinearFilter; + + if ( _renderer.shadowMapType === THREE.PCFSoftShadowMap ) { + + shadowFilter = THREE.NearestFilter; + + } + + var pars = { minFilter: shadowFilter, magFilter: shadowFilter, format: THREE.RGBAFormat }; + + light.shadowMap = new THREE.WebGLRenderTarget( light.shadowMapWidth, light.shadowMapHeight, pars ); + light.shadowMapSize = new THREE.Vector2( light.shadowMapWidth, light.shadowMapHeight ); + + light.shadowMatrix = new THREE.Matrix4(); + + } + + if ( ! light.shadowCamera ) { + + if ( light instanceof THREE.SpotLight ) { + + light.shadowCamera = new THREE.PerspectiveCamera( light.shadowCameraFov, light.shadowMapWidth / light.shadowMapHeight, light.shadowCameraNear, light.shadowCameraFar ); + + } else if ( light instanceof THREE.DirectionalLight ) { + + light.shadowCamera = new THREE.OrthographicCamera( light.shadowCameraLeft, light.shadowCameraRight, light.shadowCameraTop, light.shadowCameraBottom, light.shadowCameraNear, light.shadowCameraFar ); + + } else { + + console.error( "Unsupported light type for shadow" ); + continue; + + } + + scene.add( light.shadowCamera ); + + if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); + + } + + if ( light.shadowCameraVisible && ! light.cameraHelper ) { + + light.cameraHelper = new THREE.CameraHelper( light.shadowCamera ); + scene.add( light.cameraHelper ); + + } + + if ( light.isVirtual && virtualLight.originalCamera == camera ) { + + updateShadowCamera( camera, light ); + + } + + shadowMap = light.shadowMap; + shadowMatrix = light.shadowMatrix; + shadowCamera = light.shadowCamera; + + // + + shadowCamera.position.setFromMatrixPosition( light.matrixWorld ); + _matrixPosition.setFromMatrixPosition( light.target.matrixWorld ); + shadowCamera.lookAt( _matrixPosition ); + shadowCamera.updateMatrixWorld(); + + shadowCamera.matrixWorldInverse.getInverse( shadowCamera.matrixWorld ); + + // + + if ( light.cameraHelper ) light.cameraHelper.visible = light.shadowCameraVisible; + if ( light.shadowCameraVisible ) light.cameraHelper.update(); + + // compute shadow matrix + + shadowMatrix.set( + 0.5, 0.0, 0.0, 0.5, + 0.0, 0.5, 0.0, 0.5, + 0.0, 0.0, 0.5, 0.5, + 0.0, 0.0, 0.0, 1.0 + ); + + shadowMatrix.multiply( shadowCamera.projectionMatrix ); + shadowMatrix.multiply( shadowCamera.matrixWorldInverse ); + + // update camera matrices and frustum + + _projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse ); + _frustum.setFromMatrix( _projScreenMatrix ); + + // render shadow map + + _renderer.setRenderTarget( shadowMap ); + _renderer.clear(); + + // set object matrices & frustum culling + + _renderList.length = 0; + + projectObject( scene, scene, shadowCamera ); + + + // render regular objects + + var objectMaterial, useMorphing, useSkinning; + + for ( j = 0, jl = _renderList.length; j < jl; j ++ ) { + + webglObject = _renderList[ j ]; + + object = webglObject.object; + buffer = webglObject.buffer; + + // culling is overriden globally for all objects + // while rendering depth map + + // need to deal with MeshFaceMaterial somehow + // in that case just use the first of material.materials for now + // (proper solution would require to break objects by materials + // similarly to regular rendering and then set corresponding + // depth materials per each chunk instead of just once per object) + + objectMaterial = getObjectMaterial( object ); + + useMorphing = object.geometry.morphTargets !== undefined && object.geometry.morphTargets.length > 0 && objectMaterial.morphTargets; + useSkinning = object instanceof THREE.SkinnedMesh && objectMaterial.skinning; + + if ( object.customDepthMaterial ) { + + material = object.customDepthMaterial; + + } else if ( useSkinning ) { + + material = useMorphing ? _depthMaterialMorphSkin : _depthMaterialSkin; + + } else if ( useMorphing ) { + + material = _depthMaterialMorph; + + } else { + + material = _depthMaterial; + + } + + _renderer.setMaterialFaces( objectMaterial ); + + if ( buffer instanceof THREE.BufferGeometry ) { + + _renderer.renderBufferDirect( shadowCamera, _lights, fog, material, buffer, object ); + + } else { + + _renderer.renderBuffer( shadowCamera, _lights, fog, material, buffer, object ); + + } + + } + + // set matrices and render immediate objects + + for ( j = 0, jl = _webglObjectsImmediate.length; j < jl; j ++ ) { + + webglObject = _webglObjectsImmediate[ j ]; + object = webglObject.object; + + if ( object.visible && object.castShadow ) { + + object._modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld ); + + _renderer.renderImmediateObject( shadowCamera, _lights, fog, _depthMaterial, object ); + + } + + } + + } + + // restore GL state + + var clearColor = _renderer.getClearColor(), + clearAlpha = _renderer.getClearAlpha(); + + _gl.clearColor( clearColor.r, clearColor.g, clearColor.b, clearAlpha ); + _gl.enable( _gl.BLEND ); + + if ( _renderer.shadowMapCullFace === THREE.CullFaceFront ) { + + _gl.cullFace( _gl.BACK ); + + } + + _renderer.resetGLState(); + + }; + + function projectObject( scene, object, shadowCamera ){ + + if ( object.visible ) { + + var webglObjects = _webglObjects[ object.id ]; + + if ( webglObjects && object.castShadow && (object.frustumCulled === false || _frustum.intersectsObject( object ) === true) ) { + + for ( var i = 0, l = webglObjects.length; i < l; i ++ ) { + + var webglObject = webglObjects[ i ]; + + object._modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld ); + _renderList.push( webglObject ); + + } + + } + + for ( var i = 0, l = object.children.length; i < l; i ++ ) { + + projectObject( scene, object.children[ i ], shadowCamera ); + + } + + } + + } + + function createVirtualLight( light, cascade ) { + + var virtualLight = new THREE.DirectionalLight(); + + virtualLight.isVirtual = true; + + virtualLight.onlyShadow = true; + virtualLight.castShadow = true; + + virtualLight.shadowCameraNear = light.shadowCameraNear; + virtualLight.shadowCameraFar = light.shadowCameraFar; + + virtualLight.shadowCameraLeft = light.shadowCameraLeft; + virtualLight.shadowCameraRight = light.shadowCameraRight; + virtualLight.shadowCameraBottom = light.shadowCameraBottom; + virtualLight.shadowCameraTop = light.shadowCameraTop; + + virtualLight.shadowCameraVisible = light.shadowCameraVisible; + + virtualLight.shadowDarkness = light.shadowDarkness; + + virtualLight.shadowBias = light.shadowCascadeBias[ cascade ]; + virtualLight.shadowMapWidth = light.shadowCascadeWidth[ cascade ]; + virtualLight.shadowMapHeight = light.shadowCascadeHeight[ cascade ]; + + virtualLight.pointsWorld = []; + virtualLight.pointsFrustum = []; + + var pointsWorld = virtualLight.pointsWorld, + pointsFrustum = virtualLight.pointsFrustum; + + for ( var i = 0; i < 8; i ++ ) { + + pointsWorld[ i ] = new THREE.Vector3(); + pointsFrustum[ i ] = new THREE.Vector3(); + + } + + var nearZ = light.shadowCascadeNearZ[ cascade ]; + var farZ = light.shadowCascadeFarZ[ cascade ]; + + pointsFrustum[ 0 ].set( - 1, - 1, nearZ ); + pointsFrustum[ 1 ].set( 1, - 1, nearZ ); + pointsFrustum[ 2 ].set( - 1, 1, nearZ ); + pointsFrustum[ 3 ].set( 1, 1, nearZ ); + + pointsFrustum[ 4 ].set( - 1, - 1, farZ ); + pointsFrustum[ 5 ].set( 1, - 1, farZ ); + pointsFrustum[ 6 ].set( - 1, 1, farZ ); + pointsFrustum[ 7 ].set( 1, 1, farZ ); + + return virtualLight; + + } + + // Synchronize virtual light with the original light + + function updateVirtualLight( light, cascade ) { + + var virtualLight = light.shadowCascadeArray[ cascade ]; + + virtualLight.position.copy( light.position ); + virtualLight.target.position.copy( light.target.position ); + virtualLight.lookAt( virtualLight.target ); + + virtualLight.shadowCameraVisible = light.shadowCameraVisible; + virtualLight.shadowDarkness = light.shadowDarkness; + + virtualLight.shadowBias = light.shadowCascadeBias[ cascade ]; + + var nearZ = light.shadowCascadeNearZ[ cascade ]; + var farZ = light.shadowCascadeFarZ[ cascade ]; + + var pointsFrustum = virtualLight.pointsFrustum; + + pointsFrustum[ 0 ].z = nearZ; + pointsFrustum[ 1 ].z = nearZ; + pointsFrustum[ 2 ].z = nearZ; + pointsFrustum[ 3 ].z = nearZ; + + pointsFrustum[ 4 ].z = farZ; + pointsFrustum[ 5 ].z = farZ; + pointsFrustum[ 6 ].z = farZ; + pointsFrustum[ 7 ].z = farZ; + + } + + // Fit shadow camera's ortho frustum to camera frustum + + function updateShadowCamera( camera, light ) { + + var shadowCamera = light.shadowCamera, + pointsFrustum = light.pointsFrustum, + pointsWorld = light.pointsWorld; + + _min.set( Infinity, Infinity, Infinity ); + _max.set( - Infinity, - Infinity, - Infinity ); + + for ( var i = 0; i < 8; i ++ ) { + + var p = pointsWorld[ i ]; + + p.copy( pointsFrustum[ i ] ); + p.unproject( camera ); + + p.applyMatrix4( shadowCamera.matrixWorldInverse ); + + if ( p.x < _min.x ) _min.x = p.x; + if ( p.x > _max.x ) _max.x = p.x; + + if ( p.y < _min.y ) _min.y = p.y; + if ( p.y > _max.y ) _max.y = p.y; + + if ( p.z < _min.z ) _min.z = p.z; + if ( p.z > _max.z ) _max.z = p.z; + + } + + shadowCamera.left = _min.x; + shadowCamera.right = _max.x; + shadowCamera.top = _max.y; + shadowCamera.bottom = _min.y; + + // can't really fit near/far + //shadowCamera.near = _min.z; + //shadowCamera.far = _max.z; + + shadowCamera.updateProjectionMatrix(); + + } + + // For the moment just ignore objects that have multiple materials with different animation methods + // Only the first material will be taken into account for deciding which depth material to use for shadow maps + + function getObjectMaterial( object ) { + + return object.material instanceof THREE.MeshFaceMaterial + ? object.material.materials[ 0 ] + : object.material; + + }; + +}; + +// File:src/renderers/webgl/plugins/SpritePlugin.js + +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.SpritePlugin = function ( renderer, sprites ) { + + var gl = renderer.context; + + var vertexBuffer, elementBuffer; + var program, attributes, uniforms; + + var texture; + + // decompose matrixWorld + + var spritePosition = new THREE.Vector3(); + var spriteRotation = new THREE.Quaternion(); + var spriteScale = new THREE.Vector3(); + + var init = function () { + + var vertices = new Float32Array( [ + - 0.5, - 0.5, 0, 0, + 0.5, - 0.5, 1, 0, + 0.5, 0.5, 1, 1, + - 0.5, 0.5, 0, 1 + ] ); + + var faces = new Uint16Array( [ + 0, 1, 2, + 0, 2, 3 + ] ); + + vertexBuffer = gl.createBuffer(); + elementBuffer = gl.createBuffer(); + + gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer ); + gl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW ); + + gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); + gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW ); + + program = createProgram(); + + attributes = { + position: gl.getAttribLocation ( program, 'position' ), + uv: gl.getAttribLocation ( program, 'uv' ) + }; + + uniforms = { + uvOffset: gl.getUniformLocation( program, 'uvOffset' ), + uvScale: gl.getUniformLocation( program, 'uvScale' ), + + rotation: gl.getUniformLocation( program, 'rotation' ), + scale: gl.getUniformLocation( program, 'scale' ), + + color: gl.getUniformLocation( program, 'color' ), + map: gl.getUniformLocation( program, 'map' ), + opacity: gl.getUniformLocation( program, 'opacity' ), + + modelViewMatrix: gl.getUniformLocation( program, 'modelViewMatrix' ), + projectionMatrix: gl.getUniformLocation( program, 'projectionMatrix' ), + + fogType: gl.getUniformLocation( program, 'fogType' ), + fogDensity: gl.getUniformLocation( program, 'fogDensity' ), + fogNear: gl.getUniformLocation( program, 'fogNear' ), + fogFar: gl.getUniformLocation( program, 'fogFar' ), + fogColor: gl.getUniformLocation( program, 'fogColor' ), + + alphaTest: gl.getUniformLocation( program, 'alphaTest' ) + }; + + var canvas = document.createElement( 'canvas' ); + canvas.width = 8; + canvas.height = 8; + + var context = canvas.getContext( '2d' ); + context.fillStyle = 'white'; + context.fillRect( 0, 0, 8, 8 ); + + texture = new THREE.Texture( canvas ); + texture.needsUpdate = true; + + }; + + this.render = function ( scene, camera ) { + + if ( sprites.length === 0 ) return; + + // setup gl + + if ( program === undefined ) { + + init(); + + } + + gl.useProgram( program ); + + gl.enableVertexAttribArray( attributes.position ); + gl.enableVertexAttribArray( attributes.uv ); + + gl.disable( gl.CULL_FACE ); + gl.enable( gl.BLEND ); + + gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer ); + gl.vertexAttribPointer( attributes.position, 2, gl.FLOAT, false, 2 * 8, 0 ); + gl.vertexAttribPointer( attributes.uv, 2, gl.FLOAT, false, 2 * 8, 8 ); + + gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); + + gl.uniformMatrix4fv( uniforms.projectionMatrix, false, camera.projectionMatrix.elements ); + + gl.activeTexture( gl.TEXTURE0 ); + gl.uniform1i( uniforms.map, 0 ); + + var oldFogType = 0; + var sceneFogType = 0; + var fog = scene.fog; + + if ( fog ) { + + gl.uniform3f( uniforms.fogColor, fog.color.r, fog.color.g, fog.color.b ); + + if ( fog instanceof THREE.Fog ) { + + gl.uniform1f( uniforms.fogNear, fog.near ); + gl.uniform1f( uniforms.fogFar, fog.far ); + + gl.uniform1i( uniforms.fogType, 1 ); + oldFogType = 1; + sceneFogType = 1; + + } else if ( fog instanceof THREE.FogExp2 ) { + + gl.uniform1f( uniforms.fogDensity, fog.density ); + + gl.uniform1i( uniforms.fogType, 2 ); + oldFogType = 2; + sceneFogType = 2; + + } + + } else { + + gl.uniform1i( uniforms.fogType, 0 ); + oldFogType = 0; + sceneFogType = 0; + + } + + + // update positions and sort + + for ( var i = 0, l = sprites.length; i < l; i ++ ) { + + var sprite = sprites[ i ]; + + sprite._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, sprite.matrixWorld ); + sprite.z = - sprite._modelViewMatrix.elements[ 14 ]; + + } + + sprites.sort( painterSortStable ); + + // render all sprites + + var scale = []; + + for ( var i = 0, l = sprites.length; i < l; i ++ ) { + + var sprite = sprites[ i ]; + var material = sprite.material; + + gl.uniform1f( uniforms.alphaTest, material.alphaTest ); + gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, sprite._modelViewMatrix.elements ); + + sprite.matrixWorld.decompose( spritePosition, spriteRotation, spriteScale ); + + scale[ 0 ] = spriteScale.x; + scale[ 1 ] = spriteScale.y; + + var fogType = 0; + + if ( scene.fog && material.fog ) { + + fogType = sceneFogType; + + } + + if ( oldFogType !== fogType ) { + + gl.uniform1i( uniforms.fogType, fogType ); + oldFogType = fogType; + + } + + if ( material.map !== null ) { + + gl.uniform2f( uniforms.uvOffset, material.map.offset.x, material.map.offset.y ); + gl.uniform2f( uniforms.uvScale, material.map.repeat.x, material.map.repeat.y ); + + } else { + + gl.uniform2f( uniforms.uvOffset, 0, 0 ); + gl.uniform2f( uniforms.uvScale, 1, 1 ); + + } + + gl.uniform1f( uniforms.opacity, material.opacity ); + gl.uniform3f( uniforms.color, material.color.r, material.color.g, material.color.b ); + + gl.uniform1f( uniforms.rotation, material.rotation ); + gl.uniform2fv( uniforms.scale, scale ); + + renderer.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst ); + renderer.setDepthTest( material.depthTest ); + renderer.setDepthWrite( material.depthWrite ); + + if ( material.map && material.map.image && material.map.image.width ) { + + renderer.setTexture( material.map, 0 ); + + } else { + + renderer.setTexture( texture, 0 ); + + } + + gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); + + } + + // restore gl + + gl.enable( gl.CULL_FACE ); + + renderer.resetGLState(); + + }; + + function createProgram () { + + var program = gl.createProgram(); + + var vertexShader = gl.createShader( gl.VERTEX_SHADER ); + var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER ); + + gl.shaderSource( vertexShader, [ + + 'precision ' + renderer.getPrecision() + ' float;', + + 'uniform mat4 modelViewMatrix;', + 'uniform mat4 projectionMatrix;', + 'uniform float rotation;', + 'uniform vec2 scale;', + 'uniform vec2 uvOffset;', + 'uniform vec2 uvScale;', + + 'attribute vec2 position;', + 'attribute vec2 uv;', + + 'varying vec2 vUV;', + + 'void main() {', + + 'vUV = uvOffset + uv * uvScale;', + + 'vec2 alignedPosition = position * scale;', + + 'vec2 rotatedPosition;', + 'rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;', + 'rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;', + + 'vec4 finalPosition;', + + 'finalPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );', + 'finalPosition.xy += rotatedPosition;', + 'finalPosition = projectionMatrix * finalPosition;', + + 'gl_Position = finalPosition;', + + '}' + + ].join( '\n' ) ); + + gl.shaderSource( fragmentShader, [ + + 'precision ' + renderer.getPrecision() + ' float;', + + 'uniform vec3 color;', + 'uniform sampler2D map;', + 'uniform float opacity;', + + 'uniform int fogType;', + 'uniform vec3 fogColor;', + 'uniform float fogDensity;', + 'uniform float fogNear;', + 'uniform float fogFar;', + 'uniform float alphaTest;', + + 'varying vec2 vUV;', + + 'void main() {', + + 'vec4 texture = texture2D( map, vUV );', + + 'if ( texture.a < alphaTest ) discard;', + + 'gl_FragColor = vec4( color * texture.xyz, texture.a * opacity );', + + 'if ( fogType > 0 ) {', + + 'float depth = gl_FragCoord.z / gl_FragCoord.w;', + 'float fogFactor = 0.0;', + + 'if ( fogType == 1 ) {', + + 'fogFactor = smoothstep( fogNear, fogFar, depth );', + + '} else {', + + 'const float LOG2 = 1.442695;', + 'float fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );', + 'fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );', + + '}', + + 'gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );', + + '}', + + '}' + + ].join( '\n' ) ); + + gl.compileShader( vertexShader ); + gl.compileShader( fragmentShader ); + + gl.attachShader( program, vertexShader ); + gl.attachShader( program, fragmentShader ); + + gl.linkProgram( program ); + + return program; + + }; + + function painterSortStable ( a, b ) { + + if ( a.z !== b.z ) { + + return b.z - a.z; + + } else { + + return b.id - a.id; + + } + + }; + +}; + +// File:src/extras/GeometryUtils.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.GeometryUtils = { + + merge: function ( geometry1, geometry2, materialIndexOffset ) { + + console.warn( 'THREE.GeometryUtils: .merge() has been moved to Geometry. Use geometry.merge( geometry2, matrix, materialIndexOffset ) instead.' ); + + var matrix; + + if ( geometry2 instanceof THREE.Mesh ) { + + geometry2.matrixAutoUpdate && geometry2.updateMatrix(); + + matrix = geometry2.matrix; + geometry2 = geometry2.geometry; + + } + + geometry1.merge( geometry2, matrix, materialIndexOffset ); + + }, + + center: function ( geometry ) { + + console.warn( 'THREE.GeometryUtils: .center() has been moved to Geometry. Use geometry.center() instead.' ); + return geometry.center(); + + } + +}; + +// File:src/extras/ImageUtils.js + +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + * @author Daosheng Mu / https://github.com/DaoshengMu/ + */ + +THREE.ImageUtils = { + + crossOrigin: undefined, + + loadTexture: function ( url, mapping, onLoad, onError ) { + + var loader = new THREE.ImageLoader(); + loader.crossOrigin = this.crossOrigin; + + var texture = new THREE.Texture( undefined, mapping ); + + loader.load( url, function ( image ) { + + texture.image = image; + texture.needsUpdate = true; + + if ( onLoad ) onLoad( texture ); + + }, undefined, function ( event ) { + + if ( onError ) onError( event ); + + } ); + + texture.sourceFile = url; + + return texture; + + }, + + loadTextureCube: function ( array, mapping, onLoad, onError ) { + + var images = []; + + var loader = new THREE.ImageLoader(); + loader.crossOrigin = this.crossOrigin; + + var texture = new THREE.CubeTexture( images, mapping ); + + // no flipping needed for cube textures + + texture.flipY = false; + + var loaded = 0; + + var loadTexture = function ( i ) { + + loader.load( array[ i ], function ( image ) { + + texture.images[ i ] = image; + + loaded += 1; + + if ( loaded === 6 ) { + + texture.needsUpdate = true; + + if ( onLoad ) onLoad( texture ); + + } + + }, undefined, onError ); + + } + + for ( var i = 0, il = array.length; i < il; ++ i ) { + + loadTexture( i ); + + } + + return texture; + + }, + + loadCompressedTexture: function () { + + console.error( 'THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead.' ) + + }, + + loadCompressedTextureCube: function () { + + console.error( 'THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead.' ) + + }, + + getNormalMap: function ( image, depth ) { + + // Adapted from http://www.paulbrunt.co.uk/lab/heightnormal/ + + var cross = function ( a, b ) { + + return [ a[ 1 ] * b[ 2 ] - a[ 2 ] * b[ 1 ], a[ 2 ] * b[ 0 ] - a[ 0 ] * b[ 2 ], a[ 0 ] * b[ 1 ] - a[ 1 ] * b[ 0 ] ]; + + } + + var subtract = function ( a, b ) { + + return [ a[ 0 ] - b[ 0 ], a[ 1 ] - b[ 1 ], a[ 2 ] - b[ 2 ] ]; + + } + + var normalize = function ( a ) { + + var l = Math.sqrt( a[ 0 ] * a[ 0 ] + a[ 1 ] * a[ 1 ] + a[ 2 ] * a[ 2 ] ); + return [ a[ 0 ] / l, a[ 1 ] / l, a[ 2 ] / l ]; + + } + + depth = depth | 1; + + var width = image.width; + var height = image.height; + + var canvas = document.createElement( 'canvas' ); + canvas.width = width; + canvas.height = height; + + var context = canvas.getContext( '2d' ); + context.drawImage( image, 0, 0 ); + + var data = context.getImageData( 0, 0, width, height ).data; + var imageData = context.createImageData( width, height ); + var output = imageData.data; + + for ( var x = 0; x < width; x ++ ) { + + for ( var y = 0; y < height; y ++ ) { + + var ly = y - 1 < 0 ? 0 : y - 1; + var uy = y + 1 > height - 1 ? height - 1 : y + 1; + var lx = x - 1 < 0 ? 0 : x - 1; + var ux = x + 1 > width - 1 ? width - 1 : x + 1; + + var points = []; + var origin = [ 0, 0, data[ ( y * width + x ) * 4 ] / 255 * depth ]; + points.push( [ - 1, 0, data[ ( y * width + lx ) * 4 ] / 255 * depth ] ); + points.push( [ - 1, - 1, data[ ( ly * width + lx ) * 4 ] / 255 * depth ] ); + points.push( [ 0, - 1, data[ ( ly * width + x ) * 4 ] / 255 * depth ] ); + points.push( [ 1, - 1, data[ ( ly * width + ux ) * 4 ] / 255 * depth ] ); + points.push( [ 1, 0, data[ ( y * width + ux ) * 4 ] / 255 * depth ] ); + points.push( [ 1, 1, data[ ( uy * width + ux ) * 4 ] / 255 * depth ] ); + points.push( [ 0, 1, data[ ( uy * width + x ) * 4 ] / 255 * depth ] ); + points.push( [ - 1, 1, data[ ( uy * width + lx ) * 4 ] / 255 * depth ] ); + + var normals = []; + var num_points = points.length; + + for ( var i = 0; i < num_points; i ++ ) { + + var v1 = points[ i ]; + var v2 = points[ ( i + 1 ) % num_points ]; + v1 = subtract( v1, origin ); + v2 = subtract( v2, origin ); + normals.push( normalize( cross( v1, v2 ) ) ); + + } + + var normal = [ 0, 0, 0 ]; + + for ( var i = 0; i < normals.length; i ++ ) { + + normal[ 0 ] += normals[ i ][ 0 ]; + normal[ 1 ] += normals[ i ][ 1 ]; + normal[ 2 ] += normals[ i ][ 2 ]; + + } + + normal[ 0 ] /= normals.length; + normal[ 1 ] /= normals.length; + normal[ 2 ] /= normals.length; + + var idx = ( y * width + x ) * 4; + + output[ idx ] = ( ( normal[ 0 ] + 1.0 ) / 2.0 * 255 ) | 0; + output[ idx + 1 ] = ( ( normal[ 1 ] + 1.0 ) / 2.0 * 255 ) | 0; + output[ idx + 2 ] = ( normal[ 2 ] * 255 ) | 0; + output[ idx + 3 ] = 255; + + } + + } + + context.putImageData( imageData, 0, 0 ); + + return canvas; + + }, + + generateDataTexture: function ( width, height, color ) { + + var size = width * height; + var data = new Uint8Array( 3 * size ); + + var r = Math.floor( color.r * 255 ); + var g = Math.floor( color.g * 255 ); + var b = Math.floor( color.b * 255 ); + + for ( var i = 0; i < size; i ++ ) { + + data[ i * 3 ] = r; + data[ i * 3 + 1 ] = g; + data[ i * 3 + 2 ] = b; + + } + + var texture = new THREE.DataTexture( data, width, height, THREE.RGBFormat ); + texture.needsUpdate = true; + + return texture; + + } + +}; + +// File:src/extras/SceneUtils.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.SceneUtils = { + + createMultiMaterialObject: function ( geometry, materials ) { + + var group = new THREE.Object3D(); + + for ( var i = 0, l = materials.length; i < l; i ++ ) { + + group.add( new THREE.Mesh( geometry, materials[ i ] ) ); + + } + + return group; + + }, + + detach: function ( child, parent, scene ) { + + child.applyMatrix( parent.matrixWorld ); + parent.remove( child ); + scene.add( child ); + + }, + + attach: function ( child, scene, parent ) { + + var matrixWorldInverse = new THREE.Matrix4(); + matrixWorldInverse.getInverse( parent.matrixWorld ); + child.applyMatrix( matrixWorldInverse ); + + scene.remove( child ); + parent.add( child ); + + } + +}; + +// File:src/extras/FontUtils.js + +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * @author alteredq / http://alteredqualia.com/ + * + * For Text operations in three.js (See TextGeometry) + * + * It uses techniques used in: + * + * typeface.js and canvastext + * For converting fonts and rendering with javascript + * http://typeface.neocracy.org + * + * Triangulation ported from AS3 + * Simple Polygon Triangulation + * http://actionsnippet.com/?p=1462 + * + * A Method to triangulate shapes with holes + * http://www.sakri.net/blog/2009/06/12/an-approach-to-triangulating-polygons-with-holes/ + * + */ + +THREE.FontUtils = { + + faces: {}, + + // Just for now. face[weight][style] + + face: 'helvetiker', + weight: 'normal', + style: 'normal', + size: 150, + divisions: 10, + + getFace: function () { + + try { + + return this.faces[ this.face ][ this.weight ][ this.style ]; + + } catch (e) { + + throw "The font " + this.face + " with " + this.weight + " weight and " + this.style + " style is missing." + + }; + + }, + + loadFace: function ( data ) { + + var family = data.familyName.toLowerCase(); + + var ThreeFont = this; + + ThreeFont.faces[ family ] = ThreeFont.faces[ family ] || {}; + + ThreeFont.faces[ family ][ data.cssFontWeight ] = ThreeFont.faces[ family ][ data.cssFontWeight ] || {}; + ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data; + + var face = ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data; + + return data; + + }, + + drawText: function ( text ) { + + var characterPts = [], allPts = []; + + // RenderText + + var i, p, + face = this.getFace(), + scale = this.size / face.resolution, + offset = 0, + chars = String( text ).split( '' ), + length = chars.length; + + var fontPaths = []; + + for ( i = 0; i < length; i ++ ) { + + var path = new THREE.Path(); + + var ret = this.extractGlyphPoints( chars[ i ], face, scale, offset, path ); + offset += ret.offset; + + fontPaths.push( ret.path ); + + } + + // get the width + + var width = offset / 2; + // + // for ( p = 0; p < allPts.length; p++ ) { + // + // allPts[ p ].x -= width; + // + // } + + //var extract = this.extractPoints( allPts, characterPts ); + //extract.contour = allPts; + + //extract.paths = fontPaths; + //extract.offset = width; + + return { paths: fontPaths, offset: width }; + + }, + + + + + extractGlyphPoints: function ( c, face, scale, offset, path ) { + + var pts = []; + + var i, i2, divisions, + outline, action, length, + scaleX, scaleY, + x, y, cpx, cpy, cpx0, cpy0, cpx1, cpy1, cpx2, cpy2, + laste, + glyph = face.glyphs[ c ] || face.glyphs[ '?' ]; + + if ( ! glyph ) return; + + if ( glyph.o ) { + + outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) ); + length = outline.length; + + scaleX = scale; + scaleY = scale; + + for ( i = 0; i < length; ) { + + action = outline[ i ++ ]; + + //console.log( action ); + + switch ( action ) { + + case 'm': + + // Move To + + x = outline[ i ++ ] * scaleX + offset; + y = outline[ i ++ ] * scaleY; + + path.moveTo( x, y ); + break; + + case 'l': + + // Line To + + x = outline[ i ++ ] * scaleX + offset; + y = outline[ i ++ ] * scaleY; + path.lineTo( x,y ); + break; + + case 'q': + + // QuadraticCurveTo + + cpx = outline[ i ++ ] * scaleX + offset; + cpy = outline[ i ++ ] * scaleY; + cpx1 = outline[ i ++ ] * scaleX + offset; + cpy1 = outline[ i ++ ] * scaleY; + + path.quadraticCurveTo( cpx1, cpy1, cpx, cpy ); + + laste = pts[ pts.length - 1 ]; + + if ( laste ) { + + cpx0 = laste.x; + cpy0 = laste.y; + + for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2 ++ ) { + + var t = i2 / divisions; + var tx = THREE.Shape.Utils.b2( t, cpx0, cpx1, cpx ); + var ty = THREE.Shape.Utils.b2( t, cpy0, cpy1, cpy ); + } + + } + + break; + + case 'b': + + // Cubic Bezier Curve + + cpx = outline[ i ++ ] * scaleX + offset; + cpy = outline[ i ++ ] * scaleY; + cpx1 = outline[ i ++ ] * scaleX + offset; + cpy1 = outline[ i ++ ] * scaleY; + cpx2 = outline[ i ++ ] * scaleX + offset; + cpy2 = outline[ i ++ ] * scaleY; + + path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy ); + + laste = pts[ pts.length - 1 ]; + + if ( laste ) { + + cpx0 = laste.x; + cpy0 = laste.y; + + for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2 ++ ) { + + var t = i2 / divisions; + var tx = THREE.Shape.Utils.b3( t, cpx0, cpx1, cpx2, cpx ); + var ty = THREE.Shape.Utils.b3( t, cpy0, cpy1, cpy2, cpy ); + + } + + } + + break; + + } + + } + } + + + + return { offset: glyph.ha * scale, path:path }; + } + +}; + + +THREE.FontUtils.generateShapes = function ( text, parameters ) { + + // Parameters + + parameters = parameters || {}; + + var size = parameters.size !== undefined ? parameters.size : 100; + var curveSegments = parameters.curveSegments !== undefined ? parameters.curveSegments : 4; + + var font = parameters.font !== undefined ? parameters.font : 'helvetiker'; + var weight = parameters.weight !== undefined ? parameters.weight : 'normal'; + var style = parameters.style !== undefined ? parameters.style : 'normal'; + + THREE.FontUtils.size = size; + THREE.FontUtils.divisions = curveSegments; + + THREE.FontUtils.face = font; + THREE.FontUtils.weight = weight; + THREE.FontUtils.style = style; + + // Get a Font data json object + + var data = THREE.FontUtils.drawText( text ); + + var paths = data.paths; + var shapes = []; + + for ( var p = 0, pl = paths.length; p < pl; p ++ ) { + + Array.prototype.push.apply( shapes, paths[ p ].toShapes() ); + + } + + return shapes; + +}; + + +/** + * This code is a quick port of code written in C++ which was submitted to + * flipcode.com by John W. Ratcliff // July 22, 2000 + * See original code and more information here: + * http://www.flipcode.com/archives/Efficient_Polygon_Triangulation.shtml + * + * ported to actionscript by Zevan Rosser + * www.actionsnippet.com + * + * ported to javascript by Joshua Koo + * http://www.lab4games.net/zz85/blog + * + */ + + +( function ( namespace ) { + + var EPSILON = 0.0000000001; + + // takes in an contour array and returns + + var process = function ( contour, indices ) { + + var n = contour.length; + + if ( n < 3 ) return null; + + var result = [], + verts = [], + vertIndices = []; + + /* we want a counter-clockwise polygon in verts */ + + var u, v, w; + + if ( area( contour ) > 0.0 ) { + + for ( v = 0; v < n; v ++ ) verts[ v ] = v; + + } else { + + for ( v = 0; v < n; v ++ ) verts[ v ] = ( n - 1 ) - v; + + } + + var nv = n; + + /* remove nv - 2 vertices, creating 1 triangle every time */ + + var count = 2 * nv; /* error detection */ + + for ( v = nv - 1; nv > 2; ) { + + /* if we loop, it is probably a non-simple polygon */ + + if ( ( count -- ) <= 0 ) { + + //** Triangulate: ERROR - probable bad polygon! + + //throw ( "Warning, unable to triangulate polygon!" ); + //return null; + // Sometimes warning is fine, especially polygons are triangulated in reverse. + console.log( 'Warning, unable to triangulate polygon!' ); + + if ( indices ) return vertIndices; + return result; + + } + + /* three consecutive vertices in current polygon, <u,v,w> */ + + u = v; if ( nv <= u ) u = 0; /* previous */ + v = u + 1; if ( nv <= v ) v = 0; /* new v */ + w = v + 1; if ( nv <= w ) w = 0; /* next */ + + if ( snip( contour, u, v, w, nv, verts ) ) { + + var a, b, c, s, t; + + /* true names of the vertices */ + + a = verts[ u ]; + b = verts[ v ]; + c = verts[ w ]; + + /* output Triangle */ + + result.push( [ contour[ a ], + contour[ b ], + contour[ c ] ] ); + + + vertIndices.push( [ verts[ u ], verts[ v ], verts[ w ] ] ); + + /* remove v from the remaining polygon */ + + for ( s = v, t = v + 1; t < nv; s++, t++ ) { + + verts[ s ] = verts[ t ]; + + } + + nv --; + + /* reset error detection counter */ + + count = 2 * nv; + + } + + } + + if ( indices ) return vertIndices; + return result; + + }; + + // calculate area of the contour polygon + + var area = function ( contour ) { + + var n = contour.length; + var a = 0.0; + + for ( var p = n - 1, q = 0; q < n; p = q ++ ) { + + a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y; + + } + + return a * 0.5; + + }; + + var snip = function ( contour, u, v, w, n, verts ) { + + var p; + var ax, ay, bx, by; + var cx, cy, px, py; + + ax = contour[ verts[ u ] ].x; + ay = contour[ verts[ u ] ].y; + + bx = contour[ verts[ v ] ].x; + by = contour[ verts[ v ] ].y; + + cx = contour[ verts[ w ] ].x; + cy = contour[ verts[ w ] ].y; + + if ( EPSILON > ( ( ( bx - ax ) * ( cy - ay ) ) - ( ( by - ay ) * ( cx - ax ) ) ) ) return false; + + var aX, aY, bX, bY, cX, cY; + var apx, apy, bpx, bpy, cpx, cpy; + var cCROSSap, bCROSScp, aCROSSbp; + + aX = cx - bx; aY = cy - by; + bX = ax - cx; bY = ay - cy; + cX = bx - ax; cY = by - ay; + + for ( p = 0; p < n; p ++ ) { + + px = contour[ verts[ p ] ].x + py = contour[ verts[ p ] ].y + + if ( ( ( px === ax ) && ( py === ay ) ) || + ( ( px === bx ) && ( py === by ) ) || + ( ( px === cx ) && ( py === cy ) ) ) continue; + + apx = px - ax; apy = py - ay; + bpx = px - bx; bpy = py - by; + cpx = px - cx; cpy = py - cy; + + // see if p is inside triangle abc + + aCROSSbp = aX * bpy - aY * bpx; + cCROSSap = cX * apy - cY * apx; + bCROSScp = bX * cpy - bY * cpx; + + if ( ( aCROSSbp >= - EPSILON ) && ( bCROSScp >= - EPSILON ) && ( cCROSSap >= - EPSILON ) ) return false; + + } + + return true; + + }; + + + namespace.Triangulate = process; + namespace.Triangulate.area = area; + + return namespace; + +} )( THREE.FontUtils ); + +// To use the typeface.js face files, hook up the API +self._typeface_js = { faces: THREE.FontUtils.faces, loadFace: THREE.FontUtils.loadFace }; +THREE.typeface_js = self._typeface_js; + +// File:src/extras/audio/Audio.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Audio = function ( listener ) { + + THREE.Object3D.call( this ); + + this.type = 'Audio'; + + this.context = listener.context; + this.source = this.context.createBufferSource(); + + this.gain = this.context.createGain(); + this.gain.connect( this.context.destination ); + + this.panner = this.context.createPanner(); + this.panner.connect( this.gain ); + +}; + +THREE.Audio.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Audio.prototype.constructor = THREE.Audio; + +THREE.Audio.prototype.load = function ( file ) { + + var scope = this; + + var request = new XMLHttpRequest(); + request.open( 'GET', file, true ); + request.responseType = 'arraybuffer'; + request.onload = function ( e ) { + + scope.context.decodeAudioData( this.response, function ( buffer ) { + + scope.source.buffer = buffer; + scope.source.connect( scope.panner ); + scope.source.start( 0 ); + + } ); + + }; + request.send(); + + return this; + +}; + +THREE.Audio.prototype.setLoop = function ( value ) { + + this.source.loop = value; + +}; + +THREE.Audio.prototype.setRefDistance = function ( value ) { + + this.panner.refDistance = value; + +}; + +THREE.Audio.prototype.setRolloffFactor = function ( value ) { + + this.panner.rolloffFactor = value; + +}; + +THREE.Audio.prototype.updateMatrixWorld = ( function () { + + var position = new THREE.Vector3(); + + return function ( force ) { + + THREE.Object3D.prototype.updateMatrixWorld.call( this, force ); + + position.setFromMatrixPosition( this.matrixWorld ); + + this.panner.setPosition( position.x, position.y, position.z ); + + }; + +} )(); + +// File:src/extras/audio/AudioListener.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.AudioListener = function () { + + THREE.Object3D.call( this ); + + this.type = 'AudioListener'; + + this.context = new ( window.AudioContext || window.webkitAudioContext )(); + +}; + +THREE.AudioListener.prototype = Object.create( THREE.Object3D.prototype ); +THREE.AudioListener.prototype.constructor = THREE.AudioListener; + +THREE.AudioListener.prototype.updateMatrixWorld = ( function () { + + var position = new THREE.Vector3(); + var quaternion = new THREE.Quaternion(); + var scale = new THREE.Vector3(); + + var orientation = new THREE.Vector3(); + var velocity = new THREE.Vector3(); + + var positionPrev = new THREE.Vector3(); + + return function ( force ) { + + THREE.Object3D.prototype.updateMatrixWorld.call( this, force ); + + var listener = this.context.listener; + var up = this.up; + + this.matrixWorld.decompose( position, quaternion, scale ); + + orientation.set( 0, 0, -1 ).applyQuaternion( quaternion ); + velocity.subVectors( position, positionPrev ); + + listener.setPosition( position.x, position.y, position.z ); + listener.setOrientation( orientation.x, orientation.y, orientation.z, up.x, up.y, up.z ); + listener.setVelocity( velocity.x, velocity.y, velocity.z ); + + positionPrev.copy( position ); + + }; + +} )(); + +// File:src/extras/core/Curve.js + +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * Extensible curve object + * + * Some common of Curve methods + * .getPoint(t), getTangent(t) + * .getPointAt(u), getTagentAt(u) + * .getPoints(), .getSpacedPoints() + * .getLength() + * .updateArcLengths() + * + * This following classes subclasses THREE.Curve: + * + * -- 2d classes -- + * THREE.LineCurve + * THREE.QuadraticBezierCurve + * THREE.CubicBezierCurve + * THREE.SplineCurve + * THREE.ArcCurve + * THREE.EllipseCurve + * + * -- 3d classes -- + * THREE.LineCurve3 + * THREE.QuadraticBezierCurve3 + * THREE.CubicBezierCurve3 + * THREE.SplineCurve3 + * THREE.ClosedSplineCurve3 + * + * A series of curves can be represented as a THREE.CurvePath + * + **/ + +/************************************************************** + * Abstract Curve base class + **************************************************************/ + +THREE.Curve = function () { + +}; + +// Virtual base class method to overwrite and implement in subclasses +// - t [0 .. 1] + +THREE.Curve.prototype.getPoint = function ( t ) { + + console.log( "Warning, getPoint() not implemented!" ); + return null; + +}; + +// Get point at relative position in curve according to arc length +// - u [0 .. 1] + +THREE.Curve.prototype.getPointAt = function ( u ) { + + var t = this.getUtoTmapping( u ); + return this.getPoint( t ); + +}; + +// Get sequence of points using getPoint( t ) + +THREE.Curve.prototype.getPoints = function ( divisions ) { + + if ( ! divisions ) divisions = 5; + + var d, pts = []; + + for ( d = 0; d <= divisions; d ++ ) { + + pts.push( this.getPoint( d / divisions ) ); + + } + + return pts; + +}; + +// Get sequence of points using getPointAt( u ) + +THREE.Curve.prototype.getSpacedPoints = function ( divisions ) { + + if ( ! divisions ) divisions = 5; + + var d, pts = []; + + for ( d = 0; d <= divisions; d ++ ) { + + pts.push( this.getPointAt( d / divisions ) ); + + } + + return pts; + +}; + +// Get total curve arc length + +THREE.Curve.prototype.getLength = function () { + + var lengths = this.getLengths(); + return lengths[ lengths.length - 1 ]; + +}; + +// Get list of cumulative segment lengths + +THREE.Curve.prototype.getLengths = function ( divisions ) { + + if ( ! divisions ) divisions = (this.__arcLengthDivisions) ? (this.__arcLengthDivisions): 200; + + if ( this.cacheArcLengths + && ( this.cacheArcLengths.length == divisions + 1 ) + && ! this.needsUpdate) { + + //console.log( "cached", this.cacheArcLengths ); + return this.cacheArcLengths; + + } + + this.needsUpdate = false; + + var cache = []; + var current, last = this.getPoint( 0 ); + var p, sum = 0; + + cache.push( 0 ); + + for ( p = 1; p <= divisions; p ++ ) { + + current = this.getPoint ( p / divisions ); + sum += current.distanceTo( last ); + cache.push( sum ); + last = current; + + } + + this.cacheArcLengths = cache; + + return cache; // { sums: cache, sum:sum }; Sum is in the last element. + +}; + + +THREE.Curve.prototype.updateArcLengths = function() { + this.needsUpdate = true; + this.getLengths(); +}; + +// Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equi distance + +THREE.Curve.prototype.getUtoTmapping = function ( u, distance ) { + + var arcLengths = this.getLengths(); + + var i = 0, il = arcLengths.length; + + var targetArcLength; // The targeted u distance value to get + + if ( distance ) { + + targetArcLength = distance; + + } else { + + targetArcLength = u * arcLengths[ il - 1 ]; + + } + + //var time = Date.now(); + + // binary search for the index with largest value smaller than target u distance + + var low = 0, high = il - 1, comparison; + + while ( low <= high ) { + + i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats + + comparison = arcLengths[ i ] - targetArcLength; + + if ( comparison < 0 ) { + + low = i + 1; + continue; + + } else if ( comparison > 0 ) { + + high = i - 1; + continue; + + } else { + + high = i; + break; + + // DONE + + } + + } + + i = high; + + //console.log('b' , i, low, high, Date.now()- time); + + if ( arcLengths[ i ] == targetArcLength ) { + + var t = i / ( il - 1 ); + return t; + + } + + // we could get finer grain at lengths, or use simple interpolatation between two points + + var lengthBefore = arcLengths[ i ]; + var lengthAfter = arcLengths[ i + 1 ]; + + var segmentLength = lengthAfter - lengthBefore; + + // determine where we are between the 'before' and 'after' points + + var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength; + + // add that fractional amount to t + + var t = ( i + segmentFraction ) / ( il -1 ); + + return t; + +}; + +// Returns a unit vector tangent at t +// In case any sub curve does not implement its tangent derivation, +// 2 points a small delta apart will be used to find its gradient +// which seems to give a reasonable approximation + +THREE.Curve.prototype.getTangent = function( t ) { + + var delta = 0.0001; + var t1 = t - delta; + var t2 = t + delta; + + // Capping in case of danger + + if ( t1 < 0 ) t1 = 0; + if ( t2 > 1 ) t2 = 1; + + var pt1 = this.getPoint( t1 ); + var pt2 = this.getPoint( t2 ); + + var vec = pt2.clone().sub(pt1); + return vec.normalize(); + +}; + + +THREE.Curve.prototype.getTangentAt = function ( u ) { + + var t = this.getUtoTmapping( u ); + return this.getTangent( t ); + +}; + + + + + +/************************************************************** + * Utils + **************************************************************/ + +THREE.Curve.Utils = { + + tangentQuadraticBezier: function ( t, p0, p1, p2 ) { + + return 2 * ( 1 - t ) * ( p1 - p0 ) + 2 * t * ( p2 - p1 ); + + }, + + // Puay Bing, thanks for helping with this derivative! + + tangentCubicBezier: function (t, p0, p1, p2, p3 ) { + + return - 3 * p0 * (1 - t) * (1 - t) + + 3 * p1 * (1 - t) * (1-t) - 6 *t *p1 * (1-t) + + 6 * t * p2 * (1-t) - 3 * t * t * p2 + + 3 * t * t * p3; + + }, + + tangentSpline: function ( t, p0, p1, p2, p3 ) { + + // To check if my formulas are correct + + var h00 = 6 * t * t - 6 * t; // derived from 2t^3 − 3t^2 + 1 + var h10 = 3 * t * t - 4 * t + 1; // t^3 − 2t^2 + t + var h01 = - 6 * t * t + 6 * t; // − 2t3 + 3t2 + var h11 = 3 * t * t - 2 * t; // t3 − t2 + + return h00 + h10 + h01 + h11; + + }, + + // Catmull-Rom + + interpolate: function( p0, p1, p2, p3, t ) { + + var v0 = ( p2 - p0 ) * 0.5; + var v1 = ( p3 - p1 ) * 0.5; + var t2 = t * t; + var t3 = t * t2; + return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1; + + } + +}; + + +// TODO: Transformation for Curves? + +/************************************************************** + * 3D Curves + **************************************************************/ + +// A Factory method for creating new curve subclasses + +THREE.Curve.create = function ( constructor, getPointFunc ) { + + constructor.prototype = Object.create( THREE.Curve.prototype ); + constructor.prototype.constructor = constructor; + constructor.prototype.getPoint = getPointFunc; + + return constructor; + +}; + +// File:src/extras/core/CurvePath.js + +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * + **/ + +/************************************************************** + * Curved Path - a curve path is simply a array of connected + * curves, but retains the api of a curve + **************************************************************/ + +THREE.CurvePath = function () { + + this.curves = []; + this.bends = []; + + this.autoClose = false; // Automatically closes the path +}; + +THREE.CurvePath.prototype = Object.create( THREE.Curve.prototype ); +THREE.CurvePath.prototype.constructor = THREE.CurvePath; + +THREE.CurvePath.prototype.add = function ( curve ) { + + this.curves.push( curve ); + +}; + +THREE.CurvePath.prototype.checkConnection = function() { + // TODO + // If the ending of curve is not connected to the starting + // or the next curve, then, this is not a real path +}; + +THREE.CurvePath.prototype.closePath = function() { + // TODO Test + // and verify for vector3 (needs to implement equals) + // Add a line curve if start and end of lines are not connected + var startPoint = this.curves[0].getPoint(0); + var endPoint = this.curves[this.curves.length-1].getPoint(1); + + if (! startPoint.equals(endPoint)) { + this.curves.push( new THREE.LineCurve(endPoint, startPoint) ); + } + +}; + +// To get accurate point with reference to +// entire path distance at time t, +// following has to be done: + +// 1. Length of each sub path have to be known +// 2. Locate and identify type of curve +// 3. Get t for the curve +// 4. Return curve.getPointAt(t') + +THREE.CurvePath.prototype.getPoint = function( t ) { + + var d = t * this.getLength(); + var curveLengths = this.getCurveLengths(); + var i = 0, diff, curve; + + // To think about boundaries points. + + while ( i < curveLengths.length ) { + + if ( curveLengths[ i ] >= d ) { + + diff = curveLengths[ i ] - d; + curve = this.curves[ i ]; + + var u = 1 - diff / curve.getLength(); + + return curve.getPointAt( u ); + + break; + } + + i ++; + + } + + return null; + + // loop where sum != 0, sum > d , sum+1 <d + +}; + +/* +THREE.CurvePath.prototype.getTangent = function( t ) { +};*/ + + +// We cannot use the default THREE.Curve getPoint() with getLength() because in +// THREE.Curve, getLength() depends on getPoint() but in THREE.CurvePath +// getPoint() depends on getLength + +THREE.CurvePath.prototype.getLength = function() { + + var lens = this.getCurveLengths(); + return lens[ lens.length - 1 ]; + +}; + +// Compute lengths and cache them +// We cannot overwrite getLengths() because UtoT mapping uses it. + +THREE.CurvePath.prototype.getCurveLengths = function() { + + // We use cache values if curves and cache array are same length + + if ( this.cacheLengths && this.cacheLengths.length == this.curves.length ) { + + return this.cacheLengths; + + }; + + // Get length of subsurve + // Push sums into cached array + + var lengths = [], sums = 0; + var i, il = this.curves.length; + + for ( i = 0; i < il; i ++ ) { + + sums += this.curves[ i ].getLength(); + lengths.push( sums ); + + } + + this.cacheLengths = lengths; + + return lengths; + +}; + + + +// Returns min and max coordinates + +THREE.CurvePath.prototype.getBoundingBox = function () { + + var points = this.getPoints(); + + var maxX, maxY, maxZ; + var minX, minY, minZ; + + maxX = maxY = Number.NEGATIVE_INFINITY; + minX = minY = Number.POSITIVE_INFINITY; + + var p, i, il, sum; + + var v3 = points[0] instanceof THREE.Vector3; + + sum = v3 ? new THREE.Vector3() : new THREE.Vector2(); + + for ( i = 0, il = points.length; i < il; i ++ ) { + + p = points[ i ]; + + if ( p.x > maxX ) maxX = p.x; + else if ( p.x < minX ) minX = p.x; + + if ( p.y > maxY ) maxY = p.y; + else if ( p.y < minY ) minY = p.y; + + if ( v3 ) { + + if ( p.z > maxZ ) maxZ = p.z; + else if ( p.z < minZ ) minZ = p.z; + + } + + sum.add( p ); + + } + + var ret = { + + minX: minX, + minY: minY, + maxX: maxX, + maxY: maxY + + }; + + if ( v3 ) { + + ret.maxZ = maxZ; + ret.minZ = minZ; + + } + + return ret; + +}; + +/************************************************************** + * Create Geometries Helpers + **************************************************************/ + +/// Generate geometry from path points (for Line or Points objects) + +THREE.CurvePath.prototype.createPointsGeometry = function( divisions ) { + + var pts = this.getPoints( divisions, true ); + return this.createGeometry( pts ); + +}; + +// Generate geometry from equidistance sampling along the path + +THREE.CurvePath.prototype.createSpacedPointsGeometry = function( divisions ) { + + var pts = this.getSpacedPoints( divisions, true ); + return this.createGeometry( pts ); + +}; + +THREE.CurvePath.prototype.createGeometry = function( points ) { + + var geometry = new THREE.Geometry(); + + for ( var i = 0; i < points.length; i ++ ) { + + geometry.vertices.push( new THREE.Vector3( points[ i ].x, points[ i ].y, points[ i ].z || 0) ); + + } + + return geometry; + +}; + + +/************************************************************** + * Bend / Wrap Helper Methods + **************************************************************/ + +// Wrap path / Bend modifiers? + +THREE.CurvePath.prototype.addWrapPath = function ( bendpath ) { + + this.bends.push( bendpath ); + +}; + +THREE.CurvePath.prototype.getTransformedPoints = function( segments, bends ) { + + var oldPts = this.getPoints( segments ); // getPoints getSpacedPoints + var i, il; + + if ( ! bends ) { + + bends = this.bends; + + } + + for ( i = 0, il = bends.length; i < il; i ++ ) { + + oldPts = this.getWrapPoints( oldPts, bends[ i ] ); + + } + + return oldPts; + +}; + +THREE.CurvePath.prototype.getTransformedSpacedPoints = function( segments, bends ) { + + var oldPts = this.getSpacedPoints( segments ); + + var i, il; + + if ( ! bends ) { + + bends = this.bends; + + } + + for ( i = 0, il = bends.length; i < il; i ++ ) { + + oldPts = this.getWrapPoints( oldPts, bends[ i ] ); + + } + + return oldPts; + +}; + +// This returns getPoints() bend/wrapped around the contour of a path. +// Read http://www.planetclegg.com/projects/WarpingTextToSplines.html + +THREE.CurvePath.prototype.getWrapPoints = function ( oldPts, path ) { + + var bounds = this.getBoundingBox(); + + var i, il, p, oldX, oldY, xNorm; + + for ( i = 0, il = oldPts.length; i < il; i ++ ) { + + p = oldPts[ i ]; + + oldX = p.x; + oldY = p.y; + + xNorm = oldX / bounds.maxX; + + // If using actual distance, for length > path, requires line extrusions + //xNorm = path.getUtoTmapping(xNorm, oldX); // 3 styles. 1) wrap stretched. 2) wrap stretch by arc length 3) warp by actual distance + + xNorm = path.getUtoTmapping( xNorm, oldX ); + + // check for out of bounds? + + var pathPt = path.getPoint( xNorm ); + var normal = path.getTangent( xNorm ); + normal.set( - normal.y, normal.x ).multiplyScalar( oldY ); + + p.x = pathPt.x + normal.x; + p.y = pathPt.y + normal.y; + + } + + return oldPts; + +}; + + +// File:src/extras/core/Gyroscope.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Gyroscope = function () { + + THREE.Object3D.call( this ); + +}; + +THREE.Gyroscope.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Gyroscope.prototype.constructor = THREE.Gyroscope; + +THREE.Gyroscope.prototype.updateMatrixWorld = ( function () { + + var translationObject = new THREE.Vector3(); + var quaternionObject = new THREE.Quaternion(); + var scaleObject = new THREE.Vector3(); + + var translationWorld = new THREE.Vector3(); + var quaternionWorld = new THREE.Quaternion(); + var scaleWorld = new THREE.Vector3(); + + return function ( force ) { + + this.matrixAutoUpdate && this.updateMatrix(); + + // update matrixWorld + + if ( this.matrixWorldNeedsUpdate || force ) { + + if ( this.parent ) { + + this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); + + this.matrixWorld.decompose( translationWorld, quaternionWorld, scaleWorld ); + this.matrix.decompose( translationObject, quaternionObject, scaleObject ); + + this.matrixWorld.compose( translationWorld, quaternionObject, scaleWorld ); + + + } else { + + this.matrixWorld.copy( this.matrix ); + + } + + + this.matrixWorldNeedsUpdate = false; + + force = true; + + } + + // update children + + for ( var i = 0, l = this.children.length; i < l; i ++ ) { + + this.children[ i ].updateMatrixWorld( force ); + + } + + }; + +}() ); + +// File:src/extras/core/Path.js + +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * Creates free form 2d path using series of points, lines or curves. + * + **/ + +THREE.Path = function ( points ) { + + THREE.CurvePath.call(this); + + this.actions = []; + + if ( points ) { + + this.fromPoints( points ); + + } + +}; + +THREE.Path.prototype = Object.create( THREE.CurvePath.prototype ); +THREE.Path.prototype.constructor = THREE.Path; + +THREE.PathActions = { + + MOVE_TO: 'moveTo', + LINE_TO: 'lineTo', + QUADRATIC_CURVE_TO: 'quadraticCurveTo', // Bezier quadratic curve + BEZIER_CURVE_TO: 'bezierCurveTo', // Bezier cubic curve + CSPLINE_THRU: 'splineThru', // Catmull-rom spline + ARC: 'arc', // Circle + ELLIPSE: 'ellipse' +}; + +// TODO Clean up PATH API + +// Create path using straight lines to connect all points +// - vectors: array of Vector2 + +THREE.Path.prototype.fromPoints = function ( vectors ) { + + this.moveTo( vectors[ 0 ].x, vectors[ 0 ].y ); + + for ( var v = 1, vlen = vectors.length; v < vlen; v ++ ) { + + this.lineTo( vectors[ v ].x, vectors[ v ].y ); + + }; + +}; + +// startPath() endPath()? + +THREE.Path.prototype.moveTo = function ( x, y ) { + + var args = Array.prototype.slice.call( arguments ); + this.actions.push( { action: THREE.PathActions.MOVE_TO, args: args } ); + +}; + +THREE.Path.prototype.lineTo = function ( x, y ) { + + var args = Array.prototype.slice.call( arguments ); + + var lastargs = this.actions[ this.actions.length - 1 ].args; + + var x0 = lastargs[ lastargs.length - 2 ]; + var y0 = lastargs[ lastargs.length - 1 ]; + + var curve = new THREE.LineCurve( new THREE.Vector2( x0, y0 ), new THREE.Vector2( x, y ) ); + this.curves.push( curve ); + + this.actions.push( { action: THREE.PathActions.LINE_TO, args: args } ); + +}; + +THREE.Path.prototype.quadraticCurveTo = function( aCPx, aCPy, aX, aY ) { + + var args = Array.prototype.slice.call( arguments ); + + var lastargs = this.actions[ this.actions.length - 1 ].args; + + var x0 = lastargs[ lastargs.length - 2 ]; + var y0 = lastargs[ lastargs.length - 1 ]; + + var curve = new THREE.QuadraticBezierCurve( new THREE.Vector2( x0, y0 ), + new THREE.Vector2( aCPx, aCPy ), + new THREE.Vector2( aX, aY ) ); + this.curves.push( curve ); + + this.actions.push( { action: THREE.PathActions.QUADRATIC_CURVE_TO, args: args } ); + +}; + +THREE.Path.prototype.bezierCurveTo = function( aCP1x, aCP1y, + aCP2x, aCP2y, + aX, aY ) { + + var args = Array.prototype.slice.call( arguments ); + + var lastargs = this.actions[ this.actions.length - 1 ].args; + + var x0 = lastargs[ lastargs.length - 2 ]; + var y0 = lastargs[ lastargs.length - 1 ]; + + var curve = new THREE.CubicBezierCurve( new THREE.Vector2( x0, y0 ), + new THREE.Vector2( aCP1x, aCP1y ), + new THREE.Vector2( aCP2x, aCP2y ), + new THREE.Vector2( aX, aY ) ); + this.curves.push( curve ); + + this.actions.push( { action: THREE.PathActions.BEZIER_CURVE_TO, args: args } ); + +}; + +THREE.Path.prototype.splineThru = function( pts /*Array of Vector*/ ) { + + var args = Array.prototype.slice.call( arguments ); + var lastargs = this.actions[ this.actions.length - 1 ].args; + + var x0 = lastargs[ lastargs.length - 2 ]; + var y0 = lastargs[ lastargs.length - 1 ]; +//--- + var npts = [ new THREE.Vector2( x0, y0 ) ]; + Array.prototype.push.apply( npts, pts ); + + var curve = new THREE.SplineCurve( npts ); + this.curves.push( curve ); + + this.actions.push( { action: THREE.PathActions.CSPLINE_THRU, args: args } ); + +}; + +// FUTURE: Change the API or follow canvas API? + +THREE.Path.prototype.arc = function ( aX, aY, aRadius, + aStartAngle, aEndAngle, aClockwise ) { + + var lastargs = this.actions[ this.actions.length - 1].args; + var x0 = lastargs[ lastargs.length - 2 ]; + var y0 = lastargs[ lastargs.length - 1 ]; + + this.absarc(aX + x0, aY + y0, aRadius, + aStartAngle, aEndAngle, aClockwise ); + + }; + + THREE.Path.prototype.absarc = function ( aX, aY, aRadius, + aStartAngle, aEndAngle, aClockwise ) { + this.absellipse(aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise); + }; + +THREE.Path.prototype.ellipse = function ( aX, aY, xRadius, yRadius, + aStartAngle, aEndAngle, aClockwise ) { + + var lastargs = this.actions[ this.actions.length - 1].args; + var x0 = lastargs[ lastargs.length - 2 ]; + var y0 = lastargs[ lastargs.length - 1 ]; + + this.absellipse(aX + x0, aY + y0, xRadius, yRadius, + aStartAngle, aEndAngle, aClockwise ); + + }; + + +THREE.Path.prototype.absellipse = function ( aX, aY, xRadius, yRadius, + aStartAngle, aEndAngle, aClockwise ) { + + var args = Array.prototype.slice.call( arguments ); + var curve = new THREE.EllipseCurve( aX, aY, xRadius, yRadius, + aStartAngle, aEndAngle, aClockwise ); + this.curves.push( curve ); + + var lastPoint = curve.getPoint(1); + args.push(lastPoint.x); + args.push(lastPoint.y); + + this.actions.push( { action: THREE.PathActions.ELLIPSE, args: args } ); + + }; + +THREE.Path.prototype.getSpacedPoints = function ( divisions, closedPath ) { + + if ( ! divisions ) divisions = 40; + + var points = []; + + for ( var i = 0; i < divisions; i ++ ) { + + points.push( this.getPoint( i / divisions ) ); + + //if( !this.getPoint( i / divisions ) ) throw "DIE"; + + } + + // if ( closedPath ) { + // + // points.push( points[ 0 ] ); + // + // } + + return points; + +}; + +/* Return an array of vectors based on contour of the path */ + +THREE.Path.prototype.getPoints = function( divisions, closedPath ) { + + if (this.useSpacedPoints) { + console.log('tata'); + return this.getSpacedPoints( divisions, closedPath ); + } + + divisions = divisions || 12; + + var points = []; + + var i, il, item, action, args; + var cpx, cpy, cpx2, cpy2, cpx1, cpy1, cpx0, cpy0, + laste, j, + t, tx, ty; + + for ( i = 0, il = this.actions.length; i < il; i ++ ) { + + item = this.actions[ i ]; + + action = item.action; + args = item.args; + + switch( action ) { + + case THREE.PathActions.MOVE_TO: + + points.push( new THREE.Vector2( args[ 0 ], args[ 1 ] ) ); + + break; + + case THREE.PathActions.LINE_TO: + + points.push( new THREE.Vector2( args[ 0 ], args[ 1 ] ) ); + + break; + + case THREE.PathActions.QUADRATIC_CURVE_TO: + + cpx = args[ 2 ]; + cpy = args[ 3 ]; + + cpx1 = args[ 0 ]; + cpy1 = args[ 1 ]; + + if ( points.length > 0 ) { + + laste = points[ points.length - 1 ]; + + cpx0 = laste.x; + cpy0 = laste.y; + + } else { + + laste = this.actions[ i - 1 ].args; + + cpx0 = laste[ laste.length - 2 ]; + cpy0 = laste[ laste.length - 1 ]; + + } + + for ( j = 1; j <= divisions; j ++ ) { + + t = j / divisions; + + tx = THREE.Shape.Utils.b2( t, cpx0, cpx1, cpx ); + ty = THREE.Shape.Utils.b2( t, cpy0, cpy1, cpy ); + + points.push( new THREE.Vector2( tx, ty ) ); + + } + + break; + + case THREE.PathActions.BEZIER_CURVE_TO: + + cpx = args[ 4 ]; + cpy = args[ 5 ]; + + cpx1 = args[ 0 ]; + cpy1 = args[ 1 ]; + + cpx2 = args[ 2 ]; + cpy2 = args[ 3 ]; + + if ( points.length > 0 ) { + + laste = points[ points.length - 1 ]; + + cpx0 = laste.x; + cpy0 = laste.y; + + } else { + + laste = this.actions[ i - 1 ].args; + + cpx0 = laste[ laste.length - 2 ]; + cpy0 = laste[ laste.length - 1 ]; + + } + + + for ( j = 1; j <= divisions; j ++ ) { + + t = j / divisions; + + tx = THREE.Shape.Utils.b3( t, cpx0, cpx1, cpx2, cpx ); + ty = THREE.Shape.Utils.b3( t, cpy0, cpy1, cpy2, cpy ); + + points.push( new THREE.Vector2( tx, ty ) ); + + } + + break; + + case THREE.PathActions.CSPLINE_THRU: + + laste = this.actions[ i - 1 ].args; + + var last = new THREE.Vector2( laste[ laste.length - 2 ], laste[ laste.length - 1 ] ); + var spts = [ last ]; + + var n = divisions * args[ 0 ].length; + + spts = spts.concat( args[ 0 ] ); + + var spline = new THREE.SplineCurve( spts ); + + for ( j = 1; j <= n; j ++ ) { + + points.push( spline.getPointAt( j / n ) ) ; + + } + + break; + + case THREE.PathActions.ARC: + + var aX = args[ 0 ], aY = args[ 1 ], + aRadius = args[ 2 ], + aStartAngle = args[ 3 ], aEndAngle = args[ 4 ], + aClockwise = !! args[ 5 ]; + + var deltaAngle = aEndAngle - aStartAngle; + var angle; + var tdivisions = divisions * 2; + + for ( j = 1; j <= tdivisions; j ++ ) { + + t = j / tdivisions; + + if ( ! aClockwise ) { + + t = 1 - t; + + } + + angle = aStartAngle + t * deltaAngle; + + tx = aX + aRadius * Math.cos( angle ); + ty = aY + aRadius * Math.sin( angle ); + + //console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty); + + points.push( new THREE.Vector2( tx, ty ) ); + + } + + //console.log(points); + + break; + + case THREE.PathActions.ELLIPSE: + + var aX = args[ 0 ], aY = args[ 1 ], + xRadius = args[ 2 ], + yRadius = args[ 3 ], + aStartAngle = args[ 4 ], aEndAngle = args[ 5 ], + aClockwise = !! args[ 6 ]; + + + var deltaAngle = aEndAngle - aStartAngle; + var angle; + var tdivisions = divisions * 2; + + for ( j = 1; j <= tdivisions; j ++ ) { + + t = j / tdivisions; + + if ( ! aClockwise ) { + + t = 1 - t; + + } + + angle = aStartAngle + t * deltaAngle; + + tx = aX + xRadius * Math.cos( angle ); + ty = aY + yRadius * Math.sin( angle ); + + //console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty); + + points.push( new THREE.Vector2( tx, ty ) ); + + } + + //console.log(points); + + break; + + } // end switch + + } + + + + // Normalize to remove the closing point by default. + var lastPoint = points[ points.length - 1]; + var EPSILON = 0.0000000001; + if ( Math.abs(lastPoint.x - points[ 0 ].x) < EPSILON && + Math.abs(lastPoint.y - points[ 0 ].y) < EPSILON) + points.splice( points.length - 1, 1); + if ( closedPath ) { + + points.push( points[ 0 ] ); + + } + + return points; + +}; + +// +// Breaks path into shapes +// +// Assumptions (if parameter isCCW==true the opposite holds): +// - solid shapes are defined clockwise (CW) +// - holes are defined counterclockwise (CCW) +// +// If parameter noHoles==true: +// - all subPaths are regarded as solid shapes +// - definition order CW/CCW has no relevance +// + +THREE.Path.prototype.toShapes = function( isCCW, noHoles ) { + + function extractSubpaths( inActions ) { + + var i, il, item, action, args; + + var subPaths = [], lastPath = new THREE.Path(); + + for ( i = 0, il = inActions.length; i < il; i ++ ) { + + item = inActions[ i ]; + + args = item.args; + action = item.action; + + if ( action == THREE.PathActions.MOVE_TO ) { + + if ( lastPath.actions.length != 0 ) { + + subPaths.push( lastPath ); + lastPath = new THREE.Path(); + + } + + } + + lastPath[ action ].apply( lastPath, args ); + + } + + if ( lastPath.actions.length != 0 ) { + + subPaths.push( lastPath ); + + } + + // console.log(subPaths); + + return subPaths; + } + + function toShapesNoHoles( inSubpaths ) { + + var shapes = []; + + for ( var i = 0, il = inSubpaths.length; i < il; i ++ ) { + + var tmpPath = inSubpaths[ i ]; + + var tmpShape = new THREE.Shape(); + tmpShape.actions = tmpPath.actions; + tmpShape.curves = tmpPath.curves; + + shapes.push( tmpShape ); + } + + //console.log("shape", shapes); + + return shapes; + }; + + function isPointInsidePolygon( inPt, inPolygon ) { + var EPSILON = 0.0000000001; + + var polyLen = inPolygon.length; + + // inPt on polygon contour => immediate success or + // toggling of inside/outside at every single! intersection point of an edge + // with the horizontal line through inPt, left of inPt + // not counting lowerY endpoints of edges and whole edges on that line + var inside = false; + for( var p = polyLen - 1, q = 0; q < polyLen; p = q ++ ) { + var edgeLowPt = inPolygon[ p ]; + var edgeHighPt = inPolygon[ q ]; + + var edgeDx = edgeHighPt.x - edgeLowPt.x; + var edgeDy = edgeHighPt.y - edgeLowPt.y; + + if ( Math.abs(edgeDy) > EPSILON ) { // not parallel + if ( edgeDy < 0 ) { + edgeLowPt = inPolygon[ q ]; edgeDx = - edgeDx; + edgeHighPt = inPolygon[ p ]; edgeDy = - edgeDy; + } + if ( ( inPt.y < edgeLowPt.y ) || ( inPt.y > edgeHighPt.y ) ) continue; + + if ( inPt.y == edgeLowPt.y ) { + if ( inPt.x == edgeLowPt.x ) return true; // inPt is on contour ? + // continue; // no intersection or edgeLowPt => doesn't count !!! + } else { + var perpEdge = edgeDy * (inPt.x - edgeLowPt.x) - edgeDx * (inPt.y - edgeLowPt.y); + if ( perpEdge == 0 ) return true; // inPt is on contour ? + if ( perpEdge < 0 ) continue; + inside = ! inside; // true intersection left of inPt + } + } else { // parallel or colinear + if ( inPt.y != edgeLowPt.y ) continue; // parallel + // egde lies on the same horizontal line as inPt + if ( ( ( edgeHighPt.x <= inPt.x ) && ( inPt.x <= edgeLowPt.x ) ) || + ( ( edgeLowPt.x <= inPt.x ) && ( inPt.x <= edgeHighPt.x ) ) ) return true; // inPt: Point on contour ! + // continue; + } + } + + return inside; + } + + + var subPaths = extractSubpaths( this.actions ); + if ( subPaths.length == 0 ) return []; + + if ( noHoles === true ) return toShapesNoHoles( subPaths ); + + + var solid, tmpPath, tmpShape, shapes = []; + + if ( subPaths.length == 1) { + + tmpPath = subPaths[0]; + tmpShape = new THREE.Shape(); + tmpShape.actions = tmpPath.actions; + tmpShape.curves = tmpPath.curves; + shapes.push( tmpShape ); + return shapes; + + } + + var holesFirst = ! THREE.Shape.Utils.isClockWise( subPaths[ 0 ].getPoints() ); + holesFirst = isCCW ? ! holesFirst : holesFirst; + + // console.log("Holes first", holesFirst); + + var betterShapeHoles = []; + var newShapes = []; + var newShapeHoles = []; + var mainIdx = 0; + var tmpPoints; + + newShapes[mainIdx] = undefined; + newShapeHoles[mainIdx] = []; + + var i, il; + + for ( i = 0, il = subPaths.length; i < il; i ++ ) { + + tmpPath = subPaths[ i ]; + tmpPoints = tmpPath.getPoints(); + solid = THREE.Shape.Utils.isClockWise( tmpPoints ); + solid = isCCW ? ! solid : solid; + + if ( solid ) { + + if ( (! holesFirst ) && ( newShapes[mainIdx] ) ) mainIdx ++; + + newShapes[mainIdx] = { s: new THREE.Shape(), p: tmpPoints }; + newShapes[mainIdx].s.actions = tmpPath.actions; + newShapes[mainIdx].s.curves = tmpPath.curves; + + if ( holesFirst ) mainIdx ++; + newShapeHoles[mainIdx] = []; + + //console.log('cw', i); + + } else { + + newShapeHoles[mainIdx].push( { h: tmpPath, p: tmpPoints[0] } ); + + //console.log('ccw', i); + + } + + } + + // only Holes? -> probably all Shapes with wrong orientation + if ( ! newShapes[0] ) return toShapesNoHoles( subPaths ); + + + if ( newShapes.length > 1 ) { + var ambigious = false; + var toChange = []; + + for (var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { + betterShapeHoles[sIdx] = []; + } + for (var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { + var sh = newShapes[sIdx]; + var sho = newShapeHoles[sIdx]; + for (var hIdx = 0; hIdx < sho.length; hIdx ++ ) { + var ho = sho[hIdx]; + var hole_unassigned = true; + for (var s2Idx = 0; s2Idx < newShapes.length; s2Idx ++ ) { + if ( isPointInsidePolygon( ho.p, newShapes[s2Idx].p ) ) { + if ( sIdx != s2Idx ) toChange.push( { froms: sIdx, tos: s2Idx, hole: hIdx } ); + if ( hole_unassigned ) { + hole_unassigned = false; + betterShapeHoles[s2Idx].push( ho ); + } else { + ambigious = true; + } + } + } + if ( hole_unassigned ) { betterShapeHoles[sIdx].push( ho ); } + } + } + // console.log("ambigious: ", ambigious); + if ( toChange.length > 0 ) { + // console.log("to change: ", toChange); + if (! ambigious) newShapeHoles = betterShapeHoles; + } + } + + var tmpHoles, j, jl; + for ( i = 0, il = newShapes.length; i < il; i ++ ) { + tmpShape = newShapes[i].s; + shapes.push( tmpShape ); + tmpHoles = newShapeHoles[i]; + for ( j = 0, jl = tmpHoles.length; j < jl; j ++ ) { + tmpShape.holes.push( tmpHoles[j].h ); + } + } + + //console.log("shape", shapes); + + return shapes; + +}; + +// File:src/extras/core/Shape.js + +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * Defines a 2d shape plane using paths. + **/ + +// STEP 1 Create a path. +// STEP 2 Turn path into shape. +// STEP 3 ExtrudeGeometry takes in Shape/Shapes +// STEP 3a - Extract points from each shape, turn to vertices +// STEP 3b - Triangulate each shape, add faces. + +THREE.Shape = function () { + + THREE.Path.apply( this, arguments ); + this.holes = []; + +}; + +THREE.Shape.prototype = Object.create( THREE.Path.prototype ); +THREE.Shape.prototype.constructor = THREE.Shape; + +// Convenience method to return ExtrudeGeometry + +THREE.Shape.prototype.extrude = function ( options ) { + + var extruded = new THREE.ExtrudeGeometry( this, options ); + return extruded; + +}; + +// Convenience method to return ShapeGeometry + +THREE.Shape.prototype.makeGeometry = function ( options ) { + + var geometry = new THREE.ShapeGeometry( this, options ); + return geometry; + +}; + +// Get points of holes + +THREE.Shape.prototype.getPointsHoles = function ( divisions ) { + + var i, il = this.holes.length, holesPts = []; + + for ( i = 0; i < il; i ++ ) { + + holesPts[ i ] = this.holes[ i ].getTransformedPoints( divisions, this.bends ); + + } + + return holesPts; + +}; + +// Get points of holes (spaced by regular distance) + +THREE.Shape.prototype.getSpacedPointsHoles = function ( divisions ) { + + var i, il = this.holes.length, holesPts = []; + + for ( i = 0; i < il; i ++ ) { + + holesPts[ i ] = this.holes[ i ].getTransformedSpacedPoints( divisions, this.bends ); + + } + + return holesPts; + +}; + + +// Get points of shape and holes (keypoints based on segments parameter) + +THREE.Shape.prototype.extractAllPoints = function ( divisions ) { + + return { + + shape: this.getTransformedPoints( divisions ), + holes: this.getPointsHoles( divisions ) + + }; + +}; + +THREE.Shape.prototype.extractPoints = function ( divisions ) { + + if (this.useSpacedPoints) { + return this.extractAllSpacedPoints(divisions); + } + + return this.extractAllPoints(divisions); + +}; + +// +// THREE.Shape.prototype.extractAllPointsWithBend = function ( divisions, bend ) { +// +// return { +// +// shape: this.transform( bend, divisions ), +// holes: this.getPointsHoles( divisions, bend ) +// +// }; +// +// }; + +// Get points of shape and holes (spaced by regular distance) + +THREE.Shape.prototype.extractAllSpacedPoints = function ( divisions ) { + + return { + + shape: this.getTransformedSpacedPoints( divisions ), + holes: this.getSpacedPointsHoles( divisions ) + + }; + +}; + +/************************************************************** + * Utils + **************************************************************/ + +THREE.Shape.Utils = { + + triangulateShape: function ( contour, holes ) { + + function point_in_segment_2D_colin( inSegPt1, inSegPt2, inOtherPt ) { + // inOtherPt needs to be colinear to the inSegment + if ( inSegPt1.x != inSegPt2.x ) { + if ( inSegPt1.x < inSegPt2.x ) { + return ( ( inSegPt1.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt2.x ) ); + } else { + return ( ( inSegPt2.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt1.x ) ); + } + } else { + if ( inSegPt1.y < inSegPt2.y ) { + return ( ( inSegPt1.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt2.y ) ); + } else { + return ( ( inSegPt2.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt1.y ) ); + } + } + } + + function intersect_segments_2D( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1, inSeg2Pt2, inExcludeAdjacentSegs ) { + var EPSILON = 0.0000000001; + + var seg1dx = inSeg1Pt2.x - inSeg1Pt1.x, seg1dy = inSeg1Pt2.y - inSeg1Pt1.y; + var seg2dx = inSeg2Pt2.x - inSeg2Pt1.x, seg2dy = inSeg2Pt2.y - inSeg2Pt1.y; + + var seg1seg2dx = inSeg1Pt1.x - inSeg2Pt1.x; + var seg1seg2dy = inSeg1Pt1.y - inSeg2Pt1.y; + + var limit = seg1dy * seg2dx - seg1dx * seg2dy; + var perpSeg1 = seg1dy * seg1seg2dx - seg1dx * seg1seg2dy; + + if ( Math.abs(limit) > EPSILON ) { // not parallel + + var perpSeg2; + if ( limit > 0 ) { + if ( ( perpSeg1 < 0 ) || ( perpSeg1 > limit ) ) return []; + perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy; + if ( ( perpSeg2 < 0 ) || ( perpSeg2 > limit ) ) return []; + } else { + if ( ( perpSeg1 > 0 ) || ( perpSeg1 < limit ) ) return []; + perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy; + if ( ( perpSeg2 > 0 ) || ( perpSeg2 < limit ) ) return []; + } + + // i.e. to reduce rounding errors + // intersection at endpoint of segment#1? + if ( perpSeg2 == 0 ) { + if ( ( inExcludeAdjacentSegs ) && + ( ( perpSeg1 == 0 ) || ( perpSeg1 == limit ) ) ) return []; + return [ inSeg1Pt1 ]; + } + if ( perpSeg2 == limit ) { + if ( ( inExcludeAdjacentSegs ) && + ( ( perpSeg1 == 0 ) || ( perpSeg1 == limit ) ) ) return []; + return [ inSeg1Pt2 ]; + } + // intersection at endpoint of segment#2? + if ( perpSeg1 == 0 ) return [ inSeg2Pt1 ]; + if ( perpSeg1 == limit ) return [ inSeg2Pt2 ]; + + // return real intersection point + var factorSeg1 = perpSeg2 / limit; + return [ { x: inSeg1Pt1.x + factorSeg1 * seg1dx, + y: inSeg1Pt1.y + factorSeg1 * seg1dy } ]; + + } else { // parallel or colinear + if ( ( perpSeg1 != 0 ) || + ( seg2dy * seg1seg2dx != seg2dx * seg1seg2dy ) ) return []; + + // they are collinear or degenerate + var seg1Pt = ( (seg1dx == 0) && (seg1dy == 0) ); // segment1 ist just a point? + var seg2Pt = ( (seg2dx == 0) && (seg2dy == 0) ); // segment2 ist just a point? + // both segments are points + if ( seg1Pt && seg2Pt ) { + if ( (inSeg1Pt1.x != inSeg2Pt1.x) || + (inSeg1Pt1.y != inSeg2Pt1.y) ) return []; // they are distinct points + return [ inSeg1Pt1 ]; // they are the same point + } + // segment#1 is a single point + if ( seg1Pt ) { + if (! point_in_segment_2D_colin( inSeg2Pt1, inSeg2Pt2, inSeg1Pt1 ) ) return []; // but not in segment#2 + return [ inSeg1Pt1 ]; + } + // segment#2 is a single point + if ( seg2Pt ) { + if (! point_in_segment_2D_colin( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1 ) ) return []; // but not in segment#1 + return [ inSeg2Pt1 ]; + } + + // they are collinear segments, which might overlap + var seg1min, seg1max, seg1minVal, seg1maxVal; + var seg2min, seg2max, seg2minVal, seg2maxVal; + if (seg1dx != 0) { // the segments are NOT on a vertical line + if ( inSeg1Pt1.x < inSeg1Pt2.x ) { + seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.x; + seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.x; + } else { + seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.x; + seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.x; + } + if ( inSeg2Pt1.x < inSeg2Pt2.x ) { + seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.x; + seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.x; + } else { + seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.x; + seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.x; + } + } else { // the segments are on a vertical line + if ( inSeg1Pt1.y < inSeg1Pt2.y ) { + seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.y; + seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.y; + } else { + seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.y; + seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.y; + } + if ( inSeg2Pt1.y < inSeg2Pt2.y ) { + seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.y; + seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.y; + } else { + seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.y; + seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.y; + } + } + if ( seg1minVal <= seg2minVal ) { + if ( seg1maxVal < seg2minVal ) return []; + if ( seg1maxVal == seg2minVal ) { + if ( inExcludeAdjacentSegs ) return []; + return [ seg2min ]; + } + if ( seg1maxVal <= seg2maxVal ) return [ seg2min, seg1max ]; + return [ seg2min, seg2max ]; + } else { + if ( seg1minVal > seg2maxVal ) return []; + if ( seg1minVal == seg2maxVal ) { + if ( inExcludeAdjacentSegs ) return []; + return [ seg1min ]; + } + if ( seg1maxVal <= seg2maxVal ) return [ seg1min, seg1max ]; + return [ seg1min, seg2max ]; + } + } + } + + function isPointInsideAngle( inVertex, inLegFromPt, inLegToPt, inOtherPt ) { + // The order of legs is important + + var EPSILON = 0.0000000001; + + // translation of all points, so that Vertex is at (0,0) + var legFromPtX = inLegFromPt.x - inVertex.x, legFromPtY = inLegFromPt.y - inVertex.y; + var legToPtX = inLegToPt.x - inVertex.x, legToPtY = inLegToPt.y - inVertex.y; + var otherPtX = inOtherPt.x - inVertex.x, otherPtY = inOtherPt.y - inVertex.y; + + // main angle >0: < 180 deg.; 0: 180 deg.; <0: > 180 deg. + var from2toAngle = legFromPtX * legToPtY - legFromPtY * legToPtX; + var from2otherAngle = legFromPtX * otherPtY - legFromPtY * otherPtX; + + if ( Math.abs(from2toAngle) > EPSILON ) { // angle != 180 deg. + + var other2toAngle = otherPtX * legToPtY - otherPtY * legToPtX; + // console.log( "from2to: " + from2toAngle + ", from2other: " + from2otherAngle + ", other2to: " + other2toAngle ); + + if ( from2toAngle > 0 ) { // main angle < 180 deg. + return ( ( from2otherAngle >= 0 ) && ( other2toAngle >= 0 ) ); + } else { // main angle > 180 deg. + return ( ( from2otherAngle >= 0 ) || ( other2toAngle >= 0 ) ); + } + } else { // angle == 180 deg. + // console.log( "from2to: 180 deg., from2other: " + from2otherAngle ); + return ( from2otherAngle > 0 ); + } + } + + + function removeHoles( contour, holes ) { + + var shape = contour.concat(); // work on this shape + var hole; + + function isCutLineInsideAngles( inShapeIdx, inHoleIdx ) { + // Check if hole point lies within angle around shape point + var lastShapeIdx = shape.length - 1; + + var prevShapeIdx = inShapeIdx - 1; + if ( prevShapeIdx < 0 ) prevShapeIdx = lastShapeIdx; + + var nextShapeIdx = inShapeIdx + 1; + if ( nextShapeIdx > lastShapeIdx ) nextShapeIdx = 0; + + var insideAngle = isPointInsideAngle( shape[inShapeIdx], shape[ prevShapeIdx ], shape[ nextShapeIdx ], hole[inHoleIdx] ); + if (! insideAngle ) { + // console.log( "Vertex (Shape): " + inShapeIdx + ", Point: " + hole[inHoleIdx].x + "/" + hole[inHoleIdx].y ); + return false; + } + + // Check if shape point lies within angle around hole point + var lastHoleIdx = hole.length - 1; + + var prevHoleIdx = inHoleIdx - 1; + if ( prevHoleIdx < 0 ) prevHoleIdx = lastHoleIdx; + + var nextHoleIdx = inHoleIdx + 1; + if ( nextHoleIdx > lastHoleIdx ) nextHoleIdx = 0; + + insideAngle = isPointInsideAngle( hole[inHoleIdx], hole[ prevHoleIdx ], hole[ nextHoleIdx ], shape[inShapeIdx] ); + if (! insideAngle ) { + // console.log( "Vertex (Hole): " + inHoleIdx + ", Point: " + shape[inShapeIdx].x + "/" + shape[inShapeIdx].y ); + return false; + } + + return true; + } + + function intersectsShapeEdge( inShapePt, inHolePt ) { + // checks for intersections with shape edges + var sIdx, nextIdx, intersection; + for ( sIdx = 0; sIdx < shape.length; sIdx ++ ) { + nextIdx = sIdx+1; nextIdx %= shape.length; + intersection = intersect_segments_2D( inShapePt, inHolePt, shape[sIdx], shape[nextIdx], true ); + if ( intersection.length > 0 ) return true; + } + + return false; + } + + var indepHoles = []; + + function intersectsHoleEdge( inShapePt, inHolePt ) { + // checks for intersections with hole edges + var ihIdx, chkHole, + hIdx, nextIdx, intersection; + for ( ihIdx = 0; ihIdx < indepHoles.length; ihIdx ++ ) { + chkHole = holes[indepHoles[ihIdx]]; + for ( hIdx = 0; hIdx < chkHole.length; hIdx ++ ) { + nextIdx = hIdx+1; nextIdx %= chkHole.length; + intersection = intersect_segments_2D( inShapePt, inHolePt, chkHole[hIdx], chkHole[nextIdx], true ); + if ( intersection.length > 0 ) return true; + } + } + return false; + } + + var holeIndex, shapeIndex, + shapePt, holePt, + holeIdx, cutKey, failedCuts = [], + tmpShape1, tmpShape2, + tmpHole1, tmpHole2; + + for ( var h = 0, hl = holes.length; h < hl; h ++ ) { + + indepHoles.push( h ); + + } + + var minShapeIndex = 0; + var counter = indepHoles.length * 2; + while ( indepHoles.length > 0 ) { + counter --; + if ( counter < 0 ) { + console.log( "Infinite Loop! Holes left:" + indepHoles.length + ", Probably Hole outside Shape!" ); + break; + } + + // search for shape-vertex and hole-vertex, + // which can be connected without intersections + for ( shapeIndex = minShapeIndex; shapeIndex < shape.length; shapeIndex ++ ) { + + shapePt = shape[ shapeIndex ]; + holeIndex = - 1; + + // search for hole which can be reached without intersections + for ( var h = 0; h < indepHoles.length; h ++ ) { + holeIdx = indepHoles[h]; + + // prevent multiple checks + cutKey = shapePt.x + ":" + shapePt.y + ":" + holeIdx; + if ( failedCuts[cutKey] !== undefined ) continue; + + hole = holes[holeIdx]; + for ( var h2 = 0; h2 < hole.length; h2 ++ ) { + holePt = hole[ h2 ]; + if (! isCutLineInsideAngles( shapeIndex, h2 ) ) continue; + if ( intersectsShapeEdge( shapePt, holePt ) ) continue; + if ( intersectsHoleEdge( shapePt, holePt ) ) continue; + + holeIndex = h2; + indepHoles.splice(h,1); + + tmpShape1 = shape.slice( 0, shapeIndex+1 ); + tmpShape2 = shape.slice( shapeIndex ); + tmpHole1 = hole.slice( holeIndex ); + tmpHole2 = hole.slice( 0, holeIndex+1 ); + + shape = tmpShape1.concat( tmpHole1 ).concat( tmpHole2 ).concat( tmpShape2 ); + + minShapeIndex = shapeIndex; + + // Debug only, to show the selected cuts + // glob_CutLines.push( [ shapePt, holePt ] ); + + break; + } + if ( holeIndex >= 0 ) break; // hole-vertex found + + failedCuts[cutKey] = true; // remember failure + } + if ( holeIndex >= 0 ) break; // hole-vertex found + } + } + + return shape; /* shape with no holes */ + } + + + var i, il, f, face, + key, index, + allPointsMap = {}; + + // To maintain reference to old shape, one must match coordinates, or offset the indices from original arrays. It's probably easier to do the first. + + var allpoints = contour.concat(); + + for ( var h = 0, hl = holes.length; h < hl; h ++ ) { + + Array.prototype.push.apply( allpoints, holes[h] ); + + } + + //console.log( "allpoints",allpoints, allpoints.length ); + + // prepare all points map + + for ( i = 0, il = allpoints.length; i < il; i ++ ) { + + key = allpoints[ i ].x + ":" + allpoints[ i ].y; + + if ( allPointsMap[ key ] !== undefined ) { + + console.log( "Duplicate point", key ); + + } + + allPointsMap[ key ] = i; + + } + + // remove holes by cutting paths to holes and adding them to the shape + var shapeWithoutHoles = removeHoles( contour, holes ); + + var triangles = THREE.FontUtils.Triangulate( shapeWithoutHoles, false ); // True returns indices for points of spooled shape + //console.log( "triangles",triangles, triangles.length ); + + // check all face vertices against all points map + + for ( i = 0, il = triangles.length; i < il; i ++ ) { + + face = triangles[ i ]; + + for ( f = 0; f < 3; f ++ ) { + + key = face[ f ].x + ":" + face[ f ].y; + + index = allPointsMap[ key ]; + + if ( index !== undefined ) { + + face[ f ] = index; + + } + + } + + } + + return triangles.concat(); + + }, + + isClockWise: function ( pts ) { + + return THREE.FontUtils.Triangulate.area( pts ) < 0; + + }, + + // Bezier Curves formulas obtained from + // http://en.wikipedia.org/wiki/B%C3%A9zier_curve + + // Quad Bezier Functions + + b2p0: function ( t, p ) { + + var k = 1 - t; + return k * k * p; + + }, + + b2p1: function ( t, p ) { + + return 2 * ( 1 - t ) * t * p; + + }, + + b2p2: function ( t, p ) { + + return t * t * p; + + }, + + b2: function ( t, p0, p1, p2 ) { + + return this.b2p0( t, p0 ) + this.b2p1( t, p1 ) + this.b2p2( t, p2 ); + + }, + + // Cubic Bezier Functions + + b3p0: function ( t, p ) { + + var k = 1 - t; + return k * k * k * p; + + }, + + b3p1: function ( t, p ) { + + var k = 1 - t; + return 3 * k * k * t * p; + + }, + + b3p2: function ( t, p ) { + + var k = 1 - t; + return 3 * k * t * t * p; + + }, + + b3p3: function ( t, p ) { + + return t * t * t * p; + + }, + + b3: function ( t, p0, p1, p2, p3 ) { + + return this.b3p0( t, p0 ) + this.b3p1( t, p1 ) + this.b3p2( t, p2 ) + this.b3p3( t, p3 ); + + } + +}; + + +// File:src/extras/curves/LineCurve.js + +/************************************************************** + * Line + **************************************************************/ + +THREE.LineCurve = function ( v1, v2 ) { + + this.v1 = v1; + this.v2 = v2; + +}; + +THREE.LineCurve.prototype = Object.create( THREE.Curve.prototype ); +THREE.LineCurve.prototype.constructor = THREE.LineCurve; + +THREE.LineCurve.prototype.getPoint = function ( t ) { + + var point = this.v2.clone().sub(this.v1); + point.multiplyScalar( t ).add( this.v1 ); + + return point; + +}; + +// Line curve is linear, so we can overwrite default getPointAt + +THREE.LineCurve.prototype.getPointAt = function ( u ) { + + return this.getPoint( u ); + +}; + +THREE.LineCurve.prototype.getTangent = function( t ) { + + var tangent = this.v2.clone().sub(this.v1); + + return tangent.normalize(); + +}; + +// File:src/extras/curves/QuadraticBezierCurve.js + +/************************************************************** + * Quadratic Bezier curve + **************************************************************/ + + +THREE.QuadraticBezierCurve = function ( v0, v1, v2 ) { + + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + +}; + +THREE.QuadraticBezierCurve.prototype = Object.create( THREE.Curve.prototype ); +THREE.QuadraticBezierCurve.prototype.constructor = THREE.QuadraticBezierCurve; + + +THREE.QuadraticBezierCurve.prototype.getPoint = function ( t ) { + + var vector = new THREE.Vector2(); + + vector.x = THREE.Shape.Utils.b2( t, this.v0.x, this.v1.x, this.v2.x ); + vector.y = THREE.Shape.Utils.b2( t, this.v0.y, this.v1.y, this.v2.y ); + + return vector; + +}; + + +THREE.QuadraticBezierCurve.prototype.getTangent = function( t ) { + + var vector = new THREE.Vector2(); + + vector.x = THREE.Curve.Utils.tangentQuadraticBezier( t, this.v0.x, this.v1.x, this.v2.x ); + vector.y = THREE.Curve.Utils.tangentQuadraticBezier( t, this.v0.y, this.v1.y, this.v2.y ); + + // returns unit vector + + return vector.normalize(); + +}; + +// File:src/extras/curves/CubicBezierCurve.js + +/************************************************************** + * Cubic Bezier curve + **************************************************************/ + +THREE.CubicBezierCurve = function ( v0, v1, v2, v3 ) { + + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + this.v3 = v3; + +}; + +THREE.CubicBezierCurve.prototype = Object.create( THREE.Curve.prototype ); +THREE.CubicBezierCurve.prototype.constructor = THREE.CubicBezierCurve; + +THREE.CubicBezierCurve.prototype.getPoint = function ( t ) { + + var tx, ty; + + tx = THREE.Shape.Utils.b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ); + ty = THREE.Shape.Utils.b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ); + + return new THREE.Vector2( tx, ty ); + +}; + +THREE.CubicBezierCurve.prototype.getTangent = function( t ) { + + var tx, ty; + + tx = THREE.Curve.Utils.tangentCubicBezier( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ); + ty = THREE.Curve.Utils.tangentCubicBezier( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ); + + var tangent = new THREE.Vector2( tx, ty ); + tangent.normalize(); + + return tangent; + +}; + +// File:src/extras/curves/SplineCurve.js + +/************************************************************** + * Spline curve + **************************************************************/ + +THREE.SplineCurve = function ( points /* array of Vector2 */ ) { + + this.points = ( points == undefined ) ? [] : points; + +}; + +THREE.SplineCurve.prototype = Object.create( THREE.Curve.prototype ); +THREE.SplineCurve.prototype.constructor = THREE.SplineCurve; + +THREE.SplineCurve.prototype.getPoint = function ( t ) { + + var points = this.points; + var point = ( points.length - 1 ) * t; + + var intPoint = Math.floor( point ); + var weight = point - intPoint; + + var point0 = points[ intPoint == 0 ? intPoint : intPoint - 1 ] + var point1 = points[ intPoint ] + var point2 = points[ intPoint > points.length - 2 ? points.length -1 : intPoint + 1 ] + var point3 = points[ intPoint > points.length - 3 ? points.length -1 : intPoint + 2 ] + + var vector = new THREE.Vector2(); + + vector.x = THREE.Curve.Utils.interpolate( point0.x, point1.x, point2.x, point3.x, weight ); + vector.y = THREE.Curve.Utils.interpolate( point0.y, point1.y, point2.y, point3.y, weight ); + + return vector; + +}; + +// File:src/extras/curves/EllipseCurve.js + +/************************************************************** + * Ellipse curve + **************************************************************/ + +THREE.EllipseCurve = function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise ) { + + this.aX = aX; + this.aY = aY; + + this.xRadius = xRadius; + this.yRadius = yRadius; + + this.aStartAngle = aStartAngle; + this.aEndAngle = aEndAngle; + + this.aClockwise = aClockwise; + +}; + +THREE.EllipseCurve.prototype = Object.create( THREE.Curve.prototype ); +THREE.EllipseCurve.prototype.constructor = THREE.EllipseCurve; + +THREE.EllipseCurve.prototype.getPoint = function ( t ) { + + var deltaAngle = this.aEndAngle - this.aStartAngle; + + if ( deltaAngle < 0 ) deltaAngle += Math.PI * 2; + if ( deltaAngle > Math.PI * 2 ) deltaAngle -= Math.PI * 2; + + var angle; + + if ( this.aClockwise === true ) { + + angle = this.aEndAngle + ( 1 - t ) * ( Math.PI * 2 - deltaAngle ); + + } else { + + angle = this.aStartAngle + t * deltaAngle; + + } + + var vector = new THREE.Vector2(); + + vector.x = this.aX + this.xRadius * Math.cos( angle ); + vector.y = this.aY + this.yRadius * Math.sin( angle ); + + return vector; + +}; + +// File:src/extras/curves/ArcCurve.js + +/************************************************************** + * Arc curve + **************************************************************/ + +THREE.ArcCurve = function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { + + THREE.EllipseCurve.call( this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); +}; + +THREE.ArcCurve.prototype = Object.create( THREE.EllipseCurve.prototype ); +THREE.ArcCurve.prototype.constructor = THREE.ArcCurve; + +// File:src/extras/curves/LineCurve3.js + +/************************************************************** + * Line3D + **************************************************************/ + +THREE.LineCurve3 = THREE.Curve.create( + + function ( v1, v2 ) { + + this.v1 = v1; + this.v2 = v2; + + }, + + function ( t ) { + + var vector = new THREE.Vector3(); + + vector.subVectors( this.v2, this.v1 ); // diff + vector.multiplyScalar( t ); + vector.add( this.v1 ); + + return vector; + + } + +); + +// File:src/extras/curves/QuadraticBezierCurve3.js + +/************************************************************** + * Quadratic Bezier 3D curve + **************************************************************/ + +THREE.QuadraticBezierCurve3 = THREE.Curve.create( + + function ( v0, v1, v2 ) { + + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + + }, + + function ( t ) { + + var vector = new THREE.Vector3(); + + vector.x = THREE.Shape.Utils.b2( t, this.v0.x, this.v1.x, this.v2.x ); + vector.y = THREE.Shape.Utils.b2( t, this.v0.y, this.v1.y, this.v2.y ); + vector.z = THREE.Shape.Utils.b2( t, this.v0.z, this.v1.z, this.v2.z ); + + return vector; + + } + +); + +// File:src/extras/curves/CubicBezierCurve3.js + +/************************************************************** + * Cubic Bezier 3D curve + **************************************************************/ + +THREE.CubicBezierCurve3 = THREE.Curve.create( + + function ( v0, v1, v2, v3 ) { + + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + this.v3 = v3; + + }, + + function ( t ) { + + var vector = new THREE.Vector3(); + + vector.x = THREE.Shape.Utils.b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ); + vector.y = THREE.Shape.Utils.b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ); + vector.z = THREE.Shape.Utils.b3( t, this.v0.z, this.v1.z, this.v2.z, this.v3.z ); + + return vector; + + } + +); + +// File:src/extras/curves/SplineCurve3.js + +/************************************************************** + * Spline 3D curve + **************************************************************/ + + +THREE.SplineCurve3 = THREE.Curve.create( + + function ( points /* array of Vector3 */) { + + this.points = ( points == undefined ) ? [] : points; + + }, + + function ( t ) { + + var points = this.points; + var point = ( points.length - 1 ) * t; + + var intPoint = Math.floor( point ); + var weight = point - intPoint; + + var point0 = points[ intPoint == 0 ? intPoint : intPoint - 1 ]; + var point1 = points[ intPoint ]; + var point2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ]; + var point3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ]; + + var vector = new THREE.Vector3(); + + vector.x = THREE.Curve.Utils.interpolate( point0.x, point1.x, point2.x, point3.x, weight ); + vector.y = THREE.Curve.Utils.interpolate( point0.y, point1.y, point2.y, point3.y, weight ); + vector.z = THREE.Curve.Utils.interpolate( point0.z, point1.z, point2.z, point3.z, weight ); + + return vector; + + } + +); + +// File:src/extras/curves/ClosedSplineCurve3.js + +/************************************************************** + * Closed Spline 3D curve + **************************************************************/ + + +THREE.ClosedSplineCurve3 = THREE.Curve.create( + + function ( points /* array of Vector3 */) { + + this.points = ( points == undefined ) ? [] : points; + + }, + + function ( t ) { + + var points = this.points; + var point = ( points.length - 0 ) * t; // This needs to be from 0-length +1 + + var intPoint = Math.floor( point ); + var weight = point - intPoint; + + intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / points.length ) + 1 ) * points.length; + + var point0 = points[ ( intPoint - 1 ) % points.length ]; + var point1 = points[ ( intPoint ) % points.length ]; + var point2 = points[ ( intPoint + 1 ) % points.length ]; + var point3 = points[ ( intPoint + 2 ) % points.length ]; + + var vector = new THREE.Vector3(); + + vector.x = THREE.Curve.Utils.interpolate( point0.x, point1.x, point2.x, point3.x, weight ); + vector.y = THREE.Curve.Utils.interpolate( point0.y, point1.y, point2.y, point3.y, weight ); + vector.z = THREE.Curve.Utils.interpolate( point0.z, point1.z, point2.z, point3.z, weight ); + + return vector; + + } + +); + +// File:src/extras/animation/AnimationHandler.js + +/** + * @author mikael emtinger / http://gomo.se/ + */ + +THREE.AnimationHandler = { + + LINEAR: 0, + CATMULLROM: 1, + CATMULLROM_FORWARD: 2, + + // + + add: function () { console.warn( 'THREE.AnimationHandler.add() has been deprecated.' ); }, + get: function () { console.warn( 'THREE.AnimationHandler.get() has been deprecated.' ); }, + remove: function () { console.warn( 'THREE.AnimationHandler.remove() has been deprecated.' ); }, + + // + + animations: [], + + init: function ( data ) { + + if ( data.initialized === true ) return data; + + // loop through all keys + + for ( var h = 0; h < data.hierarchy.length; h ++ ) { + + for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) { + + // remove minus times + + if ( data.hierarchy[ h ].keys[ k ].time < 0 ) { + + data.hierarchy[ h ].keys[ k ].time = 0; + + } + + // create quaternions + + if ( data.hierarchy[ h ].keys[ k ].rot !== undefined && + ! ( data.hierarchy[ h ].keys[ k ].rot instanceof THREE.Quaternion ) ) { + + var quat = data.hierarchy[ h ].keys[ k ].rot; + data.hierarchy[ h ].keys[ k ].rot = new THREE.Quaternion().fromArray( quat ); + + } + + } + + // prepare morph target keys + + if ( data.hierarchy[ h ].keys.length && data.hierarchy[ h ].keys[ 0 ].morphTargets !== undefined ) { + + // get all used + + var usedMorphTargets = {}; + + for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) { + + for ( var m = 0; m < data.hierarchy[ h ].keys[ k ].morphTargets.length; m ++ ) { + + var morphTargetName = data.hierarchy[ h ].keys[ k ].morphTargets[ m ]; + usedMorphTargets[ morphTargetName ] = - 1; + + } + + } + + data.hierarchy[ h ].usedMorphTargets = usedMorphTargets; + + + // set all used on all frames + + for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) { + + var influences = {}; + + for ( var morphTargetName in usedMorphTargets ) { + + for ( var m = 0; m < data.hierarchy[ h ].keys[ k ].morphTargets.length; m ++ ) { + + if ( data.hierarchy[ h ].keys[ k ].morphTargets[ m ] === morphTargetName ) { + + influences[ morphTargetName ] = data.hierarchy[ h ].keys[ k ].morphTargetsInfluences[ m ]; + break; + + } + + } + + if ( m === data.hierarchy[ h ].keys[ k ].morphTargets.length ) { + + influences[ morphTargetName ] = 0; + + } + + } + + data.hierarchy[ h ].keys[ k ].morphTargetsInfluences = influences; + + } + + } + + + // remove all keys that are on the same time + + for ( var k = 1; k < data.hierarchy[ h ].keys.length; k ++ ) { + + if ( data.hierarchy[ h ].keys[ k ].time === data.hierarchy[ h ].keys[ k - 1 ].time ) { + + data.hierarchy[ h ].keys.splice( k, 1 ); + k --; + + } + + } + + + // set index + + for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) { + + data.hierarchy[ h ].keys[ k ].index = k; + + } + + } + + data.initialized = true; + + return data; + + }, + + parse: function ( root ) { + + var parseRecurseHierarchy = function ( root, hierarchy ) { + + hierarchy.push( root ); + + for ( var c = 0; c < root.children.length; c ++ ) + parseRecurseHierarchy( root.children[ c ], hierarchy ); + + }; + + // setup hierarchy + + var hierarchy = []; + + if ( root instanceof THREE.SkinnedMesh ) { + + for ( var b = 0; b < root.skeleton.bones.length; b ++ ) { + + hierarchy.push( root.skeleton.bones[ b ] ); + + } + + } else { + + parseRecurseHierarchy( root, hierarchy ); + + } + + return hierarchy; + + }, + + play: function ( animation ) { + + if ( this.animations.indexOf( animation ) === - 1 ) { + + this.animations.push( animation ); + + } + + }, + + stop: function ( animation ) { + + var index = this.animations.indexOf( animation ); + + if ( index !== - 1 ) { + + this.animations.splice( index, 1 ); + + } + + }, + + update: function ( deltaTimeMS ) { + + for ( var i = 0; i < this.animations.length; i ++ ) { + + this.animations[ i ].resetBlendWeights( ); + + } + + for ( var i = 0; i < this.animations.length; i ++ ) { + + this.animations[ i ].update( deltaTimeMS ); + + } + + } + +}; + +// File:src/extras/animation/Animation.js + +/** + * @author mikael emtinger / http://gomo.se/ + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Animation = function ( root, data ) { + + this.root = root; + this.data = THREE.AnimationHandler.init( data ); + this.hierarchy = THREE.AnimationHandler.parse( root ); + + this.currentTime = 0; + this.timeScale = 1; + + this.isPlaying = false; + this.loop = true; + this.weight = 0; + + this.interpolationType = THREE.AnimationHandler.LINEAR; + +}; + + +THREE.Animation.prototype.keyTypes = [ "pos", "rot", "scl" ]; + + +THREE.Animation.prototype.play = function ( startTime, weight ) { + + this.currentTime = startTime !== undefined ? startTime : 0; + this.weight = weight !== undefined ? weight: 1; + + this.isPlaying = true; + + this.reset(); + + THREE.AnimationHandler.play( this ); + +}; + + +THREE.Animation.prototype.stop = function() { + + this.isPlaying = false; + + THREE.AnimationHandler.stop( this ); + +}; + +THREE.Animation.prototype.reset = function () { + + for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) { + + var object = this.hierarchy[ h ]; + + if ( object.animationCache === undefined ) { + + object.animationCache = { + animations: {}, + blending: { + positionWeight: 0.0, + quaternionWeight: 0.0, + scaleWeight: 0.0 + } + }; + } + + if ( object.animationCache.animations[this.data.name] === undefined ) { + + object.animationCache.animations[this.data.name] = {}; + object.animationCache.animations[this.data.name].prevKey = { pos: 0, rot: 0, scl: 0 }; + object.animationCache.animations[this.data.name].nextKey = { pos: 0, rot: 0, scl: 0 }; + object.animationCache.animations[this.data.name].originalMatrix = object.matrix; + + } + + var animationCache = object.animationCache.animations[this.data.name]; + + // Get keys to match our current time + + for ( var t = 0; t < 3; t ++ ) { + + var type = this.keyTypes[ t ]; + + var prevKey = this.data.hierarchy[ h ].keys[ 0 ]; + var nextKey = this.getNextKeyWith( type, h, 1 ); + + while ( nextKey.time < this.currentTime && nextKey.index > prevKey.index ) { + + prevKey = nextKey; + nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 ); + + } + + animationCache.prevKey[ type ] = prevKey; + animationCache.nextKey[ type ] = nextKey; + + } + + } + +}; + +THREE.Animation.prototype.resetBlendWeights = function () { + + for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) { + + var object = this.hierarchy[ h ]; + + if ( object.animationCache !== undefined ) { + + object.animationCache.blending.positionWeight = 0.0; + object.animationCache.blending.quaternionWeight = 0.0; + object.animationCache.blending.scaleWeight = 0.0; + + } + + } + +}; + +THREE.Animation.prototype.update = (function(){ + + var points = []; + var target = new THREE.Vector3(); + var newVector = new THREE.Vector3(); + var newQuat = new THREE.Quaternion(); + + // Catmull-Rom spline + + var interpolateCatmullRom = function ( points, scale ) { + + var c = [], v3 = [], + point, intPoint, weight, w2, w3, + pa, pb, pc, pd; + + point = ( points.length - 1 ) * scale; + intPoint = Math.floor( point ); + weight = point - intPoint; + + c[ 0 ] = intPoint === 0 ? intPoint : intPoint - 1; + c[ 1 ] = intPoint; + c[ 2 ] = intPoint > points.length - 2 ? intPoint : intPoint + 1; + c[ 3 ] = intPoint > points.length - 3 ? intPoint : intPoint + 2; + + pa = points[ c[ 0 ] ]; + pb = points[ c[ 1 ] ]; + pc = points[ c[ 2 ] ]; + pd = points[ c[ 3 ] ]; + + w2 = weight * weight; + w3 = weight * w2; + + v3[ 0 ] = interpolate( pa[ 0 ], pb[ 0 ], pc[ 0 ], pd[ 0 ], weight, w2, w3 ); + v3[ 1 ] = interpolate( pa[ 1 ], pb[ 1 ], pc[ 1 ], pd[ 1 ], weight, w2, w3 ); + v3[ 2 ] = interpolate( pa[ 2 ], pb[ 2 ], pc[ 2 ], pd[ 2 ], weight, w2, w3 ); + + return v3; + + }; + + var interpolate = function ( p0, p1, p2, p3, t, t2, t3 ) { + + var v0 = ( p2 - p0 ) * 0.5, + v1 = ( p3 - p1 ) * 0.5; + + return ( 2 * ( p1 - p2 ) + v0 + v1 ) * t3 + ( - 3 * ( p1 - p2 ) - 2 * v0 - v1 ) * t2 + v0 * t + p1; + + }; + + return function ( delta ) { + + if ( this.isPlaying === false ) return; + + this.currentTime += delta * this.timeScale; + + if ( this.weight === 0 ) + return; + + // + + var duration = this.data.length; + + if ( this.currentTime > duration || this.currentTime < 0 ) { + + if ( this.loop ) { + + this.currentTime %= duration; + + if ( this.currentTime < 0 ) + this.currentTime += duration; + + this.reset(); + + } else { + + this.stop(); + + } + + } + + for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) { + + var object = this.hierarchy[ h ]; + var animationCache = object.animationCache.animations[this.data.name]; + var blending = object.animationCache.blending; + + // loop through pos/rot/scl + + for ( var t = 0; t < 3; t ++ ) { + + // get keys + + var type = this.keyTypes[ t ]; + var prevKey = animationCache.prevKey[ type ]; + var nextKey = animationCache.nextKey[ type ]; + + if ( ( this.timeScale > 0 && nextKey.time <= this.currentTime ) || + ( this.timeScale < 0 && prevKey.time >= this.currentTime ) ) { + + prevKey = this.data.hierarchy[ h ].keys[ 0 ]; + nextKey = this.getNextKeyWith( type, h, 1 ); + + while ( nextKey.time < this.currentTime && nextKey.index > prevKey.index ) { + + prevKey = nextKey; + nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 ); + + } + + animationCache.prevKey[ type ] = prevKey; + animationCache.nextKey[ type ] = nextKey; + + } + + var scale = ( this.currentTime - prevKey.time ) / ( nextKey.time - prevKey.time ); + + var prevXYZ = prevKey[ type ]; + var nextXYZ = nextKey[ type ]; + + if ( scale < 0 ) scale = 0; + if ( scale > 1 ) scale = 1; + + // interpolate + + if ( type === "pos" ) { + + if ( this.interpolationType === THREE.AnimationHandler.LINEAR ) { + + newVector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale; + newVector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale; + newVector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale; + + // blend + var proportionalWeight = this.weight / ( this.weight + blending.positionWeight ); + object.position.lerp( newVector, proportionalWeight ); + blending.positionWeight += this.weight; + + } else if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM || + this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) { + + points[ 0 ] = this.getPrevKeyWith( "pos", h, prevKey.index - 1 )[ "pos" ]; + points[ 1 ] = prevXYZ; + points[ 2 ] = nextXYZ; + points[ 3 ] = this.getNextKeyWith( "pos", h, nextKey.index + 1 )[ "pos" ]; + + scale = scale * 0.33 + 0.33; + + var currentPoint = interpolateCatmullRom( points, scale ); + var proportionalWeight = this.weight / ( this.weight + blending.positionWeight ); + blending.positionWeight += this.weight; + + // blend + + var vector = object.position; + + vector.x = vector.x + ( currentPoint[ 0 ] - vector.x ) * proportionalWeight; + vector.y = vector.y + ( currentPoint[ 1 ] - vector.y ) * proportionalWeight; + vector.z = vector.z + ( currentPoint[ 2 ] - vector.z ) * proportionalWeight; + + if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) { + + var forwardPoint = interpolateCatmullRom( points, scale * 1.01 ); + + target.set( forwardPoint[ 0 ], forwardPoint[ 1 ], forwardPoint[ 2 ] ); + target.sub( vector ); + target.y = 0; + target.normalize(); + + var angle = Math.atan2( target.x, target.z ); + object.rotation.set( 0, angle, 0 ); + + } + + } + + } else if ( type === "rot" ) { + + THREE.Quaternion.slerp( prevXYZ, nextXYZ, newQuat, scale ); + + // Avoid paying the cost of an additional slerp if we don't have to + if ( blending.quaternionWeight === 0 ) { + + object.quaternion.copy(newQuat); + blending.quaternionWeight = this.weight; + + } else { + + var proportionalWeight = this.weight / ( this.weight + blending.quaternionWeight ); + THREE.Quaternion.slerp( object.quaternion, newQuat, object.quaternion, proportionalWeight ); + blending.quaternionWeight += this.weight; + + } + + } else if ( type === "scl" ) { + + newVector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale; + newVector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale; + newVector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale; + + var proportionalWeight = this.weight / ( this.weight + blending.scaleWeight ); + object.scale.lerp( newVector, proportionalWeight ); + blending.scaleWeight += this.weight; + + } + + } + + } + + return true; + + }; + +})(); + + + + + +// Get next key with + +THREE.Animation.prototype.getNextKeyWith = function ( type, h, key ) { + + var keys = this.data.hierarchy[ h ].keys; + + if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM || + this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) { + + key = key < keys.length - 1 ? key : keys.length - 1; + + } else { + + key = key % keys.length; + + } + + for ( ; key < keys.length; key ++ ) { + + if ( keys[ key ][ type ] !== undefined ) { + + return keys[ key ]; + + } + + } + + return this.data.hierarchy[ h ].keys[ 0 ]; + +}; + +// Get previous key with + +THREE.Animation.prototype.getPrevKeyWith = function ( type, h, key ) { + + var keys = this.data.hierarchy[ h ].keys; + + if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM || + this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) { + + key = key > 0 ? key : 0; + + } else { + + key = key >= 0 ? key : key + keys.length; + + } + + + for ( ; key >= 0; key -- ) { + + if ( keys[ key ][ type ] !== undefined ) { + + return keys[ key ]; + + } + + } + + return this.data.hierarchy[ h ].keys[ keys.length - 1 ]; + +}; + +// File:src/extras/animation/KeyFrameAnimation.js + +/** + * @author mikael emtinger / http://gomo.se/ + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * @author khang duong + * @author erik kitson + */ + +THREE.KeyFrameAnimation = function ( data ) { + + this.root = data.node; + this.data = THREE.AnimationHandler.init( data ); + this.hierarchy = THREE.AnimationHandler.parse( this.root ); + this.currentTime = 0; + this.timeScale = 0.001; + this.isPlaying = false; + this.isPaused = true; + this.loop = true; + + // initialize to first keyframes + + for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) { + + var keys = this.data.hierarchy[h].keys, + sids = this.data.hierarchy[h].sids, + obj = this.hierarchy[h]; + + if ( keys.length && sids ) { + + for ( var s = 0; s < sids.length; s ++ ) { + + var sid = sids[ s ], + next = this.getNextKeyWith( sid, h, 0 ); + + if ( next ) { + + next.apply( sid ); + + } + + } + + obj.matrixAutoUpdate = false; + this.data.hierarchy[h].node.updateMatrix(); + obj.matrixWorldNeedsUpdate = true; + + } + + } + +}; + + +THREE.KeyFrameAnimation.prototype.play = function ( startTime ) { + + this.currentTime = startTime !== undefined ? startTime : 0; + + if ( this.isPlaying === false ) { + + this.isPlaying = true; + + // reset key cache + + var h, hl = this.hierarchy.length, + object, + node; + + for ( h = 0; h < hl; h ++ ) { + + object = this.hierarchy[ h ]; + node = this.data.hierarchy[ h ]; + + if ( node.animationCache === undefined ) { + + node.animationCache = {}; + node.animationCache.prevKey = null; + node.animationCache.nextKey = null; + node.animationCache.originalMatrix = object.matrix; + + } + + var keys = this.data.hierarchy[h].keys; + + if (keys.length) { + + node.animationCache.prevKey = keys[ 0 ]; + node.animationCache.nextKey = keys[ 1 ]; + + this.startTime = Math.min( keys[0].time, this.startTime ); + this.endTime = Math.max( keys[keys.length - 1].time, this.endTime ); + + } + + } + + this.update( 0 ); + + } + + this.isPaused = false; + + THREE.AnimationHandler.play( this ); + +}; + + +THREE.KeyFrameAnimation.prototype.stop = function() { + + this.isPlaying = false; + this.isPaused = false; + + THREE.AnimationHandler.stop( this ); + + // reset JIT matrix and remove cache + + for ( var h = 0; h < this.data.hierarchy.length; h ++ ) { + + var obj = this.hierarchy[ h ]; + var node = this.data.hierarchy[ h ]; + + if ( node.animationCache !== undefined ) { + + var original = node.animationCache.originalMatrix; + + original.copy( obj.matrix ); + obj.matrix = original; + + delete node.animationCache; + + } + + } + +}; + + +// Update + +THREE.KeyFrameAnimation.prototype.update = function ( delta ) { + + if ( this.isPlaying === false ) return; + + this.currentTime += delta * this.timeScale; + + // + + var duration = this.data.length; + + if ( this.loop === true && this.currentTime > duration ) { + + this.currentTime %= duration; + + } + + this.currentTime = Math.min( this.currentTime, duration ); + + for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) { + + var object = this.hierarchy[ h ]; + var node = this.data.hierarchy[ h ]; + + var keys = node.keys, + animationCache = node.animationCache; + + + if ( keys.length ) { + + var prevKey = animationCache.prevKey; + var nextKey = animationCache.nextKey; + + if ( nextKey.time <= this.currentTime ) { + + while ( nextKey.time < this.currentTime && nextKey.index > prevKey.index ) { + + prevKey = nextKey; + nextKey = keys[ prevKey.index + 1 ]; + + } + + animationCache.prevKey = prevKey; + animationCache.nextKey = nextKey; + + } + + if ( nextKey.time >= this.currentTime ) { + + prevKey.interpolate( nextKey, this.currentTime ); + + } else { + + prevKey.interpolate( nextKey, nextKey.time ); + + } + + this.data.hierarchy[ h ].node.updateMatrix(); + object.matrixWorldNeedsUpdate = true; + + } + + } + +}; + +// Get next key with + +THREE.KeyFrameAnimation.prototype.getNextKeyWith = function( sid, h, key ) { + + var keys = this.data.hierarchy[ h ].keys; + key = key % keys.length; + + for ( ; key < keys.length; key ++ ) { + + if ( keys[ key ].hasTarget( sid ) ) { + + return keys[ key ]; + + } + + } + + return keys[ 0 ]; + +}; + +// Get previous key with + +THREE.KeyFrameAnimation.prototype.getPrevKeyWith = function( sid, h, key ) { + + var keys = this.data.hierarchy[ h ].keys; + key = key >= 0 ? key : key + keys.length; + + for ( ; key >= 0; key -- ) { + + if ( keys[ key ].hasTarget( sid ) ) { + + return keys[ key ]; + + } + + } + + return keys[ keys.length - 1 ]; + +}; + +// File:src/extras/animation/MorphAnimation.js + +/** + * @author mrdoob / http://mrdoob.com + * @author willy-vvu / http://willy-vvu.github.io + */ + +THREE.MorphAnimation = function ( mesh ) { + + this.mesh = mesh; + this.frames = mesh.morphTargetInfluences.length; + this.currentTime = 0; + this.duration = 1000; + this.loop = true; + this.lastFrame = 0; + this.currentFrame = 0; + + this.isPlaying = false; + +}; + +THREE.MorphAnimation.prototype = { + + constructor: THREE.MorphAnimation, + + play: function () { + + this.isPlaying = true; + + }, + + pause: function () { + + this.isPlaying = false; + + }, + + update: function ( delta ) { + + if ( this.isPlaying === false ) return; + + this.currentTime += delta; + + if ( this.loop === true && this.currentTime > this.duration ) { + + this.currentTime %= this.duration; + + } + + this.currentTime = Math.min( this.currentTime, this.duration ); + + var interpolation = this.duration / this.frames; + var frame = Math.floor( this.currentTime / interpolation ); + + if ( frame != this.currentFrame ) { + + this.mesh.morphTargetInfluences[ this.lastFrame ] = 0; + this.mesh.morphTargetInfluences[ this.currentFrame ] = 1; + this.mesh.morphTargetInfluences[ frame ] = 0; + + this.lastFrame = this.currentFrame; + this.currentFrame = frame; + + } + + this.mesh.morphTargetInfluences[ frame ] = ( this.currentTime % interpolation ) / interpolation; + this.mesh.morphTargetInfluences[ this.lastFrame ] = 1 - this.mesh.morphTargetInfluences[ frame ]; + + } + +}; + +// File:src/extras/geometries/BoxGeometry.js + +/** + * @author mrdoob / http://mrdoob.com/ + * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Cube.as + */ + +THREE.BoxGeometry = function ( width, height, depth, widthSegments, heightSegments, depthSegments ) { + + THREE.Geometry.call( this ); + + this.type = 'BoxGeometry'; + + this.parameters = { + width: width, + height: height, + depth: depth, + widthSegments: widthSegments, + heightSegments: heightSegments, + depthSegments: depthSegments + }; + + this.widthSegments = widthSegments || 1; + this.heightSegments = heightSegments || 1; + this.depthSegments = depthSegments || 1; + + var scope = this; + + var width_half = width / 2; + var height_half = height / 2; + var depth_half = depth / 2; + + buildPlane( 'z', 'y', - 1, - 1, depth, height, width_half, 0 ); // px + buildPlane( 'z', 'y', 1, - 1, depth, height, - width_half, 1 ); // nx + buildPlane( 'x', 'z', 1, 1, width, depth, height_half, 2 ); // py + buildPlane( 'x', 'z', 1, - 1, width, depth, - height_half, 3 ); // ny + buildPlane( 'x', 'y', 1, - 1, width, height, depth_half, 4 ); // pz + buildPlane( 'x', 'y', - 1, - 1, width, height, - depth_half, 5 ); // nz + + function buildPlane( u, v, udir, vdir, width, height, depth, materialIndex ) { + + var w, ix, iy, + gridX = scope.widthSegments, + gridY = scope.heightSegments, + width_half = width / 2, + height_half = height / 2, + offset = scope.vertices.length; + + if ( ( u === 'x' && v === 'y' ) || ( u === 'y' && v === 'x' ) ) { + + w = 'z'; + + } else if ( ( u === 'x' && v === 'z' ) || ( u === 'z' && v === 'x' ) ) { + + w = 'y'; + gridY = scope.depthSegments; + + } else if ( ( u === 'z' && v === 'y' ) || ( u === 'y' && v === 'z' ) ) { + + w = 'x'; + gridX = scope.depthSegments; + + } + + var gridX1 = gridX + 1, + gridY1 = gridY + 1, + segment_width = width / gridX, + segment_height = height / gridY, + normal = new THREE.Vector3(); + + normal[ w ] = depth > 0 ? 1 : - 1; + + for ( iy = 0; iy < gridY1; iy ++ ) { + + for ( ix = 0; ix < gridX1; ix ++ ) { + + var vector = new THREE.Vector3(); + vector[ u ] = ( ix * segment_width - width_half ) * udir; + vector[ v ] = ( iy * segment_height - height_half ) * vdir; + vector[ w ] = depth; + + scope.vertices.push( vector ); + + } + + } + + for ( iy = 0; iy < gridY; iy ++ ) { + + for ( ix = 0; ix < gridX; ix ++ ) { + + var a = ix + gridX1 * iy; + var b = ix + gridX1 * ( iy + 1 ); + var c = ( ix + 1 ) + gridX1 * ( iy + 1 ); + var d = ( ix + 1 ) + gridX1 * iy; + + var uva = new THREE.Vector2( ix / gridX, 1 - iy / gridY ); + var uvb = new THREE.Vector2( ix / gridX, 1 - ( iy + 1 ) / gridY ); + var uvc = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - ( iy + 1 ) / gridY ); + var uvd = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - iy / gridY ); + + var face = new THREE.Face3( a + offset, b + offset, d + offset ); + face.normal.copy( normal ); + face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() ); + face.materialIndex = materialIndex; + + scope.faces.push( face ); + scope.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] ); + + face = new THREE.Face3( b + offset, c + offset, d + offset ); + face.normal.copy( normal ); + face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() ); + face.materialIndex = materialIndex; + + scope.faces.push( face ); + scope.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] ); + + } + + } + + } + + this.mergeVertices(); + +}; + +THREE.BoxGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.BoxGeometry.prototype.constructor = THREE.BoxGeometry; + +// File:src/extras/geometries/CircleGeometry.js + +/** + * @author hughes + */ + +THREE.CircleGeometry = function ( radius, segments, thetaStart, thetaLength ) { + + THREE.Geometry.call( this ); + + this.type = 'CircleGeometry'; + + this.parameters = { + radius: radius, + segments: segments, + thetaStart: thetaStart, + thetaLength: thetaLength + }; + + radius = radius || 50; + segments = segments !== undefined ? Math.max( 3, segments ) : 8; + + thetaStart = thetaStart !== undefined ? thetaStart : 0; + thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; + + var i, uvs = [], + center = new THREE.Vector3(), centerUV = new THREE.Vector2( 0.5, 0.5 ); + + this.vertices.push(center); + uvs.push( centerUV ); + + for ( i = 0; i <= segments; i ++ ) { + + var vertex = new THREE.Vector3(); + var segment = thetaStart + i / segments * thetaLength; + + vertex.x = radius * Math.cos( segment ); + vertex.y = radius * Math.sin( segment ); + + this.vertices.push( vertex ); + uvs.push( new THREE.Vector2( ( vertex.x / radius + 1 ) / 2, ( vertex.y / radius + 1 ) / 2 ) ); + + } + + var n = new THREE.Vector3( 0, 0, 1 ); + + for ( i = 1; i <= segments; i ++ ) { + + this.faces.push( new THREE.Face3( i, i + 1, 0, [ n.clone(), n.clone(), n.clone() ] ) ); + this.faceVertexUvs[ 0 ].push( [ uvs[ i ].clone(), uvs[ i + 1 ].clone(), centerUV.clone() ] ); + + } + + this.computeFaceNormals(); + + this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius ); + +}; + +THREE.CircleGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.CircleGeometry.prototype.constructor = THREE.CircleGeometry; + +// File:src/extras/geometries/CubeGeometry.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + + +THREE.CubeGeometry = function ( width, height, depth, widthSegments, heightSegments, depthSegments ) { + + console.warn( 'THREE.CubeGeometry has been renamed to THREE.BoxGeometry.' ); + return new THREE.BoxGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ); + + }; + +// File:src/extras/geometries/CylinderGeometry.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.CylinderGeometry = function ( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { + + THREE.Geometry.call( this ); + + this.type = 'CylinderGeometry'; + + this.parameters = { + radiusTop: radiusTop, + radiusBottom: radiusBottom, + height: height, + radialSegments: radialSegments, + heightSegments: heightSegments, + openEnded: openEnded, + thetaStart: thetaStart, + thetaLength: thetaLength + }; + + radiusTop = radiusTop !== undefined ? radiusTop : 20; + radiusBottom = radiusBottom !== undefined ? radiusBottom : 20; + height = height !== undefined ? height : 100; + + radialSegments = radialSegments || 8; + heightSegments = heightSegments || 1; + + openEnded = openEnded !== undefined ? openEnded : false; + thetaStart = thetaStart !== undefined ? thetaStart : 0; + thetaLength = thetaLength !== undefined ? thetaLength : 2 * Math.PI; + + var heightHalf = height / 2; + + var x, y, vertices = [], uvs = []; + + for ( y = 0; y <= heightSegments; y ++ ) { + + var verticesRow = []; + var uvsRow = []; + + var v = y / heightSegments; + var radius = v * ( radiusBottom - radiusTop ) + radiusTop; + + for ( x = 0; x <= radialSegments; x ++ ) { + + var u = x / radialSegments; + + var vertex = new THREE.Vector3(); + vertex.x = radius * Math.sin( u * thetaLength + thetaStart ); + vertex.y = - v * height + heightHalf; + vertex.z = radius * Math.cos( u * thetaLength + thetaStart ); + + this.vertices.push( vertex ); + + verticesRow.push( this.vertices.length - 1 ); + uvsRow.push( new THREE.Vector2( u, 1 - v ) ); + + } + + vertices.push( verticesRow ); + uvs.push( uvsRow ); + + } + + var tanTheta = ( radiusBottom - radiusTop ) / height; + var na, nb; + + for ( x = 0; x < radialSegments; x ++ ) { + + if ( radiusTop !== 0 ) { + + na = this.vertices[ vertices[ 0 ][ x ] ].clone(); + nb = this.vertices[ vertices[ 0 ][ x + 1 ] ].clone(); + + } else { + + na = this.vertices[ vertices[ 1 ][ x ] ].clone(); + nb = this.vertices[ vertices[ 1 ][ x + 1 ] ].clone(); + + } + + na.setY( Math.sqrt( na.x * na.x + na.z * na.z ) * tanTheta ).normalize(); + nb.setY( Math.sqrt( nb.x * nb.x + nb.z * nb.z ) * tanTheta ).normalize(); + + for ( y = 0; y < heightSegments; y ++ ) { + + var v1 = vertices[ y ][ x ]; + var v2 = vertices[ y + 1 ][ x ]; + var v3 = vertices[ y + 1 ][ x + 1 ]; + var v4 = vertices[ y ][ x + 1 ]; + + var n1 = na.clone(); + var n2 = na.clone(); + var n3 = nb.clone(); + var n4 = nb.clone(); + + var uv1 = uvs[ y ][ x ].clone(); + var uv2 = uvs[ y + 1 ][ x ].clone(); + var uv3 = uvs[ y + 1 ][ x + 1 ].clone(); + var uv4 = uvs[ y ][ x + 1 ].clone(); + + this.faces.push( new THREE.Face3( v1, v2, v4, [ n1, n2, n4 ] ) ); + this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv4 ] ); + + this.faces.push( new THREE.Face3( v2, v3, v4, [ n2.clone(), n3, n4.clone() ] ) ); + this.faceVertexUvs[ 0 ].push( [ uv2.clone(), uv3, uv4.clone() ] ); + + } + + } + + // top cap + + if ( openEnded === false && radiusTop > 0 ) { + + this.vertices.push( new THREE.Vector3( 0, heightHalf, 0 ) ); + + for ( x = 0; x < radialSegments; x ++ ) { + + var v1 = vertices[ 0 ][ x ]; + var v2 = vertices[ 0 ][ x + 1 ]; + var v3 = this.vertices.length - 1; + + var n1 = new THREE.Vector3( 0, 1, 0 ); + var n2 = new THREE.Vector3( 0, 1, 0 ); + var n3 = new THREE.Vector3( 0, 1, 0 ); + + var uv1 = uvs[ 0 ][ x ].clone(); + var uv2 = uvs[ 0 ][ x + 1 ].clone(); + var uv3 = new THREE.Vector2( uv2.x, 0 ); + + this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) ); + this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] ); + + } + + } + + // bottom cap + + if ( openEnded === false && radiusBottom > 0 ) { + + this.vertices.push( new THREE.Vector3( 0, - heightHalf, 0 ) ); + + for ( x = 0; x < radialSegments; x ++ ) { + + var v1 = vertices[ heightSegments ][ x + 1 ]; + var v2 = vertices[ heightSegments ][ x ]; + var v3 = this.vertices.length - 1; + + var n1 = new THREE.Vector3( 0, - 1, 0 ); + var n2 = new THREE.Vector3( 0, - 1, 0 ); + var n3 = new THREE.Vector3( 0, - 1, 0 ); + + var uv1 = uvs[ heightSegments ][ x + 1 ].clone(); + var uv2 = uvs[ heightSegments ][ x ].clone(); + var uv3 = new THREE.Vector2( uv2.x, 1 ); + + this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) ); + this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] ); + + } + + } + + this.computeFaceNormals(); + +}; + +THREE.CylinderGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.CylinderGeometry.prototype.constructor = THREE.CylinderGeometry; + +// File:src/extras/geometries/ExtrudeGeometry.js + +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * + * Creates extruded geometry from a path shape. + * + * parameters = { + * + * curveSegments: <int>, // number of points on the curves + * steps: <int>, // number of points for z-side extrusions / used for subdividing segements of extrude spline too + * amount: <int>, // Depth to extrude the shape + * + * bevelEnabled: <bool>, // turn on bevel + * bevelThickness: <float>, // how deep into the original shape bevel goes + * bevelSize: <float>, // how far from shape outline is bevel + * bevelSegments: <int>, // number of bevel layers + * + * extrudePath: <THREE.CurvePath> // 3d spline path to extrude shape along. (creates Frames if .frames aren't defined) + * frames: <THREE.TubeGeometry.FrenetFrames> // containing arrays of tangents, normals, binormals + * + * material: <int> // material index for front and back faces + * extrudeMaterial: <int> // material index for extrusion and beveled faces + * uvGenerator: <Object> // object that provides UV generator functions + * + * } + **/ + +THREE.ExtrudeGeometry = function ( shapes, options ) { + + if ( typeof( shapes ) === "undefined" ) { + shapes = []; + return; + } + + THREE.Geometry.call( this ); + + this.type = 'ExtrudeGeometry'; + + shapes = shapes instanceof Array ? shapes : [ shapes ]; + + this.addShapeList( shapes, options ); + + this.computeFaceNormals(); + + // can't really use automatic vertex normals + // as then front and back sides get smoothed too + // should do separate smoothing just for sides + + //this.computeVertexNormals(); + + //console.log( "took", ( Date.now() - startTime ) ); + +}; + +THREE.ExtrudeGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.ExtrudeGeometry.prototype.constructor = THREE.ExtrudeGeometry; + +THREE.ExtrudeGeometry.prototype.addShapeList = function ( shapes, options ) { + var sl = shapes.length; + + for ( var s = 0; s < sl; s ++ ) { + var shape = shapes[ s ]; + this.addShape( shape, options ); + } +}; + +THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) { + + var amount = options.amount !== undefined ? options.amount : 100; + + var bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6; // 10 + var bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2; // 8 + var bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3; + + var bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; // false + + var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; + + var steps = options.steps !== undefined ? options.steps : 1; + + var extrudePath = options.extrudePath; + var extrudePts, extrudeByPath = false; + + var material = options.material; + var extrudeMaterial = options.extrudeMaterial; + + // Use default WorldUVGenerator if no UV generators are specified. + var uvgen = options.UVGenerator !== undefined ? options.UVGenerator : THREE.ExtrudeGeometry.WorldUVGenerator; + + var splineTube, binormal, normal, position2; + if ( extrudePath ) { + + extrudePts = extrudePath.getSpacedPoints( steps ); + + extrudeByPath = true; + bevelEnabled = false; // bevels not supported for path extrusion + + // SETUP TNB variables + + // Reuse TNB from TubeGeomtry for now. + // TODO1 - have a .isClosed in spline? + + splineTube = options.frames !== undefined ? options.frames : new THREE.TubeGeometry.FrenetFrames(extrudePath, steps, false); + + // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length); + + binormal = new THREE.Vector3(); + normal = new THREE.Vector3(); + position2 = new THREE.Vector3(); + + } + + // Safeguards if bevels are not enabled + + if ( ! bevelEnabled ) { + + bevelSegments = 0; + bevelThickness = 0; + bevelSize = 0; + + } + + // Variables initalization + + var ahole, h, hl; // looping of holes + var scope = this; + var bevelPoints = []; + + var shapesOffset = this.vertices.length; + + var shapePoints = shape.extractPoints( curveSegments ); + + var vertices = shapePoints.shape; + var holes = shapePoints.holes; + + var reverse = ! THREE.Shape.Utils.isClockWise( vertices ) ; + + if ( reverse ) { + + vertices = vertices.reverse(); + + // Maybe we should also check if holes are in the opposite direction, just to be safe ... + + for ( h = 0, hl = holes.length; h < hl; h ++ ) { + + ahole = holes[ h ]; + + if ( THREE.Shape.Utils.isClockWise( ahole ) ) { + + holes[ h ] = ahole.reverse(); + + } + + } + + reverse = false; // If vertices are in order now, we shouldn't need to worry about them again (hopefully)! + + } + + + var faces = THREE.Shape.Utils.triangulateShape ( vertices, holes ); + + /* Vertices */ + + var contour = vertices; // vertices has all points but contour has only points of circumference + + for ( h = 0, hl = holes.length; h < hl; h ++ ) { + + ahole = holes[ h ]; + + vertices = vertices.concat( ahole ); + + } + + + function scalePt2 ( pt, vec, size ) { + + if ( ! vec ) console.log( "die" ); + + return vec.clone().multiplyScalar( size ).add( pt ); + + } + + var b, bs, t, z, + vert, vlen = vertices.length, + face, flen = faces.length, + cont, clen = contour.length; + + + // Find directions for point movement + + var RAD_TO_DEGREES = 180 / Math.PI; + + + function getBevelVec( inPt, inPrev, inNext ) { + + var EPSILON = 0.0000000001; + + // computes for inPt the corresponding point inPt' on a new contour + // shiftet by 1 unit (length of normalized vector) to the left + // if we walk along contour clockwise, this new contour is outside the old one + // + // inPt' is the intersection of the two lines parallel to the two + // adjacent edges of inPt at a distance of 1 unit on the left side. + + var v_trans_x, v_trans_y, shrink_by = 1; // resulting translation vector for inPt + + // good reading for geometry algorithms (here: line-line intersection) + // http://geomalgorithms.com/a05-_intersect-1.html + + var v_prev_x = inPt.x - inPrev.x, v_prev_y = inPt.y - inPrev.y; + var v_next_x = inNext.x - inPt.x, v_next_y = inNext.y - inPt.y; + + var v_prev_lensq = ( v_prev_x * v_prev_x + v_prev_y * v_prev_y ); + + // check for colinear edges + var colinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x ); + + if ( Math.abs( colinear0 ) > EPSILON ) { // not colinear + + // length of vectors for normalizing + + var v_prev_len = Math.sqrt( v_prev_lensq ); + var v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y ); + + // shift adjacent points by unit vectors to the left + + var ptPrevShift_x = ( inPrev.x - v_prev_y / v_prev_len ); + var ptPrevShift_y = ( inPrev.y + v_prev_x / v_prev_len ); + + var ptNextShift_x = ( inNext.x - v_next_y / v_next_len ); + var ptNextShift_y = ( inNext.y + v_next_x / v_next_len ); + + // scaling factor for v_prev to intersection point + + var sf = ( ( ptNextShift_x - ptPrevShift_x ) * v_next_y - + ( ptNextShift_y - ptPrevShift_y ) * v_next_x ) / + ( v_prev_x * v_next_y - v_prev_y * v_next_x ); + + // vector from inPt to intersection point + + v_trans_x = ( ptPrevShift_x + v_prev_x * sf - inPt.x ); + v_trans_y = ( ptPrevShift_y + v_prev_y * sf - inPt.y ); + + // Don't normalize!, otherwise sharp corners become ugly + // but prevent crazy spikes + var v_trans_lensq = ( v_trans_x * v_trans_x + v_trans_y * v_trans_y ) + if ( v_trans_lensq <= 2 ) { + return new THREE.Vector2( v_trans_x, v_trans_y ); + } else { + shrink_by = Math.sqrt( v_trans_lensq / 2 ); + } + + } else { // handle special case of colinear edges + + var direction_eq = false; // assumes: opposite + if ( v_prev_x > EPSILON ) { + if ( v_next_x > EPSILON ) { direction_eq = true; } + } else { + if ( v_prev_x < - EPSILON ) { + if ( v_next_x < - EPSILON ) { direction_eq = true; } + } else { + if ( Math.sign(v_prev_y) == Math.sign(v_next_y) ) { direction_eq = true; } + } + } + + if ( direction_eq ) { + // console.log("Warning: lines are a straight sequence"); + v_trans_x = - v_prev_y; + v_trans_y = v_prev_x; + shrink_by = Math.sqrt( v_prev_lensq ); + } else { + // console.log("Warning: lines are a straight spike"); + v_trans_x = v_prev_x; + v_trans_y = v_prev_y; + shrink_by = Math.sqrt( v_prev_lensq / 2 ); + } + + } + + return new THREE.Vector2( v_trans_x / shrink_by, v_trans_y / shrink_by ); + + } + + + var contourMovements = []; + + for ( var i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { + + if ( j === il ) j = 0; + if ( k === il ) k = 0; + + // (j)---(i)---(k) + // console.log('i,j,k', i, j , k) + + var pt_i = contour[ i ]; + var pt_j = contour[ j ]; + var pt_k = contour[ k ]; + + contourMovements[ i ]= getBevelVec( contour[ i ], contour[ j ], contour[ k ] ); + + } + + var holesMovements = [], oneHoleMovements, verticesMovements = contourMovements.concat(); + + for ( h = 0, hl = holes.length; h < hl; h ++ ) { + + ahole = holes[ h ]; + + oneHoleMovements = []; + + for ( i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { + + if ( j === il ) j = 0; + if ( k === il ) k = 0; + + // (j)---(i)---(k) + oneHoleMovements[ i ]= getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] ); + + } + + holesMovements.push( oneHoleMovements ); + verticesMovements = verticesMovements.concat( oneHoleMovements ); + + } + + + // Loop bevelSegments, 1 for the front, 1 for the back + + for ( b = 0; b < bevelSegments; b ++ ) { + //for ( b = bevelSegments; b > 0; b -- ) { + + t = b / bevelSegments; + z = bevelThickness * ( 1 - t ); + + //z = bevelThickness * t; + bs = bevelSize * ( Math.sin ( t * Math.PI/2 ) ) ; // curved + //bs = bevelSize * t ; // linear + + // contract shape + + for ( i = 0, il = contour.length; i < il; i ++ ) { + + vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); + + v( vert.x, vert.y, - z ); + + } + + // expand holes + + for ( h = 0, hl = holes.length; h < hl; h ++ ) { + + ahole = holes[ h ]; + oneHoleMovements = holesMovements[ h ]; + + for ( i = 0, il = ahole.length; i < il; i ++ ) { + + vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); + + v( vert.x, vert.y, - z ); + + } + + } + + } + + bs = bevelSize; + + // Back facing vertices + + for ( i = 0; i < vlen; i ++ ) { + + vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; + + if ( ! extrudeByPath ) { + + v( vert.x, vert.y, 0 ); + + } else { + + // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); + + normal.copy( splineTube.normals[0] ).multiplyScalar(vert.x); + binormal.copy( splineTube.binormals[0] ).multiplyScalar(vert.y); + + position2.copy( extrudePts[0] ).add(normal).add(binormal); + + v( position2.x, position2.y, position2.z ); + + } + + } + + // Add stepped vertices... + // Including front facing vertices + + var s; + + for ( s = 1; s <= steps; s ++ ) { + + for ( i = 0; i < vlen; i ++ ) { + + vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; + + if ( ! extrudeByPath ) { + + v( vert.x, vert.y, amount / steps * s ); + + } else { + + // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); + + normal.copy( splineTube.normals[s] ).multiplyScalar( vert.x ); + binormal.copy( splineTube.binormals[s] ).multiplyScalar( vert.y ); + + position2.copy( extrudePts[s] ).add( normal ).add( binormal ); + + v( position2.x, position2.y, position2.z ); + + } + + } + + } + + + // Add bevel segments planes + + //for ( b = 1; b <= bevelSegments; b ++ ) { + for ( b = bevelSegments - 1; b >= 0; b -- ) { + + t = b / bevelSegments; + z = bevelThickness * ( 1 - t ); + //bs = bevelSize * ( 1-Math.sin ( ( 1 - t ) * Math.PI/2 ) ); + bs = bevelSize * Math.sin ( t * Math.PI/2 ) ; + + // contract shape + + for ( i = 0, il = contour.length; i < il; i ++ ) { + + vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); + v( vert.x, vert.y, amount + z ); + + } + + // expand holes + + for ( h = 0, hl = holes.length; h < hl; h ++ ) { + + ahole = holes[ h ]; + oneHoleMovements = holesMovements[ h ]; + + for ( i = 0, il = ahole.length; i < il; i ++ ) { + + vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); + + if ( ! extrudeByPath ) { + + v( vert.x, vert.y, amount + z ); + + } else { + + v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z ); + + } + + } + + } + + } + + /* Faces */ + + // Top and bottom faces + + buildLidFaces(); + + // Sides faces + + buildSideFaces(); + + + ///// Internal functions + + function buildLidFaces() { + + if ( bevelEnabled ) { + + var layer = 0 ; // steps + 1 + var offset = vlen * layer; + + // Bottom faces + + for ( i = 0; i < flen; i ++ ) { + + face = faces[ i ]; + f3( face[ 2 ]+ offset, face[ 1 ]+ offset, face[ 0 ] + offset ); + + } + + layer = steps + bevelSegments * 2; + offset = vlen * layer; + + // Top faces + + for ( i = 0; i < flen; i ++ ) { + + face = faces[ i ]; + f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset ); + + } + + } else { + + // Bottom faces + + for ( i = 0; i < flen; i ++ ) { + + face = faces[ i ]; + f3( face[ 2 ], face[ 1 ], face[ 0 ] ); + + } + + // Top faces + + for ( i = 0; i < flen; i ++ ) { + + face = faces[ i ]; + f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps ); + + } + } + + } + + // Create faces for the z-sides of the shape + + function buildSideFaces() { + + var layeroffset = 0; + sidewalls( contour, layeroffset ); + layeroffset += contour.length; + + for ( h = 0, hl = holes.length; h < hl; h ++ ) { + + ahole = holes[ h ]; + sidewalls( ahole, layeroffset ); + + //, true + layeroffset += ahole.length; + + } + + } + + function sidewalls( contour, layeroffset ) { + + var j, k; + i = contour.length; + + while ( --i >= 0 ) { + + j = i; + k = i - 1; + if ( k < 0 ) k = contour.length - 1; + + //console.log('b', i,j, i-1, k,vertices.length); + + var s = 0, sl = steps + bevelSegments * 2; + + for ( s = 0; s < sl; s ++ ) { + + var slen1 = vlen * s; + var slen2 = vlen * ( s + 1 ); + + var a = layeroffset + j + slen1, + b = layeroffset + k + slen1, + c = layeroffset + k + slen2, + d = layeroffset + j + slen2; + + f4( a, b, c, d, contour, s, sl, j, k ); + + } + } + + } + + + function v( x, y, z ) { + + scope.vertices.push( new THREE.Vector3( x, y, z ) ); + + } + + function f3( a, b, c ) { + + a += shapesOffset; + b += shapesOffset; + c += shapesOffset; + + // normal, color, material + scope.faces.push( new THREE.Face3( a, b, c, null, null, material ) ); + + var uvs = uvgen.generateTopUV( scope, a, b, c ); + + scope.faceVertexUvs[ 0 ].push( uvs ); + + } + + function f4( a, b, c, d, wallContour, stepIndex, stepsLength, contourIndex1, contourIndex2 ) { + + a += shapesOffset; + b += shapesOffset; + c += shapesOffset; + d += shapesOffset; + + scope.faces.push( new THREE.Face3( a, b, d, null, null, extrudeMaterial ) ); + scope.faces.push( new THREE.Face3( b, c, d, null, null, extrudeMaterial ) ); + + var uvs = uvgen.generateSideWallUV( scope, a, b, c, d ); + + scope.faceVertexUvs[ 0 ].push( [ uvs[ 0 ], uvs[ 1 ], uvs[ 3 ] ] ); + scope.faceVertexUvs[ 0 ].push( [ uvs[ 1 ], uvs[ 2 ], uvs[ 3 ] ] ); + + } + +}; + +THREE.ExtrudeGeometry.WorldUVGenerator = { + + generateTopUV: function ( geometry, indexA, indexB, indexC ) { + + var vertices = geometry.vertices; + + var a = vertices[ indexA ]; + var b = vertices[ indexB ]; + var c = vertices[ indexC ]; + + return [ + new THREE.Vector2( a.x, a.y ), + new THREE.Vector2( b.x, b.y ), + new THREE.Vector2( c.x, c.y ) + ]; + + }, + + generateSideWallUV: function ( geometry, indexA, indexB, indexC, indexD ) { + + var vertices = geometry.vertices; + + var a = vertices[ indexA ]; + var b = vertices[ indexB ]; + var c = vertices[ indexC ]; + var d = vertices[ indexD ]; + + if ( Math.abs( a.y - b.y ) < 0.01 ) { + return [ + new THREE.Vector2( a.x, 1 - a.z ), + new THREE.Vector2( b.x, 1 - b.z ), + new THREE.Vector2( c.x, 1 - c.z ), + new THREE.Vector2( d.x, 1 - d.z ) + ]; + } else { + return [ + new THREE.Vector2( a.y, 1 - a.z ), + new THREE.Vector2( b.y, 1 - b.z ), + new THREE.Vector2( c.y, 1 - c.z ), + new THREE.Vector2( d.y, 1 - d.z ) + ]; + } + } +}; + +// File:src/extras/geometries/ShapeGeometry.js + +/** + * @author jonobr1 / http://jonobr1.com + * + * Creates a one-sided polygonal geometry from a path shape. Similar to + * ExtrudeGeometry. + * + * parameters = { + * + * curveSegments: <int>, // number of points on the curves. NOT USED AT THE MOMENT. + * + * material: <int> // material index for front and back faces + * uvGenerator: <Object> // object that provides UV generator functions + * + * } + **/ + +THREE.ShapeGeometry = function ( shapes, options ) { + + THREE.Geometry.call( this ); + + this.type = 'ShapeGeometry'; + + if ( shapes instanceof Array === false ) shapes = [ shapes ]; + + this.addShapeList( shapes, options ); + + this.computeFaceNormals(); + +}; + +THREE.ShapeGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.ShapeGeometry.prototype.constructor = THREE.ShapeGeometry; + +/** + * Add an array of shapes to THREE.ShapeGeometry. + */ +THREE.ShapeGeometry.prototype.addShapeList = function ( shapes, options ) { + + for ( var i = 0, l = shapes.length; i < l; i ++ ) { + + this.addShape( shapes[ i ], options ); + + } + + return this; + +}; + +/** + * Adds a shape to THREE.ShapeGeometry, based on THREE.ExtrudeGeometry. + */ +THREE.ShapeGeometry.prototype.addShape = function ( shape, options ) { + + if ( options === undefined ) options = {}; + var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; + + var material = options.material; + var uvgen = options.UVGenerator === undefined ? THREE.ExtrudeGeometry.WorldUVGenerator : options.UVGenerator; + + // + + var i, l, hole, s; + + var shapesOffset = this.vertices.length; + var shapePoints = shape.extractPoints( curveSegments ); + + var vertices = shapePoints.shape; + var holes = shapePoints.holes; + + var reverse = ! THREE.Shape.Utils.isClockWise( vertices ); + + if ( reverse ) { + + vertices = vertices.reverse(); + + // Maybe we should also check if holes are in the opposite direction, just to be safe... + + for ( i = 0, l = holes.length; i < l; i ++ ) { + + hole = holes[ i ]; + + if ( THREE.Shape.Utils.isClockWise( hole ) ) { + + holes[ i ] = hole.reverse(); + + } + + } + + reverse = false; + + } + + var faces = THREE.Shape.Utils.triangulateShape( vertices, holes ); + + // Vertices + + var contour = vertices; + + for ( i = 0, l = holes.length; i < l; i ++ ) { + + hole = holes[ i ]; + vertices = vertices.concat( hole ); + + } + + // + + var vert, vlen = vertices.length; + var face, flen = faces.length; + var cont, clen = contour.length; + + for ( i = 0; i < vlen; i ++ ) { + + vert = vertices[ i ]; + + this.vertices.push( new THREE.Vector3( vert.x, vert.y, 0 ) ); + + } + + for ( i = 0; i < flen; i ++ ) { + + face = faces[ i ]; + + var a = face[ 0 ] + shapesOffset; + var b = face[ 1 ] + shapesOffset; + var c = face[ 2 ] + shapesOffset; + + this.faces.push( new THREE.Face3( a, b, c, null, null, material ) ); + this.faceVertexUvs[ 0 ].push( uvgen.generateTopUV( this, a, b, c ) ); + + } + +}; + +// File:src/extras/geometries/LatheGeometry.js + +/** + * @author astrodud / http://astrodud.isgreat.org/ + * @author zz85 / https://github.com/zz85 + * @author bhouston / http://exocortex.com + */ + +// points - to create a closed torus, one must use a set of points +// like so: [ a, b, c, d, a ], see first is the same as last. +// segments - the number of circumference segments to create +// phiStart - the starting radian +// phiLength - the radian (0 to 2*PI) range of the lathed section +// 2*pi is a closed lathe, less than 2PI is a portion. + +THREE.LatheGeometry = function ( points, segments, phiStart, phiLength ) { + + THREE.Geometry.call( this ); + + this.type = 'LatheGeometry'; + + this.parameters = { + points: points, + segments: segments, + phiStart: phiStart, + phiLength: phiLength + }; + + segments = segments || 12; + phiStart = phiStart || 0; + phiLength = phiLength || 2 * Math.PI; + + var inversePointLength = 1.0 / ( points.length - 1 ); + var inverseSegments = 1.0 / segments; + + for ( var i = 0, il = segments; i <= il; i ++ ) { + + var phi = phiStart + i * inverseSegments * phiLength; + + var c = Math.cos( phi ), + s = Math.sin( phi ); + + for ( var j = 0, jl = points.length; j < jl; j ++ ) { + + var pt = points[ j ]; + + var vertex = new THREE.Vector3(); + + vertex.x = c * pt.x - s * pt.y; + vertex.y = s * pt.x + c * pt.y; + vertex.z = pt.z; + + this.vertices.push( vertex ); + + } + + } + + var np = points.length; + + for ( var i = 0, il = segments; i < il; i ++ ) { + + for ( var j = 0, jl = points.length - 1; j < jl; j ++ ) { + + var base = j + np * i; + var a = base; + var b = base + np; + var c = base + 1 + np; + var d = base + 1; + + var u0 = i * inverseSegments; + var v0 = j * inversePointLength; + var u1 = u0 + inverseSegments; + var v1 = v0 + inversePointLength; + + this.faces.push( new THREE.Face3( a, b, d ) ); + + this.faceVertexUvs[ 0 ].push( [ + + new THREE.Vector2( u0, v0 ), + new THREE.Vector2( u1, v0 ), + new THREE.Vector2( u0, v1 ) + + ] ); + + this.faces.push( new THREE.Face3( b, c, d ) ); + + this.faceVertexUvs[ 0 ].push( [ + + new THREE.Vector2( u1, v0 ), + new THREE.Vector2( u1, v1 ), + new THREE.Vector2( u0, v1 ) + + ] ); + + + } + + } + + this.mergeVertices(); + this.computeFaceNormals(); + this.computeVertexNormals(); + +}; + +THREE.LatheGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.LatheGeometry.prototype.constructor = THREE.LatheGeometry; + +// File:src/extras/geometries/PlaneGeometry.js + +/** + * @author mrdoob / http://mrdoob.com/ + * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Plane.as + */ + +THREE.PlaneGeometry = function ( width, height, widthSegments, heightSegments ) { + + console.info( 'THREE.PlaneGeometry: Consider using THREE.PlaneBufferGeometry for lower memory footprint.' ); + + THREE.Geometry.call( this ); + + this.type = 'PlaneGeometry'; + + this.parameters = { + width: width, + height: height, + widthSegments: widthSegments, + heightSegments: heightSegments + }; + + this.fromBufferGeometry( new THREE.PlaneBufferGeometry( width, height, widthSegments, heightSegments ) ); + +}; + +THREE.PlaneGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.PlaneGeometry.prototype.constructor = THREE.PlaneGeometry; + +// File:src/extras/geometries/PlaneBufferGeometry.js + +/** + * @author mrdoob / http://mrdoob.com/ + * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Plane.as + */ + +THREE.PlaneBufferGeometry = function ( width, height, widthSegments, heightSegments ) { + + THREE.BufferGeometry.call( this ); + + this.type = 'PlaneBufferGeometry'; + + this.parameters = { + width: width, + height: height, + widthSegments: widthSegments, + heightSegments: heightSegments + }; + + var width_half = width / 2; + var height_half = height / 2; + + var gridX = widthSegments || 1; + var gridY = heightSegments || 1; + + var gridX1 = gridX + 1; + var gridY1 = gridY + 1; + + var segment_width = width / gridX; + var segment_height = height / gridY; + + var vertices = new Float32Array( gridX1 * gridY1 * 3 ); + var normals = new Float32Array( gridX1 * gridY1 * 3 ); + var uvs = new Float32Array( gridX1 * gridY1 * 2 ); + + var offset = 0; + var offset2 = 0; + + for ( var iy = 0; iy < gridY1; iy ++ ) { + + var y = iy * segment_height - height_half; + + for ( var ix = 0; ix < gridX1; ix ++ ) { + + var x = ix * segment_width - width_half; + + vertices[ offset ] = x; + vertices[ offset + 1 ] = - y; + + normals[ offset + 2 ] = 1; + + uvs[ offset2 ] = ix / gridX; + uvs[ offset2 + 1 ] = 1 - ( iy / gridY ); + + offset += 3; + offset2 += 2; + + } + + } + + offset = 0; + + var indices = new ( ( vertices.length / 3 ) > 65535 ? Uint32Array : Uint16Array )( gridX * gridY * 6 ); + + for ( var iy = 0; iy < gridY; iy ++ ) { + + for ( var ix = 0; ix < gridX; ix ++ ) { + + var a = ix + gridX1 * iy; + var b = ix + gridX1 * ( iy + 1 ); + var c = ( ix + 1 ) + gridX1 * ( iy + 1 ); + var d = ( ix + 1 ) + gridX1 * iy; + + indices[ offset ] = a; + indices[ offset + 1 ] = b; + indices[ offset + 2 ] = d; + + indices[ offset + 3 ] = b; + indices[ offset + 4 ] = c; + indices[ offset + 5 ] = d; + + offset += 6; + + } + + } + + this.addAttribute( 'index', new THREE.BufferAttribute( indices, 1 ) ); + this.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) ); + this.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) ); + this.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) ); + +}; + +THREE.PlaneBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype ); +THREE.PlaneBufferGeometry.prototype.constructor = THREE.PlaneBufferGeometry; + +// File:src/extras/geometries/RingGeometry.js + +/** + * @author Kaleb Murphy + */ + +THREE.RingGeometry = function ( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) { + + THREE.Geometry.call( this ); + + this.type = 'RingGeometry'; + + this.parameters = { + innerRadius: innerRadius, + outerRadius: outerRadius, + thetaSegments: thetaSegments, + phiSegments: phiSegments, + thetaStart: thetaStart, + thetaLength: thetaLength + }; + + innerRadius = innerRadius || 0; + outerRadius = outerRadius || 50; + + thetaStart = thetaStart !== undefined ? thetaStart : 0; + thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; + + thetaSegments = thetaSegments !== undefined ? Math.max( 3, thetaSegments ) : 8; + phiSegments = phiSegments !== undefined ? Math.max( 1, phiSegments ) : 8; + + var i, o, uvs = [], radius = innerRadius, radiusStep = ( ( outerRadius - innerRadius ) / phiSegments ); + + for ( i = 0; i < phiSegments + 1; i ++ ) { // concentric circles inside ring + + for ( o = 0; o < thetaSegments + 1; o ++ ) { // number of segments per circle + + var vertex = new THREE.Vector3(); + var segment = thetaStart + o / thetaSegments * thetaLength; + vertex.x = radius * Math.cos( segment ); + vertex.y = radius * Math.sin( segment ); + + this.vertices.push( vertex ); + uvs.push( new THREE.Vector2( ( vertex.x / outerRadius + 1 ) / 2, ( vertex.y / outerRadius + 1 ) / 2 ) ); + } + + radius += radiusStep; + + } + + var n = new THREE.Vector3( 0, 0, 1 ); + + for ( i = 0; i < phiSegments; i ++ ) { // concentric circles inside ring + + var thetaSegment = i * (thetaSegments + 1); + + for ( o = 0; o < thetaSegments ; o ++ ) { // number of segments per circle + + var segment = o + thetaSegment; + + var v1 = segment; + var v2 = segment + thetaSegments + 1; + var v3 = segment + thetaSegments + 2; + + this.faces.push( new THREE.Face3( v1, v2, v3, [ n.clone(), n.clone(), n.clone() ] ) ); + this.faceVertexUvs[ 0 ].push( [ uvs[ v1 ].clone(), uvs[ v2 ].clone(), uvs[ v3 ].clone() ]); + + v1 = segment; + v2 = segment + thetaSegments + 2; + v3 = segment + 1; + + this.faces.push( new THREE.Face3( v1, v2, v3, [ n.clone(), n.clone(), n.clone() ] ) ); + this.faceVertexUvs[ 0 ].push( [ uvs[ v1 ].clone(), uvs[ v2 ].clone(), uvs[ v3 ].clone() ]); + + } + } + + this.computeFaceNormals(); + + this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius ); + +}; + +THREE.RingGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.RingGeometry.prototype.constructor = THREE.RingGeometry; + + +// File:src/extras/geometries/SphereGeometry.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.SphereGeometry = function ( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) { + + THREE.Geometry.call( this ); + + this.type = 'SphereGeometry'; + + this.parameters = { + radius: radius, + widthSegments: widthSegments, + heightSegments: heightSegments, + phiStart: phiStart, + phiLength: phiLength, + thetaStart: thetaStart, + thetaLength: thetaLength + }; + + radius = radius || 50; + + widthSegments = Math.max( 3, Math.floor( widthSegments ) || 8 ); + heightSegments = Math.max( 2, Math.floor( heightSegments ) || 6 ); + + phiStart = phiStart !== undefined ? phiStart : 0; + phiLength = phiLength !== undefined ? phiLength : Math.PI * 2; + + thetaStart = thetaStart !== undefined ? thetaStart : 0; + thetaLength = thetaLength !== undefined ? thetaLength : Math.PI; + + var x, y, vertices = [], uvs = []; + + for ( y = 0; y <= heightSegments; y ++ ) { + + var verticesRow = []; + var uvsRow = []; + + for ( x = 0; x <= widthSegments; x ++ ) { + + var u = x / widthSegments; + var v = y / heightSegments; + + var vertex = new THREE.Vector3(); + vertex.x = - radius * Math.cos( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); + vertex.y = radius * Math.cos( thetaStart + v * thetaLength ); + vertex.z = radius * Math.sin( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); + + this.vertices.push( vertex ); + + verticesRow.push( this.vertices.length - 1 ); + uvsRow.push( new THREE.Vector2( u, 1 - v ) ); + + } + + vertices.push( verticesRow ); + uvs.push( uvsRow ); + + } + + for ( y = 0; y < heightSegments; y ++ ) { + + for ( x = 0; x < widthSegments; x ++ ) { + + var v1 = vertices[ y ][ x + 1 ]; + var v2 = vertices[ y ][ x ]; + var v3 = vertices[ y + 1 ][ x ]; + var v4 = vertices[ y + 1 ][ x + 1 ]; + + var n1 = this.vertices[ v1 ].clone().normalize(); + var n2 = this.vertices[ v2 ].clone().normalize(); + var n3 = this.vertices[ v3 ].clone().normalize(); + var n4 = this.vertices[ v4 ].clone().normalize(); + + var uv1 = uvs[ y ][ x + 1 ].clone(); + var uv2 = uvs[ y ][ x ].clone(); + var uv3 = uvs[ y + 1 ][ x ].clone(); + var uv4 = uvs[ y + 1 ][ x + 1 ].clone(); + + if ( Math.abs( this.vertices[ v1 ].y ) === radius ) { + + uv1.x = ( uv1.x + uv2.x ) / 2; + this.faces.push( new THREE.Face3( v1, v3, v4, [ n1, n3, n4 ] ) ); + this.faceVertexUvs[ 0 ].push( [ uv1, uv3, uv4 ] ); + + } else if ( Math.abs( this.vertices[ v3 ].y ) === radius ) { + + uv3.x = ( uv3.x + uv4.x ) / 2; + this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) ); + this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] ); + + } else { + + this.faces.push( new THREE.Face3( v1, v2, v4, [ n1, n2, n4 ] ) ); + this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv4 ] ); + + this.faces.push( new THREE.Face3( v2, v3, v4, [ n2.clone(), n3, n4.clone() ] ) ); + this.faceVertexUvs[ 0 ].push( [ uv2.clone(), uv3, uv4.clone() ] ); + + } + + } + + } + + this.computeFaceNormals(); + + this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius ); + +}; + +THREE.SphereGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.SphereGeometry.prototype.constructor = THREE.SphereGeometry; + +// File:src/extras/geometries/TextGeometry.js + +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * @author alteredq / http://alteredqualia.com/ + * + * For creating 3D text geometry in three.js + * + * Text = 3D Text + * + * parameters = { + * size: <float>, // size of the text + * height: <float>, // thickness to extrude text + * curveSegments: <int>, // number of points on the curves + * + * font: <string>, // font name + * weight: <string>, // font weight (normal, bold) + * style: <string>, // font style (normal, italics) + * + * bevelEnabled: <bool>, // turn on bevel + * bevelThickness: <float>, // how deep into text bevel goes + * bevelSize: <float>, // how far from text outline is bevel + * } + * + */ + +/* Usage Examples + + // TextGeometry wrapper + + var text3d = new TextGeometry( text, options ); + + // Complete manner + + var textShapes = THREE.FontUtils.generateShapes( text, options ); + var text3d = new ExtrudeGeometry( textShapes, options ); + +*/ + + +THREE.TextGeometry = function ( text, parameters ) { + + parameters = parameters || {}; + + var textShapes = THREE.FontUtils.generateShapes( text, parameters ); + + // translate parameters to ExtrudeGeometry API + + parameters.amount = parameters.height !== undefined ? parameters.height : 50; + + // defaults + + if ( parameters.bevelThickness === undefined ) parameters.bevelThickness = 10; + if ( parameters.bevelSize === undefined ) parameters.bevelSize = 8; + if ( parameters.bevelEnabled === undefined ) parameters.bevelEnabled = false; + + THREE.ExtrudeGeometry.call( this, textShapes, parameters ); + + this.type = 'TextGeometry'; + +}; + +THREE.TextGeometry.prototype = Object.create( THREE.ExtrudeGeometry.prototype ); +THREE.TextGeometry.prototype.constructor = THREE.TextGeometry; + +// File:src/extras/geometries/TorusGeometry.js + +/** + * @author oosmoxiecode + * @author mrdoob / http://mrdoob.com/ + * based on http://code.google.com/p/away3d/source/browse/trunk/fp10/Away3DLite/src/away3dlite/primitives/Torus.as?r=2888 + */ + +THREE.TorusGeometry = function ( radius, tube, radialSegments, tubularSegments, arc ) { + + THREE.Geometry.call( this ); + + this.type = 'TorusGeometry'; + + this.parameters = { + radius: radius, + tube: tube, + radialSegments: radialSegments, + tubularSegments: tubularSegments, + arc: arc + }; + + radius = radius || 100; + tube = tube || 40; + radialSegments = radialSegments || 8; + tubularSegments = tubularSegments || 6; + arc = arc || Math.PI * 2; + + var center = new THREE.Vector3(), uvs = [], normals = []; + + for ( var j = 0; j <= radialSegments; j ++ ) { + + for ( var i = 0; i <= tubularSegments; i ++ ) { + + var u = i / tubularSegments * arc; + var v = j / radialSegments * Math.PI * 2; + + center.x = radius * Math.cos( u ); + center.y = radius * Math.sin( u ); + + var vertex = new THREE.Vector3(); + vertex.x = ( radius + tube * Math.cos( v ) ) * Math.cos( u ); + vertex.y = ( radius + tube * Math.cos( v ) ) * Math.sin( u ); + vertex.z = tube * Math.sin( v ); + + this.vertices.push( vertex ); + + uvs.push( new THREE.Vector2( i / tubularSegments, j / radialSegments ) ); + normals.push( vertex.clone().sub( center ).normalize() ); + + } + + } + + for ( var j = 1; j <= radialSegments; j ++ ) { + + for ( var i = 1; i <= tubularSegments; i ++ ) { + + var a = ( tubularSegments + 1 ) * j + i - 1; + var b = ( tubularSegments + 1 ) * ( j - 1 ) + i - 1; + var c = ( tubularSegments + 1 ) * ( j - 1 ) + i; + var d = ( tubularSegments + 1 ) * j + i; + + var face = new THREE.Face3( a, b, d, [ normals[ a ].clone(), normals[ b ].clone(), normals[ d ].clone() ] ); + this.faces.push( face ); + this.faceVertexUvs[ 0 ].push( [ uvs[ a ].clone(), uvs[ b ].clone(), uvs[ d ].clone() ] ); + + face = new THREE.Face3( b, c, d, [ normals[ b ].clone(), normals[ c ].clone(), normals[ d ].clone() ] ); + this.faces.push( face ); + this.faceVertexUvs[ 0 ].push( [ uvs[ b ].clone(), uvs[ c ].clone(), uvs[ d ].clone() ] ); + + } + + } + + this.computeFaceNormals(); + +}; + +THREE.TorusGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.TorusGeometry.prototype.constructor = THREE.TorusGeometry; + +// File:src/extras/geometries/TorusKnotGeometry.js + +/** + * @author oosmoxiecode + * based on http://code.google.com/p/away3d/source/browse/trunk/fp10/Away3D/src/away3d/primitives/TorusKnot.as?spec=svn2473&r=2473 + */ + +THREE.TorusKnotGeometry = function ( radius, tube, radialSegments, tubularSegments, p, q, heightScale ) { + + THREE.Geometry.call( this ); + + this.type = 'TorusKnotGeometry'; + + this.parameters = { + radius: radius, + tube: tube, + radialSegments: radialSegments, + tubularSegments: tubularSegments, + p: p, + q: q, + heightScale: heightScale + }; + + radius = radius || 100; + tube = tube || 40; + radialSegments = radialSegments || 64; + tubularSegments = tubularSegments || 8; + p = p || 2; + q = q || 3; + heightScale = heightScale || 1; + + var grid = new Array( radialSegments ); + var tang = new THREE.Vector3(); + var n = new THREE.Vector3(); + var bitan = new THREE.Vector3(); + + for ( var i = 0; i < radialSegments; ++ i ) { + + grid[ i ] = new Array( tubularSegments ); + var u = i / radialSegments * 2 * p * Math.PI; + var p1 = getPos( u, q, p, radius, heightScale ); + var p2 = getPos( u + 0.01, q, p, radius, heightScale ); + tang.subVectors( p2, p1 ); + n.addVectors( p2, p1 ); + + bitan.crossVectors( tang, n ); + n.crossVectors( bitan, tang ); + bitan.normalize(); + n.normalize(); + + for ( var j = 0; j < tubularSegments; ++ j ) { + + var v = j / tubularSegments * 2 * Math.PI; + var cx = - tube * Math.cos( v ); // TODO: Hack: Negating it so it faces outside. + var cy = tube * Math.sin( v ); + + var pos = new THREE.Vector3(); + pos.x = p1.x + cx * n.x + cy * bitan.x; + pos.y = p1.y + cx * n.y + cy * bitan.y; + pos.z = p1.z + cx * n.z + cy * bitan.z; + + grid[ i ][ j ] = this.vertices.push( pos ) - 1; + + } + + } + + for ( var i = 0; i < radialSegments; ++ i ) { + + for ( var j = 0; j < tubularSegments; ++ j ) { + + var ip = ( i + 1 ) % radialSegments; + var jp = ( j + 1 ) % tubularSegments; + + var a = grid[ i ][ j ]; + var b = grid[ ip ][ j ]; + var c = grid[ ip ][ jp ]; + var d = grid[ i ][ jp ]; + + var uva = new THREE.Vector2( i / radialSegments, j / tubularSegments ); + var uvb = new THREE.Vector2( ( i + 1 ) / radialSegments, j / tubularSegments ); + var uvc = new THREE.Vector2( ( i + 1 ) / radialSegments, ( j + 1 ) / tubularSegments ); + var uvd = new THREE.Vector2( i / radialSegments, ( j + 1 ) / tubularSegments ); + + this.faces.push( new THREE.Face3( a, b, d ) ); + this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] ); + + this.faces.push( new THREE.Face3( b, c, d ) ); + this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] ); + + } + } + + this.computeFaceNormals(); + this.computeVertexNormals(); + + function getPos( u, in_q, in_p, radius, heightScale ) { + + var cu = Math.cos( u ); + var su = Math.sin( u ); + var quOverP = in_q / in_p * u; + var cs = Math.cos( quOverP ); + + var tx = radius * ( 2 + cs ) * 0.5 * cu; + var ty = radius * ( 2 + cs ) * su * 0.5; + var tz = heightScale * radius * Math.sin( quOverP ) * 0.5; + + return new THREE.Vector3( tx, ty, tz ); + + } + +}; + +THREE.TorusKnotGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.TorusKnotGeometry.prototype.constructor = THREE.TorusKnotGeometry; + +// File:src/extras/geometries/TubeGeometry.js + +/** + * @author WestLangley / https://github.com/WestLangley + * @author zz85 / https://github.com/zz85 + * @author miningold / https://github.com/miningold + * @author jonobr1 / https://github.com/jonobr1 + * + * Modified from the TorusKnotGeometry by @oosmoxiecode + * + * Creates a tube which extrudes along a 3d spline + * + * Uses parallel transport frames as described in + * http://www.cs.indiana.edu/pub/techreports/TR425.pdf + */ + +THREE.TubeGeometry = function ( path, segments, radius, radialSegments, closed, taper ) { + + THREE.Geometry.call( this ); + + this.type = 'TubeGeometry'; + + this.parameters = { + path: path, + segments: segments, + radius: radius, + radialSegments: radialSegments, + closed: closed + }; + + segments = segments || 64; + radius = radius || 1; + radialSegments = radialSegments || 8; + closed = closed || false; + taper = taper || THREE.TubeGeometry.NoTaper; + + var grid = []; + + var scope = this, + + tangent, + normal, + binormal, + + numpoints = segments + 1, + + x, y, z, + tx, ty, tz, + u, v, r, + + cx, cy, + pos, pos2 = new THREE.Vector3(), + i, j, + ip, jp, + a, b, c, d, + uva, uvb, uvc, uvd; + + var frames = new THREE.TubeGeometry.FrenetFrames( path, segments, closed ), + tangents = frames.tangents, + normals = frames.normals, + binormals = frames.binormals; + + // proxy internals + this.tangents = tangents; + this.normals = normals; + this.binormals = binormals; + + function vert( x, y, z ) { + + return scope.vertices.push( new THREE.Vector3( x, y, z ) ) - 1; + + } + + // consruct the grid + + for ( i = 0; i < numpoints; i ++ ) { + + grid[ i ] = []; + + u = i / ( numpoints - 1 ); + + pos = path.getPointAt( u ); + + tangent = tangents[ i ]; + normal = normals[ i ]; + binormal = binormals[ i ]; + + r = radius * taper( u ); + + for ( j = 0; j < radialSegments; j ++ ) { + + v = j / radialSegments * 2 * Math.PI; + + cx = - r * Math.cos( v ); // TODO: Hack: Negating it so it faces outside. + cy = r * Math.sin( v ); + + pos2.copy( pos ); + pos2.x += cx * normal.x + cy * binormal.x; + pos2.y += cx * normal.y + cy * binormal.y; + pos2.z += cx * normal.z + cy * binormal.z; + + grid[ i ][ j ] = vert( pos2.x, pos2.y, pos2.z ); + + } + } + + + // construct the mesh + + for ( i = 0; i < segments; i ++ ) { + + for ( j = 0; j < radialSegments; j ++ ) { + + ip = ( closed ) ? (i + 1) % segments : i + 1; + jp = (j + 1) % radialSegments; + + a = grid[ i ][ j ]; // *** NOT NECESSARILY PLANAR ! *** + b = grid[ ip ][ j ]; + c = grid[ ip ][ jp ]; + d = grid[ i ][ jp ]; + + uva = new THREE.Vector2( i / segments, j / radialSegments ); + uvb = new THREE.Vector2( ( i + 1 ) / segments, j / radialSegments ); + uvc = new THREE.Vector2( ( i + 1 ) / segments, ( j + 1 ) / radialSegments ); + uvd = new THREE.Vector2( i / segments, ( j + 1 ) / radialSegments ); + + this.faces.push( new THREE.Face3( a, b, d ) ); + this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] ); + + this.faces.push( new THREE.Face3( b, c, d ) ); + this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] ); + + } + } + + this.computeFaceNormals(); + this.computeVertexNormals(); + +}; + +THREE.TubeGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.TubeGeometry.prototype.constructor = THREE.TubeGeometry; + +THREE.TubeGeometry.NoTaper = function ( u ) { + + return 1; + +}; + +THREE.TubeGeometry.SinusoidalTaper = function ( u ) { + + return Math.sin( Math.PI * u ); + +}; + +// For computing of Frenet frames, exposing the tangents, normals and binormals the spline +THREE.TubeGeometry.FrenetFrames = function ( path, segments, closed ) { + + var tangent = new THREE.Vector3(), + normal = new THREE.Vector3(), + binormal = new THREE.Vector3(), + + tangents = [], + normals = [], + binormals = [], + + vec = new THREE.Vector3(), + mat = new THREE.Matrix4(), + + numpoints = segments + 1, + theta, + epsilon = 0.0001, + smallest, + + tx, ty, tz, + i, u, v; + + + // expose internals + this.tangents = tangents; + this.normals = normals; + this.binormals = binormals; + + // compute the tangent vectors for each segment on the path + + for ( i = 0; i < numpoints; i ++ ) { + + u = i / ( numpoints - 1 ); + + tangents[ i ] = path.getTangentAt( u ); + tangents[ i ].normalize(); + + } + + initialNormal3(); + + /* + function initialNormal1(lastBinormal) { + // fixed start binormal. Has dangers of 0 vectors + normals[ 0 ] = new THREE.Vector3(); + binormals[ 0 ] = new THREE.Vector3(); + if (lastBinormal===undefined) lastBinormal = new THREE.Vector3( 0, 0, 1 ); + normals[ 0 ].crossVectors( lastBinormal, tangents[ 0 ] ).normalize(); + binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ).normalize(); + } + + function initialNormal2() { + + // This uses the Frenet-Serret formula for deriving binormal + var t2 = path.getTangentAt( epsilon ); + + normals[ 0 ] = new THREE.Vector3().subVectors( t2, tangents[ 0 ] ).normalize(); + binormals[ 0 ] = new THREE.Vector3().crossVectors( tangents[ 0 ], normals[ 0 ] ); + + normals[ 0 ].crossVectors( binormals[ 0 ], tangents[ 0 ] ).normalize(); // last binormal x tangent + binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ).normalize(); + + } + */ + + function initialNormal3() { + // select an initial normal vector perpenicular to the first tangent vector, + // and in the direction of the smallest tangent xyz component + + normals[ 0 ] = new THREE.Vector3(); + binormals[ 0 ] = new THREE.Vector3(); + smallest = Number.MAX_VALUE; + tx = Math.abs( tangents[ 0 ].x ); + ty = Math.abs( tangents[ 0 ].y ); + tz = Math.abs( tangents[ 0 ].z ); + + if ( tx <= smallest ) { + smallest = tx; + normal.set( 1, 0, 0 ); + } + + if ( ty <= smallest ) { + smallest = ty; + normal.set( 0, 1, 0 ); + } + + if ( tz <= smallest ) { + normal.set( 0, 0, 1 ); + } + + vec.crossVectors( tangents[ 0 ], normal ).normalize(); + + normals[ 0 ].crossVectors( tangents[ 0 ], vec ); + binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ); + } + + + // compute the slowly-varying normal and binormal vectors for each segment on the path + + for ( i = 1; i < numpoints; i ++ ) { + + normals[ i ] = normals[ i-1 ].clone(); + + binormals[ i ] = binormals[ i-1 ].clone(); + + vec.crossVectors( tangents[ i-1 ], tangents[ i ] ); + + if ( vec.length() > epsilon ) { + + vec.normalize(); + + theta = Math.acos( THREE.Math.clamp( tangents[ i-1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors + + normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) ); + + } + + binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); + + } + + + // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same + + if ( closed ) { + + theta = Math.acos( THREE.Math.clamp( normals[ 0 ].dot( normals[ numpoints-1 ] ), - 1, 1 ) ); + theta /= ( numpoints - 1 ); + + if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ numpoints-1 ] ) ) > 0 ) { + + theta = - theta; + + } + + for ( i = 1; i < numpoints; i ++ ) { + + // twist a little... + normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) ); + binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); + + } + + } +}; + +// File:src/extras/geometries/PolyhedronGeometry.js + +/** + * @author clockworkgeek / https://github.com/clockworkgeek + * @author timothypratley / https://github.com/timothypratley + * @author WestLangley / http://github.com/WestLangley +*/ + +THREE.PolyhedronGeometry = function ( vertices, indices, radius, detail ) { + + THREE.Geometry.call( this ); + + this.type = 'PolyhedronGeometry'; + + this.parameters = { + vertices: vertices, + indices: indices, + radius: radius, + detail: detail + }; + + radius = radius || 1; + detail = detail || 0; + + var that = this; + + for ( var i = 0, l = vertices.length; i < l; i += 3 ) { + + prepare( new THREE.Vector3( vertices[ i ], vertices[ i + 1 ], vertices[ i + 2 ] ) ); + + } + + var midpoints = [], p = this.vertices; + + var faces = []; + + for ( var i = 0, j = 0, l = indices.length; i < l; i += 3, j ++ ) { + + var v1 = p[ indices[ i ] ]; + var v2 = p[ indices[ i + 1 ] ]; + var v3 = p[ indices[ i + 2 ] ]; + + faces[ j ] = new THREE.Face3( v1.index, v2.index, v3.index, [ v1.clone(), v2.clone(), v3.clone() ] ); + + } + + var centroid = new THREE.Vector3(); + + for ( var i = 0, l = faces.length; i < l; i ++ ) { + + subdivide( faces[ i ], detail ); + + } + + + // Handle case when face straddles the seam + + for ( var i = 0, l = this.faceVertexUvs[ 0 ].length; i < l; i ++ ) { + + var uvs = this.faceVertexUvs[ 0 ][ i ]; + + var x0 = uvs[ 0 ].x; + var x1 = uvs[ 1 ].x; + var x2 = uvs[ 2 ].x; + + var max = Math.max( x0, Math.max( x1, x2 ) ); + var min = Math.min( x0, Math.min( x1, x2 ) ); + + if ( max > 0.9 && min < 0.1 ) { // 0.9 is somewhat arbitrary + + if ( x0 < 0.2 ) uvs[ 0 ].x += 1; + if ( x1 < 0.2 ) uvs[ 1 ].x += 1; + if ( x2 < 0.2 ) uvs[ 2 ].x += 1; + + } + + } + + + // Apply radius + + for ( var i = 0, l = this.vertices.length; i < l; i ++ ) { + + this.vertices[ i ].multiplyScalar( radius ); + + } + + + // Merge vertices + + this.mergeVertices(); + + this.computeFaceNormals(); + + this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius ); + + + // Project vector onto sphere's surface + + function prepare( vector ) { + + var vertex = vector.normalize().clone(); + vertex.index = that.vertices.push( vertex ) - 1; + + // Texture coords are equivalent to map coords, calculate angle and convert to fraction of a circle. + + var u = azimuth( vector ) / 2 / Math.PI + 0.5; + var v = inclination( vector ) / Math.PI + 0.5; + vertex.uv = new THREE.Vector2( u, 1 - v ); + + return vertex; + + } + + + // Approximate a curved face with recursively sub-divided triangles. + + function make( v1, v2, v3 ) { + + var face = new THREE.Face3( v1.index, v2.index, v3.index, [ v1.clone(), v2.clone(), v3.clone() ] ); + that.faces.push( face ); + + centroid.copy( v1 ).add( v2 ).add( v3 ).divideScalar( 3 ); + + var azi = azimuth( centroid ); + + that.faceVertexUvs[ 0 ].push( [ + correctUV( v1.uv, v1, azi ), + correctUV( v2.uv, v2, azi ), + correctUV( v3.uv, v3, azi ) + ] ); + + } + + + // Analytically subdivide a face to the required detail level. + + function subdivide( face, detail ) { + + var cols = Math.pow(2, detail); + var cells = Math.pow(4, detail); + var a = prepare( that.vertices[ face.a ] ); + var b = prepare( that.vertices[ face.b ] ); + var c = prepare( that.vertices[ face.c ] ); + var v = []; + + // Construct all of the vertices for this subdivision. + + for ( var i = 0 ; i <= cols; i ++ ) { + + v[ i ] = []; + + var aj = prepare( a.clone().lerp( c, i / cols ) ); + var bj = prepare( b.clone().lerp( c, i / cols ) ); + var rows = cols - i; + + for ( var j = 0; j <= rows; j ++) { + + if ( j == 0 && i == cols ) { + + v[ i ][ j ] = aj; + + } else { + + v[ i ][ j ] = prepare( aj.clone().lerp( bj, j / rows ) ); + + } + + } + + } + + // Construct all of the faces. + + for ( var i = 0; i < cols ; i ++ ) { + + for ( var j = 0; j < 2 * (cols - i) - 1; j ++ ) { + + var k = Math.floor( j / 2 ); + + if ( j % 2 == 0 ) { + + make( + v[ i ][ k + 1], + v[ i + 1 ][ k ], + v[ i ][ k ] + ); + + } else { + + make( + v[ i ][ k + 1 ], + v[ i + 1][ k + 1], + v[ i + 1 ][ k ] + ); + + } + + } + + } + + } + + + // Angle around the Y axis, counter-clockwise when looking from above. + + function azimuth( vector ) { + + return Math.atan2( vector.z, - vector.x ); + + } + + + // Angle above the XZ plane. + + function inclination( vector ) { + + return Math.atan2( - vector.y, Math.sqrt( ( vector.x * vector.x ) + ( vector.z * vector.z ) ) ); + + } + + + // Texture fixing helper. Spheres have some odd behaviours. + + function correctUV( uv, vector, azimuth ) { + + if ( ( azimuth < 0 ) && ( uv.x === 1 ) ) uv = new THREE.Vector2( uv.x - 1, uv.y ); + if ( ( vector.x === 0 ) && ( vector.z === 0 ) ) uv = new THREE.Vector2( azimuth / 2 / Math.PI + 0.5, uv.y ); + return uv.clone(); + + } + + +}; + +THREE.PolyhedronGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.PolyhedronGeometry.prototype.constructor = THREE.PolyhedronGeometry; + +// File:src/extras/geometries/DodecahedronGeometry.js + +/** + * @author Abe Pazos / https://hamoid.com + */ + +THREE.DodecahedronGeometry = function ( radius, detail ) { + + this.parameters = { + radius: radius, + detail: detail + }; + + var t = ( 1 + Math.sqrt( 5 ) ) / 2; + var r = 1 / t; + + var vertices = [ + + // (±1, ±1, ±1) + -1, -1, -1, -1, -1, 1, + -1, 1, -1, -1, 1, 1, + 1, -1, -1, 1, -1, 1, + 1, 1, -1, 1, 1, 1, + + // (0, ±1/φ, ±φ) + 0, -r, -t, 0, -r, t, + 0, r, -t, 0, r, t, + + // (±1/φ, ±φ, 0) + -r, -t, 0, -r, t, 0, + r, -t, 0, r, t, 0, + + // (±φ, 0, ±1/φ) + -t, 0, -r, t, 0, -r, + -t, 0, r, t, 0, r + ]; + + var indices = [ + 3, 11, 7, 3, 7, 15, 3, 15, 13, + 7, 19, 17, 7, 17, 6, 7, 6, 15, + 17, 4, 8, 17, 8, 10, 17, 10, 6, + 8, 0, 16, 8, 16, 2, 8, 2, 10, + 0, 12, 1, 0, 1, 18, 0, 18, 16, + 6, 10, 2, 6, 2, 13, 6, 13, 15, + 2, 16, 18, 2, 18, 3, 2, 3, 13, + 18, 1, 9, 18, 9, 11, 18, 11, 3, + 4, 14, 12, 4, 12, 0, 4, 0, 8, + 11, 9, 5, 11, 5, 19, 11, 19, 7, + 19, 5, 14, 19, 14, 4, 19, 4, 17, + 1, 12, 14, 1, 14, 5, 1, 5, 9 + ]; + + THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail ); + +}; + +THREE.DodecahedronGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.DodecahedronGeometry.prototype.constructor = THREE.DodecahedronGeometry; + +// File:src/extras/geometries/IcosahedronGeometry.js + +/** + * @author timothypratley / https://github.com/timothypratley + */ + +THREE.IcosahedronGeometry = function ( radius, detail ) { + + var t = ( 1 + Math.sqrt( 5 ) ) / 2; + + var vertices = [ + - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, 0, + 0, - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, + t, 0, - 1, t, 0, 1, - t, 0, - 1, - t, 0, 1 + ]; + + var indices = [ + 0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11, + 1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8, + 3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9, + 4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1 + ]; + + THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail ); + + this.type = 'IcosahedronGeometry'; + + this.parameters = { + radius: radius, + detail: detail + }; +}; + +THREE.IcosahedronGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.IcosahedronGeometry.prototype.constructor = THREE.IcosahedronGeometry; + +// File:src/extras/geometries/OctahedronGeometry.js + +/** + * @author timothypratley / https://github.com/timothypratley + */ + +THREE.OctahedronGeometry = function ( radius, detail ) { + + this.parameters = { + radius: radius, + detail: detail + }; + + var vertices = [ + 1, 0, 0, - 1, 0, 0, 0, 1, 0, 0,- 1, 0, 0, 0, 1, 0, 0,- 1 + ]; + + var indices = [ + 0, 2, 4, 0, 4, 3, 0, 3, 5, 0, 5, 2, 1, 2, 5, 1, 5, 3, 1, 3, 4, 1, 4, 2 + ]; + + THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail ); + + this.type = 'OctahedronGeometry'; + + this.parameters = { + radius: radius, + detail: detail + }; +}; + +THREE.OctahedronGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.OctahedronGeometry.prototype.constructor = THREE.OctahedronGeometry; + +// File:src/extras/geometries/TetrahedronGeometry.js + +/** + * @author timothypratley / https://github.com/timothypratley + */ + +THREE.TetrahedronGeometry = function ( radius, detail ) { + + var vertices = [ + 1, 1, 1, - 1, - 1, 1, - 1, 1, - 1, 1, - 1, - 1 + ]; + + var indices = [ + 2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1 + ]; + + THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail ); + + this.type = 'TetrahedronGeometry'; + + this.parameters = { + radius: radius, + detail: detail + }; + +}; + +THREE.TetrahedronGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.TetrahedronGeometry.prototype.constructor = THREE.TetrahedronGeometry; + +// File:src/extras/geometries/ParametricGeometry.js + +/** + * @author zz85 / https://github.com/zz85 + * Parametric Surfaces Geometry + * based on the brilliant article by @prideout http://prideout.net/blog/?p=44 + * + * new THREE.ParametricGeometry( parametricFunction, uSegments, ySegements ); + * + */ + +THREE.ParametricGeometry = function ( func, slices, stacks ) { + + THREE.Geometry.call( this ); + + this.type = 'ParametricGeometry'; + + this.parameters = { + func: func, + slices: slices, + stacks: stacks + }; + + var verts = this.vertices; + var faces = this.faces; + var uvs = this.faceVertexUvs[ 0 ]; + + var i, il, j, p; + var u, v; + + var stackCount = stacks + 1; + var sliceCount = slices + 1; + + for ( i = 0; i <= stacks; i ++ ) { + + v = i / stacks; + + for ( j = 0; j <= slices; j ++ ) { + + u = j / slices; + + p = func( u, v ); + verts.push( p ); + + } + } + + var a, b, c, d; + var uva, uvb, uvc, uvd; + + for ( i = 0; i < stacks; i ++ ) { + + for ( j = 0; j < slices; j ++ ) { + + a = i * sliceCount + j; + b = i * sliceCount + j + 1; + c = (i + 1) * sliceCount + j + 1; + d = (i + 1) * sliceCount + j; + + uva = new THREE.Vector2( j / slices, i / stacks ); + uvb = new THREE.Vector2( ( j + 1 ) / slices, i / stacks ); + uvc = new THREE.Vector2( ( j + 1 ) / slices, ( i + 1 ) / stacks ); + uvd = new THREE.Vector2( j / slices, ( i + 1 ) / stacks ); + + faces.push( new THREE.Face3( a, b, d ) ); + uvs.push( [ uva, uvb, uvd ] ); + + faces.push( new THREE.Face3( b, c, d ) ); + uvs.push( [ uvb.clone(), uvc, uvd.clone() ] ); + + } + + } + + // console.log(this); + + // magic bullet + // var diff = this.mergeVertices(); + // console.log('removed ', diff, ' vertices by merging'); + + this.computeFaceNormals(); + this.computeVertexNormals(); + +}; + +THREE.ParametricGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.ParametricGeometry.prototype.constructor = THREE.ParametricGeometry; + +// File:src/extras/helpers/AxisHelper.js + +/** + * @author sroucheray / http://sroucheray.org/ + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.AxisHelper = function ( size ) { + + size = size || 1; + + var vertices = new Float32Array( [ + 0, 0, 0, size, 0, 0, + 0, 0, 0, 0, size, 0, + 0, 0, 0, 0, 0, size + ] ); + + var colors = new Float32Array( [ + 1, 0, 0, 1, 0.6, 0, + 0, 1, 0, 0.6, 1, 0, + 0, 0, 1, 0, 0.6, 1 + ] ); + + var geometry = new THREE.BufferGeometry(); + geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) ); + geometry.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ) ); + + var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } ); + + THREE.Line.call( this, geometry, material, THREE.LinePieces ); + +}; + +THREE.AxisHelper.prototype = Object.create( THREE.Line.prototype ); +THREE.AxisHelper.prototype.constructor = THREE.AxisHelper; + +// File:src/extras/helpers/ArrowHelper.js + +/** + * @author WestLangley / http://github.com/WestLangley + * @author zz85 / http://github.com/zz85 + * @author bhouston / http://exocortex.com + * + * Creates an arrow for visualizing directions + * + * Parameters: + * dir - Vector3 + * origin - Vector3 + * length - Number + * color - color in hex value + * headLength - Number + * headWidth - Number + */ + +THREE.ArrowHelper = ( function () { + + var lineGeometry = new THREE.Geometry(); + lineGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 1, 0 ) ); + + var coneGeometry = new THREE.CylinderGeometry( 0, 0.5, 1, 5, 1 ); + coneGeometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, - 0.5, 0 ) ); + + return function ( dir, origin, length, color, headLength, headWidth ) { + + // dir is assumed to be normalized + + THREE.Object3D.call( this ); + + if ( color === undefined ) color = 0xffff00; + if ( length === undefined ) length = 1; + if ( headLength === undefined ) headLength = 0.2 * length; + if ( headWidth === undefined ) headWidth = 0.2 * headLength; + + this.position.copy( origin ); + + this.line = new THREE.Line( lineGeometry, new THREE.LineBasicMaterial( { color: color } ) ); + this.line.matrixAutoUpdate = false; + this.add( this.line ); + + this.cone = new THREE.Mesh( coneGeometry, new THREE.MeshBasicMaterial( { color: color } ) ); + this.cone.matrixAutoUpdate = false; + this.add( this.cone ); + + this.setDirection( dir ); + this.setLength( length, headLength, headWidth ); + + } + +}() ); + +THREE.ArrowHelper.prototype = Object.create( THREE.Object3D.prototype ); +THREE.ArrowHelper.prototype.constructor = THREE.ArrowHelper; + +THREE.ArrowHelper.prototype.setDirection = ( function () { + + var axis = new THREE.Vector3(); + var radians; + + return function ( dir ) { + + // dir is assumed to be normalized + + if ( dir.y > 0.99999 ) { + + this.quaternion.set( 0, 0, 0, 1 ); + + } else if ( dir.y < - 0.99999 ) { + + this.quaternion.set( 1, 0, 0, 0 ); + + } else { + + axis.set( dir.z, 0, - dir.x ).normalize(); + + radians = Math.acos( dir.y ); + + this.quaternion.setFromAxisAngle( axis, radians ); + + } + + }; + +}() ); + +THREE.ArrowHelper.prototype.setLength = function ( length, headLength, headWidth ) { + + if ( headLength === undefined ) headLength = 0.2 * length; + if ( headWidth === undefined ) headWidth = 0.2 * headLength; + + this.line.scale.set( 1, length - headLength, 1 ); + this.line.updateMatrix(); + + this.cone.scale.set( headWidth, headLength, headWidth ); + this.cone.position.y = length; + this.cone.updateMatrix(); + +}; + +THREE.ArrowHelper.prototype.setColor = function ( color ) { + + this.line.material.color.set( color ); + this.cone.material.color.set( color ); + +}; + +// File:src/extras/helpers/BoxHelper.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.BoxHelper = function ( object ) { + + var geometry = new THREE.BufferGeometry(); + geometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( 72 ), 3 ) ); + + THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: 0xffff00 } ), THREE.LinePieces ); + + if ( object !== undefined ) { + + this.update( object ); + + } + +}; + +THREE.BoxHelper.prototype = Object.create( THREE.Line.prototype ); +THREE.BoxHelper.prototype.constructor = THREE.BoxHelper; + +THREE.BoxHelper.prototype.update = function ( object ) { + + var geometry = object.geometry; + + if ( geometry.boundingBox === null ) { + + geometry.computeBoundingBox(); + + } + + var min = geometry.boundingBox.min; + var max = geometry.boundingBox.max; + + /* + 5____4 + 1/___0/| + | 6__|_7 + 2/___3/ + + 0: max.x, max.y, max.z + 1: min.x, max.y, max.z + 2: min.x, min.y, max.z + 3: max.x, min.y, max.z + 4: max.x, max.y, min.z + 5: min.x, max.y, min.z + 6: min.x, min.y, min.z + 7: max.x, min.y, min.z + */ + + var vertices = this.geometry.attributes.position.array; + + vertices[ 0 ] = max.x; vertices[ 1 ] = max.y; vertices[ 2 ] = max.z; + vertices[ 3 ] = min.x; vertices[ 4 ] = max.y; vertices[ 5 ] = max.z; + + vertices[ 6 ] = min.x; vertices[ 7 ] = max.y; vertices[ 8 ] = max.z; + vertices[ 9 ] = min.x; vertices[ 10 ] = min.y; vertices[ 11 ] = max.z; + + vertices[ 12 ] = min.x; vertices[ 13 ] = min.y; vertices[ 14 ] = max.z; + vertices[ 15 ] = max.x; vertices[ 16 ] = min.y; vertices[ 17 ] = max.z; + + vertices[ 18 ] = max.x; vertices[ 19 ] = min.y; vertices[ 20 ] = max.z; + vertices[ 21 ] = max.x; vertices[ 22 ] = max.y; vertices[ 23 ] = max.z; + + // + + vertices[ 24 ] = max.x; vertices[ 25 ] = max.y; vertices[ 26 ] = min.z; + vertices[ 27 ] = min.x; vertices[ 28 ] = max.y; vertices[ 29 ] = min.z; + + vertices[ 30 ] = min.x; vertices[ 31 ] = max.y; vertices[ 32 ] = min.z; + vertices[ 33 ] = min.x; vertices[ 34 ] = min.y; vertices[ 35 ] = min.z; + + vertices[ 36 ] = min.x; vertices[ 37 ] = min.y; vertices[ 38 ] = min.z; + vertices[ 39 ] = max.x; vertices[ 40 ] = min.y; vertices[ 41 ] = min.z; + + vertices[ 42 ] = max.x; vertices[ 43 ] = min.y; vertices[ 44 ] = min.z; + vertices[ 45 ] = max.x; vertices[ 46 ] = max.y; vertices[ 47 ] = min.z; + + // + + vertices[ 48 ] = max.x; vertices[ 49 ] = max.y; vertices[ 50 ] = max.z; + vertices[ 51 ] = max.x; vertices[ 52 ] = max.y; vertices[ 53 ] = min.z; + + vertices[ 54 ] = min.x; vertices[ 55 ] = max.y; vertices[ 56 ] = max.z; + vertices[ 57 ] = min.x; vertices[ 58 ] = max.y; vertices[ 59 ] = min.z; + + vertices[ 60 ] = min.x; vertices[ 61 ] = min.y; vertices[ 62 ] = max.z; + vertices[ 63 ] = min.x; vertices[ 64 ] = min.y; vertices[ 65 ] = min.z; + + vertices[ 66 ] = max.x; vertices[ 67 ] = min.y; vertices[ 68 ] = max.z; + vertices[ 69 ] = max.x; vertices[ 70 ] = min.y; vertices[ 71 ] = min.z; + + this.geometry.attributes.position.needsUpdate = true; + + this.geometry.computeBoundingSphere(); + + this.matrix = object.matrixWorld; + this.matrixAutoUpdate = false; + +}; + +// File:src/extras/helpers/BoundingBoxHelper.js + +/** + * @author WestLangley / http://github.com/WestLangley + */ + +// a helper to show the world-axis-aligned bounding box for an object + +THREE.BoundingBoxHelper = function ( object, hex ) { + + var color = ( hex !== undefined ) ? hex : 0x888888; + + this.object = object; + + this.box = new THREE.Box3(); + + THREE.Mesh.call( this, new THREE.BoxGeometry( 1, 1, 1 ), new THREE.MeshBasicMaterial( { color: color, wireframe: true } ) ); + +}; + +THREE.BoundingBoxHelper.prototype = Object.create( THREE.Mesh.prototype ); +THREE.BoundingBoxHelper.prototype.constructor = THREE.BoundingBoxHelper; + +THREE.BoundingBoxHelper.prototype.update = function () { + + this.box.setFromObject( this.object ); + + this.box.size( this.scale ); + + this.box.center( this.position ); + +}; + +// File:src/extras/helpers/CameraHelper.js + +/** + * @author alteredq / http://alteredqualia.com/ + * + * - shows frustum, line of sight and up of the camera + * - suitable for fast updates + * - based on frustum visualization in lightgl.js shadowmap example + * http://evanw.github.com/lightgl.js/tests/shadowmap.html + */ + +THREE.CameraHelper = function ( camera ) { + + var geometry = new THREE.Geometry(); + var material = new THREE.LineBasicMaterial( { color: 0xffffff, vertexColors: THREE.FaceColors } ); + + var pointMap = {}; + + // colors + + var hexFrustum = 0xffaa00; + var hexCone = 0xff0000; + var hexUp = 0x00aaff; + var hexTarget = 0xffffff; + var hexCross = 0x333333; + + // near + + addLine( "n1", "n2", hexFrustum ); + addLine( "n2", "n4", hexFrustum ); + addLine( "n4", "n3", hexFrustum ); + addLine( "n3", "n1", hexFrustum ); + + // far + + addLine( "f1", "f2", hexFrustum ); + addLine( "f2", "f4", hexFrustum ); + addLine( "f4", "f3", hexFrustum ); + addLine( "f3", "f1", hexFrustum ); + + // sides + + addLine( "n1", "f1", hexFrustum ); + addLine( "n2", "f2", hexFrustum ); + addLine( "n3", "f3", hexFrustum ); + addLine( "n4", "f4", hexFrustum ); + + // cone + + addLine( "p", "n1", hexCone ); + addLine( "p", "n2", hexCone ); + addLine( "p", "n3", hexCone ); + addLine( "p", "n4", hexCone ); + + // up + + addLine( "u1", "u2", hexUp ); + addLine( "u2", "u3", hexUp ); + addLine( "u3", "u1", hexUp ); + + // target + + addLine( "c", "t", hexTarget ); + addLine( "p", "c", hexCross ); + + // cross + + addLine( "cn1", "cn2", hexCross ); + addLine( "cn3", "cn4", hexCross ); + + addLine( "cf1", "cf2", hexCross ); + addLine( "cf3", "cf4", hexCross ); + + function addLine( a, b, hex ) { + + addPoint( a, hex ); + addPoint( b, hex ); + + } + + function addPoint( id, hex ) { + + geometry.vertices.push( new THREE.Vector3() ); + geometry.colors.push( new THREE.Color( hex ) ); + + if ( pointMap[ id ] === undefined ) { + + pointMap[ id ] = []; + + } + + pointMap[ id ].push( geometry.vertices.length - 1 ); + + } + + THREE.Line.call( this, geometry, material, THREE.LinePieces ); + + this.camera = camera; + this.matrix = camera.matrixWorld; + this.matrixAutoUpdate = false; + + this.pointMap = pointMap; + + this.update(); + +}; + +THREE.CameraHelper.prototype = Object.create( THREE.Line.prototype ); +THREE.CameraHelper.prototype.constructor = THREE.CameraHelper; + +THREE.CameraHelper.prototype.update = function () { + + var geometry, pointMap; + + var vector = new THREE.Vector3(); + var camera = new THREE.Camera(); + + var setPoint = function ( point, x, y, z ) { + + vector.set( x, y, z ).unproject( camera ); + + var points = pointMap[ point ]; + + if ( points !== undefined ) { + + for ( var i = 0, il = points.length; i < il; i ++ ) { + + geometry.vertices[ points[ i ] ].copy( vector ); + + } + + } + + }; + + return function () { + + geometry = this.geometry; + pointMap = this.pointMap; + + var w = 1, h = 1; + + // we need just camera projection matrix + // world matrix must be identity + + camera.projectionMatrix.copy( this.camera.projectionMatrix ); + + // center / target + + setPoint( "c", 0, 0, - 1 ); + setPoint( "t", 0, 0, 1 ); + + // near + + setPoint( "n1", - w, - h, - 1 ); + setPoint( "n2", w, - h, - 1 ); + setPoint( "n3", - w, h, - 1 ); + setPoint( "n4", w, h, - 1 ); + + // far + + setPoint( "f1", - w, - h, 1 ); + setPoint( "f2", w, - h, 1 ); + setPoint( "f3", - w, h, 1 ); + setPoint( "f4", w, h, 1 ); + + // up + + setPoint( "u1", w * 0.7, h * 1.1, - 1 ); + setPoint( "u2", - w * 0.7, h * 1.1, - 1 ); + setPoint( "u3", 0, h * 2, - 1 ); + + // cross + + setPoint( "cf1", - w, 0, 1 ); + setPoint( "cf2", w, 0, 1 ); + setPoint( "cf3", 0, - h, 1 ); + setPoint( "cf4", 0, h, 1 ); + + setPoint( "cn1", - w, 0, - 1 ); + setPoint( "cn2", w, 0, - 1 ); + setPoint( "cn3", 0, - h, - 1 ); + setPoint( "cn4", 0, h, - 1 ); + + geometry.verticesNeedUpdate = true; + + }; + +}(); + +// File:src/extras/helpers/DirectionalLightHelper.js + +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + * @author WestLangley / http://github.com/WestLangley + */ + +THREE.DirectionalLightHelper = function ( light, size ) { + + THREE.Object3D.call( this ); + + this.light = light; + this.light.updateMatrixWorld(); + + this.matrix = light.matrixWorld; + this.matrixAutoUpdate = false; + + size = size || 1; + + var geometry = new THREE.Geometry(); + geometry.vertices.push( + new THREE.Vector3( - size, size, 0 ), + new THREE.Vector3( size, size, 0 ), + new THREE.Vector3( size, - size, 0 ), + new THREE.Vector3( - size, - size, 0 ), + new THREE.Vector3( - size, size, 0 ) + ); + + var material = new THREE.LineBasicMaterial( { fog: false } ); + material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); + + this.lightPlane = new THREE.Line( geometry, material ); + this.add( this.lightPlane ); + + geometry = new THREE.Geometry(); + geometry.vertices.push( + new THREE.Vector3(), + new THREE.Vector3() + ); + + material = new THREE.LineBasicMaterial( { fog: false } ); + material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); + + this.targetLine = new THREE.Line( geometry, material ); + this.add( this.targetLine ); + + this.update(); + +}; + +THREE.DirectionalLightHelper.prototype = Object.create( THREE.Object3D.prototype ); +THREE.DirectionalLightHelper.prototype.constructor = THREE.DirectionalLightHelper; + +THREE.DirectionalLightHelper.prototype.dispose = function () { + + this.lightPlane.geometry.dispose(); + this.lightPlane.material.dispose(); + this.targetLine.geometry.dispose(); + this.targetLine.material.dispose(); +}; + +THREE.DirectionalLightHelper.prototype.update = function () { + + var v1 = new THREE.Vector3(); + var v2 = new THREE.Vector3(); + var v3 = new THREE.Vector3(); + + return function () { + + v1.setFromMatrixPosition( this.light.matrixWorld ); + v2.setFromMatrixPosition( this.light.target.matrixWorld ); + v3.subVectors( v2, v1 ); + + this.lightPlane.lookAt( v3 ); + this.lightPlane.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); + + this.targetLine.geometry.vertices[ 1 ].copy( v3 ); + this.targetLine.geometry.verticesNeedUpdate = true; + this.targetLine.material.color.copy( this.lightPlane.material.color ); + + }; + +}(); + +// File:src/extras/helpers/EdgesHelper.js + +/** + * @author WestLangley / http://github.com/WestLangley + */ + +THREE.EdgesHelper = function ( object, hex ) { + + var color = ( hex !== undefined ) ? hex : 0xffffff; + + var edge = [ 0, 0 ], hash = {}; + var sortFunction = function ( a, b ) { return a - b }; + + var keys = [ 'a', 'b', 'c' ]; + var geometry = new THREE.BufferGeometry(); + + var geometry2 = object.geometry.clone(); + + geometry2.mergeVertices(); + geometry2.computeFaceNormals(); + + var vertices = geometry2.vertices; + var faces = geometry2.faces; + var numEdges = 0; + + for ( var i = 0, l = faces.length; i < l; i ++ ) { + + var face = faces[ i ]; + + for ( var j = 0; j < 3; j ++ ) { + + edge[ 0 ] = face[ keys[ j ] ]; + edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ]; + edge.sort( sortFunction ); + + var key = edge.toString(); + + if ( hash[ key ] === undefined ) { + + hash[ key ] = { vert1: edge[ 0 ], vert2: edge[ 1 ], face1: i, face2: undefined }; + numEdges ++; + + } else { + + hash[ key ].face2 = i; + + } + + } + + } + + var coords = new Float32Array( numEdges * 2 * 3 ); + + var index = 0; + + for ( var key in hash ) { + + var h = hash[ key ]; + + if ( h.face2 === undefined || faces[ h.face1 ].normal.dot( faces[ h.face2 ].normal ) < 0.9999 ) { // hardwired const OK + + var vertex = vertices[ h.vert1 ]; + coords[ index ++ ] = vertex.x; + coords[ index ++ ] = vertex.y; + coords[ index ++ ] = vertex.z; + + vertex = vertices[ h.vert2 ]; + coords[ index ++ ] = vertex.x; + coords[ index ++ ] = vertex.y; + coords[ index ++ ] = vertex.z; + + } + + } + + geometry.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) ); + + THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color } ), THREE.LinePieces ); + + this.matrix = object.matrixWorld; + this.matrixAutoUpdate = false; + +}; + +THREE.EdgesHelper.prototype = Object.create( THREE.Line.prototype ); +THREE.EdgesHelper.prototype.constructor = THREE.EdgesHelper; + +// File:src/extras/helpers/FaceNormalsHelper.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author WestLangley / http://github.com/WestLangley +*/ + +THREE.FaceNormalsHelper = function ( object, size, hex, linewidth ) { + + this.object = object; + + this.size = ( size !== undefined ) ? size : 1; + + var color = ( hex !== undefined ) ? hex : 0xffff00; + + var width = ( linewidth !== undefined ) ? linewidth : 1; + + var geometry = new THREE.Geometry(); + + var faces = this.object.geometry.faces; + + for ( var i = 0, l = faces.length; i < l; i ++ ) { + + geometry.vertices.push( new THREE.Vector3(), new THREE.Vector3() ); + + } + + THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ), THREE.LinePieces ); + + this.matrixAutoUpdate = false; + + this.normalMatrix = new THREE.Matrix3(); + + this.update(); + +}; + +THREE.FaceNormalsHelper.prototype = Object.create( THREE.Line.prototype ); +THREE.FaceNormalsHelper.prototype.constructor = THREE.FaceNormalsHelper; + +THREE.FaceNormalsHelper.prototype.update = function () { + + var vertices = this.geometry.vertices; + + var object = this.object; + var objectVertices = object.geometry.vertices; + var objectFaces = object.geometry.faces; + var objectWorldMatrix = object.matrixWorld; + + object.updateMatrixWorld( true ); + + this.normalMatrix.getNormalMatrix( objectWorldMatrix ); + + for ( var i = 0, i2 = 0, l = objectFaces.length; i < l; i ++, i2 += 2 ) { + + var face = objectFaces[ i ]; + + vertices[ i2 ].copy( objectVertices[ face.a ] ) + .add( objectVertices[ face.b ] ) + .add( objectVertices[ face.c ] ) + .divideScalar( 3 ) + .applyMatrix4( objectWorldMatrix ); + + vertices[ i2 + 1 ].copy( face.normal ) + .applyMatrix3( this.normalMatrix ) + .normalize() + .multiplyScalar( this.size ) + .add( vertices[ i2 ] ); + + } + + this.geometry.verticesNeedUpdate = true; + + return this; + +}; + + +// File:src/extras/helpers/GridHelper.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.GridHelper = function ( size, step ) { + + var geometry = new THREE.Geometry(); + var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } ); + + this.color1 = new THREE.Color( 0x444444 ); + this.color2 = new THREE.Color( 0x888888 ); + + for ( var i = - size; i <= size; i += step ) { + + geometry.vertices.push( + new THREE.Vector3( - size, 0, i ), new THREE.Vector3( size, 0, i ), + new THREE.Vector3( i, 0, - size ), new THREE.Vector3( i, 0, size ) + ); + + var color = i === 0 ? this.color1 : this.color2; + + geometry.colors.push( color, color, color, color ); + + } + + THREE.Line.call( this, geometry, material, THREE.LinePieces ); + +}; + +THREE.GridHelper.prototype = Object.create( THREE.Line.prototype ); +THREE.GridHelper.prototype.constructor = THREE.GridHelper; + +THREE.GridHelper.prototype.setColors = function( colorCenterLine, colorGrid ) { + + this.color1.set( colorCenterLine ); + this.color2.set( colorGrid ); + + this.geometry.colorsNeedUpdate = true; + +} + +// File:src/extras/helpers/HemisphereLightHelper.js + +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.HemisphereLightHelper = function ( light, sphereSize, arrowLength, domeSize ) { + + THREE.Object3D.call( this ); + + this.light = light; + this.light.updateMatrixWorld(); + + this.matrix = light.matrixWorld; + this.matrixAutoUpdate = false; + + this.colors = [ new THREE.Color(), new THREE.Color() ]; + + var geometry = new THREE.SphereGeometry( sphereSize, 4, 2 ); + geometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) ); + + for ( var i = 0, il = 8; i < il; i ++ ) { + + geometry.faces[ i ].color = this.colors[ i < 4 ? 0 : 1 ]; + + } + + var material = new THREE.MeshBasicMaterial( { vertexColors: THREE.FaceColors, wireframe: true } ); + + this.lightSphere = new THREE.Mesh( geometry, material ); + this.add( this.lightSphere ); + + this.update(); + +}; + +THREE.HemisphereLightHelper.prototype = Object.create( THREE.Object3D.prototype ); +THREE.HemisphereLightHelper.prototype.constructor = THREE.HemisphereLightHelper; + +THREE.HemisphereLightHelper.prototype.dispose = function () { + this.lightSphere.geometry.dispose(); + this.lightSphere.material.dispose(); +}; + +THREE.HemisphereLightHelper.prototype.update = function () { + + var vector = new THREE.Vector3(); + + return function () { + + this.colors[ 0 ].copy( this.light.color ).multiplyScalar( this.light.intensity ); + this.colors[ 1 ].copy( this.light.groundColor ).multiplyScalar( this.light.intensity ); + + this.lightSphere.lookAt( vector.setFromMatrixPosition( this.light.matrixWorld ).negate() ); + this.lightSphere.geometry.colorsNeedUpdate = true; + + } + +}(); + +// File:src/extras/helpers/PointLightHelper.js + +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.PointLightHelper = function ( light, sphereSize ) { + + this.light = light; + this.light.updateMatrixWorld(); + + var geometry = new THREE.SphereGeometry( sphereSize, 4, 2 ); + var material = new THREE.MeshBasicMaterial( { wireframe: true, fog: false } ); + material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); + + THREE.Mesh.call( this, geometry, material ); + + this.matrix = this.light.matrixWorld; + this.matrixAutoUpdate = false; + + /* + var distanceGeometry = new THREE.IcosahedronGeometry( 1, 2 ); + var distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } ); + + this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial ); + this.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial ); + + var d = light.distance; + + if ( d === 0.0 ) { + + this.lightDistance.visible = false; + + } else { + + this.lightDistance.scale.set( d, d, d ); + + } + + this.add( this.lightDistance ); + */ + +}; + +THREE.PointLightHelper.prototype = Object.create( THREE.Mesh.prototype ); +THREE.PointLightHelper.prototype.constructor = THREE.PointLightHelper; + +THREE.PointLightHelper.prototype.dispose = function () { + + this.geometry.dispose(); + this.material.dispose(); +}; + +THREE.PointLightHelper.prototype.update = function () { + + this.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); + + /* + var d = this.light.distance; + + if ( d === 0.0 ) { + + this.lightDistance.visible = false; + + } else { + + this.lightDistance.visible = true; + this.lightDistance.scale.set( d, d, d ); + + } + */ + +}; + +// File:src/extras/helpers/SkeletonHelper.js + +/** + * @author Sean Griffin / http://twitter.com/sgrif + * @author Michael Guerrero / http://realitymeltdown.com + * @author mrdoob / http://mrdoob.com/ + * @author ikerr / http://verold.com + */ + +THREE.SkeletonHelper = function ( object ) { + + this.bones = this.getBoneList( object ); + + var geometry = new THREE.Geometry(); + + for ( var i = 0; i < this.bones.length; i ++ ) { + + var bone = this.bones[ i ]; + + if ( bone.parent instanceof THREE.Bone ) { + + geometry.vertices.push( new THREE.Vector3() ); + geometry.vertices.push( new THREE.Vector3() ); + geometry.colors.push( new THREE.Color( 0, 0, 1 ) ); + geometry.colors.push( new THREE.Color( 0, 1, 0 ) ); + + } + + } + + var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors, depthTest: false, depthWrite: false, transparent: true } ); + + THREE.Line.call( this, geometry, material, THREE.LinePieces ); + + this.root = object; + + this.matrix = object.matrixWorld; + this.matrixAutoUpdate = false; + + this.update(); + +}; + + +THREE.SkeletonHelper.prototype = Object.create( THREE.Line.prototype ); +THREE.SkeletonHelper.prototype.constructor = THREE.SkeletonHelper; + +THREE.SkeletonHelper.prototype.getBoneList = function( object ) { + + var boneList = []; + + if ( object instanceof THREE.Bone ) { + + boneList.push( object ); + + } + + for ( var i = 0; i < object.children.length; i ++ ) { + + boneList.push.apply( boneList, this.getBoneList( object.children[ i ] ) ); + + } + + return boneList; + +}; + +THREE.SkeletonHelper.prototype.update = function () { + + var geometry = this.geometry; + + var matrixWorldInv = new THREE.Matrix4().getInverse( this.root.matrixWorld ); + + var boneMatrix = new THREE.Matrix4(); + + var j = 0; + + for ( var i = 0; i < this.bones.length; i ++ ) { + + var bone = this.bones[ i ]; + + if ( bone.parent instanceof THREE.Bone ) { + + boneMatrix.multiplyMatrices( matrixWorldInv, bone.matrixWorld ); + geometry.vertices[ j ].setFromMatrixPosition( boneMatrix ); + + boneMatrix.multiplyMatrices( matrixWorldInv, bone.parent.matrixWorld ); + geometry.vertices[ j + 1 ].setFromMatrixPosition( boneMatrix ); + + j += 2; + + } + + } + + geometry.verticesNeedUpdate = true; + + geometry.computeBoundingSphere(); + +}; + +// File:src/extras/helpers/SpotLightHelper.js + +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + * @author WestLangley / http://github.com/WestLangley +*/ + +THREE.SpotLightHelper = function ( light ) { + + THREE.Object3D.call( this ); + + this.light = light; + this.light.updateMatrixWorld(); + + this.matrix = light.matrixWorld; + this.matrixAutoUpdate = false; + + var geometry = new THREE.CylinderGeometry( 0, 1, 1, 8, 1, true ); + + geometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, - 0.5, 0 ) ); + geometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) ); + + var material = new THREE.MeshBasicMaterial( { wireframe: true, fog: false } ); + + this.cone = new THREE.Mesh( geometry, material ); + this.add( this.cone ); + + this.update(); + +}; + +THREE.SpotLightHelper.prototype = Object.create( THREE.Object3D.prototype ); +THREE.SpotLightHelper.prototype.constructor = THREE.SpotLightHelper; + +THREE.SpotLightHelper.prototype.dispose = function () { + this.cone.geometry.dispose(); + this.cone.material.dispose(); +}; + +THREE.SpotLightHelper.prototype.update = function () { + + var vector = new THREE.Vector3(); + var vector2 = new THREE.Vector3(); + + return function () { + + var coneLength = this.light.distance ? this.light.distance : 10000; + var coneWidth = coneLength * Math.tan( this.light.angle ); + + this.cone.scale.set( coneWidth, coneWidth, coneLength ); + + vector.setFromMatrixPosition( this.light.matrixWorld ); + vector2.setFromMatrixPosition( this.light.target.matrixWorld ); + + this.cone.lookAt( vector2.sub( vector ) ); + + this.cone.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); + + }; + +}(); + +// File:src/extras/helpers/VertexNormalsHelper.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author WestLangley / http://github.com/WestLangley +*/ + +THREE.VertexNormalsHelper = function ( object, size, hex, linewidth ) { + + this.object = object; + + this.size = ( size !== undefined ) ? size : 1; + + var color = ( hex !== undefined ) ? hex : 0xff0000; + + var width = ( linewidth !== undefined ) ? linewidth : 1; + + var geometry = new THREE.Geometry(); + + var vertices = object.geometry.vertices; + + var faces = object.geometry.faces; + + for ( var i = 0, l = faces.length; i < l; i ++ ) { + + var face = faces[ i ]; + + for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { + + geometry.vertices.push( new THREE.Vector3(), new THREE.Vector3() ); + + } + + } + + THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ), THREE.LinePieces ); + + this.matrixAutoUpdate = false; + + this.normalMatrix = new THREE.Matrix3(); + + this.update(); + +}; + +THREE.VertexNormalsHelper.prototype = Object.create( THREE.Line.prototype ); +THREE.VertexNormalsHelper.prototype.constructor = THREE.VertexNormalsHelper; + +THREE.VertexNormalsHelper.prototype.update = ( function ( object ) { + + var v1 = new THREE.Vector3(); + + return function( object ) { + + var keys = [ 'a', 'b', 'c', 'd' ]; + + this.object.updateMatrixWorld( true ); + + this.normalMatrix.getNormalMatrix( this.object.matrixWorld ); + + var vertices = this.geometry.vertices; + + var verts = this.object.geometry.vertices; + + var faces = this.object.geometry.faces; + + var worldMatrix = this.object.matrixWorld; + + var idx = 0; + + for ( var i = 0, l = faces.length; i < l; i ++ ) { + + var face = faces[ i ]; + + for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { + + var vertexId = face[ keys[ j ] ]; + var vertex = verts[ vertexId ]; + + var normal = face.vertexNormals[ j ]; + + vertices[ idx ].copy( vertex ).applyMatrix4( worldMatrix ); + + v1.copy( normal ).applyMatrix3( this.normalMatrix ).normalize().multiplyScalar( this.size ); + + v1.add( vertices[ idx ] ); + idx = idx + 1; + + vertices[ idx ].copy( v1 ); + idx = idx + 1; + + } + + } + + this.geometry.verticesNeedUpdate = true; + + return this; + + } + +}()); + +// File:src/extras/helpers/VertexTangentsHelper.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author WestLangley / http://github.com/WestLangley +*/ + +THREE.VertexTangentsHelper = function ( object, size, hex, linewidth ) { + + this.object = object; + + this.size = ( size !== undefined ) ? size : 1; + + var color = ( hex !== undefined ) ? hex : 0x0000ff; + + var width = ( linewidth !== undefined ) ? linewidth : 1; + + var geometry = new THREE.Geometry(); + + var vertices = object.geometry.vertices; + + var faces = object.geometry.faces; + + for ( var i = 0, l = faces.length; i < l; i ++ ) { + + var face = faces[ i ]; + + for ( var j = 0, jl = face.vertexTangents.length; j < jl; j ++ ) { + + geometry.vertices.push( new THREE.Vector3() ); + geometry.vertices.push( new THREE.Vector3() ); + + } + + } + + THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ), THREE.LinePieces ); + + this.matrixAutoUpdate = false; + + this.update(); + +}; + +THREE.VertexTangentsHelper.prototype = Object.create( THREE.Line.prototype ); +THREE.VertexTangentsHelper.prototype.constructor = THREE.VertexTangentsHelper; + +THREE.VertexTangentsHelper.prototype.update = ( function ( object ) { + + var v1 = new THREE.Vector3(); + + return function( object ) { + + var keys = [ 'a', 'b', 'c', 'd' ]; + + this.object.updateMatrixWorld( true ); + + var vertices = this.geometry.vertices; + + var verts = this.object.geometry.vertices; + + var faces = this.object.geometry.faces; + + var worldMatrix = this.object.matrixWorld; + + var idx = 0; + + for ( var i = 0, l = faces.length; i < l; i ++ ) { + + var face = faces[ i ]; + + for ( var j = 0, jl = face.vertexTangents.length; j < jl; j ++ ) { + + var vertexId = face[ keys[ j ] ]; + var vertex = verts[ vertexId ]; + + var tangent = face.vertexTangents[ j ]; + + vertices[ idx ].copy( vertex ).applyMatrix4( worldMatrix ); + + v1.copy( tangent ).transformDirection( worldMatrix ).multiplyScalar( this.size ); + + v1.add( vertices[ idx ] ); + idx = idx + 1; + + vertices[ idx ].copy( v1 ); + idx = idx + 1; + + } + + } + + this.geometry.verticesNeedUpdate = true; + + return this; + + } + +}()); + +// File:src/extras/helpers/WireframeHelper.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.WireframeHelper = function ( object, hex ) { + + var color = ( hex !== undefined ) ? hex : 0xffffff; + + var edge = [ 0, 0 ], hash = {}; + var sortFunction = function ( a, b ) { return a - b }; + + var keys = [ 'a', 'b', 'c' ]; + var geometry = new THREE.BufferGeometry(); + + if ( object.geometry instanceof THREE.Geometry ) { + + var vertices = object.geometry.vertices; + var faces = object.geometry.faces; + var numEdges = 0; + + // allocate maximal size + var edges = new Uint32Array( 6 * faces.length ); + + for ( var i = 0, l = faces.length; i < l; i ++ ) { + + var face = faces[ i ]; + + for ( var j = 0; j < 3; j ++ ) { + + edge[ 0 ] = face[ keys[ j ] ]; + edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ]; + edge.sort( sortFunction ); + + var key = edge.toString(); + + if ( hash[ key ] === undefined ) { + + edges[ 2 * numEdges ] = edge[ 0 ]; + edges[ 2 * numEdges + 1 ] = edge[ 1 ]; + hash[ key ] = true; + numEdges ++; + + } + + } + + } + + var coords = new Float32Array( numEdges * 2 * 3 ); + + for ( var i = 0, l = numEdges; i < l; i ++ ) { + + for ( var j = 0; j < 2; j ++ ) { + + var vertex = vertices[ edges [ 2 * i + j] ]; + + var index = 6 * i + 3 * j; + coords[ index + 0 ] = vertex.x; + coords[ index + 1 ] = vertex.y; + coords[ index + 2 ] = vertex.z; + + } + + } + + geometry.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) ); + + } else if ( object.geometry instanceof THREE.BufferGeometry ) { + + if ( object.geometry.attributes.index !== undefined ) { // Indexed BufferGeometry + + var vertices = object.geometry.attributes.position.array; + var indices = object.geometry.attributes.index.array; + var drawcalls = object.geometry.drawcalls; + var numEdges = 0; + + if ( drawcalls.length === 0 ) { + + drawcalls = [ { count : indices.length, index : 0, start : 0 } ]; + + } + + // allocate maximal size + var edges = new Uint32Array( 2 * indices.length ); + + for ( var o = 0, ol = drawcalls.length; o < ol; ++ o ) { + + var start = drawcalls[ o ].start; + var count = drawcalls[ o ].count; + var index = drawcalls[ o ].index; + + for ( var i = start, il = start + count; i < il; i += 3 ) { + + for ( var j = 0; j < 3; j ++ ) { + + edge[ 0 ] = index + indices[ i + j ]; + edge[ 1 ] = index + indices[ i + ( j + 1 ) % 3 ]; + edge.sort( sortFunction ); + + var key = edge.toString(); + + if ( hash[ key ] === undefined ) { + + edges[ 2 * numEdges ] = edge[ 0 ]; + edges[ 2 * numEdges + 1 ] = edge[ 1 ]; + hash[ key ] = true; + numEdges ++; + + } + + } + + } + + } + + var coords = new Float32Array( numEdges * 2 * 3 ); + + for ( var i = 0, l = numEdges; i < l; i ++ ) { + + for ( var j = 0; j < 2; j ++ ) { + + var index = 6 * i + 3 * j; + var index2 = 3 * edges[ 2 * i + j]; + coords[ index + 0 ] = vertices[ index2 ]; + coords[ index + 1 ] = vertices[ index2 + 1 ]; + coords[ index + 2 ] = vertices[ index2 + 2 ]; + + } + + } + + geometry.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) ); + + } else { // non-indexed BufferGeometry + + var vertices = object.geometry.attributes.position.array; + var numEdges = vertices.length / 3; + var numTris = numEdges / 3; + + var coords = new Float32Array( numEdges * 2 * 3 ); + + for ( var i = 0, l = numTris; i < l; i ++ ) { + + for ( var j = 0; j < 3; j ++ ) { + + var index = 18 * i + 6 * j; + + var index1 = 9 * i + 3 * j; + coords[ index + 0 ] = vertices[ index1 ]; + coords[ index + 1 ] = vertices[ index1 + 1 ]; + coords[ index + 2 ] = vertices[ index1 + 2 ]; + + var index2 = 9 * i + 3 * ( ( j + 1 ) % 3 ); + coords[ index + 3 ] = vertices[ index2 ]; + coords[ index + 4 ] = vertices[ index2 + 1 ]; + coords[ index + 5 ] = vertices[ index2 + 2 ]; + + } + + } + + geometry.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) ); + + } + + } + + THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color } ), THREE.LinePieces ); + + this.matrix = object.matrixWorld; + this.matrixAutoUpdate = false; + +}; + +THREE.WireframeHelper.prototype = Object.create( THREE.Line.prototype ); +THREE.WireframeHelper.prototype.constructor = THREE.WireframeHelper; + +// File:src/extras/objects/ImmediateRenderObject.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.ImmediateRenderObject = function () { + + THREE.Object3D.call( this ); + + this.render = function ( renderCallback ) {}; + +}; + +THREE.ImmediateRenderObject.prototype = Object.create( THREE.Object3D.prototype ); +THREE.ImmediateRenderObject.prototype.constructor = THREE.ImmediateRenderObject; + +// File:src/extras/objects/MorphBlendMesh.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.MorphBlendMesh = function( geometry, material ) { + + THREE.Mesh.call( this, geometry, material ); + + this.animationsMap = {}; + this.animationsList = []; + + // prepare default animation + // (all frames played together in 1 second) + + var numFrames = this.geometry.morphTargets.length; + + var name = "__default"; + + var startFrame = 0; + var endFrame = numFrames - 1; + + var fps = numFrames / 1; + + this.createAnimation( name, startFrame, endFrame, fps ); + this.setAnimationWeight( name, 1 ); + +}; + +THREE.MorphBlendMesh.prototype = Object.create( THREE.Mesh.prototype ); +THREE.MorphBlendMesh.prototype.constructor = THREE.MorphBlendMesh; + +THREE.MorphBlendMesh.prototype.createAnimation = function ( name, start, end, fps ) { + + var animation = { + + startFrame: start, + endFrame: end, + + length: end - start + 1, + + fps: fps, + duration: ( end - start ) / fps, + + lastFrame: 0, + currentFrame: 0, + + active: false, + + time: 0, + direction: 1, + weight: 1, + + directionBackwards: false, + mirroredLoop: false + + }; + + this.animationsMap[ name ] = animation; + this.animationsList.push( animation ); + +}; + +THREE.MorphBlendMesh.prototype.autoCreateAnimations = function ( fps ) { + + var pattern = /([a-z]+)_?(\d+)/; + + var firstAnimation, frameRanges = {}; + + var geometry = this.geometry; + + for ( var i = 0, il = geometry.morphTargets.length; i < il; i ++ ) { + + var morph = geometry.morphTargets[ i ]; + var chunks = morph.name.match( pattern ); + + if ( chunks && chunks.length > 1 ) { + + var name = chunks[ 1 ]; + var num = chunks[ 2 ]; + + if ( ! frameRanges[ name ] ) frameRanges[ name ] = { start: Infinity, end: - Infinity }; + + var range = frameRanges[ name ]; + + if ( i < range.start ) range.start = i; + if ( i > range.end ) range.end = i; + + if ( ! firstAnimation ) firstAnimation = name; + + } + + } + + for ( var name in frameRanges ) { + + var range = frameRanges[ name ]; + this.createAnimation( name, range.start, range.end, fps ); + + } + + this.firstAnimation = firstAnimation; + +}; + +THREE.MorphBlendMesh.prototype.setAnimationDirectionForward = function ( name ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.direction = 1; + animation.directionBackwards = false; + + } + +}; + +THREE.MorphBlendMesh.prototype.setAnimationDirectionBackward = function ( name ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.direction = - 1; + animation.directionBackwards = true; + + } + +}; + +THREE.MorphBlendMesh.prototype.setAnimationFPS = function ( name, fps ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.fps = fps; + animation.duration = ( animation.end - animation.start ) / animation.fps; + + } + +}; + +THREE.MorphBlendMesh.prototype.setAnimationDuration = function ( name, duration ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.duration = duration; + animation.fps = ( animation.end - animation.start ) / animation.duration; + + } + +}; + +THREE.MorphBlendMesh.prototype.setAnimationWeight = function ( name, weight ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.weight = weight; + + } + +}; + +THREE.MorphBlendMesh.prototype.setAnimationTime = function ( name, time ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.time = time; + + } + +}; + +THREE.MorphBlendMesh.prototype.getAnimationTime = function ( name ) { + + var time = 0; + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + time = animation.time; + + } + + return time; + +}; + +THREE.MorphBlendMesh.prototype.getAnimationDuration = function ( name ) { + + var duration = - 1; + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + duration = animation.duration; + + } + + return duration; + +}; + +THREE.MorphBlendMesh.prototype.playAnimation = function ( name ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.time = 0; + animation.active = true; + + } else { + + console.warn( "animation[" + name + "] undefined" ); + + } + +}; + +THREE.MorphBlendMesh.prototype.stopAnimation = function ( name ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.active = false; + + } + +}; + +THREE.MorphBlendMesh.prototype.update = function ( delta ) { + + for ( var i = 0, il = this.animationsList.length; i < il; i ++ ) { + + var animation = this.animationsList[ i ]; + + if ( ! animation.active ) continue; + + var frameTime = animation.duration / animation.length; + + animation.time += animation.direction * delta; + + if ( animation.mirroredLoop ) { + + if ( animation.time > animation.duration || animation.time < 0 ) { + + animation.direction *= - 1; + + if ( animation.time > animation.duration ) { + + animation.time = animation.duration; + animation.directionBackwards = true; + + } + + if ( animation.time < 0 ) { + + animation.time = 0; + animation.directionBackwards = false; + + } + + } + + } else { + + animation.time = animation.time % animation.duration; + + if ( animation.time < 0 ) animation.time += animation.duration; + + } + + var keyframe = animation.startFrame + THREE.Math.clamp( Math.floor( animation.time / frameTime ), 0, animation.length - 1 ); + var weight = animation.weight; + + if ( keyframe !== animation.currentFrame ) { + + this.morphTargetInfluences[ animation.lastFrame ] = 0; + this.morphTargetInfluences[ animation.currentFrame ] = 1 * weight; + + this.morphTargetInfluences[ keyframe ] = 0; + + animation.lastFrame = animation.currentFrame; + animation.currentFrame = keyframe; + + } + + var mix = ( animation.time % frameTime ) / frameTime; + + if ( animation.directionBackwards ) mix = 1 - mix; + + this.morphTargetInfluences[ animation.currentFrame ] = mix * weight; + this.morphTargetInfluences[ animation.lastFrame ] = ( 1 - mix ) * weight; + + } + +}; + diff --git a/dependencies/main_files/threeModel.js b/dependencies/main_files/threeModel.js new file mode 100644 index 00000000..ae00b876 --- /dev/null +++ b/dependencies/main_files/threeModel.js @@ -0,0 +1,97 @@ +/** + * Created by aghassaei on 1/17/15. + */ + + +function ThreeModel(){ + + var camera = new THREE.PerspectiveCamera(60, window.innerWidth/window.innerHeight, 1, 1000); + var scene = new THREE.Scene(); + var renderer = new THREE.WebGLRenderer({antialias:false}); + var objects = []; + + initialize(); + + function initialize(){ + + camera.position.x = 125; + camera.position.y = 100; + camera.position.z = 165; + camera.up.set(0,0,1);//set z axis as "up" + + scene.fog = new THREE.FogExp2(0xcccccc, 0.002); + + // lights + var light = new THREE.DirectionalLight(0xffffff); + light.position.set(1, 1, 1); + scene.add(light); + light = new THREE.DirectionalLight(0x002288); + light.position.set(-1, -1, -1); + scene.add(light); + light = new THREE.AmbientLight(0x222222); + scene.add(light); + + // renderer + renderer.setClearColor(scene.fog.color, 1); + renderer.setSize(window.innerWidth, window.innerHeight); + + window.addEventListener('resize', onWindowResize, false); + } + +// function drawRandomStuff(){ +// var geometry = new THREE.CylinderGeometry(0, 10, 30, 4, 1); +// var material = new THREE.MeshLambertMaterial({color:0xffffff, shading: THREE.FlatShading}); +// +// for ( var i = 0; i < 500; i ++ ) { +// var mesh = new THREE.Mesh( geometry, material ); +// mesh.position.x = ( Math.random() - 0.5 ) * 1000; +// mesh.position.y = ( Math.random() - 0.5 ) * 1000; +// mesh.position.z = ( Math.random() - 0.5 ) * 1000; +// mesh.updateMatrix(); +// mesh.matrixAutoUpdate = false; +// scene.add(mesh); +// } +// } + + function onWindowResize(){ + camera.aspect = window.innerWidth/window.innerHeight; + camera.updateProjectionMatrix(); + renderer.setSize(window.innerWidth, window.innerHeight); + render(); + } + + function sceneAdd(object){ + scene.add(object); + objects.push(object); + } + + function sceneRemove(object){ + scene.remove(object); + objects.remove(object); + } + + function render(){ + renderer.render(scene, camera); + } + + function clearAll(){ + var children = scene.children; + for (var i=children.length;i>=0;i--){ + var object = children[i]; + if (object instanceof THREE.Mesh){// && object != this.fillGeometry.get("mesh") + scene.remove(object); + } + } + render(); + } + + return {//return public properties + render:render, + clearAll: clearAll, + sceneRemove:sceneRemove, + sceneAdd:sceneAdd, + domElement:renderer.domElement, + camera:camera, + objects:objects + } +} \ No newline at end of file diff --git a/dependencies/main_files/threeView.js b/dependencies/main_files/threeView.js new file mode 100644 index 00000000..834ed7ed --- /dev/null +++ b/dependencies/main_files/threeView.js @@ -0,0 +1,92 @@ +/** + * Created by aghassaei on 1/16/15. + */ + +ThreeView = Backbone.View.extend({ + + events: { + "mousemove": "mouseMoved", + "mousedown": "mouseDown", + "mouseup": "mouseUp" + }, + + mouseIsDown: false,//store state of mouse click + mouseProjection: new THREE.Raycaster(), + projectionTargets: null, + highlightTargets: null, + meshHandle: null, + cubeGeometry: new THREE.BoxGeometry(5,5,5), + cubeMaterial: new THREE.MeshLambertMaterial({color:0x0000ff, shading:THREE.FlatShading, vertexColors:THREE.FaceColors}), + + el: "#threeContainer", + + controls: null, + + initialize: function(options){ + + this.highlightTargets = options.highlightTargets; + this.projectionTargets = []; + var self = this; + _.each(this.highlightTargets, function(target){ + if (target.boundsBox) self.projectionTargets.push(target.boundsBox); + }); + _.bindAll(this, "animate", "mouseMoved"); + + this.controls = new THREE.OrbitControls(this.model.camera, this.$el.get(0)); + this.controls.addEventListener('change', this.model.render); + + this.meshHandle = new MeshHandle(this.model); + + this.$el.append(this.model.domElement); + + this.animate(); + }, + + mouseUp: function(){ + this.mouseIsDown = false; + }, + + mouseDown: function(e){ + this.mouseIsDown = true; +// +// var vector = new THREE.Vector2(2*(e.pageX-this.$el.offset().left)/this.$el.width()-1, 1-2*(e.pageY-this.$el.offset().top)/this.$el.height()); +// var camera = this.model.camera; +// this.mouseProjection.setFromCamera(vector, camera); +// var intersections = this.mouseProjection.intersectObjects(this.model.objects); +// +// console.log(intersections); +// +// if (intersections.length>1){ +// var voxel = new THREE.Mesh(this.cubeGeometry); +// voxel.position.copy(intersections[1].point); +// if (intersections[1].face) voxel.position.add(intersections[1].face.normal); +// voxel.position.divideScalar(5).floor().multiplyScalar(5).addScalar(2.5); +// this.model.sceneAdd(voxel); +// this.model.render(); +// } + }, + + mouseMoved: function(e){ +// if (this.mouseIsDown) return;//in the middle of a drag event +// var vector = new THREE.Vector2(2*(e.pageX-this.$el.offset().left)/this.$el.width()-1, 1-2*(e.pageY-this.$el.offset().top)/this.$el.height()); +// var camera = this.model.camera; +// this.mouseProjection.setFromCamera(vector, camera); +// var intersections = this.mouseProjection.intersectObjects(this.model.objects); +// +// +// _.each(this.highlightTargets, function(target){ +// target.checkHighlight(intersections); +// }); + }, + +// getTopObject: function(targets, intersections, index){ +// if (intersections.length<index) return null; +// if (intersections.) +// this.getTopObject(targets, intersections, index+1); +// } + + animate: function(){ + requestAnimationFrame(this.animate); + this.controls.update(); + } +}); \ No newline at end of file diff --git a/dependencies/main_files/underscore.js b/dependencies/main_files/underscore.js new file mode 100644 index 00000000..afd92e5b --- /dev/null +++ b/dependencies/main_files/underscore.js @@ -0,0 +1,1416 @@ +// Underscore.js 1.7.0 +// http://underscorejs.org +// (c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors +// Underscore may be freely distributed under the MIT license. + +(function() { + + // Baseline setup + // -------------- + + // Establish the root object, `window` in the browser, or `exports` on the server. + var root = this; + + // Save the previous value of the `_` variable. + var previousUnderscore = root._; + + // Save bytes in the minified (but not gzipped) version: + var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; + + // Create quick reference variables for speed access to core prototypes. + var + push = ArrayProto.push, + slice = ArrayProto.slice, + concat = ArrayProto.concat, + toString = ObjProto.toString, + hasOwnProperty = ObjProto.hasOwnProperty; + + // All **ECMAScript 5** native function implementations that we hope to use + // are declared here. + var + nativeIsArray = Array.isArray, + nativeKeys = Object.keys, + nativeBind = FuncProto.bind; + + // Create a safe reference to the Underscore object for use below. + var _ = function(obj) { + if (obj instanceof _) return obj; + if (!(this instanceof _)) return new _(obj); + this._wrapped = obj; + }; + + // Export the Underscore object for **Node.js**, with + // backwards-compatibility for the old `require()` API. If we're in + // the browser, add `_` as a global object. + if (typeof exports !== 'undefined') { + if (typeof module !== 'undefined' && module.exports) { + exports = module.exports = _; + } + exports._ = _; + } else { + root._ = _; + } + + // Current version. + _.VERSION = '1.7.0'; + + // Internal function that returns an efficient (for current engines) version + // of the passed-in callback, to be repeatedly applied in other Underscore + // functions. + var createCallback = function(func, context, argCount) { + if (context === void 0) return func; + switch (argCount == null ? 3 : argCount) { + case 1: return function(value) { + return func.call(context, value); + }; + case 2: return function(value, other) { + return func.call(context, value, other); + }; + case 3: return function(value, index, collection) { + return func.call(context, value, index, collection); + }; + case 4: return function(accumulator, value, index, collection) { + return func.call(context, accumulator, value, index, collection); + }; + } + return function() { + return func.apply(context, arguments); + }; + }; + + // A mostly-internal function to generate callbacks that can be applied + // to each element in a collection, returning the desired result — either + // identity, an arbitrary callback, a property matcher, or a property accessor. + _.iteratee = function(value, context, argCount) { + if (value == null) return _.identity; + if (_.isFunction(value)) return createCallback(value, context, argCount); + if (_.isObject(value)) return _.matches(value); + return _.property(value); + }; + + // Collection Functions + // -------------------- + + // The cornerstone, an `each` implementation, aka `forEach`. + // Handles raw objects in addition to array-likes. Treats all + // sparse array-likes as if they were dense. + _.each = _.forEach = function(obj, iteratee, context) { + if (obj == null) return obj; + iteratee = createCallback(iteratee, context); + var i, length = obj.length; + if (length === +length) { + for (i = 0; i < length; i++) { + iteratee(obj[i], i, obj); + } + } else { + var keys = _.keys(obj); + for (i = 0, length = keys.length; i < length; i++) { + iteratee(obj[keys[i]], keys[i], obj); + } + } + return obj; + }; + + // Return the results of applying the iteratee to each element. + _.map = _.collect = function(obj, iteratee, context) { + if (obj == null) return []; + iteratee = _.iteratee(iteratee, context); + var keys = obj.length !== +obj.length && _.keys(obj), + length = (keys || obj).length, + results = Array(length), + currentKey; + for (var index = 0; index < length; index++) { + currentKey = keys ? keys[index] : index; + results[index] = iteratee(obj[currentKey], currentKey, obj); + } + return results; + }; + + var reduceError = 'Reduce of empty array with no initial value'; + + // **Reduce** builds up a single result from a list of values, aka `inject`, + // or `foldl`. + _.reduce = _.foldl = _.inject = function(obj, iteratee, memo, context) { + if (obj == null) obj = []; + iteratee = createCallback(iteratee, context, 4); + var keys = obj.length !== +obj.length && _.keys(obj), + length = (keys || obj).length, + index = 0, currentKey; + if (arguments.length < 3) { + if (!length) throw new TypeError(reduceError); + memo = obj[keys ? keys[index++] : index++]; + } + for (; index < length; index++) { + currentKey = keys ? keys[index] : index; + memo = iteratee(memo, obj[currentKey], currentKey, obj); + } + return memo; + }; + + // The right-associative version of reduce, also known as `foldr`. + _.reduceRight = _.foldr = function(obj, iteratee, memo, context) { + if (obj == null) obj = []; + iteratee = createCallback(iteratee, context, 4); + var keys = obj.length !== + obj.length && _.keys(obj), + index = (keys || obj).length, + currentKey; + if (arguments.length < 3) { + if (!index) throw new TypeError(reduceError); + memo = obj[keys ? keys[--index] : --index]; + } + while (index--) { + currentKey = keys ? keys[index] : index; + memo = iteratee(memo, obj[currentKey], currentKey, obj); + } + return memo; + }; + + // Return the first value which passes a truth test. Aliased as `detect`. + _.find = _.detect = function(obj, predicate, context) { + var result; + predicate = _.iteratee(predicate, context); + _.some(obj, function(value, index, list) { + if (predicate(value, index, list)) { + result = value; + return true; + } + }); + return result; + }; + + // Return all the elements that pass a truth test. + // Aliased as `select`. + _.filter = _.select = function(obj, predicate, context) { + var results = []; + if (obj == null) return results; + predicate = _.iteratee(predicate, context); + _.each(obj, function(value, index, list) { + if (predicate(value, index, list)) results.push(value); + }); + return results; + }; + + // Return all the elements for which a truth test fails. + _.reject = function(obj, predicate, context) { + return _.filter(obj, _.negate(_.iteratee(predicate)), context); + }; + + // Determine whether all of the elements match a truth test. + // Aliased as `all`. + _.every = _.all = function(obj, predicate, context) { + if (obj == null) return true; + predicate = _.iteratee(predicate, context); + var keys = obj.length !== +obj.length && _.keys(obj), + length = (keys || obj).length, + index, currentKey; + for (index = 0; index < length; index++) { + currentKey = keys ? keys[index] : index; + if (!predicate(obj[currentKey], currentKey, obj)) return false; + } + return true; + }; + + // Determine if at least one element in the object matches a truth test. + // Aliased as `any`. + _.some = _.any = function(obj, predicate, context) { + if (obj == null) return false; + predicate = _.iteratee(predicate, context); + var keys = obj.length !== +obj.length && _.keys(obj), + length = (keys || obj).length, + index, currentKey; + for (index = 0; index < length; index++) { + currentKey = keys ? keys[index] : index; + if (predicate(obj[currentKey], currentKey, obj)) return true; + } + return false; + }; + + // Determine if the array or object contains a given value (using `===`). + // Aliased as `include`. + _.contains = _.include = function(obj, target) { + if (obj == null) return false; + if (obj.length !== +obj.length) obj = _.values(obj); + return _.indexOf(obj, target) >= 0; + }; + + // Invoke a method (with arguments) on every item in a collection. + _.invoke = function(obj, method) { + var args = slice.call(arguments, 2); + var isFunc = _.isFunction(method); + return _.map(obj, function(value) { + return (isFunc ? method : value[method]).apply(value, args); + }); + }; + + // Convenience version of a common use case of `map`: fetching a property. + _.pluck = function(obj, key) { + return _.map(obj, _.property(key)); + }; + + // Convenience version of a common use case of `filter`: selecting only objects + // containing specific `key:value` pairs. + _.where = function(obj, attrs) { + return _.filter(obj, _.matches(attrs)); + }; + + // Convenience version of a common use case of `find`: getting the first object + // containing specific `key:value` pairs. + _.findWhere = function(obj, attrs) { + return _.find(obj, _.matches(attrs)); + }; + + // Return the maximum element (or element-based computation). + _.max = function(obj, iteratee, context) { + var result = -Infinity, lastComputed = -Infinity, + value, computed; + if (iteratee == null && obj != null) { + obj = obj.length === +obj.length ? obj : _.values(obj); + for (var i = 0, length = obj.length; i < length; i++) { + value = obj[i]; + if (value > result) { + result = value; + } + } + } else { + iteratee = _.iteratee(iteratee, context); + _.each(obj, function(value, index, list) { + computed = iteratee(value, index, list); + if (computed > lastComputed || computed === -Infinity && result === -Infinity) { + result = value; + lastComputed = computed; + } + }); + } + return result; + }; + + // Return the minimum element (or element-based computation). + _.min = function(obj, iteratee, context) { + var result = Infinity, lastComputed = Infinity, + value, computed; + if (iteratee == null && obj != null) { + obj = obj.length === +obj.length ? obj : _.values(obj); + for (var i = 0, length = obj.length; i < length; i++) { + value = obj[i]; + if (value < result) { + result = value; + } + } + } else { + iteratee = _.iteratee(iteratee, context); + _.each(obj, function(value, index, list) { + computed = iteratee(value, index, list); + if (computed < lastComputed || computed === Infinity && result === Infinity) { + result = value; + lastComputed = computed; + } + }); + } + return result; + }; + + // Shuffle a collection, using the modern version of the + // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle). + _.shuffle = function(obj) { + var set = obj && obj.length === +obj.length ? obj : _.values(obj); + var length = set.length; + var shuffled = Array(length); + for (var index = 0, rand; index < length; index++) { + rand = _.random(0, index); + if (rand !== index) shuffled[index] = shuffled[rand]; + shuffled[rand] = set[index]; + } + return shuffled; + }; + + // Sample **n** random values from a collection. + // If **n** is not specified, returns a single random element. + // The internal `guard` argument allows it to work with `map`. + _.sample = function(obj, n, guard) { + if (n == null || guard) { + if (obj.length !== +obj.length) obj = _.values(obj); + return obj[_.random(obj.length - 1)]; + } + return _.shuffle(obj).slice(0, Math.max(0, n)); + }; + + // Sort the object's values by a criterion produced by an iteratee. + _.sortBy = function(obj, iteratee, context) { + iteratee = _.iteratee(iteratee, context); + return _.pluck(_.map(obj, function(value, index, list) { + return { + value: value, + index: index, + criteria: iteratee(value, index, list) + }; + }).sort(function(left, right) { + var a = left.criteria; + var b = right.criteria; + if (a !== b) { + if (a > b || a === void 0) return 1; + if (a < b || b === void 0) return -1; + } + return left.index - right.index; + }), 'value'); + }; + + // An internal function used for aggregate "group by" operations. + var group = function(behavior) { + return function(obj, iteratee, context) { + var result = {}; + iteratee = _.iteratee(iteratee, context); + _.each(obj, function(value, index) { + var key = iteratee(value, index, obj); + behavior(result, value, key); + }); + return result; + }; + }; + + // Groups the object's values by a criterion. Pass either a string attribute + // to group by, or a function that returns the criterion. + _.groupBy = group(function(result, value, key) { + if (_.has(result, key)) result[key].push(value); else result[key] = [value]; + }); + + // Indexes the object's values by a criterion, similar to `groupBy`, but for + // when you know that your index values will be unique. + _.indexBy = group(function(result, value, key) { + result[key] = value; + }); + + // Counts instances of an object that group by a certain criterion. Pass + // either a string attribute to count by, or a function that returns the + // criterion. + _.countBy = group(function(result, value, key) { + if (_.has(result, key)) result[key]++; else result[key] = 1; + }); + + // Use a comparator function to figure out the smallest index at which + // an object should be inserted so as to maintain order. Uses binary search. + _.sortedIndex = function(array, obj, iteratee, context) { + iteratee = _.iteratee(iteratee, context, 1); + var value = iteratee(obj); + var low = 0, high = array.length; + while (low < high) { + var mid = low + high >>> 1; + if (iteratee(array[mid]) < value) low = mid + 1; else high = mid; + } + return low; + }; + + // Safely create a real, live array from anything iterable. + _.toArray = function(obj) { + if (!obj) return []; + if (_.isArray(obj)) return slice.call(obj); + if (obj.length === +obj.length) return _.map(obj, _.identity); + return _.values(obj); + }; + + // Return the number of elements in an object. + _.size = function(obj) { + if (obj == null) return 0; + return obj.length === +obj.length ? obj.length : _.keys(obj).length; + }; + + // Split a collection into two arrays: one whose elements all satisfy the given + // predicate, and one whose elements all do not satisfy the predicate. + _.partition = function(obj, predicate, context) { + predicate = _.iteratee(predicate, context); + var pass = [], fail = []; + _.each(obj, function(value, key, obj) { + (predicate(value, key, obj) ? pass : fail).push(value); + }); + return [pass, fail]; + }; + + // Array Functions + // --------------- + + // Get the first element of an array. Passing **n** will return the first N + // values in the array. Aliased as `head` and `take`. The **guard** check + // allows it to work with `_.map`. + _.first = _.head = _.take = function(array, n, guard) { + if (array == null) return void 0; + if (n == null || guard) return array[0]; + if (n < 0) return []; + return slice.call(array, 0, n); + }; + + // Returns everything but the last entry of the array. Especially useful on + // the arguments object. Passing **n** will return all the values in + // the array, excluding the last N. The **guard** check allows it to work with + // `_.map`. + _.initial = function(array, n, guard) { + return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n))); + }; + + // Get the last element of an array. Passing **n** will return the last N + // values in the array. The **guard** check allows it to work with `_.map`. + _.last = function(array, n, guard) { + if (array == null) return void 0; + if (n == null || guard) return array[array.length - 1]; + return slice.call(array, Math.max(array.length - n, 0)); + }; + + // Returns everything but the first entry of the array. Aliased as `tail` and `drop`. + // Especially useful on the arguments object. Passing an **n** will return + // the rest N values in the array. The **guard** + // check allows it to work with `_.map`. + _.rest = _.tail = _.drop = function(array, n, guard) { + return slice.call(array, n == null || guard ? 1 : n); + }; + + // Trim out all falsy values from an array. + _.compact = function(array) { + return _.filter(array, _.identity); + }; + + // Internal implementation of a recursive `flatten` function. + var flatten = function(input, shallow, strict, output) { + if (shallow && _.every(input, _.isArray)) { + return concat.apply(output, input); + } + for (var i = 0, length = input.length; i < length; i++) { + var value = input[i]; + if (!_.isArray(value) && !_.isArguments(value)) { + if (!strict) output.push(value); + } else if (shallow) { + push.apply(output, value); + } else { + flatten(value, shallow, strict, output); + } + } + return output; + }; + + // Flatten out an array, either recursively (by default), or just one level. + _.flatten = function(array, shallow) { + return flatten(array, shallow, false, []); + }; + + // Return a version of the array that does not contain the specified value(s). + _.without = function(array) { + return _.difference(array, slice.call(arguments, 1)); + }; + + // Produce a duplicate-free version of the array. If the array has already + // been sorted, you have the option of using a faster algorithm. + // Aliased as `unique`. + _.uniq = _.unique = function(array, isSorted, iteratee, context) { + if (array == null) return []; + if (!_.isBoolean(isSorted)) { + context = iteratee; + iteratee = isSorted; + isSorted = false; + } + if (iteratee != null) iteratee = _.iteratee(iteratee, context); + var result = []; + var seen = []; + for (var i = 0, length = array.length; i < length; i++) { + var value = array[i]; + if (isSorted) { + if (!i || seen !== value) result.push(value); + seen = value; + } else if (iteratee) { + var computed = iteratee(value, i, array); + if (_.indexOf(seen, computed) < 0) { + seen.push(computed); + result.push(value); + } + } else if (_.indexOf(result, value) < 0) { + result.push(value); + } + } + return result; + }; + + // Produce an array that contains the union: each distinct element from all of + // the passed-in arrays. + _.union = function() { + return _.uniq(flatten(arguments, true, true, [])); + }; + + // Produce an array that contains every item shared between all the + // passed-in arrays. + _.intersection = function(array) { + if (array == null) return []; + var result = []; + var argsLength = arguments.length; + for (var i = 0, length = array.length; i < length; i++) { + var item = array[i]; + if (_.contains(result, item)) continue; + for (var j = 1; j < argsLength; j++) { + if (!_.contains(arguments[j], item)) break; + } + if (j === argsLength) result.push(item); + } + return result; + }; + + // Take the difference between one array and a number of other arrays. + // Only the elements present in just the first array will remain. + _.difference = function(array) { + var rest = flatten(slice.call(arguments, 1), true, true, []); + return _.filter(array, function(value){ + return !_.contains(rest, value); + }); + }; + + // Zip together multiple lists into a single array -- elements that share + // an index go together. + _.zip = function(array) { + if (array == null) return []; + var length = _.max(arguments, 'length').length; + var results = Array(length); + for (var i = 0; i < length; i++) { + results[i] = _.pluck(arguments, i); + } + return results; + }; + + // Converts lists into objects. Pass either a single array of `[key, value]` + // pairs, or two parallel arrays of the same length -- one of keys, and one of + // the corresponding values. + _.object = function(list, values) { + if (list == null) return {}; + var result = {}; + for (var i = 0, length = list.length; i < length; i++) { + if (values) { + result[list[i]] = values[i]; + } else { + result[list[i][0]] = list[i][1]; + } + } + return result; + }; + + // Return the position of the first occurrence of an item in an array, + // or -1 if the item is not included in the array. + // If the array is large and already in sort order, pass `true` + // for **isSorted** to use binary search. + _.indexOf = function(array, item, isSorted) { + if (array == null) return -1; + var i = 0, length = array.length; + if (isSorted) { + if (typeof isSorted == 'number') { + i = isSorted < 0 ? Math.max(0, length + isSorted) : isSorted; + } else { + i = _.sortedIndex(array, item); + return array[i] === item ? i : -1; + } + } + for (; i < length; i++) if (array[i] === item) return i; + return -1; + }; + + _.lastIndexOf = function(array, item, from) { + if (array == null) return -1; + var idx = array.length; + if (typeof from == 'number') { + idx = from < 0 ? idx + from + 1 : Math.min(idx, from + 1); + } + while (--idx >= 0) if (array[idx] === item) return idx; + return -1; + }; + + // Generate an integer Array containing an arithmetic progression. A port of + // the native Python `range()` function. See + // [the Python documentation](http://docs.python.org/library/functions.html#range). + _.range = function(start, stop, step) { + if (arguments.length <= 1) { + stop = start || 0; + start = 0; + } + step = step || 1; + + var length = Math.max(Math.ceil((stop - start) / step), 0); + var range = Array(length); + + for (var idx = 0; idx < length; idx++, start += step) { + range[idx] = start; + } + + return range; + }; + + // Function (ahem) Functions + // ------------------ + + // Reusable constructor function for prototype setting. + var Ctor = function(){}; + + // Create a function bound to a given object (assigning `this`, and arguments, + // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if + // available. + _.bind = function(func, context) { + var args, bound; + if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); + if (!_.isFunction(func)) throw new TypeError('Bind must be called on a function'); + args = slice.call(arguments, 2); + bound = function() { + if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments))); + Ctor.prototype = func.prototype; + var self = new Ctor; + Ctor.prototype = null; + var result = func.apply(self, args.concat(slice.call(arguments))); + if (_.isObject(result)) return result; + return self; + }; + return bound; + }; + + // Partially apply a function by creating a version that has had some of its + // arguments pre-filled, without changing its dynamic `this` context. _ acts + // as a placeholder, allowing any combination of arguments to be pre-filled. + _.partial = function(func) { + var boundArgs = slice.call(arguments, 1); + return function() { + var position = 0; + var args = boundArgs.slice(); + for (var i = 0, length = args.length; i < length; i++) { + if (args[i] === _) args[i] = arguments[position++]; + } + while (position < arguments.length) args.push(arguments[position++]); + return func.apply(this, args); + }; + }; + + // Bind a number of an object's methods to that object. Remaining arguments + // are the method names to be bound. Useful for ensuring that all callbacks + // defined on an object belong to it. + _.bindAll = function(obj) { + var i, length = arguments.length, key; + if (length <= 1) throw new Error('bindAll must be passed function names'); + for (i = 1; i < length; i++) { + key = arguments[i]; + obj[key] = _.bind(obj[key], obj); + } + return obj; + }; + + // Memoize an expensive function by storing its results. + _.memoize = function(func, hasher) { + var memoize = function(key) { + var cache = memoize.cache; + var address = hasher ? hasher.apply(this, arguments) : key; + if (!_.has(cache, address)) cache[address] = func.apply(this, arguments); + return cache[address]; + }; + memoize.cache = {}; + return memoize; + }; + + // Delays a function for the given number of milliseconds, and then calls + // it with the arguments supplied. + _.delay = function(func, wait) { + var args = slice.call(arguments, 2); + return setTimeout(function(){ + return func.apply(null, args); + }, wait); + }; + + // Defers a function, scheduling it to run after the current call stack has + // cleared. + _.defer = function(func) { + return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1))); + }; + + // Returns a function, that, when invoked, will only be triggered at most once + // during a given window of time. Normally, the throttled function will run + // as much as it can, without ever going more than once per `wait` duration; + // but if you'd like to disable the execution on the leading edge, pass + // `{leading: false}`. To disable execution on the trailing edge, ditto. + _.throttle = function(func, wait, options) { + var context, args, result; + var timeout = null; + var previous = 0; + if (!options) options = {}; + var later = function() { + previous = options.leading === false ? 0 : _.now(); + timeout = null; + result = func.apply(context, args); + if (!timeout) context = args = null; + }; + return function() { + var now = _.now(); + if (!previous && options.leading === false) previous = now; + var remaining = wait - (now - previous); + context = this; + args = arguments; + if (remaining <= 0 || remaining > wait) { + clearTimeout(timeout); + timeout = null; + previous = now; + result = func.apply(context, args); + if (!timeout) context = args = null; + } else if (!timeout && options.trailing !== false) { + timeout = setTimeout(later, remaining); + } + return result; + }; + }; + + // Returns a function, that, as long as it continues to be invoked, will not + // be triggered. The function will be called after it stops being called for + // N milliseconds. If `immediate` is passed, trigger the function on the + // leading edge, instead of the trailing. + _.debounce = function(func, wait, immediate) { + var timeout, args, context, timestamp, result; + + var later = function() { + var last = _.now() - timestamp; + + if (last < wait && last > 0) { + timeout = setTimeout(later, wait - last); + } else { + timeout = null; + if (!immediate) { + result = func.apply(context, args); + if (!timeout) context = args = null; + } + } + }; + + return function() { + context = this; + args = arguments; + timestamp = _.now(); + var callNow = immediate && !timeout; + if (!timeout) timeout = setTimeout(later, wait); + if (callNow) { + result = func.apply(context, args); + context = args = null; + } + + return result; + }; + }; + + // Returns the first function passed as an argument to the second, + // allowing you to adjust arguments, run code before and after, and + // conditionally execute the original function. + _.wrap = function(func, wrapper) { + return _.partial(wrapper, func); + }; + + // Returns a negated version of the passed-in predicate. + _.negate = function(predicate) { + return function() { + return !predicate.apply(this, arguments); + }; + }; + + // Returns a function that is the composition of a list of functions, each + // consuming the return value of the function that follows. + _.compose = function() { + var args = arguments; + var start = args.length - 1; + return function() { + var i = start; + var result = args[start].apply(this, arguments); + while (i--) result = args[i].call(this, result); + return result; + }; + }; + + // Returns a function that will only be executed after being called N times. + _.after = function(times, func) { + return function() { + if (--times < 1) { + return func.apply(this, arguments); + } + }; + }; + + // Returns a function that will only be executed before being called N times. + _.before = function(times, func) { + var memo; + return function() { + if (--times > 0) { + memo = func.apply(this, arguments); + } else { + func = null; + } + return memo; + }; + }; + + // Returns a function that will be executed at most one time, no matter how + // often you call it. Useful for lazy initialization. + _.once = _.partial(_.before, 2); + + // Object Functions + // ---------------- + + // Retrieve the names of an object's properties. + // Delegates to **ECMAScript 5**'s native `Object.keys` + _.keys = function(obj) { + if (!_.isObject(obj)) return []; + if (nativeKeys) return nativeKeys(obj); + var keys = []; + for (var key in obj) if (_.has(obj, key)) keys.push(key); + return keys; + }; + + // Retrieve the values of an object's properties. + _.values = function(obj) { + var keys = _.keys(obj); + var length = keys.length; + var values = Array(length); + for (var i = 0; i < length; i++) { + values[i] = obj[keys[i]]; + } + return values; + }; + + // Convert an object into a list of `[key, value]` pairs. + _.pairs = function(obj) { + var keys = _.keys(obj); + var length = keys.length; + var pairs = Array(length); + for (var i = 0; i < length; i++) { + pairs[i] = [keys[i], obj[keys[i]]]; + } + return pairs; + }; + + // Invert the keys and values of an object. The values must be serializable. + _.invert = function(obj) { + var result = {}; + var keys = _.keys(obj); + for (var i = 0, length = keys.length; i < length; i++) { + result[obj[keys[i]]] = keys[i]; + } + return result; + }; + + // Return a sorted list of the function names available on the object. + // Aliased as `methods` + _.functions = _.methods = function(obj) { + var names = []; + for (var key in obj) { + if (_.isFunction(obj[key])) names.push(key); + } + return names.sort(); + }; + + // Extend a given object with all the properties in passed-in object(s). + _.extend = function(obj) { + if (!_.isObject(obj)) return obj; + var source, prop; + for (var i = 1, length = arguments.length; i < length; i++) { + source = arguments[i]; + for (prop in source) { + if (hasOwnProperty.call(source, prop)) { + obj[prop] = source[prop]; + } + } + } + return obj; + }; + + // Return a copy of the object only containing the whitelisted properties. + _.pick = function(obj, iteratee, context) { + var result = {}, key; + if (obj == null) return result; + if (_.isFunction(iteratee)) { + iteratee = createCallback(iteratee, context); + for (key in obj) { + var value = obj[key]; + if (iteratee(value, key, obj)) result[key] = value; + } + } else { + var keys = concat.apply([], slice.call(arguments, 1)); + obj = new Object(obj); + for (var i = 0, length = keys.length; i < length; i++) { + key = keys[i]; + if (key in obj) result[key] = obj[key]; + } + } + return result; + }; + + // Return a copy of the object without the blacklisted properties. + _.omit = function(obj, iteratee, context) { + if (_.isFunction(iteratee)) { + iteratee = _.negate(iteratee); + } else { + var keys = _.map(concat.apply([], slice.call(arguments, 1)), String); + iteratee = function(value, key) { + return !_.contains(keys, key); + }; + } + return _.pick(obj, iteratee, context); + }; + + // Fill in a given object with default properties. + _.defaults = function(obj) { + if (!_.isObject(obj)) return obj; + for (var i = 1, length = arguments.length; i < length; i++) { + var source = arguments[i]; + for (var prop in source) { + if (obj[prop] === void 0) obj[prop] = source[prop]; + } + } + return obj; + }; + + // Create a (shallow-cloned) duplicate of an object. + _.clone = function(obj) { + if (!_.isObject(obj)) return obj; + return _.isArray(obj) ? obj.slice() : _.extend({}, obj); + }; + + // Invokes interceptor with the obj, and then returns obj. + // The primary purpose of this method is to "tap into" a method chain, in + // order to perform operations on intermediate results within the chain. + _.tap = function(obj, interceptor) { + interceptor(obj); + return obj; + }; + + // Internal recursive comparison function for `isEqual`. + var eq = function(a, b, aStack, bStack) { + // Identical objects are equal. `0 === -0`, but they aren't identical. + // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal). + if (a === b) return a !== 0 || 1 / a === 1 / b; + // A strict comparison is necessary because `null == undefined`. + if (a == null || b == null) return a === b; + // Unwrap any wrapped objects. + if (a instanceof _) a = a._wrapped; + if (b instanceof _) b = b._wrapped; + // Compare `[[Class]]` names. + var className = toString.call(a); + if (className !== toString.call(b)) return false; + switch (className) { + // Strings, numbers, regular expressions, dates, and booleans are compared by value. + case '[object RegExp]': + // RegExps are coerced to strings for comparison (Note: '' + /a/i === '/a/i') + case '[object String]': + // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is + // equivalent to `new String("5")`. + return '' + a === '' + b; + case '[object Number]': + // `NaN`s are equivalent, but non-reflexive. + // Object(NaN) is equivalent to NaN + if (+a !== +a) return +b !== +b; + // An `egal` comparison is performed for other numeric values. + return +a === 0 ? 1 / +a === 1 / b : +a === +b; + case '[object Date]': + case '[object Boolean]': + // Coerce dates and booleans to numeric primitive values. Dates are compared by their + // millisecond representations. Note that invalid dates with millisecond representations + // of `NaN` are not equivalent. + return +a === +b; + } + if (typeof a != 'object' || typeof b != 'object') return false; + // Assume equality for cyclic structures. The algorithm for detecting cyclic + // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. + var length = aStack.length; + while (length--) { + // Linear search. Performance is inversely proportional to the number of + // unique nested structures. + if (aStack[length] === a) return bStack[length] === b; + } + // Objects with different constructors are not equivalent, but `Object`s + // from different frames are. + var aCtor = a.constructor, bCtor = b.constructor; + if ( + aCtor !== bCtor && + // Handle Object.create(x) cases + 'constructor' in a && 'constructor' in b && + !(_.isFunction(aCtor) && aCtor instanceof aCtor && + _.isFunction(bCtor) && bCtor instanceof bCtor) + ) { + return false; + } + // Add the first object to the stack of traversed objects. + aStack.push(a); + bStack.push(b); + var size, result; + // Recursively compare objects and arrays. + if (className === '[object Array]') { + // Compare array lengths to determine if a deep comparison is necessary. + size = a.length; + result = size === b.length; + if (result) { + // Deep compare the contents, ignoring non-numeric properties. + while (size--) { + if (!(result = eq(a[size], b[size], aStack, bStack))) break; + } + } + } else { + // Deep compare objects. + var keys = _.keys(a), key; + size = keys.length; + // Ensure that both objects contain the same number of properties before comparing deep equality. + result = _.keys(b).length === size; + if (result) { + while (size--) { + // Deep compare each member + key = keys[size]; + if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break; + } + } + } + // Remove the first object from the stack of traversed objects. + aStack.pop(); + bStack.pop(); + return result; + }; + + // Perform a deep comparison to check if two objects are equal. + _.isEqual = function(a, b) { + return eq(a, b, [], []); + }; + + // Is a given array, string, or object empty? + // An "empty" object has no enumerable own-properties. + _.isEmpty = function(obj) { + if (obj == null) return true; + if (_.isArray(obj) || _.isString(obj) || _.isArguments(obj)) return obj.length === 0; + for (var key in obj) if (_.has(obj, key)) return false; + return true; + }; + + // Is a given value a DOM element? + _.isElement = function(obj) { + return !!(obj && obj.nodeType === 1); + }; + + // Is a given value an array? + // Delegates to ECMA5's native Array.isArray + _.isArray = nativeIsArray || function(obj) { + return toString.call(obj) === '[object Array]'; + }; + + // Is a given variable an object? + _.isObject = function(obj) { + var type = typeof obj; + return type === 'function' || type === 'object' && !!obj; + }; + + // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp. + _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) { + _['is' + name] = function(obj) { + return toString.call(obj) === '[object ' + name + ']'; + }; + }); + + // Define a fallback version of the method in browsers (ahem, IE), where + // there isn't any inspectable "Arguments" type. + if (!_.isArguments(arguments)) { + _.isArguments = function(obj) { + return _.has(obj, 'callee'); + }; + } + + // Optimize `isFunction` if appropriate. Work around an IE 11 bug. + if (typeof /./ !== 'function') { + _.isFunction = function(obj) { + return typeof obj == 'function' || false; + }; + } + + // Is a given object a finite number? + _.isFinite = function(obj) { + return isFinite(obj) && !isNaN(parseFloat(obj)); + }; + + // Is the given value `NaN`? (NaN is the only number which does not equal itself). + _.isNaN = function(obj) { + return _.isNumber(obj) && obj !== +obj; + }; + + // Is a given value a boolean? + _.isBoolean = function(obj) { + return obj === true || obj === false || toString.call(obj) === '[object Boolean]'; + }; + + // Is a given value equal to null? + _.isNull = function(obj) { + return obj === null; + }; + + // Is a given variable undefined? + _.isUndefined = function(obj) { + return obj === void 0; + }; + + // Shortcut function for checking if an object has a given property directly + // on itself (in other words, not on a prototype). + _.has = function(obj, key) { + return obj != null && hasOwnProperty.call(obj, key); + }; + + // Utility Functions + // ----------------- + + // Run Underscore.js in *noConflict* mode, returning the `_` variable to its + // previous owner. Returns a reference to the Underscore object. + _.noConflict = function() { + root._ = previousUnderscore; + return this; + }; + + // Keep the identity function around for default iteratees. + _.identity = function(value) { + return value; + }; + + // Predicate-generating functions. Often useful outside of Underscore. + _.constant = function(value) { + return function() { + return value; + }; + }; + + _.noop = function(){}; + + _.property = function(key) { + return function(obj) { + return obj[key]; + }; + }; + + // Returns a predicate for checking whether an object has a given set of `key:value` pairs. + _.matches = function(attrs) { + var pairs = _.pairs(attrs), length = pairs.length; + return function(obj) { + if (obj == null) return !length; + obj = new Object(obj); + for (var i = 0; i < length; i++) { + var pair = pairs[i], key = pair[0]; + if (pair[1] !== obj[key] || !(key in obj)) return false; + } + return true; + }; + }; + + // Run a function **n** times. + _.times = function(n, iteratee, context) { + var accum = Array(Math.max(0, n)); + iteratee = createCallback(iteratee, context, 1); + for (var i = 0; i < n; i++) accum[i] = iteratee(i); + return accum; + }; + + // Return a random integer between min and max (inclusive). + _.random = function(min, max) { + if (max == null) { + max = min; + min = 0; + } + return min + Math.floor(Math.random() * (max - min + 1)); + }; + + // A (possibly faster) way to get the current timestamp as an integer. + _.now = Date.now || function() { + return new Date().getTime(); + }; + + // List of HTML entities for escaping. + var escapeMap = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''', + '`': '`' + }; + var unescapeMap = _.invert(escapeMap); + + // Functions for escaping and unescaping strings to/from HTML interpolation. + var createEscaper = function(map) { + var escaper = function(match) { + return map[match]; + }; + // Regexes for identifying a key that needs to be escaped + var source = '(?:' + _.keys(map).join('|') + ')'; + var testRegexp = RegExp(source); + var replaceRegexp = RegExp(source, 'g'); + return function(string) { + string = string == null ? '' : '' + string; + return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string; + }; + }; + _.escape = createEscaper(escapeMap); + _.unescape = createEscaper(unescapeMap); + + // If the value of the named `property` is a function then invoke it with the + // `object` as context; otherwise, return it. + _.result = function(object, property) { + if (object == null) return void 0; + var value = object[property]; + return _.isFunction(value) ? object[property]() : value; + }; + + // Generate a unique integer id (unique within the entire client session). + // Useful for temporary DOM ids. + var idCounter = 0; + _.uniqueId = function(prefix) { + var id = ++idCounter + ''; + return prefix ? prefix + id : id; + }; + + // By default, Underscore uses ERB-style template delimiters, change the + // following template settings to use alternative delimiters. + _.templateSettings = { + evaluate : /<%([\s\S]+?)%>/g, + interpolate : /<%=([\s\S]+?)%>/g, + escape : /<%-([\s\S]+?)%>/g + }; + + // When customizing `templateSettings`, if you don't want to define an + // interpolation, evaluation or escaping regex, we need one that is + // guaranteed not to match. + var noMatch = /(.)^/; + + // Certain characters need to be escaped so that they can be put into a + // string literal. + var escapes = { + "'": "'", + '\\': '\\', + '\r': 'r', + '\n': 'n', + '\u2028': 'u2028', + '\u2029': 'u2029' + }; + + var escaper = /\\|'|\r|\n|\u2028|\u2029/g; + + var escapeChar = function(match) { + return '\\' + escapes[match]; + }; + + // JavaScript micro-templating, similar to John Resig's implementation. + // Underscore templating handles arbitrary delimiters, preserves whitespace, + // and correctly escapes quotes within interpolated code. + // NB: `oldSettings` only exists for backwards compatibility. + _.template = function(text, settings, oldSettings) { + if (!settings && oldSettings) settings = oldSettings; + settings = _.defaults({}, settings, _.templateSettings); + + // Combine delimiters into one regular expression via alternation. + var matcher = RegExp([ + (settings.escape || noMatch).source, + (settings.interpolate || noMatch).source, + (settings.evaluate || noMatch).source + ].join('|') + '|$', 'g'); + + // Compile the template source, escaping string literals appropriately. + var index = 0; + var source = "__p+='"; + text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { + source += text.slice(index, offset).replace(escaper, escapeChar); + index = offset + match.length; + + if (escape) { + source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; + } else if (interpolate) { + source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; + } else if (evaluate) { + source += "';\n" + evaluate + "\n__p+='"; + } + + // Adobe VMs need the match returned to produce the correct offest. + return match; + }); + source += "';\n"; + + // If a variable is not specified, place data values in local scope. + if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; + + source = "var __t,__p='',__j=Array.prototype.join," + + "print=function(){__p+=__j.call(arguments,'');};\n" + + source + 'return __p;\n'; + + try { + var render = new Function(settings.variable || 'obj', '_', source); + } catch (e) { + e.source = source; + throw e; + } + + var template = function(data) { + return render.call(this, data, _); + }; + + // Provide the compiled source as a convenience for precompilation. + var argument = settings.variable || 'obj'; + template.source = 'function(' + argument + '){\n' + source + '}'; + + return template; + }; + + // Add a "chain" function. Start chaining a wrapped Underscore object. + _.chain = function(obj) { + var instance = _(obj); + instance._chain = true; + return instance; + }; + + // OOP + // --------------- + // If Underscore is called as a function, it returns a wrapped object that + // can be used OO-style. This wrapper holds altered versions of all the + // underscore functions. Wrapped objects may be chained. + + // Helper function to continue chaining intermediate results. + var result = function(obj) { + return this._chain ? _(obj).chain() : obj; + }; + + // Add your own custom functions to the Underscore object. + _.mixin = function(obj) { + _.each(_.functions(obj), function(name) { + var func = _[name] = obj[name]; + _.prototype[name] = function() { + var args = [this._wrapped]; + push.apply(args, arguments); + return result.call(this, func.apply(_, args)); + }; + }); + }; + + // Add all of the Underscore functions to the wrapper object. + _.mixin(_); + + // Add all mutator Array functions to the wrapper. + _.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { + var method = ArrayProto[name]; + _.prototype[name] = function() { + var obj = this._wrapped; + method.apply(obj, arguments); + if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0]; + return result.call(this, obj); + }; + }); + + // Add all accessor Array functions to the wrapper. + _.each(['concat', 'join', 'slice'], function(name) { + var method = ArrayProto[name]; + _.prototype[name] = function() { + return result.call(this, method.apply(this._wrapped, arguments)); + }; + }); + + // Extracts the result from a wrapped and chained object. + _.prototype.value = function() { + return this._wrapped; + }; + + // AMD registration happens at the end for compatibility with AMD loaders + // that may not enforce next-turn semantics on modules. Even though general + // practice for AMD registration is to be anonymous, underscore registers + // as a named module because, like jQuery, it is a base library that is + // popular enough to be bundled in a third party lib, but not be part of + // an AMD load request. Those cases could generate an error when an + // anonymous define() is called outside of a loader request. + if (typeof define === 'function' && define.amd) { + define('underscore', [], function() { + return _; + }); + } +}.call(this)); diff --git a/dependencies/main_files/worker.js b/dependencies/main_files/worker.js new file mode 100644 index 00000000..4b708de0 --- /dev/null +++ b/dependencies/main_files/worker.js @@ -0,0 +1,58 @@ +/** + * Created by aghassaei on 1/12/15. + */ + +function myWorker(){ + + //local variables + localEnv = null;//local variables passed in from outside + working = false;//boolean that says whether I'm busy or not + arg = null;//main data we are crunching + modelMesh = null;//hold on to this so we don't have to keep passing it in + + self.onmessage = function(e) { + var data = e.data; + + if (data.url) { + var url = data.url; + var index = url.indexOf('main.html');//url of landing page + if (index != -1) { + url = url.substring(0, index); + } + //load all scripts + importScripts(url + 'dependencies/three.js'); + // importScripts(url + 'js/models/dmaBeam.js'); + } +// + if (data.model){ + var material = new THREE.MeshLambertMaterial({side:THREE.DoubleSide}); + modelMesh = new THREE.Mesh(JSON.parse(data.model), material); + } + + if (data.executable){ + + if (data.localEnv){//be sure to get local environment vars before executable runs + localEnv = JSON.parse(data.localEnv); + } + if (data.arg){//be sure to get arg before executable runs + arg = data.arg; + } + + if (working) { + console.log("problem here, already working on something else"); + return; + } + working = true; + eval(data.executable); + var result = executable(arg); + working = false; + postMessage({result:result, isWorking:working}); + } + + if (data.isWorking){ + postMessage({isWorking:working}); + } + + + }; +} diff --git a/js/fea/dmaNode.js b/js/fea/dmaNode.js index e7a53e01..5547b6c0 100644 --- a/js/fea/dmaNode.js +++ b/js/fea/dmaNode.js @@ -43,3 +43,13 @@ BeamNode.prototype.rotate = function(rx, ry, rz){ BeamNode.prototype.destroy = function(){ this._beams = null;//be sure to remove cyclic reference }; + +BeamNode.prototype.getMidPoint = function(node){ + + var x = (node.x+this.x)/2; + var y = (node.y+this.y)/2; + var z = (node.z+this.z)/2; + + return [x,y,z]; + +} diff --git a/js/fea/dmaPart.js b/js/fea/dmaPart.js index ae58792d..5dc1fba0 100644 --- a/js/fea/dmaPart.js +++ b/js/fea/dmaPart.js @@ -7,9 +7,10 @@ -function DmaPart(geometry) {//list of nodes, config tells how nodes are connected -// this.nodes = nodes; +function DmaPart(geometry, nodes) {//list of nodes, config tells how nodes are connected + this.nodes = nodes; // this.beams = this._createBeams(nodes, config); + this.scale = 10; this.geometry = geometry; this.render(); } @@ -23,7 +24,22 @@ DmaPart.prototype._createBeams = function(nodes, config){ }; DmaPart.prototype.render = function(){ + //translate geo to nodes + var midpoint = this.nodes[0].getMidPoint(this.nodes[1]); +// this.geometry.applyMatrix(new THREE.Matrix4().makeTranslation()); + + var mesh = new THREE.Mesh(this.geometry); + var scale = this.scale*2.5; + mesh.scale.set(scale, scale, scale); +// mesh.rotateZ(Math.PI); + + mesh.position.x = midpoint[0]; + mesh.position.y = midpoint[1]; + mesh.position.z = midpoint[2]; + new BeamNode(0,0,0); + + window.three.sceneAdd(mesh); window.three.render(); }; diff --git a/js/main.js b/js/main.js index 6ccd65a0..7474451b 100644 --- a/js/main.js +++ b/js/main.js @@ -32,11 +32,11 @@ $(function(){ function part_loadSTL(){ var loader = new THREE.STLLoader(); loader.addEventListener('load', part_onMeshLoad); - loader.load('data/Airbus_A300-600.stl'); + loader.load('data/triangleRot.stl'); } function part_onMeshLoad(e){ - new DmaPart(e.content); + lattice.fillWithParts(e.content); } setupNavBar(threeModel); diff --git a/js/models/lattice.js b/js/models/lattice.js index 93fd06f4..dd8620e6 100644 --- a/js/models/lattice.js +++ b/js/models/lattice.js @@ -72,9 +72,72 @@ Lattice = Backbone.Model.extend({ return nodes; }, - fillWithParts: function(){ + fillWithParts: function(geometry){ + geometry.applyMatrix(new THREE.Matrix4().makeTranslation(-2.3580117225646973,-0.32,0)); +// var scale = this.scale*3; +// geometry.makeScale(scale, scale, scale); + var nodes = this.get("nodes"); + if (!this.nodesFilled(nodes)) return; + var rot60 = geometry.clone(); + rot60.applyMatrix(new THREE.Matrix4().makeRotationZ(2*Math.PI/3)); + + var rot120 = geometry.clone(); + rot120.applyMatrix(new THREE.Matrix4().makeRotationZ(4*Math.PI/3)); + + var rot180 = geometry.clone(); + rot180.applyMatrix(new THREE.Matrix4().makeRotationZ(Math.PI)); + + var rot240 = geometry.clone(); + rot240.applyMatrix(new THREE.Matrix4().makeRotationZ(5*Math.PI/3)); + + var rot300 = geometry.clone(); + rot300.applyMatrix(new THREE.Matrix4().makeRotationZ(1*Math.PI/3)); + + + + for (var z=1;z<=nodes[0][0].length;z++){ + for (var y= 0;y<nodes[0].length-1;y++){ + for (var x=0;x<nodes.length-1;x++){ + + + if (z%2==1){ + new DmaPart(geometry, [nodes[x][y][z], nodes[x+1][y][z], nodes[x][y+1][z-1]]); + + if (y%2 == 0) new DmaPart(rot60, [nodes[x][y][z], nodes[x+1][y+1][z], nodes[x][y+1][z-1]]); + else new DmaPart(rot120, [nodes[x][y][z], nodes[x+1][y+1][z], nodes[x][y+1][z-1]]); + + if (y%2 == 0) new DmaPart(rot120, [nodes[x][y][z], nodes[x][y+1][z], nodes[x][y+1][z-1]]); + else new DmaPart(rot60, [nodes[x][y][z], nodes[x][y+1][z], nodes[x][y+1][z-1]]); + } else { + + new DmaPart(rot180, [nodes[x][y][z], nodes[x+1][y][z], nodes[x][y+1][z-1]]); + + if (y%2 == 0) new DmaPart(rot240, [nodes[x][y][z], nodes[x+1][y+1][z], nodes[x][y+1][z-1]]); + else new DmaPart(rot300, [nodes[x][y][z], nodes[x+1][y+1][z], nodes[x][y+1][z-1]]); + + if (y%2 == 0) new DmaPart(rot300, [nodes[x][y][z], nodes[x][y+1][z], nodes[x][y+1][z-1]]); + else new DmaPart(rot240, [nodes[x][y][z], nodes[x][y+1][z], nodes[x][y+1][z-1]]); + + } + } + } + } + + + + + + + + }, + + nodesFilled: function(nodes){ + if (!nodes) return false; + if (nodes.length == 0) return false; + if (nodes[0].length == 0) return false; + return (nodes[0][0].length > 0); } }); \ No newline at end of file -- GitLab