diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..0e647f682f0fc31cb5d4cd897b63a53033a458ed
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,84 @@
+cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
+enable_testing()
+
+project (gzweb)
+include(ExternalProject)
+
+set (GZWEB_MAJOR_VERSION 1)
+set (GZWEB_MINOR_VERSION 1)
+set (GZWEB_PATCH_VERSION 0)
+set (GZWEB_VERSION ${GZWEB_MAJOR_VERSION}.${GZWEB_MINOR_VERSION})
+set (GZWEB_VERSION_FULL ${GZWEB_MAJOR_VERSION}.${GZWEB_MINOR_VERSION}.${GZWEB_PATCH_VERSION})
+message (STATUS "${PROJECT_NAME} version ${GZWEB_VERSION_FULL}")
+
+set (gzweb_cmake_dir ${PROJECT_SOURCE_DIR}/cmake CACHE PATH "Location of CMake scripts")
+
+# Packaging configuration
+set (CPACK_PACKAGE_VERSION "${GZWEB_VERSION_FULL}")
+set (CPACK_PACKAGE_VERSION_MAJOR "${GZWEB_MAJOR_VERSION}")
+set (CPACK_PACKAGE_VERSION_MINOR "${GZWEB_MINOR_VERSION}")
+set (CPACK_PACKAGE_VERSION_PATCH "${GZWEB_PATCH_VERSION}")
+list (APPEND CPACK_SOURCE_GENERATOR "TBZ2")
+list (APPEND CPACK_SOURCE_IGNORE_FILES ";TODO;/.hg/;.hgignore;.swp$;/build/")
+set (CPACK_SOURCE_PACKAGE_FILE_NAME "@PROJECT_NAME@-@GZWEB_VERSION_FULL@")
+include (CPack)
+
+
+#####################################
+# Set the default build type
+if (NOT CMAKE_BUILD_TYPE)
+  set (CMAKE_BUILD_TYPE "Release" CACHE STRING
+    "Choose the type of build, options are: Debug Release RelWithDebInfo" FORCE)
+  message(STATUS "Build type not selected: Release selected by default")
+endif (NOT CMAKE_BUILD_TYPE)
+
+# find gazebo
+include (FindPkgConfig)
+if (PKG_CONFIG_FOUND)
+  pkg_check_modules(GAZEBO gazebo>=1.9.0 REQUIRED)
+  pkg_check_modules(JANSSON jansson REQUIRED)
+
+  # Find GNU Triangulation Surface Library
+  pkg_check_modules(GTS gts)
+  if (GTS_FOUND)
+    set (HAVE_GTS TRUE)
+  else ()
+    set (HAVE_GTS FALSE)
+    message (WARNING "GNU Triangulation Surface library not found,
+        gzcoarse/mesh simplification functionality will be disabled.")
+  endif ()
+else()
+  message(FATAL_ERROR "pkg-config is required; please install it")
+endif()
+
+include(FindBoost)
+find_package(Boost ${MIN_BOOST_VERSION} REQUIRED system filesystem regex thread)
+find_package(Protobuf REQUIRED)
+
+# Find tinyxml. Only debian distributions package tinyxml with a pkg-config
+find_path (tinyxml_include_dir tinyxml.h ${tinyxml_include_dirs} ENV CPATH)
+if (NOT tinyxml_include_dir)
+  message (STATUS "Looking for tinyxml.h - not found")
+  message(FATAL_ERROR "Missing: tinyxml")
+else ()
+  message (STATUS "Looking for tinyxml.h - found")
+  set (tinyxml_include_dirs ${tinyxml_include_dir} CACHE STRING
+    "tinyxml include paths. Use this to override automatic detection.")
+  set (tinyxml_libraries "tinyxml" CACHE INTERNAL "tinyxml libraries")
+endif ()
+
+find_program(ImageMagick convert)
+if ("${ImageMagick}" STREQUAL "ImageMagick-NOTFOUND")
+  message(FATAL_ERROR "imagemagick not found")
+endif()
+
+add_subdirectory(gz3d)
+add_subdirectory(tools)
+
+########### Add uninstall target ###############
+configure_file(
+  "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in"
+  "${CMAKE_CURRENT_BINARY_DIR}/cmake/cmake_uninstall.cmake"
+  IMMEDIATE @ONLY)
+add_custom_target(uninstall
+  "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake/cmake_uninstall.cmake")
diff --git a/README b/README
new file mode 100644
index 0000000000000000000000000000000000000000..751a3ba37d03c3e83a24b38aea27c7cbff7d7b75
--- /dev/null
+++ b/README
@@ -0,0 +1,120 @@
+# Gzweb - A web client for Gazebo
+
+Gzweb is a WebGL client for Gazebo. Like gzclient, it's a front-end graphical interface to gzserver and provides visualization of the simulation.
+
+http://gazebosim.org/gzweb
+
+# Installation
+
+## Gazebo
+
+Install gazebo or drcsim if you haven't done so already.
+
+http://gazebosim.org/install
+
+## Dependencies
+
+* libjansson-dev
+
+* nodejs (>= 0.10)
+
+* npm
+
+* libboost-dev
+
+* imagemagick
+
+* libtinyxml-dev
+
+* mercurial
+
+Make sure your system supports a more recent version of nodejs (>=0.10) then install the dependencies from the terminal:
+
+    sudo apt-get install libjansson-dev nodejs npm libboost-dev imagemagick libtinyxml-dev mercurial
+
+**Note** For Ubuntu Precise or older distributions, the nodejs version that comes with it may not work with gzweb. In that case, set up your system to grab and install the latest nodejs debs:
+
+    curl -sL https://deb.nodesource.com/setup | sudo bash -
+    sudo apt-get install nodejs
+
+If nodejs installs without errors then install the rest of the dependencies (leaving out npm as that should be installed with nodejs):
+
+    sudo apt-get install libjansson-dev libboost-dev imagemagick libtinyxml-dev mercurial
+
+# Build
+
+The first time you build, you'll need to gather all the gazebo models and
+put them in the right directory and prepare them for the web. To do this,
+you'll need to source the gazebo/drcsim setup.sh files, and run a deploy script.
+
+    # if you have drcsim then source /usr/share/drcsim/setup.sh
+    . /usr/share/gazebo/setup.sh
+
+Run the deploy script, this downloads models from the web, converts media files to web-compatible format, and generates thumbnails for the models. This step may take a few minutes.
+
+    ./deploy.sh -m
+
+Note the `-m` flag tells the deploy script to grab models from the model
+database and any other models in your gazebo paths. For all subsequent builds,
+the `-m` flag will not be needed, i.e.:
+
+    ./deploy.sh
+
+
+## Options
+
+To generate thumbnails manually, run the script with the `-t` flag, i.e.:
+
+    ./deploy.sh -t
+
+Mobile devices:
+
+If you'll use gzweb on mobile devices, you can create coarse versions of all models, which are lighter to load (50% of original quality). If you've already ran `./deploy.sh -m`, run just:
+
+    ./deploy.sh -c
+
+Or you can run both flags at the same time to create the model database and also generate coarse versions:
+
+    ./deploy.sh -m -c
+
+# Running gzserver, gzweb, and webgl
+
+Start gazebo or gzserver first
+
+     gzserver
+
+Start the http and websocket servers
+
+    ./start_gzweb.sh
+
+Open a browser that has webgl support and point it to the ip address and port
+where the http server is started, by default it's on port 8080, e.g.
+
+http://localhost:8080
+
+
+# Stopping gzweb server
+
+    ./stop_gzweb.sh
+
+# Build environment setup for javascript source
+
+## Dependencies
+
+Install grunt packages. From the `gzweb/gz3d/utils` directory, run:
+
+      npm install
+
+## Work Flow
+
+1. Make changes to javascript source code in `gzweb/gz3d`
+
+1. From the `gzweb` directory, run the following script, which will code check and minify javascript source files, and copy generated files to `gzweb/http`.
+
+        ./updateGZ3D.sh
+
+1. Verify your changes by starting gzweb server from the `gzweb` directory:
+
+        ./start_gzweb.sh
+
+1. Open browser to localhost:8080 or just refresh page.
diff --git a/cmake/cmake_uninstall.cmake.in b/cmake/cmake_uninstall.cmake.in
new file mode 100644
index 0000000000000000000000000000000000000000..b1aba065a69f7511ae87071eb240152a380bf845
--- /dev/null
+++ b/cmake/cmake_uninstall.cmake.in
@@ -0,0 +1,31 @@
+if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
+  message(FATAL_ERROR "Cannot find install manifest:
+                      "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt"")
+endif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
+
+if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/deps/libwebsockets/install_manifest.txt")
+  message(FATAL_ERROR "Cannot find install manifest:
+                      "@CMAKE_CURRENT_BINARY_DIR@/deps/libwebsockets/install_manifest.txt"")
+endif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/deps/libwebsockets/install_manifest.txt")
+
+file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files_root)
+file(READ "@CMAKE_CURRENT_BINARY_DIR@/deps/libwebsockets/install_manifest.txt" files_libwebsockets)
+
+LIST(APPEND files ${files_root} ${files_libwebsockets})
+
+string(REGEX REPLACE "\n" ";" files "${files}")
+foreach(file ${files})
+  message(STATUS "Uninstalling "$ENV{DESTDIR}${file}"")
+  if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
+    exec_program(
+      "@CMAKE_COMMAND@" ARGS "-E remove "$ENV{DESTDIR}${file}""
+      OUTPUT_VARIABLE rm_out
+      RETURN_VALUE rm_retval
+      )
+    if(NOT "${rm_retval}" STREQUAL 0)
+      message(FATAL_ERROR "Problem when removing "$ENV{DESTDIR}${file}"")
+    endif(NOT "${rm_retval}" STREQUAL 0)
+  else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
+    message(STATUS "File "$ENV{DESTDIR}${file}" does not exist.")
+  endif(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
+endforeach(file)
diff --git a/coarse_meshes.sh b/coarse_meshes.sh
new file mode 100755
index 0000000000000000000000000000000000000000..b2e83856b5dd784416ae4e874dc5373881f34c3d
--- /dev/null
+++ b/coarse_meshes.sh
@@ -0,0 +1,53 @@
+#!/bin/bash
+
+gzwebDir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+cd $gzwebDir
+
+gzcoarse=${gzwebDir}/build/tools/gzcoarse
+
+if [ ! -f "$gzcoarse" ]; then
+  echo "Error: gzcoarse executable not found, exiting."
+  exit
+fi
+
+echo "gzcoarse found: $gzcoarse."
+
+if [ -z "$1" ] || [ -z "$2" ]; then
+  echo "Error: Please specify percentage and the assets path as argument."
+  exit
+fi
+
+
+if [ ! -d "$2" ] && [ ! -f "$2" ]; then
+  echo "Error: $2 does not exist."
+fi
+
+percent="$1";
+echo "Simplify to $1%"
+
+assetDir="$2";
+echo "Assets directory: $2"
+
+echo "Removing old coarse dae files."
+
+# Delete coarse meshes if exist
+find $assetDir -type f -name "*_coarse.dae" -exec rm -f {} \;
+
+# Run gzcoarse on all dae meshes
+function coarseAllDae {
+  echo -e "\e[32mEntered \e[39m$2\e[32m directory.\e[39m"
+
+  # Coarsen all dae
+  for file in `find $assetDir -type f -name "*.dae"`; do
+    if [ -f "${file}" ]; then # if not a file, skip
+      echo -e "\e[33mCoarsening \e[39m$file"
+      $gzcoarse $file $percent
+    fi
+  done
+}
+
+echo "Simplifying meshes"
+coarseAllDae $1
+
+echo "Done!"
+
diff --git a/deploy.sh b/deploy.sh
new file mode 100755
index 0000000000000000000000000000000000000000..5a148aadd7a44b1f8e4e1a5834a7b9754ec22bda
--- /dev/null
+++ b/deploy.sh
@@ -0,0 +1,152 @@
+#!/bin/bash
+
+usage()
+{
+cat << EOF
+OPTIONS:
+   -h      Show this message
+   -m      Build a local model database. 
+           Option "local" to use only local models.
+   -c      Create coarse versions of all models in the local database 
+   -t      Generate a thumbnail for each model
+EOF
+exit
+}
+
+
+MODELS=
+LOCAL=
+COARSE=
+THUMBNAIL=
+GetOpts() 
+{
+  branch=""
+  argv=()
+  while [ $# -gt 0 ]
+  do
+    opt=$1
+    shift
+    case ${opt} in
+        -m)
+          MODELS=true
+          echo "Build a local model database."
+          if [ $# -eq 0 -o "${1:0:1}" = "-" ]
+          then
+            echo "Download from gazebo_models repository."
+          fi
+          if [[ "$1" == "local" ]]
+          then
+            LOCAL=true
+            echo "Only local models."
+            shift
+          fi
+          ;;
+        -c)
+          COARSE=true
+          echo "Simplify models on local database."
+          ;;
+        -t)
+          THUMBNAIL=true
+          echo "Thumbnails will be generated"
+          ;;
+        *)
+          usage
+          argv+=(${opt})
+          ;;
+    esac
+  done 
+}
+
+GetOpts $*
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+cd $DIR
+
+# Install node modules
+npm install
+
+#
+# build the c++ server component
+#
+rm -rf build
+mkdir build
+cd build
+
+
+# Run cmake and check for the exit code
+cmake ..
+
+RETVAL=$?
+if [ $RETVAL -ne 0 ]; then
+  echo There are cmake errors, exiting.
+  exit 1
+fi
+
+# continue building if cmake is happy
+make -j 8
+
+cd ../gzbridge
+$DIR/node_modules/.bin/node-gyp configure
+$DIR/node_modules/.bin/node-gyp build -d
+
+RETVAL=$?
+if [ $RETVAL -ne 0 ]; then
+  echo There are node-gyp build errors, exiting.
+  exit 1
+fi
+
+cd $DIR
+
+# build a local model database
+if [[ $MODELS ]]
+then
+  # Temporal directory for the repository
+    TMP_DIR=`mktemp -d`
+    cd $TMP_DIR
+
+  # If no arg given then download from gazebo_models repo
+  if [[ -z $LOCAL ]]
+  then
+    echo -n "Downloading gazebo_models..."
+    hg clone https://bitbucket.org/osrf/gazebo_models
+
+    echo "Download complete"
+    cd gazebo_models
+    mkdir build
+    cd build
+    echo -n "Installing gazebo_models..."
+    cmake .. -DCMAKE_INSTALL_PREFIX=$DIR/http/client
+    make install > /dev/null 2>&1
+    echo "Install complete"
+
+    # Remove temp dir
+    rm -rf $TMP_DIR
+    rm -rf $DIR/http/client/assets
+    mv $DIR/http/client/models $DIR/http/client/assets
+  fi
+
+  cd $DIR
+
+  echo "Gather all models on the local machine"
+
+  ./get_local_models.py $DIR/http/client/assets
+  ./webify_models_v2.py $DIR/http/client/assets
+
+else
+  echo "Not cloning the model repo"
+fi
+
+if [[ $MODELS ]] || [[ $THUMBNAIL ]]
+then
+  echo "Generating a thumbnail for each model. Make sure gazebo is not running"
+  ./tools/gzthumbnails.sh
+fi
+
+# build a local model database
+if [[ $COARSE ]]
+then
+  ./coarse_meshes.sh 50 http/client/assets/
+fi
+
+echo "Done"
+
diff --git a/get_local_models.py b/get_local_models.py
new file mode 100755
index 0000000000000000000000000000000000000000..c4e0ca39c95610d52d541fb2020ef427d211d625
--- /dev/null
+++ b/get_local_models.py
@@ -0,0 +1,73 @@
+#!/usr/bin/python
+from __future__ import print_function
+
+import sys
+import os
+import shutil
+import distutils.core
+
+def copy_models(src, dst):
+
+    list_dir = [os.path.join(src,x) for x in os.listdir(src)]
+    sub_dirs = [x for x in list_dir if os.path.isdir(x)]
+    for model_path in sub_dirs:
+        if 'model.config' in os.listdir(model_path):
+            # sys.stdout.write(" copying %s" % model_path)
+            print (".", end="")
+            dest_dir = os.path.join(dst, os.path.split(model_path)[1] )
+            if os.path.isdir(dest_dir):
+                shutil.rmtree(dest_dir)
+                print("overriding model %s" % model_path)
+            shutil.copytree(model_path, dest_dir)
+        else:
+            print (" %s ignored" % model_path)
+
+
+dest_dir = sys.argv[1]
+
+
+print("copying local models to %s" % dest_dir)
+
+unique_paths = None
+try:
+    gazebo_path =  os.environ['GAZEBO_MODEL_PATH'].split(':')
+
+    model_paths = [x for x in gazebo_path if os.path.isdir(x)]
+    unique_paths = list(set(model_paths))
+except:
+    print ("No local models.")
+#    exit(0)
+
+if unique_paths is not None:
+  for path in unique_paths:
+      print("\nmodel path: [%s]" % path)
+      copy_models(path, dest_dir)
+
+  print("local models transfered")
+
+
+print("copying local resources to %s" % dest_dir)
+
+def copy_resources(src, dst):
+    media_path = os.path.join(src, "media")
+    if os.path.exists(media_path) and os.path.isdir(media_path):
+      dest_dir = os.path.join(dst, "media")
+      distutils.dir_util.copy_tree(media_path, dest_dir)
+
+unique_paths = None
+try:
+    resource_path =  os.environ['GAZEBO_RESOURCE_PATH'].split(':')
+
+    resource_paths = [x for x in resource_path if os.path.isdir(x)]
+    unique_paths = list(set(resource_paths))
+except:
+    print ("No local resources")
+    exit(0)
+
+if unique_paths is not None:
+  for path in unique_paths:
+      print("\nresource path: [%s]" % path)
+      copy_resources(path, dest_dir)
+
+print("local resources transfered")
+
diff --git a/gz3d/CMakeLists.txt b/gz3d/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2a7d6ed6343a3175cb2c0356adeeb0a1d4af2174
--- /dev/null
+++ b/gz3d/CMakeLists.txt
@@ -0,0 +1,4 @@
+set (dest ${CMAKE_SOURCE_DIR}/http)
+set (files client)
+file(COPY ${files} DESTINATION ${dest})
+file(COPY build/gz3d.js DESTINATION ${dest}/client/ )
diff --git a/gz3d/build/gz3d.js b/gz3d/build/gz3d.js
new file mode 100644
index 0000000000000000000000000000000000000000..d404d1d4a57aaf6b7f744f190eba3e22675bcdba
--- /dev/null
+++ b/gz3d/build/gz3d.js
@@ -0,0 +1,8710 @@
+var GZ3D = GZ3D || {
+  REVISION : '1'
+};
+
+
+/*global $:false */
+/*global angular*/
+
+var guiEvents = new EventEmitter2({ verbose: true });
+
+var emUnits = function(value)
+    {
+      return value*parseFloat($('body').css('font-size'));
+    };
+
+var isTouchDevice = 'ontouchstart' in window || 'onmsgesturechange' in window;
+
+var isWideScreen = function()
+    {
+      return $(window).width() / emUnits(1) > 35;
+    };
+var isTallScreen = function()
+    {
+      return $(window).height() / emUnits(1) > 35;
+    };
+var lastOpenMenu = {mainMenu: 'mainMenu', insertMenu: 'insertMenu',
+    treeMenu: 'treeMenu'};
+
+var tabColors = {selected: 'rgb(34, 170, 221)', unselected: 'rgb(42, 42, 42)'};
+
+var modelList =
+  [
+    {path:'buildings', title:'Buildings',
+    examplePath1:'fast_food', examplePath2:'kitchen_dining', examplePath3:'house_1', models:
+    [
+      {modelPath:'fast_food', modelTitle:'Fast Food'},
+      {modelPath:'gas_station', modelTitle:'Gas Station'},
+      {modelPath:'house_1', modelTitle:'House 1'},
+      {modelPath:'house_2', modelTitle:'House 2'},
+      {modelPath:'house_3', modelTitle:'House 3'},
+      {modelPath:'iss', modelTitle:'International Space Station'},
+      {modelPath:'iss_half', modelTitle:'ISS half'},
+      {modelPath:'kitchen_dining', modelTitle:'Kitchen and Dining'},
+      {modelPath:'office_building', modelTitle:'Office Building'},
+      {modelPath:'powerplant', modelTitle:'Power Plant'},
+      {modelPath:'starting_pen', modelTitle:'Starting Pen'},
+      {modelPath:'willowgarage', modelTitle:'Willow Garage'}
+    ]},
+
+    {path:'furniture', title:'Furniture',
+    examplePath1:'hinged_door', examplePath2:'bookshelf', examplePath3:'table', models:
+    [
+      {modelPath:'bookshelf', modelTitle:'Book Shelf'},
+      {modelPath:'cabinet', modelTitle:'Cabinet'},
+      {modelPath:'drc_practice_door_4x8', modelTitle:'4x8 Doorway'},
+      {modelPath:'drc_practice_ladder', modelTitle:'Ladder'},
+      {modelPath:'hinged_door', modelTitle:'Hinged Door'},
+      {modelPath:'table', modelTitle:'Table'},
+      {modelPath:'table_marble', modelTitle:'Table Marble'},
+
+      {modelPath:'drc_practice_ball_valve', modelTitle:'Ball Valve'},
+      {modelPath:'drc_practice_handle_wheel_valve', modelTitle:'Handle Wheel Valve'},
+      {modelPath:'drc_practice_hand_wheel_valve', modelTitle:'Hand Wheel Valve'},
+      {modelPath:'drc_practice_wheel_valve', modelTitle:'Wheel Valve'},
+      {modelPath:'drc_practice_wheel_valve_large', modelTitle:'Wheel Valve Large'},
+      {modelPath:'door_handle', modelTitle:'Door Handle'},
+
+      {modelPath:'drc_practice_ball_valve_wall', modelTitle:'Wall (Ball Valve)'},
+      {modelPath:'drc_practice_handle_wheel_valve_wall', modelTitle:'Wall (Handle Wheel Valve)'},
+      {modelPath:'drc_practice_hand_wheel_valve_wall', modelTitle:'Wall (Hand Wheel Valve)'},
+      {modelPath:'drc_practice_valve_wall', modelTitle:'Wall (Valve)'},
+      {modelPath:'drc_practice_wheel_valve_wall', modelTitle:'Wall (Wheel Valve)'},
+      {modelPath:'drc_practice_wheel_valve_large_wall', modelTitle:'Wall (Wheel Valve Large)'},
+      {modelPath:'grey_wall', modelTitle:'Grey Wall'},
+      {modelPath:'asphalt_plane', modelTitle:'Asphalt Plane'},
+      {modelPath:'drc_practice_base_4x8', modelTitle:'Debris base'},
+      {modelPath:'ground_plane', modelTitle:'Ground Plane'},
+      {modelPath:'nist_maze_wall_120', modelTitle:'120 Maze Wall'},
+      {modelPath:'nist_maze_wall_240', modelTitle:'240 Maze Wall'},
+      {modelPath:'nist_maze_wall_triple_holes_120', modelTitle:'120 Maze Wall Triple Holes'},
+      {modelPath:'nist_simple_ramp_120', modelTitle:'Simple Ramp'},
+      {modelPath:'nist_stairs_120', modelTitle:'Stairs'}
+    ]},
+
+    {path:'kitchen', title:'Kitchen',
+    examplePath1:'saucepan',  examplePath2:'beer',  examplePath3:'bowl', models:
+    [
+      {modelPath:'beer', modelTitle:'Beer'},
+      {modelPath:'bowl', modelTitle:'Bowl'},
+      {modelPath:'coke_can', modelTitle:'Coke Can'},
+      {modelPath:'saucepan', modelTitle:'Saucepan'}
+    ]},
+
+    {path:'robocup', title:'Robocup', examplePath1:'robocup_3Dsim_ball',
+    examplePath2:'robocup14_spl_goal', examplePath3:'robocup09_spl_field', models:
+    [
+      {modelPath:'robocup09_spl_field', modelTitle:'2009 SPL Field'},
+      {modelPath:'robocup14_spl_field', modelTitle:'2014 SPL Field'},
+      {modelPath:'robocup_3Dsim_field', modelTitle:'3D Sim. Field'},
+      {modelPath:'robocup14_spl_goal', modelTitle:'SPL Goal'},
+      {modelPath:'robocup_3Dsim_goal', modelTitle:'3D Sim. Goal'},
+      {modelPath:'robocup_spl_ball', modelTitle:'SPL Ball'},
+      {modelPath:'robocup_3Dsim_ball', modelTitle:'3D Sim. Ball'}
+    ]},
+
+    {path:'robots', title:'Robots',
+    examplePath1:'pioneer3at', examplePath2:'turtlebot', examplePath3:'pr2', models:
+    [
+      {modelPath:'create', modelTitle:'Create'},
+      {modelPath:'husky', modelTitle:'Husky'},
+      {modelPath:'irobot_hand', modelTitle:'iRobot Hand'},
+      {modelPath:'pioneer2dx', modelTitle:'Pioneer 2DX'},
+      {modelPath:'pioneer3at', modelTitle:'Pioneer 3AT'},
+      {modelPath:'pr2', modelTitle:'PR2'},
+      {modelPath:'robonaut', modelTitle:'Robonaut'},
+      {modelPath:'simple_arm', modelTitle:'Simple Arm'},
+      {modelPath:'simple_arm_gripper', modelTitle:'Simple Arm and Gripper'},
+      {modelPath:'simple_gripper', modelTitle:'Simple Gripper'},
+      {modelPath:'turtlebot', modelTitle:'TurtleBot'},
+      {modelPath:'youbot', modelTitle:'YouBot'}
+    ]},
+
+    {path:'sensors', title:'Sensors',
+    examplePath1:'camera', examplePath2:'hokuyo', examplePath3:'kinect', models:
+    [
+      {modelPath:'camera', modelTitle:'Camera'},
+      {modelPath:'stereo_camera', modelTitle:'Stereo Camera'},
+      {modelPath:'hokuyo', modelTitle:'Hokuyo'},
+      {modelPath:'kinect', modelTitle:'Kinect'}
+    ]},
+
+    {path:'street', title:'Street', examplePath1:'dumpster',
+    examplePath2:'drc_practice_angled_barrier_45', examplePath3:'fire_hydrant', models:
+    [
+      {modelPath:'cinder_block', modelTitle:'Cinder Block'},
+      {modelPath:'cinder_block_2', modelTitle:'Cinder Block 2'},
+      {modelPath:'cinder_block_wide', modelTitle:'Cinder Block Wide'},
+      {modelPath:'construction_barrel', modelTitle:'Construction Barrel'},
+      {modelPath:'construction_cone', modelTitle:'Construction Cone'},
+      {modelPath:'drc_practice_angled_barrier_45', modelTitle:'Angled Barrier 45'},
+      {modelPath:'drc_practice_angled_barrier_135', modelTitle:'Angled Barrier 135'},
+      {modelPath:'drc_practice_block_wall', modelTitle:'Block Wall'},
+      {modelPath:'drc_practice_orange_jersey_barrier', modelTitle:'Jersey Barrier (Orange)'},
+      {modelPath:'drc_practice_white_jersey_barrier', modelTitle:'Jersey Barrier (White)'},
+      {modelPath:'drc_practice_truss', modelTitle:'Truss'},
+      {modelPath:'drc_practice_yellow_parking_block', modelTitle:'Parking Block'},
+      {modelPath:'dumpster', modelTitle:'Dumpster'},
+      {modelPath:'fire_hydrant', modelTitle:'Fire Hydrant'},
+      {modelPath:'jersey_barrier', modelTitle:'Jersey Barrier'},
+      {modelPath:'lamp_post', modelTitle:'Lamp Post'},
+      {modelPath:'mailbox', modelTitle:'Mailbox'},
+      {modelPath:'mud_box', modelTitle:'Mud Box'},
+      {modelPath:'nist_fiducial_barrel', modelTitle:'Fiducial Barrel'},
+      {modelPath:'speed_limit_sign', modelTitle:'Speed Limit Sign'},
+      {modelPath:'stop_sign', modelTitle:'Stop Sign'}
+
+    ]},
+
+    {path:'tools', title:'Tools', examplePath1:'hammer',
+    examplePath2:'polaris_ranger_ev', examplePath3:'cordless_drill', models:
+    [
+      {modelPath:'cordless_drill', modelTitle:'Cordless Drill'},
+      {modelPath:'fire_hose_long', modelTitle:'Fire Hose'},
+      {modelPath:'fire_hose_long_curled', modelTitle:'Fire Hose Long Curled'},
+      {modelPath:'hammer', modelTitle:'Hammer'},
+      {modelPath:'monkey_wrench', modelTitle:'Monkey Wrench'},
+      {modelPath:'polaris_ranger_ev', modelTitle:'Polaris Ranger EV'},
+      {modelPath:'polaris_ranger_xp900', modelTitle:'Polaris Ranger XP900'},
+      {modelPath:'polaris_ranger_xp900_no_roll_cage', modelTitle:'Polaris Ranger without roll cage'},
+      {modelPath:'utility_cart', modelTitle:'Utility Cart'}
+    ]},
+
+    {path:'misc', title:'Misc.', examplePath1:'brick_box_3x1x3',
+    examplePath2:'drc_practice_4x4x20', examplePath3:'double_pendulum_with_base', models:
+    [
+      {modelPath:'double_pendulum_with_base', modelTitle:'Double Pendulum With Base'},
+      {modelPath:'breakable_test', modelTitle:'Breakable_test'},
+      {modelPath:'brick_box_3x1x3', modelTitle:'Brick Box 3x1x3'},
+      {modelPath:'cube_20k', modelTitle:'Cube 20k'},
+      {modelPath:'drc_practice_2x4', modelTitle:'2x4 Lumber'},
+      {modelPath:'drc_practice_2x6', modelTitle:'2x6 Lumber'},
+      {modelPath:'drc_practice_4x4x20', modelTitle:'4x4x20 Lumber'},
+      {modelPath:'drc_practice_4x4x40', modelTitle:'4x4x40 Lumber'},
+      {modelPath:'drc_practice_blue_cylinder', modelTitle:'Blue Cylinder'},
+      {modelPath:'drc_practice_wood_slats', modelTitle:'Wood Slats'},
+      {modelPath:'nist_elevated_floor_120', modelTitle:'Elevated Floor 120'}
+    ]}
+  ];
+
+$(function()
+{
+  //Initialize
+  if ('ontouchstart' in window || 'onmsgesturechange' in window)
+  {
+    $('body').addClass('isTouchDevice');
+  }
+
+  // Toggle items
+  $('#view-collisions').buttonMarkup({icon: 'false'});
+  $('#snap-to-grid').buttonMarkup({icon: 'false'});
+  $('#open-tree-when-selected').buttonMarkup({icon: 'false'});
+  $('#view-transparent').buttonMarkup({icon: 'false'});
+  $('#view-wireframe').buttonMarkup({icon: 'false'});
+  $('#view-joints').buttonMarkup({icon: 'false'});
+  guiEvents.emit('toggle_notifications');
+  guiEvents.emit('show_orbit_indicator');
+
+  $( '#clock-touch' ).popup('option', 'arrow', 't');
+  $('#notification-popup-screen').remove();
+  $('.tab').css('border-left-color', tabColors.unselected);
+
+  if (isWideScreen())
+  {
+    guiEvents.emit('openTab', 'mainMenu', 'mainMenu');
+  }
+
+  if (isTallScreen())
+  {
+    $('.collapsible_header').click();
+    $('#expand-MODELS').click();
+    $('#expand-LIGHTS').click();
+  }
+
+  // Touch devices
+  if (isTouchDevice)
+  {
+    $('.mouse-only')
+        .css('display','none');
+
+    $('#play-header-fieldset')
+        .css('position', 'absolute')
+        .css('right', '13.6em')
+        .css('top', '0em')
+        .css('z-index', '1000');
+
+    $('#clock-header-fieldset')
+        .css('position', 'absolute')
+        .css('right', '10.2em')
+        .css('top', '0em')
+        .css('z-index', '1000');
+
+    $('#mode-header-fieldset')
+        .css('position', 'absolute')
+        .css('right', '0.5em')
+        .css('top', '0.15em')
+        .css('z-index', '1000');
+
+    $('.gzGUI').touchstart(function(event){
+        guiEvents.emit('pointerOnMenu');
+    });
+
+    $('.gzGUI').touchend(function(event){
+        guiEvents.emit('pointerOffMenu');
+    });
+
+    // long press on canvas
+    var press_time_container = 400;
+    $('#container')
+      .on('touchstart', function (event) {
+        $(this).data('checkdown', setTimeout(function () {
+          guiEvents.emit('longpress_container_start',event);
+        }, press_time_container));
+      })
+      .on('touchend', function (event) {
+        clearTimeout($(this).data('checkdown'));
+        guiEvents.emit('longpress_container_end',event,false);
+      })
+      .on('touchmove', function (event) {
+        clearTimeout($(this).data('checkdown'));
+        $(this).data('checkdown', setTimeout(function () {
+          guiEvents.emit('longpress_container_start',event);
+        }, press_time_container));
+        guiEvents.emit('longpress_container_move',event);
+      });
+
+    // long press on insert menu item
+    var press_time_insert = 400;
+    $('[id^="insert-entity-"]')
+      .on('touchstart', function (event) {
+        var path = $(this).attr('id');
+        path = path.substring(14); // after 'insert-entity-'
+        $(this).data('checkdown', setTimeout(function () {
+          guiEvents.emit('longpress_insert_start', event, path);
+        }, press_time_insert));
+      })
+      .on('touchend', function (event) {
+        clearTimeout($(this).data('checkdown'));
+        guiEvents.emit('longpress_insert_end',event,false);
+      })
+      .on('touchmove', function (event) {
+        clearTimeout($(this).data('checkdown'));
+        guiEvents.emit('longpress_insert_move',event);
+      });
+  }
+  // Mouse devices
+  else
+  {
+    $('.touch-only')
+        .css('display','none');
+
+    $('[id^="insert-entity-"]')
+      .click(function(event) {
+        var path = $(this).attr('id');
+        path = path.substring(14); // after 'insert-entity-'
+        guiEvents.emit('spawn_entity_start', path);
+      })
+      .on('mousedown', function(event) {
+        event.preventDefault();
+      });
+
+    $('#play-header-fieldset')
+        .css('position', 'absolute')
+        .css('right', '41.2em')
+        .css('top', '0em')
+        .css('z-index', '1000');
+
+    $('#clock-mouse')
+        .css('position', 'absolute')
+        .css('right', '29.0em')
+        .css('top', '0.5em')
+        .css('z-index', '100')
+        .css('width', '11em')
+        .css('height', '2.5em')
+        .css('background-color', '#333333')
+        .css('padding', '3px')
+        .css('border-radius', '5px');
+
+    $('#mode-header-fieldset')
+        .css('position', 'absolute')
+        .css('right', '24.4em')
+        .css('top', '0.15em')
+        .css('z-index', '1000');
+
+    $('#box-header-fieldset')
+        .css('position', 'absolute')
+        .css('right', '15.5em')
+        .css('top', '0em')
+        .css('z-index', '1000');
+
+    $('#sphere-header-fieldset')
+        .css('position', 'absolute')
+        .css('right', '12.5em')
+        .css('top', '0em')
+        .css('z-index', '1000');
+
+    $('#cylinder-header-fieldset')
+        .css('position', 'absolute')
+        .css('right', '9.5em')
+        .css('top', '0em')
+        .css('z-index', '1000');
+
+    $('#pointlight-header-fieldset')
+        .css('position', 'absolute')
+        .css('right', '6.5em')
+        .css('top', '0em')
+        .css('z-index', '1000');
+
+    $('#spotlight-header-fieldset')
+        .css('position', 'absolute')
+        .css('right', '3.5em')
+        .css('top', '0em')
+        .css('z-index', '1000');
+
+    $('#directionallight-header-fieldset')
+        .css('position', 'absolute')
+        .css('right', '0.5em')
+        .css('top', '0em')
+        .css('z-index', '1000');
+
+    $('.gzGUI').mouseenter(function(event){
+        guiEvents.emit('pointerOnMenu');
+    });
+
+    $('.gzGUI').mouseleave(function(event){
+        guiEvents.emit('pointerOffMenu');
+    });
+
+    // right-click
+    $('#container').mousedown(function(event)
+        {
+          event.preventDefault();
+          if(event.which === 3)
+          {
+            guiEvents.emit('right_click', event);
+          }
+        });
+
+    $('#model-popup-screen').mousedown(function(event)
+        {
+          $('#model-popup').popup('close');
+        });
+  }
+
+  $('.tab').click(function()
+      {
+        var idTab = $(this).attr('id');
+        var idMenu = idTab.substring(0,idTab.indexOf('Tab'));
+
+        if($('#'+idTab).css('border-left-color') === tabColors.unselected)
+        {
+          guiEvents.emit('openTab', lastOpenMenu[idMenu], idMenu);
+        }
+        else
+        {
+          guiEvents.emit('closeTabs', true);
+        }
+      });
+
+  $('.closePanels').click(function()
+      {
+        guiEvents.emit('closeTabs', true);
+      });
+
+  $('#view-mode').click(function()
+      {
+        guiEvents.emit('manipulation_mode', 'view');
+      });
+  $('#translate-mode').click(function()
+      {
+        guiEvents.emit('manipulation_mode', 'translate');
+      });
+  $('#rotate-mode').click(function()
+      {
+        guiEvents.emit('manipulation_mode', 'rotate');
+      });
+
+  $('[id^="header-insert-"]').click(function()
+      {
+        var entity = $(this).attr('id');
+        entity = entity.substring(14); // after 'header-insert-'
+        guiEvents.emit('closeTabs', false);
+        guiEvents.emit('spawn_entity_start', entity);
+      });
+
+  $('#play').click(function()
+      {
+        if ( $('#playText').html().indexOf('Play') !== -1 )
+        {
+          guiEvents.emit('pause', false);
+          guiEvents.emit('notification_popup','Physics engine running');
+        }
+        else
+        {
+          guiEvents.emit('pause', true);
+          guiEvents.emit('notification_popup','Physics engine paused');
+        }
+      });
+  $('#clock').click(function()
+      {
+        if ($.mobile.activePage.find('#clock-touch').parent().
+            hasClass('ui-popup-active'))
+        {
+          $( '#clock-touch' ).popup('close');
+        }
+        else
+        {
+          var position = $('#clock').offset();
+          $('#notification-popup').popup('close');
+          $('#clock-touch').popup('open', {
+              x:position.left+emUnits(1.6),
+              y:emUnits(4)});
+        }
+      });
+
+  $('#reset-model').click(function()
+      {
+        guiEvents.emit('model_reset');
+        guiEvents.emit('closeTabs', false);
+      });
+  $('#reset-world').click(function()
+      {
+        guiEvents.emit('world_reset');
+        guiEvents.emit('closeTabs', false);
+      });
+  $('#reset-view').click(function()
+      {
+        guiEvents.emit('view_reset');
+        guiEvents.emit('closeTabs', false);
+      });
+  $('#view-grid').click(function()
+      {
+        guiEvents.emit('show_grid', 'toggle');
+        guiEvents.emit('closeTabs', false);
+      });
+  $('#view-collisions').click(function()
+      {
+        guiEvents.emit('show_collision');
+        guiEvents.emit('closeTabs', false);
+      });
+  $('#view-orbit-indicator').click(function()
+      {
+        guiEvents.emit('show_orbit_indicator');
+        guiEvents.emit('closeTabs', false);
+      });
+  $( '#snap-to-grid' ).click(function() {
+    guiEvents.emit('snap_to_grid');
+    guiEvents.emit('closeTabs', false);
+  });
+  $( '#open-tree-when-selected' ).click(function() {
+    guiEvents.emit('openTreeWhenSelected');
+    guiEvents.emit('closeTabs', false);
+  });
+  $( '#toggle-notifications' ).click(function() {
+    guiEvents.emit('toggle_notifications');
+    guiEvents.emit('closeTabs', false);
+  });
+
+  // Disable Esc key to close panel
+  $('body').on('keyup', function(event)
+      {
+        if (event.which === 27)
+        {
+          return false;
+        }
+      });
+
+  // Object menu
+  $( '#view-transparent' ).click(function() {
+    $('#model-popup').popup('close');
+    guiEvents.emit('set_view_as','transparent');
+  });
+
+  $( '#view-wireframe' ).click(function() {
+    $('#model-popup').popup('close');
+    guiEvents.emit('set_view_as','wireframe');
+  });
+
+  $( '#view-joints' ).click(function() {
+    if ($('#view-joints a').css('color') === 'rgb(255, 255, 255)')
+    {
+      $('#model-popup').popup('close');
+      guiEvents.emit('view_joints');
+    }
+  });
+
+  $( '#delete-entity' ).click(function() {
+    guiEvents.emit('delete_entity');
+  });
+
+  $(window).resize(function()
+  {
+    guiEvents.emit('resizePanel');
+  });
+});
+
+function getNameFromPath(path)
+{
+  if(path === 'box')
+  {
+    return 'Box';
+  }
+  if(path === 'sphere')
+  {
+    return 'Sphere';
+  }
+  if(path === 'cylinder')
+  {
+    return 'Cylinder';
+  }
+  if(path === 'pointlight')
+  {
+    return 'Point Light';
+  }
+  if(path === 'spotlight')
+  {
+    return 'Spot Light';
+  }
+  if(path === 'directionallight')
+  {
+    return 'Directional Light';
+  }
+
+  for(var i = 0; i < modelList.length; ++i)
+  {
+    for(var j = 0; j < modelList[i].models.length; ++j)
+    {
+      if(modelList[i].models[j].modelPath === path)
+      {
+        return modelList[i].models[j].modelTitle;
+      }
+    }
+  }
+}
+
+// World tree
+var gzangular = angular.module('gzangular',[]);
+// add ng-right-click
+gzangular.directive('ngRightClick', function($parse)
+{
+  return function(scope, element, attrs)
+      {
+        var fn = $parse(attrs.ngRightClick);
+        element.bind('contextmenu', function(event)
+            {
+              scope.$apply(function()
+                  {
+                    event.preventDefault();
+                    fn(scope, {$event:event});
+                  });
+            });
+      };
+});
+
+gzangular.controller('treeControl', ['$scope', function($scope)
+{
+  $scope.updateStats = function()
+  {
+    $scope.models = modelStats;
+    $scope.lights = lightStats;
+    $scope.scene = sceneStats;
+    $scope.physics = physicsStats;
+    if (!$scope.$$phase)
+    {
+      $scope.$apply();
+    }
+  };
+
+  $scope.selectEntity = function (name)
+  {
+    $('#model-popup').popup('close');
+    guiEvents.emit('openTab', 'propertyPanel-'+name, 'treeMenu');
+    guiEvents.emit('selectEntity', name);
+  };
+
+  $scope.openEntityMenu = function (event, name)
+  {
+    $('#model-popup').popup('close');
+    guiEvents.emit('openEntityPopup', event, name);
+  };
+
+  $scope.openTab = function (tab)
+  {
+    guiEvents.emit('openTab', tab, 'treeMenu');
+  };
+
+  $scope.expandTree = function (tree)
+  {
+    var idContent = 'expandable-' + tree;
+    var idHeader = 'expand-' + tree;
+
+    if ($('#' + idContent).is(':visible'))
+    {
+      $('#' + idContent).hide();
+      $('#' + idHeader+' img').css('transform','rotate(0deg)')
+                              .css('-webkit-transform','rotate(0deg)')
+                              .css('-ms-transform','rotate(0deg)');
+    }
+    else
+    {
+      $('#' + idContent).show();
+      $('#' + idHeader+' img').css('transform','rotate(90deg)')
+                              .css('-webkit-transform','rotate(90deg)')
+                              .css('-ms-transform','rotate(90deg)');
+    }
+  };
+
+  $scope.expandProperty = function (prop, modelName, subPropShortName, subPropName, parentProp)
+  {
+    var idContent = 'expandable-' + prop + '-' + modelName;
+    var idHeader = 'expand-' + prop + '-' + modelName;
+
+    var idContentOthers, idHeaderOthers;
+
+    if (subPropShortName)
+    {
+      idContentOthers = idContent;
+      idHeaderOthers = idHeader;
+      idContent = idContent + '-' + subPropShortName;
+      idHeader = idHeader + '-' + subPropShortName;
+    }
+
+    if ($('#' + idContent).is(':visible'))
+    {
+      $('#' + idContent).hide();
+      $('#' + idHeader+' img').css('transform','rotate(0deg)')
+                              .css('-webkit-transform','rotate(0deg)')
+                              .css('-ms-transform','rotate(0deg)');
+    }
+    else
+    {
+      if (subPropShortName && (prop === 'link' || prop === 'joint'))
+      {
+        $('[id^="' + idContentOthers + '-"]').hide();
+        $('[id^="' + idHeaderOthers + '-"] img')
+            .css('transform','rotate(0deg)')
+            .css('-webkit-transform','rotate(0deg)')
+            .css('-ms-transform','rotate(0deg)');
+      }
+
+      $('#' + idContent).show();
+      $('#' + idHeader+' img').css('transform','rotate(90deg)')
+                              .css('-webkit-transform','rotate(90deg)')
+                              .css('-ms-transform','rotate(90deg)');
+
+      if (prop === 'pose' && parentProp === 'link')
+      {
+        guiEvents.emit('setPoseStats', modelName, subPropName);
+      }
+    }
+  };
+
+  $scope.changePose = function(prop1, prop2, name, value)
+  {
+    guiEvents.emit('setPose', prop1, prop2, name, value);
+  };
+
+  $scope.changeLight = function(prop, name, value)
+  {
+    guiEvents.emit('setLight', prop, name, value);
+  };
+
+  $scope.toggleProperty = function(prop, entity, subEntity)
+  {
+    // only for links so far
+    guiEvents.emit('toggleProperty', prop, entity, subEntity);
+  };
+}]);
+
+// Insert menu
+gzangular.controller('insertControl', ['$scope', function($scope)
+{
+  $scope.categories = modelList;
+
+  $scope.spawnEntity = function(path)
+  {
+    guiEvents.emit('spawn_entity_start', path);
+  };
+
+  $scope.openTab = function (tab)
+  {
+    guiEvents.emit('openTab', tab, 'insertMenu');
+  };
+}]);
+
+
+/**
+ * Graphical user interface
+ * @constructor
+ * @param {GZ3D.Scene} scene - A scene to connect to
+ */
+GZ3D.Gui = function(scene)
+{
+  this.scene = scene;
+  this.domElement = scene.getDomElement();
+  this.init();
+  this.emitter = new EventEmitter2({verbose: true});
+  this.guiEvents = guiEvents;
+};
+
+/**
+ * Initialize GUI
+ */
+GZ3D.Gui.prototype.init = function()
+{
+  this.spawnState = null;
+  this.longPressContainerState = null;
+  this.showNotifications = false;
+  this.openTreeWhenSelected = false;
+
+  var that = this;
+
+  // On guiEvents, emitter events
+  guiEvents.on('manipulation_mode',
+      function(mode)
+      {
+        that.scene.setManipulationMode(mode);
+        var space = that.scene.modelManipulator.space;
+
+        if (mode === 'view')
+        {
+          guiEvents.emit('notification_popup', 'View mode');
+        }
+        else
+        {
+          guiEvents.emit('notification_popup',
+              mode.charAt(0).toUpperCase()+
+              mode.substring(1)+' mode in '+
+              space.charAt(0).toUpperCase()+
+              space.substring(1)+' space');
+        }
+      }
+  );
+
+  // Create temp model
+  guiEvents.on('spawn_entity_start', function(entity)
+      {
+        // manually trigger view mode
+        that.scene.setManipulationMode('view');
+        $('#view-mode').prop('checked', true);
+        $('input[type="radio"]').checkboxradio('refresh');
+
+        var name = getNameFromPath(entity);
+
+        that.spawnState = 'START';
+        that.scene.spawnModel.start(entity,function(obj)
+            {
+              that.emitter.emit('entityCreated', obj, entity);
+            });
+        guiEvents.emit('notification_popup',
+            'Place '+name+' at the desired position');
+      }
+  );
+
+  // Move temp model by touch
+  guiEvents.on('spawn_entity_move', function(event)
+      {
+        that.spawnState = 'MOVE';
+        that.scene.spawnModel.onTouchMove(event,false);
+      }
+  );
+  // Place temp model by touch
+  guiEvents.on('spawn_entity_end', function()
+      {
+        if (that.spawnState === 'MOVE')
+        {
+          that.scene.spawnModel.onTouchEnd();
+        }
+        that.spawnState = null;
+      }
+  );
+
+  guiEvents.on('world_reset', function()
+      {
+        that.emitter.emit('reset', 'world');
+        guiEvents.emit('notification_popup','Reset world');
+      }
+  );
+
+  guiEvents.on('model_reset', function()
+      {
+        that.emitter.emit('reset', 'model');
+        guiEvents.emit('notification_popup','Reset model poses');
+      }
+  );
+
+  guiEvents.on('view_reset', function()
+      {
+        that.scene.resetView();
+        guiEvents.emit('notification_popup','Reset view');
+      }
+  );
+
+  guiEvents.on('pause', function(paused)
+      {
+        that.emitter.emit('pause', paused);
+      }
+  );
+
+  guiEvents.on('show_collision', function()
+      {
+        that.scene.showCollision(!that.scene.showCollisions);
+        if(!that.scene.showCollisions)
+        {
+          $('#view-collisions').buttonMarkup({icon: 'false'});
+          guiEvents.emit('notification_popup','Hiding collisions');
+        }
+        else
+        {
+          $('#view-collisions').buttonMarkup({icon: 'check'});
+          guiEvents.emit('notification_popup','Viewing collisions');
+        }
+      }
+  );
+
+  guiEvents.on('show_grid', function(option)
+      {
+        if (option === 'show')
+        {
+          that.scene.grid.visible = true;
+        }
+        else if (option === 'hide')
+        {
+          that.scene.grid.visible = false;
+        }
+        else if (option === 'toggle')
+        {
+          that.scene.grid.visible = !that.scene.grid.visible;
+        }
+
+        if(!that.scene.grid.visible)
+        {
+          $('#view-grid').buttonMarkup({icon: 'false'});
+          guiEvents.emit('notification_popup','Hiding grid');
+        }
+        else
+        {
+          $('#view-grid').buttonMarkup({icon: 'check'});
+          guiEvents.emit('notification_popup','Viewing grid');
+        }
+      }
+  );
+
+   guiEvents.on('show_orbit_indicator', function()
+      {
+        that.scene.controls.showTargetIndicator =
+            !that.scene.controls.showTargetIndicator;
+        if(!that.scene.controls.showTargetIndicator)
+        {
+          $('#view-orbit-indicator').buttonMarkup({icon: 'false'});
+          guiEvents.emit('notification_popup','Hiding orbit indicator');
+        }
+        else
+        {
+          $('#view-orbit-indicator').buttonMarkup({icon: 'check'});
+          guiEvents.emit('notification_popup','Viewing orbit indicator');
+        }
+      }
+  );
+
+  guiEvents.on('snap_to_grid',
+      function ()
+      {
+        if(that.scene.modelManipulator.snapDist === null)
+        {
+          $('#snap-to-grid').buttonMarkup({icon: 'check'});
+          that.scene.modelManipulator.snapDist = 0.5;
+          that.scene.spawnModel.snapDist = that.scene.modelManipulator.snapDist;
+          guiEvents.emit('notification_popup','Snapping to grid');
+        }
+        else
+        {
+          $('#snap-to-grid').buttonMarkup({icon: 'false'});
+          that.scene.modelManipulator.snapDist = null;
+          that.scene.spawnModel.snapDist = null;
+          guiEvents.emit('notification_popup','Not snapping to grid');
+        }
+      }
+  );
+
+  guiEvents.on('openTreeWhenSelected', function ()
+      {
+        this.openTreeWhenSelected = !this.openTreeWhenSelected;
+        if(!this.openTreeWhenSelected)
+        {
+          $('#open-tree-when-selected').buttonMarkup({icon: 'false'});
+        }
+        else
+        {
+          $('#open-tree-when-selected').buttonMarkup({icon: 'check'});
+        }
+      }
+  );
+
+  guiEvents.on('toggle_notifications', function ()
+      {
+        this.showNotifications = !this.showNotifications;
+        if(!this.showNotifications)
+        {
+          $('#toggle-notifications').buttonMarkup({icon: 'false'});
+        }
+        else
+        {
+          $('#toggle-notifications').buttonMarkup({icon: 'check'});
+        }
+      }
+  );
+
+
+  guiEvents.on('longpress_container_start',
+      function (event)
+      {
+        if (event.originalEvent.touches.length !== 1 ||
+            that.scene.modelManipulator.hovered ||
+            that.scene.spawnModel.active)
+        {
+          guiEvents.emit('longpress_container_end', event.originalEvent,true);
+        }
+        else
+        {
+          that.scene.showRadialMenu(event);
+          that.longPressContainerState = 'START';
+        }
+      }
+  );
+
+  guiEvents.on('longpress_container_end', function(event,cancel)
+      {
+        if (that.longPressContainerState !== 'START')
+        {
+          that.longPressContainerState = 'END';
+          return;
+        }
+        that.longPressContainerState = 'END';
+        if (that.scene.radialMenu.showing)
+        {
+          if (cancel)
+          {
+            that.scene.radialMenu.hide(event);
+          }
+          else
+          {
+          that.scene.radialMenu.hide(event, function(type,entity)
+              {
+                if (type === 'delete')
+                {
+                  that.emitter.emit('deleteEntity',entity);
+                  that.scene.setManipulationMode('view');
+                  $( '#view-mode' ).prop('checked', true);
+                  $('input[type="radio"]').checkboxradio('refresh');
+                }
+                else if (type === 'translate')
+                {
+                  $('#translate-mode').click();
+                  $('input[type="radio"]').checkboxradio('refresh');
+                  that.scene.attachManipulator(entity,type);
+                }
+                else if (type === 'rotate')
+                {
+                  $( '#rotate-mode' ).click();
+                  $('input[type="radio"]').checkboxradio('refresh');
+                  that.scene.attachManipulator(entity,type);
+                }
+                else if (type === 'transparent')
+                {
+                  guiEvents.emit('set_view_as','transparent');
+                }
+                else if (type === 'wireframe')
+                {
+                  guiEvents.emit('set_view_as','wireframe');
+                }
+                else if (type === 'joints')
+                {
+                  that.scene.selectEntity(entity);
+                  guiEvents.emit('view_joints');
+                }
+
+              });
+          }
+        }
+      }
+  );
+
+  guiEvents.on('longpress_container_move', function(event)
+      {
+        if (event.originalEvent.touches.length !== 1)
+        {
+          guiEvents.emit('longpress_container_end',event.originalEvent,true);
+        }
+        else
+        {
+          if (that.longPressContainerState !== 'START')
+          {
+            return;
+          }
+          if (that.scene.radialMenu.showing)
+          {
+            that.scene.radialMenu.onLongPressMove(event);
+          }
+        }
+      }
+  );
+
+  guiEvents.on('longpress_insert_start', function (event, path)
+      {
+        navigator.vibrate(50);
+        guiEvents.emit('spawn_entity_start', path);
+        event.stopPropagation();
+      }
+  );
+
+  guiEvents.on('longpress_insert_end', function(event)
+      {
+        guiEvents.emit('spawn_entity_end');
+      }
+  );
+
+  guiEvents.on('longpress_insert_move', function(event)
+      {
+        guiEvents.emit('spawn_entity_move', event);
+        event.stopPropagation();
+      }
+  );
+
+  var notificationTimeout;
+  guiEvents.on('notification_popup',
+      function (notification, duration)
+      {
+        if (this.showNotifications)
+        {
+          clearTimeout(notificationTimeout);
+          $( '#notification-popup' ).popup('close');
+          $( '#notification-popup' ).html('&nbsp;'+notification+'&nbsp;');
+          $( '#notification-popup' ).popup('open', {
+              y:window.innerHeight-50});
+          
+          if (duration === undefined)
+          {
+            duration = 2000;
+          }
+          notificationTimeout = setTimeout(function()
+          {
+            $( '#notification-popup' ).popup('close');
+          }, duration);
+        }
+      }
+  );
+
+  guiEvents.on('right_click', function (event)
+      {
+        that.scene.onRightClick(event, function(entity)
+            {
+              that.openEntityPopup(event, entity);
+            });
+      }
+  );
+
+  guiEvents.on('set_view_as', function (viewAs)
+      {
+        that.scene.setViewAs(that.scene.selectedEntity, viewAs);
+      }
+  );
+
+  guiEvents.on('view_joints', function ()
+      {
+        that.scene.viewJoints(that.scene.selectedEntity);
+      }
+  );
+
+  guiEvents.on('delete_entity', function ()
+      {
+        that.emitter.emit('deleteEntity',that.scene.selectedEntity);
+        $('#model-popup').popup('close');
+        that.scene.selectEntity(null);
+      }
+  );
+
+  guiEvents.on('pointerOnMenu', function ()
+      {
+        that.scene.pointerOnMenu = true;
+      }
+  );
+
+  guiEvents.on('pointerOffMenu', function ()
+      {
+        that.scene.pointerOnMenu = false;
+      }
+  );
+
+  guiEvents.on('openTab', function (id, parentId)
+      {
+        lastOpenMenu[parentId] = id;
+
+        $('.leftPanels').hide();
+        $('#'+id).show();
+
+        $('.tab').css('border-left-color', tabColors.unselected);
+        $('#'+parentId+'Tab').css('border-left-color', tabColors.selected);
+
+        if (id.indexOf('propertyPanel-') >= 0)
+        {
+          var entityName = id.substring(id.indexOf('-')+1);
+          var object = that.scene.getByName(entityName);
+
+          var stats = {};
+          stats.name = entityName;
+
+          stats.pose = {};
+          stats.pose.position = {x: object.position.x,
+                                 y: object.position.y,
+                                 z: object.position.z};
+
+          stats.pose.orientation = {x: object.quaternion._x,
+                                    y: object.quaternion._y,
+                                    z: object.quaternion._z,
+                                    w: object.quaternion._w};
+        }
+
+        guiEvents.emit('resizePanel');
+      }
+  );
+
+  guiEvents.on('closeTabs', function (force)
+      {
+        // Close for narrow viewports, force to always close
+        if (force || !isWideScreen())
+        {
+          $('.leftPanels').hide();
+          $('.tab').css('left', '0em');
+          $('.tab').css('border-left-color', tabColors.unselected);
+        }
+      }
+  );
+
+  guiEvents.on('setTreeSelected', function (object)
+      {
+        for (var i = 0; i < modelStats.length; ++i)
+        {
+          if (modelStats[i].name === object)
+          {
+            modelStats[i].selected = 'selectedTreeItem';
+            if (this.openTreeWhenSelected)
+            {
+              guiEvents.emit('openTab', 'propertyPanel-'+object, 'treeMenu');
+            }
+          }
+          else
+          {
+            modelStats[i].selected = 'unselectedTreeItem';
+          }
+        }
+        for (i = 0; i < lightStats.length; ++i)
+        {
+          if (lightStats[i].name === object)
+          {
+            lightStats[i].selected = 'selectedTreeItem';
+            if (this.openTreeWhenSelected)
+            {
+              guiEvents.emit('openTab', 'propertyPanel-'+object, 'treeMenu');
+            }
+          }
+          else
+          {
+            lightStats[i].selected = 'unselectedTreeItem';
+          }
+        }
+        that.updateStats();
+      }
+  );
+
+  guiEvents.on('setTreeDeselected', function ()
+      {
+        for (var i = 0; i < modelStats.length; ++i)
+        {
+          modelStats[i].selected = 'unselectedTreeItem';
+        }
+        for (i = 0; i < lightStats.length; ++i)
+        {
+          lightStats[i].selected = 'unselectedTreeItem';
+        }
+        that.updateStats();
+      }
+  );
+
+  guiEvents.on('selectEntity', function (name)
+      {
+        var object = that.scene.getByName(name);
+        that.scene.selectEntity(object);
+      }
+  );
+
+  guiEvents.on('openEntityPopup', function (event, name)
+      {
+        if (!isTouchDevice)
+        {
+          var object = that.scene.getByName(name);
+          that.openEntityPopup(event, object);
+        }
+      }
+  );
+
+  guiEvents.on('setPoseStats', function (modelName, linkName)
+      {
+        var object;
+        if (linkName === undefined)
+        {
+          object = that.scene.getByName(modelName);
+        }
+        else
+        {
+          object = that.scene.getByName(linkName);
+        }
+
+        var stats = {};
+        stats.name = object.name;
+        stats.pose = {};
+        stats.pose.position = {x: object.position.x,
+                               y: object.position.y,
+                               z: object.position.z};
+        stats.pose.orientation = {x: object.quaternion._x,
+                                  y: object.quaternion._y,
+                                  z: object.quaternion._z,
+                                  w: object.quaternion._w};
+
+        if (object.children[0] instanceof THREE.Light)
+        {
+          that.setLightStats(stats, 'update');
+        }
+        else
+        {
+          that.setModelStats(stats, 'update');
+        }
+      }
+  );
+
+  guiEvents.on('resizePanel', function ()
+      {
+        if ($('.leftPanels').is(':visible'))
+        {
+          if (isWideScreen())
+          {
+            $('.tab').css('left', '23em');
+          }
+          else
+          {
+            $('.tab').css('left', '10.5em');
+          }
+        }
+
+        if ($('.propertyPanels').is(':visible'))
+        {
+          var maxWidth = $(window).width();
+          if (isWideScreen())
+          {
+            maxWidth = emUnits(23);
+          }
+
+          $('.propertyPanels').css('width', maxWidth);
+        }
+      }
+  );
+
+  guiEvents.on('setPose', function (prop1, prop2, name, value)
+      {
+        if (value === undefined)
+        {
+          return;
+        }
+
+        var entity = that.scene.getByName(name);
+        if (prop1 === 'orientation')
+        {
+          entity['rotation']['_'+prop2] = value;
+          entity['quaternion'].setFromEuler(entity['rotation']);
+        }
+        else
+        {
+          entity[prop1][prop2] = value;
+        }
+        entity.updateMatrixWorld();
+
+        if (entity.children[0] &&
+           (entity.children[0] instanceof THREE.SpotLight ||
+            entity.children[0] instanceof THREE.DirectionalLight))
+        {
+          var lightObj = entity.children[0];
+          var dir = new THREE.Vector3(0,0,0);
+          dir.copy(entity.direction);
+          entity.localToWorld(dir);
+          lightObj.target.position.copy(dir);
+        }
+
+        that.scene.emitter.emit('entityChanged', entity);
+      }
+  );
+
+  guiEvents.on('setLight', function (prop, name, value)
+      {
+        if (value === undefined)
+        {
+          return;
+        }
+
+        var entity = that.scene.getByName(name);
+        var lightObj = entity.children[0];
+        if (prop === 'diffuse')
+        {
+          lightObj.color = new THREE.Color(value);
+        }
+        else if (prop === 'specular')
+        {
+          entity.serverProperties.specular = new THREE.Color(value);
+        }
+        else if (prop === 'range')
+        {
+          lightObj.distance = value;
+        }
+        else if (prop === 'attenuation_constant')
+        {
+          entity.serverProperties.attenuation_constant = value;
+        }
+        else if (prop === 'attenuation_linear')
+        {
+          entity.serverProperties.attenuation_linear = value;
+          lightObj.intensity = lightObj.intensity/(1+value);
+        }
+        else if (prop === 'attenuation_quadratic')
+        {
+          entity.serverProperties.attenuation_quadratic = value;
+          lightObj.intensity = lightObj.intensity/(1+value);
+        }
+
+        // updating color too often, maybe only update when popup is closed
+        that.scene.emitter.emit('entityChanged', entity);
+      }
+  );
+
+  guiEvents.on('toggleProperty', function (prop, subEntityName)
+      {
+        var entity = that.scene.getByName(subEntityName);
+        entity.serverProperties[prop] = !entity.serverProperties[prop];
+
+        that.scene.emitter.emit('linkChanged', entity);
+      }
+  );
+};
+
+/**
+ * Play/pause simulation
+ * @param {boolean} paused
+ */
+GZ3D.Gui.prototype.setPaused = function(paused)
+{
+  if (paused)
+  {
+    $('#playText').html(
+        '<img style="height:1.2em" src="style/images/play.png" title="Play">');
+  }
+  else
+  {
+    $('#playText').html(
+        '<img style="height:1.2em" src="style/images/pause.png" title="Pause">'
+        );
+  }
+};
+
+/**
+ * Update displayed real time
+ * @param {string} realTime
+ */
+GZ3D.Gui.prototype.setRealTime = function(realTime)
+{
+  $('.real-time-value').text(realTime);
+};
+
+/**
+ * Update displayed simulation time
+ * @param {string} simTime
+ */
+GZ3D.Gui.prototype.setSimTime = function(simTime)
+{
+  $('.sim-time-value').text(simTime);
+};
+
+var sceneStats = {};
+/**
+ * Update scene stats on scene tree
+ * @param {} stats
+ */
+GZ3D.Gui.prototype.setSceneStats = function(stats)
+{
+  sceneStats['ambient'] = this.round(stats.ambient, true);
+  sceneStats['background'] = this.round(stats.background, true);
+};
+
+var physicsStats = {};
+/**
+ * Update physics stats on scene tree
+ * @param {} stats
+ */
+GZ3D.Gui.prototype.setPhysicsStats = function(stats)
+{
+  physicsStats = stats;
+  physicsStats['enable_physics'] = this.trueOrFalse(
+      physicsStats['enable_physics']);
+  physicsStats['max_step_size'] = this.round(
+      physicsStats['max_step_size'], false, 3);
+  physicsStats['gravity'] = this.round(
+      physicsStats['gravity'], false, 3);
+  physicsStats['sor'] = this.round(
+      physicsStats['sor'], false, 3);
+  physicsStats['cfm'] = this.round(
+      physicsStats['cfm'], false, 3);
+  physicsStats['erp'] = this.round(
+      physicsStats['erp'], false, 3);
+  physicsStats['contact_max_correcting_vel'] = this.round(
+      physicsStats['contact_max_correcting_vel'], false, 3);
+  physicsStats['contact_surface_layer'] = this.round(
+      physicsStats['contact_surface_layer'], false, 3);
+
+  this.updateStats();
+};
+
+var modelStats = [];
+/**
+ * Update model stats on property panel
+ * @param {} stats
+ * @param {} action: 'update' / 'delete'
+ */
+GZ3D.Gui.prototype.setModelStats = function(stats, action)
+{
+  var modelName = stats.name;
+  var linkShortName;
+
+  // if it's a link
+  if (stats.name.indexOf('::') >= 0)
+  {
+    modelName = stats.name.substring(0, stats.name.indexOf('::'));
+    linkShortName = stats.name.substring(stats.name.lastIndexOf('::')+2);
+  }
+
+  if (action === 'update')
+  {
+    var model = $.grep(modelStats, function(e)
+        {
+          return e.name === modelName;
+        });
+
+    var formatted;
+
+    // New model
+    if (model.length === 0)
+    {
+      var thumbnail = this.findModelThumbnail(modelName);
+
+      formatted = this.formatStats(stats);
+
+      modelStats.push(
+          {
+            name: modelName,
+            thumbnail: thumbnail,
+            selected: 'unselectedTreeItem',
+            is_static: this.trueOrFalse(stats.is_static),
+            position: formatted.pose.position,
+            orientation: formatted.pose.orientation,
+            links: [],
+            joints: []
+          });
+
+      var newModel = modelStats[modelStats.length-1];
+
+      // links
+      for (var l = 0; l < stats.link.length; ++l)
+      {
+        var shortName = stats.link[l].name.substring(
+            stats.link[l].name.lastIndexOf('::')+2);
+
+        formatted = this.formatStats(stats.link[l]);
+
+        newModel.links.push(
+            {
+              name: stats.link[l].name,
+              shortName: shortName,
+              self_collide: this.trueOrFalse(stats.link[l].self_collide),
+              gravity: this.trueOrFalse(stats.link[l].gravity),
+              kinematic: this.trueOrFalse(stats.link[l].kinematic),
+              canonical: this.trueOrFalse(stats.link[l].canonical),
+              position: formatted.pose.position,
+              orientation: formatted.pose.orientation,
+              inertial: formatted.inertial
+            });
+      }
+
+      // joints
+      for (var j = 0; j < stats.joint.length; ++j)
+      {
+        var jointShortName = stats.joint[j].name.substring(
+            stats.joint[j].name.lastIndexOf('::')+2);
+        var parentShortName = stats.joint[j].parent.substring(
+            stats.joint[j].parent.lastIndexOf('::')+2);
+        var childShortName = stats.joint[j].child.substring(
+            stats.joint[j].child.lastIndexOf('::')+2);
+
+        var type;
+        switch (stats.joint[j].type)
+        {
+          case 1:
+              type = 'Revolute';
+              break;
+          case 2:
+              type = 'Revolute2';
+              break;
+          case 3:
+              type = 'Prismatic';
+              break;
+          case 4:
+              type = 'Universal';
+              break;
+          case 5:
+              type = 'Ball';
+              break;
+          case 6:
+              type = 'Screw';
+              break;
+          case 7:
+              type = 'Gearbox';
+              break;
+          default:
+              type = 'Unknown';
+        }
+
+        formatted = this.formatStats(stats.joint[j]);
+
+        newModel.joints.push(
+            {
+              name: stats.joint[j].name,
+              shortName: jointShortName,
+              type: type,
+              parent: stats.joint[j].parent,
+              parentShortName: parentShortName,
+              child: stats.joint[j].child,
+              childShortName: childShortName,
+              position: formatted.pose.position,
+              orientation: formatted.pose.orientation,
+              axis1: formatted.axis1,
+              axis2: formatted.axis2
+            });
+      }
+    }
+    // Update existing model
+    else
+    {
+      var link;
+
+      if (stats.link && stats.link[0])
+      {
+        var LinkShortName = stats.link[0].name;
+
+        link = $.grep(model[0].links, function(e)
+            {
+              return e.shortName === LinkShortName;
+            });
+
+        if (link[0].self_collide)
+        {
+          link[0].self_collide = this.trueOrFalse(stats.link[0].self_collide);
+        }
+        if (link[0].gravity)
+        {
+          link[0].gravity = this.trueOrFalse(stats.link[0].gravity);
+        }
+        if (link[0].kinematic)
+        {
+          link[0].kinematic = this.trueOrFalse(stats.link[0].kinematic);
+        }
+      }
+
+      // Update pose stats only if they're being displayed and are not focused
+      if (!((linkShortName &&
+          !$('#expandable-pose-'+modelName+'-'+linkShortName).is(':visible'))||
+          (!linkShortName &&
+          !$('#expandable-pose-'+modelName).is(':visible'))||
+          $('#expandable-pose-'+modelName+' input').is(':focus')))
+      {
+
+        if (stats.position)
+        {
+          stats.pose = {};
+          stats.pose.position = stats.position;
+          stats.pose.orientation = stats.orientation;
+        }
+
+        if (stats.pose)
+        {
+          formatted = this.formatStats(stats);
+
+          if (linkShortName === undefined)
+          {
+            model[0].position = formatted.pose.position;
+            model[0].orientation = formatted.pose.orientation;
+          }
+          else
+          {
+            link = $.grep(model[0].links, function(e)
+              {
+                return e.shortName === linkShortName;
+              });
+
+            link[0].position = formatted.pose.position;
+            link[0].orientation = formatted.pose.orientation;
+          }
+        }
+      }
+    }
+  }
+  else if (action === 'delete')
+  {
+    this.deleteFromStats('model', modelName);
+  }
+
+  this.updateStats();
+};
+
+var lightStats = [];
+/**
+ * Update light stats on property panel
+ * @param {} stats
+ * @param {} action: 'update' / 'delete'
+ */
+GZ3D.Gui.prototype.setLightStats = function(stats, action)
+{
+  var name = stats.name;
+
+  if (action === 'update')
+  {
+    var light = $.grep(lightStats, function(e)
+        {
+          return e.name === name;
+        });
+
+    var formatted;
+
+    // New light
+    if (light.length === 0)
+    {
+      var type = stats.type;
+
+      var thumbnail;
+      switch(type)
+      {
+        case 2:
+            thumbnail = 'style/images/spotlight.png';
+            break;
+        case 3:
+            thumbnail = 'style/images/directionallight.png';
+            break;
+        default:
+            thumbnail = 'style/images/pointlight.png';
+      }
+
+      stats.attenuation = {constant: stats.attenuation_constant,
+                           linear: stats.attenuation_linear,
+                           quadratic: stats.attenuation_quadratic};
+
+      formatted = this.formatStats(stats);
+
+      var direction;
+      if (stats.direction)
+      {
+        direction = stats.direction;
+      }
+
+      lightStats.push(
+          {
+            name: name,
+            thumbnail: thumbnail,
+            selected: 'unselectedTreeItem',
+            position: formatted.pose.position,
+            orientation: formatted.pose.orientation,
+            diffuse: formatted.diffuse,
+            specular: formatted.specular,
+            color: formatted.color,
+            range: stats.range,
+            attenuation: this.round(stats.attenuation, false, null),
+            direction: direction
+          });
+    }
+    else
+    {
+      formatted = this.formatStats(stats);
+
+      if (stats.pose)
+      {
+        light[0].position = formatted.pose.position;
+        light[0].orientation = formatted.pose.orientation;
+      }
+
+      if (stats.diffuse)
+      {
+        light[0].diffuse = formatted.diffuse;
+      }
+
+      if (stats.specular)
+      {
+        light[0].specular = formatted.specular;
+      }
+    }
+  }
+  else if (action === 'delete')
+  {
+    this.deleteFromStats('light', name);
+  }
+
+  this.updateStats();
+};
+
+/**
+ * Find thumbnail
+ * @param {} instanceName
+ * @returns string
+ */
+GZ3D.Gui.prototype.findModelThumbnail = function(instanceName)
+{
+  for(var i = 0; i < modelList.length; ++i)
+  {
+    for(var j = 0; j < modelList[i].models.length; ++j)
+    {
+      var path = modelList[i].models[j].modelPath;
+      if(instanceName.indexOf(path) >= 0)
+      {
+        return '/assets/'+path+'/thumbnails/0.png';
+      }
+    }
+  }
+  if(instanceName.indexOf('box') >= 0)
+  {
+    return 'style/images/box.png';
+  }
+  if(instanceName.indexOf('sphere') >= 0)
+  {
+    return 'style/images/sphere.png';
+  }
+  if(instanceName.indexOf('cylinder') >= 0)
+  {
+    return 'style/images/cylinder.png';
+  }
+  return 'style/images/box.png';
+};
+
+/**
+ * Update model stats
+ */
+GZ3D.Gui.prototype.updateStats = function()
+{
+  var tree = angular.element($('#treeMenu')).scope();
+  tree.updateStats();
+};
+
+/**
+ * Open entity (model/light) context menu
+ * @param {} event
+ * @param {THREE.Object3D} entity
+ */
+GZ3D.Gui.prototype.openEntityPopup = function(event, entity)
+{
+  this.scene.selectEntity(entity);
+  $('.ui-popup').popup('close');
+
+  if (entity.children[0] instanceof THREE.Light)
+  {
+    $('#view-transparent').css('visibility','collapse');
+    $('#view-wireframe').css('visibility','collapse');
+    $('#view-joints').css('visibility','collapse');
+    $('#model-popup').popup('open',
+      {x: event.clientX + emUnits(6),
+       y: event.clientY + emUnits(-8)});
+  }
+  else
+  {
+    if (this.scene.selectedEntity.viewAs === 'transparent')
+    {
+      $('#view-transparent').buttonMarkup({icon: 'check'});
+    }
+    else
+    {
+      $('#view-transparent').buttonMarkup({icon: 'false'});
+    }
+
+    if (this.scene.selectedEntity.viewAs === 'wireframe')
+    {
+      $('#view-wireframe').buttonMarkup({icon: 'check'});
+    }
+    else
+    {
+      $('#view-wireframe').buttonMarkup({icon: 'false'});
+    }
+
+    if (entity.joint === undefined || entity.joint.length === 0)
+    {
+      $('#view-joints a').css('color', '#888888');
+      $('#view-joints').buttonMarkup({icon: 'false'});
+    }
+    else
+    {
+      $('#view-joints a').css('color', '#ffffff');
+      if (entity.getObjectByName('JOINT_VISUAL', true))
+      {
+        $('#view-joints').buttonMarkup({icon: 'check'});
+      }
+      else
+      {
+        $('#view-joints').buttonMarkup({icon: 'false'});
+      }
+    }
+
+    $('#view-transparent').css('visibility','visible');
+    $('#view-wireframe').css('visibility','visible');
+    $('#view-joints').css('visibility','visible');
+    $('#model-popup').popup('open',
+      {x: event.clientX + emUnits(6),
+       y: event.clientY + emUnits(0)});
+  }
+};
+
+/**
+ * Format stats message for proper display
+ * @param {} stats
+ * @returns {Object.<position, orientation, inertial, diffuse, specular, attenuation>}
+ */
+GZ3D.Gui.prototype.formatStats = function(stats)
+{
+  var position, orientation;
+  var quat, rpy;
+  if (stats.pose)
+  {
+    position = this.round(stats.pose.position, false, null);
+
+    quat = new THREE.Quaternion(stats.pose.orientation.x,
+        stats.pose.orientation.y, stats.pose.orientation.z,
+        stats.pose.orientation.w);
+
+    rpy = new THREE.Euler();
+    rpy.setFromQuaternion(quat);
+
+    orientation = {roll: rpy._x, pitch: rpy._y, yaw: rpy._z};
+    orientation = this.round(orientation, false, null);
+  }
+  var inertial;
+  if (stats.inertial)
+  {
+    inertial = this.round(stats.inertial, false, 3);
+
+    var inertialPose = stats.inertial.pose;
+    inertial.pose = {};
+
+    inertial.pose.position = {x: inertialPose.position.x,
+                              y: inertialPose.position.y,
+                              z: inertialPose.position.z};
+
+    inertial.pose.position = this.round(inertial.pose.position, false, 3);
+
+    quat = new THREE.Quaternion(inertialPose.orientation.x,
+        inertialPose.orientation.y, inertialPose.orientation.z,
+        inertialPose.orientation.w);
+
+    rpy = new THREE.Euler();
+    rpy.setFromQuaternion(quat);
+
+    inertial.pose.orientation = {roll: rpy._x, pitch: rpy._y, yaw: rpy._z};
+    inertial.pose.orientation = this.round(inertial.pose.orientation, false, 3);
+  }
+  var diffuse, colorHex, comp;
+  var color = {};
+  if (stats.diffuse)
+  {
+    diffuse = this.round(stats.diffuse, true);
+
+    colorHex = {};
+    for (comp in diffuse)
+    {
+      colorHex[comp] = diffuse[comp].toString(16);
+      if (colorHex[comp].length === 1)
+      {
+        colorHex[comp] = '0' + colorHex[comp];
+      }
+    }
+    color.diffuse = '#' + colorHex['r'] + colorHex['g'] + colorHex['b'];
+  }
+  var specular;
+  if (stats.specular)
+  {
+    specular = this.round(stats.specular, true);
+
+    colorHex = {};
+    for (comp in specular)
+    {
+      colorHex[comp] = specular[comp].toString(16);
+      if (colorHex[comp].length === 1)
+      {
+        colorHex[comp] = '0' + colorHex[comp];
+      }
+    }
+    color.specular = '#' + colorHex['r'] + colorHex['g'] + colorHex['b'];
+  }
+  var axis1;
+  if (stats.axis1)
+  {
+    axis1 = {};
+    axis1 = this.round(stats.axis1);
+    axis1.direction = this.round(stats.axis1.xyz, false, 3);
+  }
+  var axis2;
+  if (stats.axis2)
+  {
+    axis2 = {};
+    axis2 = this.round(stats.axis2);
+    axis2.direction = this.round(stats.axis2.xyz, false, 3);
+  }
+
+  return {pose: {position: position, orientation: orientation},
+          inertial: inertial,
+          diffuse: diffuse,
+          specular: specular,
+          color: color,
+          axis1: axis1,
+          axis2: axis2};
+};
+
+/**
+ * Round numbers and format colors
+ * @param {} stats
+ * @param {} decimals - number of decimals to display, null for input fields
+ * @returns result
+ */
+GZ3D.Gui.prototype.round = function(stats, isColor, decimals)
+{
+  var result = stats;
+  if (typeof result === 'number')
+  {
+    result = this.roundNumber(result, isColor, decimals);
+  }
+  else // array of numbers
+  {
+    result = this.roundArray(result, isColor, decimals);
+  }
+  return result;
+};
+
+/**
+ * Round number and format color
+ * @param {} stats
+ * @param {} decimals - number of decimals to display, null for input fields
+ * @returns result
+ */
+GZ3D.Gui.prototype.roundNumber = function(stats, isColor, decimals)
+{
+  var result = stats;
+  if (isColor)
+  {
+    result = Math.round(result * 255);
+  }
+  else
+  {
+    if (decimals === null)
+    {
+      result = Math.round(result*1000)/1000;
+    }
+    else
+    {
+      result = result.toFixed(decimals);
+    }
+  }
+  return result;
+};
+
+/**
+ * Round each number in an array
+ * @param {} stats
+ * @param {} decimals - number of decimals to display, null for input fields
+ * @returns result
+ */
+GZ3D.Gui.prototype.roundArray = function(stats, isColor, decimals)
+{
+  var result = stats;
+  for (var key in result)
+  {
+    if (typeof result[key] === 'number')
+    {
+      result[key] = this.roundNumber(result[key], isColor, decimals);
+    }
+  }
+  return result;
+};
+
+/**
+ * Format toggle items
+ * @param {} stats: true / false
+ * @returns {Object.<icon, title>}
+ */
+GZ3D.Gui.prototype.trueOrFalse = function(stats)
+{
+  return stats ?
+      {icon: 'true', title: 'True'} :
+      {icon: 'false', title: 'False'};
+};
+
+/**
+ * Delete an entity from stats list
+ * @param {} type: 'model' / 'light'
+ * @param {} name
+ */
+GZ3D.Gui.prototype.deleteFromStats = function(type, name)
+{
+  var list = (type === 'model') ? modelStats : lightStats;
+
+  for (var i = 0; i < list.length; ++i)
+  {
+    if (list[i].name === name)
+    {
+      if ($('#propertyPanel-'+name).is(':visible'))
+      {
+        guiEvents.emit('openTab', 'treeMenu', 'treeMenu');
+      }
+
+      list.splice(i, 1);
+      break;
+    }
+  }
+};
+
+
+//var GAZEBO_MODEL_DATABASE_URI='http://gazebosim.org/models';
+
+GZ3D.GZIface = function(scene, gui)
+{
+  this.scene = scene;
+  this.gui = gui;
+
+  this.isConnected = false;
+
+  this.emitter = new EventEmitter2({ verbose: true });
+
+  this.init();
+  this.visualsToAdd = [];
+
+  this.numConnectionTrials = 0;
+  this.maxConnectionTrials = 30; // try to connect 30 times
+  this.timeToSleepBtwTrials = 1000; // wait 1 second between connection trials
+};
+
+GZ3D.GZIface.prototype.init = function()
+{
+  this.material = [];
+  this.entityMaterial = {};
+
+  this.connect();
+};
+
+GZ3D.GZIface.prototype.connect = function()
+{
+  // connect to websocket
+  this.webSocket = new ROSLIB.Ros({
+    url : 'ws://' + location.hostname + ':7681'
+  });
+
+  var that = this;
+  this.webSocket.on('connection', function() {
+    that.onConnected();
+  });
+  this.webSocket.on('error', function() {
+    that.onError();
+  });
+
+  this.numConnectionTrials++;
+};
+
+GZ3D.GZIface.prototype.onError = function()
+{
+  // init scene and show popup only for the first connection error
+  if (this.numConnectionTrials === 1)
+  {
+    this.emitter.emit('error');
+  }
+
+  var that = this;
+  // retry to connect after certain time
+  if (this.numConnectionTrials < this.maxConnectionTrials)
+  {
+    setTimeout(function() {
+      that.connect();
+    }, this.timeToSleepBtwTrials);
+  }
+};
+
+GZ3D.GZIface.prototype.onConnected = function()
+{
+  this.isConnected = true;
+  this.emitter.emit('connection');
+
+  this.heartbeatTopic = new ROSLIB.Topic({
+    ros : this.webSocket,
+    name : '~/heartbeat',
+    messageType : 'heartbeat',
+  });
+
+  var that = this;
+  var publishHeartbeat = function()
+  {
+    var hearbeatMsg =
+    {
+      alive : 1
+    };
+    that.heartbeatTopic.publish(hearbeatMsg);
+  };
+
+  setInterval(publishHeartbeat, 5000);
+
+  var statusTopic = new ROSLIB.Topic({
+    ros: this.webSocket,
+    name: '~/status',
+    messageType : 'status',
+  });
+
+  var statusUpdate = function(message)
+  {
+    if (message.status === 'error')
+    {
+      that.isConnected = false;
+      this.emitter.emit('gzstatus', 'error');
+    }
+  };
+  statusTopic.subscribe(statusUpdate.bind(this));
+
+  var materialTopic = new ROSLIB.Topic({
+    ros : this.webSocket,
+    name : '~/material',
+    messageType : 'material',
+  });
+
+  var materialUpdate = function(message)
+  {
+    this.material = message;
+    this.emitter.emit('material', this.material);
+
+  };
+  materialTopic.subscribe(materialUpdate.bind(this));
+
+  this.sceneTopic = new ROSLIB.Topic({
+    ros : this.webSocket,
+    name : '~/scene',
+    messageType : 'scene',
+  });
+
+  var sceneUpdate = function(message)
+  {
+    if (message.name)
+    {
+      this.scene.name = message.name;
+    }
+
+    if (message.grid === true)
+    {
+      this.gui.guiEvents.emit('show_grid', 'show');
+    }
+
+    if (message.ambient)
+    {
+      var ambient = new THREE.Color();
+      ambient.r = message.ambient.r;
+      ambient.g = message.ambient.g;
+      ambient.b = message.ambient.b;
+
+      this.scene.ambient.color = ambient;
+    }
+
+    if (message.background)
+    {
+      var background = new THREE.Color();
+      background.r = message.background.r;
+      background.g = message.background.g;
+      background.b = message.background.b;
+
+      this.scene.renderer.clear();
+      this.scene.renderer.setClearColor(background, 1);
+    }
+
+    for (var i = 0; i < message.light.length; ++i)
+    {
+      var light = message.light[i];
+      var lightObj = this.createLightFromMsg(light);
+      this.scene.add(lightObj);
+      this.gui.setLightStats(light, 'update');
+    }
+
+    for (var j = 0; j < message.model.length; ++j)
+    {
+      var model = message.model[j];
+      var modelObj = this.createModelFromMsg(model);
+      this.scene.add(modelObj);
+      this.gui.setModelStats(model, 'update');
+    }
+
+    this.gui.setSceneStats(message);
+    this.sceneTopic.unsubscribe();
+  };
+  this.sceneTopic.subscribe(sceneUpdate.bind(this));
+
+  this.physicsTopic = new ROSLIB.Topic({
+    ros : this.webSocket,
+    name : '~/physics',
+    messageType : 'physics',
+  });
+
+  var physicsUpdate = function(message)
+  {
+    this.gui.setPhysicsStats(message);
+  };
+  this.physicsTopic.subscribe(physicsUpdate.bind(this));
+
+
+  // Update model pose
+  var poseTopic = new ROSLIB.Topic({
+    ros : this.webSocket,
+    name : '~/pose/info',
+    messageType : 'pose',
+  });
+
+  var poseUpdate = function(message)
+  {
+    var entity = this.scene.getByName(message.name);
+    if (entity && entity !== this.scene.modelManipulator.object
+        && entity.parent !== this.scene.modelManipulator.object)
+    {
+      this.scene.updatePose(entity, message.position, message.orientation);
+      this.gui.setModelStats(message, 'update');
+    }
+  };
+
+  poseTopic.subscribe(poseUpdate.bind(this));
+
+  // Requests - for deleting models
+  var requestTopic = new ROSLIB.Topic({
+    ros : this.webSocket,
+    name : '~/request',
+    messageType : 'request',
+  });
+
+  var requestUpdate = function(message)
+  {
+    if (message.request === 'entity_delete')
+    {
+      var entity = this.scene.getByName(message.data);
+      if (entity)
+      {
+        if (entity.children[0] instanceof THREE.Light)
+        {
+          this.gui.setLightStats({name: message.data}, 'delete');
+          guiEvents.emit('notification_popup', message.data+' deleted');
+        }
+        else
+        {
+          this.gui.setModelStats({name: message.data}, 'delete');
+          guiEvents.emit('notification_popup', message.data+' deleted');
+        }
+        this.scene.remove(entity);
+      }
+    }
+  };
+
+  requestTopic.subscribe(requestUpdate.bind(this));
+
+  // Model info messages - currently used for spawning new models
+  var modelInfoTopic = new ROSLIB.Topic({
+    ros : this.webSocket,
+    name : '~/model/info',
+    messageType : 'model',
+  });
+
+  var modelUpdate = function(message)
+  {
+    if (!this.scene.getByName(message.name))
+    {
+      var modelObj = this.createModelFromMsg(message);
+      if (modelObj)
+      {
+        this.scene.add(modelObj);
+        guiEvents.emit('notification_popup', message.name+' inserted');
+      }
+
+      // visuals may arrive out of order (before the model msg),
+      // add the visual in if we find its parent here
+      var len = this.visualsToAdd.length;
+      var i = 0;
+      var j = 0;
+      while (i < len)
+      {
+        var parentName = this.visualsToAdd[j].parent_name;
+        if (parentName.indexOf(modelObj.name) >=0)
+        {
+          var parent = this.scene.getByName(parentName);
+          var visualObj = this.createVisualFromMsg(this.visualsToAdd[j]);
+          parent.add(visualObj);
+          this.visualsToAdd.splice(j, 1);
+        }
+        else
+        {
+          j++;
+        }
+        i++;
+      }
+    }
+    this.gui.setModelStats(message, 'update');
+  };
+
+  modelInfoTopic.subscribe(modelUpdate.bind(this));
+
+  // Visual messages - currently just used for collision visuals
+  var visualTopic = new ROSLIB.Topic({
+    ros : this.webSocket,
+    name : '~/visual',
+    messageType : 'visual',
+  });
+
+  var visualUpdate = function(message)
+  {
+    if (!this.scene.getByName(message.name))
+    {
+      // accept only collision visual msgs for now
+      if (message.name.indexOf('COLLISION_VISUAL') < 0)
+      {
+        return;
+      }
+
+      // delay the add if parent not found, this array will checked in
+      // modelUpdate function
+      var parent = this.scene.getByName(message.parent_name);
+      if (message.parent_name && !parent)
+      {
+        this.visualsToAdd.push(message);
+      }
+      else
+      {
+        var visualObj = this.createVisualFromMsg(message);
+        parent.add(visualObj);
+      }
+    }
+  };
+
+  visualTopic.subscribe(visualUpdate.bind(this));
+
+  // world stats
+  var worldStatsTopic = new ROSLIB.Topic({
+    ros : this.webSocket,
+    name : '~/world_stats',
+    messageType : 'world_stats',
+  });
+
+  var worldStatsUpdate = function(message)
+  {
+    this.updateStatsGuiFromMsg(message);
+  };
+
+  worldStatsTopic.subscribe(worldStatsUpdate.bind(this));
+
+  // Lights
+  var lightTopic = new ROSLIB.Topic({
+    ros : this.webSocket,
+    name : '~/light',
+    messageType : 'light',
+  });
+
+  // equivalent to modelUpdate / poseUpdate
+  var lightUpdate = function(message)
+  {
+    var entity = this.scene.getByName(message.name);
+    if (!entity)
+    {
+      var lightObj = this.createLightFromMsg(message);
+      this.scene.add(lightObj);
+      guiEvents.emit('notification_popup', message.name+' inserted');
+    }
+    else if (entity && entity !== this.scene.modelManipulator.object
+        && entity.parent !== this.scene.modelManipulator.object)
+    {
+      this.scene.updateLight(entity, message);
+    }
+    this.gui.setLightStats(message, 'update');
+  };
+
+  lightTopic.subscribe(lightUpdate.bind(this));
+
+
+  // heightmap
+  this.heightmapDataService = new ROSLIB.Service({
+    ros : this.webSocket,
+    name : '~/heightmap_data',
+    serviceType : 'heightmap_data'
+  });
+
+  // road
+  this.roadService = new ROSLIB.Service({
+    ros : this.webSocket,
+    name : '~/roads',
+    serviceType : 'roads'
+  });
+
+  var request = new ROSLIB.ServiceRequest({
+      name : 'roads'
+  });
+
+  // send service request and load road on response
+  this.roadService.callService(request,
+  function(result)
+  {
+    var roadsObj = that.createRoadsFromMsg(result);
+    this.scene.add(roadsObj);
+  });
+
+  // Model modify messages - for modifying models
+  this.modelModifyTopic = new ROSLIB.Topic({
+    ros : this.webSocket,
+    name : '~/model/modify',
+    messageType : 'model',
+  });
+
+  // Light messages - for modifying lights
+  this.lightModifyTopic = new ROSLIB.Topic({
+    ros : this.webSocket,
+    name : '~/light',
+    messageType : 'light',
+  });
+
+  var publishEntityModify = function(entity)
+  {
+    var matrix = entity.matrixWorld;
+    var translation = new THREE.Vector3();
+    var quaternion = new THREE.Quaternion();
+    var scale = new THREE.Vector3();
+    matrix.decompose(translation, quaternion, scale);
+
+    var entityMsg =
+    {
+      name : entity.name,
+      id : entity.userData,
+      createEntity : 0,
+      position :
+      {
+        x : translation.x,
+        y : translation.y,
+        z : translation.z
+      },
+      orientation :
+      {
+        w: quaternion.w,
+        x: quaternion.x,
+        y: quaternion.y,
+        z: quaternion.z
+      }
+    };
+    if (entity.children[0] &&
+        entity.children[0] instanceof THREE.Light)
+    {
+      entityMsg.diffuse =
+      {
+        r: entity.children[0].color.r,
+        g: entity.children[0].color.g,
+        b: entity.children[0].color.b
+      };
+      entityMsg.specular =
+      {
+        r: entity.serverProperties.specular.r,
+        g: entity.serverProperties.specular.g,
+        b: entity.serverProperties.specular.b
+      };
+      entityMsg.direction = entity.direction;
+      entityMsg.range = entity.children[0].distance;
+      entityMsg.attenuation_constant = entity.serverProperties.attenuation_constant;
+      entityMsg.attenuation_linear = entity.serverProperties.attenuation_linear;
+      entityMsg.attenuation_quadratic = entity.serverProperties.attenuation_quadratic;
+
+      that.lightModifyTopic.publish(entityMsg);
+    }
+    else
+    {
+      that.modelModifyTopic.publish(entityMsg);
+    }
+  };
+
+  this.scene.emitter.on('entityChanged', publishEntityModify);
+
+  // Link messages - for modifying links
+  this.linkModifyTopic = new ROSLIB.Topic({
+    ros : this.webSocket,
+    name : '~/link',
+    messageType : 'link',
+  });
+
+  var publishLinkModify = function(entity, type)
+  {
+    var modelMsg =
+    {
+      name : entity.parent.name,
+      id : entity.parent.userData,
+      link:
+      {
+        name: entity.name,
+        id: entity.userData,
+        self_collide: entity.serverProperties.self_collide,
+        gravity: entity.serverProperties.gravity,
+        kinematic: entity.serverProperties.kinematic
+      }
+    };
+
+    that.linkModifyTopic.publish(modelMsg);
+  };
+
+  this.scene.emitter.on('linkChanged', publishLinkModify);
+
+  // Factory messages - for spawning new models
+  this.factoryTopic = new ROSLIB.Topic({
+    ros : this.webSocket,
+    name : '~/factory',
+    messageType : 'factory',
+  });
+
+  // Factory messages - for spawning new lights
+  this.lightFactoryTopic = new ROSLIB.Topic({
+    ros : this.webSocket,
+    name : '~/light',
+    messageType : 'light',
+  });
+
+  var publishFactory = function(model, type)
+  {
+    var matrix = model.matrixWorld;
+    var translation = new THREE.Vector3();
+    var quaternion = new THREE.Quaternion();
+    var scale = new THREE.Vector3();
+    matrix.decompose(translation, quaternion, scale);
+    var entityMsg =
+    {
+      name : model.name,
+      type : type,
+      createEntity : 1,
+      position :
+      {
+        x : translation.x,
+        y : translation.y,
+        z : translation.z
+      },
+      orientation :
+      {
+        w: quaternion.w,
+        x: quaternion.x,
+        y: quaternion.y,
+        z: quaternion.z
+      }
+    };
+    if (model.children[0].children[0] instanceof THREE.Light)
+    {
+      that.lightFactoryTopic.publish(entityMsg);
+    }
+    else
+    {
+      that.factoryTopic.publish(entityMsg);
+    }
+  };
+
+  // For deleting models
+  this.deleteTopic = new ROSLIB.Topic({
+    ros : this.webSocket,
+    name : '~/entity_delete',
+    messageType : 'entity_delete',
+  });
+
+  var publishDeleteEntity = function(entity)
+  {
+    var modelMsg =
+    {
+      name: entity.name
+    };
+
+    that.deleteTopic.publish(modelMsg);
+  };
+
+  this.gui.emitter.on('deleteEntity',
+      function(entity)
+      {
+        publishDeleteEntity(entity);
+      }
+  );
+
+  // World control messages - for resetting world/models
+  this.worldControlTopic = new ROSLIB.Topic({
+    ros : this.webSocket,
+    name : '~/world_control',
+    messageType : 'world_control',
+  });
+
+  var publishWorldControl = function(state, resetType)
+  {
+    var worldControlMsg = {};
+    if (state !== null)
+    {
+      worldControlMsg.pause = state;
+    }
+    if (resetType)
+    {
+      worldControlMsg.reset = resetType;
+    }
+    that.worldControlTopic.publish(worldControlMsg);
+  };
+
+  this.gui.emitter.on('entityCreated', publishFactory);
+
+  this.gui.emitter.on('reset',
+      function(resetType)
+      {
+        publishWorldControl(null, resetType);
+      }
+  );
+
+  this.gui.emitter.on('pause',
+      function(paused)
+      {
+        publishWorldControl(paused, null);
+      }
+  );
+};
+
+GZ3D.GZIface.prototype.updateStatsGuiFromMsg = function(stats)
+{
+  this.gui.setPaused(stats.paused);
+
+  var simSec = stats.sim_time.sec;
+  var simNSec = stats.sim_time.nsec;
+
+  var simDay = Math.floor(simSec / 86400);
+  simSec -= simDay * 86400;
+
+  var simHour = Math.floor(simSec / 3600);
+  simSec -= simHour * 3600;
+
+  var simMin = Math.floor(simSec / 60);
+  simSec -= simMin * 60;
+
+  var simMsec = Math.floor(simNSec * 1e-6);
+
+  var realSec = stats.real_time.sec;
+  var realNSec = stats.real_time.nsec;
+
+  var realDay = Math.floor(realSec / 86400);
+  realSec -= realDay * 86400;
+
+  var realHour = Math.floor(realSec / 3600);
+  realSec -= realHour * 3600;
+
+  var realMin = Math.floor(realSec / 60);
+  realSec -= realMin * 60;
+
+  var realMsec = Math.floor(realNSec * 1e-6);
+
+  var simTimeValue = '';
+  var realTimeValue = '';
+
+  if (realDay < 10)
+  {
+    realTimeValue += '0';
+  }
+  realTimeValue += realDay.toFixed(0) + ' ';
+  if (realHour < 10)
+  {
+    realTimeValue += '0';
+  }
+  realTimeValue += realHour.toFixed(0) + ':';
+  if (realMin < 10)
+  {
+    realTimeValue += '0';
+  }
+  realTimeValue += realMin.toFixed(0)  + ':';
+  if (realSec < 10)
+  {
+    realTimeValue += '0';
+  }
+  realTimeValue += realSec.toFixed(0);
+
+  if (simDay < 10)
+  {
+    simTimeValue += '0';
+  }
+  simTimeValue += simDay.toFixed(0)  + ' ';
+  if (simHour < 10)
+  {
+    simTimeValue += '0';
+  }
+  simTimeValue += simHour.toFixed(0) + ':';
+  if (simMin < 10)
+  {
+    simTimeValue += '0';
+  }
+  simTimeValue += simMin.toFixed(0) + ':';
+  if (simSec < 10)
+  {
+    simTimeValue += '0';
+  }
+  simTimeValue += simSec.toFixed(0);
+
+  this.gui.setRealTime(realTimeValue);
+  this.gui.setSimTime(simTimeValue);
+};
+
+GZ3D.GZIface.prototype.createModelFromMsg = function(model)
+{
+  var modelObj = new THREE.Object3D();
+  modelObj.name = model.name;
+  modelObj.userData = model.id;
+  if (model.pose)
+  {
+    this.scene.setPose(modelObj, model.pose.position, model.pose.orientation);
+  }
+  for (var j = 0; j < model.link.length; ++j)
+  {
+    var link = model.link[j];
+    var linkObj = new THREE.Object3D();
+    linkObj.name = link.name;
+    linkObj.userData = link.id;
+    linkObj.serverProperties =
+        {
+          self_collide: link.self_collide,
+          gravity: link.gravity,
+          kinematic: link.kinematic
+        };
+
+    if (link.pose)
+    {
+      this.scene.setPose(linkObj, link.pose.position,
+          link.pose.orientation);
+    }
+    modelObj.add(linkObj);
+    for (var k = 0; k < link.visual.length; ++k)
+    {
+      var visual = link.visual[k];
+      var visualObj = this.createVisualFromMsg(visual);
+      if (visualObj && !visualObj.parent)
+      {
+        linkObj.add(visualObj);
+      }
+    }
+
+    for (var l = 0; l < link.collision.length; ++l)
+    {
+      var collision = link.collision[l];
+      for (var m = 0; m < link.collision[l].visual.length; ++m)
+      {
+        var collisionVisual = link.collision[l].visual[m];
+        var collisionVisualObj = this.createVisualFromMsg(collisionVisual);
+        if (collisionVisualObj && !collisionVisualObj.parent)
+        {
+          linkObj.add(collisionVisualObj);
+        }
+      }
+    }
+  }
+  if (model.joint)
+  {
+    modelObj.joint = model.joint;
+  }
+
+  return modelObj;
+};
+
+GZ3D.GZIface.prototype.createVisualFromMsg = function(visual)
+{
+  if (visual.geometry)
+  {
+    var geom = visual.geometry;
+    var visualObj = new THREE.Object3D();
+    visualObj.name = visual.name;
+    if (visual.pose)
+    {
+      this.scene.setPose(visualObj, visual.pose.position,
+          visual.pose.orientation);
+    }
+
+    visualObj.castShadow = visual.cast_shadows;
+    visualObj.receiveShadow = visual.receive_shadows;
+
+    this.createGeom(geom, visual.material, visualObj);
+
+    return visualObj;
+  }
+};
+
+GZ3D.GZIface.prototype.createLightFromMsg = function(light)
+{
+  var obj, range, direction;
+
+  if (light.type === 1)
+  {
+    direction = null;
+    range = light.range;
+  }
+  else if (light.type === 2)
+  {
+    direction = light.direction;
+    range = light.range;
+  }
+  else if (light.type === 3)
+  {
+    direction = light.direction;
+    range = null;
+  }
+
+  // equation taken from
+  // http://wiki.blender.org/index.php/Doc:2.6/Manual/Lighting/Lights/Light_Attenuation
+  var E = 1;
+  var D = 1;
+  var r = 1;
+  var L = light.attenuation_linear;
+  var Q = light.attenuation_quadratic;
+  var intensity = E*(D/(D+L*r))*(Math.pow(D,2)/(Math.pow(D,2)+Q*Math.pow(r,2)));
+
+  obj = this.scene.createLight(light.type, light.diffuse, intensity,
+        light.pose, range, light.cast_shadows, light.name,
+        direction, light.specular, light.attenuation_constant,
+        light.attenuation_linear, light.attenuation_quadratic);
+
+  return obj;
+};
+
+GZ3D.GZIface.prototype.createRoadsFromMsg = function(roads)
+{
+  var roadObj = new THREE.Object3D();
+
+  var mat = this.material['Gazebo/Road'];
+  var texture = null;
+  if (mat)
+  {
+    texture = this.parseUri('media/materials/textures/' + mat['texture']);
+  }
+  var obj = this.scene.createRoads(roads.point, roads.width, texture);
+  roadObj.add(obj);
+  return roadObj;
+};
+
+GZ3D.GZIface.prototype.parseUri = function(uri)
+{
+  var uriPath = 'assets';
+  var idx = uri.indexOf('://');
+  if (idx > 0)
+  {
+    idx +=3;
+  }
+  return uriPath + '/' + uri.substring(idx);
+};
+
+GZ3D.GZIface.prototype.createGeom = function(geom, material, parent)
+{
+  var obj;
+  var uriPath = 'assets';
+  var that = this;
+  var mat = this.parseMaterial(material);
+  if (geom.box)
+  {
+    obj = this.scene.createBox(geom.box.size.x, geom.box.size.y,
+        geom.box.size.z);
+  }
+  else if (geom.cylinder)
+  {
+    obj = this.scene.createCylinder(geom.cylinder.radius,
+        geom.cylinder.length);
+  }
+  else if (geom.sphere)
+  {
+    obj = this.scene.createSphere(geom.sphere.radius);
+  }
+  else if (geom.plane)
+  {
+    obj = this.scene.createPlane(geom.plane.normal.x, geom.plane.normal.y,
+        geom.plane.normal.z, geom.plane.size.x, geom.plane.size.y);
+  }
+  else if (geom.mesh)
+  {
+    // get model name which the mesh is in
+    var rootModel = parent;
+    while (rootModel.parent)
+    {
+      rootModel = rootModel.parent;
+    }
+
+    // find model from database, download the mesh if it exists
+    // var manifestXML;
+    // var manifestURI = GAZEBO_MODEL_DATABASE_URI + '/manifest.xml';
+    // var request = new XMLHttpRequest();
+    // request.open('GET', manifestURI, false);
+    // request.onreadystatechange = function(){
+    //   if (request.readyState === 4)
+    //   {
+    //     if (request.status === 200 || request.status === 0)
+    //     {
+    //         manifestXML = request.responseXML;
+    //     }
+    //   }
+    // };
+    // request.send();
+
+    // var uriPath;
+    // var modelAvailable = false;
+    // var modelsElem = manifestXML.getElementsByTagName('models')[0];
+    // var i;
+    // for (i = 0; i < modelsElem.getElementsByTagName('uri').length; ++i)
+    // {
+    //   var uri = modelsElem.getElementsByTagName('uri')[i];
+    //   var model = uri.substring(uri.indexOf('://') + 3);
+    //   if (model === rootModel)
+    //   {
+    //     modelAvailable = true;
+    //   }
+    // }
+
+    // if (modelAvailable)
+    {
+      var meshUri = geom.mesh.filename;
+      var submesh = geom.mesh.submesh;
+      var centerSubmesh = geom.mesh.center_submesh;
+
+      var uriType = meshUri.substring(0, meshUri.indexOf('://'));
+      if (uriType === 'file' || uriType === 'model')
+      {
+        var modelName = meshUri.substring(meshUri.indexOf('://') + 3);
+        if (geom.mesh.scale)
+        {
+          parent.scale.x = geom.mesh.scale.x;
+          parent.scale.y = geom.mesh.scale.y;
+          parent.scale.z = geom.mesh.scale.z;
+        }
+
+        var modelUri = uriPath + '/' + modelName;
+        // Use coarse version on touch devices
+        if (modelUri.indexOf('.dae') !== -1 && isTouchDevice)
+        {
+          modelUri = modelUri.substring(0,modelUri.indexOf('.dae'));
+
+          var checkModel = new XMLHttpRequest();
+          checkModel.open('HEAD', modelUri+'_coarse.dae', false);
+          checkModel.send();
+          if (checkModel.status === 404)
+          {
+            modelUri = modelUri+'.dae';
+          }
+          else
+          {
+            modelUri = modelUri+'_coarse.dae';
+          }
+        }
+
+        var materialName = parent.name + '::' + modelUri;
+        this.entityMaterial[materialName] = mat;
+
+        this.scene.loadMesh(modelUri, submesh,
+            centerSubmesh, function(dae) {
+              if (that.entityMaterial[materialName])
+              {
+                var allChildren = [];
+                dae.getDescendants(allChildren);
+                for (var c = 0; c < allChildren.length; ++c)
+                {
+                  if (allChildren[c] instanceof THREE.Mesh)
+                  {
+                    that.scene.setMaterial(allChildren[c],
+                        that.entityMaterial[materialName]);
+                    break;
+                  }
+                }
+              }
+              parent.add(dae);
+              loadGeom(parent);
+            });
+      }
+    }
+  }
+  else if (geom.heightmap)
+  {
+    var request = new ROSLIB.ServiceRequest({
+      name : that.scene.name
+    });
+
+    // redirect the texture paths to the assets dir
+    var textures = geom.heightmap.texture;
+    for ( var k = 0; k < textures.length; ++k)
+    {
+      textures[k].diffuse = this.parseUri(textures[k].diffuse);
+      textures[k].normal = this.parseUri(textures[k].normal);
+    }
+
+    var sizes = geom.heightmap.size;
+
+    // send service request and load heightmap on response
+    this.heightmapDataService.callService(request,
+        function(result)
+        {
+          var heightmap = result.heightmap;
+          // gazebo heightmap is always square shaped,
+          // and a dimension of: 2^N + 1
+          that.scene.loadHeightmap(heightmap.heights, heightmap.size.x,
+              heightmap.size.y, heightmap.width, heightmap.height,
+              heightmap.origin, textures,
+              geom.heightmap.blend, parent);
+            //console.log('Result for service call on ' + result);
+        });
+
+    //this.scene.loadHeightmap(parent)
+  }
+
+  if (obj)
+  {
+    if (mat)
+    {
+      // texture mapping for simple shapes and planes only,
+      // not used by mesh and terrain
+      this.scene.setMaterial(obj, mat);
+    }
+    obj.updateMatrix();
+    parent.add(obj);
+    loadGeom(parent);
+  }
+
+  function loadGeom(visualObj)
+  {
+    var allChildren = [];
+    visualObj.getDescendants(allChildren);
+    for (var c = 0; c < allChildren.length; ++c)
+    {
+      if (allChildren[c] instanceof THREE.Mesh)
+      {
+        allChildren[c].castShadow = true;
+        allChildren[c].receiveShadow = true;
+
+        if (visualObj.castShadows)
+        {
+          allChildren[c].castShadow = visualObj.castShadows;
+        }
+        if (visualObj.receiveShadows)
+        {
+          allChildren[c].receiveShadow = visualObj.receiveShadows;
+        }
+
+        if (visualObj.name.indexOf('COLLISION_VISUAL') >= 0)
+        {
+          allChildren[c].castShadow = false;
+          allChildren[c].receiveShadow = false;
+
+          allChildren[c].visible = this.scene.showCollisions;
+        }
+        break;
+      }
+    }
+  }
+};
+
+GZ3D.GZIface.prototype.applyMaterial = function(obj, mat)
+{
+  if (obj)
+  {
+    if (mat)
+    {
+      obj.material = new THREE.MeshPhongMaterial();
+      var ambient = mat.ambient;
+      if (ambient)
+      {
+        obj.material.ambient.setRGB(ambient[0], ambient[1], ambient[2]);
+      }
+      var diffuse = mat.diffuse;
+      if (diffuse)
+      {
+        obj.material.color.setRGB(diffuse[0], diffuse[1], diffuse[2]);
+      }
+      var specular = mat.specular;
+      if (specular)
+      {
+        obj.material.specular.setRGB(specular[0], specular[1], specular[2]);
+      }
+      var opacity = mat.opacity;
+      if (opacity)
+      {
+        if (opacity < 1)
+        {
+          obj.material.transparent = true;
+          obj.material.opacity = opacity;
+        }
+      }
+
+      if (mat.texture)
+      {
+        obj.material.map = THREE.ImageUtils.loadTexture(mat.texture);
+      }
+      if (mat.normalMap)
+      {
+        obj.material.normalMap = THREE.ImageUtils.loadTexture(mat.normalMap);
+      }
+    }
+  }
+};
+
+GZ3D.GZIface.prototype.parseMaterial = function(material)
+{
+  if (!material)
+  {
+    return null;
+  }
+
+  var uriPath = 'assets';
+  var texture;
+  var normalMap;
+  var textureUri;
+  var ambient;
+  var diffuse;
+  var specular;
+  var opacity;
+  var scale;
+  var mat;
+
+  // get texture from material script
+  var script  = material.script;
+  if (script)
+  {
+    if (script.uri.length > 0)
+    {
+      if (script.name)
+      {
+        mat = this.material[script.name];
+        if (mat)
+        {
+          ambient = mat['ambient'];
+          diffuse = mat['diffuse'];
+          specular = mat['specular'];
+          opacity = mat['opacity'];
+          scale = mat['scale'];
+
+          var textureName = mat['texture'];
+          if (textureName)
+          {
+            for (var i = 0; i < script.uri.length; ++i)
+            {
+              var type = script.uri[i].substring(0,
+                    script.uri[i].indexOf('://'));
+
+              if (type === 'model')
+              {
+                if (script.uri[i].indexOf('textures') > 0)
+                {
+                  textureUri = script.uri[i].substring(
+                      script.uri[i].indexOf('://') + 3);
+                  break;
+                }
+              }
+              else if (type === 'file')
+              {
+                if (script.uri[i].indexOf('materials') > 0)
+                {
+                  textureUri = script.uri[i].substring(
+                      script.uri[i].indexOf('://') + 3,
+                      script.uri[i].indexOf('materials') + 9) + '/textures';
+                  break;
+                }
+              }
+            }
+            if (textureUri)
+            {
+              texture = uriPath + '/' +
+                  textureUri  + '/' + textureName;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  // normal map
+  if (material.normal_map)
+  {
+    var mapUri;
+    if (material.normal_map.indexOf('://') > 0)
+    {
+      mapUri = material.normal_map.substring(
+          material.normal_map.indexOf('://') + 3,
+          material.normal_map.lastIndexOf('/'));
+    }
+    else
+    {
+      mapUri = textureUri;
+    }
+    if (mapUri)
+    {
+      var startIndex = material.normal_map.lastIndexOf('/') + 1;
+      if (startIndex < 0)
+      {
+        startIndex = 0;
+      }
+      var normalMapName = material.normal_map.substr(startIndex,
+          material.normal_map.lastIndexOf('.') - startIndex);
+      normalMap = uriPath + '/' +
+        mapUri  + '/' + normalMapName + '.png';
+    }
+  }
+
+  return {
+      texture: texture,
+      normalMap: normalMap,
+      ambient: ambient,
+      diffuse: diffuse,
+      specular: specular,
+      opacity: opacity,
+      scale: scale
+  };
+};
+
+
+/*GZ3D.GZIface.prototype.createGeom = function(geom, material, parent)
+{
+  var obj;
+
+  var uriPath = 'assets';
+  var texture;
+  var normalMap;
+  var textureUri;
+  var mat;
+  if (material)
+  {
+    // get texture from material script
+    var script  = material.script;
+    if (script)
+    {
+      if (script.uri.length > 0)
+      {
+        if (script.name)
+        {
+          mat = this.material[script.name];
+          if (mat)
+          {
+            var textureName = mat['texture'];
+            if (textureName)
+            {
+              for (var i = 0; i < script.uri.length; ++i)
+              {
+                var type = script.uri[i].substring(0,
+                      script.uri[i].indexOf('://'));
+
+                if (type === 'model')
+                {
+                  if (script.uri[i].indexOf('textures') > 0)
+                  {
+                    textureUri = script.uri[i].substring(
+                        script.uri[i].indexOf('://') + 3);
+                    break;
+                  }
+                }
+                else if (type === 'file')
+                {
+                  if (script.uri[i].indexOf('materials') > 0)
+                  {
+                    textureUri = script.uri[i].substring(
+                        script.uri[i].indexOf('://') + 3,
+                        script.uri[i].indexOf('materials') + 9) + '/textures';
+                    break;
+                  }
+                }
+              }
+              if (textureUri)
+              {
+                texture = uriPath + '/' +
+                    textureUri  + '/' + textureName;
+              }
+            }
+          }
+        }
+      }
+    }
+    // normal map
+    if (material.normal_map)
+    {
+      var mapUri;
+      if (material.normal_map.indexOf('://') > 0)
+      {
+        mapUri = material.normal_map.substring(
+            material.normal_map.indexOf('://') + 3,
+            material.normal_map.lastIndexOf('/'));
+      }
+      else
+      {
+        mapUri = textureUri;
+      }
+      if (mapUri)
+      {
+        var startIndex = material.normal_map.lastIndexOf('/') + 1;
+        if (startIndex < 0)
+        {
+          startIndex = 0;
+        }
+        var normalMapName = material.normal_map.substr(startIndex,
+            material.normal_map.lastIndexOf('.') - startIndex);
+        normalMap = uriPath + '/' +
+          mapUri  + '/' + normalMapName + '.png';
+      }
+
+    }
+  }
+
+  if (geom.box)
+  {
+    obj = this.scene.createBox(geom.box.size.x, geom.box.size.y,
+        geom.box.size.z);
+  }
+  else if (geom.cylinder)
+  {
+    obj = this.scene.createCylinder(geom.cylinder.radius,
+        geom.cylinder.length);
+  }
+  else if (geom.sphere)
+  {
+    obj = this.scene.createSphere(geom.sphere.radius);
+  }
+  else if (geom.plane)
+  {
+    obj = this.scene.createPlane(geom.plane.normal.x, geom.plane.normal.y,
+        geom.plane.normal.z, geom.plane.size.x, geom.plane.size.y);
+  }
+  else if (geom.mesh)
+  {
+    // get model name which the mesh is in
+    var rootModel = parent;
+    while (rootModel.parent)
+    {
+      rootModel = rootModel.parent;
+    }
+
+    {
+      var meshUri = geom.mesh.filename;
+      var submesh = geom.mesh.submesh;
+      var centerSubmesh = geom.mesh.center_submesh;
+
+      console.log(geom.mesh.filename + ' ' + submesh);
+
+      var uriType = meshUri.substring(0, meshUri.indexOf('://'));
+      if (uriType === 'file' || uriType === 'model')
+      {
+        var modelName = meshUri.substring(meshUri.indexOf('://') + 3);
+        if (geom.mesh.scale)
+        {
+          parent.scale.x = geom.mesh.scale.x;
+          parent.scale.y = geom.mesh.scale.y;
+          parent.scale.z = geom.mesh.scale.z;
+        }
+
+        this.scene.loadMesh(uriPath + '/' + modelName, submesh, centerSubmesh,
+            texture, normalMap, parent);
+      }
+    }
+  }
+  else if (geom.heightmap)
+  {
+    var that = this;
+    var request = new ROSLIB.ServiceRequest({
+      name : that.scene.name
+    });
+
+    // redirect the texture paths to the assets dir
+    var textures = geom.heightmap.texture;
+    for ( var k = 0; k < textures.length; ++k)
+    {
+      textures[k].diffuse = this.parseUri(textures[k].diffuse);
+      textures[k].normal = this.parseUri(textures[k].normal);
+    }
+
+    var sizes = geom.heightmap.size;
+
+    // send service request and load heightmap on response
+    this.heightmapDataService.callService(request,
+        function(result)
+        {
+          var heightmap = result.heightmap;
+          // gazebo heightmap is always square shaped,
+          // and a dimension of: 2^N + 1
+          that.scene.loadHeightmap(heightmap.heights, heightmap.size.x,
+              heightmap.size.y, heightmap.width, heightmap.height,
+              heightmap.origin, textures,
+              geom.heightmap.blend, parent);
+            //console.log('Result for service call on ' + result);
+        });
+
+    //this.scene.loadHeightmap(parent)
+  }
+
+  // texture mapping for simple shapes and planes only,
+  // not used by mesh and terrain
+  if (obj)
+  {
+
+    if (mat)
+    {
+      obj.material = new THREE.MeshPhongMaterial();
+
+      var ambient = mat['ambient'];
+      if (ambient)
+      {
+        obj.material.ambient.setRGB(ambient[0], ambient[1], ambient[2]);
+      }
+      var diffuse = mat['diffuse'];
+      if (diffuse)
+      {
+        obj.material.color.setRGB(diffuse[0], diffuse[1], diffuse[2]);
+      }
+      var specular = mat['specular'];
+      if (specular)
+      {
+        obj.material.specular.setRGB(specular[0], specular[1], specular[2]);
+      }
+      var opacity = mat['opacity'];
+      if (opacity)
+      {
+        if (opacity < 1)
+        {
+          obj.material.transparent = true;
+          obj.material.opacity = opacity;
+        }
+      }
+
+      //this.scene.setMaterial(obj, texture, normalMap);
+
+      if (texture)
+      {
+        obj.material.map = THREE.ImageUtils.loadTexture(texture);
+      }
+      if (normalMap)
+      {
+        obj.material.normalMap = THREE.ImageUtils.loadTexture(normalMap);
+      }
+    }
+    obj.updateMatrix();
+    parent.add(obj);
+  }
+};
+*/
+
+// Based on TransformControls.js
+// original author: arodic / https://github.com/arodic
+
+/**
+ * Manipulator to perform translate and rotate transforms on objects
+ * within the scene.
+ * @constructor
+ */
+GZ3D.Manipulator = function(camera, mobile, domElement, doc)
+{
+  // Needs camera for perspective
+  this.camera = camera;
+
+  // For mouse/touch events
+  this.domElement = (domElement !== undefined) ? domElement : document;
+  this.document = (doc !== undefined) ? doc : document;
+
+  // Mobile / desktop
+  this.mobile = (mobile !== undefined) ? mobile : false;
+
+  // Object to be manipulated
+  this.object = undefined;
+
+  // translate / rotate
+  this.mode = 'translate';
+
+  // world / local
+  this.space = 'world';
+
+  // hovered used for backwards compatibility
+  // Whenever it wasn't an issue, hovered and active were combined
+  // into selected
+  this.hovered = false;
+  this.selected = 'null';
+
+  this.scale = 1;
+
+  this.snapDist = null;
+  this.modifierAxis = new THREE.Vector3(1, 1, 1);
+  this.gizmo = new THREE.Object3D();
+
+  this.pickerNames = [];
+
+  var scope = this;
+
+  var changeEvent = {type: 'change'};
+
+  var ray = new THREE.Raycaster();
+  var projector = new THREE.Projector();
+  var pointerVector = new THREE.Vector3();
+
+  var point = new THREE.Vector3();
+  var offset = new THREE.Vector3();
+
+  var rotation = new THREE.Vector3();
+  var offsetRotation = new THREE.Vector3();
+  var scale = 1;
+
+  var lookAtMatrix = new THREE.Matrix4();
+  var eye = new THREE.Vector3();
+
+  var tempMatrix = new THREE.Matrix4();
+  var tempVector = new THREE.Vector3();
+  var tempQuaternion = new THREE.Quaternion();
+  var unitX = new THREE.Vector3(1, 0, 0);
+  var unitY = new THREE.Vector3(0, 1, 0);
+  var unitZ = new THREE.Vector3(0, 0, 1);
+
+  var quaternionXYZ = new THREE.Quaternion();
+  var quaternionX = new THREE.Quaternion();
+  var quaternionY = new THREE.Quaternion();
+  var quaternionZ = new THREE.Quaternion();
+  var quaternionE = new THREE.Quaternion();
+
+  var oldPosition = new THREE.Vector3();
+  var oldRotationMatrix = new THREE.Matrix4();
+
+  var parentRotationMatrix  = new THREE.Matrix4();
+  var parentScale = new THREE.Vector3();
+
+  var worldPosition = new THREE.Vector3();
+  var worldRotation = new THREE.Vector3();
+  var worldRotationMatrix  = new THREE.Matrix4();
+  var camPosition = new THREE.Vector3();
+
+  var hovered = null;
+  var hoveredColor = new THREE.Color();
+
+  // Picker currently selected (highlighted)
+  var selectedPicker = null;
+  var selectedColor = new THREE.Color();
+
+  // Intersection planes
+  var intersectionPlanes = {};
+  var intersectionPlaneList = ['XY','YZ','XZ'];
+  var currentPlane = 'XY';
+
+  var planes = new THREE.Object3D();
+  this.gizmo.add(planes);
+
+  for(var i in intersectionPlaneList)
+  {
+    intersectionPlanes[intersectionPlaneList[i]] =
+        new THREE.Mesh(new THREE.PlaneGeometry(500, 500));
+    intersectionPlanes[intersectionPlaneList[i]].material.side =
+        THREE.DoubleSide;
+    intersectionPlanes[intersectionPlaneList[i]].visible = false;
+    planes.add(intersectionPlanes[intersectionPlaneList[i]]);
+  }
+
+  intersectionPlanes['YZ'].rotation.set(0, Math.PI/2, 0);
+  intersectionPlanes['XZ'].rotation.set(-Math.PI/2, 0, 0);
+  bakeTransformations(intersectionPlanes['YZ']);
+  bakeTransformations(intersectionPlanes['XZ']);
+
+  // Geometries
+
+  var pickerAxes = {};
+  var displayAxes = {};
+
+  var HandleMaterial = function(color, over, opacity)
+  {
+    var material = new THREE.MeshBasicMaterial();
+    material.color = color;
+    if(over)
+    {
+      material.side = THREE.DoubleSide;
+      material.depthTest = false;
+      material.depthWrite = false;
+    }
+    material.opacity = opacity !== undefined ? opacity : 0.5;
+    material.transparent = true;
+    return material;
+  };
+
+  var LineMaterial = function(color, opacity)
+  {
+    var material = new THREE.LineBasicMaterial();
+    material.color = color;
+    material.depthTest = false;
+    material.depthWrite = false;
+    material.opacity = opacity !== undefined ? opacity : 1;
+    material.transparent = true;
+    return material;
+  };
+
+  // Colors
+  var white = new THREE.Color(0xffffff);
+  var gray = new THREE.Color(0x808080);
+  var red = new THREE.Color(0xff0000);
+  var green = new THREE.Color(0x00ff00);
+  var blue = new THREE.Color(0x0000ff);
+  var cyan = new THREE.Color(0x00ffff);
+  var magenta = new THREE.Color(0xff00ff);
+  var yellow = new THREE.Color(0xffff00);
+
+  var geometry, mesh;
+
+  // Translate
+
+  pickerAxes['translate'] = new THREE.Object3D();
+  displayAxes['translate'] = new THREE.Object3D();
+  this.gizmo.add(pickerAxes['translate']);
+  this.gizmo.add(displayAxes['translate']);
+
+  // Picker cylinder
+  if(this.mobile)
+  {
+    geometry = new THREE.CylinderGeometry(0.5, 0.01, 1.4, 10, 1, false);
+  }
+  else
+  {
+    geometry = new THREE.CylinderGeometry(0.2, 0.1, 0.8, 4, 1, false);
+  }
+
+  mesh = new THREE.Mesh(geometry, new HandleMaterial(red, true));
+  mesh.position.x = 0.7;
+  mesh.rotation.z = -Math.PI/2;
+  bakeTransformations(mesh);
+  mesh.name = 'TX';
+  pickerAxes['translate'].add(mesh);
+  this.pickerNames.push(mesh.name);
+
+  mesh = new THREE.Mesh(geometry, new HandleMaterial(green, true));
+  mesh.position.y = 0.7;
+  bakeTransformations(mesh);
+  mesh.name = 'TY';
+  pickerAxes['translate'].add(mesh);
+  this.pickerNames.push(mesh.name);
+
+  mesh = new THREE.Mesh(geometry, new HandleMaterial(blue, true));
+  mesh.position.z = 0.7;
+  mesh.rotation.x = Math.PI/2;
+  bakeTransformations(mesh);
+  mesh.name = 'TZ';
+  pickerAxes['translate'].add(mesh);
+  this.pickerNames.push(mesh.name);
+
+  if(this.mobile)
+  {
+    // Display cylinder
+    geometry = new THREE.CylinderGeometry(0.1, 0.1, 1, 10, 1, false);
+
+    mesh = new THREE.Mesh(geometry, new HandleMaterial(red, true));
+    mesh.position.x = 0.5;
+    mesh.rotation.z = -Math.PI/2;
+    bakeTransformations(mesh);
+    mesh.name = 'TX';
+    displayAxes['translate'].add(mesh);
+
+    mesh = new THREE.Mesh(geometry, new HandleMaterial(green, true));
+    mesh.position.y = 0.5;
+    bakeTransformations(mesh);
+    mesh.name = 'TY';
+    displayAxes['translate'].add(mesh);
+
+    mesh = new THREE.Mesh(geometry, new HandleMaterial(blue, true));
+    mesh.position.z = 0.5;
+    mesh.rotation.x = Math.PI/2;
+    bakeTransformations(mesh);
+    mesh.name = 'TZ';
+    displayAxes['translate'].add(mesh);
+
+    // Display cone (arrow tip)
+    geometry = new THREE.CylinderGeometry(0, 0.15, 0.4, 10, 1, false);
+
+    mesh = new THREE.Mesh(geometry, new HandleMaterial(red, true));
+    mesh.position.x = 1.2;
+    mesh.rotation.z = -Math.PI/2;
+    bakeTransformations(mesh);
+    mesh.name = 'TX';
+    displayAxes['translate'].add(mesh);
+
+    mesh = new THREE.Mesh(geometry, new HandleMaterial(green, true));
+    mesh.position.y = 1.2;
+    bakeTransformations(mesh);
+    mesh.name = 'TY';
+    displayAxes['translate'].add(mesh);
+
+    mesh = new THREE.Mesh(geometry, new HandleMaterial(blue, true));
+    mesh.position.z = 1.2;
+    mesh.rotation.x = Math.PI/2;
+    bakeTransformations(mesh);
+    mesh.name = 'TZ';
+    displayAxes['translate'].add(mesh);
+  }
+  else
+  {
+    // Display lines
+    geometry = new THREE.Geometry();
+    geometry.vertices.push(
+        new THREE.Vector3(0, 0, 0), new THREE.Vector3(1, 0, 0),
+        new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 1, 0),
+        new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 0, 1)
+    );
+    geometry.colors.push(
+        red, red, green, green, blue, blue
+    );
+    var material = new THREE.LineBasicMaterial({
+        vertexColors: THREE.VertexColors,
+        depthTest: false,
+        depthWrite: false,
+        transparent: true
+    });
+    mesh = new THREE.Line(geometry, material, THREE.LinePieces);
+    displayAxes['translate'].add(mesh);
+
+    // Display cone (arrow tip)
+    geometry = new THREE.CylinderGeometry(0, 0.05, 0.2, 4, 1, true);
+
+    mesh = new THREE.Mesh(geometry, new HandleMaterial(red, true, 1));
+    mesh.position.x = 1.1;
+    mesh.rotation.z = -Math.PI/2;
+    bakeTransformations(mesh);
+    mesh.name = 'TX';
+    displayAxes['translate'].add(mesh);
+
+    mesh = new THREE.Mesh(geometry, new HandleMaterial(green, true, 1));
+    mesh.position.y = 1.1;
+    bakeTransformations(mesh);
+    mesh.name = 'TY';
+    displayAxes['translate'].add(mesh);
+
+    mesh = new THREE.Mesh(geometry, new HandleMaterial(blue, true, 1));
+    mesh.position.z = 1.1;
+    mesh.rotation.x = Math.PI/2;
+    bakeTransformations(mesh);
+    mesh.name = 'TZ';
+    displayAxes['translate'].add(mesh);
+
+    // Picker and display octahedron for TXYZ
+    mesh = new THREE.Mesh(new THREE.OctahedronGeometry(0.1, 0),
+        new HandleMaterial(white, true, 0.25));
+    mesh.name = 'TXYZ';
+    this.pickerNames.push(mesh.name);
+    displayAxes['translate'].add(mesh);
+    pickerAxes['translate'].add(mesh.clone());
+
+    // Picker and display planes
+    geometry = new THREE.PlaneGeometry(0.3, 0.3);
+
+    mesh = new THREE.Mesh(geometry, new HandleMaterial(yellow, 0.25));
+    mesh.position.set(0.15, 0.15, 0);
+    bakeTransformations(mesh);
+    mesh.name = 'TXY';
+    this.pickerNames.push(mesh.name);
+    displayAxes['translate'].add(mesh);
+    pickerAxes['translate'].add(mesh.clone());
+
+    mesh = new THREE.Mesh(geometry, new HandleMaterial(cyan, 0.25));
+    mesh.position.set(0, 0.15, 0.15);
+    mesh.rotation.y = Math.PI/2;
+    bakeTransformations(mesh);
+    mesh.name = 'TYZ';
+    this.pickerNames.push(mesh.name);
+    displayAxes['translate'].add(mesh);
+    pickerAxes['translate'].add(mesh.clone());
+
+    mesh = new THREE.Mesh(geometry, new HandleMaterial(magenta, 0.25));
+    mesh.position.set(0.15, 0, 0.15);
+    mesh.rotation.x = Math.PI/2;
+    bakeTransformations(mesh);
+    mesh.name = 'TXZ';
+    this.pickerNames.push(mesh.name);
+    displayAxes['translate'].add(mesh);
+    pickerAxes['translate'].add(mesh.clone());
+
+  }
+
+  // Rotate
+
+  pickerAxes['rotate'] = new THREE.Object3D();
+  displayAxes['rotate'] = new THREE.Object3D();
+  this.gizmo.add(pickerAxes['rotate']);
+  this.gizmo.add(displayAxes['rotate']);
+
+  // RX, RY, RZ
+
+  // Picker torus
+  if(this.mobile)
+  {
+    geometry = new THREE.TorusGeometry(1, 0.3, 4, 36, 2*Math.PI);
+  }
+  else
+  {
+    geometry = new THREE.TorusGeometry(1, 0.15, 4, 6, Math.PI);
+  }
+
+  mesh = new THREE.Mesh(geometry, new HandleMaterial(red, false));
+  mesh.rotation.z = -Math.PI/2;
+  mesh.rotation.y = -Math.PI/2;
+  bakeTransformations(mesh);
+  mesh.name = 'RX';
+  pickerAxes['rotate'].add(mesh);
+  this.pickerNames.push(mesh.name);
+
+  mesh = new THREE.Mesh(geometry, new HandleMaterial(green, false));
+  mesh.rotation.z = Math.PI;
+  mesh.rotation.x = -Math.PI/2;
+  bakeTransformations(mesh);
+  mesh.name = 'RY';
+  pickerAxes['rotate'].add(mesh);
+  this.pickerNames.push(mesh.name);
+
+  mesh = new THREE.Mesh(geometry, new HandleMaterial(blue, false));
+  mesh.rotation.z = -Math.PI/2;
+  bakeTransformations(mesh);
+  mesh.name = 'RZ';
+  pickerAxes['rotate'].add(mesh);
+  this.pickerNames.push(mesh.name);
+
+  if(this.mobile)
+  {
+    // Display torus
+    geometry = new THREE.TorusGeometry(1, 0.1, 4, 36, 2*Math.PI);
+
+    mesh = new THREE.Mesh(geometry, new HandleMaterial(blue, false));
+    mesh.rotation.z = -Math.PI/2;
+    bakeTransformations(mesh);
+    mesh.name = 'RZ';
+    displayAxes['rotate'].add(mesh);
+
+    mesh = new THREE.Mesh(geometry, new HandleMaterial(red, false));
+    mesh.rotation.z = -Math.PI/2;
+    mesh.rotation.y = -Math.PI/2;
+    bakeTransformations(mesh);
+    mesh.name = 'RX';
+    displayAxes['rotate'].add(mesh);
+
+    mesh = new THREE.Mesh(geometry, new HandleMaterial(green, false));
+    mesh.rotation.z = Math.PI;
+    mesh.rotation.x = -Math.PI/2;
+    bakeTransformations(mesh);
+    mesh.name = 'RY';
+    displayAxes['rotate'].add(mesh);
+  }
+  else
+  {
+    // Display circles
+    var Circle = function(radius, facing, arc)
+    {
+      geometry = new THREE.Geometry();
+      arc = arc ? arc : 1;
+      for(var i = 0; i <= 64 * arc; ++i)
+      {
+        if(facing === 'x')
+        {
+          geometry.vertices.push(new THREE.Vector3(
+              0, Math.cos(i / 32 * Math.PI), Math.sin(i / 32 * Math.PI))
+              .multiplyScalar(radius));
+        }
+        if(facing === 'y')
+        {
+          geometry.vertices.push(new THREE.Vector3(
+              Math.cos(i / 32 * Math.PI), 0, Math.sin(i / 32 * Math.PI))
+              .multiplyScalar(radius));
+        }
+        if(facing === 'z')
+        {
+          geometry.vertices.push(new THREE.Vector3(
+              Math.sin(i / 32 * Math.PI), Math.cos(i / 32 * Math.PI), 0)
+              .multiplyScalar(radius));
+        }
+      }
+      return geometry;
+    };
+
+    mesh = new THREE.Line(new Circle(1, 'x', 0.5), new LineMaterial(red));
+    mesh.name = 'RX';
+    displayAxes['rotate'].add(mesh);
+
+    mesh = new THREE.Line(new Circle(1, 'y', 0.5), new LineMaterial(green));
+    mesh.name = 'RY';
+    displayAxes['rotate'].add(mesh);
+
+    mesh = new THREE.Line(new Circle(1, 'z', 0.5), new LineMaterial(blue));
+    mesh.name = 'RZ';
+    displayAxes['rotate'].add(mesh);
+
+    mesh = new THREE.Line(new Circle(1, 'z'), new LineMaterial(gray));
+    mesh.name = 'RXYZE';
+    this.pickerNames.push(mesh.name);
+    displayAxes['rotate'].add(mesh);
+
+    mesh = new THREE.Line(new Circle(1.25, 'z'),
+        new LineMaterial(yellow, 0.25));
+    mesh.name = 'RE';
+    this.pickerNames.push(mesh.name);
+    displayAxes['rotate'].add(mesh);
+
+    // Picker spheres
+    mesh = new THREE.Mesh(new THREE.SphereGeometry(0.95, 12, 12),
+        new HandleMaterial(white, 0.25));
+    mesh.name = 'RXYZE';
+    pickerAxes['rotate'].add(mesh);
+    this.pickerNames.push(mesh.name);
+
+    intersectionPlanes['SPHERE'] = new THREE.Mesh(new
+        THREE.SphereGeometry(0.95, 12, 12));
+    intersectionPlanes['SPHERE'].visible = false;
+    planes.add(intersectionPlanes['SPHERE']);
+
+    mesh = new THREE.Mesh(new THREE.TorusGeometry(1.30, 0.15, 4, 12),
+        new HandleMaterial(yellow, 0.25));
+    mesh.name = 'RE';
+    pickerAxes['rotate'].add(mesh);
+    this.pickerNames.push(mesh.name);
+  }
+  mesh = null;
+
+  /**
+   * Attach gizmo to an object
+   * @param {THREE.Object3D} - Model to be manipulated
+   */
+  this.attach = function(object)
+  {
+    this.object = object;
+    this.setMode(scope.mode);
+
+    if(this.mobile)
+    {
+      this.domElement.addEventListener('touchstart', onTouchStart, false);
+    }
+    else
+    {
+      this.domElement.addEventListener('mousedown', onMouseDown, false);
+      this.domElement.addEventListener('mousemove', onMouseHover, false);
+    }
+  };
+
+  /**
+   * Detatch gizmo from an object
+   * @param {THREE.Object3D} - Model
+   */
+  this.detach = function(object)
+  {
+    this.object = undefined;
+    this.selected = 'null';
+
+    this.hide();
+
+    if(this.mobile)
+    {
+      this.domElement.removeEventListener('touchstart', onTouchStart, false);
+    }
+    else
+    {
+      this.domElement.removeEventListener('mousedown', onMouseDown, false);
+      this.domElement.removeEventListener('mousemove', onMouseHover, false);
+    }
+  };
+
+  /**
+   * Update gizmo's pose and scale
+   */
+  this.update = function()
+  {
+    if(this.object === undefined)
+    {
+      return;
+    }
+
+    this.object.updateMatrixWorld();
+    worldPosition.getPositionFromMatrix(this.object.matrixWorld);
+
+    this.camera.updateMatrixWorld();
+    camPosition.getPositionFromMatrix(this.camera.matrixWorld);
+
+    scale = worldPosition.distanceTo(camPosition) / 6 * this.scale;
+    this.gizmo.position.copy(worldPosition);
+    this.gizmo.scale.set(scale, scale, scale);
+
+    for(var i in this.gizmo.children)
+    {
+      for(var j in this.gizmo.children[i].children)
+      {
+        var object = this.gizmo.children[i].children[j];
+        var name = object.name;
+
+        if(name.search('E') !== -1)
+        {
+          lookAtMatrix.lookAt(camPosition, worldPosition,
+              tempVector.set(0, 1, 0));
+          object.rotation.setFromRotationMatrix(lookAtMatrix);
+        }
+        else
+        {
+          eye.copy(camPosition).sub(worldPosition).normalize();
+
+          if (this.space === 'local')
+          {
+            tempQuaternion.setFromRotationMatrix(tempMatrix
+                .extractRotation(this.object.matrixWorld));
+
+            if (name.search('R') !== -1)
+            {
+              tempMatrix.makeRotationFromQuaternion(tempQuaternion)
+                  .getInverse(tempMatrix);
+              eye.applyProjection(tempMatrix);
+
+              if (name === 'RX')
+              {
+                quaternionX.setFromAxisAngle(unitX, Math.atan2(-eye.y, eye.z));
+                tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionX);
+              }
+              if (name ==='RY')
+              {
+                quaternionY.setFromAxisAngle(unitY, Math.atan2( eye.x, eye.z));
+                tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionY);
+              }
+              if (name === 'RZ')
+              {
+                quaternionZ.setFromAxisAngle(unitZ, Math.atan2( eye.y, eye.x));
+                tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionZ);
+              }
+            }
+            object.quaternion.copy(tempQuaternion);
+          }
+          else if (this.space === 'world')
+          {
+            object.rotation.set(0, 0, 0);
+
+            if(name === 'RX')
+            {
+              object.rotation.x = Math.atan2(-eye.y, eye.z);
+            }
+            if(name === 'RY')
+            {
+              object.rotation.y = Math.atan2( eye.x, eye.z);
+            }
+            if(name === 'RZ')
+            {
+              object.rotation.z = Math.atan2( eye.y, eye.x);
+            }
+          }
+        }
+      }
+    }
+  };
+
+  /**
+   * Hide gizmo
+   */
+  this.hide = function()
+  {
+    for(var i in displayAxes)
+    {
+      for(var j in displayAxes[i].children)
+      {
+        displayAxes[i].children[j].visible = false;
+      }
+    }
+    for(var k in pickerAxes)
+    {
+      for(var l in pickerAxes[k].children)
+      {
+        pickerAxes[k].children[l].visible = false;
+      }
+    }
+
+    for(var m in intersectionPlaneList)
+    {
+      intersectionPlanes[intersectionPlaneList[m]].visible = false;
+    }
+  };
+
+  /**
+   * Set mode
+   * @param {string} value - translate | rotate
+   */
+  this.setMode = function(value)
+  {
+    scope.mode = value;
+
+    this.hide();
+
+    for(var i in displayAxes[this.mode].children)
+    {
+      displayAxes[this.mode].children[i].visible = true;
+    }
+
+    for(var j in pickerAxes[this.mode].children)
+    {
+      pickerAxes[this.mode].children[j].visible = false; // debug
+    }
+
+    for(var k in intersectionPlaneList)
+    {
+      intersectionPlanes[intersectionPlaneList[k]].visible = false; // debug
+    }
+
+    scope.update();
+  };
+
+  /**
+   * Choose intersection plane
+   */
+  this.setIntersectionPlane = function()
+  {
+    eye.copy(camPosition).sub(worldPosition).normalize();
+
+    if (this.space === 'local')
+    {
+       eye.applyMatrix4(tempMatrix.getInverse(scope.object.matrixWorld));
+    }
+
+    if (isSelected('TXYZ'))
+    {
+      if (Math.abs(eye.x) > Math.abs(eye.y) &&
+          Math.abs(eye.x) > Math.abs(eye.z))
+      {
+        currentPlane = 'YZ';
+      }
+      else if (Math.abs(eye.y) > Math.abs(eye.x) &&
+               Math.abs(eye.y) > Math.abs(eye.z))
+      {
+        currentPlane = 'XZ';
+      }
+      else
+      {
+        currentPlane = 'XY';
+      }
+    }
+    else if (isSelected('RX') || isSelected('TYZ'))
+    {
+      currentPlane = 'YZ';
+    }
+    else if (isSelected('RY') || isSelected('TXZ'))
+    {
+      currentPlane = 'XZ';
+    }
+    else if (isSelected('RZ') || isSelected('TXY'))
+    {
+      currentPlane = 'XY';
+    }
+    else if (isSelected('X'))
+    {
+      if (Math.abs(eye.y) > Math.abs(eye.z))
+      {
+        currentPlane = 'XZ';
+      }
+      else
+      {
+        currentPlane = 'XY';
+      }
+    }
+    else if (isSelected('Y'))
+    {
+      if (Math.abs(eye.x) > Math.abs(eye.z))
+      {
+        currentPlane = 'YZ';
+      }
+      else
+      {
+        currentPlane = 'XY';
+      }
+    }
+    else if (isSelected('Z'))
+    {
+      if (Math.abs(eye.x) > Math.abs(eye.y))
+      {
+        currentPlane = 'YZ';
+      }
+      else
+      {
+        currentPlane = 'XZ';
+      }
+    }
+  };
+
+  /**
+   * Window event callback
+   * @param {} event
+   */
+  function onTouchStart(event)
+  {
+    event.preventDefault();
+
+    var intersect = intersectObjects(event, pickerAxes[scope.mode].children);
+
+    // If one of the current pickers was touched
+    if(intersect)
+    {
+      if(selectedPicker !== intersect.object)
+      {
+        // Back to original color
+        if(selectedPicker !== null)
+        {
+            selectedPicker.material.color.copy(selectedColor);
+        }
+
+        selectedPicker = intersect.object;
+
+        // Save color for when it's deselected
+        selectedColor.copy(selectedPicker.material.color);
+
+        // Darken color
+        selectedPicker.material.color.offsetHSL(0, 0, -0.3);
+
+        scope.dispatchEvent(changeEvent);
+      }
+
+      scope.selected = selectedPicker.name;
+      scope.hovered = true;
+      scope.update();
+      scope.setIntersectionPlane();
+
+      var planeIntersect = intersectObjects(event,
+          [intersectionPlanes[currentPlane]]);
+
+      if(planeIntersect)
+      {
+        oldPosition.copy(scope.object.position);
+
+        oldRotationMatrix.extractRotation(scope.object.matrix);
+        worldRotationMatrix.extractRotation(scope.object.matrixWorld);
+
+        parentRotationMatrix.extractRotation(scope.object.parent.matrixWorld);
+        parentScale.getScaleFromMatrix(tempMatrix.getInverse(
+            scope.object.parent.matrixWorld));
+
+        offset.copy(planeIntersect.point);
+      }
+    }
+
+    scope.document.addEventListener('touchmove', onPointerMove, false);
+    scope.document.addEventListener('touchend', onTouchEnd, false);
+  }
+
+
+  /**
+   * Window event callback
+   * @param {} event
+   */
+  function onTouchEnd()
+  {
+    // Previously selected picker back to its color
+    if(selectedPicker)
+    {
+      selectedPicker.material.color.copy(selectedColor);
+    }
+
+    selectedPicker = null;
+
+    scope.dispatchEvent(changeEvent);
+
+    scope.selected = 'null';
+    scope.hovered = false;
+
+    scope.document.removeEventListener('touchmove', onPointerMove, false);
+    scope.document.removeEventListener('touchend', onTouchEnd, false);
+  }
+
+  /**
+   * Window event callback
+   * @param {} event
+   */
+  function onMouseHover(event)
+  {
+    event.preventDefault();
+
+    if(event.button === 0 && scope.selected === 'null')
+    {
+      var intersect = intersectObjects(event, pickerAxes[scope.mode].children);
+
+        if(intersect)
+        {
+          if(hovered !== intersect.object)
+          {
+            if(hovered !== null)
+            {
+              hovered.material.color.copy(hoveredColor);
+            }
+
+            selectedPicker = intersect.object;
+            hovered = intersect.object;
+            hoveredColor.copy(hovered.material.color);
+
+            hovered.material.color.offsetHSL(0, 0, -0.3);
+
+            scope.dispatchEvent(changeEvent);
+          }
+          scope.hovered = true;
+        }
+        else if(hovered !== null)
+        {
+          hovered.material.color.copy(hoveredColor);
+
+          hovered = null;
+
+          scope.dispatchEvent(changeEvent);
+
+          scope.hovered = false;
+      }
+    }
+    scope.document.addEventListener('mousemove', onPointerMove, false);
+    scope.document.addEventListener('mouseup', onMouseUp, false);
+  }
+
+  /**
+   * Window event callback
+   * @param {} event
+   */
+  function onMouseDown(event)
+  {
+    event.preventDefault();
+
+    if(event.button !== 0)
+    {
+      return;
+    }
+
+    var intersect = intersectObjects(event, pickerAxes[scope.mode].children);
+
+    if(intersect)
+    {
+        scope.selected = selectedPicker.name;
+
+        scope.update();
+        scope.setIntersectionPlane();
+
+        var planeIntersect = intersectObjects(event,
+            [intersectionPlanes[currentPlane]]);
+
+        if(planeIntersect)
+        {
+          oldPosition.copy(scope.object.position);
+
+          oldRotationMatrix.extractRotation(scope.object.matrix);
+          worldRotationMatrix.extractRotation(scope.object.matrixWorld);
+
+          parentRotationMatrix.extractRotation(
+              scope.object.parent.matrixWorld);
+          parentScale.getScaleFromMatrix(tempMatrix.getInverse(
+              scope.object.parent.matrixWorld));
+
+          offset.copy(planeIntersect.point);
+        }
+    }
+
+    scope.document.addEventListener('mousemove', onPointerMove, false);
+    scope.document.addEventListener('mouseup', onMouseUp, false);
+  }
+
+  /**
+   * Window event callback (mouse move and touch move)
+   * @param {} event
+   */
+  function onPointerMove(event)
+  {
+    if(scope.selected === 'null')
+    {
+      return;
+    }
+
+    event.preventDefault();
+
+    var planeIntersect = intersectObjects(event,
+        [intersectionPlanes[currentPlane]]);
+
+    if(planeIntersect)
+    {
+      point.copy(planeIntersect.point);
+
+      if((scope.mode === 'translate') && isSelected('T'))
+      {
+        point.sub(offset);
+        point.multiply(parentScale);
+
+        if (scope.space === 'local')
+        {
+          point.applyMatrix4(tempMatrix.getInverse(worldRotationMatrix));
+
+          if(!(isSelected('X')) || scope.modifierAxis.x !== 1)
+          {
+            point.x = 0;
+          }
+          if(!(isSelected('Y')) || scope.modifierAxis.y !== 1)
+          {
+            point.y = 0;
+          }
+          if(!(isSelected('Z')) || scope.modifierAxis.z !== 1)
+          {
+            point.z = 0;
+          }
+          if (isSelected('XYZ'))
+          {
+            point.set(0, 0, 0);
+          }
+          point.applyMatrix4(oldRotationMatrix);
+
+          scope.object.position.copy(oldPosition);
+          scope.object.position.add(point);
+        }
+        if (scope.space === 'world' || isSelected('XYZ'))
+        {
+          if(!(isSelected('X')) || scope.modifierAxis.x !== 1)
+          {
+            point.x = 0;
+          }
+          if(!(isSelected('Y')) || scope.modifierAxis.y !== 1)
+          {
+            point.y = 0;
+          }
+          if(!(isSelected('Z')) || scope.modifierAxis.z !== 1)
+          {
+            point.z = 0;
+          }
+
+          point.applyMatrix4(tempMatrix.getInverse(parentRotationMatrix));
+
+          scope.object.position.copy(oldPosition);
+          scope.object.position.add(point);
+
+          if(scope.snapDist)
+          {
+            if(isSelected('X'))
+            {
+              scope.object.position.x = Math.round(scope.object.position.x /
+                  scope.snapDist) * scope.snapDist;
+            }
+            if(isSelected('Y'))
+            {
+              scope.object.position.y = Math.round(scope.object.position.y /
+                  scope.snapDist) * scope.snapDist;
+            }
+            if(isSelected('Z'))
+            {
+              scope.object.position.z = Math.round(scope.object.position.z /
+                  scope.snapDist) * scope.snapDist;
+            }
+          }
+        }
+      }
+      else if((scope.mode === 'rotate') && isSelected('R'))
+      {
+        point.sub(worldPosition);
+        point.multiply(parentScale);
+        tempVector.copy(offset).sub(worldPosition);
+        tempVector.multiply(parentScale);
+
+        if(scope.selected === 'RE')
+        {
+          point.applyMatrix4(tempMatrix.getInverse(lookAtMatrix));
+          tempVector.applyMatrix4(tempMatrix.getInverse(lookAtMatrix));
+
+          rotation.set(Math.atan2(point.z, point.y),
+                       Math.atan2(point.x, point.z),
+                       Math.atan2(point.y, point.x));
+          offsetRotation.set(Math.atan2(tempVector.z, tempVector.y),
+                             Math.atan2(tempVector.x, tempVector.z),
+                             Math.atan2(tempVector.y, tempVector.x));
+
+          tempQuaternion.setFromRotationMatrix(tempMatrix.getInverse(parentRotationMatrix));
+
+          quaternionE.setFromAxisAngle(eye, rotation.z - offsetRotation.z);
+          quaternionXYZ.setFromRotationMatrix(worldRotationMatrix);
+
+          tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionE);
+          tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionXYZ);
+
+          scope.object.quaternion.copy(tempQuaternion);
+        }
+        else if(scope.selected === 'RXYZE')
+        {
+          quaternionE.setFromEuler(point.clone().cross(tempVector).normalize()); // has this ever worked?
+
+          tempQuaternion.setFromRotationMatrix(tempMatrix.getInverse(parentRotationMatrix));
+          quaternionX.setFromAxisAngle(quaternionE, - point.clone().angleTo(tempVector));
+          quaternionXYZ.setFromRotationMatrix(worldRotationMatrix);
+
+          tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionX);
+          tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionXYZ);
+
+          scope.object.quaternion.copy(tempQuaternion);
+        }
+        else
+        {
+          if (scope.space === 'local')
+          {
+            point.applyMatrix4(tempMatrix.getInverse(worldRotationMatrix));
+
+            tempVector.applyMatrix4(tempMatrix.getInverse(worldRotationMatrix));
+
+            rotation.set(Math.atan2(point.z, point.y), Math.atan2(point.x, point.z),
+                Math.atan2(point.y, point.x));
+            offsetRotation.set(Math.atan2(tempVector.z, tempVector.y), Math.atan2(
+                tempVector.x, tempVector.z), Math.atan2(tempVector.y, tempVector.x));
+
+            quaternionXYZ.setFromRotationMatrix(oldRotationMatrix);
+            quaternionX.setFromAxisAngle(unitX, rotation.x - offsetRotation.x);
+            quaternionY.setFromAxisAngle(unitY, rotation.y - offsetRotation.y);
+            quaternionZ.setFromAxisAngle(unitZ, rotation.z - offsetRotation.z);
+
+            if (scope.selected === 'RX')
+            {
+              quaternionXYZ.multiplyQuaternions(quaternionXYZ, quaternionX);
+            }
+            if (scope.selected === 'RY')
+            {
+              quaternionXYZ.multiplyQuaternions(quaternionXYZ, quaternionY);
+            }
+            if (scope.selected === 'RZ')
+            {
+              quaternionXYZ.multiplyQuaternions(quaternionXYZ, quaternionZ);
+            }
+
+            scope.object.quaternion.copy(quaternionXYZ);
+          }
+          else if (scope.space === 'world')
+          {
+            rotation.set(Math.atan2(point.z, point.y), Math.atan2(point.x, point.z),
+                Math.atan2(point.y, point.x));
+            offsetRotation.set(Math.atan2(tempVector.z, tempVector.y), Math.atan2(
+              tempVector.x, tempVector.z), Math.atan2(tempVector.y, tempVector.x));
+
+            tempQuaternion.setFromRotationMatrix(tempMatrix.getInverse(
+              parentRotationMatrix));
+
+            quaternionX.setFromAxisAngle(unitX, rotation.x - offsetRotation.x);
+            quaternionY.setFromAxisAngle(unitY, rotation.y - offsetRotation.y);
+            quaternionZ.setFromAxisAngle(unitZ, rotation.z - offsetRotation.z);
+            quaternionXYZ.setFromRotationMatrix(worldRotationMatrix);
+
+            if(scope.selected === 'RX')
+            {
+              tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionX);
+            }
+            if(scope.selected === 'RY')
+            {
+              tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionY);
+            }
+            if(scope.selected === 'RZ')
+            {
+              tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionZ);
+            }
+
+            tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionXYZ);
+
+            scope.object.quaternion.copy(tempQuaternion);
+          }
+        }
+      }
+    }
+
+    // Move light target
+    if (scope.object.children[0] &&
+       (scope.object.children[0] instanceof THREE.SpotLight ||
+        scope.object.children[0] instanceof THREE.DirectionalLight))
+    {
+      var lightObj = scope.object.children[0];
+      var dir = new THREE.Vector3(0,0,0);
+      dir.copy(scope.object.direction);
+      scope.object.localToWorld(dir);
+      lightObj.target.position.copy(dir);
+    }
+
+    scope.update();
+    scope.dispatchEvent(changeEvent);
+  }
+
+  function onMouseUp(event)
+  {
+    scope.selected = 'null';
+
+    scope.document.removeEventListener('mousemove', onPointerMove, false);
+    scope.document.removeEventListener('mouseup', onMouseUp, false);
+  }
+
+  /**
+   * intersectObjects
+   * @param {} event
+   * @param {} objects
+   * @returns {?}
+   */
+  function intersectObjects(event, objects)
+  {
+    var pointer = event.touches ? event.touches[0] : event;
+
+    var rect = domElement.getBoundingClientRect();
+    var x = (pointer.clientX - rect.left) / rect.width;
+    var y = (pointer.clientY - rect.top) / rect.height;
+    pointerVector.set((x) * 2 - 1, - (y) * 2 + 1, 0.5);
+
+    projector.unprojectVector(pointerVector, scope.camera);
+    ray.set(camPosition, pointerVector.sub(camPosition).normalize());
+
+    // checks all intersections between the ray and the objects,
+    // true to check the descendants
+    var intersections = ray.intersectObjects(objects, true);
+    return intersections[0] ? intersections[0] : false;
+  }
+
+  /**
+   * Checks if given name is currently selected
+   * @param {} name
+   * @returns {bool}
+   */
+  function isSelected(name)
+  {
+    if(scope.selected.search(name) !== -1)
+    {
+        return true;
+    }
+    else
+    {
+        return false;
+    }
+  }
+
+  /**
+   * bakeTransformations
+   * @param {} object
+   */
+  function bakeTransformations(object)
+  {
+    var tempGeometry = new THREE.Geometry();
+    THREE.GeometryUtils.merge(tempGeometry, object);
+    object.geometry = tempGeometry;
+    object.position.set(0, 0, 0);
+    object.rotation.set(0, 0, 0);
+    object.scale.set(1, 1, 1);
+  }
+};
+
+GZ3D.Manipulator.prototype = Object.create(THREE.EventDispatcher.prototype);
+
+
+/**
+ * Radial menu for an object
+ * @constructor
+ */
+GZ3D.RadialMenu = function(domElement)
+{
+  this.domElement = ( domElement !== undefined ) ? domElement : document;
+
+  this.init();
+};
+
+/**
+ * Initialize radial menu
+ */
+GZ3D.RadialMenu.prototype.init = function()
+{
+  var scale = 1.2;
+  // Distance from starting point
+  this.radius = 70*scale;
+  // Speed to spread the menu
+  this.speed = 10*scale;
+  // Icon size
+  this.bgSize = 40*scale;
+  this.bgSizeSelected = 68*scale;
+  this.highlightSize = 45*scale;
+  this.iconProportion = 0.6;
+  this.bgShape = THREE.ImageUtils.loadTexture(
+      'style/images/icon_background.png' );
+  this.layers = {
+    ICON: 0,
+    BACKGROUND : 1,
+    HIGHLIGHT : 2
+  };
+
+  // For the opening motion
+  this.moving = false;
+  this.startPosition = null;
+
+  // Either moving or already stopped
+  this.showing = false;
+
+  // Colors
+  this.selectedColor = new THREE.Color(0x22aadd);
+  this.plainColor = new THREE.Color(0x333333);
+  this.highlightColor = new THREE.Color(0x22aadd);
+  this.disabledColor = new THREE.Color(0x888888);
+
+  // Selected item
+  this.selected = null;
+
+  // Selected model
+  this.model = null;
+
+  // Object containing all items
+  this.menu = new THREE.Object3D();
+
+  // Add items to the menu
+  this.addItem('delete','style/images/trash.png');
+  this.addItem('translate','style/images/translate.png');
+  this.addItem('rotate','style/images/rotate.png');
+  this.addItem('transparent','style/images/transparent.png');
+  this.addItem('wireframe','style/images/wireframe.png');
+  this.addItem('joints','style/images/joints.png');
+
+  this.setNumberOfItems(this.menu.children.length);
+
+  // Start hidden
+  this.hide();
+};
+
+/**
+ * Hide radial menu
+ * @param {} event - event which triggered hide
+ * @param {function} callback
+ */
+GZ3D.RadialMenu.prototype.hide = function(event,callback)
+{
+  for (var i = 0; i < this.numberOfItems; i++)
+  {
+    var item = this.menu.children[i];
+
+    item.children[this.layers.ICON].visible = false;
+    item.children[this.layers.ICON].scale.set(
+        this.bgSize*this.iconProportion,
+        this.bgSize*this.iconProportion, 1.0 );
+
+    item.children[this.layers.BACKGROUND].visible = false;
+    item.children[this.layers.BACKGROUND].material.color = this.plainColor;
+    item.children[this.layers.BACKGROUND].scale.set(
+        this.bgSize,
+        this.bgSize, 1.0 );
+
+    item.children[this.layers.HIGHLIGHT].visible = false;
+  }
+
+  this.showing = false;
+  this.moving = false;
+  this.startPosition = null;
+
+  if (callback && this.model)
+  {
+    if ( this.selected )
+    {
+      callback(this.selected,this.model);
+      this.model = null;
+    }
+  }
+  this.selected = null;
+};
+
+/**
+ * Show radial menu
+ * @param {} event - event which triggered show
+ * @param {THREE.Object3D} model - model to which the menu will be attached
+ */
+GZ3D.RadialMenu.prototype.show = function(event,model)
+{
+  if (this.showing)
+  {
+    return;
+  }
+
+  this.model = model;
+
+  if (model.children[0] instanceof THREE.Light)
+  {
+    this.setNumberOfItems(3);
+  }
+  else
+  {
+    this.setNumberOfItems(6);
+  }
+
+  var pointer = this.getPointer(event);
+  this.startPosition = pointer;
+
+  this.menu.getObjectByName('transparent').isHighlighted = false;
+  this.menu.getObjectByName('wireframe').isHighlighted = false;
+  this.menu.getObjectByName('joints').isHighlighted = false;
+  this.menu.getObjectByName('joints').isDisabled = false;
+  if (this.model.viewAs === 'transparent')
+  {
+    this.menu.getObjectByName('transparent').isHighlighted = true;
+  }
+  if (this.model.viewAs === 'wireframe')
+  {
+    this.menu.getObjectByName('wireframe').isHighlighted = true;
+  }
+  if (this.model.joint === undefined || this.model.joint.length === 0)
+  {
+    this.menu.getObjectByName('joints').isDisabled = true;
+  }
+  else if (this.model.getObjectByName('JOINT_VISUAL', true))
+  {
+    this.menu.getObjectByName('joints').isHighlighted = true;
+  }
+
+  for (var i = 0; i < this.numberOfItems; i++)
+  {
+    var item = this.menu.children[i];
+
+    item.children[this.layers.ICON].visible = true;
+    item.children[this.layers.ICON].position.set(pointer.x,pointer.y,0);
+
+    item.children[this.layers.BACKGROUND].visible = true;
+    item.children[this.layers.BACKGROUND].position.set(pointer.x,pointer.y,0);
+    if (item.isDisabled)
+    {
+      item.children[this.layers.BACKGROUND].material.color = this.disabledColor;
+    }
+
+    item.children[this.layers.HIGHLIGHT].visible = item.isHighlighted;
+    item.children[this.layers.HIGHLIGHT].position.set(pointer.x,pointer.y,0);
+  }
+
+  this.moving = true;
+  this.showing = true;
+};
+
+/**
+ * Update radial menu
+ */
+GZ3D.RadialMenu.prototype.update = function()
+{
+  if (!this.moving)
+  {
+    return;
+  }
+
+  // Move outwards
+  for (var i = 0; i < this.numberOfItems; i++)
+  {
+    var item = this.menu.children[i];
+
+    var X = item.children[this.layers.ICON].position.x -
+        this.startPosition.x;
+    var Y = item.children[this.layers.ICON].position.y -
+        this.startPosition.y;
+
+    var d = Math.sqrt(Math.pow(X,2) + Math.pow(Y,2));
+
+    if ( d < this.radius)
+    {
+      X = X - ( this.speed * Math.sin( ( this.offset - i ) * Math.PI/4 ) );
+      Y = Y - ( this.speed * Math.cos( ( this.offset - i ) * Math.PI/4 ) );
+    }
+    else
+    {
+      this.moving = false;
+    }
+
+    item.children[this.layers.ICON].position.x = X + this.startPosition.x;
+    item.children[this.layers.ICON].position.y = Y + this.startPosition.y;
+
+    item.children[this.layers.BACKGROUND].position.x = X + this.startPosition.x;
+    item.children[this.layers.BACKGROUND].position.y = Y + this.startPosition.y;
+
+    item.children[this.layers.HIGHLIGHT].position.x = X + this.startPosition.x;
+    item.children[this.layers.HIGHLIGHT].position.y = Y + this.startPosition.y;
+  }
+
+};
+
+/**
+ * Get pointer (mouse or touch) coordinates inside the canvas
+ * @param {} event
+ */
+GZ3D.RadialMenu.prototype.getPointer = function(event)
+{
+  if (event.originalEvent)
+  {
+    event = event.originalEvent;
+  }
+  var pointer = event.touches ? event.touches[ 0 ] : event;
+  var rect = this.domElement.getBoundingClientRect();
+  var posX = (pointer.clientX - rect.left);
+  var posY = (pointer.clientY - rect.top);
+
+  return {x: posX, y:posY};
+};
+
+/**
+ * Movement after long press to select items on menu
+ * @param {} event
+ */
+GZ3D.RadialMenu.prototype.onLongPressMove = function(event)
+{
+  var pointer = this.getPointer(event);
+  var pointerX = pointer.x - this.startPosition.x;
+  var pointerY = pointer.y - this.startPosition.y;
+
+  var angle = Math.atan2(pointerY,pointerX);
+
+  // Check angle region
+  var region = null;
+  // bottom-left
+  if (angle > 5*Math.PI/8 && angle < 7*Math.PI/8)
+  {
+    region = 1;
+  }
+  // left
+  else if ( (angle > -8*Math.PI/8 && angle < -7*Math.PI/8) ||
+      (angle > 7*Math.PI/8 && angle < 8*Math.PI/8) )
+  {
+    region = 2;
+  }
+  // top-left
+  else if (angle > -7*Math.PI/8 && angle < -5*Math.PI/8)
+  {
+    region = 3;
+  }
+  // top
+  else if (angle > -5*Math.PI/8 && angle < -3*Math.PI/8)
+  {
+    region = 4;
+  }
+  // top-right
+  else if (angle > -3*Math.PI/8 && angle < -1*Math.PI/8)
+  {
+    region = 5;
+  }
+  // right
+  else if (angle > -1*Math.PI/8 && angle < 1*Math.PI/8)
+  {
+    region = 6;
+  }
+  // bottom-right
+  else if (angle > 1*Math.PI/8 && angle < 3*Math.PI/8)
+  {
+    region = 7;
+  }
+  // bottom
+  else if (angle > 3*Math.PI/8 && angle < 5*Math.PI/8)
+  {
+    region = 8;
+  }
+
+  // Check if any existing item is in the region
+  var Selected = region - 4 + this.offset;
+
+  if (Selected >= this.numberOfItems || Selected < 0)
+  {
+    this.selected = null;
+    Selected = null;
+  }
+
+  var counter = 0;
+  for (var i = 0; i < this.numberOfItems; i++)
+  {
+    var item = this.menu.children[i];
+
+    if (counter === Selected)
+    {
+      item.children[this.layers.ICON].scale.set(
+          this.bgSizeSelected*this.iconProportion,
+          this.bgSizeSelected*this.iconProportion, 1.0 );
+      this.selected = item.children[this.layers.ICON].name;
+
+      if (!item.isDisabled)
+      {
+        item.children[this.layers.BACKGROUND].material.color =
+            this.selectedColor;
+      }
+      item.children[this.layers.BACKGROUND].scale.set(
+          this.bgSizeSelected,
+          this.bgSizeSelected, 1.0 );
+    }
+    else
+    {
+      item.children[this.layers.ICON].scale.set(
+          this.bgSize*this.iconProportion,
+          this.bgSize*this.iconProportion, 1.0 );
+
+      item.children[this.layers.BACKGROUND].scale.set(
+          this.bgSize, this.bgSize, 1.0 );
+      if (!item.isDisabled)
+      {
+        item.children[this.layers.BACKGROUND].material.color = this.plainColor;
+      }
+    }
+    counter++;
+  }
+};
+
+/**
+ * Create an item and add it to the menu.
+ * Create them in order
+ * @param {string} type - delete/translate/rotate/transparent/wireframe/joints
+ * @param {string} iconTexture - icon's uri
+ */
+GZ3D.RadialMenu.prototype.addItem = function(type, iconTexture)
+{
+  // Icon
+  iconTexture = THREE.ImageUtils.loadTexture( iconTexture );
+
+  var iconMaterial = new THREE.SpriteMaterial( { useScreenCoordinates: true,
+      alignment: THREE.SpriteAlignment.center } );
+  iconMaterial.map = iconTexture;
+
+  var icon = new THREE.Sprite( iconMaterial );
+  icon.scale.set( this.bgSize*this.iconProportion,
+      this.bgSize*this.iconProportion, 1.0 );
+  icon.name = type;
+
+  // Background
+  var bgMaterial = new THREE.SpriteMaterial( {
+      map: this.bgShape,
+      useScreenCoordinates: true,
+      alignment: THREE.SpriteAlignment.center,
+      color: this.plainColor } );
+
+  var bg = new THREE.Sprite( bgMaterial );
+  bg.scale.set( this.bgSize, this.bgSize, 1.0 );
+
+  // Highlight
+  var highlightMaterial = new THREE.SpriteMaterial({
+      map: this.bgShape,
+      useScreenCoordinates: true,
+      alignment: THREE.SpriteAlignment.center,
+      color: this.highlightColor});
+
+  var highlight = new THREE.Sprite(highlightMaterial);
+  highlight.scale.set(this.highlightSize, this.highlightSize, 1.0);
+  highlight.visible = false;
+
+  var item = new THREE.Object3D();
+  // Respect layer order
+  item.add(icon);
+  item.add(bg);
+  item.add(highlight);
+  item.isHighlighted = false;
+  item.name = type;
+
+  this.menu.add(item);
+};
+
+/**
+ * Set number of items (different for models and lights)
+ * @param {int} number
+ */
+GZ3D.RadialMenu.prototype.setNumberOfItems = function(number)
+{
+  this.numberOfItems = number;
+  this.offset = this.numberOfItems - 1 - Math.floor(this.numberOfItems/2);
+};
+
+/**
+ * The scene is where everything is placed, from objects, to lights and cameras.
+ * @constructor
+ */
+GZ3D.Scene = function()
+{
+  this.init();
+};
+
+/**
+ * Initialize scene
+ */
+GZ3D.Scene.prototype.init = function()
+{
+  this.name = 'default';
+  this.scene = new THREE.Scene();
+  // this.scene.name = this.name;
+  this.meshes = {};
+
+  // only support one heightmap for now.
+  this.heightmap = null;
+
+  this.selectedEntity = null;
+
+  this.manipulationMode = 'view';
+  this.pointerOnMenu = false;
+
+  this.renderer = new THREE.WebGLRenderer({antialias: true });
+  this.renderer.setClearColor(0xb2b2b2, 1); // Sky
+  this.renderer.setSize( window.innerWidth, window.innerHeight);
+  // this.renderer.shadowMapEnabled = true;
+  // this.renderer.shadowMapSoft = true;
+
+  // lights
+  this.ambient = new THREE.AmbientLight( 0x666666 );
+  this.scene.add(this.ambient);
+
+  // camera
+  this.camera = new THREE.PerspectiveCamera(
+      60, window.innerWidth / window.innerHeight, 0.1, 1000 );
+  this.defaultCameraPosition = new THREE.Vector3(0, -5, 5);
+  this.resetView();
+
+  // Grid
+  this.grid = new THREE.GridHelper(10, 1);
+  this.grid.name = 'grid';
+  this.grid.position.z = 0.05;
+  this.grid.rotation.x = Math.PI * 0.5;
+  this.grid.castShadow = false;
+  this.grid.setColors(new THREE.Color( 0xCCCCCC ),new THREE.Color( 0x4D4D4D ));
+  this.grid.material.transparent = true;
+  this.grid.material.opacity = 0.5;
+  this.grid.visible = false;
+  this.scene.add(this.grid);
+
+  this.showCollisions = false;
+
+  this.spawnModel = new GZ3D.SpawnModel(
+      this, this.getDomElement());
+  // Material for simple shapes being spawned (grey transparent)
+  this.spawnedShapeMaterial = new THREE.MeshPhongMaterial(
+      {color:0xffffff, shading: THREE.SmoothShading} );
+  this.spawnedShapeMaterial.transparent = true;
+  this.spawnedShapeMaterial.opacity = 0.5;
+
+  var that = this;
+
+  // Need to use `document` instead of getDomElement in order to get events
+  // outside the webgl div element.
+  document.addEventListener( 'mouseup',
+      function(event) {that.onPointerUp(event);}, false );
+
+  this.getDomElement().addEventListener( 'mouseup',
+      function(event) {that.onPointerUp(event);}, false );
+
+  this.getDomElement().addEventListener( 'DOMMouseScroll',
+      function(event) {that.onMouseScroll(event);}, false ); //firefox
+
+  this.getDomElement().addEventListener( 'mousewheel',
+      function(event) {that.onMouseScroll(event);}, false );
+
+  document.addEventListener( 'keydown',
+      function(event) {that.onKeyDown(event);}, false );
+
+  this.getDomElement().addEventListener( 'mousedown',
+      function(event) {that.onPointerDown(event);}, false );
+  this.getDomElement().addEventListener( 'touchstart',
+      function(event) {that.onPointerDown(event);}, false );
+
+  this.getDomElement().addEventListener( 'touchend',
+      function(event) {that.onPointerUp(event);}, false );
+
+  // Handles for translating and rotating objects
+  this.modelManipulator = new GZ3D.Manipulator(this.camera, isTouchDevice,
+      this.getDomElement());
+
+  this.timeDown = null;
+
+  this.controls = new THREE.OrbitControls(this.camera);
+  this.scene.add(this.controls.targetIndicator);
+
+  this.emitter = new EventEmitter2({ verbose: true });
+
+  // SSAO
+  this.effectsEnabled = false;
+  // depth
+  var depthShader = THREE.ShaderLib[ 'depthRGBA'];
+  var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms );
+
+  this.depthMaterial = new THREE.ShaderMaterial( {
+      fragmentShader: depthShader.fragmentShader,
+      vertexShader: depthShader.vertexShader,
+      uniforms: depthUniforms } );
+  this.depthMaterial.blending = THREE.NoBlending;
+
+  // postprocessing
+  this.composer = new THREE.EffectComposer(this.renderer );
+  this.composer.addPass( new THREE.RenderPass(this.scene,this.camera));
+
+  this.depthTarget = new THREE.WebGLRenderTarget( window.innerWidth,
+      window.innerHeight, { minFilter: THREE.NearestFilter,
+      magFilter: THREE.NearestFilter, format: THREE.RGBAFormat } );
+
+  var effect = new THREE.ShaderPass( THREE.SSAOShader );
+  effect.uniforms[ 'tDepth' ].value = this.depthTarget;
+  effect.uniforms[ 'size' ].value.set( window.innerWidth, window.innerHeight );
+  effect.uniforms[ 'cameraNear' ].value = this.camera.near;
+  effect.uniforms[ 'cameraFar' ].value = this.camera.far;
+  effect.renderToScreen = true;
+  this.composer.addPass( effect );
+
+  // Radial menu (only triggered by touch)
+  this.radialMenu = new GZ3D.RadialMenu(this.getDomElement());
+  this.scene.add(this.radialMenu.menu);
+
+  // Bounding Box
+  var vertices = [
+    new THREE.Vector3(0, 0, 0),
+    new THREE.Vector3(0, 0, 0),
+    new THREE.Vector3(0, 0, 0),
+    new THREE.Vector3(0, 0, 0),
+
+    new THREE.Vector3(0, 0, 0),
+    new THREE.Vector3(0, 0, 0),
+    new THREE.Vector3(0, 0, 0),
+    new THREE.Vector3(0, 0, 0)
+  ];
+  var boxGeometry = new THREE.Geometry();
+  boxGeometry.vertices.push(
+    vertices[0], vertices[1],
+    vertices[1], vertices[2],
+    vertices[2], vertices[3],
+    vertices[3], vertices[0],
+
+    vertices[4], vertices[5],
+    vertices[5], vertices[6],
+    vertices[6], vertices[7],
+    vertices[7], vertices[4],
+
+    vertices[0], vertices[4],
+    vertices[1], vertices[5],
+    vertices[2], vertices[6],
+    vertices[3], vertices[7]
+  );
+  this.boundingBox = new THREE.Line(boxGeometry,
+      new THREE.LineBasicMaterial({color: 0xffffff}),
+      THREE.LinePieces);
+  this.boundingBox.visible = false;
+
+  // Joint visuals
+  this.jointTypes =
+      {
+        REVOLUTE: 1,
+        REVOLUTE2: 2,
+        PRISMATIC: 3,
+        UNIVERSAL: 4,
+        BALL: 5,
+        SCREW: 6,
+        GEARBOX: 7
+      };
+  this.jointAxis = new THREE.Object3D();
+  this.jointAxis.name = 'JOINT_VISUAL';
+  var geometry, material, mesh;
+
+  // XYZ
+  var XYZaxes = new THREE.Object3D();
+
+  geometry = new THREE.CylinderGeometry(0.01, 0.01, 0.3, 10, 1, false);
+
+  material = new THREE.MeshBasicMaterial({color: new THREE.Color(0xff0000)});
+  mesh = new THREE.Mesh(geometry, material);
+  mesh.position.x = 0.15;
+  mesh.rotation.z = -Math.PI/2;
+  mesh.name = 'JOINT_VISUAL';
+  XYZaxes.add(mesh);
+
+  material = new THREE.MeshBasicMaterial({color: new THREE.Color(0x00ff00)});
+  mesh = new THREE.Mesh(geometry, material);
+  mesh.position.y = 0.15;
+  mesh.name = 'JOINT_VISUAL';
+  XYZaxes.add(mesh);
+
+  material = new THREE.MeshBasicMaterial({color: new THREE.Color(0x0000ff)});
+  mesh = new THREE.Mesh(geometry, material);
+  mesh.position.z = 0.15;
+  mesh.rotation.x = Math.PI/2;
+  mesh.name = 'JOINT_VISUAL';
+  XYZaxes.add(mesh);
+
+  geometry = new THREE.CylinderGeometry(0, 0.03, 0.1, 10, 1, true);
+
+  material = new THREE.MeshBasicMaterial({color: new THREE.Color(0xff0000)});
+  mesh = new THREE.Mesh(geometry, material);
+  mesh.position.x = 0.3;
+  mesh.rotation.z = -Math.PI/2;
+  mesh.name = 'JOINT_VISUAL';
+  XYZaxes.add(mesh);
+
+  material = new THREE.MeshBasicMaterial({color: new THREE.Color(0x00ff00)});
+  mesh = new THREE.Mesh(geometry, material);
+  mesh.position.y = 0.3;
+  mesh.name = 'JOINT_VISUAL';
+  XYZaxes.add(mesh);
+
+  material = new THREE.MeshBasicMaterial({color: new THREE.Color(0x0000ff)});
+  mesh = new THREE.Mesh(geometry, material);
+  mesh.position.z = 0.3;
+  mesh.rotation.x = Math.PI/2;
+  mesh.name = 'JOINT_VISUAL';
+  XYZaxes.add(mesh);
+
+  this.jointAxis['XYZaxes'] = XYZaxes;
+
+  var mainAxis = new THREE.Object3D();
+
+  material = new THREE.MeshLambertMaterial();
+  material.color = new THREE.Color(0xffff00);
+  material.ambient = material.color;
+
+  geometry = new THREE.CylinderGeometry(0.02, 0.02, 0.25, 36, 1, false);
+
+  mesh = new THREE.Mesh(geometry, material);
+  mesh.position.z = -0.175;
+  mesh.rotation.x = Math.PI/2;
+  mesh.name = 'JOINT_VISUAL';
+  mainAxis.add(mesh);
+
+  geometry = new THREE.CylinderGeometry(0, 0.035, 0.1, 36, 1, false);
+
+  mesh = new THREE.Mesh(geometry, material);
+  mesh.rotation.x = Math.PI/2;
+  mesh.name = 'JOINT_VISUAL';
+  mainAxis.add(mesh);
+
+  this.jointAxis['mainAxis'] = mainAxis;
+
+  var rotAxis = new THREE.Object3D();
+
+  geometry = new THREE.TorusGeometry(0.04, 0.006, 10, 36, Math.PI * 3/2);
+
+  mesh = new THREE.Mesh(geometry, material);
+  mesh.name = 'JOINT_VISUAL';
+  rotAxis.add(mesh);
+
+  geometry = new THREE.CylinderGeometry(0.015, 0, 0.025, 10, 1, false);
+
+  mesh = new THREE.Mesh(geometry, material);
+  mesh.position.y = -0.04;
+  mesh.rotation.z = Math.PI/2;
+  mesh.name = 'JOINT_VISUAL';
+  rotAxis.add(mesh);
+
+  this.jointAxis['rotAxis'] = rotAxis;
+
+  var transAxis = new THREE.Object3D();
+
+  geometry = new THREE.CylinderGeometry(0.01, 0.01, 0.1, 10, 1, true);
+
+  mesh = new THREE.Mesh(geometry, material);
+  mesh.position.x = 0.03;
+  mesh.position.y = 0.03;
+  mesh.position.z = -0.15;
+  mesh.rotation.x = Math.PI/2;
+  mesh.name = 'JOINT_VISUAL';
+  transAxis.add(mesh);
+
+  geometry = new THREE.CylinderGeometry(0.02, 0, 0.0375, 10, 1, false);
+
+  mesh = new THREE.Mesh(geometry, material);
+  mesh.position.x = 0.03;
+  mesh.position.y = 0.03;
+  mesh.position.z = -0.15 + 0.05;
+  mesh.rotation.x = -Math.PI/2;
+  mesh.name = 'JOINT_VISUAL';
+  transAxis.add(mesh);
+
+  mesh = new THREE.Mesh(geometry, material);
+  mesh.position.x = 0.03;
+  mesh.position.y = 0.03;
+  mesh.position.z = -0.15 - 0.05;
+  mesh.rotation.x = Math.PI/2;
+  mesh.name = 'JOINT_VISUAL';
+  transAxis.add(mesh);
+
+  this.jointAxis['transAxis'] = transAxis;
+
+  var screwAxis = new THREE.Object3D();
+
+  mesh = new THREE.Mesh(geometry, material);
+  mesh.position.x = -0.04;
+  mesh.position.z = -0.11;
+  mesh.rotation.z = -Math.PI/4;
+  mesh.rotation.x = -Math.PI/10;
+  mesh.name = 'JOINT_VISUAL';
+  screwAxis.add(mesh);
+
+  var radius = 0.04;
+  var length = 0.02;
+  var curve = new THREE.SplineCurve3([new THREE.Vector3(radius, 0, 0*length),
+                                      new THREE.Vector3(0, radius, 1*length),
+                                      new THREE.Vector3(-radius, 0, 2*length),
+                                      new THREE.Vector3(0, -radius, 3*length),
+                                      new THREE.Vector3(radius, 0, 4*length),
+                                      new THREE.Vector3(0, radius, 5*length),
+                                      new THREE.Vector3(-radius, 0, 6*length)]);
+  geometry = new THREE.TubeGeometry(curve, 36, 0.01, 10, false, false);
+
+  mesh = new THREE.Mesh(geometry, material);
+  mesh.position.z = -0.23;
+  mesh.name = 'JOINT_VISUAL';
+  screwAxis.add(mesh);
+
+  this.jointAxis['screwAxis'] = screwAxis;
+
+  var ballVisual = new THREE.Object3D();
+
+  geometry = new THREE.SphereGeometry(0.06);
+
+  mesh = new THREE.Mesh(geometry, material);
+  mesh.name = 'JOINT_VISUAL';
+  ballVisual.add(mesh);
+
+  this.jointAxis['ballVisual'] = ballVisual;
+};
+
+GZ3D.Scene.prototype.initScene = function()
+{
+  guiEvents.emit('show_grid', 'show');
+
+  // create a sun light
+  var obj = this.createLight(3, new THREE.Color(0.8, 0.8, 0.8), 0.9,
+       {position: {x:0, y:0, z:10}, orientation: {x:0, y:0, z:0, w:1}},
+       null, true, 'sun', {x: 0.5, y: 0.1, z: -0.9});
+
+  this.add(obj);
+};
+
+GZ3D.Scene.prototype.setSDFParser = function(sdfParser)
+{
+  this.spawnModel.sdfParser = sdfParser;
+};
+
+/**
+ * Window event callback
+ * @param {} event - mousedown or touchdown events
+ */
+GZ3D.Scene.prototype.onPointerDown = function(event)
+{
+  event.preventDefault();
+
+  if (this.spawnModel.active)
+  {
+    return;
+  }
+
+  var mainPointer = true;
+  var pos;
+  if (event.touches)
+  {
+    if (event.touches.length === 1)
+    {
+      pos = new THREE.Vector2(
+          event.touches[0].clientX, event.touches[0].clientY);
+    }
+    else if (event.touches.length === 2)
+    {
+      pos = new THREE.Vector2(
+          (event.touches[0].clientX + event.touches[1].clientX)/2,
+          (event.touches[0].clientY + event.touches[1].clientY)/2);
+    }
+    else
+    {
+      return;
+    }
+  }
+  else
+  {
+    pos = new THREE.Vector2(
+          event.clientX, event.clientY);
+    if (event.which !== 1)
+    {
+      mainPointer = false;
+    }
+  }
+
+  var intersect = new THREE.Vector3();
+  var model = this.getRayCastModel(pos, intersect);
+
+  if (intersect)
+  {
+    this.controls.target = intersect;
+  }
+
+  // Cancel in case of multitouch
+  if (event.touches && event.touches.length !== 1)
+  {
+    return;
+  }
+
+  // Manipulation modes
+  // Model found
+  if (model)
+  {
+    // Do nothing to the floor plane
+    if (model.name === 'plane')
+    {
+      this.timeDown = new Date().getTime();
+    }
+    else if (this.modelManipulator.pickerNames.indexOf(model.name) >= 0)
+    {
+      // Do not attach manipulator to itself
+    }
+    // Attach manipulator to model
+    else if (model.name !== '')
+    {
+      if (mainPointer && model.parent === this.scene)
+      {
+        this.selectEntity(model);
+      }
+    }
+    // Manipulator pickers, for mouse
+    else if (this.modelManipulator.hovered)
+    {
+      this.modelManipulator.update();
+      this.modelManipulator.object.updateMatrixWorld();
+    }
+    // Sky
+    else
+    {
+      this.timeDown = new Date().getTime();
+    }
+  }
+  // Plane from below, for example
+  else
+  {
+    this.timeDown = new Date().getTime();
+  }
+};
+
+/**
+ * Window event callback
+ * @param {} event - mouseup or touchend events
+ */
+GZ3D.Scene.prototype.onPointerUp = function(event)
+{
+  event.preventDefault();
+
+  // Clicks (<150ms) outside any models trigger view mode
+  var millisecs = new Date().getTime();
+  if (millisecs - this.timeDown < 150)
+  {
+    this.setManipulationMode('view');
+    $( '#view-mode' ).click();
+    $('input[type="radio"]').checkboxradio('refresh');
+  }
+  this.timeDown = null;
+};
+
+/**
+ * Window event callback
+ * @param {} event - mousescroll event
+ */
+GZ3D.Scene.prototype.onMouseScroll = function(event)
+{
+  event.preventDefault();
+
+  var pos = new THREE.Vector2(event.clientX, event.clientY);
+
+  var intersect = new THREE.Vector3();
+  var model = this.getRayCastModel(pos, intersect);
+
+  if (intersect)
+  {
+    this.controls.target = intersect;
+  }
+};
+
+/**
+ * Window event callback
+ * @param {} event - keydown events
+ */
+GZ3D.Scene.prototype.onKeyDown = function(event)
+{
+  if (event.shiftKey)
+  {
+    // + and - for zooming
+    if (event.keyCode === 187 || event.keyCode === 189)
+    {
+      var pos = new THREE.Vector2(window.innerWidth/2.0,
+          window.innerHeight/2.0);
+
+      var intersect = new THREE.Vector3();
+      var model = this.getRayCastModel(pos, intersect);
+
+      if (intersect)
+      {
+        this.controls.target = intersect;
+      }
+
+      if (event.keyCode === 187)
+      {
+        this.controls.dollyOut();
+      }
+      else
+      {
+        this.controls.dollyIn();
+      }
+    }
+  }
+
+  // DEL to delete entities
+  if (event.keyCode === 46)
+  {
+    if (this.selectedEntity)
+    {
+      guiEvents.emit('delete_entity');
+    }
+  }
+
+  // F2 for turning on effects
+  if (event.keyCode === 113)
+  {
+    this.effectsEnabled = !this.effectsEnabled;
+  }
+
+  // Esc/R/T for changing manipulation modes
+  if (event.keyCode === 27) // Esc
+  {
+    $( '#view-mode' ).click();
+    $('input[type="radio"]').checkboxradio('refresh');
+  }
+  if (event.keyCode === 82) // R
+  {
+    $( '#rotate-mode' ).click();
+    $('input[type="radio"]').checkboxradio('refresh');
+  }
+  if (event.keyCode === 84) // T
+  {
+    $( '#translate-mode' ).click();
+    $('input[type="radio"]').checkboxradio('refresh');
+  }
+};
+
+/**
+ * Check if there's a model immediately under canvas coordinate 'pos'
+ * @param {THREE.Vector2} pos - Canvas coordinates
+ * @param {THREE.Vector3} intersect - Empty at input,
+ * contains point of intersection in 3D world coordinates at output
+ * @returns {THREE.Object3D} model - Intercepted model closest to the camera
+ */
+GZ3D.Scene.prototype.getRayCastModel = function(pos, intersect)
+{
+  var projector = new THREE.Projector();
+  var vector = new THREE.Vector3(
+      ((pos.x - this.renderer.domElement.offsetLeft)
+      / window.innerWidth) * 2 - 1,
+      -((pos.y - this.renderer.domElement.offsetTop)
+      / window.innerHeight) * 2 + 1, 1);
+  projector.unprojectVector(vector, this.camera);
+  var ray = new THREE.Raycaster( this.camera.position,
+      vector.sub(this.camera.position).normalize() );
+
+  var allObjects = [];
+  this.scene.getDescendants(allObjects);
+  var objects = ray.intersectObjects(allObjects);
+
+  var model;
+  var point;
+  if (objects.length > 0)
+  {
+    modelsloop:
+    for (var i = 0; i < objects.length; ++i)
+    {
+      model = objects[i].object;
+      if (model.name.indexOf('_lightHelper') >= 0)
+      {
+        model = model.parent;
+        break;
+      }
+
+      if (!this.modelManipulator.hovered &&
+          (model.name === 'plane'))
+      {
+        // model = null;
+        point = objects[i].point;
+        break;
+      }
+
+      if (model.name === 'grid' || model.name === 'boundingBox' ||
+          model.name === 'JOINT_VISUAL')
+      {
+        point = objects[i].point;
+        model = null;
+        continue;
+      }
+
+      while (model.parent !== this.scene)
+      {
+        // Select current mode's handle
+        if (model.parent.parent === this.modelManipulator.gizmo &&
+            ((this.manipulationMode === 'translate' &&
+              model.name.indexOf('T') >=0) ||
+             (this.manipulationMode === 'rotate' &&
+               model.name.indexOf('R') >=0)))
+        {
+          break modelsloop;
+        }
+        model = model.parent;
+      }
+
+      if (model === this.radialMenu.menu)
+      {
+        continue;
+      }
+
+      if (model.name.indexOf('COLLISION_VISUAL') >= 0)
+      {
+        model = null;
+        continue;
+      }
+
+      if (this.modelManipulator.hovered)
+      {
+        if (model === this.modelManipulator.gizmo)
+        {
+          break;
+        }
+      }
+      else if (model.name !== '')
+      {
+        point = objects[i].point;
+        break;
+      }
+    }
+  }
+  if (point)
+  {
+    intersect.x = point.x;
+    intersect.y = point.y;
+    intersect.z = point.z;
+  }
+  return model;
+};
+
+/**
+ * Get dom element
+ * @returns {domElement}
+ */
+GZ3D.Scene.prototype.getDomElement = function()
+{
+  return this.renderer.domElement;
+};
+
+/**
+ * Render scene
+ */
+GZ3D.Scene.prototype.render = function()
+{
+  // Kill camera control when:
+  // -manipulating
+  // -using radial menu
+  // -pointer over menus
+  // -spawning
+  if (this.modelManipulator.hovered ||
+      this.radialMenu.showing ||
+      this.pointerOnMenu ||
+      this.spawnModel.active)
+  {
+    this.controls.enabled = false;
+    this.controls.update();
+  }
+  else
+  {
+    this.controls.enabled = true;
+    this.controls.update();
+  }
+
+  this.modelManipulator.update();
+  this.radialMenu.update();
+
+  if (this.effectsEnabled)
+  {
+    this.scene.overrideMaterial = this.depthMaterial;
+    this.renderer.render(this.scene, this.camera, this.depthTarget);
+    this.scene.overrideMaterial = null;
+    this.composer.render();
+  }
+  else
+  {
+    this.renderer.render(this.scene, this.camera);
+  }
+};
+
+/**
+ * Set window size
+ * @param {double} width
+ * @param {double} height
+ */
+GZ3D.Scene.prototype.setWindowSize = function(width, height)
+{
+  this.camera.aspect = width / height;
+  this.camera.updateProjectionMatrix();
+
+  this.renderer.setSize( width, height);
+  this.render();
+};
+
+/**
+ * Add object to the scene
+ * @param {THREE.Object3D} model
+ */
+GZ3D.Scene.prototype.add = function(model)
+{
+  model.viewAs = 'normal';
+  this.scene.add(model);
+};
+
+/**
+ * Remove object from the scene
+ * @param {THREE.Object3D} model
+ */
+GZ3D.Scene.prototype.remove = function(model)
+{
+  this.scene.remove(model);
+};
+
+/**
+ * Returns the object which has the given name
+ * @param {string} name
+ * @returns {THREE.Object3D} model
+ */
+GZ3D.Scene.prototype.getByName = function(name)
+{
+  return this.scene.getObjectByName(name, true);
+};
+
+/**
+ * Update a model's pose
+ * @param {THREE.Object3D} model
+ * @param {} position
+ * @param {} orientation
+ */
+GZ3D.Scene.prototype.updatePose = function(model, position, orientation)
+{
+  if (this.modelManipulator && this.modelManipulator.object &&
+      this.modelManipulator.hovered)
+  {
+    return;
+  }
+
+  this.setPose(model, position, orientation);
+};
+
+/**
+ * Set a model's pose
+ * @param {THREE.Object3D} model
+ * @param {} position
+ * @param {} orientation
+ */
+GZ3D.Scene.prototype.setPose = function(model, position, orientation)
+{
+  model.position.x = position.x;
+  model.position.y = position.y;
+  model.position.z = position.z;
+  model.quaternion.w = orientation.w;
+  model.quaternion.x = orientation.x;
+  model.quaternion.y = orientation.y;
+  model.quaternion.z = orientation.z;
+};
+
+GZ3D.Scene.prototype.removeAll = function()
+{
+  while(this.scene.children.length > 0)
+  {
+    this.scene.remove(this.scene.children[0]);
+  }
+};
+
+/**
+ * Create plane
+ * @param {double} normalX
+ * @param {double} normalY
+ * @param {double} normalZ
+ * @param {double} width
+ * @param {double} height
+ * @returns {THREE.Mesh}
+ */
+GZ3D.Scene.prototype.createPlane = function(normalX, normalY, normalZ,
+    width, height)
+{
+  var geometry = new THREE.PlaneGeometry(width, height, 1, 1);
+  var material =  new THREE.MeshPhongMaterial(
+      {color:0xbbbbbb, shading: THREE.SmoothShading} ); // Later Gazebo/Grey
+  var mesh = new THREE.Mesh(geometry, material);
+  var normal = new THREE.Vector3(normalX, normalY, normalZ);
+  var cross = normal.crossVectors(normal, mesh.up);
+  mesh.rotation = normal.applyAxisAngle(cross, -(normal.angleTo(mesh.up)));
+  mesh.name = 'plane';
+  mesh.receiveShadow = true;
+  return mesh;
+};
+
+/**
+ * Create sphere
+ * @param {double} radius
+ * @returns {THREE.Mesh}
+ */
+GZ3D.Scene.prototype.createSphere = function(radius)
+{
+  var geometry = new THREE.SphereGeometry(radius, 32, 32);
+  var mesh = new THREE.Mesh(geometry, this.spawnedShapeMaterial);
+  return mesh;
+};
+
+/**
+ * Create cylinder
+ * @param {double} radius
+ * @param {double} length
+ * @returns {THREE.Mesh}
+ */
+GZ3D.Scene.prototype.createCylinder = function(radius, length)
+{
+  var geometry = new THREE.CylinderGeometry(radius, radius, length, 32, 1,
+      false);
+  var mesh = new THREE.Mesh(geometry, this.spawnedShapeMaterial);
+  mesh.rotation.x = Math.PI * 0.5;
+  return mesh;
+};
+
+/**
+ * Create box
+ * @param {double} width
+ * @param {double} height
+ * @param {double} depth
+ * @returns {THREE.Mesh}
+ */
+GZ3D.Scene.prototype.createBox = function(width, height, depth)
+{
+  var geometry = new THREE.CubeGeometry(width, height, depth, 1, 1, 1);
+
+  // Fix UVs so textures are mapped in a way that is consistent to gazebo
+  // Some face uvs need to be rotated clockwise, while others anticlockwise
+  // After updating to threejs rev 62, geometries changed from quads (6 faces)
+  // to triangles (12 faces).
+  geometry.dynamic = true;
+  var faceUVFixA = [1, 4, 5];
+  var faceUVFixB = [0];
+  for (var i = 0; i < faceUVFixA.length; ++i)
+  {
+    var idx = faceUVFixA[i]*2;
+    var uva = geometry.faceVertexUvs[0][idx][0];
+    geometry.faceVertexUvs[0][idx][0] = geometry.faceVertexUvs[0][idx][1];
+    geometry.faceVertexUvs[0][idx][1] = geometry.faceVertexUvs[0][idx+1][1];
+    geometry.faceVertexUvs[0][idx][2] = uva;
+
+    geometry.faceVertexUvs[0][idx+1][0] = geometry.faceVertexUvs[0][idx+1][1];
+    geometry.faceVertexUvs[0][idx+1][1] = geometry.faceVertexUvs[0][idx+1][2];
+    geometry.faceVertexUvs[0][idx+1][2] = geometry.faceVertexUvs[0][idx][2];
+  }
+  for (var ii = 0; ii < faceUVFixB.length; ++ii)
+  {
+    var idxB = faceUVFixB[ii]*2;
+    var uvc = geometry.faceVertexUvs[0][idxB][0];
+    geometry.faceVertexUvs[0][idxB][0] = geometry.faceVertexUvs[0][idxB][2];
+    geometry.faceVertexUvs[0][idxB][1] = uvc;
+    geometry.faceVertexUvs[0][idxB][2] =  geometry.faceVertexUvs[0][idxB+1][1];
+
+    geometry.faceVertexUvs[0][idxB+1][2] = geometry.faceVertexUvs[0][idxB][2];
+    geometry.faceVertexUvs[0][idxB+1][1] = geometry.faceVertexUvs[0][idxB+1][0];
+    geometry.faceVertexUvs[0][idxB+1][0] = geometry.faceVertexUvs[0][idxB][1];
+  }
+  geometry.uvsNeedUpdate = true;
+
+  var mesh = new THREE.Mesh(geometry, this.spawnedShapeMaterial);
+  mesh.castShadow = true;
+  return mesh;
+};
+
+/**
+ * Create light
+ * @param {} type - 1: point, 2: spot, 3: directional
+ * @param {} diffuse
+ * @param {} intensity
+ * @param {} pose
+ * @param {} distance
+ * @param {} cast_shadows
+ * @param {} name
+ * @param {} direction
+ * @param {} specular
+ * @param {} attenuation_constant
+ * @param {} attenuation_linear
+ * @param {} attenuation_quadratic
+ * @returns {THREE.Object3D}
+ */
+GZ3D.Scene.prototype.createLight = function(type, diffuse, intensity, pose,
+    distance, cast_shadows, name, direction, specular, attenuation_constant,
+    attenuation_linear, attenuation_quadratic)
+{
+  var obj = new THREE.Object3D();
+  var color = new THREE.Color();
+
+  if (typeof(diffuse) === 'undefined')
+  {
+    diffuse = 0xffffff;
+  }
+  else if (typeof(diffuse) !== THREE.Color)
+  {
+    color.r = diffuse.r;
+    color.g = diffuse.g;
+    color.b = diffuse.b;
+    diffuse = color.clone();
+  }
+  else if (typeof(specular) !== THREE.Color)
+  {
+    color.r = specular.r;
+    color.g = specular.g;
+    color.b = specular.b;
+    specular = color.clone();
+  }
+
+  var matrixWorld;
+
+  if (pose)
+  {
+    var quaternion = new THREE.Quaternion(
+        pose.orientation.x,
+        pose.orientation.y,
+        pose.orientation.z,
+        pose.orientation.w);
+
+    var translation = new THREE.Vector3(
+        pose.position.x,
+        pose.position.y,
+        pose.position.z);
+
+    matrixWorld = new THREE.Matrix4();
+    matrixWorld.compose(translation, quaternion, new THREE.Vector3(1,1,1));
+
+    this.setPose(obj, pose.position, pose.orientation);
+    obj.matrixWorldNeedsUpdate = true;
+  }
+
+  var elements;
+  if (type === 1)
+  {
+    elements = this.createPointLight(obj, diffuse, intensity,
+        distance, cast_shadows);
+  }
+  else if (type === 2)
+  {
+    elements = this.createSpotLight(obj, diffuse, intensity,
+        distance, cast_shadows);
+  }
+  else if (type === 3)
+  {
+    elements = this.createDirectionalLight(obj, diffuse, intensity,
+        cast_shadows);
+  }
+
+  var lightObj = elements[0];
+  var helper = elements[1];
+
+  if (name)
+  {
+    lightObj.name = name;
+    obj.name = name;
+    helper.name = name + '_lightHelper';
+  }
+
+  if (direction)
+  {
+    var dir = new THREE.Vector3(direction.x, direction.y,
+        direction.z);
+
+    obj.direction = new THREE.Vector3();
+    obj.direction.copy(dir);
+
+    dir.applyMatrix4(matrixWorld); // localToWorld
+    lightObj.target.position.copy(dir);
+  }
+
+  // Add properties which exist on the server but have no meaning on THREE.js
+  obj.serverProperties = {};
+  obj.serverProperties.specular = specular;
+  obj.serverProperties.attenuation_constant = attenuation_constant;
+  obj.serverProperties.attenuation_linear = attenuation_linear;
+  obj.serverProperties.attenuation_quadratic = attenuation_quadratic;
+
+  obj.add(lightObj);
+  obj.add(helper);
+  return obj;
+};
+
+/**
+ * Create point light - called by createLight
+ * @param {} obj - light object
+ * @param {} color
+ * @param {} intensity
+ * @param {} distance
+ * @param {} cast_shadows
+ * @returns {Object.<THREE.Light, THREE.Mesh>}
+ */
+GZ3D.Scene.prototype.createPointLight = function(obj, color, intensity,
+    distance, cast_shadows)
+{
+  if (typeof(intensity) === 'undefined')
+  {
+    intensity = 0.5;
+  }
+
+  var lightObj = new THREE.PointLight(color, intensity);
+  lightObj.shadowDarkness = 0.3;
+
+  if (distance)
+  {
+    lightObj.distance = distance;
+  }
+  if (cast_shadows)
+  {
+    lightObj.castShadow = cast_shadows;
+  }
+
+  var helperGeometry = new THREE.OctahedronGeometry(0.25, 0);
+  helperGeometry.applyMatrix(new THREE.Matrix4().makeRotationX(Math.PI/2));
+  var helperMaterial = new THREE.MeshBasicMaterial(
+        {wireframe: true, color: 0x00ff00});
+  var helper = new THREE.Mesh(helperGeometry, helperMaterial);
+
+  return [lightObj, helper];
+};
+
+/**
+ * Create spot light - called by createLight
+ * @param {} obj - light object
+ * @param {} color
+ * @param {} intensity
+ * @param {} distance
+ * @param {} cast_shadows
+ * @returns {Object.<THREE.Light, THREE.Mesh>}
+ */
+GZ3D.Scene.prototype.createSpotLight = function(obj, color, intensity,
+    distance, cast_shadows)
+{
+  if (typeof(intensity) === 'undefined')
+  {
+    intensity = 1;
+  }
+  if (typeof(distance) === 'undefined')
+  {
+    distance = 20;
+  }
+
+  var lightObj = new THREE.SpotLight(color, intensity);
+  lightObj.distance = distance;
+  lightObj.position.set(0,0,0);
+  lightObj.shadowDarkness = 0.3;
+
+  if (cast_shadows)
+  {
+    lightObj.castShadow = cast_shadows;
+  }
+
+  var helperGeometry = new THREE.CylinderGeometry(0, 0.3, 0.2, 4, 1, true);
+  helperGeometry.applyMatrix(new THREE.Matrix4().makeRotationX(Math.PI/2));
+  helperGeometry.applyMatrix(new THREE.Matrix4().makeRotationZ(Math.PI/4));
+  var helperMaterial = new THREE.MeshBasicMaterial(
+        {wireframe: true, color: 0x00ff00});
+  var helper = new THREE.Mesh(helperGeometry, helperMaterial);
+
+  return [lightObj, helper];
+
+};
+
+/**
+ * Create directional light - called by createLight
+ * @param {} obj - light object
+ * @param {} color
+ * @param {} intensity
+ * @param {} cast_shadows
+ * @returns {Object.<THREE.Light, THREE.Mesh>}
+ */
+GZ3D.Scene.prototype.createDirectionalLight = function(obj, color, intensity,
+    cast_shadows)
+{
+  if (typeof(intensity) === 'undefined')
+  {
+    intensity = 1;
+  }
+
+  var lightObj = new THREE.DirectionalLight(color, intensity);
+  lightObj.shadowCameraNear = 1;
+  lightObj.shadowCameraFar = 50;
+  lightObj.shadowMapWidth = 4094;
+  lightObj.shadowMapHeight = 4094;
+  lightObj.shadowCameraVisible = false;
+  lightObj.shadowCameraBottom = -100;
+  lightObj.shadowCameraLeft = -100;
+  lightObj.shadowCameraRight = 100;
+  lightObj.shadowCameraTop = 100;
+  lightObj.shadowBias = 0.0001;
+  lightObj.position.set(0,0,0);
+  lightObj.shadowDarkness = 0.3;
+
+  if (cast_shadows)
+  {
+    lightObj.castShadow = cast_shadows;
+  }
+
+  var helperGeometry = new THREE.Geometry();
+  helperGeometry.vertices.push(new THREE.Vector3(-0.5, -0.5, 0));
+  helperGeometry.vertices.push(new THREE.Vector3(-0.5,  0.5, 0));
+  helperGeometry.vertices.push(new THREE.Vector3(-0.5,  0.5, 0));
+  helperGeometry.vertices.push(new THREE.Vector3( 0.5,  0.5, 0));
+  helperGeometry.vertices.push(new THREE.Vector3( 0.5,  0.5, 0));
+  helperGeometry.vertices.push(new THREE.Vector3( 0.5, -0.5, 0));
+  helperGeometry.vertices.push(new THREE.Vector3( 0.5, -0.5, 0));
+  helperGeometry.vertices.push(new THREE.Vector3(-0.5, -0.5, 0));
+  helperGeometry.vertices.push(new THREE.Vector3(   0,    0, 0));
+  helperGeometry.vertices.push(new THREE.Vector3(   0,    0, -0.5));
+  var helperMaterial = new THREE.LineBasicMaterial({color: 0x00ff00});
+  var helper = new THREE.Line(helperGeometry, helperMaterial,
+      THREE.LinePieces);
+
+  return [lightObj, helper];
+};
+
+/**
+ * Create roads
+ * @param {} points
+ * @param {} width
+ * @param {} texture
+ * @returns {THREE.Mesh}
+ */
+GZ3D.Scene.prototype.createRoads = function(points, width, texture)
+{
+  var geometry = new THREE.Geometry();
+  geometry.dynamic = true;
+  var texCoord = 0.0;
+  var texMaxLen = width;
+  var factor = 1.0;
+  var curLen = 0.0;
+  var tangent = new THREE.Vector3(0,0,0);
+  var pA;
+  var pB;
+  var prevPt = new THREE.Vector3(0,0,0);
+  var prevTexCoord;
+  var texCoords = [];
+  var j = 0;
+  for (var i = 0; i < points.length; ++i)
+  {
+    var pt0 =  new THREE.Vector3(points[i].x, points[i].y,
+        points[i].z);
+    var pt1;
+    if (i !== points.length - 1)
+    {
+      pt1 =  new THREE.Vector3(points[i+1].x, points[i+1].y,
+          points[i+1].z);
+    }
+    factor = 1.0;
+    if (i > 0)
+    {
+      curLen += pt0.distanceTo(prevPt);
+    }
+    texCoord = curLen/texMaxLen;
+    if (i === 0)
+    {
+      tangent.x = pt1.x;
+      tangent.y = pt1.y;
+      tangent.z = pt1.z;
+      tangent.sub(pt0);
+      tangent.normalize();
+    }
+    else if (i === points.length - 1)
+    {
+      tangent.x = pt0.x;
+      tangent.y = pt0.y;
+      tangent.z = pt0.z;
+      tangent.sub(prevPt);
+      tangent.normalize();
+    }
+    else
+    {
+      var v0 = new THREE.Vector3(0,0,0);
+      var v1 = new THREE.Vector3(0,0,0);
+      v0.x = pt0.x;
+      v0.y = pt0.y;
+      v0.z = pt0.z;
+      v0.sub(prevPt);
+      v0.normalize();
+
+      v1.x = pt1.x;
+      v1.y = pt1.y;
+      v1.z = pt1.z;
+      v1.sub(pt0);
+      v1.normalize();
+
+      var dot = v0.dot(v1*-1);
+
+      tangent.x = pt1.x;
+      tangent.y = pt1.y;
+      tangent.z = pt1.z;
+      tangent.sub(prevPt);
+      tangent.normalize();
+
+      if (dot > -0.97 && dot < 0.97)
+      {
+        factor = 1.0 / Math.sin(Math.acos(dot) * 0.5);
+      }
+    }
+    var theta = Math.atan2(tangent.x, -tangent.y);
+    pA = new THREE.Vector3(pt0.x,pt0.y,pt0.z);
+    pB = new THREE.Vector3(pt0.x,pt0.y,pt0.z);
+    var w = (width * factor)*0.5;
+    pA.x += Math.cos(theta) * w;
+    pA.y += Math.sin(theta) * w;
+    pB.x -= Math.cos(theta) * w;
+    pB.y -= Math.sin(theta) * w;
+
+    geometry.vertices.push(pA);
+    geometry.vertices.push(pB);
+
+    texCoords.push([0, texCoord]);
+    texCoords.push([1, texCoord]);
+
+    // draw triangle strips
+    if (i > 0)
+    {
+      geometry.faces.push(new THREE.Face3(j, j+1, j+2,
+        new THREE.Vector3(0, 0, 1)));
+      geometry.faceVertexUvs[0].push(
+          [new THREE.Vector2(texCoords[j][0], texCoords[j][1]),
+           new THREE.Vector2(texCoords[j+1][0], texCoords[j+1][1]),
+           new THREE.Vector2(texCoords[j+2][0], texCoords[j+2][1])]);
+      j++;
+
+      geometry.faces.push(new THREE.Face3(j, j+2, j+1,
+        new THREE.Vector3(0, 0, 1)));
+      geometry.faceVertexUvs[0].push(
+          [new THREE.Vector2(texCoords[j][0], texCoords[j][1]),
+           new THREE.Vector2(texCoords[j+2][0], texCoords[j+2][1]),
+           new THREE.Vector2(texCoords[j+1][0], texCoords[j+1][1])]);
+      j++;
+
+    }
+
+    prevPt.x = pt0.x;
+    prevPt.y = pt0.y;
+    prevPt.z = pt0.z;
+
+    prevTexCoord = texCoord;
+  }
+
+  // geometry.computeTangents();
+  geometry.computeFaceNormals();
+
+  geometry.verticesNeedUpdate = true;
+  geometry.uvsNeedUpdate = true;
+
+
+  var material =  new THREE.MeshPhongMaterial();
+
+ /* var ambient = mat['ambient'];
+  if (ambient)
+  {
+    material.ambient.setRGB(ambient[0], ambient[1], ambient[2]);
+  }
+  var diffuse = mat['diffuse'];
+  if (diffuse)
+  {
+    material.color.setRGB(diffuse[0], diffuse[1], diffuse[2]);
+  }
+  var specular = mat['specular'];
+  if (specular)
+  {
+    material.specular.setRGB(specular[0], specular[1], specular[2]);
+  }*/
+  if (texture)
+  {
+    var tex = THREE.ImageUtils.loadTexture(texture);
+    tex.wrapS = tex.wrapT = THREE.RepeatWrapping;
+    material.map = tex;
+  }
+
+  var mesh = new THREE.Mesh(geometry, material);
+  mesh.castShadow = false;
+  return mesh;
+};
+
+/**
+ * Load heightmap
+ * @param {} heights
+ * @param {} width
+ * @param {} height
+ * @param {} segmentWidth
+ * @param {} segmentHeight
+ * @param {} textures
+ * @param {} blends
+ * @param {} parent
+ */
+GZ3D.Scene.prototype.loadHeightmap = function(heights, width, height,
+    segmentWidth, segmentHeight, origin, textures, blends, parent)
+{
+  if (this.heightmap)
+  {
+    return;
+  }
+  // unfortunately large heightmaps kills the fps and freeze everything so
+  // we have to scale it down
+  var scale = 1;
+  var maxHeightmapWidth = 256;
+  var maxHeightmapHeight = 256;
+
+  if ((segmentWidth-1) > maxHeightmapWidth)
+  {
+    scale = maxHeightmapWidth / (segmentWidth-1);
+  }
+
+  var geometry = new THREE.PlaneGeometry(width, height,
+      (segmentWidth-1) * scale, (segmentHeight-1) * scale);
+  geometry.dynamic = true;
+
+  // flip the heights
+  var vertices = [];
+  for (var h = segmentHeight-1; h >= 0; --h)
+  {
+    for (var w = 0; w < segmentWidth; ++w)
+    {
+      vertices[(segmentHeight-h-1)*segmentWidth  + w]
+          = heights[h*segmentWidth + w];
+    }
+  }
+
+  // sub-sample
+  var col = (segmentWidth-1) * scale;
+  var row = (segmentHeight-1) * scale;
+  for (var r = 0; r < row; ++r)
+  {
+    for (var c = 0; c < col; ++c)
+    {
+      var index = (r * col * 1/(scale*scale)) +   (c * (1/scale));
+      geometry.vertices[r*col + c].z = vertices[index];
+    }
+  }
+
+  var mesh;
+  if (textures && textures.length > 0)
+  {
+    geometry.computeFaceNormals();
+    geometry.computeVertexNormals();
+    geometry.computeTangents();
+
+    var textureLoaded = [];
+    var repeats = [];
+    for (var t = 0; t < textures.length; ++t)
+    {
+      textureLoaded[t] = THREE.ImageUtils.loadTexture(textures[t].diffuse,
+          new THREE.UVMapping());
+      textureLoaded[t].wrapS = THREE.RepeatWrapping;
+      textureLoaded[t].wrapT = THREE.RepeatWrapping;
+      repeats[t] = width/textures[t].size;
+    }
+
+    // for now, use fixed no. of textures and blends
+    // so populate the remaining ones to make the fragment shader happy
+    for (var tt = textures.length; tt< 3; ++tt)
+    {
+      textureLoaded[tt] = textureLoaded[tt-1];
+    }
+
+    for (var b = blends.length; b < 2; ++b)
+    {
+      blends[b] = blends[b-1];
+    }
+
+    for (var rr = repeats.length; rr < 3; ++rr)
+    {
+      repeats[rr] = repeats[rr-1];
+    }
+
+    // Use the same approach as gazebo scene, grab the first directional light
+    // and use it for shading the terrain
+    var lightDir = new THREE.Vector3(0, 0, 1);
+    var lightDiffuse = new THREE.Color(0xffffff);
+    var allObjects = [];
+    this.scene.getDescendants(allObjects);
+    for (var l = 0; l < allObjects.length; ++l)
+    {
+      if (allObjects[l] instanceof THREE.DirectionalLight)
+      {
+        lightDir = allObjects[l].target.position;
+        lightDiffuse = allObjects[l].color;
+        break;
+      }
+    }
+
+    var material = new THREE.ShaderMaterial({
+      uniforms:
+      {
+        texture0: { type: 't', value: textureLoaded[0]},
+        texture1: { type: 't', value: textureLoaded[1]},
+        texture2: { type: 't', value: textureLoaded[2]},
+        repeat0: { type: 'f', value: repeats[0]},
+        repeat1: { type: 'f', value: repeats[1]},
+        repeat2: { type: 'f', value: repeats[2]},
+        minHeight1: { type: 'f', value: blends[0].min_height},
+        fadeDist1: { type: 'f', value: blends[0].fade_dist},
+        minHeight2: { type: 'f', value: blends[1].min_height},
+        fadeDist2: { type: 'f', value: blends[1].fade_dist},
+        ambient: { type: 'c', value: this.ambient.color},
+        lightDiffuse: { type: 'c', value: lightDiffuse},
+        lightDir: { type: 'v3', value: lightDir}
+      },
+      attributes: {},
+      vertexShader: document.getElementById( 'heightmapVS' ).innerHTML,
+      fragmentShader: document.getElementById( 'heightmapFS' ).innerHTML
+    });
+
+    mesh = new THREE.Mesh( geometry, material);
+  }
+  else
+  {
+    mesh = new THREE.Mesh( geometry,
+        new THREE.MeshPhongMaterial( { color: 0x555555 } ) );
+  }
+
+  mesh.position.x = origin.x;
+  mesh.position.y = origin.y;
+  mesh.position.z = origin.z;
+  parent.add(mesh);
+
+  this.heightmap = parent;
+};
+
+/**
+ * Load mesh
+ * @param {string} uri
+ * @param {} submesh
+ * @param {} centerSubmesh
+ * @param {function} callback
+ */
+GZ3D.Scene.prototype.loadMesh = function(uri, submesh, centerSubmesh,
+    callback)
+{
+  var uriPath = uri.substring(0, uri.lastIndexOf('/'));
+  var uriFile = uri.substring(uri.lastIndexOf('/') + 1);
+
+  // load urdf model
+  if (uriFile.substr(-4).toLowerCase() === '.dae')
+  {
+    return this.loadCollada(uri, submesh, centerSubmesh, callback);
+  }
+  else if (uriFile.substr(-5).toLowerCase() === '.urdf')
+  {
+    /*var urdfModel = new ROSLIB.UrdfModel({
+      string : uri
+    });
+
+    // adapted from ros3djs
+    var links = urdfModel.links;
+    for ( var l in links) {
+      var link = links[l];
+      if (link.visual && link.visual.geometry) {
+        if (link.visual.geometry.type === ROSLIB.URDF_MESH) {
+          var frameID = '/' + link.name;
+          var filename = link.visual.geometry.filename;
+          var meshType = filename.substr(-4).toLowerCase();
+          var mesh = filename.substring(filename.indexOf('://') + 3);
+          // ignore mesh files which are not in Collada format
+          if (meshType === '.dae')
+          {
+            var dae = this.loadCollada(uriPath + '/' + mesh, parent);
+            // check for a scale
+            if(link.visual.geometry.scale)
+            {
+              dae.scale = new THREE.Vector3(
+                  link.visual.geometry.scale.x,
+                  link.visual.geometry.scale.y,
+                  link.visual.geometry.scale.z
+              );
+            }
+          }
+        }
+      }
+    }*/
+  }
+};
+
+/**
+ * Load collada file
+ * @param {string} uri
+ * @param {} submesh
+ * @param {} centerSubmesh
+ * @param {function} callback
+ */
+GZ3D.Scene.prototype.loadCollada = function(uri, submesh, centerSubmesh,
+    callback)
+{
+  var dae;
+  var mesh = null;
+  /*
+  // Crashes: issue #36
+  if (this.meshes[uri])
+  {
+    dae = this.meshes[uri];
+    dae = dae.clone();
+    this.useColladaSubMesh(dae, submesh, centerSubmesh);
+    callback(dae);
+    return;
+  }
+  */
+
+  var loader = new THREE.ColladaLoader();
+  // var loader = new ColladaLoader2();
+  // loader.options.convertUpAxis = true;
+  var thatURI = uri;
+  var thatSubmesh = submesh;
+  var thatCenterSubmesh = centerSubmesh;
+
+  loader.load(uri, function(collada)
+  {
+    // check for a scale factor
+    /*if(collada.dae.asset.unit)
+    {
+      var scale = collada.dae.asset.unit;
+      collada.scene.scale = new THREE.Vector3(scale, scale, scale);
+    }*/
+
+    dae = collada.scene;
+    dae.updateMatrix();
+    this.scene.prepareColladaMesh(dae);
+    this.scene.meshes[thatURI] = dae;
+    dae = dae.clone();
+    this.scene.useColladaSubMesh(dae, thatSubmesh, centerSubmesh);
+
+    dae.name = uri;
+    callback(dae);
+  });
+};
+
+/**
+ * Prepare collada by removing other non-mesh entities such as lights
+ * @param {} dae
+ */
+GZ3D.Scene.prototype.prepareColladaMesh = function(dae)
+{
+  var allChildren = [];
+  dae.getDescendants(allChildren);
+  for (var i = 0; i < allChildren.length; ++i)
+  {
+    if (allChildren[i] instanceof THREE.Light)
+    {
+      allChildren[i].parent.remove(allChildren[i]);
+    }
+  }
+};
+
+/**
+ * Prepare collada by handling submesh-only loading
+ * @param {} dae
+ * @param {} submesh
+ * @param {} centerSubmesh
+ * @returns {THREE.Mesh} mesh
+ */
+GZ3D.Scene.prototype.useColladaSubMesh = function(dae, submesh, centerSubmesh)
+{
+  if (!submesh)
+  {
+    return null;
+  }
+
+  var mesh;
+  var allChildren = [];
+  dae.getDescendants(allChildren);
+  for (var i = 0; i < allChildren.length; ++i)
+  {
+    if (allChildren[i] instanceof THREE.Mesh)
+    {
+      if (!submesh && !mesh)
+      {
+        mesh = allChildren[i];
+      }
+
+      if (submesh)
+      {
+
+        if (allChildren[i].geometry.name === submesh)
+        {
+          if (centerSubmesh)
+          {
+            var vertices = allChildren[i].geometry.vertices;
+            var vMin = new THREE.Vector3();
+            var vMax = new THREE.Vector3();
+            vMin.x = vertices[0].x;
+            vMin.y = vertices[0].y;
+            vMin.z = vertices[0].z;
+            vMax.x = vMin.x;
+            vMax.y = vMin.y;
+            vMax.z = vMin.z;
+
+            for (var j = 1; j < vertices.length; ++j)
+            {
+              vMin.x = Math.min(vMin.x, vertices[j].x);
+              vMin.y = Math.min(vMin.y, vertices[j].y);
+              vMin.z = Math.min(vMin.z, vertices[j].z);
+              vMax.x = Math.max(vMax.x, vertices[j].x);
+              vMax.y = Math.max(vMax.y, vertices[j].y);
+              vMax.z = Math.max(vMax.z, vertices[j].z);
+            }
+
+            var center  = new THREE.Vector3();
+            center.x = vMin.x + (0.5 * (vMax.x - vMin.x));
+            center.y = vMin.y + (0.5 * (vMax.y - vMin.y));
+            center.z = vMin.z + (0.5 * (vMax.z - vMin.z));
+
+            for (var k = 0; k < vertices.length; ++k)
+            {
+              vertices[k].x -= center.x;
+              vertices[k].y -= center.y;
+              vertices[k].z -= center.z;
+            }
+            allChildren[i].geometry.verticesNeedUpdate = true;
+
+            allChildren[i].position.x = 0;
+            allChildren[i].position.y = 0;
+            allChildren[i].position.z = 0;
+
+            allChildren[i].parent.position.x = 0;
+            allChildren[i].parent.position.y = 0;
+            allChildren[i].parent.position.z = 0;
+          }
+          mesh = allChildren[i];
+        }
+        else
+        {
+          allChildren[i].parent.remove(allChildren[i]);
+        }
+      }
+    }
+  }
+  return mesh;
+};
+
+/*GZ3D.Scene.prototype.setMaterial = function(mesh, texture, normalMap)
+{
+  if (!mesh)
+  {
+    return;
+  }
+
+  if (texture || normalMap)
+  {
+    // normal map shader
+    var shader = THREE.ShaderLib['normalmap'];
+    var uniforms = THREE.UniformsUtils.clone( shader.uniforms );
+    if (texture)
+    {
+      uniforms['enableDiffuse'].value = true;
+      uniforms['tDiffuse'].value = THREE.ImageUtils.loadTexture(texture);
+    }
+    if (normalMap)
+    {
+      uniforms['tNormal'].value = THREE.ImageUtils.loadTexture(normalMap);
+    }
+
+    var parameters = { fragmentShader: shader.fragmentShader,
+        vertexShader: shader.vertexShader, uniforms: uniforms,
+        lights: true, fog: false };
+    var shaderMaterial = new THREE.ShaderMaterial(parameters);
+    mesh.geometry.computeTangents();
+    mesh.material = shaderMaterial;
+  }
+};*/
+
+/**
+ * Set material for an object
+ * @param {} obj
+ * @param {} material
+ */
+GZ3D.Scene.prototype.setMaterial = function(obj, material)
+{
+  if (obj)
+  {
+    if (material)
+    {
+      obj.material = new THREE.MeshPhongMaterial();
+      var ambient = material.ambient;
+      if (ambient)
+      {
+        obj.material.ambient.setRGB(ambient[0], ambient[1], ambient[2]);
+      }
+      var diffuse = material.diffuse;
+      if (diffuse)
+      {
+        obj.material.color.setRGB(diffuse[0], diffuse[1], diffuse[2]);
+      }
+      var specular = material.specular;
+      if (specular)
+      {
+        obj.material.specular.setRGB(specular[0], specular[1], specular[2]);
+      }
+      var opacity = material.opacity;
+      if (opacity)
+      {
+        if (opacity < 1)
+        {
+          obj.material.transparent = true;
+          obj.material.opacity = opacity;
+        }
+      }
+
+      if (material.texture)
+      {
+        var texture = THREE.ImageUtils.loadTexture(material.texture);
+        if (material.scale)
+        {
+          texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
+          texture.repeat.x = 1.0 / material.scale[0];
+          texture.repeat.y = 1.0 / material.scale[1];
+        }
+        obj.material.map = texture;
+      }
+      if (material.normalMap)
+      {
+        obj.material.normalMap =
+            THREE.ImageUtils.loadTexture(material.normalMap);
+      }
+    }
+  }
+};
+
+/**
+ * Set manipulation mode (view/translate/rotate)
+ * @param {string} mode
+ */
+GZ3D.Scene.prototype.setManipulationMode = function(mode)
+{
+  this.manipulationMode = mode;
+
+  if (mode === 'view')
+  {
+    if (this.modelManipulator.object)
+    {
+      this.emitter.emit('entityChanged', this.modelManipulator.object);
+    }
+    this.selectEntity(null);
+  }
+  else
+  {
+    // Toggle manipulaion space (world / local)
+    if (this.modelManipulator.mode === this.manipulationMode)
+    {
+      this.modelManipulator.space =
+        (this.modelManipulator.space === 'world') ? 'local' : 'world';
+    }
+    this.modelManipulator.mode = this.manipulationMode;
+    this.modelManipulator.setMode(this.modelManipulator.mode);
+    // model was selected during view mode
+    if (this.selectedEntity)
+    {
+      this.selectEntity(this.selectedEntity);
+    }
+  }
+
+};
+
+/**
+ * Show collision visuals
+ * @param {boolean} show
+ */
+GZ3D.Scene.prototype.showCollision = function(show)
+{
+  if (show === this.showCollisions)
+  {
+    return;
+  }
+
+  var allObjects = [];
+  this.scene.getDescendants(allObjects);
+  for (var i = 0; i < allObjects.length; ++i)
+  {
+    if (allObjects[i] instanceof THREE.Object3D &&
+        allObjects[i].name.indexOf('COLLISION_VISUAL') >=0)
+    {
+      var allChildren = [];
+      allObjects[i].getDescendants(allChildren);
+      for (var j =0; j < allChildren.length; ++j)
+      {
+        if (allChildren[j] instanceof THREE.Mesh)
+        {
+          allChildren[j].visible = show;
+        }
+      }
+    }
+  }
+  this.showCollisions = show;
+
+};
+
+/**
+ * Attach manipulator to an object
+ * @param {THREE.Object3D} model
+ * @param {string} mode (translate/rotate)
+ */
+GZ3D.Scene.prototype.attachManipulator = function(model,mode)
+{
+  if (this.modelManipulator.object)
+  {
+    this.emitter.emit('entityChanged', this.modelManipulator.object);
+  }
+
+  if (mode !== 'view')
+  {
+    this.modelManipulator.attach(model);
+    this.modelManipulator.mode = mode;
+    this.modelManipulator.setMode( this.modelManipulator.mode );
+    this.scene.add(this.modelManipulator.gizmo);
+  }
+};
+
+/**
+ * Reset view
+ */
+GZ3D.Scene.prototype.resetView = function()
+{
+  this.camera.position.copy(this.defaultCameraPosition);
+  this.camera.up = new THREE.Vector3(0, 0, 1);
+  this.camera.lookAt(new THREE.Vector3( 0, 0, 0 ));
+  this.camera.updateMatrix();
+};
+
+/**
+ * Show radial menu
+ * @param {} event
+ */
+GZ3D.Scene.prototype.showRadialMenu = function(e)
+{
+  var event = e.originalEvent;
+
+  var pointer = event.touches ? event.touches[ 0 ] : event;
+  var pos = new THREE.Vector2(pointer.clientX, pointer.clientY);
+
+  var intersect = new THREE.Vector3();
+  var model = this.getRayCastModel(pos, intersect);
+
+  if (model && model.name !== '' && model.name !== 'plane'
+      && this.modelManipulator.pickerNames.indexOf(model.name) === -1)
+  {
+    this.radialMenu.show(event,model);
+    this.selectEntity(model);
+  }
+};
+
+/**
+ * Show bounding box for a model. The box is aligned with the world.
+ * @param {THREE.Object3D} model
+ */
+GZ3D.Scene.prototype.showBoundingBox = function(model)
+{
+  if (typeof model === 'string')
+  {
+    model = this.scene.getObjectByName(model);
+  }
+
+  if (this.boundingBox.visible)
+  {
+    if (this.boundingBox.parent === model)
+    {
+      return;
+    }
+    else
+    {
+      this.hideBoundingBox();
+    }
+  }
+  var box = new THREE.Box3();
+  // w.r.t. world
+  box.setFromObject(model);
+  // center vertices with object
+  box.min.x = box.min.x - model.position.x;
+  box.min.y = box.min.y - model.position.y;
+  box.min.z = box.min.z - model.position.z;
+  box.max.x = box.max.x - model.position.x;
+  box.max.y = box.max.y - model.position.y;
+  box.max.z = box.max.z - model.position.z;
+
+  var vertex = new THREE.Vector3(box.max.x, box.max.y, box.max.z); // 0
+  this.boundingBox.geometry.vertices[0].copy(vertex);
+  this.boundingBox.geometry.vertices[7].copy(vertex);
+  this.boundingBox.geometry.vertices[16].copy(vertex);
+
+  vertex.set(box.min.x, box.max.y, box.max.z); // 1
+  this.boundingBox.geometry.vertices[1].copy(vertex);
+  this.boundingBox.geometry.vertices[2].copy(vertex);
+  this.boundingBox.geometry.vertices[18].copy(vertex);
+
+  vertex.set(box.min.x, box.min.y, box.max.z); // 2
+  this.boundingBox.geometry.vertices[3].copy(vertex);
+  this.boundingBox.geometry.vertices[4].copy(vertex);
+  this.boundingBox.geometry.vertices[20].copy(vertex);
+
+  vertex.set(box.max.x, box.min.y, box.max.z); // 3
+  this.boundingBox.geometry.vertices[5].copy(vertex);
+  this.boundingBox.geometry.vertices[6].copy(vertex);
+  this.boundingBox.geometry.vertices[22].copy(vertex);
+
+  vertex.set(box.max.x, box.max.y, box.min.z); // 4
+  this.boundingBox.geometry.vertices[8].copy(vertex);
+  this.boundingBox.geometry.vertices[15].copy(vertex);
+  this.boundingBox.geometry.vertices[17].copy(vertex);
+
+  vertex.set(box.min.x, box.max.y, box.min.z); // 5
+  this.boundingBox.geometry.vertices[9].copy(vertex);
+  this.boundingBox.geometry.vertices[10].copy(vertex);
+  this.boundingBox.geometry.vertices[19].copy(vertex);
+
+  vertex.set(box.min.x, box.min.y, box.min.z); // 6
+  this.boundingBox.geometry.vertices[11].copy(vertex);
+  this.boundingBox.geometry.vertices[12].copy(vertex);
+  this.boundingBox.geometry.vertices[21].copy(vertex);
+
+  vertex.set(box.max.x, box.min.y, box.min.z); // 7
+  this.boundingBox.geometry.vertices[13].copy(vertex);
+  this.boundingBox.geometry.vertices[14].copy(vertex);
+  this.boundingBox.geometry.vertices[23].copy(vertex);
+
+  this.boundingBox.geometry.verticesNeedUpdate = true;
+
+  // rotate the box back to the world
+  var modelRotation = new THREE.Matrix4();
+  modelRotation.extractRotation(model.matrixWorld);
+  var modelInverse = new THREE.Matrix4();
+  modelInverse.getInverse(modelRotation);
+  this.boundingBox.quaternion.setFromRotationMatrix(modelInverse);
+  this.boundingBox.name = 'boundingBox';
+  this.boundingBox.visible = true;
+
+  // Add box as model's child
+  model.add(this.boundingBox);
+};
+
+/**
+ * Hide bounding box
+ */
+GZ3D.Scene.prototype.hideBoundingBox = function()
+{
+  if(this.boundingBox.parent)
+  {
+    this.boundingBox.parent.remove(this.boundingBox);
+  }
+  this.boundingBox.visible = false;
+};
+
+/**
+ * Mouse right click
+ * @param {} event
+ * @param {} callback - function to be executed to the clicked model
+ */
+GZ3D.Scene.prototype.onRightClick = function(event, callback)
+{
+  var pos = new THREE.Vector2(event.clientX, event.clientY);
+  var model = this.getRayCastModel(pos, new THREE.Vector3());
+
+  if(model && model.name !== '' && model.name !== 'plane' &&
+      this.modelManipulator.pickerNames.indexOf(model.name) === -1)
+  {
+    callback(model);
+  }
+};
+
+
+/**
+ * Set model's view mode
+ * @param {} model
+ * @param {} viewAs (normal/transparent/wireframe)
+ */
+GZ3D.Scene.prototype.setViewAs = function(model, viewAs)
+{
+  // Toggle
+  if (model.viewAs === viewAs)
+  {
+    viewAs = 'normal';
+  }
+
+  function materialViewAs(material)
+  {
+    if (materials.indexOf(material.id) === -1)
+    {
+      materials.push(material.id);
+      material.transparent = true;
+
+      if (viewAs === 'transparent')
+      {
+        if (material.opacity)
+        {
+          material.originalOpacity = material.opacity;
+        }
+        else
+        {
+          material.originalOpacity = 1.0;
+        }
+        material.opacity = 0.25;
+      }
+      else
+      {
+        material.opacity =
+            material.originalOpacity ?
+            material.originalOpacity : 1.0;
+      }
+
+      if (viewAs === 'wireframe')
+      {
+        material.visible = false;
+      }
+      else
+      {
+        material.visible = true;
+      }
+    }
+  }
+
+  var wireframe;
+  var descendants = [];
+  var materials = [];
+  model.getDescendants(descendants);
+  for (var i = 0; i < descendants.length; ++i)
+  {
+    if (descendants[i].material &&
+        descendants[i].name.indexOf('boundingBox') === -1 &&
+        descendants[i].name.indexOf('COLLISION_VISUAL') === -1 &&
+        !this.getParentByPartialName(descendants[i], 'COLLISION_VISUAL')&&
+        descendants[i].name.indexOf('wireframe') === -1 &&
+        descendants[i].name.indexOf('JOINT_VISUAL') === -1)
+    {
+      if (descendants[i].material instanceof THREE.MeshFaceMaterial)
+      {
+        for (var j = 0; j < descendants[i].material.materials.length; ++j)
+        {
+          materialViewAs(descendants[i].material.materials[j]);
+        }
+      }
+      else
+      {
+        materialViewAs(descendants[i].material);
+      }
+
+      if (viewAs === 'wireframe')
+      {
+        wireframe = descendants[i].getObjectByName('wireframe');
+        if (wireframe)
+        {
+          wireframe.visible = true;
+        }
+        else
+        {
+          var mesh = new THREE.Mesh( descendants[i].geometry,
+              new THREE.MeshBasicMaterial({color: 0xffffff}));
+          wireframe = new THREE.WireframeHelper( mesh );
+          wireframe.name = 'wireframe';
+          descendants[i].add( wireframe );
+        }
+      }
+      else
+      {
+        wireframe = descendants[i].getObjectByName('wireframe');
+        if (wireframe)
+        {
+          wireframe.visible = false;
+        }
+      }
+    }
+  }
+  model.viewAs = viewAs;
+};
+
+/**
+ * Returns the closest parent whose name contains the given string
+ * @param {} object
+ * @param {} name
+ */
+GZ3D.Scene.prototype.getParentByPartialName = function(object, name)
+{
+  var parent = object.parent;
+  while (parent && parent !== this.scene)
+  {
+    if (parent.name.indexOf(name) !== -1)
+    {
+      return parent;
+    }
+
+    parent = parent.parent;
+  }
+  return null;
+};
+
+/**
+ * Select entity
+ * @param {} object
+ */
+GZ3D.Scene.prototype.selectEntity = function(object)
+{
+  if (object)
+  {
+    if (object !== this.selectedEntity)
+    {
+      this.showBoundingBox(object);
+      this.selectedEntity = object;
+    }
+    this.attachManipulator(object, this.manipulationMode);
+    guiEvents.emit('setTreeSelected', object.name);
+  }
+  else
+  {
+    if (this.modelManipulator.object)
+    {
+      this.modelManipulator.detach();
+      this.scene.remove(this.modelManipulator.gizmo);
+    }
+    this.hideBoundingBox();
+    this.selectedEntity = null;
+    guiEvents.emit('setTreeDeselected');
+  }
+};
+
+/**
+ * View joints
+ * Toggle: if there are joints, hide, otherwise, show.
+ * @param {} model
+ */
+GZ3D.Scene.prototype.viewJoints = function(model)
+{
+  if (model.joint === undefined || model.joint.length === 0)
+  {
+    return;
+  }
+
+  var child;
+
+  // Visuals already exist
+  if (model.jointVisuals)
+  {
+    // Hide = remove from parent
+    if (model.jointVisuals[0].parent !== undefined)
+    {
+      for (var v = 0; v < model.jointVisuals.length; ++v)
+      {
+        model.jointVisuals[v].parent.remove(model.jointVisuals[v]);
+      }
+    }
+    // Show: attach to parent
+    else
+    {
+      for (var s = 0; s < model.joint.length; ++s)
+      {
+        child = model.getObjectByName(model.joint[s].child);
+
+        if (!child)
+        {
+          continue;
+        }
+
+        child.add(model.jointVisuals[s]);
+      }
+    }
+  }
+  // Create visuals
+  else
+  {
+    model.jointVisuals = [];
+    for (var j = 0; j < model.joint.length; ++j)
+    {
+      child = model.getObjectByName(model.joint[j].child);
+
+      if (!child)
+      {
+        continue;
+      }
+
+      // XYZ expressed w.r.t. child
+      var jointVisual = this.jointAxis['XYZaxes'].clone();
+      child.add(jointVisual);
+      model.jointVisuals.push(jointVisual);
+      jointVisual.scale.set(0.7, 0.7, 0.7);
+
+      this.setPose(jointVisual, model.joint[j].pose.position,
+          model.joint[j].pose.orientation);
+
+      var mainAxis;
+      if (model.joint[j].type !== this.jointTypes.BALL)
+      {
+        mainAxis = this.jointAxis['mainAxis'].clone();
+        jointVisual.add(mainAxis);
+      }
+
+      var secondAxis;
+      if (model.joint[j].type === this.jointTypes.REVOLUTE2 ||
+          model.joint[j].type === this.jointTypes.UNIVERSAL)
+      {
+        secondAxis = this.jointAxis['mainAxis'].clone();
+        jointVisual.add(secondAxis);
+      }
+
+      if (model.joint[j].type === this.jointTypes.REVOLUTE ||
+          model.joint[j].type === this.jointTypes.GEARBOX)
+      {
+        mainAxis.add(this.jointAxis['rotAxis'].clone());
+      }
+      else if (model.joint[j].type === this.jointTypes.REVOLUTE2 ||
+               model.joint[j].type === this.jointTypes.UNIVERSAL)
+      {
+        mainAxis.add(this.jointAxis['rotAxis'].clone());
+        secondAxis.add(this.jointAxis['rotAxis'].clone());
+      }
+      else if (model.joint[j].type === this.jointTypes.BALL)
+      {
+        jointVisual.add(this.jointAxis['ballVisual'].clone());
+      }
+      else if (model.joint[j].type === this.jointTypes.PRISMATIC)
+      {
+        mainAxis.add(this.jointAxis['transAxis'].clone());
+      }
+      else if (model.joint[j].type === this.jointTypes.SCREW)
+      {
+        mainAxis.add(this.jointAxis['screwAxis'].clone());
+      }
+
+      var direction, tempMatrix, rotMatrix;
+      if (mainAxis)
+      {
+        // main axis expressed w.r.t. parent model or joint frame
+        // needs Gazebo issue #1268 fixed, receive use_parent_model_frame on msg
+        // for now, true by default because most old models have it true
+        if (model.joint[j].axis1.use_parent_model_frame === undefined)
+        {
+          model.joint[j].axis1.use_parent_model_frame = true;
+        }
+
+        direction = new THREE.Vector3(
+            model.joint[j].axis1.xyz.x,
+            model.joint[j].axis1.xyz.y,
+            model.joint[j].axis1.xyz.z);
+        direction.normalize();
+
+        tempMatrix = new THREE.Matrix4();
+        if (model.joint[j].axis1.use_parent_model_frame)
+        {
+          tempMatrix.extractRotation(jointVisual.matrix);
+          tempMatrix.getInverse(tempMatrix);
+          direction.applyMatrix4(tempMatrix);
+          tempMatrix.extractRotation(child.matrix);
+          tempMatrix.getInverse(tempMatrix);
+          direction.applyMatrix4(tempMatrix);
+        }
+
+        mainAxis.position =  direction.multiplyScalar(0.3);
+        rotMatrix = new THREE.Matrix4();
+        rotMatrix.lookAt(direction, new THREE.Vector3(0, 0, 0), mainAxis.up);
+        mainAxis.quaternion.setFromRotationMatrix(rotMatrix);
+      }
+
+      if (secondAxis)
+      {
+        if (model.joint[j].axis2.use_parent_model_frame === undefined)
+        {
+          model.joint[j].axis2.use_parent_model_frame = true;
+        }
+
+        direction = new THREE.Vector3(
+            model.joint[j].axis2.xyz.x,
+            model.joint[j].axis2.xyz.y,
+            model.joint[j].axis2.xyz.z);
+        direction.normalize();
+
+        tempMatrix = new THREE.Matrix4();
+        if (model.joint[j].axis2.use_parent_model_frame)
+        {
+          tempMatrix.extractRotation(jointVisual.matrix);
+          tempMatrix.getInverse(tempMatrix);
+          direction.applyMatrix4(tempMatrix);
+          tempMatrix.extractRotation(child.matrix);
+          tempMatrix.getInverse(tempMatrix);
+          direction.applyMatrix4(tempMatrix);
+        }
+
+        secondAxis.position =  direction.multiplyScalar(0.3);
+        rotMatrix = new THREE.Matrix4();
+        rotMatrix.lookAt(direction, new THREE.Vector3(0, 0, 0), secondAxis.up);
+        secondAxis.quaternion.setFromRotationMatrix(rotMatrix);
+      }
+    }
+  }
+};
+
+/**
+ * Update a light entity from a message
+ * @param {} entity
+ * @param {} msg
+ */
+GZ3D.Scene.prototype.updateLight = function(entity, msg)
+{
+  // TODO: Generalize this and createLight
+  var lightObj = entity.children[0];
+  var dir;
+
+  var color = new THREE.Color();
+
+  if (msg.diffuse)
+  {
+    color.r = msg.diffuse.r;
+    color.g = msg.diffuse.g;
+    color.b = msg.diffuse.b;
+    lightObj.color = color.clone();
+  }
+  if (msg.specular)
+  {
+    color.r = msg.specular.r;
+    color.g = msg.specular.g;
+    color.b = msg.specular.b;
+    entity.serverProperties.specular = color.clone();
+  }
+
+  var matrixWorld;
+  if (msg.pose)
+  {
+    // needed to update light's direction
+    var quaternion = new THREE.Quaternion(
+        msg.pose.orientation.x,
+        msg.pose.orientation.y,
+        msg.pose.orientation.z,
+        msg.pose.orientation.w);
+
+    var translation = new THREE.Vector3(
+        msg.pose.position.x,
+        msg.pose.position.y,
+        msg.pose.position.z);
+
+    matrixWorld = new THREE.Matrix4();
+    matrixWorld.compose(translation, quaternion, new THREE.Vector3(1,1,1));
+
+    this.setPose(entity, msg.pose.position, msg.pose.orientation);
+    entity.matrixWorldNeedsUpdate = true;
+
+    if (entity.direction)
+    {
+      dir = new THREE.Vector3(entity.direction.x, entity.direction.y,
+          entity.direction.z);
+
+      entity.direction = new THREE.Vector3();
+      entity.direction.copy(dir);
+
+      dir.applyMatrix4(matrixWorld); // localToWorld
+      lightObj.target.position.copy(dir);
+    }
+  }
+
+  if (msg.range)
+  {
+    // THREE.js's light distance impacts the attenuation factor defined in the shader:
+    // attenuation factor = 1.0 - distance-to-enlighted-point / light.distance
+    // Gazebo's range (taken from OGRE 3D API) does not contribute to attenuation;
+    // it is a hard limit for light scope.
+    // Nevertheless, we identify them for sake of simplicity.
+    lightObj.distance = msg.range;
+  }
+
+  if (msg.cast_shadows)
+  {
+    lightObj.castShadow = msg.cast_shadows;
+  }
+
+  if (msg.attenuation_constant)
+  {
+    entity.serverProperties.attenuation_constant = msg.attenuation_constant;
+  }
+  if (msg.attenuation_linear)
+  {
+    entity.serverProperties.attenuation_linear = msg.attenuation_linear;
+    lightObj.intensity = lightObj.intensity/(1+msg.attenuation_linear);
+  }
+  if (msg.attenuation_quadratic)
+  {
+    entity.serverProperties.attenuation_quadratic = msg.attenuation_quadratic;
+    lightObj.intensity = lightObj.intensity/(1+msg.attenuation_quadratic);
+  }
+
+//  Not handling these on gzweb for now
+//
+//  if (lightObj instanceof THREE.SpotLight) {
+//    if (msg.spot_outer_angle) {
+//      lightObj.angle = msg.spot_outer_angle;
+//    }
+//    if (msg.spot_falloff) {
+//      lightObj.exponent = msg.spot_falloff;
+//    }
+//  }
+
+  if (msg.direction)
+  {
+    dir = new THREE.Vector3(msg.direction.x, msg.direction.y,
+        msg.direction.z);
+
+    entity.direction = new THREE.Vector3();
+    entity.direction.copy(dir);
+
+    dir.applyMatrix4(matrixWorld); // localToWorld
+    lightObj.target.position.copy(dir);
+  }
+};
+
+/**
+ * SDF parser constructor initializes SDF parser with the given parameters
+ * and defines a DOM parser function to parse SDF XML files
+ * @param {object} scene - the gz3d scene object
+ * @param {object} gui - the gz3d gui object
+ * @param {object} gziface - the gz3d gziface object
+ */
+GZ3D.SdfParser = function(scene, gui, gziface)
+{
+  // set the sdf version
+  this.SDF_VERSION = 1.5;
+  this.MATERIAL_ROOT = 'assets/';
+
+  // set the xml parser function
+  this.parseXML = function(xmlStr) {
+    return (new window.DOMParser()).parseFromString(xmlStr, 'text/xml');
+  };
+
+  this.scene = scene;
+  this.scene.setSDFParser(this);
+  this.gui = gui;
+  this.gziface = gziface;
+  this.init();
+
+  // cache materials if more than one model needs them
+  this.materials = [];
+  this.entityMaterial = {};
+
+};
+
+/**
+ * Initializes SDF parser by connecting relevant events from gziface
+ */
+GZ3D.SdfParser.prototype.init = function()
+{
+  var that = this;
+  this.gziface.emitter.on('error', function() {
+    that.gui.guiEvents.emit('notification_popup', 'GzWeb is currently running' +
+            'without a server, and materials could not be loaded.' +
+            'When connected scene will be reinitialized', 5000);
+    that.onConnectionError();
+  });
+  
+  this.gziface.emitter.on('material', function(mat) {
+    that.materials = mat;
+  });
+  
+  this.gziface.emitter.on('gzstatus', function(gzstatus) {
+    if (gzstatus === 'error')
+    {
+      that.gui.guiEvents.emit('notification_popup', 'GzWeb is currently ' +
+              'running without a GzServer, and Scene is reinitialized.', 5000);
+      that.onConnectionError();
+    }
+  });
+};
+
+/**
+ * Event callback function for gziface connection error which occurs
+ * when gziface cannot connect to gzbridge websocket
+ * this is due to 2 reasons:
+ * 1 - gzbridge websocket might not be run yet
+ * 2 - gzbridge websocket is trying to connect to gzserver which is not running currenly
+ */
+GZ3D.SdfParser.prototype.onConnectionError = function()
+{
+  this.scene.initScene();
+  
+  var that = this;
+  var entityCreated = function(model, type)
+  {
+    if (!that.gziface.isConnected)
+    {
+      that.addModelByType(model, type);
+    }
+  };
+  this.gui.emitter.on('entityCreated', entityCreated);
+  
+  var deleteEntity = function(entity)
+  {
+    var name = entity.name;
+    var obj = that.scene.getByName(name);
+    if (obj !== undefined)
+    {
+      if (obj.children[0] instanceof THREE.Light)
+      {
+        that.gui.setLightStats({name: name}, 'delete');
+      }
+      else
+      {
+        that.gui.setModelStats({name: name}, 'delete');
+      }
+      that.scene.remove(obj);
+    }
+  };
+  this.gui.emitter.on('deleteEntity', deleteEntity);
+};
+
+/**
+ * Parses string which denotes the color
+ * @param {string} colorStr - string which denotes the color where every value
+ * should be separated with single white space
+ * @returns {object} color - color object having r,g,b and alpha values
+ */
+GZ3D.SdfParser.prototype.parseColor = function(colorStr)
+{
+  var color = {};
+  var values = colorStr.split(' ');
+
+  color.r = parseFloat(values[0]);
+  color.g = parseFloat(values[1]);
+  color.b = parseFloat(values[2]);
+  color.a = parseFloat(values[3]);
+
+  return color;
+};
+
+/**
+ * Parses string which is a 3D vector
+ * @param {string} vectorStr - string which denotes the vector where every value
+ * should be separated with single white space
+ * @returns {object} vector3D - vector having x, y, z values
+ */
+GZ3D.SdfParser.prototype.parse3DVector = function(vectorStr)
+{
+  var vector3D = {};
+  var values = vectorStr.split(' ');
+  vector3D.x = parseFloat(values[0]);
+  vector3D.y = parseFloat(values[1]);
+  vector3D.z = parseFloat(values[2]);
+  return vector3D;
+};
+
+/**
+ * Creates THREE light object according to properties of sdf object
+ * which is parsed from sdf model of the light
+ * @param {object} sdfObj - object which is parsed from the sdf string
+ * @returns {THREE.Light} lightObj - THREE light object created
+ * according to given properties. The type of light object is determined
+ * according to light type
+ */
+GZ3D.SdfParser.prototype.spawnLightFromSDF = function(sdfObj)
+{
+  var light = sdfObj.light;
+  var lightObj;
+  var color = new THREE.Color();
+  var diffuseColor = this.parseColor(light.diffuse);
+  color.r = diffuseColor.r;
+  color.g = diffuseColor.g;
+  color.b = diffuseColor.b;
+
+  if (light['@type'] === 'point')
+  {
+    lightObj = new THREE.AmbientLight(color.getHex());
+    lightObj.distance = light.range;
+    this.scene.setPose(lightObj, light.pose.position, light.pose.orientation);
+  }
+  if (light['@type'] === 'spot')
+  {
+    lightObj = new THREE.SpotLight(color.getHex());
+    lightObj.distance = light.range;
+    this.scene.setPose(lightObj, light.pose.position, light.pose.orientation);
+  }
+  else if (light['@type'] === 'directional')
+  {
+    lightObj = new THREE.DirectionalLight(color.getHex());
+
+    var direction = this.parse3DVector(light.direction);
+    var dir = new THREE.Vector3(direction.x, direction.y, direction.z);
+    var target = dir;
+    var negDir = dir.negate();
+    negDir.normalize();
+    var factor = 10;
+    var pose = this.parsePose(light.pose);
+    pose.position.x += factor * negDir.x;
+    pose.position.y += factor * negDir.y;
+    pose.position.z += factor * negDir.z;
+
+    target.x -= pose.position.x;
+    target.y -= pose.position.y;
+    target.z -= pose.position.z;
+
+    lightObj.target.position = target;
+    lightObj.shadowCameraNear = 1;
+    lightObj.shadowCameraFar = 50;
+    lightObj.shadowMapWidth = 4094;
+    lightObj.shadowMapHeight = 4094;
+    lightObj.shadowCameraVisible = false;
+    lightObj.shadowCameraBottom = -100;
+    lightObj.shadowCameraLeft = -100;
+    lightObj.shadowCameraRight = 100;
+    lightObj.shadowCameraTop = 100;
+    lightObj.shadowBias = 0.0001;
+
+    lightObj.position.set(negDir.x, negDir.y, negDir.z);
+    this.scene.setPose(lightObj, pose.position, pose.orientation);
+  }
+  lightObj.intensity = parseFloat(light.attenuation.constant);
+  lightObj.castShadow = light.cast_shadows;
+  lightObj.shadowDarkness = 0.3;
+  lightObj.name = light['@name'];
+
+  return lightObj;
+};
+
+/**
+ * Parses a string which is a 3D vector
+ * @param {string} poseStr - string which denotes the pose of the object
+ * where every value should be separated with single white space and first three denotes
+ * x,y,z and values of the pose, and following three denotes euler rotation around x,y,z
+ * @returns {object} pose - pose object having position (x,y,z)(THREE.Vector3)
+ * and orientation (THREE.Quaternion) properties
+ */
+GZ3D.SdfParser.prototype.parsePose = function(poseStr)
+{
+  var values = poseStr.split(' ');
+
+  var position = new THREE.Vector3(parseFloat(values[0]),
+          parseFloat(values[1]), parseFloat(values[2]));
+
+  // get euler rotation and convert it to Quaternion
+  var quaternion = new THREE.Quaternion();
+  var euler = new THREE.Euler(parseFloat(values[3]), parseFloat(values[4]),
+          parseFloat(values[5]), 'ZYX');
+  quaternion.setFromEuler(euler);
+
+  var pose = {
+    'position': position,
+    'orientation': quaternion
+  };
+
+  return pose;
+
+};
+
+/**
+ * Parses a string which is a 3D vector
+ * @param {string} scaleStr - string which denotes scaling in x,y,z
+ * where every value should be separated with single white space
+ * @returns {THREE.Vector3} scale - THREE Vector3 object
+ * which denotes scaling of an object in x,y,z
+ */
+GZ3D.SdfParser.prototype.parseScale = function(scaleStr)
+{
+  var values = scaleStr.split(' ');
+  var scale = new THREE.Vector3(parseFloat(values[0]), parseFloat(values[1]),
+          parseFloat(values[2]));
+  return scale;
+};
+
+/**
+ * Parses SDF material element which is going to be used by THREE library
+ * It matches material scripts with the material objects which are
+ * already parsed by gzbridge and saved by SDFParser
+ * @param {object} material - SDF material object
+ * @returns {object} material - material object which has the followings:
+ * texture, normalMap, ambient, diffuse, specular, opacity
+ */
+GZ3D.SdfParser.prototype.createMaterial = function(material)
+{
+  var textureUri, texture, mat;
+  var ambient, diffuse, specular, opacity, normalMap;
+
+  if (!material) { return null; }
+
+  var script = material.script;
+  if (script)
+  {
+    if (script.uri)
+    {
+      // if there is just one uri convert it to array
+      if (!(script.uri instanceof Array))
+      {
+        script.uri = [script.uri];
+      }
+
+      if (script.name)
+      {
+        mat = this.materials[script.name];
+        // if we already cached the materials
+        if (mat)
+        {
+          ambient = mat.ambient;
+          diffuse = mat.diffuse;
+          specular = mat.specular;
+          opacity = mat.opacity;
+
+          if (mat.texture)
+          {
+            for (var i = 0; i < script.uri.length; ++i)
+            {
+              var uriType = script.uri[i].substring(0, script.uri[i]
+                      .indexOf('://'));
+              if (uriType === 'model')
+              {
+                // if texture uri
+                if (script.uri[i].indexOf('textures') > 0)
+                {
+                  textureUri = script.uri[i].substring(script.uri[i]
+                          .indexOf('://') + 3);
+                  break;
+                }
+              }
+              else if (uriType === 'file')
+              {
+                if (script.uri[i].indexOf('materials') > 0)
+                {
+                  textureUri = script.uri[i].substring(script.uri[i]
+                          .indexOf('://') + 3, script.uri[i]
+                          .indexOf('materials') + 9)
+                          + '/textures';
+                  break;
+                }
+              }
+            }
+            texture = this.MATERIAL_ROOT + textureUri + '/' + mat.texture;
+          }
+        }
+        else
+        {
+          //TODO: how to handle if material is not cached
+          console.log(script.name + ' is not cached!!!');
+        }
+      }
+    }
+  }
+
+  // normal map
+  if (material.normal_map)
+  {
+    var mapUri;
+    if (material.normal_map.indexOf('://') > 0)
+    {
+      mapUri = material.normal_map.substring(
+              material.normal_map.indexOf('://') + 3, material.normal_map
+                      .lastIndexOf('/'));
+    }
+    else
+    {
+      mapUri = textureUri;
+    }
+    if (mapUri)
+    {
+      var startIndex = material.normal_map.lastIndexOf('/') + 1;
+      if (startIndex < 0)
+      {
+        startIndex = 0;
+      }
+      var normalMapName = material.normal_map.substr(startIndex,
+              material.normal_map.lastIndexOf('.') - startIndex);
+      normalMap = this.MATERIAL_ROOT + mapUri + '/' + normalMapName + '.png';
+    }
+  }
+
+  return {
+    texture: texture,
+    normalMap: normalMap,
+    ambient: ambient,
+    diffuse: diffuse,
+    specular: specular,
+    opacity: opacity
+  };
+
+};
+
+/**
+ * Parses a string which is a size of an object
+ * @param {string} sizeStr - string which denotes size in x,y,z
+ * where every value should be separated with single white space
+ * @returns {object} size - size object which denotes
+ * size of an object in x,y,z
+ */
+GZ3D.SdfParser.prototype.parseSize = function(sizeStr)
+{
+  var sizeObj;
+  var values = sizeStr.split(' ');
+  var x = parseFloat(values[0]);
+  var y = parseFloat(values[1]);
+  var z = parseFloat(values[2]);
+  sizeObj = {
+    'x': x,
+    'y': y,
+    'z': z
+  };
+
+  return sizeObj;
+};
+
+/**
+ * Parses SDF geometry element and creates corresponding mesh,
+ * when it creates the THREE.Mesh object it directly add it to the parent
+ * object.
+ * @param {object} geom - SDF geometry object which determines the geometry
+ *  of the object and can have following properties: box, cylinder, sphere,
+ *   plane, mesh
+ * @param {object} mat - SDF material object which is going to be parsed
+ * by createMaterial function
+ * @param {object} parent - parent 3D object
+ */
+GZ3D.SdfParser.prototype.createGeom = function(geom, mat, parent)
+{
+  var that = this;
+  var obj;
+  var size, normal;
+
+  var material = this.createMaterial(mat);
+  if (geom.box)
+  {
+    size = this.parseSize(geom.box.size);
+    obj = this.scene.createBox(size.x, size.y, size.z);
+  }
+  else if (geom.cylinder)
+  {
+    obj = this.scene.createCylinder(geom.cylinder.radius, geom.cylinder.length);
+  }
+  else if (geom.sphere)
+  {
+    obj = this.scene.createSphere(geom.sphere.radius);
+  }
+  else if (geom.plane)
+  {
+    normal = this.parseSize(geom.plane.normal);
+    size = this.parseSize(geom.plane.size);
+    obj = this.scene.createPlane(normal.x, normal.y, normal.z, size.x, size.y);
+  }
+  else if (geom.mesh)
+  {
+    {
+      var meshUri = geom.mesh.uri;
+      var submesh = geom.mesh.submesh;
+      var centerSubmesh = geom.mesh.center_submesh;
+
+      var uriType = meshUri.substring(0, meshUri.indexOf('://'));
+      if (uriType === 'file' || uriType === 'model')
+      {
+        var modelName = meshUri.substring(meshUri.indexOf('://') + 3);
+        if (geom.mesh.scale)
+        {
+          var scale = this.parseScale(geom.mesh.scale);
+          parent.scale.x = scale.x;
+          parent.scale.y = scale.y;
+          parent.scale.z = scale.z;
+        }
+
+        var modelUri = this.MATERIAL_ROOT + '/' + modelName;
+        var materialName = parent.name + '::' + modelUri;
+        this.entityMaterial[materialName] = material;
+
+        this.scene.loadMesh(modelUri, submesh, centerSubmesh, function(dae){
+          if (that.entityMaterial[materialName])
+          {
+            var allChildren = [];
+            dae.getDescendants(allChildren);
+            for (var c = 0; c < allChildren.length; ++c)
+            {
+              if (allChildren[c] instanceof THREE.Mesh)
+              {
+                that.scene.setMaterial(allChildren[c],
+                        that.entityMaterial[materialName]);
+                break;
+              }
+            }
+          }
+          parent.add(dae);
+          loadGeom(parent);
+        });
+      }
+    }
+  }
+  //TODO: how to handle height map without connecting to the server
+  //  else if (geom.heightmap)
+  //  {
+  //    var request = new ROSLIB.ServiceRequest({
+  //      name : that.scene.name
+  //    });
+  //
+  //    // redirect the texture paths to the assets dir
+  //    var textures = geom.heightmap.texture;
+  //    for ( var k = 0; k < textures.length; ++k)
+  //    {
+  //      textures[k].diffuse = this.parseUri(textures[k].diffuse);
+  //      textures[k].normal = this.parseUri(textures[k].normal);
+  //    }
+  //
+  //    var sizes = geom.heightmap.size;
+  //
+  //    // send service request and load heightmap on response
+  //    this.heightmapDataService.callService(request,
+  //        function(result)
+  //        {
+  //          var heightmap = result.heightmap;
+  //          // gazebo heightmap is always square shaped,
+  //          // and a dimension of: 2^N + 1
+  //          that.scene.loadHeightmap(heightmap.heights, heightmap.size.x,
+  //              heightmap.size.y, heightmap.width, heightmap.height,
+  //              heightmap.origin, textures,
+  //              geom.heightmap.blend, parent);
+  //            //console.log('Result for service call on ' + result);
+  //        });
+  //
+  //    //this.scene.loadHeightmap(parent)
+  //  }
+
+  if (obj)
+  {
+    if (material)
+    {
+      // texture mapping for simple shapes and planes only,
+      // not used by mesh and terrain
+      this.scene.setMaterial(obj, material);
+    }
+    obj.updateMatrix();
+    parent.add(obj);
+    loadGeom(parent);
+  }
+
+  function loadGeom(visualObj)
+  {
+    var allChildren = [];
+    visualObj.getDescendants(allChildren);
+    for (var c = 0; c < allChildren.length; ++c)
+    {
+      if (allChildren[c] instanceof THREE.Mesh)
+      {
+        allChildren[c].castShadow = true;
+        allChildren[c].receiveShadow = true;
+
+        if (visualObj.castShadows)
+        {
+          allChildren[c].castShadow = visualObj.castShadows;
+        }
+        if (visualObj.receiveShadows)
+        {
+          allChildren[c].receiveShadow = visualObj.receiveShadows;
+        }
+
+        if (visualObj.name.indexOf('COLLISION_VISUAL') >= 0)
+        {
+          allChildren[c].castShadow = false;
+          allChildren[c].receiveShadow = false;
+
+          allChildren[c].visible = this.scene.showCollisions;
+        }
+        break;
+      }
+    }
+  }
+};
+
+/**
+ * Parses SDF visual element and creates THREE 3D object by parsing
+ * geometry element using createGeom function
+ * @param {object} visual - SDF visual element
+ * @returns {THREE.Object3D} visualObj - 3D object which is created
+ * according to SDF visual element.
+ */
+GZ3D.SdfParser.prototype.createVisual = function(visual)
+{
+  //TODO: handle these node values
+  // cast_shadow, receive_shadows
+  if (visual.geometry)
+  {
+    var visualObj = new THREE.Object3D();
+    visualObj.name = visual['@name'];
+
+    if (visual.pose)
+    {
+      var visualPose = this.parsePose(visual.pose);
+      this.scene
+        .setPose(visualObj, visualPose.position, visualPose.orientation);
+    }
+
+    this.createGeom(visual.geometry, visual.material, visualObj);
+
+    return visualObj;
+  }
+
+  return null;
+
+};
+
+/**
+ * Parses SDF XML string or SDF XML DOM object
+ * @param {object} sdf - It is either SDF XML string or SDF XML DOM object
+ * @returns {THREE.Object3D} object - 3D object which is created from the
+ * given SDF.
+ */
+GZ3D.SdfParser.prototype.spawnFromSDF = function(sdf)
+{
+  //parse sdfXML
+  var sdfXML;
+  if ((typeof sdf) === 'string')
+  {
+    sdfXML = this.parseXML(sdf);
+  }
+  else
+  {
+    sdfXML = sdf;
+  }
+
+  //convert SDF XML to Json string and parse JSON string to object
+  //TODO: we need better xml 2 json object convertor
+  var myjson = xml2json(sdfXML, '\t');
+  var sdfObj = JSON.parse(myjson).sdf;
+  // it is easier to manipulate json object
+
+  if (sdfObj.model)
+  {
+    return this.spawnModelFromSDF(sdfObj);
+  }
+  else if (sdfObj.light)
+  {
+    return this.spawnLightFromSDF(sdfObj);
+  }
+};
+
+/**
+ * Loads SDF file according to given model name
+ * @param {string} modelName - name of the model
+ * @returns {THREE.Object3D} modelObject - 3D object which is created
+ * according to SDF model.
+ */
+GZ3D.SdfParser.prototype.loadSDF = function(modelName)
+{
+  var sdf = this.loadModel(modelName);
+  return this.spawnFromSDF(sdf);
+};
+
+/**
+ * Creates 3D object from parsed model SDF
+ * @param {object} sdfObj - parsed SDF object
+ * @returns {THREE.Object3D} modelObject - 3D object which is created
+ * according to SDF model object.
+ */
+GZ3D.SdfParser.prototype.spawnModelFromSDF = function(sdfObj)
+{
+  // create the model
+  var modelObj = new THREE.Object3D();
+  modelObj.name = sdfObj.model['@name'];
+  //TODO: is that needed
+  //modelObj.userData = sdfObj.model.@id;
+
+  var pose;
+  var i, j, k;
+  var visualObj;
+  var linkObj, linkPose;
+
+  if (sdfObj.model.pose)
+  {
+    pose = this.parsePose(sdfObj.model.pose);
+    this.scene.setPose(modelObj, pose.position, pose.orientation);
+  }
+
+  //convert link object to link array
+  if (!(sdfObj.model.link instanceof Array))
+  {
+    sdfObj.model.link = [sdfObj.model.link];
+  }
+
+  for (i = 0; i < sdfObj.model.link.length; ++i)
+  {
+    linkObj = this.createLink(sdfObj.model.link[i]);
+    modelObj.add(linkObj);
+  }
+
+  //  this.scene.add(modelObj);
+  return modelObj;
+
+};
+
+/**
+ * Creates a link 3D object of the model. A model consists of links
+ * these links are 3D objects. The function creates only visual elements
+ * of the link by createLink function
+ * @param {object} link - parsed SDF link object
+ * @returns {THREE.Object3D} linkObject - 3D link object
+ */
+GZ3D.SdfParser.prototype.createLink = function(link)
+{
+  var linkPose, visualObj;
+  var linkObj = new THREE.Object3D();
+  linkObj.name = link['@name'];
+
+  if (link.pose)
+  {
+    linkPose = this.parsePose(link.pose);
+    this.scene.setPose(linkObj, linkPose.position, linkPose.orientation);
+  }
+
+  if (link.visual)
+  {
+    if (!(link.visual instanceof Array))
+    {
+      link.visual = [link.visual];
+    }
+
+    for (var i = 0; i < link.visual.length; ++i)
+    {
+      visualObj = this.createVisual(link.visual[i]);
+      if (visualObj && !visualObj.parent)
+      {
+        linkObj.add(visualObj);
+      }
+    }
+  }
+
+  if (link.collision)
+  {
+    if (link.collision.visual)
+    {
+      if (!(link.collision.visual instanceof Array))
+      {
+        link.collision.visual = [link.collision.visual];
+      }
+
+      for (var j = 0; j < link.collision.visual.length; ++j)
+      {
+        visualObj = this.createVisual(link.collision.visual[j]);
+        if (visualObj && !visualObj.parent)
+        {
+          linkObj.add(visualObj);
+        }
+      }
+
+    }
+  }
+
+  return linkObj;
+};
+
+/**
+ * Creates 3D object according to model name and type of the model and add
+ * the created object to the scene.
+ * @param {THREE.Object3D} model - model object which will be added to scene
+ * @param {string} type - type of the model which can be followings: box,
+ * sphere, cylinder, spotlight, directionallight, pointlight
+ */
+GZ3D.SdfParser.prototype.addModelByType = function(model, type)
+{
+  var sdf, translation, euler;
+  var quaternion = new THREE.Quaternion();
+  var modelObj;
+
+  if (model.matrixWorld)
+  {
+    var matrix = model.matrixWorld;
+    translation = new THREE.Vector3();
+    euler = new THREE.Euler();
+    var scale = new THREE.Vector3();
+    matrix.decompose(translation, euler, scale);
+    quaternion.setFromEuler(euler);
+  }
+
+  if (type === 'box')
+  {
+    sdf = this.createBoxSDF(translation, euler);
+    modelObj = this.spawnFromSDF(sdf);
+  }
+  else if (type === 'sphere')
+  {
+    sdf = this.createSphereSDF(translation, euler);
+    modelObj = this.spawnFromSDF(sdf);
+  }
+  else if (type === 'cylinder')
+  {
+    sdf = this.createCylinderSDF(translation, euler);
+    modelObj = this.spawnFromSDF(sdf);
+  }
+  else if (type === 'spotlight')
+  {
+    modelObj = this.scene.createLight(2);
+    this.scene.setPose(modelObj, translation, quaternion);
+  }
+  else if (type === 'directionallight')
+  {
+    modelObj = this.scene.createLight(3);
+    this.scene.setPose(modelObj, translation, quaternion);
+  }
+  else if (type === 'pointlight')
+  {
+    modelObj = this.scene.createLight(1);
+    this.scene.setPose(modelObj, translation, quaternion);
+  }
+  else
+  {
+    var sdfObj = this.loadSDF(type);
+    modelObj = new THREE.Object3D();
+    modelObj.add(sdfObj);
+    modelObj.name = model.name;
+    this.scene.setPose(modelObj, translation, quaternion);
+  }
+  
+  var that = this;
+  
+  var addModelFunc;
+  addModelFunc = function()
+  {
+    // check whether object is removed
+    var obj = that.scene.getByName(modelObj.name);
+    if (obj === undefined)
+    {
+      that.scene.add(modelObj);
+      that.gui.setModelStats(modelObj, 'update');
+    }
+    else
+    {
+      setTimeout(addModelFunc, 100);
+    }
+  };
+  
+  setTimeout(addModelFunc , 100);
+
+//  this.scene.add(modelObj);
+//  this.gui.setModelStats(modelObj, 'update');
+};
+
+/**
+ * Creates SDF string for simple shapes: box, cylinder, sphere.
+ * @param {string} type - type of the model which can be followings: box,
+ * sphere, cylinder
+ * @param {THREE.Vector3} translation - denotes the x,y,z position
+ * of the object
+ * @param {THREE.Euler} euler - denotes the euler rotation of the object
+ * @param {string} geomSDF - geometry element string of 3D object which is
+ * already created according to type of the object
+ * @returns {string} sdf - SDF string of the simple shape
+ */
+GZ3D.SdfParser.prototype.createSimpleShapeSDF = function(type, translation,
+        euler, geomSDF)
+  {
+  var sdf;
+
+  sdf = '<sdf version="' + this.SDF_VERSION + '">' + '<model name="' + type
+          + '">' + '<pose>' + translation.x + ' ' + translation.y + ' '
+          + translation.z + ' ' + euler.x + ' ' + euler.y + ' ' + euler.z
+          + '</pose>' + '<link name="link">'
+          + '<inertial><mass>1.0</mass></inertial>'
+          + '<collision name="collision">' + '<geometry>' + geomSDF
+          + '</geometry>' + '</collision>' + '<visual name="visual">'
+          + '<geometry>' + geomSDF + '</geometry>' + '<material>' + '<script>'
+          + '<uri>file://media/materials/scripts/gazebo.material' + '</uri>'
+          + '<name>Gazebo/Grey</name>' + '</script>' + '</material>'
+          + '</visual>' + '</link>' + '</model>' + '</sdf>';
+
+  return sdf;
+};
+
+/**
+ * Creates SDF string of box geometry element
+ * @param {THREE.Vector3} translation - the x,y,z position of
+ * the box object
+ * @param {THREE.Euler} euler - the euler rotation of the box object
+ * @returns {string} geomSDF - geometry SDF string of the box
+ */
+GZ3D.SdfParser.prototype.createBoxSDF = function(translation, euler)
+{
+  var geomSDF = '<box>' + '<size>1.0 1.0 1.0</size>' + '</box>';
+
+  return this.createSimpleShapeSDF('box', translation, euler, geomSDF);
+};
+
+/**
+ * Creates SDF string of sphere geometry element
+ * @param {THREE.Vector3} translation - the x,y,z position of
+ * the box object
+ * @param {THREE.Euler} euler - the euler rotation of the box object
+ * @returns {string} geomSDF - geometry SDF string of the sphere
+ */
+GZ3D.SdfParser.prototype.createSphereSDF = function(translation, euler)
+{
+  var geomSDF = '<sphere>' + '<radius>0.5</radius>' + '</sphere>';
+
+  return this.createSimpleShapeSDF('sphere', translation, euler, geomSDF);
+};
+
+/**
+ * Creates SDF string of cylinder geometry element
+ * @param {THREE.Vector3} translation - the x,y,z position of
+ * the box object
+ * @param {THREE.Euler} euler - the euler rotation of the cylinder object
+ * @returns {string} geomSDF - geometry SDF string of the cylinder
+ */
+GZ3D.SdfParser.prototype.createCylinderSDF = function(translation, euler)
+{
+  var geomSDF = '<cylinder>' + '<radius>0.5</radius>' + '<length>1.0</length>'
+          + '</cylinder>';
+
+  return this.createSimpleShapeSDF('cylinder', translation, euler, geomSDF);
+};
+
+/**
+ * Loads SDF of the model. It first constructs the url of the model
+ * according to modelname
+ * @param {string} modelName - name of the model
+ * @returns {XMLDocument} modelDOM - SDF DOM object of the loaded model
+ */
+GZ3D.SdfParser.prototype.loadModel = function(modelName)
+{
+  var modelFile = this.MATERIAL_ROOT + modelName + '/model.sdf';
+
+  var xhttp = new XMLHttpRequest();
+  xhttp.overrideMimeType('text/xml');
+  xhttp.open('GET', modelFile, false);
+  xhttp.send();
+  return xhttp.responseXML;
+};
+
+/**
+ * Spawn a model into the scene
+ * @constructor
+ */
+GZ3D.SpawnModel = function(scene, domElement)
+{
+  this.scene = scene;
+  this.domElement = ( domElement !== undefined ) ? domElement : document;
+  this.init();
+  this.obj = undefined;
+  this.callback = undefined;
+  this.sdfParser = undefined;
+};
+
+/**
+ * Initialize SpawnModel
+ */
+GZ3D.SpawnModel.prototype.init = function()
+{
+  this.plane = new THREE.Plane(new THREE.Vector3(0, 0, 1), 0);
+  this.projector = new THREE.Projector();
+  this.ray = new THREE.Ray();
+  this.obj = null;
+  this.active = false;
+  this.snapDist = null;
+};
+
+/**
+ * Start spawning an entity. Only simple shapes supported so far.
+ * Adds a temp object to the scene which is not registered on the server.
+ * @param {string} entity
+ * @param {function} callback
+ */
+GZ3D.SpawnModel.prototype.start = function(entity, callback)
+{
+  if (this.active)
+  {
+    this.finish();
+  }
+
+  this.callback = callback;
+
+  this.obj = new THREE.Object3D();
+  var mesh;
+  if (entity === 'box')
+  {
+    mesh = this.scene.createBox(1, 1, 1);
+  }
+  else if (entity === 'sphere')
+  {
+    mesh = this.scene.createSphere(0.5);
+  }
+  else if (entity === 'cylinder')
+  {
+    mesh = this.scene.createCylinder(0.5, 1.0);
+  }
+  else if (entity === 'pointlight')
+  {
+    mesh = this.scene.createLight(1);
+  }
+  else if (entity === 'spotlight')
+  {
+    mesh = this.scene.createLight(2);
+  }
+  else if (entity === 'directionallight')
+  {
+    mesh = this.scene.createLight(3);
+  }
+  else
+  {
+    mesh = this.sdfParser.loadSDF(entity);
+    //TODO: add transparency to the object
+  }
+
+  this.obj.name = this.generateUniqueName(entity);
+  this.obj.add(mesh);
+
+  // temp model appears within current view
+  var pos = new THREE.Vector2(window.window.innerWidth/2, window.innerHeight/2);
+  var intersect = new THREE.Vector3();
+  this.scene.getRayCastModel(pos, intersect);
+
+  this.obj.position.x = intersect.x;
+  this.obj.position.y = intersect.y;
+  this.obj.position.z += 0.5;
+  this.scene.add(this.obj);
+  // For the inserted light to have effect
+  var allObjects = [];
+  this.scene.scene.getDescendants(allObjects);
+  for (var l = 0; l < allObjects.length; ++l)
+  {
+    if (allObjects[l].material)
+    {
+      allObjects[l].material.needsUpdate = true;
+    }
+  }
+
+  var that = this;
+
+  this.mouseDown = function(event) {that.onMouseDown(event);};
+  this.mouseUp = function(event) {that.onMouseUp(event);};
+  this.mouseMove = function(event) {that.onMouseMove(event);};
+  this.keyDown = function(event) {that.onKeyDown(event);};
+  this.touchMove = function(event) {that.onTouchMove(event,true);};
+  this.touchEnd = function(event) {that.onTouchEnd(event);};
+
+  this.domElement.addEventListener('mousedown', that.mouseDown, false);
+  this.domElement.addEventListener( 'mouseup', that.mouseUp, false);
+  this.domElement.addEventListener( 'mousemove', that.mouseMove, false);
+  document.addEventListener( 'keydown', that.keyDown, false);
+
+  this.domElement.addEventListener( 'touchmove', that.touchMove, false);
+  this.domElement.addEventListener( 'touchend', that.touchEnd, false);
+
+  this.active = true;
+
+};
+
+/**
+ * Finish spawning an entity: re-enable camera controls,
+ * remove listeners, remove temp object
+ */
+GZ3D.SpawnModel.prototype.finish = function()
+{
+  var that = this;
+
+  this.domElement.removeEventListener( 'mousedown', that.mouseDown, false);
+  this.domElement.removeEventListener( 'mouseup', that.mouseUp, false);
+  this.domElement.removeEventListener( 'mousemove', that.mouseMove, false);
+  document.removeEventListener( 'keydown', that.keyDown, false);
+
+  this.scene.remove(this.obj);
+  this.obj = undefined;
+  this.active = false;
+};
+
+/**
+ * Window event callback
+ * @param {} event - not yet
+ */
+GZ3D.SpawnModel.prototype.onMouseDown = function(event)
+{
+  // Does this ever get called?
+  // Change like this:
+  // https://bitbucket.org/osrf/gzweb/pull-request/14/switch-to-arrow-mode-when-spawning-models/diff
+  event.preventDefault();
+  event.stopImmediatePropagation();
+};
+
+/**
+ * Window event callback
+ * @param {} event - mousemove events
+ */
+GZ3D.SpawnModel.prototype.onMouseMove = function(event)
+{
+  if (!this.active)
+  {
+    return;
+  }
+
+  event.preventDefault();
+
+  this.moveSpawnedModel(event.clientX,event.clientY);
+};
+
+/**
+ * Window event callback
+ * @param {} event - touchmove events
+ */
+GZ3D.SpawnModel.prototype.onTouchMove = function(event,originalEvent)
+{
+  if (!this.active)
+  {
+    return;
+  }
+
+  var e;
+
+  if (originalEvent)
+  {
+    e = event;
+  }
+  else
+  {
+    e = event.originalEvent;
+  }
+  e.preventDefault();
+
+  if (e.touches.length === 1)
+  {
+    this.moveSpawnedModel(e.touches[ 0 ].pageX,e.touches[ 0 ].pageY);
+  }
+};
+
+/**
+ * Window event callback
+ * @param {} event - touchend events
+ */
+GZ3D.SpawnModel.prototype.onTouchEnd = function()
+{
+  if (!this.active)
+  {
+    return;
+  }
+
+  this.callback(this.obj);
+  this.finish();
+};
+
+/**
+ * Window event callback
+ * @param {} event - mousedown events
+ */
+GZ3D.SpawnModel.prototype.onMouseUp = function(event)
+{
+  if (!this.active)
+  {
+    return;
+  }
+
+  this.callback(this.obj);
+  this.finish();
+};
+
+/**
+ * Window event callback
+ * @param {} event - keydown events
+ */
+GZ3D.SpawnModel.prototype.onKeyDown = function(event)
+{
+  if ( event.keyCode === 27 ) // Esc
+  {
+    this.finish();
+  }
+};
+
+/**
+ * Move temp spawned model
+ * @param {integer} positionX - Horizontal position on the canvas
+ * @param {integer} positionY - Vertical position on the canvas
+ */
+GZ3D.SpawnModel.prototype.moveSpawnedModel = function(positionX, positionY)
+{
+  var vector = new THREE.Vector3( (positionX / window.innerWidth) * 2 - 1,
+        -(positionY / window.innerHeight) * 2 + 1, 0.5);
+  this.projector.unprojectVector(vector, this.scene.camera);
+  this.ray.set(this.scene.camera.position,
+      vector.sub(this.scene.camera.position).normalize());
+  var point = this.ray.intersectPlane(this.plane);
+  point.z = this.obj.position.z;
+
+  if(this.snapDist)
+  {
+    point.x = Math.round(point.x / this.snapDist) * this.snapDist;
+    point.y = Math.round(point.y / this.snapDist) * this.snapDist;
+  }
+
+  this.scene.setPose(this.obj, point, new THREE.Quaternion());
+
+  if (this.obj.children[0].children[0] &&
+     (this.obj.children[0].children[0] instanceof THREE.SpotLight ||
+      this.obj.children[0].children[0] instanceof THREE.DirectionalLight))
+  {
+    var lightObj = this.obj.children[0].children[0];
+    lightObj.target.position.copy(this.obj.position);
+    lightObj.target.position.add(new THREE.Vector3(0,0,-0.5));
+  }
+};
+
+/**
+ * Generate unique name for spawned entity
+ * @param {string} entity - entity type
+ */
+GZ3D.SpawnModel.prototype.generateUniqueName = function(entity)
+{
+  var i = 0;
+  while (i < 1000)
+  {
+    if (this.scene.getByName(entity+'_'+i))
+    {
+      ++i;
+    }
+    else
+    {
+      return entity+'_'+i;
+    }
+  }
+};
diff --git a/gz3d/client/favicon.ico b/gz3d/client/favicon.ico
new file mode 100644
index 0000000000000000000000000000000000000000..c0cc2e3dff34012ba3d4a7848a7ed17579788ec5
Binary files /dev/null and b/gz3d/client/favicon.ico differ
diff --git a/gz3d/client/index.html b/gz3d/client/index.html
new file mode 100644
index 0000000000000000000000000000000000000000..0b67c26134f5442016a0848ac84976856283303e
--- /dev/null
+++ b/gz3d/client/index.html
@@ -0,0 +1,1203 @@
+<!DOCTYPE html>
+<html lang="en" ng-app='gzangular'>
+  <head>
+    <title>gz3d</title>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0, initial-scale=1.0">
+    <link rel="stylesheet" href="style/jquery-mobile/jquery.mobile-1.4.0.css"/>
+    <script src="js/include/jquery-1.9.1.js"></script>
+    <script src="js/include/jquery.mobile-1.4.0.min.js"></script>
+    <script src="js/include/angular.min.js"></script>
+    <script src="js/include/three.js"></script>
+    <script src="js/include/OrbitControls.js"></script>
+    <script src="js/include/Detector.js"></script>
+    <script src="js/include/stats.min.js"></script>
+    <script src="js/include/eventemitter2.js"></script>
+    <script src="js/include/roslib.js"></script>
+    <script src="js/include/ColladaLoader.js"></script>
+    <script src="js/include/CopyShader.js"></script>
+    <script src="js/include/SSAOShader.js"></script>
+    <script src="js/include/EffectComposer.js"></script>
+    <script src="js/include/RenderPass.js"></script>
+    <script src="js/include/MaskPass.js"></script>
+    <script src="js/include/ShaderPass.js"></script>
+    <script src="js/include/xml2json.js"></script>
+    <script src="gz3d.js"></script>
+    <link rel="stylesheet" href="style/gz3d.css"/>
+  </head>
+
+  <script id="heightmapVS" type="x-shader/x-vertex">
+    varying vec2 vUv;
+    varying vec3 vPosition;
+    varying vec3 vNormal;
+
+    void main( void ) {
+      vUv = uv;
+      vPosition = position;
+      vNormal = normal;
+
+      gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
+    }
+  </script>
+
+  <script id="heightmapFS" type="x-shader/x-fragment">
+
+    uniform sampler2D texture0;
+    uniform sampler2D texture1;
+    uniform sampler2D texture2;
+
+    uniform float repeat0;
+    uniform float repeat1;
+    uniform float repeat2;
+
+    uniform float minHeight1;
+    uniform float minHeight2;
+
+    uniform float fadeDist1;
+    uniform float fadeDist2;
+
+    uniform vec3 ambient;
+    uniform vec3 lightDiffuse;
+    uniform vec3 lightDir;
+
+    varying vec2 vUv;
+    varying vec3 vPosition;
+    varying vec3 vNormal;
+
+    float blend(float distance, float fadeDist) {
+      float alpha = distance / fadeDist;
+      if (alpha < 0.0) {
+        alpha = 0.0;
+      }
+      if (alpha > 1.0) {
+        alpha = 1.0;
+      }
+      return alpha;
+    }
+
+    void main()
+    {
+      // Texture loading
+      vec3 diffuse0 = texture2D( texture0, vUv*repeat0 ).rgb;
+      vec3 diffuse1 = texture2D( texture1, vUv*repeat1 ).rgb;
+      vec3 diffuse2 = texture2D( texture2, vUv*repeat2 ).rgb;
+
+      // Get base texture
+      vec3 fragcolor = diffuse0;
+
+      // texture level 1
+      if (fadeDist1 > 0.0)
+      {
+        fragcolor = mix(
+          fragcolor,
+          diffuse1,
+          blend(vPosition.z - minHeight1, fadeDist1)
+        );
+      }
+      if (fadeDist2 > 0.0)
+      {
+        // texture level 2
+        fragcolor = mix(
+          fragcolor,
+          diffuse2,
+          blend(vPosition.z - (minHeight1 + minHeight2), fadeDist2)
+        );
+      }
+
+      vec3 lightDirNorm = normalize(lightDir);
+      float intensity = max(dot(vNormal, lightDirNorm), 0.0);
+      vec3 vLightFactor = ambient + lightDiffuse * intensity;
+      gl_FragColor = vec4(fragcolor.rgb * vLightFactor, 1.0);
+    }
+  </script>
+
+  <body>
+    <div data-role="page" data-theme="b" data-content-theme="c">
+
+      <!-- left panel -->
+      <div id="leftPanel" class="gzGUI">
+        <!-- main menu -->
+        <div id="mainMenu" class="leftPanels ui-overlay-shadow">
+          <div class="clickable panelTitle closePanels">
+            <h3>Menu</h3>
+          </div>
+          <div class="panelContent">
+            <ul>
+              <div data-role="collapsible" data-inset="false" data-iconpos="left" data-collapsed-icon="carat-r" data-expanded-icon="carat-d">
+                <h3><span class="collapsible_header">Edit</span></h3>
+                <ul data-role="listview">
+                  <li id='reset-world' data-icon="false"><a href="#"><span class="collapsible_item">Reset World</span></a></li>
+                  <li id='reset-model' data-icon="false"><a href="#"><span class="collapsible_item">Reset Model Poses</span></a></li>
+                  <li id='reset-view' data-icon="false"><a href="#"><span class="collapsible_item">Reset View</span></a></li>
+                </ul>
+              </div>
+              <div data-role="collapsible" data-inset="false" data-iconpos="left" data-collapsed-icon="carat-r" data-expanded-icon="carat-d">
+                  <h3><span class="collapsible_header">View</span></h3>
+                  <ul data-role="listview">
+                    <li id='view-grid' data-icon="false" data-iconpos="right" style="border: none !important;"><a href="#"><span class="collapsible_item">Grid</span></a></li>
+                    <li id='view-collisions' data-icon="false" data-iconpos="right" style="border: none !important;"><a href="#"><span class="collapsible_item">Collisions</span></a></li>
+                    <li id='view-orbit-indicator' data-icon="false" data-iconpos="right" style="border: none !important;"><a href="#"><span class="collapsible_item">Orbit Indicator</span></a></li>
+                  </ul>
+              </div>
+              <div data-role="collapsible" data-inset="false" data-iconpos="left" data-collapsed-icon="carat-r" data-expanded-icon="carat-d">
+                  <h3><span class="collapsible_header">Options</span></h3>
+                  <ul data-role="listview">
+                    <li id="snap-to-grid" data-icon="false" data-iconpos="right" style="border: none !important;"><a href="#"><span class="collapsible_item">Snap to Grid</span></a></li>
+                    <li id='open-tree-when-selected' data-icon="false" data-iconpos="right" style="border: none !important;"><a href="#"><span class="collapsible_item">Open tree on model selection</span></a></li>
+                    <li id='toggle-notifications' data-icon="false" data-iconpos="right" style="border: none !important;"><a href="#"><span class="collapsible_item">Notifications</span></a></li>
+                  </ul>
+              </div>
+            </ul>
+          </div>
+        </div>
+        <!-- insert menu -->
+        <div ng-controller="insertControl" class="panelsGroup">
+          <div id="insertMenu" class="leftPanels insertMenus ui-overlay-shadow">
+            <div class="clickable panelTitle closePanels" title="Insert">
+              <h3>Insert</h3>
+            </div>
+            <div class="panelContent">
+              <a ng-click="openTab('insertMenu-simple_shapes')" data-role="button" class="insertMenuItem insertMenuCategoryItem" title="Shapes">
+                Shapes<br>
+                <img src="style/images/cylinder.png">
+                <img src="style/images/sphere.png">
+                <img src="style/images/box.png">
+              </a>
+              <a ng-click="openTab('insertMenu-lights')" data-role="button" class="insertMenuItem insertMenuCategoryItem insertMenuCategoryItemLight" title="Lights">
+                Lights<br>
+                <img src="style/images/pointlight.png">
+                <img src="style/images/spotlight.png">
+                <img src="style/images/directionallight.png">
+                <img src="style/images/light_thumb.png">
+              </a>
+              <a ng-repeat="category in categories" ng-click="openTab('insertMenu-'+category.path)" data-role="button" class="insertMenuItem insertMenuCategoryItem" title="{{category.title}}">
+                {{category.title}}<br>
+                <img src="/assets/{{category.examplePath1}}/thumbnails/0.png">
+                <img src="/assets/{{category.examplePath2}}/thumbnails/0.png">
+                <img src="/assets/{{category.examplePath3}}/thumbnails/0.png">
+              </a>
+            </div>
+          </div>
+          <div id="insertMenu-simple_shapes" class="leftPanels insertMenus insertCategory ui-overlay-shadow">
+            <div class="clickable panelTitle" ng-click="openTab('insertMenu')" title="Simple Shapes">
+              <h3>Simple Shapes</h3>
+              <img src="style/images/back.png">
+            </div>
+            <div class="panelContent">
+              <a id="insert-entity-box" ng-mousedown="spawnEntity('box')" data-role="button" class="insertMenuItem" title="Box">
+                Box<br>
+                <img src="style/images/box.png">
+              </a>
+              <a id="insert-entity-sphere" ng-mousedown="spawnEntity('sphere')" data-role="button" class="insertMenuItem" title="Sphere">
+                Sphere<br>
+                <img src="style/images/sphere.png">
+              </a>
+              <a id="insert-entity-cylinder" ng-mousedown="spawnEntity('cylinder')" data-role="button" class="insertMenuItem" title="Cylinder">
+                Cylinder<br>
+                <img src="style/images/cylinder.png">
+              </a>
+            </div>
+          </div>
+          <div id="insertMenu-lights" class="leftPanels insertMenus insertCategory ui-overlay-shadow">
+            <div class="clickable panelTitle" ng-click="openTab('insertMenu')" title="Lights">
+              <h3>Lights</h3>
+              <img src="style/images/back.png">
+            </div>
+            <div class="panelContent">
+              <a id="insert-entity-pointlight" ng-mousedown="spawnEntity('pointlight')" data-role="button" class="insertMenuItem insertMenuItemLight" title="Point Light">
+                Point<br>
+                <img src="style/images/pointlight.png">
+              </a>
+              <a id="insert-entity-spotlight" ng-mousedown="spawnEntity('spotlight')" data-role="button" class="insertMenuItem insertMenuItemLight" title="Spot Light">
+                Spot<br>
+                <img src="style/images/spotlight.png">
+              </a>
+              <a id="insert-entity-directionallight" ng-mousedown="spawnEntity('directionallight')" data-role="button" class="insertMenuItem insertMenuItemLight" title="Directional Light">
+                Directional<br>
+                <img src="style/images/directionallight.png">
+              </a>
+            </div>
+          </div>
+          <div ng-repeat="category in categories" id="insertMenu-{{category.path}}" class="leftPanels insertMenus insertCategory ui-overlay-shadow">
+            <div class="clickable panelTitle" ng-click="openTab('insertMenu')" title="{{category.title}}">
+              <h3>{{category.title}}</h3>
+              <img src="style/images/back.png">
+            </div>
+            <div class="panelContent">
+              <a ng-repeat="model in category.models" id="insert-entity-{{model.modelPath}}" ng-mousedown="spawnEntity(model.modelPath)" data-role="button" class="insertMenuItem" title="{{model.modelTitle}}">
+              {{model.modelTitle}}<br>
+              <img src="/assets/{{model.modelPath}}/thumbnails/0.png">
+            </a>
+            </div>
+          </div>
+        </div>
+        <!-- scene tree -->
+        <div ng-controller="treeControl" class="panelsGroup">
+          <div id="treeMenu" class="leftPanels ui-overlay-shadow">
+            <div class="clickable panelTitle closePanels" title="Scene Tree">
+              <h3>Scene Tree</h3>
+            </div>
+            <div class="panelContent">
+              <!-- scene -->
+              <div class="clickable expandableTree notExpandableTree" ng-click="openTab('sceneProperties')">
+                Scene
+              </div>
+              <!-- physics -->
+              <div id="expand-PHYSICS" class="clickable expandableTree notExpandableTree" ng-click="openTab('physicsProperties')">
+                Physics
+              </div>
+              <!-- models -->
+              <div id="expand-MODELS" class="clickable expandableTree" ng-click="expandTree('MODELS')">
+                <img src="style/images/play.png">
+                Models
+              </div>
+              <div id="expandable-MODELS" class="expandedContent">
+                <ul data-role="listview">
+                  <div class="clickable treeItem {{model.selected}}" ng-repeat="model in models" ng-click="selectEntity(model.name)" ng-right-click="openEntityMenu($event, model.name)" title="{{model.name}}">
+                    <div class="treeItemImg"><img src="{{model.thumbnail}}"></div>
+                    <div class="treeItemTitle">{{model.name}}</div><br>
+                  </div>
+                </ul>
+              </div>
+              <!-- lights -->
+              <div id="expand-LIGHTS" class="clickable expandableTree" ng-click="expandTree('LIGHTS')">
+                <img src="style/images/play.png">
+                Lights
+              </div>
+              <div id="expandable-LIGHTS" class="expandedContent">
+                <ul data-role="listview">
+                  <div class="clickable treeItem {{light.selected}}" ng-repeat="light in lights" ng-click="selectEntity(light.name)" ng-right-click="openEntityMenu($event, light.name)" title="{{light.name}}">
+                    <div class="treeItemImg"><img src="{{light.thumbnail}}"></div>
+                    <div class="treeItemTitle">{{light.name}}</div><br>
+                  </div>
+                </ul>
+              </div>
+            </div>
+          </div>
+          <!-- property panels -->
+          <div ng-repeat="model in models" id="propertyPanel-{{model.name}}" class="leftPanels propertyPanels ui-overlay-shadow">
+            <div class="clickable panelTitle" ng-click="openTab('treeMenu')" title="{{model.name}}">
+              <h3>{{model.name}}</h3>
+              <img src="style/images/back.png">
+            </div>
+            <div class="panelContent">
+              <div class="propertyPanelImg">
+                <img src="{{model.thumbnail}}">
+              </div>
+              <div class="propertyPanelHeader">
+                <h4>Property</h4>
+                <h4>Value</h4>
+              </div>
+              <!-- general -->
+              <div id="expand-general-{{model.name}}" class="clickable expandableProperty expandableLevel1" ng-click="expandProperty('general', model.name)">
+                <img src="style/images/play.png">
+                General
+              </div>
+              <div id="expandable-general-{{model.name}}" class="expandedContent">
+                <div class="property propertyLevel2" title="{{model.name}}">
+                  <div>Name</div>
+                  <div>{{model.name}}</div>
+                </div>
+                <div class="property propertyLevel2">
+                  <div>Static</div>
+                  <div class="propertyToggle">
+                    <img src="style/images/{{model.is_static.icon}}.png">
+                    <div>
+                      {{model.is_static.title}}
+                    </div>
+                  </div>
+                </div>
+                <!-- model pose -->
+                <div id="expand-pose-{{model.name}}" class="clickable expandableProperty expandableLevel2" ng-click="expandProperty('pose', model.name)">
+                  <img src="style/images/play.png">
+                  Pose
+                </div>
+                <div id="expandable-pose-{{model.name}}" class="expandedContent">
+                  <div class="property propertyLevel3 lightBg">
+                    <div>x</div>
+                    <div class="propertyNumberEdit">
+                      <input type="number" onClick="this.select();" ng-model="model.position.x" ng-change="changePose('position', 'x', model.name, model.position.x)" data-role="none"/>
+                      <div class="propertyUnit">m</div>
+                    </div>
+                  </div>
+                  <div class="property propertyLevel3">
+                    <div>y</div>
+                    <div class="propertyNumberEdit">
+                      <input type="number" onClick="this.select();" ng-model="model.position.y" ng-change="changePose('position', 'y', model.name, model.position.y)" data-role="none"/>
+                      <div class="propertyUnit">m</div>
+                    </div>
+                  </div>
+                  <div class="property propertyLevel3 lightBg">
+                    <div>z</div>
+                    <div class="propertyNumberEdit">
+                      <input type="number" onClick="this.select();" ng-model="model.position.z" ng-change="changePose('position', 'z', model.name, model.position.z)" data-role="none"/>
+                      <div class="propertyUnit">m</div>
+                    </div>
+                  </div>
+                  <div class="property propertyLevel3">
+                    <div>Roll</div>
+                    <div class="propertyNumberEdit">
+                      <input type="number" onClick="this.select();" ng-model="model.orientation.roll" ng-change="changePose('orientation', 'x', model.name, model.orientation.roll)" data-role="none"/>
+                      <div class="propertyUnit propertyUnit3">rad</div>
+                    </div>
+                  </div>
+                  <div class="property propertyLevel3 lightBg">
+                    <div>Pitch</div>
+                    <div class="propertyNumberEdit">
+                      <input type="number" onClick="this.select();" ng-model="model.orientation.pitch" ng-change="changePose('orientation', 'y', model.name, model.orientation.pitch)" data-role="none"/>
+                      <div class="propertyUnit propertyUnit3">rad</div>
+                    </div>
+                  </div>
+                  <div class="property propertyLevel3">
+                    <div>Yaw</div>
+                    <div class="propertyNumberEdit">
+                      <input type="number" onClick="this.select();" ng-model="model.orientation.yaw" ng-change="changePose('orientation', 'z', model.name, model.orientation.yaw)" data-role="none"/>
+                      <div class="propertyUnit propertyUnit3">rad</div>
+                    </div>
+                  </div>
+                </div>
+              </div>
+              <!-- links -->
+              <div ng-if="model.links" id="expand-links-{{model.name}}" class="clickable expandableProperty expandableLevel1" ng-click="expandProperty('links', model.name)">
+                <img src="style/images/play.png">
+                Links
+              </div>
+              <div id="expandable-links-{{model.name}}" class="expandedContent">
+                <div ng-repeat="link in model.links">
+                  <div id="expand-link-{{model.name}}-{{link.shortName}}" class="clickable expandableProperty expandableLevel2" ng-click="expandProperty('link', model.name, link.shortName)" title="{{link.name}}">
+                    <img src="style/images/play.png">
+                    {{link.shortName}}
+                  </div>
+                  <div id="expandable-link-{{model.name}}-{{link.shortName}}" class="expandedContent">
+                    <div class="property propertyLevel3" title="{{link.name}}">
+                      <div>Name</div>
+                      <div>{{link.name}}</div>
+                    </div>
+                    <div class="clickable property propertyLevel3" ng-click="toggleProperty('self_collide', link.name)">
+                      <div>Self Collide</div>
+                      <div class="propertyToggle">
+                        <img src="style/images/{{link.self_collide.icon}}.png">
+                        <div>
+                          {{link.self_collide.title}}
+                        </div>
+                      </div>
+                    </div>
+                    <div class="clickable property propertyLevel3" ng-click="toggleProperty('gravity', link.name)">
+                      <div>Gravity</div>
+                      <div class="propertyToggle">
+                        <img src="style/images/{{link.gravity.icon}}.png">
+                        <div>
+                          {{link.gravity.title}}
+                        </div>
+                      </div>
+                    </div>
+                    <div class="clickable property propertyLevel3" ng-click="toggleProperty('kinematic', link.name)">
+                      <div>Kinematic</div>
+                      <div class="propertyToggle">
+                        <img src="style/images/{{link.kinematic.icon}}.png">
+                        <div>
+                          {{link.kinematic.title}}
+                        </div>
+                      </div>
+                    </div>
+                    <div class="property propertyLevel3">
+                      <div>Canonical</div>
+                      <div class="propertyToggle">
+                        <img src="style/images/{{link.canonical.icon}}.png">
+                        <div>
+                          {{link.canonical.title}}
+                        </div>
+                      </div>
+                    </div>
+                    <!-- link pose -->
+                    <div id="expand-pose-{{model.name}}-{{link.shortName}}" class="clickable expandableProperty expandableLevel3" ng-click="expandProperty('pose', model.name, link.shortName, link.name, 'link')">
+                      <img src="style/images/play.png">
+                      Pose
+                    </div>
+                    <div id="expandable-pose-{{model.name}}-{{link.shortName}}" class="expandedContent">
+                      <div class="property propertyLevel4 lightBg">
+                        <div>x</div>
+                        <div class="propertyNumber">
+                          <div>{{link.position.x}}</div>
+                          <div class="propertyUnit">m</div>
+                        </div>
+                      </div>
+                      <div class="property propertyLevel4">
+                        <div>y</div>
+                        <div class="propertyNumber">
+                          <div>{{link.position.y}}</div>
+                          <div class="propertyUnit">m</div>
+                        </div>
+                      </div>
+                      <div class="property propertyLevel4 lightBg">
+                        <div>z</div>
+                        <div class="propertyNumber">
+                          <div>{{link.position.z}}</div>
+                          <div class="propertyUnit">m</div>
+                        </div>
+                      </div>
+                      <div class="property propertyLevel4">
+                        <div>Roll</div>
+                        <div class="propertyNumber">
+                          <div>{{link.orientation.roll}}</div>
+                          <div class="propertyUnit propertyUnit3">rad</div>
+                        </div>
+                      </div>
+                      <div class="property propertyLevel4 lightBg">
+                        <div>Pitch</div>
+                        <div class="propertyNumber">
+                          <div>{{link.orientation.pitch}}</div>
+                          <div class="propertyUnit propertyUnit3">rad</div>
+                        </div>
+                      </div>
+                      <div class="property propertyLevel4">
+                        <div>Yaw</div>
+                        <div class="propertyNumber">
+                          <div>{{link.orientation.yaw}}</div>
+                          <div class="propertyUnit propertyUnit3">rad</div>
+                        </div>
+                      </div>
+                    </div>
+                    <!-- link inertial -->
+                    <div id="expand-inertial-{{model.name}}-{{link.shortName}}" class="clickable expandableProperty expandableLevel3" ng-click="expandProperty('inertial', model.name, link.shortName)">
+                      <img src="style/images/play.png">
+                      Inertial
+                    </div>
+                    <div id="expandable-inertial-{{model.name}}-{{link.shortName}}" class="expandedContent">
+                      <div class="property propertyLevel4">
+                        <div>Mass</div>
+                        <div class="propertyNumber">
+                          <div>{{link.inertial.mass}}</div>
+                          <div class="propertyUnit propertyUnit3">Kg</div>
+                        </div>
+                      </div>
+                      <!-- link inertial pose -->
+                      <div id="expand-inertial-pose-{{model.name}}-{{link.shortName}}" class="clickable expandableProperty expandableLevel4" ng-click="expandProperty('inertial-pose', model.name, link.shortName)">
+                        <img src="style/images/play.png">
+                        Pose
+                      </div>
+                      <div id="expandable-inertial-pose-{{model.name}}-{{link.shortName}}" class="expandedContent">
+                        <div class="property propertyLevel5 lightBg">
+                          <div>x</div>
+                          <div class="propertyNumber">
+                            <div>{{link.inertial.pose.position.x}}</div>
+                            <div class="propertyUnit">m</div>
+                          </div>
+                        </div>
+                        <div class="property propertyLevel5">
+                          <div>y</div>
+                          <div class="propertyNumber">
+                            <div>{{link.inertial.pose.position.y}}</div>
+                            <div class="propertyUnit">m</div>
+                          </div>
+                        </div>
+                        <div class="property propertyLevel5 lightBg">
+                          <div>z</div>
+                          <div class="propertyNumber">
+                            <div>{{link.inertial.pose.position.z}}</div>
+                            <div class="propertyUnit">m</div>
+                          </div>
+                        </div>
+                        <div class="property propertyLevel5">
+                          <div>Roll</div>
+                          <div class="propertyNumber">
+                            <div>{{link.inertial.pose.orientation.roll}}</div>
+                            <div class="propertyUnit propertyUnit3">rad</div>
+                          </div>
+                        </div>
+                        <div class="property propertyLevel5 lightBg">
+                          <div>Pitch</div>
+                          <div class="propertyNumber">
+                            <div>{{link.inertial.pose.orientation.pitch}}</div>
+                            <div class="propertyUnit propertyUnit3">rad</div>
+                          </div>
+                        </div>
+                        <div class="property propertyLevel5">
+                          <div>Yaw</div>
+                          <div class="propertyNumber">
+                            <div>{{link.inertial.pose.orientation.yaw}}</div>
+                            <div class="propertyUnit propertyUnit3">rad</div>
+                          </div>
+                        </div>
+                      </div>
+                      <!-- link inertial moments -->
+                      <div id="expand-inertial-moments-{{model.name}}-{{link.shortName}}" class="clickable expandableProperty expandableLevel4" ng-click="expandProperty('inertial-moments', model.name, link.shortName)">
+                        <img src="style/images/play.png">
+                        Moments
+                      </div>
+                      <div id="expandable-inertial-moments-{{model.name}}-{{link.shortName}}" class="expandedContent">
+                        <div class="property propertyLevel5 lightBg">
+                          <div>ixx</div>
+                          <div class="propertyNumber">
+                            <div>{{link.inertial.ixx}}</div>
+                            <div class="propertyUnit propertyUnit4">Kg.m<sup>2</sup></div>
+                          </div>
+                        </div>
+                        <div class="property propertyLevel5">
+                          <div>ixy</div>
+                          <div class="propertyNumber">
+                            <div>{{link.inertial.ixy}}</div>
+                            <div class="propertyUnit propertyUnit4">Kg.m<sup>2</sup></div>
+                          </div>
+                        </div>
+                        <div class="property propertyLevel5 lightBg">
+                          <div>ixz</div>
+                          <div class="propertyNumber">
+                            <div>{{link.inertial.ixz}}</div>
+                            <div class="propertyUnit propertyUnit4">Kg.m<sup>2</sup></div>
+                          </div>
+                        </div>
+                        <div class="property propertyLevel5">
+                          <div>iyy</div>
+                          <div class="propertyNumber">
+                            <div>{{link.inertial.iyy}}</div>
+                            <div class="propertyUnit propertyUnit4">Kg.m<sup>2</sup></div>
+                          </div>
+                        </div>
+                        <div class="property propertyLevel5 lightBg">
+                          <div>iyz</div>
+                          <div class="propertyNumber">
+                            <div>{{link.inertial.iyz}}</div>
+                            <div class="propertyUnit propertyUnit4">Kg.m<sup>2</sup></div>
+                          </div>
+                        </div>
+                        <div class="property propertyLevel5">
+                          <div>izz</div>
+                          <div class="propertyNumber">
+                            <div>{{link.inertial.izz}}</div>
+                            <div class="propertyUnit propertyUnit4">Kg.m<sup>2</sup></div>
+                          </div>
+                        </div>
+                      </div>
+                    </div>
+                  </div>
+                </div>
+              </div>
+              <!-- joints -->
+              <div ng-if="model.joints" id="expand-joints-{{model.name}}" class="clickable expandableProperty expandableLevel1" ng-click="expandProperty('joints', model.name)">
+                <img src="style/images/play.png">
+                Joints
+              </div>
+              <div id="expandable-joints-{{model.name}}" class="expandedContent">
+                <div ng-repeat="joint in model.joints">
+                  <div id="expand-joint-{{model.name}}-{{joint.shortName}}" class="clickable expandableProperty expandableLevel2" ng-click="expandProperty('joint', model.name, joint.shortName)" title="{{joint.name}}">
+                    <img src="style/images/play.png">
+                    {{joint.shortName}}
+                  </div>
+                  <div id="expandable-joint-{{model.name}}-{{joint.shortName}}" class="expandedContent">
+                    <div class="property propertyLevel3" title="{{joint.name}}">
+                      <div>Name</div>
+                      <div>{{joint.name}}</div>
+                    </div>
+                    <div class="property propertyLevel3" title="{{joint.type}}">
+                      <div>Type</div>
+                      <div>{{joint.type}}</div>
+                    </div>
+                    <div class="property propertyLevel3" title="{{joint.parent}}">
+                      <div>Parent Link</div>
+                      <div>{{joint.parentShortName}}</div>
+                    </div>
+                    <div class="property propertyLevel3" title="{{joint.child}}">
+                      <div>Child Link</div>
+                      <div>{{joint.childShortName}}</div>
+                    </div>
+                    <!-- joint pose -->
+                    <div id="expand-pose-{{model.name}}-{{joint.shortName}}" class="clickable expandableProperty expandableLevel3" ng-click="expandProperty('pose', model.name, joint.shortName, joint.name, 'joint')">
+                      <img src="style/images/play.png">
+                      Pose
+                    </div>
+                    <div id="expandable-pose-{{model.name}}-{{joint.shortName}}" class="expandedContent">
+                      <div class="property propertyLevel4 lightBg">
+                        <div>x</div>
+                        <div class="propertyNumber">{{joint.position.x}}</div>
+                      </div>
+                      <div class="property propertyLevel4">
+                        <div>y</div>
+                        <div class="propertyNumber">{{joint.position.y}}</div>
+                      </div>
+                      <div class="property propertyLevel4 lightBg">
+                        <div>z</div>
+                        <div class="propertyNumber">{{joint.position.z}}</div>
+                      </div>
+                      <div class="property propertyLevel4">
+                        <div>Roll</div>
+                        <div class="propertyNumber">{{joint.orientation.roll}}</div>
+                      </div>
+                      <div class="property propertyLevel4 lightBg">
+                        <div>Pitch</div>
+                        <div class="propertyNumber">{{joint.orientation.pitch}}</div>
+                      </div>
+                      <div class="property propertyLevel4">
+                        <div>Yaw</div>
+                        <div class="propertyNumber">{{joint.orientation.yaw}}</div>
+                      </div>
+                    </div>
+                    <!-- joint axis 1 -->
+                    <div ng-if="joint.axis1" id="expand-axis1-{{model.name}}-{{joint.shortName}}" class="clickable expandableProperty expandableLevel3" ng-click="expandProperty('axis1', model.name, joint.shortName, joint.name, 'joint')">
+                      <img src="style/images/play.png">
+                      Axis 1
+                    </div>
+                    <div id="expandable-axis1-{{model.name}}-{{joint.shortName}}" class="expandedContent">
+                      <div id="expand-axis1-direction-{{model.name}}-{{joint.shortName}}" class="clickable expandableProperty expandableLevel4" ng-click="expandProperty('axis1-direction', model.name, joint.shortName, joint.name, 'joint')">
+                        <img src="style/images/play.png">
+                        Direction
+                      </div>
+                      <div id="expandable-axis1-direction-{{model.name}}-{{joint.shortName}}" class="expandedContent">
+                        <div class="property propertyLevel5 lightBg">
+                          <div>x</div>
+                          <div class="propertyNumber">{{joint.axis1.direction.x}}</div>
+                        </div>
+                        <div class="property propertyLevel5">
+                          <div>y</div>
+                          <div class="propertyNumber">{{joint.axis1.direction.y}}</div>
+                        </div>
+                        <div class="property propertyLevel5 lightBg">
+                          <div>z</div>
+                          <div class="propertyNumber">{{joint.axis1.direction.z}}</div>
+                        </div>
+                      </div>
+                      <div class="property propertyLevel4">
+                        <div>Lower Limit</div>
+                        <div class="propertyNumber">{{joint.axis1.limit_lower}}</div>
+                      </div>
+                      <div class="property propertyLevel4">
+                        <div>Upper Limit</div>
+                        <div class="propertyNumber">{{joint.axis1.limit_upper}}</div>
+                      </div>
+                      <div class="property propertyLevel4">
+                        <div>Effort</div>
+                        <div class="propertyNumber">{{joint.axis1.limit_effort}}</div>
+                      </div>
+                      <div class="property propertyLevel4">
+                        <div>Velocity</div>
+                        <div class="propertyNumber">{{joint.axis1.limit_velocity}}</div>
+                      </div>
+                      <div class="property propertyLevel4">
+                        <div>Damping</div>
+                        <div class="propertyNumber">{{joint.axis1.damping}}</div>
+                      </div>
+                      <div class="property propertyLevel4">
+                        <div>Friction</div>
+                        <div class="propertyNumber">{{joint.axis1.friction}}</div>
+                      </div>
+                    </div>
+                    <!-- joint axis 2 -->
+                    <div ng-if="joint.axis2" id="expand-axis2-{{model.name}}-{{joint.shortName}}" class="clickable expandableProperty expandableLevel3" ng-click="expandProperty('axis2', model.name, joint.shortName, joint.name, 'joint')">
+                      <img src="style/images/play.png">
+                      Axis 2
+                    </div>
+                    <div id="expandable-axis2-{{model.name}}-{{joint.shortName}}" class="expandedContent">
+                      <div id="expand-axis2-direction-{{model.name}}-{{joint.shortName}}" class="clickable expandableProperty expandableLevel4" ng-click="expandProperty('axis2-direction', model.name, joint.shortName, joint.name, 'joint')">
+                        <img src="style/images/play.png">
+                        Direction
+                      </div>
+                      <div id="expandable-axis2-direction-{{model.name}}-{{joint.shortName}}" class="expandedContent">
+                        <div class="property propertyLevel5 lightBg">
+                          <div>x</div>
+                          <div class="propertyNumber">{{joint.axis2.direction.x}}</div>
+                        </div>
+                        <div class="property propertyLevel5">
+                          <div>y</div>
+                          <div class="propertyNumber">{{joint.axis2.direction.y}}</div>
+                        </div>
+                        <div class="property propertyLevel5 lightBg">
+                          <div>z</div>
+                          <div class="propertyNumber">{{joint.axis2.direction.z}}</div>
+                        </div>
+                      </div>
+                      <div class="property propertyLevel4">
+                        <div>Lower</div>
+                        <div class="propertyNumber">{{joint.axis2.limit_lower}}</div>
+                      </div>
+                      <div class="property propertyLevel4">
+                        <div>Upper</div>
+                        <div class="propertyNumber">{{joint.axis2.limit_upper}}</div>
+                      </div>
+                      <div class="property propertyLevel4">
+                        <div>Effort</div>
+                        <div class="propertyNumber">{{joint.axis2.limit_effort}}</div>
+                      </div>
+                      <div class="property propertyLevel4">
+                        <div>Velocity</div>
+                        <div class="propertyNumber">{{joint.axis2.limit_velocity}}</div>
+                      </div>
+                      <div class="property propertyLevel4">
+                        <div>Damping</div>
+                        <div class="propertyNumber">{{joint.axis2.damping}}</div>
+                      </div>
+                      <div class="property propertyLevel4">
+                        <div>Friction</div>
+                        <div class="propertyNumber">{{joint.axis2.friction}}</div>
+                      </div>
+                    </div>
+                  </div>
+                </div>
+              </div>
+            </div>
+          </div>
+          <!-- lights -->
+          <div ng-repeat="light in lights" id="propertyPanel-{{light.name}}" class="leftPanels propertyPanels ui-overlay-shadow">
+            <div class="clickable panelTitle" ng-click="openTab('treeMenu')" title="{{light.name}}">
+              <h3>{{light.name}}</h3>
+              <img src="style/images/back.png">
+            </div>
+            <div class="panelContent">
+              <div class="propertyPanelImg">
+                <img src="{{light.thumbnail}}">
+              </div>
+              <div class="propertyPanelHeader">
+                <h4>Property</h4>
+                <h4>Value</h4>
+              </div>
+              <div class="property propertyLevel1" title="{{light.name}}">
+                <div>Name</div>
+                <div>{{light.name}}</div>
+              </div>
+              <!-- light pose -->
+              <div id="expand-pose-{{light.name}}" class="clickable expandableProperty" ng-click="expandProperty('pose', light.name)">
+                <img src="style/images/play.png">
+                Pose
+              </div>
+              <div id="expandable-pose-{{light.name}}" class="expandedContent">
+                <div class="property propertyLevel2 lightBg">
+                  <div>x</div>
+                  <div class="propertyNumberEdit">
+                    <input type="number" onClick="this.select();" ng-model="light.position.x" ng-change="changePose('position', 'x', light.name, light.position.x)" data-role="none"/>
+                    <div class="propertyUnit">m</div>
+                  </div>
+                </div>
+                <div class="property propertyLevel2">
+                  <div>y</div>
+                  <div class="propertyNumberEdit">
+                    <input type="number" onClick="this.select();" ng-model="light.position.y" ng-change="changePose('position', 'y', light.name, light.position.y)" data-role="none"/>
+                    <div class="propertyUnit">m</div>
+                  </div>
+                </div>
+                <div class="property propertyLevel2 lightBg">
+                  <div>z</div>
+                  <div class="propertyNumberEdit">
+                    <input type="number" onClick="this.select();" ng-model="light.position.z" ng-change="changePose('position', 'z', light.name, light.position.z)" data-role="none"/>
+                    <div class="propertyUnit">m</div>
+                  </div>
+                </div>
+                <div class="property propertyLevel2">
+                  <div>Roll</div>
+                  <div class="propertyNumberEdit">
+                    <input type="number" onClick="this.select();" ng-model="light.orientation.roll" ng-change="changePose('orientation', 'x', light.name, light.orientation.roll)" data-role="none"/>
+                    <div class="propertyUnit propertyUnit3">rad</div>
+                  </div>
+                </div>
+                <div class="property propertyLevel2 lightBg">
+                  <div>Pitch</div>
+                  <div class="propertyNumberEdit">
+                    <input type="number" onClick="this.select();" ng-model="light.orientation.pitch" ng-change="changePose('orientation', 'y', light.name, light.orientation.pitch)" data-role="none"/>
+                    <div class="propertyUnit propertyUnit3">rad</div>
+                  </div>
+                </div>
+                <div class="property propertyLevel2">
+                  <div>Yaw</div>
+                  <div class="propertyNumberEdit">
+                    <input type="number" onClick="this.select();" ng-model="light.orientation.yaw" ng-change="changePose('orientation', 'z', light.name, light.orientation.yaw)" data-role="none"/>
+                    <div class="propertyUnit propertyUnit3">rad</div>
+                  </div>
+                </div>
+              </div>
+              <!-- light direction -->
+              <div ng-if="light.direction" id="expand-direction-{{light.name}}" class="clickable expandableProperty" ng-click="expandProperty('direction', light.name)">
+                <img src="style/images/play.png">
+                Direction
+              </div>
+              <div id="expandable-direction-{{light.name}}" class="expandedContent">
+                <div class="property propertyLevel2 lightBg">
+                  <div>x</div>
+                  <div class="propertyNumberEdit">
+                    <input type="number" onClick="this.select();" ng-model="light.direction.x" ng-change="changePose('direction', 'x', light.name, light.direction.x)" data-role="none"/>
+                  </div>
+                </div>
+                <div class="property propertyLevel2">
+                  <div>y</div>
+                  <div class="propertyNumberEdit">
+                    <input type="number" onClick="this.select();" ng-model="light.direction.y" ng-change="changePose('direction', 'y', light.name, light.direction.y)" data-role="none"/>
+                  </div>
+                </div>
+                <div class="property propertyLevel2 lightBg">
+                  <div>z</div>
+                  <div class="propertyNumberEdit">
+                    <input type="number" onClick="this.select();" ng-model="light.direction.z" ng-change="changePose('direction', 'z', light.name, light.direction.z)" data-role="none"/>
+                  </div>
+                </div>
+              </div>
+              <!-- light diffuse -->
+              <div id="expand-diffuse-{{light.name}}" class="clickable expandableProperty" ng-click="expandProperty('diffuse', light.name)">
+                <div>
+                  <img src="style/images/play.png">
+                  Diffuse
+                </div>
+                <div class="propertyColorEdit">
+                  <input class="clickable" type="color" ng-model="light.color.diffuse" ng-change="changeLight('diffuse', light.name, light.color.diffuse)"/>
+                </div>
+              </div>
+              <div id="expandable-diffuse-{{light.name}}" class="expandedContent">
+                <div class="property propertyLevel2 redBg">
+                  <div>Red</div>
+                  <div class="propertyNumber">{{light.diffuse.r}}</div>
+                </div>
+                <div class="property propertyLevel2 greenBg">
+                  <div>Green</div>
+                  <div class="propertyNumber">{{light.diffuse.g}}</div>
+                </div>
+                <div class="property propertyLevel2 blueBg">
+                  <div>Blue</div>
+                  <div class="propertyNumber">{{light.diffuse.b}}</div>
+                </div>
+                <div class="property propertyLevel2">
+                  <div>Alpha</div>
+                  <div class="propertyNumber">{{light.diffuse.a}}</div>
+                </div>
+              </div>
+              <!-- light specular -->
+              <div id="expand-specular-{{light.name}}" class="clickable expandableProperty" ng-click="expandProperty('specular', light.name)">
+                <div>
+                  <img src="style/images/play.png">
+                  Specular
+                </div>
+                <div class="propertyColorEdit">
+                  <input class="clickable" type="color" ng-model="light.color.specular" ng-change="changeLight('specular', light.name, light.color.specular)"/>
+                </div>
+              </div>
+              <div id="expandable-specular-{{light.name}}" class="expandedContent">
+                <div class="property propertyLevel2 redBg">
+                  <div>Red</div>
+                  <div class="propertyNumber">{{light.specular.r}}</div>
+                </div>
+                <div class="property propertyLevel2 greenBg">
+                  <div>Green</div>
+                  <div class="propertyNumber">{{light.specular.g}}</div>
+                </div>
+                <div class="property propertyLevel2 blueBg">
+                  <div>Blue</div>
+                  <div class="propertyNumber">{{light.specular.b}}</div>
+                </div>
+                <div class="property propertyLevel2">
+                  <div>Alpha</div>
+                  <div class="propertyNumber">{{light.specular.a}}</div>
+                </div>
+              </div>
+              <div class="property propertyLevel1">
+                <div>Range</div>
+                <div class="propertyNumberEdit">
+                  <input type="number" onClick="this.select();" ng-model="light.range" ng-change="changeLight('range', light.name, light.range)" data-role="none"/>
+                </div>
+              </div>
+              <!-- light attenuation -->
+              <div id="expand-attenuation-{{light.name}}" class="clickable expandableProperty" ng-click="expandProperty('attenuation', light.name)">
+                <img src="style/images/play.png">
+                Attenuation
+              </div>
+              <div id="expandable-attenuation-{{light.name}}" class="expandedContent">
+                <div class="property propertyLevel2">
+                  <div>Constant</div>
+                  <div class="propertyNumberEdit">
+                    <input type="number" onClick="this.select();" ng-model="light.attenuation.constant" ng-change="changeLight('attenuation_constant', light.name, light.attenuation.constant)" data-role="none"/>
+                  </div>
+                </div>
+                <div class="property propertyLevel2">
+                  <div>Linear</div>
+                  <div class="propertyNumberEdit">
+                    <input type="number" onClick="this.select();" ng-model="light.attenuation.linear" ng-change="changeLight('attenuation_linear', light.name, light.attenuation.linear)" data-role="none"/>
+                  </div>
+                </div>
+                <div class="property propertyLevel2">
+                  <div>Quadratic</div>
+                  <div class="propertyNumberEdit">
+                    <input type="number" onClick="this.select();" ng-model="light.attenuation.quadratic" ng-change="changeLight('attenuation_quadratic', light.name, light.attenuation.quadratic)" data-role="none"/>
+                  </div>
+                </div>
+              </div>
+            </div>
+          </div>
+          <!-- scene -->
+          <div id="sceneProperties" class="leftPanels propertyPanels ui-overlay-shadow">
+            <div class="clickable panelTitle" ng-click="openTab('treeMenu')" title="Scene">
+              <h3>Scene</h3>
+              <img src="style/images/back.png">
+            </div>
+            <div class="panelContent">
+              <div class="propertyPanelHeader">
+                <h4>Property</h4>
+                <h4>Value</h4>
+              </div>
+              <!-- scene ambient -->
+              <div id="expand-ambient-SCENE" class="clickable expandableProperty expandableLevel1" ng-click="expandProperty('ambient', 'SCENE')">
+                <div>
+                  <img src="style/images/play.png">
+                  Ambient
+                </div>
+                <div class="colorDisplay" style="background-color: rgb({{scene.ambient.r}},{{scene.ambient.g}},{{scene.ambient.b}})"></div>
+              </div>
+              <div id="expandable-ambient-SCENE" class="expandedContent">
+                <div class="property propertyLevel2 redBg">
+                  <div>Red</div>
+                  <div class="propertyNumber">{{scene.ambient.r}}</div>
+                </div>
+                <div class="property propertyLevel2 greenBg">
+                  <div>Green</div>
+                  <div class="propertyNumber">{{scene.ambient.g}}</div>
+                </div>
+                <div class="property propertyLevel2 blueBg">
+                  <div>Blue</div>
+                  <div class="propertyNumber">{{scene.ambient.b}}</div>
+                </div>
+                <div class="property propertyLevel2">
+                  <div>Alpha</div>
+                  <div class="propertyNumber">{{scene.ambient.a}}</div>
+                </div>
+              </div>
+              <!-- scene background -->
+              <div id="expand-background-SCENE" class="clickable expandableProperty expandableLevel1" ng-click="expandProperty('background', 'SCENE')">
+                <div>
+                  <img src="style/images/play.png">
+                  Background
+                </div>
+                <div class="colorDisplay" style="background-color: rgb({{scene.background.r}},{{scene.background.g}},{{scene.background.b}})"></div>
+              </div>
+              <div id="expandable-background-SCENE" class="expandedContent">
+                <div class="property propertyLevel2 redBg">
+                  <div>Red</div>
+                  <div class="propertyNumber">{{scene.background.r}}</div>
+                </div>
+                <div class="property propertyLevel2 greenBg">
+                  <div>Green</div>
+                  <div class="propertyNumber">{{scene.background.g}}</div>
+                </div>
+                <div class="property propertyLevel2 blueBg">
+                  <div>Blue</div>
+                  <div class="propertyNumber">{{scene.background.b}}</div>
+                </div>
+                <div class="property propertyLevel2">
+                  <div>Alpha</div>
+                  <div class="propertyNumber">{{scene.background.a}}</div>
+                </div>
+              </div>
+            </div>
+          </div>
+          <!-- physics -->
+          <div id="physicsProperties" class="leftPanels propertyPanels ui-overlay-shadow">
+            <div class="clickable panelTitle" ng-click="openTab('treeMenu')" title="Physics">
+              <h3>Physics</h3>
+              <img src="style/images/back.png">
+            </div>
+            <div class="panelContent">
+              <div class="propertyPanelHeader">
+                <h4>Property</h4>
+                <h4>Value</h4>
+              </div>
+              <div class="property propertyLevel1">
+                <div>Enable Physics</div>
+                <div class="propertyToggle">
+                  <img src="style/images/{{physics.enable_physics.icon}}.png">
+                  <div>
+                    {{physics.enable_physics.title}}
+                  </div>
+                </div>
+              </div>
+              <div class="property propertyLevel1">
+                <div id="real-time-update-rate">Real Time Update Rate</div>
+                <div class="propertyNumber">{{physics.real_time_update_rate}}</div>
+              </div>
+              <div class="property propertyLevel1">
+                <div>Max Step Size</div>
+                <div class="propertyNumber">{{physics.max_step_size}}</div>
+              </div>
+              <div id="expand-gravity-PHYSICS" class="clickable expandableProperty expandableLevel1" ng-click="expandProperty('gravity', 'PHYSICS')">
+                <div>
+                  <img src="style/images/play.png">
+                  Gravity
+                </div>
+              </div>
+              <div id="expandable-gravity-PHYSICS" class="expandedContent">
+                <div class="property propertyLevel2 lightBg">
+                  <div>x</div>
+                  <div class="propertyNumber">{{physics.gravity.x}}</div>
+                </div>
+                <div class="property propertyLevel2">
+                  <div>y</div>
+                  <div class="propertyNumber">{{physics.gravity.y}}</div>
+                </div>
+                <div class="property propertyLevel2 lightBg">
+                  <div>z</div>
+                  <div class="propertyNumber">{{physics.gravity.z}}</div>
+                </div>
+              </div>
+              <div id="expand-solver-PHYSICS" class="clickable expandableProperty expandableLevel1" ng-click="expandProperty('solver', 'PHYSICS')">
+                <div>
+                  <img src="style/images/play.png">
+                  Solver
+                </div>
+              </div>
+              <div id="expandable-solver-PHYSICS" class="expandedContent">
+                <div class="property propertyLevel2">
+                  <div>Iterations</div>
+                  <div class="propertyNumber">{{physics.iters}}</div>
+                </div>
+                <div class="property propertyLevel2">
+                  <div>SOR</div>
+                  <div class="propertyNumber">{{physics.sor}}</div>
+                </div>
+              </div>
+              <div id="expand-constraints-PHYSICS" class="clickable expandableProperty expandableLevel1" ng-click="expandProperty('constraints', 'PHYSICS')">
+                <div>
+                  <img src="style/images/play.png">
+                  Constraints
+                </div>
+              </div>
+              <div id="expandable-constraints-PHYSICS" class="expandedContent">
+                <div class="property propertyLevel2 lightBg">
+                  <div>CFM</div>
+                  <div class="propertyNumber">{{physics.cfm}}</div>
+                </div>
+                <div class="property propertyLevel2">
+                  <div>ERP</div>
+                  <div class="propertyNumber">{{physics.erp}}</div>
+                </div>
+                <div class="property propertyLevel2 lightBg">
+                  <div>Max Velocity</div>
+                  <div class="propertyNumber">{{physics.contact_max_correcting_vel}}</div>
+                </div>
+                <div class="property propertyLevel2">
+                  <div>Surface Layer</div>
+                  <div class="propertyNumber">{{physics.contact_surface_layer}}</div>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+      <!-- left panel -->
+
+      <!-- tabs -->
+      <a id="mainMenuTab" class="gzGUI tab clickable"><img src="style/images/bars.png" title="Menu"></a>
+      <a id="insertMenuTab" class="gzGUI tab clickable"><img src="style/images/insert.png" title="Insert"></a>
+      <a id="treeMenuTab" class="gzGUI tab clickable"><img src="style/images/tree.png" title="Scene Tree"></a>
+      <!-- tabs -->
+
+      <!-- header -->
+      <fieldset class="gzGUI" id="play-header-fieldset">
+        <a data-role="button" class="header-button" id="play"><span id="playText"><img src="style/images/play.png" title="Play"></span></a>
+      </fieldset>
+
+      <fieldset id="clock-header-fieldset"  class="gzGUI touch-only">
+        <a data-role="button" id="clock" class="header-button" data-rel="popup"><span><img src="style/images/clock.png"></span></a>
+      </fieldset>
+
+      <fieldset id="clock-mouse" class="gzGUI mouse-only">
+        <p>Real Time: <span class='real-time-value'>00 00:00:00</span></p>
+        <p>Sim Time: <span class='sim-time-value'>00 00:00:00</span></p>
+      </fieldset>
+
+      <div class="gzGUI" id="clock-touch" data-role="popup" data-history="false">
+        <p>Real Time: <span class='real-time-value'>00 00:00:00</span></p>
+        <p>Sim Time: <span class='sim-time-value'>00 00:00:00</span></p>
+      </div>
+
+      <fieldset class="gzGUI" data-role="controlgroup" data-mini="true" data-type="horizontal" id="mode-header-fieldset">
+        <input type="radio" name="radio-mini" id="view-mode" value="choice-1" checked="checked" />
+        <label for="view-mode"><img style="height:1.8em" src="style/images/arrow.png" title="View Mode (ESC)"></label>
+
+        <input type="radio" name="radio-mini" id="translate-mode" value="choice-2"  />
+        <label for="translate-mode"><img style="height:1.8em" src="style/images/translate.png" title="Translation Mode (T)"></label>
+
+        <input type="radio" name="radio-mini" id="rotate-mode" value="choice-3"  />
+        <label for="rotate-mode"><img style="height:1.8em" src="style/images/rotate.png" title="Rotation Mode (R)"></label>
+      </fieldset>
+
+      <fieldset id="box-header-fieldset" class="gzGUI mouse-only">
+        <a data-role="button" id="header-insert-box" class="header-button"><span><img src="style/images/box.png" title="Insert box"></span></a>
+      </fieldset>
+      <fieldset id="sphere-header-fieldset" class="gzGUI mouse-only">
+        <a data-role="button" id="header-insert-sphere" class="header-button"><span><img src="style/images/sphere.png" title="Insert sphere"></span></a>
+      </fieldset>
+      <fieldset id="cylinder-header-fieldset" class="gzGUI mouse-only">
+        <a data-role="button" id="header-insert-cylinder" class="header-button"><span><img src="style/images/cylinder.png" title="Insert cylinder"></span></a>
+      </fieldset>
+
+      <fieldset id="pointlight-header-fieldset" class="gzGUI mouse-only">
+        <a data-role="button" id="header-insert-pointlight" class="header-button"><span><img style="height:1.45em;" src="style/images/pointlight.png" title="Insert point light"></span></a>
+      </fieldset>
+      <fieldset id="spotlight-header-fieldset" class="gzGUI mouse-only">
+        <a data-role="button" id="header-insert-spotlight" class="header-button"><span><img src="style/images/spotlight.png" title="Insert spot light"></span></a>
+      </fieldset>
+      <fieldset id="directionallight-header-fieldset" class="gzGUI mouse-only">
+        <a data-role="button" id="header-insert-directionallight" class="header-button"><span><img src="style/images/directionallight.png" title="Insert directional light"></span></a>
+      </fieldset>
+      <!-- header -->
+
+      <!-- content -->
+      <div data-role="content" id="container">
+        <div data-role="popup" id="notification-popup" data-history="false"></div>
+        <div data-role="popup" data-corners="false" id="model-popup" data-history="false">
+          <ul data-role="listview">
+            <li id="view-transparent" data-corners="false" data-icon="false" data-iconpos="right"><a href="#">Transparent</a></li>
+            <li id="view-wireframe" data-corners="false" data-icon="false" data-iconpos="right"><a href="#">Wireframe</a></li>
+            <li id="view-joints" data-corners="false" data-icon="false" data-iconpos="right"><a href="#">Joints</a></li>
+            <li id="delete-entity" data-corners="false" data-icon="false"><a href="#">Delete</a></li>
+          </ul>
+        </div>
+      </div>
+      <!-- content -->
+
+    </div>
+    <!-- page -->
+
+    <!-- WebGL -->
+    <script>
+      if (!Detector.webgl)
+        Detector.addGetWebGLMessage();
+
+      var container, stats;
+      var scene;
+
+      init();
+      animate();
+
+      function init()
+      {
+        scene = new GZ3D.Scene();
+        gui = new GZ3D.Gui(scene);
+        iface = new GZ3D.GZIface(scene, gui);
+        sdfparser = new GZ3D.SdfParser(scene, gui, iface);
+
+        container = document.getElementById( 'container' );
+        container.appendChild(scene.getDomElement());
+
+        // FPS indicator
+        stats = new Stats();
+        stats.domElement.style.position = 'absolute';
+        stats.domElement.style.top = '0px';
+        stats.domElement.style.zIndex = 100;
+        //container.appendChild( stats.domElement );
+
+        window.addEventListener( 'resize', onWindowResize, false);
+      }
+
+      function onWindowResize()
+      {
+        scene.setWindowSize(window.innerWidth, window.innerHeight);
+        $('#clock-touch').popup('close');
+      }
+
+      function animate()
+      {
+        requestAnimationFrame(animate);
+        render();
+      }
+
+      function render()
+      {
+        scene.render();
+        //stats.update();
+      }
+    </script>
+
+  </body>
+</html>
diff --git a/gz3d/client/js/include/ColladaLoader.js b/gz3d/client/js/include/ColladaLoader.js
new file mode 100644
index 0000000000000000000000000000000000000000..f6e35d1e8e2c30a0e163953ac4eebbc1379f7a4e
--- /dev/null
+++ b/gz3d/client/js/include/ColladaLoader.js
@@ -0,0 +1,4708 @@
+/**
+ * @author Tim Knip / http://www.floorplanner.com/ / tim at floorplanner.com
+ */
+
+THREE.ColladaLoader = function () {
+
+	var COLLADA = null;
+	var scene = null;
+	var daeScene;
+
+	var readyCallbackFunc = null;
+
+ 	var sources = {};
+	var images = {};
+	var animations = {};
+	var controllers = {};
+	var geometries = {};
+	var materials = {};
+	var effects = {};
+	var cameras = {};
+	var lights = {};
+
+	var animData;
+	var visualScenes;
+	var baseUrl;
+	var morphs;
+	var skins;
+
+	var flip_uv = true;
+	var preferredShading = THREE.SmoothShading;
+
+	var options = {
+		// Force Geometry to always be centered at the local origin of the
+		// containing Mesh.
+		centerGeometry: false,
+
+		// Axis conversion is done for geometries, animations, and controllers.
+		// If we ever pull cameras or lights out of the COLLADA file, they'll
+		// need extra work.
+		convertUpAxis: false,
+
+		subdivideFaces: true,
+
+		upAxis: 'Y',
+
+		// For reflective or refractive materials we'll use this cubemap
+		defaultEnvMap: null
+
+	};
+
+	var colladaUnit = 1.0;
+	var colladaUp = 'Y';
+	var upConversion = null;
+
+	function load ( url, readyCallback, progressCallback ) {
+
+		var length = 0;
+
+		if ( document.implementation && document.implementation.createDocument ) {
+
+			var request = new XMLHttpRequest();
+
+			request.onreadystatechange = function() {
+
+				if( request.readyState == 4 ) {
+
+					if( request.status == 0 || request.status == 200 ) {
+
+
+						if ( request.responseXML ) {
+
+							readyCallbackFunc = readyCallback;
+							parse( request.responseXML, undefined, url );
+
+						} else if ( request.responseText ) {
+
+							readyCallbackFunc = readyCallback;
+							var xmlParser = new DOMParser();
+							var responseXML = xmlParser.parseFromString( request.responseText, "application/xml" );
+							parse( responseXML, undefined, url );
+
+						} else {
+
+							console.error( "ColladaLoader: Empty or non-existing file (" + url + ")" );
+
+						}
+
+					}
+
+				} else if ( request.readyState == 3 ) {
+
+					if ( progressCallback ) {
+
+						if ( length == 0 ) {
+
+							length = request.getResponseHeader( "Content-Length" );
+
+						}
+
+						progressCallback( { total: length, loaded: request.responseText.length } );
+
+					}
+
+				}
+
+			}
+
+			request.open( "GET", url, true );
+			request.send( null );
+
+		} else {
+
+			alert( "Don't know how to parse XML!" );
+
+		}
+
+	};
+
+	function parse( doc, callBack, url ) {
+
+		COLLADA = doc;
+		callBack = callBack || readyCallbackFunc;
+
+		if ( url !== undefined ) {
+
+			var parts = url.split( '/' );
+			parts.pop();
+			baseUrl = ( parts.length < 1 ? '.' : parts.join( '/' ) ) + '/';
+
+		}
+
+		parseAsset();
+		setUpConversion();
+		images = parseLib( "//dae:library_images/dae:image", _Image, "image" );
+		materials = parseLib( "//dae:library_materials/dae:material", Material, "material" );
+		effects = parseLib( "//dae:library_effects/dae:effect", Effect, "effect" );
+		geometries = parseLib( "//dae:library_geometries/dae:geometry", Geometry, "geometry" );
+		cameras = parseLib( ".//dae:library_cameras/dae:camera", Camera, "camera" );
+		lights = parseLib( ".//dae:library_lights/dae:light", Light, "light" );
+		controllers = parseLib( "//dae:library_controllers/dae:controller", Controller, "controller" );
+		animations = parseLib( "//dae:library_animations/dae:animation", Animation, "animation" );
+		visualScenes = parseLib( ".//dae:library_visual_scenes/dae:visual_scene", VisualScene, "visual_scene" );
+
+		morphs = [];
+		skins = [];
+
+		daeScene = parseScene();
+		scene = new THREE.Object3D();
+
+		for ( var i = 0; i < daeScene.nodes.length; i ++ ) {
+
+			scene.add( createSceneGraph( daeScene.nodes[ i ] ) );
+
+		}
+
+		// unit conversion
+		scene.scale.multiplyScalar( colladaUnit );
+
+		createAnimations();
+
+		var result = {
+
+			scene: scene,
+			morphs: morphs,
+			skins: skins,
+			animations: animData,
+			dae: {
+				images: images,
+				materials: materials,
+				cameras: cameras,
+				lights: lights,
+				effects: effects,
+				geometries: geometries,
+				controllers: controllers,
+				animations: animations,
+				visualScenes: visualScenes,
+				scene: daeScene
+			}
+
+		};
+
+		if ( callBack ) {
+
+			callBack( result );
+
+		}
+
+		return result;
+
+	};
+
+	function setPreferredShading ( shading ) {
+
+		preferredShading = shading;
+
+	};
+
+	function parseAsset () {
+
+		var elements = COLLADA.evaluate( '//dae:asset', COLLADA, _nsResolver, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null );
+
+		var element = elements.iterateNext();
+
+		if ( element && element.childNodes ) {
+
+			for ( var i = 0; i < element.childNodes.length; i ++ ) {
+
+				var child = element.childNodes[ i ];
+
+				switch ( child.nodeName ) {
+
+					case 'unit':
+
+						var meter = child.getAttribute( 'meter' );
+
+						if ( meter ) {
+
+							colladaUnit = parseFloat( meter );
+
+						}
+
+						break;
+
+					case 'up_axis':
+
+						colladaUp = child.textContent.charAt(0);
+						break;
+
+				}
+
+			}
+
+		}
+
+	};
+
+	function parseLib ( q, classSpec, prefix ) {
+
+		var elements = COLLADA.evaluate(q, COLLADA, _nsResolver, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null) ;
+
+		var lib = {};
+		var element = elements.iterateNext();
+		var i = 0;
+
+		while ( element ) {
+
+			var daeElement = ( new classSpec() ).parse( element );
+			if ( !daeElement.id || daeElement.id.length == 0 ) daeElement.id = prefix + ( i ++ );
+			lib[ daeElement.id ] = daeElement;
+
+			element = elements.iterateNext();
+
+		}
+
+		return lib;
+
+	};
+
+	function parseScene() {
+
+		var sceneElement = COLLADA.evaluate( './/dae:scene/dae:instance_visual_scene', COLLADA, _nsResolver, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null ).iterateNext();
+
+		if ( sceneElement ) {
+
+			var url = sceneElement.getAttribute( 'url' ).replace( /^#/, '' );
+			return visualScenes[ url.length > 0 ? url : 'visual_scene0' ];
+
+		} else {
+
+			return null;
+
+		}
+
+	};
+
+	function createAnimations() {
+
+		animData = [];
+
+		// fill in the keys
+		recurseHierarchy( scene );
+
+	};
+
+	function recurseHierarchy( node ) {
+
+		var n = daeScene.getChildById( node.name, true ),
+			newData = null;
+
+		if ( n && n.keys ) {
+
+			newData = {
+				fps: 60,
+				hierarchy: [ {
+					node: n,
+					keys: n.keys,
+					sids: n.sids
+				} ],
+				node: node,
+				name: 'animation_' + node.name,
+				length: 0
+			};
+
+			animData.push(newData);
+
+			for ( var i = 0, il = n.keys.length; i < il; i++ ) {
+
+				newData.length = Math.max( newData.length, n.keys[i].time );
+
+			}
+
+		} else  {
+
+			newData = {
+				hierarchy: [ {
+					keys: [],
+					sids: []
+				} ]
+			}
+
+		}
+
+		for ( var i = 0, il = node.children.length; i < il; i++ ) {
+
+			var d = recurseHierarchy( node.children[i] );
+
+			for ( var j = 0, jl = d.hierarchy.length; j < jl; j ++ ) {
+
+				newData.hierarchy.push( {
+					keys: [],
+					sids: []
+				} );
+
+			}
+
+		}
+
+		return newData;
+
+	};
+
+	function calcAnimationBounds () {
+
+		var start = 1000000;
+		var end = -start;
+		var frames = 0;
+
+		for ( var id in animations ) {
+
+			var animation = animations[ id ];
+
+			for ( var i = 0; i < animation.sampler.length; i ++ ) {
+
+				var sampler = animation.sampler[ i ];
+				sampler.create();
+
+				start = Math.min( start, sampler.startTime );
+				end = Math.max( end, sampler.endTime );
+				frames = Math.max( frames, sampler.input.length );
+
+			}
+
+		}
+
+		return { start:start, end:end, frames:frames };
+
+	};
+
+	function createMorph ( geometry, ctrl ) {
+
+		var morphCtrl = ctrl instanceof InstanceController ? controllers[ ctrl.url ] : ctrl;
+
+		if ( !morphCtrl || !morphCtrl.morph ) {
+
+			console.log("could not find morph controller!");
+			return;
+
+		}
+
+		var morph = morphCtrl.morph;
+
+		for ( var i = 0; i < morph.targets.length; i ++ ) {
+
+			var target_id = morph.targets[ i ];
+			var daeGeometry = geometries[ target_id ];
+
+			if ( !daeGeometry.mesh ||
+				 !daeGeometry.mesh.primitives ||
+				 !daeGeometry.mesh.primitives.length ) {
+				 continue;
+			}
+
+			var target = daeGeometry.mesh.primitives[ 0 ].geometry;
+
+			if ( target.vertices.length === geometry.vertices.length ) {
+
+				geometry.morphTargets.push( { name: "target_1", vertices: target.vertices } );
+
+			}
+
+		}
+
+		geometry.morphTargets.push( { name: "target_Z", vertices: geometry.vertices } );
+
+	};
+
+	function createSkin ( geometry, ctrl, applyBindShape ) {
+
+		var skinCtrl = controllers[ ctrl.url ];
+
+		if ( !skinCtrl || !skinCtrl.skin ) {
+
+			console.log( "could not find skin controller!" );
+			return;
+
+		}
+
+		if ( !ctrl.skeleton || !ctrl.skeleton.length ) {
+
+			console.log( "could not find the skeleton for the skin!" );
+			return;
+
+		}
+
+		var skin = skinCtrl.skin;
+		var skeleton = daeScene.getChildById( ctrl.skeleton[ 0 ] );
+		var hierarchy = [];
+
+		applyBindShape = applyBindShape !== undefined ? applyBindShape : true;
+
+		var bones = [];
+		geometry.skinWeights = [];
+		geometry.skinIndices = [];
+
+		//createBones( geometry.bones, skin, hierarchy, skeleton, null, -1 );
+		//createWeights( skin, geometry.bones, geometry.skinIndices, geometry.skinWeights );
+
+		/*
+		geometry.animation = {
+			name: 'take_001',
+			fps: 30,
+			length: 2,
+			JIT: true,
+			hierarchy: hierarchy
+		};
+		*/
+
+		if ( applyBindShape ) {
+
+			for ( var i = 0; i < geometry.vertices.length; i ++ ) {
+
+				geometry.vertices[ i ].applyMatrix4( skin.bindShapeMatrix );
+
+			}
+
+		}
+
+	};
+
+	function setupSkeleton ( node, bones, frame, parent ) {
+
+		node.world = node.world || new THREE.Matrix4();
+		node.world.copy( node.matrix );
+
+		if ( node.channels && node.channels.length ) {
+
+			var channel = node.channels[ 0 ];
+			var m = channel.sampler.output[ frame ];
+
+			if ( m instanceof THREE.Matrix4 ) {
+
+				node.world.copy( m );
+
+			}
+
+		}
+
+		if ( parent ) {
+
+			node.world.multiplyMatrices( parent, node.world );
+
+		}
+
+		bones.push( node );
+
+		for ( var i = 0; i < node.nodes.length; i ++ ) {
+
+			setupSkeleton( node.nodes[ i ], bones, frame, node.world );
+
+		}
+
+	};
+
+	function setupSkinningMatrices ( bones, skin ) {
+
+		// FIXME: this is dumb...
+
+		for ( var i = 0; i < bones.length; i ++ ) {
+
+			var bone = bones[ i ];
+			var found = -1;
+
+			if ( bone.type != 'JOINT' ) continue;
+
+			for ( var j = 0; j < skin.joints.length; j ++ ) {
+
+				if ( bone.sid == skin.joints[ j ] ) {
+
+					found = j;
+					break;
+
+				}
+
+			}
+
+			if ( found >= 0 ) {
+
+				var inv = skin.invBindMatrices[ found ];
+
+				bone.invBindMatrix = inv;
+				bone.skinningMatrix = new THREE.Matrix4();
+				bone.skinningMatrix.multiplyMatrices(bone.world, inv); // (IBMi * JMi)
+
+				bone.weights = [];
+
+				for ( var j = 0; j < skin.weights.length; j ++ ) {
+
+					for (var k = 0; k < skin.weights[ j ].length; k ++) {
+
+						var w = skin.weights[ j ][ k ];
+
+						if ( w.joint == found ) {
+
+							bone.weights.push( w );
+
+						}
+
+					}
+
+				}
+
+			} else {
+
+				throw 'ColladaLoader: Could not find joint \'' + bone.sid + '\'.';
+
+			}
+
+		}
+
+	};
+
+	function applySkin ( geometry, instanceCtrl, frame ) {
+
+		var skinController = controllers[ instanceCtrl.url ];
+
+		frame = frame !== undefined ? frame : 40;
+
+		if ( !skinController || !skinController.skin ) {
+
+			console.log( 'ColladaLoader: Could not find skin controller.' );
+			return;
+
+		}
+
+		if ( !instanceCtrl.skeleton || !instanceCtrl.skeleton.length ) {
+
+			console.log( 'ColladaLoader: Could not find the skeleton for the skin. ' );
+			return;
+
+		}
+
+		var animationBounds = calcAnimationBounds();
+		var skeleton = daeScene.getChildById( instanceCtrl.skeleton[0], true ) ||
+					   daeScene.getChildBySid( instanceCtrl.skeleton[0], true );
+
+		var i, j, w, vidx, weight;
+		var v = new THREE.Vector3(), o, s;
+
+		// move vertices to bind shape
+
+		for ( i = 0; i < geometry.vertices.length; i ++ ) {
+
+			geometry.vertices[i].applyMatrix4( skinController.skin.bindShapeMatrix );
+
+		}
+
+		// process animation, or simply pose the rig if no animation
+
+		for ( frame = 0; frame < animationBounds.frames; frame ++ ) {
+
+			var bones = [];
+			var skinned = [];
+
+			// zero skinned vertices
+
+			for ( i = 0; i < geometry.vertices.length; i++ ) {
+
+				skinned.push( new THREE.Vector3() );
+
+			}
+
+			// process the frame and setup the rig with a fresh
+			// transform, possibly from the bone's animation channel(s)
+
+			setupSkeleton( skeleton, bones, frame );
+			setupSkinningMatrices( bones, skinController.skin );
+
+			// skin 'm
+
+			for ( i = 0; i < bones.length; i ++ ) {
+
+				if ( bones[ i ].type != 'JOINT' ) continue;
+
+				for ( j = 0; j < bones[ i ].weights.length; j ++ ) {
+
+					w = bones[ i ].weights[ j ];
+					vidx = w.index;
+					weight = w.weight;
+
+					o = geometry.vertices[vidx];
+					s = skinned[vidx];
+
+					v.x = o.x;
+					v.y = o.y;
+					v.z = o.z;
+
+					v.applyMatrix4( bones[i].skinningMatrix );
+
+					s.x += (v.x * weight);
+					s.y += (v.y * weight);
+					s.z += (v.z * weight);
+
+				}
+
+			}
+
+			geometry.morphTargets.push( { name: "target_" + frame, vertices: skinned } );
+
+		}
+
+	};
+
+	function createSceneGraph ( node, parent ) {
+
+		var obj = new THREE.Object3D();
+		var skinned = false;
+		var skinController;
+		var morphController;
+		var i, j;
+
+		// FIXME: controllers
+
+		for ( i = 0; i < node.controllers.length; i ++ ) {
+
+			var controller = controllers[ node.controllers[ i ].url ];
+
+			switch ( controller.type ) {
+
+				case 'skin':
+
+					if ( geometries[ controller.skin.source ] ) {
+
+						var inst_geom = new InstanceGeometry();
+
+						inst_geom.url = controller.skin.source;
+						inst_geom.instance_material = node.controllers[ i ].instance_material;
+
+						node.geometries.push( inst_geom );
+						skinned = true;
+						skinController = node.controllers[ i ];
+
+					} else if ( controllers[ controller.skin.source ] ) {
+
+						// urgh: controller can be chained
+						// handle the most basic case...
+
+						var second = controllers[ controller.skin.source ];
+						morphController = second;
+					//	skinController = node.controllers[i];
+
+						if ( second.morph && geometries[ second.morph.source ] ) {
+
+							var inst_geom = new InstanceGeometry();
+
+							inst_geom.url = second.morph.source;
+							inst_geom.instance_material = node.controllers[ i ].instance_material;
+
+							node.geometries.push( inst_geom );
+
+						}
+
+					}
+
+					break;
+
+				case 'morph':
+
+					if ( geometries[ controller.morph.source ] ) {
+
+						var inst_geom = new InstanceGeometry();
+
+						inst_geom.url = controller.morph.source;
+						inst_geom.instance_material = node.controllers[ i ].instance_material;
+
+						node.geometries.push( inst_geom );
+						morphController = node.controllers[ i ];
+
+					}
+
+					console.log( 'ColladaLoader: Morph-controller partially supported.' );
+
+				default:
+					break;
+
+			}
+
+		}
+
+		// geometries
+
+		var double_sided_materials = {};
+
+		for ( i = 0; i < node.geometries.length; i ++ ) {
+
+			var instance_geometry = node.geometries[i];
+			var instance_materials = instance_geometry.instance_material;
+			var geometry = geometries[ instance_geometry.url ];
+			var used_materials = {};
+			var used_materials_array = [];
+			var num_materials = 0;
+			var first_material;
+
+			if ( geometry ) {
+
+				if ( !geometry.mesh || !geometry.mesh.primitives )
+					continue;
+
+				if ( obj.name.length == 0 ) {
+
+					obj.name = geometry.id;
+
+				}
+
+				// collect used fx for this geometry-instance
+
+				if ( instance_materials ) {
+
+					for ( j = 0; j < instance_materials.length; j ++ ) {
+
+						var instance_material = instance_materials[ j ];
+						var mat = materials[ instance_material.target ];
+						var effect_id = mat.instance_effect.url;
+						var shader = effects[ effect_id ].shader;
+						var material3js = shader.material;
+
+						if ( geometry.doubleSided ) {
+
+							if ( !( instance_material.symbol in double_sided_materials ) ) {
+
+								var _copied_material = material3js.clone();
+								_copied_material.side = THREE.DoubleSide;
+								double_sided_materials[ instance_material.symbol ] = _copied_material;
+
+							}
+
+							material3js = double_sided_materials[ instance_material.symbol ];
+
+						}
+
+						material3js.opacity = !material3js.opacity ? 1 : material3js.opacity;
+						used_materials[ instance_material.symbol ] = num_materials;
+						used_materials_array.push( material3js );
+						first_material = material3js;
+						first_material.name = mat.name == null || mat.name === '' ? mat.id : mat.name;
+						num_materials ++;
+
+					}
+
+				}
+
+				var mesh;
+				var material = first_material || new THREE.MeshLambertMaterial( { color: 0xdddddd, shading: THREE.FlatShading, side: geometry.doubleSided ? THREE.DoubleSide : THREE.FrontSide } );
+				var geom = geometry.mesh.geometry3js;
+
+				if ( num_materials > 1 ) {
+
+					material = new THREE.MeshFaceMaterial( used_materials_array );
+
+					for ( j = 0; j < geom.faces.length; j ++ ) {
+
+						var face = geom.faces[ j ];
+						face.materialIndex = used_materials[ face.daeMaterial ]
+
+					}
+
+				}
+
+				if ( skinController !== undefined ) {
+
+					applySkin( geom, skinController );
+
+					material.morphTargets = true;
+
+					mesh = new THREE.SkinnedMesh( geom, material, false );
+					mesh.skeleton = skinController.skeleton;
+					mesh.skinController = controllers[ skinController.url ];
+					mesh.skinInstanceController = skinController;
+					mesh.name = 'skin_' + skins.length;
+
+					skins.push( mesh );
+
+				} else if ( morphController !== undefined ) {
+
+					createMorph( geom, morphController );
+
+					material.morphTargets = true;
+
+					mesh = new THREE.Mesh( geom, material );
+					mesh.name = 'morph_' + morphs.length;
+
+					morphs.push( mesh );
+
+				} else {
+
+					mesh = new THREE.Mesh( geom, material );
+					mesh.geometry.name = geometry.name;
+					// mesh.geom.name = geometry.id;
+
+				}
+
+				node.geometries.length > 1 ? obj.add( mesh ) : obj = mesh;
+
+			}
+
+		}
+
+		for ( i = 0; i < node.cameras.length; i ++ ) {
+
+			var instance_camera = node.cameras[i];
+			var cparams = cameras[instance_camera.url];
+
+			obj = new THREE.PerspectiveCamera(cparams.fov, parseFloat(cparams.aspect_ratio),
+					parseFloat(cparams.znear), parseFloat(cparams.zfar));
+
+		}
+
+		for ( i = 0; i < node.lights.length; i ++ ) {
+
+			var instance_light = node.lights[i];
+			var lparams = lights[instance_light.url];
+
+			if ( lparams && lparams.technique ) {
+
+				var color = lparams.color.getHex();
+				var intensity = lparams.intensity;
+				var distance = 0;
+				var angle = lparams.falloff_angle;
+				var exponent; // Intentionally undefined, don't know what this is yet
+
+				switch ( lparams.technique ) {
+
+					case 'directional':
+
+						obj = new THREE.DirectionalLight( color, intensity, distance );
+						break;
+
+					case 'point':
+
+						obj = new THREE.PointLight( color, intensity, distance );
+						break;
+
+					case 'spot':
+
+						obj = new THREE.SpotLight( color, intensity, distance, angle, exponent );
+						break;
+
+					case 'ambient':
+
+						obj = new THREE.AmbientLight( color );
+						break;
+
+				}
+
+			}
+
+		}
+
+		obj.name = node.name || node.id || "";
+		obj.matrix = node.matrix;
+		obj.matrix.decompose( obj.position, obj.quaternion, obj.scale );
+
+		if ( options.centerGeometry && obj.geometry ) {
+
+			var delta = THREE.GeometryUtils.center( obj.geometry );
+			delta.multiply( obj.scale );
+			delta.applyQuaternion( obj.quaternion );
+
+			obj.position.sub( delta );
+
+		}
+
+		for ( i = 0; i < node.nodes.length; i ++ ) {
+
+			obj.add( createSceneGraph( node.nodes[i], node ) );
+
+		}
+
+		return obj;
+
+	};
+
+	function getJointId( skin, id ) {
+
+		for ( var i = 0; i < skin.joints.length; i ++ ) {
+
+			if ( skin.joints[ i ] == id ) {
+
+				return i;
+
+			}
+
+		}
+
+	};
+
+	function getLibraryNode( id ) {
+
+		return COLLADA.evaluate( './/dae:library_nodes//dae:node[@id=\'' + id + '\']', COLLADA, _nsResolver, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null ).iterateNext();
+
+	};
+
+	function getChannelsForNode (node ) {
+
+		var channels = [];
+		var startTime = 1000000;
+		var endTime = -1000000;
+
+		for ( var id in animations ) {
+
+			var animation = animations[id];
+
+			for ( var i = 0; i < animation.channel.length; i ++ ) {
+
+				var channel = animation.channel[i];
+				var sampler = animation.sampler[i];
+				var id = channel.target.split('/')[0];
+
+				if ( id == node.id ) {
+
+					sampler.create();
+					channel.sampler = sampler;
+					startTime = Math.min(startTime, sampler.startTime);
+					endTime = Math.max(endTime, sampler.endTime);
+					channels.push(channel);
+
+				}
+
+			}
+
+		}
+
+		if ( channels.length ) {
+
+			node.startTime = startTime;
+			node.endTime = endTime;
+
+		}
+
+		return channels;
+
+	};
+
+	function calcFrameDuration( node ) {
+
+		var minT = 10000000;
+
+		for ( var i = 0; i < node.channels.length; i ++ ) {
+
+			var sampler = node.channels[i].sampler;
+
+			for ( var j = 0; j < sampler.input.length - 1; j ++ ) {
+
+				var t0 = sampler.input[ j ];
+				var t1 = sampler.input[ j + 1 ];
+				minT = Math.min( minT, t1 - t0 );
+
+			}
+		}
+
+		return minT;
+
+	};
+
+	function calcMatrixAt( node, t ) {
+
+		var animated = {};
+
+		var i, j;
+
+		for ( i = 0; i < node.channels.length; i ++ ) {
+
+			var channel = node.channels[ i ];
+			animated[ channel.sid ] = channel;
+
+		}
+
+		var matrix = new THREE.Matrix4();
+
+		for ( i = 0; i < node.transforms.length; i ++ ) {
+
+			var transform = node.transforms[ i ];
+			var channel = animated[ transform.sid ];
+
+			if ( channel !== undefined ) {
+
+				var sampler = channel.sampler;
+				var value;
+
+				for ( j = 0; j < sampler.input.length - 1; j ++ ) {
+
+					if ( sampler.input[ j + 1 ] > t ) {
+
+						value = sampler.output[ j ];
+						//console.log(value.flatten)
+						break;
+
+					}
+
+				}
+
+				if ( value !== undefined ) {
+
+					if ( value instanceof THREE.Matrix4 ) {
+
+						matrix.multiplyMatrices( matrix, value );
+
+					} else {
+
+						// FIXME: handle other types
+
+						matrix.multiplyMatrices( matrix, transform.matrix );
+
+					}
+
+				} else {
+
+					matrix.multiplyMatrices( matrix, transform.matrix );
+
+				}
+
+			} else {
+
+				matrix.multiplyMatrices( matrix, transform.matrix );
+
+			}
+
+		}
+
+		return matrix;
+
+	};
+
+	function bakeAnimations ( node ) {
+
+		if ( node.channels && node.channels.length ) {
+
+			var keys = [],
+				sids = [];
+
+			for ( var i = 0, il = node.channels.length; i < il; i++ ) {
+
+				var channel = node.channels[i],
+					fullSid = channel.fullSid,
+					sampler = channel.sampler,
+					input = sampler.input,
+					transform = node.getTransformBySid( channel.sid ),
+					member;
+
+				if ( channel.arrIndices ) {
+
+					member = [];
+
+					for ( var j = 0, jl = channel.arrIndices.length; j < jl; j++ ) {
+
+						member[ j ] = getConvertedIndex( channel.arrIndices[ j ] );
+
+					}
+
+				} else {
+
+					member = getConvertedMember( channel.member );
+
+				}
+
+				if ( transform ) {
+
+					if ( sids.indexOf( fullSid ) === -1 ) {
+
+						sids.push( fullSid );
+
+					}
+
+					for ( var j = 0, jl = input.length; j < jl; j++ ) {
+
+						var time = input[j],
+							data = sampler.getData( transform.type, j ),
+							key = findKey( keys, time );
+
+						if ( !key ) {
+
+							key = new Key( time );
+							var timeNdx = findTimeNdx( keys, time );
+							keys.splice( timeNdx == -1 ? keys.length : timeNdx, 0, key );
+
+						}
+
+						key.addTarget( fullSid, transform, member, data );
+
+					}
+
+				} else {
+
+					console.log( 'Could not find transform "' + channel.sid + '" in node ' + node.id );
+
+				}
+
+			}
+
+			// post process
+			for ( var i = 0; i < sids.length; i++ ) {
+
+				var sid = sids[ i ];
+
+				for ( var j = 0; j < keys.length; j++ ) {
+
+					var key = keys[ j ];
+
+					if ( !key.hasTarget( sid ) ) {
+
+						interpolateKeys( keys, key, j, sid );
+
+					}
+
+				}
+
+			}
+
+			node.keys = keys;
+			node.sids = sids;
+
+		}
+
+	};
+
+	function findKey ( keys, time) {
+
+		var retVal = null;
+
+		for ( var i = 0, il = keys.length; i < il && retVal == null; i++ ) {
+
+			var key = keys[i];
+
+			if ( key.time === time ) {
+
+				retVal = key;
+
+			} else if ( key.time > time ) {
+
+				break;
+
+			}
+
+		}
+
+		return retVal;
+
+	};
+
+	function findTimeNdx ( keys, time) {
+
+		var ndx = -1;
+
+		for ( var i = 0, il = keys.length; i < il && ndx == -1; i++ ) {
+
+			var key = keys[i];
+
+			if ( key.time >= time ) {
+
+				ndx = i;
+
+			}
+
+		}
+
+		return ndx;
+
+	};
+
+	function interpolateKeys ( keys, key, ndx, fullSid ) {
+
+		var prevKey = getPrevKeyWith( keys, fullSid, ndx ? ndx-1 : 0 ),
+			nextKey = getNextKeyWith( keys, fullSid, ndx+1 );
+
+		if ( prevKey && nextKey ) {
+
+			var scale = (key.time - prevKey.time) / (nextKey.time - prevKey.time),
+				prevTarget = prevKey.getTarget( fullSid ),
+				nextData = nextKey.getTarget( fullSid ).data,
+				prevData = prevTarget.data,
+				data;
+
+			if ( prevTarget.type === 'matrix' ) {
+
+				data = prevData;
+
+			} else if ( prevData.length ) {
+
+				data = [];
+
+				for ( var i = 0; i < prevData.length; ++i ) {
+
+					data[ i ] = prevData[ i ] + ( nextData[ i ] - prevData[ i ] ) * scale;
+
+				}
+
+			} else {
+
+				data = prevData + ( nextData - prevData ) * scale;
+
+			}
+
+			key.addTarget( fullSid, prevTarget.transform, prevTarget.member, data );
+
+		}
+
+	};
+
+	// Get next key with given sid
+
+	function getNextKeyWith( keys, fullSid, ndx ) {
+
+		for ( ; ndx < keys.length; ndx++ ) {
+
+			var key = keys[ ndx ];
+
+			if ( key.hasTarget( fullSid ) ) {
+
+				return key;
+
+			}
+
+		}
+
+		return null;
+
+	};
+
+	// Get previous key with given sid
+
+	function getPrevKeyWith( keys, fullSid, ndx ) {
+
+		ndx = ndx >= 0 ? ndx : ndx + keys.length;
+
+		for ( ; ndx >= 0; ndx-- ) {
+
+			var key = keys[ ndx ];
+
+			if ( key.hasTarget( fullSid ) ) {
+
+				return key;
+
+			}
+
+		}
+
+		return null;
+
+	};
+
+	function _Image() {
+
+		this.id = "";
+		this.init_from = "";
+
+	};
+
+	_Image.prototype.parse = function(element) {
+
+		this.id = element.getAttribute('id');
+
+		for ( var i = 0; i < element.childNodes.length; i ++ ) {
+
+			var child = element.childNodes[ i ];
+
+			if ( child.nodeName == 'init_from' ) {
+
+				this.init_from = child.textContent;
+
+			}
+
+		}
+
+		return this;
+
+	};
+
+	function Controller() {
+
+		this.id = "";
+		this.name = "";
+		this.type = "";
+		this.skin = null;
+		this.morph = null;
+
+	};
+
+	Controller.prototype.parse = function( element ) {
+
+		this.id = element.getAttribute('id');
+		this.name = element.getAttribute('name');
+		this.type = "none";
+
+		for ( var i = 0; i < element.childNodes.length; i++ ) {
+
+			var child = element.childNodes[ i ];
+
+			switch ( child.nodeName ) {
+
+				case 'skin':
+
+					this.skin = (new Skin()).parse(child);
+					this.type = child.nodeName;
+					break;
+
+				case 'morph':
+
+					this.morph = (new Morph()).parse(child);
+					this.type = child.nodeName;
+					break;
+
+				default:
+					break;
+
+			}
+		}
+
+		return this;
+
+	};
+
+	function Morph() {
+
+		this.method = null;
+		this.source = null;
+		this.targets = null;
+		this.weights = null;
+
+	};
+
+	Morph.prototype.parse = function( element ) {
+
+		var sources = {};
+		var inputs = [];
+		var i;
+
+		this.method = element.getAttribute( 'method' );
+		this.source = element.getAttribute( 'source' ).replace( /^#/, '' );
+
+		for ( i = 0; i < element.childNodes.length; i ++ ) {
+
+			var child = element.childNodes[ i ];
+			if ( child.nodeType != 1 ) continue;
+
+			switch ( child.nodeName ) {
+
+				case 'source':
+
+					var source = ( new Source() ).parse( child );
+					sources[ source.id ] = source;
+					break;
+
+				case 'targets':
+
+					inputs = this.parseInputs( child );
+					break;
+
+				default:
+
+					console.log( child.nodeName );
+					break;
+
+			}
+
+		}
+
+		for ( i = 0; i < inputs.length; i ++ ) {
+
+			var input = inputs[ i ];
+			var source = sources[ input.source ];
+
+			switch ( input.semantic ) {
+
+				case 'MORPH_TARGET':
+
+					this.targets = source.read();
+					break;
+
+				case 'MORPH_WEIGHT':
+
+					this.weights = source.read();
+					break;
+
+				default:
+					break;
+
+			}
+		}
+
+		return this;
+
+	};
+
+	Morph.prototype.parseInputs = function(element) {
+
+		var inputs = [];
+
+		for ( var i = 0; i < element.childNodes.length; i ++ ) {
+
+			var child = element.childNodes[i];
+			if ( child.nodeType != 1) continue;
+
+			switch ( child.nodeName ) {
+
+				case 'input':
+
+					inputs.push( (new Input()).parse(child) );
+					break;
+
+				default:
+					break;
+			}
+		}
+
+		return inputs;
+
+	};
+
+	function Skin() {
+
+		this.source = "";
+		this.bindShapeMatrix = null;
+		this.invBindMatrices = [];
+		this.joints = [];
+		this.weights = [];
+
+	};
+
+	Skin.prototype.parse = function( element ) {
+
+		var sources = {};
+		var joints, weights;
+
+		this.source = element.getAttribute( 'source' ).replace( /^#/, '' );
+		this.invBindMatrices = [];
+		this.joints = [];
+		this.weights = [];
+
+		for ( var i = 0; i < element.childNodes.length; i ++ ) {
+
+			var child = element.childNodes[i];
+			if ( child.nodeType != 1 ) continue;
+
+			switch ( child.nodeName ) {
+
+				case 'bind_shape_matrix':
+
+					var f = _floats(child.textContent);
+					this.bindShapeMatrix = getConvertedMat4( f );
+					break;
+
+				case 'source':
+
+					var src = new Source().parse(child);
+					sources[ src.id ] = src;
+					break;
+
+				case 'joints':
+
+					joints = child;
+					break;
+
+				case 'vertex_weights':
+
+					weights = child;
+					break;
+
+				default:
+
+					console.log( child.nodeName );
+					break;
+
+			}
+		}
+
+		this.parseJoints( joints, sources );
+		this.parseWeights( weights, sources );
+
+		return this;
+
+	};
+
+	Skin.prototype.parseJoints = function ( element, sources ) {
+
+		for ( var i = 0; i < element.childNodes.length; i ++ ) {
+
+			var child = element.childNodes[ i ];
+			if ( child.nodeType != 1 ) continue;
+
+			switch ( child.nodeName ) {
+
+				case 'input':
+
+					var input = ( new Input() ).parse( child );
+					var source = sources[ input.source ];
+
+					if ( input.semantic == 'JOINT' ) {
+
+						this.joints = source.read();
+
+					} else if ( input.semantic == 'INV_BIND_MATRIX' ) {
+
+						this.invBindMatrices = source.read();
+
+					}
+
+					break;
+
+				default:
+					break;
+			}
+
+		}
+
+	};
+
+	Skin.prototype.parseWeights = function ( element, sources ) {
+
+		var v, vcount, inputs = [];
+
+		for ( var i = 0; i < element.childNodes.length; i ++ ) {
+
+			var child = element.childNodes[ i ];
+			if ( child.nodeType != 1 ) continue;
+
+			switch ( child.nodeName ) {
+
+				case 'input':
+
+					inputs.push( ( new Input() ).parse( child ) );
+					break;
+
+				case 'v':
+
+					v = _ints( child.textContent );
+					break;
+
+				case 'vcount':
+
+					vcount = _ints( child.textContent );
+					break;
+
+				default:
+					break;
+
+			}
+
+		}
+
+		var index = 0;
+
+		for ( var i = 0; i < vcount.length; i ++ ) {
+
+			var numBones = vcount[i];
+			var vertex_weights = [];
+
+			for ( var j = 0; j < numBones; j++ ) {
+
+				var influence = {};
+
+				for ( var k = 0; k < inputs.length; k ++ ) {
+
+					var input = inputs[ k ];
+					var value = v[ index + input.offset ];
+
+					switch ( input.semantic ) {
+
+						case 'JOINT':
+
+							influence.joint = value;//this.joints[value];
+							break;
+
+						case 'WEIGHT':
+
+							influence.weight = sources[ input.source ].data[ value ];
+							break;
+
+						default:
+							break;
+
+					}
+
+				}
+
+				vertex_weights.push( influence );
+				index += inputs.length;
+			}
+
+			for ( var j = 0; j < vertex_weights.length; j ++ ) {
+
+				vertex_weights[ j ].index = i;
+
+			}
+
+			this.weights.push( vertex_weights );
+
+		}
+
+	};
+
+	function VisualScene () {
+
+		this.id = "";
+		this.name = "";
+		this.nodes = [];
+		this.scene = new THREE.Object3D();
+
+	};
+
+	VisualScene.prototype.getChildById = function( id, recursive ) {
+
+		for ( var i = 0; i < this.nodes.length; i ++ ) {
+
+			var node = this.nodes[ i ].getChildById( id, recursive );
+
+			if ( node ) {
+
+				return node;
+
+			}
+
+		}
+
+		return null;
+
+	};
+
+	VisualScene.prototype.getChildBySid = function( sid, recursive ) {
+
+		for ( var i = 0; i < this.nodes.length; i ++ ) {
+
+			var node = this.nodes[ i ].getChildBySid( sid, recursive );
+
+			if ( node ) {
+
+				return node;
+
+			}
+
+		}
+
+		return null;
+
+	};
+
+	VisualScene.prototype.parse = function( element ) {
+
+		this.id = element.getAttribute( 'id' );
+		this.name = element.getAttribute( 'name' );
+		this.nodes = [];
+
+		for ( var i = 0; i < element.childNodes.length; i ++ ) {
+
+			var child = element.childNodes[ i ];
+			if ( child.nodeType != 1 ) continue;
+
+			switch ( child.nodeName ) {
+
+				case 'node':
+
+					this.nodes.push( ( new Node() ).parse( child ) );
+					break;
+
+				default:
+					break;
+
+			}
+
+		}
+
+		return this;
+
+	};
+
+	function Node() {
+
+		this.id = "";
+		this.name = "";
+		this.sid = "";
+		this.nodes = [];
+		this.controllers = [];
+		this.transforms = [];
+		this.geometries = [];
+		this.channels = [];
+		this.matrix = new THREE.Matrix4();
+
+	};
+
+	Node.prototype.getChannelForTransform = function( transformSid ) {
+
+		for ( var i = 0; i < this.channels.length; i ++ ) {
+
+			var channel = this.channels[i];
+			var parts = channel.target.split('/');
+			var id = parts.shift();
+			var sid = parts.shift();
+			var dotSyntax = (sid.indexOf(".") >= 0);
+			var arrSyntax = (sid.indexOf("(") >= 0);
+			var arrIndices;
+			var member;
+
+			if ( dotSyntax ) {
+
+				parts = sid.split(".");
+				sid = parts.shift();
+				member = parts.shift();
+
+			} else if ( arrSyntax ) {
+
+				arrIndices = sid.split("(");
+				sid = arrIndices.shift();
+
+				for ( var j = 0; j < arrIndices.length; j ++ ) {
+
+					arrIndices[ j ] = parseInt( arrIndices[ j ].replace( /\)/, '' ) );
+
+				}
+
+			}
+
+			if ( sid == transformSid ) {
+
+				channel.info = { sid: sid, dotSyntax: dotSyntax, arrSyntax: arrSyntax, arrIndices: arrIndices };
+				return channel;
+
+			}
+
+		}
+
+		return null;
+
+	};
+
+	Node.prototype.getChildById = function ( id, recursive ) {
+
+		if ( this.id == id ) {
+
+			return this;
+
+		}
+
+		if ( recursive ) {
+
+			for ( var i = 0; i < this.nodes.length; i ++ ) {
+
+				var n = this.nodes[ i ].getChildById( id, recursive );
+
+				if ( n ) {
+
+					return n;
+
+				}
+
+			}
+
+		}
+
+		return null;
+
+	};
+
+	Node.prototype.getChildBySid = function ( sid, recursive ) {
+
+		if ( this.sid == sid ) {
+
+			return this;
+
+		}
+
+		if ( recursive ) {
+
+			for ( var i = 0; i < this.nodes.length; i ++ ) {
+
+				var n = this.nodes[ i ].getChildBySid( sid, recursive );
+
+				if ( n ) {
+
+					return n;
+
+				}
+
+			}
+		}
+
+		return null;
+
+	};
+
+	Node.prototype.getTransformBySid = function ( sid ) {
+
+		for ( var i = 0; i < this.transforms.length; i ++ ) {
+
+			if ( this.transforms[ i ].sid == sid ) return this.transforms[ i ];
+
+		}
+
+		return null;
+
+	};
+
+	Node.prototype.parse = function( element ) {
+
+		var url;
+
+		this.id = element.getAttribute('id');
+		this.sid = element.getAttribute('sid');
+		this.name = element.getAttribute('name');
+		this.type = element.getAttribute('type');
+
+		this.type = this.type == 'JOINT' ? this.type : 'NODE';
+
+		this.nodes = [];
+		this.transforms = [];
+		this.geometries = [];
+		this.cameras = [];
+		this.lights = [];
+		this.controllers = [];
+		this.matrix = new THREE.Matrix4();
+
+		for ( var i = 0; i < element.childNodes.length; i ++ ) {
+
+			var child = element.childNodes[ i ];
+			if ( child.nodeType != 1 ) continue;
+
+			switch ( child.nodeName ) {
+
+				case 'node':
+
+					this.nodes.push( ( new Node() ).parse( child ) );
+					break;
+
+				case 'instance_camera':
+
+					this.cameras.push( ( new InstanceCamera() ).parse( child ) );
+					break;
+
+				case 'instance_controller':
+
+					this.controllers.push( ( new InstanceController() ).parse( child ) );
+					break;
+
+				case 'instance_geometry':
+
+					this.geometries.push( ( new InstanceGeometry() ).parse( child ) );
+					break;
+
+				case 'instance_light':
+
+					this.lights.push( ( new InstanceLight() ).parse( child ) );
+					break;
+
+				case 'instance_node':
+
+					url = child.getAttribute( 'url' ).replace( /^#/, '' );
+					var iNode = getLibraryNode( url );
+
+					if ( iNode ) {
+
+						this.nodes.push( ( new Node() ).parse( iNode )) ;
+
+					}
+
+					break;
+
+				case 'rotate':
+				case 'translate':
+				case 'scale':
+				case 'matrix':
+				case 'lookat':
+				case 'skew':
+
+					this.transforms.push( ( new Transform() ).parse( child ) );
+					break;
+
+				case 'extra':
+					break;
+
+				default:
+
+					console.log( child.nodeName );
+					break;
+
+			}
+
+		}
+
+		this.channels = getChannelsForNode( this );
+		bakeAnimations( this );
+
+		this.updateMatrix();
+
+		return this;
+
+	};
+
+	Node.prototype.updateMatrix = function () {
+
+		this.matrix.identity();
+
+		for ( var i = 0; i < this.transforms.length; i ++ ) {
+
+			this.transforms[ i ].apply( this.matrix );
+
+		}
+
+	};
+
+	function Transform () {
+
+		this.sid = "";
+		this.type = "";
+		this.data = [];
+		this.obj = null;
+
+	};
+
+	Transform.prototype.parse = function ( element ) {
+
+		this.sid = element.getAttribute( 'sid' );
+		this.type = element.nodeName;
+		this.data = _floats( element.textContent );
+		this.convert();
+
+		return this;
+
+	};
+
+	Transform.prototype.convert = function () {
+
+		switch ( this.type ) {
+
+			case 'matrix':
+
+				this.obj = getConvertedMat4( this.data );
+				break;
+
+			case 'rotate':
+
+				this.angle = THREE.Math.degToRad( this.data[3] );
+
+			case 'translate':
+
+				fixCoords( this.data, -1 );
+				this.obj = new THREE.Vector3( this.data[ 0 ], this.data[ 1 ], this.data[ 2 ] );
+				break;
+
+			case 'scale':
+
+				fixCoords( this.data, 1 );
+				this.obj = new THREE.Vector3( this.data[ 0 ], this.data[ 1 ], this.data[ 2 ] );
+				break;
+
+			default:
+				console.log( 'Can not convert Transform of type ' + this.type );
+				break;
+
+		}
+
+	};
+
+	Transform.prototype.apply = function () {
+
+		var m1 = new THREE.Matrix4();
+
+		return function ( matrix ) {
+
+			switch ( this.type ) {
+
+				case 'matrix':
+
+					matrix.multiply( this.obj );
+
+					break;
+
+				case 'translate':
+
+					matrix.multiply( m1.makeTranslation( this.obj.x, this.obj.y, this.obj.z ) );
+
+					break;
+
+				case 'rotate':
+
+					matrix.multiply( m1.makeRotationAxis( this.obj, this.angle ) );
+
+					break;
+
+				case 'scale':
+
+					matrix.scale( this.obj );
+
+					break;
+
+			}
+
+		};
+
+	}();
+
+	Transform.prototype.update = function ( data, member ) {
+
+		var members = [ 'X', 'Y', 'Z', 'ANGLE' ];
+
+		switch ( this.type ) {
+
+			case 'matrix':
+
+				if ( ! member ) {
+
+					this.obj.copy( data );
+
+				} else if ( member.length === 1 ) {
+
+					switch ( member[ 0 ] ) {
+
+						case 0:
+
+							this.obj.n11 = data[ 0 ];
+							this.obj.n21 = data[ 1 ];
+							this.obj.n31 = data[ 2 ];
+							this.obj.n41 = data[ 3 ];
+
+							break;
+
+						case 1:
+
+							this.obj.n12 = data[ 0 ];
+							this.obj.n22 = data[ 1 ];
+							this.obj.n32 = data[ 2 ];
+							this.obj.n42 = data[ 3 ];
+
+							break;
+
+						case 2:
+
+							this.obj.n13 = data[ 0 ];
+							this.obj.n23 = data[ 1 ];
+							this.obj.n33 = data[ 2 ];
+							this.obj.n43 = data[ 3 ];
+
+							break;
+
+						case 3:
+
+							this.obj.n14 = data[ 0 ];
+							this.obj.n24 = data[ 1 ];
+							this.obj.n34 = data[ 2 ];
+							this.obj.n44 = data[ 3 ];
+
+							break;
+
+					}
+
+				} else if ( member.length === 2 ) {
+
+					var propName = 'n' + ( member[ 0 ] + 1 ) + ( member[ 1 ] + 1 );
+					this.obj[ propName ] = data;
+
+				} else {
+
+					console.log('Incorrect addressing of matrix in transform.');
+
+				}
+
+				break;
+
+			case 'translate':
+			case 'scale':
+
+				if ( Object.prototype.toString.call( member ) === '[object Array]' ) {
+
+					member = members[ member[ 0 ] ];
+
+				}
+
+				switch ( member ) {
+
+					case 'X':
+
+						this.obj.x = data;
+						break;
+
+					case 'Y':
+
+						this.obj.y = data;
+						break;
+
+					case 'Z':
+
+						this.obj.z = data;
+						break;
+
+					default:
+
+						this.obj.x = data[ 0 ];
+						this.obj.y = data[ 1 ];
+						this.obj.z = data[ 2 ];
+						break;
+
+				}
+
+				break;
+
+			case 'rotate':
+
+				if ( Object.prototype.toString.call( member ) === '[object Array]' ) {
+
+					member = members[ member[ 0 ] ];
+
+				}
+
+				switch ( member ) {
+
+					case 'X':
+
+						this.obj.x = data;
+						break;
+
+					case 'Y':
+
+						this.obj.y = data;
+						break;
+
+					case 'Z':
+
+						this.obj.z = data;
+						break;
+
+					case 'ANGLE':
+
+						this.angle = THREE.Math.degToRad( data );
+						break;
+
+					default:
+
+						this.obj.x = data[ 0 ];
+						this.obj.y = data[ 1 ];
+						this.obj.z = data[ 2 ];
+						this.angle = THREE.Math.degToRad( data[ 3 ] );
+						break;
+
+				}
+				break;
+
+		}
+
+	};
+
+	function InstanceController() {
+
+		this.url = "";
+		this.skeleton = [];
+		this.instance_material = [];
+
+	};
+
+	InstanceController.prototype.parse = function ( element ) {
+
+		this.url = element.getAttribute('url').replace(/^#/, '');
+		this.skeleton = [];
+		this.instance_material = [];
+
+		for ( var i = 0; i < element.childNodes.length; i ++ ) {
+
+			var child = element.childNodes[ i ];
+			if ( child.nodeType !== 1 ) continue;
+
+			switch ( child.nodeName ) {
+
+				case 'skeleton':
+
+					this.skeleton.push( child.textContent.replace(/^#/, '') );
+					break;
+
+				case 'bind_material':
+
+					var instances = COLLADA.evaluate( './/dae:instance_material', child, _nsResolver, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null );
+
+					if ( instances ) {
+
+						var instance = instances.iterateNext();
+
+						while ( instance ) {
+
+							this.instance_material.push( (new InstanceMaterial()).parse(instance) );
+							instance = instances.iterateNext();
+
+						}
+
+					}
+
+					break;
+
+				case 'extra':
+					break;
+
+				default:
+					break;
+
+			}
+		}
+
+		return this;
+
+	};
+
+	function InstanceMaterial () {
+
+		this.symbol = "";
+		this.target = "";
+
+	};
+
+	InstanceMaterial.prototype.parse = function ( element ) {
+
+		this.symbol = element.getAttribute('symbol');
+		this.target = element.getAttribute('target').replace(/^#/, '');
+		return this;
+
+	};
+
+	function InstanceGeometry() {
+
+		this.url = "";
+		this.instance_material = [];
+
+	};
+
+	InstanceGeometry.prototype.parse = function ( element ) {
+
+		this.url = element.getAttribute('url').replace(/^#/, '');
+		this.instance_material = [];
+
+		for ( var i = 0; i < element.childNodes.length; i ++ ) {
+
+			var child = element.childNodes[i];
+			if ( child.nodeType != 1 ) continue;
+
+			if ( child.nodeName == 'bind_material' ) {
+
+				var instances = COLLADA.evaluate( './/dae:instance_material', child, _nsResolver, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null );
+
+				if ( instances ) {
+
+					var instance = instances.iterateNext();
+
+					while ( instance ) {
+
+						this.instance_material.push( (new InstanceMaterial()).parse(instance) );
+						instance = instances.iterateNext();
+
+					}
+
+				}
+
+				break;
+
+			}
+
+		}
+
+		return this;
+
+	};
+
+	function Geometry() {
+
+		this.id = "";
+		this.name = "";
+		this.mesh = null;
+
+	};
+
+	Geometry.prototype.parse = function ( element ) {
+
+		this.id = element.getAttribute('id');
+		this.name = element.getAttribute('name');
+
+		extractDoubleSided( this, element );
+
+		for ( var i = 0; i < element.childNodes.length; i ++ ) {
+
+			var child = element.childNodes[i];
+
+			switch ( child.nodeName ) {
+
+				case 'mesh':
+
+					this.mesh = (new Mesh(this)).parse(child);
+					break;
+
+				case 'extra':
+
+					// console.log( child );
+					break;
+
+				default:
+					break;
+			}
+		}
+
+		return this;
+
+	};
+
+	function Mesh( geometry ) {
+
+		this.geometry = geometry.id;
+		this.primitives = [];
+		this.vertices = null;
+		this.geometry3js = null;
+
+	};
+
+	Mesh.prototype.parse = function( element ) {
+
+		this.primitives = [];
+
+		var i, j;
+
+		for ( i = 0; i < element.childNodes.length; i ++ ) {
+
+			var child = element.childNodes[ i ];
+
+			switch ( child.nodeName ) {
+
+				case 'source':
+
+					_source( child );
+					break;
+
+				case 'vertices':
+
+					this.vertices = ( new Vertices() ).parse( child );
+					break;
+
+				case 'triangles':
+
+					this.primitives.push( ( new Triangles().parse( child ) ) );
+					break;
+
+				case 'polygons':
+
+					this.primitives.push( ( new Polygons().parse( child ) ) );
+					break;
+
+				case 'polylist':
+
+					this.primitives.push( ( new Polylist().parse( child ) ) );
+					break;
+
+				default:
+					break;
+
+			}
+
+		}
+
+		this.geometry3js = new THREE.Geometry();
+
+		var vertexData = sources[ this.vertices.input['POSITION'].source ].data;
+
+		for ( i = 0; i < vertexData.length; i += 3 ) {
+
+			this.geometry3js.vertices.push( getConvertedVec3( vertexData, i ).clone() );
+
+		}
+
+		for ( i = 0; i < this.primitives.length; i ++ ) {
+
+			var primitive = this.primitives[ i ];
+			primitive.setVertices( this.vertices );
+			this.handlePrimitive( primitive, this.geometry3js );
+
+		}
+
+		this.geometry3js.computeCentroids();
+		this.geometry3js.computeFaceNormals();
+
+		if ( this.geometry3js.calcNormals ) {
+
+			this.geometry3js.computeVertexNormals();
+			delete this.geometry3js.calcNormals;
+
+		}
+
+		// this.geometry3js.computeBoundingBox();
+
+		return this;
+
+	};
+
+	Mesh.prototype.handlePrimitive = function( primitive, geom ) {
+
+		var j, k, pList = primitive.p, inputs = primitive.inputs;
+		var input, index, idx32;
+		var source, numParams;
+		var vcIndex = 0, vcount = 3, maxOffset = 0;
+		var texture_sets = [];
+
+		for ( j = 0; j < inputs.length; j ++ ) {
+
+			input = inputs[ j ];
+			var offset = input.offset + 1;
+			maxOffset = (maxOffset < offset)? offset : maxOffset;
+
+			switch ( input.semantic ) {
+
+				case 'TEXCOORD':
+					texture_sets.push( input.set );
+					break;
+
+			}
+
+		}
+
+		for ( var pCount = 0; pCount < pList.length; ++pCount ) {
+
+			var p = pList[ pCount ], i = 0;
+
+			while ( i < p.length ) {
+
+				var vs = [];
+				var ns = [];
+				var ts = null;
+				var cs = [];
+
+				if ( primitive.vcount ) {
+
+					vcount = primitive.vcount.length ? primitive.vcount[ vcIndex ++ ] : primitive.vcount;
+
+				} else {
+
+					vcount = p.length / maxOffset;
+
+				}
+
+
+				for ( j = 0; j < vcount; j ++ ) {
+
+					for ( k = 0; k < inputs.length; k ++ ) {
+
+						input = inputs[ k ];
+						source = sources[ input.source ];
+
+						index = p[ i + ( j * maxOffset ) + input.offset ];
+						numParams = source.accessor.params.length;
+						idx32 = index * numParams;
+
+						switch ( input.semantic ) {
+
+							case 'VERTEX':
+
+								vs.push( index );
+
+								break;
+
+							case 'NORMAL':
+
+								ns.push( getConvertedVec3( source.data, idx32 ) );
+
+								break;
+
+							case 'TEXCOORD':
+
+								ts = ts || { };
+								if ( ts[ input.set ] === undefined ) ts[ input.set ] = [];
+								// invert the V
+								ts[ input.set ].push( new THREE.Vector2( source.data[ idx32 ], source.data[ idx32 + 1 ] ) );
+
+								break;
+
+							case 'COLOR':
+
+								cs.push( new THREE.Color().setRGB( source.data[ idx32 ], source.data[ idx32 + 1 ], source.data[ idx32 + 2 ] ) );
+
+								break;
+
+							default:
+
+								break;
+
+						}
+
+					}
+
+				}
+
+				if ( ns.length == 0 ) {
+
+					// check the vertices inputs
+					input = this.vertices.input.NORMAL;
+
+					if ( input ) {
+
+						source = sources[ input.source ];
+						numParams = source.accessor.params.length;
+
+						for ( var ndx = 0, len = vs.length; ndx < len; ndx++ ) {
+
+							ns.push( getConvertedVec3( source.data, vs[ ndx ] * numParams ) );
+
+						}
+
+					} else {
+
+						geom.calcNormals = true;
+
+					}
+
+				}
+
+				if ( !ts ) {
+
+					ts = { };
+					// check the vertices inputs
+					input = this.vertices.input.TEXCOORD;
+
+					if ( input ) {
+
+						texture_sets.push( input.set );
+						source = sources[ input.source ];
+						numParams = source.accessor.params.length;
+
+						for ( var ndx = 0, len = vs.length; ndx < len; ndx++ ) {
+
+							idx32 = vs[ ndx ] * numParams;
+							if ( ts[ input.set ] === undefined ) ts[ input.set ] = [ ];
+							// invert the V
+							ts[ input.set ].push( new THREE.Vector2( source.data[ idx32 ], 1.0 - source.data[ idx32 + 1 ] ) );
+
+						}
+
+					}
+
+				}
+
+				if ( cs.length == 0 ) {
+
+					// check the vertices inputs
+					input = this.vertices.input.COLOR;
+
+					if ( input ) {
+
+						source = sources[ input.source ];
+						numParams = source.accessor.params.length;
+
+						for ( var ndx = 0, len = vs.length; ndx < len; ndx++ ) {
+
+							idx32 = vs[ ndx ] * numParams;
+							cs.push( new THREE.Color().setRGB( source.data[ idx32 ], source.data[ idx32 + 1 ], source.data[ idx32 + 2 ] ) );
+
+						}
+
+					}
+
+				}
+
+				var face = null, faces = [], uv, uvArr;
+
+				if ( vcount === 3 ) {
+
+					faces.push( new THREE.Face3( vs[0], vs[1], vs[2], ns, cs.length ? cs : new THREE.Color() ) );
+
+				} else if ( vcount === 4 ) {
+
+					faces.push( new THREE.Face3( vs[0], vs[1], vs[3], [ns[0], ns[1], ns[3]], cs.length ? [cs[0], cs[1], cs[3]] : new THREE.Color() ) );
+
+					faces.push( new THREE.Face3( vs[1], vs[2], vs[3], [ns[1], ns[2], ns[3]], cs.length ? [cs[1], cs[2], cs[3]] : new THREE.Color() ) );
+
+				} else if ( vcount > 4 && options.subdivideFaces ) {
+
+					var clr = cs.length ? cs : new THREE.Color(),
+						vec1, vec2, vec3, v1, v2, norm;
+
+					// subdivide into multiple Face3s
+
+					for ( k = 1; k < vcount - 1; ) {
+
+						// FIXME: normals don't seem to be quite right
+
+						faces.push( new THREE.Face3( vs[0], vs[k], vs[k+1], [ ns[0], ns[k++], ns[k] ],  clr ) );
+
+					}
+
+				}
+
+				if ( faces.length ) {
+
+					for ( var ndx = 0, len = faces.length; ndx < len; ndx ++ ) {
+
+						face = faces[ndx];
+						face.daeMaterial = primitive.material;
+						geom.faces.push( face );
+
+						for ( k = 0; k < texture_sets.length; k++ ) {
+
+							uv = ts[ texture_sets[k] ];
+
+							if ( vcount > 4 ) {
+
+								// Grab the right UVs for the vertices in this face
+								uvArr = [ uv[0], uv[ndx+1], uv[ndx+2] ];
+
+							} else if ( vcount === 4 ) {
+
+								if ( ndx === 0 ) {
+
+									uvArr = [ uv[0], uv[1], uv[3] ];
+
+								} else {
+
+									uvArr = [ uv[1].clone(), uv[2], uv[3].clone() ];
+
+								}
+
+							} else {
+
+								uvArr = [ uv[0], uv[1], uv[2] ];
+
+							}
+
+							if ( geom.faceVertexUvs[k] === undefined ) {
+
+								geom.faceVertexUvs[k] = [];
+
+							}
+
+							geom.faceVertexUvs[k].push( uvArr );
+
+						}
+
+					}
+
+				} else {
+
+					console.log( 'dropped face with vcount ' + vcount + ' for geometry with id: ' + geom.id );
+
+				}
+
+				i += maxOffset * vcount;
+
+			}
+		}
+
+	};
+
+	function Polygons () {
+
+		this.material = "";
+		this.count = 0;
+		this.inputs = [];
+		this.vcount = null;
+		this.p = [];
+		this.geometry = new THREE.Geometry();
+
+	};
+
+	Polygons.prototype.setVertices = function ( vertices ) {
+
+		for ( var i = 0; i < this.inputs.length; i ++ ) {
+
+			if ( this.inputs[ i ].source == vertices.id ) {
+
+				this.inputs[ i ].source = vertices.input[ 'POSITION' ].source;
+
+			}
+
+		}
+
+	};
+
+	Polygons.prototype.parse = function ( element ) {
+
+		this.material = element.getAttribute( 'material' );
+		this.count = _attr_as_int( element, 'count', 0 );
+
+		for ( var i = 0; i < element.childNodes.length; i ++ ) {
+
+			var child = element.childNodes[ i ];
+
+			switch ( child.nodeName ) {
+
+				case 'input':
+
+					this.inputs.push( ( new Input() ).parse( element.childNodes[ i ] ) );
+					break;
+
+				case 'vcount':
+
+					this.vcount = _ints( child.textContent );
+					break;
+
+				case 'p':
+
+					this.p.push( _ints( child.textContent ) );
+					break;
+
+				case 'ph':
+
+					console.warn( 'polygon holes not yet supported!' );
+					break;
+
+				default:
+					break;
+
+			}
+
+		}
+
+		return this;
+
+	};
+
+	function Polylist () {
+
+		Polygons.call( this );
+
+		this.vcount = [];
+
+	};
+
+	Polylist.prototype = Object.create( Polygons.prototype );
+
+	function Triangles () {
+
+		Polygons.call( this );
+
+		this.vcount = 3;
+
+	};
+
+	Triangles.prototype = Object.create( Polygons.prototype );
+
+	function Accessor() {
+
+		this.source = "";
+		this.count = 0;
+		this.stride = 0;
+		this.params = [];
+
+	};
+
+	Accessor.prototype.parse = function ( element ) {
+
+		this.params = [];
+		this.source = element.getAttribute( 'source' );
+		this.count = _attr_as_int( element, 'count', 0 );
+		this.stride = _attr_as_int( element, 'stride', 0 );
+
+		for ( var i = 0; i < element.childNodes.length; i ++ ) {
+
+			var child = element.childNodes[ i ];
+
+			if ( child.nodeName == 'param' ) {
+
+				var param = {};
+				param[ 'name' ] = child.getAttribute( 'name' );
+				param[ 'type' ] = child.getAttribute( 'type' );
+				this.params.push( param );
+
+			}
+
+		}
+
+		return this;
+
+	};
+
+	function Vertices() {
+
+		this.input = {};
+
+	};
+
+	Vertices.prototype.parse = function ( element ) {
+
+		this.id = element.getAttribute('id');
+
+		for ( var i = 0; i < element.childNodes.length; i ++ ) {
+
+			if ( element.childNodes[i].nodeName == 'input' ) {
+
+				var input = ( new Input() ).parse( element.childNodes[ i ] );
+				this.input[ input.semantic ] = input;
+
+			}
+
+		}
+
+		return this;
+
+	};
+
+	function Input () {
+
+		this.semantic = "";
+		this.offset = 0;
+		this.source = "";
+		this.set = 0;
+
+	};
+
+	Input.prototype.parse = function ( element ) {
+
+		this.semantic = element.getAttribute('semantic');
+		this.source = element.getAttribute('source').replace(/^#/, '');
+		this.set = _attr_as_int(element, 'set', -1);
+		this.offset = _attr_as_int(element, 'offset', 0);
+
+		if ( this.semantic == 'TEXCOORD' && this.set < 0 ) {
+
+			this.set = 0;
+
+		}
+
+		return this;
+
+	};
+
+	function Source ( id ) {
+
+		this.id = id;
+		this.type = null;
+
+	};
+
+	Source.prototype.parse = function ( element ) {
+
+		this.id = element.getAttribute( 'id' );
+
+		for ( var i = 0; i < element.childNodes.length; i ++ ) {
+
+			var child = element.childNodes[i];
+
+			switch ( child.nodeName ) {
+
+				case 'bool_array':
+
+					this.data = _bools( child.textContent );
+					this.type = child.nodeName;
+					break;
+
+				case 'float_array':
+
+					this.data = _floats( child.textContent );
+					this.type = child.nodeName;
+					break;
+
+				case 'int_array':
+
+					this.data = _ints( child.textContent );
+					this.type = child.nodeName;
+					break;
+
+				case 'IDREF_array':
+				case 'Name_array':
+
+					this.data = _strings( child.textContent );
+					this.type = child.nodeName;
+					break;
+
+				case 'technique_common':
+
+					for ( var j = 0; j < child.childNodes.length; j ++ ) {
+
+						if ( child.childNodes[ j ].nodeName == 'accessor' ) {
+
+							this.accessor = ( new Accessor() ).parse( child.childNodes[ j ] );
+							break;
+
+						}
+					}
+					break;
+
+				default:
+					// console.log(child.nodeName);
+					break;
+
+			}
+
+		}
+
+		return this;
+
+	};
+
+	Source.prototype.read = function () {
+
+		var result = [];
+
+		//for (var i = 0; i < this.accessor.params.length; i++) {
+
+			var param = this.accessor.params[ 0 ];
+
+			//console.log(param.name + " " + param.type);
+
+			switch ( param.type ) {
+
+				case 'IDREF':
+				case 'Name': case 'name':
+				case 'float':
+
+					return this.data;
+
+				case 'float4x4':
+
+					for ( var j = 0; j < this.data.length; j += 16 ) {
+
+						var s = this.data.slice( j, j + 16 );
+						var m = getConvertedMat4( s );
+						result.push( m );
+					}
+
+					break;
+
+				default:
+
+					console.log( 'ColladaLoader: Source: Read dont know how to read ' + param.type + '.' );
+					break;
+
+			}
+
+		//}
+
+		return result;
+
+	};
+
+	function Material () {
+
+		this.id = "";
+		this.name = "";
+		this.instance_effect = null;
+
+	};
+
+	Material.prototype.parse = function ( element ) {
+
+		this.id = element.getAttribute( 'id' );
+		this.name = element.getAttribute( 'name' );
+
+		for ( var i = 0; i < element.childNodes.length; i ++ ) {
+
+			if ( element.childNodes[ i ].nodeName == 'instance_effect' ) {
+
+				this.instance_effect = ( new InstanceEffect() ).parse( element.childNodes[ i ] );
+				break;
+
+			}
+
+		}
+
+		return this;
+
+	};
+
+	function ColorOrTexture () {
+
+		this.color = new THREE.Color();
+		this.color.setRGB( Math.random(), Math.random(), Math.random() );
+		this.color.a = 1.0;
+
+		this.texture = null;
+		this.texcoord = null;
+		this.texOpts = null;
+
+	};
+
+	ColorOrTexture.prototype.isColor = function () {
+
+		return ( this.texture == null );
+
+	};
+
+	ColorOrTexture.prototype.isTexture = function () {
+
+		return ( this.texture != null );
+
+	};
+
+	ColorOrTexture.prototype.parse = function ( element ) {
+
+		if (element.nodeName == 'transparent')
+		{
+			this.opaque = element.getAttribute('opaque');
+		}
+
+		for ( var i = 0; i < element.childNodes.length; i ++ ) {
+
+			var child = element.childNodes[ i ];
+			if ( child.nodeType != 1 ) continue;
+
+			switch ( child.nodeName ) {
+
+				case 'color':
+
+					var rgba = _floats( child.textContent );
+					this.color = new THREE.Color();
+					this.color.setRGB( rgba[0], rgba[1], rgba[2] );
+					this.color.a = rgba[3];
+					break;
+
+				case 'texture':
+
+					this.texture = child.getAttribute('texture');
+					this.texcoord = child.getAttribute('texcoord');
+					// Defaults from:
+					// https://collada.org/mediawiki/index.php/Maya_texture_placement_MAYA_extension
+					this.texOpts = {
+						offsetU: 0,
+						offsetV: 0,
+						repeatU: 1,
+						repeatV: 1,
+						wrapU: 1,
+						wrapV: 1,
+					};
+					this.parseTexture( child );
+					break;
+
+				default:
+					break;
+
+			}
+
+		}
+
+		return this;
+
+	};
+
+	ColorOrTexture.prototype.parseTexture = function ( element ) {
+
+		if ( ! element.childNodes ) return this;
+
+		// This should be supported by Maya, 3dsMax, and MotionBuilder
+
+		if ( element.childNodes[1] && element.childNodes[1].nodeName === 'extra' ) {
+
+			element = element.childNodes[1];
+
+			if ( element.childNodes[1] && element.childNodes[1].nodeName === 'technique' ) {
+
+				element = element.childNodes[1];
+
+			}
+
+		}
+
+		for ( var i = 0; i < element.childNodes.length; i ++ ) {
+
+			var child = element.childNodes[ i ];
+
+			switch ( child.nodeName ) {
+
+				case 'offsetU':
+				case 'offsetV':
+				case 'repeatU':
+				case 'repeatV':
+
+					this.texOpts[ child.nodeName ] = parseFloat( child.textContent );
+					break;
+
+				case 'wrapU':
+				case 'wrapV':
+
+					this.texOpts[ child.nodeName ] = parseInt( child.textContent );
+					break;
+
+				default:
+					this.texOpts[ child.nodeName ] = child.textContent;
+					break;
+
+			}
+
+		}
+
+		return this;
+
+	};
+
+	function Shader ( type, effect ) {
+
+		this.type = type;
+		this.effect = effect;
+		this.material = null;
+
+	};
+
+	Shader.prototype.parse = function ( element ) {
+
+		for ( var i = 0; i < element.childNodes.length; i ++ ) {
+
+			var child = element.childNodes[ i ];
+			if ( child.nodeType != 1 ) continue;
+
+			switch ( child.nodeName ) {
+
+				case 'ambient':
+				case 'emission':
+				case 'diffuse':
+				case 'specular':
+				case 'transparent':
+
+					this[ child.nodeName ] = ( new ColorOrTexture() ).parse( child );
+					break;
+
+				case 'shininess':
+				case 'reflectivity':
+				case 'index_of_refraction':
+				case 'transparency':
+
+					var f = evaluateXPath( child, './/dae:float' );
+
+					if ( f.length > 0 )
+						this[ child.nodeName ] = parseFloat( f[ 0 ].textContent );
+
+					break;
+
+				default:
+					break;
+
+			}
+
+		}
+
+		this.create();
+		return this;
+
+	};
+
+	Shader.prototype.create = function() {
+
+		var props = {};
+
+		var transparent = false;
+		if (this['transparency'] !== undefined && this['transparent'] !== undefined)
+		{
+			// convert transparent color RBG to average value
+			var transparentColor = this['transparent'];
+			var transparencyLevel = (this.transparent.color.r +
+										this.transparent.color.g +
+										this.transparent.color.b)
+										/ 3 * this.transparency;
+
+			if (transparencyLevel > 0)
+			{
+				transparent = true;
+				props[ 'transparent' ] = true;
+				props[ 'opacity' ] = 1 - transparencyLevel;
+			}
+		}
+
+		for ( var prop in this ) {
+
+			switch ( prop ) {
+
+				case 'ambient':
+				case 'emission':
+				case 'diffuse':
+				case 'specular':
+
+					var cot = this[ prop ];
+
+					if ( cot instanceof ColorOrTexture ) {
+
+						if ( cot.isTexture() ) {
+
+							var samplerId = cot.texture;
+
+
+							var image;
+
+							var surfaceId = this.effect.sampler[samplerId];
+							if ( surfaceId !== undefined && surfaceId.source !== undefined ) {
+								var surface = this.effect.surface[surfaceId.source];
+								image = images[surface.init_from];
+							}
+							else {
+                image = images[samplerId];
+							}
+
+								if (image) {
+
+									var texture = THREE.ImageUtils.loadTexture(baseUrl + image.init_from);
+									texture.wrapS = cot.texOpts.wrapU ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping;
+									texture.wrapT = cot.texOpts.wrapV ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping;
+									texture.offset.x = cot.texOpts.offsetU;
+									texture.offset.y = cot.texOpts.offsetV;
+									texture.repeat.x = cot.texOpts.repeatU;
+									texture.repeat.y = cot.texOpts.repeatV;
+									props['map'] = texture;
+
+									// Texture with baked lighting?
+									if (prop === 'emission') props['emissive'] = 0xffffff;
+
+								}
+
+//							}
+
+						} else if ( prop === 'diffuse' || !transparent ) {
+
+							if ( prop === 'emission' ) {
+
+								props[ 'emissive' ] = cot.color.getHex();
+
+							} else {
+
+								props[ prop ] = cot.color.getHex();
+
+							}
+
+						}
+
+					}
+
+					break;
+
+				case 'shininess':
+
+					props[ prop ] = this[ prop ];
+					break;
+
+				case 'reflectivity':
+
+					props[ prop ] = this[ prop ];
+					if( props[ prop ] > 0.0 ) props['envMap'] = options.defaultEnvMap;
+					props['combine'] = THREE.MixOperation;	//mix regular shading with reflective component
+					break;
+
+				case 'index_of_refraction':
+
+					props[ 'refractionRatio' ] = this[ prop ]; //TODO: "index_of_refraction" becomes "refractionRatio" in shader, but I'm not sure if the two are actually comparable
+					if ( this[ prop ] !== 1.0 ) props['envMap'] = options.defaultEnvMap;
+					break;
+
+				case 'transparency':
+					// gets figured out up top
+					break;
+
+				default:
+					break;
+
+			}
+
+		}
+
+		props[ 'shading' ] = preferredShading;
+		props[ 'side' ] = this.effect.doubleSided ? THREE.DoubleSide : THREE.FrontSide;
+
+		switch ( this.type ) {
+
+			case 'constant':
+
+				if (props.emissive != undefined) props.color = props.emissive;
+				this.material = new THREE.MeshBasicMaterial( props );
+				break;
+
+			case 'phong':
+			case 'blinn':
+
+				if (props.diffuse != undefined) props.color = props.diffuse;
+				this.material = new THREE.MeshPhongMaterial( props );
+				break;
+
+			case 'lambert':
+			default:
+
+				if (props.diffuse != undefined) props.color = props.diffuse;
+				this.material = new THREE.MeshLambertMaterial( props );
+				break;
+
+		}
+
+		return this.material;
+
+	};
+
+	function Surface ( effect ) {
+
+		this.effect = effect;
+		this.init_from = null;
+		this.format = null;
+
+	};
+
+	Surface.prototype.parse = function ( element ) {
+
+		for ( var i = 0; i < element.childNodes.length; i ++ ) {
+
+			var child = element.childNodes[ i ];
+			if ( child.nodeType != 1 ) continue;
+
+			switch ( child.nodeName ) {
+
+				case 'init_from':
+
+					this.init_from = child.textContent;
+					break;
+
+				case 'format':
+
+					this.format = child.textContent;
+					break;
+
+				default:
+
+					console.log( "unhandled Surface prop: " + child.nodeName );
+					break;
+
+			}
+
+		}
+
+		return this;
+
+	};
+
+	function Sampler2D ( effect ) {
+
+		this.effect = effect;
+		this.source = null;
+		this.wrap_s = null;
+		this.wrap_t = null;
+		this.minfilter = null;
+		this.magfilter = null;
+		this.mipfilter = null;
+
+	};
+
+	Sampler2D.prototype.parse = function ( element ) {
+
+		for ( var i = 0; i < element.childNodes.length; i ++ ) {
+
+			var child = element.childNodes[ i ];
+			if ( child.nodeType != 1 ) continue;
+
+			switch ( child.nodeName ) {
+
+				case 'source':
+
+					this.source = child.textContent;
+					break;
+
+				case 'minfilter':
+
+					this.minfilter = child.textContent;
+					break;
+
+				case 'magfilter':
+
+					this.magfilter = child.textContent;
+					break;
+
+				case 'mipfilter':
+
+					this.mipfilter = child.textContent;
+					break;
+
+				case 'wrap_s':
+
+					this.wrap_s = child.textContent;
+					break;
+
+				case 'wrap_t':
+
+					this.wrap_t = child.textContent;
+					break;
+
+				default:
+
+					console.log( "unhandled Sampler2D prop: " + child.nodeName );
+					break;
+
+			}
+
+		}
+
+		return this;
+
+	};
+
+	function Effect () {
+
+		this.id = "";
+		this.name = "";
+		this.shader = null;
+		this.surface = {};
+		this.sampler = {};
+
+	};
+
+	Effect.prototype.create = function () {
+
+		if ( this.shader == null ) {
+
+			return null;
+
+		}
+
+	};
+
+	Effect.prototype.parse = function ( element ) {
+
+		this.id = element.getAttribute( 'id' );
+		this.name = element.getAttribute( 'name' );
+
+		extractDoubleSided( this, element );
+
+		this.shader = null;
+
+		for ( var i = 0; i < element.childNodes.length; i ++ ) {
+
+			var child = element.childNodes[ i ];
+			if ( child.nodeType != 1 ) continue;
+
+			switch ( child.nodeName ) {
+
+				case 'profile_COMMON':
+
+					this.parseTechnique( this.parseProfileCOMMON( child ) );
+					break;
+
+				default:
+					break;
+
+			}
+
+		}
+
+		return this;
+
+	};
+
+	Effect.prototype.parseNewparam = function ( element ) {
+
+		var sid = element.getAttribute( 'sid' );
+
+		for ( var i = 0; i < element.childNodes.length; i ++ ) {
+
+			var child = element.childNodes[ i ];
+			if ( child.nodeType != 1 ) continue;
+
+			switch ( child.nodeName ) {
+
+				case 'surface':
+
+					this.surface[sid] = ( new Surface( this ) ).parse( child );
+					break;
+
+				case 'sampler2D':
+
+					this.sampler[sid] = ( new Sampler2D( this ) ).parse( child );
+					break;
+
+				case 'extra':
+
+					break;
+
+				default:
+
+					console.log( child.nodeName );
+					break;
+
+			}
+
+		}
+
+	};
+
+	Effect.prototype.parseProfileCOMMON = function ( element ) {
+
+		var technique;
+
+		for ( var i = 0; i < element.childNodes.length; i ++ ) {
+
+			var child = element.childNodes[ i ];
+
+			if ( child.nodeType != 1 ) continue;
+
+			switch ( child.nodeName ) {
+
+				case 'profile_COMMON':
+
+					this.parseProfileCOMMON( child );
+					break;
+
+				case 'technique':
+
+					technique = child;
+					break;
+
+				case 'newparam':
+
+					this.parseNewparam( child );
+					break;
+
+				case 'image':
+
+					var _image = ( new _Image() ).parse( child );
+					images[ _image.id ] = _image;
+					break;
+
+				case 'extra':
+					break;
+
+				default:
+
+					console.log( child.nodeName );
+					break;
+
+			}
+
+		}
+
+		return technique;
+
+	};
+
+	Effect.prototype.parseTechnique= function ( element ) {
+
+		for ( var i = 0; i < element.childNodes.length; i ++ ) {
+
+			var child = element.childNodes[i];
+			if ( child.nodeType != 1 ) continue;
+
+			switch ( child.nodeName ) {
+
+				case 'constant':
+				case 'lambert':
+				case 'blinn':
+				case 'phong':
+
+					this.shader = ( new Shader( child.nodeName, this ) ).parse( child );
+					break;
+
+				default:
+					break;
+
+			}
+
+		}
+
+	};
+
+	function InstanceEffect () {
+
+		this.url = "";
+
+	};
+
+	InstanceEffect.prototype.parse = function ( element ) {
+
+		this.url = element.getAttribute( 'url' ).replace( /^#/, '' );
+		return this;
+
+	};
+
+	function Animation() {
+
+		this.id = "";
+		this.name = "";
+		this.source = {};
+		this.sampler = [];
+		this.channel = [];
+
+	};
+
+	Animation.prototype.parse = function ( element ) {
+
+		this.id = element.getAttribute( 'id' );
+		this.name = element.getAttribute( 'name' );
+		this.source = {};
+
+		for ( var i = 0; i < element.childNodes.length; i ++ ) {
+
+			var child = element.childNodes[ i ];
+
+			if ( child.nodeType != 1 ) continue;
+
+			switch ( child.nodeName ) {
+
+				case 'animation':
+
+					var anim = ( new Animation() ).parse( child );
+
+					for ( var src in anim.source ) {
+
+						this.source[ src ] = anim.source[ src ];
+
+					}
+
+					for ( var j = 0; j < anim.channel.length; j ++ ) {
+
+						this.channel.push( anim.channel[ j ] );
+						this.sampler.push( anim.sampler[ j ] );
+
+					}
+
+					break;
+
+				case 'source':
+
+					var src = ( new Source() ).parse( child );
+					this.source[ src.id ] = src;
+					break;
+
+				case 'sampler':
+
+					this.sampler.push( ( new Sampler( this ) ).parse( child ) );
+					break;
+
+				case 'channel':
+
+					this.channel.push( ( new Channel( this ) ).parse( child ) );
+					break;
+
+				default:
+					break;
+
+			}
+
+		}
+
+		return this;
+
+	};
+
+	function Channel( animation ) {
+
+		this.animation = animation;
+		this.source = "";
+		this.target = "";
+		this.fullSid = null;
+		this.sid = null;
+		this.dotSyntax = null;
+		this.arrSyntax = null;
+		this.arrIndices = null;
+		this.member = null;
+
+	};
+
+	Channel.prototype.parse = function ( element ) {
+
+		this.source = element.getAttribute( 'source' ).replace( /^#/, '' );
+		this.target = element.getAttribute( 'target' );
+
+		var parts = this.target.split( '/' );
+
+		var id = parts.shift();
+		var sid = parts.shift();
+
+		var dotSyntax = ( sid.indexOf(".") >= 0 );
+		var arrSyntax = ( sid.indexOf("(") >= 0 );
+
+		if ( dotSyntax ) {
+
+			parts = sid.split(".");
+			this.sid = parts.shift();
+			this.member = parts.shift();
+
+		} else if ( arrSyntax ) {
+
+			var arrIndices = sid.split("(");
+			this.sid = arrIndices.shift();
+
+			for (var j = 0; j < arrIndices.length; j ++ ) {
+
+				arrIndices[j] = parseInt( arrIndices[j].replace(/\)/, '') );
+
+			}
+
+			this.arrIndices = arrIndices;
+
+		} else {
+
+			this.sid = sid;
+
+		}
+
+		this.fullSid = sid;
+		this.dotSyntax = dotSyntax;
+		this.arrSyntax = arrSyntax;
+
+		return this;
+
+	};
+
+	function Sampler ( animation ) {
+
+		this.id = "";
+		this.animation = animation;
+		this.inputs = [];
+		this.input = null;
+		this.output = null;
+		this.strideOut = null;
+		this.interpolation = null;
+		this.startTime = null;
+		this.endTime = null;
+		this.duration = 0;
+
+	};
+
+	Sampler.prototype.parse = function ( element ) {
+
+		this.id = element.getAttribute( 'id' );
+		this.inputs = [];
+
+		for ( var i = 0; i < element.childNodes.length; i ++ ) {
+
+			var child = element.childNodes[ i ];
+			if ( child.nodeType != 1 ) continue;
+
+			switch ( child.nodeName ) {
+
+				case 'input':
+
+					this.inputs.push( (new Input()).parse( child ) );
+					break;
+
+				default:
+					break;
+
+			}
+
+		}
+
+		return this;
+
+	};
+
+	Sampler.prototype.create = function () {
+
+		for ( var i = 0; i < this.inputs.length; i ++ ) {
+
+			var input = this.inputs[ i ];
+			var source = this.animation.source[ input.source ];
+
+			switch ( input.semantic ) {
+
+				case 'INPUT':
+
+					this.input = source.read();
+					break;
+
+				case 'OUTPUT':
+
+					this.output = source.read();
+					this.strideOut = source.accessor.stride;
+					break;
+
+				case 'INTERPOLATION':
+
+					this.interpolation = source.read();
+					break;
+
+				case 'IN_TANGENT':
+
+					break;
+
+				case 'OUT_TANGENT':
+
+					break;
+
+				default:
+
+					console.log(input.semantic);
+					break;
+
+			}
+
+		}
+
+		this.startTime = 0;
+		this.endTime = 0;
+		this.duration = 0;
+
+		if ( this.input.length ) {
+
+			this.startTime = 100000000;
+			this.endTime = -100000000;
+
+			for ( var i = 0; i < this.input.length; i ++ ) {
+
+				this.startTime = Math.min( this.startTime, this.input[ i ] );
+				this.endTime = Math.max( this.endTime, this.input[ i ] );
+
+			}
+
+			this.duration = this.endTime - this.startTime;
+
+		}
+
+	};
+
+	Sampler.prototype.getData = function ( type, ndx ) {
+
+		var data;
+
+		if ( type === 'matrix' && this.strideOut === 16 ) {
+
+			data = this.output[ ndx ];
+
+		} else if ( this.strideOut > 1 ) {
+
+			data = [];
+			ndx *= this.strideOut;
+
+			for ( var i = 0; i < this.strideOut; ++i ) {
+
+				data[ i ] = this.output[ ndx + i ];
+
+			}
+
+			if ( this.strideOut === 3 ) {
+
+				switch ( type ) {
+
+					case 'rotate':
+					case 'translate':
+
+						fixCoords( data, -1 );
+						break;
+
+					case 'scale':
+
+						fixCoords( data, 1 );
+						break;
+
+				}
+
+			} else if ( this.strideOut === 4 && type === 'matrix' ) {
+
+				fixCoords( data, -1 );
+
+			}
+
+		} else {
+
+			data = this.output[ ndx ];
+
+		}
+
+		return data;
+
+	};
+
+	function Key ( time ) {
+
+		this.targets = [];
+		this.time = time;
+
+	};
+
+	Key.prototype.addTarget = function ( fullSid, transform, member, data ) {
+
+		this.targets.push( {
+			sid: fullSid,
+			member: member,
+			transform: transform,
+			data: data
+		} );
+
+	};
+
+	Key.prototype.apply = function ( opt_sid ) {
+
+		for ( var i = 0; i < this.targets.length; ++i ) {
+
+			var target = this.targets[ i ];
+
+			if ( !opt_sid || target.sid === opt_sid ) {
+
+				target.transform.update( target.data, target.member );
+
+			}
+
+		}
+
+	};
+
+	Key.prototype.getTarget = function ( fullSid ) {
+
+		for ( var i = 0; i < this.targets.length; ++i ) {
+
+			if ( this.targets[ i ].sid === fullSid ) {
+
+				return this.targets[ i ];
+
+			}
+
+		}
+
+		return null;
+
+	};
+
+	Key.prototype.hasTarget = function ( fullSid ) {
+
+		for ( var i = 0; i < this.targets.length; ++i ) {
+
+			if ( this.targets[ i ].sid === fullSid ) {
+
+				return true;
+
+			}
+
+		}
+
+		return false;
+
+	};
+
+	// TODO: Currently only doing linear interpolation. Should support full COLLADA spec.
+	Key.prototype.interpolate = function ( nextKey, time ) {
+
+		for ( var i = 0; i < this.targets.length; ++i ) {
+
+			var target = this.targets[ i ],
+				nextTarget = nextKey.getTarget( target.sid ),
+				data;
+
+			if ( target.transform.type !== 'matrix' && nextTarget ) {
+
+				var scale = ( time - this.time ) / ( nextKey.time - this.time ),
+					nextData = nextTarget.data,
+					prevData = target.data;
+
+				// check scale error
+
+				if ( scale < 0 || scale > 1 ) {
+
+					console.log( "Key.interpolate: Warning! Scale out of bounds:" + scale );
+					scale = scale < 0 ? 0 : 1;
+
+				}
+
+				if ( prevData.length ) {
+
+					data = [];
+
+					for ( var j = 0; j < prevData.length; ++j ) {
+
+						data[ j ] = prevData[ j ] + ( nextData[ j ] - prevData[ j ] ) * scale;
+
+					}
+
+				} else {
+
+					data = prevData + ( nextData - prevData ) * scale;
+
+				}
+
+			} else {
+
+				data = target.data;
+
+			}
+
+			target.transform.update( data, target.member );
+
+		}
+
+	};
+
+	// Camera
+	function Camera() {
+
+		this.id = "";
+		this.name = "";
+		this.technique = "";
+
+	};
+
+	Camera.prototype.parse = function ( element ) {
+
+		this.id = element.getAttribute( 'id' );
+		this.name = element.getAttribute( 'name' );
+
+		for ( var i = 0; i < element.childNodes.length; i ++ ) {
+
+			var child = element.childNodes[ i ];
+			if ( child.nodeType != 1 ) continue;
+
+			switch ( child.nodeName ) {
+
+				case 'optics':
+
+					this.parseOptics( child );
+					break;
+
+				default:
+					break;
+
+			}
+
+		}
+
+		return this;
+
+	};
+
+	Camera.prototype.parseOptics = function ( element ) {
+
+		for ( var i = 0; i < element.childNodes.length; i ++ ) {
+
+			if ( element.childNodes[ i ].nodeName == 'technique_common' ) {
+
+				var technique = element.childNodes[ i ];
+
+				for ( var j = 0; j < technique.childNodes.length; j ++ ) {
+
+					this.technique = technique.childNodes[ j ].nodeName;
+
+					if ( this.technique == 'perspective' ) {
+
+						var perspective = technique.childNodes[ j ];
+
+						for ( var k = 0; k < perspective.childNodes.length; k ++ ) {
+
+							var param = perspective.childNodes[ k ];
+
+							switch ( param.nodeName ) {
+
+								case 'yfov':
+									this.yfov = param.textContent;
+									break;
+								case 'xfov':
+									this.xfov = param.textContent;
+									break;
+								case 'znear':
+									this.znear = param.textContent;
+									break;
+								case 'zfar':
+									this.zfar = param.textContent;
+									break;
+								case 'aspect_ratio':
+									this.aspect_ratio = param.textContent;
+									break;
+
+							}
+
+						}
+
+					} else if ( this.technique == 'orthographic' ) {
+
+						var orthographic = technique.childNodes[ j ];
+
+						for ( var k = 0; k < orthographic.childNodes.length; k ++ ) {
+
+							var param = orthographic.childNodes[ k ];
+
+							switch ( param.nodeName ) {
+
+								case 'xmag':
+									this.xmag = param.textContent;
+									break;
+								case 'ymag':
+									this.ymag = param.textContent;
+									break;
+								case 'znear':
+									this.znear = param.textContent;
+									break;
+								case 'zfar':
+									this.zfar = param.textContent;
+									break;
+								case 'aspect_ratio':
+									this.aspect_ratio = param.textContent;
+									break;
+
+							}
+
+						}
+
+					}
+
+				}
+
+			}
+
+		}
+
+		return this;
+
+	};
+
+	function InstanceCamera() {
+
+		this.url = "";
+
+	};
+
+	InstanceCamera.prototype.parse = function ( element ) {
+
+		this.url = element.getAttribute('url').replace(/^#/, '');
+
+		return this;
+
+	};
+
+	// Light
+
+	function Light() {
+
+		this.id = "";
+		this.name = "";
+		this.technique = "";
+
+	};
+
+	Light.prototype.parse = function ( element ) {
+
+		this.id = element.getAttribute( 'id' );
+		this.name = element.getAttribute( 'name' );
+
+		for ( var i = 0; i < element.childNodes.length; i ++ ) {
+
+			var child = element.childNodes[ i ];
+			if ( child.nodeType != 1 ) continue;
+
+			switch ( child.nodeName ) {
+
+				case 'technique_common':
+
+					this.parseCommon( child );
+					break;
+
+				case 'technique':
+
+					this.parseTechnique( child );
+					break;
+
+				default:
+					break;
+
+			}
+
+		}
+
+		return this;
+
+	};
+
+	Light.prototype.parseCommon = function ( element ) {
+
+		for ( var i = 0; i < element.childNodes.length; i ++ ) {
+
+			switch ( element.childNodes[ i ].nodeName ) {
+
+				case 'directional':
+				case 'point':
+				case 'spot':
+				case 'ambient':
+
+					this.technique = element.childNodes[ i ].nodeName;
+
+					var light = element.childNodes[ i ];
+
+					for ( var j = 0; j < light.childNodes.length; j ++ ) {
+
+						var child = light.childNodes[j];
+
+						switch ( child.nodeName ) {
+
+							case 'color':
+
+								var rgba = _floats( child.textContent );
+								this.color = new THREE.Color(0);
+								this.color.setRGB( rgba[0], rgba[1], rgba[2] );
+								this.color.a = rgba[3];
+								break;
+
+							case 'falloff_angle':
+
+								this.falloff_angle = parseFloat( child.textContent );
+								break;
+						}
+
+					}
+
+			}
+
+		}
+
+		return this;
+
+	};
+
+	Light.prototype.parseTechnique = function ( element ) {
+
+		this.profile = element.getAttribute( 'profile' );
+
+		for ( var i = 0; i < element.childNodes.length; i ++ ) {
+
+			var child = element.childNodes[ i ];
+
+			switch ( child.nodeName ) {
+
+				case 'intensity':
+
+					this.intensity = parseFloat(child.textContent);
+					break;
+
+			}
+
+		}
+
+		return this;
+
+	};
+
+	function InstanceLight() {
+
+		this.url = "";
+
+	};
+
+	InstanceLight.prototype.parse = function ( element ) {
+
+		this.url = element.getAttribute('url').replace(/^#/, '');
+
+		return this;
+
+	};
+
+	function _source( element ) {
+
+		var id = element.getAttribute( 'id' );
+
+		if ( sources[ id ] != undefined ) {
+
+			return sources[ id ];
+
+		}
+
+		sources[ id ] = ( new Source(id )).parse( element );
+		return sources[ id ];
+
+	};
+
+	function _nsResolver( nsPrefix ) {
+
+		if ( nsPrefix == "dae" ) {
+
+			return "http://www.collada.org/2005/11/COLLADASchema";
+
+		}
+
+		return null;
+
+	};
+
+	function _bools( str ) {
+
+		var raw = _strings( str );
+		var data = [];
+
+		for ( var i = 0, l = raw.length; i < l; i ++ ) {
+
+			data.push( (raw[i] == 'true' || raw[i] == '1') ? true : false );
+
+		}
+
+		return data;
+
+	};
+
+	function _floats( str ) {
+
+		var raw = _strings(str);
+		var data = [];
+
+		for ( var i = 0, l = raw.length; i < l; i ++ ) {
+
+			data.push( parseFloat( raw[ i ] ) );
+
+		}
+
+		return data;
+
+	};
+
+	function _ints( str ) {
+
+		var raw = _strings( str );
+		var data = [];
+
+		for ( var i = 0, l = raw.length; i < l; i ++ ) {
+
+			data.push( parseInt( raw[ i ], 10 ) );
+
+		}
+
+		return data;
+
+	};
+
+	function _strings( str ) {
+
+		return ( str.length > 0 ) ? _trimString( str ).split( /\s+/ ) : [];
+
+	};
+
+	function _trimString( str ) {
+
+		return str.replace( /^\s+/, "" ).replace( /\s+$/, "" );
+
+	};
+
+	function _attr_as_float( element, name, defaultValue ) {
+
+		if ( element.hasAttribute( name ) ) {
+
+			return parseFloat( element.getAttribute( name ) );
+
+		} else {
+
+			return defaultValue;
+
+		}
+
+	};
+
+	function _attr_as_int( element, name, defaultValue ) {
+
+		if ( element.hasAttribute( name ) ) {
+
+			return parseInt( element.getAttribute( name ), 10) ;
+
+		} else {
+
+			return defaultValue;
+
+		}
+
+	};
+
+	function _attr_as_string( element, name, defaultValue ) {
+
+		if ( element.hasAttribute( name ) ) {
+
+			return element.getAttribute( name );
+
+		} else {
+
+			return defaultValue;
+
+		}
+
+	};
+
+	function _format_float( f, num ) {
+
+		if ( f === undefined ) {
+
+			var s = '0.';
+
+			while ( s.length < num + 2 ) {
+
+				s += '0';
+
+			}
+
+			return s;
+
+		}
+
+		num = num || 2;
+
+		var parts = f.toString().split( '.' );
+		parts[ 1 ] = parts.length > 1 ? parts[ 1 ].substr( 0, num ) : "0";
+
+		while( parts[ 1 ].length < num ) {
+
+			parts[ 1 ] += '0';
+
+		}
+
+		return parts.join( '.' );
+
+	};
+
+	function evaluateXPath( node, query ) {
+
+		var instances = COLLADA.evaluate( query, node, _nsResolver, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null );
+
+		var inst = instances.iterateNext();
+		var result = [];
+
+		while ( inst ) {
+
+			result.push( inst );
+			inst = instances.iterateNext();
+
+		}
+
+		return result;
+
+	};
+
+	function extractDoubleSided( obj, element ) {
+
+		obj.doubleSided = false;
+
+		var node = COLLADA.evaluate( './/dae:extra//dae:double_sided', element, _nsResolver, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null );
+
+		if ( node ) {
+
+			node = node.iterateNext();
+
+			if ( node && parseInt( node.textContent, 10 ) === 1 ) {
+
+				obj.doubleSided = true;
+
+			}
+
+		}
+
+	};
+
+	// Up axis conversion
+
+	function setUpConversion() {
+
+		if ( !options.convertUpAxis || colladaUp === options.upAxis ) {
+
+			upConversion = null;
+
+		} else {
+
+			switch ( colladaUp ) {
+
+				case 'X':
+
+					upConversion = options.upAxis === 'Y' ? 'XtoY' : 'XtoZ';
+					break;
+
+				case 'Y':
+
+					upConversion = options.upAxis === 'X' ? 'YtoX' : 'YtoZ';
+					break;
+
+				case 'Z':
+
+					upConversion = options.upAxis === 'X' ? 'ZtoX' : 'ZtoY';
+					break;
+
+			}
+
+		}
+
+	};
+
+	function fixCoords( data, sign ) {
+
+		if ( !options.convertUpAxis || colladaUp === options.upAxis ) {
+
+			return;
+
+		}
+
+		switch ( upConversion ) {
+
+			case 'XtoY':
+
+				var tmp = data[ 0 ];
+				data[ 0 ] = sign * data[ 1 ];
+				data[ 1 ] = tmp;
+				break;
+
+			case 'XtoZ':
+
+				var tmp = data[ 2 ];
+				data[ 2 ] = data[ 1 ];
+				data[ 1 ] = data[ 0 ];
+				data[ 0 ] = tmp;
+				break;
+
+			case 'YtoX':
+
+				var tmp = data[ 0 ];
+				data[ 0 ] = data[ 1 ];
+				data[ 1 ] = sign * tmp;
+				break;
+
+			case 'YtoZ':
+
+				var tmp = data[ 1 ];
+				data[ 1 ] = sign * data[ 2 ];
+				data[ 2 ] = tmp;
+				break;
+
+			case 'ZtoX':
+
+				var tmp = data[ 0 ];
+				data[ 0 ] = data[ 1 ];
+				data[ 1 ] = data[ 2 ];
+				data[ 2 ] = tmp;
+				break;
+
+			case 'ZtoY':
+
+				var tmp = data[ 1 ];
+				data[ 1 ] = data[ 2 ];
+				data[ 2 ] = sign * tmp;
+				break;
+
+		}
+
+	};
+
+	function getConvertedVec3( data, offset ) {
+
+		var arr = [ data[ offset ], data[ offset + 1 ], data[ offset + 2 ] ];
+		fixCoords( arr, -1 );
+		return new THREE.Vector3( arr[ 0 ], arr[ 1 ], arr[ 2 ] );
+
+	};
+
+	function getConvertedMat4( data ) {
+
+		if ( options.convertUpAxis ) {
+
+			// First fix rotation and scale
+
+			// Columns first
+			var arr = [ data[ 0 ], data[ 4 ], data[ 8 ] ];
+			fixCoords( arr, -1 );
+			data[ 0 ] = arr[ 0 ];
+			data[ 4 ] = arr[ 1 ];
+			data[ 8 ] = arr[ 2 ];
+			arr = [ data[ 1 ], data[ 5 ], data[ 9 ] ];
+			fixCoords( arr, -1 );
+			data[ 1 ] = arr[ 0 ];
+			data[ 5 ] = arr[ 1 ];
+			data[ 9 ] = arr[ 2 ];
+			arr = [ data[ 2 ], data[ 6 ], data[ 10 ] ];
+			fixCoords( arr, -1 );
+			data[ 2 ] = arr[ 0 ];
+			data[ 6 ] = arr[ 1 ];
+			data[ 10 ] = arr[ 2 ];
+			// Rows second
+			arr = [ data[ 0 ], data[ 1 ], data[ 2 ] ];
+			fixCoords( arr, -1 );
+			data[ 0 ] = arr[ 0 ];
+			data[ 1 ] = arr[ 1 ];
+			data[ 2 ] = arr[ 2 ];
+			arr = [ data[ 4 ], data[ 5 ], data[ 6 ] ];
+			fixCoords( arr, -1 );
+			data[ 4 ] = arr[ 0 ];
+			data[ 5 ] = arr[ 1 ];
+			data[ 6 ] = arr[ 2 ];
+			arr = [ data[ 8 ], data[ 9 ], data[ 10 ] ];
+			fixCoords( arr, -1 );
+			data[ 8 ] = arr[ 0 ];
+			data[ 9 ] = arr[ 1 ];
+			data[ 10 ] = arr[ 2 ];
+
+			// Now fix translation
+			arr = [ data[ 3 ], data[ 7 ], data[ 11 ] ];
+			fixCoords( arr, -1 );
+			data[ 3 ] = arr[ 0 ];
+			data[ 7 ] = arr[ 1 ];
+			data[ 11 ] = arr[ 2 ];
+
+		}
+
+		return new THREE.Matrix4(
+			data[0], data[1], data[2], data[3],
+			data[4], data[5], data[6], data[7],
+			data[8], data[9], data[10], data[11],
+			data[12], data[13], data[14], data[15]
+			);
+
+	};
+
+	function getConvertedIndex( index ) {
+
+		if ( index > -1 && index < 3 ) {
+
+			var members = ['X', 'Y', 'Z'],
+				indices = { X: 0, Y: 1, Z: 2 };
+
+			index = getConvertedMember( members[ index ] );
+			index = indices[ index ];
+
+		}
+
+		return index;
+
+	};
+
+	function getConvertedMember( member ) {
+
+		if ( options.convertUpAxis ) {
+
+			switch ( member ) {
+
+				case 'X':
+
+					switch ( upConversion ) {
+
+						case 'XtoY':
+						case 'XtoZ':
+						case 'YtoX':
+
+							member = 'Y';
+							break;
+
+						case 'ZtoX':
+
+							member = 'Z';
+							break;
+
+					}
+
+					break;
+
+				case 'Y':
+
+					switch ( upConversion ) {
+
+						case 'XtoY':
+						case 'YtoX':
+						case 'ZtoX':
+
+							member = 'X';
+							break;
+
+						case 'XtoZ':
+						case 'YtoZ':
+						case 'ZtoY':
+
+							member = 'Z';
+							break;
+
+					}
+
+					break;
+
+				case 'Z':
+
+					switch ( upConversion ) {
+
+						case 'XtoZ':
+
+							member = 'X';
+							break;
+
+						case 'YtoZ':
+						case 'ZtoX':
+						case 'ZtoY':
+
+							member = 'Y';
+							break;
+
+					}
+
+					break;
+
+			}
+
+		}
+
+		return member;
+
+	};
+
+	return {
+
+		load: load,
+		parse: parse,
+		setPreferredShading: setPreferredShading,
+		applySkin: applySkin,
+		geometries : geometries,
+		options: options
+
+	};
+
+};
diff --git a/gz3d/client/js/include/CopyShader.js b/gz3d/client/js/include/CopyShader.js
new file mode 100644
index 0000000000000000000000000000000000000000..7de9f3fbc11afa654c17c0d50d5c76be009fa13c
--- /dev/null
+++ b/gz3d/client/js/include/CopyShader.js
@@ -0,0 +1,46 @@
+/**
+ * @author alteredq / http://alteredqualia.com/
+ *
+ * Full-screen textured quad shader
+ */
+
+THREE.CopyShader = {
+
+	uniforms: {
+
+		"tDiffuse": { type: "t", value: null },
+		"opacity":  { type: "f", value: 1.0 }
+
+	},
+
+	vertexShader: [
+
+		"varying vec2 vUv;",
+
+		"void main() {",
+
+			"vUv = uv;",
+			"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+
+		"}"
+
+	].join("\n"),
+
+	fragmentShader: [
+
+		"uniform float opacity;",
+
+		"uniform sampler2D tDiffuse;",
+
+		"varying vec2 vUv;",
+
+		"void main() {",
+
+			"vec4 texel = texture2D( tDiffuse, vUv );",
+			"gl_FragColor = opacity * texel;",
+
+		"}"
+
+	].join("\n")
+
+};
diff --git a/gz3d/client/js/include/Detector.js b/gz3d/client/js/include/Detector.js
new file mode 100644
index 0000000000000000000000000000000000000000..4f34bf112e05074a650dec0d751ac4030138ea9b
--- /dev/null
+++ b/gz3d/client/js/include/Detector.js
@@ -0,0 +1,59 @@
+/**
+ * @author alteredq / http://alteredqualia.com/
+ * @author mr.doob / http://mrdoob.com/
+ */
+
+var Detector = {
+
+	canvas: !! window.CanvasRenderingContext2D,
+	webgl: ( function () { try { return !! window.WebGLRenderingContext && !! document.createElement( 'canvas' ).getContext( 'experimental-webgl' ); } catch( e ) { return false; } } )(),
+	workers: !! window.Worker,
+	fileapi: window.File && window.FileReader && window.FileList && window.Blob,
+
+	getWebGLErrorMessage: function () {
+
+		var element = document.createElement( 'div' );
+		element.id = 'webgl-error-message';
+		element.style.fontFamily = 'monospace';
+		element.style.fontSize = '13px';
+		element.style.fontWeight = 'normal';
+		element.style.textAlign = 'center';
+		element.style.background = '#fff';
+		element.style.color = '#000';
+		element.style.padding = '1.5em';
+		element.style.width = '400px';
+		element.style.margin = '5em auto 0';
+
+		if ( ! this.webgl ) {
+
+			element.innerHTML = window.WebGLRenderingContext ? [
+				'Your graphics card does not seem to support <a href="http://khronos.org/webgl/wiki/Getting_a_WebGL_Implementation" style="color:#000">WebGL</a>.<br />',
+				'Find out how to get it <a href="http://get.webgl.org/" style="color:#000">here</a>.'
+			].join( '\n' ) : [
+				'Your browser does not seem to support <a href="http://khronos.org/webgl/wiki/Getting_a_WebGL_Implementation" style="color:#000">WebGL</a>.<br/>',
+				'Find out how to get it <a href="http://get.webgl.org/" style="color:#000">here</a>.'
+			].join( '\n' );
+
+		}
+
+		return element;
+
+	},
+
+	addGetWebGLMessage: function ( parameters ) {
+
+		var parent, id, element;
+
+		parameters = parameters || {};
+
+		parent = parameters.parent !== undefined ? parameters.parent : document.body;
+		id = parameters.id !== undefined ? parameters.id : 'oldie';
+
+		element = Detector.getWebGLErrorMessage();
+		element.id = id;
+
+		parent.appendChild( element );
+
+	}
+
+};
diff --git a/gz3d/client/js/include/EffectComposer.js b/gz3d/client/js/include/EffectComposer.js
new file mode 100644
index 0000000000000000000000000000000000000000..ef540e856a03b3fa77a43cdd0306f37df7bdbb0d
--- /dev/null
+++ b/gz3d/client/js/include/EffectComposer.js
@@ -0,0 +1,144 @@
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.EffectComposer = function ( renderer, renderTarget ) {
+
+	this.renderer = renderer;
+
+	if ( renderTarget === undefined ) {
+
+		var width = window.innerWidth || 1;
+		var height = window.innerHeight || 1;
+		var parameters = { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBFormat, stencilBuffer: false };
+
+		renderTarget = new THREE.WebGLRenderTarget( width, height, parameters );
+
+	}
+
+	this.renderTarget1 = renderTarget;
+	this.renderTarget2 = renderTarget.clone();
+
+	this.writeBuffer = this.renderTarget1;
+	this.readBuffer = this.renderTarget2;
+
+	this.passes = [];
+
+	if ( THREE.CopyShader === undefined )
+		console.error( "THREE.EffectComposer relies on THREE.CopyShader" );
+
+	this.copyPass = new THREE.ShaderPass( THREE.CopyShader );
+
+};
+
+THREE.EffectComposer.prototype = {
+
+	swapBuffers: function() {
+
+		var tmp = this.readBuffer;
+		this.readBuffer = this.writeBuffer;
+		this.writeBuffer = tmp;
+
+	},
+
+	addPass: function ( pass ) {
+
+		this.passes.push( pass );
+
+	},
+
+	insertPass: function ( pass, index ) {
+
+		this.passes.splice( index, 0, pass );
+
+	},
+
+	render: function ( delta ) {
+
+		this.writeBuffer = this.renderTarget1;
+		this.readBuffer = this.renderTarget2;
+
+		var maskActive = false;
+
+		var pass, i, il = this.passes.length;
+
+		for ( i = 0; i < il; i ++ ) {
+
+			pass = this.passes[ i ];
+
+			if ( !pass.enabled ) continue;
+
+			pass.render( this.renderer, this.writeBuffer, this.readBuffer, delta, maskActive );
+
+			if ( pass.needsSwap ) {
+
+				if ( maskActive ) {
+
+					var context = this.renderer.context;
+
+					context.stencilFunc( context.NOTEQUAL, 1, 0xffffffff );
+
+					this.copyPass.render( this.renderer, this.writeBuffer, this.readBuffer, delta );
+
+					context.stencilFunc( context.EQUAL, 1, 0xffffffff );
+
+				}
+
+				this.swapBuffers();
+
+			}
+
+			if ( pass instanceof THREE.MaskPass ) {
+
+				maskActive = true;
+
+			} else if ( pass instanceof THREE.ClearMaskPass ) {
+
+				maskActive = false;
+
+			}
+
+		}
+
+	},
+
+	reset: function ( renderTarget ) {
+
+		if ( renderTarget === undefined ) {
+
+			renderTarget = this.renderTarget1.clone();
+
+			renderTarget.width = window.innerWidth;
+			renderTarget.height = window.innerHeight;
+
+		}
+
+		this.renderTarget1 = renderTarget;
+		this.renderTarget2 = renderTarget.clone();
+
+		this.writeBuffer = this.renderTarget1;
+		this.readBuffer = this.renderTarget2;
+
+	},
+
+	setSize: function ( width, height ) {
+
+		var renderTarget = this.renderTarget1.clone();
+
+		renderTarget.width = width;
+		renderTarget.height = height;
+
+		this.reset( renderTarget );
+
+	}
+
+};
+
+// shared ortho camera
+
+THREE.EffectComposer.camera = new THREE.OrthographicCamera( -1, 1, 1, -1, 0, 1 );
+
+THREE.EffectComposer.quad = new THREE.Mesh( new THREE.PlaneGeometry( 2, 2 ), null );
+
+THREE.EffectComposer.scene = new THREE.Scene();
+THREE.EffectComposer.scene.add( THREE.EffectComposer.quad );
diff --git a/gz3d/client/js/include/MaskPass.js b/gz3d/client/js/include/MaskPass.js
new file mode 100644
index 0000000000000000000000000000000000000000..23238b086db7883a45bfc6abaac63e9170d490fd
--- /dev/null
+++ b/gz3d/client/js/include/MaskPass.js
@@ -0,0 +1,86 @@
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.MaskPass = function ( scene, camera ) {
+
+	this.scene = scene;
+	this.camera = camera;
+
+	this.enabled = true;
+	this.clear = true;
+	this.needsSwap = false;
+
+	this.inverse = false;
+
+};
+
+THREE.MaskPass.prototype = {
+
+	render: function ( renderer, writeBuffer, readBuffer, delta ) {
+
+		var context = renderer.context;
+
+		// don't update color or depth
+
+		context.colorMask( false, false, false, false );
+		context.depthMask( false );
+
+		// set up stencil
+
+		var writeValue, clearValue;
+
+		if ( this.inverse ) {
+
+			writeValue = 0;
+			clearValue = 1;
+
+		} else {
+
+			writeValue = 1;
+			clearValue = 0;
+
+		}
+
+		context.enable( context.STENCIL_TEST );
+		context.stencilOp( context.REPLACE, context.REPLACE, context.REPLACE );
+		context.stencilFunc( context.ALWAYS, writeValue, 0xffffffff );
+		context.clearStencil( clearValue );
+
+		// draw into the stencil buffer
+
+		renderer.render( this.scene, this.camera, readBuffer, this.clear );
+		renderer.render( this.scene, this.camera, writeBuffer, this.clear );
+
+		// re-enable update of color and depth
+
+		context.colorMask( true, true, true, true );
+		context.depthMask( true );
+
+		// only render where stencil is set to 1
+
+		context.stencilFunc( context.EQUAL, 1, 0xffffffff );  // draw if == 1
+		context.stencilOp( context.KEEP, context.KEEP, context.KEEP );
+
+	}
+
+};
+
+
+THREE.ClearMaskPass = function () {
+
+	this.enabled = true;
+
+};
+
+THREE.ClearMaskPass.prototype = {
+
+	render: function ( renderer, writeBuffer, readBuffer, delta ) {
+
+		var context = renderer.context;
+
+		context.disable( context.STENCIL_TEST );
+
+	}
+
+};
diff --git a/gz3d/client/js/include/OrbitControls.js b/gz3d/client/js/include/OrbitControls.js
new file mode 100644
index 0000000000000000000000000000000000000000..eb740cc1cd4f34535a26fc6423c18e9cd31a7814
--- /dev/null
+++ b/gz3d/client/js/include/OrbitControls.js
@@ -0,0 +1,808 @@
+/**
+ * @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
+ */
+
+/**
+ * Modified by Ian Chen ichen@osrfoundation.org
+ */
+
+/*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 - middle mouse / touch: one finger move
+//    Zoom - right mouse, or mousewheel / touch: two finger spread or squish
+//    Pan - left mouse, or arrow keys / touch: two finger 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();
+  this.targetIndicator = new THREE.Mesh(new THREE.SphereGeometry(1, 20, 20),
+      new THREE.MeshPhongMaterial({emissive: 0x333300,
+      ambient: 0xffff00,
+      shading: THREE.SmoothShading}));
+  this.targetIndicator.visible = false;
+  this.showTargetIndicator = false;
+  // center is old, deprecated; use "target" instead
+  this.center = this.target;
+  this.object.lookAt(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
+
+  // 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};
+
+  ////////////
+  // 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 dollyStart = new THREE.Vector2();
+  var dollyEnd = new THREE.Vector2();
+  var dollyDelta = new THREE.Vector2();
+
+  var phiDelta = 0;
+  var thetaDelta = 0;
+  var scale = 1;
+  var pan = new THREE.Vector3();
+
+  var lastPosition = new THREE.Vector3();
+
+  var STATE = {NONE : -1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3,
+      TOUCH_DOLLY_PAN : 4};
+  var state = STATE.NONE;
+
+  var scrollTime = null;
+
+  // events
+
+  var changeEvent =
+  {
+    type: 'change'
+  };
+
+  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 panOffset = new THREE.Vector3();
+    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 panOffset = new THREE.Vector3();
+    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);
+  };
+
+  // main entry point; pass in Vector2 of change desired in pixel space,
+  // right and down are positive
+  this.pan = function (delta)
+  {
+    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 = Math.max(offset.length(), 15);
+
+      // 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 * delta.x * targetDistance / element.clientHeight);
+      scope.panUp(2 * delta.y * targetDistance / element.clientHeight);
+    }
+    else if (scope.object.top !== undefined)
+    {
+      // orthographic
+      scope.panLeft(delta.x * (scope.object.right - scope.object.left) /
+          element.clientWidth);
+      scope.panUp(delta.y * (scope.object.top - scope.object.bottom) /
+          element.clientHeight);
+    }
+    else
+    {
+      // camera neither orthographic or perspective - warn user
+      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;
+    var offset = position.clone().sub(this.target);
+
+    // angle from y-axis around z-axis
+    var theta = Math.atan2(offset.x, offset.y);
+
+    // angle from z-axis
+    var phi = Math.atan2(Math.sqrt(offset.x * offset.x + offset.y * offset.y),
+        offset.z);
+
+    if (this.autoRotate)
+    {
+      this.rotateLeft(getAutoRotationAngle());
+    }
+
+    var oldTheta = theta;
+    var oldPhi = phi;
+
+    theta -= thetaDelta;
+    phi += phiDelta;
+
+    // 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.z = radius * Math.cos(phi);
+    offset.y = radius * Math.sin(phi) * Math.cos(theta);
+
+    if (thetaDelta || phiDelta)
+    {
+      var rotateAroundWorldAxis = function (object, axis, radians)
+      {
+        rotWorldMatrix = new THREE.Matrix4();
+        rotWorldMatrix.makeRotationAxis(axis.normalize(), radians);
+        rotWorldMatrix.multiply(object.matrix); // pre-multiply
+
+        rotWorldMatrix.decompose(object.position,
+            object.quaternion, object.scale);
+        object.updateMatrix();
+      }
+
+      this.object.position.sub(this.target);
+      this.object.updateMatrix();
+      rotateAroundWorldAxis(this.object, new THREE.Vector3(0, 0, 1),
+          oldTheta - theta);
+      var localPitch = new THREE.Vector3(1, 0, 0);
+      localPitch.applyQuaternion(this.object.quaternion);
+      rotateAroundWorldAxis(this.object, localPitch, phi - oldPhi);
+      this.object.position.add(this.target);
+      this.object.updateMatrix();
+
+      /*var refObj = new THREE.Object3D();
+      refObj.position.x = this.target.x;
+      refObj.position.y = this.target.y;
+      refObj.position.z = this.target.z;
+      refObj.updateMatrix();
+      refObj.updateMatrixWorld();
+
+      var parent = this.object.parent;
+      if (parent)
+      {
+        parent.remove(this.object);
+     }
+
+      refObj.add(this.object);
+      this.object.position.x = offset.x;
+      this.object.position.y = offset.y;
+      this.object.position.z = offset.z;
+
+//      console.log (offset.x + ' ' + offset.y + ' ');
+      this.object.updateMatrix();
+      this.object.updateMatrixWorld();
+
+      var quat = new THREE.Quaternion();
+      quat.setFromEuler(new THREE.Vector3(theta, 0, phi));
+//      console.log('p t ' + phi + ' ' + theta);
+      refObj.quaternion.w = quat.w;
+      refObj.quaternion.x = quat.x;
+      refObj.quaternion.y = quat.y;
+      refObj.quaternion.z = quat.z;
+      refObj.updateMatrix();
+      refObj.updateMatrixWorld();
+
+      var matrixWorld = new THREE.Matrix4();
+      matrixWorld.copy(this.object.matrixWorld);
+
+      refObj.remove(this.object);
+      if (parent)
+        parent.add(this.object);
+
+
+      matrixWorld.decompose(this.object.position, this.object.quaternion,
+          this.object.scale);
+      console.log('p t ' + this.object.position.x + ' ' + this.object.position.y + ' ' +
+          this.object.position.z);
+
+//      this.object.matrxiWorld = matrixWorld;
+      this.object.updateMatrix();
+      this.object.updateMatrixWorld();*/
+      //this.object.lookAt(this.target);
+    }
+    else
+    {
+      position.copy(this.target).add(offset);
+//		  position.copy(newPos).add(offset);
+    }
+
+    //console.log(offset.x + ' ' + offset.y + ' ' +  offset.z);
+
+    thetaDelta = 0;
+    phiDelta = 0;
+    scale = 1;
+    pan.set(0,0,0);
+
+    if (lastPosition.distanceTo(this.object.position) > 0)
+    {
+      this.dispatchEvent(changeEvent);
+      lastPosition.copy(this.object.position);
+    }
+
+    if (scope.enabled === false)
+    {
+      setTargetIndicatorVisible(false)
+    }
+
+    var millisecs = new Date().getTime();
+    if (scrollTime && millisecs - scrollTime > 400)
+    {
+      setTargetIndicatorVisible(false)
+      scrollTime = null;
+    }
+
+    if (scope.targetIndicator.visible)
+    {
+      var scaleVec = new THREE.Vector3();
+      scaleVec.copy(object.position).sub(scope.targetIndicator.position);
+      var indicatorScale = scaleVec.length()/100;
+      scope.targetIndicator.scale.set(
+          indicatorScale, indicatorScale, indicatorScale);
+    }
+
+  };
+
+  function setTargetIndicatorVisible(visible)
+  {
+    scope.targetIndicator.visible = visible;
+    if (!scope.showTargetIndicator)
+    {
+      scope.targetIndicator.visible = false;
+    }
+  }
+
+  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.shiftKey && event.button === 0)
+    {
+      if (scope.noRotate === true)
+      {
+        return;
+      }
+      state = STATE.ROTATE;
+      rotateStart.set(event.clientX, event.clientY);
+    }
+    else if (event.button === 1)
+    {
+      if (scope.noRotate === true)
+      {
+        return;
+      }
+
+      state = STATE.ROTATE;
+
+      rotateStart.set(event.clientX, event.clientY);
+    }
+    else if (event.button === 2)
+    {
+      if (scope.noZoom === true)
+      {
+        return;
+      }
+
+      state = STATE.DOLLY;
+
+      dollyStart.set(event.clientX, event.clientY);
+    }
+    else if (event.button === 0)
+    {
+      if (scope.noPan === true)
+      {
+        return;
+      }
+
+      state = STATE.PAN;
+
+      panStart.set(event.clientX, event.clientY);
+    }
+    scope.targetIndicator.position.set(scope.target.x,scope.target.y,
+        scope.target.z);
+    setTargetIndicatorVisible(true)
+
+    scope.domElement.addEventListener('mousemove', onMouseMove, false);
+    scope.domElement.addEventListener('mouseup', onMouseUp, false);
+  }
+
+  function onMouseMove(event)
+  {
+    if (scope.enabled === false)
+    {
+      scope.domElement.removeEventListener('mousemove', onMouseMove, false);
+      scope.domElement.removeEventListener('mouseup', onMouseUp, 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);
+
+      panStart.copy(panEnd);
+    }
+
+    scope.update();
+  }
+
+  function onMouseUp(/* event */)
+  {
+    if (scope.enabled === false)
+    {
+      return;
+    }
+
+    scope.domElement.removeEventListener('mousemove', onMouseMove, false);
+    scope.domElement.removeEventListener('mouseup', onMouseUp, false);
+
+    state = STATE.NONE;
+    setTargetIndicatorVisible(false)
+  }
+
+  function onMouseWheel(event)
+  {
+    if (scope.enabled === false || scope.noZoom === true)
+    {
+      return;
+    }
+
+    scope.targetIndicator.position.set(scope.target.x,scope.target.y,
+        scope.target.z);
+    setTargetIndicatorVisible(true)
+    scrollTime = new Date().getTime();
+
+    var delta = 0;
+
+    if (event.wheelDelta) // WebKit / Opera / Explorer 9
+    {
+      delta = event.wheelDelta;
+    }
+    else if (event.detail) // Firefox
+    {
+      delta = - event.detail;
+    }
+
+    if (delta > 0)
+    {
+        scope.dollyOut();
+    }
+    else
+    {
+      scope.dollyIn();
+    }
+  }
+
+  function onKeyDown(event)
+  {
+    if (scope.enabled === false)
+    {
+      return;
+    }
+    if (scope.noKeys === true)
+    {
+      return;
+    }
+    if (scope.noPan === true)
+    {
+      return;
+    }
+
+    var needUpdate = false;
+
+    switch (event.keyCode)
+    {
+      case scope.keys.UP:
+          scope.pan(new THREE.Vector2(0, scope.keyPanSpeed));
+          needUpdate = true;
+          break;
+      case scope.keys.BOTTOM:
+          scope.pan(new THREE.Vector2(0, -scope.keyPanSpeed));
+          needUpdate = true;
+          break;
+      case scope.keys.LEFT:
+          scope.pan(new THREE.Vector2(scope.keyPanSpeed, 0));
+          needUpdate = true;
+          break;
+      case scope.keys.RIGHT:
+          scope.pan(new THREE.Vector2(-scope.keyPanSpeed, 0));
+          needUpdate = true;
+          break;
+    }
+
+    if (needUpdate)
+    {
+      scope.update();
+    }
+  }
+
+  function touchstart(event)
+  {
+    if (scope.enabled === false)
+    {
+      return;
+    }
+
+    scope.targetIndicator.position.set(scope.target.x,scope.target.y,
+        scope.target.z);
+    setTargetIndicatorVisible(true)
+
+    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 + pan
+          if (scope.noZoom === false)
+          {
+            state = STATE.TOUCH_DOLLY_PAN;
+
+            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);
+          }
+
+          if (scope.noPan === false)
+          {
+            var panAvgX = (event.touches[0].pageX + event.touches[1].pageX)/2;
+            var panAvgY = (event.touches[0].pageY + event.touches[1].pageY)/2;
+
+            panStart.set(panAvgX, panAvgY);
+          }
+          break;
+
+      default:
+          state = STATE.NONE;
+    }
+  }
+
+  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);
+          break;
+
+      case 2: // two-fingered touch: dolly + pan
+          if (state !== STATE.TOUCH_DOLLY_PAN)
+          {
+            return;
+          }
+
+          // Dolly delta
+          if (scope.noZoom === false)
+          {
+            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);
+            var dollyAbs = Math.abs(dollyDelta.y);
+            dollyStart.copy(dollyEnd);
+          }
+
+          // Pan delta
+          if (scope.noPan === false)
+          {
+            var panAvgX = (event.touches[0].pageX + event.touches[1].pageX)/2;
+            var panAvgY = (event.touches[0].pageY + event.touches[1].pageY)/2;
+
+            panEnd.set(panAvgX, panAvgY);
+            panDelta.subVectors(panEnd, panStart);
+            var panAbs = Math.max(Math.abs(panDelta.x),Math.abs(panDelta.y));
+            panStart.copy(panEnd);
+          }
+
+          // Choose one
+          if (scope.noPan === false && scope.noZoom === false)
+          {
+            if (dollyAbs > panAbs)
+            {
+              // Only dolly
+              scope.noPan = true;
+            }
+            else
+            {
+              // Only pan
+              scope.noZoom = true;
+            }
+          }
+
+          // Dolly
+          if (scope.noZoom === false)
+          {
+            // Threshold
+            if (Math.abs(dollyDelta.y) > 1.3)
+            {
+              if (dollyDelta.y > 0)
+              {
+                scope.dollyOut();
+              }
+              else
+              {
+                scope.dollyIn();
+              }
+            }
+          }
+
+          // Pan
+          if (scope.noPan === false)
+          {
+            scope.pan(panDelta);
+          }
+          break;
+
+      default:
+          state = STATE.NONE;
+    }
+  }
+
+  function touchend(/* event */)
+  {
+    if (scope.enabled === false)
+    {
+      return;
+    }
+
+    state = STATE.NONE;
+    scope.noPan = false;
+    scope.noZoom = false;
+
+    setTargetIndicatorVisible(false)
+  }
+
+  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('keydown', onKeyDown, false);
+
+  this.domElement.addEventListener('touchstart', touchstart, false);
+  this.domElement.addEventListener('touchend', touchend, false);
+  this.domElement.addEventListener('touchmove', touchmove, false);
+};
+
+THREE.OrbitControls.prototype = Object.create(THREE.EventDispatcher.prototype);
diff --git a/gz3d/client/js/include/RenderPass.js b/gz3d/client/js/include/RenderPass.js
new file mode 100644
index 0000000000000000000000000000000000000000..0dcc13fe940d7be2c1b6109fbcaa12680d8470f9
--- /dev/null
+++ b/gz3d/client/js/include/RenderPass.js
@@ -0,0 +1,51 @@
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.RenderPass = function ( scene, camera, overrideMaterial, clearColor, clearAlpha ) {
+
+	this.scene = scene;
+	this.camera = camera;
+
+	this.overrideMaterial = overrideMaterial;
+
+	this.clearColor = clearColor;
+	this.clearAlpha = ( clearAlpha !== undefined ) ? clearAlpha : 1;
+
+	this.oldClearColor = new THREE.Color();
+	this.oldClearAlpha = 1;
+
+	this.enabled = true;
+	this.clear = true;
+	this.needsSwap = false;
+
+};
+
+THREE.RenderPass.prototype = {
+
+	render: function ( renderer, writeBuffer, readBuffer, delta ) {
+
+		this.scene.overrideMaterial = this.overrideMaterial;
+
+		if ( this.clearColor ) {
+
+			this.oldClearColor.copy( renderer.getClearColor() );
+			this.oldClearAlpha = renderer.getClearAlpha();
+
+			renderer.setClearColor( this.clearColor, this.clearAlpha );
+
+		}
+
+		renderer.render( this.scene, this.camera, readBuffer, this.clear );
+
+		if ( this.clearColor ) {
+
+			renderer.setClearColor( this.oldClearColor, this.oldClearAlpha );
+
+		}
+
+		this.scene.overrideMaterial = null;
+
+	}
+
+};
diff --git a/gz3d/client/js/include/SSAOShader.js b/gz3d/client/js/include/SSAOShader.js
new file mode 100644
index 0000000000000000000000000000000000000000..4dee71ea7166dca4be859db11311828f575033b2
--- /dev/null
+++ b/gz3d/client/js/include/SSAOShader.js
@@ -0,0 +1,259 @@
+/**
+ * @author alteredq / http://alteredqualia.com/
+ *
+ * Screen-space ambient occlusion shader
+ * - ported from
+ *   SSAO GLSL shader v1.2
+ *   assembled by Martins Upitis (martinsh) (http://devlog-martinsh.blogspot.com)
+ *   original technique is made by ArKano22 (http://www.gamedev.net/topic/550699-ssao-no-halo-artifacts/)
+ * - modifications
+ * - modified to use RGBA packed depth texture (use clear color 1,1,1,1 for depth pass)
+ * - made fog more compatible with three.js linear fog
+ * - refactoring and optimizations
+ */
+
+THREE.SSAOShader = {
+
+	uniforms: {
+
+		"tDiffuse":     { type: "t", value: null },
+		"tDepth":       { type: "t", value: null },
+		"size":         { type: "v2", value: new THREE.Vector2( 512, 512 ) },
+		"cameraNear":   { type: "f", value: 1 },
+		"cameraFar":    { type: "f", value: 100 },
+		"fogNear":      { type: "f", value: 5 },
+		"fogFar":       { type: "f", value: 100 },
+		"fogEnabled":   { type: "i", value: 0 },
+		"onlyAO":       { type: "i", value: 0 },
+		"aoClamp":      { type: "f", value: 0.3 },
+		"lumInfluence": { type: "f", value: 0.9 }
+
+	},
+
+	vertexShader: [
+
+		"varying vec2 vUv;",
+
+		"void main() {",
+
+			"vUv = uv;",
+
+			"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+
+		"}"
+
+	].join("\n"),
+
+	fragmentShader: [
+
+		"uniform float cameraNear;",
+		"uniform float cameraFar;",
+
+		"uniform float fogNear;",
+		"uniform float fogFar;",
+
+		"uniform bool fogEnabled;",  // attenuate AO with linear fog
+		"uniform bool onlyAO;",      // use only ambient occlusion pass?
+
+		"uniform vec2 size;",        // texture width, height
+		"uniform float aoClamp;",    // depth clamp - reduces haloing at screen edges
+
+		"uniform float lumInfluence;",  // how much luminance affects occlusion
+
+		"uniform sampler2D tDiffuse;",
+		"uniform sampler2D tDepth;",
+
+		"varying vec2 vUv;",
+
+		// "#define PI 3.14159265",
+		"#define DL 2.399963229728653",  // PI * ( 3.0 - sqrt( 5.0 ) )
+		"#define EULER 2.718281828459045",
+
+		// helpers
+
+		"float width = size.x;",   // texture width
+		"float height = size.y;",  // texture height
+
+		"float cameraFarPlusNear = cameraFar + cameraNear;",
+		"float cameraFarMinusNear = cameraFar - cameraNear;",
+		"float cameraCoef = 2.0 * cameraNear;",
+
+		// user variables
+
+		"const int samples = 8;",     // ao sample count
+		"const float radius = 5.0;",  // ao radius
+
+		"const bool useNoise = false;",      // use noise instead of pattern for sample dithering
+		"const float noiseAmount = 0.0003;", // dithering amount
+
+		"const float diffArea = 0.4;",   // self-shadowing reduction
+		"const float gDisplace = 0.4;",  // gauss bell center
+
+		"const vec3 onlyAOColor = vec3( 1.0, 0.7, 0.5 );",
+		// "const vec3 onlyAOColor = vec3( 1.0, 1.0, 1.0 );",
+
+
+		// RGBA depth
+
+		"float unpackDepth( const in vec4 rgba_depth ) {",
+
+			"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 );",
+			"float depth = dot( rgba_depth, bit_shift );",
+			"return depth;",
+
+		"}",
+
+		// generating noise / pattern texture for dithering
+
+		"vec2 rand( const vec2 coord ) {",
+
+			"vec2 noise;",
+
+			"if ( useNoise ) {",
+
+				"float nx = dot ( coord, vec2( 12.9898, 78.233 ) );",
+				"float ny = dot ( coord, vec2( 12.9898, 78.233 ) * 2.0 );",
+
+				"noise = clamp( fract ( 43758.5453 * sin( vec2( nx, ny ) ) ), 0.0, 1.0 );",
+
+			"} else {",
+
+				"float ff = fract( 1.0 - coord.s * ( width / 2.0 ) );",
+				"float gg = fract( coord.t * ( height / 2.0 ) );",
+
+				"noise = vec2( 0.25, 0.75 ) * vec2( ff ) + vec2( 0.75, 0.25 ) * gg;",
+
+			"}",
+
+			"return ( noise * 2.0  - 1.0 ) * noiseAmount;",
+
+		"}",
+
+		"float doFog() {",
+
+			"float zdepth = unpackDepth( texture2D( tDepth, vUv ) );",
+			"float depth = -cameraFar * cameraNear / ( zdepth * cameraFarMinusNear - cameraFar );",
+
+			"return smoothstep( fogNear, fogFar, depth );",
+
+		"}",
+
+		"float readDepth( const in vec2 coord ) {",
+
+			// "return ( 2.0 * cameraNear ) / ( cameraFar + cameraNear - unpackDepth( texture2D( tDepth, coord ) ) * ( cameraFar - cameraNear ) );",
+			"return cameraCoef / ( cameraFarPlusNear - unpackDepth( texture2D( tDepth, coord ) ) * cameraFarMinusNear );",
+
+
+		"}",
+
+		"float compareDepths( const in float depth1, const in float depth2, inout int far ) {",
+
+			"float garea = 2.0;",                         // gauss bell width
+			"float diff = ( depth1 - depth2 ) * 100.0;",  // depth difference (0-100)
+
+			// reduce left bell width to avoid self-shadowing
+
+			"if ( diff < gDisplace ) {",
+
+				"garea = diffArea;",
+
+			"} else {",
+
+				"far = 1;",
+
+			"}",
+
+			"float dd = diff - gDisplace;",
+			"float gauss = pow( EULER, -2.0 * dd * dd / ( garea * garea ) );",
+			"return gauss;",
+
+		"}",
+
+		"float calcAO( float depth, float dw, float dh ) {",
+
+			"float dd = radius - depth * radius;",
+			"vec2 vv = vec2( dw, dh );",
+
+			"vec2 coord1 = vUv + dd * vv;",
+			"vec2 coord2 = vUv - dd * vv;",
+
+			"float temp1 = 0.0;",
+			"float temp2 = 0.0;",
+
+			"int far = 0;",
+			"temp1 = compareDepths( depth, readDepth( coord1 ), far );",
+
+			// DEPTH EXTRAPOLATION
+
+			"if ( far > 0 ) {",
+
+				"temp2 = compareDepths( readDepth( coord2 ), depth, far );",
+				"temp1 += ( 1.0 - temp1 ) * temp2;",
+
+			"}",
+
+			"return temp1;",
+
+		"}",
+
+		"void main() {",
+
+			"vec2 noise = rand( vUv );",
+			"float depth = readDepth( vUv );",
+
+			"float tt = clamp( depth, aoClamp, 1.0 );",
+
+			"float w = ( 1.0 / width )  / tt + ( noise.x * ( 1.0 - noise.x ) );",
+			"float h = ( 1.0 / height ) / tt + ( noise.y * ( 1.0 - noise.y ) );",
+
+			"float pw;",
+			"float ph;",
+
+			"float ao;",
+
+			"float dz = 1.0 / float( samples );",
+			"float z = 1.0 - dz / 2.0;",
+			"float l = 0.0;",
+
+			"for ( int i = 0; i <= samples; i ++ ) {",
+
+				"float r = sqrt( 1.0 - z );",
+
+				"pw = cos( l ) * r;",
+				"ph = sin( l ) * r;",
+				"ao += calcAO( depth, pw * w, ph * h );",
+				"z = z - dz;",
+				"l = l + DL;",
+
+			"}",
+
+			"ao /= float( samples );",
+			"ao = 1.0 - ao;",
+
+			"if ( fogEnabled ) {",
+
+				"ao = mix( ao, 1.0, doFog() );",
+
+			"}",
+
+			"vec3 color = texture2D( tDiffuse, vUv ).rgb;",
+
+			"vec3 lumcoeff = vec3( 0.299, 0.587, 0.114 );",
+			"float lum = dot( color.rgb, lumcoeff );",
+			"vec3 luminance = vec3( lum );",
+
+			"vec3 final = vec3( color * mix( vec3( ao ), vec3( 1.0 ), luminance * lumInfluence ) );",  // mix( color * ao, white, luminance )
+
+			"if ( onlyAO ) {",
+
+				"final = onlyAOColor * vec3( mix( vec3( ao ), vec3( 1.0 ), luminance * lumInfluence ) );",  // ambient occlusion only
+
+			"}",
+
+			"gl_FragColor = vec4( final, 1.0 );",
+
+		"}"
+
+	].join("\n")
+
+};
diff --git a/gz3d/client/js/include/ShaderPass.js b/gz3d/client/js/include/ShaderPass.js
new file mode 100644
index 0000000000000000000000000000000000000000..e8531f41058d37dffc6afb939dfbd588c482f1d0
--- /dev/null
+++ b/gz3d/client/js/include/ShaderPass.js
@@ -0,0 +1,51 @@
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.ShaderPass = function ( shader, textureID ) {
+
+	this.textureID = ( textureID !== undefined ) ? textureID : "tDiffuse";
+
+	this.uniforms = THREE.UniformsUtils.clone( shader.uniforms );
+
+	this.material = new THREE.ShaderMaterial( {
+
+		uniforms: this.uniforms,
+		vertexShader: shader.vertexShader,
+		fragmentShader: shader.fragmentShader
+
+	} );
+
+	this.renderToScreen = false;
+
+	this.enabled = true;
+	this.needsSwap = true;
+	this.clear = false;
+
+};
+
+THREE.ShaderPass.prototype = {
+
+	render: function ( renderer, writeBuffer, readBuffer, delta ) {
+
+		if ( this.uniforms[ this.textureID ] ) {
+
+			this.uniforms[ this.textureID ].value = readBuffer;
+
+		}
+
+		THREE.EffectComposer.quad.material = this.material;
+
+		if ( this.renderToScreen ) {
+
+			renderer.render( THREE.EffectComposer.scene, THREE.EffectComposer.camera );
+
+		} else {
+
+			renderer.render( THREE.EffectComposer.scene, THREE.EffectComposer.camera, writeBuffer, this.clear );
+
+		}
+
+	}
+
+};
diff --git a/gz3d/client/js/include/TrackballControls.js b/gz3d/client/js/include/TrackballControls.js
new file mode 100644
index 0000000000000000000000000000000000000000..3fb0f512b51570b17fc870e167ae66be30392cca
--- /dev/null
+++ b/gz3d/client/js/include/TrackballControls.js
@@ -0,0 +1,557 @@
+/**
+ * @author Eberhard Graether / http://egraether.com/
+ */
+
+THREE.TrackballControls = function ( object, domElement ) {
+
+	var _this = this;
+	var STATE = { NONE: -1, ROTATE: 0, ZOOM: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_ZOOM: 4, TOUCH_PAN: 5 };
+
+	this.object = object;
+	this.domElement = ( domElement !== undefined ) ? domElement : document;
+
+	// API
+
+	this.enabled = true;
+
+	this.screen = { left: 0, top: 0, width: 0, height: 0 };
+
+	this.rotateSpeed = 1.0;
+	this.zoomSpeed = 1.2;
+	this.panSpeed = 0.3;
+
+	this.noRotate = false;
+	this.noZoom = false;
+	this.noPan = false;
+	this.noRoll = false;
+
+	this.staticMoving = false;
+	this.dynamicDampingFactor = 0.2;
+
+	this.minDistance = 0;
+	this.maxDistance = Infinity;
+
+	this.keys = [ 65 /*A*/, 83 /*S*/, 68 /*D*/ ];
+
+	// internals
+
+	this.target = new THREE.Vector3();
+
+	var lastPosition = new THREE.Vector3();
+
+	var _state = STATE.NONE,
+	_prevState = STATE.NONE,
+
+	_eye = new THREE.Vector3(),
+
+	_rotateStart = new THREE.Vector3(),
+	_rotateEnd = new THREE.Vector3(),
+
+	_zoomStart = new THREE.Vector2(),
+	_zoomEnd = new THREE.Vector2(),
+
+	_touchZoomDistanceStart = 0,
+	_touchZoomDistanceEnd = 0,
+
+	_panStart = new THREE.Vector2(),
+	_panEnd = new THREE.Vector2();
+
+	// for reset
+
+	this.target0 = this.target.clone();
+	this.position0 = this.object.position.clone();
+	this.up0 = this.object.up.clone();
+
+	// events
+
+	var changeEvent = { type: 'change' };
+
+
+	// methods
+
+	this.handleResize = function () {
+
+		if ( this.domElement === document ) {
+
+			this.screen.left = 0;
+			this.screen.top = 0;
+			this.screen.width = window.innerWidth;
+			this.screen.height = window.innerHeight;
+
+		} else {
+
+			this.screen = this.domElement.getBoundingClientRect();
+
+		}
+
+	};
+
+	this.handleEvent = function ( event ) {
+
+		if ( typeof this[ event.type ] == 'function' ) {
+
+			this[ event.type ]( event );
+
+		}
+
+	};
+
+	this.getMouseOnScreen = function ( clientX, clientY ) {
+
+		return new THREE.Vector2(
+			( clientX - _this.screen.left ) / _this.screen.width,
+			( clientY - _this.screen.top ) / _this.screen.height
+		);
+
+	};
+
+	this.getMouseProjectionOnBall = function ( clientX, clientY ) {
+
+		var mouseOnBall = new THREE.Vector3(
+			( clientX - _this.screen.width * 0.5 - _this.screen.left ) / (_this.screen.width*.5),
+			( _this.screen.height * 0.5 + _this.screen.top - clientY ) / (_this.screen.height*.5),
+			0.0
+		);
+
+		var length = mouseOnBall.length();
+
+		if ( _this.noRoll ) {
+
+			if ( length < Math.SQRT1_2 ) {
+
+				mouseOnBall.z = Math.sqrt( 1.0 - length*length );
+
+			} else {
+
+				mouseOnBall.z = .5 / length;
+				
+			}
+
+		} else if ( length > 1.0 ) {
+
+			mouseOnBall.normalize();
+
+		} else {
+
+			mouseOnBall.z = Math.sqrt( 1.0 - length * length );
+
+		}
+
+		_eye.copy( _this.object.position ).sub( _this.target );
+
+		var projection = _this.object.up.clone().setLength( mouseOnBall.y );
+		projection.add( _this.object.up.clone().cross( _eye ).setLength( mouseOnBall.x ) );
+		projection.add( _eye.setLength( mouseOnBall.z ) );
+
+		return projection;
+
+	};
+
+	this.rotateCamera = function () {
+
+		var angle = Math.acos( _rotateStart.dot( _rotateEnd ) / _rotateStart.length() / _rotateEnd.length() );
+
+		if ( angle ) {
+
+			var axis = ( new THREE.Vector3() ).crossVectors( _rotateStart, _rotateEnd ).normalize(),
+				quaternion = new THREE.Quaternion();
+
+			angle *= _this.rotateSpeed;
+
+			quaternion.setFromAxisAngle( axis, -angle );
+
+			_eye.applyQuaternion( quaternion );
+			_this.object.up.applyQuaternion( quaternion );
+
+			_rotateEnd.applyQuaternion( quaternion );
+
+			if ( _this.staticMoving ) {
+
+				_rotateStart.copy( _rotateEnd );
+
+			} else {
+
+				quaternion.setFromAxisAngle( axis, angle * ( _this.dynamicDampingFactor - 1.0 ) );
+				_rotateStart.applyQuaternion( quaternion );
+
+			}
+
+		}
+
+	};
+
+	this.zoomCamera = function () {
+
+		if ( _state === STATE.TOUCH_ZOOM ) {
+
+			var factor = _touchZoomDistanceStart / _touchZoomDistanceEnd;
+			_touchZoomDistanceStart = _touchZoomDistanceEnd;
+			_eye.multiplyScalar( factor );
+
+		} else {
+
+			var factor = 1.0 + ( _zoomEnd.y - _zoomStart.y ) * _this.zoomSpeed;
+
+			if ( factor !== 1.0 && factor > 0.0 ) {
+
+				_eye.multiplyScalar( factor );
+
+				if ( _this.staticMoving ) {
+
+					_zoomStart.copy( _zoomEnd );
+
+				} else {
+
+					_zoomStart.y += ( _zoomEnd.y - _zoomStart.y ) * this.dynamicDampingFactor;
+
+				}
+
+			}
+
+		}
+
+	};
+
+	this.panCamera = function () {
+
+		var mouseChange = _panEnd.clone().sub( _panStart );
+
+		if ( mouseChange.lengthSq() ) {
+
+			mouseChange.multiplyScalar( _eye.length() * _this.panSpeed );
+
+			var pan = _eye.clone().cross( _this.object.up ).setLength( mouseChange.x );
+			pan.add( _this.object.up.clone().setLength( mouseChange.y ) );
+
+			_this.object.position.add( pan );
+			_this.target.add( pan );
+
+			if ( _this.staticMoving ) {
+
+				_panStart = _panEnd;
+
+			} else {
+
+				_panStart.add( mouseChange.subVectors( _panEnd, _panStart ).multiplyScalar( _this.dynamicDampingFactor ) );
+
+			}
+
+		}
+
+	};
+
+	this.checkDistances = function () {
+
+		if ( !_this.noZoom || !_this.noPan ) {
+
+			if ( _eye.lengthSq() > _this.maxDistance * _this.maxDistance ) {
+
+				_this.object.position.addVectors( _this.target, _eye.setLength( _this.maxDistance ) );
+
+			}
+
+			if ( _eye.lengthSq() < _this.minDistance * _this.minDistance ) {
+
+				_this.object.position.addVectors( _this.target, _eye.setLength( _this.minDistance ) );
+
+			}
+
+		}
+
+	};
+
+	this.update = function () {
+
+		_eye.subVectors( _this.object.position, _this.target );
+
+		if ( !_this.noRotate ) {
+
+			_this.rotateCamera();
+
+		}
+
+		if ( !_this.noZoom ) {
+
+			_this.zoomCamera();
+
+		}
+
+		if ( !_this.noPan ) {
+
+			_this.panCamera();
+
+		}
+
+		_this.object.position.addVectors( _this.target, _eye );
+
+		_this.checkDistances();
+
+		_this.object.lookAt( _this.target );
+
+		if ( lastPosition.distanceToSquared( _this.object.position ) > 0 ) {
+
+			_this.dispatchEvent( changeEvent );
+
+			lastPosition.copy( _this.object.position );
+
+		}
+
+	};
+
+	this.reset = function () {
+
+		_state = STATE.NONE;
+		_prevState = STATE.NONE;
+
+		_this.target.copy( _this.target0 );
+		_this.object.position.copy( _this.position0 );
+		_this.object.up.copy( _this.up0 );
+
+		_eye.subVectors( _this.object.position, _this.target );
+
+		_this.object.lookAt( _this.target );
+
+		_this.dispatchEvent( changeEvent );
+
+		lastPosition.copy( _this.object.position );
+
+	};
+
+	// listeners
+
+	function keydown( event ) {
+
+		if ( _this.enabled === false ) return;
+
+		window.removeEventListener( 'keydown', keydown );
+
+		_prevState = _state;
+
+		if ( _state !== STATE.NONE ) {
+
+			return;
+
+		} else if ( event.keyCode === _this.keys[ STATE.ROTATE ] && !_this.noRotate ) {
+
+			_state = STATE.ROTATE;
+
+		} else if ( event.keyCode === _this.keys[ STATE.ZOOM ] && !_this.noZoom ) {
+
+			_state = STATE.ZOOM;
+
+		} else if ( event.keyCode === _this.keys[ STATE.PAN ] && !_this.noPan ) {
+
+			_state = STATE.PAN;
+
+		}
+
+	}
+
+	function keyup( event ) {
+
+		if ( _this.enabled === false ) return;
+
+		_state = _prevState;
+
+		window.addEventListener( 'keydown', keydown, false );
+
+	}
+
+	function mousedown( event ) {
+
+		if ( _this.enabled === false ) return;
+
+		event.preventDefault();
+		event.stopPropagation();
+
+		if ( _state === STATE.NONE ) {
+
+			_state = event.button;
+
+		}
+
+		if ( _state === STATE.ROTATE && !_this.noRotate ) {
+
+			_rotateStart = _this.getMouseProjectionOnBall( event.clientX, event.clientY );
+			_rotateEnd.copy(_rotateStart)
+
+		} else if ( _state === STATE.ZOOM && !_this.noZoom ) {
+
+			_zoomStart = _this.getMouseOnScreen( event.clientX, event.clientY );
+			_zoomEnd.copy(_zoomStart);
+
+		} else if ( _state === STATE.PAN && !_this.noPan ) {
+
+			_panStart = _this.getMouseOnScreen( event.clientX, event.clientY );
+			_panEnd.copy(_panStart)
+
+		}
+
+		document.addEventListener( 'mousemove', mousemove, false );
+		document.addEventListener( 'mouseup', mouseup, false );
+
+	}
+
+	function mousemove( event ) {
+
+		if ( _this.enabled === false ) return;
+
+		event.preventDefault();
+		event.stopPropagation();
+
+		if ( _state === STATE.ROTATE && !_this.noRotate ) {
+
+			_rotateEnd = _this.getMouseProjectionOnBall( event.clientX, event.clientY );
+
+		} else if ( _state === STATE.ZOOM && !_this.noZoom ) {
+
+			_zoomEnd = _this.getMouseOnScreen( event.clientX, event.clientY );
+
+		} else if ( _state === STATE.PAN && !_this.noPan ) {
+
+			_panEnd = _this.getMouseOnScreen( event.clientX, event.clientY );
+
+		}
+
+	}
+
+	function mouseup( event ) {
+
+		if ( _this.enabled === false ) return;
+
+		event.preventDefault();
+		event.stopPropagation();
+
+		_state = STATE.NONE;
+
+		document.removeEventListener( 'mousemove', mousemove );
+		document.removeEventListener( 'mouseup', mouseup );
+
+	}
+
+	function mousewheel( event ) {
+
+		if ( _this.enabled === false ) return;
+
+		event.preventDefault();
+		event.stopPropagation();
+
+		var delta = 0;
+
+		if ( event.wheelDelta ) { // WebKit / Opera / Explorer 9
+
+			delta = event.wheelDelta / 40;
+
+		} else if ( event.detail ) { // Firefox
+
+			delta = - event.detail / 3;
+
+		}
+
+		_zoomStart.y += delta * 0.01;
+
+	}
+
+	function touchstart( event ) {
+
+		if ( _this.enabled === false ) return;
+
+		switch ( event.touches.length ) {
+
+			case 1:
+				_state = STATE.TOUCH_ROTATE;
+				_rotateStart = _rotateEnd = _this.getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
+				break;
+
+			case 2:
+				_state = STATE.TOUCH_ZOOM;
+				var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
+				var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
+				_touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt( dx * dx + dy * dy );
+				break;
+
+			case 3:
+				_state = STATE.TOUCH_PAN;
+				_panStart = _panEnd = _this.getMouseOnScreen( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
+				break;
+
+			default:
+				_state = STATE.NONE;
+
+		}
+
+	}
+
+	function touchmove( event ) {
+
+		if ( _this.enabled === false ) return;
+
+		event.preventDefault();
+		event.stopPropagation();
+
+		switch ( event.touches.length ) {
+
+			case 1:
+				_rotateEnd = _this.getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
+				break;
+
+			case 2:
+				var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
+				var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
+				_touchZoomDistanceEnd = Math.sqrt( dx * dx + dy * dy )
+				break;
+
+			case 3:
+				_panEnd = _this.getMouseOnScreen( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
+				break;
+
+			default:
+				_state = STATE.NONE;
+
+		}
+
+	}
+
+	function touchend( event ) {
+
+		if ( _this.enabled === false ) return;
+
+		switch ( event.touches.length ) {
+
+			case 1:
+				_rotateStart = _rotateEnd = _this.getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
+				break;
+
+			case 2:
+				_touchZoomDistanceStart = _touchZoomDistanceEnd = 0;
+				break;
+
+			case 3:
+				_panStart = _panEnd = _this.getMouseOnScreen( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
+				break;
+
+		}
+
+		_state = STATE.NONE;
+
+	}
+
+	this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false );
+
+	this.domElement.addEventListener( 'mousedown', mousedown, false );
+
+	this.domElement.addEventListener( 'mousewheel', mousewheel, false );
+	this.domElement.addEventListener( 'DOMMouseScroll', mousewheel, false ); // firefox
+
+	this.domElement.addEventListener( 'touchstart', touchstart, false );
+	this.domElement.addEventListener( 'touchend', touchend, false );
+	this.domElement.addEventListener( 'touchmove', touchmove, false );
+
+	window.addEventListener( 'keydown', keydown, false );
+	window.addEventListener( 'keyup', keyup, false );
+
+	this.handleResize();
+
+};
+
+THREE.TrackballControls.prototype = Object.create( THREE.EventDispatcher.prototype );
diff --git a/gz3d/client/js/include/TransformControls.js b/gz3d/client/js/include/TransformControls.js
new file mode 100644
index 0000000000000000000000000000000000000000..7688c0be17b0b8b3bb34459a21ecdc0619b62c53
--- /dev/null
+++ b/gz3d/client/js/include/TransformControls.js
@@ -0,0 +1,946 @@
+/**
+ * @author arodic / https://github.com/arodic
+ */
+
+ //"use strict";
+
+THREE.TransformControls = function ( camera, domElement, doc ) {
+
+	// TODO: Make non-uniform scale and rotate play nice in hierarchies
+	// TODO: ADD RXYZ contol
+
+	this.camera = camera;
+	this.domElement = ( domElement !== undefined ) ? domElement : document;
+	this.document = ( doc !== undefined ) ? doc : document;
+
+	this.object = undefined;
+
+	this.active = false;
+	this.hovered = false;
+
+	this.mode = 'translate';
+	this.space = 'world';
+	this.scale = 1;
+
+	this.snapDist = null;
+	this.modifierAxis = new THREE.Vector3( 1, 1, 1 );
+	this.gizmo = new THREE.Object3D();
+
+	var scope = this;
+
+	var changeEvent = { type: 'change' };
+
+	var showPickers = false; // debug
+
+	var ray = new THREE.Raycaster();
+	var projector = new THREE.Projector();
+	var pointerVector = new THREE.Vector3();
+
+	var point = new THREE.Vector3();
+	var offset = new THREE.Vector3();
+
+	var rotation = new THREE.Vector3();
+	var offsetRotation = new THREE.Vector3();
+	var scale = 1;
+
+	var lookAtMatrix = new THREE.Matrix4();
+	var eye = new THREE.Vector3()
+
+	var tempMatrix = new THREE.Matrix4();
+	var tempVector = new THREE.Vector3();
+	var tempQuaternion = new THREE.Quaternion();
+	var unitX = new THREE.Vector3( 1, 0, 0 );
+	var unitY = new THREE.Vector3( 0, 1, 0 );
+	var unitZ = new THREE.Vector3( 0, 0, 1 );
+
+	var quaternionXYZ = new THREE.Quaternion();
+	var quaternionX = new THREE.Quaternion();
+	var quaternionY = new THREE.Quaternion();
+	var quaternionZ = new THREE.Quaternion();
+	var quaternionE = new THREE.Quaternion();
+
+	var oldPosition = new THREE.Vector3();
+	var oldScale = new THREE.Vector3();
+	var oldRotationMatrix = new THREE.Matrix4();
+
+	var parentRotationMatrix  = new THREE.Matrix4();
+	var parentScale = new THREE.Vector3();
+
+	var worldPosition = new THREE.Vector3();
+//	var worldRotation = new THREE.Euler();
+	var worldRotation = new THREE.Vector3();
+	var worldRotationMatrix  = new THREE.Matrix4();
+	var camPosition = new THREE.Vector3();
+//	var camRotation = new THREE.Euler();
+	var camRotation = new THREE.Vector3();
+
+	var displayAxes = {};
+	var pickerAxes = {};
+	var intersectionPlanes = {};
+	var intersectionPlaneList = ['XY','YZ','XZ','XYZE']; // E
+	var currentPlane = 'XY';
+
+	// intersection planes
+	{
+
+		var planes = new THREE.Object3D();
+		this.gizmo.add(planes);
+
+		for ( var i in intersectionPlaneList ){
+
+			intersectionPlanes[intersectionPlaneList[i]] = new THREE.Mesh( new THREE.PlaneGeometry( 500, 500 ) );
+			intersectionPlanes[intersectionPlaneList[i]].material.side = THREE.DoubleSide;
+			intersectionPlanes[intersectionPlaneList[i]].visible = false;
+			planes.add(intersectionPlanes[intersectionPlaneList[i]]);
+
+		}
+
+		intersectionPlanes['YZ'].rotation.set( 0, Math.PI/2, 0 );
+		intersectionPlanes['XZ'].rotation.set( -Math.PI/2, 0, 0 );
+		bakeTransformations(intersectionPlanes['YZ']);
+		bakeTransformations(intersectionPlanes['XZ']);
+
+	}
+
+	// gizmo geometry
+	{
+
+		displayAxes["translate"] = new THREE.Object3D();
+		displayAxes["rotate"] = new THREE.Object3D();
+		displayAxes["scale"] = new THREE.Object3D();
+		this.gizmo.add( displayAxes["translate"] );
+		this.gizmo.add( displayAxes["rotate"] );
+		this.gizmo.add( displayAxes["scale"] );
+
+		pickerAxes["translate"] = new THREE.Object3D();
+		pickerAxes["rotate"] = new THREE.Object3D();
+		pickerAxes["scale"] = new THREE.Object3D();
+		this.gizmo.add( pickerAxes["translate"] );
+		this.gizmo.add( pickerAxes["rotate"] );
+		this.gizmo.add( pickerAxes["scale"] );
+
+		var HandleMaterial = function ( color, opacity ) {
+			var material = new THREE.MeshBasicMaterial();
+			material.color = color;
+			material.side = THREE.DoubleSide;
+			material.depthTest = false;
+			material.depthWrite = false;
+			material.opacity = opacity !== undefined ? opacity : 1;
+			material.transparent = true;
+			return material;
+		}
+
+		var LineMaterial = function ( color, opacity ) {
+			var material = new THREE.LineBasicMaterial();
+			material.color = color;
+			material.depthTest = false;
+			material.depthWrite = false;
+			material.opacity = opacity !== undefined ? opacity : 1;
+			material.transparent = true;
+			return material;
+		}
+
+		// materials by color
+		var white = new THREE.Color( 0xffffff );
+		var gray = new THREE.Color( 0x808080 );
+		var red = new THREE.Color( 0xff0000 );
+		var green = new THREE.Color( 0x00ff00 );
+		var blue = new THREE.Color( 0x0000ff );
+		var cyan = new THREE.Color( 0x00ffff );
+		var magenta = new THREE.Color( 0xff00ff );
+		var yellow = new THREE.Color( 0xffff00 );
+
+		var geometry, mesh;
+
+		// Line axes
+
+		geometry = new THREE.Geometry();
+		geometry.vertices.push(
+			new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 1, 0, 0 ),
+			new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 1, 0 ),
+			new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 0, 1 )
+		);
+		geometry.colors.push(
+			red, red, green, green, blue, blue
+		);
+		material = new THREE.LineBasicMaterial( {
+			vertexColors: THREE.VertexColors,
+			depthTest: false,
+			depthWrite: false,
+			transparent: true
+		} );
+		mesh = new THREE.Line( geometry, material, THREE.LinePieces );
+		displayAxes['translate'].add( mesh );
+		displayAxes['scale'].add( mesh.clone() );
+
+		// Translate handles
+
+		mesh = new THREE.Mesh( new THREE.OctahedronGeometry( 0.1, 0 ), HandleMaterial( white, 0.25 ) );
+		mesh.name = 'TXYZ';
+		displayAxes['translate'].add( mesh );
+		pickerAxes['translate'].add( mesh.clone() );
+
+		geometry = new THREE.PlaneGeometry( 0.3, 0.3 );
+
+		mesh = new THREE.Mesh( geometry, HandleMaterial( yellow, 0.25 ) );
+		mesh.position.set( 0.15, 0.15, 0 );
+		bakeTransformations( mesh );
+		mesh.name = 'TXY';
+		displayAxes['translate'].add( mesh );
+		pickerAxes['translate'].add( mesh.clone() );
+
+		mesh = new THREE.Mesh( geometry, HandleMaterial( cyan, 0.25 ) );
+		mesh.position.set( 0, 0.15, 0.15 );
+		mesh.rotation.y = Math.PI/2;
+		bakeTransformations( mesh );
+		mesh.name = 'TYZ';
+		displayAxes['translate'].add( mesh );
+		pickerAxes['translate'].add( mesh.clone() );
+
+		mesh = new THREE.Mesh( geometry, HandleMaterial( magenta, 0.25 ) );
+		mesh.position.set( 0.15, 0, 0.15 );
+		mesh.rotation.x = Math.PI/2;
+		bakeTransformations( mesh );
+		mesh.name = 'TXZ';
+		displayAxes['translate'].add( mesh );
+		pickerAxes['translate'].add( mesh.clone() );
+
+		geometry = new THREE.CylinderGeometry( 0, 0.05, 0.2, 4, 1, true );
+
+		mesh = new THREE.Mesh( geometry, HandleMaterial( red ) );
+		mesh.position.x = 1.1;
+		mesh.rotation.z = -Math.PI/2;
+		bakeTransformations( mesh );
+		mesh.name = 'TX';
+		displayAxes['translate'].add( mesh );
+
+		mesh = new THREE.Mesh( geometry, HandleMaterial( green ) );
+		mesh.position.y = 1.1;
+		bakeTransformations( mesh );
+		mesh.name = 'TY';
+		displayAxes['translate'].add( mesh );
+
+		mesh = new THREE.Mesh( geometry, HandleMaterial( blue ) );
+		mesh.position.z = 1.1;
+		mesh.rotation.x = Math.PI/2;
+		bakeTransformations( mesh );
+		mesh.name = 'TZ';
+		displayAxes['translate'].add( mesh );
+
+		geometry = new THREE.CylinderGeometry( 0.2, 0.1, 0.8, 4, 1, false );
+
+		mesh = new THREE.Mesh( geometry, HandleMaterial( red ) );
+		mesh.position.x = 0.7;
+		mesh.rotation.z = -Math.PI/2;
+		bakeTransformations( mesh );
+		mesh.name = 'TX';
+		pickerAxes['translate'].add( mesh );
+
+		mesh = new THREE.Mesh( geometry, HandleMaterial( green ) );
+		mesh.position.y = 0.7;
+		bakeTransformations( mesh );
+		mesh.name = 'TY';
+		pickerAxes['translate'].add( mesh );
+
+		mesh = new THREE.Mesh( geometry, HandleMaterial( blue ) );
+		mesh.position.z = 0.7;
+		mesh.rotation.x = Math.PI/2;
+		bakeTransformations( mesh );
+		mesh.name = 'TZ';
+		pickerAxes['translate'].add( mesh );
+
+		// scale manipulators
+
+		geometry = new THREE.CubeGeometry( 0.125, 0.125, 0.125 );
+
+		mesh = new THREE.Mesh( geometry, HandleMaterial( white, 0.25 ) );
+		mesh.name = 'SXYZ';
+		displayAxes['scale'].add( mesh );
+		pickerAxes['scale'].add( mesh.clone() );
+
+		mesh = new THREE.Mesh( geometry, HandleMaterial( red ) );
+		mesh.position.set( 1.05, 0, 0 );
+		bakeTransformations( mesh );
+		mesh.name = 'SX';
+		displayAxes['scale'].add( mesh );
+		pickerAxes['scale'].add( mesh.clone() );
+
+		mesh = new THREE.Mesh( geometry, HandleMaterial( green ) );
+		mesh.position.set( 0, 1.05, 0 );
+		bakeTransformations( mesh );
+		mesh.name = 'SY';
+		displayAxes['scale'].add( mesh );
+		pickerAxes['scale'].add( mesh.clone() );
+
+		mesh = new THREE.Mesh( geometry, HandleMaterial( blue ) );
+		mesh.position.set( 0, 0, 1.05 );
+		bakeTransformations( mesh );
+		mesh.name = 'SZ';
+		displayAxes['scale'].add( mesh );
+		pickerAxes['scale'].add( mesh.clone() );
+
+		// rotate manipulators
+
+		var Circle = function ( radius, facing, arc ) {
+
+			geometry = new THREE.Geometry();
+			arc = arc ? arc : 1;
+			for ( var i = 0; i <= 64 * arc; ++i ) {
+				if ( facing == 'x' ) geometry.vertices.push( new THREE.Vector3( 0, Math.cos( i / 32 * Math.PI ), Math.sin( i / 32 * Math.PI ) ).multiplyScalar(radius) );
+				if ( facing == 'y' ) geometry.vertices.push( new THREE.Vector3( Math.cos( i / 32 * Math.PI ), 0, Math.sin( i / 32 * Math.PI ) ).multiplyScalar(radius) );
+				if ( facing == 'z' ) geometry.vertices.push( new THREE.Vector3( Math.sin( i / 32 * Math.PI ), Math.cos( i / 32 * Math.PI ), 0 ).multiplyScalar(radius) );
+			}
+
+			return geometry;
+		}
+
+		mesh = new THREE.Line( Circle( 1, 'x', 0.5 ), LineMaterial( red ) );
+		mesh.name = 'RX';
+		displayAxes['rotate'].add( mesh );
+
+		mesh = new THREE.Line( Circle( 1, 'y', 0.5 ), LineMaterial( green ) );
+		mesh.name = 'RY';
+		displayAxes['rotate'].add( mesh );
+
+		mesh = new THREE.Line( Circle( 1, 'z', 0.5 ), LineMaterial( blue ) );
+		mesh.name = 'RZ';
+		displayAxes['rotate'].add( mesh );
+
+		mesh = new THREE.Line( Circle( 1, 'z' ), LineMaterial( gray ) );
+		mesh.name = 'RXYZE';
+		displayAxes['rotate'].add( mesh );
+
+		mesh = new THREE.Line( Circle( 1.25, 'z' ), LineMaterial( yellow, 0.25 ) );
+		mesh.name = 'RE';
+		displayAxes['rotate'].add( mesh );
+
+		geometry = new THREE.TorusGeometry( 1, 0.15, 4, 6, Math.PI );
+
+		mesh = new THREE.Mesh( geometry, HandleMaterial( red ) );
+		mesh.rotation.z = -Math.PI/2;
+		mesh.rotation.y = -Math.PI/2;
+		bakeTransformations( mesh );
+		mesh.name = 'RX';
+		pickerAxes['rotate'].add( mesh );
+
+		mesh = new THREE.Mesh( geometry, HandleMaterial( green ) );
+		mesh.rotation.z = Math.PI;
+		mesh.rotation.x = -Math.PI/2;
+		bakeTransformations( mesh );
+		mesh.name = 'RY';
+		pickerAxes['rotate'].add( mesh );
+
+		mesh = new THREE.Mesh( geometry, HandleMaterial( blue ) );
+		mesh.rotation.z = -Math.PI/2;
+		bakeTransformations( mesh );
+		mesh.name = 'RZ';
+		pickerAxes['rotate'].add( mesh );
+
+		mesh = new THREE.Mesh( new THREE.SphereGeometry( 0.95, 12, 12 ), HandleMaterial( white, 0.25 ) );
+		mesh.name = 'RXYZE';
+		pickerAxes['rotate'].add( mesh );
+
+		intersectionPlanes['SPHERE'] = new THREE.Mesh( new THREE.SphereGeometry( 0.95, 12, 12 ) );
+		intersectionPlanes['SPHERE'].visible = false;
+		planes.add(intersectionPlanes['SPHERE']);
+
+		mesh = new THREE.Mesh( new THREE.TorusGeometry( 1.30, 0.15, 4, 12 ), HandleMaterial( yellow, 0.25 ) );
+		mesh.name = 'RE';
+		pickerAxes['rotate'].add( mesh );
+
+		mesh = null;
+
+	}
+
+	this.attach = function ( object ) {
+
+		this.object = object;
+	 	this.setMode( scope.mode );
+
+		this.domElement.addEventListener( 'mousedown', onMouseDown, false );
+		this.domElement.addEventListener( 'mousemove', onMouseHover, false );
+		this.document.addEventListener( 'keydown', onKeyDown, false );
+
+	}
+
+	this.detach = function ( object ) {
+
+		this.object = undefined;
+		this.hovered = false;
+
+	 	this.hide();
+
+		this.domElement.removeEventListener( 'mousedown', onMouseDown, false );
+		this.domElement.removeEventListener( 'mousemove', onMouseHover, false );
+		this.document.removeEventListener( 'keydown', onKeyDown, false );
+
+	}
+
+	this.update = function () {
+
+		if ( this.object === undefined ) return;
+
+		this.object.updateMatrixWorld();
+		worldPosition.getPositionFromMatrix( this.object.matrixWorld );
+		// worldRotation.setFromRotationMatrix( tempMatrix.extractRotation( this.object.matrixWorld ) );
+
+		this.camera.updateMatrixWorld();
+		camPosition.getPositionFromMatrix( this.camera.matrixWorld );
+		// camRotation.setFromRotationMatrix( tempMatrix.extractRotation( this.camera.matrixWorld ) );
+
+		scale = worldPosition.distanceTo( camPosition ) / 6 * this.scale;
+		this.gizmo.position.copy( worldPosition )
+		this.gizmo.scale.set( scale, scale, scale );
+
+		for ( var i in this.gizmo.children ) {
+
+			for ( var j in this.gizmo.children[i].children ) {
+
+				var object = this.gizmo.children[i].children[j];
+				var name = object.name;
+
+				if ( name.search('E') != -1 ){
+
+					lookAtMatrix.lookAt( camPosition, worldPosition, tempVector.set( 0, 1, 0 ));
+					object.rotation.setFromRotationMatrix( lookAtMatrix );
+
+				} else {
+
+					eye.copy( camPosition ).sub( worldPosition ).normalize();
+
+					if ( this.space == 'local' ) {
+
+						// tempQuaternion.setFromEuler( worldRotation );
+						tempQuaternion.setFromRotationMatrix( tempMatrix.extractRotation( this.object.matrixWorld ) );
+
+						if ( name.search('R') != -1 ){
+
+							tempMatrix.makeRotationFromQuaternion( tempQuaternion ).getInverse( tempMatrix );
+							eye.applyProjection( tempMatrix );
+
+							if ( name == 'RX' ) {
+								quaternionX.setFromAxisAngle( unitX, Math.atan2( -eye.y, eye.z ) );
+								tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionX );
+							}
+
+							if ( name == 'RY' ) {
+								quaternionY.setFromAxisAngle( unitY, Math.atan2( eye.x, eye.z ) );
+								tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionY );
+							}
+
+							if ( name == 'RZ' ) {
+								quaternionZ.setFromAxisAngle( unitZ, Math.atan2( eye.y, eye.x ) );
+								tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionZ );
+							}
+
+						}
+
+						object.quaternion.copy( tempQuaternion );
+
+					} else if ( this.space == 'world' ) {
+
+						object.rotation.set( 0, 0, 0 );
+
+						if ( name == 'RX' ) object.rotation.x = Math.atan2( -eye.y, eye.z );
+						if ( name == 'RY' ) object.rotation.y = Math.atan2(  eye.x, eye.z );
+						if ( name == 'RZ' ) object.rotation.z = Math.atan2(  eye.y, eye.x );
+
+					}
+
+				}
+
+			}
+
+		}
+
+	}
+
+	this.hide = function () {
+
+		for ( var i in displayAxes ) {
+
+			for ( var j in displayAxes[i].children ) {
+
+				displayAxes[i].children[j].visible = false;
+
+			}
+
+		}
+
+		for ( var i in pickerAxes ) {
+
+			for ( var j in pickerAxes[i].children ) {
+
+				pickerAxes[i].children[j].visible = false;
+
+			}
+
+		}
+
+	}
+
+	this.setMode = function ( value ) {
+
+		scope.mode = value;
+
+		this.hide();
+
+		if ( scope.mode == 'scale' ) scope.space = 'local';
+
+		for ( var i in displayAxes[this.mode].children ) {
+
+			displayAxes[this.mode].children[i].visible = true;
+
+		}
+
+		for ( var i in pickerAxes[this.mode].children ) {
+
+			pickerAxes[this.mode].children[i].visible = showPickers;
+
+		}
+
+		scope.update();
+
+	}
+
+	this.setIntersectionPlane = function () {
+
+		eye.copy( camPosition ).sub( worldPosition ).normalize();
+
+		if ( this.space == 'local' ) {
+
+			eye.applyMatrix4( tempMatrix.getInverse( scope.object.matrixWorld ) );
+
+		}
+
+		if ( isActive("X") ) {
+
+			if ( eye.y > eye.z ) currentPlane = 'XZ';
+			else currentPlane = 'XY';
+
+		}
+
+		if ( isActive("Y") ) {
+
+			if ( eye.x > eye.z ) currentPlane = 'YZ';
+			else currentPlane = 'XY';
+
+		}
+
+		if ( isActive("Z") ) {
+
+			if ( eye.x > eye.y ) currentPlane = 'YZ';
+			else currentPlane = 'XZ';
+
+		}
+
+		if ( isActive("XY") ) {
+
+			currentPlane = 'XY';
+
+		}
+
+		if ( isActive("YZ") ) {
+
+			currentPlane = 'YZ';
+
+		}
+
+		if ( isActive("XZ") ) {
+
+			currentPlane = 'XZ';
+
+		}
+
+		if ( isActive("XYZ") || isActive("E") ) {
+
+			currentPlane = 'XYZE';
+
+		}
+
+	 	if ( isActive("RX") ) {
+
+			currentPlane = 'YZ';
+
+		}
+
+		if ( isActive("RY") ) {
+
+			currentPlane = 'XZ';
+
+		}
+
+		if ( isActive("RZ") ) {
+
+			currentPlane = 'XY';
+
+		}
+
+		if ( isActive("RXYZ") ) {
+
+			currentPlane = 'SPHERE';
+
+		}
+
+	}
+
+	var hovered = null;
+	var hoveredColor = new THREE.Color();
+
+	function onMouseHover( event ) {
+
+		event.preventDefault();
+
+		if ( event.button === 0 && scope.active === false ) {
+
+			var intersect = intersectObjects( event, pickerAxes[scope.mode].children );
+
+			if ( intersect ) {
+
+				if ( hovered !== intersect.object ) {
+
+					if ( hovered !== null ) {
+
+						hovered.material.color.copy( hoveredColor );
+
+					}
+
+					hovered = intersect.object;
+					hoveredColor.copy( hovered.material.color );
+
+                    hovered.material.color.offsetHSL( 0, 0, -0.3 );
+
+					scope.dispatchEvent( changeEvent );
+
+				}
+
+				scope.hovered = true;
+
+			} else if ( hovered !== null ) {
+
+				hovered.material.color.copy( hoveredColor );
+
+				hovered = null;
+
+				scope.dispatchEvent( changeEvent );
+
+				scope.hovered = false;
+
+			}
+
+		}
+
+		scope.document.addEventListener( 'mousemove', onMouseMove, false );
+		scope.document.addEventListener( 'mouseup', onMouseUp, false );
+
+	};
+
+	function onMouseDown( event ) {
+
+		event.preventDefault();
+
+		if ( event.button === 0 ) {
+
+			var intersect = intersectObjects( event, pickerAxes[scope.mode].children );
+
+			if ( intersect ) {
+
+				scope.active = intersect.object.name;
+
+				scope.update();
+				scope.setIntersectionPlane();
+
+				var planeIntersect = intersectObjects( event, [intersectionPlanes[currentPlane]] );
+
+				if ( planeIntersect ) {
+
+					oldPosition.copy( scope.object.position );
+					oldScale.copy( scope.object.scale );
+
+					oldRotationMatrix.extractRotation( scope.object.matrix );
+					worldRotationMatrix.extractRotation( scope.object.matrixWorld );
+
+					parentRotationMatrix.extractRotation( scope.object.parent.matrixWorld );
+					parentScale.getScaleFromMatrix( tempMatrix.getInverse( scope.object.parent.matrixWorld ) );
+
+					offset.copy( planeIntersect.point );
+
+				}
+
+			}
+
+		}
+
+		scope.document.addEventListener( 'mousemove', onMouseMove, false );
+		scope.document.addEventListener( 'mouseup', onMouseUp, false );
+
+	};
+
+	function onMouseMove( event ) {
+
+		if ( scope.active ) {
+
+			var planeIntersect = intersectObjects( event, [intersectionPlanes[currentPlane]] );
+
+			if ( planeIntersect ) {
+
+				point.copy( planeIntersect.point );
+
+				if ( ( scope.mode == 'translate' ) && isActive("T") ) {
+
+					point.sub( offset );
+					point.multiply(parentScale);
+
+					if ( scope.space == 'local' ) {
+
+						point.applyMatrix4( tempMatrix.getInverse( worldRotationMatrix ) );
+
+						if ( !(isActive("X")) || scope.modifierAxis.x != 1 ) point.x = 0;
+						if ( !(isActive("Y")) || scope.modifierAxis.y != 1 ) point.y = 0;
+						if ( !(isActive("Z")) || scope.modifierAxis.z != 1 ) point.z = 0;
+						if ( isActive("XYZ") ) point.set( 0, 0, 0 );
+
+						point.applyMatrix4( oldRotationMatrix );
+
+						scope.object.position.copy( oldPosition );
+						scope.object.position.add( point );
+
+					}
+
+					if ( scope.space == 'world' || isActive("XYZ") ) {
+
+						if ( !(isActive("X")) || scope.modifierAxis.x != 1 ) point.x = 0;
+						if ( !(isActive("Y")) || scope.modifierAxis.y != 1 ) point.y = 0;
+						if ( !(isActive("Z")) || scope.modifierAxis.z != 1 ) point.z = 0;
+
+						point.applyMatrix4( tempMatrix.getInverse( parentRotationMatrix ) );
+
+						scope.object.position.copy( oldPosition );
+						scope.object.position.add( point );
+
+						if ( scope.snapDist ) {
+							if ( isActive("X") ) scope.object.position.x = Math.round( scope.object.position.x / scope.snapDist ) * scope.snapDist;
+							if ( isActive("Y") ) scope.object.position.y = Math.round( scope.object.position.y / scope.snapDist ) * scope.snapDist;
+							if ( isActive("Z") ) scope.object.position.z = Math.round( scope.object.position.z / scope.snapDist ) * scope.snapDist;
+						}
+
+					}
+
+				} else if ( ( scope.mode == 'scale') && isActive("S") ) {
+
+					point.sub( offset );
+					point.multiply(parentScale);
+
+					if ( scope.space == 'local' ) {
+
+						if ( isActive("XYZ")) {
+
+							scale = 1 + ( ( point.y ) / 50 );
+
+							scope.object.scale.x = oldScale.x * scale;
+							scope.object.scale.y = oldScale.y * scale;
+							scope.object.scale.z = oldScale.z * scale;
+
+						} else {
+
+							point.applyMatrix4( tempMatrix.getInverse( worldRotationMatrix ) );
+
+							if ( !(isActive("X")) || scope.modifierAxis.x != 1 ) point.x = 0;
+							if ( !(isActive("Y")) || scope.modifierAxis.y != 1 ) point.y = 0;
+							if ( !(isActive("Z")) || scope.modifierAxis.z != 1 ) point.z = 0;
+
+							if ( isActive("X") ) scope.object.scale.x = oldScale.x * ( 1 + point.x / 50 );
+							if ( isActive("Y") ) scope.object.scale.y = oldScale.y * ( 1 + point.y / 50 );
+							if ( isActive("Z") ) scope.object.scale.z = oldScale.z * ( 1 + point.z / 50 );
+
+						}
+
+					}
+
+				} else if ( ( scope.mode == 'rotate' ) && isActive("R") ) {
+
+					point.sub( worldPosition );
+					point.multiply(parentScale);
+					tempVector.copy(offset).sub( worldPosition );
+					tempVector.multiply(parentScale);
+
+					if ( scope.active == "RE" ) {
+
+						point.applyMatrix4( tempMatrix.getInverse( lookAtMatrix ) );
+						tempVector.applyMatrix4( tempMatrix.getInverse( lookAtMatrix ) );
+
+						rotation.set( Math.atan2( point.z, point.y ), Math.atan2( point.x, point.z ), Math.atan2( point.y, point.x ) );
+						offsetRotation.set( Math.atan2( tempVector.z, tempVector.y ), Math.atan2( tempVector.x, tempVector.z ), Math.atan2( tempVector.y, tempVector.x ) );
+
+						tempQuaternion.setFromRotationMatrix( tempMatrix.getInverse( parentRotationMatrix ) );
+
+						quaternionE.setFromAxisAngle( eye, rotation.z - offsetRotation.z );
+						quaternionXYZ.setFromRotationMatrix( worldRotationMatrix );
+
+						tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionE );
+						tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionXYZ );
+
+						scope.object.quaternion.copy( tempQuaternion );
+
+					} else if ( scope.active == "RXYZE" ) {
+
+						quaternionE.setFromEuler( point.clone().cross(tempVector).normalize() ); // rotation axis
+
+						tempQuaternion.setFromRotationMatrix( tempMatrix.getInverse( parentRotationMatrix ) );
+						quaternionX.setFromAxisAngle( quaternionE, - point.clone().angleTo(tempVector) );
+						quaternionXYZ.setFromRotationMatrix( worldRotationMatrix );
+
+						tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionX );
+						tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionXYZ );
+
+						scope.object.quaternion.copy( tempQuaternion );
+
+					} else if ( scope.space == 'local' ) {
+
+						point.applyMatrix4( tempMatrix.getInverse( worldRotationMatrix ) );
+
+						tempVector.applyMatrix4( tempMatrix.getInverse( worldRotationMatrix ) );
+
+						rotation.set( Math.atan2( point.z, point.y ), Math.atan2( point.x, point.z ), Math.atan2( point.y, point.x ) );
+						offsetRotation.set( Math.atan2( tempVector.z, tempVector.y ), Math.atan2( tempVector.x, tempVector.z ), Math.atan2( tempVector.y, tempVector.x ) );
+
+						quaternionXYZ.setFromRotationMatrix( oldRotationMatrix );
+						quaternionX.setFromAxisAngle( unitX, rotation.x - offsetRotation.x );
+						quaternionY.setFromAxisAngle( unitY, rotation.y - offsetRotation.y );
+						quaternionZ.setFromAxisAngle( unitZ, rotation.z - offsetRotation.z );
+
+						if ( scope.active == "RX" ) quaternionXYZ.multiplyQuaternions( quaternionXYZ, quaternionX );
+						if ( scope.active == "RY" ) quaternionXYZ.multiplyQuaternions( quaternionXYZ, quaternionY );
+						if ( scope.active == "RZ" ) quaternionXYZ.multiplyQuaternions( quaternionXYZ, quaternionZ );
+
+						scope.object.quaternion.copy( quaternionXYZ );
+
+					} else if ( scope.space == 'world' ) {
+
+						rotation.set( Math.atan2( point.z, point.y ), Math.atan2( point.x, point.z ), Math.atan2( point.y, point.x ) );
+						offsetRotation.set( Math.atan2( tempVector.z, tempVector.y ), Math.atan2( tempVector.x, tempVector.z ), Math.atan2( tempVector.y, tempVector.x ) );
+
+						tempQuaternion.setFromRotationMatrix( tempMatrix.getInverse( parentRotationMatrix ) );
+
+						quaternionX.setFromAxisAngle( unitX, rotation.x - offsetRotation.x );
+						quaternionY.setFromAxisAngle( unitY, rotation.y - offsetRotation.y );
+						quaternionZ.setFromAxisAngle( unitZ, rotation.z - offsetRotation.z );
+						quaternionXYZ.setFromRotationMatrix( worldRotationMatrix );
+
+						if ( scope.active == "RX" ) tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionX );
+						if ( scope.active == "RY" ) tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionY );
+						if ( scope.active == "RZ" ) tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionZ );
+
+						tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionXYZ );
+
+						scope.object.quaternion.copy( tempQuaternion );
+
+					}
+
+				}
+
+			}
+
+			scope.update();
+			scope.dispatchEvent( changeEvent );
+
+		}
+
+	}
+
+	function onMouseUp( event ) {
+
+		scope.active = false;
+
+		scope.document.removeEventListener( 'mousemove', onMouseMove, false );
+		scope.document.removeEventListener( 'mouseup', onMouseUp, false );
+
+	}
+
+	function onKeyDown( event ) {
+
+		var currentMode = scope.mode;
+		var currentSpace = scope.space;
+		var currentScale = scope.scale;
+
+		if ( event.keyCode == 84 ) { // T
+
+			if ( scope.mode == 'translate' ) scope.space = ( scope.space == 'world' ) ? 'local' : 'world';
+			scope.mode = 'translate';
+
+		}
+
+		if ( event.keyCode == 82 ) { // R
+
+			if ( scope.mode == 'rotate' ) scope.space = ( scope.space == 'world' ) ? 'local' : 'world';
+			scope.mode = 'rotate';
+
+		}
+
+		/*if ( event.keyCode == 82 ) { // R
+
+			scope.mode = 'scale';
+			scope.space = 'local';
+
+		}*/
+
+		if ( event.keyCode == 187 || event.keyCode == 107 ) { // +,=,num+
+
+			scope.scale += 0.1
+
+		}
+
+		if ( event.keyCode == 189 || event.keyCode == 109) { // -,_,num-
+
+			scope.scale -= 0.1
+			scope.scale = Math.max( scope.scale, 0.1 );
+
+		}
+
+		if ( scope.mode !== currentMode || scope.space !== currentSpace || scope.scale !== currentScale ) {
+
+			scope.setMode( scope.mode );
+			scope.dispatchEvent( changeEvent );
+
+		}
+
+	}
+
+	function intersectObjects( event, objects ) {
+
+		pointerVector.set(
+			( event.layerX / scope.domElement.offsetWidth ) * 2 - 1,
+			- ( event.layerY / scope.domElement.offsetHeight ) * 2 + 1,
+			0.5
+		);
+
+		projector.unprojectVector( pointerVector, scope.camera );
+		ray.set( camPosition, pointerVector.sub( camPosition ).normalize() );
+
+		var intersections = ray.intersectObjects( objects, true );
+		return intersections[0] ? intersections[0] : false;
+
+	}
+
+	function isActive( name ) {
+
+		if ( scope.active.search( name ) != -1 ) return true;
+		else return false;
+
+	}
+
+	function bakeTransformations( object ) {
+
+		var tempGeometry = new THREE.Geometry();
+		THREE.GeometryUtils.merge( tempGeometry, object );
+		object.geometry = tempGeometry;
+		object.position.set( 0, 0, 0 );
+		object.rotation.set( 0, 0, 0 );
+		object.scale.set( 1, 1, 1 );
+
+	}
+
+};
+
+THREE.TransformControls.prototype = Object.create( THREE.EventDispatcher.prototype );
+
diff --git a/gz3d/client/js/include/angular.min.js b/gz3d/client/js/include/angular.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..4aa4bcdf9279b4eb33c9dc741fdbd1466dd9d148
--- /dev/null
+++ b/gz3d/client/js/include/angular.min.js
@@ -0,0 +1,220 @@
+/*
+ AngularJS v1.3.0-beta.7
+ (c) 2010-2014 Google, Inc. http://angularjs.org
+ License: MIT
+*/
+(function(N,S,s){'use strict';function I(b){return function(){var a=arguments[0],c,a="["+(b?b+":":"")+a+"] http://errors.angularjs.org/1.3.0-beta.7/"+(b?b+"/":"")+a;for(c=1;c<arguments.length;c++)a=a+(1==c?"?":"&")+"p"+(c-1)+"="+encodeURIComponent("function"==typeof arguments[c]?arguments[c].toString().replace(/ \{[\s\S]*$/,""):"undefined"==typeof arguments[c]?"undefined":"string"!=typeof arguments[c]?JSON.stringify(arguments[c]):arguments[c]);return Error(a)}}function cb(b){if(null==b||Ca(b))return!1;
+var a=b.length;return 1===b.nodeType&&a?!0:x(b)||M(b)||0===a||"number"===typeof a&&0<a&&a-1 in b}function q(b,a,c){var d;if(b)if(O(b))for(d in b)"prototype"==d||("length"==d||"name"==d||b.hasOwnProperty&&!b.hasOwnProperty(d))||a.call(c,b[d],d);else if(b.forEach&&b.forEach!==q)b.forEach(a,c);else if(cb(b))for(d=0;d<b.length;d++)a.call(c,b[d],d);else for(d in b)b.hasOwnProperty(d)&&a.call(c,b[d],d);return b}function Zb(b){var a=[],c;for(c in b)b.hasOwnProperty(c)&&a.push(c);return a.sort()}function dd(b,
+a,c){for(var d=Zb(b),e=0;e<d.length;e++)a.call(c,b[d[e]],d[e]);return d}function $b(b){return function(a,c){b(c,a)}}function db(){for(var b=ka.length,a;b;){b--;a=ka[b].charCodeAt(0);if(57==a)return ka[b]="A",ka.join("");if(90==a)ka[b]="0";else return ka[b]=String.fromCharCode(a+1),ka.join("")}ka.unshift("0");return ka.join("")}function ac(b,a){a?b.$$hashKey=a:delete b.$$hashKey}function w(b){var a=b.$$hashKey;q(arguments,function(a){a!==b&&q(a,function(a,c){b[c]=a})});ac(b,a);return b}function Q(b){return parseInt(b,
+10)}function bc(b,a){return w(new (w(function(){},{prototype:b})),a)}function D(){}function Da(b){return b}function $(b){return function(){return b}}function A(b){return"undefined"===typeof b}function F(b){return"undefined"!==typeof b}function R(b){return null!=b&&"object"===typeof b}function x(b){return"string"===typeof b}function Cb(b){return"number"===typeof b}function qa(b){return"[object Date]"===xa.call(b)}function M(b){return"[object Array]"===xa.call(b)}function O(b){return"function"===typeof b}
+function eb(b){return"[object RegExp]"===xa.call(b)}function Ca(b){return b&&b.document&&b.location&&b.alert&&b.setInterval}function ed(b){return!(!b||!(b.nodeName||b.prop&&b.attr&&b.find))}function fd(b,a,c){var d=[];q(b,function(b,f,g){d.push(a.call(c,b,f,g))});return d}function fb(b,a){if(b.indexOf)return b.indexOf(a);for(var c=0;c<b.length;c++)if(a===b[c])return c;return-1}function Ea(b,a){var c=fb(b,a);0<=c&&b.splice(c,1);return a}function aa(b,a){if(Ca(b)||b&&b.$evalAsync&&b.$watch)throw Oa("cpws");
+if(a){if(b===a)throw Oa("cpi");if(M(b))for(var c=a.length=0;c<b.length;c++)a.push(aa(b[c]));else{c=a.$$hashKey;q(a,function(b,c){delete a[c]});for(var d in b)a[d]=aa(b[d]);ac(a,c)}}else(a=b)&&(M(b)?a=aa(b,[]):qa(b)?a=new Date(b.getTime()):eb(b)?a=RegExp(b.source):R(b)&&(a=aa(b,{})));return a}function cc(b,a){a=a||{};for(var c in b)!b.hasOwnProperty(c)||"$"===c.charAt(0)&&"$"===c.charAt(1)||(a[c]=b[c]);return a}function ya(b,a){if(b===a)return!0;if(null===b||null===a)return!1;if(b!==b&&a!==a)return!0;
+var c=typeof b,d;if(c==typeof a&&"object"==c)if(M(b)){if(!M(a))return!1;if((c=b.length)==a.length){for(d=0;d<c;d++)if(!ya(b[d],a[d]))return!1;return!0}}else{if(qa(b))return qa(a)&&b.getTime()==a.getTime();if(eb(b)&&eb(a))return b.toString()==a.toString();if(b&&b.$evalAsync&&b.$watch||a&&a.$evalAsync&&a.$watch||Ca(b)||Ca(a)||M(a))return!1;c={};for(d in b)if("$"!==d.charAt(0)&&!O(b[d])){if(!ya(b[d],a[d]))return!1;c[d]=!0}for(d in a)if(!c.hasOwnProperty(d)&&"$"!==d.charAt(0)&&a[d]!==s&&!O(a[d]))return!1;
+return!0}return!1}function dc(){return S.securityPolicy&&S.securityPolicy.isActive||S.querySelector&&!(!S.querySelector("[ng-csp]")&&!S.querySelector("[data-ng-csp]"))}function gb(b,a){var c=2<arguments.length?ra.call(arguments,2):[];return!O(a)||a instanceof RegExp?a:c.length?function(){return arguments.length?a.apply(b,c.concat(ra.call(arguments,0))):a.apply(b,c)}:function(){return arguments.length?a.apply(b,arguments):a.call(b)}}function gd(b,a){var c=a;"string"===typeof b&&"$"===b.charAt(0)?c=
+s:Ca(a)?c="$WINDOW":a&&S===a?c="$DOCUMENT":a&&(a.$evalAsync&&a.$watch)&&(c="$SCOPE");return c}function sa(b,a){return"undefined"===typeof b?s:JSON.stringify(b,gd,a?"  ":null)}function ec(b){return x(b)?JSON.parse(b):b}function Pa(b){"function"===typeof b?b=!0:b&&0!==b.length?(b=u(""+b),b=!("f"==b||"0"==b||"false"==b||"no"==b||"n"==b||"[]"==b)):b=!1;return b}function ga(b){b=y(b).clone();try{b.empty()}catch(a){}var c=y("<div>").append(b).html();try{return 3===b[0].nodeType?u(c):c.match(/^(<[^>]+>)/)[1].replace(/^<([\w\-]+)/,
+function(a,b){return"<"+u(b)})}catch(d){return u(c)}}function fc(b){try{return decodeURIComponent(b)}catch(a){}}function gc(b){var a={},c,d;q((b||"").split("&"),function(b){b&&(c=b.split("="),d=fc(c[0]),F(d)&&(b=F(c[1])?fc(c[1]):!0,a[d]?M(a[d])?a[d].push(b):a[d]=[a[d],b]:a[d]=b))});return a}function Db(b){var a=[];q(b,function(b,d){M(b)?q(b,function(b){a.push(za(d,!0)+(!0===b?"":"="+za(b,!0)))}):a.push(za(d,!0)+(!0===b?"":"="+za(b,!0)))});return a.length?a.join("&"):""}function hb(b){return za(b,
+!0).replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+")}function za(b,a){return encodeURIComponent(b).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,a?"%20":"+")}function hd(b,a){var c,d,e=hc.length;b=y(b);for(d=0;d<e;++d)if(c=hc[d]+a,x(c=b.attr(c)))return c;return null}function id(b,a){function c(a){a&&d.push(a)}var d=[b],e,f,g={},h=["ng:app","ng-app","x-ng-app","data-ng-app"],m=/\sng[:\-]app(:\s*([\w\d_]+);?)?\s/;q(h,function(a){h[a]=
+!0;c(S.getElementById(a));a=a.replace(":","\\:");b.querySelectorAll&&(q(b.querySelectorAll("."+a),c),q(b.querySelectorAll("."+a+"\\:"),c),q(b.querySelectorAll("["+a+"]"),c))});q(d,function(a){if(!e){var b=m.exec(" "+a.className+" ");b?(e=a,f=(b[2]||"").replace(/\s+/g,",")):q(a.attributes,function(b){!e&&h[b.name]&&(e=a,f=b.value)})}});e&&(g.strictDi=null!==hd(e,"strict-di"),a(e,f?[f]:[],g))}function ic(b,a,c){R(c)||(c={});c=w({strictDi:!1},c);var d=function(){b=y(b);if(b.injector()){var d=b[0]===
+S?"document":ga(b);throw Oa("btstrpd",d);}a=a||[];a.unshift(["$provide",function(a){a.value("$rootElement",b)}]);a.unshift("ng");d=Eb(a,c.strictDi);d.invoke(["$rootScope","$rootElement","$compile","$injector","$animate",function(a,b,c,d,e){a.$apply(function(){b.data("$injector",d);c(b)(a)})}]);return d},e=/^NG_DEFER_BOOTSTRAP!/;if(N&&!e.test(N.name))return d();N.name=N.name.replace(e,"");Qa.resumeBootstrap=function(b){q(b,function(b){a.push(b)});d()}}function ib(b,a){a=a||"_";return b.replace(jd,
+function(b,d){return(d?a:"")+b.toLowerCase()})}function Fb(b,a,c){if(!b)throw Oa("areq",a||"?",c||"required");return b}function Ra(b,a,c){c&&M(b)&&(b=b[b.length-1]);Fb(O(b),a,"not a function, got "+(b&&"object"==typeof b?b.constructor.name||"Object":typeof b));return b}function Aa(b,a){if("hasOwnProperty"===b)throw Oa("badname",a);}function jc(b,a,c){if(!a)return b;a=a.split(".");for(var d,e=b,f=a.length,g=0;g<f;g++)d=a[g],b&&(b=(e=b)[d]);return!c&&O(b)?gb(e,b):b}function Gb(b){var a=b[0];b=b[b.length-
+1];if(a===b)return y(a);var c=[a];do{a=a.nextSibling;if(!a)break;c.push(a)}while(a!==b);return y(c)}function kd(b){var a=I("$injector"),c=I("ng");b=b.angular||(b.angular={});b.$$minErr=b.$$minErr||I;return b.module||(b.module=function(){var b={};return function(e,f,g){if("hasOwnProperty"===e)throw c("badname","module");f&&b.hasOwnProperty(e)&&(b[e]=null);return b[e]||(b[e]=function(){function b(a,d,e){return function(){c[e||"push"]([a,d,arguments]);return p}}if(!f)throw a("nomod",e);var c=[],d=[],
+l=b("$injector","invoke"),p={_invokeQueue:c,_runBlocks:d,requires:f,name:e,provider:b("$provide","provider"),factory:b("$provide","factory"),service:b("$provide","service"),value:b("$provide","value"),constant:b("$provide","constant","unshift"),animation:b("$animateProvider","register"),filter:b("$filterProvider","register"),controller:b("$controllerProvider","register"),directive:b("$compileProvider","directive"),config:l,run:function(a){d.push(a);return this}};g&&l(g);return p}())}}())}function ld(b){w(b,
+{bootstrap:ic,copy:aa,extend:w,equals:ya,element:y,forEach:q,injector:Eb,noop:D,bind:gb,toJson:sa,fromJson:ec,identity:Da,isUndefined:A,isDefined:F,isString:x,isFunction:O,isObject:R,isNumber:Cb,isElement:ed,isArray:M,version:md,isDate:qa,lowercase:u,uppercase:Fa,callbacks:{counter:0},$$minErr:I,$$csp:dc});Sa=kd(N);try{Sa("ngLocale")}catch(a){Sa("ngLocale",[]).provider("$locale",nd)}Sa("ng",["ngLocale"],["$provide",function(a){a.provider({$$sanitizeUri:od});a.provider("$compile",kc).directive({a:pd,
+input:lc,textarea:lc,form:qd,script:rd,select:sd,style:td,option:ud,ngBind:vd,ngBindHtml:wd,ngBindTemplate:xd,ngClass:yd,ngClassEven:zd,ngClassOdd:Ad,ngCloak:Bd,ngController:Cd,ngForm:Dd,ngHide:Ed,ngIf:Fd,ngInclude:Gd,ngInit:Hd,ngNonBindable:Id,ngPluralize:Jd,ngRepeat:Kd,ngShow:Ld,ngStyle:Md,ngSwitch:Nd,ngSwitchWhen:Od,ngSwitchDefault:Pd,ngOptions:Qd,ngTransclude:Rd,ngModel:Sd,ngList:Td,ngChange:Ud,required:mc,ngRequired:mc,ngValue:Vd,ngModelOptions:Wd}).directive({ngInclude:Xd}).directive(Hb).directive(nc);
+a.provider({$anchorScroll:Yd,$animate:Zd,$browser:$d,$cacheFactory:ae,$controller:be,$document:ce,$exceptionHandler:de,$filter:oc,$interpolate:ee,$interval:fe,$http:ge,$httpBackend:he,$location:ie,$log:je,$parse:ke,$rootScope:le,$q:me,$sce:ne,$sceDelegate:oe,$sniffer:pe,$templateCache:qe,$timeout:re,$window:se,$$rAF:te,$$asyncCallback:ue})}])}function Ta(b){return b.replace(ve,function(a,b,d,e){return e?d.toUpperCase():d}).replace(we,"Moz$1")}function Ib(b,a,c,d){function e(b){var e=c&&b?[this.filter(b)]:
+[this],m=a,k,l,p,n,r,t;if(!d||null!=b)for(;e.length;)for(k=e.shift(),l=0,p=k.length;l<p;l++)for(n=y(k[l]),m?n.triggerHandler("$destroy"):m=!m,r=0,n=(t=n.children()).length;r<n;r++)e.push(Ga(t[r]));return f.apply(this,arguments)}var f=Ga.fn[b],f=f.$original||f;e.$original=f;Ga.fn[b]=e}function xe(b,a){var c,d,e=a.createDocumentFragment(),f=[];if(Jb.test(b)){c=c||e.appendChild(a.createElement("div"));d=(ye.exec(b)||["",""])[1].toLowerCase();d=da[d]||da._default;c.innerHTML=d[1]+b.replace(ze,"<$1></$2>")+
+d[2];for(d=d[0];d--;)c=c.lastChild;f=f.concat(ra.call(c.childNodes,void 0));c=e.firstChild;c.textContent=""}else f.push(a.createTextNode(b));e.textContent="";e.innerHTML="";q(f,function(a){e.appendChild(a)});return e}function U(b){if(b instanceof U)return b;x(b)&&(b=Y(b));if(!(this instanceof U)){if(x(b)&&"<"!=b.charAt(0))throw Kb("nosel");return new U(b)}if(x(b)){var a;a=S;var c;b=(c=Ae.exec(b))?[a.createElement(c[1])]:(c=xe(b,a))?c.childNodes:[]}pc(this,b)}function Lb(b){return b.cloneNode(!0)}
+function Ha(b){qc(b);var a=0;for(b=b.childNodes||[];a<b.length;a++)Ha(b[a])}function rc(b,a,c,d){if(F(d))throw Kb("offargs");var e=la(b,"events");la(b,"handle")&&(A(a)?q(e,function(a,c){Ua(b,c,a);delete e[c]}):q(a.split(" "),function(a){A(c)?(Ua(b,a,e[a]),delete e[a]):Ea(e[a]||[],c)}))}function qc(b,a){var c=b[jb],d=Va[c];d&&(a?delete Va[c].data[a]:(d.handle&&(d.events.$destroy&&d.handle({},"$destroy"),rc(b)),delete Va[c],b[jb]=s))}function la(b,a,c){var d=b[jb],d=Va[d||-1];if(F(c))d||(b[jb]=d=++Be,
+d=Va[d]={}),d[a]=c;else return d&&d[a]}function sc(b,a,c){var d=la(b,"data"),e=F(c),f=!e&&F(a),g=f&&!R(a);d||g||la(b,"data",d={});if(e)d[a]=c;else if(f){if(g)return d&&d[a];w(d,a)}else return d}function Mb(b,a){return b.getAttribute?-1<(" "+(b.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ").indexOf(" "+a+" "):!1}function kb(b,a){a&&b.setAttribute&&q(a.split(" "),function(a){b.setAttribute("class",Y((" "+(b.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ").replace(" "+Y(a)+" "," ")))})}
+function lb(b,a){if(a&&b.setAttribute){var c=(" "+(b.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ");q(a.split(" "),function(a){a=Y(a);-1===c.indexOf(" "+a+" ")&&(c+=a+" ")});b.setAttribute("class",Y(c))}}function pc(b,a){if(a){a=a.nodeName||!F(a.length)||Ca(a)?[a]:a;for(var c=0;c<a.length;c++)b.push(a[c])}}function tc(b,a){return mb(b,"$"+(a||"ngController")+"Controller")}function mb(b,a,c){b=y(b);9==b[0].nodeType&&(b=b.find("html"));for(a=M(a)?a:[a];b.length;){for(var d=b[0],e=0,f=a.length;e<
+f;e++)if((c=b.data(a[e]))!==s)return c;b=y(d.parentNode||11===d.nodeType&&d.host)}}function uc(b){for(var a=0,c=b.childNodes;a<c.length;a++)Ha(c[a]);for(;b.firstChild;)b.removeChild(b.firstChild)}function vc(b,a){var c=nb[a.toLowerCase()];return c&&wc[b.nodeName]&&c}function Ce(b,a){var c=function(c,e){c.preventDefault||(c.preventDefault=function(){c.returnValue=!1});c.stopPropagation||(c.stopPropagation=function(){c.cancelBubble=!0});c.target||(c.target=c.srcElement||S);if(A(c.defaultPrevented)){var f=
+c.preventDefault;c.preventDefault=function(){c.defaultPrevented=!0;f.call(c)};c.defaultPrevented=!1}c.isDefaultPrevented=function(){return c.defaultPrevented||!1===c.returnValue};var g=cc(a[e||c.type]||[]);q(g,function(a){a.call(b,c)});8>=P?(c.preventDefault=null,c.stopPropagation=null,c.isDefaultPrevented=null):(delete c.preventDefault,delete c.stopPropagation,delete c.isDefaultPrevented)};c.elem=b;return c}function Ia(b){var a=typeof b,c;"object"==a&&null!==b?"function"==typeof(c=b.$$hashKey)?c=
+b.$$hashKey():c===s&&(c=b.$$hashKey=db()):c=b;return a+":"+c}function Wa(b){q(b,this.put,this)}function De(b){return(b=b.toString().replace(xc,"").match(yc))?"function("+(b[1]||"").replace(/[\s\r\n]+/," ")+")":"fn"}function Nb(b,a,c){var d;if("function"==typeof b){if(!(d=b.$inject)){d=[];if(b.length){if(a)throw x(c)&&c||(c=b.name||De(b)),Ja("strictdi",c);a=b.toString().replace(xc,"");a=a.match(yc);q(a[1].split(Ee),function(a){a.replace(Fe,function(a,b,c){d.push(c)})})}b.$inject=d}}else M(b)?(a=b.length-
+1,Ra(b[a],"fn"),d=b.slice(0,a)):Ra(b,"fn",!0);return d}function Eb(b,a){function c(a){return function(b,c){if(R(b))q(b,$b(a));else return a(b,c)}}function d(a,b){Aa(a,"service");if(O(b)||M(b))b=n.instantiate(b);if(!b.$get)throw Ja("pget",a);return p[a+m]=b}function e(a,b){return d(a,{$get:b})}function f(a){var b=[],c,d,e,h;q(a,function(a){if(!l.get(a)){l.put(a,!0);try{if(x(a))for(c=Sa(a),b=b.concat(f(c.requires)).concat(c._runBlocks),d=c._invokeQueue,e=0,h=d.length;e<h;e++){var g=d[e],m=n.get(g[0]);
+m[g[1]].apply(m,g[2])}else O(a)?b.push(n.invoke(a)):M(a)?b.push(n.invoke(a)):Ra(a,"module")}catch(k){throw M(a)&&(a=a[a.length-1]),k.message&&(k.stack&&-1==k.stack.indexOf(k.message))&&(k=k.message+"\n"+k.stack),Ja("modulerr",a,k.stack||k.message||k);}}});return b}function g(b,c){function d(a){if(b.hasOwnProperty(a)){if(b[a]===h)throw Ja("cdep",k.join(" <- "));return b[a]}try{return k.unshift(a),b[a]=h,b[a]=c(a)}catch(e){throw b[a]===h&&delete b[a],e;}finally{k.shift()}}function e(b,c,f,h){"string"===
+typeof f&&(h=f,f=null);var g=[];h=Nb(b,a,h);var m,k,l;k=0;for(m=h.length;k<m;k++){l=h[k];if("string"!==typeof l)throw Ja("itkn",l);g.push(f&&f.hasOwnProperty(l)?f[l]:d(l))}b.$inject||(b=b[m]);return b.apply(c,g)}return{invoke:e,instantiate:function(a,b,c){var d=function(){};d.prototype=(M(a)?a[a.length-1]:a).prototype;d=new d;a=e(a,d,b,c);return R(a)||O(a)?a:d},get:d,annotate:Nb,has:function(a){return p.hasOwnProperty(a+m)||b.hasOwnProperty(a)}}}a=!0===a;var h={},m="Provider",k=[],l=new Wa,p={$provide:{provider:c(d),
+factory:c(e),service:c(function(a,b){return e(a,["$injector",function(a){return a.instantiate(b)}])}),value:c(function(a,b){return e(a,$(b))}),constant:c(function(a,b){Aa(a,"constant");p[a]=b;r[a]=b}),decorator:function(a,b){var c=n.get(a+m),d=c.$get;c.$get=function(){var a=t.invoke(d,c);return t.invoke(b,null,{$delegate:a})}}}},n=p.$injector=g(p,function(){throw Ja("unpr",k.join(" <- "));},a),r={},t=r.$injector=g(r,function(a){var b=n.get(a+m);return t.invoke(b.$get,b,s,a)},a);q(f(b),function(a){t.invoke(a||
+D)});return t}function Yd(){var b=!0;this.disableAutoScrolling=function(){b=!1};this.$get=["$window","$location","$rootScope",function(a,c,d){function e(a){var b=null;q(a,function(a){b||"a"!==u(a.nodeName)||(b=a)});return b}function f(){var b=c.hash(),d;b?(d=g.getElementById(b))?d.scrollIntoView():(d=e(g.getElementsByName(b)))?d.scrollIntoView():"top"===b&&a.scrollTo(0,0):a.scrollTo(0,0)}var g=a.document;b&&d.$watch(function(){return c.hash()},function(){d.$evalAsync(f)});return f}]}function ue(){this.$get=
+["$$rAF","$timeout",function(b,a){return b.supported?function(a){return b(a)}:function(b){return a(b,0,!1)}}]}function Ge(b,a,c,d){function e(a){try{a.apply(null,ra.call(arguments,1))}finally{if(t--,0===t)for(;J.length;)try{J.pop()()}catch(b){c.error(b)}}}function f(a,b){(function ba(){q(E,function(a){a()});B=b(ba,a)})()}function g(){v=null;z!=h.url()&&(z=h.url(),q(ma,function(a){a(h.url())}))}var h=this,m=a[0],k=b.location,l=b.history,p=b.setTimeout,n=b.clearTimeout,r={};h.isMock=!1;var t=0,J=[];
+h.$$completeOutstandingRequest=e;h.$$incOutstandingRequestCount=function(){t++};h.notifyWhenNoOutstandingRequests=function(a){q(E,function(a){a()});0===t?a():J.push(a)};var E=[],B;h.addPollFn=function(a){A(B)&&f(100,p);E.push(a);return a};var z=k.href,W=a.find("base"),v=null;h.url=function(a,c){k!==b.location&&(k=b.location);l!==b.history&&(l=b.history);if(a){if(z!=a)return z=a,d.history?c?l.replaceState(null,"",a):(l.pushState(null,"",a),W.attr("href",W.attr("href"))):(v=a,c?k.replace(a):k.href=
+a),h}else return v||k.href.replace(/%27/g,"'")};var ma=[],K=!1;h.onUrlChange=function(a){if(!K){if(d.history)y(b).on("popstate",g);if(d.hashchange)y(b).on("hashchange",g);else h.addPollFn(g);K=!0}ma.push(a);return a};h.baseHref=function(){var a=W.attr("href");return a?a.replace(/^(https?\:)?\/\/[^\/]*/,""):""};var L={},Z="",G=h.baseHref();h.cookies=function(a,b){var d,e,f,h;if(a)b===s?m.cookie=escape(a)+"=;path="+G+";expires=Thu, 01 Jan 1970 00:00:00 GMT":x(b)&&(d=(m.cookie=escape(a)+"="+escape(b)+
+";path="+G).length+1,4096<d&&c.warn("Cookie '"+a+"' possibly not set or overflowed because it was too large ("+d+" > 4096 bytes)!"));else{if(m.cookie!==Z)for(Z=m.cookie,d=Z.split("; "),L={},f=0;f<d.length;f++)e=d[f],h=e.indexOf("="),0<h&&(a=unescape(e.substring(0,h)),L[a]===s&&(L[a]=unescape(e.substring(h+1))));return L}};h.defer=function(a,b){var c;t++;c=p(function(){delete r[c];e(a)},b||0);r[c]=!0;return c};h.defer.cancel=function(a){return r[a]?(delete r[a],n(a),e(D),!0):!1}}function $d(){this.$get=
+["$window","$log","$sniffer","$document",function(b,a,c,d){return new Ge(b,d,a,c)}]}function ae(){this.$get=function(){function b(b,d){function e(a){a!=p&&(n?n==a&&(n=a.n):n=a,f(a.n,a.p),f(a,p),p=a,p.n=null)}function f(a,b){a!=b&&(a&&(a.p=b),b&&(b.n=a))}if(b in a)throw I("$cacheFactory")("iid",b);var g=0,h=w({},d,{id:b}),m={},k=d&&d.capacity||Number.MAX_VALUE,l={},p=null,n=null;return a[b]={put:function(a,b){if(k<Number.MAX_VALUE){var c=l[a]||(l[a]={key:a});e(c)}if(!A(b))return a in m||g++,m[a]=b,
+g>k&&this.remove(n.key),b},get:function(a){if(k<Number.MAX_VALUE){var b=l[a];if(!b)return;e(b)}return m[a]},remove:function(a){if(k<Number.MAX_VALUE){var b=l[a];if(!b)return;b==p&&(p=b.p);b==n&&(n=b.n);f(b.n,b.p);delete l[a]}delete m[a];g--},removeAll:function(){m={};g=0;l={};p=n=null},destroy:function(){l=h=m=null;delete a[b]},info:function(){return w({},h,{size:g})}}}var a={};b.info=function(){var b={};q(a,function(a,e){b[e]=a.info()});return b};b.get=function(b){return a[b]};return b}}function qe(){this.$get=
+["$cacheFactory",function(b){return b("templates")}]}function kc(b,a){var c={},d="Directive",e=/^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,f=/(([\d\w\-_]+)(?:\:([^;]+))?;?)/,g=/^(on[a-z]+|formaction)$/;this.directive=function m(a,e){Aa(a,"directive");x(a)?(Fb(e,"directiveFactory"),c.hasOwnProperty(a)||(c[a]=[],b.factory(a+d,["$injector","$exceptionHandler",function(b,d){var e=[];q(c[a],function(c,f){try{var g=b.invoke(c);O(g)?g={compile:$(g)}:!g.compile&&g.link&&(g.compile=$(g.link));g.priority=g.priority||
+0;g.index=f;g.name=g.name||a;g.require=g.require||g.controller&&g.name;g.restrict=g.restrict||"A";e.push(g)}catch(m){d(m)}});return e}])),c[a].push(e)):q(a,$b(m));return this};this.aHrefSanitizationWhitelist=function(b){return F(b)?(a.aHrefSanitizationWhitelist(b),this):a.aHrefSanitizationWhitelist()};this.imgSrcSanitizationWhitelist=function(b){return F(b)?(a.imgSrcSanitizationWhitelist(b),this):a.imgSrcSanitizationWhitelist()};this.$get=["$injector","$interpolate","$exceptionHandler","$http","$templateCache",
+"$parse","$controller","$rootScope","$document","$sce","$animate","$$sanitizeUri",function(a,b,l,p,n,r,t,J,E,B,z,W){function v(a,b,c,d,e){a instanceof y||(a=y(a));q(a,function(b,c){3==b.nodeType&&b.nodeValue.match(/\S+/)&&(a[c]=y(b).wrap("<span></span>").parent()[0])});var f=K(a,b,a,c,d,e);ma(a,"ng-scope");return function(b,c,d){Fb(b,"scope");var e=c?Ka.clone.call(a):a;q(d,function(a,b){e.data("$"+b+"Controller",a)});d=0;for(var g=e.length;d<g;d++){var m=e[d].nodeType;1!==m&&9!==m||e.eq(d).data("$scope",
+b)}c&&c(e,b);f&&f(b,e,e);return e}}function ma(a,b){try{a.addClass(b)}catch(c){}}function K(a,b,c,d,e,f){function g(a,c,d,e){var f,k,l,r,p,n,E;f=c.length;var Xa=Array(f);for(p=0;p<f;p++)Xa[p]=c[p];E=p=0;for(n=m.length;p<n;E++)k=Xa[E],c=m[p++],f=m[p++],l=y(k),c?(c.scope?(r=a.$new(),l.data("$scope",r)):r=a,(l=c.transclude)||!e&&b?c(f,r,k,d,L(a,l||b)):c(f,r,k,d,e)):f&&f(a,k.childNodes,s,e)}for(var m=[],k,l,r,p,n=0;n<a.length;n++)k=new Ob,l=Z(a[n],[],k,0===n?d:s,e),(f=l.length?ha(l,a[n],k,b,c,null,[],
+[],f):null)&&f.scope&&ma(y(a[n]),"ng-scope"),k=f&&f.terminal||!(r=a[n].childNodes)||!r.length?null:K(r,f?f.transclude:b),m.push(f,k),p=p||f||k,f=null;return p?g:null}function L(a,b){return function(c,d,e){var f=!1;c||(c=a.$new(),f=c.$$transcluded=!0);d=b(c,d,e);if(f)d.on("$destroy",gb(c,c.$destroy));return d}}function Z(a,b,c,d,g){var m=c.$attr,k;switch(a.nodeType){case 1:ba(b,na(La(a).toLowerCase()),"E",d,g);var l,p,r;k=a.attributes;for(var n=0,E=k&&k.length;n<E;n++){var t=!1,B=!1;l=k[n];if(!P||
+8<=P||l.specified){p=l.name;r=na(p);Q.test(r)&&(p=ib(r.substr(6),"-"));var J=r.replace(/(Start|End)$/,"");r===J+"Start"&&(t=p,B=p.substr(0,p.length-5)+"end",p=p.substr(0,p.length-6));r=na(p.toLowerCase());m[r]=p;c[r]=l=Y(l.value);vc(a,r)&&(c[r]=!0);N(a,b,l,r);ba(b,r,"A",d,g,t,B)}}a=a.className;if(x(a)&&""!==a)for(;k=f.exec(a);)r=na(k[2]),ba(b,r,"C",d,g)&&(c[r]=Y(k[3])),a=a.substr(k.index+k[0].length);break;case 3:u(b,a.nodeValue);break;case 8:try{if(k=e.exec(a.nodeValue))r=na(k[1]),ba(b,r,"M",d,g)&&
+(c[r]=Y(k[2]))}catch(z){}}b.sort(qb);return b}function G(a,b,c){var d=[],e=0;if(b&&a.hasAttribute&&a.hasAttribute(b)){do{if(!a)throw ia("uterdir",b,c);1==a.nodeType&&(a.hasAttribute(b)&&e++,a.hasAttribute(c)&&e--);d.push(a);a=a.nextSibling}while(0<e)}else d.push(a);return y(d)}function C(a,b,c){return function(d,e,f,g,k){e=G(e[0],b,c);return a(d,e,f,g,k)}}function ha(a,c,d,e,f,g,m,p,n){function E(a,b,c,d){if(a){c&&(a=C(a,c,d));a.require=H.require;a.directiveName=ca;if(L===H||H.$$isolateScope)a=zc(a,
+{isolateScope:!0});m.push(a)}if(b){c&&(b=C(b,c,d));b.require=H.require;b.directiveName=ca;if(L===H||H.$$isolateScope)b=zc(b,{isolateScope:!0});p.push(b)}}function B(a,b,c,d){var e,f="data",g=!1;if(x(b)){for(;"^"==(e=b.charAt(0))||"?"==e;)b=b.substr(1),"^"==e&&(f="inheritedData"),g=g||"?"==e;e=null;d&&"data"===f&&(e=d[b]);e=e||c[f]("$"+b+"Controller");if(!e&&!g)throw ia("ctreq",b,a);}else M(b)&&(e=[],q(b,function(b){e.push(B(a,b,c,d))}));return e}function J(a,e,f,g,n){function E(a,b){var c;2>arguments.length&&
+(b=a,a=s);w&&(c=Z);return n(a,b,c)}var z,v,Xa,C,K,G,Z={},pb;z=c===f?d:cc(d,new Ob(y(f),d.$attr));v=z.$$element;if(L){var ba=/^\s*([@=&])(\??)\s*(\w*)\s*$/;g=y(f);G=e.$new(!0);ha&&ha===L.$$originalDirective?g.data("$isolateScope",G):g.data("$isolateScopeNoTemplate",G);ma(g,"ng-isolate-scope");q(L.scope,function(a,c){var d=a.match(ba)||[],f=d[3]||c,g="?"==d[2],d=d[1],m,l,p,n;G.$$isolateBindings[c]=d+f;switch(d){case "@":z.$observe(f,function(a){G[c]=a});z.$$observers[f].$$scope=e;z[f]&&(G[c]=b(z[f])(e));
+break;case "=":if(g&&!z[f])break;l=r(z[f]);n=l.literal?ya:function(a,b){return a===b};p=l.assign||function(){m=G[c]=l(e);throw ia("nonassign",z[f],L.name);};m=G[c]=l(e);G.$watch(function(){var a=l(e);n(a,G[c])||(n(a,m)?p(e,a=G[c]):G[c]=a);return m=a},null,l.literal);break;case "&":l=r(z[f]);G[c]=function(a){return l(e,a)};break;default:throw ia("iscp",L.name,c,a);}})}pb=n&&E;W&&q(W,function(a){var b={$scope:a===L||a.$$isolateScope?G:e,$element:v,$attrs:z,$transclude:pb},c;K=a.controller;"@"==K&&(K=
+z[a.name]);c=t(K,b);Z[a.name]=c;w||v.data("$"+a.name+"Controller",c);a.controllerAs&&(b.$scope[a.controllerAs]=c)});g=0;for(Xa=m.length;g<Xa;g++)try{C=m[g],C(C.isolateScope?G:e,v,z,C.require&&B(C.directiveName,C.require,v,Z),pb)}catch(ob){l(ob,ga(v))}g=e;L&&(L.template||null===L.templateUrl)&&(g=G);a&&a(g,f.childNodes,s,n);for(g=p.length-1;0<=g;g--)try{C=p[g],C(C.isolateScope?G:e,v,z,C.require&&B(C.directiveName,C.require,v,Z),pb)}catch(H){l(H,ga(v))}}n=n||{};for(var z=-Number.MAX_VALUE,K,W=n.controllerDirectives,
+L=n.newIsolateScopeDirective,ha=n.templateDirective,ba=n.nonTlbTranscludeDirective,qb=!1,w=n.hasElementTranscludeDirective,u=d.$$element=y(c),H,ca,T,I=e,N,ja=0,P=a.length;ja<P;ja++){H=a[ja];var Q=H.$$start,V=H.$$end;Q&&(u=G(c,Q,V));T=s;if(z>H.priority)break;if(T=H.scope)K=K||H,H.templateUrl||(A("new/isolated scope",L,H,u),R(T)&&(L=H));ca=H.name;!H.templateUrl&&H.controller&&(T=H.controller,W=W||{},A("'"+ca+"' controller",W[ca],H,u),W[ca]=H);if(T=H.transclude)qb=!0,H.$$tlb||(A("transclusion",ba,H,
+u),ba=H),"element"==T?(w=!0,z=H.priority,T=G(c,Q,V),u=d.$$element=y(S.createComment(" "+ca+": "+d[ca]+" ")),c=u[0],rb(f,y(ra.call(T,0)),c),I=v(T,e,z,g&&g.name,{nonTlbTranscludeDirective:ba})):(T=y(Lb(c)).contents(),u.empty(),I=v(T,e));if(H.template)if(A("template",ha,H,u),ha=H,T=O(H.template)?H.template(u,d):H.template,T=U(T),H.replace){g=H;T=Jb.test(T)?y(Y(T)):[];c=T[0];if(1!=T.length||1!==c.nodeType)throw ia("tplrt",ca,"");rb(f,u,c);P={$attr:{}};T=Z(c,[],P);var X=a.splice(ja+1,a.length-(ja+1));
+L&&ob(T);a=a.concat(T).concat(X);F(d,P);P=a.length}else u.html(T);if(H.templateUrl)A("template",ha,H,u),ha=H,H.replace&&(g=H),J=D(a.splice(ja,a.length-ja),u,d,f,I,m,p,{controllerDirectives:W,newIsolateScopeDirective:L,templateDirective:ha,nonTlbTranscludeDirective:ba}),P=a.length;else if(H.compile)try{N=H.compile(u,d,I),O(N)?E(null,N,Q,V):N&&E(N.pre,N.post,Q,V)}catch(He){l(He,ga(u))}H.terminal&&(J.terminal=!0,z=Math.max(z,H.priority))}J.scope=K&&!0===K.scope;J.transclude=qb&&I;n.hasElementTranscludeDirective=
+w;return J}function ob(a){for(var b=0,c=a.length;b<c;b++)a[b]=bc(a[b],{$$isolateScope:!0})}function ba(b,e,f,g,k,p,r){if(e===k)return null;k=null;if(c.hasOwnProperty(e)){var n;e=a.get(e+d);for(var E=0,t=e.length;E<t;E++)try{n=e[E],(g===s||g>n.priority)&&-1!=n.restrict.indexOf(f)&&(p&&(n=bc(n,{$$start:p,$$end:r})),b.push(n),k=n)}catch(z){l(z)}}return k}function F(a,b){var c=b.$attr,d=a.$attr,e=a.$$element;q(a,function(d,e){"$"!=e.charAt(0)&&(b[e]&&(d+=("style"===e?";":" ")+b[e]),a.$set(e,d,!0,c[e]))});
+q(b,function(b,f){"class"==f?(ma(e,b),a["class"]=(a["class"]?a["class"]+" ":"")+b):"style"==f?(e.attr("style",e.attr("style")+";"+b),a.style=(a.style?a.style+";":"")+b):"$"==f.charAt(0)||a.hasOwnProperty(f)||(a[f]=b,d[f]=c[f])})}function D(a,b,c,d,e,f,g,k){var m=[],l,r,E=b[0],t=a.shift(),z=w({},t,{templateUrl:null,transclude:null,replace:null,$$originalDirective:t}),J=O(t.templateUrl)?t.templateUrl(b,c):t.templateUrl;b.empty();p.get(B.getTrustedResourceUrl(J),{cache:n}).success(function(p){var n,
+B;p=U(p);if(t.replace){p=Jb.test(p)?y(Y(p)):[];n=p[0];if(1!=p.length||1!==n.nodeType)throw ia("tplrt",t.name,J);p={$attr:{}};rb(d,b,n);var v=Z(n,[],p);R(t.scope)&&ob(v);a=v.concat(a);F(c,p)}else n=E,b.html(p);a.unshift(z);l=ha(a,n,c,e,b,t,f,g,k);q(d,function(a,c){a==n&&(d[c]=b[0])});for(r=K(b[0].childNodes,e);m.length;){p=m.shift();B=m.shift();var C=m.shift(),G=m.shift(),v=b[0];if(B!==E){var W=B.className;k.hasElementTranscludeDirective&&t.replace||(v=Lb(n));rb(C,y(B),v);ma(y(v),W)}B=l.transclude?
+L(p,l.transclude):G;l(r,p,v,d,B)}m=null}).error(function(a,b,c,d){throw ia("tpload",d.url);});return function(a,b,c,d,e){m?(m.push(b),m.push(c),m.push(d),m.push(e)):l(r,b,c,d,e)}}function qb(a,b){var c=b.priority-a.priority;return 0!==c?c:a.name!==b.name?a.name<b.name?-1:1:a.index-b.index}function A(a,b,c,d){if(b)throw ia("multidir",b.name,c.name,a,ga(d));}function u(a,c){var d=b(c,!0);d&&a.push({priority:0,compile:$(function(a,b){var c=b.parent(),e=c.data("$binding")||[];e.push(d);ma(c.data("$binding",
+e),"ng-binding");a.$watch(d,function(a){b[0].nodeValue=a})})})}function I(a,b){if("srcdoc"==b)return B.HTML;var c=La(a);if("xlinkHref"==b||"FORM"==c&&"action"==b||"IMG"!=c&&("src"==b||"ngSrc"==b))return B.RESOURCE_URL}function N(a,c,d,e){var f=b(d,!0);if(f){if("multiple"===e&&"SELECT"===La(a))throw ia("selmulti",ga(a));c.push({priority:100,compile:function(){return{pre:function(c,d,m){d=m.$$observers||(m.$$observers={});if(g.test(e))throw ia("nodomevents");if(f=b(m[e],!0,I(a,e)))m[e]=f(c),(d[e]||
+(d[e]=[])).$$inter=!0,(m.$$observers&&m.$$observers[e].$$scope||c).$watch(f,function(a,b){"class"===e&&a!=b?m.$updateClass(a,b):m.$set(e,a)})}}}})}}function rb(a,b,c){var d=b[0],e=b.length,f=d.parentNode,g,m;if(a)for(g=0,m=a.length;g<m;g++)if(a[g]==d){a[g++]=c;m=g+e-1;for(var k=a.length;g<k;g++,m++)m<k?a[g]=a[m]:delete a[g];a.length-=e-1;break}f&&f.replaceChild(c,d);a=S.createDocumentFragment();a.appendChild(d);c[y.expando]=d[y.expando];d=1;for(e=b.length;d<e;d++)f=b[d],y(f).remove(),a.appendChild(f),
+delete b[d];b[0]=c;b.length=1}function zc(a,b){return w(function(){return a.apply(null,arguments)},a,b)}var Ob=function(a,b){this.$$element=a;this.$attr=b||{}};Ob.prototype={$normalize:na,$addClass:function(a){a&&0<a.length&&z.addClass(this.$$element,a)},$removeClass:function(a){a&&0<a.length&&z.removeClass(this.$$element,a)},$updateClass:function(a,b){var c=Ac(a,b),d=Ac(b,a);0===c.length?z.removeClass(this.$$element,d):0===d.length?z.addClass(this.$$element,c):z.setClass(this.$$element,c,d)},$set:function(a,
+b,c,d){var e=vc(this.$$element[0],a);e&&(this.$$element.prop(a,b),d=e);this[a]=b;d?this.$attr[a]=d:(d=this.$attr[a])||(this.$attr[a]=d=ib(a,"-"));e=La(this.$$element);if("A"===e&&"href"===a||"IMG"===e&&"src"===a)this[a]=b=W(b,"src"===a);!1!==c&&(null===b||b===s?this.$$element.removeAttr(d):this.$$element.attr(d,b));(c=this.$$observers)&&q(c[a],function(a){try{a(b)}catch(c){l(c)}})},$observe:function(a,b){var c=this,d=c.$$observers||(c.$$observers={}),e=d[a]||(d[a]=[]);e.push(b);J.$evalAsync(function(){e.$$inter||
+b(c[a])});return function(){Ea(e,b)}}};var ca=b.startSymbol(),ja=b.endSymbol(),U="{{"==ca||"}}"==ja?Da:function(a){return a.replace(/\{\{/g,ca).replace(/}}/g,ja)},Q=/^ngAttr[A-Z]/;return v}]}function na(b){return Ta(b.replace(Ie,""))}function Ac(b,a){var c="",d=b.split(/\s+/),e=a.split(/\s+/),f=0;a:for(;f<d.length;f++){for(var g=d[f],h=0;h<e.length;h++)if(g==e[h])continue a;c+=(0<c.length?" ":"")+g}return c}function be(){var b={},a=/^(\S+)(\s+as\s+(\w+))?$/;this.register=function(a,d){Aa(a,"controller");
+R(a)?w(b,a):b[a]=d};this.$get=["$injector","$window",function(c,d){return function(e,f){var g,h,m;x(e)&&(g=e.match(a),h=g[1],m=g[3],e=b.hasOwnProperty(h)?b[h]:jc(f.$scope,h,!0)||jc(d,h,!0),Ra(e,h,!0));g=c.instantiate(e,f,h);if(m){if(!f||"object"!=typeof f.$scope)throw I("$controller")("noscp",h||e.name,m);f.$scope[m]=g}return g}}]}function ce(){this.$get=["$window",function(b){return y(b.document)}]}function de(){this.$get=["$log",function(b){return function(a,c){b.error.apply(b,arguments)}}]}function Bc(b){var a=
+{},c,d,e;if(!b)return a;q(b.split("\n"),function(b){e=b.indexOf(":");c=u(Y(b.substr(0,e)));d=Y(b.substr(e+1));c&&(a[c]=a[c]?a[c]+(", "+d):d)});return a}function Cc(b){var a=R(b)?b:s;return function(c){a||(a=Bc(b));return c?a[u(c)]||null:a}}function Dc(b,a,c){if(O(c))return c(b,a);q(c,function(c){b=c(b,a)});return b}function ge(){var b=/^\s*(\[|\{[^\{])/,a=/[\}\]]\s*$/,c=/^\)\]\}',?\n/,d={"Content-Type":"application/json;charset=utf-8"},e=this.defaults={transformResponse:[function(d){x(d)&&(d=d.replace(c,
+""),b.test(d)&&a.test(d)&&(d=ec(d)));return d}],transformRequest:[function(a){return R(a)&&"[object File]"!==xa.call(a)&&"[object Blob]"!==xa.call(a)?sa(a):a}],headers:{common:{Accept:"application/json, text/plain, */*"},post:aa(d),put:aa(d),patch:aa(d)},xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN"},f=this.interceptors=[],g=this.responseInterceptors=[];this.$get=["$httpBackend","$browser","$cacheFactory","$rootScope","$q","$injector",function(a,b,c,d,p,n){function r(a){function c(a){var b=
+w({},a,{data:Dc(a.data,a.headers,d.transformResponse)});return 200<=a.status&&300>a.status?b:p.reject(b)}var d={method:"get",transformRequest:e.transformRequest,transformResponse:e.transformResponse},f=function(a){function b(a){var c;q(a,function(b,d){O(b)&&(c=b(),null!=c?a[d]=c:delete a[d])})}var c=e.headers,d=w({},a.headers),f,g,c=w({},c.common,c[u(a.method)]);b(c);b(d);a:for(f in c){a=u(f);for(g in d)if(u(g)===a)continue a;d[f]=c[f]}return d}(a);w(d,a);d.headers=f;d.method=Fa(d.method);(a=Pb(d.url)?
+b.cookies()[d.xsrfCookieName||e.xsrfCookieName]:s)&&(f[d.xsrfHeaderName||e.xsrfHeaderName]=a);var g=[function(a){f=a.headers;var b=Dc(a.data,Cc(f),a.transformRequest);A(a.data)&&q(f,function(a,b){"content-type"===u(b)&&delete f[b]});A(a.withCredentials)&&!A(e.withCredentials)&&(a.withCredentials=e.withCredentials);return t(a,b,f).then(c,c)},s],h=p.when(d);for(q(B,function(a){(a.request||a.requestError)&&g.unshift(a.request,a.requestError);(a.response||a.responseError)&&g.push(a.response,a.responseError)});g.length;){a=
+g.shift();var k=g.shift(),h=h.then(a,k)}h.success=function(a){h.then(function(b){a(b.data,b.status,b.headers,d)});return h};h.error=function(a){h.then(null,function(b){a(b.data,b.status,b.headers,d)});return h};return h}function t(b,c,f){function g(a,b,c,e){B&&(200<=a&&300>a?B.put(s,[a,b,Bc(c),e]):B.remove(s));m(b,a,c,e);d.$$phase||d.$apply()}function m(a,c,d,e){c=Math.max(c,0);(200<=c&&300>c?n.resolve:n.reject)({data:a,status:c,headers:Cc(d),config:b,statusText:e})}function k(){var a=fb(r.pendingRequests,
+b);-1!==a&&r.pendingRequests.splice(a,1)}var n=p.defer(),t=n.promise,B,q,s=J(b.url,b.params);r.pendingRequests.push(b);t.then(k,k);(b.cache||e.cache)&&(!1!==b.cache&&"GET"==b.method)&&(B=R(b.cache)?b.cache:R(e.cache)?e.cache:E);if(B)if(q=B.get(s),F(q)){if(q.then)return q.then(k,k),q;M(q)?m(q[1],q[0],aa(q[2]),q[3]):m(q,200,{},"OK")}else B.put(s,t);A(q)&&a(b.method,s,c,g,f,b.timeout,b.withCredentials,b.responseType);return t}function J(a,b){if(!b)return a;var c=[];dd(b,function(a,b){null===a||A(a)||
+(M(a)||(a=[a]),q(a,function(a){R(a)&&(a=sa(a));c.push(za(b)+"="+za(a))}))});0<c.length&&(a+=(-1==a.indexOf("?")?"?":"&")+c.join("&"));return a}var E=c("$http"),B=[];q(f,function(a){B.unshift(x(a)?n.get(a):n.invoke(a))});q(g,function(a,b){var c=x(a)?n.get(a):n.invoke(a);B.splice(b,0,{response:function(a){return c(p.when(a))},responseError:function(a){return c(p.reject(a))}})});r.pendingRequests=[];(function(a){q(arguments,function(a){r[a]=function(b,c){return r(w(c||{},{method:a,url:b}))}})})("get",
+"delete","head","jsonp");(function(a){q(arguments,function(a){r[a]=function(b,c,d){return r(w(d||{},{method:a,url:b,data:c}))}})})("post","put");r.defaults=e;return r}]}function Je(b){if(8>=P&&(!b.match(/^(get|post|head|put|delete|options)$/i)||!N.XMLHttpRequest))return new N.ActiveXObject("Microsoft.XMLHTTP");if(N.XMLHttpRequest)return new N.XMLHttpRequest;throw I("$httpBackend")("noxhr");}function he(){this.$get=["$browser","$window","$document",function(b,a,c){return Ke(b,Je,b.defer,a.angular.callbacks,
+c[0])}]}function Ke(b,a,c,d,e){function f(a,b,c){var f=e.createElement("script"),g=null;f.type="text/javascript";f.src=a;f.async=!0;g=function(a){Ua(f,"load",g);Ua(f,"error",g);e.body.removeChild(f);f=null;var h=-1,t="unknown";a&&("load"!==a.type||d[b].called||(a={type:"error"}),t=a.type,h="error"===a.type?404:200);c&&c(h,t)};sb(f,"load",g);sb(f,"error",g);e.body.appendChild(f);return g}var g=-1;return function(e,m,k,l,p,n,r,t){function J(){B=g;W&&W();v&&v.abort()}function E(a,d,e,f,g){K&&c.cancel(K);
+W=v=null;0===d&&(d=e?200:"file"==ta(m).protocol?404:0);a(1223===d?204:d,e,f,g||"");b.$$completeOutstandingRequest(D)}var B;b.$$incOutstandingRequestCount();m=m||b.url();if("jsonp"==u(e)){var z="_"+(d.counter++).toString(36);d[z]=function(a){d[z].data=a;d[z].called=!0};var W=f(m.replace("JSON_CALLBACK","angular.callbacks."+z),z,function(a,b){E(l,a,d[z].data,"",b);d[z]=D})}else{var v=a(e);v.open(e,m,!0);q(p,function(a,b){F(a)&&v.setRequestHeader(b,a)});v.onreadystatechange=function(){if(v&&4==v.readyState){var a=
+null,b=null;B!==g&&(a=v.getAllResponseHeaders(),b="response"in v?v.response:v.responseText);E(l,B||v.status,b,a,v.statusText||"")}};r&&(v.withCredentials=!0);if(t)try{v.responseType=t}catch(s){if("json"!==t)throw s;}v.send(k||null)}if(0<n)var K=c(J,n);else n&&n.then&&n.then(J)}}function ee(){var b="{{",a="}}";this.startSymbol=function(a){return a?(b=a,this):b};this.endSymbol=function(b){return b?(a=b,this):a};this.$get=["$parse","$exceptionHandler","$sce",function(c,d,e){function f(f,k,l){for(var p,
+n,r=0,t=[],J=[],E=[],B=f.length,z=!1,q=!1,v=[],y={},K={};r<B;)if(-1!=(p=f.indexOf(b,r))&&-1!=(n=f.indexOf(a,p+g)))r!==p&&(q=!0),t.push(f.substring(r,p)),r=f.substring(p+g,n),J.push(r),E.push(c(r)),r=n+h,z=!0;else{r!==B&&(q=!0,t.push(f.substring(r)));break}t.length===J.length&&t.push("");if(l&&z&&(q||1<J.length))throw Ec("noconcat",f);if(!k||z){v.length=t.length+J.length;var L=function(a){for(var b=0,c=J.length;b<c;b++)v[2*b]=t[b],v[2*b+1]=a[b];v[2*c]=t[c];return v.join("")},Z=function(a){a=l?e.getTrusted(l,
+a):e.valueOf(a);null===a||A(a)?a="":"string"!=typeof a&&(a=sa(a));return a};return w(function(a){var b=a.$id||"notAScope",c=y[b],e=K[b],g=0,h=J.length,k=Array(h),l,p=e===s?!0:!1;c||(c=[],p=!0,a.$on&&a.$on("$destroy",function(){y[b]=null;K[b]=null}));try{for(;g<h;g++)l=Z(E[g](a)),l!==c[g]&&(p=!0),k[g]=l;p&&(y[b]=k,K[b]=e=L(k))}catch(n){a=Ec("interr",f,n.toString()),d(a)}return e},{exp:f,separators:t,expressions:J})}}var g=b.length,h=a.length;f.startSymbol=function(){return b};f.endSymbol=function(){return a};
+return f}]}function fe(){this.$get=["$rootScope","$window","$q",function(b,a,c){function d(d,g,h,m){var k=a.setInterval,l=a.clearInterval,p=c.defer(),n=p.promise,r=0,t=F(m)&&!m;h=F(h)?h:0;n.then(null,null,d);n.$$intervalId=k(function(){p.notify(r++);0<h&&r>=h&&(p.resolve(r),l(n.$$intervalId),delete e[n.$$intervalId]);t||b.$apply()},g);e[n.$$intervalId]=p;return n}var e={};d.cancel=function(a){return a&&a.$$intervalId in e?(e[a.$$intervalId].reject("canceled"),clearInterval(a.$$intervalId),delete e[a.$$intervalId],
+!0):!1};return d}]}function nd(){this.$get=function(){return{id:"en-us",NUMBER_FORMATS:{DECIMAL_SEP:".",GROUP_SEP:",",PATTERNS:[{minInt:1,minFrac:0,maxFrac:3,posPre:"",posSuf:"",negPre:"-",negSuf:"",gSize:3,lgSize:3},{minInt:1,minFrac:2,maxFrac:2,posPre:"\u00a4",posSuf:"",negPre:"(\u00a4",negSuf:")",gSize:3,lgSize:3}],CURRENCY_SYM:"$"},DATETIME_FORMATS:{MONTH:"January February March April May June July August September October November December".split(" "),SHORTMONTH:"Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),
+DAY:"Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),SHORTDAY:"Sun Mon Tue Wed Thu Fri Sat".split(" "),AMPMS:["AM","PM"],medium:"MMM d, y h:mm:ss a","short":"M/d/yy h:mm a",fullDate:"EEEE, MMMM d, y",longDate:"MMMM d, y",mediumDate:"MMM d, y",shortDate:"M/d/yy",mediumTime:"h:mm:ss a",shortTime:"h:mm a"},pluralCat:function(b){return 1===b?"one":"other"}}}}function Qb(b){b=b.split("/");for(var a=b.length;a--;)b[a]=hb(b[a]);return b.join("/")}function Fc(b,a,c){b=ta(b,c);a.$$protocol=
+b.protocol;a.$$host=b.hostname;a.$$port=Q(b.port)||Le[b.protocol]||null}function Gc(b,a,c){var d="/"!==b.charAt(0);d&&(b="/"+b);b=ta(b,c);a.$$path=decodeURIComponent(d&&"/"===b.pathname.charAt(0)?b.pathname.substring(1):b.pathname);a.$$search=gc(b.search);a.$$hash=decodeURIComponent(b.hash);a.$$path&&"/"!=a.$$path.charAt(0)&&(a.$$path="/"+a.$$path)}function oa(b,a){if(0===a.indexOf(b))return a.substr(b.length)}function Ya(b){var a=b.indexOf("#");return-1==a?b:b.substr(0,a)}function Rb(b){return b.substr(0,
+Ya(b).lastIndexOf("/")+1)}function Hc(b,a){this.$$html5=!0;a=a||"";var c=Rb(b);Fc(b,this,b);this.$$parse=function(a){var e=oa(c,a);if(!x(e))throw Sb("ipthprfx",a,c);Gc(e,this,b);this.$$path||(this.$$path="/");this.$$compose()};this.$$compose=function(){var a=Db(this.$$search),b=this.$$hash?"#"+hb(this.$$hash):"";this.$$url=Qb(this.$$path)+(a?"?"+a:"")+b;this.$$absUrl=c+this.$$url.substr(1)};this.$$rewrite=function(d){var e;if((e=oa(b,d))!==s)return d=e,(e=oa(a,e))!==s?c+(oa("/",e)||e):b+d;if((e=oa(c,
+d))!==s)return c+e;if(c==d+"/")return c}}function Tb(b,a){var c=Rb(b);Fc(b,this,b);this.$$parse=function(d){var e=oa(b,d)||oa(c,d),e="#"==e.charAt(0)?oa(a,e):this.$$html5?e:"";if(!x(e))throw Sb("ihshprfx",d,a);Gc(e,this,b);d=this.$$path;var f=/^\/[A-Z]:(\/.*)/;0===e.indexOf(b)&&(e=e.replace(b,""));f.exec(e)||(d=(e=f.exec(d))?e[1]:d);this.$$path=d;this.$$compose()};this.$$compose=function(){var c=Db(this.$$search),e=this.$$hash?"#"+hb(this.$$hash):"";this.$$url=Qb(this.$$path)+(c?"?"+c:"")+e;this.$$absUrl=
+b+(this.$$url?a+this.$$url:"")};this.$$rewrite=function(a){if(Ya(b)==Ya(a))return a}}function Ub(b,a){this.$$html5=!0;Tb.apply(this,arguments);var c=Rb(b);this.$$rewrite=function(d){var e;if(b==Ya(d))return d;if(e=oa(c,d))return b+a+e;if(c===d+"/")return c};this.$$compose=function(){var c=Db(this.$$search),e=this.$$hash?"#"+hb(this.$$hash):"";this.$$url=Qb(this.$$path)+(c?"?"+c:"")+e;this.$$absUrl=b+a+this.$$url}}function tb(b){return function(){return this[b]}}function Ic(b,a){return function(c){if(A(c))return this[b];
+this[b]=a(c);this.$$compose();return this}}function ie(){var b="",a=!1;this.hashPrefix=function(a){return F(a)?(b=a,this):b};this.html5Mode=function(b){return F(b)?(a=b,this):a};this.$get=["$rootScope","$browser","$sniffer","$rootElement",function(c,d,e,f){function g(a){c.$broadcast("$locationChangeSuccess",h.absUrl(),a)}var h,m,k=d.baseHref(),l=d.url(),p;a?(p=l.substring(0,l.indexOf("/",l.indexOf("//")+2))+(k||"/"),m=e.history?Hc:Ub):(p=Ya(l),m=Tb);h=new m(p,"#"+b);h.$$parse(h.$$rewrite(l));f.on("click",
+function(a){if(!a.ctrlKey&&!a.metaKey&&2!=a.which){for(var e=y(a.target);"a"!==u(e[0].nodeName);)if(e[0]===f[0]||!(e=e.parent())[0])return;var g=e.prop("href");R(g)&&"[object SVGAnimatedString]"===g.toString()&&(g=ta(g.animVal).href);if(m===Ub){var k=e.attr("href")||e.attr("xlink:href");if(0>k.indexOf("://"))if(g="#"+b,"/"==k[0])g=p+g+k;else if("#"==k[0])g=p+g+(h.path()||"/")+k;else{for(var l=h.path().split("/"),k=k.split("/"),n=0;n<k.length;n++)"."!=k[n]&&(".."==k[n]?l.pop():k[n].length&&l.push(k[n]));
+g=p+g+l.join("/")}}l=h.$$rewrite(g);g&&(!e.attr("target")&&l&&!a.isDefaultPrevented())&&(a.preventDefault(),l!=d.url()&&(h.$$parse(l),c.$apply(),N.angular["ff-684208-preventDefault"]=!0))}});h.absUrl()!=l&&d.url(h.absUrl(),!0);d.onUrlChange(function(a){h.absUrl()!=a&&(c.$evalAsync(function(){var b=h.absUrl();h.$$parse(a);c.$broadcast("$locationChangeStart",a,b).defaultPrevented?(h.$$parse(b),d.url(b)):g(b)}),c.$$phase||c.$digest())});var n=0;c.$watch(function(){var a=d.url(),b=h.$$replace;n&&a==h.absUrl()||
+(n++,c.$evalAsync(function(){c.$broadcast("$locationChangeStart",h.absUrl(),a).defaultPrevented?h.$$parse(a):(d.url(h.absUrl(),b),g(a))}));h.$$replace=!1;return n});return h}]}function je(){var b=!0,a=this;this.debugEnabled=function(a){return F(a)?(b=a,this):b};this.$get=["$window",function(c){function d(a){a instanceof Error&&(a.stack?a=a.message&&-1===a.stack.indexOf(a.message)?"Error: "+a.message+"\n"+a.stack:a.stack:a.sourceURL&&(a=a.message+"\n"+a.sourceURL+":"+a.line));return a}function e(a){var b=
+c.console||{},e=b[a]||b.log||D;a=!1;try{a=!!e.apply}catch(m){}return a?function(){var a=[];q(arguments,function(b){a.push(d(b))});return e.apply(b,a)}:function(a,b){e(a,null==b?"":b)}}return{log:e("log"),info:e("info"),warn:e("warn"),error:e("error"),debug:function(){var c=e("debug");return function(){b&&c.apply(a,arguments)}}()}}]}function ea(b,a){if("constructor"===b)throw Ba("isecfld",a);return b}function Za(b,a){if(b){if(b.constructor===b)throw Ba("isecfn",a);if(b.document&&b.location&&b.alert&&
+b.setInterval)throw Ba("isecwindow",a);if(b.children&&(b.nodeName||b.prop&&b.attr&&b.find))throw Ba("isecdom",a);}return b}function ub(b,a,c,d,e){e=e||{};a=a.split(".");for(var f,g=0;1<a.length;g++){f=ea(a.shift(),d);var h=b[f];h||(h={},b[f]=h);b=h;b.then&&e.unwrapPromises&&(ua(d),"$$v"in b||function(a){a.then(function(b){a.$$v=b})}(b),b.$$v===s&&(b.$$v={}),b=b.$$v)}f=ea(a.shift(),d);return b[f]=c}function Jc(b,a,c,d,e,f,g){ea(b,f);ea(a,f);ea(c,f);ea(d,f);ea(e,f);return g.unwrapPromises?function(g,
+m){var k=m&&m.hasOwnProperty(b)?m:g,l;if(null==k)return k;(k=k[b])&&k.then&&(ua(f),"$$v"in k||(l=k,l.$$v=s,l.then(function(a){l.$$v=a})),k=k.$$v);if(!a)return k;if(null==k)return s;(k=k[a])&&k.then&&(ua(f),"$$v"in k||(l=k,l.$$v=s,l.then(function(a){l.$$v=a})),k=k.$$v);if(!c)return k;if(null==k)return s;(k=k[c])&&k.then&&(ua(f),"$$v"in k||(l=k,l.$$v=s,l.then(function(a){l.$$v=a})),k=k.$$v);if(!d)return k;if(null==k)return s;(k=k[d])&&k.then&&(ua(f),"$$v"in k||(l=k,l.$$v=s,l.then(function(a){l.$$v=
+a})),k=k.$$v);if(!e)return k;if(null==k)return s;(k=k[e])&&k.then&&(ua(f),"$$v"in k||(l=k,l.$$v=s,l.then(function(a){l.$$v=a})),k=k.$$v);return k}:function(f,g){var k=g&&g.hasOwnProperty(b)?g:f;if(null==k)return k;k=k[b];if(!a)return k;if(null==k)return s;k=k[a];if(!c)return k;if(null==k)return s;k=k[c];if(!d)return k;if(null==k)return s;k=k[d];return e?null==k?s:k=k[e]:k}}function Me(b,a){ea(b,a);return function(a,d){return null==a?s:(d&&d.hasOwnProperty(b)?d:a)[b]}}function Ne(b,a,c){ea(b,c);ea(a,
+c);return function(c,e){if(null==c)return s;c=(e&&e.hasOwnProperty(b)?e:c)[b];return null==c?s:c[a]}}function Kc(b,a,c){if(Vb.hasOwnProperty(b))return Vb[b];var d=b.split("."),e=d.length,f;if(a.unwrapPromises||1!==e)if(a.unwrapPromises||2!==e)if(a.csp)f=6>e?Jc(d[0],d[1],d[2],d[3],d[4],c,a):function(b,f){var g=0,h;do h=Jc(d[g++],d[g++],d[g++],d[g++],d[g++],c,a)(b,f),f=s,b=h;while(g<e);return h};else{var g="var p;\n";q(d,function(b,d){ea(b,c);g+="if(s == null) return undefined;\ns="+(d?"s":'((k&&k.hasOwnProperty("'+
+b+'"))?k:s)')+'["'+b+'"];\n'+(a.unwrapPromises?'if (s && s.then) {\n pw("'+c.replace(/(["\r\n])/g,"\\$1")+'");\n if (!("$$v" in s)) {\n p=s;\n p.$$v = undefined;\n p.then(function(v) {p.$$v=v;});\n}\n s=s.$$v\n}\n':"")});var g=g+"return s;",h=new Function("s","k","pw",g);h.toString=$(g);f=a.unwrapPromises?function(a,b){return h(a,b,ua)}:h}else f=Ne(d[0],d[1],c);else f=Me(d[0],c);"hasOwnProperty"!==b&&(Vb[b]=f);return f}function ke(){var b={},a={csp:!1,unwrapPromises:!1,logPromiseWarnings:!0};this.unwrapPromises=
+function(b){return F(b)?(a.unwrapPromises=!!b,this):a.unwrapPromises};this.logPromiseWarnings=function(b){return F(b)?(a.logPromiseWarnings=b,this):a.logPromiseWarnings};this.$get=["$filter","$sniffer","$log",function(c,d,e){a.csp=d.csp;ua=function(b){a.logPromiseWarnings&&!Lc.hasOwnProperty(b)&&(Lc[b]=!0,e.warn("[$parse] Promise found in the expression `"+b+"`. Automatic unwrapping of promises in Angular expressions is deprecated."))};return function(d){var e;switch(typeof d){case "string":if(b.hasOwnProperty(d))return b[d];
+e=new Wb(a);e=(new $a(e,c,a)).parse(d,!1);"hasOwnProperty"!==d&&(b[d]=e);return e;case "function":return d;default:return D}}}]}function me(){this.$get=["$rootScope","$exceptionHandler",function(b,a){return Oe(function(a){b.$evalAsync(a)},a)}]}function Oe(b,a){function c(a){return a}function d(a){return g(a)}var e=function(){var g=[],k,l;return l={resolve:function(a){if(g){var c=g;g=s;k=f(a);c.length&&b(function(){for(var a,b=0,d=c.length;b<d;b++)a=c[b],k.then(a[0],a[1],a[2])})}},reject:function(a){l.resolve(h(a))},
+notify:function(a){if(g){var c=g;g.length&&b(function(){for(var b,d=0,e=c.length;d<e;d++)b=c[d],b[2](a)})}},promise:{then:function(b,f,h){var l=e(),J=function(d){try{l.resolve((O(b)?b:c)(d))}catch(e){l.reject(e),a(e)}},E=function(b){try{l.resolve((O(f)?f:d)(b))}catch(c){l.reject(c),a(c)}},B=function(b){try{l.notify((O(h)?h:c)(b))}catch(d){a(d)}};g?g.push([J,E,B]):k.then(J,E,B);return l.promise},"catch":function(a){return this.then(null,a)},"finally":function(a){function b(a,c){var d=e();c?d.resolve(a):
+d.reject(a);return d.promise}function d(e,f){var g=null;try{g=(a||c)()}catch(h){return b(h,!1)}return g&&O(g.then)?g.then(function(){return b(e,f)},function(a){return b(a,!1)}):b(e,f)}return this.then(function(a){return d(a,!0)},function(a){return d(a,!1)})}}}},f=function(a){return a&&O(a.then)?a:{then:function(c){var d=e();b(function(){d.resolve(c(a))});return d.promise}}},g=function(a){var b=e();b.reject(a);return b.promise},h=function(c){return{then:function(f,g){var h=e();b(function(){try{h.resolve((O(g)?
+g:d)(c))}catch(b){h.reject(b),a(b)}});return h.promise}}};return{defer:e,reject:g,when:function(h,k,l,p){var n=e(),r,t=function(b){try{return(O(k)?k:c)(b)}catch(d){return a(d),g(d)}},J=function(b){try{return(O(l)?l:d)(b)}catch(c){return a(c),g(c)}},E=function(b){try{return(O(p)?p:c)(b)}catch(d){a(d)}};b(function(){f(h).then(function(a){r||(r=!0,n.resolve(f(a).then(t,J,E)))},function(a){r||(r=!0,n.resolve(J(a)))},function(a){r||n.notify(E(a))})});return n.promise},all:function(a){var b=e(),c=0,d=M(a)?
+[]:{};q(a,function(a,e){c++;f(a).then(function(a){d.hasOwnProperty(e)||(d[e]=a,--c||b.resolve(d))},function(a){d.hasOwnProperty(e)||b.reject(a)})});0===c&&b.resolve(d);return b.promise}}}function te(){this.$get=["$window","$timeout",function(b,a){var c=b.requestAnimationFrame||b.webkitRequestAnimationFrame||b.mozRequestAnimationFrame,d=b.cancelAnimationFrame||b.webkitCancelAnimationFrame||b.mozCancelAnimationFrame||b.webkitCancelRequestAnimationFrame,e=!!c,f=e?function(a){var b=c(a);return function(){d(b)}}:
+function(b){var c=a(b,16.66,!1);return function(){a.cancel(c)}};f.supported=e;return f}]}function le(){var b=10,a=I("$rootScope"),c=null;this.digestTtl=function(a){arguments.length&&(b=a);return b};this.$get=["$injector","$exceptionHandler","$parse","$browser",function(d,e,f,g){function h(){this.$id=db();this.$$phase=this.$parent=this.$$watchers=this.$$nextSibling=this.$$prevSibling=this.$$childHead=this.$$childTail=null;this["this"]=this.$root=this;this.$$destroyed=!1;this.$$asyncQueue=[];this.$$postDigestQueue=
+[];this.$$listeners={};this.$$listenerCount={};this.$$isolateBindings={}}function m(b){if(n.$$phase)throw a("inprog",n.$$phase);n.$$phase=b}function k(a,b){var c=f(a);Ra(c,b);return c}function l(a,b,c){do a.$$listenerCount[c]-=b,0===a.$$listenerCount[c]&&delete a.$$listenerCount[c];while(a=a.$parent)}function p(){}h.prototype={constructor:h,$new:function(a){a?(a=new h,a.$root=this.$root,a.$$asyncQueue=this.$$asyncQueue,a.$$postDigestQueue=this.$$postDigestQueue):(this.$$childScopeClass||(this.$$childScopeClass=
+function(){this.$$watchers=this.$$nextSibling=this.$$childHead=this.$$childTail=null;this.$$listeners={};this.$$listenerCount={};this.$id=db();this.$$childScopeClass=null},this.$$childScopeClass.prototype=this),a=new this.$$childScopeClass);a["this"]=a;a.$parent=this;a.$$prevSibling=this.$$childTail;this.$$childHead?this.$$childTail=this.$$childTail.$$nextSibling=a:this.$$childHead=this.$$childTail=a;return a},$watch:function(a,b,d){var e=k(a,"watch"),f=this.$$watchers,g={fn:b,last:p,get:e,exp:a,
+eq:!!d};c=null;if(!O(b)){var h=k(b||D,"listener");g.fn=function(a,b,c){h(c)}}if("string"==typeof a&&e.constant){var m=g.fn;g.fn=function(a,b,c){m.call(this,a,b,c);Ea(f,g)}}f||(f=this.$$watchers=[]);f.unshift(g);return function(){Ea(f,g);c=null}},$watchGroup:function(a,b){var c=Array(a.length),d=Array(a.length),e=[],f=0,g=this;q(a,function(a,b){e.push(g.$watch(a,function(a,e){d[b]=a;c[b]=e;f++}))},this);e.push(g.$watch(function(){return f},function(){b(d,c,g)}));return function(){q(e,function(a){a()})}},
+$watchCollection:function(a,b){var c=this,d,e,g,h=1<b.length,k=0,m=f(a),l=[],n={},p=!0,q=0;return this.$watch(function(){d=m(c);var a,b;if(R(d))if(cb(d))for(e!==l&&(e=l,q=e.length=0,k++),a=d.length,q!==a&&(k++,e.length=q=a),b=0;b<a;b++)e[b]!==e[b]&&d[b]!==d[b]||e[b]===d[b]||(k++,e[b]=d[b]);else{e!==n&&(e=n={},q=0,k++);a=0;for(b in d)d.hasOwnProperty(b)&&(a++,e.hasOwnProperty(b)?e[b]!==d[b]&&(k++,e[b]=d[b]):(q++,e[b]=d[b],k++));if(q>a)for(b in k++,e)e.hasOwnProperty(b)&&!d.hasOwnProperty(b)&&(q--,
+delete e[b])}else e!==d&&(e=d,k++);return k},function(){p?(p=!1,b(d,d,c)):b(d,g,c);if(h)if(R(d))if(cb(d)){g=Array(d.length);for(var a=0;a<d.length;a++)g[a]=d[a]}else for(a in g={},d)Mc.call(d,a)&&(g[a]=d[a]);else g=d})},$digest:function(){var d,f,g,h,k=this.$$asyncQueue,l=this.$$postDigestQueue,q,v,s=b,K,L=[],y,G,C;m("$digest");c=null;do{v=!1;for(K=this;k.length;){try{C=k.shift(),C.scope.$eval(C.expression)}catch(F){n.$$phase=null,e(F)}c=null}a:do{if(h=K.$$watchers)for(q=h.length;q--;)try{if(d=h[q])if((f=
+d.get(K))!==(g=d.last)&&!(d.eq?ya(f,g):"number"==typeof f&&"number"==typeof g&&isNaN(f)&&isNaN(g)))v=!0,c=d,d.last=d.eq?aa(f):f,d.fn(f,g===p?f:g,K),5>s&&(y=4-s,L[y]||(L[y]=[]),G=O(d.exp)?"fn: "+(d.exp.name||d.exp.toString()):d.exp,G+="; newVal: "+sa(f)+"; oldVal: "+sa(g),L[y].push(G));else if(d===c){v=!1;break a}}catch(u){n.$$phase=null,e(u)}if(!(h=K.$$childHead||K!==this&&K.$$nextSibling))for(;K!==this&&!(h=K.$$nextSibling);)K=K.$parent}while(K=h);if((v||k.length)&&!s--)throw n.$$phase=null,a("infdig",
+b,sa(L));}while(v||k.length);for(n.$$phase=null;l.length;)try{l.shift()()}catch(x){e(x)}},$destroy:function(){if(!this.$$destroyed){var a=this.$parent;this.$broadcast("$destroy");this.$$destroyed=!0;this!==n&&(q(this.$$listenerCount,gb(null,l,this)),a.$$childHead==this&&(a.$$childHead=this.$$nextSibling),a.$$childTail==this&&(a.$$childTail=this.$$prevSibling),this.$$prevSibling&&(this.$$prevSibling.$$nextSibling=this.$$nextSibling),this.$$nextSibling&&(this.$$nextSibling.$$prevSibling=this.$$prevSibling),
+this.$parent=this.$$nextSibling=this.$$prevSibling=this.$$childHead=this.$$childTail=this.$root=null,this.$$listeners={},this.$$watchers=this.$$asyncQueue=this.$$postDigestQueue=[],this.$destroy=this.$digest=this.$apply=D,this.$on=this.$watch=this.$watchGroup=function(){return D})}},$eval:function(a,b){return f(a)(this,b)},$evalAsync:function(a){n.$$phase||n.$$asyncQueue.length||g.defer(function(){n.$$asyncQueue.length&&n.$digest()});this.$$asyncQueue.push({scope:this,expression:a})},$$postDigest:function(a){this.$$postDigestQueue.push(a)},
+$apply:function(a){try{return m("$apply"),this.$eval(a)}catch(b){e(b)}finally{n.$$phase=null;try{n.$digest()}catch(c){throw e(c),c;}}},$on:function(a,b){var c=this.$$listeners[a];c||(this.$$listeners[a]=c=[]);c.push(b);var d=this;do d.$$listenerCount[a]||(d.$$listenerCount[a]=0),d.$$listenerCount[a]++;while(d=d.$parent);var e=this;return function(){c[fb(c,b)]=null;l(e,1,a)}},$emit:function(a,b){var c=[],d,f=this,g=!1,h={name:a,targetScope:f,stopPropagation:function(){g=!0},preventDefault:function(){h.defaultPrevented=
+!0},defaultPrevented:!1},k=[h].concat(ra.call(arguments,1)),m,l;do{d=f.$$listeners[a]||c;h.currentScope=f;m=0;for(l=d.length;m<l;m++)if(d[m])try{d[m].apply(null,k)}catch(n){e(n)}else d.splice(m,1),m--,l--;if(g)break;f=f.$parent}while(f);return h},$broadcast:function(a,b){for(var c=this,d=this,f={name:a,targetScope:this,preventDefault:function(){f.defaultPrevented=!0},defaultPrevented:!1},g=[f].concat(ra.call(arguments,1)),h,k;c=d;){f.currentScope=c;d=c.$$listeners[a]||[];h=0;for(k=d.length;h<k;h++)if(d[h])try{d[h].apply(null,
+g)}catch(m){e(m)}else d.splice(h,1),h--,k--;if(!(d=c.$$listenerCount[a]&&c.$$childHead||c!==this&&c.$$nextSibling))for(;c!==this&&!(d=c.$$nextSibling);)c=c.$parent}return f}};var n=new h;return n}]}function od(){var b=/^\s*(https?|ftp|mailto|tel|file):/,a=/^\s*(https?|ftp|file|blob):|data:image\//;this.aHrefSanitizationWhitelist=function(a){return F(a)?(b=a,this):b};this.imgSrcSanitizationWhitelist=function(b){return F(b)?(a=b,this):a};this.$get=function(){return function(c,d){var e=d?a:b,f;if(!P||
+8<=P)if(f=ta(c).href,""!==f&&!f.match(e))return"unsafe:"+f;return c}}}function Pe(b){if("self"===b)return b;if(x(b)){if(-1<b.indexOf("***"))throw va("iwcard",b);b=b.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g,"\\$1").replace(/\x08/g,"\\x08").replace("\\*\\*",".*").replace("\\*","[^:/.?&;]*");return RegExp("^"+b+"$")}if(eb(b))return RegExp("^"+b.source+"$");throw va("imatcher");}function Nc(b){var a=[];F(b)&&q(b,function(b){a.push(Pe(b))});return a}function oe(){this.SCE_CONTEXTS=fa;var b=["self"],a=[];
+this.resourceUrlWhitelist=function(a){arguments.length&&(b=Nc(a));return b};this.resourceUrlBlacklist=function(b){arguments.length&&(a=Nc(b));return a};this.$get=["$injector",function(c){function d(a){var b=function(a){this.$$unwrapTrustedValue=function(){return a}};a&&(b.prototype=new a);b.prototype.valueOf=function(){return this.$$unwrapTrustedValue()};b.prototype.toString=function(){return this.$$unwrapTrustedValue().toString()};return b}var e=function(a){throw va("unsafe");};c.has("$sanitize")&&
+(e=c.get("$sanitize"));var f=d(),g={};g[fa.HTML]=d(f);g[fa.CSS]=d(f);g[fa.URL]=d(f);g[fa.JS]=d(f);g[fa.RESOURCE_URL]=d(g[fa.URL]);return{trustAs:function(a,b){var c=g.hasOwnProperty(a)?g[a]:null;if(!c)throw va("icontext",a,b);if(null===b||b===s||""===b)return b;if("string"!==typeof b)throw va("itype",a);return new c(b)},getTrusted:function(c,d){if(null===d||d===s||""===d)return d;var f=g.hasOwnProperty(c)?g[c]:null;if(f&&d instanceof f)return d.$$unwrapTrustedValue();if(c===fa.RESOURCE_URL){var f=
+ta(d.toString()),l,p,n=!1;l=0;for(p=b.length;l<p;l++)if("self"===b[l]?Pb(f):b[l].exec(f.href)){n=!0;break}if(n)for(l=0,p=a.length;l<p;l++)if("self"===a[l]?Pb(f):a[l].exec(f.href)){n=!1;break}if(n)return d;throw va("insecurl",d.toString());}if(c===fa.HTML)return e(d);throw va("unsafe");},valueOf:function(a){return a instanceof f?a.$$unwrapTrustedValue():a}}}]}function ne(){var b=!0;this.enabled=function(a){arguments.length&&(b=!!a);return b};this.$get=["$parse","$sniffer","$sceDelegate",function(a,
+c,d){if(b&&c.msie&&8>c.msieDocumentMode)throw va("iequirks");var e=aa(fa);e.isEnabled=function(){return b};e.trustAs=d.trustAs;e.getTrusted=d.getTrusted;e.valueOf=d.valueOf;b||(e.trustAs=e.getTrusted=function(a,b){return b},e.valueOf=Da);e.parseAs=function(b,c){var d=a(c);return d.literal&&d.constant?d:function(a,c){return e.getTrusted(b,d(a,c))}};var f=e.parseAs,g=e.getTrusted,h=e.trustAs;q(fa,function(a,b){var c=u(b);e[Ta("parse_as_"+c)]=function(b){return f(a,b)};e[Ta("get_trusted_"+c)]=function(b){return g(a,
+b)};e[Ta("trust_as_"+c)]=function(b){return h(a,b)}});return e}]}function pe(){this.$get=["$window","$document",function(b,a){var c={},d=Q((/android (\d+)/.exec(u((b.navigator||{}).userAgent))||[])[1]),e=/Boxee/i.test((b.navigator||{}).userAgent),f=a[0]||{},g=f.documentMode,h,m=/^(Moz|webkit|O|ms)(?=[A-Z])/,k=f.body&&f.body.style,l=!1,p=!1;if(k){for(var n in k)if(l=m.exec(n)){h=l[0];h=h.substr(0,1).toUpperCase()+h.substr(1);break}h||(h="WebkitOpacity"in k&&"webkit");l=!!("transition"in k||h+"Transition"in
+k);p=!!("animation"in k||h+"Animation"in k);!d||l&&p||(l=x(f.body.style.webkitTransition),p=x(f.body.style.webkitAnimation))}return{history:!(!b.history||!b.history.pushState||4>d||e),hashchange:"onhashchange"in b&&(!g||7<g),hasEvent:function(a){if("input"==a&&9==P)return!1;if(A(c[a])){var b=f.createElement("div");c[a]="on"+a in b}return c[a]},csp:dc(),vendorPrefix:h,transitions:l,animations:p,android:d,msie:P,msieDocumentMode:g}}]}function re(){this.$get=["$rootScope","$browser","$q","$exceptionHandler",
+function(b,a,c,d){function e(e,h,m){var k=c.defer(),l=k.promise,p=F(m)&&!m;h=a.defer(function(){try{k.resolve(e())}catch(a){k.reject(a),d(a)}finally{delete f[l.$$timeoutId]}p||b.$apply()},h);l.$$timeoutId=h;f[h]=k;return l}var f={};e.cancel=function(b){return b&&b.$$timeoutId in f?(f[b.$$timeoutId].reject("canceled"),delete f[b.$$timeoutId],a.defer.cancel(b.$$timeoutId)):!1};return e}]}function ta(b,a){var c=b;P&&(V.setAttribute("href",c),c=V.href);V.setAttribute("href",c);return{href:V.href,protocol:V.protocol?
+V.protocol.replace(/:$/,""):"",host:V.host,search:V.search?V.search.replace(/^\?/,""):"",hash:V.hash?V.hash.replace(/^#/,""):"",hostname:V.hostname,port:V.port,pathname:"/"===V.pathname.charAt(0)?V.pathname:"/"+V.pathname}}function Pb(b){b=x(b)?ta(b):b;return b.protocol===Oc.protocol&&b.host===Oc.host}function se(){this.$get=$(N)}function oc(b){function a(d,e){if(R(d)){var f={};q(d,function(b,c){f[c]=a(c,b)});return f}return b.factory(d+c,e)}var c="Filter";this.register=a;this.$get=["$injector",function(a){return function(b){return a.get(b+
+c)}}];a("currency",Pc);a("date",Qc);a("filter",Qe);a("json",Re);a("limitTo",Se);a("lowercase",Te);a("number",Rc);a("orderBy",Sc);a("uppercase",Ue)}function Qe(){return function(b,a,c){if(!M(b))return b;var d=typeof c,e=[];e.check=function(a){for(var b=0;b<e.length;b++)if(!e[b](a))return!1;return!0};"function"!==d&&(c="boolean"===d&&c?function(a,b){return Qa.equals(a,b)}:function(a,b){if(a&&b&&"object"===typeof a&&"object"===typeof b){for(var d in a)if("$"!==d.charAt(0)&&Mc.call(a,d)&&c(a[d],b[d]))return!0;
+return!1}b=(""+b).toLowerCase();return-1<(""+a).toLowerCase().indexOf(b)});var f=function(a,b){if("string"==typeof b&&"!"===b.charAt(0))return!f(a,b.substr(1));switch(typeof a){case "boolean":case "number":case "string":return c(a,b);case "object":switch(typeof b){case "object":return c(a,b);default:for(var d in a)if("$"!==d.charAt(0)&&f(a[d],b))return!0}return!1;case "array":for(d=0;d<a.length;d++)if(f(a[d],b))return!0;return!1;default:return!1}};switch(typeof a){case "boolean":case "number":case "string":a=
+{$:a};case "object":for(var g in a)(function(b){"undefined"!=typeof a[b]&&e.push(function(c){return f("$"==b?c:c&&c[b],a[b])})})(g);break;case "function":e.push(a);break;default:return b}d=[];for(g=0;g<b.length;g++){var h=b[g];e.check(h)&&d.push(h)}return d}}function Pc(b){var a=b.NUMBER_FORMATS;return function(b,d){A(d)&&(d=a.CURRENCY_SYM);return Tc(b,a.PATTERNS[1],a.GROUP_SEP,a.DECIMAL_SEP,2).replace(/\u00A4/g,d)}}function Rc(b){var a=b.NUMBER_FORMATS;return function(b,d){return Tc(b,a.PATTERNS[0],
+a.GROUP_SEP,a.DECIMAL_SEP,d)}}function Tc(b,a,c,d,e){if(null==b||!isFinite(b)||R(b))return"";var f=0>b;b=Math.abs(b);var g=b+"",h="",m=[],k=!1;if(-1!==g.indexOf("e")){var l=g.match(/([\d\.]+)e(-?)(\d+)/);l&&"-"==l[2]&&l[3]>e+1?g="0":(h=g,k=!0)}if(k)0<e&&(-1<b&&1>b)&&(h=b.toFixed(e));else{g=(g.split(Uc)[1]||"").length;A(e)&&(e=Math.min(Math.max(a.minFrac,g),a.maxFrac));g=Math.pow(10,e);b=Math.round(b*g)/g;b=(""+b).split(Uc);g=b[0];b=b[1]||"";var l=0,p=a.lgSize,n=a.gSize;if(g.length>=p+n)for(l=g.length-
+p,k=0;k<l;k++)0===(l-k)%n&&0!==k&&(h+=c),h+=g.charAt(k);for(k=l;k<g.length;k++)0===(g.length-k)%p&&0!==k&&(h+=c),h+=g.charAt(k);for(;b.length<e;)b+="0";e&&"0"!==e&&(h+=d+b.substr(0,e))}m.push(f?a.negPre:a.posPre);m.push(h);m.push(f?a.negSuf:a.posSuf);return m.join("")}function vb(b,a,c){var d="";0>b&&(d="-",b=-b);for(b=""+b;b.length<a;)b="0"+b;c&&(b=b.substr(b.length-a));return d+b}function X(b,a,c,d){c=c||0;return function(e){e=e["get"+b]();if(0<c||e>-c)e+=c;0===e&&-12==c&&(e=12);return vb(e,a,d)}}
+function wb(b,a){return function(c,d){var e=c["get"+b](),f=Fa(a?"SHORT"+b:b);return d[f][e]}}function Vc(b){var a=(new Date(b,0,1)).getDay();return new Date(b,0,(4>=a?5:12)-a)}function Wc(b){return function(a){var c=Vc(a.getFullYear());a=+new Date(a.getFullYear(),a.getMonth(),a.getDate()+(4-a.getDay()))-+c;a=1+Math.round(a/6048E5);return vb(a,b)}}function Qc(b){function a(a){var b;if(b=a.match(c)){a=new Date(0);var f=0,g=0,h=b[8]?a.setUTCFullYear:a.setFullYear,m=b[8]?a.setUTCHours:a.setHours;b[9]&&
+(f=Q(b[9]+b[10]),g=Q(b[9]+b[11]));h.call(a,Q(b[1]),Q(b[2])-1,Q(b[3]));f=Q(b[4]||0)-f;g=Q(b[5]||0)-g;h=Q(b[6]||0);b=Math.round(1E3*parseFloat("0."+(b[7]||0)));m.call(a,f,g,h,b)}return a}var c=/^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;return function(c,e){var f="",g=[],h,m;e=e||"mediumDate";e=b.DATETIME_FORMATS[e]||e;x(c)&&(c=Ve.test(c)?Q(c):a(c));Cb(c)&&(c=new Date(c));if(!qa(c))return c;for(;e;)(m=We.exec(e))?(g=g.concat(ra.call(m,1)),e=
+g.pop()):(g.push(e),e=null);q(g,function(a){h=Xe[a];f+=h?h(c,b.DATETIME_FORMATS):a.replace(/(^'|'$)/g,"").replace(/''/g,"'")});return f}}function Re(){return function(b){return sa(b,!0)}}function Se(){return function(b,a){if(!M(b)&&!x(b))return b;a=Infinity===Math.abs(Number(a))?Number(a):Q(a);if(x(b))return a?0<=a?b.slice(0,a):b.slice(a,b.length):"";var c=[],d,e;a>b.length?a=b.length:a<-b.length&&(a=-b.length);0<a?(d=0,e=a):(d=b.length+a,e=b.length);for(;d<e;d++)c.push(b[d]);return c}}function Sc(b){return function(a,
+c,d){function e(a,b){return Pa(b)?function(b,c){return a(c,b)}:a}function f(a,b){var c=typeof a,d=typeof b;return c==d?("string"==c&&(a=a.toLowerCase(),b=b.toLowerCase()),a===b?0:a<b?-1:1):c<d?-1:1}if(!M(a)||!c)return a;c=M(c)?c:[c];c=fd(c,function(a){var c=!1,d=a||Da;if(x(a)){if("+"==a.charAt(0)||"-"==a.charAt(0))c="-"==a.charAt(0),a=a.substring(1);d=b(a);if(d.constant){var g=d();return e(function(a,b){return f(a[g],b[g])},c)}}return e(function(a,b){return f(d(a),d(b))},c)});for(var g=[],h=0;h<a.length;h++)g.push(a[h]);
+return g.sort(e(function(a,b){for(var d=0;d<c.length;d++){var e=c[d](a,b);if(0!==e)return e}return 0},d))}}function wa(b){O(b)&&(b={link:b});b.restrict=b.restrict||"AC";return $(b)}function Xc(b,a,c,d){function e(a,c){c=c?"-"+ib(c,"-"):"";d.removeClass(b,(a?xb:yb)+c);d.addClass(b,(a?yb:xb)+c)}var f=this,g=b.parent().controller("form")||zb,h=0,m=f.$error={},k=[];f.$name=a.name||a.ngForm;f.$dirty=!1;f.$pristine=!0;f.$valid=!0;f.$invalid=!1;g.$addControl(f);b.addClass(Ma);e(!0);f.$addControl=function(a){Aa(a.$name,
+"input");k.push(a);a.$name&&(f[a.$name]=a)};f.$removeControl=function(a){a.$name&&f[a.$name]===a&&delete f[a.$name];q(m,function(b,c){f.$setValidity(c,!0,a)});Ea(k,a)};f.$setValidity=function(a,b,c){var d=m[a];if(b)d&&(Ea(d,c),d.length||(h--,h||(e(b),f.$valid=!0,f.$invalid=!1),m[a]=!1,e(!0,a),g.$setValidity(a,!0,f)));else{h||e(b);if(d){if(-1!=fb(d,c))return}else m[a]=d=[],h++,e(!1,a),g.$setValidity(a,!1,f);d.push(c);f.$valid=!1;f.$invalid=!0}};f.$setDirty=function(){d.removeClass(b,Ma);d.addClass(b,
+Ab);f.$dirty=!0;f.$pristine=!1;g.$setDirty()};f.$setPristine=function(){d.removeClass(b,Ab);d.addClass(b,Ma);f.$dirty=!1;f.$pristine=!0;q(k,function(a){a.$setPristine()})}}function pa(b,a,c,d){b.$setValidity(a,c);return c?d:s}function Ye(b,a,c){var d=c.prop("validity");R(d)&&b.$parsers.push(function(c){if(b.$error[a]||!(d.badInput||d.customError||d.typeMismatch)||d.valueMissing)return c;b.$setValidity(a,!1)})}function ab(b,a,c,d,e,f){var g=a.prop("validity"),h=a[0].placeholder,m={};if(!e.android){var k=
+!1;a.on("compositionstart",function(a){k=!0});a.on("compositionend",function(){k=!1;l()})}var l=function(e){if(!k){var f=a.val(),n=e&&e.type;if(P&&"input"===(e||m).type&&a[0].placeholder!==h)h=a[0].placeholder;else if(Pa(c.ngTrim||"T")&&(f=Y(f)),d.$viewValue!==f||g&&""===f&&!g.valueMissing)b.$$phase?d.$setViewValue(f,n):b.$apply(function(){d.$setViewValue(f,n)})}};if(d.$options&&d.$options.updateOn)a.on(d.$options.updateOn,l);if(!d.$options||d.$options.updateOnDefault){if(e.hasEvent("input"))a.on("input",
+l);else{var p,n=function(a){p||(p=f.defer(function(){l(a);p=null}))};a.on("keydown",function(a){var b=a.keyCode;91===b||(15<b&&19>b||37<=b&&40>=b)||n(a)});if(e.hasEvent("paste"))a.on("paste cut",n)}a.on("change",l)}d.$render=function(){a.val(d.$isEmpty(d.$viewValue)?"":d.$viewValue)};var r=c.ngPattern;r&&((e=r.match(/^\/(.*)\/([gim]*)$/))?(r=RegExp(e[1],e[2]),e=function(a){return pa(d,"pattern",d.$isEmpty(a)||r.test(a),a)}):e=function(c){var e=b.$eval(r);if(!e||!e.test)throw I("ngPattern")("noregexp",
+r,e,ga(a));return pa(d,"pattern",d.$isEmpty(c)||e.test(c),c)},d.$formatters.push(e),d.$parsers.push(e));if(c.ngMinlength){var q=Q(c.ngMinlength);e=function(a){return pa(d,"minlength",d.$isEmpty(a)||a.length>=q,a)};d.$parsers.push(e);d.$formatters.push(e)}if(c.ngMaxlength){var J=Q(c.ngMaxlength);e=function(a){return pa(d,"maxlength",d.$isEmpty(a)||a.length<=J,a)};d.$parsers.push(e);d.$formatters.push(e)}}function Bb(b,a){return function(c){var d;return qa(c)?c:x(c)&&(b.lastIndex=0,c=b.exec(c))?(c.shift(),
+d={yyyy:0,MM:1,dd:1,HH:0,mm:0},q(c,function(b,c){c<a.length&&(d[a[c]]=+b)}),new Date(d.yyyy,d.MM-1,d.dd,d.HH,d.mm)):NaN}}function bb(b,a,c,d){return function(e,f,g,h,m,k,l){ab(e,f,g,h,m,k);h.$parsers.push(function(d){if(h.$isEmpty(d))return h.$setValidity(b,!0),null;if(a.test(d))return h.$setValidity(b,!0),c(d);h.$setValidity(b,!1);return s});h.$formatters.push(function(a){return qa(a)?l("date")(a,d):""});g.min&&(e=function(a){var b=h.$isEmpty(a)||c(a)>=c(g.min);h.$setValidity("min",b);return b?a:
+s},h.$parsers.push(e),h.$formatters.push(e));g.max&&(e=function(a){var b=h.$isEmpty(a)||c(a)<=c(g.max);h.$setValidity("max",b);return b?a:s},h.$parsers.push(e),h.$formatters.push(e))}}function Xb(b,a){b="ngClass"+b;return["$animate",function(c){function d(a,b){var c=[],d=0;a:for(;d<a.length;d++){for(var e=a[d],l=0;l<b.length;l++)if(e==b[l])continue a;c.push(e)}return c}function e(a){if(!M(a)){if(x(a))return a.split(" ");if(R(a)){var b=[];q(a,function(a,c){a&&b.push(c)});return b}}return a}return{restrict:"AC",
+link:function(f,g,h){function m(a,b){var c=g.data("$classCounts")||{},d=[];q(a,function(a){if(0<b||c[a])c[a]=(c[a]||0)+b,c[a]===+(0<b)&&d.push(a)});g.data("$classCounts",c);return d.join(" ")}function k(b){if(!0===a||f.$index%2===a){var k=e(b||[]);if(!l){var q=m(k,1);h.$addClass(q)}else if(!ya(b,l)){var t=e(l),q=d(k,t),k=d(t,k),k=m(k,-1),q=m(q,1);0===q.length?c.removeClass(g,k):0===k.length?c.addClass(g,q):c.setClass(g,q,k)}}l=aa(b)}var l;f.$watch(h[b],k,!0);h.$observe("class",function(a){k(f.$eval(h[b]))});
+"ngClass"!==b&&f.$watch("$index",function(c,d){var g=c&1;if(g!==d&1){var k=e(f.$eval(h[b]));g===a?(g=m(k,1),h.$addClass(g)):(g=m(k,-1),h.$removeClass(g))}})}}}]}var u=function(b){return x(b)?b.toLowerCase():b},Mc=Object.prototype.hasOwnProperty,Fa=function(b){return x(b)?b.toUpperCase():b},P,y,Ga,ra=[].slice,Ze=[].push,xa=Object.prototype.toString,Oa=I("ng"),Qa=N.angular||(N.angular={}),Sa,La,ka=["0","0","0"];P=Q((/msie (\d+)/.exec(u(navigator.userAgent))||[])[1]);isNaN(P)&&(P=Q((/trident\/.*; rv:(\d+)/.exec(u(navigator.userAgent))||
+[])[1]));D.$inject=[];Da.$inject=[];var Y=function(){return String.prototype.trim?function(b){return x(b)?b.trim():b}:function(b){return x(b)?b.replace(/^\s\s*/,"").replace(/\s\s*$/,""):b}}();La=9>P?function(b){b=b.nodeName?b:b[0];return b.scopeName&&"HTML"!=b.scopeName?Fa(b.scopeName+":"+b.nodeName):b.nodeName}:function(b){return b.nodeName?b.nodeName:b[0].nodeName};var hc=["ng-","data-ng-","ng:","x-ng-"],jd=/[A-Z]/g,md={full:"1.3.0-beta.7",major:1,minor:3,dot:0,codeName:"proper-attribution"},Va=
+U.cache={},jb=U.expando="ng-"+(new Date).getTime(),Be=1,sb=N.document.addEventListener?function(b,a,c){b.addEventListener(a,c,!1)}:function(b,a,c){b.attachEvent("on"+a,c)},Ua=N.document.removeEventListener?function(b,a,c){b.removeEventListener(a,c,!1)}:function(b,a,c){b.detachEvent("on"+a,c)};U._data=function(b){return this.cache[b[this.expando]]||{}};var ve=/([\:\-\_]+(.))/g,we=/^moz([A-Z])/,Kb=I("jqLite"),Ae=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,Jb=/<|&#?\w+;/,ye=/<([\w:]+)/,ze=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,
+da={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,"",""]};da.optgroup=da.option;da.tbody=da.tfoot=da.colgroup=da.caption=da.thead;da.th=da.td;var Ka=U.prototype={ready:function(b){function a(){c||(c=!0,b())}var c=!1;"complete"===S.readyState?setTimeout(a):(this.on("DOMContentLoaded",a),U(N).on("load",a))},
+toString:function(){var b=[];q(this,function(a){b.push(""+a)});return"["+b.join(", ")+"]"},eq:function(b){return 0<=b?y(this[b]):y(this[this.length+b])},length:0,push:Ze,sort:[].sort,splice:[].splice},nb={};q("multiple selected checked disabled readOnly required open".split(" "),function(b){nb[u(b)]=b});var wc={};q("input select option textarea button form details".split(" "),function(b){wc[Fa(b)]=!0});q({data:sc,inheritedData:mb,scope:function(b){return y(b).data("$scope")||mb(b.parentNode||b,["$isolateScope",
+"$scope"])},isolateScope:function(b){return y(b).data("$isolateScope")||y(b).data("$isolateScopeNoTemplate")},controller:tc,injector:function(b){return mb(b,"$injector")},removeAttr:function(b,a){b.removeAttribute(a)},hasClass:Mb,css:function(b,a,c){a=Ta(a);if(F(c))b.style[a]=c;else{var d;8>=P&&(d=b.currentStyle&&b.currentStyle[a],""===d&&(d="auto"));d=d||b.style[a];8>=P&&(d=""===d?s:d);return d}},attr:function(b,a,c){var d=u(a);if(nb[d])if(F(c))c?(b[a]=!0,b.setAttribute(a,d)):(b[a]=!1,b.removeAttribute(d));
+else return b[a]||(b.attributes.getNamedItem(a)||D).specified?d:s;else if(F(c))b.setAttribute(a,c);else if(b.getAttribute)return b=b.getAttribute(a,2),null===b?s:b},prop:function(b,a,c){if(F(c))b[a]=c;else return b[a]},text:function(){function b(b,d){var e=a[b.nodeType];if(A(d))return e?b[e]:"";b[e]=d}var a=[];9>P?(a[1]="innerText",a[3]="nodeValue"):a[1]=a[3]="textContent";b.$dv="";return b}(),val:function(b,a){if(A(a)){if("SELECT"===La(b)&&b.multiple){var c=[];q(b.options,function(a){a.selected&&
+c.push(a.value||a.text)});return 0===c.length?null:c}return b.value}b.value=a},html:function(b,a){if(A(a))return b.innerHTML;for(var c=0,d=b.childNodes;c<d.length;c++)Ha(d[c]);b.innerHTML=a},empty:uc},function(b,a){U.prototype[a]=function(a,d){var e,f;if(b!==uc&&(2==b.length&&b!==Mb&&b!==tc?a:d)===s){if(R(a)){for(e=0;e<this.length;e++)if(b===sc)b(this[e],a);else for(f in a)b(this[e],f,a[f]);return this}e=b.$dv;f=e===s?Math.min(this.length,1):this.length;for(var g=0;g<f;g++){var h=b(this[g],a,d);e=
+e?e+h:h}return e}for(e=0;e<this.length;e++)b(this[e],a,d);return this}});q({removeData:qc,dealoc:Ha,on:function a(c,d,e,f){if(F(f))throw Kb("onargs");var g=la(c,"events"),h=la(c,"handle");g||la(c,"events",g={});h||la(c,"handle",h=Ce(c,g));q(d.split(" "),function(d){var f=g[d];if(!f){if("mouseenter"==d||"mouseleave"==d){var l=S.body.contains||S.body.compareDocumentPosition?function(a,c){var d=9===a.nodeType?a.documentElement:a,e=c&&c.parentNode;return a===e||!!(e&&1===e.nodeType&&(d.contains?d.contains(e):
+a.compareDocumentPosition&&a.compareDocumentPosition(e)&16))}:function(a,c){if(c)for(;c=c.parentNode;)if(c===a)return!0;return!1};g[d]=[];a(c,{mouseleave:"mouseout",mouseenter:"mouseover"}[d],function(a){var c=a.relatedTarget;c&&(c===this||l(this,c))||h(a,d)})}else sb(c,d,h),g[d]=[];f=g[d]}f.push(e)})},off:rc,one:function(a,c,d){a=y(a);a.on(c,function f(){a.off(c,d);a.off(c,f)});a.on(c,d)},replaceWith:function(a,c){var d,e=a.parentNode;Ha(a);q(new U(c),function(c){d?e.insertBefore(c,d.nextSibling):
+e.replaceChild(c,a);d=c})},children:function(a){var c=[];q(a.childNodes,function(a){1===a.nodeType&&c.push(a)});return c},contents:function(a){return a.contentDocument||a.childNodes||[]},append:function(a,c){q(new U(c),function(c){1!==a.nodeType&&11!==a.nodeType||a.appendChild(c)})},prepend:function(a,c){if(1===a.nodeType){var d=a.firstChild;q(new U(c),function(c){a.insertBefore(c,d)})}},wrap:function(a,c){c=y(c)[0];var d=a.parentNode;d&&d.replaceChild(c,a);c.appendChild(a)},remove:function(a){Ha(a);
+var c=a.parentNode;c&&c.removeChild(a)},after:function(a,c){var d=a,e=a.parentNode;q(new U(c),function(a){e.insertBefore(a,d.nextSibling);d=a})},addClass:lb,removeClass:kb,toggleClass:function(a,c,d){c&&q(c.split(" "),function(c){var f=d;A(f)&&(f=!Mb(a,c));(f?lb:kb)(a,c)})},parent:function(a){return(a=a.parentNode)&&11!==a.nodeType?a:null},next:function(a){if(a.nextElementSibling)return a.nextElementSibling;for(a=a.nextSibling;null!=a&&1!==a.nodeType;)a=a.nextSibling;return a},find:function(a,c){return a.getElementsByTagName?
+a.getElementsByTagName(c):[]},clone:Lb,triggerHandler:function(a,c,d){c=(la(a,"events")||{})[c];d=d||[];var e=[{preventDefault:D,stopPropagation:D}];q(c,function(c){c.apply(a,e.concat(d))})}},function(a,c){U.prototype[c]=function(c,e,f){for(var g,h=0;h<this.length;h++)A(g)?(g=a(this[h],c,e,f),F(g)&&(g=y(g))):pc(g,a(this[h],c,e,f));return F(g)?g:this};U.prototype.bind=U.prototype.on;U.prototype.unbind=U.prototype.off});Wa.prototype={put:function(a,c){this[Ia(a)]=c},get:function(a){return this[Ia(a)]},
+remove:function(a){var c=this[a=Ia(a)];delete this[a];return c}};var yc=/^function\s*[^\(]*\(\s*([^\)]*)\)/m,Ee=/,/,Fe=/^\s*(_?)(\S+?)\1\s*$/,xc=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg,Ja=I("$injector");Eb.$$annotate=Nb;var $e=I("$animate"),Zd=["$provide",function(a){this.$$selectors={};this.register=function(c,d){var e=c+"-animation";if(c&&"."!=c.charAt(0))throw $e("notcsel",c);this.$$selectors[c.substr(1)]=e;a.factory(e,d)};this.classNameFilter=function(a){1===arguments.length&&(this.$$classNameFilter=
+a instanceof RegExp?a:null);return this.$$classNameFilter};this.$get=["$timeout","$$asyncCallback",function(a,d){return{enter:function(a,c,g,h){g?g.after(a):c.prepend(a);h&&d(h)},leave:function(a,c){a.remove();c&&d(c)},move:function(a,c,d,h){this.enter(a,c,d,h)},addClass:function(a,c,g){c=x(c)?c:M(c)?c.join(" "):"";q(a,function(a){lb(a,c)});g&&d(g)},removeClass:function(a,c,g){c=x(c)?c:M(c)?c.join(" "):"";q(a,function(a){kb(a,c)});g&&d(g)},setClass:function(a,c,g,h){q(a,function(a){lb(a,c);kb(a,g)});
+h&&d(h)},enabled:D}}]}],ia=I("$compile");kc.$inject=["$provide","$$sanitizeUriProvider"];var Ie=/^(x[\:\-_]|data[\:\-_])/i,Ec=I("$interpolate"),af=/^([^\?#]*)(\?([^#]*))?(#(.*))?$/,Le={http:80,https:443,ftp:21},Sb=I("$location");Ub.prototype=Tb.prototype=Hc.prototype={$$html5:!1,$$replace:!1,absUrl:tb("$$absUrl"),url:function(a,c){if(A(a))return this.$$url;var d=af.exec(a);d[1]&&this.path(decodeURIComponent(d[1]));(d[2]||d[1])&&this.search(d[3]||"");this.hash(d[5]||"",c);return this},protocol:tb("$$protocol"),
+host:tb("$$host"),port:tb("$$port"),path:Ic("$$path",function(a){return"/"==a.charAt(0)?a:"/"+a}),search:function(a,c){switch(arguments.length){case 0:return this.$$search;case 1:if(x(a))this.$$search=gc(a);else if(R(a))this.$$search=a;else throw Sb("isrcharg");break;default:A(c)||null===c?delete this.$$search[a]:this.$$search[a]=c}this.$$compose();return this},hash:Ic("$$hash",Da),replace:function(){this.$$replace=!0;return this}};var Ba=I("$parse"),Lc={},ua,Na={"null":function(){return null},"true":function(){return!0},
+"false":function(){return!1},undefined:D,"+":function(a,c,d,e){d=d(a,c);e=e(a,c);return F(d)?F(e)?d+e:d:F(e)?e:s},"-":function(a,c,d,e){d=d(a,c);e=e(a,c);return(F(d)?d:0)-(F(e)?e:0)},"*":function(a,c,d,e){return d(a,c)*e(a,c)},"/":function(a,c,d,e){return d(a,c)/e(a,c)},"%":function(a,c,d,e){return d(a,c)%e(a,c)},"^":function(a,c,d,e){return d(a,c)^e(a,c)},"=":D,"===":function(a,c,d,e){return d(a,c)===e(a,c)},"!==":function(a,c,d,e){return d(a,c)!==e(a,c)},"==":function(a,c,d,e){return d(a,c)==e(a,
+c)},"!=":function(a,c,d,e){return d(a,c)!=e(a,c)},"<":function(a,c,d,e){return d(a,c)<e(a,c)},">":function(a,c,d,e){return d(a,c)>e(a,c)},"<=":function(a,c,d,e){return d(a,c)<=e(a,c)},">=":function(a,c,d,e){return d(a,c)>=e(a,c)},"&&":function(a,c,d,e){return d(a,c)&&e(a,c)},"||":function(a,c,d,e){return d(a,c)||e(a,c)},"&":function(a,c,d,e){return d(a,c)&e(a,c)},"|":function(a,c,d,e){return e(a,c)(a,c,d(a,c))},"!":function(a,c,d){return!d(a,c)}},bf={n:"\n",f:"\f",r:"\r",t:"\t",v:"\v","'":"'",'"':'"'},
+Wb=function(a){this.options=a};Wb.prototype={constructor:Wb,lex:function(a){this.text=a;this.index=0;this.ch=s;this.lastCh=":";this.tokens=[];var c;for(a=[];this.index<this.text.length;){this.ch=this.text.charAt(this.index);if(this.is("\"'"))this.readString(this.ch);else if(this.isNumber(this.ch)||this.is(".")&&this.isNumber(this.peek()))this.readNumber();else if(this.isIdent(this.ch))this.readIdent(),this.was("{,")&&("{"===a[0]&&(c=this.tokens[this.tokens.length-1]))&&(c.json=-1===c.text.indexOf("."));
+else if(this.is("(){}[].,;:?"))this.tokens.push({index:this.index,text:this.ch,json:this.was(":[,")&&this.is("{[")||this.is("}]:,")}),this.is("{[")&&a.unshift(this.ch),this.is("}]")&&a.shift(),this.index++;else if(this.isWhitespace(this.ch)){this.index++;continue}else{var d=this.ch+this.peek(),e=d+this.peek(2),f=Na[this.ch],g=Na[d],h=Na[e];h?(this.tokens.push({index:this.index,text:e,fn:h}),this.index+=3):g?(this.tokens.push({index:this.index,text:d,fn:g}),this.index+=2):f?(this.tokens.push({index:this.index,
+text:this.ch,fn:f,json:this.was("[,:")&&this.is("+-")}),this.index+=1):this.throwError("Unexpected next character ",this.index,this.index+1)}this.lastCh=this.ch}return this.tokens},is:function(a){return-1!==a.indexOf(this.ch)},was:function(a){return-1!==a.indexOf(this.lastCh)},peek:function(a){a=a||1;return this.index+a<this.text.length?this.text.charAt(this.index+a):!1},isNumber:function(a){return"0"<=a&&"9">=a},isWhitespace:function(a){return" "===a||"\r"===a||"\t"===a||"\n"===a||"\v"===a||"\u00a0"===
+a},isIdent:function(a){return"a"<=a&&"z">=a||"A"<=a&&"Z">=a||"_"===a||"$"===a},isExpOperator:function(a){return"-"===a||"+"===a||this.isNumber(a)},throwError:function(a,c,d){d=d||this.index;c=F(c)?"s "+c+"-"+this.index+" ["+this.text.substring(c,d)+"]":" "+d;throw Ba("lexerr",a,c,this.text);},readNumber:function(){for(var a="",c=this.index;this.index<this.text.length;){var d=u(this.text.charAt(this.index));if("."==d||this.isNumber(d))a+=d;else{var e=this.peek();if("e"==d&&this.isExpOperator(e))a+=
+d;else if(this.isExpOperator(d)&&e&&this.isNumber(e)&&"e"==a.charAt(a.length-1))a+=d;else if(!this.isExpOperator(d)||e&&this.isNumber(e)||"e"!=a.charAt(a.length-1))break;else this.throwError("Invalid exponent")}this.index++}a*=1;this.tokens.push({index:c,text:a,json:!0,fn:function(){return a}})},readIdent:function(){for(var a=this,c="",d=this.index,e,f,g,h;this.index<this.text.length;){h=this.text.charAt(this.index);if("."===h||this.isIdent(h)||this.isNumber(h))"."===h&&(e=this.index),c+=h;else break;
+this.index++}if(e)for(f=this.index;f<this.text.length;){h=this.text.charAt(f);if("("===h){g=c.substr(e-d+1);c=c.substr(0,e-d);this.index=f;break}if(this.isWhitespace(h))f++;else break}d={index:d,text:c};if(Na.hasOwnProperty(c))d.fn=Na[c],d.json=Na[c];else{var m=Kc(c,this.options,this.text);d.fn=w(function(a,c){return m(a,c)},{assign:function(d,e){return ub(d,c,e,a.text,a.options)}})}this.tokens.push(d);g&&(this.tokens.push({index:e,text:".",json:!1}),this.tokens.push({index:e+1,text:g,json:!1}))},
+readString:function(a){var c=this.index;this.index++;for(var d="",e=a,f=!1;this.index<this.text.length;){var g=this.text.charAt(this.index),e=e+g;if(f)"u"===g?(g=this.text.substring(this.index+1,this.index+5),g.match(/[\da-f]{4}/i)||this.throwError("Invalid unicode escape [\\u"+g+"]"),this.index+=4,d+=String.fromCharCode(parseInt(g,16))):d=(f=bf[g])?d+f:d+g,f=!1;else if("\\"===g)f=!0;else{if(g===a){this.index++;this.tokens.push({index:c,text:e,string:d,json:!0,fn:function(){return d}});return}d+=
+g}this.index++}this.throwError("Unterminated quote",c)}};var $a=function(a,c,d){this.lexer=a;this.$filter=c;this.options=d};$a.ZERO=w(function(){return 0},{constant:!0});$a.prototype={constructor:$a,parse:function(a,c){this.text=a;this.json=c;this.tokens=this.lexer.lex(a);c&&(this.assignment=this.logicalOR,this.functionCall=this.fieldAccess=this.objectIndex=this.filterChain=function(){this.throwError("is not valid json",{text:a,index:0})});var d=c?this.primary():this.statements();0!==this.tokens.length&&
+this.throwError("is an unexpected token",this.tokens[0]);d.literal=!!d.literal;d.constant=!!d.constant;return d},primary:function(){var a;if(this.expect("("))a=this.filterChain(),this.consume(")");else if(this.expect("["))a=this.arrayDeclaration();else if(this.expect("{"))a=this.object();else{var c=this.expect();(a=c.fn)||this.throwError("not a primary expression",c);c.json&&(a.constant=!0,a.literal=!0)}for(var d;c=this.expect("(","[",".");)"("===c.text?(a=this.functionCall(a,d),d=null):"["===c.text?
+(d=a,a=this.objectIndex(a)):"."===c.text?(d=a,a=this.fieldAccess(a)):this.throwError("IMPOSSIBLE");return a},throwError:function(a,c){throw Ba("syntax",c.text,a,c.index+1,this.text,this.text.substring(c.index));},peekToken:function(){if(0===this.tokens.length)throw Ba("ueoe",this.text);return this.tokens[0]},peek:function(a,c,d,e){if(0<this.tokens.length){var f=this.tokens[0],g=f.text;if(g===a||g===c||g===d||g===e||!(a||c||d||e))return f}return!1},expect:function(a,c,d,e){return(a=this.peek(a,c,d,
+e))?(this.json&&!a.json&&this.throwError("is not valid json",a),this.tokens.shift(),a):!1},consume:function(a){this.expect(a)||this.throwError("is unexpected, expecting ["+a+"]",this.peek())},unaryFn:function(a,c){return w(function(d,e){return a(d,e,c)},{constant:c.constant})},ternaryFn:function(a,c,d){return w(function(e,f){return a(e,f)?c(e,f):d(e,f)},{constant:a.constant&&c.constant&&d.constant})},binaryFn:function(a,c,d){return w(function(e,f){return c(e,f,a,d)},{constant:a.constant&&d.constant})},
+statements:function(){for(var a=[];;)if(0<this.tokens.length&&!this.peek("}",")",";","]")&&a.push(this.filterChain()),!this.expect(";"))return 1===a.length?a[0]:function(c,d){for(var e,f=0;f<a.length;f++){var g=a[f];g&&(e=g(c,d))}return e}},filterChain:function(){for(var a=this.expression(),c;;)if(c=this.expect("|"))a=this.binaryFn(a,c.fn,this.filter());else return a},filter:function(){for(var a=this.expect(),c=this.$filter(a.text),d=[];;)if(a=this.expect(":"))d.push(this.expression());else{var e=
+function(a,e,h){h=[h];for(var m=0;m<d.length;m++)h.push(d[m](a,e));return c.apply(a,h)};return function(){return e}}},expression:function(){return this.assignment()},assignment:function(){var a=this.ternary(),c,d;return(d=this.expect("="))?(a.assign||this.throwError("implies assignment but ["+this.text.substring(0,d.index)+"] can not be assigned to",d),c=this.ternary(),function(d,f){return a.assign(d,c(d,f),f)}):a},ternary:function(){var a=this.logicalOR(),c,d;if(this.expect("?")){c=this.ternary();
+if(d=this.expect(":"))return this.ternaryFn(a,c,this.ternary());this.throwError("expected :",d)}else return a},logicalOR:function(){for(var a=this.logicalAND(),c;;)if(c=this.expect("||"))a=this.binaryFn(a,c.fn,this.logicalAND());else return a},logicalAND:function(){var a=this.equality(),c;if(c=this.expect("&&"))a=this.binaryFn(a,c.fn,this.logicalAND());return a},equality:function(){var a=this.relational(),c;if(c=this.expect("==","!=","===","!=="))a=this.binaryFn(a,c.fn,this.equality());return a},
+relational:function(){var a=this.additive(),c;if(c=this.expect("<",">","<=",">="))a=this.binaryFn(a,c.fn,this.relational());return a},additive:function(){for(var a=this.multiplicative(),c;c=this.expect("+","-");)a=this.binaryFn(a,c.fn,this.multiplicative());return a},multiplicative:function(){for(var a=this.unary(),c;c=this.expect("*","/","%");)a=this.binaryFn(a,c.fn,this.unary());return a},unary:function(){var a;return this.expect("+")?this.primary():(a=this.expect("-"))?this.binaryFn($a.ZERO,a.fn,
+this.unary()):(a=this.expect("!"))?this.unaryFn(a.fn,this.unary()):this.primary()},fieldAccess:function(a){var c=this,d=this.expect().text,e=Kc(d,this.options,this.text);return w(function(c,d,h){return e(h||a(c,d))},{assign:function(e,g,h){return ub(a(e,h),d,g,c.text,c.options)}})},objectIndex:function(a){var c=this,d=this.expression();this.consume("]");return w(function(e,f){var g=a(e,f),h=d(e,f),m;if(!g)return s;(g=Za(g[h],c.text))&&(g.then&&c.options.unwrapPromises)&&(m=g,"$$v"in g||(m.$$v=s,m.then(function(a){m.$$v=
+a})),g=g.$$v);return g},{assign:function(e,f,g){var h=d(e,g);return Za(a(e,g),c.text)[h]=f}})},functionCall:function(a,c){var d=[];if(")"!==this.peekToken().text){do d.push(this.expression());while(this.expect(","))}this.consume(")");var e=this;return function(f,g){for(var h=[],m=c?c(f,g):f,k=0;k<d.length;k++)h.push(d[k](f,g));k=a(f,g,m)||D;Za(m,e.text);Za(k,e.text);h=k.apply?k.apply(m,h):k(h[0],h[1],h[2],h[3],h[4]);return Za(h,e.text)}},arrayDeclaration:function(){var a=[],c=!0;if("]"!==this.peekToken().text){do{if(this.peek("]"))break;
+var d=this.expression();a.push(d);d.constant||(c=!1)}while(this.expect(","))}this.consume("]");return w(function(c,d){for(var g=[],h=0;h<a.length;h++)g.push(a[h](c,d));return g},{literal:!0,constant:c})},object:function(){var a=[],c=!0;if("}"!==this.peekToken().text){do{if(this.peek("}"))break;var d=this.expect(),d=d.string||d.text;this.consume(":");var e=this.expression();a.push({key:d,value:e});e.constant||(c=!1)}while(this.expect(","))}this.consume("}");return w(function(c,d){for(var e={},m=0;m<
+a.length;m++){var k=a[m];e[k.key]=k.value(c,d)}return e},{literal:!0,constant:c})}};var Vb={},va=I("$sce"),fa={HTML:"html",CSS:"css",URL:"url",RESOURCE_URL:"resourceUrl",JS:"js"},V=S.createElement("a"),Oc=ta(N.location.href,!0);oc.$inject=["$provide"];Pc.$inject=["$locale"];Rc.$inject=["$locale"];var Uc=".",Xe={yyyy:X("FullYear",4),yy:X("FullYear",2,0,!0),y:X("FullYear",1),MMMM:wb("Month"),MMM:wb("Month",!0),MM:X("Month",2,1),M:X("Month",1,1),dd:X("Date",2),d:X("Date",1),HH:X("Hours",2),H:X("Hours",
+1),hh:X("Hours",2,-12),h:X("Hours",1,-12),mm:X("Minutes",2),m:X("Minutes",1),ss:X("Seconds",2),s:X("Seconds",1),sss:X("Milliseconds",3),EEEE:wb("Day"),EEE:wb("Day",!0),a:function(a,c){return 12>a.getHours()?c.AMPMS[0]:c.AMPMS[1]},Z:function(a){a=-1*a.getTimezoneOffset();return a=(0<=a?"+":"")+(vb(Math[0<a?"floor":"ceil"](a/60),2)+vb(Math.abs(a%60),2))},ww:Wc(2),w:Wc(1)},We=/((?:[^yMdHhmsaZEw']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|w+))(.*)/,Ve=/^\-?\d+$/;Qc.$inject=["$locale"];var Te=
+$(u),Ue=$(Fa);Sc.$inject=["$parse"];var pd=$({restrict:"E",compile:function(a,c){8>=P&&(c.href||c.name||c.$set("href",""),a.append(S.createComment("IE fix")));if(!c.href&&!c.xlinkHref&&!c.name)return function(a,c){var f="[object SVGAnimatedString]"===xa.call(c.prop("href"))?"xlink:href":"href";c.on("click",function(a){c.attr(f)||a.preventDefault()})}}}),Hb={};q(nb,function(a,c){if("multiple"!=a){var d=na("ng-"+c);Hb[d]=function(){return{priority:100,link:function(a,f,g){a.$watch(g[d],function(a){g.$set(c,
+!!a)})}}}}});q(["src","srcset","href"],function(a){var c=na("ng-"+a);Hb[c]=function(){return{priority:99,link:function(d,e,f){var g=a,h=a;"href"===a&&"[object SVGAnimatedString]"===xa.call(e.prop("href"))&&(h="xlinkHref",f.$attr[h]="xlink:href",g=null);f.$observe(c,function(a){a&&(f.$set(h,a),P&&g&&e.prop(g,f[h]))})}}}});var zb={$addControl:D,$removeControl:D,$setValidity:D,$setDirty:D,$setPristine:D};Xc.$inject=["$element","$attrs","$scope","$animate"];var Yc=function(a){return["$timeout",function(c){return{name:"form",
+restrict:a?"EAC":"E",controller:Xc,compile:function(){return{pre:function(a,e,f,g){if(!f.action){var h=function(a){a.preventDefault?a.preventDefault():a.returnValue=!1};sb(e[0],"submit",h);e.on("$destroy",function(){c(function(){Ua(e[0],"submit",h)},0,!1)})}var m=e.parent().controller("form"),k=f.name||f.ngForm;k&&ub(a,k,g,k);if(m)e.on("$destroy",function(){m.$removeControl(g);k&&ub(a,k,s,k);w(g,zb)})}}}}}]},qd=Yc(),Dd=Yc(!0),cf=/^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/,
+df=/^[a-z0-9!#$%&'*+/=?^_`{|}~.-]+@[a-z0-9-]+(\.[a-z0-9-]+)*$/i,ef=/^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/,Zc=/^(\d{4})-(\d{2})-(\d{2})$/,$c=/^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)$/,Yb=/^(\d{4})-W(\d\d)$/,ad=/^(\d{4})-(\d\d)$/,bd=/^(\d\d):(\d\d)$/,ff=/(\b|^)default(\b|$)/,cd={text:ab,date:bb("date",Zc,Bb(Zc,["yyyy","MM","dd"]),"yyyy-MM-dd"),"datetime-local":bb("datetimelocal",$c,Bb($c,["yyyy","MM","dd","HH","mm"]),"yyyy-MM-ddTHH:mm"),time:bb("time",bd,Bb(bd,["HH","mm"]),"HH:mm"),week:bb("week",Yb,function(a){if(qa(a))return a;
+if(x(a)){Yb.lastIndex=0;var c=Yb.exec(a);if(c){a=+c[1];var d=+c[2],c=Vc(a),d=7*(d-1);return new Date(a,0,c.getDate()+d)}}return NaN},"yyyy-Www"),month:bb("month",ad,Bb(ad,["yyyy","MM"]),"yyyy-MM"),number:function(a,c,d,e,f,g){ab(a,c,d,e,f,g);e.$parsers.push(function(a){var c=e.$isEmpty(a);if(c||ef.test(a))return e.$setValidity("number",!0),""===a?null:c?a:parseFloat(a);e.$setValidity("number",!1);return s});Ye(e,"number",c);e.$formatters.push(function(a){return e.$isEmpty(a)?"":""+a});d.min&&(a=function(a){var c=
+parseFloat(d.min);return pa(e,"min",e.$isEmpty(a)||a>=c,a)},e.$parsers.push(a),e.$formatters.push(a));d.max&&(a=function(a){var c=parseFloat(d.max);return pa(e,"max",e.$isEmpty(a)||a<=c,a)},e.$parsers.push(a),e.$formatters.push(a));e.$formatters.push(function(a){return pa(e,"number",e.$isEmpty(a)||Cb(a),a)})},url:function(a,c,d,e,f,g){ab(a,c,d,e,f,g);a=function(a){return pa(e,"url",e.$isEmpty(a)||cf.test(a),a)};e.$formatters.push(a);e.$parsers.push(a)},email:function(a,c,d,e,f,g){ab(a,c,d,e,f,g);
+a=function(a){return pa(e,"email",e.$isEmpty(a)||df.test(a),a)};e.$formatters.push(a);e.$parsers.push(a)},radio:function(a,c,d,e){A(d.name)&&c.attr("name",db());var f=function(f){c[0].checked&&a.$apply(function(){e.$setViewValue(d.value,f&&f.type)})};if(e.$options&&e.$options.updateOn)c.on(e.$options.updateOn,f);if(!e.$options||e.$options.updateOnDefault)c.on("click",f);e.$render=function(){c[0].checked=d.value==e.$viewValue};d.$observe("value",e.$render)},checkbox:function(a,c,d,e){var f=d.ngTrueValue,
+g=d.ngFalseValue;x(f)||(f=!0);x(g)||(g=!1);d=function(d){a.$apply(function(){e.$setViewValue(c[0].checked,d&&d.type)})};if(e.$options&&e.$options.updateOn)c.on(e.$options.updateOn,d);if(!e.$options||e.$options.updateOnDefault)c.on("click",d);e.$render=function(){c[0].checked=e.$viewValue};e.$isEmpty=function(a){return a!==f};e.$formatters.push(function(a){return a===f});e.$parsers.push(function(a){return a?f:g})},hidden:D,button:D,submit:D,reset:D,file:D},lc=["$browser","$sniffer","$filter",function(a,
+c,d){return{restrict:"E",require:["?ngModel"],link:function(e,f,g,h){h[0]&&(cd[u(g.type)]||cd.text)(e,f,g,h[0],c,a,d)}}}],yb="ng-valid",xb="ng-invalid",Ma="ng-pristine",Ab="ng-dirty",gf=["$scope","$exceptionHandler","$attrs","$element","$parse","$animate","$timeout",function(a,c,d,e,f,g,h){function m(a,c){c=c?"-"+ib(c,"-"):"";g.removeClass(e,(a?xb:yb)+c);g.addClass(e,(a?yb:xb)+c)}this.$modelValue=this.$viewValue=Number.NaN;this.$parsers=[];this.$formatters=[];this.$viewChangeListeners=[];this.$pristine=
+!0;this.$dirty=!1;this.$valid=!0;this.$invalid=!1;this.$name=d.name;var k=f(d.ngModel),l=k.assign,p=null,n=this;if(!l)throw I("ngModel")("nonassign",d.ngModel,ga(e));this.$render=D;this.$isEmpty=function(a){return A(a)||""===a||null===a||a!==a};var r=e.inheritedData("$formController")||zb,t=0,s=this.$error={};e.addClass(Ma);m(!0);this.$setValidity=function(a,c){s[a]!==!c&&(c?(s[a]&&t--,t||(m(!0),n.$valid=!0,n.$invalid=!1)):(m(!1),n.$invalid=!0,n.$valid=!1,t++),s[a]=!c,m(c,a),r.$setValidity(a,c,n))};
+this.$setPristine=function(){n.$dirty=!1;n.$pristine=!0;g.removeClass(e,Ab);g.addClass(e,Ma)};this.$cancelUpdate=function(){h.cancel(p);n.$render()};this.$$realSetViewValue=function(d){n.$viewValue=d;n.$pristine&&(n.$dirty=!0,n.$pristine=!1,g.removeClass(e,Ma),g.addClass(e,Ab),r.$setDirty());q(n.$parsers,function(a){d=a(d)});n.$modelValue!==d&&(n.$modelValue=d,l(a,d),q(n.$viewChangeListeners,function(a){try{a()}catch(d){c(d)}}))};this.$setViewValue=function(a,c){var d=n.$options&&(R(n.$options.debounce)?
+n.$options.debounce[c]||n.$options.debounce["default"]||0:n.$options.debounce)||0;h.cancel(p);d?p=h(function(){n.$$realSetViewValue(a)},d):n.$$realSetViewValue(a)};a.$watch(function(){var c=k(a);if(n.$modelValue!==c){var d=n.$formatters,e=d.length;for(n.$modelValue=c;e--;)c=d[e](c);n.$viewValue!==c&&(n.$viewValue=c,n.$render())}return c})}],Sd=function(){return{require:["ngModel","^?form","^?ngModelOptions"],controller:gf,link:function(a,c,d,e){var f=e[0],g=e[1]||zb;g.$addControl(f);e[2]&&(f.$options=
+e[2].$options);a.$on("$destroy",function(){g.$removeControl(f)})}}},Ud=$({require:"ngModel",link:function(a,c,d,e){e.$viewChangeListeners.push(function(){a.$eval(d.ngChange)})}}),mc=function(){return{require:"?ngModel",link:function(a,c,d,e){if(e){d.required=!0;var f=function(a){if(d.required&&e.$isEmpty(a))e.$setValidity("required",!1);else return e.$setValidity("required",!0),a};e.$formatters.push(f);e.$parsers.unshift(f);d.$observe("required",function(){f(e.$viewValue)})}}}},Td=function(){return{require:"ngModel",
+link:function(a,c,d,e){var f=(a=/\/(.*)\//.exec(d.ngList))&&RegExp(a[1])||d.ngList||",";e.$parsers.push(function(a){if(!A(a)){var c=[];a&&q(a.split(f),function(a){a&&c.push(Y(a))});return c}});e.$formatters.push(function(a){return M(a)?a.join(", "):s});e.$isEmpty=function(a){return!a||!a.length}}}},hf=/^(true|false|\d+)$/,Vd=function(){return{priority:100,compile:function(a,c){return hf.test(c.ngValue)?function(a,c,f){f.$set("value",a.$eval(f.ngValue))}:function(a,c,f){a.$watch(f.ngValue,function(a){f.$set("value",
+a)})}}}},Wd=function(){return{controller:["$scope","$attrs",function(a,c){var d=this;this.$options=a.$eval(c.ngModelOptions);this.$options.updateOn?(this.$options.updateOnDefault=!1,this.$options.updateOn=this.$options.updateOn.replace(ff,function(){d.$options.updateOnDefault=!0;return" "})):this.$options.updateOnDefault=!0}]}},vd=wa(function(a,c,d){c.addClass("ng-binding").data("$binding",d.ngBind);a.$watch(d.ngBind,function(a){c.text(a==s?"":a)})}),xd=["$interpolate",function(a){return function(c,
+d,e){c=a(d.attr(e.$attr.ngBindTemplate));d.addClass("ng-binding").data("$binding",c);e.$observe("ngBindTemplate",function(a){d.text(a)})}}],wd=["$sce","$parse",function(a,c){return function(d,e,f){e.addClass("ng-binding").data("$binding",f.ngBindHtml);var g=c(f.ngBindHtml);d.$watch(function(){return(g(d)||"").toString()},function(c){e.html(a.getTrustedHtml(g(d))||"")})}}],yd=Xb("",!0),Ad=Xb("Odd",0),zd=Xb("Even",1),Bd=wa({compile:function(a,c){c.$set("ngCloak",s);a.removeClass("ng-cloak")}}),Cd=[function(){return{scope:!0,
+controller:"@",priority:500}}],nc={};q("click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste".split(" "),function(a){var c=na("ng-"+a);nc[c]=["$parse",function(d){return{compile:function(e,f){var g=d(f[c]);return function(c,d,e){d.on(u(a),function(a){c.$apply(function(){g(c,{$event:a})})})}}}}]});var Fd=["$animate",function(a){return{transclude:"element",priority:600,terminal:!0,restrict:"A",$$tlb:!0,link:function(c,
+d,e,f,g){var h,m,k;c.$watch(e.ngIf,function(f){Pa(f)?m||(m=c.$new(),g(m,function(c){c[c.length++]=S.createComment(" end ngIf: "+e.ngIf+" ");h={clone:c};a.enter(c,d.parent(),d)})):(k&&(k.remove(),k=null),m&&(m.$destroy(),m=null),h&&(k=Gb(h.clone),a.leave(k,function(){k=null}),h=null))})}}}],Gd=["$http","$templateCache","$anchorScroll","$animate","$sce",function(a,c,d,e,f){return{restrict:"ECA",priority:400,terminal:!0,transclude:"element",controller:Qa.noop,compile:function(g,h){var m=h.ngInclude||
+h.src,k=h.onload||"",l=h.autoscroll;return function(g,h,q,t,s){var E=0,B,z,y,v=function(){z&&(z.remove(),z=null);B&&(B.$destroy(),B=null);y&&(e.leave(y,function(){z=null}),z=y,y=null)};g.$watch(f.parseAsResourceUrl(m),function(f){var m=function(){!F(l)||l&&!g.$eval(l)||d()},q=++E;f?(a.get(f,{cache:c}).success(function(a){if(q===E){var c=g.$new();t.template=a;a=s(c,function(a){v();e.enter(a,null,h,m)});B=c;y=a;B.$emit("$includeContentLoaded");g.$eval(k)}}).error(function(){q===E&&v()}),g.$emit("$includeContentRequested")):
+(v(),t.template=null)})}}}}],Xd=["$compile",function(a){return{restrict:"ECA",priority:-400,require:"ngInclude",link:function(c,d,e,f){d.html(f.template);a(d.contents())(c)}}}],Hd=wa({priority:450,compile:function(){return{pre:function(a,c,d){a.$eval(d.ngInit)}}}}),Id=wa({terminal:!0,priority:1E3}),Jd=["$locale","$interpolate",function(a,c){var d=/{}/g;return{restrict:"EA",link:function(e,f,g){var h=g.count,m=g.$attr.when&&f.attr(g.$attr.when),k=g.offset||0,l=e.$eval(m)||{},p={},n=c.startSymbol(),
+r=c.endSymbol(),t=/^when(Minus)?(.+)$/;q(g,function(a,c){t.test(c)&&(l[u(c.replace("when","").replace("Minus","-"))]=f.attr(g.$attr[c]))});q(l,function(a,e){p[e]=c(a.replace(d,n+h+"-"+k+r))});e.$watch(function(){var c=parseFloat(e.$eval(h));if(isNaN(c))return"";c in l||(c=a.pluralCat(c-k));return p[c](e)},function(a){f.text(a)})}}}],Kd=["$parse","$animate",function(a,c){var d=I("ngRepeat");return{transclude:"element",priority:1E3,terminal:!0,$$tlb:!0,link:function(e,f,g,h,m){var k=g.ngRepeat,l=k.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?\s*$/),
+p,n,r,t,s,E,B={$id:Ia};if(!l)throw d("iexp",k);g=l[1];h=l[2];(l=l[3])?(p=a(l),n=function(a,c,d){E&&(B[E]=a);B[s]=c;B.$index=d;return p(e,B)}):(r=function(a,c){return Ia(c)},t=function(a){return a});l=g.match(/^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\))$/);if(!l)throw d("iidexp",g);s=l[3]||l[1];E=l[2];var z={};e.$watchCollection(h,function(a){var g,h,l=f[0],p,B={},F,C,u,x,D,w,A=[];if(cb(a))D=a,p=n||r;else{p=n||t;D=[];for(u in a)a.hasOwnProperty(u)&&"$"!=u.charAt(0)&&D.push(u);D.sort()}F=D.length;
+h=A.length=D.length;for(g=0;g<h;g++)if(u=a===D?g:D[g],x=a[u],x=p(u,x,g),Aa(x,"`track by` id"),z.hasOwnProperty(x))w=z[x],delete z[x],B[x]=w,A[g]=w;else{if(B.hasOwnProperty(x))throw q(A,function(a){a&&a.scope&&(z[a.id]=a)}),d("dupes",k,x);A[g]={id:x};B[x]=!1}for(u in z)z.hasOwnProperty(u)&&(w=z[u],g=Gb(w.clone),c.leave(g),q(g,function(a){a.$$NG_REMOVED=!0}),w.scope.$destroy());g=0;for(h=D.length;g<h;g++){u=a===D?g:D[g];x=a[u];w=A[g];A[g-1]&&(l=A[g-1].clone[A[g-1].clone.length-1]);if(w.scope){C=w.scope;
+p=l;do p=p.nextSibling;while(p&&p.$$NG_REMOVED);w.clone[0]!=p&&c.move(Gb(w.clone),null,y(l));l=w.clone[w.clone.length-1]}else C=e.$new();C[s]=x;E&&(C[E]=u);C.$index=g;C.$first=0===g;C.$last=g===F-1;C.$middle=!(C.$first||C.$last);C.$odd=!(C.$even=0===(g&1));w.scope||m(C,function(a){a[a.length++]=S.createComment(" end ngRepeat: "+k+" ");c.enter(a,null,y(l));l=a;w.scope=C;w.clone=a;B[w.id]=w})}z=B})}}}],Ld=["$animate",function(a){return function(c,d,e){c.$watch(e.ngShow,function(c){a[Pa(c)?"removeClass":
+"addClass"](d,"ng-hide")})}}],Ed=["$animate",function(a){return function(c,d,e){c.$watch(e.ngHide,function(c){a[Pa(c)?"addClass":"removeClass"](d,"ng-hide")})}}],Md=wa(function(a,c,d){a.$watch(d.ngStyle,function(a,d){d&&a!==d&&q(d,function(a,d){c.css(d,"")});a&&c.css(a)},!0)}),Nd=["$animate",function(a){return{restrict:"EA",require:"ngSwitch",controller:["$scope",function(){this.cases={}}],link:function(c,d,e,f){var g,h,m,k=[];c.$watch(e.ngSwitch||e.on,function(d){var p,n=k.length;if(0<n){if(m){for(p=
+0;p<n;p++)m[p].remove();m=null}m=[];for(p=0;p<n;p++){var r=h[p];k[p].$destroy();m[p]=r;a.leave(r,function(){m.splice(p,1);0===m.length&&(m=null)})}}h=[];k=[];if(g=f.cases["!"+d]||f.cases["?"])c.$eval(e.change),q(g,function(d){var e=c.$new();k.push(e);d.transclude(e,function(c){var e=d.element;h.push(c);a.enter(c,e.parent(),e)})})})}}}],Od=wa({transclude:"element",priority:800,require:"^ngSwitch",link:function(a,c,d,e,f){e.cases["!"+d.ngSwitchWhen]=e.cases["!"+d.ngSwitchWhen]||[];e.cases["!"+d.ngSwitchWhen].push({transclude:f,
+element:c})}}),Pd=wa({transclude:"element",priority:800,require:"^ngSwitch",link:function(a,c,d,e,f){e.cases["?"]=e.cases["?"]||[];e.cases["?"].push({transclude:f,element:c})}}),Rd=wa({link:function(a,c,d,e,f){if(!f)throw I("ngTransclude")("orphan",ga(c));f(function(a){c.empty();c.append(a)})}}),rd=["$templateCache",function(a){return{restrict:"E",terminal:!0,compile:function(c,d){"text/ng-template"==d.type&&a.put(d.id,c[0].text)}}}],jf=I("ngOptions"),Qd=$({terminal:!0}),sd=["$compile","$parse",function(a,
+c){var d=/^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/,e={$setViewValue:D};return{restrict:"E",require:["select","?ngModel"],controller:["$element","$scope","$attrs",function(a,c,d){var m=this,k={},l=e,p;m.databound=d.ngModel;m.init=function(a,c,d){l=a;p=d};m.addOption=function(c){Aa(c,'"option value"');k[c]=!0;l.$viewValue==c&&(a.val(c),p.parent()&&
+p.remove())};m.removeOption=function(a){this.hasOption(a)&&(delete k[a],l.$viewValue==a&&this.renderUnknownOption(a))};m.renderUnknownOption=function(c){c="? "+Ia(c)+" ?";p.val(c);a.prepend(p);a.val(c);p.prop("selected",!0)};m.hasOption=function(a){return k.hasOwnProperty(a)};c.$on("$destroy",function(){m.renderUnknownOption=D})}],link:function(e,g,h,m){function k(a,c,d,e){d.$render=function(){var a=d.$viewValue;e.hasOption(a)?(w.parent()&&w.remove(),c.val(a),""===a&&x.prop("selected",!0)):A(a)&&
+x?c.val(""):e.renderUnknownOption(a)};c.on("change",function(){a.$apply(function(){w.parent()&&w.remove();d.$setViewValue(c.val())})})}function l(a,c,d){var e;d.$render=function(){var a=new Wa(d.$viewValue);q(c.find("option"),function(c){c.selected=F(a.get(c.value))})};a.$watch(function(){ya(e,d.$viewValue)||(e=aa(d.$viewValue),d.$render())});c.on("change",function(){a.$apply(function(){var a=[];q(c.find("option"),function(c){c.selected&&a.push(c.value)});d.$setViewValue(a)})})}function p(e,f,g){function h(){var a=
+{"":[]},c=[""],d,k,s,t,w;t=g.$modelValue;w=y(e)||[];var D=n?Zb(w):w,E,A,C;A={};s=!1;var G,I;if(r)if(v&&M(t))for(s=new Wa([]),C=0;C<t.length;C++)A[m]=t[C],s.put(v(e,A),t[C]);else s=new Wa(t);for(C=0;E=D.length,C<E;C++){k=C;if(n){k=D[C];if("$"===k.charAt(0))continue;A[n]=k}A[m]=w[k];d=p(e,A)||"";(k=a[d])||(k=a[d]=[],c.push(d));r?d=F(s.remove(v?v(e,A):q(e,A))):(v?(d={},d[m]=t,d=v(e,d)===v(e,A)):d=t===q(e,A),s=s||d);G=l(e,A);G=F(G)?G:"";k.push({id:v?v(e,A):n?D[C]:C,label:G,selected:d})}r||(u||null===
+t?a[""].unshift({id:"",label:"",selected:!s}):s||a[""].unshift({id:"?",label:"",selected:!0}));A=0;for(D=c.length;A<D;A++){d=c[A];k=a[d];x.length<=A?(t={element:z.clone().attr("label",d),label:k.label},w=[t],x.push(w),f.append(t.element)):(w=x[A],t=w[0],t.label!=d&&t.element.attr("label",t.label=d));G=null;C=0;for(E=k.length;C<E;C++)s=k[C],(d=w[C+1])?(G=d.element,d.label!==s.label&&G.text(d.label=s.label),d.id!==s.id&&G.val(d.id=s.id),d.selected!==s.selected&&G.prop("selected",d.selected=s.selected)):
+(""===s.id&&u?I=u:(I=B.clone()).val(s.id).attr("selected",s.selected).text(s.label),w.push({element:I,label:s.label,id:s.id,selected:s.selected}),G?G.after(I):t.element.append(I),G=I);for(C++;w.length>C;)w.pop().element.remove()}for(;x.length>A;)x.pop()[0].element.remove()}var k;if(!(k=t.match(d)))throw jf("iexp",t,ga(f));var l=c(k[2]||k[1]),m=k[4]||k[6],n=k[5],p=c(k[3]||""),q=c(k[2]?k[1]:m),y=c(k[7]),v=k[8]?c(k[8]):null,x=[[{element:f,label:""}]];u&&(a(u)(e),u.removeClass("ng-scope"),u.remove());
+f.empty();f.on("change",function(){e.$apply(function(){var a,c=y(e)||[],d={},h,k,l,p,t,w,u;if(r)for(k=[],p=0,w=x.length;p<w;p++)for(a=x[p],l=1,t=a.length;l<t;l++){if((h=a[l].element)[0].selected){h=h.val();n&&(d[n]=h);if(v)for(u=0;u<c.length&&(d[m]=c[u],v(e,d)!=h);u++);else d[m]=c[h];k.push(q(e,d))}}else{h=f.val();if("?"==h)k=s;else if(""===h)k=null;else if(v)for(u=0;u<c.length;u++){if(d[m]=c[u],v(e,d)==h){k=q(e,d);break}}else d[m]=c[h],n&&(d[n]=h),k=q(e,d);1<x[0].length&&x[0][1].id!==h&&(x[0][1].selected=
+!1)}g.$setViewValue(k)})});g.$render=h;e.$watch(h)}if(m[1]){var n=m[0];m=m[1];var r=h.multiple,t=h.ngOptions,u=!1,x,B=y(S.createElement("option")),z=y(S.createElement("optgroup")),w=B.clone();h=0;for(var v=g.children(),D=v.length;h<D;h++)if(""===v[h].value){x=u=v.eq(h);break}n.init(m,u,w);r&&(m.$isEmpty=function(a){return!a||0===a.length});t?p(e,g,m):r?l(e,g,m):k(e,g,m,n)}}}}],ud=["$interpolate",function(a){var c={addOption:D,removeOption:D};return{restrict:"E",priority:100,compile:function(d,e){if(A(e.value)){var f=
+a(d.text(),!0);f||e.$set("value",d.text())}return function(a,d,e){var k=d.parent(),l=k.data("$selectController")||k.parent().data("$selectController");l&&l.databound?d.prop("selected",!1):l=c;f?a.$watch(f,function(a,c){e.$set("value",a);c!==a&&l.removeOption(c);l.addOption(a)}):l.addOption(e.value);d.on("$destroy",function(){l.removeOption(e.value)})}}}}],td=$({restrict:"E",terminal:!1});N.angular.bootstrap?console.log("WARNING: Tried to load angular more than once."):((Ga=N.jQuery)?(y=Ga,w(Ga.fn,
+{scope:Ka.scope,isolateScope:Ka.isolateScope,controller:Ka.controller,injector:Ka.injector,inheritedData:Ka.inheritedData}),Ib("remove",!0,!0,!1),Ib("empty",!1,!1,!1),Ib("html",!1,!1,!0)):y=U,Qa.element=y,ld(Qa),y(S).ready(function(){id(S,ic)}))})(window,document);!angular.$$csp()&&angular.element(document).find("head").prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide{display:none !important;}ng\\:form{display:block;}</style>');
+//# sourceMappingURL=angular.min.js.map
+
diff --git a/gz3d/client/js/include/eventemitter2.js b/gz3d/client/js/include/eventemitter2.js
new file mode 100644
index 0000000000000000000000000000000000000000..7c65c8dc8c63be141e5627d0720ee0f4bef4d4ab
--- /dev/null
+++ b/gz3d/client/js/include/eventemitter2.js
@@ -0,0 +1,560 @@
+;!function(exports, undefined) {
+
+  var isArray = Array.isArray ? Array.isArray : function _isArray(obj) {
+    return Object.prototype.toString.call(obj) === "[object Array]";
+  };
+  var defaultMaxListeners = 10;
+
+  function init() {
+    this._events = {};
+    if (this._conf) {
+      configure.call(this, this._conf);
+    }
+  }
+
+  function configure(conf) {
+    if (conf) {
+
+      this._conf = conf;
+
+      conf.delimiter && (this.delimiter = conf.delimiter);
+      conf.maxListeners && (this._events.maxListeners = conf.maxListeners);
+      conf.wildcard && (this.wildcard = conf.wildcard);
+      conf.newListener && (this.newListener = conf.newListener);
+
+      if (this.wildcard) {
+        this.listenerTree = {};
+      }
+    }
+  }
+
+  function EventEmitter(conf) {
+    this._events = {};
+    this.newListener = false;
+    configure.call(this, conf);
+  }
+
+  //
+  // Attention, function return type now is array, always !
+  // It has zero elements if no any matches found and one or more
+  // elements (leafs) if there are matches
+  //
+  function searchListenerTree(handlers, type, tree, i) {
+    if (!tree) {
+      return [];
+    }
+    var listeners=[], leaf, len, branch, xTree, xxTree, isolatedBranch, endReached,
+        typeLength = type.length, currentType = type[i], nextType = type[i+1];
+    if (i === typeLength && tree._listeners) {
+      //
+      // If at the end of the event(s) list and the tree has listeners
+      // invoke those listeners.
+      //
+      if (typeof tree._listeners === 'function') {
+        handlers && handlers.push(tree._listeners);
+        return [tree];
+      } else {
+        for (leaf = 0, len = tree._listeners.length; leaf < len; leaf++) {
+          handlers && handlers.push(tree._listeners[leaf]);
+        }
+        return [tree];
+      }
+    }
+
+    if ((currentType === '*' || currentType === '**') || tree[currentType]) {
+      //
+      // If the event emitted is '*' at this part
+      // or there is a concrete match at this patch
+      //
+      if (currentType === '*') {
+        for (branch in tree) {
+          if (branch !== '_listeners' && tree.hasOwnProperty(branch)) {
+            listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i+1));
+          }
+        }
+        return listeners;
+      } else if(currentType === '**') {
+        endReached = (i+1 === typeLength || (i+2 === typeLength && nextType === '*'));
+        if(endReached && tree._listeners) {
+          // The next element has a _listeners, add it to the handlers.
+          listeners = listeners.concat(searchListenerTree(handlers, type, tree, typeLength));
+        }
+
+        for (branch in tree) {
+          if (branch !== '_listeners' && tree.hasOwnProperty(branch)) {
+            if(branch === '*' || branch === '**') {
+              if(tree[branch]._listeners && !endReached) {
+                listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], typeLength));
+              }
+              listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i));
+            } else if(branch === nextType) {
+              listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i+2));
+            } else {
+              // No match on this one, shift into the tree but not in the type array.
+              listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i));
+            }
+          }
+        }
+        return listeners;
+      }
+
+      listeners = listeners.concat(searchListenerTree(handlers, type, tree[currentType], i+1));
+    }
+
+    xTree = tree['*'];
+    if (xTree) {
+      //
+      // If the listener tree will allow any match for this part,
+      // then recursively explore all branches of the tree
+      //
+      searchListenerTree(handlers, type, xTree, i+1);
+    }
+
+    xxTree = tree['**'];
+    if(xxTree) {
+      if(i < typeLength) {
+        if(xxTree._listeners) {
+          // If we have a listener on a '**', it will catch all, so add its handler.
+          searchListenerTree(handlers, type, xxTree, typeLength);
+        }
+
+        // Build arrays of matching next branches and others.
+        for(branch in xxTree) {
+          if(branch !== '_listeners' && xxTree.hasOwnProperty(branch)) {
+            if(branch === nextType) {
+              // We know the next element will match, so jump twice.
+              searchListenerTree(handlers, type, xxTree[branch], i+2);
+            } else if(branch === currentType) {
+              // Current node matches, move into the tree.
+              searchListenerTree(handlers, type, xxTree[branch], i+1);
+            } else {
+              isolatedBranch = {};
+              isolatedBranch[branch] = xxTree[branch];
+              searchListenerTree(handlers, type, { '**': isolatedBranch }, i+1);
+            }
+          }
+        }
+      } else if(xxTree._listeners) {
+        // We have reached the end and still on a '**'
+        searchListenerTree(handlers, type, xxTree, typeLength);
+      } else if(xxTree['*'] && xxTree['*']._listeners) {
+        searchListenerTree(handlers, type, xxTree['*'], typeLength);
+      }
+    }
+
+    return listeners;
+  }
+
+  function growListenerTree(type, listener) {
+
+    type = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
+
+    //
+    // Looks for two consecutive '**', if so, don't add the event at all.
+    //
+    for(var i = 0, len = type.length; i+1 < len; i++) {
+      if(type[i] === '**' && type[i+1] === '**') {
+        return;
+      }
+    }
+
+    var tree = this.listenerTree;
+    var name = type.shift();
+
+    while (name) {
+
+      if (!tree[name]) {
+        tree[name] = {};
+      }
+
+      tree = tree[name];
+
+      if (type.length === 0) {
+
+        if (!tree._listeners) {
+          tree._listeners = listener;
+        }
+        else if(typeof tree._listeners === 'function') {
+          tree._listeners = [tree._listeners, listener];
+        }
+        else if (isArray(tree._listeners)) {
+
+          tree._listeners.push(listener);
+
+          if (!tree._listeners.warned) {
+
+            var m = defaultMaxListeners;
+
+            if (typeof this._events.maxListeners !== 'undefined') {
+              m = this._events.maxListeners;
+            }
+
+            if (m > 0 && tree._listeners.length > m) {
+
+              tree._listeners.warned = true;
+              console.error('(node) warning: possible EventEmitter memory ' +
+                            'leak detected. %d listeners added. ' +
+                            'Use emitter.setMaxListeners() to increase limit.',
+                            tree._listeners.length);
+              console.trace();
+            }
+          }
+        }
+        return true;
+      }
+      name = type.shift();
+    }
+    return true;
+  };
+
+  // By default EventEmitters will print a warning if more than
+  // 10 listeners are added to it. This is a useful default which
+  // helps finding memory leaks.
+  //
+  // Obviously not all Emitters should be limited to 10. This function allows
+  // that to be increased. Set to zero for unlimited.
+
+  EventEmitter.prototype.delimiter = '.';
+
+  EventEmitter.prototype.setMaxListeners = function(n) {
+    this._events || init.call(this);
+    this._events.maxListeners = n;
+    if (!this._conf) this._conf = {};
+    this._conf.maxListeners = n;
+  };
+
+  EventEmitter.prototype.event = '';
+
+  EventEmitter.prototype.once = function(event, fn) {
+    this.many(event, 1, fn);
+    return this;
+  };
+
+  EventEmitter.prototype.many = function(event, ttl, fn) {
+    var self = this;
+
+    if (typeof fn !== 'function') {
+      throw new Error('many only accepts instances of Function');
+    }
+
+    function listener() {
+      if (--ttl === 0) {
+        self.off(event, listener);
+      }
+      fn.apply(this, arguments);
+    };
+
+    listener._origin = fn;
+
+    this.on(event, listener);
+
+    return self;
+  };
+
+  EventEmitter.prototype.emit = function() {
+
+    this._events || init.call(this);
+
+    var type = arguments[0];
+
+    if (type === 'newListener' && !this.newListener) {
+      if (!this._events.newListener) { return false; }
+    }
+
+    // Loop through the *_all* functions and invoke them.
+    if (this._all) {
+      var l = arguments.length;
+      var args = new Array(l - 1);
+      for (var i = 1; i < l; i++) args[i - 1] = arguments[i];
+      for (i = 0, l = this._all.length; i < l; i++) {
+        this.event = type;
+        this._all[i].apply(this, args);
+      }
+    }
+
+    // If there is no 'error' event listener then throw.
+    if (type === 'error') {
+
+      if (!this._all &&
+        !this._events.error &&
+        !(this.wildcard && this.listenerTree.error)) {
+
+        if (arguments[1] instanceof Error) {
+          throw arguments[1]; // Unhandled 'error' event
+        } else {
+          throw new Error("Uncaught, unspecified 'error' event.");
+        }
+        return false;
+      }
+    }
+
+    var handler;
+
+    if(this.wildcard) {
+      handler = [];
+      var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
+      searchListenerTree.call(this, handler, ns, this.listenerTree, 0);
+    }
+    else {
+      handler = this._events[type];
+    }
+
+    if (typeof handler === 'function') {
+      this.event = type;
+      if (arguments.length === 1) {
+        handler.call(this);
+      }
+      else if (arguments.length > 1)
+        switch (arguments.length) {
+          case 2:
+            handler.call(this, arguments[1]);
+            break;
+          case 3:
+            handler.call(this, arguments[1], arguments[2]);
+            break;
+          // slower
+          default:
+            var l = arguments.length;
+            var args = new Array(l - 1);
+            for (var i = 1; i < l; i++) args[i - 1] = arguments[i];
+            handler.apply(this, args);
+        }
+      return true;
+    }
+    else if (handler) {
+      var l = arguments.length;
+      var args = new Array(l - 1);
+      for (var i = 1; i < l; i++) args[i - 1] = arguments[i];
+
+      var listeners = handler.slice();
+      for (var i = 0, l = listeners.length; i < l; i++) {
+        this.event = type;
+        listeners[i].apply(this, args);
+      }
+      return (listeners.length > 0) || this._all;
+    }
+    else {
+      return this._all;
+    }
+
+  };
+
+  EventEmitter.prototype.on = function(type, listener) {
+
+    if (typeof type === 'function') {
+      this.onAny(type);
+      return this;
+    }
+
+    if (typeof listener !== 'function') {
+      throw new Error('on only accepts instances of Function');
+    }
+    this._events || init.call(this);
+
+    // To avoid recursion in the case that type == "newListeners"! Before
+    // adding it to the listeners, first emit "newListeners".
+    this.emit('newListener', type, listener);
+
+    if(this.wildcard) {
+      growListenerTree.call(this, type, listener);
+      return this;
+    }
+
+    if (!this._events[type]) {
+      // Optimize the case of one listener. Don't need the extra array object.
+      this._events[type] = listener;
+    }
+    else if(typeof this._events[type] === 'function') {
+      // Adding the second element, need to change to array.
+      this._events[type] = [this._events[type], listener];
+    }
+    else if (isArray(this._events[type])) {
+      // If we've already got an array, just append.
+      this._events[type].push(listener);
+
+      // Check for listener leak
+      if (!this._events[type].warned) {
+
+        var m = defaultMaxListeners;
+
+        if (typeof this._events.maxListeners !== 'undefined') {
+          m = this._events.maxListeners;
+        }
+
+        if (m > 0 && this._events[type].length > m) {
+
+          this._events[type].warned = true;
+          console.error('(node) warning: possible EventEmitter memory ' +
+                        'leak detected. %d listeners added. ' +
+                        'Use emitter.setMaxListeners() to increase limit.',
+                        this._events[type].length);
+          console.trace();
+        }
+      }
+    }
+    return this;
+  };
+
+  EventEmitter.prototype.onAny = function(fn) {
+
+    if(!this._all) {
+      this._all = [];
+    }
+
+    if (typeof fn !== 'function') {
+      throw new Error('onAny only accepts instances of Function');
+    }
+
+    // Add the function to the event listener collection.
+    this._all.push(fn);
+    return this;
+  };
+
+  EventEmitter.prototype.addListener = EventEmitter.prototype.on;
+
+  EventEmitter.prototype.off = function(type, listener) {
+    if (typeof listener !== 'function') {
+      throw new Error('removeListener only takes instances of Function');
+    }
+
+    var handlers,leafs=[];
+
+    if(this.wildcard) {
+      var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
+      leafs = searchListenerTree.call(this, null, ns, this.listenerTree, 0);
+    }
+    else {
+      // does not use listeners(), so no side effect of creating _events[type]
+      if (!this._events[type]) return this;
+      handlers = this._events[type];
+      leafs.push({_listeners:handlers});
+    }
+
+    for (var iLeaf=0; iLeaf<leafs.length; iLeaf++) {
+      var leaf = leafs[iLeaf];
+      handlers = leaf._listeners;
+      if (isArray(handlers)) {
+
+        var position = -1;
+
+        for (var i = 0, length = handlers.length; i < length; i++) {
+          if (handlers[i] === listener ||
+            (handlers[i].listener && handlers[i].listener === listener) ||
+            (handlers[i]._origin && handlers[i]._origin === listener)) {
+            position = i;
+            break;
+          }
+        }
+
+        if (position < 0) {
+          return this;
+        }
+
+        if(this.wildcard) {
+          leaf._listeners.splice(position, 1);
+        }
+        else {
+          this._events[type].splice(position, 1);
+        }
+
+        if (handlers.length === 0) {
+          if(this.wildcard) {
+            delete leaf._listeners;
+          }
+          else {
+            delete this._events[type];
+          }
+        }
+      }
+      else if (handlers === listener ||
+        (handlers.listener && handlers.listener === listener) ||
+        (handlers._origin && handlers._origin === listener)) {
+        if(this.wildcard) {
+          delete leaf._listeners;
+        }
+        else {
+          delete this._events[type];
+        }
+      }
+    }
+
+    return this;
+  };
+
+  EventEmitter.prototype.offAny = function(fn) {
+    var i = 0, l = 0, fns;
+    if (fn && this._all && this._all.length > 0) {
+      fns = this._all;
+      for(i = 0, l = fns.length; i < l; i++) {
+        if(fn === fns[i]) {
+          fns.splice(i, 1);
+          return this;
+        }
+      }
+    } else {
+      this._all = [];
+    }
+    return this;
+  };
+
+  EventEmitter.prototype.removeListener = EventEmitter.prototype.off;
+
+  EventEmitter.prototype.removeAllListeners = function(type) {
+    if (arguments.length === 0) {
+      !this._events || init.call(this);
+      return this;
+    }
+
+    if(this.wildcard) {
+      var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
+      var leafs = searchListenerTree.call(this, null, ns, this.listenerTree, 0);
+
+      for (var iLeaf=0; iLeaf<leafs.length; iLeaf++) {
+        var leaf = leafs[iLeaf];
+        leaf._listeners = null;
+      }
+    }
+    else {
+      if (!this._events[type]) return this;
+      this._events[type] = null;
+    }
+    return this;
+  };
+
+  EventEmitter.prototype.listeners = function(type) {
+    if(this.wildcard) {
+      var handlers = [];
+      var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
+      searchListenerTree.call(this, handlers, ns, this.listenerTree, 0);
+      return handlers;
+    }
+
+    this._events || init.call(this);
+
+    if (!this._events[type]) this._events[type] = [];
+    if (!isArray(this._events[type])) {
+      this._events[type] = [this._events[type]];
+    }
+    return this._events[type];
+  };
+
+  EventEmitter.prototype.listenersAny = function() {
+
+    if(this._all) {
+      return this._all;
+    }
+    else {
+      return [];
+    }
+
+  };
+
+  if (typeof define === 'function' && define.amd) {
+    define(function() {
+      return EventEmitter;
+    });
+  } else {
+    exports.EventEmitter2 = EventEmitter;
+  }
+
+}(typeof process !== 'undefined' && typeof process.title !== 'undefined' && typeof exports !== 'undefined' ? exports : window);
diff --git a/gz3d/client/js/include/gzwebsocket.js b/gz3d/client/js/include/gzwebsocket.js
new file mode 100644
index 0000000000000000000000000000000000000000..aaf36ce071286cf35fdd49216a1413103db0b1a7
--- /dev/null
+++ b/gz3d/client/js/include/gzwebsocket.js
@@ -0,0 +1,109 @@
+GZWebSocket = function(options) {
+  options = options || {};
+  var url = options.url;
+  this.socket = null;
+  this.idCounter = 0;
+
+  // Sets unlimited event listeners (EventEmitter2).
+  // this.setMaxListeners(0);
+
+  // begin by checking if a URL was given
+  if (url) {
+    this.connect(url);
+  }
+};
+
+GZWebSocket.prototype.__proto__ = EventEmitter2.prototype;
+
+/**
+ * Connect to the specified WebSocket.
+ *
+ * @param url - WebSocket URL
+ */
+GZWebSocket.prototype.connect = function(url) {
+  var that = this;
+
+  /**
+   * Emits a 'connection' event on WebSocket connection.
+   *
+   * @param event - the argument to emit with the event.
+   */
+  function onOpen(event) {
+    console.log("connected!");
+    that.emit('connection', event);
+  }
+
+  /**
+   * Emits a 'close' event on WebSocket disconnection.
+   *
+   * @param event - the argument to emit with the event.
+   */
+  function onClose(event) {
+    that.emit('close', event);
+  }
+
+  /**
+   * Emits an 'error' event whenever there was an error.
+   *
+   * @param event - the argument to emit with the event.
+   */
+  function onError(event) {
+    that.emit('error', event);
+  }
+
+  /**
+   * Parses message responses from rosbridge and sends to the appropriate
+   * topic, service, or param.
+   *
+   * @param message - the raw JSON message
+   */
+  function onMessage(message) {
+    function handleMessage(message) {
+      if (message.op === 'publish') {
+        that.emit(message.topic, message.msg);
+      } else if (message.op === 'service_response') {
+        that.emit(message.id, message.values);
+      }
+    }
+
+    console.log("got msg!" + message.data);
+
+    var data = JSON.parse(message.data);
+    handleMessage(data);
+
+  }
+
+  console.log("connecting to " + url);
+
+  this.socket = new WebSocket(url, "default");
+  this.socket.onopen = onOpen;
+  this.socket.onclose = onClose;
+  this.socket.onerror = onError;
+  this.socket.onmessage = onMessage;
+};
+
+/**
+ * Disconnect from the WebSocket server.
+ */
+GZWebSocket.prototype.close = function() {
+  if (this.socket) {
+    this.socket.close();
+  }
+};
+
+/**
+ * Sends the message over the WebSocket, but queues the message up if not yet
+ * connected.
+ */
+GZWebSocket.prototype.callOnConnection = function(message) {
+  var that = this;
+  var messageJson = JSON.stringify(message);
+
+  if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
+    that.once('connection', function() {
+      that.socket.send(messageJson);
+    });
+  } else {
+    that.socket.send(messageJson);
+  }
+};
diff --git a/gz3d/client/js/include/jquery-1.9.1.js b/gz3d/client/js/include/jquery-1.9.1.js
new file mode 100644
index 0000000000000000000000000000000000000000..e2c203fe978b83f4ca8d1ba97633f5a5d50bd53b
--- /dev/null
+++ b/gz3d/client/js/include/jquery-1.9.1.js
@@ -0,0 +1,9597 @@
+/*!
+ * jQuery JavaScript Library v1.9.1
+ * http://jquery.com/
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ *
+ * Copyright 2005, 2012 jQuery Foundation, Inc. and other contributors
+ * Released under the MIT license
+ * http://jquery.org/license
+ *
+ * Date: 2013-2-4
+ */
+(function( window, undefined ) {
+
+// Can't do this because several apps including ASP.NET trace
+// the stack via arguments.caller.callee and Firefox dies if
+// you try to trace through "use strict" call chains. (#13335)
+// Support: Firefox 18+
+//"use strict";
+var
+	// The deferred used on DOM ready
+	readyList,
+
+	// A central reference to the root jQuery(document)
+	rootjQuery,
+
+	// Support: IE<9
+	// For `typeof node.method` instead of `node.method !== undefined`
+	core_strundefined = typeof undefined,
+
+	// Use the correct document accordingly with window argument (sandbox)
+	document = window.document,
+	location = window.location,
+
+	// Map over jQuery in case of overwrite
+	_jQuery = window.jQuery,
+
+	// Map over the $ in case of overwrite
+	_$ = window.$,
+
+	// [[Class]] -> type pairs
+	class2type = {},
+
+	// List of deleted data cache ids, so we can reuse them
+	core_deletedIds = [],
+
+	core_version = "1.9.1",
+
+	// Save a reference to some core methods
+	core_concat = core_deletedIds.concat,
+	core_push = core_deletedIds.push,
+	core_slice = core_deletedIds.slice,
+	core_indexOf = core_deletedIds.indexOf,
+	core_toString = class2type.toString,
+	core_hasOwn = class2type.hasOwnProperty,
+	core_trim = core_version.trim,
+
+	// Define a local copy of jQuery
+	jQuery = function( selector, context ) {
+		// The jQuery object is actually just the init constructor 'enhanced'
+		return new jQuery.fn.init( selector, context, rootjQuery );
+	},
+
+	// Used for matching numbers
+	core_pnum = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,
+
+	// Used for splitting on whitespace
+	core_rnotwhite = /\S+/g,
+
+	// Make sure we trim BOM and NBSP (here's looking at you, Safari 5.0 and IE)
+	rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,
+
+	// 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 = /^(?:(<[\w\W]+>)[^>]*|#([\w-]*))$/,
+
+	// Match a standalone tag
+	rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/,
+
+	// JSON RegExp
+	rvalidchars = /^[\],:{}\s]*$/,
+	rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g,
+	rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,
+	rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/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();
+	},
+
+	// The ready event handler
+	completed = function( event ) {
+
+		// readyState === "complete" is good enough for us to call the dom ready in oldIE
+		if ( document.addEventListener || event.type === "load" || document.readyState === "complete" ) {
+			detach();
+			jQuery.ready();
+		}
+	},
+	// Clean-up method for dom ready events
+	detach = function() {
+		if ( document.addEventListener ) {
+			document.removeEventListener( "DOMContentLoaded", completed, false );
+			window.removeEventListener( "load", completed, false );
+
+		} else {
+			document.detachEvent( "onreadystatechange", completed );
+			window.detachEvent( "onload", completed );
+		}
+	};
+
+jQuery.fn = jQuery.prototype = {
+	// The current version of jQuery being used
+	jquery: core_version,
+
+	constructor: jQuery,
+	init: function( selector, context, rootjQuery ) {
+		var match, elem;
+
+		// HANDLE: $(""), $(null), $(undefined), $(false)
+		if ( !selector ) {
+			return this;
+		}
+
+		// Handle HTML strings
+		if ( typeof selector === "string" ) {
+			if ( selector.charAt(0) === "<" && selector.charAt( 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;
+
+					// scripts is true for back-compat
+					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] );
+
+					// Check parentNode to catch when Blackberry 4.6 returns
+					// nodes that are no longer in the document #6963
+					if ( elem && elem.parentNode ) {
+						// Handle the case where IE and Opera return items
+						// by name instead of ID
+						if ( elem.id !== match[2] ) {
+							return rootjQuery.find( selector );
+						}
+
+						// Otherwise, we 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 rootjQuery.ready( selector );
+		}
+
+		if ( selector.selector !== undefined ) {
+			this.selector = selector.selector;
+			this.context = selector.context;
+		}
+
+		return jQuery.makeArray( selector, this );
+	},
+
+	// Start with an empty selector
+	selector: "",
+
+	// The default length of a jQuery object is 0
+	length: 0,
+
+	// The number of elements contained in the matched element set
+	size: function() {
+		return this.length;
+	},
+
+	toArray: function() {
+		return core_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 a 'clean' array
+			this.toArray() :
+
+			// Return just the object
+			( num < 0 ? this[ this.length + num ] : this[ num ] );
+	},
+
+	// Take an array of elements and push it onto the stack
+	// (returning the new matched element set)
+	pushStack: function( elems ) {
+
+		// Build a new jQuery matched element set
+		var ret = jQuery.merge( this.constructor(), elems );
+
+		// Add the old object onto the stack (as a reference)
+		ret.prevObject = this;
+		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 );
+	},
+
+	ready: function( fn ) {
+		// Add the callback
+		jQuery.ready.promise().done( fn );
+
+		return this;
+	},
+
+	slice: function() {
+		return this.pushStack( core_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] ] : [] );
+	},
+
+	map: function( callback ) {
+		return this.pushStack( jQuery.map(this, function( elem, i ) {
+			return callback.call( elem, i, elem );
+		}));
+	},
+
+	end: function() {
+		return this.prevObject || this.constructor(null);
+	},
+
+	// For internal use only.
+	// Behaves like an Array's method, not like a jQuery method.
+	push: core_push,
+	sort: [].sort,
+	splice: [].splice
+};
+
+// Give the init function the jQuery prototype for later instantiation
+jQuery.fn.init.prototype = jQuery.fn;
+
+jQuery.extend = jQuery.fn.extend = function() {
+	var src, copyIsArray, copy, name, options, clone,
+		target = arguments[0] || {},
+		i = 1,
+		length = arguments.length,
+		deep = false;
+
+	// Handle a deep copy situation
+	if ( typeof target === "boolean" ) {
+		deep = target;
+		target = arguments[1] || {};
+		// skip the boolean and the target
+		i = 2;
+	}
+
+	// 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 ( length === i ) {
+		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({
+	noConflict: function( deep ) {
+		if ( window.$ === jQuery ) {
+			window.$ = _$;
+		}
+
+		if ( deep && window.jQuery === jQuery ) {
+			window.jQuery = _jQuery;
+		}
+
+		return jQuery;
+	},
+
+	// 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;
+		}
+
+		// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
+		if ( !document.body ) {
+			return setTimeout( jQuery.ready );
+		}
+
+		// 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.trigger ) {
+			jQuery( document ).trigger("ready").off("ready");
+		}
+	},
+
+	// See test/unit/core.js for details concerning isFunction.
+	// Since version 1.3, DOM methods and functions like alert
+	// aren't supported. They return false on IE (#2968).
+	isFunction: function( obj ) {
+		return jQuery.type(obj) === "function";
+	},
+
+	isArray: Array.isArray || function( obj ) {
+		return jQuery.type(obj) === "array";
+	},
+
+	isWindow: function( obj ) {
+		return obj != null && obj == obj.window;
+	},
+
+	isNumeric: function( obj ) {
+		return !isNaN( parseFloat(obj) ) && isFinite( obj );
+	},
+
+	type: function( obj ) {
+		if ( obj == null ) {
+			return String( obj );
+		}
+		return typeof obj === "object" || typeof obj === "function" ?
+			class2type[ core_toString.call(obj) ] || "object" :
+			typeof obj;
+	},
+
+	isPlainObject: function( obj ) {
+		// Must be an Object.
+		// Because of IE, we also have to check the presence of the constructor property.
+		// Make sure that DOM nodes and window objects don't pass through, as well
+		if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
+			return false;
+		}
+
+		try {
+			// Not own constructor property must be Object
+			if ( obj.constructor &&
+				!core_hasOwn.call(obj, "constructor") &&
+				!core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
+				return false;
+			}
+		} catch ( e ) {
+			// IE8,9 Will throw exceptions on certain host objects #9897
+			return false;
+		}
+
+		// Own properties are enumerated firstly, so to speed up,
+		// if last one is own, then all properties are own.
+
+		var key;
+		for ( key in obj ) {}
+
+		return key === undefined || core_hasOwn.call( obj, key );
+	},
+
+	isEmptyObject: function( obj ) {
+		var name;
+		for ( name in obj ) {
+			return false;
+		}
+		return true;
+	},
+
+	error: function( msg ) {
+		throw new Error( msg );
+	},
+
+	// 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
+	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 ) {
+			jQuery( scripts ).remove();
+		}
+		return jQuery.merge( [], parsed.childNodes );
+	},
+
+	parseJSON: function( data ) {
+		// Attempt to parse using the native JSON parser first
+		if ( window.JSON && window.JSON.parse ) {
+			return window.JSON.parse( data );
+		}
+
+		if ( data === null ) {
+			return data;
+		}
+
+		if ( typeof data === "string" ) {
+
+			// Make sure leading/trailing whitespace is removed (IE can't handle it)
+			data = jQuery.trim( data );
+
+			if ( data ) {
+				// Make sure the incoming data is actual JSON
+				// Logic borrowed from http://json.org/json2.js
+				if ( rvalidchars.test( data.replace( rvalidescape, "@" )
+					.replace( rvalidtokens, "]" )
+					.replace( rvalidbraces, "")) ) {
+
+					return ( new Function( "return " + data ) )();
+				}
+			}
+		}
+
+		jQuery.error( "Invalid JSON: " + data );
+	},
+
+	// Cross-browser xml parsing
+	parseXML: function( data ) {
+		var xml, tmp;
+		if ( !data || typeof data !== "string" ) {
+			return null;
+		}
+		try {
+			if ( window.DOMParser ) { // Standard
+				tmp = new DOMParser();
+				xml = tmp.parseFromString( data , "text/xml" );
+			} else { // IE
+				xml = new ActiveXObject( "Microsoft.XMLDOM" );
+				xml.async = "false";
+				xml.loadXML( data );
+			}
+		} catch( e ) {
+			xml = undefined;
+		}
+		if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) {
+			jQuery.error( "Invalid XML: " + data );
+		}
+		return xml;
+	},
+
+	noop: function() {},
+
+	// Evaluates a script in a global context
+	// Workarounds based on findings by Jim Driscoll
+	// http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context
+	globalEval: function( data ) {
+		if ( data && jQuery.trim( data ) ) {
+			// We use execScript on Internet Explorer
+			// We use an anonymous function so that context is window
+			// rather than jQuery in Firefox
+			( window.execScript || function( data ) {
+				window[ "eval" ].call( window, data );
+			} )( data );
+		}
+	},
+
+	// Convert dashed to camelCase; used by the css and data modules
+	// 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;
+	},
+
+	// Use native String.trim function wherever possible
+	trim: core_trim && !core_trim.call("\uFEFF\xA0") ?
+		function( text ) {
+			return text == null ?
+				"" :
+				core_trim.call( text );
+		} :
+
+		// Otherwise use our own trimming functionality
+		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 {
+				core_push.call( ret, arr );
+			}
+		}
+
+		return ret;
+	},
+
+	inArray: function( elem, arr, i ) {
+		var len;
+
+		if ( arr ) {
+			if ( core_indexOf ) {
+				return core_indexOf.call( arr, elem, i );
+			}
+
+			len = arr.length;
+			i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;
+
+			for ( ; i < len; i++ ) {
+				// Skip accessing in sparse arrays
+				if ( i in arr && arr[ i ] === elem ) {
+					return i;
+				}
+			}
+		}
+
+		return -1;
+	},
+
+	merge: function( first, second ) {
+		var l = second.length,
+			i = first.length,
+			j = 0;
+
+		if ( typeof l === "number" ) {
+			for ( ; j < l; j++ ) {
+				first[ i++ ] = second[ j ];
+			}
+		} else {
+			while ( second[j] !== undefined ) {
+				first[ i++ ] = second[ j++ ];
+			}
+		}
+
+		first.length = i;
+
+		return first;
+	},
+
+	grep: function( elems, callback, inv ) {
+		var retVal,
+			ret = [],
+			i = 0,
+			length = elems.length;
+		inv = !!inv;
+
+		// Go through the array, only saving the items
+		// that pass the validator function
+		for ( ; i < length; i++ ) {
+			retVal = !!callback( elems[ i ], i );
+			if ( inv !== retVal ) {
+				ret.push( elems[ i ] );
+			}
+		}
+
+		return ret;
+	},
+
+	// 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
+		if ( isArray ) {
+			for ( ; i < length; i++ ) {
+				value = callback( elems[ i ], i, arg );
+
+				if ( value != null ) {
+					ret[ ret.length ] = value;
+				}
+			}
+
+		// Go through every key on the object,
+		} else {
+			for ( i in elems ) {
+				value = callback( elems[ i ], i, arg );
+
+				if ( value != null ) {
+					ret[ ret.length ] = value;
+				}
+			}
+		}
+
+		// Flatten any nested arrays
+		return core_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 args, proxy, tmp;
+
+		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 = core_slice.call( arguments, 2 );
+		proxy = function() {
+			return fn.apply( context || this, args.concat( core_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;
+	},
+
+	// Multifunctional method to get and set values of a collection
+	// The value/s can optionally be executed if it's a function
+	access: function( elems, fn, key, value, chainable, emptyGet, raw ) {
+		var i = 0,
+			length = 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 < length; i++ ) {
+					fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) );
+				}
+			}
+		}
+
+		return chainable ?
+			elems :
+
+			// Gets
+			bulk ?
+				fn.call( elems ) :
+				length ? fn( elems[0], key ) : emptyGet;
+	},
+
+	now: function() {
+		return ( new Date() ).getTime();
+	}
+});
+
+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 );
+
+		// Standards-based browsers support DOMContentLoaded
+		} else if ( document.addEventListener ) {
+			// Use the handy event callback
+			document.addEventListener( "DOMContentLoaded", completed, false );
+
+			// A fallback to window.onload, that will always work
+			window.addEventListener( "load", completed, false );
+
+		// If IE event model is used
+		} else {
+			// Ensure firing before onload, maybe late but safe also for iframes
+			document.attachEvent( "onreadystatechange", completed );
+
+			// A fallback to window.onload, that will always work
+			window.attachEvent( "onload", completed );
+
+			// If IE and not a frame
+			// continually check to see if the document is ready
+			var top = false;
+
+			try {
+				top = window.frameElement == null && document.documentElement;
+			} catch(e) {}
+
+			if ( top && top.doScroll ) {
+				(function doScrollCheck() {
+					if ( !jQuery.isReady ) {
+
+						try {
+							// Use the trick by Diego Perini
+							// http://javascript.nwbox.com/IEContentLoaded/
+							top.doScroll("left");
+						} catch(e) {
+							return setTimeout( doScrollCheck, 50 );
+						}
+
+						// detach all dom ready events
+						detach();
+
+						// and execute any waiting functions
+						jQuery.ready();
+					}
+				})();
+			}
+		}
+	}
+	return readyList.promise( obj );
+};
+
+// 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 ( jQuery.isWindow( obj ) ) {
+		return false;
+	}
+
+	if ( obj.nodeType === 1 && length ) {
+		return true;
+	}
+
+	return type === "array" || type !== "function" &&
+		( length === 0 ||
+		typeof length === "number" && length > 0 && ( length - 1 ) in obj );
+}
+
+// All jQuery objects should point back to these
+rootjQuery = jQuery(document);
+// 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( core_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 // Flag to know if list is currently firing
+		firing,
+		// Last fire value (for non-forgettable lists)
+		memory,
+		// Flag to know if list was already fired
+		fired,
+		// End of the loop when firing
+		firingLength,
+		// Index of currently firing callback (modified by remove if needed)
+		firingIndex,
+		// First callback to fire (used internally by add and fireWith)
+		firingStart,
+		// 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 = [];
+				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 ) {
+				args = args || [];
+				args = [ context, args.slice ? args.slice() : args ];
+				if ( list && ( !fired || stack ) ) {
+					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 action = tuple[ 0 ],
+								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[ action + "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 = core_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 ? core_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();
+	}
+});
+jQuery.support = (function() {
+
+	var support, all, a,
+		input, select, fragment,
+		opt, eventName, isSupported, i,
+		div = document.createElement("div");
+
+	// Setup
+	div.setAttribute( "className", "t" );
+	div.innerHTML = "  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>";
+
+	// Support tests won't run in some limited or non-browser environments
+	all = div.getElementsByTagName("*");
+	a = div.getElementsByTagName("a")[ 0 ];
+	if ( !all || !a || !all.length ) {
+		return {};
+	}
+
+	// First batch of tests
+	select = document.createElement("select");
+	opt = select.appendChild( document.createElement("option") );
+	input = div.getElementsByTagName("input")[ 0 ];
+
+	a.style.cssText = "top:1px;float:left;opacity:.5";
+	support = {
+		// Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7)
+		getSetAttribute: div.className !== "t",
+
+		// IE strips leading whitespace when .innerHTML is used
+		leadingWhitespace: div.firstChild.nodeType === 3,
+
+		// Make sure that tbody elements aren't automatically inserted
+		// IE will insert them into empty tables
+		tbody: !div.getElementsByTagName("tbody").length,
+
+		// Make sure that link elements get serialized correctly by innerHTML
+		// This requires a wrapper element in IE
+		htmlSerialize: !!div.getElementsByTagName("link").length,
+
+		// Get the style information from getAttribute
+		// (IE uses .cssText instead)
+		style: /top/.test( a.getAttribute("style") ),
+
+		// Make sure that URLs aren't manipulated
+		// (IE normalizes it by default)
+		hrefNormalized: a.getAttribute("href") === "/a",
+
+		// Make sure that element opacity exists
+		// (IE uses filter instead)
+		// Use a regex to work around a WebKit issue. See #5145
+		opacity: /^0.5/.test( a.style.opacity ),
+
+		// Verify style float existence
+		// (IE uses styleFloat instead of cssFloat)
+		cssFloat: !!a.style.cssFloat,
+
+		// Check the default checkbox/radio value ("" on WebKit; "on" elsewhere)
+		checkOn: !!input.value,
+
+		// Make sure that a selected-by-default option has a working selected property.
+		// (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
+		optSelected: opt.selected,
+
+		// Tests for enctype support on a form (#6743)
+		enctype: !!document.createElement("form").enctype,
+
+		// Makes sure cloning an html5 element does not cause problems
+		// Where outerHTML is undefined, this still works
+		html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav></:nav>",
+
+		// jQuery.support.boxModel DEPRECATED in 1.8 since we don't support Quirks Mode
+		boxModel: document.compatMode === "CSS1Compat",
+
+		// Will be defined later
+		deleteExpando: true,
+		noCloneEvent: true,
+		inlineBlockNeedsLayout: false,
+		shrinkWrapBlocks: false,
+		reliableMarginRight: true,
+		boxSizingReliable: true,
+		pixelPosition: false
+	};
+
+	// Make sure checked status is properly cloned
+	input.checked = true;
+	support.noCloneChecked = input.cloneNode( true ).checked;
+
+	// Make sure that the options inside disabled selects aren't marked as disabled
+	// (WebKit marks them as disabled)
+	select.disabled = true;
+	support.optDisabled = !opt.disabled;
+
+	// Support: IE<9
+	try {
+		delete div.test;
+	} catch( e ) {
+		support.deleteExpando = false;
+	}
+
+	// Check if we can trust getAttribute("value")
+	input = document.createElement("input");
+	input.setAttribute( "value", "" );
+	support.input = input.getAttribute( "value" ) === "";
+
+	// Check if an input maintains its value after becoming a radio
+	input.value = "t";
+	input.setAttribute( "type", "radio" );
+	support.radioValue = input.value === "t";
+
+	// #11217 - WebKit loses check when the name is after the checked attribute
+	input.setAttribute( "checked", "t" );
+	input.setAttribute( "name", "t" );
+
+	fragment = document.createDocumentFragment();
+	fragment.appendChild( input );
+
+	// Check if a disconnected checkbox will retain its checked
+	// value of true after appended to the DOM (IE6/7)
+	support.appendChecked = input.checked;
+
+	// WebKit doesn't clone checked state correctly in fragments
+	support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked;
+
+	// Support: IE<9
+	// Opera does not clone events (and typeof div.attachEvent === undefined).
+	// IE9-10 clones events bound via attachEvent, but they don't trigger with .click()
+	if ( div.attachEvent ) {
+		div.attachEvent( "onclick", function() {
+			support.noCloneEvent = false;
+		});
+
+		div.cloneNode( true ).click();
+	}
+
+	// Support: IE<9 (lack submit/change bubble), Firefox 17+ (lack focusin event)
+	// Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP), test/csp.php
+	for ( i in { submit: true, change: true, focusin: true }) {
+		div.setAttribute( eventName = "on" + i, "t" );
+
+		support[ i + "Bubbles" ] = eventName in window || div.attributes[ eventName ].expando === false;
+	}
+
+	div.style.backgroundClip = "content-box";
+	div.cloneNode( true ).style.backgroundClip = "";
+	support.clearCloneStyle = div.style.backgroundClip === "content-box";
+
+	// Run tests that need a body at doc ready
+	jQuery(function() {
+		var container, marginDiv, tds,
+			divReset = "padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",
+			body = document.getElementsByTagName("body")[0];
+
+		if ( !body ) {
+			// Return for frameset docs that don't have a body
+			return;
+		}
+
+		container = document.createElement("div");
+		container.style.cssText = "border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px";
+
+		body.appendChild( container ).appendChild( div );
+
+		// Support: IE8
+		// Check if table cells still have offsetWidth/Height when they are set
+		// to display:none and there are still other visible table cells in a
+		// table row; if so, offsetWidth/Height are not reliable for use when
+		// determining if an element has been hidden directly using
+		// display:none (it is still safe to use offsets if a parent element is
+		// hidden; don safety goggles and see bug #4512 for more information).
+		div.innerHTML = "<table><tr><td></td><td>t</td></tr></table>";
+		tds = div.getElementsByTagName("td");
+		tds[ 0 ].style.cssText = "padding:0;margin:0;border:0;display:none";
+		isSupported = ( tds[ 0 ].offsetHeight === 0 );
+
+		tds[ 0 ].style.display = "";
+		tds[ 1 ].style.display = "none";
+
+		// Support: IE8
+		// Check if empty table cells still have offsetWidth/Height
+		support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 );
+
+		// Check box-sizing and margin behavior
+		div.innerHTML = "";
+		div.style.cssText = "box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;";
+		support.boxSizing = ( div.offsetWidth === 4 );
+		support.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== 1 );
+
+		// Use window.getComputedStyle because jsdom on node.js will break without it.
+		if ( window.getComputedStyle ) {
+			support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%";
+			support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px";
+
+			// Check if div with explicit width and no margin-right incorrectly
+			// gets computed margin-right based on width of container. (#3333)
+			// Fails in WebKit before Feb 2011 nightlies
+			// WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
+			marginDiv = div.appendChild( document.createElement("div") );
+			marginDiv.style.cssText = div.style.cssText = divReset;
+			marginDiv.style.marginRight = marginDiv.style.width = "0";
+			div.style.width = "1px";
+
+			support.reliableMarginRight =
+				!parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight );
+		}
+
+		if ( typeof div.style.zoom !== core_strundefined ) {
+			// Support: IE<8
+			// Check if natively block-level elements act like inline-block
+			// elements when setting their display to 'inline' and giving
+			// them layout
+			div.innerHTML = "";
+			div.style.cssText = divReset + "width:1px;padding:1px;display:inline;zoom:1";
+			support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 );
+
+			// Support: IE6
+			// Check if elements with layout shrink-wrap their children
+			div.style.display = "block";
+			div.innerHTML = "<div></div>";
+			div.firstChild.style.width = "5px";
+			support.shrinkWrapBlocks = ( div.offsetWidth !== 3 );
+
+			if ( support.inlineBlockNeedsLayout ) {
+				// Prevent IE 6 from affecting layout for positioned elements #11048
+				// Prevent IE from shrinking the body in IE 7 mode #12869
+				// Support: IE<8
+				body.style.zoom = 1;
+			}
+		}
+
+		body.removeChild( container );
+
+		// Null elements to avoid leaks in IE
+		container = div = tds = marginDiv = null;
+	});
+
+	// Null elements to avoid leaks in IE
+	all = select = fragment = opt = a = input = null;
+
+	return support;
+})();
+
+var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/,
+	rmultiDash = /([A-Z])/g;
+
+function internalData( elem, name, data, pvt /* Internal Use Only */ ){
+	if ( !jQuery.acceptData( elem ) ) {
+		return;
+	}
+
+	var thisCache, ret,
+		internalKey = jQuery.expando,
+		getByName = typeof name === "string",
+
+		// We have to handle DOM nodes and JS objects differently because IE6-7
+		// can't GC object references properly across the DOM-JS boundary
+		isNode = elem.nodeType,
+
+		// Only DOM nodes need the global jQuery cache; JS object data is
+		// attached directly to the object so GC can occur automatically
+		cache = isNode ? jQuery.cache : elem,
+
+		// Only defining an ID for JS objects if its cache already exists allows
+		// the code to shortcut on the same path as a DOM node with no cache
+		id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey;
+
+	// Avoid doing any more work than we need to when trying to get data on an
+	// object that has no data at all
+	if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && getByName && data === undefined ) {
+		return;
+	}
+
+	if ( !id ) {
+		// Only DOM nodes need a new unique ID for each element since their data
+		// ends up in the global cache
+		if ( isNode ) {
+			elem[ internalKey ] = id = core_deletedIds.pop() || jQuery.guid++;
+		} else {
+			id = internalKey;
+		}
+	}
+
+	if ( !cache[ id ] ) {
+		cache[ id ] = {};
+
+		// Avoids exposing jQuery metadata on plain JS objects when the object
+		// is serialized using JSON.stringify
+		if ( !isNode ) {
+			cache[ id ].toJSON = jQuery.noop;
+		}
+	}
+
+	// An object can be passed to jQuery.data instead of a key/value pair; this gets
+	// shallow copied over onto the existing cache
+	if ( typeof name === "object" || typeof name === "function" ) {
+		if ( pvt ) {
+			cache[ id ] = jQuery.extend( cache[ id ], name );
+		} else {
+			cache[ id ].data = jQuery.extend( cache[ id ].data, name );
+		}
+	}
+
+	thisCache = cache[ id ];
+
+	// jQuery data() is stored in a separate object inside the object's internal data
+	// cache in order to avoid key collisions between internal data and user-defined
+	// data.
+	if ( !pvt ) {
+		if ( !thisCache.data ) {
+			thisCache.data = {};
+		}
+
+		thisCache = thisCache.data;
+	}
+
+	if ( data !== undefined ) {
+		thisCache[ jQuery.camelCase( name ) ] = data;
+	}
+
+	// Check for both converted-to-camel and non-converted data property names
+	// If a data property was specified
+	if ( getByName ) {
+
+		// First Try to find as-is property data
+		ret = thisCache[ name ];
+
+		// Test for null|undefined property data
+		if ( ret == null ) {
+
+			// Try to find the camelCased property
+			ret = thisCache[ jQuery.camelCase( name ) ];
+		}
+	} else {
+		ret = thisCache;
+	}
+
+	return ret;
+}
+
+function internalRemoveData( elem, name, pvt ) {
+	if ( !jQuery.acceptData( elem ) ) {
+		return;
+	}
+
+	var i, l, thisCache,
+		isNode = elem.nodeType,
+
+		// See jQuery.data for more information
+		cache = isNode ? jQuery.cache : elem,
+		id = isNode ? elem[ jQuery.expando ] : jQuery.expando;
+
+	// If there is already no cache entry for this object, there is no
+	// purpose in continuing
+	if ( !cache[ id ] ) {
+		return;
+	}
+
+	if ( name ) {
+
+		thisCache = pvt ? cache[ id ] : cache[ id ].data;
+
+		if ( thisCache ) {
+
+			// Support array or space separated string names for data keys
+			if ( !jQuery.isArray( name ) ) {
+
+				// try the string as a key before any manipulation
+				if ( name in thisCache ) {
+					name = [ name ];
+				} else {
+
+					// split the camel cased version by spaces unless a key with the spaces exists
+					name = jQuery.camelCase( name );
+					if ( name in thisCache ) {
+						name = [ name ];
+					} else {
+						name = name.split(" ");
+					}
+				}
+			} else {
+				// 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 = name.concat( jQuery.map( name, jQuery.camelCase ) );
+			}
+
+			for ( i = 0, l = name.length; i < l; i++ ) {
+				delete thisCache[ name[i] ];
+			}
+
+			// If there is no data left in the cache, we want to continue
+			// and let the cache object itself get destroyed
+			if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) {
+				return;
+			}
+		}
+	}
+
+	// See jQuery.data for more information
+	if ( !pvt ) {
+		delete cache[ id ].data;
+
+		// Don't destroy the parent cache unless the internal data object
+		// had been the only thing left in it
+		if ( !isEmptyDataObject( cache[ id ] ) ) {
+			return;
+		}
+	}
+
+	// Destroy the cache
+	if ( isNode ) {
+		jQuery.cleanData( [ elem ], true );
+
+	// Use delete when supported for expandos or `cache` is not a window per isWindow (#10080)
+	} else if ( jQuery.support.deleteExpando || cache != cache.window ) {
+		delete cache[ id ];
+
+	// When all else fails, null
+	} else {
+		cache[ id ] = null;
+	}
+}
+
+jQuery.extend({
+	cache: {},
+
+	// Unique for each copy of jQuery on the page
+	// Non-digits removed to match rinlinejQuery
+	expando: "jQuery" + ( core_version + Math.random() ).replace( /\D/g, "" ),
+
+	// The following elements throw uncatchable exceptions if you
+	// attempt to add expando properties to them.
+	noData: {
+		"embed": true,
+		// Ban all objects except for Flash (which handle expandos)
+		"object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",
+		"applet": true
+	},
+
+	hasData: function( elem ) {
+		elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
+		return !!elem && !isEmptyDataObject( elem );
+	},
+
+	data: function( elem, name, data ) {
+		return internalData( elem, name, data );
+	},
+
+	removeData: function( elem, name ) {
+		return internalRemoveData( elem, name );
+	},
+
+	// For internal use only.
+	_data: function( elem, name, data ) {
+		return internalData( elem, name, data, true );
+	},
+
+	_removeData: function( elem, name ) {
+		return internalRemoveData( elem, name, true );
+	},
+
+	// A method for determining if a DOM node can handle the data expando
+	acceptData: function( elem ) {
+		// Do not set data on non-element because it will not be cleared (#8335).
+		if ( elem.nodeType && elem.nodeType !== 1 && elem.nodeType !== 9 ) {
+			return false;
+		}
+
+		var noData = elem.nodeName && jQuery.noData[ elem.nodeName.toLowerCase() ];
+
+		// nodes accept data unless otherwise specified; rejection can be conditional
+		return !noData || noData !== true && elem.getAttribute("classid") === noData;
+	}
+});
+
+jQuery.fn.extend({
+	data: function( key, value ) {
+		var attrs, name,
+			elem = this[0],
+			i = 0,
+			data = null;
+
+		// Gets all values
+		if ( key === undefined ) {
+			if ( this.length ) {
+				data = jQuery.data( elem );
+
+				if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) {
+					attrs = elem.attributes;
+					for ( ; i < attrs.length; i++ ) {
+						name = attrs[i].name;
+
+						if ( !name.indexOf( "data-" ) ) {
+							name = jQuery.camelCase( name.slice(5) );
+
+							dataAttr( elem, name, data[ name ] );
+						}
+					}
+					jQuery._data( elem, "parsedAttrs", true );
+				}
+			}
+
+			return data;
+		}
+
+		// Sets multiple values
+		if ( typeof key === "object" ) {
+			return this.each(function() {
+				jQuery.data( this, key );
+			});
+		}
+
+		return jQuery.access( this, function( value ) {
+
+			if ( value === undefined ) {
+				// Try to fetch any internally stored data first
+				return elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : null;
+			}
+
+			this.each(function() {
+				jQuery.data( this, key, value );
+			});
+		}, null, value, arguments.length > 1, null, true );
+	},
+
+	removeData: function( key ) {
+		return this.each(function() {
+			jQuery.removeData( this, key );
+		});
+	}
+});
+
+function dataAttr( elem, key, data ) {
+	// If nothing was found internally, try to fetch any
+	// data from the HTML5 data-* attribute
+	if ( data === undefined && elem.nodeType === 1 ) {
+
+		var 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
+			jQuery.data( elem, key, data );
+
+		} else {
+			data = undefined;
+		}
+	}
+
+	return data;
+}
+
+// checks a cache object for emptiness
+function isEmptyDataObject( obj ) {
+	var name;
+	for ( name in obj ) {
+
+		// if the public data object is empty, the private is still empty
+		if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) {
+			continue;
+		}
+		if ( name !== "toJSON" ) {
+			return false;
+		}
+	}
+
+	return true;
+}
+jQuery.extend({
+	queue: function( elem, type, data ) {
+		var queue;
+
+		if ( elem ) {
+			type = ( type || "fx" ) + "queue";
+			queue = jQuery._data( elem, type );
+
+			// Speed up dequeue by getting out quickly if this is just a lookup
+			if ( data ) {
+				if ( !queue || jQuery.isArray(data) ) {
+					queue = jQuery._data( 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--;
+		}
+
+		hooks.cur = fn;
+		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 intended for public consumption - generates a queueHooks object, or returns the current one
+	_queueHooks: function( elem, type ) {
+		var key = type + "queueHooks";
+		return jQuery._data( elem, key ) || jQuery._data( elem, key, {
+			empty: jQuery.Callbacks("once memory").add(function() {
+				jQuery._removeData( elem, type + "queue" );
+				jQuery._removeData( elem, 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 );
+		});
+	},
+	// Based off of the plugin by Clint Helfers, with permission.
+	// http://blindsignals.com/index.php/2009/07/jquery-delay/
+	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 );
+			};
+		});
+	},
+	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 = jQuery._data( elements[ i ], type + "queueHooks" );
+			if ( tmp && tmp.empty ) {
+				count++;
+				tmp.empty.add( resolve );
+			}
+		}
+		resolve();
+		return defer.promise( obj );
+	}
+});
+var nodeHook, boolHook,
+	rclass = /[\t\r\n]/g,
+	rreturn = /\r/g,
+	rfocusable = /^(?:input|select|textarea|button|object)$/i,
+	rclickable = /^(?:a|area)$/i,
+	rboolean = /^(?:checked|selected|autofocus|autoplay|async|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped)$/i,
+	ruseDefault = /^(?:checked|selected)$/i,
+	getSetAttribute = jQuery.support.getSetAttribute,
+	getSetInput = jQuery.support.input;
+
+jQuery.fn.extend({
+	attr: function( name, value ) {
+		return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 );
+	},
+
+	removeAttr: function( name ) {
+		return this.each(function() {
+			jQuery.removeAttr( this, name );
+		});
+	},
+
+	prop: function( name, value ) {
+		return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 );
+	},
+
+	removeProp: function( name ) {
+		name = jQuery.propFix[ name ] || name;
+		return this.each(function() {
+			// try/catch handles cases where IE balks (such as removing a property on window)
+			try {
+				this[ name ] = undefined;
+				delete this[ name ];
+			} catch( e ) {}
+		});
+	},
+
+	addClass: function( value ) {
+		var classes, elem, cur, clazz, j,
+			i = 0,
+			len = this.length,
+			proceed = typeof value === "string" && value;
+
+		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( core_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 + " ";
+						}
+					}
+					elem.className = jQuery.trim( cur );
+
+				}
+			}
+		}
+
+		return this;
+	},
+
+	removeClass: function( value ) {
+		var classes, elem, cur, clazz, j,
+			i = 0,
+			len = this.length,
+			proceed = arguments.length === 0 || typeof value === "string" && value;
+
+		if ( jQuery.isFunction( value ) ) {
+			return this.each(function( j ) {
+				jQuery( this ).removeClass( value.call( this, j, this.className ) );
+			});
+		}
+		if ( proceed ) {
+			classes = ( value || "" ).match( core_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 + " ", " " );
+						}
+					}
+					elem.className = value ? jQuery.trim( cur ) : "";
+				}
+			}
+		}
+
+		return this;
+	},
+
+	toggleClass: function( value, stateVal ) {
+		var type = typeof value,
+			isBool = typeof stateVal === "boolean";
+
+		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 ),
+					state = stateVal,
+					classNames = value.match( core_rnotwhite ) || [];
+
+				while ( (className = classNames[ i++ ]) ) {
+					// check each className given, space separated list
+					state = isBool ? state : !self.hasClass( className );
+					self[ state ? "addClass" : "removeClass" ]( className );
+				}
+
+			// Toggle whole class name
+			} else if ( type === core_strundefined || type === "boolean" ) {
+				if ( this.className ) {
+					// store className if set
+					jQuery._data( 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 ? "" : jQuery._data( 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;
+	},
+
+	val: function( value ) {
+		var ret, hooks, 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,
+				self = jQuery(this);
+
+			if ( this.nodeType !== 1 ) {
+				return;
+			}
+
+			if ( isFunction ) {
+				val = value.call( this, i, self.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 ) {
+				// attributes.value is undefined in Blackberry 4.7 but
+				// uses .value. See #6932
+				var val = elem.attributes.value;
+				return !val || val.specified ? elem.value : elem.text;
+			}
+		},
+		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 ];
+
+					// oldIE 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
+							( jQuery.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 values = jQuery.makeArray( value );
+
+				jQuery(elem).find("option").each(function() {
+					this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;
+				});
+
+				if ( !values.length ) {
+					elem.selectedIndex = -1;
+				}
+				return values;
+			}
+		}
+	},
+
+	attr: function( elem, name, value ) {
+		var hooks, notxml, 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 === core_strundefined ) {
+			return jQuery.prop( elem, name, value );
+		}
+
+		notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
+
+		// All attributes are lowercase
+		// Grab necessary hook if one is defined
+		if ( notxml ) {
+			name = name.toLowerCase();
+			hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook );
+		}
+
+		if ( value !== undefined ) {
+
+			if ( value === null ) {
+				jQuery.removeAttr( elem, name );
+
+			} else if ( hooks && notxml && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
+				return ret;
+
+			} else {
+				elem.setAttribute( name, value + "" );
+				return value;
+			}
+
+		} else if ( hooks && notxml && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
+			return ret;
+
+		} else {
+
+			// In IE9+, Flash objects don't have .getAttribute (#12945)
+			// Support: IE9+
+			if ( typeof elem.getAttribute !== core_strundefined ) {
+				ret =  elem.getAttribute( 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( core_rnotwhite );
+
+		if ( attrNames && elem.nodeType === 1 ) {
+			while ( (name = attrNames[i++]) ) {
+				propName = jQuery.propFix[ name ] || name;
+
+				// Boolean attributes get special treatment (#10870)
+				if ( rboolean.test( name ) ) {
+					// Set corresponding property to false for boolean attributes
+					// Also clear defaultChecked/defaultSelected (if appropriate) for IE<8
+					if ( !getSetAttribute && ruseDefault.test( name ) ) {
+						elem[ jQuery.camelCase( "default-" + name ) ] =
+							elem[ propName ] = false;
+					} else {
+						elem[ propName ] = false;
+					}
+
+				// See #9699 for explanation of this approach (setting first, then removal)
+				} else {
+					jQuery.attr( elem, name, "" );
+				}
+
+				elem.removeAttribute( getSetAttribute ? name : propName );
+			}
+		}
+	},
+
+	attrHooks: {
+		type: {
+			set: function( elem, value ) {
+				if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) {
+					// Setting the type on a radio button after the value resets the value in IE6-9
+					// Reset value to default in case type is set after value during creation
+					var val = elem.value;
+					elem.setAttribute( "type", value );
+					if ( val ) {
+						elem.value = val;
+					}
+					return value;
+				}
+			}
+		}
+	},
+
+	propFix: {
+		tabindex: "tabIndex",
+		readonly: "readOnly",
+		"for": "htmlFor",
+		"class": "className",
+		maxlength: "maxLength",
+		cellspacing: "cellSpacing",
+		cellpadding: "cellPadding",
+		rowspan: "rowSpan",
+		colspan: "colSpan",
+		usemap: "useMap",
+		frameborder: "frameBorder",
+		contenteditable: "contentEditable"
+	},
+
+	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 ) {
+			if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
+				return ret;
+
+			} else {
+				return ( elem[ name ] = value );
+			}
+
+		} else {
+			if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
+				return ret;
+
+			} else {
+				return elem[ name ];
+			}
+		}
+	},
+
+	propHooks: {
+		tabIndex: {
+			get: function( elem ) {
+				// elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
+				// http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
+				var attributeNode = elem.getAttributeNode("tabindex");
+
+				return attributeNode && attributeNode.specified ?
+					parseInt( attributeNode.value, 10 ) :
+					rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
+						0 :
+						undefined;
+			}
+		}
+	}
+});
+
+// Hook for boolean attributes
+boolHook = {
+	get: function( elem, name ) {
+		var
+			// Use .prop to determine if this attribute is understood as boolean
+			prop = jQuery.prop( elem, name ),
+
+			// Fetch it accordingly
+			attr = typeof prop === "boolean" && elem.getAttribute( name ),
+			detail = typeof prop === "boolean" ?
+
+				getSetInput && getSetAttribute ?
+					attr != null :
+					// oldIE fabricates an empty string for missing boolean attributes
+					// and conflates checked/selected into attroperties
+					ruseDefault.test( name ) ?
+						elem[ jQuery.camelCase( "default-" + name ) ] :
+						!!attr :
+
+				// fetch an attribute node for properties not recognized as boolean
+				elem.getAttributeNode( name );
+
+		return detail && detail.value !== false ?
+			name.toLowerCase() :
+			undefined;
+	},
+	set: function( elem, value, name ) {
+		if ( value === false ) {
+			// Remove boolean attributes when set to false
+			jQuery.removeAttr( elem, name );
+		} else if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) {
+			// IE<8 needs the *property* name
+			elem.setAttribute( !getSetAttribute && jQuery.propFix[ name ] || name, name );
+
+		// Use defaultChecked and defaultSelected for oldIE
+		} else {
+			elem[ jQuery.camelCase( "default-" + name ) ] = elem[ name ] = true;
+		}
+
+		return name;
+	}
+};
+
+// fix oldIE value attroperty
+if ( !getSetInput || !getSetAttribute ) {
+	jQuery.attrHooks.value = {
+		get: function( elem, name ) {
+			var ret = elem.getAttributeNode( name );
+			return jQuery.nodeName( elem, "input" ) ?
+
+				// Ignore the value *property* by using defaultValue
+				elem.defaultValue :
+
+				ret && ret.specified ? ret.value : undefined;
+		},
+		set: function( elem, value, name ) {
+			if ( jQuery.nodeName( elem, "input" ) ) {
+				// Does not return so that setAttribute is also used
+				elem.defaultValue = value;
+			} else {
+				// Use nodeHook if defined (#1954); otherwise setAttribute is fine
+				return nodeHook && nodeHook.set( elem, value, name );
+			}
+		}
+	};
+}
+
+// IE6/7 do not support getting/setting some attributes with get/setAttribute
+if ( !getSetAttribute ) {
+
+	// Use this for any attribute in IE6/7
+	// This fixes almost every IE6/7 issue
+	nodeHook = jQuery.valHooks.button = {
+		get: function( elem, name ) {
+			var ret = elem.getAttributeNode( name );
+			return ret && ( name === "id" || name === "name" || name === "coords" ? ret.value !== "" : ret.specified ) ?
+				ret.value :
+				undefined;
+		},
+		set: function( elem, value, name ) {
+			// Set the existing or create a new attribute node
+			var ret = elem.getAttributeNode( name );
+			if ( !ret ) {
+				elem.setAttributeNode(
+					(ret = elem.ownerDocument.createAttribute( name ))
+				);
+			}
+
+			ret.value = value += "";
+
+			// Break association with cloned elements by also using setAttribute (#9646)
+			return name === "value" || value === elem.getAttribute( name ) ?
+				value :
+				undefined;
+		}
+	};
+
+	// Set contenteditable to false on removals(#10429)
+	// Setting to empty string throws an error as an invalid value
+	jQuery.attrHooks.contenteditable = {
+		get: nodeHook.get,
+		set: function( elem, value, name ) {
+			nodeHook.set( elem, value === "" ? false : value, name );
+		}
+	};
+
+	// Set width and height to auto instead of 0 on empty string( Bug #8150 )
+	// This is for removals
+	jQuery.each([ "width", "height" ], function( i, name ) {
+		jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
+			set: function( elem, value ) {
+				if ( value === "" ) {
+					elem.setAttribute( name, "auto" );
+					return value;
+				}
+			}
+		});
+	});
+}
+
+
+// Some attributes require a special call on IE
+// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
+if ( !jQuery.support.hrefNormalized ) {
+	jQuery.each([ "href", "src", "width", "height" ], function( i, name ) {
+		jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
+			get: function( elem ) {
+				var ret = elem.getAttribute( name, 2 );
+				return ret == null ? undefined : ret;
+			}
+		});
+	});
+
+	// href/src property should get the full normalized URL (#10299/#12915)
+	jQuery.each([ "href", "src" ], function( i, name ) {
+		jQuery.propHooks[ name ] = {
+			get: function( elem ) {
+				return elem.getAttribute( name, 4 );
+			}
+		};
+	});
+}
+
+if ( !jQuery.support.style ) {
+	jQuery.attrHooks.style = {
+		get: function( elem ) {
+			// Return undefined in the case of empty string
+			// Note: IE uppercases css property names, but if we were to .toLowerCase()
+			// .cssText, that would destroy case senstitivity in URL's, like in "background"
+			return elem.style.cssText || undefined;
+		},
+		set: function( elem, value ) {
+			return ( elem.style.cssText = value + "" );
+		}
+	};
+}
+
+// Safari mis-reports the default selected property of an option
+// Accessing the parent's selectedIndex property fixes it
+if ( !jQuery.support.optSelected ) {
+	jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, {
+		get: function( elem ) {
+			var parent = elem.parentNode;
+
+			if ( parent ) {
+				parent.selectedIndex;
+
+				// Make sure that it also works with optgroups, see #5701
+				if ( parent.parentNode ) {
+					parent.parentNode.selectedIndex;
+				}
+			}
+			return null;
+		}
+	});
+}
+
+// IE6/7 call enctype encoding
+if ( !jQuery.support.enctype ) {
+	jQuery.propFix.enctype = "encoding";
+}
+
+// Radios and checkboxes getter/setter
+if ( !jQuery.support.checkOn ) {
+	jQuery.each([ "radio", "checkbox" ], function() {
+		jQuery.valHooks[ this ] = {
+			get: function( elem ) {
+				// Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified
+				return elem.getAttribute("value") === null ? "on" : elem.value;
+			}
+		};
+	});
+}
+jQuery.each([ "radio", "checkbox" ], function() {
+	jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], {
+		set: function( elem, value ) {
+			if ( jQuery.isArray( value ) ) {
+				return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );
+			}
+		}
+	});
+});
+var rformElems = /^(?:input|select|textarea)$/i,
+	rkeyEvent = /^key/,
+	rmouseEvent = /^(?:mouse|contextmenu)|click/,
+	rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
+	rtypenamespace = /^([^.]*)(?:\.(.+)|)$/;
+
+function returnTrue() {
+	return true;
+}
+
+function returnFalse() {
+	return false;
+}
+
+/*
+ * 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 tmp, events, t, handleObjIn,
+			special, eventHandle, handleObj,
+			handlers, type, namespaces, origType,
+			elemData = jQuery._data( 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 !== core_strundefined && (!e || jQuery.event.triggered !== e.type) ?
+					jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
+					undefined;
+			};
+			// Add elem as a property of the handle fn to prevent a memory leak with IE non-native events
+			eventHandle.elem = elem;
+		}
+
+		// Handle multiple events separated by a space
+		// jQuery(...).bind("mouseover mouseout", fn);
+		types = ( types || "" ).match( core_rnotwhite ) || [""];
+		t = types.length;
+		while ( t-- ) {
+			tmp = rtypenamespace.exec( types[t] ) || [];
+			type = origType = tmp[1];
+			namespaces = ( tmp[2] || "" ).split( "." ).sort();
+
+			// 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/attachEvent if the special events handler returns false
+				if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
+					// Bind the global event handler to the element
+					if ( elem.addEventListener ) {
+						elem.addEventListener( type, eventHandle, false );
+
+					} else if ( elem.attachEvent ) {
+						elem.attachEvent( "on" + type, eventHandle );
+					}
+				}
+			}
+
+			if ( special.add ) {
+				special.add.call( elem, handleObj );
+
+				if ( !handleObj.handler.guid ) {
+					handleObj.handler.guid = handler.guid;
+				}
+			}
+
+			// Add to the element's handler list, delegates in front
+			if ( selector ) {
+				handlers.splice( handlers.delegateCount++, 0, handleObj );
+			} else {
+				handlers.push( handleObj );
+			}
+
+			// Keep track of which events have ever been used, for event optimization
+			jQuery.event.global[ type ] = true;
+		}
+
+		// Nullify elem to prevent memory leaks in IE
+		elem = null;
+	},
+
+	// Detach an event or set of events from an element
+	remove: function( elem, types, handler, selector, mappedTypes ) {
+		var j, handleObj, tmp,
+			origCount, t, events,
+			special, handlers, type,
+			namespaces, origType,
+			elemData = jQuery.hasData( elem ) && jQuery._data( elem );
+
+		if ( !elemData || !(events = elemData.events) ) {
+			return;
+		}
+
+		// Once for each type.namespace in types; type may be omitted
+		types = ( types || "" ).match( core_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;
+
+			// removeData also checks for emptiness and clears the expando if empty
+			// so use it instead of delete
+			jQuery._removeData( elem, "events" );
+		}
+	},
+
+	trigger: function( event, data, elem, onlyHandlers ) {
+		var handle, ontype, cur,
+			bubbleType, special, tmp, i,
+			eventPath = [ elem || document ],
+			type = core_hasOwn.call( event, "type" ) ? event.type : event,
+			namespaces = core_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 );
+
+		event.isTrigger = true;
+		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 = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" );
+			if ( handle ) {
+				handle.apply( cur, data );
+			}
+
+			// Native handler
+			handle = ontype && cur[ ontype ];
+			if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === 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( elem.ownerDocument, data ) === false) &&
+				!(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) {
+
+				// Call a native DOM method on the target with the same name name as the event.
+				// Can't use an .isFunction() check here because IE6/7 fails that test.
+				// Don't do default actions on window, that's where global variables be (#6170)
+				if ( ontype && 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;
+					try {
+						elem[ type ]();
+					} catch ( e ) {
+						// IE<9 dies on focus/blur to hidden element (#1486,#12518)
+						// only reproducible on winXP IE8 native, not IE9 in IE8 mode
+					}
+					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, ret, handleObj, matched, j,
+			handlerQueue = [],
+			args = core_slice.call( arguments ),
+			handlers = ( jQuery._data( 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 sel, handleObj, matches, i,
+			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 check non-elements (#13208)
+				// Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
+				if ( cur.nodeType === 1 && (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;
+	},
+
+	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: IE<9
+		// Fix target property (#1925)
+		if ( !event.target ) {
+			event.target = originalEvent.srcElement || document;
+		}
+
+		// Support: Chrome 23+, Safari?
+		// Target should not be a text node (#504, #13143)
+		if ( event.target.nodeType === 3 ) {
+			event.target = event.target.parentNode;
+		}
+
+		// Support: IE<9
+		// For mouse/key events, metaKey==false if it's undefined (#3368, #11328)
+		event.metaKey = !!event.metaKey;
+
+		return fixHook.filter ? fixHook.filter( event, originalEvent ) : event;
+	},
+
+	// 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 fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),
+		filter: function( event, original ) {
+			var body, eventDoc, doc,
+				button = original.button,
+				fromElement = original.fromElement;
+
+			// 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 relatedTarget, if necessary
+			if ( !event.relatedTarget && fromElement ) {
+				event.relatedTarget = fromElement === event.target ? original.toElement : fromElement;
+			}
+
+			// 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;
+		}
+	},
+
+	special: {
+		load: {
+			// Prevent triggered image.load events from bubbling to window.load
+			noBubble: true
+		},
+		click: {
+			// For checkbox, fire native event so checked state will be right
+			trigger: function() {
+				if ( jQuery.nodeName( this, "input" ) && this.type === "checkbox" && this.click ) {
+					this.click();
+					return false;
+				}
+			}
+		},
+		focus: {
+			// Fire native event if possible so blur/focus sequence is correct
+			trigger: function() {
+				if ( this !== document.activeElement && this.focus ) {
+					try {
+						this.focus();
+						return false;
+					} catch ( e ) {
+						// Support: IE<9
+						// If we error on focus to hidden element (#1486, #12518),
+						// let .trigger() run the handlers
+					}
+				}
+			},
+			delegateType: "focusin"
+		},
+		blur: {
+			trigger: function() {
+				if ( this === document.activeElement && this.blur ) {
+					this.blur();
+					return false;
+				}
+			},
+			delegateType: "focusout"
+		},
+
+		beforeunload: {
+			postDispatch: function( event ) {
+
+				// Even when returnValue equals to undefined Firefox will still show alert
+				if ( event.result !== undefined ) {
+					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 = document.removeEventListener ?
+	function( elem, type, handle ) {
+		if ( elem.removeEventListener ) {
+			elem.removeEventListener( type, handle, false );
+		}
+	} :
+	function( elem, type, handle ) {
+		var name = "on" + type;
+
+		if ( elem.detachEvent ) {
+
+			// #8545, #7054, preventing memory leaks for custom events in IE6-8
+			// detachEvent needed property on element, by name of that event, to properly expose it to GC
+			if ( typeof elem[ name ] === core_strundefined ) {
+				elem[ name ] = null;
+			}
+
+			elem.detachEvent( name, handle );
+		}
+	};
+
+jQuery.Event = function( src, props ) {
+	// Allow instantiation without the 'new' keyword
+	if ( !(this instanceof jQuery.Event) ) {
+		return new jQuery.Event( src, props );
+	}
+
+	// Event object
+	if ( src && src.type ) {
+		this.originalEvent = src;
+		this.type = src.type;
+
+		// Events bubbling up the document may have been marked as prevented
+		// by a handler lower down the tree; reflect the correct value.
+		this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false ||
+			src.getPreventDefault && src.getPreventDefault() ) ? 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 ) {
+			return;
+		}
+
+		// If preventDefault exists, run it on the original event
+		if ( e.preventDefault ) {
+			e.preventDefault();
+
+		// Support: IE
+		// Otherwise set the returnValue property of the original event to false
+		} else {
+			e.returnValue = false;
+		}
+	},
+	stopPropagation: function() {
+		var e = this.originalEvent;
+
+		this.isPropagationStopped = returnTrue;
+		if ( !e ) {
+			return;
+		}
+		// If stopPropagation exists, run it on the original event
+		if ( e.stopPropagation ) {
+			e.stopPropagation();
+		}
+
+		// Support: IE
+		// Set the cancelBubble property of the original event to true
+		e.cancelBubble = true;
+	},
+	stopImmediatePropagation: function() {
+		this.isImmediatePropagationStopped = returnTrue;
+		this.stopPropagation();
+	}
+};
+
+// Create mouseenter/leave events using mouseover/out and event-time checks
+jQuery.each({
+	mouseenter: "mouseover",
+	mouseleave: "mouseout"
+}, 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;
+		}
+	};
+});
+
+// IE submit delegation
+if ( !jQuery.support.submitBubbles ) {
+
+	jQuery.event.special.submit = {
+		setup: function() {
+			// Only need this for delegated form submit events
+			if ( jQuery.nodeName( this, "form" ) ) {
+				return false;
+			}
+
+			// Lazy-add a submit handler when a descendant form may potentially be submitted
+			jQuery.event.add( this, "click._submit keypress._submit", function( e ) {
+				// Node name check avoids a VML-related crash in IE (#9807)
+				var elem = e.target,
+					form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined;
+				if ( form && !jQuery._data( form, "submitBubbles" ) ) {
+					jQuery.event.add( form, "submit._submit", function( event ) {
+						event._submit_bubble = true;
+					});
+					jQuery._data( form, "submitBubbles", true );
+				}
+			});
+			// return undefined since we don't need an event listener
+		},
+
+		postDispatch: function( event ) {
+			// If form was submitted by the user, bubble the event up the tree
+			if ( event._submit_bubble ) {
+				delete event._submit_bubble;
+				if ( this.parentNode && !event.isTrigger ) {
+					jQuery.event.simulate( "submit", this.parentNode, event, true );
+				}
+			}
+		},
+
+		teardown: function() {
+			// Only need this for delegated form submit events
+			if ( jQuery.nodeName( this, "form" ) ) {
+				return false;
+			}
+
+			// Remove delegated handlers; cleanData eventually reaps submit handlers attached above
+			jQuery.event.remove( this, "._submit" );
+		}
+	};
+}
+
+// IE change delegation and checkbox/radio fix
+if ( !jQuery.support.changeBubbles ) {
+
+	jQuery.event.special.change = {
+
+		setup: function() {
+
+			if ( rformElems.test( this.nodeName ) ) {
+				// IE doesn't fire change on a check/radio until blur; trigger it on click
+				// after a propertychange. Eat the blur-change in special.change.handle.
+				// This still fires onchange a second time for check/radio after blur.
+				if ( this.type === "checkbox" || this.type === "radio" ) {
+					jQuery.event.add( this, "propertychange._change", function( event ) {
+						if ( event.originalEvent.propertyName === "checked" ) {
+							this._just_changed = true;
+						}
+					});
+					jQuery.event.add( this, "click._change", function( event ) {
+						if ( this._just_changed && !event.isTrigger ) {
+							this._just_changed = false;
+						}
+						// Allow triggered, simulated change events (#11500)
+						jQuery.event.simulate( "change", this, event, true );
+					});
+				}
+				return false;
+			}
+			// Delegated event; lazy-add a change handler on descendant inputs
+			jQuery.event.add( this, "beforeactivate._change", function( e ) {
+				var elem = e.target;
+
+				if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "changeBubbles" ) ) {
+					jQuery.event.add( elem, "change._change", function( event ) {
+						if ( this.parentNode && !event.isSimulated && !event.isTrigger ) {
+							jQuery.event.simulate( "change", this.parentNode, event, true );
+						}
+					});
+					jQuery._data( elem, "changeBubbles", true );
+				}
+			});
+		},
+
+		handle: function( event ) {
+			var elem = event.target;
+
+			// Swallow native change events from checkbox/radio, we already triggered them above
+			if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) {
+				return event.handleObj.handler.apply( this, arguments );
+			}
+		},
+
+		teardown: function() {
+			jQuery.event.remove( this, "._change" );
+
+			return !rformElems.test( this.nodeName );
+		}
+	};
+}
+
+// Create "bubbling" focus and blur events
+if ( !jQuery.support.focusinBubbles ) {
+	jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
+
+		// Attach a single capturing handler while someone wants focusin/focusout
+		var attaches = 0,
+			handler = function( event ) {
+				jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );
+			};
+
+		jQuery.event.special[ fix ] = {
+			setup: function() {
+				if ( attaches++ === 0 ) {
+					document.addEventListener( orig, handler, true );
+				}
+			},
+			teardown: function() {
+				if ( --attaches === 0 ) {
+					document.removeEventListener( orig, handler, true );
+				}
+			}
+		};
+	});
+}
+
+jQuery.fn.extend({
+
+	on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
+		var type, origFn;
+
+		// 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 );
+		});
+	},
+
+	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 );
+	},
+
+	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 );
+		}
+	}
+});
+/*!
+ * Sizzle CSS Selector Engine
+ * Copyright 2012 jQuery Foundation and other contributors
+ * Released under the MIT license
+ * http://sizzlejs.com/
+ */
+(function( window, undefined ) {
+
+var i,
+	cachedruns,
+	Expr,
+	getText,
+	isXML,
+	compile,
+	hasDuplicate,
+	outermostContext,
+
+	// Local document vars
+	setDocument,
+	document,
+	docElem,
+	documentIsXML,
+	rbuggyQSA,
+	rbuggyMatches,
+	matches,
+	contains,
+	sortOrder,
+
+	// Instance-specific data
+	expando = "sizzle" + -(new Date()),
+	preferredDoc = window.document,
+	support = {},
+	dirruns = 0,
+	done = 0,
+	classCache = createCache(),
+	tokenCache = createCache(),
+	compilerCache = createCache(),
+
+	// General-purpose constants
+	strundefined = typeof undefined,
+	MAX_NEGATIVE = 1 << 31,
+
+	// Array methods
+	arr = [],
+	pop = arr.pop,
+	push = arr.push,
+	slice = arr.slice,
+	// Use a stripped-down indexOf if we can't use a native one
+	indexOf = arr.indexOf || function( elem ) {
+		var i = 0,
+			len = this.length;
+		for ( ; i < len; i++ ) {
+			if ( this[i] === elem ) {
+				return i;
+			}
+		}
+		return -1;
+	},
+
+
+	// 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#" ),
+
+	// Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors
+	operators = "([*^$|!~]?=)",
+	attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace +
+		"*(?:" + operators + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]",
+
+	// Prefer arguments quoted,
+	//   then not containing pseudos/brackets,
+	//   then attribute selectors/non-parenthetical expressions,
+	//   then anything else
+	// These preferences are here to reduce the number of selectors
+	//   needing tokenize in the PSEUDO preFilter
+	pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)\\)|)",
+
+	// Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
+	rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
+
+	rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
+	rcombinators = new RegExp( "^" + whitespace + "*([\\x20\\t\\r\\n\\f>+~])" + whitespace + "*" ),
+	rpseudo = new RegExp( pseudos ),
+	ridentifier = new RegExp( "^" + identifier + "$" ),
+
+	matchExpr = {
+		"ID": new RegExp( "^#(" + characterEncoding + ")" ),
+		"CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ),
+		"NAME": new RegExp( "^\\[name=['\"]?(" + 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" ),
+		// 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" )
+	},
+
+	rsibling = /[\x20\t\r\n\f]*[+~]/,
+
+	rnative = /^[^{]+\{\s*\[native code/,
+
+	// Easily-parseable/retrievable ID or TAG or CLASS selectors
+	rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
+
+	rinputs = /^(?:input|select|textarea|button)$/i,
+	rheader = /^h\d$/i,
+
+	rescape = /'|\\/g,
+	rattributeQuotes = /\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,
+
+	// CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
+	runescape = /\\([\da-fA-F]{1,6}[\x20\t\r\n\f]?|.)/g,
+	funescape = function( _, escaped ) {
+		var high = "0x" + escaped - 0x10000;
+		// NaN means non-codepoint
+		return high !== high ?
+			escaped :
+			// BMP codepoint
+			high < 0 ?
+				String.fromCharCode( high + 0x10000 ) :
+				// Supplemental Plane codepoint (surrogate pair)
+				String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
+	};
+
+// Use a stripped-down slice if we can't use a native one
+try {
+	slice.call( preferredDoc.documentElement.childNodes, 0 )[0].nodeType;
+} catch ( e ) {
+	slice = function( i ) {
+		var elem,
+			results = [];
+		while ( (elem = this[i++]) ) {
+			results.push( elem );
+		}
+		return results;
+	};
+}
+
+/**
+ * For feature detection
+ * @param {Function} fn The function to test for native support
+ */
+function isNative( fn ) {
+	return rnative.test( fn + "" );
+}
+
+/**
+ * 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 cache,
+		keys = [];
+
+	return (cache = function( 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);
+	});
+}
+
+/**
+ * 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 {
+		// release memory in IE
+		div = null;
+	}
+}
+
+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 || [];
+
+	if ( !selector || typeof selector !== "string" ) {
+		return results;
+	}
+
+	if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) {
+		return [];
+	}
+
+	if ( !documentIsXML && !seed ) {
+
+		// Shortcuts
+		if ( (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 #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, slice.call(context.getElementsByTagName( selector ), 0) );
+				return results;
+
+			// Speed-up: Sizzle(".CLASS")
+			} else if ( (m = match[3]) && support.getByClassName && context.getElementsByClassName ) {
+				push.apply( results, slice.call(context.getElementsByClassName( m ), 0) );
+				return results;
+			}
+		}
+
+		// QSA path
+		if ( support.qsa && !rbuggyQSA.test(selector) ) {
+			old = true;
+			nid = expando;
+			newContext = context;
+			newSelector = nodeType === 9 && 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 ) && context.parentNode || context;
+				newSelector = groups.join(",");
+			}
+
+			if ( newSelector ) {
+				try {
+					push.apply( results, slice.call( newContext.querySelectorAll(
+						newSelector
+					), 0 ) );
+					return results;
+				} catch(qsaError) {
+				} finally {
+					if ( !old ) {
+						context.removeAttribute("id");
+					}
+				}
+			}
+		}
+	}
+
+	// All others
+	return select( selector.replace( rtrim, "$1" ), context, results, seed );
+}
+
+/**
+ * Detect xml
+ * @param {Element|Object} elem An element or a document
+ */
+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 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;
+
+	// Support tests
+	documentIsXML = isXML( doc );
+
+	// Check if getElementsByTagName("*") returns only elements
+	support.tagNameNoComments = assert(function( div ) {
+		div.appendChild( doc.createComment("") );
+		return !div.getElementsByTagName("*").length;
+	});
+
+	// Check if attributes should be retrieved by attribute nodes
+	support.attributes = assert(function( div ) {
+		div.innerHTML = "<select></select>";
+		var type = typeof div.lastChild.getAttribute("multiple");
+		// IE8 returns a string for some attributes even when not present
+		return type !== "boolean" && type !== "string";
+	});
+
+	// Check if getElementsByClassName can be trusted
+	support.getByClassName = assert(function( div ) {
+		// Opera can't find a second classname (in 9.6)
+		div.innerHTML = "<div class='hidden e'></div><div class='hidden'></div>";
+		if ( !div.getElementsByClassName || !div.getElementsByClassName("e").length ) {
+			return false;
+		}
+
+		// Safari 3.2 caches class attributes and doesn't catch changes
+		div.lastChild.className = "e";
+		return div.getElementsByClassName("e").length === 2;
+	});
+
+	// Check if getElementById returns elements by name
+	// Check if getElementsByName privileges form controls or returns elements by ID
+	support.getByName = assert(function( div ) {
+		// Inject content
+		div.id = expando + 0;
+		div.innerHTML = "<a name='" + expando + "'></a><div name='" + expando + "'></div>";
+		docElem.insertBefore( div, docElem.firstChild );
+
+		// Test
+		var pass = doc.getElementsByName &&
+			// buggy browsers will return fewer than the correct 2
+			doc.getElementsByName( expando ).length === 2 +
+			// buggy browsers will return more than the correct 0
+			doc.getElementsByName( expando + 0 ).length;
+		support.getIdNotName = !doc.getElementById( expando );
+
+		// Cleanup
+		docElem.removeChild( div );
+
+		return pass;
+	});
+
+	// IE6/7 return modified attributes
+	Expr.attrHandle = assert(function( div ) {
+		div.innerHTML = "<a href='#'></a>";
+		return div.firstChild && typeof div.firstChild.getAttribute !== strundefined &&
+			div.firstChild.getAttribute("href") === "#";
+	}) ?
+		{} :
+		{
+			"href": function( elem ) {
+				return elem.getAttribute( "href", 2 );
+			},
+			"type": function( elem ) {
+				return elem.getAttribute("type");
+			}
+		};
+
+	// ID find and filter
+	if ( support.getIdNotName ) {
+		Expr.find["ID"] = function( id, context ) {
+			if ( typeof context.getElementById !== strundefined && !documentIsXML ) {
+				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 {
+		Expr.find["ID"] = function( id, context ) {
+			if ( typeof context.getElementById !== strundefined && !documentIsXML ) {
+				var m = context.getElementById( id );
+
+				return m ?
+					m.id === id || typeof m.getAttributeNode !== strundefined && m.getAttributeNode("id").value === id ?
+						[m] :
+						undefined :
+					[];
+			}
+		};
+		Expr.filter["ID"] =  function( id ) {
+			var attrId = id.replace( runescape, funescape );
+			return function( elem ) {
+				var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id");
+				return node && node.value === attrId;
+			};
+		};
+	}
+
+	// Tag
+	Expr.find["TAG"] = support.tagNameNoComments ?
+		function( tag, context ) {
+			if ( typeof context.getElementsByTagName !== strundefined ) {
+				return context.getElementsByTagName( tag );
+			}
+		} :
+		function( tag, context ) {
+			var elem,
+				tmp = [],
+				i = 0,
+				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;
+		};
+
+	// Name
+	Expr.find["NAME"] = support.getByName && function( tag, context ) {
+		if ( typeof context.getElementsByName !== strundefined ) {
+			return context.getElementsByName( name );
+		}
+	};
+
+	// Class
+	Expr.find["CLASS"] = support.getByClassName && function( className, context ) {
+		if ( typeof context.getElementsByClassName !== strundefined && !documentIsXML ) {
+			return context.getElementsByClassName( className );
+		}
+	};
+
+	// QSA and matchesSelector support
+
+	// matchesSelector(:active) reports false when true (IE9/Opera 11.5)
+	rbuggyMatches = [];
+
+	// qSa(:focus) reports false when true (Chrome 21),
+	// no need to also add to buggyMatches since matches checks buggyQSA
+	// A support test would require too much code (would include document ready)
+	rbuggyQSA = [ ":focus" ];
+
+	if ( (support.qsa = isNative(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 explictly
+			// setting a boolean content attribute,
+			// since its presence should be enough
+			// http://bugs.jquery.com/ticket/12359
+			div.innerHTML = "<select><option selected=''></option></select>";
+
+			// IE8 - Some boolean attributes are not treated correctly
+			if ( !div.querySelectorAll("[selected]").length ) {
+				rbuggyQSA.push( "\\[" + whitespace + "*(?:checked|disabled|ismap|multiple|readonly|selected|value)" );
+			}
+
+			// 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");
+			}
+		});
+
+		assert(function( div ) {
+
+			// Opera 10-12/IE8 - ^= $= *= and empty values
+			// Should not select anything
+			div.innerHTML = "<input type='hidden' i=''/>";
+			if ( div.querySelectorAll("[i^='']").length ) {
+				rbuggyQSA.push( "[*^$]=" + 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 = isNative( (matches = docElem.matchesSelector ||
+		docElem.mozMatchesSelector ||
+		docElem.webkitMatchesSelector ||
+		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 = new RegExp( rbuggyQSA.join("|") );
+	rbuggyMatches = new RegExp( rbuggyMatches.join("|") );
+
+	// Element contains another
+	// Purposefully does not implement inclusive descendent
+	// As in, an element does not contain itself
+	contains = isNative(docElem.contains) || docElem.compareDocumentPosition ?
+		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;
+		};
+
+	// Document order sorting
+	sortOrder = docElem.compareDocumentPosition ?
+	function( a, b ) {
+		var compare;
+
+		if ( a === b ) {
+			hasDuplicate = true;
+			return 0;
+		}
+
+		if ( (compare = b.compareDocumentPosition && a.compareDocumentPosition && a.compareDocumentPosition( b )) ) {
+			if ( compare & 1 || a.parentNode && a.parentNode.nodeType === 11 ) {
+				if ( a === doc || contains( preferredDoc, a ) ) {
+					return -1;
+				}
+				if ( b === doc || contains( preferredDoc, b ) ) {
+					return 1;
+				}
+				return 0;
+			}
+			return compare & 4 ? -1 : 1;
+		}
+
+		return a.compareDocumentPosition ? -1 : 1;
+	} :
+	function( a, b ) {
+		var cur,
+			i = 0,
+			aup = a.parentNode,
+			bup = b.parentNode,
+			ap = [ a ],
+			bp = [ b ];
+
+		// Exit early if the nodes are identical
+		if ( a === b ) {
+			hasDuplicate = true;
+			return 0;
+
+		// Parentless nodes are either documents or disconnected
+		} else if ( !aup || !bup ) {
+			return a === doc ? -1 :
+				b === doc ? 1 :
+				aup ? -1 :
+				bup ? 1 :
+				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;
+	};
+
+	// Always assume the presence of duplicates if sort doesn't
+	// pass them to our comparison function (as in Google Chrome).
+	hasDuplicate = false;
+	[0, 0].sort( sortOrder );
+	support.detectDuplicates = hasDuplicate;
+
+	return document;
+};
+
+Sizzle.matches = function( expr, elements ) {
+	return Sizzle( expr, null, null, elements );
+};
+
+Sizzle.matchesSelector = function( elem, expr ) {
+	// Set document vars if needed
+	if ( ( elem.ownerDocument || elem ) !== document ) {
+		setDocument( elem );
+	}
+
+	// Make sure that attribute selectors are quoted
+	expr = expr.replace( rattributeQuotes, "='$1']" );
+
+	// rbuggyQSA always contains :focus, so no need for an existence check
+	if ( support.matchesSelector && !documentIsXML && (!rbuggyMatches || !rbuggyMatches.test(expr)) && !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 ) {
+	var val;
+
+	// Set document vars if needed
+	if ( ( elem.ownerDocument || elem ) !== document ) {
+		setDocument( elem );
+	}
+
+	if ( !documentIsXML ) {
+		name = name.toLowerCase();
+	}
+	if ( (val = Expr.attrHandle[ name ]) ) {
+		return val( elem );
+	}
+	if ( documentIsXML || support.attributes ) {
+		return elem.getAttribute( name );
+	}
+	return ( (val = elem.getAttributeNode( name )) || elem.getAttribute( name ) ) && elem[ name ] === true ?
+		name :
+		val && val.specified ? val.value : null;
+};
+
+Sizzle.error = function( msg ) {
+	throw new Error( "Syntax error, unrecognized expression: " + msg );
+};
+
+// Document sorting and removing duplicates
+Sizzle.uniqueSort = function( results ) {
+	var elem,
+		duplicates = [],
+		i = 1,
+		j = 0;
+
+	// Unless we *know* we can detect duplicates, assume their presence
+	hasDuplicate = !support.detectDuplicates;
+	results.sort( sortOrder );
+
+	if ( hasDuplicate ) {
+		for ( ; (elem = results[i]); i++ ) {
+			if ( elem === results[ i - 1 ] ) {
+				j = duplicates.push( i );
+			}
+		}
+		while ( j-- ) {
+			results.splice( duplicates[ j ], 1 );
+		}
+	}
+
+	return results;
+};
+
+function siblingCheck( a, b ) {
+	var cur = b && a,
+		diff = cur && ( ~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
+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
+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
+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]);
+				}
+			}
+		});
+	});
+}
+
+/**
+ * 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
+		for ( ; (node = elem[i]); 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 (see #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,
+
+	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[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[5] && match[2];
+
+			if ( matchExpr["CHILD"].test( match[0] ) ) {
+				return null;
+			}
+
+			// Accept quoted arguments as-is
+			if ( match[4] ) {
+				match[2] = match[4];
+
+			// 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( nodeName ) {
+			if ( nodeName === "*" ) {
+				return function() { return true; };
+			}
+
+			nodeName = nodeName.replace( runescape, funescape ).toLowerCase();
+			return 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( elem.className || (typeof elem.getAttribute !== strundefined && 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 + " " ).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.call( 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 );
+					return !results.pop();
+				};
+		}),
+
+		"has": markFunction(function( selector ) {
+			return function( elem ) {
+				return Sizzle( selector, elem ).length > 0;
+			};
+		}),
+
+		"contains": markFunction(function( text ) {
+			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 identifider
+			if ( !ridentifier.test(lang || "") ) {
+				Sizzle.error( "unsupported lang: " + lang );
+			}
+			lang = lang.replace( runescape, funescape ).toLowerCase();
+			return function( elem ) {
+				var elemLang;
+				do {
+					if ( (elemLang = documentIsXML ?
+						elem.getAttribute("xml:lang") || elem.getAttribute("lang") :
+						elem.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 only affected by element nodes and content nodes(including text(3), cdata(4)),
+			//   not comment, processing instructions, or others
+			// Thanks to Diego Perini for the nodeName shortcut
+			//   Greater than "@" means alpha characters (specifically not starting with "#" or "?")
+			for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
+				if ( elem.nodeName > "@" || elem.nodeType === 3 || elem.nodeType === 4 ) {
+					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;
+			// IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)
+			// use getAttribute instead to test this case
+			return elem.nodeName.toLowerCase() === "input" &&
+				elem.type === "text" &&
+				( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === elem.type );
+		},
+
+		// 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;
+		})
+	}
+};
+
+// 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 );
+}
+
+function tokenize( 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 data, cache, outerCache,
+				dirkey = 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 ( (cache = outerCache[ dir ]) && cache[0] === dirkey ) {
+							if ( (data = cache[1]) === true || data === cachedruns ) {
+								return data === true;
+							}
+						} else {
+							cache = outerCache[ dir ] = [ dirkey ];
+							cache[1] = matcher( elem, context, xml ) || cachedruns;
+							if ( cache[1] === true ) {
+								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 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.call( 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.call( checkContext, elem ) > -1;
+		}, implicitRelative, true ),
+		matchers = [ function( elem, context, xml ) {
+			return ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
+				(checkContext = context).nodeType ?
+					matchContext( elem, context, xml ) :
+					matchAnyContext( elem, context, xml ) );
+		} ];
+
+	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( tokens.slice( 0, i - 1 ) ).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 ) {
+	// A counter to specify which element is currently being matched
+	var matcherCachedRuns = 0,
+		bySet = setMatchers.length > 0,
+		byElement = elementMatchers.length > 0,
+		superMatcher = function( seed, context, xml, results, expandContext ) {
+			var elem, j, matcher,
+				setMatched = [],
+				matchedCount = 0,
+				i = "0",
+				unmatched = seed && [],
+				outermost = expandContext != null,
+				contextBackup = outermostContext,
+				// We must always have either seed elements or context
+				elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ),
+				// Use integer dirruns iff this is the outermost matcher
+				dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1);
+
+			if ( outermost ) {
+				outermostContext = context !== document && context;
+				cachedruns = matcherCachedRuns;
+			}
+
+			// Add elements passing elementMatchers directly to results
+			// Keep `i` a string if there are no elements so `matchedCount` will be "00" below
+			for ( ; (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;
+						cachedruns = ++matcherCachedRuns;
+					}
+				}
+
+				// 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, group /* 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 ( !group ) {
+			group = tokenize( selector );
+		}
+		i = group.length;
+		while ( i-- ) {
+			cached = matcherFromTokens( group[i] );
+			if ( cached[ expando ] ) {
+				setMatchers.push( cached );
+			} else {
+				elementMatchers.push( cached );
+			}
+		}
+
+		// Cache the compiled function
+		cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
+	}
+	return cached;
+};
+
+function multipleContexts( selector, contexts, results ) {
+	var i = 0,
+		len = contexts.length;
+	for ( ; i < len; i++ ) {
+		Sizzle( selector, contexts[i], results );
+	}
+	return results;
+}
+
+function select( selector, context, results, seed ) {
+	var i, tokens, token, type, find,
+		match = tokenize( selector );
+
+	if ( !seed ) {
+		// Try to minimize operations if there is 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" &&
+					context.nodeType === 9 && !documentIsXML &&
+					Expr.relative[ tokens[1].type ] ) {
+
+				context = Expr.find["ID"]( token.matches[0].replace( runescape, funescape ), context )[0];
+				if ( !context ) {
+					return results;
+				}
+
+				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 ) && 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, slice.call( seed, 0 ) );
+							return results;
+						}
+
+						break;
+					}
+				}
+			}
+		}
+	}
+
+	// Compile and execute a filtering function
+	// Provide `match` to avoid retokenization if we modified the selector above
+	compile( selector, match )(
+		seed,
+		context,
+		documentIsXML,
+		results,
+		rsibling.test( selector )
+	);
+	return results;
+}
+
+// Deprecated
+Expr.pseudos["nth"] = Expr.pseudos["eq"];
+
+// Easy API for creating new setFilters
+function setFilters() {}
+Expr.filters = setFilters.prototype = Expr.pseudos;
+Expr.setFilters = new setFilters();
+
+// Initialize with the default document
+setDocument();
+
+// Override sizzle attribute retrieval
+Sizzle.attr = jQuery.attr;
+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;
+
+
+})( window );
+var runtil = /Until$/,
+	rparentsprev = /^(?:parents|prev(?:Until|All))/,
+	isSimple = /^.[^:#\[\.,]*$/,
+	rneedsContext = jQuery.expr.match.needsContext,
+	// methods guaranteed to produce a unique set when starting from a unique set
+	guaranteedUnique = {
+		children: true,
+		contents: true,
+		next: true,
+		prev: true
+	};
+
+jQuery.fn.extend({
+	find: function( selector ) {
+		var i, ret, self,
+			len = this.length;
+
+		if ( typeof selector !== "string" ) {
+			self = this;
+			return this.pushStack( jQuery( selector ).filter(function() {
+				for ( i = 0; i < len; i++ ) {
+					if ( jQuery.contains( self[ i ], this ) ) {
+						return true;
+					}
+				}
+			}) );
+		}
+
+		ret = [];
+		for ( i = 0; i < len; i++ ) {
+			jQuery.find( selector, this[ 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;
+		return ret;
+	},
+
+	has: function( target ) {
+		var i,
+			targets = jQuery( target, this ),
+			len = targets.length;
+
+		return this.filter(function() {
+			for ( i = 0; i < len; i++ ) {
+				if ( jQuery.contains( this, targets[i] ) ) {
+					return true;
+				}
+			}
+		});
+	},
+
+	not: function( selector ) {
+		return this.pushStack( winnow(this, selector, false) );
+	},
+
+	filter: function( selector ) {
+		return this.pushStack( winnow(this, selector, true) );
+	},
+
+	is: function( selector ) {
+		return !!selector && (
+			typeof selector === "string" ?
+				// 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".
+				rneedsContext.test( selector ) ?
+					jQuery( selector, this.context ).index( this[0] ) >= 0 :
+					jQuery.filter( selector, this ).length > 0 :
+				this.filter( selector ).length > 0 );
+	},
+
+	closest: function( selectors, context ) {
+		var cur,
+			i = 0,
+			l = this.length,
+			ret = [],
+			pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ?
+				jQuery( selectors, context || this.context ) :
+				0;
+
+		for ( ; i < l; i++ ) {
+			cur = this[i];
+
+			while ( cur && cur.ownerDocument && cur !== context && cur.nodeType !== 11 ) {
+				if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) {
+					ret.push( cur );
+					break;
+				}
+				cur = cur.parentNode;
+			}
+		}
+
+		return this.pushStack( ret.length > 1 ? jQuery.unique( ret ) : ret );
+	},
+
+	// Determine the position of an element within
+	// the matched set of elements
+	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 jQuery.inArray( this[0], jQuery( elem ) );
+		}
+
+		// Locate the position of the desired element
+		return jQuery.inArray(
+			// If it receives a jQuery object, the first element is used
+			elem.jquery ? elem[0] : elem, this );
+	},
+
+	add: function( selector, context ) {
+		var set = typeof selector === "string" ?
+				jQuery( selector, context ) :
+				jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ),
+			all = jQuery.merge( this.get(), set );
+
+		return this.pushStack( jQuery.unique(all) );
+	},
+
+	addBack: function( selector ) {
+		return this.add( selector == null ?
+			this.prevObject : this.prevObject.filter(selector)
+		);
+	}
+});
+
+jQuery.fn.andSelf = jQuery.fn.addBack;
+
+function sibling( cur, dir ) {
+	do {
+		cur = cur[ dir ];
+	} while ( cur && 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 jQuery.nodeName( elem, "iframe" ) ?
+			elem.contentDocument || elem.contentWindow.document :
+			jQuery.merge( [], elem.childNodes );
+	}
+}, function( name, fn ) {
+	jQuery.fn[ name ] = function( until, selector ) {
+		var ret = jQuery.map( this, fn, until );
+
+		if ( !runtil.test( name ) ) {
+			selector = until;
+		}
+
+		if ( selector && typeof selector === "string" ) {
+			ret = jQuery.filter( selector, ret );
+		}
+
+		ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret;
+
+		if ( this.length > 1 && rparentsprev.test( name ) ) {
+			ret = ret.reverse();
+		}
+
+		return this.pushStack( ret );
+	};
+});
+
+jQuery.extend({
+	filter: function( expr, elems, not ) {
+		if ( not ) {
+			expr = ":not(" + expr + ")";
+		}
+
+		return elems.length === 1 ?
+			jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] :
+			jQuery.find.matches(expr, elems);
+	},
+
+	dir: function( elem, dir, until ) {
+		var matched = [],
+			cur = elem[ dir ];
+
+		while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
+			if ( cur.nodeType === 1 ) {
+				matched.push( cur );
+			}
+			cur = cur[dir];
+		}
+		return matched;
+	},
+
+	sibling: function( n, elem ) {
+		var r = [];
+
+		for ( ; n; n = n.nextSibling ) {
+			if ( n.nodeType === 1 && n !== elem ) {
+				r.push( n );
+			}
+		}
+
+		return r;
+	}
+});
+
+// Implement the identical functionality for filter and not
+function winnow( elements, qualifier, keep ) {
+
+	// Can't pass null or undefined to indexOf in Firefox 4
+	// Set to 0 to skip string check
+	qualifier = qualifier || 0;
+
+	if ( jQuery.isFunction( qualifier ) ) {
+		return jQuery.grep(elements, function( elem, i ) {
+			var retVal = !!qualifier.call( elem, i, elem );
+			return retVal === keep;
+		});
+
+	} else if ( qualifier.nodeType ) {
+		return jQuery.grep(elements, function( elem ) {
+			return ( elem === qualifier ) === keep;
+		});
+
+	} else if ( typeof qualifier === "string" ) {
+		var filtered = jQuery.grep(elements, function( elem ) {
+			return elem.nodeType === 1;
+		});
+
+		if ( isSimple.test( qualifier ) ) {
+			return jQuery.filter(qualifier, filtered, !keep);
+		} else {
+			qualifier = jQuery.filter( qualifier, filtered );
+		}
+	}
+
+	return jQuery.grep(elements, function( elem ) {
+		return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep;
+	});
+}
+function createSafeFragment( document ) {
+	var list = nodeNames.split( "|" ),
+		safeFrag = document.createDocumentFragment();
+
+	if ( safeFrag.createElement ) {
+		while ( list.length ) {
+			safeFrag.createElement(
+				list.pop()
+			);
+		}
+	}
+	return safeFrag;
+}
+
+var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" +
+		"header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",
+	rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g,
+	rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i"),
+	rleadingWhitespace = /^\s+/,
+	rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,
+	rtagName = /<([\w:]+)/,
+	rtbody = /<tbody/i,
+	rhtml = /<|&#?\w+;/,
+	rnoInnerhtml = /<(?:script|style|link)/i,
+	manipulation_rcheckableType = /^(?:checkbox|radio)$/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 = {
+		option: [ 1, "<select multiple='multiple'>", "</select>" ],
+		legend: [ 1, "<fieldset>", "</fieldset>" ],
+		area: [ 1, "<map>", "</map>" ],
+		param: [ 1, "<object>", "</object>" ],
+		thead: [ 1, "<table>", "</table>" ],
+		tr: [ 2, "<table><tbody>", "</tbody></table>" ],
+		col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
+		td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
+
+		// IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags,
+		// unless wrapped in a div with non-breaking characters in front of it.
+		_default: jQuery.support.htmlSerialize ? [ 0, "", "" ] : [ 1, "X<div>", "</div>"  ]
+	},
+	safeFragment = createSafeFragment( document ),
+	fragmentDiv = safeFragment.appendChild( document.createElement("div") );
+
+wrapMap.optgroup = wrapMap.option;
+wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
+wrapMap.th = wrapMap.td;
+
+jQuery.fn.extend({
+	text: function( value ) {
+		return jQuery.access( this, function( value ) {
+			return value === undefined ?
+				jQuery.text( this ) :
+				this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) );
+		}, null, value, arguments.length );
+	},
+
+	wrapAll: function( html ) {
+		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
+			var 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.firstChild && elem.firstChild.nodeType === 1 ) {
+					elem = elem.firstChild;
+				}
+
+				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();
+	},
+
+	append: function() {
+		return this.domManip(arguments, true, function( elem ) {
+			if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
+				this.appendChild( elem );
+			}
+		});
+	},
+
+	prepend: function() {
+		return this.domManip(arguments, true, function( elem ) {
+			if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
+				this.insertBefore( elem, this.firstChild );
+			}
+		});
+	},
+
+	before: function() {
+		return this.domManip( arguments, false, function( elem ) {
+			if ( this.parentNode ) {
+				this.parentNode.insertBefore( elem, this );
+			}
+		});
+	},
+
+	after: function() {
+		return this.domManip( arguments, false, function( elem ) {
+			if ( this.parentNode ) {
+				this.parentNode.insertBefore( elem, this.nextSibling );
+			}
+		});
+	},
+
+	// keepData is for internal use only--do not document
+	remove: function( selector, keepData ) {
+		var elem,
+			i = 0;
+
+		for ( ; (elem = this[i]) != null; i++ ) {
+			if ( !selector || jQuery.filter( selector, [ elem ] ).length > 0 ) {
+				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++ ) {
+			// Remove element nodes and prevent memory leaks
+			if ( elem.nodeType === 1 ) {
+				jQuery.cleanData( getAll( elem, false ) );
+			}
+
+			// Remove any remaining nodes
+			while ( elem.firstChild ) {
+				elem.removeChild( elem.firstChild );
+			}
+
+			// If this is a select, ensure that it displays empty (#12336)
+			// Support: IE<9
+			if ( elem.options && jQuery.nodeName( elem, "select" ) ) {
+				elem.options.length = 0;
+			}
+		}
+
+		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 jQuery.access( this, function( value ) {
+			var elem = this[0] || {},
+				i = 0,
+				l = this.length;
+
+			if ( value === undefined ) {
+				return elem.nodeType === 1 ?
+					elem.innerHTML.replace( rinlinejQuery, "" ) :
+					undefined;
+			}
+
+			// See if we can take a shortcut and just use innerHTML
+			if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
+				( jQuery.support.htmlSerialize || !rnoshimcache.test( value )  ) &&
+				( jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value ) ) &&
+				!wrapMap[ ( rtagName.exec( value ) || ["", ""] )[1].toLowerCase() ] ) {
+
+				value = value.replace( rxhtmlTag, "<$1></$2>" );
+
+				try {
+					for (; i < l; i++ ) {
+						// Remove element nodes and prevent memory leaks
+						elem = this[i] || {};
+						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( value ) {
+		var isFunc = jQuery.isFunction( value );
+
+		// Make sure that the elements are removed from the DOM before they are inserted
+		// this can help fix replacing a parent with child elements
+		if ( !isFunc && typeof value !== "string" ) {
+			value = jQuery( value ).not( this ).detach();
+		}
+
+		return this.domManip( [ value ], true, function( elem ) {
+			var next = this.nextSibling,
+				parent = this.parentNode;
+
+			if ( parent ) {
+				jQuery( this ).remove();
+				parent.insertBefore( elem, next );
+			}
+		});
+	},
+
+	detach: function( selector ) {
+		return this.remove( selector, true );
+	},
+
+	domManip: function( args, table, callback ) {
+
+		// Flatten any nested arrays
+		args = core_concat.apply( [], args );
+
+		var first, node, hasScripts,
+			scripts, doc, fragment,
+			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" || jQuery.support.checkClone || !rchecked.test( value ) ) ) {
+			return this.each(function( index ) {
+				var self = set.eq( index );
+				if ( isFunction ) {
+					args[0] = value.call( this, index, table ? self.html() : undefined );
+				}
+				self.domManip( args, table, callback );
+			});
+		}
+
+		if ( l ) {
+			fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this );
+			first = fragment.firstChild;
+
+			if ( fragment.childNodes.length === 1 ) {
+				fragment = first;
+			}
+
+			if ( first ) {
+				table = table && jQuery.nodeName( first, "tr" );
+				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 ) {
+							jQuery.merge( scripts, getAll( node, "script" ) );
+						}
+					}
+
+					callback.call(
+						table && jQuery.nodeName( this[i], "table" ) ?
+							findOrAppend( this[i], "tbody" ) :
+							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 || "" ) &&
+							!jQuery._data( node, "globalEval" ) && jQuery.contains( doc, node ) ) {
+
+							if ( node.src ) {
+								// Hope ajax is available...
+								jQuery.ajax({
+									url: node.src,
+									type: "GET",
+									dataType: "script",
+									async: false,
+									global: false,
+									"throws": true
+								});
+							} else {
+								jQuery.globalEval( ( node.text || node.textContent || node.innerHTML || "" ).replace( rcleanScript, "" ) );
+							}
+						}
+					}
+				}
+
+				// Fix #11809: Avoid leaking memory
+				fragment = first = null;
+			}
+		}
+
+		return this;
+	}
+});
+
+function findOrAppend( elem, tag ) {
+	return elem.getElementsByTagName( tag )[0] || elem.appendChild( elem.ownerDocument.createElement( tag ) );
+}
+
+// Replace/restore the type attribute of script elements for safe DOM manipulation
+function disableScript( elem ) {
+	var attr = elem.getAttributeNode("type");
+	elem.type = ( attr && attr.specified ) + "/" + 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 elem,
+		i = 0;
+	for ( ; (elem = elems[i]) != null; i++ ) {
+		jQuery._data( elem, "globalEval", !refElements || jQuery._data( refElements[i], "globalEval" ) );
+	}
+}
+
+function cloneCopyEvent( src, dest ) {
+
+	if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) {
+		return;
+	}
+
+	var type, i, l,
+		oldData = jQuery._data( src ),
+		curData = jQuery._data( dest, oldData ),
+		events = oldData.events;
+
+	if ( events ) {
+		delete curData.handle;
+		curData.events = {};
+
+		for ( type in events ) {
+			for ( i = 0, l = events[ type ].length; i < l; i++ ) {
+				jQuery.event.add( dest, type, events[ type ][ i ] );
+			}
+		}
+	}
+
+	// make the cloned public data object a copy from the original
+	if ( curData.data ) {
+		curData.data = jQuery.extend( {}, curData.data );
+	}
+}
+
+function fixCloneNodeIssues( src, dest ) {
+	var nodeName, e, data;
+
+	// We do not need to do anything for non-Elements
+	if ( dest.nodeType !== 1 ) {
+		return;
+	}
+
+	nodeName = dest.nodeName.toLowerCase();
+
+	// IE6-8 copies events bound via attachEvent when using cloneNode.
+	if ( !jQuery.support.noCloneEvent && dest[ jQuery.expando ] ) {
+		data = jQuery._data( dest );
+
+		for ( e in data.events ) {
+			jQuery.removeEvent( dest, e, data.handle );
+		}
+
+		// Event data gets referenced instead of copied if the expando gets copied too
+		dest.removeAttribute( jQuery.expando );
+	}
+
+	// IE blanks contents when cloning scripts, and tries to evaluate newly-set text
+	if ( nodeName === "script" && dest.text !== src.text ) {
+		disableScript( dest ).text = src.text;
+		restoreScript( dest );
+
+	// IE6-10 improperly clones children of object elements using classid.
+	// IE10 throws NoModificationAllowedError if parent is null, #12132.
+	} else if ( nodeName === "object" ) {
+		if ( dest.parentNode ) {
+			dest.outerHTML = src.outerHTML;
+		}
+
+		// This path appears unavoidable for IE9. When cloning an object
+		// element in IE9, the outerHTML strategy above is not sufficient.
+		// If the src has innerHTML and the destination does not,
+		// copy the src.innerHTML into the dest.innerHTML. #10324
+		if ( jQuery.support.html5Clone && ( src.innerHTML && !jQuery.trim(dest.innerHTML) ) ) {
+			dest.innerHTML = src.innerHTML;
+		}
+
+	} else if ( nodeName === "input" && manipulation_rcheckableType.test( src.type ) ) {
+		// IE6-8 fails to persist the checked state of a cloned checkbox
+		// or radio button. Worse, IE6-7 fail to give the cloned element
+		// a checked appearance if the defaultChecked value isn't also set
+
+		dest.defaultChecked = dest.checked = src.checked;
+
+		// IE6-7 get confused and end up setting the value of a cloned
+		// checkbox/radio button to an empty string instead of "on"
+		if ( dest.value !== src.value ) {
+			dest.value = src.value;
+		}
+
+	// IE6-8 fails to return the selected option to the default selected
+	// state when cloning options
+	} else if ( nodeName === "option" ) {
+		dest.defaultSelected = dest.selected = src.defaultSelected;
+
+	// IE6-8 fails to set the defaultValue to the correct value when
+	// cloning other types of input fields
+	} else if ( nodeName === "input" || nodeName === "textarea" ) {
+		dest.defaultValue = src.defaultValue;
+	}
+}
+
+jQuery.each({
+	appendTo: "append",
+	prependTo: "prepend",
+	insertBefore: "before",
+	insertAfter: "after",
+	replaceAll: "replaceWith"
+}, function( name, original ) {
+	jQuery.fn[ name ] = function( selector ) {
+		var elems,
+			i = 0,
+			ret = [],
+			insert = jQuery( selector ),
+			last = insert.length - 1;
+
+		for ( ; i <= last; i++ ) {
+			elems = i === last ? this : this.clone(true);
+			jQuery( insert[i] )[ original ]( elems );
+
+			// Modern browsers can apply jQuery collections as arrays, but oldIE needs a .get()
+			core_push.apply( ret, elems.get() );
+		}
+
+		return this.pushStack( ret );
+	};
+});
+
+function getAll( context, tag ) {
+	var elems, elem,
+		i = 0,
+		found = typeof context.getElementsByTagName !== core_strundefined ? context.getElementsByTagName( tag || "*" ) :
+			typeof context.querySelectorAll !== core_strundefined ? context.querySelectorAll( tag || "*" ) :
+			undefined;
+
+	if ( !found ) {
+		for ( found = [], elems = context.childNodes || context; (elem = elems[i]) != null; i++ ) {
+			if ( !tag || jQuery.nodeName( elem, tag ) ) {
+				found.push( elem );
+			} else {
+				jQuery.merge( found, getAll( elem, tag ) );
+			}
+		}
+	}
+
+	return tag === undefined || tag && jQuery.nodeName( context, tag ) ?
+		jQuery.merge( [ context ], found ) :
+		found;
+}
+
+// Used in buildFragment, fixes the defaultChecked property
+function fixDefaultChecked( elem ) {
+	if ( manipulation_rcheckableType.test( elem.type ) ) {
+		elem.defaultChecked = elem.checked;
+	}
+}
+
+jQuery.extend({
+	clone: function( elem, dataAndEvents, deepDataAndEvents ) {
+		var destElements, node, clone, i, srcElements,
+			inPage = jQuery.contains( elem.ownerDocument, elem );
+
+		if ( jQuery.support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) {
+			clone = elem.cloneNode( true );
+
+		// IE<=8 does not properly clone detached, unknown element nodes
+		} else {
+			fragmentDiv.innerHTML = elem.outerHTML;
+			fragmentDiv.removeChild( clone = fragmentDiv.firstChild );
+		}
+
+		if ( (!jQuery.support.noCloneEvent || !jQuery.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 );
+
+			// Fix all IE cloning issues
+			for ( i = 0; (node = srcElements[i]) != null; ++i ) {
+				// Ensure that the destination node is not null; Fixes #9587
+				if ( destElements[i] ) {
+					fixCloneNodeIssues( node, 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; (node = srcElements[i]) != null; i++ ) {
+					cloneCopyEvent( node, destElements[i] );
+				}
+			} else {
+				cloneCopyEvent( elem, clone );
+			}
+		}
+
+		// Preserve script evaluation history
+		destElements = getAll( clone, "script" );
+		if ( destElements.length > 0 ) {
+			setGlobalEval( destElements, !inPage && getAll( elem, "script" ) );
+		}
+
+		destElements = srcElements = node = null;
+
+		// Return the cloned set
+		return clone;
+	},
+
+	buildFragment: function( elems, context, scripts, selection ) {
+		var j, elem, contains,
+			tmp, tag, tbody, wrap,
+			l = elems.length,
+
+			// Ensure a safe fragment
+			safe = createSafeFragment( context ),
+
+			nodes = [],
+			i = 0;
+
+		for ( ; i < l; i++ ) {
+			elem = elems[ i ];
+
+			if ( elem || elem === 0 ) {
+
+				// Add nodes directly
+				if ( jQuery.type( elem ) === "object" ) {
+					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 || safe.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;
+					}
+
+					// Manually add leading whitespace removed by IE
+					if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
+						nodes.push( context.createTextNode( rleadingWhitespace.exec( elem )[0] ) );
+					}
+
+					// Remove IE's autoinserted <tbody> from table fragments
+					if ( !jQuery.support.tbody ) {
+
+						// String was a <table>, *may* have spurious <tbody>
+						elem = tag === "table" && !rtbody.test( elem ) ?
+							tmp.firstChild :
+
+							// String was a bare <thead> or <tfoot>
+							wrap[1] === "<table>" && !rtbody.test( elem ) ?
+								tmp :
+								0;
+
+						j = elem && elem.childNodes.length;
+						while ( j-- ) {
+							if ( jQuery.nodeName( (tbody = elem.childNodes[j]), "tbody" ) && !tbody.childNodes.length ) {
+								elem.removeChild( tbody );
+							}
+						}
+					}
+
+					jQuery.merge( nodes, tmp.childNodes );
+
+					// Fix #12392 for WebKit and IE > 9
+					tmp.textContent = "";
+
+					// Fix #12392 for oldIE
+					while ( tmp.firstChild ) {
+						tmp.removeChild( tmp.firstChild );
+					}
+
+					// Remember the top-level container for proper cleanup
+					tmp = safe.lastChild;
+				}
+			}
+		}
+
+		// Fix #11356: Clear elements from fragment
+		if ( tmp ) {
+			safe.removeChild( tmp );
+		}
+
+		// Reset defaultChecked for any radios and checkboxes
+		// about to be appended to the DOM in IE 6/7 (#8060)
+		if ( !jQuery.support.appendChecked ) {
+			jQuery.grep( getAll( nodes, "input" ), fixDefaultChecked );
+		}
+
+		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( safe.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 );
+					}
+				}
+			}
+		}
+
+		tmp = null;
+
+		return safe;
+	},
+
+	cleanData: function( elems, /* internal */ acceptData ) {
+		var elem, type, id, data,
+			i = 0,
+			internalKey = jQuery.expando,
+			cache = jQuery.cache,
+			deleteExpando = jQuery.support.deleteExpando,
+			special = jQuery.event.special;
+
+		for ( ; (elem = elems[i]) != null; i++ ) {
+
+			if ( acceptData || jQuery.acceptData( elem ) ) {
+
+				id = elem[ internalKey ];
+				data = id && cache[ id ];
+
+				if ( data ) {
+					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 );
+							}
+						}
+					}
+
+					// Remove cache only if it was not already removed by jQuery.event.remove
+					if ( cache[ id ] ) {
+
+						delete cache[ id ];
+
+						// IE does not allow us to delete expando properties from nodes,
+						// nor does it have a removeAttribute function on Document nodes;
+						// we must handle all of these cases
+						if ( deleteExpando ) {
+							delete elem[ internalKey ];
+
+						} else if ( typeof elem.removeAttribute !== core_strundefined ) {
+							elem.removeAttribute( internalKey );
+
+						} else {
+							elem[ internalKey ] = null;
+						}
+
+						core_deletedIds.push( id );
+					}
+				}
+			}
+		}
+	}
+});
+var iframe, getStyles, curCSS,
+	ralpha = /alpha\([^)]*\)/i,
+	ropacity = /opacity\s*=\s*([^)]*)/,
+	rposition = /^(top|right|bottom|left)$/,
+	// 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]).+)/,
+	rmargin = /^margin/,
+	rnumsplit = new RegExp( "^(" + core_pnum + ")(.*)$", "i" ),
+	rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ),
+	rrelNum = new RegExp( "^([+-])=(" + core_pnum + ")", "i" ),
+	elemdisplay = { BODY: "block" },
+
+	cssShow = { position: "absolute", visibility: "hidden", display: "block" },
+	cssNormalTransform = {
+		letterSpacing: 0,
+		fontWeight: 400
+	},
+
+	cssExpand = [ "Top", "Right", "Bottom", "Left" ],
+	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.charAt(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 isHidden( 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 );
+}
+
+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 ] = jQuery._data( 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 ] = jQuery._data( elem, "olddisplay", css_defaultDisplay(elem.nodeName) );
+			}
+		} else {
+
+			if ( !values[ index ] ) {
+				hidden = isHidden( elem );
+
+				if ( display && display !== "none" || !hidden ) {
+					jQuery._data( 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.fn.extend({
+	css: function( name, value ) {
+		return jQuery.access( this, function( elem, name, value ) {
+			var len, styles,
+				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 ) {
+		var bool = typeof state === "boolean";
+
+		return this.each(function() {
+			if ( bool ? state : isHidden( this ) ) {
+				jQuery( this ).show();
+			} else {
+				jQuery( this ).hide();
+			}
+		});
+	}
+});
+
+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;
+				}
+			}
+		}
+	},
+
+	// Exclude the following css properties to add px
+	cssNumber: {
+		"columnCount": true,
+		"fillOpacity": true,
+		"fontWeight": true,
+		"lineHeight": true,
+		"opacity": 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: {
+		// normalize float css property
+		"float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat"
+	},
+
+	// 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
+		// followed by the unprefixed version
+		hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
+
+		// Check if we're setting a value
+		if ( value !== undefined ) {
+			type = typeof value;
+
+			// convert relative number strings (+= 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 NaN and null values aren't set. See: #7116
+			if ( value == null || type === "number" && isNaN( value ) ) {
+				return;
+			}
+
+			// If a number was passed in, add 'px' to the (except for certain CSS properties)
+			if ( type === "number" && !jQuery.cssNumber[ origName ] ) {
+				value += "px";
+			}
+
+			// Fixes #8908, it can be done more correctly by specifing setters in cssHooks,
+			// but it would mean to define eight (for every problematic property) identical functions
+			if ( !jQuery.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 ) {
+
+				// Wrapped to prevent IE from throwing errors when 'invalid' values are provided
+				// Fixes bug #5509
+				try {
+					style[ name ] = value;
+				} catch(e) {}
+			}
+
+		} 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 num, val, 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 ) );
+
+		// gets hook for the prefixed version
+		// followed by the unprefixed version
+		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 ];
+		}
+
+		// Return, converting to number 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;
+	},
+
+	// A method for quickly swapping in/out CSS properties to get correct calculations
+	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;
+	}
+});
+
+// NOTE: we've included the "window" in window.getComputedStyle
+// because jsdom on node.js will break without it.
+if ( window.getComputedStyle ) {
+	getStyles = function( elem ) {
+		return window.getComputedStyle( elem, null );
+	};
+
+	curCSS = function( elem, name, _computed ) {
+		var width, minWidth, maxWidth,
+			computed = _computed || getStyles( elem ),
+
+			// getPropertyValue is only needed for .css('filter') in IE9, see #12537
+			ret = computed ? computed.getPropertyValue( name ) || computed[ name ] : undefined,
+			style = elem.style;
+
+		if ( computed ) {
+
+			if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) {
+				ret = jQuery.style( elem, name );
+			}
+
+			// A tribute to the "awesome hack by Dean Edwards"
+			// Chrome < 17 and Safari 5.0 uses "computed value" instead of "used value" for margin-right
+			// Safari 5.1.7 (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;
+	};
+} else if ( document.documentElement.currentStyle ) {
+	getStyles = function( elem ) {
+		return elem.currentStyle;
+	};
+
+	curCSS = function( elem, name, _computed ) {
+		var left, rs, rsLeft,
+			computed = _computed || getStyles( elem ),
+			ret = computed ? computed[ name ] : undefined,
+			style = elem.style;
+
+		// Avoid setting ret to empty string here
+		// so we don't default to auto
+		if ( ret == null && style && style[ name ] ) {
+			ret = style[ name ];
+		}
+
+		// From the awesome hack by Dean Edwards
+		// http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
+
+		// If we're not dealing with a regular pixel number
+		// but a number that has a weird ending, we need to convert it to pixels
+		// but not position css attributes, as those are proportional to the parent element instead
+		// and we can't measure the parent instead because it might trigger a "stacking dolls" problem
+		if ( rnumnonpx.test( ret ) && !rposition.test( name ) ) {
+
+			// Remember the original values
+			left = style.left;
+			rs = elem.runtimeStyle;
+			rsLeft = rs && rs.left;
+
+			// Put in the new values to get a computed value out
+			if ( rsLeft ) {
+				rs.left = elem.currentStyle.left;
+			}
+			style.left = name === "fontSize" ? "1em" : ret;
+			ret = style.pixelLeft + "px";
+
+			// Revert the changed values
+			style.left = left;
+			if ( rsLeft ) {
+				rs.left = rsLeft;
+			}
+		}
+
+		return ret === "" ? "auto" : ret;
+	};
+}
+
+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.support.boxSizing && 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;
+		}
+
+		// we need the check for style in case a browser which returns unreliable values
+		// for getComputedStyle silently falls back to the reliable elem.style
+		valueIsBorderBox = isBorderBox && ( jQuery.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";
+}
+
+// Try to determine the default display value of an element
+function css_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'/>")
+				.css( "cssText", "display:block !important" )
+			).appendTo( doc.documentElement );
+
+			// Always write a new HTML skeleton so Webkit and Firefox don't choke on reuse
+			doc = ( iframe[0].contentWindow || iframe[0].contentDocument ).document;
+			doc.write("<!doctype html><html><body>");
+			doc.close();
+
+			display = actualDisplay( nodeName, doc );
+			iframe.detach();
+		}
+
+		// Store the correct default display
+		elemdisplay[ nodeName ] = display;
+	}
+
+	return display;
+}
+
+// Called ONLY from within css_defaultDisplay
+function actualDisplay( name, doc ) {
+	var elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ),
+		display = jQuery.css( elem[0], "display" );
+	elem.remove();
+	return display;
+}
+
+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
+				// however, it must have a current display style that would benefit from this
+				return elem.offsetWidth === 0 && rdisplayswap.test( jQuery.css( elem, "display" ) ) ?
+					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.support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
+					styles
+				) : 0
+			);
+		}
+	};
+});
+
+if ( !jQuery.support.opacity ) {
+	jQuery.cssHooks.opacity = {
+		get: function( elem, computed ) {
+			// IE uses filters for opacity
+			return ropacity.test( (computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || "" ) ?
+				( 0.01 * parseFloat( RegExp.$1 ) ) + "" :
+				computed ? "1" : "";
+		},
+
+		set: function( elem, value ) {
+			var style = elem.style,
+				currentStyle = elem.currentStyle,
+				opacity = jQuery.isNumeric( value ) ? "alpha(opacity=" + value * 100 + ")" : "",
+				filter = currentStyle && currentStyle.filter || style.filter || "";
+
+			// IE has trouble with opacity if it does not have layout
+			// Force it by setting the zoom level
+			style.zoom = 1;
+
+			// if setting opacity to 1, and no other filters exist - attempt to remove filter attribute #6652
+			// if value === "", then remove inline opacity #12685
+			if ( ( value >= 1 || value === "" ) &&
+					jQuery.trim( filter.replace( ralpha, "" ) ) === "" &&
+					style.removeAttribute ) {
+
+				// Setting style.filter to null, "" & " " still leave "filter:" in the cssText
+				// if "filter:" is present at all, clearType is disabled, we want to avoid this
+				// style.removeAttribute is IE Only, but so apparently is this code path...
+				style.removeAttribute( "filter" );
+
+				// if there is no filter style applied in a css rule or unset inline opacity, we are done
+				if ( value === "" || currentStyle && !currentStyle.filter ) {
+					return;
+				}
+			}
+
+			// otherwise, set new filter values
+			style.filter = ralpha.test( filter ) ?
+				filter.replace( ralpha, opacity ) :
+				filter + " " + opacity;
+		}
+	};
+}
+
+// These hooks cannot be added until DOM ready because the support test
+// for it is not run until after DOM ready
+jQuery(function() {
+	if ( !jQuery.support.reliableMarginRight ) {
+		jQuery.cssHooks.marginRight = {
+			get: function( elem, computed ) {
+				if ( computed ) {
+					// WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
+					// Work around by temporarily setting element display to inline-block
+					return jQuery.swap( elem, { "display": "inline-block" },
+						curCSS, [ elem, "marginRight" ] );
+				}
+			}
+		};
+	}
+
+	// Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
+	// getComputedStyle returns percent when specified for top/left/bottom/right
+	// rather than make the css module depend on the offset module, we just check for it here
+	if ( !jQuery.support.pixelPosition && jQuery.fn.position ) {
+		jQuery.each( [ "top", "left" ], function( i, prop ) {
+			jQuery.cssHooks[ prop ] = {
+				get: 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;
+					}
+				}
+			};
+		});
+	}
+
+});
+
+if ( jQuery.expr && jQuery.expr.filters ) {
+	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.support.reliableHiddenOffsets && ((elem.style && elem.style.display) || jQuery.css( elem, "display" )) === "none");
+	};
+
+	jQuery.expr.filters.visible = function( elem ) {
+		return !jQuery.expr.filters.hidden( elem );
+	};
+}
+
+// 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;
+	}
+});
+var r20 = /%20/g,
+	rbracket = /\[\]$/,
+	rCRLF = /\r?\n/g,
+	rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i,
+	rsubmittable = /^(?:input|select|textarea|keygen)/i;
+
+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 || !manipulation_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();
+	}
+});
+
+//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, "+" );
+};
+
+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 );
+	}
+}
+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.hover = function( fnOver, fnOut ) {
+	return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
+};
+var
+	// Document location
+	ajaxLocParts,
+	ajaxLocation,
+	ajax_nonce = jQuery.now(),
+
+	ajax_rquery = /\?/,
+	rhash = /#.*$/,
+	rts = /([?&])_=[^&]*/,
+	rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL
+	// #7653, #8125, #8152: local protocol detection
+	rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/,
+	rnoContent = /^(?:GET|HEAD)$/,
+	rprotocol = /^\/\//,
+	rurl = /^([\w.+-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,
+
+	// Keep a copy of the old load method
+	_load = jQuery.fn.load,
+
+	/* 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("*");
+
+// #8138, IE may throw an exception when accessing
+// a field from window.location if document.domain has been set
+try {
+	ajaxLocation = location.href;
+} catch( e ) {
+	// Use the href attribute of an A element
+	// since IE will modify it given document.location
+	ajaxLocation = document.createElement( "a" );
+	ajaxLocation.href = "";
+	ajaxLocation = ajaxLocation.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( core_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 deep, key,
+		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;
+}
+
+jQuery.fn.load = function( url, params, callback ) {
+	if ( typeof url !== "string" && _load ) {
+		return _load.apply( this, arguments );
+	}
+
+	var selector, response, type,
+		self = this,
+		off = url.indexOf(" ");
+
+	if ( off >= 0 ) {
+		selector = url.slice( off, url.length );
+		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.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.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"
+		},
+
+		// Data converters
+		// Keys separate source (or catchall "*") and destination types with a single space
+		converters: {
+
+			// Convert anything to text
+			"* text": window.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 // Cross-domain detection vars
+			parts,
+			// Loop variable
+			i,
+			// URL without anti-cache param
+			cacheURL,
+			// Response headers as string
+			responseHeadersString,
+			// timeout handle
+			timeoutTimer,
+
+			// To know if global events are to be dispatched
+			fireGlobals,
+
+			transport,
+			// Response headers
+			responseHeaders,
+			// 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 (#5866: IE7 issue with protocol-less urls)
+		// 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( core_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
+		fireGlobals = 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 += ( ajax_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_=" + ajax_nonce++ ) :
+
+					// Otherwise add one to the end
+					cacheURL + ( ajax_rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ajax_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;
+
+			// Get response data
+			if ( responses ) {
+				response = ajaxHandleResponses( s, jqXHR, responses );
+			}
+
+			// If successful, handle type chaining
+			if ( status >= 200 && status < 300 || status === 304 ) {
+
+				// 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 ) {
+					isSuccess = true;
+					statusText = "nocontent";
+
+				// if not modified
+				} else if ( status === 304 ) {
+					isSuccess = true;
+					statusText = "notmodified";
+
+				// If we have data, let's convert it
+				} else {
+					isSuccess = ajaxConvert( s, response );
+					statusText = isSuccess.state;
+					success = isSuccess.data;
+					error = isSuccess.error;
+					isSuccess = !error;
+				}
+			} else {
+				// We extract error from statusText
+				// then normalize statusText and status 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;
+	},
+
+	getScript: function( url, callback ) {
+		return jQuery.get( url, undefined, callback, "script" );
+	},
+
+	getJSON: function( url, data, callback ) {
+		return jQuery.get( url, data, callback, "json" );
+	}
+});
+
+/* Handles responses to an ajax request:
+ * - sets all responseXXX fields accordingly
+ * - finds the right dataType (mediates between content-type and expected dataType)
+ * - returns the corresponding response
+ */
+function ajaxHandleResponses( s, jqXHR, responses ) {
+	var firstDataType, ct, finalDataType, type,
+		contents = s.contents,
+		dataTypes = s.dataTypes,
+		responseFields = s.responseFields;
+
+	// Fill responseXXX fields
+	for ( type in responseFields ) {
+		if ( type in responses ) {
+			jqXHR[ responseFields[type] ] = responses[ type ];
+		}
+	}
+
+	// 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
+function ajaxConvert( s, response ) {
+	var conv2, current, conv, tmp,
+		converters = {},
+		i = 0,
+		// Work with a copy of dataTypes in case we need to modify it for conversion
+		dataTypes = s.dataTypes.slice(),
+		prev = dataTypes[ 0 ];
+
+	// Apply the dataFilter if provided
+	if ( s.dataFilter ) {
+		response = s.dataFilter( response, s.dataType );
+	}
+
+	// Create converters map with lowercased keys
+	if ( dataTypes[ 1 ] ) {
+		for ( conv in s.converters ) {
+			converters[ conv.toLowerCase() ] = s.converters[ conv ];
+		}
+	}
+
+	// Convert to each sequential dataType, tolerating list modification
+	for ( ; (current = dataTypes[++i]); ) {
+
+		// There's only work to do if current dataType is non-auto
+		if ( current !== "*" ) {
+
+			// Convert response if prev dataType is non-auto and differs from current
+			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.splice( i--, 0, current );
+								}
+
+								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 };
+						}
+					}
+				}
+			}
+
+			// Update prev for next iteration
+			prev = current;
+		}
+	}
+
+	return { state: "success", data: response };
+}
+// 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 global
+jQuery.ajaxPrefilter( "script", function( s ) {
+	if ( s.cache === undefined ) {
+		s.cache = false;
+	}
+	if ( s.crossDomain ) {
+		s.type = "GET";
+		s.global = false;
+	}
+});
+
+// Bind script tag hack transport
+jQuery.ajaxTransport( "script", function(s) {
+
+	// This transport only deals with cross domain requests
+	if ( s.crossDomain ) {
+
+		var script,
+			head = document.head || jQuery("head")[0] || document.documentElement;
+
+		return {
+
+			send: function( _, callback ) {
+
+				script = document.createElement("script");
+
+				script.async = true;
+
+				if ( s.scriptCharset ) {
+					script.charset = s.scriptCharset;
+				}
+
+				script.src = s.url;
+
+				// Attach handlers for all browsers
+				script.onload = script.onreadystatechange = function( _, isAbort ) {
+
+					if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) {
+
+						// Handle memory leak in IE
+						script.onload = script.onreadystatechange = null;
+
+						// Remove the script
+						if ( script.parentNode ) {
+							script.parentNode.removeChild( script );
+						}
+
+						// Dereference the script
+						script = null;
+
+						// Callback if not abort
+						if ( !isAbort ) {
+							callback( 200, "success" );
+						}
+					}
+				};
+
+				// Circumvent IE6 bugs with base elements (#2709 and #4378) by prepending
+				// Use native DOM manipulation to avoid our domManip AJAX trickery
+				head.insertBefore( script, head.firstChild );
+			},
+
+			abort: function() {
+				if ( script ) {
+					script.onload( undefined, true );
+				}
+			}
+		};
+	}
+});
+var oldCallbacks = [],
+	rjsonp = /(=)\?(?=&|$)|\?\?/;
+
+// Default jsonp settings
+jQuery.ajaxSetup({
+	jsonp: "callback",
+	jsonpCallback: function() {
+		var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( ajax_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 += ( ajax_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";
+	}
+});
+var xhrCallbacks, xhrSupported,
+	xhrId = 0,
+	// #5280: Internet Explorer will keep connections alive if we don't abort on unload
+	xhrOnUnloadAbort = window.ActiveXObject && function() {
+		// Abort all pending requests
+		var key;
+		for ( key in xhrCallbacks ) {
+			xhrCallbacks[ key ]( undefined, true );
+		}
+	};
+
+// Functions to create xhrs
+function createStandardXHR() {
+	try {
+		return new window.XMLHttpRequest();
+	} catch( e ) {}
+}
+
+function createActiveXHR() {
+	try {
+		return new window.ActiveXObject("Microsoft.XMLHTTP");
+	} catch( e ) {}
+}
+
+// Create the request object
+// (This is still attached to ajaxSettings for backward compatibility)
+jQuery.ajaxSettings.xhr = window.ActiveXObject ?
+	/* Microsoft failed to properly
+	 * implement the XMLHttpRequest in IE7 (can't request local files),
+	 * so we use the ActiveXObject when it is available
+	 * Additionally XMLHttpRequest can be disabled in IE7/IE8 so
+	 * we need a fallback.
+	 */
+	function() {
+		return !this.isLocal && createStandardXHR() || createActiveXHR();
+	} :
+	// For all other browsers, use the standard XMLHttpRequest object
+	createStandardXHR;
+
+// Determine support properties
+xhrSupported = jQuery.ajaxSettings.xhr();
+jQuery.support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported );
+xhrSupported = jQuery.support.ajax = !!xhrSupported;
+
+// Create transport if the browser can provide an xhr
+if ( xhrSupported ) {
+
+	jQuery.ajaxTransport(function( s ) {
+		// Cross domain only allowed if supported through XMLHttpRequest
+		if ( !s.crossDomain || jQuery.support.cors ) {
+
+			var callback;
+
+			return {
+				send: function( headers, complete ) {
+
+					// Get a new xhr
+					var handle, i,
+						xhr = s.xhr();
+
+					// Open the socket
+					// Passing null username, generates a login popup on Opera (#2865)
+					if ( s.username ) {
+						xhr.open( s.type, s.url, s.async, s.username, s.password );
+					} else {
+						xhr.open( s.type, s.url, s.async );
+					}
+
+					// Apply custom fields if provided
+					if ( s.xhrFields ) {
+						for ( i in s.xhrFields ) {
+							xhr[ i ] = s.xhrFields[ i ];
+						}
+					}
+
+					// Override mime type if needed
+					if ( s.mimeType && xhr.overrideMimeType ) {
+						xhr.overrideMimeType( s.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 ( !s.crossDomain && !headers["X-Requested-With"] ) {
+						headers["X-Requested-With"] = "XMLHttpRequest";
+					}
+
+					// Need an extra try/catch for cross domain requests in Firefox 3
+					try {
+						for ( i in headers ) {
+							xhr.setRequestHeader( i, headers[ i ] );
+						}
+					} catch( err ) {}
+
+					// Do send the request
+					// This may raise an exception which is actually
+					// handled in jQuery.ajax (so no try/catch here)
+					xhr.send( ( s.hasContent && s.data ) || null );
+
+					// Listener
+					callback = function( _, isAbort ) {
+						var status, responseHeaders, statusText, responses;
+
+						// Firefox throws exceptions when accessing properties
+						// of an xhr when a network error occurred
+						// http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE)
+						try {
+
+							// Was never called and is aborted or complete
+							if ( callback && ( isAbort || xhr.readyState === 4 ) ) {
+
+								// Only called once
+								callback = undefined;
+
+								// Do not keep as active anymore
+								if ( handle ) {
+									xhr.onreadystatechange = jQuery.noop;
+									if ( xhrOnUnloadAbort ) {
+										delete xhrCallbacks[ handle ];
+									}
+								}
+
+								// If it's an abort
+								if ( isAbort ) {
+									// Abort it manually if needed
+									if ( xhr.readyState !== 4 ) {
+										xhr.abort();
+									}
+								} else {
+									responses = {};
+									status = xhr.status;
+									responseHeaders = xhr.getAllResponseHeaders();
+
+									// When requesting binary data, IE6-9 will throw an exception
+									// on any attempt to access responseText (#11426)
+									if ( typeof xhr.responseText === "string" ) {
+										responses.text = xhr.responseText;
+									}
+
+									// Firefox throws an exception when accessing
+									// statusText for faulty cross-domain requests
+									try {
+										statusText = xhr.statusText;
+									} catch( e ) {
+										// We normalize with Webkit giving an empty statusText
+										statusText = "";
+									}
+
+									// Filter status for non standard behaviors
+
+									// If the request is local and we have data: assume a success
+									// (success with no data won't get notified, that's the best we
+									// can do given current implementations)
+									if ( !status && s.isLocal && !s.crossDomain ) {
+										status = responses.text ? 200 : 404;
+									// IE - #1450: sometimes returns 1223 when it should be 204
+									} else if ( status === 1223 ) {
+										status = 204;
+									}
+								}
+							}
+						} catch( firefoxAccessException ) {
+							if ( !isAbort ) {
+								complete( -1, firefoxAccessException );
+							}
+						}
+
+						// Call complete if needed
+						if ( responses ) {
+							complete( status, statusText, responses, responseHeaders );
+						}
+					};
+
+					if ( !s.async ) {
+						// if we're in sync mode we fire the callback
+						callback();
+					} else if ( xhr.readyState === 4 ) {
+						// (IE6 & IE7) if it's in cache and has been
+						// retrieved directly we need to fire the callback
+						setTimeout( callback );
+					} else {
+						handle = ++xhrId;
+						if ( xhrOnUnloadAbort ) {
+							// Create the active xhrs callbacks list if needed
+							// and attach the unload handler
+							if ( !xhrCallbacks ) {
+								xhrCallbacks = {};
+								jQuery( window ).unload( xhrOnUnloadAbort );
+							}
+							// Add to list of active xhrs callbacks
+							xhrCallbacks[ handle ] = callback;
+						}
+						xhr.onreadystatechange = callback;
+					}
+				},
+
+				abort: function() {
+					if ( callback ) {
+						callback( undefined, true );
+					}
+				}
+			};
+		}
+	});
+}
+var fxNow, timerId,
+	rfxtypes = /^(?:toggle|show|hide)$/,
+	rfxnum = new RegExp( "^(?:([+-])=|)(" + core_pnum + ")([a-z%]*)$", "i" ),
+	rrun = /queueHooks$/,
+	animationPrefilters = [ defaultPrefilter ],
+	tweeners = {
+		"*": [function( prop, value ) {
+			var end, unit,
+				tween = this.createTween( prop, value ),
+				parts = rfxnum.exec( value ),
+				target = tween.cur(),
+				start = +target || 0,
+				scale = 1,
+				maxIterations = 20;
+
+			if ( parts ) {
+				end = +parts[2];
+				unit = parts[3] || ( jQuery.cssNumber[ prop ] ? "" : "px" );
+
+				// We need to compute starting value
+				if ( unit !== "px" && start ) {
+					// Iteratively approximate from a nonzero starting point
+					// Prefer the current property, because this process will be trivial if it uses the same units
+					// Fallback to end or a simple constant
+					start = jQuery.css( tween.elem, prop, true ) || end || 1;
+
+					do {
+						// If previous iteration zeroed out, double until we get *something*
+						// Use a string for doubling factor 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()
+					// And breaking the loop if scale is unchanged or perfect, or if we've just had enough
+					} while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations );
+				}
+
+				tween.unit = unit;
+				tween.start = start;
+				// If a +=/-= token was provided, we're doing a relative animation
+				tween.end = parts[1] ? start + ( parts[1] + 1 ) * end : end;
+			}
+			return tween;
+		}]
+	};
+
+// Animations created synchronously will run synchronously
+function createFxNow() {
+	setTimeout(function() {
+		fxNow = undefined;
+	});
+	return ( fxNow = jQuery.now() );
+}
+
+function createTweens( animation, props ) {
+	jQuery.each( props, function( prop, value ) {
+		var collection = ( tweeners[ prop ] || [] ).concat( tweeners[ "*" ] ),
+			index = 0,
+			length = collection.length;
+		for ( ; index < length; index++ ) {
+			if ( collection[ index ].call( animation, prop, value ) ) {
+
+				// we're done with this property
+				return;
+			}
+		}
+	});
+}
+
+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 ),
+				// 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;
+		}
+	}
+
+	createTweens( animation, props );
+
+	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 );
+}
+
+function propFilter( props, specialEasing ) {
+	var value, name, index, easing, 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 wont overwrite keys already present.
+			// also - reusing 'index' from above 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;
+		}
+	}
+}
+
+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 );
+		}
+	}
+});
+
+function defaultPrefilter( elem, props, opts ) {
+	/*jshint validthis:true */
+	var prop, index, length,
+		value, dataShow, toggle,
+		tween, hooks, oldfire,
+		anim = this,
+		style = elem.style,
+		orig = {},
+		handled = [],
+		hidden = elem.nodeType && isHidden( elem );
+
+	// 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() {
+			// doing this makes sure that the complete handler will be 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 IE does 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
+		if ( jQuery.css( elem, "display" ) === "inline" &&
+				jQuery.css( elem, "float" ) === "none" ) {
+
+			// inline-level elements accept inline-block;
+			// block-level elements need to be inline with layout
+			if ( !jQuery.support.inlineBlockNeedsLayout || css_defaultDisplay( elem.nodeName ) === "inline" ) {
+				style.display = "inline-block";
+
+			} else {
+				style.zoom = 1;
+			}
+		}
+	}
+
+	if ( opts.overflow ) {
+		style.overflow = "hidden";
+		if ( !jQuery.support.shrinkWrapBlocks ) {
+			anim.always(function() {
+				style.overflow = opts.overflow[ 0 ];
+				style.overflowX = opts.overflow[ 1 ];
+				style.overflowY = opts.overflow[ 2 ];
+			});
+		}
+	}
+
+
+	// show/hide pass
+	for ( index in props ) {
+		value = props[ index ];
+		if ( rfxtypes.exec( value ) ) {
+			delete props[ index ];
+			toggle = toggle || value === "toggle";
+			if ( value === ( hidden ? "hide" : "show" ) ) {
+				continue;
+			}
+			handled.push( index );
+		}
+	}
+
+	length = handled.length;
+	if ( length ) {
+		dataShow = jQuery._data( elem, "fxshow" ) || jQuery._data( elem, "fxshow", {} );
+		if ( "hidden" in dataShow ) {
+			hidden = dataShow.hidden;
+		}
+
+		// 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;
+			jQuery._removeData( elem, "fxshow" );
+			for ( prop in orig ) {
+				jQuery.style( elem, prop, orig[ prop ] );
+			}
+		});
+		for ( index = 0 ; index < length ; index++ ) {
+			prop = handled[ index ];
+			tween = anim.createTween( prop, hidden ? dataShow[ prop ] : 0 );
+			orig[ prop ] = dataShow[ prop ] || jQuery.style( elem, prop );
+
+			if ( !( prop in dataShow ) ) {
+				dataShow[ prop ] = tween.start;
+				if ( hidden ) {
+					tween.end = tween.start;
+					tween.start = prop === "width" || prop === "height" ? 1 : 0;
+				}
+			}
+		}
+	}
+}
+
+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
+			// so, 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 its
+			// 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;
+			}
+		}
+	}
+};
+
+// Remove in 2.0 - this supports IE8's 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.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 );
+	};
+});
+
+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 );
+				doAnimation.finish = function() {
+					anim.stop( true );
+				};
+				// Empty animations, or finishing resolves immediately
+				if ( empty || jQuery._data( 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 = jQuery._data( 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 = jQuery._data( 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.cur && hooks.cur.finish ) {
+				hooks.cur.finish.call( this );
+			}
+
+			// 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;
+		});
+	}
+});
+
+// Generate parameters to create a standard animation
+function genFx( type, includeWidth ) {
+	var which,
+		attrs = { height: type },
+		i = 0;
+
+	// if we include width, step value is 1 to do all cssExpand values,
+	// if we don't include width, 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;
+}
+
+// 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.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.easing = {
+	linear: function( p ) {
+		return p;
+	},
+	swing: function( p ) {
+		return 0.5 - Math.cos( p*Math.PI ) / 2;
+	}
+};
+
+jQuery.timers = [];
+jQuery.fx = Tween.prototype.init;
+jQuery.fx.tick = function() {
+	var timer,
+		timers = jQuery.timers,
+		i = 0;
+
+	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 ) {
+	if ( timer() && jQuery.timers.push( timer ) ) {
+		jQuery.fx.start();
+	}
+};
+
+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
+};
+
+// Back Compat <1.8 extension point
+jQuery.fx.step = {};
+
+if ( jQuery.expr && jQuery.expr.filters ) {
+	jQuery.expr.filters.animated = function( elem ) {
+		return jQuery.grep(jQuery.timers, function( fn ) {
+			return elem === fn.elem;
+		}).length;
+	};
+}
+jQuery.fn.offset = function( options ) {
+	if ( arguments.length ) {
+		return options === undefined ?
+			this :
+			this.each(function( i ) {
+				jQuery.offset.setOffset( this, options, i );
+			});
+	}
+
+	var docElem, win,
+		box = { top: 0, left: 0 },
+		elem = this[ 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;
+	}
+
+	// If we don't have gBCR, just use 0,0 rather than error
+	// BlackBerry 5, iOS 3 (original iPhone)
+	if ( typeof elem.getBoundingClientRect !== core_strundefined ) {
+		box = elem.getBoundingClientRect();
+	}
+	win = getWindow( doc );
+	return {
+		top: box.top  + ( win.pageYOffset || docElem.scrollTop )  - ( docElem.clientTop  || 0 ),
+		left: box.left + ( win.pageXOffset || docElem.scrollLeft ) - ( docElem.clientLeft || 0 )
+	};
+};
+
+jQuery.offset = {
+
+	setOffset: function( elem, options, i ) {
+		var position = jQuery.css( elem, "position" );
+
+		// set position first, in-case top/left are set even on static elem
+		if ( position === "static" ) {
+			elem.style.position = "relative";
+		}
+
+		var curElem = jQuery( elem ),
+			curOffset = curElem.offset(),
+			curCSSTop = jQuery.css( elem, "top" ),
+			curCSSLeft = jQuery.css( elem, "left" ),
+			calculatePosition = ( position === "absolute" || position === "fixed" ) && jQuery.inArray("auto", [curCSSTop, curCSSLeft]) > -1,
+			props = {}, curPosition = {}, curTop, curLeft;
+
+		// 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({
+
+	position: function() {
+		if ( !this[ 0 ] ) {
+			return;
+		}
+
+		var offsetParent, offset,
+			parentOffset = { top: 0, left: 0 },
+			elem = this[ 0 ];
+
+		// fixed elements are offset from window (parentOffset = {top:0, left: 0}, because it is it's only offset parent
+		if ( jQuery.css( elem, "position" ) === "fixed" ) {
+			// we assume that getBoundingClientRect is available 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
+		// note: when an element has margin: auto the offsetLeft and marginLeft
+		// are the same in Safari causing offset.left to incorrectly be 0
+		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 || document.documentElement;
+			while ( offsetParent && ( !jQuery.nodeName( offsetParent, "html" ) && jQuery.css( offsetParent, "position") === "static" ) ) {
+				offsetParent = offsetParent.offsetParent;
+			}
+			return offsetParent || document.documentElement;
+		});
+	}
+});
+
+
+// Create scrollLeft and scrollTop methods
+jQuery.each( {scrollLeft: "pageXOffset", scrollTop: "pageYOffset"}, function( method, prop ) {
+	var top = /Y/.test( prop );
+
+	jQuery.fn[ method ] = function( val ) {
+		return jQuery.access( this, function( elem, method, val ) {
+			var win = getWindow( elem );
+
+			if ( val === undefined ) {
+				return win ? (prop in win) ? win[ prop ] :
+					win.document.documentElement[ method ] :
+					elem[ method ];
+			}
+
+			if ( win ) {
+				win.scrollTo(
+					!top ? val : jQuery( win ).scrollLeft(),
+					top ? val : jQuery( win ).scrollTop()
+				);
+
+			} else {
+				elem[ method ] = val;
+			}
+		}, method, val, arguments.length, null );
+	};
+});
+
+function getWindow( elem ) {
+	return jQuery.isWindow( elem ) ?
+		elem :
+		elem.nodeType === 9 ?
+			elem.defaultView || elem.parentWindow :
+			false;
+}
+// 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 jQuery.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
+					// unfortunately, this causes bug #3838 in IE6/8 only, but there is currently no good, small way to fix it.
+					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 );
+		};
+	});
+});
+// Limit scope pollution from any deprecated API
+// (function() {
+
+// })();
+// Expose jQuery to the global object
+window.jQuery = window.$ = jQuery;
+
+// Expose jQuery as an AMD module, but only for AMD loaders that
+// understand the issues with loading multiple versions of jQuery
+// in a page that all might call define(). The loader will indicate
+// they have special allowances for multiple jQuery versions by
+// specifying define.amd.jQuery = true. Register as a named module,
+// since jQuery can be concatenated with other files that may use define,
+// but not use 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.
+if ( typeof define === "function" && define.amd && define.amd.jQuery ) {
+	define( "jquery", [], function () { return jQuery; } );
+}
+
+})( window );
diff --git a/gz3d/client/js/include/jquery.mobile-1.4.0.min.js b/gz3d/client/js/include/jquery.mobile-1.4.0.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..6163bcacf2712e6c053c6e254906107976c21421
--- /dev/null
+++ b/gz3d/client/js/include/jquery.mobile-1.4.0.min.js
@@ -0,0 +1,11 @@
+/*! jQuery Mobile 1.4.0 | Git HEAD hash: f09aae0 <> 2013-12-19T17:34:22Z | (c) 2010, 2013 jQuery Foundation, Inc. | jquery.org/license */
+!function(a,b,c){"function"==typeof define&&define.amd?define(["jquery"],function(d){return c(d,a,b),d.mobile}):c(a.jQuery,a,b)}(this,document,function(a,b,c){!function(a){a.mobile={}}(a),function(a){a.extend(a.mobile,{version:"1.4.0",subPageUrlKey:"ui-page",hideUrlBar:!0,keepNative:":jqmData(role='none'), :jqmData(role='nojs')",activePageClass:"ui-page-active",activeBtnClass:"ui-btn-active",focusClass:"ui-focus",ajaxEnabled:!0,hashListeningEnabled:!0,linkBindingEnabled:!0,defaultPageTransition:"fade",maxTransitionWidth:!1,minScrollBack:0,defaultDialogTransition:"pop",pageLoadErrorMessage:"Error Loading Page",pageLoadErrorMessageTheme:"a",phonegapNavigationEnabled:!1,autoInitializePage:!0,pushStateEnabled:!0,ignoreContentEnabled:!1,buttonMarkup:{hoverDelay:200},dynamicBaseEnabled:!0,pageContainer:a(),allowCrossDomainPages:!1,dialogHashKey:"&ui-state=dialog"})}(a,this),function(a,b,c){var d={},e=a.find,f=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,g=/:jqmData\(([^)]*)\)/g;a.extend(a.mobile,{ns:"",getAttribute:function(b,c){var d;b=b.jquery?b[0]:b,b&&b.getAttribute&&(d=b.getAttribute("data-"+a.mobile.ns+c));try{d="true"===d?!0:"false"===d?!1:"null"===d?null:+d+""===d?+d:f.test(d)?JSON.parse(d):d}catch(e){}return d},nsNormalizeDict:d,nsNormalize:function(b){return d[b]||(d[b]=a.camelCase(a.mobile.ns+b))},closestPageData:function(a){return a.closest(":jqmData(role='page'), :jqmData(role='dialog')").data("mobile-page")}}),a.fn.jqmData=function(b,d){var e;return"undefined"!=typeof b&&(b&&(b=a.mobile.nsNormalize(b)),e=arguments.length<2||d===c?this.data(b):this.data(b,d)),e},a.jqmData=function(b,c,d){var e;return"undefined"!=typeof c&&(e=a.data(b,c?a.mobile.nsNormalize(c):c,d)),e},a.fn.jqmRemoveData=function(b){return this.removeData(a.mobile.nsNormalize(b))},a.jqmRemoveData=function(b,c){return a.removeData(b,a.mobile.nsNormalize(c))},a.find=function(b,c,d,f){return b.indexOf(":jqmData")>-1&&(b=b.replace(g,"[data-"+(a.mobile.ns||"")+"$1]")),e.call(this,b,c,d,f)},a.extend(a.find,e)}(a,this),function(a,b){function d(b,c){var d,f,g,h=b.nodeName.toLowerCase();return"area"===h?(d=b.parentNode,f=d.name,b.href&&f&&"map"===d.nodeName.toLowerCase()?(g=a("img[usemap=#"+f+"]")[0],!!g&&e(g)):!1):(/input|select|textarea|button|object/.test(h)?!b.disabled:"a"===h?b.href||c:c)&&e(b)}function e(b){return a.expr.filters.visible(b)&&!a(b).parents().addBack().filter(function(){return"hidden"===a.css(this,"visibility")}).length}var f=0,g=/^ui-id-\d+$/;a.ui=a.ui||{},a.extend(a.ui,{version:"c0ab71056b936627e8a7821f03c044aec6280a40",keyCode:{BACKSPACE:8,COMMA:188,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SPACE:32,TAB:9,UP:38}}),a.fn.extend({focus:function(b){return function(c,d){return"number"==typeof c?this.each(function(){var b=this;setTimeout(function(){a(b).focus(),d&&d.call(b)},c)}):b.apply(this,arguments)}}(a.fn.focus),scrollParent:function(){var b;return b=a.ui.ie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?this.parents().filter(function(){return/(relative|absolute|fixed)/.test(a.css(this,"position"))&&/(auto|scroll)/.test(a.css(this,"overflow")+a.css(this,"overflow-y")+a.css(this,"overflow-x"))}).eq(0):this.parents().filter(function(){return/(auto|scroll)/.test(a.css(this,"overflow")+a.css(this,"overflow-y")+a.css(this,"overflow-x"))}).eq(0),/fixed/.test(this.css("position"))||!b.length?a(this[0].ownerDocument||c):b},uniqueId:function(){return this.each(function(){this.id||(this.id="ui-id-"+ ++f)})},removeUniqueId:function(){return this.each(function(){g.test(this.id)&&a(this).removeAttr("id")})}}),a.extend(a.expr[":"],{data:a.expr.createPseudo?a.expr.createPseudo(function(b){return function(c){return!!a.data(c,b)}}):function(b,c,d){return!!a.data(b,d[3])},focusable:function(b){return d(b,!isNaN(a.attr(b,"tabindex")))},tabbable:function(b){var c=a.attr(b,"tabindex"),e=isNaN(c);return(e||c>=0)&&d(b,!e)}}),a("<a>").outerWidth(1).jquery||a.each(["Width","Height"],function(c,d){function e(b,c,d,e){return a.each(f,function(){c-=parseFloat(a.css(b,"padding"+this))||0,d&&(c-=parseFloat(a.css(b,"border"+this+"Width"))||0),e&&(c-=parseFloat(a.css(b,"margin"+this))||0)}),c}var f="Width"===d?["Left","Right"]:["Top","Bottom"],g=d.toLowerCase(),h={innerWidth:a.fn.innerWidth,innerHeight:a.fn.innerHeight,outerWidth:a.fn.outerWidth,outerHeight:a.fn.outerHeight};a.fn["inner"+d]=function(c){return c===b?h["inner"+d].call(this):this.each(function(){a(this).css(g,e(this,c)+"px")})},a.fn["outer"+d]=function(b,c){return"number"!=typeof b?h["outer"+d].call(this,b):this.each(function(){a(this).css(g,e(this,b,!0,c)+"px")})}}),a.fn.addBack||(a.fn.addBack=function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}),a("<a>").data("a-b","a").removeData("a-b").data("a-b")&&(a.fn.removeData=function(b){return function(c){return arguments.length?b.call(this,a.camelCase(c)):b.call(this)}}(a.fn.removeData)),a.ui.ie=!!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase()),a.support.selectstart="onselectstart"in c.createElement("div"),a.fn.extend({disableSelection:function(){return this.bind((a.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection",function(a){a.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")},zIndex:function(d){if(d!==b)return this.css("zIndex",d);if(this.length)for(var e,f,g=a(this[0]);g.length&&g[0]!==c;){if(e=g.css("position"),("absolute"===e||"relative"===e||"fixed"===e)&&(f=parseInt(g.css("zIndex"),10),!isNaN(f)&&0!==f))return f;g=g.parent()}return 0}}),a.ui.plugin={add:function(b,c,d){var e,f=a.ui[b].prototype;for(e in d)f.plugins[e]=f.plugins[e]||[],f.plugins[e].push([c,d[e]])},call:function(a,b,c,d){var e,f=a.plugins[b];if(f&&(d||a.element[0].parentNode&&11!==a.element[0].parentNode.nodeType))for(e=0;e<f.length;e++)a.options[f[e][0]]&&f[e][1].apply(a.element,c)}}}(a),function(a,b){a.extend(a.mobile,{window:a(b),document:a(c),keyCode:a.ui.keyCode,behaviors:{},silentScroll:function(c){"number"!==a.type(c)&&(c=a.mobile.defaultHomeScroll),a.event.special.scrollstart.enabled=!1,setTimeout(function(){b.scrollTo(0,c),a.mobile.document.trigger("silentscroll",{x:0,y:c})},20),setTimeout(function(){a.event.special.scrollstart.enabled=!0},150)},getClosestBaseUrl:function(b){var c=a(b).closest(".ui-page").jqmData("url"),d=a.mobile.path.documentBase.hrefNoHash;return a.mobile.dynamicBaseEnabled&&c&&a.mobile.path.isPath(c)||(c=d),a.mobile.path.makeUrlAbsolute(c,d)},removeActiveLinkClass:function(b){!a.mobile.activeClickedLink||a.mobile.activeClickedLink.closest("."+a.mobile.activePageClass).length&&!b||a.mobile.activeClickedLink.removeClass(a.mobile.activeBtnClass),a.mobile.activeClickedLink=null},getInheritedTheme:function(a,b){for(var c,d,e=a[0],f="",g=/ui-(bar|body|overlay)-([a-z])\b/;e&&(c=e.className||"",!(c&&(d=g.exec(c))&&(f=d[2])));)e=e.parentNode;return f||b||"a"},enhanceable:function(a){return this.haveParents(a,"enhance")},hijackable:function(a){return this.haveParents(a,"ajax")},haveParents:function(b,c){if(!a.mobile.ignoreContentEnabled)return b;var d,e,f,g,h,i=b.length,j=a();for(g=0;i>g;g++){for(e=b.eq(g),f=!1,d=b[g];d;){if(h=d.getAttribute?d.getAttribute("data-"+a.mobile.ns+c):"","false"===h){f=!0;break}d=d.parentNode}f||(j=j.add(e))}return j},getScreenHeight:function(){return b.innerHeight||a.mobile.window.height()},resetActivePageHeight:function(b){var c=a("."+a.mobile.activePageClass),d=c.height(),e=c.outerHeight(!0);b="number"==typeof b?b:a.mobile.getScreenHeight(),c.css("min-height",b-(e-d))},loading:function(){var b=this.loading._widget||a(a.mobile.loader.prototype.defaultHtml).loader(),c=b.loader.apply(b,arguments);return this.loading._widget=b,c}}),a.addDependents=function(b,c){var d=a(b),e=d.jqmData("dependents")||a();d.jqmData("dependents",a(e).add(c))},a.fn.extend({removeWithDependents:function(){a.removeWithDependents(this)},enhanceWithin:function(){var b,c={},d=a.mobile.page.prototype.keepNativeSelector(),e=this;a.mobile.nojs&&a.mobile.nojs(this),a.mobile.links&&a.mobile.links(this),a.mobile.degradeInputsWithin&&a.mobile.degradeInputsWithin(this),a.fn.buttonMarkup&&this.find(a.fn.buttonMarkup.initSelector).not(d).jqmEnhanceable().buttonMarkup(),a.fn.fieldcontain&&this.find(":jqmData(role='fieldcontain')").not(d).jqmEnhanceable().fieldcontain(),a.each(a.mobile.widgets,function(b,f){if(f.initSelector){var g=a.mobile.enhanceable(e.find(f.initSelector));g.length>0&&(g=g.not(d)),g.length>0&&(c[f.prototype.widgetName]=g)}});for(b in c)c[b][b]();return this},addDependents:function(b){a.addDependents(this,b)},getEncodedText:function(){return a("<a>").text(this.text()).html()},jqmEnhanceable:function(){return a.mobile.enhanceable(this)},jqmHijackable:function(){return a.mobile.hijackable(this)}}),a.removeWithDependents=function(b){var c=a(b);(c.jqmData("dependents")||a()).remove(),c.remove()},a.addDependents=function(b,c){var d=a(b),e=d.jqmData("dependents")||a();d.jqmData("dependents",a(e).add(c))},a.find.matches=function(b,c){return a.find(b,null,null,c)},a.find.matchesSelector=function(b,c){return a.find(c,null,null,[b]).length>0}}(a,this),function(a,b){var c=0,d=Array.prototype.slice,e=a.cleanData;a.cleanData=function(b){for(var c,d=0;null!=(c=b[d]);d++)try{a(c).triggerHandler("remove")}catch(f){}e(b)},a.widget=function(b,c,d){var e,f,g,h,i={},j=b.split(".")[0];return b=b.split(".")[1],e=j+"-"+b,d||(d=c,c=a.Widget),a.expr[":"][e.toLowerCase()]=function(b){return!!a.data(b,e)},a[j]=a[j]||{},f=a[j][b],g=a[j][b]=function(a,b){return this._createWidget?(arguments.length&&this._createWidget(a,b),void 0):new g(a,b)},a.extend(g,f,{version:d.version,_proto:a.extend({},d),_childConstructors:[]}),h=new c,h.options=a.widget.extend({},h.options),a.each(d,function(b,d){return a.isFunction(d)?(i[b]=function(){var a=function(){return c.prototype[b].apply(this,arguments)},e=function(a){return c.prototype[b].apply(this,a)};return function(){var b,c=this._super,f=this._superApply;return this._super=a,this._superApply=e,b=d.apply(this,arguments),this._super=c,this._superApply=f,b}}(),void 0):(i[b]=d,void 0)}),g.prototype=a.widget.extend(h,{widgetEventPrefix:f?h.widgetEventPrefix||b:b},i,{constructor:g,namespace:j,widgetName:b,widgetFullName:e}),f?(a.each(f._childConstructors,function(b,c){var d=c.prototype;a.widget(d.namespace+"."+d.widgetName,g,c._proto)}),delete f._childConstructors):c._childConstructors.push(g),a.widget.bridge(b,g),g},a.widget.extend=function(c){for(var e,f,g=d.call(arguments,1),h=0,i=g.length;i>h;h++)for(e in g[h])f=g[h][e],g[h].hasOwnProperty(e)&&f!==b&&(c[e]=a.isPlainObject(f)?a.isPlainObject(c[e])?a.widget.extend({},c[e],f):a.widget.extend({},f):f);return c},a.widget.bridge=function(c,e){var f=e.prototype.widgetFullName||c;a.fn[c]=function(g){var h="string"==typeof g,i=d.call(arguments,1),j=this;return g=!h&&i.length?a.widget.extend.apply(null,[g].concat(i)):g,h?this.each(function(){var d,e=a.data(this,f);return"instance"===g?(j=e,!1):e?a.isFunction(e[g])&&"_"!==g.charAt(0)?(d=e[g].apply(e,i),d!==e&&d!==b?(j=d&&d.jquery?j.pushStack(d.get()):d,!1):void 0):a.error("no such method '"+g+"' for "+c+" widget instance"):a.error("cannot call methods on "+c+" prior to initialization; "+"attempted to call method '"+g+"'")}):this.each(function(){var b=a.data(this,f);b?b.option(g||{})._init():a.data(this,f,new e(g,this))}),j}},a.Widget=function(){},a.Widget._childConstructors=[],a.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"<div>",options:{disabled:!1,create:null},_createWidget:function(b,d){d=a(d||this.defaultElement||this)[0],this.element=a(d),this.uuid=c++,this.eventNamespace="."+this.widgetName+this.uuid,this.options=a.widget.extend({},this.options,this._getCreateOptions(),b),this.bindings=a(),this.hoverable=a(),this.focusable=a(),d!==this&&(a.data(d,this.widgetFullName,this),this._on(!0,this.element,{remove:function(a){a.target===d&&this.destroy()}}),this.document=a(d.style?d.ownerDocument:d.document||d),this.window=a(this.document[0].defaultView||this.document[0].parentWindow)),this._create(),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:a.noop,_getCreateEventData:a.noop,_create:a.noop,_init:a.noop,destroy:function(){this._destroy(),this.element.unbind(this.eventNamespace).removeData(this.widgetFullName).removeData(a.camelCase(this.widgetFullName)),this.widget().unbind(this.eventNamespace).removeAttr("aria-disabled").removeClass(this.widgetFullName+"-disabled "+"ui-state-disabled"),this.bindings.unbind(this.eventNamespace),this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus")},_destroy:a.noop,widget:function(){return this.element},option:function(c,d){var e,f,g,h=c;if(0===arguments.length)return a.widget.extend({},this.options);if("string"==typeof c)if(h={},e=c.split("."),c=e.shift(),e.length){for(f=h[c]=a.widget.extend({},this.options[c]),g=0;g<e.length-1;g++)f[e[g]]=f[e[g]]||{},f=f[e[g]];if(c=e.pop(),d===b)return f[c]===b?null:f[c];f[c]=d}else{if(d===b)return this.options[c]===b?null:this.options[c];h[c]=d}return this._setOptions(h),this},_setOptions:function(a){var b;for(b in a)this._setOption(b,a[b]);return this},_setOption:function(a,b){return this.options[a]=b,"disabled"===a&&(this.widget().toggleClass(this.widgetFullName+"-disabled",!!b),this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus")),this},enable:function(){return this._setOptions({disabled:!1})},disable:function(){return this._setOptions({disabled:!0})},_on:function(b,c,d){var e,f=this;"boolean"!=typeof b&&(d=c,c=b,b=!1),d?(c=e=a(c),this.bindings=this.bindings.add(c)):(d=c,c=this.element,e=this.widget()),a.each(d,function(d,g){function h(){return b||f.options.disabled!==!0&&!a(this).hasClass("ui-state-disabled")?("string"==typeof g?f[g]:g).apply(f,arguments):void 0}"string"!=typeof g&&(h.guid=g.guid=g.guid||h.guid||a.guid++);var i=d.match(/^(\w+)\s*(.*)$/),j=i[1]+f.eventNamespace,k=i[2];k?e.delegate(k,j,h):c.bind(j,h)})},_off:function(a,b){b=(b||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,a.unbind(b).undelegate(b)},_delay:function(a,b){function c(){return("string"==typeof a?d[a]:a).apply(d,arguments)}var d=this;return setTimeout(c,b||0)},_hoverable:function(b){this.hoverable=this.hoverable.add(b),this._on(b,{mouseenter:function(b){a(b.currentTarget).addClass("ui-state-hover")},mouseleave:function(b){a(b.currentTarget).removeClass("ui-state-hover")}})},_focusable:function(b){this.focusable=this.focusable.add(b),this._on(b,{focusin:function(b){a(b.currentTarget).addClass("ui-state-focus")},focusout:function(b){a(b.currentTarget).removeClass("ui-state-focus")}})},_trigger:function(b,c,d){var e,f,g=this.options[b];if(d=d||{},c=a.Event(c),c.type=(b===this.widgetEventPrefix?b:this.widgetEventPrefix+b).toLowerCase(),c.target=this.element[0],f=c.originalEvent)for(e in f)e in c||(c[e]=f[e]);return this.element.trigger(c,d),!(a.isFunction(g)&&g.apply(this.element[0],[c].concat(d))===!1||c.isDefaultPrevented())}},a.each({show:"fadeIn",hide:"fadeOut"},function(b,c){a.Widget.prototype["_"+b]=function(d,e,f){"string"==typeof e&&(e={effect:e});var g,h=e?e===!0||"number"==typeof e?c:e.effect||c:b;e=e||{},"number"==typeof e&&(e={duration:e}),g=!a.isEmptyObject(e),e.complete=f,e.delay&&d.delay(e.delay),g&&a.effects&&a.effects.effect[h]?d[b](e):h!==b&&d[h]?d[h](e.duration,e.easing,f):d.queue(function(c){a(this)[b](),f&&f.call(d[0]),c()})}})}(a),function(a){var b=/[A-Z]/g,c=function(a){return"-"+a.toLowerCase()};a.extend(a.Widget.prototype,{_getCreateOptions:function(){var d,e,f=this.element[0],g={};if(!a.mobile.getAttribute(f,"defaults"))for(d in this.options)e=a.mobile.getAttribute(f,d.replace(b,c)),null!=e&&(g[d]=e);return g}}),a.mobile.widget=a.Widget}(a),function(a){var b="ui-loader",c=a("html");a.widget("mobile.loader",{options:{theme:"a",textVisible:!1,html:"",text:"loading"},defaultHtml:"<div class='"+b+"'>"+"<span class='ui-icon-loading'></span>"+"<h1></h1>"+"</div>",fakeFixLoader:function(){var b=a("."+a.mobile.activeBtnClass).first();this.element.css({top:a.support.scrollTop&&this.window.scrollTop()+this.window.height()/2||b.length&&b.offset().top||100})},checkLoaderPosition:function(){var b=this.element.offset(),c=this.window.scrollTop(),d=a.mobile.getScreenHeight();(b.top<c||b.top-c>d)&&(this.element.addClass("ui-loader-fakefix"),this.fakeFixLoader(),this.window.unbind("scroll",this.checkLoaderPosition).bind("scroll",a.proxy(this.fakeFixLoader,this)))},resetHtml:function(){this.element.html(a(this.defaultHtml).html())},show:function(d,e,f){var g,h,i;this.resetHtml(),"object"===a.type(d)?(i=a.extend({},this.options,d),d=i.theme):(i=this.options,d=d||i.theme),h=e||(i.text===!1?"":i.text),c.addClass("ui-loading"),g=i.textVisible,this.element.attr("class",b+" ui-corner-all ui-body-"+d+" ui-loader-"+(g||e||d.text?"verbose":"default")+(i.textonly||f?" ui-loader-textonly":"")),i.html?this.element.html(i.html):this.element.find("h1").text(h),this.element.appendTo(a.mobile.pageContainer),this.checkLoaderPosition(),this.window.bind("scroll",a.proxy(this.checkLoaderPosition,this))},hide:function(){c.removeClass("ui-loading"),this.options.text&&this.element.removeClass("ui-loader-fakefix"),a.mobile.window.unbind("scroll",this.fakeFixLoader),a.mobile.window.unbind("scroll",this.checkLoaderPosition)}})}(a,this),function(a,b,d){function e(a){return a=a||location.href,"#"+a.replace(/^[^#]*#?(.*)$/,"$1")}var f,g="hashchange",h=c,i=a.event.special,j=h.documentMode,k="on"+g in b&&(j===d||j>7);a.fn[g]=function(a){return a?this.bind(g,a):this.trigger(g)},a.fn[g].delay=50,i[g]=a.extend(i[g],{setup:function(){return k?!1:(a(f.start),void 0)},teardown:function(){return k?!1:(a(f.stop),void 0)}}),f=function(){function c(){var d=e(),h=n(j);d!==j?(m(j=d,h),a(b).trigger(g)):h!==j&&(location.href=location.href.replace(/#.*/,"")+h),f=setTimeout(c,a.fn[g].delay)}var f,i={},j=e(),l=function(a){return a},m=l,n=l;return i.start=function(){f||c()},i.stop=function(){f&&clearTimeout(f),f=d},b.attachEvent&&!b.addEventListener&&!k&&function(){var b,d;i.start=function(){b||(d=a.fn[g].src,d=d&&d+e(),b=a('<iframe tabindex="-1" title="empty"/>').hide().one("load",function(){d||m(e()),c()}).attr("src",d||"javascript:0").insertAfter("body")[0].contentWindow,h.onpropertychange=function(){try{"title"===event.propertyName&&(b.document.title=h.title)}catch(a){}})},i.stop=l,n=function(){return e(b.location.href)},m=function(c,d){var e=b.document,f=a.fn[g].domain;c!==d&&(e.title=h.title,e.open(),f&&e.write('<script>document.domain="'+f+'"</script>'),e.close(),b.location.hash=c)}}(),i}()}(a,this),function(a){b.matchMedia=b.matchMedia||function(a){var b,c=a.documentElement,d=c.firstElementChild||c.firstChild,e=a.createElement("body"),f=a.createElement("div");return f.id="mq-test-1",f.style.cssText="position:absolute;top:-100em",e.style.background="none",e.appendChild(f),function(a){return f.innerHTML='&shy;<style media="'+a+'"> #mq-test-1 { width: 42px; }</style>',c.insertBefore(e,d),b=42===f.offsetWidth,c.removeChild(e),{matches:b,media:a}}}(c),a.mobile.media=function(a){return b.matchMedia(a).matches}}(a),function(a){var b={touch:"ontouchend"in c};a.mobile.support=a.mobile.support||{},a.extend(a.support,b),a.extend(a.mobile.support,b)}(a),function(a){a.extend(a.support,{orientation:"orientation"in b&&"onorientationchange"in b})}(a),function(a,d){function e(a){var b,c=a.charAt(0).toUpperCase()+a.substr(1),e=(a+" "+p.join(c+" ")+c).split(" ");for(b in e)if(o[e[b]]!==d)return!0}function f(a,b,d){var e,f,g=c.createElement("div"),h=function(a){return a.charAt(0).toUpperCase()+a.substr(1)},i=function(a){return""===a?"":"-"+a.charAt(0).toLowerCase()+a.substr(1)+"-"},j=function(c){var d=i(c)+a+": "+b+";",e=h(c),j=e+(""===e?a:h(a));g.setAttribute("style",d),g.style[j]&&(f=!0)},k=d?d:p;for(e=0;e<k.length;e++)j(k[e]);return!!f}function g(){var c=b,d=!(!c.document.createElementNS||!c.document.createElementNS("http://www.w3.org/2000/svg","svg").createSVGRect||c.opera&&-1===navigator.userAgent.indexOf("Chrome")),e=function(b){b&&d||a("html").addClass("ui-nosvg")},f=new c.Image;f.onerror=function(){e(!1)},f.onload=function(){e(1===f.width&&1===f.height)},f.src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw=="}function h(){var e,f,g,h="transform-3d",i=a.mobile.media("(-"+p.join("-"+h+"),(-")+"-"+h+"),("+h+")");if(i)return!!i;e=c.createElement("div"),f={MozTransform:"-moz-transform",transform:"transform"},n.append(e);for(g in f)e.style[g]!==d&&(e.style[g]="translate3d( 100px, 1px, 1px )",i=b.getComputedStyle(e).getPropertyValue(f[g]));return!!i&&"none"!==i}function i(){var b,c,d=location.protocol+"//"+location.host+location.pathname+"ui-dir/",e=a("head base"),f=null,g="";return e.length?g=e.attr("href"):e=f=a("<base>",{href:d}).appendTo("head"),b=a("<a href='testurl' />").prependTo(n),c=b[0].href,e[0].href=g||location.pathname,f&&f.remove(),0===c.indexOf(d)}function j(){var a,d=c.createElement("x"),e=c.documentElement,f=b.getComputedStyle;return"pointerEvents"in d.style?(d.style.pointerEvents="auto",d.style.pointerEvents="x",e.appendChild(d),a=f&&"auto"===f(d,"").pointerEvents,e.removeChild(d),!!a):!1}function k(){var a=c.createElement("div");return"undefined"!=typeof a.getBoundingClientRect}function l(){var a=b,c=navigator.userAgent,d=navigator.platform,e=c.match(/AppleWebKit\/([0-9]+)/),f=!!e&&e[1],g=c.match(/Fennec\/([0-9]+)/),h=!!g&&g[1],i=c.match(/Opera Mobi\/([0-9]+)/),j=!!i&&i[1];return(d.indexOf("iPhone")>-1||d.indexOf("iPad")>-1||d.indexOf("iPod")>-1)&&f&&534>f||a.operamini&&"[object OperaMini]"==={}.toString.call(a.operamini)||i&&7458>j||c.indexOf("Android")>-1&&f&&533>f||h&&6>h||"palmGetResource"in b&&f&&534>f||c.indexOf("MeeGo")>-1&&c.indexOf("NokiaBrowser/8.5.0")>-1?!1:!0}var m,n=a("<body>").prependTo("html"),o=n[0].style,p=["Webkit","Moz","O"],q="palmGetResource"in b,r=b.opera,s=b.operamini&&"[object OperaMini]"==={}.toString.call(b.operamini),t=b.blackberry&&!e("-webkit-transform");a.extend(a.mobile,{browser:{}}),a.mobile.browser.oldIE=function(){var a=3,b=c.createElement("div"),d=b.all||[];do b.innerHTML="<!--[if gt IE "+ ++a+"]><br><![endif]-->";while(d[0]);return a>4?a:!a}(),a.extend(a.support,{cssTransitions:"WebKitTransitionEvent"in b||f("transition","height 100ms linear",["Webkit","Moz",""])&&!a.mobile.browser.oldIE&&!r,pushState:"pushState"in history&&"replaceState"in history&&!(b.navigator.userAgent.indexOf("Firefox")>=0&&b.top!==b)&&-1===b.navigator.userAgent.search(/CriOS/),mediaquery:a.mobile.media("only all"),cssPseudoElement:!!e("content"),touchOverflow:!!e("overflowScrolling"),cssTransform3d:h(),cssAnimations:!!e("animationName"),boxShadow:!!e("boxShadow")&&!t,fixedPosition:l(),scrollTop:("pageXOffset"in b||"scrollTop"in c.documentElement||"scrollTop"in n[0])&&!q&&!s,dynamicBaseTag:i(),cssPointerEvents:j(),boundingRect:k(),inlineSVG:g}),n.remove(),m=function(){var a=b.navigator.userAgent;return a.indexOf("Nokia")>-1&&(a.indexOf("Symbian/3")>-1||a.indexOf("Series60/5")>-1)&&a.indexOf("AppleWebKit")>-1&&a.match(/(BrowserNG|NokiaBrowser)\/7\.[0-3]/)}(),a.mobile.gradeA=function(){return(a.support.mediaquery&&a.support.cssPseudoElement||a.mobile.browser.oldIE&&a.mobile.browser.oldIE>=8)&&(a.support.boundingRect||null!==a.fn.jquery.match(/1\.[0-7+]\.[0-9+]?/))},a.mobile.ajaxBlacklist=b.blackberry&&!b.WebKitPoint||s||m,m&&a(function(){a("head link[rel='stylesheet']").attr("rel","alternate stylesheet").attr("rel","stylesheet")}),a.support.boxShadow||a("html").addClass("ui-noboxshadow")}(a),function(a,b){var c,d=a.mobile.window,e=function(){};a.event.special.beforenavigate={setup:function(){d.on("navigate",e)},teardown:function(){d.off("navigate",e)}},a.event.special.navigate=c={bound:!1,pushStateEnabled:!0,originalEventName:b,isPushStateEnabled:function(){return a.support.pushState&&a.mobile.pushStateEnabled===!0&&this.isHashChangeEnabled()},isHashChangeEnabled:function(){return a.mobile.hashListeningEnabled===!0},popstate:function(b){var c=new a.Event("navigate"),e=new a.Event("beforenavigate"),f=b.originalEvent.state||{};e.originalEvent=b,d.trigger(e),e.isDefaultPrevented()||(b.historyState&&a.extend(f,b.historyState),c.originalEvent=b,setTimeout(function(){d.trigger(c,{state:f})},0))},hashchange:function(b){var c=new a.Event("navigate"),e=new a.Event("beforenavigate");e.originalEvent=b,d.trigger(e),e.isDefaultPrevented()||(c.originalEvent=b,d.trigger(c,{state:b.hashchangeState||{}}))},setup:function(){c.bound||(c.bound=!0,c.isPushStateEnabled()?(c.originalEventName="popstate",d.bind("popstate.navigate",c.popstate)):c.isHashChangeEnabled()&&(c.originalEventName="hashchange",d.bind("hashchange.navigate",c.hashchange)))}}}(a),function(a,c){var d,e,f="&ui-state=dialog";a.mobile.path=d={uiStateKey:"&ui-state",urlParseRE:/^\s*(((([^:\/#\?]+:)?(?:(\/\/)((?:(([^:@\/#\?]+)(?:\:([^:@\/#\?]+))?)@)?(([^:\/#\?\]\[]+|\[[^\/\]@#?]+\])(?:\:([0-9]+))?))?)?)?((\/?(?:[^\/\?#]+\/+)*)([^\?#]*)))?(\?[^#]+)?)(#.*)?/,getLocation:function(a){var b=a?this.parseUrl(a):location,c=this.parseUrl(a||location.href).hash;return c="#"===c?"":c,b.protocol+"//"+b.host+b.pathname+b.search+c},getDocumentUrl:function(b){return b?a.extend({},d.documentUrl):d.documentUrl.href},parseLocation:function(){return this.parseUrl(this.getLocation())},parseUrl:function(b){if("object"===a.type(b))return b;var c=d.urlParseRE.exec(b||"")||[];return{href:c[0]||"",hrefNoHash:c[1]||"",hrefNoSearch:c[2]||"",domain:c[3]||"",protocol:c[4]||"",doubleSlash:c[5]||"",authority:c[6]||"",username:c[8]||"",password:c[9]||"",host:c[10]||"",hostname:c[11]||"",port:c[12]||"",pathname:c[13]||"",directory:c[14]||"",filename:c[15]||"",search:c[16]||"",hash:c[17]||""}},makePathAbsolute:function(a,b){var c,d,e,f;if(a&&"/"===a.charAt(0))return a;for(a=a||"",b=b?b.replace(/^\/|(\/[^\/]*|[^\/]+)$/g,""):"",c=b?b.split("/"):[],d=a.split("/"),e=0;e<d.length;e++)switch(f=d[e]){case".":break;case"..":c.length&&c.pop();break;default:c.push(f)}return"/"+c.join("/")},isSameDomain:function(a,b){return d.parseUrl(a).domain===d.parseUrl(b).domain},isRelativeUrl:function(a){return""===d.parseUrl(a).protocol},isAbsoluteUrl:function(a){return""!==d.parseUrl(a).protocol},makeUrlAbsolute:function(a,b){if(!d.isRelativeUrl(a))return a;b===c&&(b=this.documentBase);var e=d.parseUrl(a),f=d.parseUrl(b),g=e.protocol||f.protocol,h=e.protocol?e.doubleSlash:e.doubleSlash||f.doubleSlash,i=e.authority||f.authority,j=""!==e.pathname,k=d.makePathAbsolute(e.pathname||f.filename,f.pathname),l=e.search||!j&&f.search||"",m=e.hash;return g+h+i+k+l+m},addSearchParams:function(b,c){var e=d.parseUrl(b),f="object"==typeof c?a.param(c):c,g=e.search||"?";return e.hrefNoSearch+g+("?"!==g.charAt(g.length-1)?"&":"")+f+(e.hash||"")},convertUrlToDataUrl:function(a){var c=d.parseUrl(a);return d.isEmbeddedPage(c)?c.hash.split(f)[0].replace(/^#/,"").replace(/\?.*$/,""):d.isSameDomain(c,this.documentBase)?c.hrefNoHash.replace(this.documentBase.domain,"").split(f)[0]:b.decodeURIComponent(a)},get:function(a){return a===c&&(a=d.parseLocation().hash),d.stripHash(a).replace(/[^\/]*\.[^\/*]+$/,"")},set:function(a){location.hash=a},isPath:function(a){return/\//.test(a)},clean:function(a){return a.replace(this.documentBase.domain,"")},stripHash:function(a){return a.replace(/^#/,"")},stripQueryParams:function(a){return a.replace(/\?.*$/,"")},cleanHash:function(a){return d.stripHash(a.replace(/\?.*$/,"").replace(f,""))},isHashValid:function(a){return/^#[^#]+$/.test(a)},isExternal:function(a){var b=d.parseUrl(a);return b.protocol&&b.domain!==this.documentUrl.domain?!0:!1},hasProtocol:function(a){return/^(:?\w+:)/.test(a)},isEmbeddedPage:function(a){var b=d.parseUrl(a);return""!==b.protocol?!this.isPath(b.hash)&&b.hash&&(b.hrefNoHash===this.documentUrl.hrefNoHash||this.documentBaseDiffers&&b.hrefNoHash===this.documentBase.hrefNoHash):/^#/.test(b.href)},squash:function(a,b){var c,e,f,g,h=this.isPath(a),i=this.parseUrl(a),j=i.hash,k="";return b=b||(d.isPath(a)?d.getLocation():d.getDocumentUrl()),e=h?d.stripHash(a):a,e=d.isPath(i.hash)?d.stripHash(i.hash):e,g=e.indexOf(this.uiStateKey),g>-1&&(k=e.slice(g),e=e.slice(0,g)),c=d.makeUrlAbsolute(e,b),f=this.parseUrl(c).search,h?((d.isPath(j)||0===j.replace("#","").indexOf(this.uiStateKey))&&(j=""),k&&-1===j.indexOf(this.uiStateKey)&&(j+=k),-1===j.indexOf("#")&&""!==j&&(j="#"+j),c=d.parseUrl(c),c=c.protocol+"//"+c.host+c.pathname+f+j):c+=c.indexOf("#")>-1?k:"#"+k,c},isPreservableHash:function(a){return 0===a.replace("#","").indexOf(this.uiStateKey)},hashToSelector:function(a){var b="#"===a.substring(0,1);return b&&(a=a.substring(1)),(b?"#":"")+a.replace(/([!"#$%&'()*+,./:;<=>?@[\]^`{|}~])/g,"\\$1")},getFilePath:function(b){var c="&"+a.mobile.subPageUrlKey;return b&&b.split(c)[0].split(f)[0]},isFirstPageUrl:function(b){var e=d.parseUrl(d.makeUrlAbsolute(b,this.documentBase)),f=e.hrefNoHash===this.documentUrl.hrefNoHash||this.documentBaseDiffers&&e.hrefNoHash===this.documentBase.hrefNoHash,g=a.mobile.firstPage,h=g&&g[0]?g[0].id:c;return f&&(!e.hash||"#"===e.hash||h&&e.hash.replace(/^#/,"")===h)},isPermittedCrossDomainRequest:function(b,c){return a.mobile.allowCrossDomainPages&&("file:"===b.protocol||"content:"===b.protocol)&&-1!==c.search(/^https?:/)}},d.documentUrl=d.parseLocation(),e=a("head").find("base"),d.documentBase=e.length?d.parseUrl(d.makeUrlAbsolute(e.attr("href"),d.documentUrl.href)):d.documentUrl,d.documentBaseDiffers=d.documentUrl.hrefNoHash!==d.documentBase.hrefNoHash,d.getDocumentBase=function(b){return b?a.extend({},d.documentBase):d.documentBase.href},a.extend(a.mobile,{getDocumentUrl:d.getDocumentUrl,getDocumentBase:d.getDocumentBase})}(a),function(a,b){a.mobile.History=function(a,b){this.stack=a||[],this.activeIndex=b||0},a.extend(a.mobile.History.prototype,{getActive:function(){return this.stack[this.activeIndex]},getLast:function(){return this.stack[this.previousIndex]},getNext:function(){return this.stack[this.activeIndex+1]},getPrev:function(){return this.stack[this.activeIndex-1]},add:function(a,b){b=b||{},this.getNext()&&this.clearForward(),b.hash&&-1===b.hash.indexOf("#")&&(b.hash="#"+b.hash),b.url=a,this.stack.push(b),this.activeIndex=this.stack.length-1},clearForward:function(){this.stack=this.stack.slice(0,this.activeIndex+1)},find:function(a,b,c){b=b||this.stack;var d,e,f,g=b.length;for(e=0;g>e;e++)if(d=b[e],(decodeURIComponent(a)===decodeURIComponent(d.url)||decodeURIComponent(a)===decodeURIComponent(d.hash))&&(f=e,c))return f;return f},closest:function(a){var c,d=this.activeIndex;return c=this.find(a,this.stack.slice(0,d)),c===b&&(c=this.find(a,this.stack.slice(d),!0),c=c===b?c:c+d),c},direct:function(c){var d=this.closest(c.url),e=this.activeIndex;d!==b&&(this.activeIndex=d,this.previousIndex=e),e>d?(c.present||c.back||a.noop)(this.getActive(),"back"):d>e?(c.present||c.forward||a.noop)(this.getActive(),"forward"):d===b&&c.missing&&c.missing(this.getActive())}})}(a),function(a){var d=a.mobile.path,e=location.href;a.mobile.Navigator=function(b){this.history=b,this.ignoreInitialHashChange=!0,a.mobile.window.bind({"popstate.history":a.proxy(this.popstate,this),"hashchange.history":a.proxy(this.hashchange,this)})},a.extend(a.mobile.Navigator.prototype,{squash:function(e,f){var g,h,i=d.isPath(e)?d.stripHash(e):e;return h=d.squash(e),g=a.extend({hash:i,url:h},f),b.history.replaceState(g,g.title||c.title,h),g},hash:function(a,b){var c,e,f,g;return c=d.parseUrl(a),e=d.parseLocation(),e.pathname+e.search===c.pathname+c.search?f=c.hash?c.hash:c.pathname+c.search:d.isPath(a)?(g=d.parseUrl(b),f=g.pathname+g.search+(d.isPreservableHash(g.hash)?g.hash.replace("#",""):"")):f=a,f},go:function(e,f,g){var h,i,j,k,l=a.event.special.navigate.isPushStateEnabled();i=d.squash(e),j=this.hash(e,i),g&&j!==d.stripHash(d.parseLocation().hash)&&(this.preventNextHashChange=g),this.preventHashAssignPopState=!0,b.location.hash=j,this.preventHashAssignPopState=!1,h=a.extend({url:i,hash:j,title:c.title},f),l&&(k=new a.Event("popstate"),k.originalEvent={type:"popstate",state:null},this.squash(e,h),g||(this.ignorePopState=!0,a.mobile.window.trigger(k))),this.history.add(h.url,h)
+},popstate:function(b){var c,f;if(a.event.special.navigate.isPushStateEnabled())return this.preventHashAssignPopState?(this.preventHashAssignPopState=!1,b.stopImmediatePropagation(),void 0):this.ignorePopState?(this.ignorePopState=!1,void 0):!b.originalEvent.state&&1===this.history.stack.length&&this.ignoreInitialHashChange&&(this.ignoreInitialHashChange=!1,location.href===e)?(b.preventDefault(),void 0):(c=d.parseLocation().hash,!b.originalEvent.state&&c?(f=this.squash(c),this.history.add(f.url,f),b.historyState=f,void 0):(this.history.direct({url:(b.originalEvent.state||{}).url||c,present:function(c,d){b.historyState=a.extend({},c),b.historyState.direction=d}}),void 0))},hashchange:function(b){var e,f;if(a.event.special.navigate.isHashChangeEnabled()&&!a.event.special.navigate.isPushStateEnabled()){if(this.preventNextHashChange)return this.preventNextHashChange=!1,b.stopImmediatePropagation(),void 0;e=this.history,f=d.parseLocation().hash,this.history.direct({url:f,present:function(c,d){b.hashchangeState=a.extend({},c),b.hashchangeState.direction=d},missing:function(){e.add(f,{hash:f,title:c.title})}})}}})}(a),function(a){a.mobile.navigate=function(b,c,d){a.mobile.navigate.navigator.go(b,c,d)},a.mobile.navigate.history=new a.mobile.History,a.mobile.navigate.navigator=new a.mobile.Navigator(a.mobile.navigate.history);var b=a.mobile.path.parseLocation();a.mobile.navigate.history.add(b.href,{hash:b.hash})}(a),function(a,b,c,d){function e(a){for(;a&&"undefined"!=typeof a.originalEvent;)a=a.originalEvent;return a}function f(b,c){var f,g,h,i,j,k,l,m,n,o=b.type;if(b=a.Event(b),b.type=c,f=b.originalEvent,g=a.event.props,o.search(/^(mouse|click)/)>-1&&(g=E),f)for(l=g.length,i;l;)i=g[--l],b[i]=f[i];if(o.search(/mouse(down|up)|click/)>-1&&!b.which&&(b.which=1),-1!==o.search(/^touch/)&&(h=e(f),o=h.touches,j=h.changedTouches,k=o&&o.length?o[0]:j&&j.length?j[0]:d))for(m=0,n=C.length;n>m;m++)i=C[m],b[i]=k[i];return b}function g(b){for(var c,d,e={};b;){c=a.data(b,z);for(d in c)c[d]&&(e[d]=e.hasVirtualBinding=!0);b=b.parentNode}return e}function h(b,c){for(var d;b;){if(d=a.data(b,z),d&&(!c||d[c]))return b;b=b.parentNode}return null}function i(){M=!1}function j(){M=!0}function k(){Q=0,K.length=0,L=!1,j()}function l(){i()}function m(){n(),G=setTimeout(function(){G=0,k()},a.vmouse.resetTimerDuration)}function n(){G&&(clearTimeout(G),G=0)}function o(b,c,d){var e;return(d&&d[b]||!d&&h(c.target,b))&&(e=f(c,b),a(c.target).trigger(e)),e}function p(b){var c,d=a.data(b.target,A);L||Q&&Q===d||(c=o("v"+b.type,b),c&&(c.isDefaultPrevented()&&b.preventDefault(),c.isPropagationStopped()&&b.stopPropagation(),c.isImmediatePropagationStopped()&&b.stopImmediatePropagation()))}function q(b){var c,d,f,h=e(b).touches;h&&1===h.length&&(c=b.target,d=g(c),d.hasVirtualBinding&&(Q=P++,a.data(c,A,Q),n(),l(),J=!1,f=e(b).touches[0],H=f.pageX,I=f.pageY,o("vmouseover",b,d),o("vmousedown",b,d)))}function r(a){M||(J||o("vmousecancel",a,g(a.target)),J=!0,m())}function s(b){if(!M){var c=e(b).touches[0],d=J,f=a.vmouse.moveDistanceThreshold,h=g(b.target);J=J||Math.abs(c.pageX-H)>f||Math.abs(c.pageY-I)>f,J&&!d&&o("vmousecancel",b,h),o("vmousemove",b,h),m()}}function t(a){if(!M){j();var b,c,d=g(a.target);o("vmouseup",a,d),J||(b=o("vclick",a,d),b&&b.isDefaultPrevented()&&(c=e(a).changedTouches[0],K.push({touchID:Q,x:c.clientX,y:c.clientY}),L=!0)),o("vmouseout",a,d),J=!1,m()}}function u(b){var c,d=a.data(b,z);if(d)for(c in d)if(d[c])return!0;return!1}function v(){}function w(b){var c=b.substr(1);return{setup:function(){u(this)||a.data(this,z,{});var d=a.data(this,z);d[b]=!0,F[b]=(F[b]||0)+1,1===F[b]&&O.bind(c,p),a(this).bind(c,v),N&&(F.touchstart=(F.touchstart||0)+1,1===F.touchstart&&O.bind("touchstart",q).bind("touchend",t).bind("touchmove",s).bind("scroll",r))},teardown:function(){--F[b],F[b]||O.unbind(c,p),N&&(--F.touchstart,F.touchstart||O.unbind("touchstart",q).unbind("touchmove",s).unbind("touchend",t).unbind("scroll",r));var d=a(this),e=a.data(this,z);e&&(e[b]=!1),d.unbind(c,v),u(this)||d.removeData(z)}}}var x,y,z="virtualMouseBindings",A="virtualTouchID",B="vmouseover vmousedown vmousemove vmouseup vclick vmouseout vmousecancel".split(" "),C="clientX clientY pageX pageY screenX screenY".split(" "),D=a.event.mouseHooks?a.event.mouseHooks.props:[],E=a.event.props.concat(D),F={},G=0,H=0,I=0,J=!1,K=[],L=!1,M=!1,N="addEventListener"in c,O=a(c),P=1,Q=0;for(a.vmouse={moveDistanceThreshold:10,clickDistanceThreshold:10,resetTimerDuration:1500},y=0;y<B.length;y++)a.event.special[B[y]]=w(B[y]);N&&c.addEventListener("click",function(b){var c,d,e,f,g,h,i=K.length,j=b.target;if(i)for(c=b.clientX,d=b.clientY,x=a.vmouse.clickDistanceThreshold,e=j;e;){for(f=0;i>f;f++)if(g=K[f],h=0,e===j&&Math.abs(g.x-c)<x&&Math.abs(g.y-d)<x||a.data(e,A)===g.touchID)return b.preventDefault(),b.stopPropagation(),void 0;e=e.parentNode}},!0)}(a,b,c),function(a){function b(b,c,d){var e=d.type;d.type=c,a.event.dispatch.call(b,d),d.type=e}var d=a(c),e=a.mobile.support.touch,f="touchmove scroll",g=e?"touchstart":"mousedown",h=e?"touchend":"mouseup",i=e?"touchmove":"mousemove";a.each("touchstart touchmove touchend tap taphold swipe swipeleft swiperight scrollstart scrollstop".split(" "),function(b,c){a.fn[c]=function(a){return a?this.bind(c,a):this.trigger(c)},a.attrFn&&(a.attrFn[c]=!0)}),a.event.special.scrollstart={enabled:!0,setup:function(){function c(a,c){d=c,b(g,d?"scrollstart":"scrollstop",a)}var d,e,g=this,h=a(g);h.bind(f,function(b){a.event.special.scrollstart.enabled&&(d||c(b,!0),clearTimeout(e),e=setTimeout(function(){c(b,!1)},50))})},teardown:function(){a(this).unbind(f)}},a.event.special.tap={tapholdThreshold:750,emitTapOnTaphold:!0,setup:function(){var c=this,e=a(c),f=!1;e.bind("vmousedown",function(g){function h(){clearTimeout(k)}function i(){h(),e.unbind("vclick",j).unbind("vmouseup",h),d.unbind("vmousecancel",i)}function j(a){i(),f||l!==a.target?f&&a.stopPropagation():b(c,"tap",a)}if(f=!1,g.which&&1!==g.which)return!1;var k,l=g.target;e.bind("vmouseup",h).bind("vclick",j),d.bind("vmousecancel",i),k=setTimeout(function(){a.event.special.tap.emitTapOnTaphold||(f=!0),b(c,"taphold",a.Event("taphold",{target:l}))},a.event.special.tap.tapholdThreshold)})},teardown:function(){a(this).unbind("vmousedown").unbind("vclick").unbind("vmouseup"),d.unbind("vmousecancel")}},a.event.special.swipe={scrollSupressionThreshold:30,durationThreshold:1e3,horizontalDistanceThreshold:30,verticalDistanceThreshold:75,start:function(b){var c=b.originalEvent.touches?b.originalEvent.touches[0]:b;return{time:(new Date).getTime(),coords:[c.pageX,c.pageY],origin:a(b.target)}},stop:function(a){var b=a.originalEvent.touches?a.originalEvent.touches[0]:a;return{time:(new Date).getTime(),coords:[b.pageX,b.pageY]}},handleSwipe:function(c,d,e,f){if(d.time-c.time<a.event.special.swipe.durationThreshold&&Math.abs(c.coords[0]-d.coords[0])>a.event.special.swipe.horizontalDistanceThreshold&&Math.abs(c.coords[1]-d.coords[1])<a.event.special.swipe.verticalDistanceThreshold){var g=c.coords[0]>d.coords[0]?"swipeleft":"swiperight";return b(e,"swipe",a.Event("swipe",{target:f,swipestart:c,swipestop:d})),b(e,g,a.Event(g,{target:f,swipestart:c,swipestop:d})),!0}return!1},setup:function(){var b=this,c=a(b);c.bind(g,function(d){function e(c){g&&(f=a.event.special.swipe.stop(c),k||(k=a.event.special.swipe.handleSwipe(g,f,b,j)),Math.abs(g.coords[0]-f.coords[0])>a.event.special.swipe.scrollSupressionThreshold&&c.preventDefault())}var f,g=a.event.special.swipe.start(d),j=d.target,k=!1;c.bind(i,e).one(h,function(){k=!0,c.unbind(i,e)})})},teardown:function(){a(this).unbind(g).unbind(i).unbind(h)}},a.each({scrollstop:"scrollstart",taphold:"tap",swipeleft:"swipe",swiperight:"swipe"},function(b,c){a.event.special[b]={setup:function(){a(this).bind(c,a.noop)},teardown:function(){a(this).unbind(c)}}})}(a,this),function(a){a.event.special.throttledresize={setup:function(){a(this).bind("resize",f)},teardown:function(){a(this).unbind("resize",f)}};var b,c,d,e=250,f=function(){c=(new Date).getTime(),d=c-g,d>=e?(g=c,a(this).trigger("throttledresize")):(b&&clearTimeout(b),b=setTimeout(f,e-d))},g=0}(a),function(a,b){function d(){var a=e();a!==f&&(f=a,l.trigger(m))}var e,f,g,h,i,j,k,l=a(b),m="orientationchange",n={0:!0,180:!0};a.support.orientation&&(i=b.innerWidth||l.width(),j=b.innerHeight||l.height(),k=50,g=i>j&&i-j>k,h=n[b.orientation],(g&&h||!g&&!h)&&(n={"-90":!0,90:!0})),a.event.special.orientationchange=a.extend({},a.event.special.orientationchange,{setup:function(){return a.support.orientation&&!a.event.special.orientationchange.disabled?!1:(f=e(),l.bind("throttledresize",d),void 0)},teardown:function(){return a.support.orientation&&!a.event.special.orientationchange.disabled?!1:(l.unbind("throttledresize",d),void 0)},add:function(a){var b=a.handler;a.handler=function(a){return a.orientation=e(),b.apply(this,arguments)}}}),a.event.special.orientationchange.orientation=e=function(){var d=!0,e=c.documentElement;return d=a.support.orientation?n[b.orientation]:e&&e.clientWidth/e.clientHeight<1.1,d?"portrait":"landscape"},a.fn[m]=function(a){return a?this.bind(m,a):this.trigger(m)},a.attrFn&&(a.attrFn[m]=!0)}(a,this),function(a){var b=a("head").children("base"),c={element:b.length?b:a("<base>",{href:a.mobile.path.documentBase.hrefNoHash}).prependTo(a("head")),linkSelector:"[src], link[href], a[rel='external'], :jqmData(ajax='false'), a[target]",set:function(b){a.mobile.dynamicBaseEnabled&&a.support.dynamicBaseTag&&c.element.attr("href",a.mobile.path.makeUrlAbsolute(b,a.mobile.path.documentBase))},rewrite:function(b,d){var e=a.mobile.path.get(b);d.find(c.linkSelector).each(function(b,c){var d=a(c).is("[href]")?"href":a(c).is("[src]")?"src":"action",f=a(c).attr(d);f=f.replace(location.protocol+"//"+location.host+location.pathname,""),/^(\w+:|#|\/)/.test(f)||a(c).attr(d,e+f)})},reset:function(){c.element.attr("href",a.mobile.path.documentBase.hrefNoSearch)}};a.mobile.base=c}(a),function(a,b){a.mobile.widgets={};var c=a.widget,d=a.mobile.keepNative;a.widget=function(c){return function(){var d=c.apply(this,arguments),e=d.prototype.widgetName;return d.initSelector=d.prototype.initSelector!==b?d.prototype.initSelector:":jqmData(role='"+e+"')",a.mobile.widgets[e]=d,d}}(a.widget),a.extend(a.widget,c),a.mobile.document.on("create",function(b){a(b.target).enhanceWithin()}),a.widget("mobile.page",{options:{theme:"a",domCache:!1,keepNativeDefault:a.mobile.keepNative,contentTheme:null,enhanced:!1},_createWidget:function(){a.Widget.prototype._createWidget.apply(this,arguments),this._trigger("init")},_create:function(){return this._trigger("beforecreate")===!1?!1:(this.options.enhanced||this._enhance(),this._on(this.element,{pagebeforehide:"removeContainerBackground",pagebeforeshow:"_handlePageBeforeShow"}),this.element.enhanceWithin(),"dialog"===a.mobile.getAttribute(this.element[0],"role")&&a.mobile.dialog&&this.element.dialog(),void 0)},_enhance:function(){var c="data-"+a.mobile.ns,d=this;this.options.role&&this.element.attr("data-"+a.mobile.ns+"role",this.options.role),this.element.attr("tabindex","0").addClass("ui-page ui-page-theme-"+this.options.theme),this.element.find("["+c+"role='content']").each(function(){var e=a(this),f=this.getAttribute(c+"theme")||b;d.options.contentTheme=f||d.options.contentTheme||d.options.dialog&&d.options.theme||"dialog"===d.element.jqmData("role")&&d.options.theme,e.addClass("ui-content"),d.options.contentTheme&&e.addClass("ui-body-"+d.options.contentTheme),e.attr("role","main").addClass("ui-content")})},bindRemove:function(b){var c=this.element;!c.data("mobile-page").options.domCache&&c.is(":jqmData(external-page='true')")&&c.bind("pagehide.remove",b||function(b,c){if(!c.samePage){var d=a(this),e=new a.Event("pageremove");d.trigger(e),e.isDefaultPrevented()||d.removeWithDependents()}})},_setOptions:function(c){c.theme!==b&&this.element.removeClass("ui-body-"+this.options.theme).addClass("ui-body-"+c.theme),c.contentTheme!==b&&this.element.find("[data-"+a.mobile.ns+"='content']").removeClass("ui-body-"+this.options.contentTheme).addClass("ui-body-"+c.contentTheme)},_handlePageBeforeShow:function(){this.setContainerBackground()},removeContainerBackground:function(){this.element.closest(":mobile-pagecontainer").pagecontainer({theme:"none"})},setContainerBackground:function(a){this.element.parent().pagecontainer({theme:a||this.options.theme})},keepNativeSelector:function(){var b=this.options,c=a.trim(b.keepNative||""),e=a.trim(a.mobile.keepNative),f=a.trim(b.keepNativeDefault),g=d===e?"":e,h=""===g?f:"";return(c?[c]:[]).concat(g?[g]:[]).concat(h?[h]:[]).join(", ")}})}(a),function(a,d){a.widget("mobile.pagecontainer",{options:{theme:"a"},initSelector:!1,_create:function(){this.setLastScrollEnabled=!0,this._on(this.window,{navigate:"_filterNavigateEvents"}),this._on(this.window,{navigate:"_disableRecordScroll",scrollstop:"_delayedRecordScroll"}),this._on({pagechange:"_afterContentChange"}),this.window.one("navigate",a.proxy(function(){this.setLastScrollEnabled=!0},this))},_setOptions:function(a){a.theme!==d&&"none"!==a.theme?this.element.removeClass("ui-overlay-"+this.options.theme).addClass("ui-overlay-"+a.theme):a.theme!==d&&this.element.removeClass("ui-overlay-"+this.options.theme),this._super(a)},_disableRecordScroll:function(){this.setLastScrollEnabled=!1},_enableRecordScroll:function(){this.setLastScrollEnabled=!0},_afterContentChange:function(){this.setLastScrollEnabled=!0,this._off(this.window,"scrollstop"),this._on(this.window,{scrollstop:"_delayedRecordScroll"})},_recordScroll:function(){if(this.setLastScrollEnabled){var a,b,c,d=this._getActiveHistory();d&&(a=this._getScroll(),b=this._getMinScroll(),c=this._getDefaultScroll(),d.lastScroll=b>a?c:a)}},_delayedRecordScroll:function(){setTimeout(a.proxy(this,"_recordScroll"),100)},_getScroll:function(){return this.window.scrollTop()},_getMinScroll:function(){return a.mobile.minScrollBack},_getDefaultScroll:function(){return a.mobile.defaultHomeScroll},_filterNavigateEvents:function(b,c){var d;b.originalEvent&&b.originalEvent.isDefaultPrevented()||(d=b.originalEvent.type.indexOf("hashchange")>-1?c.state.hash:c.state.url,d||(d=this._getHash()),d&&"#"!==d&&0!==d.indexOf("#"+a.mobile.path.uiStateKey)||(d=location.href),this._handleNavigate(d,c.state))},_getHash:function(){return a.mobile.path.parseLocation().hash},getActivePage:function(){return this.activePage},_getInitialContent:function(){return a.mobile.firstPage},_getHistory:function(){return a.mobile.navigate.history},_getActiveHistory:function(){return a.mobile.navigate.history.getActive()},_getDocumentBase:function(){return a.mobile.path.documentBase},back:function(){this.go(-1)},forward:function(){this.go(1)},go:function(c){if(a.mobile.hashListeningEnabled)b.history.go(c);else{var d=a.mobile.navigate.history.activeIndex,e=d+parseInt(c,10),f=a.mobile.navigate.history.stack[e].url,g=c>=1?"forward":"back";a.mobile.navigate.history.activeIndex=e,a.mobile.navigate.history.previousIndex=d,this.change(f,{direction:g,changeHash:!1,fromHashChange:!0})}},_handleDestination:function(b){var c;return"string"===a.type(b)&&(b=a.mobile.path.stripHash(b)),b&&(c=this._getHistory(),b=a.mobile.path.isPath(b)?b:a.mobile.path.makeUrlAbsolute("#"+b,this._getDocumentBase()),b===a.mobile.path.makeUrlAbsolute("#"+c.initialDst,this._getDocumentBase())&&c.stack.length&&c.stack[0].url!==c.initialDst.replace(a.mobile.dialogHashKey,"")&&(b=this._getInitialContent())),b||this._getInitialContent()},_handleDialog:function(b,c){var d,e,f=this.getActivePage();return f&&!f.hasClass("ui-dialog")?("back"===c.direction?this.back():this.forward(),!1):(d=c.pageUrl,e=this._getActiveHistory(),a.extend(b,{role:e.role,transition:e.transition,reverse:"back"===c.direction}),d)},_handleNavigate:function(b,c){var e=a.mobile.path.stripHash(b),f=this._getHistory(),g=0===f.stack.length?"none":d,h={changeHash:!1,fromHashChange:!0,reverse:"back"===c.direction};a.extend(h,c,{transition:(f.getLast()||{}).transition||g}),f.activeIndex>0&&e.indexOf(a.mobile.dialogHashKey)>-1&&f.initialDst!==e&&(e=this._handleDialog(h,c),e===!1)||this._changeContent(this._handleDestination(e),h)},_changeContent:function(b,c){a.mobile.changePage(b,c)},_getBase:function(){return a.mobile.base},_getNs:function(){return a.mobile.ns},_enhance:function(a,b){return a.page({role:b})},_include:function(a,b){a.appendTo(this.element),this._enhance(a,b.role),a.page("bindRemove")},_find:function(b){var c,d=this._createFileUrl(b),e=this._createDataUrl(b),f=this._getInitialContent();return c=this.element.children("[data-"+this._getNs()+"url='"+e+"']"),0===c.length&&e&&!a.mobile.path.isPath(e)&&(c=this.element.children(a.mobile.path.hashToSelector("#"+e)).attr("data-"+this._getNs()+"url",e).jqmData("url",e)),0===c.length&&a.mobile.path.isFirstPageUrl(d)&&f&&f.parent().length&&(c=a(f)),c},_getLoader:function(){return a.mobile.loading()},_showLoading:function(b,c,d,e){this._loadMsg||(this._loadMsg=setTimeout(a.proxy(function(){this._getLoader().loader("show",c,d,e),this._loadMsg=0},this),b))},_hideLoading:function(){clearTimeout(this._loadMsg),this._loadMsg=0,this._getLoader().loader("hide")},_showError:function(){this._hideLoading(),this._showLoading(0,a.mobile.pageLoadErrorMessageTheme,a.mobile.pageLoadErrorMessage,!0),setTimeout(a.proxy(this,"_hideLoading"),1500)},_parse:function(b,c){var d,e=a("<div></div>");return e.get(0).innerHTML=b,d=e.find(":jqmData(role='page'), :jqmData(role='dialog')").first(),d.length||(d=a("<div data-"+this._getNs()+"role='page'>"+(b.split(/<\/?body[^>]*>/gim)[1]||"")+"</div>")),d.attr("data-"+this._getNs()+"url",a.mobile.path.convertUrlToDataUrl(c)).attr("data-"+this._getNs()+"external-page",!0),d},_setLoadedTitle:function(b,c){var d=c.match(/<title[^>]*>([^<]*)/)&&RegExp.$1;d&&!b.jqmData("title")&&(d=a("<div>"+d+"</div>").text(),b.jqmData("title",d))},_isRewritableBaseTag:function(){return a.mobile.dynamicBaseEnabled&&!a.support.dynamicBaseTag},_createDataUrl:function(b){return a.mobile.path.convertUrlToDataUrl(b)},_createFileUrl:function(b){return a.mobile.path.getFilePath(b)},_triggerWithDeprecated:function(b,c,d){var e=a.Event("page"+b),f=a.Event(this.widgetName+b);return(d||this.element).trigger(e,c),this.element.trigger(f,c),{deprecatedEvent:e,event:f}},_loadSuccess:function(b,c,e,f){var g=this._createFileUrl(b),h=this._createDataUrl(b);return a.proxy(function(i,j,k){var l,m=new RegExp("(<[^>]+\\bdata-"+this._getNs()+"role=[\"']?page[\"']?[^>]*>)"),n=new RegExp("\\bdata-"+this._getNs()+"url=[\"']?([^\"'>]*)[\"']?");m.test(i)&&RegExp.$1&&n.test(RegExp.$1)&&RegExp.$1&&(g=a.mobile.path.getFilePath(a("<div>"+RegExp.$1+"</div>").text())),e.prefetch===d&&this._getBase().set(g),l=this._parse(i,g),this._setLoadedTitle(l,i),c.xhr=k,c.textStatus=j,c.page=l,c.content=l,this._trigger("load",d,c)&&(this._isRewritableBaseTag()&&l&&this._getBase().rewrite(g,l),this._include(l,e),b.indexOf("&"+a.mobile.subPageUrlKey)>-1&&(l=this.element.children("[data-"+this._getNs()+"url='"+h+"']")),e.showLoadMsg&&this._hideLoading(),this.element.trigger("pageload"),f.resolve(b,e,l))},this)},_loadDefaults:{type:"get",data:d,reloadPage:!1,reload:!1,role:d,showLoadMsg:!1,loadMsgDelay:50},load:function(b,c){var e,f,g,h,i=c&&c.deferred||a.Deferred(),j=a.extend({},this._loadDefaults,c),k=null,l=a.mobile.path.makeUrlAbsolute(b,this._findBaseWithDefault());return j.reload=j.reloadPage,j.data&&"get"===j.type&&(l=a.mobile.path.addSearchParams(l,j.data),j.data=d),j.data&&"post"===j.type&&(j.reload=!0),e=this._createFileUrl(l),f=this._createDataUrl(l),k=this._find(l),0===k.length&&a.mobile.path.isEmbeddedPage(e)&&!a.mobile.path.isFirstPageUrl(e)?(i.reject(l,j),void 0):(this._getBase().reset(),k.length&&!j.reload?(this._enhance(k,j.role),i.resolve(l,j,k),j.prefetch||this._getBase().set(b),void 0):(h={url:b,absUrl:l,dataUrl:f,deferred:i,options:j},g=this._triggerWithDeprecated("beforeload",h),g.deprecatedEvent.isDefaultPrevented()||g.event.isDefaultPrevented()?void 0:(j.showLoadMsg&&this._showLoading(j.loadMsgDelay),j.prefetch===d&&this._getBase().reset(),a.mobile.allowCrossDomainPages||a.mobile.path.isSameDomain(a.mobile.path.documentUrl,l)?(a.ajax({url:e,type:j.type,data:j.data,contentType:j.contentType,dataType:"html",success:this._loadSuccess(l,h,j,i),error:this._loadError(l,h,j,i)}),void 0):(i.reject(l,j),void 0))))},_loadError:function(b,c,d,e){return a.proxy(function(f,g,h){this._getBase().set(a.mobile.path.get()),c.xhr=f,c.textStatus=g,c.errorThrown=h;var i=this._triggerWithDeprecated("loadfailed",c);i.deprecatedEvent.isDefaultPrevented()||i.event.isDefaultPrevented()||(d.showLoadMsg&&this._showError(),e.reject(b,d))},this)},_getTransitionHandler:function(b){return b=a.mobile._maybeDegradeTransition(b),a.mobile.transitionHandlers[b]||a.mobile.defaultTransitionHandler},_triggerCssTransitionEvents:function(b,c,d){var e=!1;d=d||"",c&&(b[0]===c[0]&&(e=!0),this._triggerWithDeprecated(d+"hide",{nextPage:b,samePage:e},c)),this._triggerWithDeprecated(d+"show",{prevPage:c||a("")},b)},_cssTransition:function(b,c,d){var e,f,g=d.transition,h=d.reverse,i=d.deferred;this._triggerCssTransitionEvents(b,c,"before"),this._hideLoading(),e=this._getTransitionHandler(g),f=new e(g,h,b,c).transition(),f.done(function(){i.resolve.apply(i,arguments)}),f.done(a.proxy(function(){this._triggerCssTransitionEvents(b,c)},this))},_releaseTransitionLock:function(){f=!1,e.length>0&&a.mobile.changePage.apply(null,e.pop())},_removeActiveLinkClass:function(b){a.mobile.removeActiveLinkClass(b)},_loadUrl:function(b,c,d){d.target=b,d.deferred=a.Deferred(),this.load(b,d),d.deferred.done(a.proxy(function(a,b,d){f=!1,b.absUrl=c.absUrl,this.transition(d,c,b)},this)),d.deferred.fail(a.proxy(function(){this._removeActiveLinkClass(!0),this._releaseTransitionLock(),this._triggerWithDeprecated("changefailed",c)},this))},_triggerPageBeforeChange:function(b,c,d){var e=new a.Event("pagebeforechange");return a.extend(c,{toPage:b,options:d}),c.absUrl="string"===a.type(b)?a.mobile.path.makeUrlAbsolute(b,this._findBaseWithDefault()):d.absUrl,this.element.trigger(e,c),e.isDefaultPrevented()?!1:!0},change:function(b,c){if(f)return e.unshift(arguments),void 0;var d=a.extend({},a.mobile.changePage.defaults,c),g={};d.fromPage=d.fromPage||this.activePage,this._triggerPageBeforeChange(b,g,d)&&(b=g.toPage,"string"===a.type(b)?(f=!0,this._loadUrl(b,g,d)):this.transition(b,g,d))},transition:function(b,g,h){var i,j,k,l,m,n,o,p,q,r,s,t,u,v;if(f)return e.unshift([b,h]),void 0;if(this._triggerPageBeforeChange(b,g,h)&&(v=this._triggerWithDeprecated("beforetransition",g),!v.deprecatedEvent.isDefaultPrevented()&&!v.event.isDefaultPrevented())){if(f=!0,b[0]!==a.mobile.firstPage[0]||h.dataUrl||(h.dataUrl=a.mobile.path.documentUrl.hrefNoHash),i=h.fromPage,j=h.dataUrl&&a.mobile.path.convertUrlToDataUrl(h.dataUrl)||b.jqmData("url"),k=j,l=a.mobile.path.getFilePath(j),m=a.mobile.navigate.history.getActive(),n=0===a.mobile.navigate.history.activeIndex,o=0,p=c.title,q=("dialog"===h.role||"dialog"===b.jqmData("role"))&&b.jqmData("dialog")!==!0,i&&i[0]===b[0]&&!h.allowSamePageTransition)return f=!1,this._triggerWithDeprecated("transition",g),this.element.trigger("pagechange",g),h.fromHashChange&&a.mobile.navigate.history.direct({url:j}),void 0;b.page({role:h.role}),h.fromHashChange&&(o="back"===h.direction?-1:1);try{c.activeElement&&"body"!==c.activeElement.nodeName.toLowerCase()?a(c.activeElement).blur():a("input:focus, textarea:focus, select:focus").blur()}catch(w){}r=!1,q&&m&&(m.url&&m.url.indexOf(a.mobile.dialogHashKey)>-1&&this.activePage&&!this.activePage.hasClass("ui-dialog")&&a.mobile.navigate.history.activeIndex>0&&(h.changeHash=!1,r=!0),j=m.url||"",j+=!r&&j.indexOf("#")>-1?a.mobile.dialogHashKey:"#"+a.mobile.dialogHashKey,0===a.mobile.navigate.history.activeIndex&&j===a.mobile.navigate.history.initialDst&&(j+=a.mobile.dialogHashKey)),s=m?b.jqmData("title")||b.children(":jqmData(role='header')").find(".ui-title").text():p,s&&p===c.title&&(p=s),b.jqmData("title")||b.jqmData("title",p),h.transition=h.transition||(o&&!n?m.transition:d)||(q?a.mobile.defaultDialogTransition:a.mobile.defaultPageTransition),!o&&r&&(a.mobile.navigate.history.getActive().pageUrl=k),j&&!h.fromHashChange&&(!a.mobile.path.isPath(j)&&j.indexOf("#")<0&&(j="#"+j),t={transition:h.transition,title:p,pageUrl:k,role:h.role},h.changeHash!==!1&&a.mobile.hashListeningEnabled?a.mobile.navigate(j,t,!0):b[0]!==a.mobile.firstPage[0]&&a.mobile.navigate.history.add(j,t)),c.title=p,a.mobile.activePage=b,this.activePage=b,h.reverse=h.reverse||0>o,u=a.Deferred(),this._cssTransition(b,i,{transition:h.transition,reverse:h.reverse,deferred:u}),u.done(a.proxy(function(c,d,e,f,i){a.mobile.removeActiveLinkClass(),h.duplicateCachedPage&&h.duplicateCachedPage.remove(),i||a.mobile.focusPage(b),this._releaseTransitionLock(),this.element.trigger("pagechange",g),this._triggerWithDeprecated("transition",g)},this))}},_findBaseWithDefault:function(){var b=this.activePage&&a.mobile.getClosestBaseUrl(this.activePage);return b||a.mobile.path.documentBase.hrefNoHash}}),a.mobile.navreadyDeferred=a.Deferred();var e=[],f=!1}(a),function(a,c){function d(a){for(;a&&("string"!=typeof a.nodeName||"a"!==a.nodeName.toLowerCase());)a=a.parentNode;return a}var e=a.Deferred(),f=a.mobile.path.documentUrl,g=null;a.mobile.loadPage=function(b,c){var d;return c=c||{},d=c.pageContainer||a.mobile.pageContainer,c.deferred=a.Deferred(),d.pagecontainer("load",b,c),c.deferred.promise()},a.mobile.back=function(){var c=b.navigator;this.phonegapNavigationEnabled&&c&&c.app&&c.app.backHistory?c.app.backHistory():a.mobile.pageContainer.pagecontainer("back")},a.mobile.focusPage=function(a){var b=a.find("[autofocus]"),c=a.find(".ui-title:eq(0)");return b.length?(b.focus(),void 0):(c.length?c.focus():a.focus(),void 0)},a.mobile._maybeDegradeTransition=a.mobile._maybeDegradeTransition||function(a){return a},a.fn.animationComplete=function(b){return a.support.cssTransitions?a(this).one("webkitAnimationEnd animationend",b):(setTimeout(b,0),a(this))},a.mobile.changePage=function(b,c){a.mobile.pageContainer.pagecontainer("change",b,c)},a.mobile.changePage.defaults={transition:c,reverse:!1,changeHash:!0,fromHashChange:!1,role:c,duplicateCachedPage:c,pageContainer:c,showLoadMsg:!0,dataUrl:c,fromPage:c,allowSamePageTransition:!1},a.mobile._registerInternalEvents=function(){var e=function(b,c){var d,e,h,i,j=!0;return!a.mobile.ajaxEnabled||b.is(":jqmData(ajax='false')")||!b.jqmHijackable().length||b.attr("target")?!1:(d=g&&g.attr("formaction")||b.attr("action"),i=(b.attr("method")||"get").toLowerCase(),d||(d=a.mobile.getClosestBaseUrl(b),"get"===i&&(d=a.mobile.path.parseUrl(d).hrefNoSearch),d===a.mobile.path.documentBase.hrefNoHash&&(d=f.hrefNoSearch)),d=a.mobile.path.makeUrlAbsolute(d,a.mobile.getClosestBaseUrl(b)),a.mobile.path.isExternal(d)&&!a.mobile.path.isPermittedCrossDomainRequest(f,d)?!1:(c||(e=b.serializeArray(),g&&g[0].form===b[0]&&(h=g.attr("name"),h&&(a.each(e,function(a,b){return b.name===h?(h="",!1):void 0}),h&&e.push({name:h,value:g.attr("value")}))),j={url:d,options:{type:i,data:a.param(e),transition:b.jqmData("transition"),reverse:"reverse"===b.jqmData("direction"),reloadPage:!0}}),j))};a.mobile.document.delegate("form","submit",function(b){var c;b.isDefaultPrevented()||(c=e(a(this)),c&&(a.mobile.changePage(c.url,c.options),b.preventDefault()))}),a.mobile.document.bind("vclick",function(b){var c,f,h=b.target,i=!1;if(!(b.which>1)&&a.mobile.linkBindingEnabled){if(g=a(h),a.data(h,"mobile-button")){if(!e(a(h).closest("form"),!0))return;h.parentNode&&(h=h.parentNode)}else{if(h=d(h),!h||"#"===a.mobile.path.parseUrl(h.getAttribute("href")||"#").hash)return;if(!a(h).jqmHijackable().length)return}~h.className.indexOf("ui-link-inherit")?h.parentNode&&(f=a.data(h.parentNode,"buttonElements")):f=a.data(h,"buttonElements"),f?h=f.outer:i=!0,c=a(h),i&&(c=c.closest(".ui-btn")),c.length>0&&!c.hasClass("ui-state-disabled")&&(a.mobile.removeActiveLinkClass(!0),a.mobile.activeClickedLink=c,a.mobile.activeClickedLink.addClass(a.mobile.activeBtnClass))}}),a.mobile.document.bind("click",function(e){if(a.mobile.linkBindingEnabled&&!e.isDefaultPrevented()){var g,h,i,j,k,l,m,n=d(e.target),o=a(n),p=function(){b.setTimeout(function(){a.mobile.removeActiveLinkClass(!0)},200)};if(a.mobile.activeClickedLink&&a.mobile.activeClickedLink[0]===e.target.parentNode&&p(),n&&!(e.which>1)&&o.jqmHijackable().length){if(o.is(":jqmData(rel='back')"))return a.mobile.back(),!1;if(g=a.mobile.getClosestBaseUrl(o),h=a.mobile.path.makeUrlAbsolute(o.attr("href")||"#",g),!a.mobile.ajaxEnabled&&!a.mobile.path.isEmbeddedPage(h))return p(),void 0;if(-1!==h.search("#")){if(h=h.replace(/[^#]*#/,""),!h)return e.preventDefault(),void 0;h=a.mobile.path.isPath(h)?a.mobile.path.makeUrlAbsolute(h,g):a.mobile.path.makeUrlAbsolute("#"+h,f.hrefNoHash)}if(i=o.is("[rel='external']")||o.is(":jqmData(ajax='false')")||o.is("[target]"),j=i||a.mobile.path.isExternal(h)&&!a.mobile.path.isPermittedCrossDomainRequest(f,h))return p(),void 0;k=o.jqmData("transition"),l="reverse"===o.jqmData("direction")||o.jqmData("back"),m=o.attr("data-"+a.mobile.ns+"rel")||c,a.mobile.changePage(h,{transition:k,reverse:l,role:m,link:o}),e.preventDefault()}}}),a.mobile.document.delegate(".ui-page","pageshow.prefetch",function(){var b=[];a(this).find("a:jqmData(prefetch)").each(function(){var c=a(this),d=c.attr("href");d&&-1===a.inArray(d,b)&&(b.push(d),a.mobile.loadPage(d,{role:c.attr("data-"+a.mobile.ns+"rel"),prefetch:!0}))})}),a.mobile.pageContainer.pagecontainer(),a.mobile.document.bind("pageshow",a.mobile.resetActivePageHeight),a.mobile.window.bind("throttledresize",a.mobile.resetActivePageHeight)},a(function(){e.resolve()}),a.when(e,a.mobile.navreadyDeferred).done(function(){a.mobile._registerInternalEvents()})}(a),function(a,b){a.mobile.Transition=function(){this.init.apply(this,arguments)},a.extend(a.mobile.Transition.prototype,{toPreClass:" ui-page-pre-in",init:function(b,c,d,e){a.extend(this,{name:b,reverse:c,$to:d,$from:e,deferred:new a.Deferred})},cleanFrom:function(){this.$from.removeClass(a.mobile.activePageClass+" out in reverse "+this.name).height("")},beforeDoneIn:function(){},beforeDoneOut:function(){},beforeStartOut:function(){},doneIn:function(){this.beforeDoneIn(),this.$to.removeClass("out in reverse "+this.name).height(""),this.toggleViewportClass(),a.mobile.window.scrollTop()!==this.toScroll&&this.scrollPage(),this.sequential||this.$to.addClass(a.mobile.activePageClass),this.deferred.resolve(this.name,this.reverse,this.$to,this.$from,!0)},doneOut:function(a,b,c,d){this.beforeDoneOut(),this.startIn(a,b,c,d)},hideIn:function(a){this.$to.css("z-index",-10),a.call(this),this.$to.css("z-index","")},scrollPage:function(){a.event.special.scrollstart.enabled=!1,(a.mobile.hideUrlBar||this.toScroll!==a.mobile.defaultHomeScroll)&&b.scrollTo(0,this.toScroll),setTimeout(function(){a.event.special.scrollstart.enabled=!0},150)},startIn:function(b,c,d,e){this.hideIn(function(){this.$to.addClass(a.mobile.activePageClass+this.toPreClass),e||a.mobile.focusPage(this.$to),this.$to.height(b+this.toScroll),d||this.scrollPage()}),d||this.$to.animationComplete(a.proxy(function(){this.doneIn()},this)),this.$to.removeClass(this.toPreClass).addClass(this.name+" in "+c),d&&this.doneIn()},startOut:function(b,c,d){this.beforeStartOut(b,c,d),this.$from.height(b+a.mobile.window.scrollTop()).addClass(this.name+" out"+c)},toggleViewportClass:function(){a.mobile.pageContainer.toggleClass("ui-mobile-viewport-transitioning viewport-"+this.name)},transition:function(){var b=this.reverse?" reverse":"",c=a.mobile.getScreenHeight(),d=a.mobile.maxTransitionWidth!==!1&&a.mobile.window.width()>a.mobile.maxTransitionWidth,e=!a.support.cssTransitions||!a.support.cssAnimations||d||!this.name||"none"===this.name||Math.max(a.mobile.window.scrollTop(),this.toScroll)>a.mobile.getMaxScrollForTransition();return this.toScroll=a.mobile.navigate.history.getActive().lastScroll||a.mobile.defaultHomeScroll,this.toggleViewportClass(),this.$from&&!e?this.startOut(c,b,e):this.doneOut(c,b,e,!0),this.deferred.promise()
+}})}(a,this),function(a){a.mobile.SerialTransition=function(){this.init.apply(this,arguments)},a.extend(a.mobile.SerialTransition.prototype,a.mobile.Transition.prototype,{sequential:!0,beforeDoneOut:function(){this.$from&&this.cleanFrom()},beforeStartOut:function(b,c,d){this.$from.animationComplete(a.proxy(function(){this.doneOut(b,c,d)},this))}})}(a),function(a){a.mobile.ConcurrentTransition=function(){this.init.apply(this,arguments)},a.extend(a.mobile.ConcurrentTransition.prototype,a.mobile.Transition.prototype,{sequential:!1,beforeDoneIn:function(){this.$from&&this.cleanFrom()},beforeStartOut:function(a,b,c){this.doneOut(a,b,c)}})}(a),function(a){var b=function(){return 3*a.mobile.getScreenHeight()};a.mobile.transitionHandlers={sequential:a.mobile.SerialTransition,simultaneous:a.mobile.ConcurrentTransition},a.mobile.defaultTransitionHandler=a.mobile.transitionHandlers.sequential,a.mobile.transitionFallbacks={},a.mobile._maybeDegradeTransition=function(b){return b&&!a.support.cssTransform3d&&a.mobile.transitionFallbacks[b]&&(b=a.mobile.transitionFallbacks[b]),b},a.mobile.getMaxScrollForTransition=a.mobile.getMaxScrollForTransition||b}(a),function(a){a.mobile.transitionFallbacks.flip="fade"}(a,this),function(a){a.mobile.transitionFallbacks.flow="fade"}(a,this),function(a){a.mobile.transitionFallbacks.pop="fade"}(a,this),function(a){a.mobile.transitionHandlers.slide=a.mobile.transitionHandlers.simultaneous,a.mobile.transitionFallbacks.slide="fade"}(a,this),function(a){a.mobile.transitionFallbacks.slidedown="fade"}(a,this),function(a){a.mobile.transitionFallbacks.slidefade="fade"}(a,this),function(a){a.mobile.transitionFallbacks.slideup="fade"}(a,this),function(a){a.mobile.transitionFallbacks.turn="fade"}(a,this),function(a){a.mobile.degradeInputs={color:!1,date:!1,datetime:!1,"datetime-local":!1,email:!1,month:!1,number:!1,range:"number",search:"text",tel:!1,time:!1,url:!1,week:!1},a.mobile.page.prototype.options.degradeInputs=a.mobile.degradeInputs,a.mobile.degradeInputsWithin=function(b){b=a(b),b.find("input").not(a.mobile.page.prototype.keepNativeSelector()).each(function(){var b,c,d,e,f=a(this),g=this.getAttribute("type"),h=a.mobile.degradeInputs[g]||"text";a.mobile.degradeInputs[g]&&(b=a("<div>").html(f.clone()).html(),c=b.indexOf(" type=")>-1,d=c?/\s+type=["']?\w+['"]?/:/\/?>/,e=' type="'+h+'" data-'+a.mobile.ns+'type="'+g+'"'+(c?"":">"),f.replaceWith(b.replace(d,e)))})}}(a),function(a,b,c){a.widget("mobile.page",a.mobile.page,{options:{closeBtn:"left",closeBtnText:"Close",overlayTheme:"a",corners:!0,dialog:!1},_create:function(){this._super(),this.options.dialog&&(a.extend(this,{_inner:this.element.children(),_headerCloseButton:null}),this.options.enhanced||this._setCloseBtn(this.options.closeBtn))},_enhance:function(){this._super(),this.options.dialog&&this.element.addClass("ui-dialog").wrapInner(a("<div/>",{role:"dialog","class":"ui-dialog-contain ui-overlay-shadow"+(this.options.corners?" ui-corner-all":"")}))},_setOptions:function(b){var d,e,f=this.options;b.corners!==c&&this._inner.toggleClass("ui-corner-all",!!b.corners),b.overlayTheme!==c&&a.mobile.activePage[0]===this.element[0]&&(f.overlayTheme=b.overlayTheme,this._handlePageBeforeShow()),b.closeBtnText!==c&&(d=f.closeBtn,e=b.closeBtnText),b.closeBtn!==c&&(d=b.closeBtn),d&&this._setCloseBtn(d,e),this._super(b)},_handlePageBeforeShow:function(){this.options.overlayTheme&&this.options.dialog?(this.removeContainerBackground(),this.setContainerBackground(this.options.overlayTheme)):this._super()},_setCloseBtn:function(b,c){var d,e=this._headerCloseButton;b="left"===b?"left":"right"===b?"right":"none","none"===b?e&&(e.remove(),e=null):e?(e.removeClass("ui-btn-left ui-btn-right").addClass("ui-btn-"+b),c&&e.text(c)):(d=this._inner.find(":jqmData(role='header')").first(),e=a("<a></a>",{href:"#","class":"ui-btn ui-corner-all ui-icon-delete ui-btn-icon-notext ui-btn-"+b}).attr("data-"+a.mobile.ns+"rel","back").text(c||this.options.closeBtnText||"").prependTo(d),this._on(e,{click:"close"})),this._headerCloseButton=e}})}(a,this),function(a,b,c){a.widget("mobile.dialog",{options:{closeBtn:"left",closeBtnText:"Close",overlayTheme:"a",corners:!0},_handlePageBeforeShow:function(){this._isCloseable=!0,this.options.overlayTheme&&this.element.page("removeContainerBackground").page("setContainerBackground",this.options.overlayTheme)},_handlePageBeforeHide:function(){this._isCloseable=!1},_handleVClickSubmit:function(b){var c,d=a(b.target).closest("vclick"===b.type?"a":"form");d.length&&!d.jqmData("transition")&&(c={},c["data-"+a.mobile.ns+"transition"]=(a.mobile.navigate.history.getActive()||{}).transition||a.mobile.defaultDialogTransition,c["data-"+a.mobile.ns+"direction"]="reverse",d.attr(c))},_create:function(){var b=this.element,c=this.options;b.addClass("ui-dialog").wrapInner(a("<div/>",{role:"dialog","class":"ui-dialog-contain ui-overlay-shadow"+(c.corners?" ui-corner-all":"")})),a.extend(this,{_isCloseable:!1,_inner:b.children(),_headerCloseButton:null}),this._on(b,{vclick:"_handleVClickSubmit",submit:"_handleVClickSubmit",pagebeforeshow:"_handlePageBeforeShow",pagebeforehide:"_handlePageBeforeHide"}),this._setCloseBtn(c.closeBtn)},_setOptions:function(b){var d,e,f=this.options;b.corners!==c&&this._inner.toggleClass("ui-corner-all",!!b.corners),b.overlayTheme!==c&&a.mobile.activePage[0]===this.element[0]&&(f.overlayTheme=b.overlayTheme,this._handlePageBeforeShow()),b.closeBtnText!==c&&(d=f.closeBtn,e=b.closeBtnText),b.closeBtn!==c&&(d=b.closeBtn),d&&this._setCloseBtn(d,e),this._super(b)},_setCloseBtn:function(b,c){var d,e=this._headerCloseButton;b="left"===b?"left":"right"===b?"right":"none","none"===b?e&&(e.remove(),e=null):e?(e.removeClass("ui-btn-left ui-btn-right").addClass("ui-btn-"+b),c&&e.text(c)):(d=this._inner.find(":jqmData(role='header')").first(),e=a("<a></a>",{role:"button",href:"#","class":"ui-btn ui-corner-all ui-icon-delete ui-btn-icon-notext ui-btn-"+b}).text(c||this.options.closeBtnText||"").prependTo(d),this._on(e,{click:"close"})),this._headerCloseButton=e},close:function(){var b=a.mobile.navigate.history;this._isCloseable&&(this._isCloseable=!1,a.mobile.hashListeningEnabled&&b.activeIndex>0?a.mobile.back():a.mobile.pageContainer.pagecontainer("back"))}})}(a,this),function(a,b){var c=/([A-Z])/g;a.widget("mobile.collapsible",{options:{enhanced:!1,expandCueText:null,collapseCueText:null,collapsed:!0,heading:"h1,h2,h3,h4,h5,h6,legend",collapsedIcon:null,expandedIcon:null,iconpos:null,theme:null,contentTheme:null,inset:null,corners:null,mini:null},_create:function(){var b=this.element,c={accordion:b.closest(":jqmData(role='collapsible-set')"+(a.mobile.collapsibleset?", :mobile-collapsibleset":"")).addClass("ui-collapsible-set")};a.extend(this,{_ui:c}),this.options.enhanced?(c.heading=a(".ui-collapsible-heading",this.element[0]),c.content=c.heading.next(),c.anchor=a("a",c.heading[0]).first(),c.status=c.anchor.children(".ui-collapsible-heading-status")):this._enhance(b,c),this._on(c.heading,{tap:function(){c.heading.find("a").first().addClass(a.mobile.activeBtnClass)},click:function(a){this._handleExpandCollapse(!c.heading.hasClass("ui-collapsible-heading-collapsed")),a.preventDefault(),a.stopPropagation()}})},_getOptions:function(b){var d,e=this._ui.accordion,f=this._ui.accordionWidget;b=a.extend({},b),e.length&&!f&&(this._ui.accordionWidget=f=e.data("mobile-collapsibleset"));for(d in b)b[d]=null!=b[d]?b[d]:f?f.options[d]:e.length?a.mobile.getAttribute(e[0],d.replace(c,"-$1").toLowerCase()):null,null==b[d]&&(b[d]=a.mobile.collapsible.defaults[d]);return b},_themeClassFromOption:function(a,b){return b?"none"===b?"":a+b:""},_enhance:function(b,c){var d,e=this._getOptions(this.options),f=this._themeClassFromOption("ui-body-",e.contentTheme);return b.addClass("ui-collapsible "+(e.inset?"ui-collapsible-inset ":"")+(e.inset&&e.corners?"ui-corner-all ":"")+(f?"ui-collapsible-themed-content ":"")),c.originalHeading=b.children(this.options.heading).first(),c.content=b.wrapInner("<div class='ui-collapsible-content "+f+"'></div>").children(".ui-collapsible-content"),c.heading=c.originalHeading,c.heading.is("legend")&&(c.heading=a("<div role='heading'>"+c.heading.html()+"</div>"),c.placeholder=a("<div><!-- placeholder for legend --></div>").insertBefore(c.originalHeading),c.originalHeading.remove()),d=e.collapsed?e.collapsedIcon?"ui-icon-"+e.collapsedIcon:"":e.expandedIcon?"ui-icon-"+e.expandedIcon:"",c.status=a("<span class='ui-collapsible-heading-status'></span>"),c.anchor=c.heading.detach().addClass("ui-collapsible-heading").append(c.status).wrapInner("<a href='#' class='ui-collapsible-heading-toggle'></a>").find("a").first().addClass("ui-btn "+(d?d+" ":"")+(d?"ui-btn-icon-"+("right"===e.iconpos?"right":"left")+" ":"")+this._themeClassFromOption("ui-btn-",e.theme)+" "+(e.mini?"ui-mini ":"")),c.heading.insertBefore(c.content),this._handleExpandCollapse(this.options.collapsed),c},refresh:function(){var b,c={};for(b in a.mobile.collapsible.defaults)c[b]=this.options[b];this._setOptions(c)},_setOptions:function(a){var c,d,e,f,g=this.element,h=this._getOptions(this.options),i=this._ui,j=i.anchor,k=i.status,l=this._getOptions(a);a.collapsed!==b&&this._handleExpandCollapse(a.collapsed),c=g.hasClass("ui-collapsible-collapsed"),c?(l.expandCueText!==b&&k.text(l.expandCueText),l.collapsedIcon!==b&&(h.collapsedIcon&&j.removeClass("ui-icon-"+h.collapsedIcon),l.collapsedIcon&&j.addClass("ui-icon-"+l.collapsedIcon))):(l.collapseCueText!==b&&k.text(l.collapseCueText),l.expandedIcon!==b&&(h.expandedIcon&&j.removeClass("ui-icon-"+h.expandedIcon),l.expandedIcon&&j.addClass("ui-icon-"+l.expandedIcon))),l.iconpos!==b&&(j.removeClass("ui-btn-icon-"+("right"===h.iconPos?"right":"left")),j.addClass("ui-btn-icon-"+("right"===l.iconPos?"right":"left"))),l.theme!==b&&(e=this._themeClassFromOption("ui-btn-",h.theme),d=this._themeClassFromOption("ui-btn-",l.theme),j.removeClass(e).addClass(d)),l.contentTheme!==b&&(e=this._themeClassFromOption("ui-body-",h.theme),d=this._themeClassFromOption("ui-body-",l.theme),i.content.removeClass(e).addClass(d)),l.inset!==b&&(g.toggleClass("ui-collapsible-inset",l.inset),f=!(!l.inset||!l.corners&&!h.corners)),l.corners!==b&&(f=!(!l.corners||!l.inset&&!h.inset)),f!==b&&g.toggleClass("ui-corner-all",f),l.mini!==b&&j.toggleClass("ui-mini",l.mini),this._super(a)},_handleExpandCollapse:function(b){var c=this._getOptions(this.options),d=this._ui;d.status.text(b?c.expandCueText:c.collapseCueText),d.heading.toggleClass("ui-collapsible-heading-collapsed",b).find("a").first().toggleClass("ui-icon-"+c.expandedIcon,!b).toggleClass("ui-icon-"+c.collapsedIcon,b||c.expandedIcon===c.collapsedIcon).removeClass(a.mobile.activeBtnClass),this.element.toggleClass("ui-collapsible-collapsed",b),d.content.toggleClass("ui-collapsible-content-collapsed",b).attr("aria-hidden",b).trigger("updatelayout"),this.options.collapsed=b,this._trigger(b?"collapse":"expand")},expand:function(){this._handleExpandCollapse(!1)},collapse:function(){this._handleExpandCollapse(!0)},_destroy:function(){var a=this._ui,b=this.options;b.enhanced||(a.placeholder?(a.originalHeading.insertBefore(a.placeholder),a.placeholder.remove(),a.heading.remove()):(a.status.remove(),a.heading.removeClass("ui-collapsible-heading ui-collapsible-heading-collapsed").children().contents().unwrap()),a.anchor.contents().unwrap(),a.content.contents().unwrap(),this.element.removeClass("ui-collapsible ui-collapsible-collapsed ui-collapsible-themed-content ui-collapsible-inset ui-corner-all"))}}),a.mobile.collapsible.defaults={expandCueText:" click to expand contents",collapseCueText:" click to collapse contents",collapsedIcon:"plus",contentTheme:"inherit",expandedIcon:"minus",iconpos:"left",inset:!0,corners:!0,theme:"inherit",mini:!1}}(a),function(a){a.mobile.behaviors.addFirstLastClasses={_getVisibles:function(a,b){var c;return b?c=a.not(".ui-screen-hidden"):(c=a.filter(":visible"),0===c.length&&(c=a.not(".ui-screen-hidden"))),c},_addFirstLastClasses:function(a,b,c){a.removeClass("ui-first-child ui-last-child"),b.eq(0).addClass("ui-first-child").end().last().addClass("ui-last-child"),c||this.element.trigger("updatelayout")},_removeFirstLastClasses:function(a){a.removeClass("ui-first-child ui-last-child")}}}(a),function(a,b){var c=":mobile-collapsible, "+a.mobile.collapsible.initSelector;a.widget("mobile.collapsibleset",a.extend({initSelector:":jqmData(role='collapsible-set'),:jqmData(role='collapsibleset')",options:a.extend({enhanced:!1},a.mobile.collapsible.defaults),_handleCollapsibleExpand:function(b){var c=a(b.target).closest(".ui-collapsible");c.parent().is(":mobile-collapsibleset, :jqmData(role='collapsible-set')")&&c.siblings(".ui-collapsible:not(.ui-collapsible-collapsed)").collapsible("collapse")},_create:function(){var b=this.element,c=this.options;a.extend(this,{_classes:""}),c.enhanced||(b.addClass("ui-collapsible-set "+this._themeClassFromOption("ui-group-theme-",c.theme)+" "+(c.corners&&c.inset?"ui-corner-all ":"")),this.element.find(a.mobile.collapsible.initSelector).collapsible()),this._on(b,{collapsibleexpand:"_handleCollapsibleExpand"})},_themeClassFromOption:function(a,b){return b?"none"===b?"":a+b:""},_init:function(){this._refresh(!0),this.element.children(c).filter(":jqmData(collapsed='false')").collapsible("expand")},_setOptions:function(a){var c,d,e=this.element,f=this._themeClassFromOption("ui-group-theme-",a.theme);return f&&e.removeClass(this._themeClassFromOption("ui-group-theme-",this.options.theme)).addClass(f),a.inset!==b&&(d=!(!a.inset||!a.corners&&!this.options.corners)),a.corners!==b&&(d=!(!a.corners||!a.inset&&!this.options.inset)),d!==b&&e.toggleClass("ui-corner-all",d),c=this._super(a),this.element.children(":mobile-collapsible").collapsible("refresh"),c},_destroy:function(){var a=this.element;this._removeFirstLastClasses(a.children(c)),a.removeClass("ui-collapsible-set ui-corner-all "+this._themeClassFromOption("ui-group-theme-",this.options.theme)).children(":mobile-collapsible").collapsible("destroy")},_refresh:function(b){var d=this.element.children(c);this.element.find(a.mobile.collapsible.initSelector).not(".ui-collapsible").collapsible(),this._addFirstLastClasses(d,this._getVisibles(d,b),b)},refresh:function(){this._refresh(!1)}},a.mobile.behaviors.addFirstLastClasses))}(a),function(a){a.fn.fieldcontain=function(){return this.addClass("ui-field-contain")}}(a),function(a){a.fn.grid=function(b){return this.each(function(){var c,d,e=a(this),f=a.extend({grid:null},b),g=e.children(),h={solo:1,a:2,b:3,c:4,d:5},i=f.grid;if(!i)if(g.length<=5)for(d in h)h[d]===g.length&&(i=d);else i="a",e.addClass("ui-grid-duo");c=h[i],e.addClass("ui-grid-"+i),g.filter(":nth-child("+c+"n+1)").addClass("ui-block-a"),c>1&&g.filter(":nth-child("+c+"n+2)").addClass("ui-block-b"),c>2&&g.filter(":nth-child("+c+"n+3)").addClass("ui-block-c"),c>3&&g.filter(":nth-child("+c+"n+4)").addClass("ui-block-d"),c>4&&g.filter(":nth-child("+c+"n+5)").addClass("ui-block-e")})}}(a),function(a,b){a.widget("mobile.navbar",{options:{iconpos:"top",grid:null},_create:function(){var d=this.element,e=d.find("a"),f=e.filter(":jqmData(icon)").length?this.options.iconpos:b;d.addClass("ui-navbar").attr("role","navigation").find("ul").jqmEnhanceable().grid({grid:this.options.grid}),e.each(function(){var b=a.mobile.getAttribute(this,"icon"),c=a.mobile.getAttribute(this,"theme"),d="ui-btn";c&&(d+=" ui-btn-"+c),b&&(d+=" ui-icon-"+b+" ui-btn-icon-"+f),a(this).addClass(d)}),d.delegate("a","vclick",function(){var b=a(this);b.hasClass("ui-state-disabled")||b.hasClass("ui-disabled")||b.hasClass(a.mobile.activeBtnClass)||(e.removeClass(a.mobile.activeBtnClass),b.addClass(a.mobile.activeBtnClass),a(c).one("pagehide",function(){b.removeClass(a.mobile.activeBtnClass)}))}),d.closest(".ui-page").bind("pagebeforeshow",function(){e.filter(".ui-state-persist").addClass(a.mobile.activeBtnClass)})}})}(a),function(a){var b=a.mobile.getAttribute;a.widget("mobile.listview",a.extend({options:{theme:null,countTheme:null,dividerTheme:null,icon:"carat-r",splitIcon:"carat-r",splitTheme:null,corners:!0,shadow:!0,inset:!1},_create:function(){var a=this,b="";b+=a.options.inset?" ui-listview-inset":"",a.options.inset&&(b+=a.options.corners?" ui-corner-all":"",b+=a.options.shadow?" ui-shadow":""),a.element.addClass(" ui-listview"+b),a.refresh(!0)},_findFirstElementByTagName:function(a,b,c,d){var e={};for(e[c]=e[d]=!0;a;){if(e[a.nodeName])return a;a=a[b]}return null},_addThumbClasses:function(b){var c,d,e=b.length;for(c=0;e>c;c++)d=a(this._findFirstElementByTagName(b[c].firstChild,"nextSibling","img","IMG")),d.length&&a(this._findFirstElementByTagName(d[0].parentNode,"parentNode","li","LI")).addClass(d.hasClass("ui-li-icon")?"ui-li-has-icon":"ui-li-has-thumb")},_getChildrenByTagName:function(b,c,d){var e=[],f={};for(f[c]=f[d]=!0,b=b.firstChild;b;)f[b.nodeName]&&e.push(b),b=b.nextSibling;return a(e)},_beforeListviewRefresh:a.noop,_afterListviewRefresh:a.noop,refresh:function(c){var d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x=this.options,y=this.element,z=!!a.nodeName(y[0],"ol"),A=y.attr("start"),B={},C=y.find(".ui-li-count"),D=b(y[0],"counttheme")||this.options.countTheme,E=D?"ui-body-"+D:"ui-body-inherit";for(x.theme&&y.addClass("ui-group-theme-"+x.theme),z&&(A||0===A)&&(n=parseInt(A,10)-1,y.css("counter-reset","listnumbering "+n)),this._beforeListviewRefresh(),w=this._getChildrenByTagName(y[0],"li","LI"),e=0,f=w.length;f>e;e++)g=w.eq(e),h="",(c||g[0].className.search(/\bui-li-static\b|\bui-li-divider\b/)<0)&&(l=this._getChildrenByTagName(g[0],"a","A"),m="list-divider"===b(g[0],"role"),p=g.attr("value"),i=b(g[0],"theme"),l.length&&l[0].className.search(/\bui-btn\b/)<0&&!m?(j=b(g[0],"icon"),k=j===!1?!1:j||x.icon,l.removeClass("ui-link"),d="ui-btn",i&&(d+=" ui-btn-"+i),l.length>1?(h="ui-li-has-alt",q=l.last(),r=b(q[0],"theme")||x.splitTheme||b(g[0],"theme",!0),s=r?" ui-btn-"+r:"",t=b(q[0],"icon")||b(g[0],"icon")||x.splitIcon,u="ui-btn ui-btn-icon-notext ui-icon-"+t+s,q.attr("title",a.trim(q.getEncodedText())).addClass(u).empty()):k&&(d+=" ui-btn-icon-right ui-icon-"+k),l.first().addClass(d)):m?(v=b(g[0],"theme")||x.dividerTheme||x.theme,h="ui-li-divider ui-bar-"+(v?v:"inherit"),g.attr("role","heading")):l.length<=0&&(h="ui-li-static ui-body-"+(i?i:"inherit")),z&&p&&(o=parseInt(p,10)-1,g.css("counter-reset","listnumbering "+o))),B[h]||(B[h]=[]),B[h].push(g[0]);for(h in B)a(B[h]).addClass(h);C.each(function(){a(this).closest("li").addClass("ui-li-has-count")}),E&&C.addClass(E),this._addThumbClasses(w),this._addThumbClasses(w.find(".ui-btn")),this._afterListviewRefresh(),this._addFirstLastClasses(w,this._getVisibles(w,c),c)}},a.mobile.behaviors.addFirstLastClasses))}(a),function(a){function b(b){var c=a.trim(b.text())||null;return c?c=c.slice(0,1).toUpperCase():null}a.widget("mobile.listview",a.mobile.listview,{options:{autodividers:!1,autodividersSelector:b},_beforeListviewRefresh:function(){this.options.autodividers&&(this._replaceDividers(),this._superApply(arguments))},_replaceDividers:function(){var b,d,e,f,g,h=null,i=this.element;for(i.children("li:jqmData(role='list-divider')").remove(),d=i.children("li"),b=0;b<d.length;b++)e=d[b],f=this.options.autodividersSelector(a(e)),f&&h!==f&&(g=c.createElement("li"),g.appendChild(c.createTextNode(f)),g.setAttribute("data-"+a.mobile.ns+"role","list-divider"),e.parentNode.insertBefore(g,e)),h=f}})}(a),function(a){var b=/(^|\s)ui-li-divider($|\s)/,c=/(^|\s)ui-screen-hidden($|\s)/;a.widget("mobile.listview",a.mobile.listview,{options:{hideDividers:!1},_afterListviewRefresh:function(){var a,d,e,f=!0;if(this._superApply(arguments),this.options.hideDividers)for(a=this._getChildrenByTagName(this.element[0],"li","LI"),d=a.length-1;d>-1;d--)e=a[d],e.className.match(b)?(f&&(e.className=e.className+" ui-screen-hidden"),f=!0):e.className.match(c)||(f=!1)}})}(a),function(a){a.mobile.nojs=function(b){a(":jqmData(role='nojs')",b).addClass("ui-nojs")}}(a),function(a){a.mobile.behaviors.formReset={_handleFormReset:function(){this._on(this.element.closest("form"),{reset:function(){this._delay("_reset")}})}}}(a),function(a,b){a.widget("mobile.checkboxradio",a.extend({initSelector:"input:not( :jqmData(role='flipswitch' ) )[type='checkbox'],input[type='radio']:not( :jqmData(role='flipswitch' ))",options:{theme:"inherit",mini:!1,wrapperClass:null,enhanced:!1,iconpos:"left"},_create:function(){var b=this.element,c=this.options,d=function(a,b){return a.jqmData(b)||a.closest("form, fieldset").jqmData(b)},e=b.closest("label"),f=e.length?e:b.closest("form, fieldset, :jqmData(role='page'), :jqmData(role='dialog')").find("label").filter("[for='"+a.mobile.path.hashToSelector(b[0].id)+"']").first(),g=b[0].type,h="ui-"+g+"-on",i="ui-"+g+"-off";("checkbox"===g||"radio"===g)&&(this.element[0].disabled&&(this.options.disabled=!0),c.iconpos=d(b,"iconpos")||f.attr("data-"+a.mobile.ns+"iconpos")||c.iconpos,c.mini=d(b,"mini")||c.mini,a.extend(this,{input:b,label:f,parentLabel:e,inputtype:g,checkedClass:h,uncheckedClass:i}),this.options.enhanced||this._enhance(),this._on(f,{vmouseover:"_handleLabelVMouseOver",vclick:"_handleLabelVClick"}),this._on(b,{vmousedown:"_cacheVals",vclick:"_handleInputVClick",focus:"_handleInputFocus",blur:"_handleInputBlur"}),this._handleFormReset(),this.refresh())},_enhance:function(){this.label.addClass("ui-btn ui-corner-all"),this.parentLabel.length>0?this.input.add(this.label).wrapAll(this._wrapper()):(this.element.wrap(this._wrapper()),this.element.parent().prepend(this.label)),this._setOptions({theme:this.options.theme,iconpos:this.options.iconpos,mini:this.options.mini})},_wrapper:function(){return a("<div class='"+(this.options.wrapperClass?this.options.wrapperClass:"")+" ui-"+this.inputtype+(this.options.disabled?" ui-state-disabled":"")+"' >")},_handleInputFocus:function(){this.label.addClass(a.mobile.focusClass)},_handleInputBlur:function(){this.label.removeClass(a.mobile.focusClass)},_handleInputVClick:function(){var a=this.element;a.is(":checked")?(a.prop("checked",!0),this._getInputSet().not(a).prop("checked",!1)):a.prop("checked",!1),this._updateAll()},_handleLabelVMouseOver:function(a){this.label.parent().hasClass("ui-state-disabled")&&a.stopPropagation()},_handleLabelVClick:function(a){var b=this.element;return b.is(":disabled")?(a.preventDefault(),void 0):(this._cacheVals(),b.prop("checked","radio"===this.inputtype&&!0||!b.prop("checked")),b.triggerHandler("click"),this._getInputSet().not(b).prop("checked",!1),this._updateAll(),!1)},_cacheVals:function(){this._getInputSet().each(function(){a(this).attr("data-"+a.mobile.ns+"cacheVal",this.checked)})},_getInputSet:function(){return"checkbox"===this.inputtype?this.element:this.element.closest("form, :jqmData(role='page'), :jqmData(role='dialog')").find("input[name='"+this.element[0].name+"'][type='"+this.inputtype+"']")},_updateAll:function(){var b=this;this._getInputSet().each(function(){var c=a(this);(this.checked||"checkbox"===b.inputtype)&&c.trigger("change")}).checkboxradio("refresh")},_reset:function(){this.refresh()},_hasIcon:function(){var b,c,d=a.mobile.controlgroup;return d&&(b=this.element.closest(":mobile-controlgroup,"+d.prototype.initSelector),b.length>0)?(c=a.data(b[0],"mobile-controlgroup"),"horizontal"!==(c?c.options.type:b.attr("data-"+a.mobile.ns+"type"))):!0},refresh:function(){var b=this._hasIcon(),c=this.element[0].checked,d=a.mobile.activeBtnClass,e="ui-btn-icon-"+this.options.iconpos,f=[],g=[];b?(g.push(d),f.push(e)):(g.push(e),(c?f:g).push(d)),c?(f.push(this.checkedClass),g.push(this.uncheckedClass)):(f.push(this.uncheckedClass),g.push(this.checkedClass)),this.label.addClass(f.join(" ")).removeClass(g.join(" "))},widget:function(){return this.label.parent()},_setOptions:function(a){var c=this.label,d=this.options,e=this.widget(),f=this._hasIcon();a.disabled!==b&&(this.input.prop("disabled",!!a.disabled),e.toggleClass("ui-state-disabled",!!a.disabled)),a.mini!==b&&e.toggleClass("ui-mini",!!a.mini),a.theme!==b&&c.removeClass("ui-btn-"+d.theme).addClass("ui-btn-"+a.theme),a.wrapperClass!==b&&e.removeClass(d.wrapperClass).addClass(a.wrapperClass),a.iconpos!==b&&f?c.removeClass("ui-btn-icon-"+d.iconpos).addClass("ui-btn-icon-"+a.iconpos):f||c.removeClass("ui-btn-icon-"+d.iconpos),this._super(a)}},a.mobile.behaviors.formReset))}(a),function(a,b){a.widget("mobile.button",{initSelector:"input[type='button'], input[type='submit'], input[type='reset']",options:{theme:null,icon:null,iconpos:"left",iconshadow:!1,corners:!0,shadow:!0,inline:null,mini:null,wrapperClass:null,enhanced:!1},_create:function(){this.element.is(":disabled")&&(this.options.disabled=!0),this.options.enhanced||this._enhance(),a.extend(this,{wrapper:this.element.parent()}),this._on({focus:function(){this.widget().addClass(a.mobile.focusClass)},blur:function(){this.widget().removeClass(a.mobile.focusClass)}}),this.refresh(!0)},_enhance:function(){this.element.wrap(this._button())},_button:function(){var b=this.options,c=this._getIconClasses(this.options);return a("<div class='ui-btn ui-input-btn"+(b.wrapperClass?" "+b.wrapperClass:"")+(b.theme?" ui-btn-"+b.theme:"")+(b.corners?" ui-corner-all":"")+(b.shadow?" ui-shadow":"")+(b.inline?" ui-btn-inline":"")+(b.mini?" ui-mini":"")+(b.disabled?" ui-state-disabled":"")+(c?" "+c:"")+"' >"+this.element.val()+"</div>")},widget:function(){return this.wrapper},_destroy:function(){this.element.insertBefore(this.button),this.button.remove()},_getIconClasses:function(a){return a.icon?"ui-icon-"+a.icon+(a.iconshadow?" ui-shadow-icon":"")+" ui-btn-icon-"+a.iconpos:""},_setOptions:function(c){var d=this.widget();c.theme!==b&&d.removeClass(this.options.theme).addClass("ui-btn-"+c.theme),c.corners!==b&&d.toggleClass("ui-corner-all",c.corners),c.shadow!==b&&d.toggleClass("ui-shadow",c.shadow),c.inline!==b&&d.toggleClass("ui-btn-inline",c.inline),c.mini!==b&&d.toggleClass("ui-mini",c.mini),c.disabled!==b&&(this.element.prop("disabled",c.disabled),d.toggleClass("ui-state-disabled",c.disabled)),(c.icon!==b||c.iconshadow!==b||c.iconpos!==b)&&d.removeClass(this._getIconClasses(this.options)).addClass(this._getIconClasses(a.extend({},this.options,c))),this._super(c)},refresh:function(b){if(this.options.icon&&"notext"===this.options.iconpos&&this.element.attr("title")&&this.element.attr("title",this.element.val()),!b){var c=this.element.detach();a(this.wrapper).text(this.element.val()).append(c)}}})}(a),function(a){var b=a("meta[name=viewport]"),c=b.attr("content"),d=c+",maximum-scale=1, user-scalable=no",e=c+",maximum-scale=10, user-scalable=yes",f=/(user-scalable[\s]*=[\s]*no)|(maximum-scale[\s]*=[\s]*1)[$,\s]/.test(c);a.mobile.zoom=a.extend({},{enabled:!f,locked:!1,disable:function(c){f||a.mobile.zoom.locked||(b.attr("content",d),a.mobile.zoom.enabled=!1,a.mobile.zoom.locked=c||!1)},enable:function(c){f||a.mobile.zoom.locked&&c!==!0||(b.attr("content",e),a.mobile.zoom.enabled=!0,a.mobile.zoom.locked=!1)},restore:function(){f||(b.attr("content",c),a.mobile.zoom.enabled=!0)}})}(a),function(a,b){a.widget("mobile.textinput",{initSelector:"input[type='text'],input[type='search'],:jqmData(type='search'),input[type='number'],:jqmData(type='number'),input[type='password'],input[type='email'],input[type='url'],input[type='tel'],textarea,input[type='time'],input[type='date'],input[type='month'],input[type='week'],input[type='datetime'],input[type='datetime-local'],input[type='color'],input:not([type]),input[type='file']",options:{theme:null,corners:!0,mini:!1,preventFocusZoom:/iPhone|iPad|iPod/.test(navigator.platform)&&navigator.userAgent.indexOf("AppleWebKit")>-1,wrapperClass:"",enhanced:!1},_create:function(){var b=this.options,c=this.element.is("[type='search'], :jqmData(type='search')"),d="TEXTAREA"===this.element[0].tagName,e=this.element.is("[data-"+(a.mobile.ns||"")+"type='range']"),f=(this.element.is("input")||this.element.is("[data-"+(a.mobile.ns||"")+"type='search']"))&&!e;this.element.prop("disabled")&&(b.disabled=!0),a.extend(this,{classes:this._classesFromOptions(),isSearch:c,isTextarea:d,isRange:e,inputNeedsWrap:f}),this._autoCorrect(),b.enhanced||this._enhance(),this._on({focus:"_handleFocus",blur:"_handleBlur"})},refresh:function(){this.setOptions({disabled:this.element.is(":disabled")})},_enhance:function(){var a=[];this.isTextarea&&a.push("ui-input-text"),(this.isTextarea||this.isRange)&&a.push("ui-shadow-inset"),this.inputNeedsWrap?this.element.wrap(this._wrap()):a=a.concat(this.classes),this.element.addClass(a.join(" "))},widget:function(){return this.inputNeedsWrap?this.element.parent():this.element},_classesFromOptions:function(){var a=this.options,b=[];return b.push("ui-body-"+(null===a.theme?"inherit":a.theme)),a.corners&&b.push("ui-corner-all"),a.mini&&b.push("ui-mini"),a.disabled&&b.push("ui-state-disabled"),a.wrapperClass&&b.push(a.wrapperClass),b},_wrap:function(){return a("<div class='"+(this.isSearch?"ui-input-search ":"ui-input-text ")+this.classes.join(" ")+" "+"ui-shadow-inset'></div>")},_autoCorrect:function(){"undefined"==typeof this.element[0].autocorrect||a.support.touchOverflow||(this.element[0].setAttribute("autocorrect","off"),this.element[0].setAttribute("autocomplete","off"))},_handleBlur:function(){this.widget().removeClass(a.mobile.focusClass),this.options.preventFocusZoom&&a.mobile.zoom.enable(!0)},_handleFocus:function(){this.options.preventFocusZoom&&a.mobile.zoom.disable(!0),this.widget().addClass(a.mobile.focusClass)},_setOptions:function(a){var c=this.widget();this._super(a),(a.disabled!==b||a.mini!==b||a.corners!==b||a.theme!==b||a.wrapperClass!==b)&&(c.removeClass(this.classes.join(" ")),this.classes=this._classesFromOptions(),c.addClass(this.classes.join(" "))),a.disabled!==b&&this.element.prop("disabled",!!a.disabled)},_destroy:function(){this.options.enhanced||(this.inputNeedsWrap&&this.element.unwrap(),this.element.removeClass("ui-input-text "+this.classes.join(" ")))}})}(a),function(a,d){a.widget("mobile.slider",a.extend({initSelector:"input[type='range'], :jqmData(type='range'), :jqmData(role='slider')",widgetEventPrefix:"slide",options:{theme:null,trackTheme:null,corners:!0,mini:!1,highlight:!1},_create:function(){var e,f,g,h,i,j,k,l,m,n,o=this,p=this.element,q=this.options.trackTheme||a.mobile.getAttribute(p[0],"theme"),r=q?" ui-bar-"+q:" ui-bar-inherit",s=this.options.corners||p.jqmData("corners")?" ui-corner-all":"",t=this.options.mini||p.jqmData("mini")?" ui-mini":"",u=p[0].nodeName.toLowerCase(),v="select"===u,w=p.parent().is(":jqmData(role='rangeslider')"),x=v?"ui-slider-switch":"",y=p.attr("id"),z=a("[for='"+y+"']"),A=z.attr("id")||y+"-label",B=v?0:parseFloat(p.attr("min")),C=v?p.find("option").length-1:parseFloat(p.attr("max")),D=b.parseFloat(p.attr("step")||1),E=c.createElement("a"),F=a(E),G=c.createElement("div"),H=a(G),I=this.options.highlight&&!v?function(){var b=c.createElement("div");return b.className="ui-slider-bg "+a.mobile.activeBtnClass,a(b).prependTo(H)}():!1;if(z.attr("id",A),this.isToggleSwitch=v,E.setAttribute("href","#"),G.setAttribute("role","application"),G.className=[this.isToggleSwitch?"ui-slider ui-slider-track ui-shadow-inset ":"ui-slider-track ui-shadow-inset ",x,r,s,t].join(""),E.className="ui-slider-handle",G.appendChild(E),F.attr({role:"slider","aria-valuemin":B,"aria-valuemax":C,"aria-valuenow":this._value(),"aria-valuetext":this._value(),title:this._value(),"aria-labelledby":A}),a.extend(this,{slider:H,handle:F,control:p,type:u,step:D,max:C,min:B,valuebg:I,isRangeslider:w,dragging:!1,beforeStart:null,userModified:!1,mouseMoved:!1}),v){for(k=p.attr("tabindex"),k&&F.attr("tabindex",k),p.attr("tabindex","-1").focus(function(){a(this).blur(),F.focus()}),f=c.createElement("div"),f.className="ui-slider-inneroffset",g=0,h=G.childNodes.length;h>g;g++)f.appendChild(G.childNodes[g]);for(G.appendChild(f),F.addClass("ui-slider-handle-snapping"),e=p.find("option"),i=0,j=e.length;j>i;i++)l=i?"a":"b",m=i?" "+a.mobile.activeBtnClass:"",n=c.createElement("span"),n.className=["ui-slider-label ui-slider-label-",l,m].join(""),n.setAttribute("role","img"),n.appendChild(c.createTextNode(e[i].innerHTML)),a(n).prependTo(H);
+o._labels=a(".ui-slider-label",H)}p.addClass(v?"ui-slider-switch":"ui-slider-input"),this._on(p,{change:"_controlChange",keyup:"_controlKeyup",blur:"_controlBlur",vmouseup:"_controlVMouseUp"}),H.bind("vmousedown",a.proxy(this._sliderVMouseDown,this)).bind("vclick",!1),this._on(c,{vmousemove:"_preventDocumentDrag"}),this._on(H.add(c),{vmouseup:"_sliderVMouseUp"}),H.insertAfter(p),v||w||(f=this.options.mini?"<div class='ui-slider ui-mini'>":"<div class='ui-slider'>",p.add(H).wrapAll(f)),this._on(this.handle,{vmousedown:"_handleVMouseDown",keydown:"_handleKeydown",keyup:"_handleKeyup"}),this.handle.bind("vclick",!1),this._handleFormReset(),this.refresh(d,d,!0)},_setOptions:function(a){a.theme!==d&&this._setTheme(a.theme),a.trackTheme!==d&&this._setTrackTheme(a.trackTheme),a.corners!==d&&this._setCorners(a.corners),a.mini!==d&&this._setMini(a.mini),a.highlight!==d&&this._setHighlight(a.highlight),a.disabled!==d&&this._setDisabled(a.disabled),this._super(a)},_controlChange:function(a){return this._trigger("controlchange",a)===!1?!1:(this.mouseMoved||this.refresh(this._value(),!0),void 0)},_controlKeyup:function(){this.refresh(this._value(),!0,!0)},_controlBlur:function(){this.refresh(this._value(),!0)},_controlVMouseUp:function(){this._checkedRefresh()},_handleVMouseDown:function(){this.handle.focus()},_handleKeydown:function(b){var c=this._value();if(!this.options.disabled){switch(b.keyCode){case a.mobile.keyCode.HOME:case a.mobile.keyCode.END:case a.mobile.keyCode.PAGE_UP:case a.mobile.keyCode.PAGE_DOWN:case a.mobile.keyCode.UP:case a.mobile.keyCode.RIGHT:case a.mobile.keyCode.DOWN:case a.mobile.keyCode.LEFT:b.preventDefault(),this._keySliding||(this._keySliding=!0,this.handle.addClass("ui-state-active"))}switch(b.keyCode){case a.mobile.keyCode.HOME:this.refresh(this.min);break;case a.mobile.keyCode.END:this.refresh(this.max);break;case a.mobile.keyCode.PAGE_UP:case a.mobile.keyCode.UP:case a.mobile.keyCode.RIGHT:this.refresh(c+this.step);break;case a.mobile.keyCode.PAGE_DOWN:case a.mobile.keyCode.DOWN:case a.mobile.keyCode.LEFT:this.refresh(c-this.step)}}},_handleKeyup:function(){this._keySliding&&(this._keySliding=!1,this.handle.removeClass("ui-state-active"))},_sliderVMouseDown:function(a){return this.options.disabled||1!==a.which&&0!==a.which&&a.which!==d?!1:this._trigger("beforestart",a)===!1?!1:(this.dragging=!0,this.userModified=!1,this.mouseMoved=!1,this.isToggleSwitch&&(this.beforeStart=this.element[0].selectedIndex),this.refresh(a),this._trigger("start"),!1)},_sliderVMouseUp:function(){return this.dragging?(this.dragging=!1,this.isToggleSwitch&&(this.handle.addClass("ui-slider-handle-snapping"),this.mouseMoved?this.userModified?this.refresh(0===this.beforeStart?1:0):this.refresh(this.beforeStart):this.refresh(0===this.beforeStart?1:0)),this.mouseMoved=!1,this._trigger("stop"),!1):void 0},_preventDocumentDrag:function(a){return this._trigger("drag",a)===!1?!1:this.dragging&&!this.options.disabled?(this.mouseMoved=!0,this.isToggleSwitch&&this.handle.removeClass("ui-slider-handle-snapping"),this.refresh(a),this.userModified=this.beforeStart!==this.element[0].selectedIndex,!1):void 0},_checkedRefresh:function(){this.value!==this._value()&&this.refresh(this._value())},_value:function(){return this.isToggleSwitch?this.element[0].selectedIndex:parseFloat(this.element.val())},_reset:function(){this.refresh(d,!1,!0)},refresh:function(b,d,e){var f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z=this,A=a.mobile.getAttribute(this.element[0],"theme"),B=this.options.theme||A,C=B?" ui-btn-"+B:"",D=this.options.trackTheme||A,E=D?" ui-bar-"+D:" ui-bar-inherit",F=this.options.corners?" ui-corner-all":"",G=this.options.mini?" ui-mini":"";if(z.slider[0].className=[this.isToggleSwitch?"ui-slider ui-slider-switch ui-slider-track ui-shadow-inset":"ui-slider-track ui-shadow-inset",E,F,G].join(""),(this.options.disabled||this.element.prop("disabled"))&&this.disable(),this.value=this._value(),this.options.highlight&&!this.isToggleSwitch&&0===this.slider.find(".ui-slider-bg").length&&(this.valuebg=function(){var b=c.createElement("div");return b.className="ui-slider-bg "+a.mobile.activeBtnClass,a(b).prependTo(z.slider)}()),this.handle.addClass("ui-btn"+C+" ui-shadow"),l=this.element,m=!this.isToggleSwitch,n=m?[]:l.find("option"),o=m?parseFloat(l.attr("min")):0,p=m?parseFloat(l.attr("max")):n.length-1,q=m&&parseFloat(l.attr("step"))>0?parseFloat(l.attr("step")):1,"object"==typeof b){if(h=b,i=8,f=this.slider.offset().left,g=this.slider.width(),j=g/((p-o)/q),!this.dragging||h.pageX<f-i||h.pageX>f+g+i)return;k=j>1?100*((h.pageX-f)/g):Math.round(100*((h.pageX-f)/g))}else null==b&&(b=m?parseFloat(l.val()||0):l[0].selectedIndex),k=100*((parseFloat(b)-o)/(p-o));if(!isNaN(k)&&(r=k/100*(p-o)+o,s=(r-o)%q,t=r-s,2*Math.abs(s)>=q&&(t+=s>0?q:-q),u=100/((p-o)/q),r=parseFloat(t.toFixed(5)),"undefined"==typeof j&&(j=g/((p-o)/q)),j>1&&m&&(k=(r-o)*u*(1/q)),0>k&&(k=0),k>100&&(k=100),o>r&&(r=o),r>p&&(r=p),this.handle.css("left",k+"%"),this.handle[0].setAttribute("aria-valuenow",m?r:n.eq(r).attr("value")),this.handle[0].setAttribute("aria-valuetext",m?r:n.eq(r).getEncodedText()),this.handle[0].setAttribute("title",m?r:n.eq(r).getEncodedText()),this.valuebg&&this.valuebg.css("width",k+"%"),this._labels&&(v=100*(this.handle.width()/this.slider.width()),w=k&&v+(100-v)*k/100,x=100===k?0:Math.min(v+100-w,100),this._labels.each(function(){var b=a(this).hasClass("ui-slider-label-a");a(this).width((b?w:x)+"%")})),!e)){if(y=!1,m?(y=l.val()!==r,l.val(r)):(y=l[0].selectedIndex!==r,l[0].selectedIndex=r),this._trigger("beforechange",b)===!1)return!1;!d&&y&&l.trigger("change")}},_setHighlight:function(a){a=!!a,a?(this.options.highlight=!!a,this.refresh()):this.valuebg&&(this.valuebg.remove(),this.valuebg=!1)},_setTheme:function(a){this.handle.removeClass("ui-btn-"+this.options.theme).addClass("ui-btn-"+a);var b=this.options.theme?this.options.theme:"inherit",c=a?a:"inherit";this.control.removeClass("ui-body-"+b).addClass("ui-body-"+c)},_setTrackTheme:function(a){var b=this.options.trackTheme?this.options.trackTheme:"inherit",c=a?a:"inherit";this.slider.removeClass("ui-body-"+b).addClass("ui-body-"+c)},_setMini:function(a){a=!!a,this.isToggleSwitch||this.isRangeslider||(this.slider.parent().toggleClass("ui-mini",a),this.element.toggleClass("ui-mini",a)),this.slider.toggleClass("ui-mini",a)},_setCorners:function(a){this.slider.toggleClass("ui-corner-all",a),this.isToggleSwitch||this.control.toggleClass("ui-corner-all",a)},_setDisabled:function(a){a=!!a,this.element.prop("disabled",a),this.slider.toggleClass("ui-state-disabled").attr("aria-disabled",a)}},a.mobile.behaviors.formReset))}(a),function(a){function b(){return c||(c=a("<div></div>",{"class":"ui-slider-popup ui-shadow ui-corner-all"})),c.clone()}var c;a.widget("mobile.slider",a.mobile.slider,{options:{popupEnabled:!1,showValue:!1},_create:function(){this._super(),a.extend(this,{_currentValue:null,_popup:null,_popupVisible:!1}),this._setOption("popupEnabled",this.options.popupEnabled),this._on(this.handle,{vmousedown:"_showPopup"}),this._on(this.slider.add(this.document),{vmouseup:"_hidePopup"}),this._refresh()},_positionPopup:function(){var a=this.handle.offset();this._popup.offset({left:a.left+(this.handle.width()-this._popup.width())/2,top:a.top-this._popup.outerHeight()-5})},_setOption:function(a,c){this._super(a,c),"showValue"===a?this.handle.html(c&&!this.options.mini?this._value():""):"popupEnabled"===a&&c&&!this._popup&&(this._popup=b().addClass("ui-body-"+(this.options.theme||"a")).insertBefore(this.element))},refresh:function(){this._super.apply(this,arguments),this._refresh()},_refresh:function(){var a,b=this.options;b.popupEnabled&&this.handle.removeAttr("title"),a=this._value(),a!==this._currentValue&&(this._currentValue=a,b.popupEnabled&&this._popup?(this._positionPopup(),this._popup.html(a)):b.showValue&&!this.options.mini&&this.handle.html(a))},_showPopup:function(){this.options.popupEnabled&&!this._popupVisible&&(this.handle.html(""),this._popup.show(),this._positionPopup(),this._popupVisible=!0)},_hidePopup:function(){var a=this.options;a.popupEnabled&&this._popupVisible&&(a.showValue&&!a.mini&&this.handle.html(this._value()),this._popup.hide(),this._popupVisible=!1)}})}(a),function(a,b){a.widget("mobile.flipswitch",a.extend({options:{onText:"On",offText:"Off",theme:null,enhanced:!1,wrapperClass:null,corners:!0,mini:!1},_create:function(){this.options.enhanced?a.extend(this,{flipswitch:this.element.parent(),on:this.element.find(".ui-flipswitch-on").eq(0),off:this.element.find(".ui-flipswitch-off").eq(0),type:this.element.get(0).tagName}):this._enhance(),this._handleFormReset(),this.element.is(":disabled")&&this._setOptions({disabled:!0}),this._on(this.flipswitch,{click:"_toggle",swipeleft:"_left",swiperight:"_right"}),this._on(this.on,{keydown:"_keydown"}),this._on({change:"refresh"})},widget:function(){return this.flipswitch},_left:function(){this.flipswitch.removeClass("ui-flipswitch-active"),"SELECT"===this.type?this.element.get(0).selectedIndex=0:this.element.prop("checked",!1),this.element.trigger("change")},_right:function(){this.flipswitch.addClass("ui-flipswitch-active"),"SELECT"===this.type?this.element.get(0).selectedIndex=1:this.element.prop("checked",!0),this.element.trigger("change")},_enhance:function(){var b=a("<div>"),c=this.options,d=this.element,e=c.theme?c.theme:"inherit",f=a("<span></span>",{tabindex:1}),g=a("<span></span>"),h=d.get(0).tagName,i="INPUT"===h?c.onText:d.find("option").eq(1).text(),j="INPUT"===h?c.offText:d.find("option").eq(0).text();f.addClass("ui-flipswitch-on ui-btn ui-shadow ui-btn-inherit").text(i),g.addClass("ui-flipswitch-off").text(j),b.addClass("ui-flipswitch ui-shadow-inset ui-bar-"+e+" "+(c.wrapperClass?c.wrapperClass:"")+" "+(d.is(":checked")||d.find("option").eq(1).is(":selected")?"ui-flipswitch-active":"")+(d.is(":disabled")?" ui-state-disabled":"")+(c.corners?" ui-corner-all":"")+(c.mini?" ui-mini":"")).append(f,g),d.addClass("ui-flipswitch-input").after(b).appendTo(b),a.extend(this,{flipswitch:b,on:f,off:g,type:h})},_reset:function(){this.refresh()},refresh:function(){var a,b=this.flipswitch.hasClass("ui-flipswitch-active")?"_right":"_left";a="SELECT"===this.type?this.element.get(0).selectedIndex>0?"_right":"_left":this.element.prop("checked")?"_right":"_left",a!==b&&this[a]()},_toggle:function(){var a=this.flipswitch.hasClass("ui-flipswitch-active")?"_left":"_right";this[a]()},_keydown:function(b){b.which===a.mobile.keyCode.LEFT?this._left():b.which===a.mobile.keyCode.RIGHT?this._right():b.which===a.mobile.keyCode.SPACE&&(this._toggle(),b.preventDefault())},_setOptions:function(a){if(a.theme!==b){var c=a.theme?a.theme:"inherit",d=a.theme?a.theme:"inherit";this.widget().removeClass("ui-bar-"+c).addClass("ui-bar-"+d)}a.onText!==b&&this.on.text(a.onText),a.offText!==b&&this.off.text(a.offText),a.disabled!==b&&this.widget().toggleClass("ui-state-disabled",a.disabled),a.mini!==b&&this.widget().toggleClass("ui-mini",a.mini),a.corners!==b&&this.widget().toggleClass("ui-corner-all",a.corners),this._super(a)},_destroy:function(){this.options.enhanced||(this.on.remove(),this.off.remove(),this.element.unwrap(),this.flipswitch.remove(),this.removeClass("ui-flipswitch-input"))}},a.mobile.behaviors.formReset))}(a),function(a,b){a.widget("mobile.rangeslider",a.extend({options:{theme:null,trackTheme:null,corners:!0,mini:!1,highlight:!0},_create:function(){var b=this.element,c=this.options.mini?"ui-rangeslider ui-mini":"ui-rangeslider",d=b.find("input").first(),e=b.find("input").last(),f=b.find("label").first(),g=a.data(d.get(0),"mobile-slider")||a.data(d.slider().get(0),"mobile-slider"),h=a.data(e.get(0),"mobile-slider")||a.data(e.slider().get(0),"mobile-slider"),i=g.slider,j=h.slider,k=g.handle,l=a("<div class='ui-rangeslider-sliders' />").appendTo(b);d.addClass("ui-rangeslider-first"),e.addClass("ui-rangeslider-last"),b.addClass(c),i.appendTo(l),j.appendTo(l),f.insertBefore(b),k.prependTo(j),a.extend(this,{_inputFirst:d,_inputLast:e,_sliderFirst:i,_sliderLast:j,_label:f,_targetVal:null,_sliderTarget:!1,_sliders:l,_proxy:!1}),this.refresh(),this._on(this.element.find("input.ui-slider-input"),{slidebeforestart:"_slidebeforestart",slidestop:"_slidestop",slidedrag:"_slidedrag",slidebeforechange:"_change",blur:"_change",keyup:"_change"}),this._on({mousedown:"_change"}),this._on(this.element.closest("form"),{reset:"_handleReset"}),this._on(k,{vmousedown:"_dragFirstHandle"})},_handleReset:function(){var a=this;setTimeout(function(){a._updateHighlight()},0)},_dragFirstHandle:function(b){return a.data(this._inputFirst.get(0),"mobile-slider").dragging=!0,a.data(this._inputFirst.get(0),"mobile-slider").refresh(b),!1},_slidedrag:function(b){var c=a(b.target).is(this._inputFirst),d=c?this._inputLast:this._inputFirst;return this._sliderTarget=!1,"first"===this._proxy&&c||"last"===this._proxy&&!c?(a.data(d.get(0),"mobile-slider").dragging=!0,a.data(d.get(0),"mobile-slider").refresh(b),!1):void 0},_slidestop:function(b){var c=a(b.target).is(this._inputFirst);this._proxy=!1,this.element.find("input").trigger("vmouseup"),this._sliderFirst.css("z-index",c?1:"")},_slidebeforestart:function(b){this._sliderTarget=!1,a(b.originalEvent.target).hasClass("ui-slider-track")&&(this._sliderTarget=!0,this._targetVal=a(b.target).val())},_setOptions:function(a){a.theme!==b&&this._setTheme(a.theme),a.trackTheme!==b&&this._setTrackTheme(a.trackTheme),a.mini!==b&&this._setMini(a.mini),a.highlight!==b&&this._setHighlight(a.highlight),this._super(a),this.refresh()},refresh:function(){var a=this.element,b=this.options;(this._inputFirst.is(":disabled")||this._inputLast.is(":disabled"))&&(this.options.disabled=!0),a.find("input").slider({theme:b.theme,trackTheme:b.trackTheme,disabled:b.disabled,corners:b.corners,mini:b.mini,highlight:b.highlight}).slider("refresh"),this._updateHighlight()},_change:function(b){if("keyup"===b.type)return this._updateHighlight(),!1;var c=this,d=parseFloat(this._inputFirst.val(),10),e=parseFloat(this._inputLast.val(),10),f=a(b.target).hasClass("ui-rangeslider-first"),g=f?this._inputFirst:this._inputLast,h=f?this._inputLast:this._inputFirst;if(this._inputFirst.val()>this._inputLast.val()&&"mousedown"===b.type&&!a(b.target).hasClass("ui-slider-handle"))g.blur();else if("mousedown"===b.type)return;return d>e&&!this._sliderTarget?(g.val(f?e:d).slider("refresh"),this._trigger("normalize")):d>e&&(g.val(this._targetVal).slider("refresh"),setTimeout(function(){h.val(f?d:e).slider("refresh"),a.data(h.get(0),"mobile-slider").handle.focus(),c._sliderFirst.css("z-index",f?"":1),c._trigger("normalize")},0),this._proxy=f?"first":"last"),d===e?(a.data(g.get(0),"mobile-slider").handle.css("z-index",1),a.data(h.get(0),"mobile-slider").handle.css("z-index",0)):(a.data(h.get(0),"mobile-slider").handle.css("z-index",""),a.data(g.get(0),"mobile-slider").handle.css("z-index","")),this._updateHighlight(),d>=e?!1:void 0},_updateHighlight:function(){var b=parseInt(a.data(this._inputFirst.get(0),"mobile-slider").handle.get(0).style.left,10),c=parseInt(a.data(this._inputLast.get(0),"mobile-slider").handle.get(0).style.left,10),d=c-b;this.element.find(".ui-slider-bg").css({"margin-left":b+"%",width:d+"%"})},_setTheme:function(a){this._inputFirst.slider("option","theme",a),this._inputLast.slider("option","theme",a)},_setTrackTheme:function(a){this._inputFirst.slider("option","trackTheme",a),this._inputLast.slider("option","trackTheme",a)},_setMini:function(a){this._inputFirst.slider("option","mini",a),this._inputLast.slider("option","mini",a),this.element.toggleClass("ui-mini",!!a)},_setHighlight:function(a){this._inputFirst.slider("option","highlight",a),this._inputLast.slider("option","highlight",a)},_destroy:function(){this._label.prependTo(this.element),this.element.removeClass("ui-rangeslider ui-mini"),this._inputFirst.after(this._sliderFirst),this._inputLast.after(this._sliderLast),this._sliders.remove(),this.element.find("input").removeClass("ui-rangeslider-first ui-rangeslider-last").slider("destroy")}},a.mobile.behaviors.formReset))}(a),function(a,b){a.widget("mobile.textinput",a.mobile.textinput,{options:{clearBtn:!1,clearBtnText:"Clear text"},_create:function(){this._super(),(this.options.clearBtn||this.isSearch)&&this._addClearBtn()},clearButton:function(){return a("<a href='#' class='ui-input-clear ui-btn ui-icon-delete ui-btn-icon-notext ui-corner-all' title='"+this.options.clearBtnText+"'>"+this.options.clearBtnText+"</a>")},_clearBtnClick:function(a){this.element.val("").focus().trigger("change"),this._clearBtn.addClass("ui-input-clear-hidden"),a.preventDefault()},_addClearBtn:function(){this.options.enhanced||this._enhanceClear(),a.extend(this,{_clearBtn:this.widget().find("a.ui-input-clear")}),this._bindClearEvents(),this._toggleClear()},_enhanceClear:function(){this.clearButton().appendTo(this.widget()),this.widget().addClass("ui-input-has-clear")},_bindClearEvents:function(){this._on(this._clearBtn,{click:"_clearBtnClick"}),this._on({keyup:"_toggleClear",change:"_toggleClear",input:"_toggleClear",focus:"_toggleClear",blur:"_toggleClear",cut:"_toggleClear",paste:"_toggleClear"})},_unbindClear:function(){this._off(this._clearBtn,"click"),this._off(this.element,"keyup change input focus blur cut paste")},_setOptions:function(a){this._super(a),a.clearbtn===b||this.element.is("textarea, :jqmData(type='range')")||(a.clearBtn?this._addClearBtn():this._destroyClear()),a.clearBtnText!==b&&this._clearBtn!==b&&this._clearBtn.text(a.clearBtnText)},_toggleClear:function(){this._delay("_toggleClearClass",0)},_toggleClearClass:function(){this._clearBtn.toggleClass("ui-input-clear-hidden",!this.element.val())},_destroyClear:function(){this.element.removeClass("ui-input-has-clear"),this._unbindClear()._clearBtn.remove()},_destroy:function(){this._super(),this._destroyClear()}})}(a),function(a,b){a.widget("mobile.textinput",a.mobile.textinput,{options:{autogrow:!0,keyupTimeoutBuffer:100},_create:function(){this._super(),this.options.autogrow&&this.isTextarea&&this._autogrow()},_autogrow:function(){this._on({keyup:"_timeout",change:"_timeout",input:"_timeout",paste:"_timeout"}),this._on(!0,this.document,{pageshow:"_handleShow",popupbeforeposition:"_handleShow",updatelayout:"_handleShow",panelopen:"_handleShow"})},_handleShow:function(b){a.contains(b.target,this.element[0])&&this.element.is(":visible")&&("popupbeforeposition"!==b.type&&this.element.addClass("ui-textinput-autogrow-resize").one("transitionend webkitTransitionEnd oTransitionEnd",a.proxy(function(){this.element.removeClass("ui-textinput-autogrow-resize")},this)),this._prepareHeightUpdate())},_unbindAutogrow:function(){this._off(this.element,"keyup change input paste"),this._off(this.document,"pageshow popupbeforeposition updatelayout panelopen")},keyupTimeout:null,_prepareHeightUpdate:function(a){this.keyupTimeout&&clearTimeout(this.keyupTimeout),a===b?this._updateHeight():this.keyupTimeout=this._delay("_updateHeight",a)},_timeout:function(){this._prepareHeightUpdate(this.options.keyupTimeoutBuffer)},_updateHeight:function(){this.keyupTimeout=0,this.element.css({height:0,"min-height":0,"max-height":0});var a,b,c,d=this.element[0].scrollHeight,e=this.element[0].clientHeight,f=parseFloat(this.element.css("border-top-width")),g=parseFloat(this.element.css("border-bottom-width")),h=f+g,i=d+h+15;0===e&&(a=parseFloat(this.element.css("padding-top")),b=parseFloat(this.element.css("padding-bottom")),c=a+b,i+=c),this.element.css({height:i,"min-height":"","max-height":""})},refresh:function(){this.options.autogrow&&this.isTextarea&&this._updateHeight()},_setOptions:function(a){this._super(a),a.autogrow!==b&&this.isTextarea&&(a.autogrow?this._autogrow():this._unbindAutogrow())}})}(a),function(a){a.widget("mobile.selectmenu",a.extend({initSelector:"select:not( :jqmData(role='slider')):not( :jqmData(role='flipswitch') )",options:{theme:null,icon:"carat-d",iconpos:"right",inline:!1,corners:!0,shadow:!0,iconshadow:!1,overlayTheme:null,dividerTheme:null,hidePlaceholderMenuItems:!0,closeText:"Close",nativeMenu:!0,preventFocusZoom:/iPhone|iPad|iPod/.test(navigator.platform)&&navigator.userAgent.indexOf("AppleWebKit")>-1,mini:!1},_button:function(){return a("<div/>")},_setDisabled:function(a){return this.element.attr("disabled",a),this.button.attr("aria-disabled",a),this._setOption("disabled",a)},_focusButton:function(){var a=this;setTimeout(function(){a.button.focus()},40)},_selectOptions:function(){return this.select.find("option")},_preExtension:function(){var b=this.options.inline||this.element.jqmData("inline"),c=this.options.mini||this.element.jqmData("mini"),d="";~this.element[0].className.indexOf("ui-btn-left")&&(d=" ui-btn-left"),~this.element[0].className.indexOf("ui-btn-right")&&(d=" ui-btn-right"),b&&(d+=" ui-btn-inline"),c&&(d+=" ui-mini"),this.select=this.element.removeClass("ui-btn-left ui-btn-right").wrap("<div class='ui-select"+d+"'>"),this.selectId=this.select.attr("id")||"select-"+this.uuid,this.buttonId=this.selectId+"-button",this.label=a("label[for='"+this.selectId+"']"),this.isMultiple=this.select[0].multiple},_destroy:function(){var a=this.element.parents(".ui-select");a.length>0&&(a.is(".ui-btn-left, .ui-btn-right")&&this.element.addClass(a.hasClass("ui-btn-left")?"ui-btn-left":"ui-btn-right"),this.element.insertAfter(a),a.remove())},_create:function(){this._preExtension(),this.button=this._button();var c=this,d=this.options,e=d.icon?d.iconpos||this.select.jqmData("iconpos"):!1,f=this.button.insertBefore(this.select).attr("id",this.buttonId).addClass("ui-btn"+(d.icon?" ui-icon-"+d.icon+" ui-btn-icon-"+e+(d.iconshadow?" ui-shadow-icon":""):"")+(d.theme?" ui-btn-"+d.theme:"")+(d.corners?" ui-corner-all":"")+(d.shadow?" ui-shadow":""));this.setButtonText(),d.nativeMenu&&b.opera&&b.opera.version&&f.addClass("ui-select-nativeonly"),this.isMultiple&&(this.buttonCount=a("<span>").addClass("ui-li-count ui-body-inherit").hide().appendTo(f.addClass("ui-li-has-count"))),(d.disabled||this.element.attr("disabled"))&&this.disable(),this.select.change(function(){c.refresh(),d.nativeMenu&&this.blur()}),this._handleFormReset(),this._on(this.button,{keydown:"_handleKeydown"}),this.build()},build:function(){var b=this;this.select.appendTo(b.button).bind("vmousedown",function(){b.button.addClass(a.mobile.activeBtnClass)}).bind("focus",function(){b.button.addClass(a.mobile.focusClass)}).bind("blur",function(){b.button.removeClass(a.mobile.focusClass)}).bind("focus vmouseover",function(){b.button.trigger("vmouseover")}).bind("vmousemove",function(){b.button.removeClass(a.mobile.activeBtnClass)}).bind("change blur vmouseout",function(){b.button.trigger("vmouseout").removeClass(a.mobile.activeBtnClass)}),b.button.bind("vmousedown",function(){b.options.preventFocusZoom&&a.mobile.zoom.disable(!0)}),b.label.bind("click focus",function(){b.options.preventFocusZoom&&a.mobile.zoom.disable(!0)}),b.select.bind("focus",function(){b.options.preventFocusZoom&&a.mobile.zoom.disable(!0)}),b.button.bind("mouseup",function(){b.options.preventFocusZoom&&setTimeout(function(){a.mobile.zoom.enable(!0)},0)}),b.select.bind("blur",function(){b.options.preventFocusZoom&&a.mobile.zoom.enable(!0)})},selected:function(){return this._selectOptions().filter(":selected")},selectedIndices:function(){var a=this;return this.selected().map(function(){return a._selectOptions().index(this)}).get()},setButtonText:function(){var b=this,d=this.selected(),e=this.placeholder,f=a(c.createElement("span"));this.button.children("span").not(".ui-li-count").remove().end().end().prepend(function(){return e=d.length?d.map(function(){return a(this).text()}).get().join(", "):b.placeholder,e?f.text(e):f.html("&nbsp;"),f.addClass(b.select.attr("class")).addClass(d.attr("class")).removeClass("ui-screen-hidden")}())},setButtonCount:function(){var a=this.selected();this.isMultiple&&this.buttonCount[a.length>1?"show":"hide"]().text(a.length)},_handleKeydown:function(){this._delay("_refreshButton")},_reset:function(){this.refresh()},_refreshButton:function(){this.setButtonText(),this.setButtonCount()},refresh:function(){this._refreshButton()},open:a.noop,close:a.noop,disable:function(){this._setDisabled(!0),this.button.addClass("ui-state-disabled")},enable:function(){this._setDisabled(!1),this.button.removeClass("ui-state-disabled")}},a.mobile.behaviors.formReset))}(a),function(a){a.mobile.links=function(b){a(b).find("a").jqmEnhanceable().filter(":jqmData(rel='popup')[href][href!='']").each(function(){var a=this,b=a.getAttribute("href").substring(1);b&&(a.setAttribute("aria-haspopup",!0),a.setAttribute("aria-owns",b),a.setAttribute("aria-expanded",!1))}).end().not(".ui-btn, :jqmData(role='none'), :jqmData(role='nojs')").addClass("ui-link")}}(a),function(a,c){function d(a,b,c,d){var e=d;return e=b>a?c+(a-b)/2:Math.min(Math.max(c,d-b/2),c+a-b)}function e(a){return{x:a.scrollLeft(),y:a.scrollTop(),cx:a[0].innerWidth||a.width(),cy:a[0].innerHeight||a.height()}}a.widget("mobile.popup",{options:{wrapperClass:null,theme:null,overlayTheme:null,shadow:!0,corners:!0,transition:"none",positionTo:"origin",tolerance:null,closeLinkSelector:"a:jqmData(rel='back')",closeLinkEvents:"click.popup",navigateEvents:"navigate.popup",closeEvents:"navigate.popup pagebeforechange.popup",dismissible:!0,enhanced:!1,history:!a.mobile.browser.oldIE},_create:function(){var b=this.element,c=b.attr("id"),d=this.options;d.history=d.history&&a.mobile.ajaxEnabled&&a.mobile.hashListeningEnabled,a.extend(this,{_scrollTop:0,_page:b.closest(".ui-page"),_ui:null,_fallbackTransition:"",_currentTransition:!1,_prerequisites:null,_isOpen:!1,_tolerance:null,_resizeData:null,_ignoreResizeTo:0,_orientationchangeInProgress:!1}),0===this._page.length&&(this._page=a("body")),d.enhanced?this._ui={container:b.parent(),screen:b.parent().prev(),placeholder:a(this.document[0].getElementById(c+"-placeholder"))}:(this._ui=this._enhance(b,c),this._applyTransition(d.transition)),this._setTolerance(d.tolerance)._ui.focusElement=this._ui.container,this._on(this._ui.screen,{vclick:"_eatEventAndClose"}),this._on(this.window,{orientationchange:a.proxy(this,"_handleWindowOrientationchange"),resize:a.proxy(this,"_handleWindowResize"),keyup:a.proxy(this,"_handleWindowKeyUp")}),this._on(this.document,{focusin:"_handleDocumentFocusIn"})},_enhance:function(b,c){var d=this.options,e=d.wrapperClass,f={screen:a("<div class='ui-screen-hidden ui-popup-screen "+this._themeClassFromOption("ui-overlay-",d.overlayTheme)+"'></div>"),placeholder:a("<div style='display: none;'><!-- placeholder --></div>"),container:a("<div class='ui-popup-container ui-popup-hidden ui-popup-truncate"+(e?" "+e:"")+"'></div>")},g=this.document[0].createDocumentFragment();return g.appendChild(f.screen[0]),g.appendChild(f.container[0]),c&&(f.screen.attr("id",c+"-screen"),f.container.attr("id",c+"-popup"),f.placeholder.attr("id",c+"-placeholder").html("<!-- placeholder for "+c+" -->")),this._page[0].appendChild(g),f.placeholder.insertAfter(b),b.detach().addClass("ui-popup "+this._themeClassFromOption("ui-body-",d.theme)+" "+(d.shadow?"ui-overlay-shadow ":"")+(d.corners?"ui-corner-all ":"")).appendTo(f.container),f},_eatEventAndClose:function(a){return a.preventDefault(),a.stopImmediatePropagation(),this.options.dismissible&&this.close(),!1},_resizeScreen:function(){var a=this._ui.screen,b=this._ui.container.outerHeight(!0),c=a.removeAttr("style").height(),d=this.document.height()-1;d>c?a.height(d):b>c&&a.height(b)},_handleWindowKeyUp:function(b){return this._isOpen&&b.keyCode===a.mobile.keyCode.ESCAPE?this._eatEventAndClose(b):void 0},_expectResizeEvent:function(){var a=e(this.window);if(this._resizeData){if(a.x===this._resizeData.windowCoordinates.x&&a.y===this._resizeData.windowCoordinates.y&&a.cx===this._resizeData.windowCoordinates.cx&&a.cy===this._resizeData.windowCoordinates.cy)return!1;clearTimeout(this._resizeData.timeoutId)}return this._resizeData={timeoutId:this._delay("_resizeTimeout",200),windowCoordinates:a},!0},_resizeTimeout:function(){this._isOpen?this._expectResizeEvent()||(this._ui.container.hasClass("ui-popup-hidden")&&(this._ui.container.removeClass("ui-popup-hidden ui-popup-truncate"),this.reposition({positionTo:"window"}),this._ignoreResizeEvents()),this._resizeScreen(),this._resizeData=null,this._orientationchangeInProgress=!1):(this._resizeData=null,this._orientationchangeInProgress=!1)},_stopIgnoringResizeEvents:function(){this._ignoreResizeTo=0},_ignoreResizeEvents:function(){this._ignoreResizeTo&&clearTimeout(this._ignoreResizeTo),this._ignoreResizeTo=this._delay("_stopIgnoringResizeEvents",1e3)},_handleWindowResize:function(){this._isOpen&&0===this._ignoreResizeTo&&(!this._expectResizeEvent()&&!this._orientationchangeInProgress||this._ui.container.hasClass("ui-popup-hidden")||this._ui.container.addClass("ui-popup-hidden ui-popup-truncate").removeAttr("style"))},_handleWindowOrientationchange:function(){!this._orientationchangeInProgress&&this._isOpen&&0===this._ignoreResizeTo&&(this._expectResizeEvent(),this._orientationchangeInProgress=!0)},_handleDocumentFocusIn:function(b){var c,d=b.target,e=this._ui;if(this._isOpen){if(d!==e.container[0]){if(c=a(d),0===c.parents().filter(e.container[0]).length)return a(this.document[0].activeElement).one("focus",function(){c.blur()}),e.focusElement.focus(),b.preventDefault(),b.stopImmediatePropagation(),!1;e.focusElement[0]===e.container[0]&&(e.focusElement=c)}this._ignoreResizeEvents()}},_themeClassFromOption:function(a,b){return b?"none"===b?"":a+b:a+"inherit"},_applyTransition:function(b){return b&&(this._ui.container.removeClass(this._fallbackTransition),"none"!==b&&(this._fallbackTransition=a.mobile._maybeDegradeTransition(b),"none"===this._fallbackTransition&&(this._fallbackTransition=""),this._ui.container.addClass(this._fallbackTransition))),this},_setOptions:function(a){var b=this.options,d=this.element,e=this._ui.screen;return a.wrapperClass!==c&&this._ui.container.removeClass(b.wrapperClass).addClass(a.wrapperClass),a.theme!==c&&d.removeClass(this._themeClassFromOption("ui-body-",b.theme)).addClass(this._themeClassFromOption("ui-body-",a.theme)),a.overlayTheme!==c&&(e.removeClass(this._themeClassFromOption("ui-overlay-",b.overlayTheme)).addClass(this._themeClassFromOption("ui-overlay-",a.overlayTheme)),this._isOpen&&e.addClass("in")),a.shadow!==c&&d.toggleClass("ui-overlay-shadow",a.shadow),a.corners!==c&&d.toggleClass("ui-corner-all",a.corners),a.transition!==c&&(this._currentTransition||this._applyTransition(a.transition)),a.tolerance!==c&&this._setTolerance(a.tolerance),a.disabled!==c&&a.disabled&&this.close(),this._super(a)},_setTolerance:function(b){var d,e={t:30,r:15,b:30,l:15};if(b!==c)switch(d=String(b).split(","),a.each(d,function(a,b){d[a]=parseInt(b,10)}),d.length){case 1:isNaN(d[0])||(e.t=e.r=e.b=e.l=d[0]);break;case 2:isNaN(d[0])||(e.t=e.b=d[0]),isNaN(d[1])||(e.l=e.r=d[1]);break;case 4:isNaN(d[0])||(e.t=d[0]),isNaN(d[1])||(e.r=d[1]),isNaN(d[2])||(e.b=d[2]),isNaN(d[3])||(e.l=d[3])}return this._tolerance=e,this},_clampPopupWidth:function(a){var b,c=e(this.window),d={x:this._tolerance.l,y:c.y+this._tolerance.t,cx:c.cx-this._tolerance.l-this._tolerance.r,cy:c.cy-this._tolerance.t-this._tolerance.b};return a||this._ui.container.css("max-width",d.cx),b={cx:this._ui.container.outerWidth(!0),cy:this._ui.container.outerHeight(!0)},{rc:d,menuSize:b}},_calculateFinalLocation:function(a,b){var c,e=b.rc,f=b.menuSize;return c={left:d(e.cx,f.cx,e.x,a.x),top:d(e.cy,f.cy,e.y,a.y)},c.top=Math.max(0,c.top),c.top-=Math.min(c.top,Math.max(0,c.top+f.cy-this.document.height())),c},_placementCoords:function(a){return this._calculateFinalLocation(a,this._clampPopupWidth())},_createPrerequisites:function(b,c,d){var e,f=this;e={screen:a.Deferred(),container:a.Deferred()},e.screen.then(function(){e===f._prerequisites&&b()}),e.container.then(function(){e===f._prerequisites&&c()}),a.when(e.screen,e.container).done(function(){e===f._prerequisites&&(f._prerequisites=null,d())}),f._prerequisites=e},_animate:function(b){return this._ui.screen.removeClass(b.classToRemove).addClass(b.screenClassToAdd),b.prerequisites.screen.resolve(),b.transition&&"none"!==b.transition&&(b.applyTransition&&this._applyTransition(b.transition),this._fallbackTransition)?(this._ui.container.animationComplete(a.proxy(b.prerequisites.container,"resolve")).addClass(b.containerClassToAdd).removeClass(b.classToRemove),void 0):(this._ui.container.removeClass(b.classToRemove),b.prerequisites.container.resolve(),void 0)
+},_desiredCoords:function(b){var c,d=null,f=e(this.window),g=b.x,h=b.y,i=b.positionTo;if(i&&"origin"!==i)if("window"===i)g=f.cx/2+f.x,h=f.cy/2+f.y;else{try{d=a(i)}catch(j){d=null}d&&(d.filter(":visible"),0===d.length&&(d=null))}return d&&(c=d.offset(),g=c.left+d.outerWidth()/2,h=c.top+d.outerHeight()/2),("number"!==a.type(g)||isNaN(g))&&(g=f.cx/2+f.x),("number"!==a.type(h)||isNaN(h))&&(h=f.cy/2+f.y),{x:g,y:h}},_reposition:function(a){a={x:a.x,y:a.y,positionTo:a.positionTo},this._trigger("beforeposition",c,a),this._ui.container.offset(this._placementCoords(this._desiredCoords(a)))},reposition:function(a){this._isOpen&&this._reposition(a)},_openPrerequisitesComplete:function(){var a=this.element.attr("id");this._ui.container.addClass("ui-popup-active"),this._isOpen=!0,this._resizeScreen(),this._ui.container.attr("tabindex","0").focus(),this._ignoreResizeEvents(),a&&this.document.find("[aria-haspopup='true'][aria-owns='"+a+"']").attr("aria-expanded",!0),this._trigger("afteropen")},_open:function(b){var c=a.extend({},this.options,b),d=function(){var a=navigator.userAgent,b=a.match(/AppleWebKit\/([0-9\.]+)/),c=!!b&&b[1],d=a.match(/Android (\d+(?:\.\d+))/),e=!!d&&d[1],f=a.indexOf("Chrome")>-1;return null!==d&&"4.0"===e&&c&&c>534.13&&!f?!0:!1}();this._createPrerequisites(a.noop,a.noop,a.proxy(this,"_openPrerequisitesComplete")),this._currentTransition=c.transition,this._applyTransition(c.transition),this._ui.screen.removeClass("ui-screen-hidden"),this._ui.container.removeClass("ui-popup-truncate"),this._reposition(c),this._ui.container.removeClass("ui-popup-hidden"),this.options.overlayTheme&&d&&this.element.closest(".ui-page").addClass("ui-popup-open"),this._animate({additionalCondition:!0,transition:c.transition,classToRemove:"",screenClassToAdd:"in",containerClassToAdd:"in",applyTransition:!1,prerequisites:this._prerequisites})},_closePrerequisiteScreen:function(){this._ui.screen.removeClass("out").addClass("ui-screen-hidden")},_closePrerequisiteContainer:function(){this._ui.container.removeClass("reverse out").addClass("ui-popup-hidden ui-popup-truncate").removeAttr("style")},_closePrerequisitesDone:function(){var b=this._ui.container,d=this.element.attr("id");b.removeAttr("tabindex"),a.mobile.popup.active=c,a(":focus",b[0]).add(b[0]).blur(),d&&this.document.find("[aria-haspopup='true'][aria-owns='"+d+"']").attr("aria-expanded",!1),this._trigger("afterclose")},_close:function(b){this._ui.container.removeClass("ui-popup-active"),this._page.removeClass("ui-popup-open"),this._isOpen=!1,this._createPrerequisites(a.proxy(this,"_closePrerequisiteScreen"),a.proxy(this,"_closePrerequisiteContainer"),a.proxy(this,"_closePrerequisitesDone")),this._animate({additionalCondition:this._ui.screen.hasClass("in"),transition:b?"none":this._currentTransition,classToRemove:"in",screenClassToAdd:"out",containerClassToAdd:"reverse out",applyTransition:!0,prerequisites:this._prerequisites})},_unenhance:function(){this.options.enhanced||(this._setOptions({theme:a.mobile.popup.prototype.options.theme}),this.element.detach().insertAfter(this._ui.placeholder).removeClass("ui-popup ui-overlay-shadow ui-corner-all ui-body-inherit"),this._ui.screen.remove(),this._ui.container.remove(),this._ui.placeholder.remove())},_destroy:function(){return a.mobile.popup.active===this?(this.element.one("popupafterclose",a.proxy(this,"_unenhance")),this.close()):this._unenhance(),this},_closePopup:function(c,d){var e,f,g=this.options,h=!1;c&&c.isDefaultPrevented()||a.mobile.popup.active!==this||(b.scrollTo(0,this._scrollTop),c&&"pagebeforechange"===c.type&&d&&(e="string"==typeof d.toPage?d.toPage:d.toPage.jqmData("url"),e=a.mobile.path.parseUrl(e),f=e.pathname+e.search+e.hash,this._myUrl!==a.mobile.path.makeUrlAbsolute(f)?h=!0:c.preventDefault()),this.window.off(g.closeEvents),this.element.undelegate(g.closeLinkSelector,g.closeLinkEvents),this._close(h))},_bindContainerClose:function(){this.window.on(this.options.closeEvents,a.proxy(this,"_closePopup"))},widget:function(){return this._ui.container},open:function(b){var c,d,e,f,g,h,i=this,j=this.options;return a.mobile.popup.active||j.disabled?this:(a.mobile.popup.active=this,this._scrollTop=this.window.scrollTop(),j.history?(h=a.mobile.navigate.history,d=a.mobile.dialogHashKey,e=a.mobile.activePage,f=e?e.hasClass("ui-dialog"):!1,this._myUrl=c=h.getActive().url,(g=c.indexOf(d)>-1&&!f&&h.activeIndex>0)?(i._open(b),i._bindContainerClose(),this):(-1!==c.indexOf(d)||f?c=a.mobile.path.parseLocation().hash+d:c+=c.indexOf("#")>-1?d:"#"+d,0===h.activeIndex&&c===h.initialDst&&(c+=d),this.window.one("beforenavigate",function(a){a.preventDefault(),i._open(b),i._bindContainerClose()}),this.urlAltered=!0,a.mobile.navigate(c,{role:"dialog"}),this)):(i._open(b),i._bindContainerClose(),i.element.delegate(j.closeLinkSelector,j.closeLinkEvents,function(a){i.close(),a.preventDefault()}),this))},close:function(){return a.mobile.popup.active!==this?this:(this._scrollTop=this.window.scrollTop(),this.options.history&&this.urlAltered?(a.mobile.back(),this.urlAltered=!1):this._closePopup(),this)}}),a.mobile.popup.handleLink=function(b){var c,d=a.mobile.path,e=a(d.hashToSelector(d.parseUrl(b.attr("href")).hash)).first();e.length>0&&e.data("mobile-popup")&&(c=b.offset(),e.popup("open",{x:c.left+b.outerWidth()/2,y:c.top+b.outerHeight()/2,transition:b.jqmData("transition"),positionTo:b.jqmData("position-to")})),setTimeout(function(){b.removeClass(a.mobile.activeBtnClass)},300)},a.mobile.document.on("pagebeforechange",function(b,c){"popup"===c.options.role&&(a.mobile.popup.handleLink(c.options.link),b.preventDefault())})}(a),function(a,b){var d=".ui-disabled,.ui-state-disabled,.ui-li-divider,.ui-screen-hidden,:jqmData(role='placeholder')",e=function(a,b,c){var e=a[c+"All"]().not(d).first();e.length&&(b.blur().attr("tabindex","-1"),e.find("a").first().focus())};a.widget("mobile.selectmenu",a.mobile.selectmenu,{_create:function(){var a=this.options;return a.nativeMenu=a.nativeMenu||this.element.parents(":jqmData(role='popup'),:mobile-popup").length>0,this._super()},_handleSelectFocus:function(){this.element.blur(),this.button.focus()},_handleKeydown:function(a){this._super(a),this._handleButtonVclickKeydown(a)},_handleButtonVclickKeydown:function(b){this.options.disabled||this.isOpen||("vclick"===b.type||b.keyCode&&(b.keyCode===a.mobile.keyCode.ENTER||b.keyCode===a.mobile.keyCode.SPACE))&&(this._decideFormat(),"overlay"===this.menuType?this.button.attr("href","#"+this.popupId).attr("data-"+(a.mobile.ns||"")+"rel","popup"):this.button.attr("href","#"+this.dialogId).attr("data-"+(a.mobile.ns||"")+"rel","dialog"),this.isOpen=!0)},_handleListFocus:function(b){var c="focusin"===b.type?{tabindex:"0",event:"vmouseover"}:{tabindex:"-1",event:"vmouseout"};a(b.target).attr("tabindex",c.tabindex).trigger(c.event)},_handleListKeydown:function(b){var c=a(b.target),d=c.closest("li");switch(b.keyCode){case 38:return e(d,c,"prev"),!1;case 40:return e(d,c,"next"),!1;case 13:case 32:return c.trigger("click"),!1}},_handleMenuPageHide:function(){this.thisPage.page("bindRemove")},_handleHeaderCloseClick:function(){return"overlay"===this.menuType?(this.close(),!1):void 0},build:function(){var c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v=this.options;return v.nativeMenu?this._super():(u=this,c=this.selectId,d=c+"-listbox",e=c+"-dialog",f=this.label,g=this.element.closest(".ui-page"),h=this.element[0].multiple,i=c+"-menu",j=v.theme?" data-"+a.mobile.ns+"theme='"+v.theme+"'":"",k=v.overlayTheme?" data-"+a.mobile.ns+"theme='"+v.overlayTheme+"'":"",l=v.dividerTheme&&h?" data-"+a.mobile.ns+"divider-theme='"+v.dividerTheme+"'":"",m=a("<div data-"+a.mobile.ns+"role='dialog' class='ui-selectmenu' id='"+e+"'"+j+k+">"+"<div data-"+a.mobile.ns+"role='header'>"+"<div class='ui-title'>"+f.getEncodedText()+"</div>"+"</div>"+"<div data-"+a.mobile.ns+"role='content'></div>"+"</div>"),n=a("<div id='"+d+"' class='ui-selectmenu'>").insertAfter(this.select).popup({theme:v.overlayTheme}),o=a("<ul class='ui-selectmenu-list' id='"+i+"' role='listbox' aria-labelledby='"+this.buttonId+"'"+j+l+">").appendTo(n),p=a("<div class='ui-header ui-bar-"+(v.theme?v.theme:"inherit")+"'>").prependTo(n),q=a("<h1 class='ui-title'>").appendTo(p),this.isMultiple&&(t=a("<a>",{role:"button",text:v.closeText,href:"#","class":"ui-btn ui-corner-all ui-btn-left ui-btn-icon-notext ui-icon-delete"}).appendTo(p)),a.extend(this,{selectId:c,menuId:i,popupId:d,dialogId:e,thisPage:g,menuPage:m,label:f,isMultiple:h,theme:v.theme,listbox:n,list:o,header:p,headerTitle:q,headerClose:t,menuPageContent:r,menuPageClose:s,placeholder:""}),this.refresh(),this._origTabIndex===b&&(this._origTabIndex=null===this.select[0].getAttribute("tabindex")?!1:this.select.attr("tabindex")),this.select.attr("tabindex","-1"),this._on(this.select,{focus:"_handleSelectFocus"}),this._on(this.button,{vclick:"_handleButtonVclickKeydown"}),this.list.attr("role","listbox"),this._on(this.list,{focusin:"_handleListFocus",focusout:"_handleListFocus",keydown:"_handleListKeydown"}),this.list.delegate("li:not(.ui-disabled,.ui-state-disabled,.ui-li-divider)","click",function(b){var c=u.select[0].selectedIndex,d=a.mobile.getAttribute(this,"option-index"),e=u._selectOptions().eq(d)[0];e.selected=u.isMultiple?!e.selected:!0,u.isMultiple&&a(this).find("a").toggleClass("ui-checkbox-on",e.selected).toggleClass("ui-checkbox-off",!e.selected),(u.isMultiple||c!==d)&&u.select.trigger("change"),u.isMultiple?u.list.find("li:not(.ui-li-divider)").eq(d).find("a").first().focus():u.close(),b.preventDefault()}),this._on(this.menuPage,{pagehide:"_handleMenuPageHide"}),this._on(this.listbox,{popupafterclose:"close"}),this.isMultiple&&this._on(this.headerClose,{click:"_handleHeaderCloseClick"}),this)},_isRebuildRequired:function(){var a=this.list.find("li"),b=this._selectOptions().not(".ui-screen-hidden");return b.text()!==a.text()},selected:function(){return this._selectOptions().filter(":selected:not( :jqmData(placeholder='true') )")},refresh:function(b){var c,d;return this.options.nativeMenu?this._super(b):(c=this,(b||this._isRebuildRequired())&&c._buildList(),d=this.selectedIndices(),c.setButtonText(),c.setButtonCount(),c.list.find("li:not(.ui-li-divider)").find("a").removeClass(a.mobile.activeBtnClass).end().attr("aria-selected",!1).each(function(b){if(a.inArray(b,d)>-1){var e=a(this);e.attr("aria-selected",!0),c.isMultiple?e.find("a").removeClass("ui-checkbox-off").addClass("ui-checkbox-on"):e.hasClass("ui-screen-hidden")?e.next().find("a").addClass(a.mobile.activeBtnClass):e.find("a").addClass(a.mobile.activeBtnClass)}}),void 0)},close:function(){if(!this.options.disabled&&this.isOpen){var a=this;"page"===a.menuType?(a.menuPage.dialog("close"),a.list.appendTo(a.listbox)):a.listbox.popup("close"),a._focusButton(),a.isOpen=!1}},open:function(){this.button.click()},_focusMenuItem:function(){var b=this.list.find("a."+a.mobile.activeBtnClass);0===b.length&&(b=this.list.find("li:not("+d+") a.ui-btn")),b.first().focus()},_decideFormat:function(){var b=this,c=this.window,d=b.list.parent(),e=d.outerHeight(),f=c.scrollTop(),g=b.button.offset().top,h=c.height();e>h-80||!a.support.scrollTop?(b.menuPage.appendTo(a.mobile.pageContainer).page(),b.menuPageContent=b.menuPage.find(".ui-content"),b.menuPageClose=b.menuPage.find(".ui-header a"),b.thisPage.unbind("pagehide.remove"),0===f&&g>h&&b.thisPage.one("pagehide",function(){a(this).jqmData("lastScroll",g)}),b.menuPage.one({pageshow:a.proxy(this,"_focusMenuItem"),pagehide:a.proxy(this,"close")}),b.menuType="page",b.menuPageContent.append(b.list),b.menuPage.find("div .ui-title").text(b.label.text())):(b.menuType="overlay",b.listbox.one({popupafteropen:a.proxy(this,"_focusMenuItem")}))},_buildList:function(){var b,d,e,f,g,h,i,j,k,l,m,n,o,p,q=this,r=this.options,s=this.placeholder,t=!0,u="false",v="data-"+a.mobile.ns,w=v+"option-index",x=v+"icon",y=v+"role",z=v+"placeholder",A=c.createDocumentFragment(),B=!1;for(q.list.empty().filter(".ui-listview").listview("destroy"),b=this._selectOptions(),d=b.length,e=this.select[0],g=0;d>g;g++,B=!1)h=b[g],i=a(h),i.hasClass("ui-screen-hidden")||(j=h.parentNode,k=i.text(),l=c.createElement("a"),m=[],l.setAttribute("href","#"),l.appendChild(c.createTextNode(k)),j!==e&&"optgroup"===j.nodeName.toLowerCase()&&(n=j.getAttribute("label"),n!==f&&(o=c.createElement("li"),o.setAttribute(y,"list-divider"),o.setAttribute("role","option"),o.setAttribute("tabindex","-1"),o.appendChild(c.createTextNode(n)),A.appendChild(o),f=n)),!t||h.getAttribute("value")&&0!==k.length&&!i.jqmData("placeholder")||(t=!1,B=!0,null===h.getAttribute(z)&&(this._removePlaceholderAttr=!0),h.setAttribute(z,!0),r.hidePlaceholderMenuItems&&m.push("ui-screen-hidden"),s!==k&&(s=q.placeholder=k)),p=c.createElement("li"),h.disabled&&(m.push("ui-state-disabled"),p.setAttribute("aria-disabled",!0)),p.setAttribute(w,g),p.setAttribute(x,u),B&&p.setAttribute(z,!0),p.className=m.join(" "),p.setAttribute("role","option"),l.setAttribute("tabindex","-1"),this.isMultiple&&a(l).addClass("ui-btn ui-checkbox-off ui-btn-icon-right"),p.appendChild(l),A.appendChild(p));q.list[0].appendChild(A),this.isMultiple||s.length?this.headerTitle.text(this.placeholder):this.header.addClass("ui-screen-hidden"),q.list.listview()},_button:function(){return this.options.nativeMenu?this._super():a("<a>",{href:"#",role:"button",id:this.buttonId,"aria-haspopup":"true","aria-owns":this.menuId})},_destroy:function(){this.options.nativeMenu||(this.close(),this._origTabIndex!==b&&(this._origTabIndex!==!1?this.select.attr("tabindex",this._origTabIndex):this.select.removeAttr("tabindex")),this._removePlaceholderAttr&&this._selectOptions().removeAttr("data-"+a.mobile.ns+"placeholder"),this.listbox.remove(),this.menuPage.remove()),this._super()}})}(a),function(a,b){function c(a,b){var c=b?b:[];return c.push("ui-btn"),a.theme&&c.push("ui-btn-"+a.theme),a.icon&&(c=c.concat(["ui-icon-"+a.icon,"ui-btn-icon-"+a.iconpos]),a.iconshadow&&c.push("ui-shadow-icon")),a.inline&&c.push("ui-btn-inline"),a.shadow&&c.push("ui-shadow"),a.corners&&c.push("ui-corner-all"),a.mini&&c.push("ui-mini"),c}function d(a){var c,d,e,g=!1,h=!0,i={icon:"",inline:!1,shadow:!1,corners:!1,iconshadow:!1,mini:!1},j=[];for(a=a.split(" "),c=0;c<a.length;c++)e=!0,d=f[a[c]],d!==b?(e=!1,i[d]=!0):0===a[c].indexOf("ui-btn-icon-")?(e=!1,h=!1,i.iconpos=a[c].substring(12)):0===a[c].indexOf("ui-icon-")?(e=!1,i.icon=a[c].substring(8)):0===a[c].indexOf("ui-btn-")&&8===a[c].length?(e=!1,i.theme=a[c].substring(7)):"ui-btn"===a[c]&&(e=!1,g=!0),e&&j.push(a[c]);return h&&(i.icon=""),{options:i,unknownClasses:j,alreadyEnhanced:g}}function e(a){return"-"+a.toLowerCase()}var f={"ui-shadow":"shadow","ui-corner-all":"corners","ui-btn-inline":"inline","ui-shadow-icon":"iconshadow","ui-mini":"mini"},g=function(){var c=a.mobile.getAttribute.apply(this,arguments);return null==c?b:c},h=/[A-Z]/g;a.fn.buttonMarkup=function(f,i){var j,k,l,m,n,o=a.fn.buttonMarkup.defaults;for(j=0;j<this.length;j++){if(l=this[j],k=i?{alreadyEnhanced:!1,unknownClasses:[]}:d(l.className),m=a.extend({},k.alreadyEnhanced?k.options:{},f),!k.alreadyEnhanced)for(n in o)m[n]===b&&(m[n]=g(l,n.replace(h,e)));l.className=c(a.extend({},o,m),k.unknownClasses).join(" "),"button"!==l.tagName.toLowerCase()&&l.setAttribute("role","button")}return this},a.fn.buttonMarkup.defaults={icon:"",iconpos:"left",theme:null,inline:!1,shadow:!0,corners:!0,iconshadow:!1,mini:!1},a.extend(a.fn.buttonMarkup,{initSelector:"a:jqmData(role='button'), .ui-bar > a, .ui-bar > :jqmData(role='controlgroup') > a, button"})}(a),function(a,b){a.widget("mobile.controlgroup",a.extend({options:{enhanced:!1,theme:null,shadow:!1,corners:!0,excludeInvisible:!0,type:"vertical",mini:!1},_create:function(){var b=this.element,c=this.options;a.fn.buttonMarkup&&this.element.find(a.fn.buttonMarkup.initSelector).buttonMarkup(),a.each(this._childWidgets,a.proxy(function(b,c){a.mobile[c]&&this.element.find(a.mobile[c].initSelector).not(a.mobile.page.prototype.keepNativeSelector())[c]()},this)),a.extend(this,{_ui:null,_initialRefresh:!0}),this._ui=c.enhanced?{groupLegend:b.children(".ui-controlgroup-label").children(),childWrapper:b.children(".ui-controlgroup-controls")}:this._enhance()},_childWidgets:["checkboxradio","selectmenu","button"],_themeClassFromOption:function(a){return a?"none"===a?"":"ui-group-theme-"+a:""},_enhance:function(){var b=this.element,c=this.options,d={groupLegend:b.children("legend"),childWrapper:b.addClass("ui-controlgroup ui-controlgroup-"+("horizontal"===c.type?"horizontal":"vertical")+" "+this._themeClassFromOption(c.theme)+" "+(c.corners?"ui-corner-all ":"")+(c.mini?"ui-mini ":"")).wrapInner("<div class='ui-controlgroup-controls "+(c.shadow===!0?"ui-shadow":"")+"'></div>").children()};return d.groupLegend.length>0&&a("<div role='heading' class='ui-controlgroup-label'></div>").append(d.groupLegend).prependTo(b),d},_init:function(){this.refresh()},_setOptions:function(a){var c,d,e=this.element;return a.type!==b&&(e.removeClass("ui-controlgroup-horizontal ui-controlgroup-vertical").addClass("ui-controlgroup-"+("horizontal"===a.type?"horizontal":"vertical")),c=!0),a.theme!==b&&e.removeClass(this._themeClassFromOption(this.options.theme)).addClass(this._themeClassFromOption(a.theme)),a.corners!==b&&e.toggleClass("ui-corner-all",a.corners),a.mini!==b&&e.toggleClass("ui-mini",a.mini),a.shadow!==b&&this._ui.childWrapper.toggleClass("ui-shadow",a.shadow),a.excludeInvisible!==b&&(this.options.excludeInvisible=a.excludeInvisible,c=!0),d=this._super(a),c&&this.refresh(),d},container:function(){return this._ui.childWrapper},refresh:function(){var b=this.container(),c=b.find(".ui-btn").not(".ui-slider-handle"),d=this._initialRefresh;a.mobile.checkboxradio&&b.find(":mobile-checkboxradio").checkboxradio("refresh"),this._addFirstLastClasses(c,this.options.excludeInvisible?this._getVisibles(c,d):c,d),this._initialRefresh=!1},_destroy:function(){var a,b,c=this.options;return c.enhanced?this:(a=this._ui,b=this.element.removeClass("ui-controlgroup ui-controlgroup-horizontal ui-controlgroup-vertical ui-corner-all ui-mini "+this._themeClassFromOption(c.theme)).find(".ui-btn").not(".ui-slider-handle"),this._removeFirstLastClasses(b),a.groupLegend.unwrap(),a.childWrapper.children().unwrap(),void 0)}},a.mobile.behaviors.addFirstLastClasses))}(a),function(a,b){a.widget("mobile.toolbar",{initSelector:":jqmData(role='footer'), :jqmData(role='header')",options:{theme:null,addBackBtn:!1,backBtnTheme:null,backBtnText:"Back"},_create:function(){var b,c,d=this.element.is(":jqmData(role='header')")?"header":"footer",e=this.element.closest(".ui-page");0===e.length&&(e=!1,this._on(this.document,{pageshow:"refresh"})),a.extend(this,{role:d,page:e,leftbtn:b,rightbtn:c,backBtn:null}),this.element.attr("role","header"===d?"banner":"contentinfo").addClass("ui-"+d),this.refresh(),this._setOptions(this.options)},_setOptions:function(c){if(c.addBackBtn!==b&&(this.options.addBackBtn&&"header"===this.role&&a(".ui-page").length>1&&this.page[0].getAttribute("data-"+a.mobile.ns+"url")!==a.mobile.path.stripHash(location.hash)&&!this.leftbtn?this._addBackButton():this.element.find(".ui-toolbar-back-btn").remove()),null!=c.backBtnTheme&&this.element.find(".ui-toolbar-back-btn").addClass("ui-btn ui-btn-"+c.backBtnTheme),c.backBtnText!==b&&this.element.find(".ui-toolbar-back-btn .ui-btn-text").text(c.backBtnText),c.theme!==b){var d=this.options.theme?this.options.theme:"inherit",e=c.theme?c.theme:"inherit";this.element.removeClass("ui-bar-"+d).addClass("ui-bar-"+e)}this._super(c)},refresh:function(){"header"===this.role&&this._addHeaderButtonClasses(),this.page||(this._setRelative(),"footer"===this.role&&this.element.appendTo("body")),this._addHeadingClasses(),this._btnMarkup()},_setRelative:function(){a("[data-"+a.mobile.ns+"role='page']").css({position:"relative"})},_btnMarkup:function(){this.element.children("a").attr("data-"+a.mobile.ns+"role","button"),this.element.trigger("create")},_addHeaderButtonClasses:function(){var a=this.element.children("a, button");this.leftbtn=a.hasClass("ui-btn-left"),this.rightbtn=a.hasClass("ui-btn-right"),this.leftbtn=this.leftbtn||a.eq(0).not(".ui-btn-right").addClass("ui-btn-left").length,this.rightbtn=this.rightbtn||a.eq(1).addClass("ui-btn-right").length},_addBackButton:function(){var b,c=this.options;this.backBtn||(b=c.backBtnTheme||c.theme,this.backBtn=a("<a role='button' href='javascript:void(0);' class='ui-btn ui-corner-all ui-shadow ui-btn-left "+(b?"ui-btn-"+b+" ":"")+"ui-toolbar-back-btn ui-icon-carat-l ui-btn-icon-left' "+"data-"+a.mobile.ns+"rel='back'>"+c.backBtnText+"</a>").prependTo(this.element))},_addHeadingClasses:function(){this.element.children("h1, h2, h3, h4, h5, h6").addClass("ui-title").attr({role:"heading","aria-level":"1"})}})}(a),function(a,b){a.widget("mobile.toolbar",a.mobile.toolbar,{options:{position:null,visibleOnPageShow:!0,disablePageZoom:!0,transition:"slide",fullscreen:!1,tapToggle:!0,tapToggleBlacklist:"a, button, input, select, textarea, .ui-header-fixed, .ui-footer-fixed, .ui-flipswitch, .ui-popup, .ui-panel, .ui-panel-dismiss-open",hideDuringFocus:"input, textarea, select",updatePagePadding:!0,trackPersistentToolbars:!0,supportBlacklist:function(){return!a.support.fixedPosition}},_create:function(){this._super(),"fixed"!==this.options.position||this.options.supportBlacklist()||this._makeFixed()},_makeFixed:function(){this.element.addClass("ui-"+this.role+"-fixed"),this.updatePagePadding(),this._addTransitionClass(),this._bindPageEvents(),this._bindToggleHandlers(),this._setOptions(this.options)},_setOptions:function(c){if("fixed"===c.position&&"fixed"!==this.options.position&&this._makeFixed(),"fixed"===this.options.position&&!this.options.supportBlacklist()){var d=this.page?this.page:a(".ui-page-active").length>0?a(".ui-page-active"):a(".ui-page").eq(0);c.fullscreen!==b&&(c.fullscreen?(this.element.addClass("ui-"+this.role+"-fullscreen"),d.addClass("ui-page-"+this.role+"-fullscreen")):(this.element.removeClass("ui-"+this.role+"-fullscreen"),d.removeClass("ui-page-"+this.role+"-fullscreen").addClass("ui-page-"+this.role+"-fixed")))}this._super(c)},_addTransitionClass:function(){var a=this.options.transition;a&&"none"!==a&&("slide"===a&&(a=this.element.hasClass("ui-header")?"slidedown":"slideup"),this.element.addClass(a))},_bindPageEvents:function(){var a=this.page?this.element.closest(".ui-page"):this.document;this._on(a,{pagebeforeshow:"_handlePageBeforeShow",webkitAnimationStart:"_handleAnimationStart",animationstart:"_handleAnimationStart",updatelayout:"_handleAnimationStart",pageshow:"_handlePageShow",pagebeforehide:"_handlePageBeforeHide"})},_handlePageBeforeShow:function(){var b=this.options;b.disablePageZoom&&a.mobile.zoom.disable(!0),b.visibleOnPageShow||this.hide(!0)},_handleAnimationStart:function(){this.options.updatePagePadding&&this.updatePagePadding(this.page?this.page:".ui-page-active")},_handlePageShow:function(){this.updatePagePadding(this.page?this.page:".ui-page-active"),this.options.updatePagePadding&&this._on(this.window,{throttledresize:"updatePagePadding"})},_handlePageBeforeHide:function(b,c){var d,e,f,g,h=this.options;h.disablePageZoom&&a.mobile.zoom.enable(!0),h.updatePagePadding&&this._off(this.window,"throttledresize"),h.trackPersistentToolbars&&(d=a(".ui-footer-fixed:jqmData(id)",this.page),e=a(".ui-header-fixed:jqmData(id)",this.page),f=d.length&&c.nextPage&&a(".ui-footer-fixed:jqmData(id='"+d.jqmData("id")+"')",c.nextPage)||a(),g=e.length&&c.nextPage&&a(".ui-header-fixed:jqmData(id='"+e.jqmData("id")+"')",c.nextPage)||a(),(f.length||g.length)&&(f.add(g).appendTo(a.mobile.pageContainer),c.nextPage.one("pageshow",function(){g.prependTo(this),f.appendTo(this)})))},_visible:!0,updatePagePadding:function(c){var d=this.element,e="header"===this.role,f=parseFloat(d.css(e?"top":"bottom"));this.options.fullscreen||(c=c&&c.type===b&&c||this.page||d.closest(".ui-page"),c=this.page?this.page:".ui-page-active",a(c).css("padding-"+(e?"top":"bottom"),d.outerHeight()+f))},_useTransition:function(b){var c=this.window,d=this.element,e=c.scrollTop(),f=d.height(),g=this.page?d.closest(".ui-page").height():a(".ui-page-active").height(),h=a.mobile.getScreenHeight();return!b&&(this.options.transition&&"none"!==this.options.transition&&("header"===this.role&&!this.options.fullscreen&&e>f||"footer"===this.role&&!this.options.fullscreen&&g-f>e+h)||this.options.fullscreen)},show:function(a){var b="ui-fixed-hidden",c=this.element;this._useTransition(a)?c.removeClass("out "+b).addClass("in").animationComplete(function(){c.removeClass("in")}):c.removeClass(b),this._visible=!0},hide:function(a){var b="ui-fixed-hidden",c=this.element,d="out"+("slide"===this.options.transition?" reverse":"");this._useTransition(a)?c.addClass(d).removeClass("in").animationComplete(function(){c.addClass(b).removeClass(d)}):c.addClass(b).removeClass(d),this._visible=!1},toggle:function(){this[this._visible?"hide":"show"]()},_bindToggleHandlers:function(){var b,c,d=this,e=d.options,f=!0,g=this.page?this.page:a(".ui-page");g.bind("vclick",function(b){e.tapToggle&&!a(b.target).closest(e.tapToggleBlacklist).length&&d.toggle()}).bind("focusin focusout",function(g){screen.width<1025&&a(g.target).is(e.hideDuringFocus)&&!a(g.target).closest(".ui-header-fixed, .ui-footer-fixed").length&&("focusout"!==g.type||f?"focusin"===g.type&&f&&(clearTimeout(b),f=!1,c=setTimeout(function(){d.hide()},0)):(f=!0,clearTimeout(c),b=setTimeout(function(){d.show()},0)))})},_setRelative:function(){"fixed"!==this.options.position&&a("[data-"+a.mobile.ns+"role='page']").css({position:"relative"})},_destroy:function(){var a=this.element,b=a.hasClass("ui-header");a.closest(".ui-page").css("padding-"+(b?"top":"bottom"),""),a.removeClass("ui-header-fixed ui-footer-fixed ui-header-fullscreen ui-footer-fullscreen in out fade slidedown slideup ui-fixed-hidden"),a.closest(".ui-page").removeClass("ui-page-header-fixed ui-page-footer-fixed ui-page-header-fullscreen ui-page-footer-fullscreen")}})}(a),function(a){a.widget("mobile.toolbar",a.mobile.toolbar,{_makeFixed:function(){this._super(),this._workarounds()},_workarounds:function(){var a=navigator.userAgent,b=navigator.platform,c=a.match(/AppleWebKit\/([0-9]+)/),d=!!c&&c[1],e=null,f=this;if(b.indexOf("iPhone")>-1||b.indexOf("iPad")>-1||b.indexOf("iPod")>-1)e="ios";else{if(!(a.indexOf("Android")>-1))return;e="android"}if("ios"===e)f._bindScrollWorkaround();else{if(!("android"===e&&d&&534>d))return;f._bindScrollWorkaround(),f._bindListThumbWorkaround()}},_viewportOffset:function(){var a=this.element,b=a.hasClass("ui-header"),c=Math.abs(a.offset().top-this.window.scrollTop());return b||(c=Math.round(c-this.window.height()+a.outerHeight())-60),c},_bindScrollWorkaround:function(){var a=this;this._on(this.window,{scrollstop:function(){var b=a._viewportOffset();b>2&&a._visible&&a._triggerRedraw()}})},_bindListThumbWorkaround:function(){this.element.closest(".ui-page").addClass("ui-android-2x-fixed")},_triggerRedraw:function(){var b=parseFloat(a(".ui-page-active").css("padding-bottom"));a(".ui-page-active").css("padding-bottom",b+1+"px"),setTimeout(function(){a(".ui-page-active").css("padding-bottom",b+"px")},0)},destroy:function(){this._super(),this.element.closest(".ui-page-active").removeClass("ui-android-2x-fix")}})}(a),function(a,b){function c(){var a=e.clone(),b=a.eq(0),c=a.eq(1),d=c.children(),f=d.children();return{arEls:c.add(b),gd:b,ct:c,ar:d,bg:f}}var d=a.mobile.browser.oldIE&&a.mobile.browser.oldIE<=8,e=a("<div class='ui-popup-arrow-guide'></div><div class='ui-popup-arrow-container"+(d?" ie":"")+"'>"+"<div class='ui-popup-arrow'>"+"<div class='ui-popup-arrow-background'></div>"+"</div>"+"</div>"),f=Math.sqrt(2)/2;a.widget("mobile.popup",a.mobile.popup,{options:{arrow:""},_create:function(){var a,b=this._super();return this.options.arrow&&(this._ui.arrow=a=this._addArrow()),b},_addArrow:function(){var a,b=this.options,d=c();return a=this._themeClassFromOption("ui-body-",b.theme),d.ar.addClass(a+(b.shadow?" ui-overlay-shadow":"")),d.bg.addClass(a),d.arEls.hide().appendTo(this.element),d},_unenhance:function(){var a=this._ui.arrow;return a&&a.arEls.remove(),this._super()},_tryAnArrow:function(a,b,c,d,e){var f,g,h,i={},j={};return d.arFull[a.dimKey]>d.guideDims[a.dimKey]?e:(i[a.fst]=c[a.fst]+(d.arHalf[a.oDimKey]+d.menuHalf[a.oDimKey])*a.offsetFactor-d.contentBox[a.fst]+(d.clampInfo.menuSize[a.oDimKey]-d.contentBox[a.oDimKey])*a.arrowOffsetFactor,i[a.snd]=c[a.snd],f=d.result||this._calculateFinalLocation(i,d.clampInfo),g={x:f.left,y:f.top},j[a.fst]=g[a.fst]+d.contentBox[a.fst]+a.tipOffset,j[a.snd]=Math.max(f[a.prop]+d.guideOffset[a.prop]+d.arHalf[a.dimKey],Math.min(f[a.prop]+d.guideOffset[a.prop]+d.guideDims[a.dimKey]-d.arHalf[a.dimKey],c[a.snd])),h=Math.abs(c.x-j.x)+Math.abs(c.y-j.y),(!e||h<e.diff)&&(j[a.snd]-=d.arHalf[a.dimKey]+f[a.prop]+d.contentBox[a.snd],e={dir:b,diff:h,result:f,posProp:a.prop,posVal:j[a.snd]}),e)},_getPlacementState:function(a){var b,c,d=this._ui.arrow,e={clampInfo:this._clampPopupWidth(!a),arFull:{cx:d.ct.width(),cy:d.ct.height()},guideDims:{cx:d.gd.width(),cy:d.gd.height()},guideOffset:d.gd.offset()};return b=this.element.offset(),d.gd.css({left:0,top:0,right:0,bottom:0}),c=d.gd.offset(),e.contentBox={x:c.left-b.left,y:c.top-b.top,cx:d.gd.width(),cy:d.gd.height()},d.gd.removeAttr("style"),e.guideOffset={left:e.guideOffset.left-b.left,top:e.guideOffset.top-b.top},e.arHalf={cx:e.arFull.cx/2,cy:e.arFull.cy/2},e.menuHalf={cx:e.clampInfo.menuSize.cx/2,cy:e.clampInfo.menuSize.cy/2},e},_placementCoords:function(b){var c,e,g,h,i,j,k,l=this.options.arrow,m=this._ui.arrow;return m?(m.arEls.show(),k={},c=this._getPlacementState(!0),g={l:{fst:"x",snd:"y",prop:"top",dimKey:"cy",oDimKey:"cx",offsetFactor:1,tipOffset:-c.arHalf.cx,arrowOffsetFactor:0},r:{fst:"x",snd:"y",prop:"top",dimKey:"cy",oDimKey:"cx",offsetFactor:-1,tipOffset:c.arHalf.cx+c.contentBox.cx,arrowOffsetFactor:1},b:{fst:"y",snd:"x",prop:"left",dimKey:"cx",oDimKey:"cy",offsetFactor:-1,tipOffset:c.arHalf.cy+c.contentBox.cy,arrowOffsetFactor:1},t:{fst:"y",snd:"x",prop:"left",dimKey:"cx",oDimKey:"cy",offsetFactor:1,tipOffset:-c.arHalf.cy,arrowOffsetFactor:0}},a.each((l===!0?"l,t,r,b":l).split(","),a.proxy(function(a,d){e=this._tryAnArrow(g[d],d,b,c,e)},this)),e?(m.ct.removeClass("ui-popup-arrow-l ui-popup-arrow-t ui-popup-arrow-r ui-popup-arrow-b").addClass("ui-popup-arrow-"+e.dir).removeAttr("style").css(e.posProp,e.posVal).show(),d||(i=this.element.offset(),k[g[e.dir].fst]=m.ct.offset(),k[g[e.dir].snd]={left:i.left+c.contentBox.x,top:i.top+c.contentBox.y},h=m.bg.removeAttr("style").css("cx"===g[e.dir].dimKey?"width":"height",c.contentBox[g[e.dir].dimKey]).offset(),j={dx:k.x.left-h.left,dy:k.y.top-h.top},m.bg.css({left:f*j.dy+f*j.dx,top:f*j.dy-f*j.dx})),e.result):(m.arEls.hide(),this._super(b))):this._super(b)},_setOptions:function(a){var c,d=this.options.theme,e=this._ui.arrow,f=this._super(a);if(a.arrow!==b){if(!e&&a.arrow)return this._ui.arrow=this._addArrow(),void 0;e&&!a.arrow&&(e.arEls.remove(),this._ui.arrow=null)}return e=this._ui.arrow,e&&(a.theme!==b&&(d=this._themeClassFromOption("ui-body-",d),c=this._themeClassFromOption("ui-body-",a.theme),e.ar.removeClass(d).addClass(c),e.bg.removeClass(d).addClass(c)),a.shadow!==b&&e.ar.toggleClass("ui-overlay-shadow",a.shadow)),f},_destroy:function(){var a=this._ui.arrow;return a&&a.arEls.remove(),this._super()}})}(a),function(a,c){a.widget("mobile.panel",{options:{classes:{panel:"ui-panel",panelOpen:"ui-panel-open",panelClosed:"ui-panel-closed",panelFixed:"ui-panel-fixed",panelInner:"ui-panel-inner",modal:"ui-panel-dismiss",modalOpen:"ui-panel-dismiss-open",pageContainer:"ui-panel-page-container",pageWrapper:"ui-panel-wrapper",pageFixedToolbar:"ui-panel-fixed-toolbar",pageContentPrefix:"ui-panel-page-content",animate:"ui-panel-animate"},animate:!0,theme:null,position:"left",dismissible:!0,display:"reveal",swipeClose:!0,positionFixed:!1},_panelID:null,_closeLink:null,_parentPage:null,_page:null,_modal:null,_panelInner:null,_wrapper:null,_fixedToolbars:null,_create:function(){var b=this.element,c=b.closest(":jqmData(role='page')");
+a.extend(this,{_panelID:b.attr("id"),_closeLink:b.find(":jqmData(rel='close')"),_parentPage:c.length>0?c:!1,_page:this._getPage,_panelInner:this._getPanelInner(),_wrapper:this._getWrapper,_fixedToolbars:this._getFixedToolbars}),this._addPanelClasses(),a.support.cssTransform3d&&this.options.animate&&this.element.addClass(this.options.classes.animate),this._bindUpdateLayout(),this._bindCloseEvents(),this._bindLinkListeners(),this._bindPageEvents(),this.options.dismissible&&this._createModal(),this._bindSwipeEvents()},_getPanelInner:function(){var a=this.element.find("."+this.options.classes.panelInner);return 0===a.length&&(a=this.element.children().wrapAll("<div class='"+this.options.classes.panelInner+"' />").parent()),a},_createModal:function(){var b=this,c=b._parentPage?b._parentPage.parent():b.element.parent();b._modal=a("<div class='"+b.options.classes.modal+"' data-panelid='"+b._panelID+"'></div>").on("mousedown",function(){b.close()}).appendTo(c)},_getPage:function(){var b=this._parentPage?this._parentPage:a("."+a.mobile.activePageClass);return b},_getWrapper:function(){var a=this._page().find("."+this.options.classes.pageWrapper);return 0===a.length&&(a=this._page().children(".ui-header:not(.ui-header-fixed), .ui-content:not(.ui-popup), .ui-footer:not(.ui-footer-fixed)").wrapAll("<div class='"+this.options.classes.pageWrapper+"'></div>").parent()),a},_getFixedToolbars:function(){var b=a("body").children(".ui-header-fixed, .ui-footer-fixed"),c=this._page().find(".ui-header-fixed, .ui-footer-fixed"),d=b.add(c).addClass(this.options.classes.pageFixedToolbar);return d},_getPosDisplayClasses:function(a){return a+"-position-"+this.options.position+" "+a+"-display-"+this.options.display},_getPanelClasses:function(){var a=this.options.classes.panel+" "+this._getPosDisplayClasses(this.options.classes.panel)+" "+this.options.classes.panelClosed+" "+"ui-body-"+(this.options.theme?this.options.theme:"inherit");return this.options.positionFixed&&(a+=" "+this.options.classes.panelFixed),a},_addPanelClasses:function(){this.element.addClass(this._getPanelClasses())},_bindCloseEvents:function(){var a=this;a._closeLink.on("click.panel",function(b){return b.preventDefault(),a.close(),!1}),a.element.on("click.panel","a:jqmData(ajax='false')",function(){a.close()})},_positionPanel:function(){var c=this,d=c._panelInner.outerHeight(),e=d>a.mobile.getScreenHeight();e||!c.options.positionFixed?(e&&(c._unfixPanel(),a.mobile.resetActivePageHeight(d)),b.scrollTo(0,a.mobile.defaultHomeScroll)):c._fixPanel()},_bindFixListener:function(){this._on(a(b),{throttledresize:"_positionPanel"})},_unbindFixListener:function(){this._off(a(b),"throttledresize")},_unfixPanel:function(){this.options.positionFixed&&a.support.fixedPosition&&this.element.removeClass(this.options.classes.panelFixed)},_fixPanel:function(){this.options.positionFixed&&a.support.fixedPosition&&this.element.addClass(this.options.classes.panelFixed)},_bindUpdateLayout:function(){var a=this;a.element.on("updatelayout",function(){a._open&&a._positionPanel()})},_bindLinkListeners:function(){this._on("body",{"click a":"_handleClick"})},_handleClick:function(b){if(b.currentTarget.href.split("#")[1]===this._panelID&&this._panelID!==c){b.preventDefault();var d=a(b.target);return d.hasClass("ui-btn")&&(d.addClass(a.mobile.activeBtnClass),this.element.one("panelopen panelclose",function(){d.removeClass(a.mobile.activeBtnClass)})),this.toggle(),!1}},_bindSwipeEvents:function(){var a=this,b=a._modal?a.element.add(a._modal):a.element;a.options.swipeClose&&("left"===a.options.position?b.on("swipeleft.panel",function(){a.close()}):b.on("swiperight.panel",function(){a.close()}))},_bindPageEvents:function(){var a=this;this.document.on("panelbeforeopen",function(b){a._open&&b.target!==a.element[0]&&a.close()}).on("keyup.panel",function(b){27===b.keyCode&&a._open&&a.close()}),a._parentPage?this.document.on("pagehide",":jqmData(role='page')",function(){a._open&&a.close(!0)}):this.document.on("pagebeforehide",function(){a._open&&a.close(!0)})},_open:!1,_pageContentOpenClasses:null,_modalOpenClasses:null,open:function(b){if(!this._open){var c=this,d=c.options,e=function(){c.document.off("panelclose"),c._page().jqmData("panel","open"),a.support.cssTransform3d&&d.animate&&"overlay"!==d.display&&(c._wrapper().addClass(d.classes.animate),c._fixedToolbars().addClass(d.classes.animate)),!b&&a.support.cssTransform3d&&d.animate?c.document.on(c._transitionEndEvents,f):setTimeout(f,0),d.theme&&"overlay"!==d.display&&c._page().parent().addClass(d.classes.pageContainer+"-themed "+d.classes.pageContainer+"-"+d.theme),c.element.removeClass(d.classes.panelClosed).addClass(d.classes.panelOpen),c._positionPanel(),c._pageContentOpenClasses=c._getPosDisplayClasses(d.classes.pageContentPrefix),"overlay"!==d.display&&(c._page().parent().addClass(d.classes.pageContainer),c._wrapper().addClass(c._pageContentOpenClasses),c._fixedToolbars().addClass(c._pageContentOpenClasses)),c._modalOpenClasses=c._getPosDisplayClasses(d.classes.modal)+" "+d.classes.modalOpen,c._modal&&c._modal.addClass(c._modalOpenClasses).height(Math.max(c._modal.height(),c.document.height()))},f=function(){c.document.off(c._transitionEndEvents,f),"overlay"!==d.display&&(c._wrapper().addClass(d.classes.pageContentPrefix+"-open"),c._fixedToolbars().addClass(d.classes.pageContentPrefix+"-open")),c._bindFixListener(),c._trigger("open")};c._trigger("beforeopen"),"open"===c._page().jqmData("panel")?c.document.on("panelclose",function(){e()}):e(),c._open=!0}},close:function(b){if(this._open){var c=this,d=this.options,e=function(){!b&&a.support.cssTransform3d&&d.animate?c.document.on(c._transitionEndEvents,f):setTimeout(f,0),c.element.removeClass(d.classes.panelOpen),"overlay"!==d.display&&(c._wrapper().removeClass(c._pageContentOpenClasses),c._fixedToolbars().removeClass(c._pageContentOpenClasses)),c._modal&&c._modal.removeClass(c._modalOpenClasses)},f=function(){c.document.off(c._transitionEndEvents,f),d.theme&&"overlay"!==d.display&&c._page().parent().removeClass(d.classes.pageContainer+"-themed "+d.classes.pageContainer+"-"+d.theme),c.element.addClass(d.classes.panelClosed),"overlay"!==d.display&&(c._page().parent().removeClass(d.classes.pageContainer),c._wrapper().removeClass(d.classes.pageContentPrefix+"-open"),c._fixedToolbars().removeClass(d.classes.pageContentPrefix+"-open")),a.support.cssTransform3d&&d.animate&&"overlay"!==d.display&&(c._wrapper().removeClass(d.classes.animate),c._fixedToolbars().removeClass(d.classes.animate)),c._fixPanel(),c._unbindFixListener(),a.mobile.resetActivePageHeight(),c._page().jqmRemoveData("panel"),c._trigger("close")};c._trigger("beforeclose"),e(),c._open=!1}},toggle:function(){this[this._open?"close":"open"]()},_transitionEndEvents:"webkitTransitionEnd oTransitionEnd otransitionend transitionend msTransitionEnd",_destroy:function(){var b,c=this.options,d=a("body > :mobile-panel").length+a.mobile.activePage.find(":mobile-panel").length>1;"overlay"!==c.display&&(b=a("body > :mobile-panel").add(a.mobile.activePage.find(":mobile-panel")),0===b.not(".ui-panel-display-overlay").not(this.element).length&&this._wrapper().children().unwrap(),this._open&&(this._fixedToolbars().removeClass(c.classes.pageContentPrefix+"-open"),a.support.cssTransform3d&&c.animate&&this._fixedToolbars().removeClass(c.classes.animate),this._page().parent().removeClass(c.classes.pageContainer),c.theme&&this._page().parent().removeClass(c.classes.pageContainer+"-themed "+c.classes.pageContainer+"-"+c.theme))),d||(this.document.off("panelopen panelclose"),this._open&&(this.document.off(this._transitionEndEvents),a.mobile.resetActivePageHeight())),this._open&&this._page().jqmRemoveData("panel"),this._panelInner.children().unwrap(),this.element.removeClass([this._getPanelClasses(),c.classes.panelOpen,c.classes.animate].join(" ")).off("swipeleft.panel swiperight.panel").off("panelbeforeopen").off("panelhide").off("keyup.panel").off("updatelayout").off(this._transitionEndEvents),this._closeLink.off("click.panel"),this._modal&&this._modal.remove()}})}(a),function(a,b){a.widget("mobile.table",{options:{classes:{table:"ui-table"},enhanced:!1},_create:function(){this.options.enhanced||this.element.addClass(this.options.classes.table),a.extend(this,{headers:b,allHeaders:b}),this._refresh(!0)},_setHeaders:function(){var a=this.element.find("thead tr");this.headers=this.element.find("tr:eq(0)").children(),this.allHeaders=this.headers.add(a.children())},refresh:function(){this._refresh()},rebuild:a.noop,_refresh:function(){var b=this.element,c=b.find("thead tr");this._setHeaders(),c.each(function(){var d=0;a(this).children().each(function(){var e,f=parseInt(this.getAttribute("colspan"),10),g=":nth-child("+(d+1)+")";if(this.setAttribute("data-"+a.mobile.ns+"colstart",d+1),f)for(e=0;f-1>e;e++)d++,g+=", :nth-child("+(d+1)+")";a(this).jqmData("cells",b.find("tr").not(c.eq(0)).not(this).children(g)),d++})})}})}(a),function(a){a.widget("mobile.table",a.mobile.table,{options:{mode:"columntoggle",columnBtnTheme:null,columnPopupTheme:null,columnBtnText:"Columns...",classes:a.extend(a.mobile.table.prototype.options.classes,{popup:"ui-table-columntoggle-popup",columnBtn:"ui-table-columntoggle-btn",priorityPrefix:"ui-table-priority-",columnToggleTable:"ui-table-columntoggle"})},_create:function(){this._super(),"columntoggle"===this.options.mode&&(a.extend(this,{_menu:null}),this.options.enhanced?(this._menu=a(this.document[0].getElementById(this._id()+"-popup")).children().first(),this._addToggles(this._menu,!0),this._bindToggles(this._menu)):(this._menu=this._enhanceColToggle(),this.element.addClass(this.options.classes.columnToggleTable)),this._setupEvents(),this._setToggleState())},_id:function(){return this.element.attr("id")||this.widgetName+this.uuid},_setupEvents:function(){this._on(this.window,{throttledresize:"_setToggleState"})},_bindToggles:function(a){var b=a.find("input");this._on(b,{change:"_menuInputChange"})},_addToggles:function(b,c){var d,e=0,f=this.options,g=b.controlgroup("container");c?d=b.find("input"):g.empty(),this.headers.not("td").each(function(){var b=a(this),h=a.mobile.getAttribute(this,"priority"),i=b.add(b.jqmData("cells"));h&&(i.addClass(f.classes.priorityPrefix+h),(c?d.eq(e++):a("<label><input type='checkbox' checked />"+(b.children("abbr").first().attr("title")||b.text())+"</label>").appendTo(g).children(0).checkboxradio({theme:f.columnPopupTheme})).jqmData("cells",i))}),c||(b.controlgroup("refresh"),this._bindToggles(b))},_menuInputChange:function(b){var c=a(b.target),d=c[0].checked;c.jqmData("cells").toggleClass("ui-table-cell-hidden",!d).toggleClass("ui-table-cell-visible",d),c[0].getAttribute("locked")?(c.removeAttr("locked"),this._unlockCells(c.jqmData("cells"))):c.attr("locked",!0)},_unlockCells:function(a){a.removeClass("ui-table-cell-hidden ui-table-cell-visible")},_enhanceColToggle:function(){var b,c,d,e,f=this.element,g=this.options,h=a.mobile.ns,i=this.document[0].createDocumentFragment();return b=this._id()+"-popup",c=a("<a href='#"+b+"' "+"class='"+g.classes.columnBtn+" ui-btn "+"ui-btn-"+(g.columnBtnTheme||"a")+" ui-corner-all ui-shadow ui-mini' "+"data-"+h+"rel='popup'>"+g.columnBtnText+"</a>"),d=a("<div class='"+g.classes.popup+"' id='"+b+"'></div>"),e=a("<fieldset></fieldset>").controlgroup(),this._addToggles(e,!1),e.appendTo(d),i.appendChild(d[0]),i.appendChild(c[0]),f.before(i),d.popup(),e},rebuild:function(){this._super(),"columntoggle"===this.options.mode&&this._refresh(!1)},_refresh:function(a){this._super(a),a||"columntoggle"!==this.options.mode||(this._unlockCells(this.allHeaders),this._addToggles(this._menu,a),this._setToggleState())},_setToggleState:function(){this._menu.find("input").each(function(){var b=a(this);this.checked="table-cell"===b.jqmData("cells").eq(0).css("display"),b.checkboxradio("refresh")})},_destroy:function(){this._super()}})}(a),function(a){a.widget("mobile.table",a.mobile.table,{options:{mode:"reflow",classes:a.extend(a.mobile.table.prototype.options.classes,{reflowTable:"ui-table-reflow",cellLabels:"ui-table-cell-label"})},_create:function(){this._super(),"reflow"===this.options.mode&&(this.options.enhanced||(this.element.addClass(this.options.classes.reflowTable),this._updateReflow()))},rebuild:function(){this._super(),"reflow"===this.options.mode&&this._refresh(!1)},_refresh:function(a){this._super(a),a||"reflow"!==this.options.mode||this._updateReflow()},_updateReflow:function(){var b=this,c=this.options;a(b.allHeaders.get().reverse()).each(function(){var d,e,f=a(this).jqmData("cells"),g=a.mobile.getAttribute(this,"colstart"),h=f.not(this).filter("thead th").length&&" ui-table-cell-label-top",i=a(this).text();""!==i&&(h?(d=parseInt(this.getAttribute("colspan"),10),e="",d&&(e="td:nth-child("+d+"n + "+g+")"),b._addLabels(f.filter(e),c.classes.cellLabels+h,i)):b._addLabels(f,c.classes.cellLabels,i))})},_addLabels:function(a,b,c){a.not(":has(b."+b+")").prepend("<b class='"+b+"'>"+c+"</b>")}})}(a),function(a,c){var d=function(b,c){return-1===(""+(a.mobile.getAttribute(this,"filtertext")||a(this).text())).toLowerCase().indexOf(c)};a.widget("mobile.filterable",{initSelector:":jqmData(filter='true')",options:{filterReveal:!1,filterCallback:d,enhanced:!1,input:null,children:"> li, > option, > optgroup option, > tbody tr, > .ui-controlgroup-controls > .ui-btn, > .ui-controlgroup-controls > .ui-checkbox, > .ui-controlgroup-controls > .ui-radio"},_create:function(){var b=this.options;a.extend(this,{_search:null,_timer:0}),this._setInput(b.input),b.enhanced||this._filterItems((this._search&&this._search.val()||"").toLowerCase())},_onKeyUp:function(){var c,d,e=this._search;if(e){if(c=e.val().toLowerCase(),d=a.mobile.getAttribute(e[0],"lastval")+"",d&&d===c)return;this._timer&&(b.clearTimeout(this._timer),this._timer=0),this._timer=this._delay(function(){this._trigger("beforefilter","beforefilter",{input:e}),e[0].setAttribute("data-"+a.mobile.ns+"lastval",c),this._filterItems(c),this._timer=0},250)}},_getFilterableItems:function(){var b=this.element,c=this.options.children,d=c?a.isFunction(c)?c():c.nodeName?a(c):c.jquery?c:this.element.find(c):{length:0};return 0===d.length&&(d=b.children()),d},_filterItems:function(b){var c,e,f,g,h=[],i=[],j=this.options,k=this._getFilterableItems();if(null!=b)for(e=j.filterCallback||d,f=k.length,c=0;f>c;c++)g=e.call(k[c],c,b)?i:h,g.push(k[c]);0===i.length?k[j.filterReveal?"addClass":"removeClass"]("ui-screen-hidden"):(a(i).addClass("ui-screen-hidden"),a(h).removeClass("ui-screen-hidden")),this._refreshChildWidget()},_refreshChildWidget:function(){var b,c,d=["collapsibleset","selectmenu","controlgroup","listview"];for(c=d.length-1;c>-1;c--)b=d[c],a.mobile[b]&&(b=this.element.data("mobile-"+b),b&&a.isFunction(b.refresh)&&b.refresh())},_setInput:function(c){var d=this._search;this._timer&&(b.clearTimeout(this._timer),this._timer=0),d&&(this._off(d,"keyup change input"),d=null),c&&(d=c.jquery?c:c.nodeName?a(c):this.document.find(c),this._on(d,{keyup:"_onKeyUp",change:"_onKeyUp",input:"_onKeyUp"})),this._search=d},_setOptions:function(a){var b=!(a.filterReveal===c&&a.filterCallback===c&&a.children===c);this._super(a),a.input!==c&&(this._setInput(a.input),b=!0),b&&this.refresh()},_destroy:function(){var a=this.options,b=this._getFilterableItems();a.enhanced?b.toggleClass("ui-screen-hidden",a.filterReveal):b.removeClass("ui-screen-hidden")},refresh:function(){this._timer&&(b.clearTimeout(this._timer),this._timer=0),this._filterItems((this._search&&this._search.val()||"").toLowerCase())}})}(a),function(a,b){var c=function(a,b){return function(c){b.call(this,c),a._syncTextInputOptions(c)}},d=/(^|\s)ui-li-divider(\s|$)/,e=a.mobile.filterable.prototype.options.filterCallback;a.mobile.filterable.prototype.options.filterCallback=function(a,b){return!this.className.match(d)&&e.call(this,a,b)},a.widget("mobile.filterable",a.mobile.filterable,{options:{filterPlaceholder:"Filter items...",filterTheme:null},_create:function(){var b,c,d=this.element,e=["collapsibleset","selectmenu","controlgroup","listview"],f={};for(this._super(),a.extend(this,{_widget:null}),b=e.length-1;b>-1;b--)if(c=e[b],a.mobile[c]){if(this._setWidget(d.data("mobile-"+c)))break;f[c+"create"]="_handleCreate"}this._widget||this._on(d,f)},_handleCreate:function(a){this._setWidget(this.element.data("mobile-"+a.type.substring(0,a.type.length-6)))},_setWidget:function(a){return!this._widget&&a&&(this._widget=a,this._widget._setOptions=c(this,this._widget._setOptions)),this._widget&&(this._syncTextInputOptions(this._widget.options),"listview"===this._widget.widgetName&&(this._widget.options.hidedividers=!0,this._widget.element.listview("refresh"))),!!this._widget},_isSearchInternal:function(){return this._search&&this._search.jqmData("ui-filterable-"+this.uuid+"-internal")},_setInput:function(b){var c=this.options,d=!0,e={};if(!b){if(this._isSearchInternal())return;d=!1,b=a("<input data-"+a.mobile.ns+"type='search' "+"placeholder='"+c.filterPlaceholder+"'></input>").jqmData("ui-filterable-"+this.uuid+"-internal",!0),a("<form class='ui-filterable'></form>").append(b).submit(function(a){a.preventDefault(),b.blur()}).insertBefore(this.element),a.mobile.textinput&&(null!=this.options.filterTheme&&(e.theme=c.filterTheme),b.textinput(e))}this._super(b),this._isSearchInternal()&&d&&this._search.attr("placeholder",this.options.filterPlaceholder)},_setOptions:function(c){var d=this._super(c);return c.filterPlaceholder!==b&&this._isSearchInternal()&&this._search.attr("placeholder",c.filterPlaceholder),c.filterTheme!==b&&this._search&&a.mobile.textinput&&this._search.textinput("option","theme",c.filterTheme),d},_destroy:function(){this._isSearchInternal()&&this._search.remove(),this._super()},_syncTextInputOptions:function(c){var d,e={};if(this._isSearchInternal()&&a.mobile.textinput){for(d in a.mobile.textinput.prototype.options)c[d]!==b&&(e[d]="theme"===d&&null!=this.options.filterTheme?this.options.filterTheme:c[d]);this._search.textinput("option",e)}}})}(a),function(a,b){function c(){return++e}function d(a){return a.hash.length>1&&decodeURIComponent(a.href.replace(f,""))===decodeURIComponent(location.href.replace(f,""))}var e=0,f=/#.*$/;a.widget("ui.tabs",{version:"fadf2b312a05040436451c64bbfaf4814bc62c56",delay:300,options:{active:null,collapsible:!1,event:"click",heightStyle:"content",hide:null,show:null,activate:null,beforeActivate:null,beforeLoad:null,load:null},_create:function(){var b=this,c=this.options;this.running=!1,this.element.addClass("ui-tabs ui-widget ui-widget-content ui-corner-all").toggleClass("ui-tabs-collapsible",c.collapsible).delegate(".ui-tabs-nav > li","mousedown"+this.eventNamespace,function(b){a(this).is(".ui-state-disabled")&&b.preventDefault()}).delegate(".ui-tabs-anchor","focus"+this.eventNamespace,function(){a(this).closest("li").is(".ui-state-disabled")&&this.blur()}),this._processTabs(),c.active=this._initialActive(),a.isArray(c.disabled)&&(c.disabled=a.unique(c.disabled.concat(a.map(this.tabs.filter(".ui-state-disabled"),function(a){return b.tabs.index(a)}))).sort()),this.active=this.options.active!==!1&&this.anchors.length?this._findActive(c.active):a(),this._refresh(),this.active.length&&this.load(c.active)},_initialActive:function(){var b=this.options.active,c=this.options.collapsible,d=location.hash.substring(1);return null===b&&(d&&this.tabs.each(function(c,e){return a(e).attr("aria-controls")===d?(b=c,!1):void 0}),null===b&&(b=this.tabs.index(this.tabs.filter(".ui-tabs-active"))),(null===b||-1===b)&&(b=this.tabs.length?0:!1)),b!==!1&&(b=this.tabs.index(this.tabs.eq(b)),-1===b&&(b=c?!1:0)),!c&&b===!1&&this.anchors.length&&(b=0),b},_getCreateEventData:function(){return{tab:this.active,panel:this.active.length?this._getPanelForTab(this.active):a()}},_tabKeydown:function(b){var c=a(this.document[0].activeElement).closest("li"),d=this.tabs.index(c),e=!0;if(!this._handlePageNav(b)){switch(b.keyCode){case a.ui.keyCode.RIGHT:case a.ui.keyCode.DOWN:d++;break;case a.ui.keyCode.UP:case a.ui.keyCode.LEFT:e=!1,d--;break;case a.ui.keyCode.END:d=this.anchors.length-1;break;case a.ui.keyCode.HOME:d=0;break;case a.ui.keyCode.SPACE:return b.preventDefault(),clearTimeout(this.activating),this._activate(d),void 0;case a.ui.keyCode.ENTER:return b.preventDefault(),clearTimeout(this.activating),this._activate(d===this.options.active?!1:d),void 0;default:return}b.preventDefault(),clearTimeout(this.activating),d=this._focusNextTab(d,e),b.ctrlKey||(c.attr("aria-selected","false"),this.tabs.eq(d).attr("aria-selected","true"),this.activating=this._delay(function(){this.option("active",d)},this.delay))}},_panelKeydown:function(b){this._handlePageNav(b)||b.ctrlKey&&b.keyCode===a.ui.keyCode.UP&&(b.preventDefault(),this.active.focus())},_handlePageNav:function(b){return b.altKey&&b.keyCode===a.ui.keyCode.PAGE_UP?(this._activate(this._focusNextTab(this.options.active-1,!1)),!0):b.altKey&&b.keyCode===a.ui.keyCode.PAGE_DOWN?(this._activate(this._focusNextTab(this.options.active+1,!0)),!0):void 0},_findNextTab:function(b,c){function d(){return b>e&&(b=0),0>b&&(b=e),b}for(var e=this.tabs.length-1;-1!==a.inArray(d(),this.options.disabled);)b=c?b+1:b-1;return b},_focusNextTab:function(a,b){return a=this._findNextTab(a,b),this.tabs.eq(a).focus(),a},_setOption:function(a,b){return"active"===a?(this._activate(b),void 0):"disabled"===a?(this._setupDisabled(b),void 0):(this._super(a,b),"collapsible"===a&&(this.element.toggleClass("ui-tabs-collapsible",b),b||this.options.active!==!1||this._activate(0)),"event"===a&&this._setupEvents(b),"heightStyle"===a&&this._setupHeightStyle(b),void 0)},_tabId:function(a){return a.attr("aria-controls")||"ui-tabs-"+c()},_sanitizeSelector:function(a){return a?a.replace(/[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g,"\\$&"):""},refresh:function(){var b=this.options,c=this.tablist.children(":has(a[href])");b.disabled=a.map(c.filter(".ui-state-disabled"),function(a){return c.index(a)}),this._processTabs(),b.active!==!1&&this.anchors.length?this.active.length&&!a.contains(this.tablist[0],this.active[0])?this.tabs.length===b.disabled.length?(b.active=!1,this.active=a()):this._activate(this._findNextTab(Math.max(0,b.active-1),!1)):b.active=this.tabs.index(this.active):(b.active=!1,this.active=a()),this._refresh()},_refresh:function(){this._setupDisabled(this.options.disabled),this._setupEvents(this.options.event),this._setupHeightStyle(this.options.heightStyle),this.tabs.not(this.active).attr({"aria-selected":"false",tabIndex:-1}),this.panels.not(this._getPanelForTab(this.active)).hide().attr({"aria-expanded":"false","aria-hidden":"true"}),this.active.length?(this.active.addClass("ui-tabs-active ui-state-active").attr({"aria-selected":"true",tabIndex:0}),this._getPanelForTab(this.active).show().attr({"aria-expanded":"true","aria-hidden":"false"})):this.tabs.eq(0).attr("tabIndex",0)},_processTabs:function(){var b=this;this.tablist=this._getList().addClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all").attr("role","tablist"),this.tabs=this.tablist.find("> li:has(a[href])").addClass("ui-state-default ui-corner-top").attr({role:"tab",tabIndex:-1}),this.anchors=this.tabs.map(function(){return a("a",this)[0]}).addClass("ui-tabs-anchor").attr({role:"presentation",tabIndex:-1}),this.panels=a(),this.anchors.each(function(c,e){var f,g,h,i=a(e).uniqueId().attr("id"),j=a(e).closest("li"),k=j.attr("aria-controls");d(e)?(f=e.hash,g=b.element.find(b._sanitizeSelector(f))):(h=b._tabId(j),f="#"+h,g=b.element.find(f),g.length||(g=b._createPanel(h),g.insertAfter(b.panels[c-1]||b.tablist)),g.attr("aria-live","polite")),g.length&&(b.panels=b.panels.add(g)),k&&j.data("ui-tabs-aria-controls",k),j.attr({"aria-controls":f.substring(1),"aria-labelledby":i}),g.attr("aria-labelledby",i)}),this.panels.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").attr("role","tabpanel")},_getList:function(){return this.element.find("ol,ul").eq(0)},_createPanel:function(b){return a("<div>").attr("id",b).addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").data("ui-tabs-destroy",!0)},_setupDisabled:function(b){a.isArray(b)&&(b.length?b.length===this.anchors.length&&(b=!0):b=!1);for(var c,d=0;c=this.tabs[d];d++)b===!0||-1!==a.inArray(d,b)?a(c).addClass("ui-state-disabled").attr("aria-disabled","true"):a(c).removeClass("ui-state-disabled").removeAttr("aria-disabled");this.options.disabled=b},_setupEvents:function(b){var c={click:function(a){a.preventDefault()}};b&&a.each(b.split(" "),function(a,b){c[b]="_eventHandler"}),this._off(this.anchors.add(this.tabs).add(this.panels)),this._on(this.anchors,c),this._on(this.tabs,{keydown:"_tabKeydown"}),this._on(this.panels,{keydown:"_panelKeydown"}),this._focusable(this.tabs),this._hoverable(this.tabs)},_setupHeightStyle:function(b){var c,d=this.element.parent();"fill"===b?(c=d.height(),c-=this.element.outerHeight()-this.element.height(),this.element.siblings(":visible").each(function(){var b=a(this),d=b.css("position");"absolute"!==d&&"fixed"!==d&&(c-=b.outerHeight(!0))}),this.element.children().not(this.panels).each(function(){c-=a(this).outerHeight(!0)}),this.panels.each(function(){a(this).height(Math.max(0,c-a(this).innerHeight()+a(this).height()))}).css("overflow","auto")):"auto"===b&&(c=0,this.panels.each(function(){c=Math.max(c,a(this).height("").height())}).height(c))},_eventHandler:function(b){var c=this.options,d=this.active,e=a(b.currentTarget),f=e.closest("li"),g=f[0]===d[0],h=g&&c.collapsible,i=h?a():this._getPanelForTab(f),j=d.length?this._getPanelForTab(d):a(),k={oldTab:d,oldPanel:j,newTab:h?a():f,newPanel:i};b.preventDefault(),f.hasClass("ui-state-disabled")||f.hasClass("ui-tabs-loading")||this.running||g&&!c.collapsible||this._trigger("beforeActivate",b,k)===!1||(c.active=h?!1:this.tabs.index(f),this.active=g?a():f,this.xhr&&this.xhr.abort(),j.length||i.length||a.error("jQuery UI Tabs: Mismatching fragment identifier."),i.length&&this.load(this.tabs.index(f),b),this._toggle(b,k))},_toggle:function(b,c){function d(){f.running=!1,f._trigger("activate",b,c)}function e(){c.newTab.closest("li").addClass("ui-tabs-active ui-state-active"),g.length&&f.options.show?f._show(g,f.options.show,d):(g.show(),d())}var f=this,g=c.newPanel,h=c.oldPanel;this.running=!0,h.length&&this.options.hide?this._hide(h,this.options.hide,function(){c.oldTab.closest("li").removeClass("ui-tabs-active ui-state-active"),e()}):(c.oldTab.closest("li").removeClass("ui-tabs-active ui-state-active"),h.hide(),e()),h.attr({"aria-expanded":"false","aria-hidden":"true"}),c.oldTab.attr("aria-selected","false"),g.length&&h.length?c.oldTab.attr("tabIndex",-1):g.length&&this.tabs.filter(function(){return 0===a(this).attr("tabIndex")}).attr("tabIndex",-1),g.attr({"aria-expanded":"true","aria-hidden":"false"}),c.newTab.attr({"aria-selected":"true",tabIndex:0})},_activate:function(b){var c,d=this._findActive(b);d[0]!==this.active[0]&&(d.length||(d=this.active),c=d.find(".ui-tabs-anchor")[0],this._eventHandler({target:c,currentTarget:c,preventDefault:a.noop}))},_findActive:function(b){return b===!1?a():this.tabs.eq(b)},_getIndex:function(a){return"string"==typeof a&&(a=this.anchors.index(this.anchors.filter("[href$='"+a+"']"))),a},_destroy:function(){this.xhr&&this.xhr.abort(),this.element.removeClass("ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible"),this.tablist.removeClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all").removeAttr("role"),this.anchors.removeClass("ui-tabs-anchor").removeAttr("role").removeAttr("tabIndex").removeUniqueId(),this.tabs.add(this.panels).each(function(){a.data(this,"ui-tabs-destroy")?a(this).remove():a(this).removeClass("ui-state-default ui-state-active ui-state-disabled ui-corner-top ui-corner-bottom ui-widget-content ui-tabs-active ui-tabs-panel").removeAttr("tabIndex").removeAttr("aria-live").removeAttr("aria-busy").removeAttr("aria-selected").removeAttr("aria-labelledby").removeAttr("aria-hidden").removeAttr("aria-expanded").removeAttr("role")}),this.tabs.each(function(){var b=a(this),c=b.data("ui-tabs-aria-controls");c?b.attr("aria-controls",c).removeData("ui-tabs-aria-controls"):b.removeAttr("aria-controls")}),this.panels.show(),"content"!==this.options.heightStyle&&this.panels.css("height","")},enable:function(c){var d=this.options.disabled;d!==!1&&(c===b?d=!1:(c=this._getIndex(c),d=a.isArray(d)?a.map(d,function(a){return a!==c?a:null}):a.map(this.tabs,function(a,b){return b!==c?b:null})),this._setupDisabled(d))},disable:function(c){var d=this.options.disabled;if(d!==!0){if(c===b)d=!0;else{if(c=this._getIndex(c),-1!==a.inArray(c,d))return;d=a.isArray(d)?a.merge([c],d).sort():[c]}this._setupDisabled(d)}},load:function(b,c){b=this._getIndex(b);var e=this,f=this.tabs.eq(b),g=f.find(".ui-tabs-anchor"),h=this._getPanelForTab(f),i={tab:f,panel:h};d(g[0])||(this.xhr=a.ajax(this._ajaxSettings(g,c,i)),this.xhr&&"canceled"!==this.xhr.statusText&&(f.addClass("ui-tabs-loading"),h.attr("aria-busy","true"),this.xhr.success(function(a){setTimeout(function(){h.html(a),e._trigger("load",c,i)},1)}).complete(function(a,b){setTimeout(function(){"abort"===b&&e.panels.stop(!1,!0),f.removeClass("ui-tabs-loading"),h.removeAttr("aria-busy"),a===e.xhr&&delete e.xhr},1)})))},_ajaxSettings:function(b,c,d){var e=this;return{url:b.attr("href"),beforeSend:function(b,f){return e._trigger("beforeLoad",c,a.extend({jqXHR:b,ajaxSettings:f},d))}}},_getPanelForTab:function(b){var c=a(b).attr("aria-controls");return this.element.find(this._sanitizeSelector("#"+c))}})}(a),function(){}(a),function(a,b){function c(a){e=a.originalEvent,i=e.accelerationIncludingGravity,f=Math.abs(i.x),g=Math.abs(i.y),h=Math.abs(i.z),!b.orientation&&(f>7||(h>6&&8>g||8>h&&g>6)&&f>5)?d.enabled&&d.disable():d.enabled||d.enable()}a.mobile.iosorientationfixEnabled=!0;var d,e,f,g,h,i,j=navigator.userAgent;return/iPhone|iPad|iPod/.test(navigator.platform)&&/OS [1-5]_[0-9_]* like Mac OS X/i.test(j)&&j.indexOf("AppleWebKit")>-1?(d=a.mobile.zoom,a.mobile.document.on("mobileinit",function(){a.mobile.iosorientationfixEnabled&&a.mobile.window.bind("orientationchange.iosorientationfix",d.enable).bind("devicemotion.iosorientationfix",c)}),void 0):(a.mobile.iosorientationfixEnabled=!1,void 0)}(a,this),function(a,b){function d(){e.removeClass("ui-mobile-rendering")}var e=a("html"),f=a.mobile.window;a(b.document).trigger("mobileinit"),a.mobile.gradeA()&&(a.mobile.ajaxBlacklist&&(a.mobile.ajaxEnabled=!1),e.addClass("ui-mobile ui-mobile-rendering"),setTimeout(d,5e3),a.extend(a.mobile,{initializePage:function(){var b=a.mobile.path,e=a(":jqmData(role='page'), :jqmData(role='dialog')"),g=b.stripHash(b.stripQueryParams(b.parseLocation().hash)),h=c.getElementById(g);e.length||(e=a("body").wrapInner("<div data-"+a.mobile.ns+"role='page'></div>").children(0)),e.each(function(){var b=a(this);b[0].getAttribute("data-"+a.mobile.ns+"url")||b.attr("data-"+a.mobile.ns+"url",b.attr("id")||location.pathname+location.search)}),a.mobile.firstPage=e.first(),a.mobile.pageContainer=a.mobile.firstPage.parent().addClass("ui-mobile-viewport").pagecontainer(),a.mobile.navreadyDeferred.resolve(),f.trigger("pagecontainercreate"),a.mobile.loading("show"),d(),a.mobile.hashListeningEnabled&&a.mobile.path.isHashValid(location.hash)&&(a(h).is(":jqmData(role='page')")||a.mobile.path.isPath(g)||g===a.mobile.dialogHashKey)?a.event.special.navigate.isPushStateEnabled()?(a.mobile.navigate.history.stack=[],a.mobile.navigate(a.mobile.path.isPath(location.hash)?location.hash:location.href)):f.trigger("hashchange",[!0]):(a.mobile.path.isHashValid(location.hash)&&(a.mobile.navigate.history.initialDst=g.replace("#","")),a.event.special.navigate.isPushStateEnabled()&&a.mobile.navigate.navigator.squash(b.parseLocation().href),a.mobile.changePage(a.mobile.firstPage,{transition:"none",reverse:!0,changeHash:!1,fromHashChange:!0}))}}),a(function(){a.support.inlineSVG(),a.mobile.hideUrlBar&&b.scrollTo(0,1),a.mobile.defaultHomeScroll=a.support.scrollTop&&1!==a.mobile.window.scrollTop()?1:0,a.mobile.autoInitializePage&&a.mobile.initializePage(),a.mobile.hideUrlBar&&f.load(a.mobile.silentScroll),a.support.cssPointerEvents||a.mobile.document.delegate(".ui-state-disabled,.ui-disabled","vclick",function(a){a.preventDefault(),a.stopImmediatePropagation()
+})}))}(a,this)});
+/*
+//@ sourceMappingURL=jquery.mobile-1.4.0.min.map
+*/
\ No newline at end of file
diff --git a/gz3d/client/js/include/roslib.js b/gz3d/client/js/include/roslib.js
new file mode 100644
index 0000000000000000000000000000000000000000..f2981d5c732564233a759ff254fe635926d82b39
--- /dev/null
+++ b/gz3d/client/js/include/roslib.js
@@ -0,0 +1,1632 @@
+/**
+ * @author Russell Toris - rctoris@wpi.edu
+ */
+
+var ROSLIB = ROSLIB || {
+  REVISION : '6'
+};
+
+//URDF types
+ROSLIB.URDF_SPHERE = 0;
+ROSLIB.URDF_BOX = 1;
+ROSLIB.URDF_CYLINDER = 2;
+ROSLIB.URDF_MESH = 3;
+
+/**
+ * @author Russell Toris - rctoris@wpi.edu
+ */
+
+/**
+ * An actionlib action client.
+ *
+ * Emits the following events:
+ *  * 'timeout' - if a timeout occurred while sending a goal
+ *  * 'status' - the status messages received from the action server
+ *  * 'feedback' -  the feedback messages received from the action server
+ *  * 'result' - the result returned from the action server
+ *
+ *  @constructor
+ *  @param options - object with following keys:
+ *   * ros - the ROSLIB.Ros connection handle
+ *   * serverName - the action server name, like /fibonacci
+ *   * actionName - the action message name, like 'actionlib_tutorials/FibonacciAction'
+ *   * timeout - the timeout length when connecting to the action server
+ */
+ROSLIB.ActionClient = function(options) {
+  var that = this;
+  options = options || {};
+  this.ros = options.ros;
+  this.serverName = options.serverName;
+  this.actionName = options.actionName;
+  this.timeout = options.timeout;
+  this.goals = {};
+
+  // flag to check if a status has been received
+  var receivedStatus = false;
+
+  // create the topics associated with actionlib
+  var feedbackListener = new ROSLIB.Topic({
+    ros : this.ros,
+    name : this.serverName + '/feedback',
+    messageType : this.actionName + 'Feedback'
+  });
+
+  var statusListener = new ROSLIB.Topic({
+    ros : this.ros,
+    name : this.serverName + '/status',
+    messageType : 'actionlib_msgs/GoalStatusArray'
+  });
+
+  var resultListener = new ROSLIB.Topic({
+    ros : this.ros,
+    name : this.serverName + '/result',
+    messageType : this.actionName + 'Result'
+  });
+
+  this.goalTopic = new ROSLIB.Topic({
+    ros : this.ros,
+    name : this.serverName + '/goal',
+    messageType : this.actionName + 'Goal'
+  });
+
+  this.cancelTopic = new ROSLIB.Topic({
+    ros : this.ros,
+    name : this.serverName + '/cancel',
+    messageType : 'actionlib_msgs/GoalID'
+  });
+
+  // advertise the goal and cancel topics
+  this.goalTopic.advertise();
+  this.cancelTopic.advertise();
+
+  // subscribe to the status topic
+  statusListener.subscribe(function(statusMessage) {
+    receivedStatus = true;
+    statusMessage.status_list.forEach(function(status) {
+      var goal = that.goals[status.goal_id.id];
+      if (goal) {
+        goal.emit('status', status);
+      }
+    });
+  });
+
+  // subscribe the the feedback topic
+  feedbackListener.subscribe(function(feedbackMessage) {
+    var goal = that.goals[feedbackMessage.status.goal_id.id];
+    if (goal) {
+      goal.emit('status', feedbackMessage.status);
+      goal.emit('feedback', feedbackMessage.feedback);
+    }
+  });
+
+  // subscribe to the result topic
+  resultListener.subscribe(function(resultMessage) {
+    var goal = that.goals[resultMessage.status.goal_id.id];
+
+    if (goal) {
+      goal.emit('status', resultMessage.status);
+      goal.emit('result', resultMessage.result);
+    }
+  });
+
+  // If timeout specified, emit a 'timeout' event if the action server does not respond
+  if (this.timeout) {
+    setTimeout(function() {
+      if (!receivedStatus) {
+        that.emit('timeout');
+      }
+    }, this.timeout);
+  }
+};
+ROSLIB.ActionClient.prototype.__proto__ = EventEmitter2.prototype;
+
+/**
+ * Cancel all goals associated with this ActionClient.
+ */
+ROSLIB.ActionClient.prototype.cancel = function() {
+  var cancelMessage = new ROSLIB.Message();
+  this.cancelTopic.publish(cancelMessage);
+};
+
+
+/**
+ * @author Russell Toris - rctoris@wpi.edu
+ */
+
+/**
+ * An actionlib goal goal is associated with an action server.
+ *
+ * Emits the following events:
+ *  * 'timeout' - if a timeout occurred while sending a goal
+ *
+ *  @constructor
+ *  @param object with following keys:
+ *   * actionClient - the ROSLIB.ActionClient to use with this goal
+ *   * goalMessage - The JSON object containing the goal for the action server
+ */
+ROSLIB.Goal = function(options) {
+  var that = this;
+  this.actionClient = options.actionClient;
+  this.goalMessage = options.goalMessage;
+  this.isFinished = false;
+
+  // Used to create random IDs
+  var date = new Date();
+
+  // Create a random ID
+  this.goalID = 'goal_' + Math.random() + '_' + date.getTime();
+  // Fill in the goal message
+  this.goalMessage = new ROSLIB.Message({
+    goal_id : {
+      stamp : {
+        secs : 0,
+        nsecs : 0
+      },
+      id : this.goalID
+    },
+    goal : this.goalMessage
+  });
+
+  this.on('status', function(status) {
+    that.status = status;
+  });
+
+  this.on('result', function(result) {
+    that.isFinished = true;
+    that.result = result;
+  });
+
+  this.on('feedback', function(feedback) {
+    that.feedback = feedback;
+  });
+
+  // Add the goal
+  this.actionClient.goals[this.goalID] = this;
+};
+ROSLIB.Goal.prototype.__proto__ = EventEmitter2.prototype;
+
+/**
+ * Send the goal to the action server.
+ *
+ * @param timeout (optional) - a timeout length for the goal's result
+ */
+ROSLIB.Goal.prototype.send = function(timeout) {
+  var that = this;
+  that.actionClient.goalTopic.publish(that.goalMessage);
+  if (timeout) {
+    setTimeout(function() {
+      if (!that.isFinished) {
+        that.emit('timeout');
+      }
+    }, timeout);
+  }
+};
+
+/**
+ * Cancel the current goal.
+ */
+ROSLIB.Goal.prototype.cancel = function() {
+  var cancelMessage = new ROSLIB.Message({
+    id : this.goalID
+  });
+  this.actionClient.cancelTopic.publish(cancelMessage);
+};
+
+/**
+ * @author Brandon Alexander - baalexander@gmail.com
+ */
+
+/**
+ * Message objects are used for publishing and subscribing to and from topics.
+ *
+ * @constructor
+ * @param values - object matching the fields defined in the .msg definition file
+ */
+ROSLIB.Message = function(values) {
+  var that = this;
+  values = values || {};
+
+  Object.keys(values).forEach(function(name) {
+    that[name] = values[name];
+  });
+};
+
+/**
+ * @author Brandon Alexander - baalexander@gmail.com
+ */
+
+/**
+ * A ROS parameter.
+ *
+ * @constructor
+ * @param options - possible keys include:
+ *   * ros - the ROSLIB.Ros connection handle
+ *   * name - the param name, like max_vel_x
+ */
+ROSLIB.Param = function(options) {
+  options = options || {};
+  this.ros = options.ros;
+  this.name = options.name;
+};
+
+/**
+ * Fetches the value of the param.
+ *
+ * @param callback - function with the following params:
+ *  * value - the value of the param from ROS.
+ */
+ROSLIB.Param.prototype.get = function(callback) {
+  var paramClient = new ROSLIB.Service({
+    ros : this.ros,
+    name : '/rosapi/get_param',
+    serviceType : 'rosapi/GetParam'
+  });
+
+  var request = new ROSLIB.ServiceRequest({
+    name : this.name,
+    value : JSON.stringify('')
+  });
+
+  paramClient.callService(request, function(result) {
+    var value = JSON.parse(result.value);
+    callback(value);
+  });
+};
+
+/**
+ * Sets the value of the param in ROS.
+ *
+ * @param value - value to set param to.
+ */
+ROSLIB.Param.prototype.set = function(value) {
+  var paramClient = new ROSLIB.Service({
+    ros : this.ros,
+    name : '/rosapi/set_param',
+    serviceType : 'rosapi/SetParam'
+  });
+
+  var request = new ROSLIB.ServiceRequest({
+    name : this.name,
+    value : JSON.stringify(value)
+  });
+
+  paramClient.callService(request, function() {
+  });
+};
+
+/**
+ * @author Brandon Alexander - baalexander@gmail.com
+ */
+
+/**
+ * Manages connection to the server and all interactions with ROS.
+ *
+ * Emits the following events:
+ *  * 'error' - there was an error with ROS
+ *  * 'connection' - connected to the WebSocket server
+ *  * 'close' - disconnected to the WebSocket server
+ *  * <topicName> - a message came from rosbridge with the given topic name
+ *  * <serviceID> - a service response came from rosbridge with the given ID
+ *
+ * @constructor
+ * @param options - possible keys include:
+ *   * url (optional) - the WebSocket URL for rosbridge (can be specified later with `connect`)
+ */
+ROSLIB.Ros = function(options) {
+  options = options || {};
+  var url = options.url;
+  this.socket = null;
+  this.idCounter = 0;
+
+  // Sets unlimited event listeners.
+  this.setMaxListeners(0);
+
+  // begin by checking if a URL was given
+  if (url) {
+    this.connect(url);
+  }
+};
+ROSLIB.Ros.prototype.__proto__ = EventEmitter2.prototype;
+
+/**
+ * Connect to the specified WebSocket.
+ *
+ * @param url - WebSocket URL for Rosbridge
+ */
+ROSLIB.Ros.prototype.connect = function(url) {
+  var that = this;
+
+  /**
+   * Emits a 'connection' event on WebSocket connection.
+   *
+   * @param event - the argument to emit with the event.
+   */
+  function onOpen(event) {
+    that.emit('connection', event);
+  }
+
+  /**
+   * Emits a 'close' event on WebSocket disconnection.
+   *
+   * @param event - the argument to emit with the event.
+   */
+  function onClose(event) {
+    that.emit('close', event);
+  }
+
+  /**
+   * Emits an 'error' event whenever there was an error.
+   *
+   * @param event - the argument to emit with the event.
+   */
+  function onError(event) {
+    that.emit('error', event);
+  }
+
+  /**
+   * If a message was compressed as a PNG image (a compression hack since
+   * gzipping over WebSockets * is not supported yet), this function places the
+   * "image" in a canvas element then decodes the * "image" as a Base64 string.
+   *
+   * @param data - object containing the PNG data.
+   * @param callback - function with params:
+   *   * data - the uncompressed data
+   */
+  function decompressPng(data, callback) {
+    // Uncompresses the data before sending it through (use image/canvas to do so).
+    var image = new Image();
+    // When the image loads, extracts the raw data (JSON message).
+    image.onload = function() {
+      // Creates a local canvas to draw on.
+      var canvas = document.createElement('canvas');
+      var context = canvas.getContext('2d');
+
+      // Sets width and height.
+      canvas.width = image.width;
+      canvas.height = image.height;
+
+      // Puts the data into the image.
+      context.drawImage(image, 0, 0);
+      // Grabs the raw, uncompressed data.
+      var imageData = context.getImageData(0, 0, image.width, image.height).data;
+
+      // Constructs the JSON.
+      var jsonData = '';
+      for ( var i = 0; i < imageData.length; i += 4) {
+        // RGB
+        jsonData += String.fromCharCode(imageData[i], imageData[i + 1], imageData[i + 2]);
+      }
+      var decompressedData = JSON.parse(jsonData);
+      callback(decompressedData);
+    };
+    // Sends the image data to load.
+    image.src = 'data:image/png;base64,' + data.data;
+  }
+
+  /**
+   * Parses message responses from rosbridge and sends to the appropriate
+   * topic, service, or param.
+   *
+   * @param message - the raw JSON message from rosbridge.
+   */
+  function onMessage(message) {
+    function handleMessage(message) {
+      if (message.op === 'publish') {
+        that.emit(message.topic, message.msg);
+      } else if (message.op === 'service_response') {
+        that.emit(message.id, message.values);
+      }
+    }
+
+    var data = JSON.parse(message.data);
+    if (data.op === 'png') {
+      decompressPng(data, function(decompressedData) {
+        handleMessage(decompressedData);
+      });
+    } else {
+      handleMessage(data);
+    }
+  }
+
+  this.socket = new WebSocket(url);
+  this.socket.onopen = onOpen;
+  this.socket.onclose = onClose;
+  this.socket.onerror = onError;
+  this.socket.onmessage = onMessage;
+};
+
+/**
+ * Disconnect from the WebSocket server.
+ */
+ROSLIB.Ros.prototype.close = function() {
+  if (this.socket) {
+    this.socket.close();
+  }
+};
+
+/**
+ * Sends an authorization request to the server.
+ *
+ * @param mac - MAC (hash) string given by the trusted source.
+ * @param client - IP of the client.
+ * @param dest - IP of the destination.
+ * @param rand - Random string given by the trusted source.
+ * @param t - Time of the authorization request.
+ * @param level - User level as a string given by the client.
+ * @param end - End time of the client's session.
+ */
+ROSLIB.Ros.prototype.authenticate = function(mac, client, dest, rand, t, level, end) {
+  // create the request
+  var auth = {
+    op : 'auth',
+    mac : mac,
+    client : client,
+    dest : dest,
+    rand : rand,
+    t : t,
+    level : level,
+    end : end
+  };
+  // send the request
+  this.callOnConnection(auth);
+};
+
+/**
+ * Sends the message over the WebSocket, but queues the message up if not yet
+ * connected.
+ */
+ROSLIB.Ros.prototype.callOnConnection = function(message) {
+  var that = this;
+  var messageJson = JSON.stringify(message);
+
+  if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
+    that.once('connection', function() {
+      that.socket.send(messageJson);
+    });
+  } else {
+    that.socket.send(messageJson);
+  }
+};
+
+/**
+ * Retrieves list of topics in ROS as an array.
+ *
+ * @param callback function with params:
+ *   * topics - Array of topic names
+ */
+ROSLIB.Ros.prototype.getTopics = function(callback) {
+  var topicsClient = new ROSLIB.Service({
+    ros : this,
+    name : '/rosapi/topics',
+    serviceType : 'rosapi/Topics'
+  });
+
+  var request = new ROSLIB.ServiceRequest();
+
+  topicsClient.callService(request, function(result) {
+    callback(result.topics);
+  });
+};
+
+/**
+ * Retrieves list of active service names in ROS.
+ *
+ * @param callback - function with the following params:
+ *   * services - array of service names
+ */
+ROSLIB.Ros.prototype.getServices = function(callback) {
+  var servicesClient = new ROSLIB.Service({
+    ros : this,
+    name : '/rosapi/services',
+    serviceType : 'rosapi/Services'
+  });
+
+  var request = new ROSLIB.ServiceRequest();
+
+  servicesClient.callService(request, function(result) {
+    callback(result.services);
+  });
+};
+
+/**
+ * Retrieves list of param names from the ROS Parameter Server.
+ *
+ * @param callback function with params:
+ *  * params - array of param names.
+ */
+ROSLIB.Ros.prototype.getParams = function(callback) {
+  var paramsClient = new ROSLIB.Service({
+    ros : this,
+    name : '/rosapi/get_param_names',
+    serviceType : 'rosapi/GetParamNames'
+  });
+
+  var request = new ROSLIB.ServiceRequest();
+  paramsClient.callService(request, function(result) {
+    callback(result.names);
+  });
+};
+
+/**
+ * @author Brandon Alexander - baalexander@gmail.com
+ */
+
+/**
+ * A ROS service client.
+ *
+ * @constructor
+ * @params options - possible keys include:
+ *   * ros - the ROSLIB.Ros connection handle
+ *   * name - the service name, like /add_two_ints
+ *   * serviceType - the service type, like 'rospy_tutorials/AddTwoInts'
+ */
+ROSLIB.Service = function(options) {
+  options = options || {};
+  this.ros = options.ros;
+  this.name = options.name;
+  this.serviceType = options.serviceType;
+};
+
+/**
+ * Calls the service. Returns the service response in the callback.
+ *
+ * @param request - the ROSLIB.ServiceRequest to send
+ * @param callback - function with params:
+ *   * response - the response from the service request
+ */
+ROSLIB.Service.prototype.callService = function(request, callback) {
+  this.ros.idCounter++;
+  var serviceCallId = 'call_service:' + this.name + ':' + this.ros.idCounter;
+
+  this.ros.once(serviceCallId, function(data) {
+    var response = new ROSLIB.ServiceResponse(data);
+    callback(response);
+  });
+
+  var requestValues = [];
+  Object.keys(request).forEach(function(name) {
+    requestValues.push(request[name]);
+  });
+
+  var call = {
+    op : 'call_service',
+    id : serviceCallId,
+    service : this.name,
+    args : requestValues
+  };
+  this.ros.callOnConnection(call);
+};
+
+/**
+ * @author Brandon Alexander - balexander@willowgarage.com
+ */
+
+/**
+ * A ServiceRequest is passed into the service call.
+ *
+ * @constructor
+ * @param values - object matching the fields defined in the .srv definition file
+ */
+ROSLIB.ServiceRequest = function(values) {
+  var that = this;
+  values = values || {};
+
+  Object.keys(values).forEach(function(name) {
+    that[name] = values[name];
+  });
+};
+
+/**
+ * @author Brandon Alexander - balexander@willowgarage.com
+ */
+
+/**
+ * A ServiceResponse is returned from the service call.
+ *
+ * @constructor
+ * @param values - object matching the fields defined in the .srv definition file
+ */
+ROSLIB.ServiceResponse = function(values) {
+  var that = this;
+  values = values || {};
+
+  Object.keys(values).forEach(function(name) {
+    that[name] = values[name];
+  });
+};
+
+/**
+ * @author Brandon Alexander - baalexander@gmail.com
+ */
+
+/**
+ * Publish and/or subscribe to a topic in ROS.
+ *
+ * Emits the following events:
+ *  * 'warning' - if there are any warning during the Topic creation
+ *  * 'message' - the message data from rosbridge
+ *
+ * @constructor
+ * @param options - object with following keys:
+ *   * ros - the ROSLIB.Ros connection handle
+ *   * name - the topic name, like /cmd_vel
+ *   * messageType - the message type, like 'std_msgs/String'
+ *   * compression - the type of compression to use, like 'png'
+ *   * throttle_rate - the rate at which to throttle the topics
+ */
+ROSLIB.Topic = function(options) {
+  options = options || {};
+  this.ros = options.ros;
+  this.name = options.name;
+  this.messageType = options.messageType;
+  this.isAdvertised = false;
+  this.compression = options.compression || 'none';
+  this.throttle_rate = options.throttle_rate || 0;
+
+  // Check for valid compression types
+  if (this.compression && this.compression !== 'png' && this.compression !== 'none') {
+    this.emit('warning', this.compression +
+      ' compression is not supported. No compression will be used.');
+  }
+
+  // Check if throttle rate is negative
+  if (this.throttle_rate < 0) {
+    this.emit('warning', this.throttle_rate + ' is not allowed. Set to 0');
+    this.throttle_rate = 0;
+  }
+};
+ROSLIB.Topic.prototype.__proto__ = EventEmitter2.prototype;
+
+/**
+ * Every time a message is published for the given topic, the callback
+ * will be called with the message object.
+ *
+ * @param callback - function with the following params:
+ *   * message - the published message
+ */
+ROSLIB.Topic.prototype.subscribe = function(callback) {
+  var that = this;
+
+  this.on('message', function(message) {
+    callback(message);
+  });
+
+  this.ros.on(this.name, function(data) {
+    var message = new ROSLIB.Message(data);
+    that.emit('message', message);
+  });
+
+  this.ros.idCounter++;
+  var subscribeId = 'subscribe:' + this.name + ':' + this.ros.idCounter;
+  var call = {
+    op : 'subscribe',
+    id : subscribeId,
+    type : this.messageType,
+    topic : this.name,
+    compression : this.compression,
+    throttle_rate : this.throttle_rate
+  };
+
+  this.ros.callOnConnection(call);
+};
+
+/**
+ * Unregisters as a subscriber for the topic. Unsubscribing will remove
+ * all subscribe callbacks.
+ */
+ROSLIB.Topic.prototype.unsubscribe = function() {
+  this.ros.removeAllListeners([ this.name ]);
+  this.ros.idCounter++;
+  var unsubscribeId = 'unsubscribe:' + this.name + ':' + this.ros.idCounter;
+  var call = {
+    op : 'unsubscribe',
+    id : unsubscribeId,
+    topic : this.name
+  };
+  this.ros.callOnConnection(call);
+};
+
+/**
+ * Registers as a publisher for the topic.
+ */
+ROSLIB.Topic.prototype.advertise = function() {
+  this.ros.idCounter++;
+  var advertiseId = 'advertise:' + this.name + ':' + this.ros.idCounter;
+  var call = {
+    op : 'advertise',
+    id : advertiseId,
+    type : this.messageType,
+    topic : this.name
+  };
+  this.ros.callOnConnection(call);
+  this.isAdvertised = true;
+};
+
+/**
+ * Unregisters as a publisher for the topic.
+ */
+ROSLIB.Topic.prototype.unadvertise = function() {
+  this.ros.idCounter++;
+  var unadvertiseId = 'unadvertise:' + this.name + ':' + this.ros.idCounter;
+  var call = {
+    op : 'unadvertise',
+    id : unadvertiseId,
+    topic : this.name
+  };
+  this.ros.callOnConnection(call);
+  this.isAdvertised = false;
+};
+
+/**
+ * Publish the message.
+ *
+ * @param message - A ROSLIB.Message object.
+ */
+ROSLIB.Topic.prototype.publish = function(message) {
+  if (!this.isAdvertised) {
+    this.advertise();
+  }
+
+  this.ros.idCounter++;
+  var publishId = 'publish:' + this.name + ':' + this.ros.idCounter;
+  var call = {
+    op : 'publish',
+    id : publishId,
+    topic : this.name,
+    msg : message
+  };
+  this.ros.callOnConnection(call);
+};
+
+/**
+ * @author David Gossow - dgossow@willowgarage.com
+ */
+
+/**
+ * A Pose in 3D space. Values are copied into this object.
+ *
+ *  @constructor
+ *  @param options - object with following keys:
+ *   * position - the Vector3 describing the position
+ *   * orientation - the ROSLIB.Quaternion describing the orientation
+ */
+ROSLIB.Pose = function(options) {
+  options = options || {};
+  // copy the values into this object if they exist
+  this.position = new ROSLIB.Vector3(options.position);
+  this.orientation = new ROSLIB.Quaternion(options.orientation);
+};
+
+/**
+ * Apply a transform against this pose.
+ *
+ * @param tf the transform
+ */
+ROSLIB.Pose.prototype.applyTransform = function(tf) {
+  this.position.multiplyQuaternion(tf.rotation);
+  this.position.add(tf.translation);
+  var tmp = tf.rotation.clone();
+  tmp.multiply(this.orientation);
+  this.orientation = tmp;
+};
+
+/**
+ * Clone a copy of this pose.
+ *
+ * @returns the cloned pose
+ */
+ROSLIB.Pose.prototype.clone = function() {
+  return new ROSLIB.Pose(this);
+};
+
+/**
+ * @author David Gossow - dgossow@willowgarage.com
+ */
+
+/**
+ * A Quaternion.
+ *
+ *  @constructor
+ *  @param options - object with following keys:
+ *   * x - the x value
+ *   * y - the y value
+ *   * z - the z value
+ *   * w - the w value
+ */
+ROSLIB.Quaternion = function(options) {
+  options = options || {};
+  this.x = options.x || 0;
+  this.y = options.y || 0;
+  this.z = options.z || 0;
+  this.w = options.w || 1;
+};
+
+/**
+ * Perform a conjugation on this quaternion.
+ */
+ROSLIB.Quaternion.prototype.conjugate = function() {
+  this.x *= -1;
+  this.y *= -1;
+  this.z *= -1;
+};
+
+/**
+ * Perform a normalization on this quaternion.
+ */
+ROSLIB.Quaternion.prototype.normalize = function() {
+  var l = Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w);
+  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;
+  }
+};
+
+/**
+ * Convert this quaternion into its inverse.
+ */
+ROSLIB.Quaternion.prototype.invert = function() {
+  this.conjugate();
+  this.normalize();
+};
+
+/**
+ * Set the values of this quaternion to the product of itself and the given quaternion.
+ *
+ * @param q the quaternion to multiply with
+ */
+ROSLIB.Quaternion.prototype.multiply = function(q) {
+  var newX = this.x * q.w + this.y * q.z - this.z * q.y + this.w * q.x;
+  var newY = -this.x * q.z + this.y * q.w + this.z * q.x + this.w * q.y;
+  var newZ = this.x * q.y - this.y * q.x + this.z * q.w + this.w * q.z;
+  var newW = -this.x * q.x - this.y * q.y - this.z * q.z + this.w * q.w;
+  this.x = newX;
+  this.y = newY;
+  this.z = newZ;
+  this.w = newW;
+};
+
+/**
+ * Clone a copy of this quaternion.
+ *
+ * @returns the cloned quaternion
+ */
+ROSLIB.Quaternion.prototype.clone = function() {
+  return new ROSLIB.Quaternion(this);
+};
+
+
+/**
+ * @author David Gossow - dgossow@willowgarage.com
+ */
+
+/**
+ * A Transform in 3-space. Values are copied into this object.
+ *
+ *  @constructor
+ *  @param options - object with following keys:
+ *   * translation - the Vector3 describing the translation
+ *   * rotation - the ROSLIB.Quaternion describing the rotation
+ */
+ROSLIB.Transform = function(options) {
+  options = options || {};
+  // Copy the values into this object if they exist
+  this.translation = new ROSLIB.Vector3(options.translation);
+  this.rotation = new ROSLIB.Quaternion(options.rotation);
+};
+
+/**
+ * Clone a copy of this transform.
+ *
+ * @returns the cloned transform
+ */
+ROSLIB.Transform.prototype.clone = function() {
+  return new ROSLIB.Transform(this);
+};
+
+/**
+ * @author David Gossow - dgossow@willowgarage.com
+ */
+
+/**
+ * A 3D vector.
+ *
+ *  @constructor
+ *  @param options - object with following keys:
+ *   * x - the x value
+ *   * y - the y value
+ *   * z - the z value
+ */
+ROSLIB.Vector3 = function(options) {
+  options = options || {};
+  this.x = options.x || 0;
+  this.y = options.y || 0;
+  this.z = options.z || 0;
+};
+
+/**
+ * Set the values of this vector to the sum of itself and the given vector.
+ *
+ * @param v the vector to add with
+ */
+ROSLIB.Vector3.prototype.add = function(v) {
+  this.x += v.x;
+  this.y += v.y;
+  this.z += v.z;
+};
+
+/**
+ * Set the values of this vector to the difference of itself and the given vector.
+ *
+ * @param v the vector to subtract with
+ */
+ROSLIB.Vector3.prototype.subtract = function(v) {
+  this.x -= v.x;
+  this.y -= v.y;
+  this.z -= v.z;
+};
+
+/**
+ * Multiply the given Quaternion with this vector.
+ *
+ * @param q - the quaternion to multiply with
+ */
+ROSLIB.Vector3.prototype.multiplyQuaternion = function(q) {
+  var ix = q.w * this.x + q.y * this.z - q.z * this.y;
+  var iy = q.w * this.y + q.z * this.x - q.x * this.z;
+  var iz = q.w * this.z + q.x * this.y - q.y * this.x;
+  var iw = -q.x * this.x - q.y * this.y - q.z * this.z;
+  this.x = ix * q.w + iw * -q.x + iy * -q.z - iz * -q.y;
+  this.y = iy * q.w + iw * -q.y + iz * -q.x - ix * -q.z;
+  this.z = iz * q.w + iw * -q.z + ix * -q.y - iy * -q.x;
+};
+
+/**
+ * Clone a copy of this vector.
+ *
+ * @returns the cloned vector
+ */
+ROSLIB.Vector3.prototype.clone = function() {
+  return new ROSLIB.Vector3(this);
+};
+
+/**
+ * @author David Gossow - dgossow@willowgarage.com
+ */
+
+/**
+ * A TF Client that listens to TFs from tf2_web_republisher.
+ *
+ *  @constructor
+ *  @param options - object with following keys:
+ *   * ros - the ROSLIB.Ros connection handle
+ *   * fixedFrame - the fixed frame, like /base_link
+ *   * angularThres - the angular threshold for the TF republisher
+ *   * transThres - the translation threshold for the TF republisher
+ *   * rate - the rate for the TF republisher
+ *   * goalUpdateDelay - the goal update delay for the TF republisher
+ */
+ROSLIB.TFClient = function(options) {
+  options = options || {};
+  this.ros = options.ros;
+  this.fixedFrame = options.fixedFrame || '/base_link';
+  this.angularThres = options.angularThres || 2.0;
+  this.transThres = options.transThres || 0.01;
+  this.rate = options.rate || 10.0;
+  this.goalUpdateDelay = options.goalUpdateDelay || 50;
+
+  this.currentGoal = false;
+  this.frameInfos = {};
+  this.goalUpdateRequested = false;
+
+  // Create an ActionClient
+  this.actionClient = new ROSLIB.ActionClient({
+    ros : this.ros,
+    serverName : '/tf2_web_republisher',
+    actionName : 'tf2_web_republisher/TFSubscriptionAction'
+  });
+};
+
+/**
+ * Process the incoming TF message and send them out using the callback
+ * functions.
+ *
+ * @param tf - the TF message from the server
+ */
+ROSLIB.TFClient.prototype.processFeedback = function(tf) {
+  var that = this;
+  tf.transforms.forEach(function(transform) {
+    var frameID = transform.child_frame_id;
+    var info = that.frameInfos[frameID];
+    if (info !== undefined) {
+      info.transform = new ROSLIB.Transform({
+        translation : transform.transform.translation,
+        rotation : transform.transform.rotation
+      });
+      info.cbs.forEach(function(cb) {
+        cb(info.transform);
+      });
+    }
+  });
+};
+
+/**
+ * Create and send a new goal to the tf2_web_republisher based on the current
+ * list of TFs.
+ */
+ROSLIB.TFClient.prototype.updateGoal = function() {
+  // Anytime the list of frames changes, we will need to send a new goal.
+  if (this.currentGoal) {
+    this.currentGoal.cancel();
+  }
+
+  var goalMessage = {
+    source_frames : [],
+    target_frame : this.fixedFrame,
+    angular_thres : this.angularThres,
+    trans_thres : this.transThres,
+    rate : this.rate
+  };
+
+  for (var frame in this.frameInfos) {
+    goalMessage.source_frames.push(frame);
+  }
+
+  this.currentGoal = new ROSLIB.Goal({
+    actionClient : this.actionClient,
+    goalMessage : goalMessage
+  });
+  this.currentGoal.on('feedback', this.processFeedback.bind(this));
+  this.currentGoal.send();
+  this.goalUpdateRequested = false;
+};
+
+/**
+ * Subscribe to the given TF frame.
+ *
+ * @param frameID - the TF frame to subscribe to
+ * @param callback - function with params:
+ *   * transform - the transform data
+ */
+ROSLIB.TFClient.prototype.subscribe = function(frameID, callback) {
+  // make sure the frame id is relative
+  if (frameID[0] === '/') {
+    frameID = frameID.substring(1);
+  }
+  // if there is no callback registered for the given frame, create emtpy callback list
+  if (this.frameInfos[frameID] === undefined) {
+    this.frameInfos[frameID] = {
+      cbs : []
+    };
+    if (!this.goalUpdateRequested) {
+      setTimeout(this.updateGoal.bind(this), this.goalUpdateDelay);
+      this.goalUpdateRequested = true;
+    }
+  } else {
+    // if we already have a transform, call back immediately
+    if (this.frameInfos[frameID].transform !== undefined) {
+      callback(this.frameInfos[frameID].transform);
+    }
+  }
+  this.frameInfos[frameID].cbs.push(callback);
+};
+
+/**
+ * Unsubscribe from the given TF frame.
+ *
+ * @param frameID - the TF frame to unsubscribe from
+ * @param callback - the callback function to remove
+ */
+ROSLIB.TFClient.prototype.unsubscribe = function(frameID, callback) {
+  var info = this.frameInfos[frameID];
+  if (info !== undefined) {
+    var cbIndex = info.cbs.indexOf(callback);
+    if (cbIndex >= 0) {
+      info.cbs.splice(cbIndex, 1);
+      if (info.cbs.length === 0) {
+        delete this.frameInfos[frameID];
+      }
+      this.needUpdate = true;
+    }
+  }
+};
+
+
+/**
+ * @author Benjamin Pitzer - ben.pitzer@gmail.com
+ * @author Russell Toris - rctoris@wpi.edu
+ */
+
+/**
+ * A Box element in a URDF.
+ *
+ * @constructor
+ * @param options - object with following keys:
+ *  * xml - the XML element to parse
+ */
+ROSLIB.UrdfBox = function(options) {
+  options = options || {};
+  var that = this;
+  var xml = options.xml;
+  this.dimension = null;
+  this.type = null;
+
+  /**
+   * Initialize the element with the given XML node.
+   *
+   * @param xml - the XML element to parse
+   */
+  var initXml = function(xml) {
+    this.type = ROSLIB.URDF_BOX;
+
+    // Parse the string
+    var xyz = xml.getAttribute('size').split(' ');
+    that.dimension = new ROSLIB.Vector3({
+      x : parseFloat(xyz[0]),
+      y : parseFloat(xyz[1]),
+      z : parseFloat(xyz[2])
+    });
+  };
+
+  // Pass it to the XML parser
+  initXml(xml);
+};
+
+/**
+ * @author Benjamin Pitzer - ben.pitzer@gmail.com
+ * @author Russell Toris - rctoris@wpi.edu
+ */
+
+/**
+ * A Color element in a URDF.
+ *
+ * @constructor
+ * @param options - object with following keys:
+ *  * xml - the XML element to parse
+ */
+ROSLIB.UrdfColor = function(options) {
+  options = options || {};
+  var that = this;
+  var xml = options.xml;
+  this.r = null;
+  this.g = null;
+  this.b = null;
+  this.a = null;
+
+  /**
+   * Initialize the element with the given XML node.
+   *
+   * @param xml - the XML element to parse
+   */
+  var initXml = function(xml) {
+    // Parse the string
+    var rgba = xml.getAttribute('rgba').split(' ');
+    that.r = parseFloat(rgba[0]);
+    that.g = parseFloat(rgba[1]);
+    that.b = parseFloat(rgba[2]);
+    that.a = parseFloat(rgba[3]);
+    return true;
+  };
+
+  // Pass it to the XML parser
+  initXml(xml);
+};
+
+/**
+ * @author Benjamin Pitzer - ben.pitzer@gmail.com
+ * @author Russell Toris - rctoris@wpi.edu
+ */
+
+/**
+ * A Cylinder element in a URDF.
+ *
+ * @constructor
+ * @param options - object with following keys:
+ *  * xml - the XML element to parse
+ */
+ROSLIB.UrdfCylinder = function(options) {
+  options = options || {};
+  var that = this;
+  var xml = options.xml;
+  this.type = null;
+  this.length = null;
+  this.radius = null;
+
+  /**
+   * Initialize the element with the given XML node.
+   *
+   * @param xml - the XML element to parse
+   */
+  var initXml = function(xml) {
+    that.type = ROSLIB.URDF_CYLINDER;
+    that.length = parseFloat(xml.getAttribute('length'));
+    that.radius = parseFloat(xml.getAttribute('radius'));
+  };
+
+  // Pass it to the XML parser
+  initXml(xml);
+};
+
+
+/**
+ * @author Benjamin Pitzer - ben.pitzer@gmail.com
+ * @author Russell Toris - rctoris@wpi.edu
+ */
+
+/**
+ * A Link element in a URDF.
+ *
+ * @constructor
+ * @param options - object with following keys:
+ *  * xml - the XML element to parse
+ */
+ROSLIB.UrdfLink = function(options) {
+  options = options || {};
+  var that = this;
+  var xml = options.xml;
+  this.name = null;
+  this.visual = null;
+
+  /**
+   * Initialize the element with the given XML node.
+   *
+   * @param xml - the XML element to parse
+   */
+  var initXml = function(xml) {
+    that.name = xml.getAttribute('name');
+    var visuals = xml.getElementsByTagName('visual');
+    if (visuals.length > 0) {
+      that.visual = new ROSLIB.UrdfVisual({
+        xml : visuals[0]
+      });
+    }
+  };
+
+  // Pass it to the XML parser
+  initXml(xml);
+};
+
+
+/**
+ * @author Benjamin Pitzer - ben.pitzer@gmail.com
+ * @author Russell Toris - rctoris@wpi.edu
+ */
+
+/**
+ * A Material element in a URDF.
+ *
+ * @constructor
+ * @param options - object with following keys:
+ *  * xml - the XML element to parse
+ */
+ROSLIB.UrdfMaterial = function(options) {
+  options = options || {};
+  var that = this;
+  var xml = options.xml;
+  this.name = null;
+  this.textureFilename = null;
+  this.color = null;
+
+  /**
+   * Initialize the element with the given XML node.
+   *
+   * @param xml - the XML element to parse
+   */
+  var initXml = function(xml) {
+    that.name = xml.getAttribute('name');
+
+    // Texture
+    var textures = xml.getElementsByTagName('texture');
+    if (textures.length > 0) {
+      that.textureFilename = textures[0].getAttribute('filename');
+    }
+
+    // Color
+    var colors = xml.getElementsByTagName('color');
+    if (colors.length > 0) {
+      // Parse the RBGA string
+      that.color = new ROSLIB.UrdfColor({
+        xml : colors[0]
+      });
+    }
+  };
+
+  // Pass it to the XML parser
+  initXml(xml);
+};
+
+/**
+ * @author Benjamin Pitzer - ben.pitzer@gmail.com
+ * @author Russell Toris - rctoris@wpi.edu
+ */
+
+/**
+ * A Mesh element in a URDF.
+ *
+ * @constructor
+ * @param options - object with following keys:
+ *  * xml - the XML element to parse
+ */
+ROSLIB.UrdfMesh = function(options) {
+  options = options || {};
+  var that = this;
+  var xml = options.xml;
+  this.filename = null;
+  this.scale = null;
+  this.type = null;
+
+  /**
+   * Initialize the element with the given XML node.
+   *
+   * @param xml - the XML element to parse
+   */
+  var initXml = function(xml) {
+    that.type = ROSLIB.URDF_MESH;
+    that.filename = xml.getAttribute('filename');
+
+    // Check for a scale
+    var scale = xml.getAttribute('scale');
+    if (scale) {
+      // Get the XYZ
+      var xyz = scale.split(' ');
+      that.scale = new ROSLIB.Vector3({
+        x : parseFloat(xyz[0]),
+        y : parseFloat(xyz[1]),
+        z : parseFloat(xyz[2])
+      });
+    }
+  };
+
+  // Pass it to the XML parser
+  initXml(xml);
+};
+
+
+/**
+ * @author Benjamin Pitzer - ben.pitzer@gmail.com
+ * @author Russell Toris - rctoris@wpi.edu
+ */
+
+/**
+ * A URDF Model can be used to parse a given URDF into the appropriate elements.
+ *
+ * @constructor
+ * @param options - object with following keys:
+ *  * xml - the XML element to parse
+ *  * string - the XML element to parse as a string
+ */
+ROSLIB.UrdfModel = function(options) {
+  options = options || {};
+  var that = this;
+  var xml = options.xml;
+  var string = options.string;
+  this.materials = [];
+  this.links = [];
+
+  /**
+   * Initialize the model with the given XML node.
+   *
+   * @param xml - the XML element to parse
+   */
+  var initXml = function(xml) {
+    // Get the robot tag
+    var robotXml = xml.evaluate('//robot', xml, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
+
+    // Get the robot name
+    that.name = robotXml.getAttribute('name');
+
+    // Parse all the visual elements we need
+    for (var n in robotXml.childNodes) {
+      var node = robotXml.childNodes[n];
+      if (node.tagName === 'material') {
+        var material = new ROSLIB.UrdfMaterial({
+          xml : node
+        });
+        // Make sure this is unique
+        if (that.materials[material.name]) {
+          console.warn('Material ' + material.name + 'is not unique.');
+        } else {
+          that.materials[material.name] = material;
+        }
+      } else if (node.tagName === 'link') {
+        var link = new ROSLIB.UrdfLink({
+          xml : node
+        });
+        // Make sure this is unique
+        if (that.links[link.name]) {
+          console.warn('Link ' + link.name + ' is not unique.');
+        } else {
+          // Check for a material
+          if (link.visual && link.visual.material) {
+            if (that.materials[link.visual.material.name]) {
+              link.visual.material = that.materials[link.visual.material.name];
+            } else if (link.visual.material) {
+              that.materials[link.visual.material.name] = link.visual.material;
+            }
+          }
+
+          // Add the link
+          that.links[link.name] = link;
+        }
+      }
+    }
+  };
+
+  // Check if we are using a string or an XML element
+  if (string) {
+    // Parse the string
+    var parser = new DOMParser();
+    xml = parser.parseFromString(string, 'text/xml');
+  }
+  // Pass it to the XML parser
+  initXml(xml);
+};
+
+
+/**
+ * @author Benjamin Pitzer - ben.pitzer@gmail.com
+ * @author Russell Toris - rctoris@wpi.edu
+ */
+
+/**
+ * A Sphere element in a URDF.
+ *
+ * @constructor
+ * @param options - object with following keys:
+ *  * xml - the XML element to parse
+ */
+ROSLIB.UrdfSphere = function(options) {
+  options = options || {};
+  var that = this;
+  var xml = options.xml;
+  this.radius = null;
+  this.type = null;
+
+  /**
+   * Initialize the element with the given XML node.
+   *
+   * @param xml - the XML element to parse
+   */
+  var initXml = function(xml) {
+    that.type = ROSLIB.URDF_SPHERE;
+    that.radius = parseFloat(xml.getAttribute('radius'));
+  };
+
+  // pass it to the XML parser
+  initXml(xml);
+};
+
+
+/**
+ * @author Benjamin Pitzer - ben.pitzer@gmail.com
+ * @author Russell Toris - rctoris@wpi.edu
+ */
+
+/**
+ * A Visual element in a URDF.
+ *
+ * @constructor
+ * @param options - object with following keys:
+ *  * xml - the XML element to parse
+ */
+ROSLIB.UrdfVisual = function(options) {
+  options = options || {};
+  var that = this;
+  var xml = options.xml;
+  this.origin = null;
+  this.geometry = null;
+  this.material = null;
+
+  /**
+   * Initialize the element with the given XML node.
+   *
+   * @param xml - the XML element to parse
+   */
+  var initXml = function(xml) {
+    // Origin
+    var origins = xml.getElementsByTagName('origin');
+    if (origins.length === 0) {
+      // use the identity as the default
+      that.origin = new ROSLIB.Pose();
+    } else {
+      // Check the XYZ
+      var xyz = origins[0].getAttribute('xyz');
+      var position = new ROSLIB.Vector3();
+      if (xyz) {
+        xyz = xyz.split(' ');
+        position = new ROSLIB.Vector3({
+          x : parseFloat(xyz[0]),
+          y : parseFloat(xyz[1]),
+          z : parseFloat(xyz[2])
+        });
+      }
+
+      // Check the RPY
+      var rpy = origins[0].getAttribute('rpy');
+      var orientation = new ROSLIB.Quaternion();
+      if (rpy) {
+        rpy = rpy.split(' ');
+        // Convert from RPY
+        var roll = parseFloat(rpy[0]);
+        var pitch = parseFloat(rpy[1]);
+        var yaw = parseFloat(rpy[2]);
+        var phi = roll / 2.0;
+        var the = pitch / 2.0;
+        var psi = yaw / 2.0;
+        var x = Math.sin(phi) * Math.cos(the) * Math.cos(psi) - Math.cos(phi) * Math.sin(the)
+            * Math.sin(psi);
+        var y = Math.cos(phi) * Math.sin(the) * Math.cos(psi) + Math.sin(phi) * Math.cos(the)
+            * Math.sin(psi);
+        var z = Math.cos(phi) * Math.cos(the) * Math.sin(psi) - Math.sin(phi) * Math.sin(the)
+            * Math.cos(psi);
+        var w = Math.cos(phi) * Math.cos(the) * Math.cos(psi) + Math.sin(phi) * Math.sin(the)
+            * Math.sin(psi);
+
+        orientation = new ROSLIB.Quaternion({
+          x : x,
+          y : y,
+          z : z,
+          w : w
+        });
+        orientation.normalize();
+      }
+      that.origin = new ROSLIB.Pose({
+        position : position,
+        orientation : orientation
+      });
+    }
+
+    // Geometry
+    var geoms = xml.getElementsByTagName('geometry');
+    if (geoms.length > 0) {
+      var shape = null;
+      // Check for the shape
+      for (var n in geoms[0].childNodes) {
+        var node = geoms[0].childNodes[n];
+        if (node.nodeType === 1) {
+          shape = node;
+          break;
+        }
+      }
+      // Check the type
+      var type = shape.nodeName;
+      if (type === 'sphere') {
+        that.geometry = new ROSLIB.UrdfSphere({
+          xml : shape
+        });
+      } else if (type === 'box') {
+        that.geometry = new ROSLIB.UrdfBox({
+          xml : shape
+        });
+      } else if (type === 'cylinder') {
+        that.geometry = new ROSLIB.UrdfCylinder({
+          xml : shape
+        });
+      } else if (type === 'mesh') {
+        that.geometry = new ROSLIB.UrdfMesh({
+          xml : shape
+        });
+      } else {
+        console.warn('Unknown geometry type ' + type);
+      }
+    }
+
+    // Material
+    var materials = xml.getElementsByTagName('material');
+    if (materials.length > 0) {
+      that.material = new ROSLIB.UrdfMaterial({
+        xml : materials[0]
+      });
+    }
+  };
+
+  // Pass it to the XML parser
+  initXml(xml);
+};
diff --git a/gz3d/client/js/include/roslib.min.js b/gz3d/client/js/include/roslib.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..c6496db855e784772799545d224c2f5f9fe2be25
--- /dev/null
+++ b/gz3d/client/js/include/roslib.min.js
@@ -0,0 +1 @@
+var ROSLIB=ROSLIB||{REVISION:"6"};ROSLIB.URDF_SPHERE=0,ROSLIB.URDF_BOX=1,ROSLIB.URDF_CYLINDER=2,ROSLIB.URDF_MESH=3,ROSLIB.ActionClient=function(t){var e=this;t=t||{},this.ros=t.ros,this.serverName=t.serverName,this.actionName=t.actionName,this.timeout=t.timeout,this.goals={};var i=!1,s=new ROSLIB.Topic({ros:this.ros,name:this.serverName+"/feedback",messageType:this.actionName+"Feedback"}),n=new ROSLIB.Topic({ros:this.ros,name:this.serverName+"/status",messageType:"actionlib_msgs/GoalStatusArray"}),o=new ROSLIB.Topic({ros:this.ros,name:this.serverName+"/result",messageType:this.actionName+"Result"});this.goalTopic=new ROSLIB.Topic({ros:this.ros,name:this.serverName+"/goal",messageType:this.actionName+"Goal"}),this.cancelTopic=new ROSLIB.Topic({ros:this.ros,name:this.serverName+"/cancel",messageType:"actionlib_msgs/GoalID"}),this.goalTopic.advertise(),this.cancelTopic.advertise(),n.subscribe(function(t){i=!0,t.status_list.forEach(function(t){var i=e.goals[t.goal_id.id];i&&i.emit("status",t)})}),s.subscribe(function(t){var i=e.goals[t.status.goal_id.id];i&&(i.emit("status",t.status),i.emit("feedback",t.feedback))}),o.subscribe(function(t){var i=e.goals[t.status.goal_id.id];i&&(i.emit("status",t.status),i.emit("result",t.result))}),this.timeout&&setTimeout(function(){i||e.emit("timeout")},this.timeout)},ROSLIB.ActionClient.prototype.__proto__=EventEmitter2.prototype,ROSLIB.ActionClient.prototype.cancel=function(){var t=new ROSLIB.Message;this.cancelTopic.publish(t)},ROSLIB.Goal=function(t){var e=this;this.actionClient=t.actionClient,this.goalMessage=t.goalMessage,this.isFinished=!1;var i=new Date;this.goalID="goal_"+Math.random()+"_"+i.getTime(),this.goalMessage=new ROSLIB.Message({goal_id:{stamp:{secs:0,nsecs:0},id:this.goalID},goal:this.goalMessage}),this.on("status",function(t){e.status=t}),this.on("result",function(t){e.isFinished=!0,e.result=t}),this.on("feedback",function(t){e.feedback=t}),this.actionClient.goals[this.goalID]=this},ROSLIB.Goal.prototype.__proto__=EventEmitter2.prototype,ROSLIB.Goal.prototype.send=function(t){var e=this;e.actionClient.goalTopic.publish(e.goalMessage),t&&setTimeout(function(){e.isFinished||e.emit("timeout")},t)},ROSLIB.Goal.prototype.cancel=function(){var t=new ROSLIB.Message({id:this.goalID});this.actionClient.cancelTopic.publish(t)},ROSLIB.Message=function(t){var e=this;t=t||{},Object.keys(t).forEach(function(i){e[i]=t[i]})},ROSLIB.Param=function(t){t=t||{},this.ros=t.ros,this.name=t.name},ROSLIB.Param.prototype.get=function(t){var e=new ROSLIB.Service({ros:this.ros,name:"/rosapi/get_param",serviceType:"rosapi/GetParam"}),i=new ROSLIB.ServiceRequest({name:this.name,value:JSON.stringify("")});e.callService(i,function(e){var i=JSON.parse(e.value);t(i)})},ROSLIB.Param.prototype.set=function(t){var e=new ROSLIB.Service({ros:this.ros,name:"/rosapi/set_param",serviceType:"rosapi/SetParam"}),i=new ROSLIB.ServiceRequest({name:this.name,value:JSON.stringify(t)});e.callService(i,function(){})},ROSLIB.Ros=function(t){t=t||{};var e=t.url;this.socket=null,this.idCounter=0,this.setMaxListeners(0),e&&this.connect(e)},ROSLIB.Ros.prototype.__proto__=EventEmitter2.prototype,ROSLIB.Ros.prototype.connect=function(t){function e(t){a.emit("connection",t)}function i(t){a.emit("close",t)}function s(t){a.emit("error",t)}function n(t,e){var i=new Image;i.onload=function(){var t=document.createElement("canvas"),s=t.getContext("2d");t.width=i.width,t.height=i.height,s.drawImage(i,0,0);for(var n=s.getImageData(0,0,i.width,i.height).data,o="",a=0;n.length>a;a+=4)o+=String.fromCharCode(n[a],n[a+1],n[a+2]);var r=JSON.parse(o);e(r)},i.src="data:image/png;base64,"+t.data}function o(t){function e(t){"publish"===t.op?a.emit(t.topic,t.msg):"service_response"===t.op&&a.emit(t.id,t.values)}var i=JSON.parse(t.data);"png"===i.op?n(i,function(t){e(t)}):e(i)}var a=this;this.socket=new WebSocket(t),this.socket.onopen=e,this.socket.onclose=i,this.socket.onerror=s,this.socket.onmessage=o},ROSLIB.Ros.prototype.close=function(){this.socket&&this.socket.close()},ROSLIB.Ros.prototype.authenticate=function(t,e,i,s,n,o,a){var r={op:"auth",mac:t,client:e,dest:i,rand:s,t:n,level:o,end:a};this.callOnConnection(r)},ROSLIB.Ros.prototype.callOnConnection=function(t){var e=this,i=JSON.stringify(t);this.socket&&this.socket.readyState===WebSocket.OPEN?e.socket.send(i):e.once("connection",function(){e.socket.send(i)})},ROSLIB.Ros.prototype.getTopics=function(t){var e=new ROSLIB.Service({ros:this,name:"/rosapi/topics",serviceType:"rosapi/Topics"}),i=new ROSLIB.ServiceRequest;e.callService(i,function(e){t(e.topics)})},ROSLIB.Ros.prototype.getServices=function(t){var e=new ROSLIB.Service({ros:this,name:"/rosapi/services",serviceType:"rosapi/Services"}),i=new ROSLIB.ServiceRequest;e.callService(i,function(e){t(e.services)})},ROSLIB.Ros.prototype.getParams=function(t){var e=new ROSLIB.Service({ros:this,name:"/rosapi/get_param_names",serviceType:"rosapi/GetParamNames"}),i=new ROSLIB.ServiceRequest;e.callService(i,function(e){t(e.names)})},ROSLIB.Service=function(t){t=t||{},this.ros=t.ros,this.name=t.name,this.serviceType=t.serviceType},ROSLIB.Service.prototype.callService=function(t,e){this.ros.idCounter++;var i="call_service:"+this.name+":"+this.ros.idCounter;this.ros.once(i,function(t){var i=new ROSLIB.ServiceResponse(t);e(i)});var s=[];Object.keys(t).forEach(function(e){s.push(t[e])});var n={op:"call_service",id:i,service:this.name,args:s};this.ros.callOnConnection(n)},ROSLIB.ServiceRequest=function(t){var e=this;t=t||{},Object.keys(t).forEach(function(i){e[i]=t[i]})},ROSLIB.ServiceResponse=function(t){var e=this;t=t||{},Object.keys(t).forEach(function(i){e[i]=t[i]})},ROSLIB.Topic=function(t){t=t||{},this.ros=t.ros,this.name=t.name,this.messageType=t.messageType,this.isAdvertised=!1,this.compression=t.compression||"none",this.throttle_rate=t.throttle_rate||0,this.compression&&"png"!==this.compression&&"none"!==this.compression&&this.emit("warning",this.compression+" compression is not supported. No compression will be used."),0>this.throttle_rate&&(this.emit("warning",this.throttle_rate+" is not allowed. Set to 0"),this.throttle_rate=0)},ROSLIB.Topic.prototype.__proto__=EventEmitter2.prototype,ROSLIB.Topic.prototype.subscribe=function(t){var e=this;this.on("message",function(e){t(e)}),this.ros.on(this.name,function(t){var i=new ROSLIB.Message(t);e.emit("message",i)}),this.ros.idCounter++;var i="subscribe:"+this.name+":"+this.ros.idCounter,s={op:"subscribe",id:i,type:this.messageType,topic:this.name,compression:this.compression,throttle_rate:this.throttle_rate};this.ros.callOnConnection(s)},ROSLIB.Topic.prototype.unsubscribe=function(){this.ros.removeAllListeners([this.name]),this.ros.idCounter++;var t="unsubscribe:"+this.name+":"+this.ros.idCounter,e={op:"unsubscribe",id:t,topic:this.name};this.ros.callOnConnection(e)},ROSLIB.Topic.prototype.advertise=function(){this.ros.idCounter++;var t="advertise:"+this.name+":"+this.ros.idCounter,e={op:"advertise",id:t,type:this.messageType,topic:this.name};this.ros.callOnConnection(e),this.isAdvertised=!0},ROSLIB.Topic.prototype.unadvertise=function(){this.ros.idCounter++;var t="unadvertise:"+this.name+":"+this.ros.idCounter,e={op:"unadvertise",id:t,topic:this.name};this.ros.callOnConnection(e),this.isAdvertised=!1},ROSLIB.Topic.prototype.publish=function(t){this.isAdvertised||this.advertise(),this.ros.idCounter++;var e="publish:"+this.name+":"+this.ros.idCounter,i={op:"publish",id:e,topic:this.name,msg:t};this.ros.callOnConnection(i)},ROSLIB.Pose=function(t){t=t||{},this.position=new ROSLIB.Vector3(t.position),this.orientation=new ROSLIB.Quaternion(t.orientation)},ROSLIB.Pose.prototype.applyTransform=function(t){this.position.multiplyQuaternion(t.rotation),this.position.add(t.translation);var e=t.rotation.clone();e.multiply(this.orientation),this.orientation=e},ROSLIB.Pose.prototype.clone=function(){return new ROSLIB.Pose(this)},ROSLIB.Quaternion=function(t){t=t||{},this.x=t.x||0,this.y=t.y||0,this.z=t.z||0,this.w=t.w||1},ROSLIB.Quaternion.prototype.conjugate=function(){this.x*=-1,this.y*=-1,this.z*=-1},ROSLIB.Quaternion.prototype.normalize=function(){var t=Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w);0===t?(this.x=0,this.y=0,this.z=0,this.w=1):(t=1/t,this.x=this.x*t,this.y=this.y*t,this.z=this.z*t,this.w=this.w*t)},ROSLIB.Quaternion.prototype.invert=function(){this.conjugate(),this.normalize()},ROSLIB.Quaternion.prototype.multiply=function(t){var e=this.x*t.w+this.y*t.z-this.z*t.y+this.w*t.x,i=-this.x*t.z+this.y*t.w+this.z*t.x+this.w*t.y,s=this.x*t.y-this.y*t.x+this.z*t.w+this.w*t.z,n=-this.x*t.x-this.y*t.y-this.z*t.z+this.w*t.w;this.x=e,this.y=i,this.z=s,this.w=n},ROSLIB.Quaternion.prototype.clone=function(){return new ROSLIB.Quaternion(this)},ROSLIB.Transform=function(t){t=t||{},this.translation=new ROSLIB.Vector3(t.translation),this.rotation=new ROSLIB.Quaternion(t.rotation)},ROSLIB.Transform.prototype.clone=function(){return new ROSLIB.Transform(this)},ROSLIB.Vector3=function(t){t=t||{},this.x=t.x||0,this.y=t.y||0,this.z=t.z||0},ROSLIB.Vector3.prototype.add=function(t){this.x+=t.x,this.y+=t.y,this.z+=t.z},ROSLIB.Vector3.prototype.subtract=function(t){this.x-=t.x,this.y-=t.y,this.z-=t.z},ROSLIB.Vector3.prototype.multiplyQuaternion=function(t){var e=t.w*this.x+t.y*this.z-t.z*this.y,i=t.w*this.y+t.z*this.x-t.x*this.z,s=t.w*this.z+t.x*this.y-t.y*this.x,n=-t.x*this.x-t.y*this.y-t.z*this.z;this.x=e*t.w+n*-t.x+i*-t.z-s*-t.y,this.y=i*t.w+n*-t.y+s*-t.x-e*-t.z,this.z=s*t.w+n*-t.z+e*-t.y-i*-t.x},ROSLIB.Vector3.prototype.clone=function(){return new ROSLIB.Vector3(this)},ROSLIB.TFClient=function(t){t=t||{},this.ros=t.ros,this.fixedFrame=t.fixedFrame||"/base_link",this.angularThres=t.angularThres||2,this.transThres=t.transThres||.01,this.rate=t.rate||10,this.goalUpdateDelay=t.goalUpdateDelay||50,this.currentGoal=!1,this.frameInfos={},this.goalUpdateRequested=!1,this.actionClient=new ROSLIB.ActionClient({ros:this.ros,serverName:"/tf2_web_republisher",actionName:"tf2_web_republisher/TFSubscriptionAction"})},ROSLIB.TFClient.prototype.processFeedback=function(t){var e=this;t.transforms.forEach(function(t){var i=t.child_frame_id,s=e.frameInfos[i];void 0!==s&&(s.transform=new ROSLIB.Transform({translation:t.transform.translation,rotation:t.transform.rotation}),s.cbs.forEach(function(t){t(s.transform)}))})},ROSLIB.TFClient.prototype.updateGoal=function(){this.currentGoal&&this.currentGoal.cancel();var t={source_frames:[],target_frame:this.fixedFrame,angular_thres:this.angularThres,trans_thres:this.transThres,rate:this.rate};for(var e in this.frameInfos)t.source_frames.push(e);this.currentGoal=new ROSLIB.Goal({actionClient:this.actionClient,goalMessage:t}),this.currentGoal.on("feedback",this.processFeedback.bind(this)),this.currentGoal.send(),this.goalUpdateRequested=!1},ROSLIB.TFClient.prototype.subscribe=function(t,e){"/"===t[0]&&(t=t.substring(1)),void 0===this.frameInfos[t]?(this.frameInfos[t]={cbs:[]},this.goalUpdateRequested||(setTimeout(this.updateGoal.bind(this),this.goalUpdateDelay),this.goalUpdateRequested=!0)):void 0!==this.frameInfos[t].transform&&e(this.frameInfos[t].transform),this.frameInfos[t].cbs.push(e)},ROSLIB.TFClient.prototype.unsubscribe=function(t,e){var i=this.frameInfos[t];if(void 0!==i){var s=i.cbs.indexOf(e);s>=0&&(i.cbs.splice(s,1),0===i.cbs.length&&delete this.frameInfos[t],this.needUpdate=!0)}},ROSLIB.UrdfBox=function(t){t=t||{};var e=this,i=t.xml;this.dimension=null,this.type=null;var s=function(t){this.type=ROSLIB.URDF_BOX;var i=t.getAttribute("size").split(" ");e.dimension=new ROSLIB.Vector3({x:parseFloat(i[0]),y:parseFloat(i[1]),z:parseFloat(i[2])})};s(i)},ROSLIB.UrdfColor=function(t){t=t||{};var e=this,i=t.xml;this.r=null,this.g=null,this.b=null,this.a=null;var s=function(t){var i=t.getAttribute("rgba").split(" ");return e.r=parseFloat(i[0]),e.g=parseFloat(i[1]),e.b=parseFloat(i[2]),e.a=parseFloat(i[3]),!0};s(i)},ROSLIB.UrdfCylinder=function(t){t=t||{};var e=this,i=t.xml;this.type=null,this.length=null,this.radius=null;var s=function(t){e.type=ROSLIB.URDF_CYLINDER,e.length=parseFloat(t.getAttribute("length")),e.radius=parseFloat(t.getAttribute("radius"))};s(i)},ROSLIB.UrdfLink=function(t){t=t||{};var e=this,i=t.xml;this.name=null,this.visual=null;var s=function(t){e.name=t.getAttribute("name");var i=t.getElementsByTagName("visual");i.length>0&&(e.visual=new ROSLIB.UrdfVisual({xml:i[0]}))};s(i)},ROSLIB.UrdfMaterial=function(t){t=t||{};var e=this,i=t.xml;this.name=null,this.textureFilename=null,this.color=null;var s=function(t){e.name=t.getAttribute("name");var i=t.getElementsByTagName("texture");i.length>0&&(e.textureFilename=i[0].getAttribute("filename"));var s=t.getElementsByTagName("color");s.length>0&&(e.color=new ROSLIB.UrdfColor({xml:s[0]}))};s(i)},ROSLIB.UrdfMesh=function(t){t=t||{};var e=this,i=t.xml;this.filename=null,this.scale=null,this.type=null;var s=function(t){e.type=ROSLIB.URDF_MESH,e.filename=t.getAttribute("filename");var i=t.getAttribute("scale");if(i){var s=i.split(" ");e.scale=new ROSLIB.Vector3({x:parseFloat(s[0]),y:parseFloat(s[1]),z:parseFloat(s[2])})}};s(i)},ROSLIB.UrdfModel=function(t){t=t||{};var e=this,i=t.xml,s=t.string;this.materials=[],this.links=[];var n=function(t){var i=t.evaluate("//robot",t,null,XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue;e.name=i.getAttribute("name");for(var s in i.childNodes){var n=i.childNodes[s];if("material"===n.tagName){var o=new ROSLIB.UrdfMaterial({xml:n});e.materials[o.name]?console.warn("Material "+o.name+"is not unique."):e.materials[o.name]=o}else if("link"===n.tagName){var a=new ROSLIB.UrdfLink({xml:n});e.links[a.name]?console.warn("Link "+a.name+" is not unique."):(a.visual&&a.visual.material&&(e.materials[a.visual.material.name]?a.visual.material=e.materials[a.visual.material.name]:a.visual.material&&(e.materials[a.visual.material.name]=a.visual.material)),e.links[a.name]=a)}}};if(s){var o=new DOMParser;i=o.parseFromString(s,"text/xml")}n(i)},ROSLIB.UrdfSphere=function(t){t=t||{};var e=this,i=t.xml;this.radius=null,this.type=null;var s=function(t){e.type=ROSLIB.URDF_SPHERE,e.radius=parseFloat(t.getAttribute("radius"))};s(i)},ROSLIB.UrdfVisual=function(t){t=t||{};var e=this,i=t.xml;this.origin=null,this.geometry=null,this.material=null;var s=function(t){var i=t.getElementsByTagName("origin");if(0===i.length)e.origin=new ROSLIB.Pose;else{var s=i[0].getAttribute("xyz"),n=new ROSLIB.Vector3;s&&(s=s.split(" "),n=new ROSLIB.Vector3({x:parseFloat(s[0]),y:parseFloat(s[1]),z:parseFloat(s[2])}));var o=i[0].getAttribute("rpy"),a=new ROSLIB.Quaternion;if(o){o=o.split(" ");var r=parseFloat(o[0]),h=parseFloat(o[1]),c=parseFloat(o[2]),l=r/2,u=h/2,p=c/2,m=Math.sin(l)*Math.cos(u)*Math.cos(p)-Math.cos(l)*Math.sin(u)*Math.sin(p),f=Math.cos(l)*Math.sin(u)*Math.cos(p)+Math.sin(l)*Math.cos(u)*Math.sin(p),v=Math.cos(l)*Math.cos(u)*Math.sin(p)-Math.sin(l)*Math.sin(u)*Math.cos(p),S=Math.cos(l)*Math.cos(u)*Math.cos(p)+Math.sin(l)*Math.sin(u)*Math.sin(p);a=new ROSLIB.Quaternion({x:m,y:f,z:v,w:S}),a.normalize()}e.origin=new ROSLIB.Pose({position:n,orientation:a})}var R=t.getElementsByTagName("geometry");if(R.length>0){var d=null;for(var g in R[0].childNodes){var O=R[0].childNodes[g];if(1===O.nodeType){d=O;break}}var y=d.nodeName;"sphere"===y?e.geometry=new ROSLIB.UrdfSphere({xml:d}):"box"===y?e.geometry=new ROSLIB.UrdfBox({xml:d}):"cylinder"===y?e.geometry=new ROSLIB.UrdfCylinder({xml:d}):"mesh"===y?e.geometry=new ROSLIB.UrdfMesh({xml:d}):console.warn("Unknown geometry type "+y)}var I=t.getElementsByTagName("material");I.length>0&&(e.material=new ROSLIB.UrdfMaterial({xml:I[0]}))};s(i)};
diff --git a/gz3d/client/js/include/stats.min.js b/gz3d/client/js/include/stats.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..73744ef79b5660b9aec360e162f511ff716ba673
--- /dev/null
+++ b/gz3d/client/js/include/stats.min.js
@@ -0,0 +1,6 @@
+// stats.js - http://github.com/mrdoob/stats.js
+var Stats=function(){var l=Date.now(),m=l,g=0,n=Infinity,o=0,h=0,p=Infinity,q=0,r=0,s=0,f=document.createElement("div");f.id="stats";f.addEventListener("mousedown",function(b){b.preventDefault();t(++s%2)},!1);f.style.cssText="width:80px;opacity:0.9;cursor:pointer";var a=document.createElement("div");a.id="fps";a.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#002";f.appendChild(a);var i=document.createElement("div");i.id="fpsText";i.style.cssText="color:#0ff;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px";
+i.innerHTML="FPS";a.appendChild(i);var c=document.createElement("div");c.id="fpsGraph";c.style.cssText="position:relative;width:74px;height:30px;background-color:#0ff";for(a.appendChild(c);74>c.children.length;){var j=document.createElement("span");j.style.cssText="width:1px;height:30px;float:left;background-color:#113";c.appendChild(j)}var d=document.createElement("div");d.id="ms";d.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#020;display:none";f.appendChild(d);var k=document.createElement("div");
+k.id="msText";k.style.cssText="color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px";k.innerHTML="MS";d.appendChild(k);var e=document.createElement("div");e.id="msGraph";e.style.cssText="position:relative;width:74px;height:30px;background-color:#0f0";for(d.appendChild(e);74>e.children.length;)j=document.createElement("span"),j.style.cssText="width:1px;height:30px;float:left;background-color:#131",e.appendChild(j);var t=function(b){s=b;switch(s){case 0:a.style.display=
+"block";d.style.display="none";break;case 1:a.style.display="none",d.style.display="block"}};return{REVISION:11,domElement:f,setMode:t,begin:function(){l=Date.now()},end:function(){var b=Date.now();g=b-l;n=Math.min(n,g);o=Math.max(o,g);k.textContent=g+" MS ("+n+"-"+o+")";var a=Math.min(30,30-30*(g/200));e.appendChild(e.firstChild).style.height=a+"px";r++;b>m+1E3&&(h=Math.round(1E3*r/(b-m)),p=Math.min(p,h),q=Math.max(q,h),i.textContent=h+" FPS ("+p+"-"+q+")",a=Math.min(30,30-30*(h/100)),c.appendChild(c.firstChild).style.height=
+a+"px",m=b,r=0);return b},update:function(){l=this.end()}}};
diff --git a/gz3d/client/js/include/three.js b/gz3d/client/js/include/three.js
new file mode 100644
index 0000000000000000000000000000000000000000..8a94595e7e862de89c65ee4652552334534984c9
--- /dev/null
+++ b/gz3d/client/js/include/three.js
@@ -0,0 +1,36980 @@
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author Larry Battle / http://bateru.com/news
+ * @author bhouston / http://exocortex.com
+ */
+
+var THREE = { REVISION: '62' };
+
+self.console = self.console || {
+
+	info: function () {},
+	log: function () {},
+	debug: function () {},
+	warn: function () {},
+	error: function () {}
+
+};
+
+String.prototype.trim = String.prototype.trim || function () {
+
+	return this.replace( /^\s+|\s+$/g, '' );
+
+};
+
+// based on https://github.com/documentcloud/underscore/blob/bf657be243a075b5e72acc8a83e6f12a564d8f55/underscore.js#L767
+THREE.extend = function ( obj, source ) {
+
+	// ECMAScript5 compatibility based on: http://www.nczonline.net/blog/2012/12/11/are-your-mixins-ecmascript-5-compatible/
+	if ( Object.keys ) {
+
+		var keys = Object.keys( source );
+
+		for (var i = 0, il = keys.length; i < il; i++) {
+
+			var prop = keys[i];
+			Object.defineProperty( obj, prop, Object.getOwnPropertyDescriptor( source, prop ) );
+
+		}
+
+	} else {
+
+		var safeHasOwnProperty = {}.hasOwnProperty;
+
+		for ( var prop in source ) {
+
+			if ( safeHasOwnProperty.call( source, prop ) ) {
+
+				obj[prop] = source[prop];
+
+			}
+
+		}
+
+	}
+
+	return obj;
+
+};
+
+// http://paulirish.com/2011/requestanimationframe-for-smart-animating/
+// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
+
+// requestAnimationFrame polyfill by Erik Möller
+// fixes from Paul Irish and Tino Zijdel
+// using 'self' instead of 'window' for compatibility with both NodeJS and IE10.
+( function () {
+
+	var lastTime = 0;
+	var vendors = [ 'ms', 'moz', 'webkit', 'o' ];
+
+	for ( var x = 0; x < vendors.length && !self.requestAnimationFrame; ++ x ) {
+
+		self.requestAnimationFrame = self[ vendors[ x ] + 'RequestAnimationFrame' ];
+		self.cancelAnimationFrame = self[ vendors[ x ] + 'CancelAnimationFrame' ] || self[ vendors[ x ] + 'CancelRequestAnimationFrame' ];
+
+	}
+
+	if ( self.requestAnimationFrame === undefined && self['setTimeout'] !== undefined ) {
+
+		self.requestAnimationFrame = function ( callback ) {
+
+			var currTime = Date.now(), timeToCall = Math.max( 0, 16 - ( currTime - lastTime ) );
+			var id = self.setTimeout( function() { callback( currTime + timeToCall ); }, timeToCall );
+			lastTime = currTime + timeToCall;
+			return id;
+
+		};
+
+	}
+
+	if( self.cancelAnimationFrame === undefined && self['clearTimeout'] !== undefined ) {
+
+		self.cancelAnimationFrame = function ( id ) { self.clearTimeout( id ) };
+
+	}
+
+}() );
+
+// 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;
+
+// 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 = function () {};
+
+THREE.CubeReflectionMapping = function () {};
+THREE.CubeRefractionMapping = function () {};
+
+THREE.SphericalReflectionMapping = function () {};
+THREE.SphericalRefractionMapping = function () {};
+
+// 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;
+
+// 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;
+
+/*
+// Potential future 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;
+*/
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.Color = function ( value ) {
+
+	if ( value !== undefined ) this.set( value );
+
+	return this;
+
+};
+
+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 () {
+
+		var hsl = { h: 0, s: 0, l: 0 };
+
+		return function () {
+
+			// h,s,l ranges are in 0.0 - 1.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 };
+
+/**
+ * @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,
+
+	_euler: undefined,
+
+	_updateEuler: function ( callback ) {
+
+		if ( this._euler !== undefined ) {
+
+			this._euler.setFromQuaternion( this, undefined, false );
+
+		}
+
+	},
+
+	get x () {
+
+		return this._x;
+
+	},
+
+	set x ( value ) {
+
+		this._x = value;
+		this._updateEuler();
+
+	},
+
+	get y () {
+
+		return this._y;
+
+	},
+
+	set y ( value ) {
+
+		this._y = value;
+		this._updateEuler();
+
+	},
+
+	get z () {
+
+		return this._z;
+
+	},
+
+	set z ( value ) {
+
+		this._z = value;
+		this._updateEuler();
+
+	},
+
+	get w () {
+
+		return this._w;
+
+	},
+
+	set w ( value ) {
+
+		this._w = value;
+		this._updateEuler();
+
+	},
+
+	set: function ( x, y, z, w ) {
+
+		this._x = x;
+		this._y = y;
+		this._z = z;
+		this._w = w;
+
+		this._updateEuler();
+
+		return this;
+
+	},
+
+	copy: function ( quaternion ) {
+
+		this._x = quaternion._x;
+		this._y = quaternion._y;
+		this._z = quaternion._z;
+		this._w = quaternion._w;
+
+		this._updateEuler();
+
+		return this;
+
+	},
+
+	setFromEuler: function ( euler, update ) {
+
+		if ( euler instanceof THREE.Euler === false ) {
+
+			throw new Error( 'ERROR: Quaternion\'s .setFromEuler() now expects a Euler rotation rather than a Vector3 and order.  Please update your code.' );
+		}
+
+		// 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._updateEuler();
+
+		return this;
+
+	},
+
+	setFromAxisAngle: function ( axis, angle ) {
+
+		// from http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm
+		// axis have to be 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._updateEuler();
+
+		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._updateEuler();
+
+		return this;
+
+	},
+
+	inverse: function () {
+
+		this.conjugate().normalize();
+
+		return this;
+
+	},
+
+	conjugate: function () {
+
+		this._x *= -1;
+		this._y *= -1;
+		this._z *= -1;
+
+		this._updateEuler();
+
+		return this;
+
+	},
+
+	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;
+
+		}
+
+		return this;
+
+	},
+
+	multiply: function ( q, p ) {
+
+		if ( p !== undefined ) {
+
+			console.warn( 'DEPRECATED: Quaternion\'s .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._updateEuler();
+
+		return this;
+
+	},
+
+	multiplyVector3: function ( vector ) {
+
+		console.warn( 'DEPRECATED: Quaternion\'s .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.' );
+		return vector.applyQuaternion( this );
+
+	},
+
+	slerp: function ( qb, t ) {
+
+		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._updateEuler();
+
+		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 ) {
+
+		this._x = array[ 0 ];
+		this._y = array[ 1 ];
+		this._z = array[ 2 ];
+		this._w = array[ 3 ];
+
+		this._updateEuler();
+
+		return this;
+
+	},
+
+	toArray: function () {
+
+		return [ this._x, this._y, this._z, this._w ];
+
+	},
+
+	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 );
+
+}
+
+/**
+ * @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( 'DEPRECATED: Vector2\'s .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( 'DEPRECATED: Vector2\'s .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;
+
+	},
+
+	multiplyScalar: function ( s ) {
+
+		this.x *= s;
+		this.y *= s;
+
+		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;
+
+	},
+
+	negate: function() {
+
+		return this.multiplyScalar( - 1 );
+
+	},
+
+	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 ) {
+
+		this.x = array[ 0 ];
+		this.y = array[ 1 ];
+
+		return this;
+
+	},
+
+	toArray: function () {
+
+		return [ this.x, this.y ];
+
+	},
+
+	clone: function () {
+
+		return new THREE.Vector2( this.x, this.y );
+
+	}
+
+};
+
+/**
+ * @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( 'DEPRECATED: Vector3\'s .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( 'DEPRECATED: Vector3\'s .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( 'DEPRECATED: Vector3\'s .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;
+
+	},
+
+	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;
+
+	},
+
+	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;
+
+	},
+
+	negate: function () {
+
+		return this.multiplyScalar( - 1 );
+
+	},
+
+	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( 'DEPRECATED: Vector3\'s .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;
+
+	},
+
+	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( "REMOVED: Vector3\'s setEulerFromRotationMatrix has been removed in favor of Euler.setFromRotationMatrix(), please update your code.");
+
+	},
+
+	setEulerFromQuaternion: function ( q, order ) {
+
+		console.error( "REMOVED: Vector3\'s setEulerFromQuaternion: has been removed in favor of Euler.setFromQuaternion(), please update your code.");
+
+	},
+
+	getPositionFromMatrix: function ( m ) {
+
+		this.x = m.elements[12];
+		this.y = m.elements[13];
+		this.z = m.elements[14];
+
+		return this;
+
+	},
+
+	getScaleFromMatrix: 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;
+	},
+
+	getColumnFromMatrix: 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 ) {
+
+		this.x = array[ 0 ];
+		this.y = array[ 1 ];
+		this.z = array[ 2 ];
+
+		return this;
+
+	},
+
+	toArray: function () {
+
+		return [ this.x, this.y, this.z ];
+
+	},
+
+	clone: function () {
+
+		return new THREE.Vector3( this.x, this.y, this.z );
+
+	}
+
+};
+
+THREE.extend( THREE.Vector3.prototype, {
+
+	applyEuler: function () {
+
+		var quaternion = new THREE.Quaternion();
+
+		return function ( euler ) {
+
+			if ( euler instanceof THREE.Euler === false ) {
+
+				console.error( 'ERROR: Vector3\'s .applyEuler() now expects a Euler rotation rather than a Vector3 and order.  Please update your code.' );
+
+			}
+
+			this.applyQuaternion( quaternion.setFromEuler( euler ) );
+
+			return this;
+
+		};
+
+	}(),
+
+	applyAxisAngle: function () {
+
+		var quaternion = new THREE.Quaternion();
+
+		return function ( axis, angle ) {
+
+			this.applyQuaternion( quaternion.setFromAxisAngle( axis, angle ) );
+
+			return this;
+
+		};
+
+	}(),
+
+	projectOnVector: function () {
+
+		var v1 = new THREE.Vector3();
+
+		return function ( vector ) {
+
+			v1.copy( vector ).normalize();
+			var d = this.dot( v1 );
+			return this.copy( v1 ).multiplyScalar( d );
+
+		};
+
+	}(),
+
+	projectOnPlane: function () {
+
+		var v1 = new THREE.Vector3();
+
+		return function ( planeNormal ) {
+
+			v1.copy( this ).projectOnVector( planeNormal );
+
+			return this.sub( v1 );
+
+		}
+
+	}(),
+
+	reflect: function () {
+
+		var v1 = new THREE.Vector3();
+
+		return function ( vector ) {
+
+		    v1.copy( this ).projectOnVector( vector ).multiplyScalar( 2 );
+
+		    return this.subVectors( v1, this );
+
+		}
+
+	}()
+
+} );
+
+/**
+ * @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( 'DEPRECATED: Vector4\'s .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( 'DEPRECATED: Vector4\'s .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;
+
+	},
+
+	negate: function() {
+
+		return this.multiplyScalar( -1 );
+
+	},
+
+	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 ) {
+
+		this.x = array[ 0 ];
+		this.y = array[ 1 ];
+		this.z = array[ 2 ];
+		this.w = array[ 3 ];
+
+		return this;
+
+	},
+
+	toArray: function () {
+
+		return [ this.x, this.y, this.z, this.w ];
+
+	},
+
+	clone: function () {
+
+		return new THREE.Vector4( this.x, this.y, this.z, this.w );
+
+	}
+
+};
+
+/**
+ * @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,
+
+	_quaternion: undefined,
+
+	_updateQuaternion: function () {
+
+		if ( this._quaternion !== undefined ) {
+
+			this._quaternion.setFromEuler( this, false );
+
+		}
+
+	},
+
+	get x () {
+
+		return this._x;
+
+	},
+
+	set x ( value ) {
+
+		this._x = value;
+		this._updateQuaternion();
+
+	},
+
+	get y () {
+
+		return this._y;
+
+	},
+
+	set y ( value ) {
+
+		this._y = value;
+		this._updateQuaternion();
+
+	},
+
+	get z () {
+
+		return this._z;
+
+	},
+
+	set z ( value ) {
+
+		this._z = value;
+		this._updateQuaternion();
+
+	},
+
+	get order () {
+
+		return this._order;
+
+	},
+
+	set order ( value ) {
+
+		this._order = value;
+		this._updateQuaternion();
+
+	},
+
+	set: function ( x, y, z, order ) {
+
+		this._x = x;
+		this._y = y;
+		this._z = z;
+		this._order = order || this._order;
+
+		this._updateQuaternion();
+
+		return this;
+
+	},
+
+	copy: function ( euler ) {
+
+		this._x = euler._x;
+		this._y = euler._y;
+		this._z = euler._z;
+		this._order = euler._order;
+
+		this._updateQuaternion();
+
+		return this;
+
+	},
+
+	setFromRotationMatrix: function ( m, order ) {
+
+		// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
+
+		// clamp, to handle numerical problems
+
+		function clamp( x ) {
+
+			return Math.min( Math.max( x, -1 ), 1 );
+
+		}
+
+		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 ) );
+
+			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 ) );
+
+			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 ) );
+
+			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 ) );
+
+			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 ) );
+
+			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 ) );
+
+			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( 'WARNING: Euler.setFromRotationMatrix() given unsupported order: ' + order )
+
+		}
+
+		this._order = order;
+
+		this._updateQuaternion();
+
+		return this;
+
+	},
+
+	setFromQuaternion: function ( q, order, update ) {
+
+		// q is assumed to be normalized
+
+		// clamp, to handle numerical problems
+
+		function clamp( x ) {
+
+			return Math.min( Math.max( x, -1 ), 1 );
+
+		}
+
+		// http://www.mathworks.com/matlabcentral/fileexchange/20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/content/SpinCalc.m
+
+		var sqx = q.x * q.x;
+		var sqy = q.y * q.y;
+		var sqz = q.z * q.z;
+		var sqw = q.w * q.w;
+
+		order = order || this._order;
+
+		if ( order === 'XYZ' ) {
+
+			this._x = Math.atan2( 2 * ( q.x * q.w - q.y * q.z ), ( sqw - sqx - sqy + sqz ) );
+			this._y = Math.asin(  clamp( 2 * ( q.x * q.z + q.y * q.w ) ) );
+			this._z = Math.atan2( 2 * ( q.z * q.w - q.x * q.y ), ( sqw + sqx - sqy - sqz ) );
+
+		} else if ( order ===  'YXZ' ) {
+
+			this._x = Math.asin(  clamp( 2 * ( q.x * q.w - q.y * q.z ) ) );
+			this._y = Math.atan2( 2 * ( q.x * q.z + q.y * q.w ), ( sqw - sqx - sqy + sqz ) );
+			this._z = Math.atan2( 2 * ( q.x * q.y + q.z * q.w ), ( sqw - sqx + sqy - sqz ) );
+
+		} else if ( order === 'ZXY' ) {
+
+			this._x = Math.asin(  clamp( 2 * ( q.x * q.w + q.y * q.z ) ) );
+			this._y = Math.atan2( 2 * ( q.y * q.w - q.z * q.x ), ( sqw - sqx - sqy + sqz ) );
+			this._z = Math.atan2( 2 * ( q.z * q.w - q.x * q.y ), ( sqw - sqx + sqy - sqz ) );
+
+		} else if ( order === 'ZYX' ) {
+
+			this._x = Math.atan2( 2 * ( q.x * q.w + q.z * q.y ), ( sqw - sqx - sqy + sqz ) );
+			this._y = Math.asin(  clamp( 2 * ( q.y * q.w - q.x * q.z ) ) );
+			this._z = Math.atan2( 2 * ( q.x * q.y + q.z * q.w ), ( sqw + sqx - sqy - sqz ) );
+
+		} else if ( order === 'YZX' ) {
+
+			this._x = Math.atan2( 2 * ( q.x * q.w - q.z * q.y ), ( sqw - sqx + sqy - sqz ) );
+			this._y = Math.atan2( 2 * ( q.y * q.w - q.x * q.z ), ( sqw + sqx - sqy - sqz ) );
+			this._z = Math.asin(  clamp( 2 * ( q.x * q.y + q.z * q.w ) ) );
+
+		} else if ( order === 'XZY' ) {
+
+			this._x = Math.atan2( 2 * ( q.x * q.w + q.y * q.z ), ( sqw - sqx + sqy - sqz ) );
+			this._y = Math.atan2( 2 * ( q.x * q.z + q.y * q.w ), ( sqw + sqx - sqy - sqz ) );
+			this._z = Math.asin(  clamp( 2 * ( q.z * q.w - q.x * q.y ) ) );
+
+		} else {
+
+			console.warn( 'WARNING: Euler.setFromQuaternion() given unsupported order: ' + order )
+
+		}
+
+		this._order = order;
+
+		if ( update !== false ) this._updateQuaternion();
+
+		return this;
+
+	},
+
+	reorder: function () {
+
+		// WARNING: this discards revolution information -bhouston
+
+		var q = new THREE.Quaternion();
+
+		return function ( newOrder ) {
+
+			q.setFromEuler( this );
+			this.setFromQuaternion( q, newOrder );
+
+		};
+
+
+	}(),
+
+	fromArray: function ( array ) {
+
+		this._x = array[ 0 ];
+		this._y = array[ 1 ];
+		this._z = array[ 2 ];
+		if ( array[ 3 ] !== undefined ) this._order = array[ 3 ];
+
+		this._updateQuaternion();
+
+		return this;
+
+	},
+
+	toArray: function () {
+
+		return [ this._x, this._y, this._z, this._order ];
+
+	},
+
+	equals: function ( euler ) {
+
+		return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order );
+
+	},
+
+	clone: function () {
+
+		return new THREE.Euler( this._x, this._y, this._z, this._order );
+
+	}
+
+};
+
+/**
+ * @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 );
+
+	}
+
+};
+
+/**
+ * @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 ) {
+
+		if ( points.length > 0 ) {
+
+			var point = points[ 0 ];
+
+			this.min.copy( point );
+			this.max.copy( point );
+
+			for ( var i = 1, il = points.length; i < il; i ++ ) {
+
+				point = points[ i ];
+
+				if ( point.x < this.min.x ) {
+
+					this.min.x = point.x;
+
+				} else if ( point.x > this.max.x ) {
+
+					this.max.x = point.x;
+
+				}
+
+				if ( point.y < this.min.y ) {
+
+					this.min.y = point.y;
+
+				} else if ( point.y > this.max.y ) {
+
+					this.max.y = point.y;
+
+				}
+
+			}
+
+		} else {
+
+			this.makeEmpty();
+
+		}
+
+		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 ) {
+
+		// This can potentially have a divide by zero if the box
+		// has a size dimension of 0.
+
+		return new THREE.Vector2(
+			( 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 );
+
+	}
+
+};
+
+/**
+ * @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;
+
+	},
+
+	addPoint: function ( point ) {
+
+		if ( point.x < this.min.x ) {
+
+			this.min.x = point.x;
+
+		} else if ( point.x > this.max.x ) {
+
+			this.max.x = point.x;
+
+		}
+
+		if ( point.y < this.min.y ) {
+
+			this.min.y = point.y;
+
+		} else if ( point.y > this.max.y ) {
+
+			this.max.y = point.y;
+
+		}
+
+		if ( point.z < this.min.z ) {
+
+			this.min.z = point.z;
+
+		} else if ( point.z > this.max.z ) {
+
+			this.max.z = point.z;
+
+		}
+
+	},
+
+	setFromPoints: function ( points ) {
+
+		if ( points.length > 0 ) {
+
+			var point = points[ 0 ];
+
+			this.min.copy( point );
+			this.max.copy( point );
+
+			for ( var i = 1, il = points.length; i < il; i ++ ) {
+
+				this.addPoint( points[ i ] )
+
+			}
+
+		} else {
+
+			this.makeEmpty();
+
+		}
+
+		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 ) {
+
+				if ( node.geometry !== undefined && node.geometry.vertices !== undefined ) {
+
+					var vertices = node.geometry.vertices;
+
+					for ( var i = 0, il = vertices.length; i < il; i++ ) {
+
+						v1.copy( vertices[ i ] );
+
+						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 ) {
+
+		// This can potentially have a divide by zero if the box
+		// has a size dimension of 0.
+
+		return new THREE.Vector3(
+			( 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 );
+
+	}
+
+};
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ * @author WestLangley / http://github.com/WestLangley
+ * @author bhouston / http://exocortex.com
+ */
+
+THREE.Matrix3 = function ( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) {
+
+	this.elements = new Float32Array(9);
+
+	this.set(
+
+		( n11 !== undefined ) ? n11 : 1, n12 || 0, n13 || 0,
+		n21 || 0, ( n22 !== undefined ) ? n22 : 1, n23 || 0,
+		n31 || 0, n32 || 0, ( n33 !== undefined ) ? n33 : 1
+
+	);
+};
+
+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( 'DEPRECATED: Matrix3\'s .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead.' );
+		return vector.applyMatrix3( this );
+
+	},
+
+	multiplyVector3Array: function() {
+
+		var v1 = new THREE.Vector3();
+
+		return function ( a ) {
+
+			for ( var i = 0, il = a.length; i < il; i += 3 ) {
+
+				v1.x = a[ i ];
+				v1.y = a[ i + 1 ];
+				v1.z = a[ i + 2 ];
+
+				v1.applyMatrix3(this);
+
+				a[ i ]     = v1.x;
+				a[ i + 1 ] = v1.y;
+				a[ i + 2 ] = v1.z;
+
+			}
+
+			return a;
+
+		};
+
+	}(),
+
+	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;
+
+	},
+
+	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;
+
+	},
+
+	clone: function () {
+
+		var te = this.elements;
+
+		return new THREE.Matrix3(
+
+			te[0], te[3], te[6],
+			te[1], te[4], te[7],
+			te[2], te[5], te[8]
+
+		);
+
+	}
+
+};
+
+/**
+ * @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 ( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) {
+
+	this.elements = new Float32Array( 16 );
+
+	// TODO: if n11 is undefined, then just set to identity, otherwise copy all other values into matrix
+	//   we should not support semi specification of Matrix4, it is just weird.
+
+	var te = this.elements;
+
+	te[0] = ( n11 !== undefined ) ? n11 : 1; te[4] = n12 || 0; te[8] = n13 || 0; te[12] = n14 || 0;
+	te[1] = n21 || 0; te[5] = ( n22 !== undefined ) ? n22 : 1; te[9] = n23 || 0; te[13] = n24 || 0;
+	te[2] = n31 || 0; te[6] = n32 || 0; te[10] = ( n33 !== undefined ) ? n33 : 1; te[14] = n34 || 0;
+	te[3] = n41 || 0; te[7] = n42 || 0; te[11] = n43 || 0; te[15] = ( n44 !== undefined ) ? n44 : 1;
+
+};
+
+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( 'DEPRECATED: Matrix4\'s .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;
+
+	},
+
+	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( 'ERROR: Matrix\'s .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.  Please update your code.' );
+
+		}
+
+		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( 'DEPRECATED: Matrix4\'s .setRotationFromQuaternion() has been deprecated in favor of makeRotationFromQuaternion.  Please update your code.' );
+
+		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( 'DEPRECATED: Matrix4\'s .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( 'DEPRECATED: Matrix4\'s .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) or vector.applyProjection( matrix ) instead.' );
+		return vector.applyProjection( this );
+
+	},
+
+	multiplyVector4: function ( vector ) {
+
+		console.warn( 'DEPRECATED: Matrix4\'s .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead.' );
+		return vector.applyMatrix4( this );
+
+	},
+
+	multiplyVector3Array: function() {
+
+		var v1 = new THREE.Vector3();
+
+		return function ( a ) {
+
+			for ( var i = 0, il = a.length; i < il; i += 3 ) {
+
+				v1.x = a[ i ];
+				v1.y = a[ i + 1 ];
+				v1.z = a[ i + 2 ];
+
+				v1.applyProjection( this );
+
+				a[ i ]     = v1.x;
+				a[ i + 1 ] = v1.y;
+				a[ i + 2 ] = v1.z;
+
+			}
+
+			return a;
+
+		};
+
+	}(),
+
+	rotateAxis: function ( v ) {
+
+		console.warn( 'DEPRECATED: Matrix4\'s .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead.' );
+
+		v.transformDirection( this );
+
+	},
+
+	crossVector: function ( vector ) {
+
+		console.warn( 'DEPRECATED: Matrix4\'s .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;
+
+	},
+
+	flattenToArray: function ( flat ) {
+
+		var te = this.elements;
+		flat[ 0 ] = te[0]; flat[ 1 ] = te[1]; flat[ 2 ] = te[2]; flat[ 3 ] = te[3];
+		flat[ 4 ] = te[4]; flat[ 5 ] = te[5]; flat[ 6 ] = te[6]; flat[ 7 ] = te[7];
+		flat[ 8 ] = te[8]; flat[ 9 ] = te[9]; flat[ 10 ] = te[10]; flat[ 11 ] = te[11];
+		flat[ 12 ] = te[12]; flat[ 13 ] = te[13]; flat[ 14 ] = te[14]; flat[ 15 ] = te[15];
+
+		return flat;
+
+	},
+
+	flattenToArrayOffset: function( flat, offset ) {
+
+		var te = this.elements;
+		flat[ offset ] = te[0];
+		flat[ offset + 1 ] = te[1];
+		flat[ offset + 2 ] = te[2];
+		flat[ offset + 3 ] = te[3];
+
+		flat[ offset + 4 ] = te[4];
+		flat[ offset + 5 ] = te[5];
+		flat[ offset + 6 ] = te[6];
+		flat[ offset + 7 ] = te[7];
+
+		flat[ offset + 8 ]  = te[8];
+		flat[ offset + 9 ]  = te[9];
+		flat[ offset + 10 ] = te[10];
+		flat[ offset + 11 ] = te[11];
+
+		flat[ offset + 12 ] = te[12];
+		flat[ offset + 13 ] = te[13];
+		flat[ offset + 14 ] = te[14];
+		flat[ offset + 15 ] = te[15];
+
+		return flat;
+
+	},
+
+	getPosition: function() {
+
+		var v1 = new THREE.Vector3();
+
+		return function () {
+
+			console.warn( 'DEPRECATED: Matrix4\'s .getPosition() has been removed. Use Vector3.getPositionFromMatrix( 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( 'DEPRECATED: Matrix4\'s .translate() has been removed.');
+
+	},
+
+	rotateX: function ( angle ) {
+
+		console.warn( 'DEPRECATED: Matrix4\'s .rotateX() has been removed.');
+
+	},
+
+	rotateY: function ( angle ) {
+
+		console.warn( 'DEPRECATED: Matrix4\'s .rotateY() has been removed.');
+
+	},
+
+	rotateZ: function ( angle ) {
+
+		console.warn( 'DEPRECATED: Matrix4\'s .rotateZ() has been removed.');
+
+	},
+
+	rotateByAxis: function ( axis, angle ) {
+
+		console.warn( 'DEPRECATED: Matrix4\'s .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();
+
+			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 () {
+
+		var te = this.elements;
+
+		return new THREE.Matrix4(
+
+			te[0], te[4], te[8], te[12],
+			te[1], te[5], te[9], te[13],
+			te[2], te[6], te[10], te[14],
+			te[3], te[7], te[11], te[15]
+
+		);
+
+	}
+
+};
+
+/**
+ * @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( 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
+
+		var segCenter = v0.clone().add( v1 ).multiplyScalar( 0.5 );
+		var segDir = v1.clone().sub( v0 ).normalize();
+		var segExtent = v0.distanceTo( v1 ) * 0.5;
+		var diff = this.origin.clone().sub( segCenter );
+		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.clone().multiplyScalar( s0 ).add( this.origin ) );
+
+		}
+
+		if ( optionalPointOnSegment ) {
+
+			optionalPointOnSegment.copy( segDir.clone().multiplyScalar( s1 ).add( segCenter ) );
+
+		}
+
+		return sqrDist;
+
+	},
+
+	isIntersectionSphere: function ( sphere ) {
+
+		return this.distanceToPoint( sphere.center ) <= sphere.radius;
+
+	},
+
+	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 );
+
+	}
+
+};
+
+/**
+ * @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 );
+
+	}
+
+};
+
+/**
+ * @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 );
+
+	}
+
+};
+
+/**
+ * @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();
+
+		return function ( matrix, optionalNormalMatrix ) {
+
+			// compute new normal based on theory here:
+			// http://www.songho.ca/opengl/gl_normaltransform.html
+			optionalNormalMatrix = optionalNormalMatrix || new THREE.Matrix3().getNormalMatrix( matrix );
+			var newNormal = v1.copy( this.normal ).applyMatrix3( optionalNormalMatrix );
+
+			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 );
+
+	}
+
+};
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.Math = {
+
+	PI2: Math.PI * 2,
+
+	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 low + Math.floor( Math.random() * ( high - low + 1 ) );
+
+	},
+
+	// 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() );
+
+	},
+
+	sign: function ( x ) {
+
+		return ( x < 0 ) ? -1 : ( ( x > 0 ) ? 1 : 0 );
+
+	},
+
+	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;
+
+		};
+
+	}()
+
+};
+
+/**
+ * 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;
+
+	};
+
+};
+
+/**
+ * @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 );
+
+	}
+
+};
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.Vertex = function ( v ) {
+
+	console.warn( 'THREE.Vertex has been DEPRECATED. Use THREE.Vector3 instead.')
+	return v;
+
+};
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.UV = function ( u, v ) {
+
+	console.warn( 'THREE.UV has been DEPRECATED. Use THREE.Vector2 instead.')
+	return new THREE.Vector2( u, v );
+
+};
+
+/**
+ * @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;
+
+	}
+
+};
+
+/**
+ * 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 index = listeners[ type ].indexOf( listener );
+
+		if ( index !== - 1 ) {
+
+			listeners[ type ].splice( index, 1 );
+
+		}
+
+	},
+
+	dispatchEvent: function () {
+
+		var array = [];
+
+		return function ( event ) {
+
+			if ( this._listeners === undefined ) return;
+
+			var listeners = this._listeners;
+			var listenerArray = listeners[ event.type ];
+
+			if ( listenerArray !== undefined ) {
+
+				event.target = this;
+
+				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 );
+
+				}
+
+			}
+
+		};
+
+	}()
+
+};
+
+/**
+ * @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;
+
+	};
+
+	var sphere = new THREE.Sphere();
+	var localRay = new THREE.Ray();
+	var facePlane = new THREE.Plane();
+	var intersectPoint = new THREE.Vector3();
+	var matrixPosition = new THREE.Vector3();
+
+	var inverseMatrix = new THREE.Matrix4();
+
+	var descSort = function ( a, b ) {
+
+		return a.distance - b.distance;
+
+	};
+
+	var vA = new THREE.Vector3();
+	var vB = new THREE.Vector3();
+	var vC = new THREE.Vector3();
+
+	var intersectObject = function ( object, raycaster, intersects ) {
+
+		if ( object instanceof THREE.Sprite ) {
+
+			matrixPosition.getPositionFromMatrix( object.matrixWorld );
+			var distance = raycaster.ray.distanceToPoint( matrixPosition );
+
+			if ( distance > object.scale.x ) {
+
+				return intersects;
+
+			}
+
+			intersects.push( {
+
+				distance: distance,
+				point: object.position,
+				face: null,
+				object: object
+
+			} );
+
+		} else if ( object instanceof THREE.LOD ) {
+
+			matrixPosition.getPositionFromMatrix( object.matrixWorld );
+			var distance = raycaster.ray.origin.distanceTo( matrixPosition );
+
+			intersectObject( object.getObjectForDistance( distance ), raycaster, intersects );
+
+		} else if ( object instanceof THREE.Mesh ) {
+
+			var geometry = object.geometry;
+
+			// Checking boundingSphere distance to ray
+
+			if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();
+
+			sphere.copy( geometry.boundingSphere );
+			sphere.applyMatrix4( object.matrixWorld );
+
+			if ( raycaster.ray.isIntersectionSphere( sphere ) === false ) {
+
+				return intersects;
+
+			}
+
+			// Check boundingBox before continuing
+
+			inverseMatrix.getInverse( object.matrixWorld );
+			localRay.copy( raycaster.ray ).applyMatrix4( inverseMatrix );
+
+			if ( geometry.boundingBox !== null ) {
+
+				if ( localRay.isIntersectionBox( geometry.boundingBox ) === false )  {
+
+					return intersects;
+
+				}
+
+			}
+
+			if ( geometry instanceof THREE.BufferGeometry ) {
+
+				var material = object.material;
+
+				if ( material === undefined ) return intersects;
+				if ( geometry.dynamic === false ) return intersects;
+
+				var a, b, c;
+				var precision = raycaster.precision;
+
+				if ( geometry.attributes.index !== undefined ) {
+
+					var offsets = geometry.offsets;
+					var indices = geometry.attributes.index.array;
+					var positions = geometry.attributes.position.array;
+					var offLength = geometry.offsets.length;
+
+					var fl = geometry.attributes.index.array.length / 3;
+
+					for ( var oi = 0; oi < offLength; ++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.set(
+								positions[ a * 3 ],
+								positions[ a * 3 + 1 ],
+								positions[ a * 3 + 2 ]
+							);
+							vB.set(
+								positions[ b * 3 ],
+								positions[ b * 3 + 1 ],
+								positions[ b * 3 + 2 ]
+							);
+							vC.set(
+								positions[ c * 3 ],
+								positions[ c * 3 + 1 ],
+								positions[ c * 3 + 2 ]
+							);
+
+
+							if ( material.side === THREE.BackSide ) {
+
+								var intersectionPoint = localRay.intersectTriangle( vC, vB, vA, true );
+
+							} else {
+
+								var intersectionPoint = localRay.intersectTriangle( vA, vB, vC, material.side !== THREE.DoubleSide );
+
+							}
+
+							if ( intersectionPoint === null ) continue;
+
+							intersectionPoint.applyMatrix4( object.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: null,
+								faceIndex: null,
+								object: object
+
+							} );
+
+						}
+
+					}
+
+				} else {
+
+					var offsets = geometry.offsets;
+					var positions = geometry.attributes.position.array;
+					var offLength = geometry.offsets.length;
+
+					var fl = geometry.attributes.position.array.length;
+
+					for ( var i = 0; i < fl; i += 3 ) {
+
+						a = i;
+						b = i + 1;
+						c = i + 2;
+
+						vA.set(
+							positions[ a * 3 ],
+							positions[ a * 3 + 1 ],
+							positions[ a * 3 + 2 ]
+						);
+						vB.set(
+							positions[ b * 3 ],
+							positions[ b * 3 + 1 ],
+							positions[ b * 3 + 2 ]
+						);
+						vC.set(
+							positions[ c * 3 ],
+							positions[ c * 3 + 1 ],
+							positions[ c * 3 + 2 ]
+						);
+
+
+						if ( material.side === THREE.BackSide ) {
+
+							var intersectionPoint = localRay.intersectTriangle( vC, vB, vA, true );
+
+						} else {
+
+							var intersectionPoint = localRay.intersectTriangle( vA, vB, vC, material.side !== THREE.DoubleSide );
+
+						}
+
+						if ( intersectionPoint === null ) continue;
+
+						intersectionPoint.applyMatrix4( object.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: null,
+							faceIndex: null,
+							object: object
+
+						} );
+
+					}
+
+				}
+
+			} else if ( geometry instanceof THREE.Geometry ) {
+
+				var isFaceMaterial = object.material instanceof THREE.MeshFaceMaterial;
+				var objectMaterials = isFaceMaterial === true ? object.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 ] : object.material;
+
+					if ( material === undefined ) continue;
+
+					a = vertices[ face.a ];
+					b = vertices[ face.b ];
+					c = vertices[ face.c ];
+
+					if ( material.side === THREE.BackSide ) {
+
+						var intersectionPoint = localRay.intersectTriangle( c, b, a, true );
+
+					} else {
+
+						var intersectionPoint = localRay.intersectTriangle( a, b, c, material.side !== THREE.DoubleSide );
+
+					}
+
+					if ( intersectionPoint === null ) continue;
+
+					intersectionPoint.applyMatrix4( object.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: object
+
+					} );
+
+				}
+
+			}
+
+		} else if ( object instanceof THREE.Line ) {
+
+			var precision = raycaster.linePrecision;
+			var precisionSq = precision * precision;
+
+			var geometry = object.geometry;
+
+			if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();
+
+			// Checking boundingSphere distance to ray
+
+			sphere.copy( geometry.boundingSphere );
+			sphere.applyMatrix4( object.matrixWorld );
+
+			if ( raycaster.ray.isIntersectionSphere( sphere ) === false ) {
+
+				return intersects;
+
+			}
+
+			inverseMatrix.getInverse( object.matrixWorld );
+			localRay.copy( raycaster.ray ).applyMatrix4( inverseMatrix );
+
+			/* if ( geometry instanceof THREE.BufferGeometry ) {
+
+			} else */ if ( geometry instanceof THREE.Geometry ) {
+
+				var vertices = geometry.vertices;
+				var nbVertices = vertices.length;
+				var interSegment = new THREE.Vector3();
+				var interRay = new THREE.Vector3();
+				var step = object.type === THREE.LineStrip ? 1 : 2;
+
+				for ( var i = 0; i < nbVertices - 1; i = i + step ) {
+
+					var distSq = localRay.distanceSqToSegment( vertices[ i ], vertices[ i + 1 ], interRay, interSegment );
+
+					if ( distSq > precisionSq ) continue;
+
+					var distance = localRay.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( object.matrixWorld ),
+						face: null,
+						faceIndex: null,
+						object: object
+
+					} );
+
+				}
+
+			}
+
+		}
+
+	};
+
+	var intersectDescendants = function ( object, raycaster, intersects ) {
+
+		var descendants = object.getDescendants();
+
+		for ( var i = 0, l = descendants.length; i < l; i ++ ) {
+
+			intersectObject( descendants[ i ], raycaster, intersects );
+
+		}
+	};
+
+	//
+
+	THREE.Raycaster.prototype.precision = 0.0001;
+	THREE.Raycaster.prototype.linePrecision = 1;
+
+	THREE.Raycaster.prototype.set = function ( origin, direction ) {
+
+		this.ray.set( origin, direction );
+		// direction is assumed to be normalized (for accurate distance calculations)
+
+	};
+
+	THREE.Raycaster.prototype.intersectObject = function ( object, recursive ) {
+
+		var intersects = [];
+
+		if ( recursive === true ) {
+
+			intersectDescendants( object, this, intersects );
+
+		}
+
+		intersectObject( object, this, intersects );
+
+		intersects.sort( descSort );
+
+		return intersects;
+
+	};
+
+	THREE.Raycaster.prototype.intersectObjects = function ( objects, recursive ) {
+
+		var intersects = [];
+
+		for ( var i = 0, l = objects.length; i < l; i ++ ) {
+
+			intersectObject( objects[ i ], this, intersects );
+
+			if ( recursive === true ) {
+
+				intersectDescendants( objects[ i ], this, intersects );
+
+			}
+
+		}
+
+		intersects.sort( descSort );
+
+		return intersects;
+
+	};
+
+}( THREE ) );
+
+/**
+ * @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 () {
+
+	this.id = THREE.Object3DIdCount ++;
+	this.uuid = THREE.Math.generateUUID();
+
+	this.name = '';
+
+	this.parent = undefined;
+	this.children = [];
+
+	this.up = new THREE.Vector3( 0, 1, 0 );
+
+	this.position = new THREE.Vector3();
+	this.rotation = new THREE.Euler();
+	this.quaternion = new THREE.Quaternion();
+	this.scale = new THREE.Vector3( 1, 1, 1 );
+
+	// keep rotation and quaternion in sync
+
+	this.rotation._quaternion = this.quaternion;
+	this.quaternion._euler = this.rotation;
+
+	this.renderDepth = null;
+
+	this.rotationAutoUpdate = true;
+
+	this.matrix = new THREE.Matrix4();
+	this.matrixWorld = new THREE.Matrix4();
+
+	this.matrixAutoUpdate = true;
+	this.matrixWorldNeedsUpdate = true;
+
+	this.visible = true;
+
+	this.castShadow = false;
+	this.receiveShadow = false;
+
+	this.frustumCulled = true;
+
+	this.userData = {};
+
+};
+
+
+THREE.Object3D.prototype = {
+
+	constructor: THREE.Object3D,
+
+	get eulerOrder () {
+
+		console.warn( 'DEPRECATED: Object3D\'s .eulerOrder has been moved to Object3D\'s .rotation.order.' );
+
+		return this.rotation.order;
+
+	},
+
+	set eulerOrder ( value ) {
+
+		console.warn( 'DEPRECATED: Object3D\'s .eulerOrder has been moved to Object3D\'s .rotation.order.' );
+
+		this.rotation.order = value;
+
+	},
+
+	get useQuaternion () {
+
+		console.warn( 'DEPRECATED: Object3D\'s .useQuaternion has been removed. The library now uses quaternions by default.' );
+
+	},
+
+	set useQuaternion ( value ) {
+
+		console.warn( 'DEPRECATED: Object3D\'s .useQuaternion has been removed. The library now uses quaternions by default.' );
+
+	},
+
+	applyMatrix: function () {
+
+		var m1 = new THREE.Matrix4();
+
+		return function ( matrix ) {
+
+			this.matrix.multiplyMatrices( matrix, this.matrix );
+
+			this.position.getPositionFromMatrix( this.matrix );
+
+			this.scale.getScaleFromMatrix( this.matrix );
+
+			m1.extractRotation( this.matrix );
+
+			this.quaternion.setFromRotationMatrix( m1 );
+
+		}
+
+	}(),
+
+	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 );
+
+			v1.applyQuaternion( this.quaternion );
+
+			this.position.add( v1.multiplyScalar( distance ) );
+
+			return this;
+
+		}
+
+	}(),
+
+	translate: function ( distance, axis ) {
+
+		console.warn( 'DEPRECATED: Object3D\'s .translate() has been removed. Use .translateOnAxis( axis, distance ) instead. Note args have been changed.' );
+		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 ( object === this ) {
+
+			console.warn( 'THREE.Object3D.add: An object can\'t be added as a child of itself.' );
+			return;
+
+		}
+
+		if ( object instanceof THREE.Object3D ) {
+
+			if ( object.parent !== undefined ) {
+
+				object.parent.remove( object );
+
+			}
+
+			object.parent = this;
+			object.dispatchEvent( { type: 'added' } );
+
+			this.children.push( object );
+
+			// add to scene
+
+			var scene = this;
+
+			while ( scene.parent !== undefined ) {
+
+				scene = scene.parent;
+
+			}
+
+			if ( scene !== undefined && scene instanceof THREE.Scene )  {
+
+				scene.__addObject( object );
+
+			}
+
+		}
+
+	},
+
+	remove: function ( object ) {
+
+		var index = this.children.indexOf( object );
+
+		if ( index !== - 1 ) {
+
+			object.parent = undefined;
+			object.dispatchEvent( { type: 'removed' } );
+
+			this.children.splice( index, 1 );
+
+			// remove from scene
+
+			var scene = this;
+
+			while ( scene.parent !== undefined ) {
+
+				scene = scene.parent;
+
+			}
+
+			if ( scene !== undefined && scene instanceof THREE.Scene ) {
+
+				scene.__removeObject( object );
+
+			}
+
+		}
+
+	},
+
+	traverse: function ( callback ) {
+
+		callback( this );
+
+		for ( var i = 0, l = this.children.length; i < l; i ++ ) {
+
+			this.children[ i ].traverse( callback );
+
+		}
+
+	},
+
+	getObjectById: function ( id, recursive ) {
+
+		for ( var i = 0, l = this.children.length; i < l; i ++ ) {
+
+			var child = this.children[ i ];
+
+			if ( child.id === id ) {
+
+				return child;
+
+			}
+
+			if ( recursive === true ) {
+
+				child = child.getObjectById( id, recursive );
+
+				if ( child !== undefined ) {
+
+					return child;
+
+				}
+
+			}
+
+		}
+
+		return undefined;
+
+	},
+
+	getObjectByName: function ( name, recursive ) {
+
+		for ( var i = 0, l = this.children.length; i < l; i ++ ) {
+
+			var child = this.children[ i ];
+
+			if ( child.name === name ) {
+
+				return child;
+
+			}
+
+			if ( recursive === true ) {
+
+				child = child.getObjectByName( name, recursive );
+
+				if ( child !== undefined ) {
+
+					return child;
+
+				}
+
+			}
+
+		}
+
+		return undefined;
+
+	},
+
+	getChildByName: function ( name, recursive ) {
+
+		console.warn( 'DEPRECATED: Object3D\'s .getChildByName() has been renamed to .getObjectByName().' );
+		return this.getObjectByName( name, recursive );
+
+	},
+
+	getDescendants: function ( array ) {
+
+		if ( array === undefined ) array = [];
+
+		Array.prototype.push.apply( array, this.children );
+
+		for ( var i = 0, l = this.children.length; i < l; i ++ ) {
+
+			this.children[ i ].getDescendants( array );
+
+		}
+
+		return array;
+
+	},
+
+	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 );
+
+		}
+
+	},
+
+	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.renderDepth = this.renderDepth;
+
+		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;
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author supereggbert / http://www.paulbrunt.co.uk/
+ * @author julianwa / https://github.com/julianwa
+ */
+
+THREE.Projector = function () {
+
+	var _object, _objectCount, _objectPool = [], _objectPoolLength = 0,
+	_vertex, _vertexCount, _vertexPool = [], _vertexPoolLength = 0,
+	_face, _face3Count, _face3Pool = [], _face3PoolLength = 0,
+	_line, _lineCount, _linePool = [], _linePoolLength = 0,
+	_sprite, _spriteCount, _spritePool = [], _spritePoolLength = 0,
+
+	_renderData = { objects: [], sprites: [], lights: [], elements: [] },
+
+	_vector3 = new THREE.Vector3(),
+	_vector4 = new THREE.Vector4(),
+
+	_clipBox = new THREE.Box3( new THREE.Vector3( -1, -1, -1 ), new THREE.Vector3( 1, 1, 1 ) ),
+	_boundingBox = new THREE.Box3(),
+	_points3 = new Array( 3 ),
+	_points4 = new Array( 4 ),
+
+	_viewMatrix = new THREE.Matrix4(),
+	_viewProjectionMatrix = new THREE.Matrix4(),
+
+	_modelMatrix,
+	_modelViewProjectionMatrix = new THREE.Matrix4(),
+
+	_normalMatrix = new THREE.Matrix3(),
+	_normalViewMatrix = new THREE.Matrix3(),
+
+	_centroid = new THREE.Vector3(),
+
+	_frustum = new THREE.Frustum(),
+
+	_clippedVertex1PositionScreen = new THREE.Vector4(),
+	_clippedVertex2PositionScreen = new THREE.Vector4();
+
+	this.projectVector = function ( vector, camera ) {
+
+		camera.matrixWorldInverse.getInverse( camera.matrixWorld );
+
+		_viewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );
+
+		return vector.applyProjection( _viewProjectionMatrix );
+
+	};
+
+	this.unprojectVector = function ( vector, camera ) {
+
+		camera.projectionMatrixInverse.getInverse( camera.projectionMatrix );
+
+		_viewProjectionMatrix.multiplyMatrices( camera.matrixWorld, camera.projectionMatrixInverse );
+
+		return vector.applyProjection( _viewProjectionMatrix );
+
+	};
+
+	this.pickingRay = function ( vector, camera ) {
+
+		// set two vectors with opposing z values
+		vector.z = -1.0;
+		var end = new THREE.Vector3( vector.x, vector.y, 1.0 );
+
+		this.unprojectVector( vector, camera );
+		this.unprojectVector( end, camera );
+
+		// find direction from vector to end
+		end.sub( vector ).normalize();
+
+		return new THREE.Raycaster( vector, end );
+
+	};
+
+	var getObject = function ( object ) {
+
+		_object = getNextObjectInPool();
+		_object.id = object.id;
+		_object.object = object;
+
+		if ( object.renderDepth !== null ) {
+
+			_object.z = object.renderDepth;
+
+		} else {
+
+			_vector3.getPositionFromMatrix( object.matrixWorld );
+			_vector3.applyProjection( _viewProjectionMatrix );
+			_object.z = _vector3.z;
+
+		}
+
+		return _object;
+
+	};
+
+	var projectObject = function ( object ) {
+
+		if ( object.visible === false ) return;
+
+		if ( object instanceof THREE.Light ) {
+
+			_renderData.lights.push( object );
+
+		} else if ( object instanceof THREE.Mesh || object instanceof THREE.Line ) {
+
+			if ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) {
+
+				_renderData.objects.push( getObject( object ) );
+
+			}
+
+		} else if ( object instanceof THREE.Sprite ) {
+
+			_renderData.sprites.push( getObject( object ) );
+
+		}
+
+		for ( var i = 0, l = object.children.length; i < l; i ++ ) {
+
+			projectObject( object.children[ i ] );
+
+		}
+
+	};
+
+	var projectGraph = function ( root, sortObjects ) {
+
+		_objectCount = 0;
+
+		_renderData.objects.length = 0;
+		_renderData.sprites.length = 0;
+		_renderData.lights.length = 0;
+
+		projectObject( root );
+
+		if ( sortObjects === true ) {
+
+			_renderData.objects.sort( painterSort );
+
+		}
+
+	};
+
+	this.projectScene = function ( scene, camera, sortObjects, sortElements ) {
+
+		var visible = false,
+		o, ol, v, vl, f, fl, n, nl, c, cl, u, ul, object,
+		geometry, vertices, faces, face, faceVertexNormals, faceVertexUvs, uvs,
+		v1, v2, v3, v4, isFaceMaterial, objectMaterials;
+
+		_face3Count = 0;
+		_lineCount = 0;
+		_spriteCount = 0;
+
+		_renderData.elements.length = 0;
+
+		if ( scene.autoUpdate === true ) scene.updateMatrixWorld();
+		if ( camera.parent === undefined ) camera.updateMatrixWorld();
+
+		_viewMatrix.copy( camera.matrixWorldInverse.getInverse( camera.matrixWorld ) );
+		_viewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, _viewMatrix );
+
+		_normalViewMatrix.getNormalMatrix( _viewMatrix );
+
+		_frustum.setFromMatrix( _viewProjectionMatrix );
+
+		projectGraph( scene, sortObjects );
+
+		for ( o = 0, ol = _renderData.objects.length; o < ol; o ++ ) {
+
+			object = _renderData.objects[ o ].object;
+
+			_modelMatrix = object.matrixWorld;
+
+			_vertexCount = 0;
+
+			if ( object instanceof THREE.Mesh ) {
+
+				geometry = object.geometry;
+
+				vertices = geometry.vertices;
+				faces = geometry.faces;
+				faceVertexUvs = geometry.faceVertexUvs;
+
+				_normalMatrix.getNormalMatrix( _modelMatrix );
+
+				isFaceMaterial = object.material instanceof THREE.MeshFaceMaterial;
+				objectMaterials = isFaceMaterial === true ? object.material : null;
+
+				for ( v = 0, vl = vertices.length; v < vl; v ++ ) {
+
+					_vertex = getNextVertexInPool();
+
+					_vertex.positionWorld.copy( vertices[ v ] ).applyMatrix4( _modelMatrix );
+					_vertex.positionScreen.copy( _vertex.positionWorld ).applyMatrix4( _viewProjectionMatrix );
+
+					var invW = 1 / _vertex.positionScreen.w;
+
+					_vertex.positionScreen.x *= invW;
+					_vertex.positionScreen.y *= invW;
+					_vertex.positionScreen.z *= invW;
+
+					_vertex.visible = ! ( _vertex.positionScreen.x < -1 || _vertex.positionScreen.x > 1 ||
+							      _vertex.positionScreen.y < -1 || _vertex.positionScreen.y > 1 ||
+							      _vertex.positionScreen.z < -1 || _vertex.positionScreen.z > 1 );
+
+				}
+
+				for ( f = 0, fl = faces.length; f < fl; f ++ ) {
+
+					face = faces[ f ];
+
+					var material = isFaceMaterial === true
+						? objectMaterials.materials[ face.materialIndex ]
+						: object.material;
+
+					if ( material === undefined ) continue;
+
+					var side = material.side;
+
+					v1 = _vertexPool[ face.a ];
+					v2 = _vertexPool[ face.b ];
+					v3 = _vertexPool[ face.c ];
+
+					_points3[ 0 ] = v1.positionScreen;
+					_points3[ 1 ] = v2.positionScreen;
+					_points3[ 2 ] = v3.positionScreen;
+
+					if ( v1.visible === true || v2.visible === true || v3.visible === true ||
+						_clipBox.isIntersectionBox( _boundingBox.setFromPoints( _points3 ) ) ) {
+
+						visible = ( ( v3.positionScreen.x - v1.positionScreen.x ) *
+							    ( v2.positionScreen.y - v1.positionScreen.y ) -
+							    ( v3.positionScreen.y - v1.positionScreen.y ) *
+							    ( v2.positionScreen.x - v1.positionScreen.x ) ) < 0;
+
+						if ( side === THREE.DoubleSide || visible === ( side === THREE.FrontSide ) ) {
+
+							_face = getNextFace3InPool();
+
+							_face.id = object.id;
+							_face.v1.copy( v1 );
+							_face.v2.copy( v2 );
+							_face.v3.copy( v3 );
+
+						} else {
+
+							continue;
+
+						}
+
+					} else {
+
+						continue;
+
+					}
+
+					_face.normalModel.copy( face.normal );
+
+					if ( visible === false && ( side === THREE.BackSide || side === THREE.DoubleSide ) ) {
+
+						_face.normalModel.negate();
+
+					}
+
+					_face.normalModel.applyMatrix3( _normalMatrix ).normalize();
+
+					_face.normalModelView.copy( _face.normalModel ).applyMatrix3( _normalViewMatrix );
+
+					_face.centroidModel.copy( face.centroid ).applyMatrix4( _modelMatrix );
+
+					faceVertexNormals = face.vertexNormals;
+
+					for ( n = 0, nl = Math.min( faceVertexNormals.length, 3 ); n < nl; n ++ ) {
+
+						var normalModel = _face.vertexNormalsModel[ n ];
+						normalModel.copy( faceVertexNormals[ n ] );
+
+						if ( visible === false && ( side === THREE.BackSide || side === THREE.DoubleSide ) ) {
+
+							normalModel.negate();
+
+						}
+
+						normalModel.applyMatrix3( _normalMatrix ).normalize();
+
+						var normalModelView = _face.vertexNormalsModelView[ n ];
+						normalModelView.copy( normalModel ).applyMatrix3( _normalViewMatrix );
+
+					}
+
+					_face.vertexNormalsLength = faceVertexNormals.length;
+
+					for ( c = 0, cl = Math.min( faceVertexUvs.length, 3 ); c < cl; c ++ ) {
+
+						uvs = faceVertexUvs[ c ][ f ];
+
+						if ( uvs === undefined ) continue;
+
+						for ( u = 0, ul = uvs.length; u < ul; u ++ ) {
+
+							_face.uvs[ c ][ u ] = uvs[ u ];
+
+						}
+
+					}
+
+					_face.color = face.color;
+					_face.material = material;
+
+					_centroid.copy( _face.centroidModel ).applyProjection( _viewProjectionMatrix );
+
+					_face.z = _centroid.z;
+
+					_renderData.elements.push( _face );
+
+				}
+
+			} else if ( object instanceof THREE.Line ) {
+
+				_modelViewProjectionMatrix.multiplyMatrices( _viewProjectionMatrix, _modelMatrix );
+
+				vertices = object.geometry.vertices;
+
+				v1 = getNextVertexInPool();
+				v1.positionScreen.copy( vertices[ 0 ] ).applyMatrix4( _modelViewProjectionMatrix );
+
+				// Handle LineStrip and LinePieces
+				var step = object.type === THREE.LinePieces ? 2 : 1;
+
+				for ( v = 1, vl = vertices.length; v < vl; v ++ ) {
+
+					v1 = getNextVertexInPool();
+					v1.positionScreen.copy( vertices[ v ] ).applyMatrix4( _modelViewProjectionMatrix );
+
+					if ( ( v + 1 ) % step > 0 ) continue;
+
+					v2 = _vertexPool[ _vertexCount - 2 ];
+
+					_clippedVertex1PositionScreen.copy( v1.positionScreen );
+					_clippedVertex2PositionScreen.copy( v2.positionScreen );
+
+					if ( clipLine( _clippedVertex1PositionScreen, _clippedVertex2PositionScreen ) === true ) {
+
+						// Perform the perspective divide
+						_clippedVertex1PositionScreen.multiplyScalar( 1 / _clippedVertex1PositionScreen.w );
+						_clippedVertex2PositionScreen.multiplyScalar( 1 / _clippedVertex2PositionScreen.w );
+
+						_line = getNextLineInPool();
+
+						_line.id = object.id;
+						_line.v1.positionScreen.copy( _clippedVertex1PositionScreen );
+						_line.v2.positionScreen.copy( _clippedVertex2PositionScreen );
+
+						_line.z = Math.max( _clippedVertex1PositionScreen.z, _clippedVertex2PositionScreen.z );
+
+						_line.material = object.material;
+
+						if ( object.material.vertexColors === THREE.VertexColors ) {
+
+							_line.vertexColors[ 0 ].copy( object.geometry.colors[ v ] );
+							_line.vertexColors[ 1 ].copy( object.geometry.colors[ v - 1 ] );
+
+						}
+
+						_renderData.elements.push( _line );
+
+					}
+
+				}
+
+			}
+
+		}
+
+		for ( o = 0, ol = _renderData.sprites.length; o < ol; o++ ) {
+
+			object = _renderData.sprites[ o ].object;
+
+			_modelMatrix = object.matrixWorld;
+
+			if ( object instanceof THREE.Sprite ) {
+
+				_vector4.set( _modelMatrix.elements[12], _modelMatrix.elements[13], _modelMatrix.elements[14], 1 );
+				_vector4.applyMatrix4( _viewProjectionMatrix );
+
+				var invW = 1 / _vector4.w;
+
+				_vector4.z *= invW;
+
+				if ( _vector4.z > -1 && _vector4.z < 1 ) {
+
+					_sprite = getNextSpriteInPool();
+					_sprite.id = object.id;
+					_sprite.x = _vector4.x * invW;
+					_sprite.y = _vector4.y * invW;
+					_sprite.z = _vector4.z;
+					_sprite.object = object;
+
+					_sprite.rotation = object.rotation;
+
+					_sprite.scale.x = object.scale.x * Math.abs( _sprite.x - ( _vector4.x + camera.projectionMatrix.elements[0] ) / ( _vector4.w + camera.projectionMatrix.elements[12] ) );
+					_sprite.scale.y = object.scale.y * Math.abs( _sprite.y - ( _vector4.y + camera.projectionMatrix.elements[5] ) / ( _vector4.w + camera.projectionMatrix.elements[13] ) );
+
+					_sprite.material = object.material;
+
+					_renderData.elements.push( _sprite );
+
+				}
+
+			}
+
+		}
+
+		if ( sortElements === true ) _renderData.elements.sort( painterSort );
+
+		return _renderData;
+
+	};
+
+	// Pools
+
+	function getNextObjectInPool() {
+
+		if ( _objectCount === _objectPoolLength ) {
+
+			var object = new THREE.RenderableObject();
+			_objectPool.push( object );
+			_objectPoolLength ++;
+			_objectCount ++;
+			return object;
+
+		}
+
+		return _objectPool[ _objectCount ++ ];
+
+	}
+
+	function getNextVertexInPool() {
+
+		if ( _vertexCount === _vertexPoolLength ) {
+
+			var vertex = new THREE.RenderableVertex();
+			_vertexPool.push( vertex );
+			_vertexPoolLength ++;
+			_vertexCount ++;
+			return vertex;
+
+		}
+
+		return _vertexPool[ _vertexCount ++ ];
+
+	}
+
+	function getNextFace3InPool() {
+
+		if ( _face3Count === _face3PoolLength ) {
+
+			var face = new THREE.RenderableFace3();
+			_face3Pool.push( face );
+			_face3PoolLength ++;
+			_face3Count ++;
+			return face;
+
+		}
+
+		return _face3Pool[ _face3Count ++ ];
+
+
+	}
+
+	function getNextLineInPool() {
+
+		if ( _lineCount === _linePoolLength ) {
+
+			var line = new THREE.RenderableLine();
+			_linePool.push( line );
+			_linePoolLength ++;
+			_lineCount ++
+			return line;
+
+		}
+
+		return _linePool[ _lineCount ++ ];
+
+	}
+
+	function getNextSpriteInPool() {
+
+		if ( _spriteCount === _spritePoolLength ) {
+
+			var sprite = new THREE.RenderableSprite();
+			_spritePool.push( sprite );
+			_spritePoolLength ++;
+			_spriteCount ++
+			return sprite;
+
+		}
+
+		return _spritePool[ _spriteCount ++ ];
+
+	}
+
+	//
+
+	function painterSort( a, b ) {
+
+		if ( a.z !== b.z ) {
+
+			return b.z - a.z;
+
+		} else if ( a.id !== b.id ) {
+
+			return a.id - b.id;
+
+		} else {
+
+			return 0;
+
+		}
+
+	}
+
+	function clipLine( s1, s2 ) {
+
+		var alpha1 = 0, alpha2 = 1,
+
+		// Calculate the boundary coordinate of each vertex for the near and far clip planes,
+		// Z = -1 and Z = +1, respectively.
+		bc1near =  s1.z + s1.w,
+		bc2near =  s2.z + s2.w,
+		bc1far =  - s1.z + s1.w,
+		bc2far =  - s2.z + s2.w;
+
+		if ( bc1near >= 0 && bc2near >= 0 && bc1far >= 0 && bc2far >= 0 ) {
+
+			// Both vertices lie entirely within all clip planes.
+			return true;
+
+		} else if ( ( bc1near < 0 && bc2near < 0) || (bc1far < 0 && bc2far < 0 ) ) {
+
+			// Both vertices lie entirely outside one of the clip planes.
+			return false;
+
+		} else {
+
+			// The line segment spans at least one clip plane.
+
+			if ( bc1near < 0 ) {
+
+				// v1 lies outside the near plane, v2 inside
+				alpha1 = Math.max( alpha1, bc1near / ( bc1near - bc2near ) );
+
+			} else if ( bc2near < 0 ) {
+
+				// v2 lies outside the near plane, v1 inside
+				alpha2 = Math.min( alpha2, bc1near / ( bc1near - bc2near ) );
+
+			}
+
+			if ( bc1far < 0 ) {
+
+				// v1 lies outside the far plane, v2 inside
+				alpha1 = Math.max( alpha1, bc1far / ( bc1far - bc2far ) );
+
+			} else if ( bc2far < 0 ) {
+
+				// v2 lies outside the far plane, v2 inside
+				alpha2 = Math.min( alpha2, bc1far / ( bc1far - bc2far ) );
+
+			}
+
+			if ( alpha2 < alpha1 ) {
+
+				// The line segment spans two boundaries, but is outside both of them.
+				// (This can't happen when we're only clipping against just near/far but good
+				//  to leave the check here for future usage if other clip planes are added.)
+				return false;
+
+			} else {
+
+				// Update the s1 and s2 vertices to match the clipped line segment.
+				s1.lerp( s2, alpha1 );
+				s2.lerp( s1, 1 - alpha2 );
+
+				return true;
+
+			}
+
+		}
+
+	}
+
+};
+
+/**
+ * @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;
+
+	this.centroid = new THREE.Vector3();
+
+};
+
+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.centroid.copy( this.centroid );
+
+		face.materialIndex = this.materialIndex;
+
+		var i, il;
+		for ( i = 0, il = this.vertexNormals.length; i < il; i ++ ) face.vertexNormals[ i ] = this.vertexNormals[ i ].clone();
+		for ( i = 0, il = this.vertexColors.length; i < il; i ++ ) face.vertexColors[ i ] = this.vertexColors[ i ].clone();
+		for ( i = 0, il = this.vertexTangents.length; i < il; i ++ ) face.vertexTangents[ i ] = this.vertexTangents[ i ].clone();
+
+		return face;
+
+	}
+
+};
+
+/**
+ * @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 );
+
+};
+
+/**
+ * @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 () {
+
+	this.id = THREE.GeometryIdCount ++;
+	this.uuid = THREE.Math.generateUUID();
+
+	this.name = '';
+
+	this.vertices = [];
+	this.colors = [];  // one-to-one vertex colors, used in ParticleSystem 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.buffersNeedUpdate = 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();
+
+			}
+
+			face.centroid.applyMatrix4( matrix );
+
+		}
+
+		if ( this.boundingBox instanceof THREE.Box3 ) {
+
+			this.computeBoundingBox();
+
+		}
+
+		if ( this.boundingSphere instanceof THREE.Sphere ) {
+
+			this.computeBoundingSphere();
+
+		}
+
+	},
+
+	computeCentroids: function () {
+
+		var f, fl, face;
+
+		for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
+
+			face = this.faces[ f ];
+			face.centroid.set( 0, 0, 0 );
+
+			face.centroid.add( this.vertices[ face.a ] );
+			face.centroid.add( this.vertices[ face.b ] );
+			face.centroid.add( this.vertices[ face.c ] );
+			face.centroid.divideScalar( 3 );
+
+		}
+
+	},
+
+	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;
+
+		// create internal buffers for reuse when calling this method repeatedly
+		// (otherwise memory allocation / deallocation every frame is big resource hog)
+
+		if ( this.__tmpVertices === undefined ) {
+
+			this.__tmpVertices = new Array( this.vertices.length );
+			vertices = this.__tmpVertices;
+
+			for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) {
+
+				vertices[ v ] = new THREE.Vector3();
+
+			}
+
+			for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
+
+				face = this.faces[ f ];
+				face.vertexNormals = [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ];
+
+			}
+
+		} else {
+
+			vertices = this.__tmpVertices;
+
+			for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) {
+
+				vertices[ v ].set( 0, 0, 0 );
+
+			}
+
+		}
+
+		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 ].copy( vertices[ face.a ] );
+			face.vertexNormals[ 1 ].copy( vertices[ face.b ] );
+			face.vertexNormals[ 2 ].copy( vertices[ face.c ] );
+
+		}
+
+	},
+
+	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 ++ ) {
+
+					face = this.faces[ 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 );
+
+	},
+
+	/*
+	 * 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;
+
+		// reset cache of vertices as it now will be changing.
+		this.__tmpVertices = undefined;
+
+		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;
+
+	},
+
+	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() );
+
+		}
+
+		var uvs = this.faceVertexUvs[ 0 ];
+
+		for ( var i = 0, il = uvs.length; i < il; i ++ ) {
+
+			var uv = uvs[ i ], uvCopy = [];
+
+			for ( var j = 0, jl = uv.length; j < jl; j ++ ) {
+
+				uvCopy.push( new THREE.Vector2( uv[ j ].x, uv[ j ].y ) );
+
+			}
+
+			geometry.faceVertexUvs[ 0 ].push( uvCopy );
+
+		}
+
+		return geometry;
+
+	},
+
+	dispose: function () {
+
+		this.dispatchEvent( { type: 'dispose' } );
+
+	}
+
+};
+
+THREE.EventDispatcher.prototype.apply( THREE.Geometry.prototype );
+
+THREE.GeometryIdCount = 0;
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.BufferGeometry = function () {
+
+	this.id = THREE.GeometryIdCount ++;
+	this.uuid = THREE.Math.generateUUID();
+
+	this.name = '';
+
+	// attributes
+
+	this.attributes = {};
+
+	// attributes typed arrays are kept only if dynamic flag is set
+
+	this.dynamic = true;
+
+	// offsets for chunks when using indexed elements
+
+	this.offsets = [];
+
+	// boundings
+
+	this.boundingBox = null;
+	this.boundingSphere = null;
+
+	this.hasTangents = false;
+
+	// for compatibility
+
+	this.morphTargets = [];
+
+};
+
+THREE.BufferGeometry.prototype = {
+
+	constructor: THREE.BufferGeometry,
+
+	addAttribute: function( name, type, numItems, itemSize ) {
+
+		this.attributes[ name ] = {
+
+			itemSize: itemSize,
+			array: new type( numItems * itemSize )
+
+		};
+
+	},
+
+	applyMatrix: function ( matrix ) {
+
+		var positionArray;
+		var normalArray;
+
+		if ( this.attributes[ "position" ] ) positionArray = this.attributes[ "position" ].array;
+		if ( this.attributes[ "normal" ] ) normalArray = this.attributes[ "normal" ].array;
+
+		if ( positionArray !== undefined ) {
+
+			matrix.multiplyVector3Array( positionArray );
+			this.verticesNeedUpdate = true;
+
+		}
+
+		if ( normalArray !== undefined ) {
+
+			var normalMatrix = new THREE.Matrix3().getNormalMatrix( matrix );
+
+			normalMatrix.multiplyVector3Array( normalArray );
+
+			this.normalizeNormals();
+
+			this.normalsNeedUpdate = true;
+
+		}
+
+	},
+
+	computeBoundingBox: function () {
+
+		if ( this.boundingBox === null ) {
+
+			this.boundingBox = new THREE.Box3();
+
+		}
+
+		var positions = this.attributes[ "position" ].array;
+
+		if ( positions ) {
+
+			var bb = this.boundingBox;
+			var x, y, z;
+
+			if( positions.length >= 3 ) {
+				bb.min.x = bb.max.x = positions[ 0 ];
+				bb.min.y = bb.max.y = positions[ 1 ];
+				bb.min.z = bb.max.z = positions[ 2 ];
+			}
+
+			for ( var i = 3, il = positions.length; i < il; i += 3 ) {
+
+				x = positions[ i ];
+				y = positions[ i + 1 ];
+				z = positions[ i + 2 ];
+
+				// bounding box
+
+				if ( x < bb.min.x ) {
+
+					bb.min.x = x;
+
+				} else if ( x > bb.max.x ) {
+
+					bb.max.x = x;
+
+				}
+
+				if ( y < bb.min.y ) {
+
+					bb.min.y = y;
+
+				} else if ( y > bb.max.y ) {
+
+					bb.max.y = y;
+
+				}
+
+				if ( z < bb.min.z ) {
+
+					bb.min.z = z;
+
+				} else if ( z > bb.max.z ) {
+
+					bb.max.z = z;
+
+				}
+
+			}
+
+		}
+
+		if ( positions === undefined || positions.length === 0 ) {
+
+			this.boundingBox.min.set( 0, 0, 0 );
+			this.boundingBox.max.set( 0, 0, 0 );
+
+		}
+
+	},
+
+	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 ) {
+
+				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.addPoint( vector );
+
+				}
+
+				box.center( center );
+
+				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 );
+
+			}
+
+		}
+
+	}(),
+
+	computeVertexNormals: function () {
+
+		if ( this.attributes[ "position" ] ) {
+
+			var i, il;
+			var j, jl;
+
+			var nVertexElements = this.attributes[ "position" ].array.length;
+
+			if ( this.attributes[ "normal" ] === undefined ) {
+
+				this.attributes[ "normal" ] = {
+
+					itemSize: 3,
+					array: new Float32Array( nVertexElements )
+
+				};
+
+			} else {
+
+				// reset existing normals to zero
+
+				for ( i = 0, il = this.attributes[ "normal" ].array.length; i < il; i ++ ) {
+
+					this.attributes[ "normal" ].array[ i ] = 0;
+
+				}
+
+			}
+
+			var positions = this.attributes[ "position" ].array;
+			var normals = this.attributes[ "normal" ].array;
+
+			var vA, vB, vC, x, y, z,
+
+			pA = new THREE.Vector3(),
+			pB = new THREE.Vector3(),
+			pC = new THREE.Vector3(),
+
+			cb = new THREE.Vector3(),
+			ab = new THREE.Vector3();
+
+			// indexed elements
+
+			if ( this.attributes[ "index" ] ) {
+
+				var indices = this.attributes[ "index" ].array;
+
+				var offsets = this.offsets;
+
+				for ( j = 0, jl = offsets.length; j < jl; ++ j ) {
+
+					var start = offsets[ j ].start;
+					var count = offsets[ j ].count;
+					var index = offsets[ j ].index;
+
+					for ( i = start, il = start + count; i < il; i += 3 ) {
+
+						vA = index + indices[ i ];
+						vB = index + indices[ i + 1 ];
+						vC = index + indices[ i + 2 ];
+
+						x = positions[ vA * 3 ];
+						y = positions[ vA * 3 + 1 ];
+						z = positions[ vA * 3 + 2 ];
+						pA.set( x, y, z );
+
+						x = positions[ vB * 3 ];
+						y = positions[ vB * 3 + 1 ];
+						z = positions[ vB * 3 + 2 ];
+						pB.set( x, y, z );
+
+						x = positions[ vC * 3 ];
+						y = positions[ vC * 3 + 1 ];
+						z = positions[ vC * 3 + 2 ];
+						pC.set( x, y, z );
+
+						cb.subVectors( pC, pB );
+						ab.subVectors( pA, pB );
+						cb.cross( ab );
+
+						normals[ vA * 3 ]     += cb.x;
+						normals[ vA * 3 + 1 ] += cb.y;
+						normals[ vA * 3 + 2 ] += cb.z;
+
+						normals[ vB * 3 ]     += cb.x;
+						normals[ vB * 3 + 1 ] += cb.y;
+						normals[ vB * 3 + 2 ] += cb.z;
+
+						normals[ vC * 3 ]     += cb.x;
+						normals[ vC * 3 + 1 ] += cb.y;
+						normals[ vC * 3 + 2 ] += cb.z;
+
+					}
+
+				}
+
+			// non-indexed elements (unconnected triangle soup)
+
+			} else {
+
+				for ( i = 0, il = positions.length; i < il; i += 9 ) {
+
+					x = positions[ i ];
+					y = positions[ i + 1 ];
+					z = positions[ i + 2 ];
+					pA.set( x, y, z );
+
+					x = positions[ i + 3 ];
+					y = positions[ i + 4 ];
+					z = positions[ i + 5 ];
+					pB.set( x, y, z );
+
+					x = positions[ i + 6 ];
+					y = positions[ i + 7 ];
+					z = positions[ i + 8 ];
+					pC.set( x, y, z );
+
+					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();
+
+			this.normalsNeedUpdate = true;
+
+		}
+
+	},
+
+	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;
+
+		}
+
+	},
+
+	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 ) {
+
+			var nTangentElements = 4 * nVertices;
+
+			this.attributes[ "tangent" ] = {
+
+				itemSize: 4,
+				array: new Float32Array( nTangentElements )
+
+			};
+
+		}
+
+		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 xA, yA, zA,
+			xB, yB, zB,
+			xC, yC, zC,
+
+			uA, vA,
+			uB, vB,
+			uC, vC,
+
+			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 ) {
+
+			xA = positions[ a * 3 ];
+			yA = positions[ a * 3 + 1 ];
+			zA = positions[ a * 3 + 2 ];
+
+			xB = positions[ b * 3 ];
+			yB = positions[ b * 3 + 1 ];
+			zB = positions[ b * 3 + 2 ];
+
+			xC = positions[ c * 3 ];
+			yC = positions[ c * 3 + 1 ];
+			zC = positions[ c * 3 + 2 ];
+
+			uA = uvs[ a * 2 ];
+			vA = uvs[ a * 2 + 1 ];
+
+			uB = uvs[ b * 2 ];
+			vB = uvs[ b * 2 + 1 ];
+
+			uC = uvs[ c * 2 ];
+			vC = uvs[ c * 2 + 1 ];
+
+			x1 = xB - xA;
+			x2 = xC - xA;
+
+			y1 = yB - yA;
+			y2 = yC - yA;
+
+			z1 = zB - zA;
+			z2 = zC - zA;
+
+			s1 = uB - uA;
+			s2 = uC - uA;
+
+			t1 = vB - vA;
+			t2 = vC - vA;
+
+			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;
+
+		var offsets = this.offsets;
+
+		for ( j = 0, jl = offsets.length; j < jl; ++ j ) {
+
+			var start = offsets[ j ].start;
+			var count = offsets[ j ].count;
+			var index = offsets[ 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.x = normals[ v * 3 ];
+			n.y = normals[ v * 3 + 1 ];
+			n.z = normals[ v * 3 + 2 ];
+
+			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 = offsets.length; j < jl; ++ j ) {
+
+			var start = offsets[ j ].start;
+			var count = offsets[ j ].count;
+			var index = offsets[ 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 );
+
+			}
+
+		}
+
+		this.hasTangents = true;
+		this.tangentsNeedUpdate = true;
+
+	},
+
+	clone: function () {
+
+		var geometry = new THREE.BufferGeometry();
+
+		var types = [ Int8Array, Uint8Array, Uint8ClampedArray, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array ];
+
+		for ( var attr in this.attributes ) {
+
+			var sourceAttr = this.attributes[ attr ];
+			var sourceArray = sourceAttr.array;
+
+			var attribute = {
+
+				itemSize: sourceAttr.itemSize,
+				numItems: sourceAttr.numItems,
+				array: null
+
+			};
+
+			for ( var i = 0, il = types.length; i < il; i ++ ) {
+
+				var type = types[ i ];
+
+				if ( sourceArray instanceof type ) {
+
+					attribute.array = new type( sourceArray );
+					break;
+
+				}
+
+			}
+
+			geometry.attributes[ attr ] = attribute;
+
+		}
+
+		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 );
+
+/**
+ * @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.matrixWorldInverse = new THREE.Matrix4();
+
+	this.projectionMatrix = new THREE.Matrix4();
+	this.projectionMatrixInverse = new THREE.Matrix4();
+
+};
+
+THREE.Camera.prototype = Object.create( THREE.Object3D.prototype );
+
+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 );
+	camera.projectionMatrixInverse.copy( this.projectionMatrixInverse );
+
+	return camera;
+};
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.OrthographicCamera = function ( left, right, top, bottom, near, far ) {
+
+	THREE.Camera.call( this );
+
+	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.updateProjectionMatrix = function () {
+
+	this.projectionMatrix.makeOrthographic( this.left, this.right, this.top, this.bottom, this.near, this.far );
+
+};
+
+THREE.OrthographicCamera.prototype.clone = function () {
+
+	var camera = new THREE.OrthographicCamera();
+
+	THREE.Camera.prototype.clone.call( this, camera );
+
+	camera.left = this.left;
+	camera.right = this.right;
+	camera.top = this.top;
+	camera.bottom = this.bottom;
+
+	camera.near = this.near;
+	camera.far = this.far;
+
+	return camera;
+};
+
+/**
+ * @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.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 );
+
+
+/**
+ * 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 () {
+
+	if ( this.fullWidth ) {
+
+		var aspect = this.fullWidth / this.fullHeight;
+		var top = Math.tan( THREE.Math.degToRad( this.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( this.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.fov = this.fov;
+	camera.aspect = this.aspect;
+	camera.near = this.near;
+	camera.far = this.far;
+
+	return camera;
+};
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.Light = function ( hex ) {
+
+	THREE.Object3D.call( this );
+
+	this.color = new THREE.Color( hex );
+
+};
+
+THREE.Light.prototype = Object.create( THREE.Object3D.prototype );
+
+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;
+
+};
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.AmbientLight = function ( hex ) {
+
+	THREE.Light.call( this, hex );
+
+};
+
+THREE.AmbientLight.prototype = Object.create( THREE.Light.prototype );
+
+THREE.AmbientLight.prototype.clone = function () {
+
+	var light = new THREE.AmbientLight();
+
+	THREE.Light.prototype.clone.call( this, light );
+
+	return light;
+
+};
+
+/**
+ * @author MPanknin / http://www.redplant.de/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.AreaLight = function ( hex, intensity ) {
+
+	THREE.Light.call( this, hex );
+
+	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 );
+
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.DirectionalLight = function ( hex, intensity ) {
+
+	THREE.Light.call( this, hex );
+
+	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.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;
+
+	return light;
+
+};
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.HemisphereLight = function ( skyColorHex, groundColorHex, intensity ) {
+
+	THREE.Light.call( this, skyColorHex );
+
+	this.position.set( 0, 100, 0 );
+
+	this.groundColor = new THREE.Color( groundColorHex );
+	this.intensity = ( intensity !== undefined ) ? intensity : 1;
+
+};
+
+THREE.HemisphereLight.prototype = Object.create( THREE.Light.prototype );
+
+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;
+
+};
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.PointLight = function ( hex, intensity, distance ) {
+
+	THREE.Light.call( this, hex );
+
+	this.intensity = ( intensity !== undefined ) ? intensity : 1;
+	this.distance = ( distance !== undefined ) ? distance : 0;
+
+};
+
+THREE.PointLight.prototype = Object.create( THREE.Light.prototype );
+
+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;
+
+};
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.SpotLight = function ( hex, intensity, distance, angle, exponent ) {
+
+	THREE.Light.call( this, hex );
+
+	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.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;
+
+	return light;
+
+};
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.Loader = function ( showStatus ) {
+
+	this.showStatus = showStatus;
+	this.statusDomElement = showStatus ? THREE.Loader.prototype.addStatusElement() : null;
+
+	this.onLoadStart = function () {};
+	this.onLoadProgress = function () {};
+	this.onLoadComplete = function () {};
+
+};
+
+THREE.Loader.prototype = {
+
+	constructor: THREE.Loader,
+
+	crossOrigin: 'anonymous',
+
+	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 / 1000 ).toFixed(2) + " KB";
+
+		}
+
+		this.statusDomElement.innerHTML = message;
+
+	},
+
+	extractUrlBase: function ( url ) {
+
+		var parts = url.split( '/' );
+		parts.pop();
+		return ( parts.length < 1 ? '.' : parts.join( '/' ) ) + '/';
+
+	},
+
+	initMaterials: function ( materials, texturePath ) {
+
+		var array = [];
+
+		for ( var i = 0; i < materials.length; ++ i ) {
+
+			array[ i ] = THREE.Loader.prototype.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 _this = this;
+
+		function is_pow2( n ) {
+
+			var l = Math.log( n ) / Math.LN2;
+			return Math.floor( l ) == l;
+
+		}
+
+		function nearest_pow2( n ) {
+
+			var l = Math.log( n ) / Math.LN2;
+			return Math.pow( 2, Math.round(  l ) );
+
+		}
+
+		function load_image( where, url ) {
+
+			var image = new Image();
+
+			image.onload = function () {
+
+				if ( !is_pow2( this.width ) || !is_pow2( this.height ) ) {
+
+					var width = nearest_pow2( this.width );
+					var height = nearest_pow2( this.height );
+
+					where.image.width = width;
+					where.image.height = height;
+					where.image.getContext( '2d' ).drawImage( this, 0, 0, width, height );
+
+				} else {
+
+					where.image = this;
+
+				}
+
+				where.needsUpdate = true;
+
+			};
+
+			image.crossOrigin = _this.crossOrigin;
+			image.src = url;
+
+		}
+
+		function create_texture( where, name, sourceFile, repeat, offset, wrap, anisotropy ) {
+
+			var isCompressed = /\.dds$/i.test( sourceFile );
+			var fullPath = texturePath + "/" + sourceFile;
+
+			if ( isCompressed ) {
+
+				var texture = THREE.ImageUtils.loadCompressedTexture( fullPath );
+
+				where[ name ] = texture;
+
+			} else {
+
+				var texture = document.createElement( 'canvas' );
+
+				where[ name ] = new THREE.Texture( texture );
+
+			}
+
+			where[ name ].sourceFile = sourceFile;
+
+			if( repeat ) {
+
+				where[ name ].repeat.set( repeat[ 0 ], repeat[ 1 ] );
+
+				if ( repeat[ 0 ] !== 1 ) where[ name ].wrapS = THREE.RepeatWrapping;
+				if ( repeat[ 1 ] !== 1 ) where[ name ].wrapT = THREE.RepeatWrapping;
+
+			}
+
+			if ( offset ) {
+
+				where[ name ].offset.set( offset[ 0 ], offset[ 1 ] );
+
+			}
+
+			if ( wrap ) {
+
+				var wrapMap = {
+					"repeat": THREE.RepeatWrapping,
+					"mirror": THREE.MirroredRepeatWrapping
+				}
+
+				if ( wrapMap[ wrap[ 0 ] ] !== undefined ) where[ name ].wrapS = wrapMap[ wrap[ 0 ] ];
+				if ( wrapMap[ wrap[ 1 ] ] !== undefined ) where[ name ].wrapT = wrapMap[ wrap[ 1 ] ];
+
+			}
+
+			if ( anisotropy ) {
+
+				where[ name ].anisotropy = anisotropy;
+
+			}
+
+			if ( ! isCompressed ) {
+
+				load_image( where[ name ], fullPath );
+
+			}
+
+		}
+
+		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 );
+
+		}
+
+		// 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.mapBumpScale ) {
+
+			mpars.bumpScale = m.mapBumpScale;
+
+		}
+
+		// special case for normal mapped material
+
+		if ( m.mapNormal ) {
+
+			var shader = THREE.ShaderLib[ "normalmap" ];
+			var uniforms = THREE.UniformsUtils.clone( shader.uniforms );
+
+			uniforms[ "tNormal" ].value = mpars.normalMap;
+
+			if ( m.mapNormalFactor ) {
+
+				uniforms[ "uNormalScale" ].value.set( m.mapNormalFactor, m.mapNormalFactor );
+
+			}
+
+			if ( mpars.map ) {
+
+				uniforms[ "tDiffuse" ].value = mpars.map;
+				uniforms[ "enableDiffuse" ].value = true;
+
+			}
+
+			if ( mpars.specularMap ) {
+
+				uniforms[ "tSpecular" ].value = mpars.specularMap;
+				uniforms[ "enableSpecular" ].value = true;
+
+			}
+
+			if ( mpars.lightMap ) {
+
+				uniforms[ "tAO" ].value = mpars.lightMap;
+				uniforms[ "enableAO" ].value = true;
+
+			}
+
+			// for the moment don't handle displacement texture
+
+			uniforms[ "uDiffuseColor" ].value.setHex( mpars.color );
+			uniforms[ "uSpecularColor" ].value.setHex( mpars.specular );
+			uniforms[ "uAmbientColor" ].value.setHex( mpars.ambient );
+
+			uniforms[ "uShininess" ].value = mpars.shininess;
+
+			if ( mpars.opacity !== undefined ) {
+
+				uniforms[ "uOpacity" ].value = mpars.opacity;
+
+			}
+
+			var parameters = { fragmentShader: shader.fragmentShader, vertexShader: shader.vertexShader, uniforms: uniforms, lights: true, fog: true };
+			var material = new THREE.ShaderMaterial( parameters );
+
+			if ( mpars.transparent ) {
+
+				material.transparent = true;
+
+			}
+
+		} else {
+
+			var material = new THREE[ mtype ]( mpars );
+
+		}
+
+		if ( m.DbgName !== undefined ) material.name = m.DbgName;
+
+		return material;
+
+	}
+
+};
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.XHRLoader = function ( manager ) {
+
+	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
+
+};
+
+THREE.XHRLoader.prototype = {
+
+	constructor: THREE.XHRLoader,
+
+	load: function ( url, onLoad, onProgress, onError ) {
+
+		var scope = this;
+		var request = new XMLHttpRequest();
+
+		if ( onLoad !== undefined ) {
+
+			request.addEventListener( 'load', function ( event ) {
+
+				onLoad( event.target.responseText );
+				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;
+
+		request.open( 'GET', url, true );
+		request.send( null );
+
+		scope.manager.itemStart( url );
+
+	},
+
+	setCrossOrigin: function ( value ) {
+
+		this.crossOrigin = value;
+
+	}
+
+};
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.ImageLoader = function ( manager ) {
+
+	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
+
+};
+
+THREE.ImageLoader.prototype = {
+
+	constructor: THREE.ImageLoader,
+
+	load: function ( url, onLoad, onProgress, onError ) {
+
+		var scope = this;
+		var image = document.createElement( 'img' );
+
+		if ( onLoad !== undefined ) {
+
+			image.addEventListener( 'load', function ( event ) {
+
+				scope.manager.itemEnd( url );
+				onLoad( this );
+
+			}, 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;
+
+	}
+
+}
+
+/**
+ * @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.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 );
+					var result = context.parse( json, texturePath );
+					callback( result.geometry, result.materials );
+
+				} else {
+
+					console.warn( "THREE.JSONLoader: [" + url + "] seems to be unreachable or file there 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.computeCentroids();
+	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 i, l, x, y, z, w, a, b, c, d;
+
+		if ( json.skinWeights ) {
+
+			for ( i = 0, l = json.skinWeights.length; i < l; i += 2 ) {
+
+				x = json.skinWeights[ i     ];
+				y = json.skinWeights[ i + 1 ];
+				z = 0;
+				w = 0;
+
+				geometry.skinWeights.push( new THREE.Vector4( x, y, z, w ) );
+
+			}
+
+		}
+
+		if ( json.skinIndices ) {
+
+			for ( i = 0, l = json.skinIndices.length; i < l; i += 2 ) {
+
+				a = json.skinIndices[ i     ];
+				b = json.skinIndices[ i + 1 ];
+				c = 0;
+				d = 0;
+
+				geometry.skinIndices.push( new THREE.Vector4( a, b, c, d ) );
+
+			}
+
+		}
+
+		geometry.bones = json.bones;
+		// 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 ) {
+
+		return { geometry: geometry };
+
+	} else {
+
+		var materials = this.initMaterials( json.materials, texturePath );
+
+		if ( this.needsTangents( materials ) ) {
+
+			geometry.computeTangents();
+
+		}
+
+		return { geometry: geometry, materials: materials };
+
+	}
+
+};
+
+/**
+ * @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();
+
+/**
+ * @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();
+		loader.setCrossOrigin( this.crossOrigin );
+		loader.load( url, function ( text ) {
+
+			onLoad( scope.parse( JSON.parse( text ) ) );
+
+		} );
+
+	},
+
+	setCrossOrigin: function ( value ) {
+
+		this.crossOrigin = value;
+
+	},
+
+	parse: function ( json ) {
+
+		var geometry = new THREE.BufferGeometry();
+
+		var attributes = json.attributes;
+		var offsets = json.offsets;
+		var boundingSphere = json.boundingSphere;
+
+		for ( var key in attributes ) {
+
+			var attribute = attributes[ key ];
+
+			geometry.attributes[ key ] = {
+				itemSize: attribute.itemSize,
+				array: new self[ attribute.type ]( attribute.array )
+			}
+
+		}
+
+		if ( offsets !== undefined ) {
+
+			geometry.offsets = JSON.parse( JSON.stringify( offsets ) );
+
+		}
+
+		if ( boundingSphere !== undefined ) {
+
+			geometry.boundingSphere = new THREE.Sphere(
+				new THREE.Vector3().fromArray( boundingSphere.center !== undefined ? boundingSphere.center : [ 0, 0, 0 ] ),
+				boundingSphere.radius
+			);
+
+		}
+
+		return geometry;
+
+	}
+
+};
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.GeometryLoader = function ( manager ) {
+
+	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
+
+};
+
+THREE.GeometryLoader.prototype = {
+
+	constructor: THREE.GeometryLoader,
+
+	load: function ( url, onLoad, onProgress, onError ) {
+
+		var scope = this;
+
+		var loader = new THREE.XHRLoader();
+		loader.setCrossOrigin( this.crossOrigin );
+		loader.load( url, function ( text ) {
+
+			onLoad( scope.parse( JSON.parse( text ) ) );
+
+		} );
+
+	},
+
+	setCrossOrigin: function ( value ) {
+
+		this.crossOrigin = value;
+
+	},
+
+	parse: function ( json ) {
+
+
+
+	}
+
+};
+
+/**
+ * @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();
+		loader.setCrossOrigin( this.crossOrigin );
+		loader.load( url, function ( text ) {
+
+			onLoad( scope.parse( JSON.parse( text ) ) );
+
+		} );
+
+	},
+
+	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.vertexColors !== undefined ) material.vertexColors = json.vertexColors;
+		if ( json.blending !== undefined ) material.blending = json.blending;
+		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;
+
+	}
+
+};
+
+/**
+ * @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 ) ) );
+
+		} );
+
+	},
+
+	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 'CircleGeometry':
+
+						geometry = new THREE.CircleGeometry(
+							data.radius,
+							data.segments
+						);
+
+						break;
+
+					case 'CubeGeometry':
+
+						geometry = new THREE.CubeGeometry(
+							data.width,
+							data.height,
+							data.depth,
+							data.widthSegments,
+							data.heightSegments,
+							data.depthSegments
+						);
+
+						break;
+
+					case 'CylinderGeometry':
+
+						geometry = new THREE.CylinderGeometry(
+							data.radiusTop,
+							data.radiusBottom,
+							data.height,
+							data.radiusSegments,
+							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.error( 'THREE.ObjectLoader: Undefined geometry ' + data.geometry );
+
+					}
+
+					if ( material === undefined ) {
+
+						console.error( 'THREE.ObjectLoader: Undefined material ' + data.material );
+
+					}
+
+					object = new THREE.Mesh( geometry, material );
+
+					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;
+
+		}
+
+	}()
+
+};
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.SceneLoader = function () {
+
+	this.onLoadStart = function () {};
+	this.onLoadProgress = function() {};
+	this.onLoadComplete = function () {};
+
+	this.callbackSync = function () {};
+	this.callbackProgress = function () {};
+
+	this.geometryHandlers = {};
+	this.hierarchyHandlers = {};
+
+	this.addGeometryHandler( "ascii", THREE.JSONLoader );
+
+};
+
+THREE.SceneLoader.prototype = {
+
+	constructor: THREE.SceneLoader,
+
+	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 ) {
+
+			scope.parse( JSON.parse( text ), onLoad, url );
+
+		} );
+
+	},
+
+	setCrossOrigin: function ( value ) {
+
+		this.crossOrigin = value;
+
+	},
+
+	addGeometryHandler: function ( typeID, loaderClass ) {
+
+		this.geometryHandlers[ typeID ] = { "loaderClass": loaderClass };
+
+	},
+
+	addHierarchyHandler: function ( typeID, loaderClass ) {
+
+		this.hierarchyHandlers[ typeID ] = { "loaderClass": loaderClass };
+
+	},
+
+	parse: function ( json, callbackFinished, url ) {
+
+		var scope = this;
+
+		var urlBase = THREE.Loader.prototype.extractUrlBase( url );
+
+		var geometry, material, camera, fog,
+			texture, images, color,
+			light, hex, intensity,
+			counter_models, counter_textures,
+			total_models, total_textures,
+			result;
+
+		var target_array = [];
+
+		var data = json;
+
+		// async geometry loaders
+
+		for ( var typeID in this.geometryHandlers ) {
+
+			var loaderClass = this.geometryHandlers[ typeID ][ "loaderClass" ];
+			this.geometryHandlers[ typeID ][ "loaderObject" ] = new loaderClass();
+
+		}
+
+		// async hierachy loaders
+
+		for ( var typeID in this.hierarchyHandlers ) {
+
+			var loaderClass = this.hierarchyHandlers[ typeID ][ "loaderClass" ];
+			this.hierarchyHandlers[ typeID ][ "loaderObject" ] = new loaderClass();
+
+		}
+
+		counter_models = 0;
+		counter_textures = 0;
+
+		result = {
+
+			scene: new THREE.Scene(),
+			geometries: {},
+			face_materials: {},
+			materials: {},
+			textures: {},
+			objects: {},
+			cameras: {},
+			lights: {},
+			fogs: {},
+			empties: {},
+			groups: {}
+
+		};
+
+		if ( data.transform ) {
+
+			var position = data.transform.position,
+				rotation = data.transform.rotation,
+				scale = data.transform.scale;
+
+			if ( position ) {
+
+				result.scene.position.fromArray( position );
+
+			}
+
+			if ( rotation ) {
+
+				result.scene.rotation.fromArray( rotation );
+
+			}
+
+			if ( scale ) {
+
+				result.scene.scale.fromArray( scale );
+
+			}
+
+			if ( position || rotation || scale ) {
+
+				result.scene.updateMatrix();
+				result.scene.updateMatrixWorld();
+
+			}
+
+		}
+
+		function get_url( source_url, url_type ) {
+
+			if ( url_type == "relativeToHTML" ) {
+
+				return source_url;
+
+			} else {
+
+				return urlBase + "/" + source_url;
+
+			}
+
+		};
+
+		// toplevel loader function, delegates to handle_children
+
+		function handle_objects() {
+
+			handle_children( result.scene, data.objects );
+
+		}
+
+		// handle all the children from the loaded json and attach them to given parent
+
+		function handle_children( parent, children ) {
+
+			var mat, dst, pos, rot, scl, quat;
+
+			for ( var objID in children ) {
+
+				// check by id if child has already been handled,
+				// if not, create new object
+
+				var object = result.objects[ objID ];
+				var objJSON = children[ objID ];
+
+				if ( object === undefined ) {
+
+					// meshes
+
+					if ( objJSON.type && ( objJSON.type in scope.hierarchyHandlers ) ) {
+
+						if ( objJSON.loading === undefined ) {
+
+							var reservedTypes = {
+								"type": 1, "url": 1, "material": 1,
+								"position": 1, "rotation": 1, "scale" : 1,
+								"visible": 1, "children": 1, "userData": 1,
+								"skin": 1, "morph": 1, "mirroredLoop": 1, "duration": 1
+							};
+
+							var loaderParameters = {};
+
+							for ( var parType in objJSON ) {
+
+								if ( ! ( parType in reservedTypes ) ) {
+
+									loaderParameters[ parType ] = objJSON[ parType ];
+
+								}
+
+							}
+
+							material = result.materials[ objJSON.material ];
+
+							objJSON.loading = true;
+
+							var loader = scope.hierarchyHandlers[ objJSON.type ][ "loaderObject" ];
+
+							// ColladaLoader
+
+							if ( loader.options ) {
+
+								loader.load( get_url( objJSON.url, data.urlBaseType ), create_callback_hierachy( objID, parent, material, objJSON ) );
+
+							// UTF8Loader
+							// OBJLoader
+
+							} else {
+
+								loader.load( get_url( objJSON.url, data.urlBaseType ), create_callback_hierachy( objID, parent, material, objJSON ), loaderParameters );
+
+							}
+
+						}
+
+					} else if ( objJSON.geometry !== undefined ) {
+
+						geometry = result.geometries[ objJSON.geometry ];
+
+						// geometry already loaded
+
+						if ( geometry ) {
+
+							var needsTangents = false;
+
+							material = result.materials[ objJSON.material ];
+							needsTangents = material instanceof THREE.ShaderMaterial;
+
+							pos = objJSON.position;
+							rot = objJSON.rotation;
+							scl = objJSON.scale;
+							mat = objJSON.matrix;
+							quat = objJSON.quaternion;
+
+							// use materials from the model file
+							// if there is no material specified in the object
+
+							if ( ! objJSON.material ) {
+
+								material = new THREE.MeshFaceMaterial( result.face_materials[ objJSON.geometry ] );
+
+							}
+
+							// use materials from the model file
+							// if there is just empty face material
+							// (must create new material as each model has its own face material)
+
+							if ( ( material instanceof THREE.MeshFaceMaterial ) && material.materials.length === 0 ) {
+
+								material = new THREE.MeshFaceMaterial( result.face_materials[ objJSON.geometry ] );
+
+							}
+
+							if ( material instanceof THREE.MeshFaceMaterial ) {
+
+								for ( var i = 0; i < material.materials.length; i ++ ) {
+
+									needsTangents = needsTangents || ( material.materials[ i ] instanceof THREE.ShaderMaterial );
+
+								}
+
+							}
+
+							if ( needsTangents ) {
+
+								geometry.computeTangents();
+
+							}
+
+							if ( objJSON.skin ) {
+
+								object = new THREE.SkinnedMesh( geometry, material );
+
+							} else if ( objJSON.morph ) {
+
+								object = new THREE.MorphAnimMesh( geometry, material );
+
+								if ( objJSON.duration !== undefined ) {
+
+									object.duration = objJSON.duration;
+
+								}
+
+								if ( objJSON.time !== undefined ) {
+
+									object.time = objJSON.time;
+
+								}
+
+								if ( objJSON.mirroredLoop !== undefined ) {
+
+									object.mirroredLoop = objJSON.mirroredLoop;
+
+								}
+
+								if ( material.morphNormals ) {
+
+									geometry.computeMorphNormals();
+
+								}
+
+							} else {
+
+								object = new THREE.Mesh( geometry, material );
+
+							}
+
+							object.name = objID;
+
+							if ( mat ) {
+
+								object.matrixAutoUpdate = false;
+								object.matrix.set(
+									mat[0],  mat[1],  mat[2],  mat[3],
+									mat[4],  mat[5],  mat[6],  mat[7],
+									mat[8],  mat[9],  mat[10], mat[11],
+									mat[12], mat[13], mat[14], mat[15]
+								);
+
+							} else {
+
+								object.position.fromArray( pos );
+
+								if ( quat ) {
+
+									object.quaternion.fromArray( quat );
+
+								} else {
+
+									object.rotation.fromArray( rot );
+
+								}
+
+								object.scale.fromArray( scl );
+
+							}
+
+							object.visible = objJSON.visible;
+							object.castShadow = objJSON.castShadow;
+							object.receiveShadow = objJSON.receiveShadow;
+
+							parent.add( object );
+
+							result.objects[ objID ] = object;
+
+						}
+
+					// lights
+
+					} else if ( objJSON.type === "DirectionalLight" || objJSON.type === "PointLight" || objJSON.type === "AmbientLight" ) {
+
+						hex = ( objJSON.color !== undefined ) ? objJSON.color : 0xffffff;
+						intensity = ( objJSON.intensity !== undefined ) ? objJSON.intensity : 1;
+
+						if ( objJSON.type === "DirectionalLight" ) {
+
+							pos = objJSON.direction;
+
+							light = new THREE.DirectionalLight( hex, intensity );
+							light.position.fromArray( pos );
+
+							if ( objJSON.target ) {
+
+								target_array.push( { "object": light, "targetName" : objJSON.target } );
+
+								// kill existing default target
+								// otherwise it gets added to scene when parent gets added
+
+								light.target = null;
+
+							}
+
+						} else if ( objJSON.type === "PointLight" ) {
+
+							pos = objJSON.position;
+							dst = objJSON.distance;
+
+							light = new THREE.PointLight( hex, intensity, dst );
+							light.position.fromArray( pos );
+
+						} else if ( objJSON.type === "AmbientLight" ) {
+
+							light = new THREE.AmbientLight( hex );
+
+						}
+
+						parent.add( light );
+
+						light.name = objID;
+						result.lights[ objID ] = light;
+						result.objects[ objID ] = light;
+
+					// cameras
+
+					} else if ( objJSON.type === "PerspectiveCamera" || objJSON.type === "OrthographicCamera" ) {
+
+						pos = objJSON.position;
+						rot = objJSON.rotation;
+						quat = objJSON.quaternion;
+
+						if ( objJSON.type === "PerspectiveCamera" ) {
+
+							camera = new THREE.PerspectiveCamera( objJSON.fov, objJSON.aspect, objJSON.near, objJSON.far );
+
+						} else if ( objJSON.type === "OrthographicCamera" ) {
+
+							camera = new THREE.OrthographicCamera( objJSON.left, objJSON.right, objJSON.top, objJSON.bottom, objJSON.near, objJSON.far );
+
+						}
+
+						camera.name = objID;
+						camera.position.fromArray( pos );
+
+						if ( quat !== undefined ) {
+
+							camera.quaternion.fromArray( quat );
+
+						} else if ( rot !== undefined ) {
+
+							camera.rotation.fromArray( rot );
+
+						}
+
+						parent.add( camera );
+
+						result.cameras[ objID ] = camera;
+						result.objects[ objID ] = camera;
+
+					// pure Object3D
+
+					} else {
+
+						pos = objJSON.position;
+						rot = objJSON.rotation;
+						scl = objJSON.scale;
+						quat = objJSON.quaternion;
+
+						object = new THREE.Object3D();
+						object.name = objID;
+						object.position.fromArray( pos );
+
+						if ( quat ) {
+
+							object.quaternion.fromArray( quat );
+
+						} else {
+
+							object.rotation.fromArray( rot );
+
+						}
+
+						object.scale.fromArray( scl );
+						object.visible = ( objJSON.visible !== undefined ) ? objJSON.visible : false;
+
+						parent.add( object );
+
+						result.objects[ objID ] = object;
+						result.empties[ objID ] = object;
+
+					}
+
+					if ( object ) {
+
+						if ( objJSON.userData !== undefined ) {
+
+							for ( var key in objJSON.userData ) {
+
+								var value = objJSON.userData[ key ];
+								object.userData[ key ] = value;
+
+							}
+
+						}
+
+						if ( objJSON.groups !== undefined ) {
+
+							for ( var i = 0; i < objJSON.groups.length; i ++ ) {
+
+								var groupID = objJSON.groups[ i ];
+
+								if ( result.groups[ groupID ] === undefined ) {
+
+									result.groups[ groupID ] = [];
+
+								}
+
+								result.groups[ groupID ].push( objID );
+
+							}
+
+						}
+
+					}
+
+				}
+
+				if ( object !== undefined && objJSON.children !== undefined ) {
+
+					handle_children( object, objJSON.children );
+
+				}
+
+			}
+
+		};
+
+		function handle_mesh( geo, mat, id ) {
+
+			result.geometries[ id ] = geo;
+			result.face_materials[ id ] = mat;
+			handle_objects();
+
+		};
+
+		function handle_hierarchy( node, id, parent, material, obj ) {
+
+			var p = obj.position;
+			var r = obj.rotation;
+			var q = obj.quaternion;
+			var s = obj.scale;
+
+			node.position.fromArray( p );
+
+			if ( q ) {
+
+				node.quaternion.fromArray( q );
+
+			} else {
+
+				node.rotation.fromArray( r );
+
+			}
+
+			node.scale.fromArray( s );
+
+			// override children materials
+			// if object material was specified in JSON explicitly
+
+			if ( material ) {
+
+				node.traverse( function ( child ) {
+
+					child.material = material;
+
+				} );
+
+			}
+
+			// override children visibility
+			// with root node visibility as specified in JSON
+
+			var visible = ( obj.visible !== undefined ) ? obj.visible : true;
+
+			node.traverse( function ( child ) {
+
+				child.visible = visible;
+
+			} );
+
+			parent.add( node );
+
+			node.name = id;
+
+			result.objects[ id ] = node;
+			handle_objects();
+
+		};
+
+		function create_callback_geometry( id ) {
+
+			return function ( geo, mat ) {
+
+				geo.name = id;
+
+				handle_mesh( geo, mat, id );
+
+				counter_models -= 1;
+
+				scope.onLoadComplete();
+
+				async_callback_gate();
+
+			}
+
+		};
+
+		function create_callback_hierachy( id, parent, material, obj ) {
+
+			return function ( event ) {
+
+				var result;
+
+				// loaders which use EventDispatcher
+
+				if ( event.content ) {
+
+					result = event.content;
+
+				// ColladaLoader
+
+				} else if ( event.dae ) {
+
+					result = event.scene;
+
+
+				// UTF8Loader
+
+				} else {
+
+					result = event;
+
+				}
+
+				handle_hierarchy( result, id, parent, material, obj );
+
+				counter_models -= 1;
+
+				scope.onLoadComplete();
+
+				async_callback_gate();
+
+			}
+
+		};
+
+		function create_callback_embed( id ) {
+
+			return function ( geo, mat ) {
+
+				geo.name = id;
+
+				result.geometries[ id ] = geo;
+				result.face_materials[ id ] = mat;
+
+			}
+
+		};
+
+		function async_callback_gate() {
+
+			var progress = {
+
+				totalModels : total_models,
+				totalTextures : total_textures,
+				loadedModels : total_models - counter_models,
+				loadedTextures : total_textures - counter_textures
+
+			};
+
+			scope.callbackProgress( progress, result );
+
+			scope.onLoadProgress();
+
+			if ( counter_models === 0 && counter_textures === 0 ) {
+
+				finalize();
+				callbackFinished( result );
+
+			}
+
+		};
+
+		function finalize() {
+
+			// take care of targets which could be asynchronously loaded objects
+
+			for ( var i = 0; i < target_array.length; i ++ ) {
+
+				var ta = target_array[ i ];
+
+				var target = result.objects[ ta.targetName ];
+
+				if ( target ) {
+
+					ta.object.target = target;
+
+				} else {
+
+					// if there was error and target of specified name doesn't exist in the scene file
+					// create instead dummy target
+					// (target must be added to scene explicitly as parent is already added)
+
+					ta.object.target = new THREE.Object3D();
+					result.scene.add( ta.object.target );
+
+				}
+
+				ta.object.target.userData.targetInverse = ta.object;
+
+			}
+
+		};
+
+		var callbackTexture = function ( count ) {
+
+			counter_textures -= count;
+			async_callback_gate();
+
+			scope.onLoadComplete();
+
+		};
+
+		// must use this instead of just directly calling callbackTexture
+		// because of closure in the calling context loop
+
+		var generateTextureCallback = function ( count ) {
+
+			return function () {
+
+				callbackTexture( count );
+
+			};
+
+		};
+
+		function traverse_json_hierarchy( objJSON, callback ) {
+
+			callback( objJSON );
+
+			if ( objJSON.children !== undefined ) {
+
+				for ( var objChildID in objJSON.children ) {
+
+					traverse_json_hierarchy( objJSON.children[ objChildID ], callback );
+
+				}
+
+			}
+
+		};
+
+		// first go synchronous elements
+
+		// fogs
+
+		var fogID, fogJSON;
+
+		for ( fogID in data.fogs ) {
+
+			fogJSON = data.fogs[ fogID ];
+
+			if ( fogJSON.type === "linear" ) {
+
+				fog = new THREE.Fog( 0x000000, fogJSON.near, fogJSON.far );
+
+			} else if ( fogJSON.type === "exp2" ) {
+
+				fog = new THREE.FogExp2( 0x000000, fogJSON.density );
+
+			}
+
+			color = fogJSON.color;
+			fog.color.setRGB( color[0], color[1], color[2] );
+
+			result.fogs[ fogID ] = fog;
+
+		}
+
+		// now come potentially asynchronous elements
+
+		// geometries
+
+		// count how many geometries will be loaded asynchronously
+
+		var geoID, geoJSON;
+
+		for ( geoID in data.geometries ) {
+
+			geoJSON = data.geometries[ geoID ];
+
+			if ( geoJSON.type in this.geometryHandlers ) {
+
+				counter_models += 1;
+
+				scope.onLoadStart();
+
+			}
+
+		}
+
+		// count how many hierarchies will be loaded asynchronously
+
+		for ( var objID in data.objects ) {
+
+			traverse_json_hierarchy( data.objects[ objID ], function ( objJSON ) {
+
+				if ( objJSON.type && ( objJSON.type in scope.hierarchyHandlers ) ) {
+
+					counter_models += 1;
+
+					scope.onLoadStart();
+
+				}
+
+			});
+
+		}
+
+		total_models = counter_models;
+
+		for ( geoID in data.geometries ) {
+
+			geoJSON = data.geometries[ geoID ];
+
+			if ( geoJSON.type === "cube" ) {
+
+				geometry = new THREE.CubeGeometry( geoJSON.width, geoJSON.height, geoJSON.depth, geoJSON.widthSegments, geoJSON.heightSegments, geoJSON.depthSegments );
+				geometry.name = geoID;
+				result.geometries[ geoID ] = geometry;
+
+			} else if ( geoJSON.type === "plane" ) {
+
+				geometry = new THREE.PlaneGeometry( geoJSON.width, geoJSON.height, geoJSON.widthSegments, geoJSON.heightSegments );
+				geometry.name = geoID;
+				result.geometries[ geoID ] = geometry;
+
+			} else if ( geoJSON.type === "sphere" ) {
+
+				geometry = new THREE.SphereGeometry( geoJSON.radius, geoJSON.widthSegments, geoJSON.heightSegments );
+				geometry.name = geoID;
+				result.geometries[ geoID ] = geometry;
+
+			} else if ( geoJSON.type === "cylinder" ) {
+
+				geometry = new THREE.CylinderGeometry( geoJSON.topRad, geoJSON.botRad, geoJSON.height, geoJSON.radSegs, geoJSON.heightSegs );
+				geometry.name = geoID;
+				result.geometries[ geoID ] = geometry;
+
+			} else if ( geoJSON.type === "torus" ) {
+
+				geometry = new THREE.TorusGeometry( geoJSON.radius, geoJSON.tube, geoJSON.segmentsR, geoJSON.segmentsT );
+				geometry.name = geoID;
+				result.geometries[ geoID ] = geometry;
+
+			} else if ( geoJSON.type === "icosahedron" ) {
+
+				geometry = new THREE.IcosahedronGeometry( geoJSON.radius, geoJSON.subdivisions );
+				geometry.name = geoID;
+				result.geometries[ geoID ] = geometry;
+
+			} else if ( geoJSON.type in this.geometryHandlers ) {
+
+				var loaderParameters = {};
+
+				for ( var parType in geoJSON ) {
+
+					if ( parType !== "type" && parType !== "url" ) {
+
+						loaderParameters[ parType ] = geoJSON[ parType ];
+
+					}
+
+				}
+
+				var loader = this.geometryHandlers[ geoJSON.type ][ "loaderObject" ];
+				loader.load( get_url( geoJSON.url, data.urlBaseType ), create_callback_geometry( geoID ), loaderParameters );
+
+			} else if ( geoJSON.type === "embedded" ) {
+
+				var modelJson = data.embeds[ geoJSON.id ],
+					texture_path = "";
+
+				// pass metadata along to jsonLoader so it knows the format version
+
+				modelJson.metadata = data.metadata;
+
+				if ( modelJson ) {
+
+					var jsonLoader = this.geometryHandlers[ "ascii" ][ "loaderObject" ];
+					var model = jsonLoader.parse( modelJson, texture_path );
+					create_callback_embed( geoID )( model.geometry, model.materials );
+
+				}
+
+			}
+
+		}
+
+		// textures
+
+		// count how many textures will be loaded asynchronously
+
+		var textureID, textureJSON;
+
+		for ( textureID in data.textures ) {
+
+			textureJSON = data.textures[ textureID ];
+
+			if ( textureJSON.url instanceof Array ) {
+
+				counter_textures += textureJSON.url.length;
+
+				for( var n = 0; n < textureJSON.url.length; n ++ ) {
+
+					scope.onLoadStart();
+
+				}
+
+			} else {
+
+				counter_textures += 1;
+
+				scope.onLoadStart();
+
+			}
+
+		}
+
+		total_textures = counter_textures;
+
+		for ( textureID in data.textures ) {
+
+			textureJSON = data.textures[ textureID ];
+
+			if ( textureJSON.mapping !== undefined && THREE[ textureJSON.mapping ] !== undefined ) {
+
+				textureJSON.mapping = new THREE[ textureJSON.mapping ]();
+
+			}
+
+			if ( textureJSON.url instanceof Array ) {
+
+				var count = textureJSON.url.length;
+				var url_array = [];
+
+				for( var i = 0; i < count; i ++ ) {
+
+					url_array[ i ] = get_url( textureJSON.url[ i ], data.urlBaseType );
+
+				}
+
+				var isCompressed = /\.dds$/i.test( url_array[ 0 ] );
+
+				if ( isCompressed ) {
+
+					texture = THREE.ImageUtils.loadCompressedTextureCube( url_array, textureJSON.mapping, generateTextureCallback( count ) );
+
+				} else {
+
+					texture = THREE.ImageUtils.loadTextureCube( url_array, textureJSON.mapping, generateTextureCallback( count ) );
+
+				}
+
+			} else {
+
+				var isCompressed = /\.dds$/i.test( textureJSON.url );
+				var fullUrl = get_url( textureJSON.url, data.urlBaseType );
+				var textureCallback = generateTextureCallback( 1 );
+
+				if ( isCompressed ) {
+
+					texture = THREE.ImageUtils.loadCompressedTexture( fullUrl, textureJSON.mapping, textureCallback );
+
+				} else {
+
+					texture = THREE.ImageUtils.loadTexture( fullUrl, textureJSON.mapping, textureCallback );
+
+				}
+
+				if ( THREE[ textureJSON.minFilter ] !== undefined )
+					texture.minFilter = THREE[ textureJSON.minFilter ];
+
+				if ( THREE[ textureJSON.magFilter ] !== undefined )
+					texture.magFilter = THREE[ textureJSON.magFilter ];
+
+				if ( textureJSON.anisotropy ) texture.anisotropy = textureJSON.anisotropy;
+
+				if ( textureJSON.repeat ) {
+
+					texture.repeat.set( textureJSON.repeat[ 0 ], textureJSON.repeat[ 1 ] );
+
+					if ( textureJSON.repeat[ 0 ] !== 1 ) texture.wrapS = THREE.RepeatWrapping;
+					if ( textureJSON.repeat[ 1 ] !== 1 ) texture.wrapT = THREE.RepeatWrapping;
+
+				}
+
+				if ( textureJSON.offset ) {
+
+					texture.offset.set( textureJSON.offset[ 0 ], textureJSON.offset[ 1 ] );
+
+				}
+
+				// handle wrap after repeat so that default repeat can be overriden
+
+				if ( textureJSON.wrap ) {
+
+					var wrapMap = {
+						"repeat": THREE.RepeatWrapping,
+						"mirror": THREE.MirroredRepeatWrapping
+					}
+
+					if ( wrapMap[ textureJSON.wrap[ 0 ] ] !== undefined ) texture.wrapS = wrapMap[ textureJSON.wrap[ 0 ] ];
+					if ( wrapMap[ textureJSON.wrap[ 1 ] ] !== undefined ) texture.wrapT = wrapMap[ textureJSON.wrap[ 1 ] ];
+
+				}
+
+			}
+
+			result.textures[ textureID ] = texture;
+
+		}
+
+		// materials
+
+		var matID, matJSON;
+		var parID;
+
+		for ( matID in data.materials ) {
+
+			matJSON = data.materials[ matID ];
+
+			for ( parID in matJSON.parameters ) {
+
+				if ( parID === "envMap" || parID === "map" || parID === "lightMap" || parID === "bumpMap" ) {
+
+					matJSON.parameters[ parID ] = result.textures[ matJSON.parameters[ parID ] ];
+
+				} else if ( parID === "shading" ) {
+
+					matJSON.parameters[ parID ] = ( matJSON.parameters[ parID ] === "flat" ) ? THREE.FlatShading : THREE.SmoothShading;
+
+				} else if ( parID === "side" ) {
+
+					if ( matJSON.parameters[ parID ] == "double" ) {
+
+						matJSON.parameters[ parID ] = THREE.DoubleSide;
+
+					} else if ( matJSON.parameters[ parID ] == "back" ) {
+
+						matJSON.parameters[ parID ] = THREE.BackSide;
+
+					} else {
+
+						matJSON.parameters[ parID ] = THREE.FrontSide;
+
+					}
+
+				} else if ( parID === "blending" ) {
+
+					matJSON.parameters[ parID ] = matJSON.parameters[ parID ] in THREE ? THREE[ matJSON.parameters[ parID ] ] : THREE.NormalBlending;
+
+				} else if ( parID === "combine" ) {
+
+					matJSON.parameters[ parID ] = matJSON.parameters[ parID ] in THREE ? THREE[ matJSON.parameters[ parID ] ] : THREE.MultiplyOperation;
+
+				} else if ( parID === "vertexColors" ) {
+
+					if ( matJSON.parameters[ parID ] == "face" ) {
+
+						matJSON.parameters[ parID ] = THREE.FaceColors;
+
+					// default to vertex colors if "vertexColors" is anything else face colors or 0 / null / false
+
+					} else if ( matJSON.parameters[ parID ] ) {
+
+						matJSON.parameters[ parID ] = THREE.VertexColors;
+
+					}
+
+				} else if ( parID === "wrapRGB" ) {
+
+					var v3 = matJSON.parameters[ parID ];
+					matJSON.parameters[ parID ] = new THREE.Vector3( v3[ 0 ], v3[ 1 ], v3[ 2 ] );
+
+				}
+
+			}
+
+			if ( matJSON.parameters.opacity !== undefined && matJSON.parameters.opacity < 1.0 ) {
+
+				matJSON.parameters.transparent = true;
+
+			}
+
+			if ( matJSON.parameters.normalMap ) {
+
+				var shader = THREE.ShaderLib[ "normalmap" ];
+				var uniforms = THREE.UniformsUtils.clone( shader.uniforms );
+
+				var diffuse = matJSON.parameters.color;
+				var specular = matJSON.parameters.specular;
+				var ambient = matJSON.parameters.ambient;
+				var shininess = matJSON.parameters.shininess;
+
+				uniforms[ "tNormal" ].value = result.textures[ matJSON.parameters.normalMap ];
+
+				if ( matJSON.parameters.normalScale ) {
+
+					uniforms[ "uNormalScale" ].value.set( matJSON.parameters.normalScale[ 0 ], matJSON.parameters.normalScale[ 1 ] );
+
+				}
+
+				if ( matJSON.parameters.map ) {
+
+					uniforms[ "tDiffuse" ].value = matJSON.parameters.map;
+					uniforms[ "enableDiffuse" ].value = true;
+
+				}
+
+				if ( matJSON.parameters.envMap ) {
+
+					uniforms[ "tCube" ].value = matJSON.parameters.envMap;
+					uniforms[ "enableReflection" ].value = true;
+					uniforms[ "uReflectivity" ].value = matJSON.parameters.reflectivity;
+
+				}
+
+				if ( matJSON.parameters.lightMap ) {
+
+					uniforms[ "tAO" ].value = matJSON.parameters.lightMap;
+					uniforms[ "enableAO" ].value = true;
+
+				}
+
+				if ( matJSON.parameters.specularMap ) {
+
+					uniforms[ "tSpecular" ].value = result.textures[ matJSON.parameters.specularMap ];
+					uniforms[ "enableSpecular" ].value = true;
+
+				}
+
+				if ( matJSON.parameters.displacementMap ) {
+
+					uniforms[ "tDisplacement" ].value = result.textures[ matJSON.parameters.displacementMap ];
+					uniforms[ "enableDisplacement" ].value = true;
+
+					uniforms[ "uDisplacementBias" ].value = matJSON.parameters.displacementBias;
+					uniforms[ "uDisplacementScale" ].value = matJSON.parameters.displacementScale;
+
+				}
+
+				uniforms[ "uDiffuseColor" ].value.setHex( diffuse );
+				uniforms[ "uSpecularColor" ].value.setHex( specular );
+				uniforms[ "uAmbientColor" ].value.setHex( ambient );
+
+				uniforms[ "uShininess" ].value = shininess;
+
+				if ( matJSON.parameters.opacity ) {
+
+					uniforms[ "uOpacity" ].value = matJSON.parameters.opacity;
+
+				}
+
+				var parameters = { fragmentShader: shader.fragmentShader, vertexShader: shader.vertexShader, uniforms: uniforms, lights: true, fog: true };
+
+				material = new THREE.ShaderMaterial( parameters );
+
+			} else {
+
+				material = new THREE[ matJSON.type ]( matJSON.parameters );
+
+			}
+
+			material.name = matID;
+
+			result.materials[ matID ] = material;
+
+		}
+
+		// second pass through all materials to initialize MeshFaceMaterials
+		// that could be referring to other materials out of order
+
+		for ( matID in data.materials ) {
+
+			matJSON = data.materials[ matID ];
+
+			if ( matJSON.parameters.materials ) {
+
+				var materialArray = [];
+
+				for ( var i = 0; i < matJSON.parameters.materials.length; i ++ ) {
+
+					var label = matJSON.parameters.materials[ i ];
+					materialArray.push( result.materials[ label ] );
+
+				}
+
+				result.materials[ matID ].materials = materialArray;
+
+			}
+
+		}
+
+		// objects ( synchronous init of procedural primitives )
+
+		handle_objects();
+
+		// defaults
+
+		if ( result.cameras && data.defaults.camera ) {
+
+			result.currentCamera = result.cameras[ data.defaults.camera ];
+
+		}
+
+		if ( result.fogs && data.defaults.fog ) {
+
+			result.scene.fog = result.fogs[ data.defaults.fog ];
+
+		}
+
+		// synchronous callback
+
+		scope.callbackSync( result );
+
+		// just in case there are no async elements
+
+		async_callback_gate();
+
+	}
+
+}
+
+/**
+ * @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 );
+
+			}
+
+		} );
+
+	},
+
+	setCrossOrigin: function ( value ) {
+
+		this.crossOrigin = value;
+
+	}
+
+};
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.Material = function () {
+
+	this.id = THREE.MaterialIdCount ++;
+	this.uuid = THREE.Math.generateUUID();
+
+	this.name = '';
+
+	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;
+
+				}
+
+			}
+
+		}
+
+	},
+
+	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;
+
+/**
+ * @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.color = new THREE.Color( 0xffffff );
+
+	this.linewidth = 1;
+	this.linecap = 'round';
+	this.linejoin = 'round';
+
+	this.vertexColors = false;
+
+	this.fog = true;
+
+	this.setValues( parameters );
+
+};
+
+THREE.LineBasicMaterial.prototype = Object.create( THREE.Material.prototype );
+
+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;
+
+};
+
+/**
+ * @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.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.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;
+
+};
+
+/**
+ * @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> ),
+ *
+ *  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.color = new THREE.Color( 0xffffff ); // emissive
+
+	this.map = null;
+
+	this.lightMap = null;
+
+	this.specularMap = 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.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.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;
+
+};
+
+/**
+ * @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> ),
+ *
+ *  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.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.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.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.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;
+
+};
+
+/**
+ * @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> ),
+ *
+ *  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.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.perPixel = true;
+
+	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.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.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.perPixel = this.perPixel;
+
+	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.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;
+
+};
+
+/**
+ * @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.wireframe = false;
+	this.wireframeLinewidth = 1;
+
+	this.setValues( parameters );
+
+};
+
+THREE.MeshDepthMaterial.prototype = Object.create( THREE.Material.prototype );
+
+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;
+
+};
+
+/**
+ * @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.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.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;
+
+};
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.MeshFaceMaterial = function ( materials ) {
+
+	this.materials = materials instanceof Array ? materials : [];
+
+};
+
+THREE.MeshFaceMaterial.prototype.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;
+
+};
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ *
+ * parameters = {
+ *  color: <hex>,
+ *  opacity: <float>,
+ *  map: new THREE.Texture( <Image> ),
+ *
+ *  size: <float>,
+ *
+ *  blending: THREE.NormalBlending,
+ *  depthTest: <bool>,
+ *  depthWrite: <bool>,
+ *
+ *  vertexColors: <bool>,
+ *
+ *  fog: <bool>
+ * }
+ */
+
+THREE.ParticleSystemMaterial = function ( parameters ) {
+
+	THREE.Material.call( this );
+
+	this.color = new THREE.Color( 0xffffff );
+
+	this.map = null;
+
+	this.size = 1;
+	this.sizeAttenuation = true;
+
+	this.vertexColors = false;
+
+	this.fog = true;
+
+	this.setValues( parameters );
+
+};
+
+THREE.ParticleSystemMaterial.prototype = Object.create( THREE.Material.prototype );
+
+THREE.ParticleSystemMaterial.prototype.clone = function () {
+
+	var material = new THREE.ParticleSystemMaterial();
+
+	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 = THREE.ParticleSystemMaterial;
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ *
+ * parameters = {
+ *  fragmentShader: <string>,
+ *  vertexShader: <string>,
+ *
+ *  uniforms: { "parameter1": { type: "f", value: 1.0 }, "parameter2": { type: "i" value2: 2 } },
+ *
+ *  defines: { "label" : "value" },
+ *
+ *  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.fragmentShader = "void main() {}";
+	this.vertexShader = "void main() {}";
+	this.uniforms = {};
+	this.defines = {};
+	this.attributes = null;
+
+	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 ]
+	};
+
+	// By default, bind position to attribute index 0. In WebGL, attribute 0
+	// should always be used to avoid potentially expensive emulation.
+	this.index0AttributeName = "position";
+
+	this.setValues( parameters );
+
+};
+
+THREE.ShaderMaterial.prototype = Object.create( THREE.Material.prototype );
+
+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;
+
+};
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ *
+ * parameters = {
+ *  color: <hex>,
+ *  opacity: <float>,
+ *  map: new THREE.Texture( <Image> ),
+ *
+ *  blending: THREE.NormalBlending,
+ *  depthTest: <bool>,
+ *  depthWrite: <bool>,
+ *
+ *  useScreenCoordinates: <bool>,
+ *  sizeAttenuation: <bool>,
+ *  alignment: THREE.SpriteAlignment.center,
+ *
+ *	uvOffset: new THREE.Vector2(),
+ *	uvScale: new THREE.Vector2(),
+ *
+ *  fog: <bool>
+ * }
+ */
+
+THREE.SpriteMaterial = function ( parameters ) {
+
+	THREE.Material.call( this );
+
+	// defaults
+
+	this.color = new THREE.Color( 0xffffff );
+	this.map = new THREE.Texture();
+
+	this.useScreenCoordinates = true;
+	this.depthTest = !this.useScreenCoordinates;
+	this.sizeAttenuation = !this.useScreenCoordinates;
+	this.alignment = THREE.SpriteAlignment.center.clone();
+
+	this.fog = false;
+
+	this.uvOffset = new THREE.Vector2( 0, 0 );
+	this.uvScale  = new THREE.Vector2( 1, 1 );
+
+	// set parameters
+
+	this.setValues( parameters );
+
+	// override coupled defaults if not specified explicitly by parameters
+
+	parameters = parameters || {};
+
+	if ( parameters.depthTest === undefined ) this.depthTest = !this.useScreenCoordinates;
+	if ( parameters.sizeAttenuation === undefined ) this.sizeAttenuation = !this.useScreenCoordinates;
+
+};
+
+THREE.SpriteMaterial.prototype = Object.create( THREE.Material.prototype );
+
+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.useScreenCoordinates = this.useScreenCoordinates;
+	material.sizeAttenuation = this.sizeAttenuation;
+	material.alignment.copy( this.alignment );
+
+	material.uvOffset.copy( this.uvOffset );
+	material.uvScale.copy( this.uvScale );
+
+	material.fog = this.fog;
+
+	return material;
+
+};
+
+// Alignment enums
+
+THREE.SpriteAlignment = {};
+THREE.SpriteAlignment.topLeft = new THREE.Vector2( 0.5, -0.5 );
+THREE.SpriteAlignment.topCenter = new THREE.Vector2( 0, -0.5 );
+THREE.SpriteAlignment.topRight = new THREE.Vector2( -0.5, -0.5 );
+THREE.SpriteAlignment.centerLeft = new THREE.Vector2( 0.5, 0 );
+THREE.SpriteAlignment.center = new THREE.Vector2( 0, 0 );
+THREE.SpriteAlignment.centerRight = new THREE.Vector2( -0.5, 0 );
+THREE.SpriteAlignment.bottomLeft = new THREE.Vector2( 0.5, 0.5 );
+THREE.SpriteAlignment.bottomCenter = new THREE.Vector2( 0, 0.5 );
+THREE.SpriteAlignment.bottomRight = new THREE.Vector2( -0.5, 0.5 );
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ *
+ * parameters = {
+ *  color: <hex>,
+ *  program: <function>,
+ *  opacity: <float>,
+ *  blending: THREE.NormalBlending
+ * }
+ */
+
+THREE.SpriteCanvasMaterial = function ( parameters ) {
+
+	THREE.Material.call( this );
+
+	this.color = new THREE.Color( 0xffffff );
+	this.program = function ( context, color ) {};
+
+	this.setValues( parameters );
+
+};
+
+THREE.SpriteCanvasMaterial.prototype = Object.create( THREE.Material.prototype );
+
+THREE.SpriteCanvasMaterial.prototype.clone = function () {
+
+	var material = new THREE.SpriteCanvasMaterial();
+
+	THREE.Material.prototype.clone.call( this, material );
+
+	material.color.copy( this.color );
+	material.program = this.program;
+
+	return material;
+
+};
+
+// backwards compatibility
+
+THREE.ParticleCanvasMaterial = THREE.SpriteCanvasMaterial;
+/**
+ * @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 ) {
+
+	this.id = THREE.TextureIdCount ++;
+	this.uuid = THREE.Math.generateUUID();
+
+	this.name = '';
+
+	this.image = image;
+	this.mipmaps = [];
+
+	this.mapping = mapping !== undefined ? mapping : new THREE.UVMapping();
+
+	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.prototype = {
+
+	constructor: THREE.Texture,
+
+	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;
+
+	},
+
+	dispose: function () {
+
+		this.dispatchEvent( { type: 'dispose' } );
+
+	}
+
+};
+
+THREE.EventDispatcher.prototype.apply( THREE.Texture.prototype );
+
+THREE.TextureIdCount = 0;
+
+/**
+ * @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;
+
+	this.generateMipmaps = false; // WebGL currently can't generate mipmaps for compressed textures, they must be embedded in DDS file
+
+};
+
+THREE.CompressedTexture.prototype = Object.create( THREE.Texture.prototype );
+
+THREE.CompressedTexture.prototype.clone = function () {
+
+	var texture = new THREE.CompressedTexture();
+
+	THREE.Texture.prototype.clone.call( this, texture );
+
+	return texture;
+
+};
+
+/**
+ * @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.clone = function () {
+
+	var texture = new THREE.DataTexture();
+
+	THREE.Texture.prototype.clone.call( this, texture );
+
+	return texture;
+
+};
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.ParticleSystem = function ( geometry, material ) {
+
+	THREE.Object3D.call( this );
+
+	this.geometry = geometry !== undefined ? geometry : new THREE.Geometry();
+	this.material = material !== undefined ? material : new THREE.ParticleSystemMaterial( { color: Math.random() * 0xffffff } );
+
+	this.sortParticles = false;
+	this.frustumCulled = false;
+
+};
+
+THREE.ParticleSystem.prototype = Object.create( THREE.Object3D.prototype );
+
+THREE.ParticleSystem.prototype.clone = function ( object ) {
+
+	if ( object === undefined ) object = new THREE.ParticleSystem( this.geometry, this.material );
+
+	object.sortParticles = this.sortParticles;
+
+	THREE.Object3D.prototype.clone.call( this, object );
+
+	return object;
+
+};
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.Line = function ( geometry, material, type ) {
+
+	THREE.Object3D.call( this );
+
+	this.geometry = geometry !== undefined ? geometry : new THREE.Geometry();
+	this.material = material !== undefined ? material : new THREE.LineBasicMaterial( { color: Math.random() * 0xffffff } );
+
+	this.type = ( type !== undefined ) ? type : THREE.LineStrip;
+
+};
+
+THREE.LineStrip = 0;
+THREE.LinePieces = 1;
+
+THREE.Line.prototype = Object.create( THREE.Object3D.prototype );
+
+THREE.Line.prototype.clone = function ( object ) {
+
+	if ( object === undefined ) object = new THREE.Line( this.geometry, this.material, this.type );
+
+	THREE.Object3D.prototype.clone.call( this, object );
+
+	return object;
+
+};
+
+/**
+ * @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.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.updateMorphTargets = function () {
+
+	if ( 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.clone = function ( object ) {
+
+	if ( object === undefined ) object = new THREE.Mesh( this.geometry, this.material );
+
+	THREE.Object3D.prototype.clone.call( this, object );
+
+	return object;
+
+};
+
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.Bone = function( belongsToSkin ) {
+
+	THREE.Object3D.call( this );
+
+	this.skin = belongsToSkin;
+	this.skinMatrix = new THREE.Matrix4();
+
+};
+
+THREE.Bone.prototype = Object.create( THREE.Object3D.prototype );
+
+THREE.Bone.prototype.update = function ( parentSkinMatrix, forceUpdate ) {
+
+	// update local
+
+	if ( this.matrixAutoUpdate ) {
+
+		forceUpdate |= this.updateMatrix();
+
+	}
+
+	// update skin matrix
+
+	if ( forceUpdate || this.matrixWorldNeedsUpdate ) {
+
+		if( parentSkinMatrix ) {
+
+			this.skinMatrix.multiplyMatrices( parentSkinMatrix, this.matrix );
+
+		} else {
+
+			this.skinMatrix.copy( this.matrix );
+
+		}
+
+		this.matrixWorldNeedsUpdate = false;
+		forceUpdate = true;
+
+	}
+
+	// update children
+
+	var child, i, l = this.children.length;
+
+	for ( i = 0; i < l; i ++ ) {
+
+		this.children[ i ].update( this.skinMatrix, forceUpdate );
+
+	}
+
+};
+
+
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.SkinnedMesh = function ( geometry, material, useVertexTexture ) {
+
+	THREE.Mesh.call( this, geometry, material );
+
+	//
+
+	this.useVertexTexture = useVertexTexture !== undefined ? useVertexTexture : true;
+
+	// init bones
+
+	this.identityMatrix = new THREE.Matrix4();
+
+	this.bones = [];
+	this.boneMatrices = [];
+
+	var b, bone, gbone, p, q, s;
+
+	if ( this.geometry && this.geometry.bones !== undefined ) {
+
+		for ( b = 0; b < this.geometry.bones.length; b ++ ) {
+
+			gbone = this.geometry.bones[ b ];
+
+			p = gbone.pos;
+			q = gbone.rotq;
+			s = gbone.scl;
+
+			bone = this.addBone();
+
+			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 ( b = 0; b < this.bones.length; b ++ ) {
+
+			gbone = this.geometry.bones[ b ];
+			bone = this.bones[ b ];
+
+			if ( gbone.parent === -1 ) {
+
+				this.add( bone );
+
+			} else {
+
+				this.bones[ gbone.parent ].add( bone );
+
+			}
+
+		}
+
+		//
+
+		var nBones = this.bones.length;
+
+		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 ( nBones > 256 )
+				size = 64;
+			else if ( nBones > 64 )
+				size = 32;
+			else if ( nBones > 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 * nBones );
+
+		}
+
+		this.pose();
+
+	}
+
+};
+
+THREE.SkinnedMesh.prototype = Object.create( THREE.Mesh.prototype );
+
+THREE.SkinnedMesh.prototype.addBone = function( bone ) {
+
+	if ( bone === undefined ) {
+
+		bone = new THREE.Bone( this );
+
+	}
+
+	this.bones.push( bone );
+
+	return bone;
+
+};
+
+THREE.SkinnedMesh.prototype.updateMatrixWorld = function () {
+
+	var offsetMatrix = new THREE.Matrix4();
+
+	return function ( force ) {
+
+		this.matrixAutoUpdate && this.updateMatrix();
+
+		// update matrixWorld
+
+		if ( this.matrixWorldNeedsUpdate || force ) {
+
+			if ( this.parent ) {
+
+				this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix );
+
+			} else {
+
+				this.matrixWorld.copy( this.matrix );
+
+			}
+
+			this.matrixWorldNeedsUpdate = false;
+
+			force = true;
+
+		}
+
+		// update children
+
+		for ( var i = 0, l = this.children.length; i < l; i ++ ) {
+
+			var child = this.children[ i ];
+
+			if ( child instanceof THREE.Bone ) {
+
+				child.update( this.identityMatrix, false );
+
+			} else {
+
+				child.updateMatrixWorld( true );
+
+			}
+
+		}
+
+		// make a snapshot of the bones' rest position
+
+		if ( this.boneInverses == undefined ) {
+
+			this.boneInverses = [];
+
+			for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) {
+
+				var inverse = new THREE.Matrix4();
+
+				inverse.getInverse( this.bones[ b ].skinMatrix );
+
+				this.boneInverses.push( inverse );
+
+			}
+
+		}
+
+		// 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;
+
+			// TODO: we could get rid of this multiplication step if the skinMatrix
+			// was already representing the offset; however, this requires some
+			// major changes to the animation system
+
+			offsetMatrix.multiplyMatrices( this.bones[ b ].skinMatrix, this.boneInverses[ b ] );
+			offsetMatrix.flattenToArrayOffset( this.boneMatrices, b * 16 );
+
+		}
+
+		if ( this.useVertexTexture ) {
+
+			this.boneTexture.needsUpdate = true;
+
+		}
+
+	};
+
+}();
+
+THREE.SkinnedMesh.prototype.pose = function () {
+
+	this.updateMatrixWorld( true );
+
+	this.normalizeSkinWeights();
+
+};
+
+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.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;
+
+};
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.MorphAnimMesh = function ( geometry, material ) {
+
+	THREE.Mesh.call( this, geometry, material );
+
+	// 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.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.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;
+
+};
+
+/**
+ * @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.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.update = function () {
+
+	var v1 = new THREE.Vector3();
+	var v2 = new THREE.Vector3();
+
+	return function ( camera ) {
+
+		if ( this.objects.length > 1 ) {
+
+			v1.getPositionFromMatrix( camera.matrixWorld );
+			v2.getPositionFromMatrix( 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 () {
+
+	// TODO
+
+};
+
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.Sprite = function ( material ) {
+
+	THREE.Object3D.call( this );
+
+	this.material = ( material !== undefined ) ? material : new THREE.SpriteMaterial();
+
+	this.rotation = 0;
+
+};
+
+THREE.Sprite.prototype = Object.create( THREE.Object3D.prototype );
+
+/*
+ * Custom update matrix
+ */
+
+THREE.Sprite.prototype.updateMatrix = function () {
+
+	this.matrix.compose( this.position, this.quaternion, this.scale );
+
+	this.matrixWorldNeedsUpdate = true;
+
+};
+
+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;
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.Scene = function () {
+
+	THREE.Object3D.call( this );
+
+	this.fog = null;
+	this.overrideMaterial = null;
+
+	this.autoUpdate = true; // checked by the renderer
+	this.matrixAutoUpdate = false;
+
+	this.__lights = [];
+
+	this.__objectsAdded = [];
+	this.__objectsRemoved = [];
+
+};
+
+THREE.Scene.prototype = Object.create( THREE.Object3D.prototype );
+
+THREE.Scene.prototype.__addObject = function ( object ) {
+
+	if ( object instanceof THREE.Light ) {
+
+		if ( this.__lights.indexOf( object ) === - 1 ) {
+
+			this.__lights.push( object );
+
+		}
+
+		if ( object.target && object.target.parent === undefined ) {
+
+			this.add( object.target );
+
+		}
+
+	} else if ( !( object instanceof THREE.Camera || object instanceof THREE.Bone ) ) {
+
+		this.__objectsAdded.push( object );
+
+		// check if previously removed
+
+		var i = this.__objectsRemoved.indexOf( object );
+
+		if ( i !== -1 ) {
+
+			this.__objectsRemoved.splice( i, 1 );
+
+		}
+
+	}
+
+	for ( var c = 0; c < object.children.length; c ++ ) {
+
+		this.__addObject( object.children[ c ] );
+
+	}
+
+};
+
+THREE.Scene.prototype.__removeObject = function ( object ) {
+
+	if ( object instanceof THREE.Light ) {
+
+		var i = this.__lights.indexOf( object );
+
+		if ( i !== -1 ) {
+
+			this.__lights.splice( i, 1 );
+
+		}
+
+		if ( object.shadowCascadeArray ) {
+
+			for ( var x = 0; x < object.shadowCascadeArray.length; x ++ ) {
+
+				this.__removeObject( object.shadowCascadeArray[ x ] );
+
+			}
+
+		}
+
+	} else if ( !( object instanceof THREE.Camera ) ) {
+
+		this.__objectsRemoved.push( object );
+
+		// check if previously added
+
+		var i = this.__objectsAdded.indexOf( object );
+
+		if ( i !== -1 ) {
+
+			this.__objectsAdded.splice( i, 1 );
+
+		}
+
+	}
+
+	for ( var c = 0; c < object.children.length; c ++ ) {
+
+		this.__removeObject( object.children[ c ] );
+
+	}
+
+};
+
+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;
+
+};
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.Fog = function ( hex, near, far ) {
+
+	this.name = '';
+
+	this.color = new THREE.Color( hex );
+
+	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 );
+
+};
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.FogExp2 = function ( hex, density ) {
+
+	this.name = '';
+
+	this.color = new THREE.Color( hex );
+	this.density = ( density !== undefined ) ? density : 0.00025;
+
+};
+
+THREE.FogExp2.prototype.clone = function () {
+
+	return new THREE.FogExp2( this.color.getHex(), this.density );
+
+};
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.CanvasRenderer = function ( parameters ) {
+
+	console.log( 'THREE.CanvasRenderer', THREE.REVISION );
+
+	var smoothstep = THREE.Math.smoothstep;
+
+	parameters = parameters || {};
+
+	var _this = this,
+	_renderData, _elements, _lights,
+	_projector = new THREE.Projector(),
+
+	_canvas = parameters.canvas !== undefined
+			? parameters.canvas
+			: document.createElement( 'canvas' ),
+
+	_canvasWidth = _canvas.width,
+	_canvasHeight = _canvas.height,
+	_canvasWidthHalf = Math.floor( _canvasWidth / 2 ),
+	_canvasHeightHalf = Math.floor( _canvasHeight / 2 ),
+
+	_context = _canvas.getContext( '2d' ),
+
+	_clearColor = new THREE.Color( 0x000000 ),
+	_clearAlpha = 0,
+
+	_contextGlobalAlpha = 1,
+	_contextGlobalCompositeOperation = 0,
+	_contextStrokeStyle = null,
+	_contextFillStyle = null,
+	_contextLineWidth = null,
+	_contextLineCap = null,
+	_contextLineJoin = null,
+	_contextDashSize = null,
+	_contextGapSize = 0,
+
+	_camera,
+
+	_v1, _v2, _v3, _v4,
+	_v5 = new THREE.RenderableVertex(),
+	_v6 = new THREE.RenderableVertex(),
+
+	_v1x, _v1y, _v2x, _v2y, _v3x, _v3y,
+	_v4x, _v4y, _v5x, _v5y, _v6x, _v6y,
+
+	_color = new THREE.Color(),
+	_color1 = new THREE.Color(),
+	_color2 = new THREE.Color(),
+	_color3 = new THREE.Color(),
+	_color4 = new THREE.Color(),
+
+	_diffuseColor = new THREE.Color(),
+	_emissiveColor = new THREE.Color(),
+
+	_lightColor = new THREE.Color(),
+
+	_patterns = {}, _imagedatas = {},
+
+	_near, _far,
+
+	_image, _uvs,
+	_uv1x, _uv1y, _uv2x, _uv2y, _uv3x, _uv3y,
+
+	_clipBox = new THREE.Box2(),
+	_clearBox = new THREE.Box2(),
+	_elemBox = new THREE.Box2(),
+
+	_ambientLight = new THREE.Color(),
+	_directionalLights = new THREE.Color(),
+	_pointLights = new THREE.Color(),
+
+	_vector3 = new THREE.Vector3(), // Needed for PointLight
+
+	_pixelMap, _pixelMapContext, _pixelMapImage, _pixelMapData,
+	_gradientMap, _gradientMapContext, _gradientMapQuality = 16;
+
+	_pixelMap = document.createElement( 'canvas' );
+	_pixelMap.width = _pixelMap.height = 2;
+
+	_pixelMapContext = _pixelMap.getContext( '2d' );
+	_pixelMapContext.fillStyle = 'rgba(0,0,0,1)';
+	_pixelMapContext.fillRect( 0, 0, 2, 2 );
+
+	_pixelMapImage = _pixelMapContext.getImageData( 0, 0, 2, 2 );
+	_pixelMapData = _pixelMapImage.data;
+
+	_gradientMap = document.createElement( 'canvas' );
+	_gradientMap.width = _gradientMap.height = _gradientMapQuality;
+
+	_gradientMapContext = _gradientMap.getContext( '2d' );
+	_gradientMapContext.translate( - _gradientMapQuality / 2, - _gradientMapQuality / 2 );
+	_gradientMapContext.scale( _gradientMapQuality, _gradientMapQuality );
+
+	_gradientMapQuality --; // Fix UVs
+
+	// dash+gap fallbacks for Firefox and everything else
+
+	if ( _context.setLineDash === undefined ) {
+
+		if ( _context.mozDash !== undefined ) {
+
+			_context.setLineDash = function ( values ) {
+
+				_context.mozDash = values[ 0 ] !== null ? values : null;
+
+			}
+
+		} else {
+
+			_context.setLineDash = function () {}
+
+		}
+
+	}
+
+	this.domElement = _canvas;
+
+	this.devicePixelRatio = parameters.devicePixelRatio !== undefined
+				? parameters.devicePixelRatio
+				: self.devicePixelRatio !== undefined
+					? self.devicePixelRatio
+					: 1;
+
+	this.autoClear = true;
+	this.sortObjects = true;
+	this.sortElements = true;
+
+	this.info = {
+
+		render: {
+
+			vertices: 0,
+			faces: 0
+
+		}
+
+	}
+
+	// WebGLRenderer compatibility
+
+	this.supportsVertexTextures = function () {};
+	this.setFaceCulling = function () {};
+
+	this.setSize = function ( width, height, updateStyle ) {
+
+		_canvasWidth = width * this.devicePixelRatio;
+		_canvasHeight = height * this.devicePixelRatio;
+
+		_canvasWidthHalf = Math.floor( _canvasWidth / 2 );
+		_canvasHeightHalf = Math.floor( _canvasHeight / 2 );
+
+		_canvas.width = _canvasWidth;
+		_canvas.height = _canvasHeight;
+
+		if ( this.devicePixelRatio !== 1 && updateStyle !== false ) {
+
+			_canvas.style.width = width + 'px';
+			_canvas.style.height = height + 'px';
+
+		}
+
+		_clipBox.set(
+			new THREE.Vector2( - _canvasWidthHalf, - _canvasHeightHalf ),
+			new THREE.Vector2( _canvasWidthHalf, _canvasHeightHalf )
+		);
+
+		_clearBox.set(
+			new THREE.Vector2( - _canvasWidthHalf, - _canvasHeightHalf ),
+			new THREE.Vector2( _canvasWidthHalf, _canvasHeightHalf )
+		);
+
+		_contextGlobalAlpha = 1;
+		_contextGlobalCompositeOperation = 0;
+		_contextStrokeStyle = null;
+		_contextFillStyle = null;
+		_contextLineWidth = null;
+		_contextLineCap = null;
+		_contextLineJoin = null;
+
+	};
+
+	this.setClearColor = function ( color, alpha ) {
+
+		_clearColor.set( color );
+		_clearAlpha = alpha !== undefined ? alpha : 1;
+
+		_clearBox.set(
+			new THREE.Vector2( - _canvasWidthHalf, - _canvasHeightHalf ),
+			new THREE.Vector2( _canvasWidthHalf, _canvasHeightHalf )
+		);
+
+	};
+
+	this.setClearColorHex = function ( hex, alpha ) {
+
+		console.warn( 'DEPRECATED: .setClearColorHex() is being removed. Use .setClearColor() instead.' );
+		this.setClearColor( hex, alpha );
+
+	};
+
+	this.getMaxAnisotropy = function () {
+
+		return 0;
+
+	};
+
+	this.clear = function () {
+
+		_context.setTransform( 1, 0, 0, - 1, _canvasWidthHalf, _canvasHeightHalf );
+
+		if ( _clearBox.empty() === false ) {
+
+			_clearBox.intersect( _clipBox );
+			_clearBox.expandByScalar( 2 );
+
+			if ( _clearAlpha < 1 ) {
+
+				_context.clearRect(
+					_clearBox.min.x | 0,
+					_clearBox.min.y | 0,
+					( _clearBox.max.x - _clearBox.min.x ) | 0,
+					( _clearBox.max.y - _clearBox.min.y ) | 0
+				);
+
+			}
+
+			if ( _clearAlpha > 0 ) {
+
+				setBlending( THREE.NormalBlending );
+				setOpacity( 1 );
+
+				setFillStyle( 'rgba(' + Math.floor( _clearColor.r * 255 ) + ',' + Math.floor( _clearColor.g * 255 ) + ',' + Math.floor( _clearColor.b * 255 ) + ',' + _clearAlpha + ')' );
+
+				_context.fillRect(
+					_clearBox.min.x | 0,
+					_clearBox.min.y | 0,
+					( _clearBox.max.x - _clearBox.min.x ) | 0,
+					( _clearBox.max.y - _clearBox.min.y ) | 0
+				);
+
+			}
+
+			_clearBox.makeEmpty();
+
+		}
+
+
+	};
+
+	this.render = function ( scene, camera ) {
+
+		if ( camera instanceof THREE.Camera === false ) {
+
+			console.error( 'THREE.CanvasRenderer.render: camera is not an instance of THREE.Camera.' );
+			return;
+
+		}
+
+		if ( this.autoClear === true ) this.clear();
+
+		_context.setTransform( 1, 0, 0, - 1, _canvasWidthHalf, _canvasHeightHalf );
+
+		_this.info.render.vertices = 0;
+		_this.info.render.faces = 0;
+
+		_renderData = _projector.projectScene( scene, camera, this.sortObjects, this.sortElements );
+		_elements = _renderData.elements;
+		_lights = _renderData.lights;
+		_camera = camera;
+
+		/* DEBUG
+		setFillStyle( 'rgba( 0, 255, 255, 0.5 )' );
+		_context.fillRect( _clipBox.min.x, _clipBox.min.y, _clipBox.max.x - _clipBox.min.x, _clipBox.max.y - _clipBox.min.y );
+		*/
+
+		calculateLights();
+
+		for ( var e = 0, el = _elements.length; e < el; e++ ) {
+
+			var element = _elements[ e ];
+
+			var material = element.material;
+
+			if ( material === undefined || material.visible === false ) continue;
+
+			_elemBox.makeEmpty();
+
+			if ( element instanceof THREE.RenderableSprite ) {
+
+				_v1 = element;
+				_v1.x *= _canvasWidthHalf; _v1.y *= _canvasHeightHalf;
+
+				renderSprite( _v1, element, material );
+
+			} else if ( element instanceof THREE.RenderableLine ) {
+
+				_v1 = element.v1; _v2 = element.v2;
+
+				_v1.positionScreen.x *= _canvasWidthHalf; _v1.positionScreen.y *= _canvasHeightHalf;
+				_v2.positionScreen.x *= _canvasWidthHalf; _v2.positionScreen.y *= _canvasHeightHalf;
+
+				_elemBox.setFromPoints( [
+					_v1.positionScreen,
+					_v2.positionScreen
+				] );
+
+				if ( _clipBox.isIntersectionBox( _elemBox ) === true ) {
+
+					renderLine( _v1, _v2, element, material );
+
+				}
+
+			} else if ( element instanceof THREE.RenderableFace3 ) {
+
+				_v1 = element.v1; _v2 = element.v2; _v3 = element.v3;
+
+				if ( _v1.positionScreen.z < -1 || _v1.positionScreen.z > 1 ) continue;
+				if ( _v2.positionScreen.z < -1 || _v2.positionScreen.z > 1 ) continue;
+				if ( _v3.positionScreen.z < -1 || _v3.positionScreen.z > 1 ) continue;
+
+				_v1.positionScreen.x *= _canvasWidthHalf; _v1.positionScreen.y *= _canvasHeightHalf;
+				_v2.positionScreen.x *= _canvasWidthHalf; _v2.positionScreen.y *= _canvasHeightHalf;
+				_v3.positionScreen.x *= _canvasWidthHalf; _v3.positionScreen.y *= _canvasHeightHalf;
+
+				if ( material.overdraw > 0 ) {
+
+					expand( _v1.positionScreen, _v2.positionScreen, material.overdraw );
+					expand( _v2.positionScreen, _v3.positionScreen, material.overdraw );
+					expand( _v3.positionScreen, _v1.positionScreen, material.overdraw );
+
+				}
+
+				_elemBox.setFromPoints( [
+					_v1.positionScreen,
+					_v2.positionScreen,
+					_v3.positionScreen
+				] );
+
+				if ( _clipBox.isIntersectionBox( _elemBox ) === true ) {
+
+					renderFace3( _v1, _v2, _v3, 0, 1, 2, element, material );
+
+				}
+
+			}
+
+			/* DEBUG
+			setLineWidth( 1 );
+			setStrokeStyle( 'rgba( 0, 255, 0, 0.5 )' );
+			_context.strokeRect( _elemBox.min.x, _elemBox.min.y, _elemBox.max.x - _elemBox.min.x, _elemBox.max.y - _elemBox.min.y );
+			*/
+
+			_clearBox.union( _elemBox );
+
+		}
+
+		/* DEBUG
+		setLineWidth( 1 );
+		setStrokeStyle( 'rgba( 255, 0, 0, 0.5 )' );
+		_context.strokeRect( _clearBox.min.x, _clearBox.min.y, _clearBox.max.x - _clearBox.min.x, _clearBox.max.y - _clearBox.min.y );
+		*/
+
+		_context.setTransform( 1, 0, 0, 1, 0, 0 );
+
+	};
+
+	//
+
+	function calculateLights() {
+
+		_ambientLight.setRGB( 0, 0, 0 );
+		_directionalLights.setRGB( 0, 0, 0 );
+		_pointLights.setRGB( 0, 0, 0 );
+
+		for ( var l = 0, ll = _lights.length; l < ll; l ++ ) {
+
+			var light = _lights[ l ];
+			var lightColor = light.color;
+
+			if ( light instanceof THREE.AmbientLight ) {
+
+				_ambientLight.add( lightColor );
+
+			} else if ( light instanceof THREE.DirectionalLight ) {
+
+				// for sprites
+
+				_directionalLights.add( lightColor );
+
+			} else if ( light instanceof THREE.PointLight ) {
+
+				// for sprites
+
+				_pointLights.add( lightColor );
+
+			}
+
+		}
+
+	}
+
+	function calculateLight( position, normal, color ) {
+
+		for ( var l = 0, ll = _lights.length; l < ll; l ++ ) {
+
+			var light = _lights[ l ];
+
+			_lightColor.copy( light.color );
+
+			if ( light instanceof THREE.DirectionalLight ) {
+
+				var lightPosition = _vector3.getPositionFromMatrix( light.matrixWorld ).normalize();
+
+				var amount = normal.dot( lightPosition );
+
+				if ( amount <= 0 ) continue;
+
+				amount *= light.intensity;
+
+				color.add( _lightColor.multiplyScalar( amount ) );
+
+			} else if ( light instanceof THREE.PointLight ) {
+
+				var lightPosition = _vector3.getPositionFromMatrix( light.matrixWorld );
+
+				var amount = normal.dot( _vector3.subVectors( lightPosition, position ).normalize() );
+
+				if ( amount <= 0 ) continue;
+
+				amount *= light.distance == 0 ? 1 : 1 - Math.min( position.distanceTo( lightPosition ) / light.distance, 1 );
+
+				if ( amount == 0 ) continue;
+
+				amount *= light.intensity;
+
+				color.add( _lightColor.multiplyScalar( amount ) );
+
+			}
+
+		}
+
+	}
+
+	function renderSprite( v1, element, material ) {
+
+		setOpacity( material.opacity );
+		setBlending( material.blending );
+
+		var width, height, scaleX, scaleY,
+		bitmap, bitmapWidth, bitmapHeight;
+
+		if ( material instanceof THREE.SpriteMaterial ||
+			 material instanceof THREE.ParticleSystemMaterial ) { // Backwards compatibility
+
+			if ( material.map.image !== undefined ) {
+
+				bitmap = material.map.image;
+				bitmapWidth = bitmap.width >> 1;
+				bitmapHeight = bitmap.height >> 1;
+
+				scaleX = element.scale.x * _canvasWidthHalf;
+				scaleY = element.scale.y * _canvasHeightHalf;
+
+				width = scaleX * bitmapWidth;
+				height = scaleY * bitmapHeight;
+
+				// TODO: Rotations break this...
+
+				_elemBox.min.set( v1.x - width, v1.y - height );
+				_elemBox.max.set( v1.x + width, v1.y + height );
+
+				if ( _clipBox.isIntersectionBox( _elemBox ) === false ) {
+
+					_elemBox.makeEmpty();
+					return;
+
+				}
+
+				_context.save();
+				_context.translate( v1.x, v1.y );
+				_context.rotate( - element.rotation );
+				_context.scale( scaleX, - scaleY );
+
+				_context.translate( - bitmapWidth, - bitmapHeight );
+				_context.drawImage( bitmap, 0, 0 );
+				_context.restore();
+
+			} else {
+
+				scaleX = element.object.scale.x;
+				scaleY = element.object.scale.y;
+
+				// TODO: Be able to disable this
+
+				scaleX *= element.scale.x * _canvasWidthHalf;
+				scaleY *= element.scale.y * _canvasHeightHalf;
+
+				_elemBox.min.set( v1.x - scaleX, v1.y - scaleY );
+				_elemBox.max.set( v1.x + scaleX, v1.y + scaleY );
+
+				if ( _clipBox.isIntersectionBox( _elemBox ) === false ) {
+
+					_elemBox.makeEmpty();
+					return;
+
+				}
+
+				setFillStyle( material.color.getStyle() );
+
+				_context.save();
+				_context.translate( v1.x, v1.y );
+				_context.rotate( - element.rotation );
+				_context.scale( scaleX, scaleY );
+				_context.fillRect( -1, -1, 2, 2 );
+				_context.restore();
+
+			}
+
+			/* DEBUG
+			setStrokeStyle( 'rgb(255,255,0)' );
+			_context.beginPath();
+			_context.moveTo( v1.x - 10, v1.y );
+			_context.lineTo( v1.x + 10, v1.y );
+			_context.moveTo( v1.x, v1.y - 10 );
+			_context.lineTo( v1.x, v1.y + 10 );
+			_context.stroke();
+			*/
+
+		} else if ( material instanceof THREE.SpriteCanvasMaterial ) {
+
+			width = element.scale.x * _canvasWidthHalf;
+			height = element.scale.y * _canvasHeightHalf;
+
+			_elemBox.min.set( v1.x - width, v1.y - height );
+			_elemBox.max.set( v1.x + width, v1.y + height );
+
+			if ( _clipBox.isIntersectionBox( _elemBox ) === false ) {
+
+				_elemBox.makeEmpty();
+				return;
+
+			}
+
+			setStrokeStyle( material.color.getStyle() );
+			setFillStyle( material.color.getStyle() );
+
+			_context.save();
+			_context.translate( v1.x, v1.y );
+			_context.rotate( - element.rotation );
+			_context.scale( width, height );
+
+			material.program( _context );
+
+			_context.restore();
+
+		}
+
+	}
+
+	function renderLine( v1, v2, element, material ) {
+
+		setOpacity( material.opacity );
+		setBlending( material.blending );
+
+		_context.beginPath();
+		_context.moveTo( v1.positionScreen.x, v1.positionScreen.y );
+		_context.lineTo( v2.positionScreen.x, v2.positionScreen.y );
+
+		if ( material instanceof THREE.LineBasicMaterial ) {
+
+			setLineWidth( material.linewidth );
+			setLineCap( material.linecap );
+			setLineJoin( material.linejoin );
+
+			if ( material.vertexColors !== THREE.VertexColors ) {
+
+				setStrokeStyle( material.color.getStyle() );
+
+			} else {
+
+				var colorStyle1 = element.vertexColors[0].getStyle();
+				var colorStyle2 = element.vertexColors[1].getStyle();
+
+				if ( colorStyle1 === colorStyle2 ) {
+
+					setStrokeStyle( colorStyle1 );
+
+				} else {
+
+					try {
+
+						var grad = _context.createLinearGradient(
+							v1.positionScreen.x,
+							v1.positionScreen.y,
+							v2.positionScreen.x,
+							v2.positionScreen.y
+						);
+						grad.addColorStop( 0, colorStyle1 );
+						grad.addColorStop( 1, colorStyle2 );
+
+					} catch ( exception ) {
+
+						grad = colorStyle1;
+
+					}
+
+					setStrokeStyle( grad );
+
+				}
+
+			}
+
+			_context.stroke();
+			_elemBox.expandByScalar( material.linewidth * 2 );
+
+		} else if ( material instanceof THREE.LineDashedMaterial ) {
+
+			setLineWidth( material.linewidth );
+			setLineCap( material.linecap );
+			setLineJoin( material.linejoin );
+			setStrokeStyle( material.color.getStyle() );
+			setDashAndGap( material.dashSize, material.gapSize );
+
+			_context.stroke();
+
+			_elemBox.expandByScalar( material.linewidth * 2 );
+
+			setDashAndGap( null, null );
+
+		}
+
+	}
+
+	function renderFace3( v1, v2, v3, uv1, uv2, uv3, element, material ) {
+
+		_this.info.render.vertices += 3;
+		_this.info.render.faces ++;
+
+		setOpacity( material.opacity );
+		setBlending( material.blending );
+
+		_v1x = v1.positionScreen.x; _v1y = v1.positionScreen.y;
+		_v2x = v2.positionScreen.x; _v2y = v2.positionScreen.y;
+		_v3x = v3.positionScreen.x; _v3y = v3.positionScreen.y;
+
+		drawTriangle( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y );
+
+		if ( ( material instanceof THREE.MeshLambertMaterial || material instanceof THREE.MeshPhongMaterial ) && material.map === null ) {
+
+			_diffuseColor.copy( material.color );
+			_emissiveColor.copy( material.emissive );
+
+			if ( material.vertexColors === THREE.FaceColors ) {
+
+				_diffuseColor.multiply( element.color );
+
+			}
+
+			if ( material.wireframe === false && material.shading == THREE.SmoothShading && element.vertexNormalsLength == 3 ) {
+
+				_color1.copy( _ambientLight );
+				_color2.copy( _ambientLight );
+				_color3.copy( _ambientLight );
+
+				calculateLight( element.v1.positionWorld, element.vertexNormalsModel[ 0 ], _color1 );
+				calculateLight( element.v2.positionWorld, element.vertexNormalsModel[ 1 ], _color2 );
+				calculateLight( element.v3.positionWorld, element.vertexNormalsModel[ 2 ], _color3 );
+
+				_color1.multiply( _diffuseColor ).add( _emissiveColor );
+				_color2.multiply( _diffuseColor ).add( _emissiveColor );
+				_color3.multiply( _diffuseColor ).add( _emissiveColor );
+				_color4.addColors( _color2, _color3 ).multiplyScalar( 0.5 );
+
+				_image = getGradientTexture( _color1, _color2, _color3, _color4 );
+
+				clipImage( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, 0, 0, 1, 0, 0, 1, _image );
+
+			} else {
+
+				_color.copy( _ambientLight );
+
+				calculateLight( element.centroidModel, element.normalModel, _color );
+
+				_color.multiply( _diffuseColor ).add( _emissiveColor );
+
+				material.wireframe === true
+					? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin )
+					: fillPath( _color );
+
+			}
+
+		} else if ( material instanceof THREE.MeshBasicMaterial || material instanceof THREE.MeshLambertMaterial || material instanceof THREE.MeshPhongMaterial ) {
+
+			if ( material.map !== null ) {
+
+				if ( material.map.mapping instanceof THREE.UVMapping ) {
+
+					_uvs = element.uvs[ 0 ];
+					patternPath( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _uvs[ uv1 ].x, _uvs[ uv1 ].y, _uvs[ uv2 ].x, _uvs[ uv2 ].y, _uvs[ uv3 ].x, _uvs[ uv3 ].y, material.map );
+
+				}
+
+
+			} else if ( material.envMap !== null ) {
+
+				if ( material.envMap.mapping instanceof THREE.SphericalReflectionMapping ) {
+
+					_vector3.copy( element.vertexNormalsModelView[ uv1 ] );
+					_uv1x = 0.5 * _vector3.x + 0.5;
+					_uv1y = 0.5 * _vector3.y + 0.5;
+
+					_vector3.copy( element.vertexNormalsModelView[ uv2 ] );
+					_uv2x = 0.5 * _vector3.x + 0.5;
+					_uv2y = 0.5 * _vector3.y + 0.5;
+
+					_vector3.copy( element.vertexNormalsModelView[ uv3 ] );
+					_uv3x = 0.5 * _vector3.x + 0.5;
+					_uv3y = 0.5 * _vector3.y + 0.5;
+
+					patternPath( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _uv1x, _uv1y, _uv2x, _uv2y, _uv3x, _uv3y, material.envMap );
+
+				}/* else if ( material.envMap.mapping == THREE.SphericalRefractionMapping ) {
+
+
+
+				}*/
+
+
+			} else {
+
+				_color.copy( material.color );
+
+				if ( material.vertexColors === THREE.FaceColors ) {
+
+					_color.multiply( element.color );
+
+				}
+
+				material.wireframe === true
+					? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin )
+					: fillPath( _color );
+
+			}
+
+		} else if ( material instanceof THREE.MeshDepthMaterial ) {
+
+			_near = _camera.near;
+			_far = _camera.far;
+
+			_color1.r = _color1.g = _color1.b = 1 - smoothstep( v1.positionScreen.z * v1.positionScreen.w, _near, _far );
+			_color2.r = _color2.g = _color2.b = 1 - smoothstep( v2.positionScreen.z * v2.positionScreen.w, _near, _far );
+			_color3.r = _color3.g = _color3.b = 1 - smoothstep( v3.positionScreen.z * v3.positionScreen.w, _near, _far );
+			_color4.addColors( _color2, _color3 ).multiplyScalar( 0.5 );
+
+			_image = getGradientTexture( _color1, _color2, _color3, _color4 );
+
+			clipImage( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, 0, 0, 1, 0, 0, 1, _image );
+
+		} else if ( material instanceof THREE.MeshNormalMaterial ) {
+
+			var normal;
+
+			if ( material.shading == THREE.FlatShading ) {
+
+				normal = element.normalModelView;
+
+				_color.setRGB( normal.x, normal.y, normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 );
+
+				material.wireframe === true
+					? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin )
+					: fillPath( _color );
+
+			} else if ( material.shading == THREE.SmoothShading ) {
+
+				normal = element.vertexNormalsModelView[ uv1 ];
+				_color1.setRGB( normal.x, normal.y, normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 );
+
+				normal = element.vertexNormalsModelView[ uv2 ];
+				_color2.setRGB( normal.x, normal.y, normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 );
+
+				normal = element.vertexNormalsModelView[ uv3 ];
+				_color3.setRGB( normal.x, normal.y, normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 );
+
+				_color4.addColors( _color2, _color3 ).multiplyScalar( 0.5 );
+
+				_image = getGradientTexture( _color1, _color2, _color3, _color4 );
+
+				clipImage( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, 0, 0, 1, 0, 0, 1, _image );
+
+			}
+
+		}
+
+	}
+
+	//
+
+	function drawTriangle( x0, y0, x1, y1, x2, y2 ) {
+
+		_context.beginPath();
+		_context.moveTo( x0, y0 );
+		_context.lineTo( x1, y1 );
+		_context.lineTo( x2, y2 );
+		_context.closePath();
+
+	}
+
+	function strokePath( color, linewidth, linecap, linejoin ) {
+
+		setLineWidth( linewidth );
+		setLineCap( linecap );
+		setLineJoin( linejoin );
+		setStrokeStyle( color.getStyle() );
+
+		_context.stroke();
+
+		_elemBox.expandByScalar( linewidth * 2 );
+
+	}
+
+	function fillPath( color ) {
+
+		setFillStyle( color.getStyle() );
+		_context.fill();
+
+	}
+
+	function patternPath( x0, y0, x1, y1, x2, y2, u0, v0, u1, v1, u2, v2, texture ) {
+
+		if ( texture instanceof THREE.DataTexture || texture.image === undefined || texture.image.width == 0 ) return;
+
+		if ( texture.needsUpdate === true ) {
+
+			var repeatX = texture.wrapS == THREE.RepeatWrapping;
+			var repeatY = texture.wrapT == THREE.RepeatWrapping;
+
+			_patterns[ texture.id ] = _context.createPattern(
+				texture.image, repeatX === true && repeatY === true
+					? 'repeat'
+					: repeatX === true && repeatY === false
+						? 'repeat-x'
+						: repeatX === false && repeatY === true
+							? 'repeat-y'
+							: 'no-repeat'
+			);
+
+			texture.needsUpdate = false;
+
+		}
+
+		_patterns[ texture.id ] === undefined
+			? setFillStyle( 'rgba(0,0,0,1)' )
+			: setFillStyle( _patterns[ texture.id ] );
+
+		// http://extremelysatisfactorytotalitarianism.com/blog/?p=2120
+
+		var a, b, c, d, e, f, det, idet,
+		offsetX = texture.offset.x / texture.repeat.x,
+		offsetY = texture.offset.y / texture.repeat.y,
+		width = texture.image.width * texture.repeat.x,
+		height = texture.image.height * texture.repeat.y;
+
+		u0 = ( u0 + offsetX ) * width;
+		v0 = ( 1.0 - v0 + offsetY ) * height;
+
+		u1 = ( u1 + offsetX ) * width;
+		v1 = ( 1.0 - v1 + offsetY ) * height;
+
+		u2 = ( u2 + offsetX ) * width;
+		v2 = ( 1.0 - v2 + offsetY ) * height;
+
+		x1 -= x0; y1 -= y0;
+		x2 -= x0; y2 -= y0;
+
+		u1 -= u0; v1 -= v0;
+		u2 -= u0; v2 -= v0;
+
+		det = u1 * v2 - u2 * v1;
+
+		if ( det === 0 ) {
+
+			if ( _imagedatas[ texture.id ] === undefined ) {
+
+				var canvas = document.createElement( 'canvas' )
+				canvas.width = texture.image.width;
+				canvas.height = texture.image.height;
+
+				var context = canvas.getContext( '2d' );
+				context.drawImage( texture.image, 0, 0 );
+
+				_imagedatas[ texture.id ] = context.getImageData( 0, 0, texture.image.width, texture.image.height ).data;
+
+			}
+
+			var data = _imagedatas[ texture.id ];
+			var index = ( Math.floor( u0 ) + Math.floor( v0 ) * texture.image.width ) * 4;
+
+			_color.setRGB( data[ index ] / 255, data[ index + 1 ] / 255, data[ index + 2 ] / 255 );
+			fillPath( _color );
+
+			return;
+
+		}
+
+		idet = 1 / det;
+
+		a = ( v2 * x1 - v1 * x2 ) * idet;
+		b = ( v2 * y1 - v1 * y2 ) * idet;
+		c = ( u1 * x2 - u2 * x1 ) * idet;
+		d = ( u1 * y2 - u2 * y1 ) * idet;
+
+		e = x0 - a * u0 - c * v0;
+		f = y0 - b * u0 - d * v0;
+
+		_context.save();
+		_context.transform( a, b, c, d, e, f );
+		_context.fill();
+		_context.restore();
+
+	}
+
+	function clipImage( x0, y0, x1, y1, x2, y2, u0, v0, u1, v1, u2, v2, image ) {
+
+		// http://extremelysatisfactorytotalitarianism.com/blog/?p=2120
+
+		var a, b, c, d, e, f, det, idet,
+		width = image.width - 1,
+		height = image.height - 1;
+
+		u0 *= width; v0 *= height;
+		u1 *= width; v1 *= height;
+		u2 *= width; v2 *= height;
+
+		x1 -= x0; y1 -= y0;
+		x2 -= x0; y2 -= y0;
+
+		u1 -= u0; v1 -= v0;
+		u2 -= u0; v2 -= v0;
+
+		det = u1 * v2 - u2 * v1;
+
+		idet = 1 / det;
+
+		a = ( v2 * x1 - v1 * x2 ) * idet;
+		b = ( v2 * y1 - v1 * y2 ) * idet;
+		c = ( u1 * x2 - u2 * x1 ) * idet;
+		d = ( u1 * y2 - u2 * y1 ) * idet;
+
+		e = x0 - a * u0 - c * v0;
+		f = y0 - b * u0 - d * v0;
+
+		_context.save();
+		_context.transform( a, b, c, d, e, f );
+		_context.clip();
+		_context.drawImage( image, 0, 0 );
+		_context.restore();
+
+	}
+
+	function getGradientTexture( color1, color2, color3, color4 ) {
+
+		// http://mrdoob.com/blog/post/710
+
+		_pixelMapData[ 0 ] = ( color1.r * 255 ) | 0;
+		_pixelMapData[ 1 ] = ( color1.g * 255 ) | 0;
+		_pixelMapData[ 2 ] = ( color1.b * 255 ) | 0;
+
+		_pixelMapData[ 4 ] = ( color2.r * 255 ) | 0;
+		_pixelMapData[ 5 ] = ( color2.g * 255 ) | 0;
+		_pixelMapData[ 6 ] = ( color2.b * 255 ) | 0;
+
+		_pixelMapData[ 8 ] = ( color3.r * 255 ) | 0;
+		_pixelMapData[ 9 ] = ( color3.g * 255 ) | 0;
+		_pixelMapData[ 10 ] = ( color3.b * 255 ) | 0;
+
+		_pixelMapData[ 12 ] = ( color4.r * 255 ) | 0;
+		_pixelMapData[ 13 ] = ( color4.g * 255 ) | 0;
+		_pixelMapData[ 14 ] = ( color4.b * 255 ) | 0;
+
+		_pixelMapContext.putImageData( _pixelMapImage, 0, 0 );
+		_gradientMapContext.drawImage( _pixelMap, 0, 0 );
+
+		return _gradientMap;
+
+	}
+
+	// Hide anti-alias gaps
+
+	function expand( v1, v2, pixels ) {
+
+		var x = v2.x - v1.x, y = v2.y - v1.y,
+		det = x * x + y * y, idet;
+
+		if ( det === 0 ) return;
+
+		idet = pixels / Math.sqrt( det );
+
+		x *= idet; y *= idet;
+
+		v2.x += x; v2.y += y;
+		v1.x -= x; v1.y -= y;
+
+	}
+
+	// Context cached methods.
+
+	function setOpacity( value ) {
+
+		if ( _contextGlobalAlpha !== value ) {
+
+			_context.globalAlpha = value;
+			_contextGlobalAlpha = value;
+
+		}
+
+	}
+
+	function setBlending( value ) {
+
+		if ( _contextGlobalCompositeOperation !== value ) {
+
+			if ( value === THREE.NormalBlending ) {
+
+				_context.globalCompositeOperation = 'source-over';
+
+			} else if ( value === THREE.AdditiveBlending ) {
+
+				_context.globalCompositeOperation = 'lighter';
+
+			} else if ( value === THREE.SubtractiveBlending ) {
+
+				_context.globalCompositeOperation = 'darker';
+
+			}
+
+			_contextGlobalCompositeOperation = value;
+
+		}
+
+	}
+
+	function setLineWidth( value ) {
+
+		if ( _contextLineWidth !== value ) {
+
+			_context.lineWidth = value;
+			_contextLineWidth = value;
+
+		}
+
+	}
+
+	function setLineCap( value ) {
+
+		// "butt", "round", "square"
+
+		if ( _contextLineCap !== value ) {
+
+			_context.lineCap = value;
+			_contextLineCap = value;
+
+		}
+
+	}
+
+	function setLineJoin( value ) {
+
+		// "round", "bevel", "miter"
+
+		if ( _contextLineJoin !== value ) {
+
+			_context.lineJoin = value;
+			_contextLineJoin = value;
+
+		}
+
+	}
+
+	function setStrokeStyle( value ) {
+
+		if ( _contextStrokeStyle !== value ) {
+
+			_context.strokeStyle = value;
+			_contextStrokeStyle = value;
+
+		}
+
+	}
+
+	function setFillStyle( value ) {
+
+		if ( _contextFillStyle !== value ) {
+
+			_context.fillStyle = value;
+			_contextFillStyle = value;
+
+		}
+
+	}
+
+	function setDashAndGap( dashSizeValue, gapSizeValue ) {
+
+		if ( _contextDashSize !== dashSizeValue || _contextGapSize !== gapSizeValue ) {
+
+			_context.setLineDash( [ dashSizeValue, gapSizeValue ] );
+			_contextDashSize = dashSizeValue;
+			_contextGapSize = gapSizeValue;
+
+		}
+
+	}
+
+};
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ * @author mrdoob / http://mrdoob.com/
+ * @author mikael emtinger / http://gomo.se/
+ */
+
+THREE.ShaderChunk = {
+
+	// FOG
+
+	fog_pars_fragment: [
+
+		"#ifdef USE_FOG",
+
+			"uniform vec3 fogColor;",
+
+			"#ifdef FOG_EXP2",
+
+				"uniform float fogDensity;",
+
+			"#else",
+
+				"uniform float fogNear;",
+				"uniform float fogFar;",
+
+			"#endif",
+
+		"#endif"
+
+	].join("\n"),
+
+	fog_fragment: [
+
+		"#ifdef USE_FOG",
+
+			"float depth = gl_FragCoord.z / gl_FragCoord.w;",
+
+			"#ifdef FOG_EXP2",
+
+				"const float LOG2 = 1.442695;",
+				"float fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );",
+				"fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );",
+
+			"#else",
+
+				"float fogFactor = smoothstep( fogNear, fogFar, depth );",
+
+			"#endif",
+
+			"gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );",
+
+		"#endif"
+
+	].join("\n"),
+
+	// ENVIRONMENT MAP
+
+	envmap_pars_fragment: [
+
+		"#ifdef USE_ENVMAP",
+
+			"uniform float reflectivity;",
+			"uniform samplerCube envMap;",
+			"uniform float flipEnvMap;",
+			"uniform int combine;",
+
+			"#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )",
+
+				"uniform bool useRefract;",
+				"uniform float refractionRatio;",
+
+			"#else",
+
+				"varying vec3 vReflect;",
+
+			"#endif",
+
+		"#endif"
+
+	].join("\n"),
+
+	envmap_fragment: [
+
+		"#ifdef USE_ENVMAP",
+
+			"vec3 reflectVec;",
+
+			"#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )",
+
+				"vec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );",
+
+				"if ( useRefract ) {",
+
+					"reflectVec = refract( cameraToVertex, normal, refractionRatio );",
+
+				"} else { ",
+
+					"reflectVec = reflect( cameraToVertex, normal );",
+
+				"}",
+
+			"#else",
+
+				"reflectVec = vReflect;",
+
+			"#endif",
+
+			"#ifdef DOUBLE_SIDED",
+
+				"float flipNormal = ( -1.0 + 2.0 * float( gl_FrontFacing ) );",
+				"vec4 cubeColor = textureCube( envMap, flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );",
+
+			"#else",
+
+				"vec4 cubeColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );",
+
+			"#endif",
+
+			"#ifdef GAMMA_INPUT",
+
+				"cubeColor.xyz *= cubeColor.xyz;",
+
+			"#endif",
+
+			"if ( combine == 1 ) {",
+
+				"gl_FragColor.xyz = mix( gl_FragColor.xyz, cubeColor.xyz, specularStrength * reflectivity );",
+
+			"} else if ( combine == 2 ) {",
+
+				"gl_FragColor.xyz += cubeColor.xyz * specularStrength * reflectivity;",
+
+			"} else {",
+
+				"gl_FragColor.xyz = mix( gl_FragColor.xyz, gl_FragColor.xyz * cubeColor.xyz, specularStrength * reflectivity );",
+
+			"}",
+
+		"#endif"
+
+	].join("\n"),
+
+	envmap_pars_vertex: [
+
+		"#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP )",
+
+			"varying vec3 vReflect;",
+
+			"uniform float refractionRatio;",
+			"uniform bool useRefract;",
+
+		"#endif"
+
+	].join("\n"),
+
+	worldpos_vertex : [
+
+		"#if defined( USE_ENVMAP ) || defined( PHONG ) || defined( LAMBERT ) || defined ( USE_SHADOWMAP )",
+
+			"#ifdef USE_SKINNING",
+
+				"vec4 worldPosition = modelMatrix * skinned;",
+
+			"#endif",
+
+			"#if defined( USE_MORPHTARGETS ) && ! defined( USE_SKINNING )",
+
+				"vec4 worldPosition = modelMatrix * vec4( morphed, 1.0 );",
+
+			"#endif",
+
+			"#if ! defined( USE_MORPHTARGETS ) && ! defined( USE_SKINNING )",
+
+				"vec4 worldPosition = modelMatrix * vec4( position, 1.0 );",
+
+			"#endif",
+
+		"#endif"
+
+	].join("\n"),
+
+	envmap_vertex : [
+
+		"#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP )",
+
+			"vec3 worldNormal = mat3( modelMatrix[ 0 ].xyz, modelMatrix[ 1 ].xyz, modelMatrix[ 2 ].xyz ) * objectNormal;",
+			"worldNormal = normalize( worldNormal );",
+
+			"vec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );",
+
+			"if ( useRefract ) {",
+
+				"vReflect = refract( cameraToVertex, worldNormal, refractionRatio );",
+
+			"} else {",
+
+				"vReflect = reflect( cameraToVertex, worldNormal );",
+
+			"}",
+
+		"#endif"
+
+	].join("\n"),
+
+	// COLOR MAP (particles)
+
+	map_particle_pars_fragment: [
+
+		"#ifdef USE_MAP",
+
+			"uniform sampler2D map;",
+
+		"#endif"
+
+	].join("\n"),
+
+
+	map_particle_fragment: [
+
+		"#ifdef USE_MAP",
+
+			"gl_FragColor = gl_FragColor * texture2D( map, vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y ) );",
+
+		"#endif"
+
+	].join("\n"),
+
+	// COLOR MAP (triangles)
+
+	map_pars_vertex: [
+
+		"#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP )",
+
+			"varying vec2 vUv;",
+			"uniform vec4 offsetRepeat;",
+
+		"#endif"
+
+	].join("\n"),
+
+	map_pars_fragment: [
+
+		"#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP )",
+
+			"varying vec2 vUv;",
+
+		"#endif",
+
+		"#ifdef USE_MAP",
+
+			"uniform sampler2D map;",
+
+		"#endif"
+
+	].join("\n"),
+
+	map_vertex: [
+
+		"#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP )",
+
+			"vUv = uv * offsetRepeat.zw + offsetRepeat.xy;",
+
+		"#endif"
+
+	].join("\n"),
+
+	map_fragment: [
+
+		"#ifdef USE_MAP",
+
+			"vec4 texelColor = texture2D( map, vUv );",
+
+			"#ifdef GAMMA_INPUT",
+
+				"texelColor.xyz *= texelColor.xyz;",
+
+			"#endif",
+
+			"gl_FragColor = gl_FragColor * texelColor;",
+
+		"#endif"
+
+	].join("\n"),
+
+	// LIGHT MAP
+
+	lightmap_pars_fragment: [
+
+		"#ifdef USE_LIGHTMAP",
+
+			"varying vec2 vUv2;",
+			"uniform sampler2D lightMap;",
+
+		"#endif"
+
+	].join("\n"),
+
+	lightmap_pars_vertex: [
+
+		"#ifdef USE_LIGHTMAP",
+
+			"varying vec2 vUv2;",
+
+		"#endif"
+
+	].join("\n"),
+
+	lightmap_fragment: [
+
+		"#ifdef USE_LIGHTMAP",
+
+			"gl_FragColor = gl_FragColor * texture2D( lightMap, vUv2 );",
+
+		"#endif"
+
+	].join("\n"),
+
+	lightmap_vertex: [
+
+		"#ifdef USE_LIGHTMAP",
+
+			"vUv2 = uv2;",
+
+		"#endif"
+
+	].join("\n"),
+
+	// BUMP MAP
+
+	bumpmap_pars_fragment: [
+
+		"#ifdef USE_BUMPMAP",
+
+			"uniform sampler2D bumpMap;",
+			"uniform float bumpScale;",
+
+			// Derivative maps - bump mapping unparametrized surfaces by Morten Mikkelsen
+			//	http://mmikkelsen3d.blogspot.sk/2011/07/derivative-maps.html
+
+			// Evaluate the derivative of the height w.r.t. screen-space using forward differencing (listing 2)
+
+			"vec2 dHdxy_fwd() {",
+
+				"vec2 dSTdx = dFdx( vUv );",
+				"vec2 dSTdy = dFdy( vUv );",
+
+				"float Hll = bumpScale * texture2D( bumpMap, vUv ).x;",
+				"float dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;",
+				"float dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;",
+
+				"return vec2( dBx, dBy );",
+
+			"}",
+
+			"vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {",
+
+				"vec3 vSigmaX = dFdx( surf_pos );",
+				"vec3 vSigmaY = dFdy( surf_pos );",
+				"vec3 vN = surf_norm;",		// normalized
+
+				"vec3 R1 = cross( vSigmaY, vN );",
+				"vec3 R2 = cross( vN, vSigmaX );",
+
+				"float fDet = dot( vSigmaX, R1 );",
+
+				"vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );",
+				"return normalize( abs( fDet ) * surf_norm - vGrad );",
+
+			"}",
+
+		"#endif"
+
+	].join("\n"),
+
+	// NORMAL MAP
+
+	normalmap_pars_fragment: [
+
+		"#ifdef USE_NORMALMAP",
+
+			"uniform sampler2D normalMap;",
+			"uniform vec2 normalScale;",
+
+			// Per-Pixel Tangent Space Normal Mapping
+			// http://hacksoflife.blogspot.ch/2009/11/per-pixel-tangent-space-normal-mapping.html
+
+			"vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {",
+
+				"vec3 q0 = dFdx( eye_pos.xyz );",
+				"vec3 q1 = dFdy( eye_pos.xyz );",
+				"vec2 st0 = dFdx( vUv.st );",
+				"vec2 st1 = dFdy( vUv.st );",
+
+				"vec3 S = normalize(  q0 * st1.t - q1 * st0.t );",
+				"vec3 T = normalize( -q0 * st1.s + q1 * st0.s );",
+				"vec3 N = normalize( surf_norm );",
+
+				"vec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;",
+				"mapN.xy = normalScale * mapN.xy;",
+				"mat3 tsn = mat3( S, T, N );",
+				"return normalize( tsn * mapN );",
+
+			"}",
+
+		"#endif"
+
+	].join("\n"),
+
+	// SPECULAR MAP
+
+	specularmap_pars_fragment: [
+
+		"#ifdef USE_SPECULARMAP",
+
+			"uniform sampler2D specularMap;",
+
+		"#endif"
+
+	].join("\n"),
+
+	specularmap_fragment: [
+
+		"float specularStrength;",
+
+		"#ifdef USE_SPECULARMAP",
+
+			"vec4 texelSpecular = texture2D( specularMap, vUv );",
+			"specularStrength = texelSpecular.r;",
+
+		"#else",
+
+			"specularStrength = 1.0;",
+
+		"#endif"
+
+	].join("\n"),
+
+	// LIGHTS LAMBERT
+
+	lights_lambert_pars_vertex: [
+
+		"uniform vec3 ambient;",
+		"uniform vec3 diffuse;",
+		"uniform vec3 emissive;",
+
+		"uniform vec3 ambientLightColor;",
+
+		"#if MAX_DIR_LIGHTS > 0",
+
+			"uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];",
+			"uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];",
+
+		"#endif",
+
+		"#if MAX_HEMI_LIGHTS > 0",
+
+			"uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];",
+			"uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];",
+			"uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];",
+
+		"#endif",
+
+		"#if MAX_POINT_LIGHTS > 0",
+
+			"uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];",
+			"uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];",
+			"uniform float pointLightDistance[ MAX_POINT_LIGHTS ];",
+
+		"#endif",
+
+		"#if MAX_SPOT_LIGHTS > 0",
+
+			"uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];",
+			"uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];",
+			"uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];",
+			"uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];",
+			"uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];",
+			"uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];",
+
+		"#endif",
+
+		"#ifdef WRAP_AROUND",
+
+			"uniform vec3 wrapRGB;",
+
+		"#endif"
+
+	].join("\n"),
+
+	lights_lambert_vertex: [
+
+		"vLightFront = vec3( 0.0 );",
+
+		"#ifdef DOUBLE_SIDED",
+
+			"vLightBack = vec3( 0.0 );",
+
+		"#endif",
+
+		"transformedNormal = normalize( transformedNormal );",
+
+		"#if MAX_DIR_LIGHTS > 0",
+
+		"for( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {",
+
+			"vec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );",
+			"vec3 dirVector = normalize( lDirection.xyz );",
+
+			"float dotProduct = dot( transformedNormal, dirVector );",
+			"vec3 directionalLightWeighting = vec3( max( dotProduct, 0.0 ) );",
+
+			"#ifdef DOUBLE_SIDED",
+
+				"vec3 directionalLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );",
+
+				"#ifdef WRAP_AROUND",
+
+					"vec3 directionalLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );",
+
+				"#endif",
+
+			"#endif",
+
+			"#ifdef WRAP_AROUND",
+
+				"vec3 directionalLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );",
+				"directionalLightWeighting = mix( directionalLightWeighting, directionalLightWeightingHalf, wrapRGB );",
+
+				"#ifdef DOUBLE_SIDED",
+
+					"directionalLightWeightingBack = mix( directionalLightWeightingBack, directionalLightWeightingHalfBack, wrapRGB );",
+
+				"#endif",
+
+			"#endif",
+
+			"vLightFront += directionalLightColor[ i ] * directionalLightWeighting;",
+
+			"#ifdef DOUBLE_SIDED",
+
+				"vLightBack += directionalLightColor[ i ] * directionalLightWeightingBack;",
+
+			"#endif",
+
+		"}",
+
+		"#endif",
+
+		"#if MAX_POINT_LIGHTS > 0",
+
+			"for( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {",
+
+				"vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );",
+				"vec3 lVector = lPosition.xyz - mvPosition.xyz;",
+
+				"float lDistance = 1.0;",
+				"if ( pointLightDistance[ i ] > 0.0 )",
+					"lDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );",
+
+				"lVector = normalize( lVector );",
+				"float dotProduct = dot( transformedNormal, lVector );",
+
+				"vec3 pointLightWeighting = vec3( max( dotProduct, 0.0 ) );",
+
+				"#ifdef DOUBLE_SIDED",
+
+					"vec3 pointLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );",
+
+					"#ifdef WRAP_AROUND",
+
+						"vec3 pointLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );",
+
+					"#endif",
+
+				"#endif",
+
+				"#ifdef WRAP_AROUND",
+
+					"vec3 pointLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );",
+					"pointLightWeighting = mix( pointLightWeighting, pointLightWeightingHalf, wrapRGB );",
+
+					"#ifdef DOUBLE_SIDED",
+
+						"pointLightWeightingBack = mix( pointLightWeightingBack, pointLightWeightingHalfBack, wrapRGB );",
+
+					"#endif",
+
+				"#endif",
+
+				"vLightFront += pointLightColor[ i ] * pointLightWeighting * lDistance;",
+
+				"#ifdef DOUBLE_SIDED",
+
+					"vLightBack += pointLightColor[ i ] * pointLightWeightingBack * lDistance;",
+
+				"#endif",
+
+			"}",
+
+		"#endif",
+
+		"#if MAX_SPOT_LIGHTS > 0",
+
+			"for( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {",
+
+				"vec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );",
+				"vec3 lVector = lPosition.xyz - mvPosition.xyz;",
+
+				"float spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - worldPosition.xyz ) );",
+
+				"if ( spotEffect > spotLightAngleCos[ i ] ) {",
+
+					"spotEffect = max( pow( spotEffect, spotLightExponent[ i ] ), 0.0 );",
+
+					"float lDistance = 1.0;",
+					"if ( spotLightDistance[ i ] > 0.0 )",
+						"lDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );",
+
+					"lVector = normalize( lVector );",
+
+					"float dotProduct = dot( transformedNormal, lVector );",
+					"vec3 spotLightWeighting = vec3( max( dotProduct, 0.0 ) );",
+
+					"#ifdef DOUBLE_SIDED",
+
+						"vec3 spotLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );",
+
+						"#ifdef WRAP_AROUND",
+
+							"vec3 spotLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );",
+
+						"#endif",
+
+					"#endif",
+
+					"#ifdef WRAP_AROUND",
+
+						"vec3 spotLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );",
+						"spotLightWeighting = mix( spotLightWeighting, spotLightWeightingHalf, wrapRGB );",
+
+						"#ifdef DOUBLE_SIDED",
+
+							"spotLightWeightingBack = mix( spotLightWeightingBack, spotLightWeightingHalfBack, wrapRGB );",
+
+						"#endif",
+
+					"#endif",
+
+					"vLightFront += spotLightColor[ i ] * spotLightWeighting * lDistance * spotEffect;",
+
+					"#ifdef DOUBLE_SIDED",
+
+						"vLightBack += spotLightColor[ i ] * spotLightWeightingBack * lDistance * spotEffect;",
+
+					"#endif",
+
+				"}",
+
+			"}",
+
+		"#endif",
+
+		"#if MAX_HEMI_LIGHTS > 0",
+
+			"for( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {",
+
+				"vec4 lDirection = viewMatrix * vec4( hemisphereLightDirection[ i ], 0.0 );",
+				"vec3 lVector = normalize( lDirection.xyz );",
+
+				"float dotProduct = dot( transformedNormal, lVector );",
+
+				"float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;",
+				"float hemiDiffuseWeightBack = -0.5 * dotProduct + 0.5;",
+
+				"vLightFront += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );",
+
+				"#ifdef DOUBLE_SIDED",
+
+					"vLightBack += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeightBack );",
+
+				"#endif",
+
+			"}",
+
+		"#endif",
+
+		"vLightFront = vLightFront * diffuse + ambient * ambientLightColor + emissive;",
+
+		"#ifdef DOUBLE_SIDED",
+
+			"vLightBack = vLightBack * diffuse + ambient * ambientLightColor + emissive;",
+
+		"#endif"
+
+	].join("\n"),
+
+	// LIGHTS PHONG
+
+	lights_phong_pars_vertex: [
+
+		"#ifndef PHONG_PER_PIXEL",
+
+		"#if MAX_POINT_LIGHTS > 0",
+
+			"uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];",
+			"uniform float pointLightDistance[ MAX_POINT_LIGHTS ];",
+
+			"varying vec4 vPointLight[ MAX_POINT_LIGHTS ];",
+
+		"#endif",
+
+		"#if MAX_SPOT_LIGHTS > 0",
+
+			"uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];",
+			"uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];",
+
+			"varying vec4 vSpotLight[ MAX_SPOT_LIGHTS ];",
+
+		"#endif",
+
+		"#endif",
+
+		"#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP )",
+
+			"varying vec3 vWorldPosition;",
+
+		"#endif"
+
+	].join("\n"),
+
+
+	lights_phong_vertex: [
+
+		"#ifndef PHONG_PER_PIXEL",
+
+		"#if MAX_POINT_LIGHTS > 0",
+
+			"for( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {",
+
+				"vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );",
+				"vec3 lVector = lPosition.xyz - mvPosition.xyz;",
+
+				"float lDistance = 1.0;",
+				"if ( pointLightDistance[ i ] > 0.0 )",
+					"lDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );",
+
+				"vPointLight[ i ] = vec4( lVector, lDistance );",
+
+			"}",
+
+		"#endif",
+
+		"#if MAX_SPOT_LIGHTS > 0",
+
+			"for( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {",
+
+				"vec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );",
+				"vec3 lVector = lPosition.xyz - mvPosition.xyz;",
+
+				"float lDistance = 1.0;",
+				"if ( spotLightDistance[ i ] > 0.0 )",
+					"lDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );",
+
+				"vSpotLight[ i ] = vec4( lVector, lDistance );",
+
+			"}",
+
+		"#endif",
+
+		"#endif",
+
+		"#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP )",
+
+			"vWorldPosition = worldPosition.xyz;",
+
+		"#endif"
+
+	].join("\n"),
+
+	lights_phong_pars_fragment: [
+
+		"uniform vec3 ambientLightColor;",
+
+		"#if MAX_DIR_LIGHTS > 0",
+
+			"uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];",
+			"uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];",
+
+		"#endif",
+
+		"#if MAX_HEMI_LIGHTS > 0",
+
+			"uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];",
+			"uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];",
+			"uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];",
+
+		"#endif",
+
+		"#if MAX_POINT_LIGHTS > 0",
+
+			"uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];",
+
+			"#ifdef PHONG_PER_PIXEL",
+
+				"uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];",
+				"uniform float pointLightDistance[ MAX_POINT_LIGHTS ];",
+
+			"#else",
+
+				"varying vec4 vPointLight[ MAX_POINT_LIGHTS ];",
+
+			"#endif",
+
+		"#endif",
+
+		"#if MAX_SPOT_LIGHTS > 0",
+
+			"uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];",
+			"uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];",
+			"uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];",
+			"uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];",
+			"uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];",
+
+			"#ifdef PHONG_PER_PIXEL",
+
+				"uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];",
+
+			"#else",
+
+				"varying vec4 vSpotLight[ MAX_SPOT_LIGHTS ];",
+
+			"#endif",
+
+		"#endif",
+
+		"#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP )",
+
+			"varying vec3 vWorldPosition;",
+
+		"#endif",
+
+		"#ifdef WRAP_AROUND",
+
+			"uniform vec3 wrapRGB;",
+
+		"#endif",
+
+		"varying vec3 vViewPosition;",
+		"varying vec3 vNormal;"
+
+	].join("\n"),
+
+	lights_phong_fragment: [
+
+		"vec3 normal = normalize( vNormal );",
+		"vec3 viewPosition = normalize( vViewPosition );",
+
+		"#ifdef DOUBLE_SIDED",
+
+			"normal = normal * ( -1.0 + 2.0 * float( gl_FrontFacing ) );",
+
+		"#endif",
+
+		"#ifdef USE_NORMALMAP",
+
+			"normal = perturbNormal2Arb( -vViewPosition, normal );",
+
+		"#elif defined( USE_BUMPMAP )",
+
+			"normal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );",
+
+		"#endif",
+
+		"#if MAX_POINT_LIGHTS > 0",
+
+			"vec3 pointDiffuse  = vec3( 0.0 );",
+			"vec3 pointSpecular = vec3( 0.0 );",
+
+			"for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {",
+
+				"#ifdef PHONG_PER_PIXEL",
+
+					"vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );",
+					"vec3 lVector = lPosition.xyz + vViewPosition.xyz;",
+
+					"float lDistance = 1.0;",
+					"if ( pointLightDistance[ i ] > 0.0 )",
+						"lDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );",
+
+					"lVector = normalize( lVector );",
+
+				"#else",
+
+					"vec3 lVector = normalize( vPointLight[ i ].xyz );",
+					"float lDistance = vPointLight[ i ].w;",
+
+				"#endif",
+
+				// diffuse
+
+				"float dotProduct = dot( normal, lVector );",
+
+				"#ifdef WRAP_AROUND",
+
+					"float pointDiffuseWeightFull = max( dotProduct, 0.0 );",
+					"float pointDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );",
+
+					"vec3 pointDiffuseWeight = mix( vec3 ( pointDiffuseWeightFull ), vec3( pointDiffuseWeightHalf ), wrapRGB );",
+
+				"#else",
+
+					"float pointDiffuseWeight = max( dotProduct, 0.0 );",
+
+				"#endif",
+
+				"pointDiffuse  += diffuse * pointLightColor[ i ] * pointDiffuseWeight * lDistance;",
+
+				// specular
+
+				"vec3 pointHalfVector = normalize( lVector + viewPosition );",
+				"float pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );",
+				"float pointSpecularWeight = specularStrength * max( pow( pointDotNormalHalf, shininess ), 0.0 );",
+
+				"#ifdef PHYSICALLY_BASED_SHADING",
+
+					// 2.0 => 2.0001 is hack to work around ANGLE bug
+
+					"float specularNormalization = ( shininess + 2.0001 ) / 8.0;",
+
+					"vec3 schlick = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( lVector, pointHalfVector ), 5.0 );",
+					"pointSpecular += schlick * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * lDistance * specularNormalization;",
+
+				"#else",
+
+					"pointSpecular += specular * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * lDistance;",
+
+				"#endif",
+
+			"}",
+
+		"#endif",
+
+		"#if MAX_SPOT_LIGHTS > 0",
+
+			"vec3 spotDiffuse  = vec3( 0.0 );",
+			"vec3 spotSpecular = vec3( 0.0 );",
+
+			"for ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {",
+
+				"#ifdef PHONG_PER_PIXEL",
+
+					"vec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );",
+					"vec3 lVector = lPosition.xyz + vViewPosition.xyz;",
+
+					"float lDistance = 1.0;",
+					"if ( spotLightDistance[ i ] > 0.0 )",
+						"lDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );",
+
+					"lVector = normalize( lVector );",
+
+				"#else",
+
+					"vec3 lVector = normalize( vSpotLight[ i ].xyz );",
+					"float lDistance = vSpotLight[ i ].w;",
+
+				"#endif",
+
+				"float spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - vWorldPosition ) );",
+
+				"if ( spotEffect > spotLightAngleCos[ i ] ) {",
+
+					"spotEffect = max( pow( spotEffect, spotLightExponent[ i ] ), 0.0 );",
+
+					// diffuse
+
+					"float dotProduct = dot( normal, lVector );",
+
+					"#ifdef WRAP_AROUND",
+
+						"float spotDiffuseWeightFull = max( dotProduct, 0.0 );",
+						"float spotDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );",
+
+						"vec3 spotDiffuseWeight = mix( vec3 ( spotDiffuseWeightFull ), vec3( spotDiffuseWeightHalf ), wrapRGB );",
+
+					"#else",
+
+						"float spotDiffuseWeight = max( dotProduct, 0.0 );",
+
+					"#endif",
+
+					"spotDiffuse += diffuse * spotLightColor[ i ] * spotDiffuseWeight * lDistance * spotEffect;",
+
+					// specular
+
+					"vec3 spotHalfVector = normalize( lVector + viewPosition );",
+					"float spotDotNormalHalf = max( dot( normal, spotHalfVector ), 0.0 );",
+					"float spotSpecularWeight = specularStrength * max( pow( spotDotNormalHalf, shininess ), 0.0 );",
+
+					"#ifdef PHYSICALLY_BASED_SHADING",
+
+						// 2.0 => 2.0001 is hack to work around ANGLE bug
+
+						"float specularNormalization = ( shininess + 2.0001 ) / 8.0;",
+
+						"vec3 schlick = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( lVector, spotHalfVector ), 5.0 );",
+						"spotSpecular += schlick * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * lDistance * specularNormalization * spotEffect;",
+
+					"#else",
+
+						"spotSpecular += specular * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * lDistance * spotEffect;",
+
+					"#endif",
+
+				"}",
+
+			"}",
+
+		"#endif",
+
+		"#if MAX_DIR_LIGHTS > 0",
+
+			"vec3 dirDiffuse  = vec3( 0.0 );",
+			"vec3 dirSpecular = vec3( 0.0 );" ,
+
+			"for( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {",
+
+				"vec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );",
+				"vec3 dirVector = normalize( lDirection.xyz );",
+
+				// diffuse
+
+				"float dotProduct = dot( normal, dirVector );",
+
+				"#ifdef WRAP_AROUND",
+
+					"float dirDiffuseWeightFull = max( dotProduct, 0.0 );",
+					"float dirDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );",
+
+					"vec3 dirDiffuseWeight = mix( vec3( dirDiffuseWeightFull ), vec3( dirDiffuseWeightHalf ), wrapRGB );",
+
+				"#else",
+
+					"float dirDiffuseWeight = max( dotProduct, 0.0 );",
+
+				"#endif",
+
+				"dirDiffuse  += diffuse * directionalLightColor[ i ] * dirDiffuseWeight;",
+
+				// specular
+
+				"vec3 dirHalfVector = normalize( dirVector + viewPosition );",
+				"float dirDotNormalHalf = max( dot( normal, dirHalfVector ), 0.0 );",
+				"float dirSpecularWeight = specularStrength * max( pow( dirDotNormalHalf, shininess ), 0.0 );",
+
+				"#ifdef PHYSICALLY_BASED_SHADING",
+
+					/*
+					// fresnel term from skin shader
+					"const float F0 = 0.128;",
+
+					"float base = 1.0 - dot( viewPosition, dirHalfVector );",
+					"float exponential = pow( base, 5.0 );",
+
+					"float fresnel = exponential + F0 * ( 1.0 - exponential );",
+					*/
+
+					/*
+					// fresnel term from fresnel shader
+					"const float mFresnelBias = 0.08;",
+					"const float mFresnelScale = 0.3;",
+					"const float mFresnelPower = 5.0;",
+
+					"float fresnel = mFresnelBias + mFresnelScale * pow( 1.0 + dot( normalize( -viewPosition ), normal ), mFresnelPower );",
+					*/
+
+					// 2.0 => 2.0001 is hack to work around ANGLE bug
+
+					"float specularNormalization = ( shininess + 2.0001 ) / 8.0;",
+
+					//"dirSpecular += specular * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization * fresnel;",
+
+					"vec3 schlick = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( dirVector, dirHalfVector ), 5.0 );",
+					"dirSpecular += schlick * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization;",
+
+				"#else",
+
+					"dirSpecular += specular * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight;",
+
+				"#endif",
+
+			"}",
+
+		"#endif",
+
+		"#if MAX_HEMI_LIGHTS > 0",
+
+			"vec3 hemiDiffuse  = vec3( 0.0 );",
+			"vec3 hemiSpecular = vec3( 0.0 );" ,
+
+			"for( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {",
+
+				"vec4 lDirection = viewMatrix * vec4( hemisphereLightDirection[ i ], 0.0 );",
+				"vec3 lVector = normalize( lDirection.xyz );",
+
+				// diffuse
+
+				"float dotProduct = dot( normal, lVector );",
+				"float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;",
+
+				"vec3 hemiColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );",
+
+				"hemiDiffuse += diffuse * hemiColor;",
+
+				// specular (sky light)
+
+				"vec3 hemiHalfVectorSky = normalize( lVector + viewPosition );",
+				"float hemiDotNormalHalfSky = 0.5 * dot( normal, hemiHalfVectorSky ) + 0.5;",
+				"float hemiSpecularWeightSky = specularStrength * max( pow( hemiDotNormalHalfSky, shininess ), 0.0 );",
+
+				// specular (ground light)
+
+				"vec3 lVectorGround = -lVector;",
+
+				"vec3 hemiHalfVectorGround = normalize( lVectorGround + viewPosition );",
+				"float hemiDotNormalHalfGround = 0.5 * dot( normal, hemiHalfVectorGround ) + 0.5;",
+				"float hemiSpecularWeightGround = specularStrength * max( pow( hemiDotNormalHalfGround, shininess ), 0.0 );",
+
+				"#ifdef PHYSICALLY_BASED_SHADING",
+
+					"float dotProductGround = dot( normal, lVectorGround );",
+
+					// 2.0 => 2.0001 is hack to work around ANGLE bug
+
+					"float specularNormalization = ( shininess + 2.0001 ) / 8.0;",
+
+					"vec3 schlickSky = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( lVector, hemiHalfVectorSky ), 5.0 );",
+					"vec3 schlickGround = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( lVectorGround, hemiHalfVectorGround ), 5.0 );",
+					"hemiSpecular += hemiColor * specularNormalization * ( schlickSky * hemiSpecularWeightSky * max( dotProduct, 0.0 ) + schlickGround * hemiSpecularWeightGround * max( dotProductGround, 0.0 ) );",
+
+				"#else",
+
+					"hemiSpecular += specular * hemiColor * ( hemiSpecularWeightSky + hemiSpecularWeightGround ) * hemiDiffuseWeight;",
+
+				"#endif",
+
+			"}",
+
+		"#endif",
+
+		"vec3 totalDiffuse = vec3( 0.0 );",
+		"vec3 totalSpecular = vec3( 0.0 );",
+
+		"#if MAX_DIR_LIGHTS > 0",
+
+			"totalDiffuse += dirDiffuse;",
+			"totalSpecular += dirSpecular;",
+
+		"#endif",
+
+		"#if MAX_HEMI_LIGHTS > 0",
+
+			"totalDiffuse += hemiDiffuse;",
+			"totalSpecular += hemiSpecular;",
+
+		"#endif",
+
+		"#if MAX_POINT_LIGHTS > 0",
+
+			"totalDiffuse += pointDiffuse;",
+			"totalSpecular += pointSpecular;",
+
+		"#endif",
+
+		"#if MAX_SPOT_LIGHTS > 0",
+
+			"totalDiffuse += spotDiffuse;",
+			"totalSpecular += spotSpecular;",
+
+		"#endif",
+
+		"#ifdef METAL",
+
+			"gl_FragColor.xyz = gl_FragColor.xyz * ( emissive + totalDiffuse + ambientLightColor * ambient + totalSpecular );",
+
+		"#else",
+
+			"gl_FragColor.xyz = gl_FragColor.xyz * ( emissive + totalDiffuse + ambientLightColor * ambient ) + totalSpecular;",
+
+		"#endif"
+
+	].join("\n"),
+
+	// VERTEX COLORS
+
+	color_pars_fragment: [
+
+		"#ifdef USE_COLOR",
+
+			"varying vec3 vColor;",
+
+		"#endif"
+
+	].join("\n"),
+
+
+	color_fragment: [
+
+		"#ifdef USE_COLOR",
+
+			"gl_FragColor = gl_FragColor * vec4( vColor, 1.0 );",
+
+		"#endif"
+
+	].join("\n"),
+
+	color_pars_vertex: [
+
+		"#ifdef USE_COLOR",
+
+			"varying vec3 vColor;",
+
+		"#endif"
+
+	].join("\n"),
+
+
+	color_vertex: [
+
+		"#ifdef USE_COLOR",
+
+			"#ifdef GAMMA_INPUT",
+
+				"vColor = color * color;",
+
+			"#else",
+
+				"vColor = color;",
+
+			"#endif",
+
+		"#endif"
+
+	].join("\n"),
+
+	// SKINNING
+
+	skinning_pars_vertex: [
+
+		"#ifdef USE_SKINNING",
+
+			"#ifdef BONE_TEXTURE",
+
+				"uniform sampler2D boneTexture;",
+				"uniform int boneTextureWidth;",
+				"uniform int boneTextureHeight;",
+
+				"mat4 getBoneMatrix( const in float i ) {",
+
+					"float j = i * 4.0;",
+					"float x = mod( j, float( boneTextureWidth ) );",
+					"float y = floor( j / float( boneTextureWidth ) );",
+
+					"float dx = 1.0 / float( boneTextureWidth );",
+					"float dy = 1.0 / float( boneTextureHeight );",
+
+					"y = dy * ( y + 0.5 );",
+
+					"vec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );",
+					"vec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );",
+					"vec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );",
+					"vec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );",
+
+					"mat4 bone = mat4( v1, v2, v3, v4 );",
+
+					"return bone;",
+
+				"}",
+
+			"#else",
+
+				"uniform mat4 boneGlobalMatrices[ MAX_BONES ];",
+
+				"mat4 getBoneMatrix( const in float i ) {",
+
+					"mat4 bone = boneGlobalMatrices[ int(i) ];",
+					"return bone;",
+
+				"}",
+
+			"#endif",
+
+		"#endif"
+
+	].join("\n"),
+
+	skinbase_vertex: [
+
+		"#ifdef USE_SKINNING",
+
+			"mat4 boneMatX = getBoneMatrix( skinIndex.x );",
+			"mat4 boneMatY = getBoneMatrix( skinIndex.y );",
+
+		"#endif"
+
+	].join("\n"),
+
+	skinning_vertex: [
+
+		"#ifdef USE_SKINNING",
+
+			"#ifdef USE_MORPHTARGETS",
+
+			"vec4 skinVertex = vec4( morphed, 1.0 );",
+
+			"#else",
+
+			"vec4 skinVertex = vec4( position, 1.0 );",
+
+			"#endif",
+
+			"vec4 skinned  = boneMatX * skinVertex * skinWeight.x;",
+			"skinned 	  += boneMatY * skinVertex * skinWeight.y;",
+
+		"#endif"
+
+	].join("\n"),
+
+	// MORPHING
+
+	morphtarget_pars_vertex: [
+
+		"#ifdef USE_MORPHTARGETS",
+
+			"#ifndef USE_MORPHNORMALS",
+
+			"uniform float morphTargetInfluences[ 8 ];",
+
+			"#else",
+
+			"uniform float morphTargetInfluences[ 4 ];",
+
+			"#endif",
+
+		"#endif"
+
+	].join("\n"),
+
+	morphtarget_vertex: [
+
+		"#ifdef USE_MORPHTARGETS",
+
+			"vec3 morphed = vec3( 0.0 );",
+			"morphed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];",
+			"morphed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];",
+			"morphed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];",
+			"morphed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];",
+
+			"#ifndef USE_MORPHNORMALS",
+
+			"morphed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];",
+			"morphed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];",
+			"morphed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];",
+			"morphed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];",
+
+			"#endif",
+
+			"morphed += position;",
+
+		"#endif"
+
+	].join("\n"),
+
+	default_vertex : [
+
+		"vec4 mvPosition;",
+
+		"#ifdef USE_SKINNING",
+
+			"mvPosition = modelViewMatrix * skinned;",
+
+		"#endif",
+
+		"#if !defined( USE_SKINNING ) && defined( USE_MORPHTARGETS )",
+
+			"mvPosition = modelViewMatrix * vec4( morphed, 1.0 );",
+
+		"#endif",
+
+		"#if !defined( USE_SKINNING ) && ! defined( USE_MORPHTARGETS )",
+
+			"mvPosition = modelViewMatrix * vec4( position, 1.0 );",
+
+		"#endif",
+
+		"gl_Position = projectionMatrix * mvPosition;"
+
+	].join("\n"),
+
+	morphnormal_vertex: [
+
+		"#ifdef USE_MORPHNORMALS",
+
+			"vec3 morphedNormal = vec3( 0.0 );",
+
+			"morphedNormal +=  ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];",
+			"morphedNormal +=  ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];",
+			"morphedNormal +=  ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];",
+			"morphedNormal +=  ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];",
+
+			"morphedNormal += normal;",
+
+		"#endif"
+
+	].join("\n"),
+
+	skinnormal_vertex: [
+
+		"#ifdef USE_SKINNING",
+
+			"mat4 skinMatrix = skinWeight.x * boneMatX;",
+			"skinMatrix 	+= skinWeight.y * boneMatY;",
+
+			"#ifdef USE_MORPHNORMALS",
+
+			"vec4 skinnedNormal = skinMatrix * vec4( morphedNormal, 0.0 );",
+
+			"#else",
+
+			"vec4 skinnedNormal = skinMatrix * vec4( normal, 0.0 );",
+
+			"#endif",
+
+		"#endif"
+
+	].join("\n"),
+
+	defaultnormal_vertex: [
+
+		"vec3 objectNormal;",
+
+		"#ifdef USE_SKINNING",
+
+			"objectNormal = skinnedNormal.xyz;",
+
+		"#endif",
+
+		"#if !defined( USE_SKINNING ) && defined( USE_MORPHNORMALS )",
+
+			"objectNormal = morphedNormal;",
+
+		"#endif",
+
+		"#if !defined( USE_SKINNING ) && ! defined( USE_MORPHNORMALS )",
+
+			"objectNormal = normal;",
+
+		"#endif",
+
+		"#ifdef FLIP_SIDED",
+
+			"objectNormal = -objectNormal;",
+
+		"#endif",
+
+		"vec3 transformedNormal = normalMatrix * objectNormal;"
+
+	].join("\n"),
+
+	// SHADOW MAP
+
+	// based on SpiderGL shadow map and Fabien Sanglard's GLSL shadow mapping examples
+	//  http://spidergl.org/example.php?id=6
+	// 	http://fabiensanglard.net/shadowmapping
+
+	shadowmap_pars_fragment: [
+
+		"#ifdef USE_SHADOWMAP",
+
+			"uniform sampler2D shadowMap[ MAX_SHADOWS ];",
+			"uniform vec2 shadowMapSize[ MAX_SHADOWS ];",
+
+			"uniform float shadowDarkness[ MAX_SHADOWS ];",
+			"uniform float shadowBias[ MAX_SHADOWS ];",
+
+			"varying vec4 vShadowCoord[ MAX_SHADOWS ];",
+
+			"float unpackDepth( const in vec4 rgba_depth ) {",
+
+				"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 );",
+				"float depth = dot( rgba_depth, bit_shift );",
+				"return depth;",
+
+			"}",
+
+		"#endif"
+
+	].join("\n"),
+
+	shadowmap_fragment: [
+
+		"#ifdef USE_SHADOWMAP",
+
+			"#ifdef SHADOWMAP_DEBUG",
+
+				"vec3 frustumColors[3];",
+				"frustumColors[0] = vec3( 1.0, 0.5, 0.0 );",
+				"frustumColors[1] = vec3( 0.0, 1.0, 0.8 );",
+				"frustumColors[2] = vec3( 0.0, 0.5, 1.0 );",
+
+			"#endif",
+
+			"#ifdef SHADOWMAP_CASCADE",
+
+				"int inFrustumCount = 0;",
+
+			"#endif",
+
+			"float fDepth;",
+			"vec3 shadowColor = vec3( 1.0 );",
+
+			"for( int i = 0; i < MAX_SHADOWS; i ++ ) {",
+
+				"vec3 shadowCoord = vShadowCoord[ i ].xyz / vShadowCoord[ i ].w;",
+
+				// "if ( something && something )" 		 breaks ATI OpenGL shader compiler
+				// "if ( all( something, something ) )"  using this instead
+
+				"bvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );",
+				"bool inFrustum = all( inFrustumVec );",
+
+				// don't shadow pixels outside of light frustum
+				// use just first frustum (for cascades)
+				// don't shadow pixels behind far plane of light frustum
+
+				"#ifdef SHADOWMAP_CASCADE",
+
+					"inFrustumCount += int( inFrustum );",
+					"bvec3 frustumTestVec = bvec3( inFrustum, inFrustumCount == 1, shadowCoord.z <= 1.0 );",
+
+				"#else",
+
+					"bvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );",
+
+				"#endif",
+
+				"bool frustumTest = all( frustumTestVec );",
+
+				"if ( frustumTest ) {",
+
+					"shadowCoord.z += shadowBias[ i ];",
+
+					"#if defined( SHADOWMAP_TYPE_PCF )",
+
+						// Percentage-close filtering
+						// (9 pixel kernel)
+						// http://fabiensanglard.net/shadowmappingPCF/
+
+						"float shadow = 0.0;",
+
+						/*
+						// nested loops breaks shader compiler / validator on some ATI cards when using OpenGL
+						// must enroll loop manually
+
+						"for ( float y = -1.25; y <= 1.25; y += 1.25 )",
+							"for ( float x = -1.25; x <= 1.25; x += 1.25 ) {",
+
+								"vec4 rgbaDepth = texture2D( shadowMap[ i ], vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy );",
+
+								// doesn't seem to produce any noticeable visual difference compared to simple "texture2D" lookup
+								//"vec4 rgbaDepth = texture2DProj( shadowMap[ i ], vec4( vShadowCoord[ i ].w * ( vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy ), 0.05, vShadowCoord[ i ].w ) );",
+
+								"float fDepth = unpackDepth( rgbaDepth );",
+
+								"if ( fDepth < shadowCoord.z )",
+									"shadow += 1.0;",
+
+						"}",
+
+						"shadow /= 9.0;",
+
+						*/
+
+						"const float shadowDelta = 1.0 / 9.0;",
+
+						"float xPixelOffset = 1.0 / shadowMapSize[ i ].x;",
+						"float yPixelOffset = 1.0 / shadowMapSize[ i ].y;",
+
+						"float dx0 = -1.25 * xPixelOffset;",
+						"float dy0 = -1.25 * yPixelOffset;",
+						"float dx1 = 1.25 * xPixelOffset;",
+						"float dy1 = 1.25 * yPixelOffset;",
+
+						"fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );",
+						"if ( fDepth < shadowCoord.z ) shadow += shadowDelta;",
+
+						"fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );",
+						"if ( fDepth < shadowCoord.z ) shadow += shadowDelta;",
+
+						"fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );",
+						"if ( fDepth < shadowCoord.z ) shadow += shadowDelta;",
+
+						"fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );",
+						"if ( fDepth < shadowCoord.z ) shadow += shadowDelta;",
+
+						"fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );",
+						"if ( fDepth < shadowCoord.z ) shadow += shadowDelta;",
+
+						"fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );",
+						"if ( fDepth < shadowCoord.z ) shadow += shadowDelta;",
+
+						"fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );",
+						"if ( fDepth < shadowCoord.z ) shadow += shadowDelta;",
+
+						"fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );",
+						"if ( fDepth < shadowCoord.z ) shadow += shadowDelta;",
+
+						"fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );",
+						"if ( fDepth < shadowCoord.z ) shadow += shadowDelta;",
+
+						"shadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );",
+
+					"#elif defined( SHADOWMAP_TYPE_PCF_SOFT )",
+
+						// Percentage-close filtering
+						// (9 pixel kernel)
+						// http://fabiensanglard.net/shadowmappingPCF/
+
+						"float shadow = 0.0;",
+
+						"float xPixelOffset = 1.0 / shadowMapSize[ i ].x;",
+						"float yPixelOffset = 1.0 / shadowMapSize[ i ].y;",
+
+						"float dx0 = -1.0 * xPixelOffset;",
+						"float dy0 = -1.0 * yPixelOffset;",
+						"float dx1 = 1.0 * xPixelOffset;",
+						"float dy1 = 1.0 * yPixelOffset;",
+
+						"mat3 shadowKernel;",
+						"mat3 depthKernel;",
+
+						"depthKernel[0][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );",
+						"depthKernel[0][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );",
+						"depthKernel[0][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );",
+						"depthKernel[1][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );",
+						"depthKernel[1][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );",
+						"depthKernel[1][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );",
+						"depthKernel[2][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );",
+						"depthKernel[2][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );",
+						"depthKernel[2][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );",
+
+						"vec3 shadowZ = vec3( shadowCoord.z );",
+						"shadowKernel[0] = vec3(lessThan(depthKernel[0], shadowZ ));",
+						"shadowKernel[0] *= vec3(0.25);",
+
+						"shadowKernel[1] = vec3(lessThan(depthKernel[1], shadowZ ));",
+						"shadowKernel[1] *= vec3(0.25);",
+
+						"shadowKernel[2] = vec3(lessThan(depthKernel[2], shadowZ ));",
+						"shadowKernel[2] *= vec3(0.25);",
+
+						"vec2 fractionalCoord = 1.0 - fract( shadowCoord.xy * shadowMapSize[i].xy );",
+
+						"shadowKernel[0] = mix( shadowKernel[1], shadowKernel[0], fractionalCoord.x );",
+						"shadowKernel[1] = mix( shadowKernel[2], shadowKernel[1], fractionalCoord.x );",
+
+						"vec4 shadowValues;",
+						"shadowValues.x = mix( shadowKernel[0][1], shadowKernel[0][0], fractionalCoord.y );",
+						"shadowValues.y = mix( shadowKernel[0][2], shadowKernel[0][1], fractionalCoord.y );",
+						"shadowValues.z = mix( shadowKernel[1][1], shadowKernel[1][0], fractionalCoord.y );",
+						"shadowValues.w = mix( shadowKernel[1][2], shadowKernel[1][1], fractionalCoord.y );",
+
+						"shadow = dot( shadowValues, vec4( 1.0 ) );",
+
+						"shadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );",
+
+					"#else",
+
+						"vec4 rgbaDepth = texture2D( shadowMap[ i ], shadowCoord.xy );",
+						"float fDepth = unpackDepth( rgbaDepth );",
+
+						"if ( fDepth < shadowCoord.z )",
+
+							// spot with multiple shadows is darker
+
+							"shadowColor = shadowColor * vec3( 1.0 - shadowDarkness[ i ] );",
+
+							// spot with multiple shadows has the same color as single shadow spot
+
+							//"shadowColor = min( shadowColor, vec3( shadowDarkness[ i ] ) );",
+
+					"#endif",
+
+				"}",
+
+
+				"#ifdef SHADOWMAP_DEBUG",
+
+					"#ifdef SHADOWMAP_CASCADE",
+
+						"if ( inFrustum && inFrustumCount == 1 ) gl_FragColor.xyz *= frustumColors[ i ];",
+
+					"#else",
+
+						"if ( inFrustum ) gl_FragColor.xyz *= frustumColors[ i ];",
+
+					"#endif",
+
+				"#endif",
+
+			"}",
+
+			"#ifdef GAMMA_OUTPUT",
+
+				"shadowColor *= shadowColor;",
+
+			"#endif",
+
+			"gl_FragColor.xyz = gl_FragColor.xyz * shadowColor;",
+
+		"#endif"
+
+	].join("\n"),
+
+	shadowmap_pars_vertex: [
+
+		"#ifdef USE_SHADOWMAP",
+
+			"varying vec4 vShadowCoord[ MAX_SHADOWS ];",
+			"uniform mat4 shadowMatrix[ MAX_SHADOWS ];",
+
+		"#endif"
+
+	].join("\n"),
+
+	shadowmap_vertex: [
+
+		"#ifdef USE_SHADOWMAP",
+
+			"for( int i = 0; i < MAX_SHADOWS; i ++ ) {",
+
+				"vShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;",
+
+			"}",
+
+		"#endif"
+
+	].join("\n"),
+
+	// ALPHATEST
+
+	alphatest_fragment: [
+
+		"#ifdef ALPHATEST",
+
+			"if ( gl_FragColor.a < ALPHATEST ) discard;",
+
+		"#endif"
+
+	].join("\n"),
+
+	// LINEAR SPACE
+
+	linear_to_gamma_fragment: [
+
+		"#ifdef GAMMA_OUTPUT",
+
+			"gl_FragColor.xyz = sqrt( gl_FragColor.xyz );",
+
+		"#endif"
+
+	].join("\n")
+
+
+};
+
+THREE.UniformsUtils = {
+
+	merge: function ( uniforms ) {
+
+		var u, p, tmp, merged = {};
+
+		for ( u = 0; u < uniforms.length; u ++ ) {
+
+			tmp = this.clone( uniforms[ u ] );
+
+			for ( p in tmp ) {
+
+				merged[ p ] = tmp[ p ];
+
+			}
+
+		}
+
+		return merged;
+
+	},
+
+	clone: function ( uniforms_src ) {
+
+		var u, p, parameter, parameter_src, uniforms_dst = {};
+
+		for ( u in uniforms_src ) {
+
+			uniforms_dst[ u ] = {};
+
+			for ( p in uniforms_src[ u ] ) {
+
+				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;
+
+	}
+
+};
+
+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 },
+
+		"envMap" : { type: "t", value: null },
+		"flipEnvMap" : { type: "f", value: -1 },
+		"useRefract" : { type: "i", value: 0 },
+		"reflectivity" : { type: "f", value: 1.0 },
+		"refractionRatio" : { type: "f", value: 0.98 },
+		"combine" : { type: "i", value: 0 },
+
+		"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: [] }
+
+	}
+
+};
+
+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" ],
+
+			"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[ "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[ "lightmap_pars_fragment" ],
+			THREE.ShaderChunk[ "envmap_pars_fragment" ],
+			THREE.ShaderChunk[ "fog_pars_fragment" ],
+			THREE.ShaderChunk[ "shadowmap_pars_fragment" ],
+			THREE.ShaderChunk[ "specularmap_pars_fragment" ],
+
+			"void main() {",
+
+				"gl_FragColor = vec4( diffuse, opacity );",
+
+				THREE.ShaderChunk[ "map_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" ],
+
+			"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[ "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[ "lightmap_pars_fragment" ],
+			THREE.ShaderChunk[ "envmap_pars_fragment" ],
+			THREE.ShaderChunk[ "fog_pars_fragment" ],
+			THREE.ShaderChunk[ "shadowmap_pars_fragment" ],
+			THREE.ShaderChunk[ "specularmap_pars_fragment" ],
+
+			"void main() {",
+
+				"gl_FragColor = vec4( vec3 ( 1.0 ), opacity );",
+
+				THREE.ShaderChunk[ "map_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" ],
+
+			"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" ],
+
+				"vViewPosition = -mvPosition.xyz;",
+
+				THREE.ShaderChunk[ "worldpos_vertex" ],
+				THREE.ShaderChunk[ "envmap_vertex" ],
+				THREE.ShaderChunk[ "lights_phong_vertex" ],
+				THREE.ShaderChunk[ "shadowmap_vertex" ],
+
+			"}"
+
+		].join("\n"),
+
+		fragmentShader: [
+
+			"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[ "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" ],
+
+			"void main() {",
+
+				"gl_FragColor = vec4( vec3 ( 1.0 ), opacity );",
+
+				THREE.ShaderChunk[ "map_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" ],
+
+			"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[ "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" ],
+
+			"void main() {",
+
+				"gl_FragColor = vec4( psColor, opacity );",
+
+				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" ],
+
+			"void main() {",
+
+				THREE.ShaderChunk[ "color_vertex" ],
+
+				"vLineDistance = scale * lineDistance;",
+
+				"vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
+				"gl_Position = projectionMatrix * mvPosition;",
+
+			"}"
+
+		].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" ],
+
+			"void main() {",
+
+				"if ( mod( vLineDistance, totalSize ) > dashSize ) {",
+
+					"discard;",
+
+				"}",
+
+				"gl_FragColor = vec4( diffuse, opacity );",
+
+				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: [
+
+			"void main() {",
+
+				"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+
+			"}"
+
+		].join("\n"),
+
+		fragmentShader: [
+
+			"uniform float mNear;",
+			"uniform float mFar;",
+			"uniform float opacity;",
+
+			"void main() {",
+
+				"float depth = gl_FragCoord.z / gl_FragCoord.w;",
+				"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" ],
+
+			"void main() {",
+
+				"vNormal = normalize( normalMatrix * normal );",
+
+				THREE.ShaderChunk[ "morphtarget_vertex" ],
+				THREE.ShaderChunk[ "default_vertex" ],
+
+			"}"
+
+		].join("\n"),
+
+		fragmentShader: [
+
+			"uniform float opacity;",
+			"varying vec3 vNormal;",
+
+			"void main() {",
+
+				"gl_FragColor = vec4( 0.5 * normalize( vNormal ) + 0.5, opacity );",
+
+			"}"
+
+		].join("\n")
+
+	},
+
+	/* -------------------------------------------------------------------------
+	//	Normal map shader
+	//		- Blinn-Phong
+	//		- normal + diffuse + specular + AO + displacement + reflection + shadow maps
+	//		- point and directional lights (use with "lights: true" material option)
+	 ------------------------------------------------------------------------- */
+
+	'normalmap' : {
+
+		uniforms: THREE.UniformsUtils.merge( [
+
+			THREE.UniformsLib[ "fog" ],
+			THREE.UniformsLib[ "lights" ],
+			THREE.UniformsLib[ "shadowmap" ],
+
+			{
+
+			"enableAO"		  : { type: "i", value: 0 },
+			"enableDiffuse"	  : { type: "i", value: 0 },
+			"enableSpecular"  : { type: "i", value: 0 },
+			"enableReflection": { type: "i", value: 0 },
+			"enableDisplacement": { type: "i", value: 0 },
+
+			"tDisplacement": { type: "t", value: null }, // must go first as this is vertex texture
+			"tDiffuse"	   : { type: "t", value: null },
+			"tCube"		   : { type: "t", value: null },
+			"tNormal"	   : { type: "t", value: null },
+			"tSpecular"	   : { type: "t", value: null },
+			"tAO"		   : { type: "t", value: null },
+
+			"uNormalScale": { type: "v2", value: new THREE.Vector2( 1, 1 ) },
+
+			"uDisplacementBias": { type: "f", value: 0.0 },
+			"uDisplacementScale": { type: "f", value: 1.0 },
+
+			"uDiffuseColor": { type: "c", value: new THREE.Color( 0xffffff ) },
+			"uSpecularColor": { type: "c", value: new THREE.Color( 0x111111 ) },
+			"uAmbientColor": { type: "c", value: new THREE.Color( 0xffffff ) },
+			"uShininess": { type: "f", value: 30 },
+			"uOpacity": { type: "f", value: 1 },
+
+			"useRefract": { type: "i", value: 0 },
+			"uRefractionRatio": { type: "f", value: 0.98 },
+			"uReflectivity": { type: "f", value: 0.5 },
+
+			"uOffset" : { type: "v2", value: new THREE.Vector2( 0, 0 ) },
+			"uRepeat" : { type: "v2", value: new THREE.Vector2( 1, 1 ) },
+
+			"wrapRGB"  : { type: "v3", value: new THREE.Vector3( 1, 1, 1 ) }
+
+			}
+
+		] ),
+
+		fragmentShader: [
+
+			"uniform vec3 uAmbientColor;",
+			"uniform vec3 uDiffuseColor;",
+			"uniform vec3 uSpecularColor;",
+			"uniform float uShininess;",
+			"uniform float uOpacity;",
+
+			"uniform bool enableDiffuse;",
+			"uniform bool enableSpecular;",
+			"uniform bool enableAO;",
+			"uniform bool enableReflection;",
+
+			"uniform sampler2D tDiffuse;",
+			"uniform sampler2D tNormal;",
+			"uniform sampler2D tSpecular;",
+			"uniform sampler2D tAO;",
+
+			"uniform samplerCube tCube;",
+
+			"uniform vec2 uNormalScale;",
+
+			"uniform bool useRefract;",
+			"uniform float uRefractionRatio;",
+			"uniform float uReflectivity;",
+
+			"varying vec3 vTangent;",
+			"varying vec3 vBinormal;",
+			"varying vec3 vNormal;",
+			"varying vec2 vUv;",
+
+			"uniform vec3 ambientLightColor;",
+
+			"#if MAX_DIR_LIGHTS > 0",
+
+				"uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];",
+				"uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];",
+
+			"#endif",
+
+			"#if MAX_HEMI_LIGHTS > 0",
+
+				"uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];",
+				"uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];",
+				"uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];",
+
+			"#endif",
+
+			"#if MAX_POINT_LIGHTS > 0",
+
+				"uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];",
+				"uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];",
+				"uniform float pointLightDistance[ MAX_POINT_LIGHTS ];",
+
+			"#endif",
+
+			"#if MAX_SPOT_LIGHTS > 0",
+
+				"uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];",
+				"uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];",
+				"uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];",
+				"uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];",
+				"uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];",
+				"uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];",
+
+			"#endif",
+
+			"#ifdef WRAP_AROUND",
+
+				"uniform vec3 wrapRGB;",
+
+			"#endif",
+
+			"varying vec3 vWorldPosition;",
+			"varying vec3 vViewPosition;",
+
+			THREE.ShaderChunk[ "shadowmap_pars_fragment" ],
+			THREE.ShaderChunk[ "fog_pars_fragment" ],
+
+			"void main() {",
+
+				"gl_FragColor = vec4( vec3( 1.0 ), uOpacity );",
+
+				"vec3 specularTex = vec3( 1.0 );",
+
+				"vec3 normalTex = texture2D( tNormal, vUv ).xyz * 2.0 - 1.0;",
+				"normalTex.xy *= uNormalScale;",
+				"normalTex = normalize( normalTex );",
+
+				"if( enableDiffuse ) {",
+
+					"#ifdef GAMMA_INPUT",
+
+						"vec4 texelColor = texture2D( tDiffuse, vUv );",
+						"texelColor.xyz *= texelColor.xyz;",
+
+						"gl_FragColor = gl_FragColor * texelColor;",
+
+					"#else",
+
+						"gl_FragColor = gl_FragColor * texture2D( tDiffuse, vUv );",
+
+					"#endif",
+
+				"}",
+
+				"if( enableAO ) {",
+
+					"#ifdef GAMMA_INPUT",
+
+						"vec4 aoColor = texture2D( tAO, vUv );",
+						"aoColor.xyz *= aoColor.xyz;",
+
+						"gl_FragColor.xyz = gl_FragColor.xyz * aoColor.xyz;",
+
+					"#else",
+
+						"gl_FragColor.xyz = gl_FragColor.xyz * texture2D( tAO, vUv ).xyz;",
+
+					"#endif",
+
+				"}",
+
+				"if( enableSpecular )",
+					"specularTex = texture2D( tSpecular, vUv ).xyz;",
+
+				"mat3 tsb = mat3( normalize( vTangent ), normalize( vBinormal ), normalize( vNormal ) );",
+				"vec3 finalNormal = tsb * normalTex;",
+
+				"#ifdef FLIP_SIDED",
+
+					"finalNormal = -finalNormal;",
+
+				"#endif",
+
+				"vec3 normal = normalize( finalNormal );",
+				"vec3 viewPosition = normalize( vViewPosition );",
+
+				// point lights
+
+				"#if MAX_POINT_LIGHTS > 0",
+
+					"vec3 pointDiffuse = vec3( 0.0 );",
+					"vec3 pointSpecular = vec3( 0.0 );",
+
+					"for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {",
+
+						"vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );",
+						"vec3 pointVector = lPosition.xyz + vViewPosition.xyz;",
+
+						"float pointDistance = 1.0;",
+						"if ( pointLightDistance[ i ] > 0.0 )",
+							"pointDistance = 1.0 - min( ( length( pointVector ) / pointLightDistance[ i ] ), 1.0 );",
+
+						"pointVector = normalize( pointVector );",
+
+						// diffuse
+
+						"#ifdef WRAP_AROUND",
+
+							"float pointDiffuseWeightFull = max( dot( normal, pointVector ), 0.0 );",
+							"float pointDiffuseWeightHalf = max( 0.5 * dot( normal, pointVector ) + 0.5, 0.0 );",
+
+							"vec3 pointDiffuseWeight = mix( vec3 ( pointDiffuseWeightFull ), vec3( pointDiffuseWeightHalf ), wrapRGB );",
+
+						"#else",
+
+							"float pointDiffuseWeight = max( dot( normal, pointVector ), 0.0 );",
+
+						"#endif",
+
+						"pointDiffuse += pointDistance * pointLightColor[ i ] * uDiffuseColor * pointDiffuseWeight;",
+
+						// specular
+
+						"vec3 pointHalfVector = normalize( pointVector + viewPosition );",
+						"float pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );",
+						"float pointSpecularWeight = specularTex.r * max( pow( pointDotNormalHalf, uShininess ), 0.0 );",
+
+						"#ifdef PHYSICALLY_BASED_SHADING",
+
+							// 2.0 => 2.0001 is hack to work around ANGLE bug
+
+							"float specularNormalization = ( uShininess + 2.0001 ) / 8.0;",
+
+							"vec3 schlick = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( pointVector, pointHalfVector ), 5.0 );",
+							"pointSpecular += schlick * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * pointDistance * specularNormalization;",
+
+						"#else",
+
+							"pointSpecular += pointDistance * pointLightColor[ i ] * uSpecularColor * pointSpecularWeight * pointDiffuseWeight;",
+
+						"#endif",
+
+					"}",
+
+				"#endif",
+
+				// spot lights
+
+				"#if MAX_SPOT_LIGHTS > 0",
+
+					"vec3 spotDiffuse = vec3( 0.0 );",
+					"vec3 spotSpecular = vec3( 0.0 );",
+
+					"for ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {",
+
+						"vec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );",
+						"vec3 spotVector = lPosition.xyz + vViewPosition.xyz;",
+
+						"float spotDistance = 1.0;",
+						"if ( spotLightDistance[ i ] > 0.0 )",
+							"spotDistance = 1.0 - min( ( length( spotVector ) / spotLightDistance[ i ] ), 1.0 );",
+
+						"spotVector = normalize( spotVector );",
+
+						"float spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - vWorldPosition ) );",
+
+						"if ( spotEffect > spotLightAngleCos[ i ] ) {",
+
+							"spotEffect = max( pow( spotEffect, spotLightExponent[ i ] ), 0.0 );",
+
+							// diffuse
+
+							"#ifdef WRAP_AROUND",
+
+								"float spotDiffuseWeightFull = max( dot( normal, spotVector ), 0.0 );",
+								"float spotDiffuseWeightHalf = max( 0.5 * dot( normal, spotVector ) + 0.5, 0.0 );",
+
+								"vec3 spotDiffuseWeight = mix( vec3 ( spotDiffuseWeightFull ), vec3( spotDiffuseWeightHalf ), wrapRGB );",
+
+							"#else",
+
+								"float spotDiffuseWeight = max( dot( normal, spotVector ), 0.0 );",
+
+							"#endif",
+
+							"spotDiffuse += spotDistance * spotLightColor[ i ] * uDiffuseColor * spotDiffuseWeight * spotEffect;",
+
+							// specular
+
+							"vec3 spotHalfVector = normalize( spotVector + viewPosition );",
+							"float spotDotNormalHalf = max( dot( normal, spotHalfVector ), 0.0 );",
+							"float spotSpecularWeight = specularTex.r * max( pow( spotDotNormalHalf, uShininess ), 0.0 );",
+
+							"#ifdef PHYSICALLY_BASED_SHADING",
+
+								// 2.0 => 2.0001 is hack to work around ANGLE bug
+
+								"float specularNormalization = ( uShininess + 2.0001 ) / 8.0;",
+
+								"vec3 schlick = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( spotVector, spotHalfVector ), 5.0 );",
+								"spotSpecular += schlick * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * spotDistance * specularNormalization * spotEffect;",
+
+							"#else",
+
+								"spotSpecular += spotDistance * spotLightColor[ i ] * uSpecularColor * spotSpecularWeight * spotDiffuseWeight * spotEffect;",
+
+							"#endif",
+
+						"}",
+
+					"}",
+
+				"#endif",
+
+				// directional lights
+
+				"#if MAX_DIR_LIGHTS > 0",
+
+					"vec3 dirDiffuse = vec3( 0.0 );",
+					"vec3 dirSpecular = vec3( 0.0 );",
+
+					"for( int i = 0; i < MAX_DIR_LIGHTS; i++ ) {",
+
+						"vec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );",
+						"vec3 dirVector = normalize( lDirection.xyz );",
+
+						// diffuse
+
+						"#ifdef WRAP_AROUND",
+
+							"float directionalLightWeightingFull = max( dot( normal, dirVector ), 0.0 );",
+							"float directionalLightWeightingHalf = max( 0.5 * dot( normal, dirVector ) + 0.5, 0.0 );",
+
+							"vec3 dirDiffuseWeight = mix( vec3( directionalLightWeightingFull ), vec3( directionalLightWeightingHalf ), wrapRGB );",
+
+						"#else",
+
+							"float dirDiffuseWeight = max( dot( normal, dirVector ), 0.0 );",
+
+						"#endif",
+
+						"dirDiffuse += directionalLightColor[ i ] * uDiffuseColor * dirDiffuseWeight;",
+
+						// specular
+
+						"vec3 dirHalfVector = normalize( dirVector + viewPosition );",
+						"float dirDotNormalHalf = max( dot( normal, dirHalfVector ), 0.0 );",
+						"float dirSpecularWeight = specularTex.r * max( pow( dirDotNormalHalf, uShininess ), 0.0 );",
+
+						"#ifdef PHYSICALLY_BASED_SHADING",
+
+							// 2.0 => 2.0001 is hack to work around ANGLE bug
+
+							"float specularNormalization = ( uShininess + 2.0001 ) / 8.0;",
+
+							"vec3 schlick = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( dirVector, dirHalfVector ), 5.0 );",
+							"dirSpecular += schlick * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization;",
+
+						"#else",
+
+							"dirSpecular += directionalLightColor[ i ] * uSpecularColor * dirSpecularWeight * dirDiffuseWeight;",
+
+						"#endif",
+
+					"}",
+
+				"#endif",
+
+				// hemisphere lights
+
+				"#if MAX_HEMI_LIGHTS > 0",
+
+					"vec3 hemiDiffuse  = vec3( 0.0 );",
+					"vec3 hemiSpecular = vec3( 0.0 );" ,
+
+					"for( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {",
+
+						"vec4 lDirection = viewMatrix * vec4( hemisphereLightDirection[ i ], 0.0 );",
+						"vec3 lVector = normalize( lDirection.xyz );",
+
+						// diffuse
+
+						"float dotProduct = dot( normal, lVector );",
+						"float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;",
+
+						"vec3 hemiColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );",
+
+						"hemiDiffuse += uDiffuseColor * hemiColor;",
+
+						// specular (sky light)
+
+
+						"vec3 hemiHalfVectorSky = normalize( lVector + viewPosition );",
+						"float hemiDotNormalHalfSky = 0.5 * dot( normal, hemiHalfVectorSky ) + 0.5;",
+						"float hemiSpecularWeightSky = specularTex.r * max( pow( hemiDotNormalHalfSky, uShininess ), 0.0 );",
+
+						// specular (ground light)
+
+						"vec3 lVectorGround = -lVector;",
+
+						"vec3 hemiHalfVectorGround = normalize( lVectorGround + viewPosition );",
+						"float hemiDotNormalHalfGround = 0.5 * dot( normal, hemiHalfVectorGround ) + 0.5;",
+						"float hemiSpecularWeightGround = specularTex.r * max( pow( hemiDotNormalHalfGround, uShininess ), 0.0 );",
+
+						"#ifdef PHYSICALLY_BASED_SHADING",
+
+							"float dotProductGround = dot( normal, lVectorGround );",
+
+							// 2.0 => 2.0001 is hack to work around ANGLE bug
+
+							"float specularNormalization = ( uShininess + 2.0001 ) / 8.0;",
+
+							"vec3 schlickSky = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( lVector, hemiHalfVectorSky ), 5.0 );",
+							"vec3 schlickGround = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( lVectorGround, hemiHalfVectorGround ), 5.0 );",
+							"hemiSpecular += hemiColor * specularNormalization * ( schlickSky * hemiSpecularWeightSky * max( dotProduct, 0.0 ) + schlickGround * hemiSpecularWeightGround * max( dotProductGround, 0.0 ) );",
+
+						"#else",
+
+							"hemiSpecular += uSpecularColor * hemiColor * ( hemiSpecularWeightSky + hemiSpecularWeightGround ) * hemiDiffuseWeight;",
+
+						"#endif",
+
+					"}",
+
+				"#endif",
+
+				// all lights contribution summation
+
+				"vec3 totalDiffuse = vec3( 0.0 );",
+				"vec3 totalSpecular = vec3( 0.0 );",
+
+				"#if MAX_DIR_LIGHTS > 0",
+
+					"totalDiffuse += dirDiffuse;",
+					"totalSpecular += dirSpecular;",
+
+				"#endif",
+
+				"#if MAX_HEMI_LIGHTS > 0",
+
+					"totalDiffuse += hemiDiffuse;",
+					"totalSpecular += hemiSpecular;",
+
+				"#endif",
+
+				"#if MAX_POINT_LIGHTS > 0",
+
+					"totalDiffuse += pointDiffuse;",
+					"totalSpecular += pointSpecular;",
+
+				"#endif",
+
+				"#if MAX_SPOT_LIGHTS > 0",
+
+					"totalDiffuse += spotDiffuse;",
+					"totalSpecular += spotSpecular;",
+
+				"#endif",
+
+				"#ifdef METAL",
+
+					"gl_FragColor.xyz = gl_FragColor.xyz * ( totalDiffuse + ambientLightColor * uAmbientColor + totalSpecular );",
+
+				"#else",
+
+					"gl_FragColor.xyz = gl_FragColor.xyz * ( totalDiffuse + ambientLightColor * uAmbientColor ) + totalSpecular;",
+
+				"#endif",
+
+				"if ( enableReflection ) {",
+
+					"vec3 vReflect;",
+					"vec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );",
+
+					"if ( useRefract ) {",
+
+						"vReflect = refract( cameraToVertex, normal, uRefractionRatio );",
+
+					"} else {",
+
+						"vReflect = reflect( cameraToVertex, normal );",
+
+					"}",
+
+					"vec4 cubeColor = textureCube( tCube, vec3( -vReflect.x, vReflect.yz ) );",
+
+					"#ifdef GAMMA_INPUT",
+
+						"cubeColor.xyz *= cubeColor.xyz;",
+
+					"#endif",
+
+					"gl_FragColor.xyz = mix( gl_FragColor.xyz, cubeColor.xyz, specularTex.r * uReflectivity );",
+
+				"}",
+
+				THREE.ShaderChunk[ "shadowmap_fragment" ],
+				THREE.ShaderChunk[ "linear_to_gamma_fragment" ],
+				THREE.ShaderChunk[ "fog_fragment" ],
+
+			"}"
+
+		].join("\n"),
+
+		vertexShader: [
+
+			"attribute vec4 tangent;",
+
+			"uniform vec2 uOffset;",
+			"uniform vec2 uRepeat;",
+
+			"uniform bool enableDisplacement;",
+
+			"#ifdef VERTEX_TEXTURES",
+
+				"uniform sampler2D tDisplacement;",
+				"uniform float uDisplacementScale;",
+				"uniform float uDisplacementBias;",
+
+			"#endif",
+
+			"varying vec3 vTangent;",
+			"varying vec3 vBinormal;",
+			"varying vec3 vNormal;",
+			"varying vec2 vUv;",
+
+			"varying vec3 vWorldPosition;",
+			"varying vec3 vViewPosition;",
+
+			THREE.ShaderChunk[ "skinning_pars_vertex" ],
+			THREE.ShaderChunk[ "shadowmap_pars_vertex" ],
+
+			"void main() {",
+
+				THREE.ShaderChunk[ "skinbase_vertex" ],
+				THREE.ShaderChunk[ "skinnormal_vertex" ],
+
+				// normal, tangent and binormal vectors
+
+				"#ifdef USE_SKINNING",
+
+					"vNormal = normalize( normalMatrix * skinnedNormal.xyz );",
+
+					"vec4 skinnedTangent = skinMatrix * vec4( tangent.xyz, 0.0 );",
+					"vTangent = normalize( normalMatrix * skinnedTangent.xyz );",
+
+				"#else",
+
+					"vNormal = normalize( normalMatrix * normal );",
+					"vTangent = normalize( normalMatrix * tangent.xyz );",
+
+				"#endif",
+
+				"vBinormal = normalize( cross( vNormal, vTangent ) * tangent.w );",
+
+				"vUv = uv * uRepeat + uOffset;",
+
+				// displacement mapping
+
+				"vec3 displacedPosition;",
+
+				"#ifdef VERTEX_TEXTURES",
+
+					"if ( enableDisplacement ) {",
+
+						"vec3 dv = texture2D( tDisplacement, uv ).xyz;",
+						"float df = uDisplacementScale * dv.x + uDisplacementBias;",
+						"displacedPosition = position + normalize( normal ) * df;",
+
+					"} else {",
+
+						"#ifdef USE_SKINNING",
+
+							"vec4 skinVertex = vec4( position, 1.0 );",
+
+							"vec4 skinned  = boneMatX * skinVertex * skinWeight.x;",
+							"skinned 	  += boneMatY * skinVertex * skinWeight.y;",
+
+							"displacedPosition  = skinned.xyz;",
+
+						"#else",
+
+							"displacedPosition = position;",
+
+						"#endif",
+
+					"}",
+
+				"#else",
+
+					"#ifdef USE_SKINNING",
+
+						"vec4 skinVertex = vec4( position, 1.0 );",
+
+						"vec4 skinned  = boneMatX * skinVertex * skinWeight.x;",
+						"skinned 	  += boneMatY * skinVertex * skinWeight.y;",
+
+						"displacedPosition  = skinned.xyz;",
+
+					"#else",
+
+						"displacedPosition = position;",
+
+					"#endif",
+
+				"#endif",
+
+				//
+
+				"vec4 mvPosition = modelViewMatrix * vec4( displacedPosition, 1.0 );",
+				"vec4 worldPosition = modelMatrix * vec4( displacedPosition, 1.0 );",
+
+				"gl_Position = projectionMatrix * mvPosition;",
+
+				//
+
+				"vWorldPosition = worldPosition.xyz;",
+				"vViewPosition = -mvPosition.xyz;",
+
+				// shadows
+
+				"#ifdef USE_SHADOWMAP",
+
+					"for( int i = 0; i < MAX_SHADOWS; i ++ ) {",
+
+						"vShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;",
+
+					"}",
+
+				"#endif",
+
+			"}"
+
+		].join("\n")
+
+	},
+
+	/* -------------------------------------------------------------------------
+	//	Cube map shader
+	 ------------------------------------------------------------------------- */
+
+	'cube': {
+
+		uniforms: { "tCube": { type: "t", value: null },
+					"tFlip": { type: "f", value: -1 } },
+
+		vertexShader: [
+
+			"varying vec3 vWorldPosition;",
+
+			"void main() {",
+
+				"vec4 worldPosition = modelMatrix * vec4( position, 1.0 );",
+				"vWorldPosition = worldPosition.xyz;",
+
+				"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+
+			"}"
+
+		].join("\n"),
+
+		fragmentShader: [
+
+			"uniform samplerCube tCube;",
+			"uniform float tFlip;",
+
+			"varying vec3 vWorldPosition;",
+
+			"void main() {",
+
+				"gl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );",
+
+			"}"
+
+		].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 here:
+	//		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" ],
+
+			"void main() {",
+
+				THREE.ShaderChunk[ "skinbase_vertex" ],
+				THREE.ShaderChunk[ "morphtarget_vertex" ],
+				THREE.ShaderChunk[ "skinning_vertex" ],
+				THREE.ShaderChunk[ "default_vertex" ],
+
+			"}"
+
+		].join("\n"),
+
+		fragmentShader: [
+
+			"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 = fract( depth * bit_shift );",
+				"res -= res.xxyz * bit_mask;",
+				"return res;",
+
+			"}",
+
+			"void main() {",
+
+				"gl_FragData[ 0 ] = pack_depth( gl_FragCoord.z );",
+
+				//"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")
+
+	}
+
+};
+
+/**
+ * @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' ),
+
+	_precision = parameters.precision !== undefined ? parameters.precision : 'highp',
+
+	_alpha = parameters.alpha !== undefined ? parameters.alpha : true,
+	_premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true,
+	_antialias = parameters.antialias !== undefined ? parameters.antialias : false,
+	_stencil = parameters.stencil !== undefined ? parameters.stencil : true,
+	_preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false,
+
+	_clearColor = new THREE.Color( 0x000000 ),
+	_clearAlpha = 0;
+
+	// public properties
+
+	this.domElement = _canvas;
+	this.context = null;
+	this.devicePixelRatio = parameters.devicePixelRatio !== undefined
+				? parameters.devicePixelRatio
+				: self.devicePixelRatio !== undefined
+					? self.devicePixelRatio
+					: 1;
+
+	// clearing
+
+	this.autoClear = true;
+	this.autoClearColor = true;
+	this.autoClearDepth = true;
+	this.autoClearStencil = true;
+
+	// scene graph
+
+	this.sortObjects = true;
+	this.autoUpdateObjects = true;
+
+	// physically based shading
+
+	this.gammaInput = false;
+	this.gammaOutput = false;
+	this.physicallyBasedShading = false;
+
+	// shadow map
+
+	this.shadowMapEnabled = false;
+	this.shadowMapAutoUpdate = true;
+	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;
+
+	// custom render plugins
+
+	this.renderPluginsPre = [];
+	this.renderPluginsPost = [];
+
+	// 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 = [],
+	_programs_counter = 0,
+
+	// internal state cache
+
+	_currentProgram = null,
+	_currentFramebuffer = null,
+	_currentMaterialId = -1,
+	_currentGeometryGroupHash = null,
+	_currentCamera = null,
+	_geometryGroupCounter = 0,
+
+	_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,
+
+	_enabledAttributes = {},
+
+	// 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: new Array(), positions: new Array() },
+		point: { length: 0, colors: new Array(), positions: new Array(), distances: new Array() },
+		spot: { length: 0, colors: new Array(), positions: new Array(), distances: new Array(), directions: new Array(), anglesCos: new Array(), exponents: new Array() },
+		hemi: { length: 0, skyColors: new Array(), groundColors: new Array(), positions: new Array() }
+
+	};
+
+	// initialize
+
+	var _gl;
+
+	var _glExtensionTextureFloat;
+	var _glExtensionTextureFloatLinear;
+	var _glExtensionStandardDerivatives;
+	var _glExtensionTextureFilterAnisotropic;
+	var _glExtensionCompressedTextureS3TC;
+
+	initGL();
+
+	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 _maxAnisotropy = _glExtensionTextureFilterAnisotropic ? _gl.getParameter( _glExtensionTextureFilterAnisotropic.MAX_TEXTURE_MAX_ANISOTROPY_EXT ) : 0;
+
+	var _supportsVertexTextures = ( _maxVertexTextures > 0 );
+	var _supportsBoneTextures = _supportsVertexTextures && _glExtensionTextureFloat;
+
+	var _compressedTextureFormats = _glExtensionCompressedTextureS3TC ? _gl.getParameter( _gl.COMPRESSED_TEXTURE_FORMATS ) : [];
+
+	//
+
+	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 _vertexShaderPrecisionHighpInt = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.HIGH_INT );
+	var _vertexShaderPrecisionMediumpInt = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.MEDIUM_INT );
+	var _vertexShaderPrecisionLowpInt = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.LOW_INT );
+
+	var _fragmentShaderPrecisionHighpInt = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.HIGH_INT );
+	var _fragmentShaderPrecisionMediumpInt = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.MEDIUM_INT );
+	var _fragmentShaderPrecisionLowpInt = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.LOW_INT );
+
+	// 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( "WebGLRenderer: highp not supported, using mediump" );
+
+		} else {
+
+			_precision = "lowp";
+			console.warn( "WebGLRenderer: highp and mediump not supported, using lowp" );
+
+		}
+
+	}
+
+	if ( _precision === "mediump" && ! mediumpAvailable ) {
+
+		_precision = "lowp";
+		console.warn( "WebGLRenderer: mediump not supported, using lowp" );
+
+	}
+
+	// API
+
+	this.getContext = function () {
+
+		return _gl;
+
+	};
+
+	this.supportsVertexTextures = function () {
+
+		return _supportsVertexTextures;
+
+	};
+
+	this.supportsFloatTextures = function () {
+
+		return _glExtensionTextureFloat;
+
+	};
+
+	this.supportsStandardDerivatives = function () {
+
+		return _glExtensionStandardDerivatives;
+
+	};
+
+	this.supportsCompressedTextureS3TC = function () {
+
+		return _glExtensionCompressedTextureS3TC;
+
+	};
+
+	this.getMaxAnisotropy  = function () {
+
+		return _maxAnisotropy;
+
+	};
+
+	this.getPrecision = function () {
+
+		return _precision;
+
+	};
+
+	this.setSize = function ( width, height, updateStyle ) {
+
+		_canvas.width = width * this.devicePixelRatio;
+		_canvas.height = height * this.devicePixelRatio;
+
+		if ( this.devicePixelRatio !== 1 && updateStyle !== false ) {
+
+			_canvas.style.width = width + 'px';
+			_canvas.style.height = height + 'px';
+
+		}
+
+		this.setViewport( 0, 0, _canvas.width, _canvas.height );
+
+	};
+
+	this.setViewport = function ( x, y, width, height ) {
+
+		_viewportX = x !== undefined ? x : 0;
+		_viewportY = y !== undefined ? y : 0;
+
+		_viewportWidth = width !== undefined ? width : _canvas.width;
+		_viewportHeight = height !== undefined ? height : _canvas.height;
+
+		_gl.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight );
+
+	};
+
+	this.setScissor = function ( x, y, width, height ) {
+
+		_gl.scissor( x, y, width, height );
+
+	};
+
+	this.enableScissorTest = function ( enable ) {
+
+		enable ? _gl.enable( _gl.SCISSOR_TEST ) : _gl.disable( _gl.SCISSOR_TEST );
+
+	};
+
+	// Clearing
+
+	this.setClearColor = function ( color, alpha ) {
+
+		_clearColor.set( color );
+		_clearAlpha = alpha !== undefined ? alpha : 1;
+
+		_gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha );
+
+	};
+
+	this.setClearColorHex = function ( hex, alpha ) {
+
+		console.warn( 'DEPRECATED: .setClearColorHex() is being removed. Use .setClearColor() instead.' );
+		this.setClearColor( hex, alpha );
+
+	};
+
+	this.getClearColor = function () {
+
+		return _clearColor;
+
+	};
+
+	this.getClearAlpha = function () {
+
+		return _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.clearTarget = function ( renderTarget, color, depth, stencil ) {
+
+		this.setRenderTarget( renderTarget );
+		this.clear( color, depth, stencil );
+
+	};
+
+	// Plugins
+
+	this.addPostPlugin = function ( plugin ) {
+
+		plugin.init( this );
+		this.renderPluginsPost.push( plugin );
+
+	};
+
+	this.addPrePlugin = function ( plugin ) {
+
+		plugin.init( this );
+		this.renderPluginsPre.push( plugin );
+
+	};
+
+	// Rendering
+
+	this.updateShadowMap = function ( scene, camera ) {
+
+		_currentProgram = null;
+		_oldBlending = -1;
+		_oldDepthTest = -1;
+		_oldDepthWrite = -1;
+		_currentGeometryGroupHash = -1;
+		_currentMaterialId = -1;
+		_lightsNeedUpdate = true;
+		_oldDoubleSided = -1;
+		_oldFlipSided = -1;
+
+		this.shadowMapPlugin.update( scene, camera );
+
+	};
+
+	// Internal functions
+
+	// 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 m, ml;
+
+		if ( geometryGroup.numMorphTargets ) {
+
+			geometryGroup.__webglMorphTargetsBuffers = [];
+
+			for ( m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) {
+
+				geometryGroup.__webglMorphTargetsBuffers.push( _gl.createBuffer() );
+
+			}
+
+		}
+
+		if ( geometryGroup.numMorphNormals ) {
+
+			geometryGroup.__webglMorphNormalsBuffers = [];
+
+			for ( m = 0, ml = geometryGroup.numMorphNormals; m < ml; m ++ ) {
+
+				geometryGroup.__webglMorphNormalsBuffers.push( _gl.createBuffer() );
+
+			}
+
+		}
+
+		_this.info.memory.geometries ++;
+
+	};
+
+	// Events
+
+	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 ) {
+
+		if ( geometry.__webglVertexBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglVertexBuffer );
+		if ( geometry.__webglNormalBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglNormalBuffer );
+		if ( geometry.__webglTangentBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglTangentBuffer );
+		if ( geometry.__webglColorBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglColorBuffer );
+		if ( geometry.__webglUVBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglUVBuffer );
+		if ( geometry.__webglUV2Buffer !== undefined ) _gl.deleteBuffer( geometry.__webglUV2Buffer );
+
+		if ( geometry.__webglSkinIndicesBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglSkinIndicesBuffer );
+		if ( geometry.__webglSkinWeightsBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglSkinWeightsBuffer );
+
+		if ( geometry.__webglFaceBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglFaceBuffer );
+		if ( geometry.__webglLineBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglLineBuffer );
+
+		if ( geometry.__webglLineDistanceBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglLineDistanceBuffer );
+		// custom attributes
+
+		if ( geometry.__webglCustomAttributesList !== undefined ) {
+
+			for ( var id in geometry.__webglCustomAttributesList ) {
+
+				_gl.deleteBuffer( geometry.__webglCustomAttributesList[ id ].buffer );
+
+			}
+
+		}
+
+		_this.info.memory.geometries --;
+
+	};
+
+	var deallocateGeometry = function ( geometry ) {
+
+		geometry.__webglInit = undefined;
+
+		if ( geometry instanceof THREE.BufferGeometry ) {
+
+			var attributes = geometry.attributes;
+
+			for ( var key in attributes ) {
+
+				if ( attributes[ key ].buffer !== undefined ) {
+
+					_gl.deleteBuffer( attributes[ key ].buffer );
+
+				}
+
+			}
+
+			_this.info.memory.geometries --;
+
+		} else {
+
+			if ( geometry.geometryGroups !== undefined ) {
+
+				for ( var g in geometry.geometryGroups ) {
+
+					var geometryGroup = geometry.geometryGroups[ g ];
+
+					if ( geometryGroup.numMorphTargets !== undefined ) {
+
+						for ( var m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) {
+
+							_gl.deleteBuffer( geometryGroup.__webglMorphTargetsBuffers[ m ] );
+
+						}
+
+					}
+
+					if ( geometryGroup.numMorphNormals !== undefined ) {
+
+						for ( var m = 0, ml = geometryGroup.numMorphNormals; m < ml; m ++ ) {
+
+							_gl.deleteBuffer( geometryGroup.__webglMorphNormalsBuffers[ m ] );
+
+						}
+
+					}
+
+					deleteBuffers( geometryGroup );
+
+				}
+
+			} else {
+
+				deleteBuffers( geometry );
+
+			}
+
+		}
+
+	};
+
+	var deallocateTexture = function ( texture ) {
+
+		if ( texture.image && texture.image.__webglTextureCube ) {
+
+			// cube texture
+
+			_gl.deleteTexture( texture.image.__webglTextureCube );
+
+		} else {
+
+			// 2D texture
+
+			if ( ! texture.__webglInit ) return;
+
+			texture.__webglInit = false;
+			_gl.deleteTexture( texture.__webglTexture );
+
+		}
+
+	};
+
+	var deallocateRenderTarget = function ( renderTarget ) {
+
+		if ( !renderTarget || ! renderTarget.__webglTexture ) return;
+
+		_gl.deleteTexture( 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 );
+
+		}
+
+	};
+
+	var deallocateMaterial = function ( material ) {
+
+		var program = material.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 ( geometry, object ) {
+
+		var nvertices = geometry.vertices.length;
+
+		var material = object.material;
+
+		if ( material.attributes ) {
+
+			if ( geometry.__webglCustomAttributesList === undefined ) {
+
+				geometry.__webglCustomAttributesList = [];
+
+			}
+
+			for ( var a in material.attributes ) {
+
+				var attribute = material.attributes[ a ];
+
+				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 = a;
+
+					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 ( geometry, 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 ( geometry, 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 ),
+
+			uvType = bufferGuessUVType( material ),
+			normalType = bufferGuessNormalType( material ),
+			vertexColorType = bufferGuessVertexColorType( material );
+
+		// console.log( "uvType", uvType, "normalType", normalType, "vertexColorType", vertexColorType, object, geometryGroup, material );
+
+		geometryGroup.__vertexArray = new Float32Array( nvertices * 3 );
+
+		if ( normalType ) {
+
+			geometryGroup.__normalArray = new Float32Array( nvertices * 3 );
+
+		}
+
+		if ( geometry.hasTangents ) {
+
+			geometryGroup.__tangentArray = new Float32Array( nvertices * 4 );
+
+		}
+
+		if ( vertexColorType ) {
+
+			geometryGroup.__colorArray = new Float32Array( nvertices * 3 );
+
+		}
+
+		if ( uvType ) {
+
+			if ( geometry.faceVertexUvs.length > 0 ) {
+
+				geometryGroup.__uvArray = new Float32Array( nvertices * 2 );
+
+			}
+
+			if ( geometry.faceVertexUvs.length > 1 ) {
+
+				geometryGroup.__uv2Array = new Float32Array( nvertices * 2 );
+
+			}
+
+		}
+
+		if ( object.geometry.skinWeights.length && object.geometry.skinIndices.length ) {
+
+			geometryGroup.__skinIndexArray = new Float32Array( nvertices * 4 );
+			geometryGroup.__skinWeightArray = new Float32Array( nvertices * 4 );
+
+		}
+
+		geometryGroup.__faceArray = new Uint16Array( ntris * 3 );
+		geometryGroup.__lineArray = new Uint16Array( nlines * 2 );
+
+		var m, ml;
+
+		if ( geometryGroup.numMorphTargets ) {
+
+			geometryGroup.__morphTargetsArrays = [];
+
+			for ( m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) {
+
+				geometryGroup.__morphTargetsArrays.push( new Float32Array( nvertices * 3 ) );
+
+			}
+
+		}
+
+		if ( geometryGroup.numMorphNormals ) {
+
+			geometryGroup.__morphNormalsArrays = [];
+
+			for ( m = 0, ml = geometryGroup.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 a 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[ a ];
+
+				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 = a;
+
+					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;
+
+	};
+
+	function bufferGuessNormalType ( material ) {
+
+		// only MeshBasicMaterial and MeshDepthMaterial don't need normals
+
+		if ( ( material instanceof THREE.MeshBasicMaterial && !material.envMap ) || material instanceof THREE.MeshDepthMaterial ) {
+
+			return false;
+
+		}
+
+		if ( materialNeedsSmoothNormals( material ) ) {
+
+			return THREE.SmoothShading;
+
+		} else {
+
+			return THREE.FlatShading;
+
+		}
+
+	};
+
+	function bufferGuessVertexColorType( material ) {
+
+		if ( material.vertexColors ) {
+
+			return material.vertexColors;
+
+		}
+
+		return false;
+
+	};
+
+	function bufferGuessUVType( material ) {
+
+		// material must use some texture to require uvs
+
+		if ( material.map ||
+		     material.lightMap ||
+		     material.bumpMap ||
+		     material.normalMap ||
+		     material.specularMap ||
+		     material instanceof THREE.ShaderMaterial ) {
+
+			return true;
+
+		}
+
+		return false;
+
+	};
+
+	//
+
+	function initDirectBuffers( geometry ) {
+
+		var a, attribute, type;
+
+		for ( a in geometry.attributes ) {
+
+			if ( a === "index" ) {
+
+				type = _gl.ELEMENT_ARRAY_BUFFER;
+
+			} else {
+
+				type = _gl.ARRAY_BUFFER;
+
+			}
+
+			attribute = geometry.attributes[ a ];
+
+			if ( attribute.numItems === undefined ) {
+
+				attribute.numItems = attribute.array.length;
+
+			}
+
+			attribute.buffer = _gl.createBuffer();
+
+			_gl.bindBuffer( type, attribute.buffer );
+			_gl.bufferData( type, attribute.array, _gl.STATIC_DRAW );
+
+		}
+
+	};
+
+	// 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 ( object.sortParticles ) {
+
+			_projScreenMatrixPS.copy( _projScreenMatrix );
+			_projScreenMatrixPS.multiply( object.matrixWorld );
+
+			for ( v = 0; v < vl; v ++ ) {
+
+				vertex = vertices[ v ];
+
+				_vector3.copy( vertex );
+				_vector3.applyProjection( _projScreenMatrixPS );
+
+				sortArray[ v ] = [ _vector3.z, v ];
+
+			}
+
+			sortArray.sort( numericalSort );
+
+			for ( v = 0; v < vl; v ++ ) {
+
+				vertex = vertices[ sortArray[v][1] ];
+
+				offset = v * 3;
+
+				vertexArray[ offset ]     = vertex.x;
+				vertexArray[ offset + 1 ] = vertex.y;
+				vertexArray[ offset + 2 ] = vertex.z;
+
+			}
+
+			for ( c = 0; c < cl; c ++ ) {
+
+				offset = c * 3;
+
+				color = colors[ sortArray[c][1] ];
+
+				colorArray[ offset ]     = color.r;
+				colorArray[ offset + 1 ] = color.g;
+				colorArray[ offset + 2 ] = color.b;
+
+			}
+
+			if ( customAttributes ) {
+
+				for ( i = 0, il = customAttributes.length; i < il; i ++ ) {
+
+					customAttribute = customAttributes[ i ];
+
+					if ( ! ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) ) continue;
+
+					offset = 0;
+
+					cal = customAttribute.value.length;
+
+					if ( customAttribute.size === 1 ) {
+
+						for ( ca = 0; ca < cal; ca ++ ) {
+
+							index = sortArray[ ca ][ 1 ];
+
+							customAttribute.array[ ca ] = customAttribute.value[ index ];
+
+						}
+
+					} else if ( customAttribute.size === 2 ) {
+
+						for ( ca = 0; ca < cal; ca ++ ) {
+
+							index = sortArray[ ca ][ 1 ];
+
+							value = customAttribute.value[ index ];
+
+							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 ++ ) {
+
+								index = sortArray[ ca ][ 1 ];
+
+								value = customAttribute.value[ index ];
+
+								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 ++ ) {
+
+								index = sortArray[ ca ][ 1 ];
+
+								value = customAttribute.value[ index ];
+
+								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 ++ ) {
+
+							index = sortArray[ ca ][ 1 ];
+
+							value = customAttribute.value[ index ];
+
+							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;
+
+						}
+
+					}
+
+				}
+
+			}
+
+		} else {
+
+			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;
+
+				}
+
+			}
+
+			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;
+
+				}
+
+			}
+
+			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;
+
+							}
+
+						}
+
+					}
+
+				}
+
+			}
+
+		}
+
+		if ( dirtyVertices || object.sortParticles ) {
+
+			_gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglVertexBuffer );
+			_gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint );
+
+		}
+
+		if ( dirtyColors || object.sortParticles ) {
+
+			_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 || object.sortParticles ) {
+
+					_gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer );
+					_gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint );
+
+				}
+
+			}
+
+		}
+
+
+	};
+
+	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 );
+
+				}
+
+			}
+
+		}
+
+	};
+
+	function setMeshBuffers( geometryGroup, object, hint, dispose, material ) {
+
+		if ( ! geometryGroup.__inittedArrays ) {
+
+			return;
+
+		}
+
+		var normalType = bufferGuessNormalType( material ),
+		vertexColorType = bufferGuessVertexColorType( material ),
+		uvType = bufferGuessUVType( material ),
+
+		needsSmoothNormals = ( normalType === THREE.SmoothShading );
+
+		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, c4,
+		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 && vertexColorType ) {
+
+			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 && vertexColorType === 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 && normalType ) {
+
+			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 && uvType ) {
+
+			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 && uvType ) {
+
+			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;
+
+		}
+
+	};
+
+	function setDirectBuffers ( geometry, hint, dispose ) {
+
+		var attributes = geometry.attributes;
+
+		var attributeName, attributeItem;
+
+		for ( attributeName in attributes ) {
+
+			attributeItem = attributes[ attributeName ];
+
+			if ( attributeItem.needsUpdate ) {
+
+				if ( attributeName === 'index' ) {
+
+					_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, attributeItem.buffer );
+					_gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, attributeItem.array, hint );
+
+				} else {
+
+					_gl.bindBuffer( _gl.ARRAY_BUFFER, attributeItem.buffer );
+					_gl.bufferData( _gl.ARRAY_BUFFER, attributeItem.array, hint );
+
+				}
+
+				attributeItem.needsUpdate = false;
+
+			}
+
+			if ( dispose && ! attributeItem.dynamic ) {
+
+				attributeItem.array = null;
+
+			}
+
+		}
+
+	};
+
+	// Buffer rendering
+
+	this.renderBufferImmediate = function ( object, program, material ) {
+
+		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 );
+			_gl.enableVertexAttribArray( 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 );
+			_gl.enableVertexAttribArray( 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 );
+			_gl.enableVertexAttribArray( 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 );
+			_gl.enableVertexAttribArray( program.attributes.color );
+			_gl.vertexAttribPointer( program.attributes.color, 3, _gl.FLOAT, false, 0, 0 );
+
+		}
+
+		_gl.drawArrays( _gl.TRIANGLES, 0, object.count );
+
+		object.count = 0;
+
+	};
+
+	this.renderBufferDirect = function ( camera, lights, fog, material, geometry, object ) {
+
+		if ( material.visible === false ) return;
+
+		var linewidth, a, attribute;
+		var attributeItem, attributeName, attributePointer, attributeSize;
+
+		var program = setProgram( camera, lights, fog, material, object );
+
+		var programAttributes = program.attributes;
+		var geometryAttributes = geometry.attributes;
+
+		var updateBuffers = false,
+			wireframeBit = material.wireframe ? 1 : 0,
+			geometryHash = ( geometry.id * 0xffffff ) + ( program.id * 2 ) + wireframeBit;
+
+		if ( geometryHash !== _currentGeometryGroupHash ) {
+
+			_currentGeometryGroupHash = geometryHash;
+			updateBuffers = true;
+
+		}
+
+		if ( updateBuffers ) {
+
+			disableAttributes();
+
+		}
+
+		// render mesh
+
+		if ( object instanceof THREE.Mesh ) {
+
+			var index = geometryAttributes[ "index" ];
+
+			// indexed triangles
+
+			if ( index ) {
+
+				var offsets = geometry.offsets;
+
+				// 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 ) {
+
+						for ( attributeName in programAttributes ) {
+
+							attributePointer = programAttributes[ attributeName ];
+							attributeItem = geometryAttributes[ attributeName ];
+
+							if ( attributePointer >= 0 ) {
+
+								if ( attributeItem ) {
+
+									attributeSize = attributeItem.itemSize;
+									_gl.bindBuffer( _gl.ARRAY_BUFFER, attributeItem.buffer );
+									enableAttribute( attributePointer );
+									_gl.vertexAttribPointer( attributePointer, attributeSize, _gl.FLOAT, false, 0, startIndex * attributeSize * 4 ); // 4 bytes per Float32
+
+								} else if ( material.defaultAttributeValues ) {
+
+									if ( material.defaultAttributeValues[ attributeName ].length === 2 ) {
+
+										_gl.vertexAttrib2fv( attributePointer, material.defaultAttributeValues[ attributeName ] );
+
+									} else if ( material.defaultAttributeValues[ attributeName ].length === 3 ) {
+
+										_gl.vertexAttrib3fv( attributePointer, material.defaultAttributeValues[ attributeName ] );
+
+									}
+
+								}
+
+							}
+
+						}
+
+						// indices
+
+						_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer );
+
+					}
+
+					// render indexed triangles
+
+					_gl.drawElements( _gl.TRIANGLES, offsets[ i ].count, _gl.UNSIGNED_SHORT, offsets[ i ].start * 2 ); // 2 bytes per Uint16
+
+					_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;
+
+				}
+
+			// non-indexed triangles
+
+			} else {
+
+				if ( updateBuffers ) {
+
+					for ( attributeName in programAttributes ) {
+
+						if ( attributeName === 'index') continue;
+
+						attributePointer = programAttributes[ attributeName ];
+						attributeItem = geometryAttributes[ attributeName ];
+
+						if ( attributePointer >= 0 ) {
+
+							if ( attributeItem ) {
+
+								attributeSize = attributeItem.itemSize;
+								_gl.bindBuffer( _gl.ARRAY_BUFFER, attributeItem.buffer );
+								enableAttribute( attributePointer );
+								_gl.vertexAttribPointer( attributePointer, attributeSize, _gl.FLOAT, false, 0, 0 );
+
+							} else if ( material.defaultAttributeValues && material.defaultAttributeValues[ attributeName ] ) {
+
+								if ( material.defaultAttributeValues[ attributeName ].length === 2 ) {
+
+									_gl.vertexAttrib2fv( attributePointer, material.defaultAttributeValues[ attributeName ] );
+
+								} else if ( material.defaultAttributeValues[ attributeName ].length === 3 ) {
+
+									_gl.vertexAttrib3fv( attributePointer, material.defaultAttributeValues[ attributeName ] );
+
+								}
+
+							}
+
+						}
+
+					}
+
+				}
+
+				var position = geometry.attributes[ "position" ];
+
+				// render non-indexed triangles
+
+				_gl.drawArrays( _gl.TRIANGLES, 0, position.numItems / 3 );
+
+				_this.info.render.calls ++;
+				_this.info.render.vertices += position.numItems / 3;
+				_this.info.render.faces += position.numItems / 3 / 3;
+
+			}
+
+		// render particles
+
+		} else if ( object instanceof THREE.ParticleSystem ) {
+
+			if ( updateBuffers ) {
+
+				for ( attributeName in programAttributes ) {
+
+					attributePointer = programAttributes[ attributeName ];
+					attributeItem = geometryAttributes[ attributeName ];
+
+					if ( attributePointer >= 0 ) {
+
+						if ( attributeItem ) {
+
+							attributeSize = attributeItem.itemSize;
+							_gl.bindBuffer( _gl.ARRAY_BUFFER, attributeItem.buffer );
+							enableAttribute( attributePointer );
+							_gl.vertexAttribPointer( attributePointer, attributeSize, _gl.FLOAT, false, 0, 0 );
+
+						} else if ( material.defaultAttributeValues && material.defaultAttributeValues[ attributeName ] ) {
+
+							if ( material.defaultAttributeValues[ attributeName ].length === 2 ) {
+
+								_gl.vertexAttrib2fv( attributePointer, material.defaultAttributeValues[ attributeName ] );
+
+							} else if ( material.defaultAttributeValues[ attributeName ].length === 3 ) {
+
+								_gl.vertexAttrib3fv( attributePointer, material.defaultAttributeValues[ attributeName ] );
+
+							}
+
+						}
+
+					}
+
+				}
+
+				var position = geometryAttributes[ "position" ];
+
+				// render particles
+
+				_gl.drawArrays( _gl.POINTS, 0, position.numItems / 3 );
+
+				_this.info.render.calls ++;
+				_this.info.render.points += position.numItems / 3;
+
+			}
+
+		} else if ( object instanceof THREE.Line ) {
+
+			if ( updateBuffers ) {
+
+				for ( attributeName in programAttributes ) {
+
+					attributePointer = programAttributes[ attributeName ];
+					attributeItem = geometryAttributes[ attributeName ];
+
+					if ( attributePointer >= 0 ) {
+
+						if ( attributeItem ) {
+
+							attributeSize = attributeItem.itemSize;
+							_gl.bindBuffer( _gl.ARRAY_BUFFER, attributeItem.buffer );
+							enableAttribute( attributePointer );
+							_gl.vertexAttribPointer( attributePointer, attributeSize, _gl.FLOAT, false, 0, 0 );
+
+						} else if ( material.defaultAttributeValues && material.defaultAttributeValues[ attributeName ] ) {
+
+							if ( material.defaultAttributeValues[ attributeName ].length === 2 ) {
+
+								_gl.vertexAttrib2fv( attributePointer, material.defaultAttributeValues[ attributeName ] );
+
+							} else if ( material.defaultAttributeValues[ attributeName ].length === 3 ) {
+
+								_gl.vertexAttrib3fv( attributePointer, material.defaultAttributeValues[ attributeName ] );
+
+							}
+
+						}
+
+					}
+
+				}
+
+				// render lines
+
+				var primitives = ( object.type === THREE.LineStrip ) ? _gl.LINE_STRIP : _gl.LINES;
+
+				setLineWidth( material.linewidth );
+
+				var position = geometryAttributes[ "position" ];
+
+				_gl.drawArrays( primitives, 0, position.numItems / 3 );
+
+				_this.info.render.calls ++;
+				_this.info.render.points += position.numItems;
+
+			}
+
+    	}
+
+	};
+
+	this.renderBuffer = function ( camera, lights, fog, material, geometryGroup, object ) {
+
+		if ( material.visible === false ) return;
+
+		var linewidth, a, attribute, i, il;
+
+		var program = setProgram( camera, lights, fog, material, object );
+
+		var attributes = program.attributes;
+
+		var updateBuffers = false,
+			wireframeBit = material.wireframe ? 1 : 0,
+			geometryGroupHash = ( geometryGroup.id * 0xffffff ) + ( program.id * 2 ) + wireframeBit;
+
+		if ( geometryGroupHash !== _currentGeometryGroupHash ) {
+
+			_currentGeometryGroupHash = geometryGroupHash;
+			updateBuffers = true;
+
+		}
+
+		if ( updateBuffers ) {
+
+			disableAttributes();
+
+		}
+
+		// 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 ( i = 0, il = geometryGroup.__webglCustomAttributesList.length; i < il; i ++ ) {
+
+					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 ) {
+
+
+					_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 ) {
+
+
+					_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 ) {
+
+
+					_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 );
+
+			}
+
+		}
+
+		// render mesh
+
+		if ( object instanceof THREE.Mesh ) {
+
+			// wireframe
+
+			if ( material.wireframe ) {
+
+				setLineWidth( material.wireframeLinewidth );
+
+				if ( updateBuffers ) _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglLineBuffer );
+				_gl.drawElements( _gl.LINES, geometryGroup.__webglLineCount, _gl.UNSIGNED_SHORT, 0 );
+
+			// triangles
+
+			} else {
+
+				if ( updateBuffers ) _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglFaceBuffer );
+				_gl.drawElements( _gl.TRIANGLES, geometryGroup.__webglFaceCount, _gl.UNSIGNED_SHORT, 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 primitives = ( object.type === THREE.LineStrip ) ? _gl.LINE_STRIP : _gl.LINES;
+
+			setLineWidth( material.linewidth );
+
+			_gl.drawArrays( primitives, 0, geometryGroup.__webglLineCount );
+
+			_this.info.render.calls ++;
+
+		// render particles
+
+		} else if ( object instanceof THREE.ParticleSystem ) {
+
+			_gl.drawArrays( _gl.POINTS, 0, geometryGroup.__webglParticleCount );
+
+			_this.info.render.calls ++;
+			_this.info.render.points += geometryGroup.__webglParticleCount;
+
+		}
+
+	};
+
+	function enableAttribute( attribute ) {
+
+		if ( ! _enabledAttributes[ attribute ] ) {
+
+			_gl.enableVertexAttribArray( attribute );
+			_enabledAttributes[ attribute ] = true;
+
+		}
+
+	};
+
+	function disableAttributes() {
+
+		for ( var attribute in _enabledAttributes ) {
+
+			if ( _enabledAttributes[ attribute ] ) {
+
+				_gl.disableVertexAttribArray( attribute );
+				_enabledAttributes[ attribute ] = false;
+
+			}
+
+		}
+
+	};
+
+	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;
+
+			while ( m < material.numSupportedMorphTargets && m < order.length ) {
+
+				if ( attributes[ "morphTarget" + m ] >= 0 ) {
+
+					_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ order[ m ] ] );
+					enableAttribute( attributes[ "morphTarget" + m ] );
+					_gl.vertexAttribPointer( attributes[ "morphTarget" + m ], 3, _gl.FLOAT, false, 0, 0 );
+
+				}
+
+				if ( attributes[ "morphNormal" + m ] >= 0 && material.morphNormals ) {
+
+					_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphNormalsBuffers[ order[ m ] ] );
+					enableAttribute( attributes[ "morphNormal" + m ] );
+					_gl.vertexAttribPointer( attributes[ "morphNormal" + m ], 3, _gl.FLOAT, false, 0, 0 );
+
+				}
+
+				object.__webglMorphTargetInfluences[ m ] = influences[ order[ m ] ];
+
+				m ++;
+			}
+
+		} else {
+
+			// find the most influencing
+
+			var influence, activeInfluenceIndices = [];
+			var influences = object.morphTargetInfluences;
+			var i, il = influences.length;
+
+			for ( i = 0; i < il; i ++ ) {
+
+				influence = influences[ i ];
+
+				if ( influence > 0 ) {
+
+					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 influenceIndex, m = 0;
+
+			while ( m < material.numSupportedMorphTargets ) {
+
+				if ( activeInfluenceIndices[ m ] ) {
+
+					influenceIndex = activeInfluenceIndices[ m ][ 1 ];
+
+					if ( attributes[ "morphTarget" + m ] >= 0 ) {
+
+						_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ influenceIndex ] );
+						enableAttribute( attributes[ "morphTarget" + m ] );
+						_gl.vertexAttribPointer( attributes[ "morphTarget" + m ], 3, _gl.FLOAT, false, 0, 0 );
+
+					}
+
+					if ( attributes[ "morphNormal" + m ] >= 0 && material.morphNormals ) {
+
+						_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphNormalsBuffers[ influenceIndex ] );
+						enableAttribute( attributes[ "morphNormal" + m ] );
+						_gl.vertexAttribPointer( attributes[ "morphNormal" + m ], 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;
+
+				}
+
+				m ++;
+
+			}
+
+		}
+
+		// 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.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 i, il,
+
+		webglObject, object,
+		renderList,
+
+		lights = scene.__lights,
+		fog = scene.fog;
+
+		// reset caching for this frame
+
+		_currentMaterialId = -1;
+		_lightsNeedUpdate = true;
+
+		// update scene graph
+
+		if ( scene.autoUpdate === true ) scene.updateMatrixWorld();
+
+		// update camera matrices and frustum
+
+		if ( camera.parent === undefined ) camera.updateMatrixWorld();
+
+		camera.matrixWorldInverse.getInverse( camera.matrixWorld );
+
+		_projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );
+		_frustum.setFromMatrix( _projScreenMatrix );
+
+		// update WebGL objects
+
+		if ( this.autoUpdateObjects ) this.initWebGLObjects( scene );
+
+		// custom render plugins (pre pass)
+
+		renderPlugins( this.renderPluginsPre, 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 regular objects (frustum culled)
+
+		renderList = scene.__webglObjects;
+
+		for ( i = 0, il = renderList.length; i < il; i ++ ) {
+
+			webglObject = renderList[ i ];
+			object = webglObject.object;
+
+			webglObject.id = i;
+			webglObject.render = false;
+
+			if ( object.visible ) {
+
+				if ( ! ( object instanceof THREE.Mesh || object instanceof THREE.ParticleSystem ) || ! ( object.frustumCulled ) || _frustum.intersectsObject( object ) ) {
+
+					setupMatrices( object, camera );
+
+					unrollBufferMaterial( webglObject );
+
+					webglObject.render = true;
+
+					if ( this.sortObjects === true ) {
+
+						if ( object.renderDepth !== null ) {
+
+							webglObject.z = object.renderDepth;
+
+						} else {
+
+							_vector3.getPositionFromMatrix( object.matrixWorld );
+							_vector3.applyProjection( _projScreenMatrix );
+
+							webglObject.z = _vector3.z;
+
+						}
+
+					}
+
+				}
+
+			}
+
+		}
+
+		if ( this.sortObjects ) {
+
+			renderList.sort( painterSortStable );
+
+		}
+
+		// set matrices for immediate objects
+
+		renderList = scene.__webglObjectsImmediate;
+
+		for ( i = 0, il = renderList.length; i < il; i ++ ) {
+
+			webglObject = renderList[ i ];
+			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( scene.__webglObjects, false, "", camera, lights, fog, true, material );
+			renderObjectsImmediate( scene.__webglObjectsImmediate, "", camera, lights, fog, false, material );
+
+		} else {
+
+			var material = null;
+
+			// opaque pass (front-to-back order)
+
+			this.setBlending( THREE.NoBlending );
+
+			renderObjects( scene.__webglObjects, true, "opaque", camera, lights, fog, false, material );
+			renderObjectsImmediate( scene.__webglObjectsImmediate, "opaque", camera, lights, fog, false, material );
+
+			// transparent pass (back-to-front order)
+
+			renderObjects( scene.__webglObjects, false, "transparent", camera, lights, fog, true, material );
+			renderObjectsImmediate( scene.__webglObjectsImmediate, "transparent", camera, lights, fog, true, material );
+
+		}
+
+		// custom render plugins (post pass)
+
+		renderPlugins( this.renderPluginsPost, scene, camera );
+
+
+		// 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 renderPlugins( plugins, scene, camera ) {
+
+		if ( ! plugins.length ) return;
+
+		for ( var i = 0, il = plugins.length; i < il; i ++ ) {
+
+			// reset state for plugin (to start from clean slate)
+
+			_currentProgram = null;
+			_currentCamera = null;
+
+			_oldBlending = -1;
+			_oldDepthTest = -1;
+			_oldDepthWrite = -1;
+			_oldDoubleSided = -1;
+			_oldFlipSided = -1;
+			_currentGeometryGroupHash = -1;
+			_currentMaterialId = -1;
+
+			_lightsNeedUpdate = true;
+
+			plugins[ i ].render( scene, camera, _currentWidth, _currentHeight );
+
+			// reset state after plugin (anything could have changed)
+
+			_currentProgram = null;
+			_currentCamera = null;
+
+			_oldBlending = -1;
+			_oldDepthTest = -1;
+			_oldDepthWrite = -1;
+			_oldDoubleSided = -1;
+			_oldFlipSided = -1;
+			_currentGeometryGroupHash = -1;
+			_currentMaterialId = -1;
+
+			_lightsNeedUpdate = true;
+
+		}
+
+	};
+
+	function renderObjects ( renderList, reverse, materialType, camera, lights, fog, useBlending, overrideMaterial ) {
+
+		var webglObject, object, buffer, material, start, end, delta;
+
+		if ( reverse ) {
+
+			start = renderList.length - 1;
+			end = -1;
+			delta = -1;
+
+		} else {
+
+			start = 0;
+			end = renderList.length;
+			delta = 1;
+		}
+
+		for ( var i = start; i !== end; i += delta ) {
+
+			webglObject = renderList[ i ];
+
+			if ( webglObject.render ) {
+
+				object = webglObject.object;
+				buffer = webglObject.buffer;
+
+				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.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 webglObject, object, material, program;
+
+		for ( var i = 0, il = renderList.length; i < il; i ++ ) {
+
+			webglObject = renderList[ i ];
+			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 );
+
+		_currentGeometryGroupHash = -1;
+
+		_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,
+			buffer = globject.buffer,
+			material, materialIndex, meshMaterial;
+
+		meshMaterial = object.material;
+
+		if ( meshMaterial instanceof THREE.MeshFaceMaterial ) {
+
+			materialIndex = buffer.materialIndex;
+
+			material = meshMaterial.materials[ materialIndex ];
+
+			if ( material.transparent ) {
+
+				globject.transparent = material;
+				globject.opaque = null;
+
+			} else {
+
+				globject.opaque = material;
+				globject.transparent = null;
+
+			}
+
+		} else {
+
+			material = meshMaterial;
+
+			if ( material ) {
+
+				if ( material.transparent ) {
+
+					globject.transparent = material;
+					globject.opaque = null;
+
+				} else {
+
+					globject.opaque = material;
+					globject.transparent = null;
+
+				}
+
+			}
+
+		}
+
+	};
+
+	// Geometry splitting
+
+	function sortFacesByMaterial ( geometry, material ) {
+
+		var f, fl, face, materialIndex, vertices,
+			groupHash, hash_map = {};
+
+		var numMorphTargets = geometry.morphTargets.length;
+		var numMorphNormals = geometry.morphNormals.length;
+
+		var usesFaceMaterial = material instanceof THREE.MeshFaceMaterial;
+
+		geometry.geometryGroups = {};
+
+		for ( f = 0, fl = geometry.faces.length; f < fl; f ++ ) {
+
+			face = geometry.faces[ f ];
+			materialIndex = usesFaceMaterial ? face.materialIndex : 0;
+
+			if ( hash_map[ materialIndex ] === undefined ) {
+
+				hash_map[ materialIndex ] = { 'hash': materialIndex, 'counter': 0 };
+
+			}
+
+			groupHash = hash_map[ materialIndex ].hash + '_' + hash_map[ materialIndex ].counter;
+
+			if ( geometry.geometryGroups[ groupHash ] === undefined ) {
+
+				geometry.geometryGroups[ groupHash ] = { 'faces3': [], 'materialIndex': materialIndex, 'vertices': 0, 'numMorphTargets': numMorphTargets, 'numMorphNormals': numMorphNormals };
+
+			}
+
+			vertices = 3;
+
+			if ( geometry.geometryGroups[ groupHash ].vertices + vertices > 65535 ) {
+
+				hash_map[ materialIndex ].counter += 1;
+				groupHash = hash_map[ materialIndex ].hash + '_' + hash_map[ materialIndex ].counter;
+
+				if ( geometry.geometryGroups[ groupHash ] === undefined ) {
+
+					geometry.geometryGroups[ groupHash ] = { 'faces3': [], 'materialIndex': materialIndex, 'vertices': 0, 'numMorphTargets': numMorphTargets, 'numMorphNormals': numMorphNormals };
+
+				}
+
+			}
+
+			geometry.geometryGroups[ groupHash ].faces3.push( f );
+			geometry.geometryGroups[ groupHash ].vertices += vertices;
+
+		}
+
+		geometry.geometryGroupsList = [];
+
+		for ( var g in geometry.geometryGroups ) {
+
+			geometry.geometryGroups[ g ].id = _geometryGroupCounter ++;
+
+			geometry.geometryGroupsList.push( geometry.geometryGroups[ g ] );
+
+		}
+
+	};
+
+	// Objects refresh
+
+	this.initWebGLObjects = function ( scene ) {
+
+		if ( !scene.__webglObjects ) {
+
+			scene.__webglObjects = [];
+			scene.__webglObjectsImmediate = [];
+			scene.__webglSprites = [];
+			scene.__webglFlares = [];
+
+		}
+
+		while ( scene.__objectsAdded.length ) {
+
+			addObject( scene.__objectsAdded[ 0 ], scene );
+			scene.__objectsAdded.splice( 0, 1 );
+
+		}
+
+		while ( scene.__objectsRemoved.length ) {
+
+			removeObject( scene.__objectsRemoved[ 0 ], scene );
+			scene.__objectsRemoved.splice( 0, 1 );
+
+		}
+
+		// update must be called after objects adding / removal
+
+		for ( var o = 0, ol = scene.__webglObjects.length; o < ol; o ++ ) {
+
+			var object = scene.__webglObjects[ o ].object;
+
+			// TODO: Remove this hack (WebGLRenderer refactoring)
+
+			if ( object.__webglInit === undefined ) {
+
+				if ( object.__webglActive !== undefined ) {
+
+					removeObject( object, scene );
+
+				}
+
+				addObject( object, scene );
+
+			}
+
+			updateObject( object );
+
+		}
+
+	};
+
+	// Objects adding
+
+	function addObject( object, scene ) {
+
+		var g, geometry, material, geometryGroup;
+
+		if ( object.__webglInit === undefined ) {
+
+			object.__webglInit = true;
+
+			object._modelViewMatrix = new THREE.Matrix4();
+			object._normalMatrix = new THREE.Matrix3();
+
+			if ( object.geometry !== undefined && object.geometry.__webglInit === undefined ) {
+
+				object.geometry.__webglInit = true;
+				object.geometry.addEventListener( 'dispose', onGeometryDispose );
+
+			}
+
+			geometry = object.geometry;
+
+			if ( geometry === undefined ) {
+
+				// fail silently for now
+
+			} else if ( geometry instanceof THREE.BufferGeometry ) {
+
+				initDirectBuffers( geometry );
+
+			} else if ( object instanceof THREE.Mesh ) {
+
+				material = object.material;
+
+				if ( geometry.geometryGroups === undefined ) {
+
+					sortFacesByMaterial( geometry, material );
+
+				}
+
+				// create separate VBOs per geometry chunk
+
+				for ( g in geometry.geometryGroups ) {
+
+					geometryGroup = geometry.geometryGroups[ g ];
+
+					// initialise VBO on the first access
+
+					if ( ! geometryGroup.__webglVertexBuffer ) {
+
+						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;
+
+					}
+
+				}
+
+			} else if ( object instanceof THREE.Line ) {
+
+				if ( ! geometry.__webglVertexBuffer ) {
+
+					createLineBuffers( geometry );
+					initLineBuffers( geometry, object );
+
+					geometry.verticesNeedUpdate = true;
+					geometry.colorsNeedUpdate = true;
+					geometry.lineDistancesNeedUpdate = true;
+
+				}
+
+			} else if ( object instanceof THREE.ParticleSystem ) {
+
+				if ( ! geometry.__webglVertexBuffer ) {
+
+					createParticleBuffers( geometry );
+					initParticleBuffers( geometry, object );
+
+					geometry.verticesNeedUpdate = true;
+					geometry.colorsNeedUpdate = true;
+
+				}
+
+			}
+
+		}
+
+		if ( object.__webglActive === undefined ) {
+
+			if ( object instanceof THREE.Mesh ) {
+
+				geometry = object.geometry;
+
+				if ( geometry instanceof THREE.BufferGeometry ) {
+
+					addBuffer( scene.__webglObjects, geometry, object );
+
+				} else if ( geometry instanceof THREE.Geometry ) {
+
+					for ( g in geometry.geometryGroups ) {
+
+						geometryGroup = geometry.geometryGroups[ g ];
+
+						addBuffer( scene.__webglObjects, geometryGroup, object );
+
+					}
+
+				}
+
+			} else if ( object instanceof THREE.Line ||
+						object instanceof THREE.ParticleSystem ) {
+
+				geometry = object.geometry;
+				addBuffer( scene.__webglObjects, geometry, object );
+
+			} else if ( object instanceof THREE.ImmediateRenderObject || object.immediateRenderCallback ) {
+
+				addBufferImmediate( scene.__webglObjectsImmediate, object );
+
+			} else if ( object instanceof THREE.Sprite ) {
+
+				scene.__webglSprites.push( object );
+
+			} else if ( object instanceof THREE.LensFlare ) {
+
+				scene.__webglFlares.push( object );
+
+			}
+
+			object.__webglActive = true;
+
+		}
+
+	};
+
+	function addBuffer( objlist, buffer, object ) {
+
+		objlist.push(
+			{
+				id: null,
+				buffer: buffer,
+				object: object,
+				opaque: null,
+				transparent: 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,
+			geometryGroup, customAttributesDirty, material;
+
+		if ( geometry instanceof THREE.BufferGeometry ) {
+
+			setDirectBuffers( geometry, _gl.DYNAMIC_DRAW, !geometry.dynamic );
+
+		} else if ( object instanceof THREE.Mesh ) {
+
+			// check all geometry groups
+
+			for( var i = 0, il = geometry.geometryGroupsList.length; i < il; i ++ ) {
+
+				geometryGroup = geometry.geometryGroupsList[ i ];
+
+				material = getBufferMaterial( object, geometryGroup );
+
+				if ( geometry.buffersNeedUpdate ) {
+
+					initMeshBuffers( geometryGroup, object );
+
+				}
+
+				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;
+
+			geometry.buffersNeedUpdate = false;
+
+			material.attributes && clearCustomAttributes( material );
+
+		} else if ( object instanceof THREE.Line ) {
+
+			material = getBufferMaterial( object, geometry );
+
+			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.ParticleSystem ) {
+
+			material = getBufferMaterial( object, geometry );
+
+			customAttributesDirty = material.attributes && areCustomAttributesDirty( material );
+
+			if ( geometry.verticesNeedUpdate || geometry.colorsNeedUpdate || object.sortParticles || 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 a in material.attributes ) {
+
+			if ( material.attributes[ a ].needsUpdate ) return true;
+
+		}
+
+		return false;
+
+	};
+
+	function clearCustomAttributes( material ) {
+
+		for ( var a in material.attributes ) {
+
+			material.attributes[ a ].needsUpdate = false;
+
+		}
+
+	};
+
+	// Objects removal
+
+	function removeObject( object, scene ) {
+
+		if ( object instanceof THREE.Mesh  ||
+			 object instanceof THREE.ParticleSystem ||
+			 object instanceof THREE.Line ) {
+
+			removeInstances( scene.__webglObjects, object );
+
+		} else if ( object instanceof THREE.Sprite ) {
+
+			removeInstancesDirect( scene.__webglSprites, object );
+
+		} else if ( object instanceof THREE.LensFlare ) {
+
+			removeInstancesDirect( scene.__webglFlares, object );
+
+		} else if ( object instanceof THREE.ImmediateRenderObject || object.immediateRenderCallback ) {
+
+			removeInstances( scene.__webglObjectsImmediate, object );
+
+		}
+
+		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 );
+
+			}
+
+		}
+
+	};
+
+	function removeInstancesDirect( objlist, object ) {
+
+		for ( var o = objlist.length - 1; o >= 0; o -- ) {
+
+			if ( objlist[ o ] === object ) {
+
+				objlist.splice( o, 1 );
+
+			}
+
+		}
+
+	};
+
+	// Materials
+
+	this.initMaterial = function ( material, lights, fog, object ) {
+
+		material.addEventListener( 'dispose', onMaterialDispose );
+
+		var u, a, identifiers, i, parameters, maxLightCount, maxBones, maxShadows, shaderID;
+
+		if ( material instanceof THREE.MeshDepthMaterial ) {
+
+			shaderID = 'depth';
+
+		} else if ( material instanceof THREE.MeshNormalMaterial ) {
+
+			shaderID = 'normal';
+
+		} else if ( material instanceof THREE.MeshBasicMaterial ) {
+
+			shaderID = 'basic';
+
+		} else if ( material instanceof THREE.MeshLambertMaterial ) {
+
+			shaderID = 'lambert';
+
+		} else if ( material instanceof THREE.MeshPhongMaterial ) {
+
+			shaderID = 'phong';
+
+		} else if ( material instanceof THREE.LineBasicMaterial ) {
+
+			shaderID = 'basic';
+
+		} else if ( material instanceof THREE.LineDashedMaterial ) {
+
+			shaderID = 'dashed';
+
+		} else if ( material instanceof THREE.ParticleSystemMaterial ) {
+
+			shaderID = 'particle_basic';
+
+		}
+
+		if ( shaderID ) {
+
+			setMaterialShaders( material, THREE.ShaderLib[ shaderID ] );
+
+		}
+
+		// heuristics to create shader parameters according to lights in the scene
+		// (not to blow over maxLights budget)
+
+		maxLightCount = allocateLights( lights );
+
+		maxShadows = allocateShadows( lights );
+
+		maxBones = allocateBones( object );
+
+		parameters = {
+
+			map: !!material.map,
+			envMap: !!material.envMap,
+			lightMap: !!material.lightMap,
+			bumpMap: !!material.bumpMap,
+			normalMap: !!material.normalMap,
+			specularMap: !!material.specularMap,
+
+			vertexColors: material.vertexColors,
+
+			fog: fog,
+			useFog: material.fog,
+			fogExp: fog instanceof THREE.FogExp2,
+
+			sizeAttenuation: material.sizeAttenuation,
+
+			skinning: material.skinning,
+			maxBones: maxBones,
+			useVertexTexture: _supportsBoneTextures && object && object.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,
+			shadowMapType: this.shadowMapType,
+			shadowMapDebug: this.shadowMapDebug,
+			shadowMapCascade: this.shadowMapCascade,
+
+			alphaTest: material.alphaTest,
+			metal: material.metal,
+			perPixel: material.perPixel,
+			wrapAround: material.wrapAround,
+			doubleSided: material.side === THREE.DoubleSide,
+			flipSided: material.side === THREE.BackSide
+
+		};
+
+		material.program = buildProgram( shaderID, material.fragmentShader, material.vertexShader, material.uniforms, material.attributes, material.defines, parameters, material.index0AttributeName );
+
+		var attributes = material.program.attributes;
+
+		if ( material.morphTargets ) {
+
+			material.numSupportedMorphTargets = 0;
+
+			var id, base = "morphTarget";
+
+			for ( 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 ( u in material.uniforms ) {
+
+			material.uniformsList.push( [ material.uniforms[ u ], u ] );
+
+		}
+
+	};
+
+	function setMaterialShaders( material, shaders ) {
+
+		material.uniforms = THREE.UniformsUtils.clone( shaders.uniforms );
+		material.vertexShader = shaders.vertexShader;
+		material.fragmentShader = shaders.fragmentShader;
+
+	};
+
+	function setProgram( camera, lights, fog, material, object ) {
+
+		_usedTextureUnits = 0;
+
+		if ( material.needsUpdate ) {
+
+			if ( material.program ) deallocateMaterial( material );
+
+			_this.initMaterial( material, lights, fog, object );
+			material.needsUpdate = false;
+
+		}
+
+		if ( material.morphTargets ) {
+
+			if ( ! object.__webglMorphTargetInfluences ) {
+
+				object.__webglMorphTargetInfluences = new Float32Array( _this.maxMorphTargets );
+
+			}
+
+		}
+
+		var refreshMaterial = false;
+
+		var program = material.program,
+			p_uniforms = program.uniforms,
+			m_uniforms = material.uniforms;
+
+		if ( program !== _currentProgram ) {
+
+			_gl.useProgram( program );
+			_currentProgram = program;
+
+			refreshMaterial = true;
+
+		}
+
+		if ( material.id !== _currentMaterialId ) {
+
+			_currentMaterialId = material.id;
+			refreshMaterial = true;
+
+		}
+
+		if ( refreshMaterial || camera !== _currentCamera ) {
+
+			_gl.uniformMatrix4fv( p_uniforms.projectionMatrix, false, camera.projectionMatrix.elements );
+
+			if ( camera !== _currentCamera ) _currentCamera = camera;
+
+		}
+
+		// 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 ( _supportsBoneTextures && object.useVertexTexture ) {
+
+				if ( p_uniforms.boneTexture !== null ) {
+
+					var textureUnit = getTextureUnit();
+
+					_gl.uniform1i( p_uniforms.boneTexture, textureUnit );
+					_this.setTexture( object.boneTexture, textureUnit );
+
+				}
+
+				if ( p_uniforms.boneTextureWidth !== null ) {
+
+					_gl.uniform1i( p_uniforms.boneTextureWidth, object.boneTextureWidth );
+
+				}
+
+				if ( p_uniforms.boneTextureHeight !== null ) {
+
+					_gl.uniform1i( p_uniforms.boneTextureHeight, object.boneTextureHeight );
+
+				}
+
+			} else {
+
+				if ( p_uniforms.boneGlobalMatrices !== null ) {
+
+					_gl.uniformMatrix4fv( p_uniforms.boneGlobalMatrices, false, object.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 ) {
+
+					setupLights( program, lights );
+					_lightsNeedUpdate = false;
+
+				}
+
+				refreshUniformsLights( m_uniforms, _lights );
+
+			}
+
+			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.ParticleSystemMaterial ) {
+
+				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( program, material.uniformsList );
+
+			// 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.getPositionFromMatrix( 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.ShaderMaterial ||
+				 material.skinning ) {
+
+				if ( p_uniforms.viewMatrix !== null ) {
+
+					_gl.uniformMatrix4fv( p_uniforms.viewMatrix, false, camera.matrixWorldInverse.elements );
+
+				}
+
+			}
+
+		}
+
+		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;
+
+		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
+
+		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;
+
+		}
+
+		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;
+		uniforms.combine.value = material.combine;
+		uniforms.useRefract.value = material.envMap && material.envMap.mapping instanceof THREE.CubeRefractionMapping;
+
+	};
+
+	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;
+
+	};
+
+	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 ( program, uniforms ) {
+
+		var uniform, value, type, location, texture, textureUnit, i, il, j, jl, offset;
+
+		for ( j = 0, jl = uniforms.length; j < jl; j ++ ) {
+
+			location = program.uniforms[ uniforms[ j ][ 1 ] ];
+			if ( !location ) continue;
+
+			uniform = uniforms[ j ][ 0 ];
+
+			type = uniform.type;
+			value = uniform.value;
+
+			if ( type === "i" ) { // single integer
+
+				_gl.uniform1i( location, value );
+
+			} else if ( type === "f" ) { // single float
+
+				_gl.uniform1f( location, value );
+
+			} else if ( type === "v2" ) { // single THREE.Vector2
+
+				_gl.uniform2f( location, value.x, value.y );
+
+			} else if ( type === "v3" ) { // single THREE.Vector3
+
+				_gl.uniform3f( location, value.x, value.y, value.z );
+
+			} else if ( type === "v4" ) { // single THREE.Vector4
+
+				_gl.uniform4f( location, value.x, value.y, value.z, value.w );
+
+			} else if ( type === "c" ) { // single THREE.Color
+
+				_gl.uniform3f( location, value.r, value.g, value.b );
+
+			} else if ( type === "iv1" ) { // flat array of integers (JS or typed array)
+
+				_gl.uniform1iv( location, value );
+
+			} else if ( type === "iv" ) { // flat array of integers with 3 x N size (JS or typed array)
+
+				_gl.uniform3iv( location, value );
+
+			} else if ( type === "fv1" ) { // flat array of floats (JS or typed array)
+
+				_gl.uniform1fv( location, value );
+
+			} else if ( type === "fv" ) { // flat array of floats with 3 x N size (JS or typed array)
+
+				_gl.uniform3fv( location, value );
+
+			} else if ( type === "v2v" ) { // array of THREE.Vector2
+
+				if ( uniform._array === undefined ) {
+
+					uniform._array = new Float32Array( 2 * value.length );
+
+				}
+
+				for ( 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 );
+
+			} else if ( type === "v3v" ) { // array of THREE.Vector3
+
+				if ( uniform._array === undefined ) {
+
+					uniform._array = new Float32Array( 3 * value.length );
+
+				}
+
+				for ( 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 );
+
+			} else if ( type === "v4v" ) { // array of THREE.Vector4
+
+				if ( uniform._array === undefined ) {
+
+					uniform._array = new Float32Array( 4 * value.length );
+
+				}
+
+				for ( 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 );
+
+			} else if ( type === "m4") { // single THREE.Matrix4
+
+				if ( uniform._array === undefined ) {
+
+					uniform._array = new Float32Array( 16 );
+
+				}
+
+				value.flattenToArray( uniform._array );
+				_gl.uniformMatrix4fv( location, false, uniform._array );
+
+			} else if ( type === "m4v" ) { // array of THREE.Matrix4
+
+				if ( uniform._array === undefined ) {
+
+					uniform._array = new Float32Array( 16 * value.length );
+
+				}
+
+				for ( i = 0, il = value.length; i < il; i ++ ) {
+
+					value[ i ].flattenToArrayOffset( uniform._array, i * 16 );
+
+				}
+
+				_gl.uniformMatrix4fv( location, false, uniform._array );
+
+			} else if ( type === "t" ) { // single THREE.Texture (2d or cube)
+
+				texture = value;
+				textureUnit = getTextureUnit();
+
+				_gl.uniform1i( location, textureUnit );
+
+				if ( !texture ) continue;
+
+				if ( texture.image instanceof Array && texture.image.length === 6 ) {
+
+					setCubeTexture( texture, textureUnit );
+
+				} else if ( texture instanceof THREE.WebGLRenderTargetCube ) {
+
+					setCubeTextureDynamic( texture, textureUnit );
+
+				} else {
+
+					_this.setTexture( texture, textureUnit );
+
+				}
+
+			} else if ( type === "tv" ) { // array of THREE.Texture (2d)
+
+				if ( uniform._array === undefined ) {
+
+					uniform._array = [];
+
+				}
+
+				for( i = 0, il = uniform.value.length; i < il; i ++ ) {
+
+					uniform._array[ i ] = getTextureUnit();
+
+				}
+
+				_gl.uniform1iv( location, uniform._array );
+
+				for( i = 0, il = uniform.value.length; i < il; i ++ ) {
+
+					texture = uniform.value[ i ];
+					textureUnit = uniform._array[ i ];
+
+					if ( !texture ) continue;
+
+					_this.setTexture( texture, textureUnit );
+
+				}
+
+			} else {
+
+				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 ( program, 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.getPositionFromMatrix( light.matrixWorld );
+				_vector3.getPositionFromMatrix( light.target.matrixWorld );
+				_direction.sub( _vector3 );
+				_direction.normalize();
+
+				// skip lights with undefined direction
+				// these create troubles in OpenGL (making pixel black)
+
+				if ( _direction.x === 0 && _direction.y === 0 && _direction.z === 0 ) continue;
+
+				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.getPositionFromMatrix( 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 );
+
+				}
+
+				_vector3.getPositionFromMatrix( light.matrixWorld );
+
+				spotPositions[ spotOffset ]     = _vector3.x;
+				spotPositions[ spotOffset + 1 ] = _vector3.y;
+				spotPositions[ spotOffset + 2 ] = _vector3.z;
+
+				spotDistances[ spotLength ] = distance;
+
+				_direction.copy( _vector3 );
+				_vector3.getPositionFromMatrix( 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.getPositionFromMatrix( light.matrixWorld );
+				_direction.normalize();
+
+				// skip lights with undefined direction
+				// these create troubles in OpenGL (making pixel black)
+
+				if ( _direction.x === 0 && _direction.y === 0 && _direction.z === 0 ) continue;
+
+				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 ) {
+
+		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;
+
+		}
+
+	};
+
+	// Defines
+
+	function generateDefines ( 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" );
+
+	};
+
+	// Shaders
+
+	function buildProgram ( shaderID, fragmentShader, vertexShader, uniforms, attributes, defines, parameters, index0AttributeName ) {
+
+		var p, pl, d, program, code;
+		var chunks = [];
+
+		// Generate code
+
+		if ( shaderID ) {
+
+			chunks.push( shaderID );
+
+		} else {
+
+			chunks.push( fragmentShader );
+			chunks.push( vertexShader );
+
+		}
+
+		for ( d in defines ) {
+
+			chunks.push( d );
+			chunks.push( defines[ d ] );
+
+		}
+
+		for ( p in parameters ) {
+
+			chunks.push( p );
+			chunks.push( parameters[ p ] );
+
+		}
+
+		code = chunks.join();
+
+		// Check if code has been already compiled
+
+		for ( p = 0, pl = _programs.length; p < pl; p ++ ) {
+
+			var programInfo = _programs[ p ];
+
+			if ( programInfo.code === code ) {
+
+				// console.log( "Code already compiled." /*: \n\n" + code*/ );
+
+				programInfo.usedTimes ++;
+
+				return programInfo.program;
+
+			}
+
+		}
+
+		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";
+
+		}
+
+		// console.log( "building new program " );
+
+		//
+
+		var customDefines = generateDefines( defines );
+
+		//
+
+		program = _gl.createProgram();
+
+		var prefix_vertex = [
+
+			"precision " + _precision + " float;",
+			"precision " + _precision + " int;",
+
+			customDefines,
+
+			_supportsVertexTextures ? "#define VERTEX_TEXTURES" : "",
+
+			_this.gammaInput ? "#define GAMMA_INPUT" : "",
+			_this.gammaOutput ? "#define GAMMA_OUTPUT" : "",
+			_this.physicallyBasedShading ? "#define PHYSICALLY_BASED_SHADING" : "",
+
+			"#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.lightMap ? "#define USE_LIGHTMAP" : "",
+			parameters.bumpMap ? "#define USE_BUMPMAP" : "",
+			parameters.normalMap ? "#define USE_NORMALMAP" : "",
+			parameters.specularMap ? "#define USE_SPECULARMAP" : "",
+			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.perPixel ? "#define PHONG_PER_PIXEL" : "",
+			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" : "",
+
+			"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");
+
+		var prefix_fragment = [
+
+			"precision " + _precision + " float;",
+			"precision " + _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" : "",
+			_this.physicallyBasedShading ? "#define PHYSICALLY_BASED_SHADING" : "",
+
+			( 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.lightMap ? "#define USE_LIGHTMAP" : "",
+			parameters.bumpMap ? "#define USE_BUMPMAP" : "",
+			parameters.normalMap ? "#define USE_NORMALMAP" : "",
+			parameters.specularMap ? "#define USE_SPECULARMAP" : "",
+			parameters.vertexColors ? "#define USE_COLOR" : "",
+
+			parameters.metal ? "#define METAL" : "",
+			parameters.perPixel ? "#define PHONG_PER_PIXEL" : "",
+			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" : "",
+
+			"uniform mat4 viewMatrix;",
+			"uniform vec3 cameraPosition;",
+			""
+
+		].join("\n");
+
+		var glVertexShader = getShader( "vertex", prefix_vertex + vertexShader );
+		var glFragmentShader = getShader( "fragment", prefix_fragment + fragmentShader );
+
+		_gl.attachShader( program, glVertexShader );
+		_gl.attachShader( program, glFragmentShader );
+
+		//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
+		if ( index0AttributeName ) {
+			_gl.bindAttribLocation( program, 0, index0AttributeName );
+		}
+
+		_gl.linkProgram( program );
+
+		if ( !_gl.getProgramParameter( program, _gl.LINK_STATUS ) ) {
+
+			console.error( "Could not initialise shader\n" + "VALIDATE_STATUS: " + _gl.getProgramParameter( program, _gl.VALIDATE_STATUS ) + ", gl error [" + _gl.getError() + "]" );
+			console.error( "Program Info Log: " + _gl.getProgramInfoLog( program ) );
+		}
+
+		// clean up
+
+		_gl.deleteShader( glFragmentShader );
+		_gl.deleteShader( glVertexShader );
+
+		// console.log( prefix_fragment + fragmentShader );
+		// console.log( prefix_vertex + vertexShader );
+
+		program.uniforms = {};
+		program.attributes = {};
+
+		var identifiers, u, a, i;
+
+		// cache uniform locations
+
+		identifiers = [
+
+			'viewMatrix', 'modelViewMatrix', 'projectionMatrix', 'normalMatrix', 'modelMatrix', 'cameraPosition',
+			'morphTargetInfluences'
+
+		];
+
+		if ( parameters.useVertexTexture ) {
+
+			identifiers.push( 'boneTexture' );
+			identifiers.push( 'boneTextureWidth' );
+			identifiers.push( 'boneTextureHeight' );
+
+		} else {
+
+			identifiers.push( 'boneGlobalMatrices' );
+
+		}
+
+		for ( u in uniforms ) {
+
+			identifiers.push( u );
+
+		}
+
+		cacheUniformLocations( program, identifiers );
+
+		// cache attributes locations
+
+		identifiers = [
+
+			"position", "normal", "uv", "uv2", "tangent", "color",
+			"skinIndex", "skinWeight", "lineDistance"
+
+		];
+
+		for ( i = 0; i < parameters.maxMorphTargets; i ++ ) {
+
+			identifiers.push( "morphTarget" + i );
+
+		}
+
+		for ( i = 0; i < parameters.maxMorphNormals; i ++ ) {
+
+			identifiers.push( "morphNormal" + i );
+
+		}
+
+		for ( a in attributes ) {
+
+			identifiers.push( a );
+
+		}
+
+		cacheAttributeLocations( program, identifiers );
+
+		program.id = _programs_counter ++;
+
+		_programs.push( { program: program, code: code, usedTimes: 1 } );
+
+		_this.info.memory.programs = _programs.length;
+
+		return program;
+
+	};
+
+	// Shader parameters cache
+
+	function cacheUniformLocations ( program, identifiers ) {
+
+		var i, l, id;
+
+		for( i = 0, l = identifiers.length; i < l; i ++ ) {
+
+			id = identifiers[ i ];
+			program.uniforms[ id ] = _gl.getUniformLocation( program, id );
+
+		}
+
+	};
+
+	function cacheAttributeLocations ( program, identifiers ) {
+
+		var i, l, id;
+
+		for( i = 0, l = identifiers.length; i < l; i ++ ) {
+
+			id = identifiers[ i ];
+			program.attributes[ id ] = _gl.getAttribLocation( program, id );
+
+		}
+
+	};
+
+	function addLineNumbers ( string ) {
+
+		var chunks = string.split( "\n" );
+
+		for ( var i = 0, il = chunks.length; i < il; i ++ ) {
+
+			// Chrome reports shader errors on lines
+			// starting counting from 1
+
+			chunks[ i ] = ( i + 1 ) + ": " + chunks[ i ];
+
+		}
+
+		return chunks.join( "\n" );
+
+	};
+
+	function getShader ( type, string ) {
+
+		var shader;
+
+		if ( type === "fragment" ) {
+
+			shader = _gl.createShader( _gl.FRAGMENT_SHADER );
+
+		} else if ( type === "vertex" ) {
+
+			shader = _gl.createShader( _gl.VERTEX_SHADER );
+
+		}
+
+		_gl.shaderSource( shader, string );
+		_gl.compileShader( shader );
+
+		if ( !_gl.getShaderParameter( shader, _gl.COMPILE_STATUS ) ) {
+
+			console.error( _gl.getShaderInfoLog( shader ) );
+			console.error( addLineNumbers( string ) );
+			return null;
+
+		}
+
+		return shader;
+
+	};
+
+	// Textures
+
+
+	function isPowerOfTwo ( value ) {
+
+		return ( value & ( value - 1 ) ) === 0;
+
+	};
+
+	function setTextureParameters ( textureType, texture, isImagePowerOfTwo ) {
+
+		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 );
+
+			_gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterFallback( texture.magFilter ) );
+			_gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterFallback( texture.minFilter ) );
+
+		}
+
+		if ( _glExtensionTextureFilterAnisotropic && texture.type !== THREE.FloatType ) {
+
+			if ( texture.anisotropy > 1 || texture.__oldAnisotropy ) {
+
+				_gl.texParameterf( textureType, _glExtensionTextureFilterAnisotropic.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, _maxAnisotropy ) );
+				texture.__oldAnisotropy = texture.anisotropy;
+
+			}
+
+		}
+
+	};
+
+	this.setTexture = function ( texture, slot ) {
+
+		if ( texture.needsUpdate ) {
+
+			if ( ! texture.__webglInit ) {
+
+				texture.__webglInit = true;
+
+				texture.addEventListener( 'dispose', onTextureDispose );
+
+				texture.__webglTexture = _gl.createTexture();
+
+				_this.info.memory.textures ++;
+
+			}
+
+			_gl.activeTexture( _gl.TEXTURE0 + slot );
+			_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 );
+
+			var image = texture.image,
+			isImagePowerOfTwo = isPowerOfTwo( image.width ) && 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 ) {
+						_gl.compressedTexImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, mipmap.data );
+					} 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();
+
+		} else {
+
+			_gl.activeTexture( _gl.TEXTURE0 + slot );
+			_gl.bindTexture( _gl.TEXTURE_2D, texture.__webglTexture );
+
+		}
+
+	};
+
+	function clampToMaxSize ( image, maxSize ) {
+
+		if ( image.width <= maxSize && image.height <= maxSize ) {
+
+			return image;
+
+		}
+
+		// Warning: Scaling through the canvas will only work with images that use
+		// premultiplied alpha.
+
+		var maxDimension = Math.max( image.width, image.height );
+		var newWidth = Math.floor( image.width * maxSize / maxDimension );
+		var newHeight = Math.floor( image.height * maxSize / maxDimension );
+
+		var canvas = document.createElement( 'canvas' );
+		canvas.width = newWidth;
+		canvas.height = newHeight;
+
+		var ctx = canvas.getContext( "2d" );
+		ctx.drawImage( image, 0, 0, image.width, image.height, 0, 0, newWidth, newHeight );
+
+		return canvas;
+
+	}
+
+	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 cubeImage = [];
+
+				for ( var i = 0; i < 6; i ++ ) {
+
+					if ( _this.autoScaleCubemaps && ! isCompressed ) {
+
+						cubeImage[ i ] = clampToMaxSize( texture.image[ i ], _maxCubemapSize );
+
+					} else {
+
+						cubeImage[ i ] = texture.image[ i ];
+
+					}
+
+				}
+
+				var image = cubeImage[ 0 ],
+				isImagePowerOfTwo = isPowerOfTwo( image.width ) && 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 ) {
+
+						_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 ) {
+
+								_gl.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, mipmap.data );
+
+							} 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 ) {
+
+			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 = isPowerOfTwo( renderTarget.width ) && 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 ) {
+
+		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;
+
+		if ( _glExtensionCompressedTextureS3TC !== undefined ) {
+
+			if ( p === THREE.RGB_S3TC_DXT1_Format ) return _glExtensionCompressedTextureS3TC.COMPRESSED_RGB_S3TC_DXT1_EXT;
+			if ( p === THREE.RGBA_S3TC_DXT1_Format ) return _glExtensionCompressedTextureS3TC.COMPRESSED_RGBA_S3TC_DXT1_EXT;
+			if ( p === THREE.RGBA_S3TC_DXT3_Format ) return _glExtensionCompressedTextureS3TC.COMPRESSED_RGBA_S3TC_DXT3_EXT;
+			if ( p === THREE.RGBA_S3TC_DXT5_Format ) return _glExtensionCompressedTextureS3TC.COMPRESSED_RGBA_S3TC_DXT5_EXT;
+
+		}
+
+		return 0;
+
+	};
+
+	// Allocations
+
+	function allocateBones ( object ) {
+
+		if ( _supportsBoneTextures && object && object.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.bones.length, maxBones );
+
+				if ( maxBones < object.bones.length ) {
+
+					console.warn( "WebGLRenderer: too many bones - " + object.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 ) 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;
+
+	};
+
+	// Initialization
+
+	function initGL() {
+
+		try {
+
+			var attributes = {
+				alpha: _alpha,
+				premultipliedAlpha: _premultipliedAlpha,
+				antialias: _antialias,
+				stencil: _stencil,
+				preserveDrawingBuffer: _preserveDrawingBuffer
+			};
+
+			_gl = _canvas.getContext( 'webgl', attributes ) || _canvas.getContext( 'experimental-webgl', attributes );
+
+			if ( _gl === null ) {
+
+				throw 'Error creating WebGL context.';
+
+			}
+
+		} catch ( error ) {
+
+			console.error( error );
+
+		}
+
+		_glExtensionTextureFloat = _gl.getExtension( 'OES_texture_float' );
+		_glExtensionTextureFloatLinear = _gl.getExtension( 'OES_texture_float_linear' );
+		_glExtensionStandardDerivatives = _gl.getExtension( 'OES_standard_derivatives' );
+
+		_glExtensionTextureFilterAnisotropic = _gl.getExtension( 'EXT_texture_filter_anisotropic' ) || _gl.getExtension( 'MOZ_EXT_texture_filter_anisotropic' ) || _gl.getExtension( 'WEBKIT_EXT_texture_filter_anisotropic' );
+
+		_glExtensionCompressedTextureS3TC = _gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) || _gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) || _gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' );
+
+		if ( ! _glExtensionTextureFloat ) {
+
+			console.log( 'THREE.WebGLRenderer: Float textures not supported.' );
+
+		}
+
+		if ( ! _glExtensionStandardDerivatives ) {
+
+			console.log( 'THREE.WebGLRenderer: Standard derivatives not supported.' );
+
+		}
+
+		if ( ! _glExtensionTextureFilterAnisotropic ) {
+
+			console.log( 'THREE.WebGLRenderer: Anisotropic texture filtering not supported.' );
+
+		}
+
+		if ( ! _glExtensionCompressedTextureS3TC ) {
+
+			console.log( 'THREE.WebGLRenderer: S3TC compressed textures not supported.' );
+
+		}
+
+		if ( _gl.getShaderPrecisionFormat === undefined ) {
+
+			_gl.getShaderPrecisionFormat = function() {
+
+				return {
+					"rangeMin"  : 1,
+					"rangeMax"  : 1,
+					"precision" : 1
+				};
+
+			}
+		}
+
+	};
+
+	function setDefaultGLState () {
+
+		_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 );
+
+	};
+
+	// default plugins (order is important)
+
+	this.shadowMapPlugin = new THREE.ShadowMapPlugin();
+	this.addPrePlugin( this.shadowMapPlugin );
+
+	this.addPostPlugin( new THREE.SpritePlugin() );
+	this.addPostPlugin( new THREE.LensFlarePlugin() );
+
+};
+
+/**
+ * @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,
+
+	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 );
+
+/**
+ * @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 );
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.RenderableVertex = function () {
+
+	this.positionWorld = new THREE.Vector3();
+	this.positionScreen = new THREE.Vector4();
+
+	this.visible = true;
+
+};
+
+THREE.RenderableVertex.prototype.copy = function ( vertex ) {
+
+	this.positionWorld.copy( vertex.positionWorld );
+	this.positionScreen.copy( vertex.positionScreen );
+
+};
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.RenderableFace3 = function () {
+
+	this.id = 0;
+
+	this.v1 = new THREE.RenderableVertex();
+	this.v2 = new THREE.RenderableVertex();
+	this.v3 = new THREE.RenderableVertex();
+
+	this.centroidModel = new THREE.Vector3();
+
+	this.normalModel = new THREE.Vector3();
+	this.normalModelView = new THREE.Vector3();
+
+	this.vertexNormalsLength = 0;
+	this.vertexNormalsModel = [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ];
+	this.vertexNormalsModelView = [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ];
+
+	this.color = null;
+	this.material = null;
+	this.uvs = [[]];
+
+	this.z = 0;
+
+};
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.RenderableObject = function () {
+
+	this.id = 0;
+
+	this.object = null;
+	this.z = 0;
+
+};
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.RenderableSprite = function () {
+
+	this.id = 0;
+
+	this.object = null;
+
+	this.x = 0;
+	this.y = 0;
+	this.z = 0;
+
+	this.rotation = 0;
+	this.scale = new THREE.Vector2();
+
+	this.material = null;
+
+};
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.RenderableLine = function () {
+
+	this.id = 0;
+
+	this.v1 = new THREE.RenderableVertex();
+	this.v2 = new THREE.RenderableVertex();
+
+	this.vertexColors = [ new THREE.Color(), new THREE.Color() ];
+	this.material = null;
+
+	this.z = 0;
+
+};
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.GeometryUtils = {
+
+	// Merge two geometries or geometry and geometry from object (using object's transform)
+
+	merge: function ( geometry1, object2 /* mesh | geometry */, materialIndexOffset ) {
+
+		var matrix, normalMatrix,
+		vertexOffset = geometry1.vertices.length,
+		uvPosition = geometry1.faceVertexUvs[ 0 ].length,
+		geometry2 = object2 instanceof THREE.Mesh ? object2.geometry : object2,
+		vertices1 = geometry1.vertices,
+		vertices2 = geometry2.vertices,
+		faces1 = geometry1.faces,
+		faces2 = geometry2.faces,
+		uvs1 = geometry1.faceVertexUvs[ 0 ],
+		uvs2 = geometry2.faceVertexUvs[ 0 ];
+
+		if ( materialIndexOffset === undefined ) materialIndexOffset = 0;
+
+		if ( object2 instanceof THREE.Mesh ) {
+
+			object2.matrixAutoUpdate && object2.updateMatrix();
+
+			matrix = object2.matrix;
+
+			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 ) 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 ) {
+
+				faceCopy.normal.applyMatrix3( normalMatrix ).normalize();
+
+			}
+
+			for ( var j = 0, jl = faceVertexNormals.length; j < jl; j ++ ) {
+
+				normal = faceVertexNormals[ j ].clone();
+
+				if ( normalMatrix ) {
+
+					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;
+
+			faceCopy.centroid.copy( face.centroid );
+
+			if ( matrix ) {
+
+				faceCopy.centroid.applyMatrix4( matrix );
+
+			}
+
+			faces1.push( faceCopy );
+
+		}
+
+		// uvs
+
+		for ( i = 0, il = uvs2.length; i < il; i ++ ) {
+
+			var uv = uvs2[ i ], uvCopy = [];
+
+			for ( var j = 0, jl = uv.length; j < jl; j ++ ) {
+
+				uvCopy.push( new THREE.Vector2( uv[ j ].x, uv[ j ].y ) );
+
+			}
+
+			uvs1.push( uvCopy );
+
+		}
+
+	},
+
+	// Get random point in triangle (via barycentric coordinates)
+	// 	(uniform distribution)
+	// 	http://www.cgafaq.info/wiki/Random_Point_In_Triangle
+
+	randomPointInTriangle: function () {
+
+		var vector = new THREE.Vector3();
+
+		return function ( vectorA, vectorB, vectorC ) {
+
+			var point = new THREE.Vector3();
+
+			var a = THREE.Math.random16();
+			var b = THREE.Math.random16();
+
+			if ( ( a + b ) > 1 ) {
+
+				a = 1 - a;
+				b = 1 - b;
+
+			}
+
+			var c = 1 - a - b;
+
+			point.copy( vectorA );
+			point.multiplyScalar( a );
+
+			vector.copy( vectorB );
+			vector.multiplyScalar( b );
+
+			point.add( vector );
+
+			vector.copy( vectorC );
+			vector.multiplyScalar( c );
+
+			point.add( vector );
+
+			return point;
+
+		};
+
+	}(),
+
+	// Get random point in face (triangle / quad)
+	// (uniform distribution)
+
+	randomPointInFace: function ( face, geometry, useCachedAreas ) {
+
+		var vA, vB, vC, vD;
+
+		vA = geometry.vertices[ face.a ];
+		vB = geometry.vertices[ face.b ];
+		vC = geometry.vertices[ face.c ];
+
+		return THREE.GeometryUtils.randomPointInTriangle( vA, vB, vC );
+
+	},
+
+	// Get uniformly distributed random points in mesh
+	// 	- create array with cumulative sums of face areas
+	//  - pick random number from 0 to total area
+	//  - find corresponding place in area array by binary search
+	//	- get random point in face
+
+	randomPointsInGeometry: function ( geometry, n ) {
+
+		var face, i,
+			faces = geometry.faces,
+			vertices = geometry.vertices,
+			il = faces.length,
+			totalArea = 0,
+			cumulativeAreas = [],
+			vA, vB, vC, vD;
+
+		// precompute face areas
+
+		for ( i = 0; i < il; i ++ ) {
+
+			face = faces[ i ];
+
+			vA = vertices[ face.a ];
+			vB = vertices[ face.b ];
+			vC = vertices[ face.c ];
+
+			face._area = THREE.GeometryUtils.triangleArea( vA, vB, vC );
+
+			totalArea += face._area;
+
+			cumulativeAreas[ i ] = totalArea;
+
+		}
+
+		// binary search cumulative areas array
+
+		function binarySearchIndices( value ) {
+
+			function binarySearch( start, end ) {
+
+				// return closest larger index
+				// if exact number is not found
+
+				if ( end < start )
+					return start;
+
+				var mid = start + Math.floor( ( end - start ) / 2 );
+
+				if ( cumulativeAreas[ mid ] > value ) {
+
+					return binarySearch( start, mid - 1 );
+
+				} else if ( cumulativeAreas[ mid ] < value ) {
+
+					return binarySearch( mid + 1, end );
+
+				} else {
+
+					return mid;
+
+				}
+
+			}
+
+			var result = binarySearch( 0, cumulativeAreas.length - 1 )
+			return result;
+
+		}
+
+		// pick random face weighted by face area
+
+		var r, index,
+			result = [];
+
+		var stats = {};
+
+		for ( i = 0; i < n; i ++ ) {
+
+			r = THREE.Math.random16() * totalArea;
+
+			index = binarySearchIndices( r );
+
+			result[ i ] = THREE.GeometryUtils.randomPointInFace( faces[ index ], geometry, true );
+
+			if ( ! stats[ index ] ) {
+
+				stats[ index ] = 1;
+
+			} else {
+
+				stats[ index ] += 1;
+
+			}
+
+		}
+
+		return result;
+
+	},
+
+	// Get triangle area (half of parallelogram)
+	//	http://mathworld.wolfram.com/TriangleArea.html
+
+	triangleArea: function () {
+
+		var vector1 = new THREE.Vector3();
+		var vector2 = new THREE.Vector3();
+
+		return function ( vectorA, vectorB, vectorC ) {
+
+			vector1.subVectors( vectorB, vectorA );
+			vector2.subVectors( vectorC, vectorA );
+			vector1.cross( vector2 );
+
+			return 0.5 * vector1.length();
+
+		};
+
+	}(),
+
+	// Center geometry so that 0,0,0 is in center of bounding box
+
+	center: function ( geometry ) {
+
+		geometry.computeBoundingBox();
+
+		var bb = geometry.boundingBox;
+
+		var offset = new THREE.Vector3();
+
+		offset.addVectors( bb.min, bb.max );
+		offset.multiplyScalar( -0.5 );
+
+		geometry.applyMatrix( new THREE.Matrix4().makeTranslation( offset.x, offset.y, offset.z ) );
+		geometry.computeBoundingBox();
+
+		return offset;
+
+	},
+
+	triangulateQuads: function ( geometry ) {
+
+		var i, il, j, jl;
+
+		var faces = [];
+		var faceVertexUvs = [];
+
+		for ( i = 0, il = geometry.faceVertexUvs.length; i < il; i ++ ) {
+
+			faceVertexUvs[ i ] = [];
+
+		}
+
+		for ( i = 0, il = geometry.faces.length; i < il; i ++ ) {
+
+			var face = geometry.faces[ i ];
+
+			faces.push( face );
+
+			for ( j = 0, jl = geometry.faceVertexUvs.length; j < jl; j ++ ) {
+
+				faceVertexUvs[ j ].push( geometry.faceVertexUvs[ j ][ i ] );
+
+			}
+
+		}
+
+		geometry.faces = faces;
+		geometry.faceVertexUvs = faceVertexUvs;
+
+		geometry.computeCentroids();
+		geometry.computeFaceNormals();
+		geometry.computeVertexNormals();
+
+		if ( geometry.hasTangents ) geometry.computeTangents();
+
+	}
+
+};
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.ImageUtils = {
+
+	crossOrigin: 'anonymous',
+
+	loadTexture: function ( url, mapping, onLoad, onError ) {
+
+		var loader = new THREE.ImageLoader();
+		loader.crossOrigin = this.crossOrigin;
+
+		var texture = new THREE.Texture( undefined, mapping );
+
+		var image = loader.load( url, function () {
+
+			texture.needsUpdate = true;
+
+			if ( onLoad ) onLoad( texture );
+
+		} );
+
+		texture.image = image;
+		texture.sourceFile = url;
+
+		return texture;
+
+	},
+
+	loadCompressedTexture: function ( url, mapping, onLoad, onError ) {
+
+		var texture = new THREE.CompressedTexture();
+		texture.mapping = mapping;
+
+		var request = new XMLHttpRequest();
+
+		request.onload = function () {
+
+			var buffer = request.response;
+			var dds = THREE.ImageUtils.parseDDS( buffer, true );
+
+			texture.format = dds.format;
+
+			texture.mipmaps = dds.mipmaps;
+			texture.image.width = dds.width;
+			texture.image.height = dds.height;
+
+			// gl.generateMipmap fails for compressed textures
+			// mipmaps must be embedded in the DDS file
+			// or texture filters must not use mipmapping
+
+			texture.generateMipmaps = false;
+
+			texture.needsUpdate = true;
+
+			if ( onLoad ) onLoad( texture );
+
+		}
+
+		request.onerror = onError;
+
+		request.open( 'GET', url, true );
+		request.responseType = "arraybuffer";
+		request.send( null );
+
+		return texture;
+
+	},
+
+	loadTextureCube: function ( array, mapping, onLoad, onError ) {
+
+		var images = [];
+		images.loadCount = 0;
+
+		var texture = new THREE.Texture();
+		texture.image = images;
+		if ( mapping !== undefined ) texture.mapping = mapping;
+
+		// no flipping needed for cube textures
+
+		texture.flipY = false;
+
+		for ( var i = 0, il = array.length; i < il; ++ i ) {
+
+			var cubeImage = new Image();
+			images[ i ] = cubeImage;
+
+			cubeImage.onload = function () {
+
+				images.loadCount += 1;
+
+				if ( images.loadCount === 6 ) {
+
+					texture.needsUpdate = true;
+					if ( onLoad ) onLoad( texture );
+
+				}
+
+			};
+
+			cubeImage.onerror = onError;
+
+			cubeImage.crossOrigin = this.crossOrigin;
+			cubeImage.src = array[ i ];
+
+		}
+
+		return texture;
+
+	},
+
+	loadCompressedTextureCube: function ( array, mapping, onLoad, onError ) {
+
+		var images = [];
+		images.loadCount = 0;
+
+		var texture = new THREE.CompressedTexture();
+		texture.image = images;
+		if ( mapping !== undefined ) texture.mapping = mapping;
+
+		// no flipping for cube textures
+		// (also flipping doesn't work for compressed textures )
+
+		texture.flipY = false;
+
+		// can't generate mipmaps for compressed textures
+		// mips must be embedded in DDS files
+
+		texture.generateMipmaps = false;
+
+		var generateCubeFaceCallback = function ( rq, img ) {
+
+			return function () {
+
+				var buffer = rq.response;
+				var dds = THREE.ImageUtils.parseDDS( buffer, true );
+
+				img.format = dds.format;
+
+				img.mipmaps = dds.mipmaps;
+				img.width = dds.width;
+				img.height = dds.height;
+
+				images.loadCount += 1;
+
+				if ( images.loadCount === 6 ) {
+
+					texture.format = dds.format;
+					texture.needsUpdate = true;
+					if ( onLoad ) onLoad( texture );
+
+				}
+
+			}
+
+		}
+
+		// compressed cubemap textures as 6 separate DDS files
+
+		if ( array instanceof Array ) {
+
+			for ( var i = 0, il = array.length; i < il; ++ i ) {
+
+				var cubeImage = {};
+				images[ i ] = cubeImage;
+
+				var request = new XMLHttpRequest();
+
+				request.onload = generateCubeFaceCallback( request, cubeImage );
+				request.onerror = onError;
+
+				var url = array[ i ];
+
+				request.open( 'GET', url, true );
+				request.responseType = "arraybuffer";
+				request.send( null );
+
+			}
+
+		// compressed cubemap texture stored in a single DDS file
+
+		} else {
+
+			var url = array;
+			var request = new XMLHttpRequest();
+
+			request.onload = function( ) {
+
+				var buffer = request.response;
+				var dds = THREE.ImageUtils.parseDDS( buffer, true );
+
+				if ( dds.isCubemap ) {
+
+					var faces = dds.mipmaps.length / dds.mipmapCount;
+
+					for ( var f = 0; f < faces; f ++ ) {
+
+						images[ f ] = { mipmaps : [] };
+
+						for ( var i = 0; i < dds.mipmapCount; i ++ ) {
+
+							images[ f ].mipmaps.push( dds.mipmaps[ f * dds.mipmapCount + i ] );
+							images[ f ].format = dds.format;
+							images[ f ].width = dds.width;
+							images[ f ].height = dds.height;
+
+						}
+
+					}
+
+					texture.format = dds.format;
+					texture.needsUpdate = true;
+					if ( onLoad ) onLoad( texture );
+
+				}
+
+			}
+
+			request.onerror = onError;
+
+			request.open( 'GET', url, true );
+			request.responseType = "arraybuffer";
+			request.send( null );
+
+		}
+
+		return texture;
+
+	},
+
+	loadDDSTexture: function ( url, mapping, onLoad, onError ) {
+
+		var images = [];
+		images.loadCount = 0;
+
+		var texture = new THREE.CompressedTexture();
+		texture.image = images;
+		if ( mapping !== undefined ) texture.mapping = mapping;
+
+		// no flipping for cube textures
+		// (also flipping doesn't work for compressed textures )
+
+		texture.flipY = false;
+
+		// can't generate mipmaps for compressed textures
+		// mips must be embedded in DDS files
+
+		texture.generateMipmaps = false;
+
+		{
+			var request = new XMLHttpRequest();
+
+			request.onload = function( ) {
+
+				var buffer = request.response;
+				var dds = THREE.ImageUtils.parseDDS( buffer, true );
+
+				if ( dds.isCubemap ) {
+
+					var faces = dds.mipmaps.length / dds.mipmapCount;
+
+					for ( var f = 0; f < faces; f ++ ) {
+
+						images[ f ] = { mipmaps : [] };
+
+						for ( var i = 0; i < dds.mipmapCount; i ++ ) {
+
+							images[ f ].mipmaps.push( dds.mipmaps[ f * dds.mipmapCount + i ] );
+							images[ f ].format = dds.format;
+							images[ f ].width = dds.width;
+							images[ f ].height = dds.height;
+
+						}
+
+					}
+
+
+				} else {
+					texture.image.width = dds.width;
+					texture.image.height = dds.height;
+					texture.mipmaps = dds.mipmaps;
+				}
+
+				texture.format = dds.format;
+				texture.needsUpdate = true;
+				if ( onLoad ) onLoad( texture );
+
+			}
+
+			request.onerror = onError;
+
+			request.open( 'GET', url, true );
+			request.responseType = "arraybuffer";
+			request.send( null );
+
+		}
+
+		return texture;
+
+	},
+
+	parseDDS: function ( buffer, loadMipmaps ) {
+
+		var dds = { mipmaps: [], width: 0, height: 0, format: null, mipmapCount: 1 };
+
+		// Adapted from @toji's DDS utils
+		//	https://github.com/toji/webgl-texture-utils/blob/master/texture-util/dds.js
+
+		// All values and structures referenced from:
+		// http://msdn.microsoft.com/en-us/library/bb943991.aspx/
+
+		var DDS_MAGIC = 0x20534444;
+
+		var DDSD_CAPS = 0x1,
+			DDSD_HEIGHT = 0x2,
+			DDSD_WIDTH = 0x4,
+			DDSD_PITCH = 0x8,
+			DDSD_PIXELFORMAT = 0x1000,
+			DDSD_MIPMAPCOUNT = 0x20000,
+			DDSD_LINEARSIZE = 0x80000,
+			DDSD_DEPTH = 0x800000;
+
+		var DDSCAPS_COMPLEX = 0x8,
+			DDSCAPS_MIPMAP = 0x400000,
+			DDSCAPS_TEXTURE = 0x1000;
+
+		var DDSCAPS2_CUBEMAP = 0x200,
+			DDSCAPS2_CUBEMAP_POSITIVEX = 0x400,
+			DDSCAPS2_CUBEMAP_NEGATIVEX = 0x800,
+			DDSCAPS2_CUBEMAP_POSITIVEY = 0x1000,
+			DDSCAPS2_CUBEMAP_NEGATIVEY = 0x2000,
+			DDSCAPS2_CUBEMAP_POSITIVEZ = 0x4000,
+			DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x8000,
+			DDSCAPS2_VOLUME = 0x200000;
+
+		var DDPF_ALPHAPIXELS = 0x1,
+			DDPF_ALPHA = 0x2,
+			DDPF_FOURCC = 0x4,
+			DDPF_RGB = 0x40,
+			DDPF_YUV = 0x200,
+			DDPF_LUMINANCE = 0x20000;
+
+		function fourCCToInt32( value ) {
+
+			return value.charCodeAt(0) +
+				(value.charCodeAt(1) << 8) +
+				(value.charCodeAt(2) << 16) +
+				(value.charCodeAt(3) << 24);
+
+		}
+
+		function int32ToFourCC( value ) {
+
+			return String.fromCharCode(
+				value & 0xff,
+				(value >> 8) & 0xff,
+				(value >> 16) & 0xff,
+				(value >> 24) & 0xff
+			);
+		}
+
+		function loadARGBMip( buffer, dataOffset, width, height ) {
+			var dataLength = width*height*4;
+			var srcBuffer = new Uint8Array( buffer, dataOffset, dataLength );
+			var byteArray = new Uint8Array( dataLength );
+			var dst = 0;
+			var src = 0;
+			for ( var y = 0; y < height; y++ ) {
+				for ( var x = 0; x < width; x++ ) {
+					var b = srcBuffer[src]; src++;
+					var g = srcBuffer[src]; src++;
+					var r = srcBuffer[src]; src++;
+					var a = srcBuffer[src]; src++;
+					byteArray[dst] = r; dst++;	//r
+					byteArray[dst] = g; dst++;	//g
+					byteArray[dst] = b; dst++;	//b
+					byteArray[dst] = a; dst++;	//a
+				}
+			}
+			return byteArray;
+		}
+
+		var FOURCC_DXT1 = fourCCToInt32("DXT1");
+		var FOURCC_DXT3 = fourCCToInt32("DXT3");
+		var FOURCC_DXT5 = fourCCToInt32("DXT5");
+
+		var headerLengthInt = 31; // The header length in 32 bit ints
+
+		// Offsets into the header array
+
+		var off_magic = 0;
+
+		var off_size = 1;
+		var off_flags = 2;
+		var off_height = 3;
+		var off_width = 4;
+
+		var off_mipmapCount = 7;
+
+		var off_pfFlags = 20;
+		var off_pfFourCC = 21;
+		var off_RGBBitCount = 22;
+		var off_RBitMask = 23;
+		var off_GBitMask = 24;
+		var off_BBitMask = 25;
+		var off_ABitMask = 26;
+
+		var off_caps = 27;
+		var off_caps2 = 28;
+		var off_caps3 = 29;
+		var off_caps4 = 30;
+
+		// Parse header
+
+		var header = new Int32Array( buffer, 0, headerLengthInt );
+
+		if ( header[ off_magic ] !== DDS_MAGIC ) {
+
+			console.error( "ImageUtils.parseDDS(): Invalid magic number in DDS header" );
+			return dds;
+
+		}
+
+		if ( ! header[ off_pfFlags ] & DDPF_FOURCC ) {
+
+			console.error( "ImageUtils.parseDDS(): Unsupported format, must contain a FourCC code" );
+			return dds;
+
+		}
+
+		var blockBytes;
+
+		var fourCC = header[ off_pfFourCC ];
+
+		var isRGBAUncompressed = false;
+
+		switch ( fourCC ) {
+
+			case FOURCC_DXT1:
+
+				blockBytes = 8;
+				dds.format = THREE.RGB_S3TC_DXT1_Format;
+				break;
+
+			case FOURCC_DXT3:
+
+				blockBytes = 16;
+				dds.format = THREE.RGBA_S3TC_DXT3_Format;
+				break;
+
+			case FOURCC_DXT5:
+
+				blockBytes = 16;
+				dds.format = THREE.RGBA_S3TC_DXT5_Format;
+				break;
+
+			default:
+
+				if( header[off_RGBBitCount] ==32
+					&& header[off_RBitMask]&0xff0000
+					&& header[off_GBitMask]&0xff00
+					&& header[off_BBitMask]&0xff
+					&& header[off_ABitMask]&0xff000000  ) {
+					isRGBAUncompressed = true;
+					blockBytes = 64;
+					dds.format = THREE.RGBAFormat;
+				} else {
+					console.error( "ImageUtils.parseDDS(): Unsupported FourCC code: ", int32ToFourCC( fourCC ) );
+					return dds;
+				}
+		}
+
+		dds.mipmapCount = 1;
+
+		if ( header[ off_flags ] & DDSD_MIPMAPCOUNT && loadMipmaps !== false ) {
+
+			dds.mipmapCount = Math.max( 1, header[ off_mipmapCount ] );
+
+		}
+
+		//TODO: Verify that all faces of the cubemap are present with DDSCAPS2_CUBEMAP_POSITIVEX, etc.
+
+		dds.isCubemap = header[ off_caps2 ] & DDSCAPS2_CUBEMAP ? true : false;
+
+		dds.width = header[ off_width ];
+		dds.height = header[ off_height ];
+
+		var dataOffset = header[ off_size ] + 4;
+
+		// Extract mipmaps buffers
+
+		var width = dds.width;
+		var height = dds.height;
+
+		var faces = dds.isCubemap ? 6 : 1;
+
+		for ( var face = 0; face < faces; face ++ ) {
+
+			for ( var i = 0; i < dds.mipmapCount; i ++ ) {
+
+				if( isRGBAUncompressed ) {
+					var byteArray = loadARGBMip( buffer, dataOffset, width, height );
+					var dataLength = byteArray.length;
+				} else {
+					var dataLength = Math.max( 4, width ) / 4 * Math.max( 4, height ) / 4 * blockBytes;
+					var byteArray = new Uint8Array( buffer, dataOffset, dataLength );
+				}
+
+				var mipmap = { "data": byteArray, "width": width, "height": height };
+				dds.mipmaps.push( mipmap );
+
+				dataOffset += dataLength;
+
+				width = Math.max( width * 0.5, 1 );
+				height = Math.max( height * 0.5, 1 );
+
+			}
+
+			width = dds.width;
+			height = dds.height;
+
+		}
+
+		return dds;
+
+	},
+
+	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;
+
+	}
+
+};
+
+/**
+ * @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 );
+
+	}
+
+};
+
+/**
+ * @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() {
+
+		return this.faces[ this.face ][ this.weight ][ this.style ];
+
+	},
+
+	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( cpx, cpy, cpx1, cpy1, cpx2, cpy2 );
+
+					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++ ) {
+
+			if( (p === u) || (p === v) || (p === w) ) continue;
+
+			px = contour[ verts[ p ] ].x
+			py = contour[ verts[ p ] ].y
+
+			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;
+
+/**
+ * @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.getPoint = getPointFunc;
+
+	return constructor;
+
+};
+
+/**
+ * @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.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, as well as centroid
+
+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,
+		centroid: sum.divideScalar( il )
+
+	};
+
+	if ( v3 ) {
+
+		ret.maxZ = maxZ;
+		ret.minZ = minZ;
+
+	}
+
+	return ret;
+
+};
+
+/**************************************************************
+ *	Create Geometries Helpers
+ **************************************************************/
+
+/// Generate geometry from path points (for Line or ParticleSystem 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.getNormalVector( xNorm ).multiplyScalar( oldY );
+
+		p.x = pathPt.x + normal.x;
+		p.y = pathPt.y + normal.y;
+
+	}
+
+	return oldPts;
+
+};
+
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.Gyroscope = function () {
+
+	THREE.Object3D.call( this );
+
+};
+
+THREE.Gyroscope.prototype = Object.create( THREE.Object3D.prototype );
+
+THREE.Gyroscope.prototype.updateMatrixWorld = 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( this.translationWorld, this.quaternionWorld, this.scaleWorld );
+			this.matrix.decompose( this.translationObject, this.quaternionObject, this.scaleObject );
+
+			this.matrixWorld.compose( this.translationWorld, this.quaternionObject, this.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 );
+
+	}
+
+};
+
+THREE.Gyroscope.prototype.translationWorld = new THREE.Vector3();
+THREE.Gyroscope.prototype.translationObject = new THREE.Vector3();
+THREE.Gyroscope.prototype.quaternionWorld = new THREE.Quaternion();
+THREE.Gyroscope.prototype.quaternionObject = new THREE.Quaternion();
+THREE.Gyroscope.prototype.scaleWorld = new THREE.Vector3();
+THREE.Gyroscope.prototype.scaleObject = new THREE.Vector3();
+
+
+/**
+ * @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.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
+
+THREE.Path.prototype.toShapes = function( isCCW ) {
+
+	var i, il, item, action, args;
+
+	var subPaths = [], lastPath = new THREE.Path();
+
+	for ( i = 0, il = this.actions.length; i < il; i ++ ) {
+
+		item = this.actions[ 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);
+
+	if ( subPaths.length == 0 ) return [];
+
+	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);
+
+	if ( holesFirst ) {
+
+		tmpShape = new THREE.Shape();
+
+		for ( i = 0, il = subPaths.length; i < il; i ++ ) {
+
+			tmpPath = subPaths[ i ];
+			solid = THREE.Shape.Utils.isClockWise( tmpPath.getPoints() );
+			solid = isCCW ? !solid : solid;
+
+			if ( solid ) {
+
+				tmpShape.actions = tmpPath.actions;
+				tmpShape.curves = tmpPath.curves;
+
+				shapes.push( tmpShape );
+				tmpShape = new THREE.Shape();
+
+				//console.log('cw', i);
+
+			} else {
+
+				tmpShape.holes.push( tmpPath );
+
+				//console.log('ccw', i);
+
+			}
+
+		}
+
+	} else {
+
+		// Shapes first
+		tmpShape = undefined;
+
+		for ( i = 0, il = subPaths.length; i < il; i ++ ) {
+
+			tmpPath = subPaths[ i ];
+			solid = THREE.Shape.Utils.isClockWise( tmpPath.getPoints() );
+			solid = isCCW ? !solid : solid;
+
+			if ( solid ) {
+
+				if ( tmpShape ) shapes.push( tmpShape );
+
+				tmpShape = new THREE.Shape();
+				tmpShape.actions = tmpPath.actions;
+				tmpShape.curves = tmpPath.curves;
+
+			} else {
+
+				tmpShape.holes.push( tmpPath );
+
+			}
+
+		}
+
+		shapes.push( tmpShape );
+
+	}
+
+	//console.log("shape", shapes);
+
+	return shapes;
+
+};
+
+/**
+ * @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 );
+
+// 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 = {
+
+	/*
+		contour - array of vector2 for contour
+		holes   - array of array of vector2
+	*/
+
+	removeHoles: function ( contour, holes ) {
+
+		var shape = contour.concat(); // work on this shape
+		var allpoints = shape.concat();
+
+		/* For each isolated shape, find the closest points and break to the hole to allow triangulation */
+
+
+		var prevShapeVert, nextShapeVert,
+			prevHoleVert, nextHoleVert,
+			holeIndex, shapeIndex,
+			shapeId, shapeGroup,
+			h, h2,
+			hole, shortest, d,
+			p, pts1, pts2,
+			tmpShape1, tmpShape2,
+			tmpHole1, tmpHole2,
+			verts = [];
+
+		for ( h = 0; h < holes.length; h ++ ) {
+
+			hole = holes[ h ];
+
+			/*
+			shapeholes[ h ].concat(); // preserves original
+			holes.push( hole );
+			*/
+
+			Array.prototype.push.apply( allpoints, hole );
+
+			shortest = Number.POSITIVE_INFINITY;
+
+
+			// Find the shortest pair of pts between shape and hole
+
+			// Note: Actually, I'm not sure now if we could optimize this to be faster than O(m*n)
+			// Using distanceToSquared() intead of distanceTo() should speed a little
+			// since running square roots operations are reduced.
+
+			for ( h2 = 0; h2 < hole.length; h2 ++ ) {
+
+				pts1 = hole[ h2 ];
+				var dist = [];
+
+				for ( p = 0; p < shape.length; p++ ) {
+
+					pts2 = shape[ p ];
+					d = pts1.distanceToSquared( pts2 );
+					dist.push( d );
+
+					if ( d < shortest ) {
+
+						shortest = d;
+						holeIndex = h2;
+						shapeIndex = p;
+
+					}
+
+				}
+
+			}
+
+			//console.log("shortest", shortest, dist);
+
+			prevShapeVert = ( shapeIndex - 1 ) >= 0 ? shapeIndex - 1 : shape.length - 1;
+			prevHoleVert = ( holeIndex - 1 ) >= 0 ? holeIndex - 1 : hole.length - 1;
+
+			var areaapts = [
+
+				hole[ holeIndex ],
+				shape[ shapeIndex ],
+				shape[ prevShapeVert ]
+
+			];
+
+			var areaa = THREE.FontUtils.Triangulate.area( areaapts );
+
+			var areabpts = [
+
+				hole[ holeIndex ],
+				hole[ prevHoleVert ],
+				shape[ shapeIndex ]
+
+			];
+
+			var areab = THREE.FontUtils.Triangulate.area( areabpts );
+
+			var shapeOffset = 1;
+			var holeOffset = -1;
+
+			var oldShapeIndex = shapeIndex, oldHoleIndex = holeIndex;
+			shapeIndex += shapeOffset;
+			holeIndex += holeOffset;
+
+			if ( shapeIndex < 0 ) { shapeIndex += shape.length;  }
+			shapeIndex %= shape.length;
+
+			if ( holeIndex < 0 ) { holeIndex += hole.length;  }
+			holeIndex %= hole.length;
+
+			prevShapeVert = ( shapeIndex - 1 ) >= 0 ? shapeIndex - 1 : shape.length - 1;
+			prevHoleVert = ( holeIndex - 1 ) >= 0 ? holeIndex - 1 : hole.length - 1;
+
+			areaapts = [
+
+				hole[ holeIndex ],
+				shape[ shapeIndex ],
+				shape[ prevShapeVert ]
+
+			];
+
+			var areaa2 = THREE.FontUtils.Triangulate.area( areaapts );
+
+			areabpts = [
+
+				hole[ holeIndex ],
+				hole[ prevHoleVert ],
+				shape[ shapeIndex ]
+
+			];
+
+			var areab2 = THREE.FontUtils.Triangulate.area( areabpts );
+			//console.log(areaa,areab ,areaa2,areab2, ( areaa + areab ),  ( areaa2 + areab2 ));
+
+			if ( ( areaa + areab ) > ( areaa2 + areab2 ) ) {
+
+				// In case areas are not correct.
+				//console.log("USE THIS");
+
+				shapeIndex = oldShapeIndex;
+				holeIndex = oldHoleIndex ;
+
+				if ( shapeIndex < 0 ) { shapeIndex += shape.length;  }
+				shapeIndex %= shape.length;
+
+				if ( holeIndex < 0 ) { holeIndex += hole.length;  }
+				holeIndex %= hole.length;
+
+				prevShapeVert = ( shapeIndex - 1 ) >= 0 ? shapeIndex - 1 : shape.length - 1;
+				prevHoleVert = ( holeIndex - 1 ) >= 0 ? holeIndex - 1 : hole.length - 1;
+
+			} else {
+
+				//console.log("USE THAT ")
+
+			}
+
+			tmpShape1 = shape.slice( 0, shapeIndex );
+			tmpShape2 = shape.slice( shapeIndex );
+			tmpHole1 = hole.slice( holeIndex );
+			tmpHole2 = hole.slice( 0, holeIndex );
+
+			// Should check orders here again?
+
+			var trianglea = [
+
+				hole[ holeIndex ],
+				shape[ shapeIndex ],
+				shape[ prevShapeVert ]
+
+			];
+
+			var triangleb = [
+
+				hole[ holeIndex ] ,
+				hole[ prevHoleVert ],
+				shape[ shapeIndex ]
+
+			];
+
+			verts.push( trianglea );
+			verts.push( triangleb );
+
+			shape = tmpShape1.concat( tmpHole1 ).concat( tmpHole2 ).concat( tmpShape2 );
+
+		}
+
+		return {
+
+			shape:shape, 		/* shape with no holes */
+			isolatedPts: verts, /* isolated faces */
+			allpoints: allpoints
+
+		}
+
+
+	},
+
+	triangulateShape: function ( contour, holes ) {
+
+		var shapeWithoutHoles = THREE.Shape.Utils.removeHoles( contour, holes );
+
+		var shape = shapeWithoutHoles.shape,
+			allpoints = shapeWithoutHoles.allpoints,
+			isolatedPts = shapeWithoutHoles.isolatedPts;
+
+		var triangles = THREE.FontUtils.Triangulate( shape, false ); // True returns indices for points of spooled shape
+
+		// 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.
+
+		//console.log( "triangles",triangles, triangles.length );
+		//console.log( "allpoints",allpoints, allpoints.length );
+
+		var i, il, f, face,
+			key, index,
+			allPointsMap = {},
+			isolatedPointsMap = {};
+
+		// 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;
+
+		}
+
+		// 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;
+
+				}
+
+			}
+
+		}
+
+		// check isolated points vertices against all points map
+
+		for ( i = 0, il = isolatedPts.length; i < il; i ++ ) {
+
+			face = isolatedPts[ 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( isolatedPts );
+
+	}, // end triangulate shapes
+
+	/*
+	triangulate2 : function( pts, holes ) {
+
+		// For use with Poly2Tri.js
+
+		var allpts = pts.concat();
+		var shape = [];
+		for (var p in pts) {
+			shape.push(new js.poly2tri.Point(pts[p].x, pts[p].y));
+		}
+
+		var swctx = new js.poly2tri.SweepContext(shape);
+
+		for (var h in holes) {
+			var aHole = holes[h];
+			var newHole = []
+			for (i in aHole) {
+				newHole.push(new js.poly2tri.Point(aHole[i].x, aHole[i].y));
+				allpts.push(aHole[i]);
+			}
+			swctx.AddHole(newHole);
+		}
+
+		var find;
+		var findIndexForPt = function (pt) {
+			find = new THREE.Vector2(pt.x, pt.y);
+			var p;
+			for (p=0, pl = allpts.length; p<pl; p++) {
+				if (allpts[p].equals(find)) return p;
+			}
+			return -1;
+		};
+
+		// triangulate
+		js.poly2tri.sweep.Triangulate(swctx);
+
+		var triangles =  swctx.GetTriangles();
+		var tr ;
+		var facesPts = [];
+		for (var t in triangles) {
+			tr =  triangles[t];
+			facesPts.push([
+				findIndexForPt(tr.GetPoint(0)),
+				findIndexForPt(tr.GetPoint(1)),
+				findIndexForPt(tr.GetPoint(2))
+					]);
+		}
+
+
+	//	console.log(facesPts);
+	//	console.log("triangles", triangles.length, triangles);
+
+		// Returns array of faces with 3 element each
+	return facesPts;
+	},
+*/
+
+	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 );
+
+	}
+
+};
+
+
+/**************************************************************
+ *	Line
+ **************************************************************/
+
+THREE.LineCurve = function ( v1, v2 ) {
+
+	this.v1 = v1;
+	this.v2 = v2;
+
+};
+
+THREE.LineCurve.prototype = Object.create( THREE.Curve.prototype );
+
+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();
+
+};
+/**************************************************************
+ *	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.getPoint = function ( t ) {
+
+	var tx, ty;
+
+	tx = THREE.Shape.Utils.b2( t, this.v0.x, this.v1.x, this.v2.x );
+	ty = THREE.Shape.Utils.b2( t, this.v0.y, this.v1.y, this.v2.y );
+
+	return new THREE.Vector2( tx, ty );
+
+};
+
+
+THREE.QuadraticBezierCurve.prototype.getTangent = function( t ) {
+
+	var tx, ty;
+
+	tx = THREE.Curve.Utils.tangentQuadraticBezier( t, this.v0.x, this.v1.x, this.v2.x );
+	ty = THREE.Curve.Utils.tangentQuadraticBezier( t, this.v0.y, this.v1.y, this.v2.y );
+
+	// returns unit vector
+
+	var tangent = new THREE.Vector2( tx, ty );
+	tangent.normalize();
+
+	return tangent;
+
+};
+/**************************************************************
+ *	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.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;
+
+};
+/**************************************************************
+ *	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.getPoint = function ( t ) {
+
+	var v = new THREE.Vector2();
+	var c = [];
+	var points = this.points, point, intPoint, weight;
+	point = ( points.length - 1 ) * t;
+
+	intPoint = Math.floor( point );
+	weight = point - intPoint;
+
+	c[ 0 ] = intPoint == 0 ? intPoint : intPoint - 1;
+	c[ 1 ] = intPoint;
+	c[ 2 ] = intPoint  > points.length - 2 ? points.length -1 : intPoint + 1;
+	c[ 3 ] = intPoint  > points.length - 3 ? points.length -1 : intPoint + 2;
+
+	v.x = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].x, points[ c[ 1 ] ].x, points[ c[ 2 ] ].x, points[ c[ 3 ] ].x, weight );
+	v.y = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].y, points[ c[ 1 ] ].y, points[ c[ 2 ] ].y, points[ c[ 3 ] ].y, weight );
+
+	return v;
+
+};
+/**************************************************************
+ *	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.getPoint = function ( t ) {
+
+	var angle;
+	var deltaAngle = this.aEndAngle - this.aStartAngle;
+
+	if ( deltaAngle < 0 ) deltaAngle += Math.PI * 2;
+	if ( deltaAngle > Math.PI * 2 ) deltaAngle -= Math.PI * 2;
+
+	if ( this.aClockwise === true ) {
+
+		angle = this.aEndAngle + ( 1 - t ) * ( Math.PI * 2 - deltaAngle );
+
+	} else {
+
+		angle = this.aStartAngle + t * deltaAngle;
+
+	}
+
+	var tx = this.aX + this.xRadius * Math.cos( angle );
+	var ty = this.aY + this.yRadius * Math.sin( angle );
+
+	return new THREE.Vector2( tx, ty );
+
+};
+
+/**************************************************************
+ *	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 );
+/**************************************************************
+ *	Line3D
+ **************************************************************/
+
+THREE.LineCurve3 = THREE.Curve.create(
+
+	function ( v1, v2 ) {
+
+		this.v1 = v1;
+		this.v2 = v2;
+
+	},
+
+	function ( t ) {
+
+		var r = new THREE.Vector3();
+
+
+		r.subVectors( this.v2, this.v1 ); // diff
+		r.multiplyScalar( t );
+		r.add( this.v1 );
+
+		return r;
+
+	}
+
+);
+
+/**************************************************************
+ *	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 tx, ty, tz;
+
+		tx = THREE.Shape.Utils.b2( t, this.v0.x, this.v1.x, this.v2.x );
+		ty = THREE.Shape.Utils.b2( t, this.v0.y, this.v1.y, this.v2.y );
+		tz = THREE.Shape.Utils.b2( t, this.v0.z, this.v1.z, this.v2.z );
+
+		return new THREE.Vector3( tx, ty, tz );
+
+	}
+
+);
+/**************************************************************
+ *	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 tx, ty, tz;
+
+		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 );
+		tz = THREE.Shape.Utils.b3( t, this.v0.z, this.v1.z, this.v2.z, this.v3.z );
+
+		return new THREE.Vector3( tx, ty, tz );
+
+	}
+
+);
+/**************************************************************
+ *	Spline 3D curve
+ **************************************************************/
+
+
+THREE.SplineCurve3 = THREE.Curve.create(
+
+	function ( points /* array of Vector3 */) {
+
+		this.points = (points == undefined) ? [] : points;
+
+	},
+
+	function ( t ) {
+
+		var v = new THREE.Vector3();
+		var c = [];
+		var points = this.points, point, intPoint, weight;
+		point = ( points.length - 1 ) * t;
+
+		intPoint = Math.floor( point );
+		weight = point - intPoint;
+
+		c[ 0 ] = intPoint == 0 ? intPoint : intPoint - 1;
+		c[ 1 ] = intPoint;
+		c[ 2 ] = intPoint  > points.length - 2 ? points.length - 1 : intPoint + 1;
+		c[ 3 ] = intPoint  > points.length - 3 ? points.length - 1 : intPoint + 2;
+
+		var pt0 = points[ c[0] ],
+			pt1 = points[ c[1] ],
+			pt2 = points[ c[2] ],
+			pt3 = points[ c[3] ];
+
+		v.x = THREE.Curve.Utils.interpolate(pt0.x, pt1.x, pt2.x, pt3.x, weight);
+		v.y = THREE.Curve.Utils.interpolate(pt0.y, pt1.y, pt2.y, pt3.y, weight);
+		v.z = THREE.Curve.Utils.interpolate(pt0.z, pt1.z, pt2.z, pt3.z, weight);
+
+		return v;
+
+	}
+
+);
+
+
+// THREE.SplineCurve3.prototype.getTangent = function(t) {
+// 		var v = new THREE.Vector3();
+// 		var c = [];
+// 		var points = this.points, point, intPoint, weight;
+// 		point = ( points.length - 1 ) * t;
+
+// 		intPoint = Math.floor( point );
+// 		weight = point - intPoint;
+
+// 		c[ 0 ] = intPoint == 0 ? intPoint : intPoint - 1;
+// 		c[ 1 ] = intPoint;
+// 		c[ 2 ] = intPoint  > points.length - 2 ? points.length - 1 : intPoint + 1;
+// 		c[ 3 ] = intPoint  > points.length - 3 ? points.length - 1 : intPoint + 2;
+
+// 		var pt0 = points[ c[0] ],
+// 			pt1 = points[ c[1] ],
+// 			pt2 = points[ c[2] ],
+// 			pt3 = points[ c[3] ];
+
+// 	// t = weight;
+// 	v.x = THREE.Curve.Utils.tangentSpline( t, pt0.x, pt1.x, pt2.x, pt3.x );
+// 	v.y = THREE.Curve.Utils.tangentSpline( t, pt0.y, pt1.y, pt2.y, pt3.y );
+// 	v.z = THREE.Curve.Utils.tangentSpline( t, pt0.z, pt1.z, pt2.z, pt3.z );
+
+// 	return v;
+
+// }
+/**************************************************************
+ *	Closed Spline 3D curve
+ **************************************************************/
+
+
+THREE.ClosedSplineCurve3 = THREE.Curve.create(
+
+	function ( points /* array of Vector3 */) {
+
+		this.points = (points == undefined) ? [] : points;
+
+	},
+
+    function ( t ) {
+
+        var v = new THREE.Vector3();
+        var c = [];
+        var points = this.points, point, intPoint, weight;
+        point = ( points.length - 0 ) * t;
+            // This needs to be from 0-length +1
+
+        intPoint = Math.floor( point );
+        weight = point - intPoint;
+
+        intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / points.length ) + 1 ) * points.length;
+        c[ 0 ] = ( intPoint - 1 ) % points.length;
+        c[ 1 ] = ( intPoint ) % points.length;
+        c[ 2 ] = ( intPoint + 1 ) % points.length;
+        c[ 3 ] = ( intPoint + 2 ) % points.length;
+
+        v.x = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].x, points[ c[ 1 ] ].x, points[ c[ 2 ] ].x, points[ c[ 3 ] ].x, weight );
+        v.y = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].y, points[ c[ 1 ] ].y, points[ c[ 2 ] ].y, points[ c[ 3 ] ].y, weight );
+        v.z = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].z, points[ c[ 1 ] ].z, points[ c[ 2 ] ].z, points[ c[ 3 ] ].z, weight );
+
+        return v;
+
+    }
+
+);
+/**
+ * @author mikael emtinger / http://gomo.se/
+ */
+
+THREE.AnimationHandler = (function() {
+
+	var playing = [];
+	var library = {};
+	var that    = {};
+
+
+	//--- update ---
+
+	that.update = function( deltaTimeMS ) {
+
+		for( var i = 0; i < playing.length; i ++ )
+			playing[ i ].update( deltaTimeMS );
+
+	};
+
+
+	//--- add ---
+
+	that.addToUpdate = function( animation ) {
+
+		if ( playing.indexOf( animation ) === -1 )
+			playing.push( animation );
+
+	};
+
+
+	//--- remove ---
+
+	that.removeFromUpdate = function( animation ) {
+
+		var index = playing.indexOf( animation );
+
+		if( index !== -1 )
+			playing.splice( index, 1 );
+
+	};
+
+
+	//--- add ---
+
+	that.add = function( data ) {
+
+		if ( library[ data.name ] !== undefined )
+			console.log( "THREE.AnimationHandler.add: Warning! " + data.name + " already exists in library. Overwriting." );
+
+		library[ data.name ] = data;
+		initData( data );
+
+	};
+
+
+	//--- get ---
+
+	that.get = function( name ) {
+
+		if ( typeof name === "string" ) {
+
+			if ( library[ name ] ) {
+
+				return library[ name ];
+
+			} else {
+
+				console.log( "THREE.AnimationHandler.get: Couldn't find animation " + name );
+				return null;
+
+			}
+
+		} else {
+
+			// todo: add simple tween library
+
+		}
+
+	};
+
+	//--- parse ---
+
+	that.parse = function( root ) {
+
+		// setup hierarchy
+
+		var hierarchy = [];
+
+		if ( root instanceof THREE.SkinnedMesh ) {
+
+			for( var b = 0; b < root.bones.length; b++ ) {
+
+				hierarchy.push( root.bones[ b ] );
+
+			}
+
+		} else {
+
+			parseRecurseHierarchy( root, hierarchy );
+
+		}
+
+		return hierarchy;
+
+	};
+
+	var parseRecurseHierarchy = function( root, hierarchy ) {
+
+		hierarchy.push( root );
+
+		for( var c = 0; c < root.children.length; c++ )
+			parseRecurseHierarchy( root.children[ c ], hierarchy );
+
+	}
+
+
+	//--- init data ---
+
+	var initData = function( data ) {
+
+		if( data.initialized === true )
+			return;
+
+
+		// 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( quat[0], quat[1], quat[2], quat[3] );
+
+				}
+
+			}
+
+
+			// 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;
+
+			}
+
+		}
+
+
+		// JIT
+
+		var lengthInFrames = parseInt( data.length * data.fps, 10 );
+
+		data.JIT = {};
+		data.JIT.hierarchy = [];
+
+		for( var h = 0; h < data.hierarchy.length; h ++ )
+			data.JIT.hierarchy.push( new Array( lengthInFrames ) );
+
+
+		// done
+
+		data.initialized = true;
+
+	};
+
+
+	// interpolation types
+
+	that.LINEAR = 0;
+	that.CATMULLROM = 1;
+	that.CATMULLROM_FORWARD = 2;
+
+	return that;
+
+}());
+
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.Animation = function ( root, name, interpolationType ) {
+
+	this.root = root;
+	this.data = THREE.AnimationHandler.get( name );
+	this.hierarchy = THREE.AnimationHandler.parse( root );
+
+	this.currentTime = 0;
+	this.timeScale = 1;
+
+	this.isPlaying = false;
+	this.isPaused = true;
+	this.loop = true;
+
+	this.interpolationType = interpolationType !== undefined ? interpolationType : THREE.AnimationHandler.LINEAR;
+
+	this.points = [];
+	this.target = new THREE.Vector3();
+
+};
+
+THREE.Animation.prototype.play = function ( loop, startTimeMS ) {
+
+	if ( this.isPlaying === false ) {
+
+		this.isPlaying = true;
+		this.loop = loop !== undefined ? loop : true;
+		this.currentTime = startTimeMS !== undefined ? startTimeMS : 0;
+
+		// reset key cache
+
+		var h, hl = this.hierarchy.length,
+			object;
+
+		for ( h = 0; h < hl; h ++ ) {
+
+			object = this.hierarchy[ h ];
+
+			object.matrixAutoUpdate = true;
+
+			if ( object.animationCache === undefined ) {
+
+				object.animationCache = {};
+				object.animationCache.prevKey = { pos: 0, rot: 0, scl: 0 };
+				object.animationCache.nextKey = { pos: 0, rot: 0, scl: 0 };
+				object.animationCache.originalMatrix = object instanceof THREE.Bone ? object.skinMatrix : object.matrix;
+
+			}
+
+			var prevKey = object.animationCache.prevKey;
+			var nextKey = object.animationCache.nextKey;
+
+			prevKey.pos = this.data.hierarchy[ h ].keys[ 0 ];
+			prevKey.rot = this.data.hierarchy[ h ].keys[ 0 ];
+			prevKey.scl = this.data.hierarchy[ h ].keys[ 0 ];
+
+			nextKey.pos = this.getNextKeyWith( "pos", h, 1 );
+			nextKey.rot = this.getNextKeyWith( "rot", h, 1 );
+			nextKey.scl = this.getNextKeyWith( "scl", h, 1 );
+
+		}
+
+		this.update( 0 );
+
+	}
+
+	this.isPaused = false;
+
+	THREE.AnimationHandler.addToUpdate( this );
+
+};
+
+
+THREE.Animation.prototype.pause = function() {
+
+	if ( this.isPaused === true ) {
+
+		THREE.AnimationHandler.addToUpdate( this );
+
+	} else {
+
+		THREE.AnimationHandler.removeFromUpdate( this );
+
+	}
+
+	this.isPaused = !this.isPaused;
+
+};
+
+
+THREE.Animation.prototype.stop = function() {
+
+	this.isPlaying = false;
+	this.isPaused  = false;
+	THREE.AnimationHandler.removeFromUpdate( this );
+
+};
+
+
+THREE.Animation.prototype.update = function ( deltaTimeMS ) {
+
+	// early out
+
+	if ( this.isPlaying === false ) return;
+
+
+	// vars
+
+	var types = [ "pos", "rot", "scl" ];
+	var type;
+	var scale;
+	var vector;
+	var prevXYZ, nextXYZ;
+	var prevKey, nextKey;
+	var object;
+	var animationCache;
+	var frame;
+	var JIThierarchy = this.data.JIT.hierarchy;
+	var currentTime, unloopedCurrentTime;
+	var currentPoint, forwardPoint, angle;
+
+
+	this.currentTime += deltaTimeMS * this.timeScale;
+
+	unloopedCurrentTime = this.currentTime;
+	currentTime = this.currentTime = this.currentTime % this.data.length;
+	frame = parseInt( Math.min( currentTime * this.data.fps, this.data.length * this.data.fps ), 10 );
+
+
+	for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) {
+
+		object = this.hierarchy[ h ];
+		animationCache = object.animationCache;
+
+		// loop through pos/rot/scl
+
+		for ( var t = 0; t < 3; t ++ ) {
+
+			// get keys
+
+			type    = types[ t ];
+			prevKey = animationCache.prevKey[ type ];
+			nextKey = animationCache.nextKey[ type ];
+
+			// switch keys?
+
+			if ( nextKey.time <= unloopedCurrentTime ) {
+
+				// did we loop?
+
+				if ( currentTime < unloopedCurrentTime ) {
+
+					if ( this.loop ) {
+
+						prevKey = this.data.hierarchy[ h ].keys[ 0 ];
+						nextKey = this.getNextKeyWith( type, h, 1 );
+
+						while( nextKey.time < currentTime ) {
+
+							prevKey = nextKey;
+							nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 );
+
+						}
+
+					} else {
+
+						this.stop();
+						return;
+
+					}
+
+				} else {
+
+					do {
+
+						prevKey = nextKey;
+						nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 );
+
+					} while( nextKey.time < currentTime )
+
+				}
+
+				animationCache.prevKey[ type ] = prevKey;
+				animationCache.nextKey[ type ] = nextKey;
+
+			}
+
+
+			object.matrixAutoUpdate = true;
+			object.matrixWorldNeedsUpdate = true;
+
+			scale = ( currentTime - prevKey.time ) / ( nextKey.time - prevKey.time );
+			prevXYZ = prevKey[ type ];
+			nextXYZ = nextKey[ type ];
+
+
+			// check scale error
+
+			if ( scale < 0 || scale > 1 ) {
+
+				console.log( "THREE.Animation.update: Warning! Scale out of bounds:" + scale + " on bone " + h );
+				scale = scale < 0 ? 0 : 1;
+
+			}
+
+			// interpolate
+
+			if ( type === "pos" ) {
+
+				vector = object.position;
+
+				if ( this.interpolationType === THREE.AnimationHandler.LINEAR ) {
+
+					vector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale;
+					vector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale;
+					vector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale;
+
+				} else if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM ||
+						    this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
+
+					this.points[ 0 ] = this.getPrevKeyWith( "pos", h, prevKey.index - 1 )[ "pos" ];
+					this.points[ 1 ] = prevXYZ;
+					this.points[ 2 ] = nextXYZ;
+					this.points[ 3 ] = this.getNextKeyWith( "pos", h, nextKey.index + 1 )[ "pos" ];
+
+					scale = scale * 0.33 + 0.33;
+
+					currentPoint = this.interpolateCatmullRom( this.points, scale );
+
+					vector.x = currentPoint[ 0 ];
+					vector.y = currentPoint[ 1 ];
+					vector.z = currentPoint[ 2 ];
+
+					if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
+
+						forwardPoint = this.interpolateCatmullRom( this.points, scale * 1.01 );
+
+						this.target.set( forwardPoint[ 0 ], forwardPoint[ 1 ], forwardPoint[ 2 ] );
+						this.target.sub( vector );
+						this.target.y = 0;
+						this.target.normalize();
+
+						angle = Math.atan2( this.target.x, this.target.z );
+						object.rotation.set( 0, angle, 0 );
+
+					}
+
+				}
+
+			} else if ( type === "rot" ) {
+
+				THREE.Quaternion.slerp( prevXYZ, nextXYZ, object.quaternion, scale );
+
+			} else if ( type === "scl" ) {
+
+				vector = object.scale;
+
+				vector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale;
+				vector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale;
+				vector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale;
+
+			}
+
+		}
+
+	}
+
+};
+
+// Catmull-Rom spline
+
+THREE.Animation.prototype.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 ] = this.interpolate( pa[ 0 ], pb[ 0 ], pc[ 0 ], pd[ 0 ], weight, w2, w3 );
+	v3[ 1 ] = this.interpolate( pa[ 1 ], pb[ 1 ], pc[ 1 ], pd[ 1 ], weight, w2, w3 );
+	v3[ 2 ] = this.interpolate( pa[ 2 ], pb[ 2 ], pc[ 2 ], pd[ 2 ], weight, w2, w3 );
+
+	return v3;
+
+};
+
+THREE.Animation.prototype.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;
+
+};
+
+
+
+// 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 ];
+
+};
+
+/**
+ * @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( root, data, JITCompile ) {
+
+	this.root = root;
+	this.data = THREE.AnimationHandler.get( data );
+	this.hierarchy = THREE.AnimationHandler.parse( root );
+	this.currentTime = 0;
+	this.timeScale = 0.001;
+	this.isPlaying = false;
+	this.isPaused = true;
+	this.loop = true;
+	this.JITCompile = JITCompile !== undefined ? JITCompile : 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;
+
+		}
+
+	}
+
+};
+
+// Play
+
+THREE.KeyFrameAnimation.prototype.play = function( loop, startTimeMS ) {
+
+	if( !this.isPlaying ) {
+
+		this.isPlaying = true;
+		this.loop = loop !== undefined ? loop : true;
+		this.currentTime = startTimeMS !== undefined ? startTimeMS : 0;
+		this.startTimeMs = startTimeMS;
+		this.startTime = 10000000;
+		this.endTime = -this.startTime;
+
+
+		// 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 instanceof THREE.Bone ? object.skinMatrix : 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.addToUpdate( this );
+
+};
+
+
+
+// Pause
+
+THREE.KeyFrameAnimation.prototype.pause = function() {
+
+	if( this.isPaused ) {
+
+		THREE.AnimationHandler.addToUpdate( this );
+
+	} else {
+
+		THREE.AnimationHandler.removeFromUpdate( this );
+
+	}
+
+	this.isPaused = !this.isPaused;
+
+};
+
+
+// Stop
+
+THREE.KeyFrameAnimation.prototype.stop = function() {
+
+	this.isPlaying = false;
+	this.isPaused  = false;
+	THREE.AnimationHandler.removeFromUpdate( 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;
+
+			if( obj instanceof THREE.Bone ) {
+
+				original.copy( obj.skinMatrix );
+				obj.skinMatrix = original;
+
+			} else {
+
+				original.copy( obj.matrix );
+				obj.matrix = original;
+
+			}
+
+			delete node.animationCache;
+
+		}
+
+	}
+
+};
+
+
+// Update
+
+THREE.KeyFrameAnimation.prototype.update = function( deltaTimeMS ) {
+
+	// early out
+
+	if( !this.isPlaying ) return;
+
+
+	// vars
+
+	var prevKey, nextKey;
+	var object;
+	var node;
+	var frame;
+	var JIThierarchy = this.data.JIT.hierarchy;
+	var currentTime, unloopedCurrentTime;
+	var looped;
+
+
+	// update
+
+	this.currentTime += deltaTimeMS * this.timeScale;
+
+	unloopedCurrentTime = this.currentTime;
+	currentTime         = this.currentTime = this.currentTime % this.data.length;
+
+	// if looped around, the current time should be based on the startTime
+	if ( currentTime < this.startTimeMs ) {
+
+		currentTime = this.currentTime = this.startTimeMs + currentTime;
+
+	}
+
+	frame               = parseInt( Math.min( currentTime * this.data.fps, this.data.length * this.data.fps ), 10 );
+	looped 				= currentTime < unloopedCurrentTime;
+
+	if ( looped && !this.loop ) {
+
+		// Set the animation to the last keyframes and stop
+		for ( var h = 0, hl = this.hierarchy.length; h < hl; h++ ) {
+
+			var keys = this.data.hierarchy[h].keys,
+				sids = this.data.hierarchy[h].sids,
+				end = keys.length-1,
+				obj = this.hierarchy[h];
+
+			if ( keys.length ) {
+
+				for ( var s = 0; s < sids.length; s++ ) {
+
+					var sid = sids[ s ],
+						prev = this.getPrevKeyWith( sid, h, end );
+
+					if ( prev ) {
+						prev.apply( sid );
+
+					}
+
+				}
+
+				this.data.hierarchy[h].node.updateMatrix();
+				obj.matrixWorldNeedsUpdate = true;
+
+			}
+
+		}
+
+		this.stop();
+		return;
+
+	}
+
+	// check pre-infinity
+	if ( currentTime < this.startTime ) {
+
+		return;
+
+	}
+
+	// update
+
+	for ( var h = 0, hl = this.hierarchy.length; h < hl; h++ ) {
+
+		object = this.hierarchy[ h ];
+		node = this.data.hierarchy[ h ];
+
+		var keys = node.keys,
+			animationCache = node.animationCache;
+
+		// use JIT?
+
+		if ( this.JITCompile && JIThierarchy[ h ][ frame ] !== undefined ) {
+
+			if( object instanceof THREE.Bone ) {
+
+				object.skinMatrix = JIThierarchy[ h ][ frame ];
+				object.matrixWorldNeedsUpdate = false;
+
+			} else {
+
+				object.matrix = JIThierarchy[ h ][ frame ];
+				object.matrixWorldNeedsUpdate = true;
+
+			}
+
+		// use interpolation
+
+		} else if ( keys.length ) {
+
+			// make sure so original matrix and not JIT matrix is set
+
+			if ( this.JITCompile && animationCache ) {
+
+				if( object instanceof THREE.Bone ) {
+
+					object.skinMatrix = animationCache.originalMatrix;
+
+				} else {
+
+					object.matrix = animationCache.originalMatrix;
+
+				}
+
+			}
+
+			prevKey = animationCache.prevKey;
+			nextKey = animationCache.nextKey;
+
+			if ( prevKey && nextKey ) {
+
+				// switch keys?
+
+				if ( nextKey.time <= unloopedCurrentTime ) {
+
+					// did we loop?
+
+					if ( looped && this.loop ) {
+
+						prevKey = keys[ 0 ];
+						nextKey = keys[ 1 ];
+
+						while ( nextKey.time < currentTime ) {
+
+							prevKey = nextKey;
+							nextKey = keys[ prevKey.index + 1 ];
+
+						}
+
+					} else if ( !looped ) {
+
+						var lastIndex = keys.length - 1;
+
+						while ( nextKey.time < currentTime && nextKey.index !== lastIndex ) {
+
+							prevKey = nextKey;
+							nextKey = keys[ prevKey.index + 1 ];
+
+						}
+
+					}
+
+					animationCache.prevKey = prevKey;
+					animationCache.nextKey = nextKey;
+
+				}
+                if(nextKey.time >= currentTime)
+                    prevKey.interpolate( nextKey, currentTime );
+                else
+                    prevKey.interpolate( nextKey, nextKey.time);
+
+			}
+
+			this.data.hierarchy[h].node.updateMatrix();
+			object.matrixWorldNeedsUpdate = true;
+
+		}
+
+	}
+
+	// update JIT?
+
+	if ( this.JITCompile ) {
+
+		if ( JIThierarchy[ 0 ][ frame ] === undefined ) {
+
+			this.hierarchy[ 0 ].updateMatrixWorld( true );
+
+			for ( var h = 0; h < this.hierarchy.length; h++ ) {
+
+				if( this.hierarchy[ h ] instanceof THREE.Bone ) {
+
+					JIThierarchy[ h ][ frame ] = this.hierarchy[ h ].skinMatrix.clone();
+
+				} else {
+
+					JIThierarchy[ h ][ frame ] = this.hierarchy[ h ].matrix.clone();
+
+				}
+
+			}
+
+		}
+
+	}
+
+};
+
+// 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 ];
+
+};
+
+/**
+ * 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 );
+
+	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 );
+
+/*
+ *	@author zz85 / http://twitter.com/blurspline / http://www.lab4games.net/zz85/blog
+ *
+ *	A general perpose camera, for setting FOV, Lens Focal Length,
+ *		and switching between perspective and orthographic views easily.
+ *		Use this only if you do not wish to manage
+ *		both a Orthographic and Perspective Camera
+ *
+ */
+
+
+THREE.CombinedCamera = function ( width, height, fov, near, far, orthoNear, orthoFar ) {
+
+	THREE.Camera.call( this );
+
+	this.fov = fov;
+
+	this.left = -width / 2;
+	this.right = width / 2
+	this.top = height / 2;
+	this.bottom = -height / 2;
+
+	// We could also handle the projectionMatrix internally, but just wanted to test nested camera objects
+
+	this.cameraO = new THREE.OrthographicCamera( width / - 2, width / 2, height / 2, height / - 2, 	orthoNear, orthoFar );
+	this.cameraP = new THREE.PerspectiveCamera( fov, width / height, near, far );
+
+	this.zoom = 1;
+
+	this.toPerspective();
+
+	var aspect = width/height;
+
+};
+
+THREE.CombinedCamera.prototype = Object.create( THREE.Camera.prototype );
+
+THREE.CombinedCamera.prototype.toPerspective = function () {
+
+	// Switches to the Perspective Camera
+
+	this.near = this.cameraP.near;
+	this.far = this.cameraP.far;
+
+	this.cameraP.fov =  this.fov / this.zoom ;
+
+	this.cameraP.updateProjectionMatrix();
+
+	this.projectionMatrix = this.cameraP.projectionMatrix;
+
+	this.inPerspectiveMode = true;
+	this.inOrthographicMode = false;
+
+};
+
+THREE.CombinedCamera.prototype.toOrthographic = function () {
+
+	// Switches to the Orthographic camera estimating viewport from Perspective
+
+	var fov = this.fov;
+	var aspect = this.cameraP.aspect;
+	var near = this.cameraP.near;
+	var far = this.cameraP.far;
+
+	// The size that we set is the mid plane of the viewing frustum
+
+	var hyperfocus = ( near + far ) / 2;
+
+	var halfHeight = Math.tan( fov / 2 ) * hyperfocus;
+	var planeHeight = 2 * halfHeight;
+	var planeWidth = planeHeight * aspect;
+	var halfWidth = planeWidth / 2;
+
+	halfHeight /= this.zoom;
+	halfWidth /= this.zoom;
+
+	this.cameraO.left = -halfWidth;
+	this.cameraO.right = halfWidth;
+	this.cameraO.top = halfHeight;
+	this.cameraO.bottom = -halfHeight;
+
+	// this.cameraO.left = -farHalfWidth;
+	// this.cameraO.right = farHalfWidth;
+	// this.cameraO.top = farHalfHeight;
+	// this.cameraO.bottom = -farHalfHeight;
+
+	// this.cameraO.left = this.left / this.zoom;
+	// this.cameraO.right = this.right / this.zoom;
+	// this.cameraO.top = this.top / this.zoom;
+	// this.cameraO.bottom = this.bottom / this.zoom;
+
+	this.cameraO.updateProjectionMatrix();
+
+	this.near = this.cameraO.near;
+	this.far = this.cameraO.far;
+	this.projectionMatrix = this.cameraO.projectionMatrix;
+
+	this.inPerspectiveMode = false;
+	this.inOrthographicMode = true;
+
+};
+
+
+THREE.CombinedCamera.prototype.setSize = function( width, height ) {
+
+	this.cameraP.aspect = width / height;
+	this.left = -width / 2;
+	this.right = width / 2
+	this.top = height / 2;
+	this.bottom = -height / 2;
+
+};
+
+
+THREE.CombinedCamera.prototype.setFov = function( fov ) {
+
+	this.fov = fov;
+
+	if ( this.inPerspectiveMode ) {
+
+		this.toPerspective();
+
+	} else {
+
+		this.toOrthographic();
+
+	}
+
+};
+
+// For mantaining similar API with PerspectiveCamera
+
+THREE.CombinedCamera.prototype.updateProjectionMatrix = function() {
+
+	if ( this.inPerspectiveMode ) {
+
+		this.toPerspective();
+
+	} else {
+
+		this.toPerspective();
+		this.toOrthographic();
+
+	}
+
+};
+
+/*
+* 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.CombinedCamera.prototype.setLens = function ( focalLength, frameHeight ) {
+
+	if ( frameHeight === undefined ) frameHeight = 24;
+
+	var fov = 2 * THREE.Math.radToDeg( Math.atan( frameHeight / ( focalLength * 2 ) ) );
+
+	this.setFov( fov );
+
+	return fov;
+};
+
+
+THREE.CombinedCamera.prototype.setZoom = function( zoom ) {
+
+	this.zoom = zoom;
+
+	if ( this.inPerspectiveMode ) {
+
+		this.toPerspective();
+
+	} else {
+
+		this.toOrthographic();
+
+	}
+
+};
+
+THREE.CombinedCamera.prototype.toFrontView = function() {
+
+	this.rotation.x = 0;
+	this.rotation.y = 0;
+	this.rotation.z = 0;
+
+	// should we be modifing the matrix instead?
+
+	this.rotationAutoUpdate = false;
+
+};
+
+THREE.CombinedCamera.prototype.toBackView = function() {
+
+	this.rotation.x = 0;
+	this.rotation.y = Math.PI;
+	this.rotation.z = 0;
+	this.rotationAutoUpdate = false;
+
+};
+
+THREE.CombinedCamera.prototype.toLeftView = function() {
+
+	this.rotation.x = 0;
+	this.rotation.y = - Math.PI / 2;
+	this.rotation.z = 0;
+	this.rotationAutoUpdate = false;
+
+};
+
+THREE.CombinedCamera.prototype.toRightView = function() {
+
+	this.rotation.x = 0;
+	this.rotation.y = Math.PI / 2;
+	this.rotation.z = 0;
+	this.rotationAutoUpdate = false;
+
+};
+
+THREE.CombinedCamera.prototype.toTopView = function() {
+
+	this.rotation.x = - Math.PI / 2;
+	this.rotation.y = 0;
+	this.rotation.z = 0;
+	this.rotationAutoUpdate = false;
+
+};
+
+THREE.CombinedCamera.prototype.toBottomView = function() {
+
+	this.rotation.x = Math.PI / 2;
+	this.rotation.y = 0;
+	this.rotation.z = 0;
+	this.rotationAutoUpdate = false;
+
+};
+
+
+/**
+ * @author hughes
+ */
+
+THREE.CircleGeometry = function ( radius, segments, thetaStart, thetaLength ) {
+
+	THREE.Geometry.call( this );
+
+	this.radius = radius = radius || 50;
+	this.segments = segments = segments !== undefined ? Math.max( 3, segments ) : 8;
+
+	this.thetaStart = thetaStart = thetaStart !== undefined ? thetaStart : 0;
+	this.thetaLength = 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 ++ ) {
+
+		var v1 = i;
+		var v2 = i + 1 ;
+		var v3 = 0;
+
+		this.faces.push( new THREE.Face3( v1, v2, v3, [ n, n, n ] ) );
+		this.faceVertexUvs[ 0 ].push( [ uvs[ i ], uvs[ i + 1 ], centerUV ] );
+
+	}
+
+	this.computeCentroids();
+	this.computeFaceNormals();
+
+	this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius );
+
+};
+
+THREE.CircleGeometry.prototype = Object.create( THREE.Geometry.prototype );
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Cube.as
+ */
+
+THREE.CubeGeometry = function ( width, height, depth, widthSegments, heightSegments, depthSegments ) {
+
+	THREE.Geometry.call( this );
+
+	var scope = this;
+
+	this.width = width;
+	this.height = height;
+	this.depth = depth;
+
+	this.widthSegments = widthSegments || 1;
+	this.heightSegments = heightSegments || 1;
+	this.depthSegments = depthSegments || 1;
+
+	var width_half = this.width / 2;
+	var height_half = this.height / 2;
+	var depth_half = this.depth / 2;
+
+	buildPlane( 'z', 'y', - 1, - 1, this.depth, this.height, width_half, 0 ); // px
+	buildPlane( 'z', 'y',   1, - 1, this.depth, this.height, - width_half, 1 ); // nx
+	buildPlane( 'x', 'z',   1,   1, this.width, this.depth, height_half, 2 ); // py
+	buildPlane( 'x', 'z',   1, - 1, this.width, this.depth, - height_half, 3 ); // ny
+	buildPlane( 'x', 'y',   1, - 1, this.width, this.height, depth_half, 4 ); // pz
+	buildPlane( 'x', 'y', - 1, - 1, this.width, this.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.computeCentroids();
+	this.mergeVertices();
+
+};
+
+THREE.CubeGeometry.prototype = Object.create( THREE.Geometry.prototype );
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.CylinderGeometry = function ( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded ) {
+
+	THREE.Geometry.call( this );
+
+	this.radiusTop = radiusTop = radiusTop !== undefined ? radiusTop : 20;
+	this.radiusBottom = radiusBottom = radiusBottom !== undefined ? radiusBottom : 20;
+	this.height = height = height !== undefined ? height : 100;
+
+	this.radialSegments = radialSegments = radialSegments || 8;
+	this.heightSegments = heightSegments = heightSegments || 1;
+
+	this.openEnded = openEnded = openEnded !== undefined ? openEnded : false;
+
+	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 * Math.PI * 2 );
+			vertex.y = - v * height + heightHalf;
+			vertex.z = radius * Math.cos( u * Math.PI * 2 );
+
+			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, n3, n4 ] ) );
+			this.faceVertexUvs[ 0 ].push( [ uv2, uv3, uv4 ] );
+
+		}
+
+	}
+
+	// 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.u, 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[ y ][ x + 1 ];
+			var v2 = vertices[ y ][ 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[ y ][ x + 1 ].clone();
+			var uv2 = uvs[ y ][ x ].clone();
+			var uv3 = new THREE.Vector2( uv2.u, 1 );
+
+			this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) );
+			this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] );
+
+		}
+
+	}
+
+	this.computeCentroids();
+	this.computeFaceNormals();
+
+}
+
+THREE.CylinderGeometry.prototype = Object.create( THREE.Geometry.prototype );
+
+/**
+ * @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 );
+
+	shapes = shapes instanceof Array ? shapes : [ shapes ];
+
+	this.shapebb = shapes[ shapes.length - 1 ].getBoundingBox();
+
+	this.addShapeList( shapes, options );
+
+	this.computeCentroids();
+	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.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 shapebb = this.shapebb;
+	//shapebb = shape.getBoundingBox();
+
+
+
+	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( pt_i, pt_j, pt_k ) {
+
+		// Algorithm 2
+
+		return getBevelVec2( pt_i, pt_j, pt_k );
+
+	}
+
+	function getBevelVec1( pt_i, pt_j, pt_k ) {
+
+		var anglea = Math.atan2( pt_j.y - pt_i.y, pt_j.x - pt_i.x );
+		var angleb = Math.atan2( pt_k.y - pt_i.y, pt_k.x - pt_i.x );
+
+		if ( anglea > angleb ) {
+
+			angleb += Math.PI * 2;
+
+		}
+
+		var anglec = ( anglea + angleb ) / 2;
+
+
+		//console.log('angle1', anglea * RAD_TO_DEGREES,'angle2', angleb * RAD_TO_DEGREES, 'anglec', anglec *RAD_TO_DEGREES);
+
+		var x = - Math.cos( anglec );
+		var y = - Math.sin( anglec );
+
+		var vec = new THREE.Vector2( x, y ); //.normalize();
+
+		return vec;
+
+	}
+
+	function getBevelVec2( pt_i, pt_j, pt_k ) {
+
+		var a = THREE.ExtrudeGeometry.__v1,
+			b = THREE.ExtrudeGeometry.__v2,
+			v_hat = THREE.ExtrudeGeometry.__v3,
+			w_hat = THREE.ExtrudeGeometry.__v4,
+			p = THREE.ExtrudeGeometry.__v5,
+			q = THREE.ExtrudeGeometry.__v6,
+			v, w,
+			v_dot_w_hat, q_sub_p_dot_w_hat,
+			s, intersection;
+
+		// good reading for line-line intersection
+		// http://sputsoft.com/blog/2010/03/line-line-intersection.html
+
+		// define a as vector j->i
+		// define b as vectot k->i
+
+		a.set( pt_i.x - pt_j.x, pt_i.y - pt_j.y );
+		b.set( pt_i.x - pt_k.x, pt_i.y - pt_k.y );
+
+		// get unit vectors
+
+		v = a.normalize();
+		w = b.normalize();
+
+		// normals from pt i
+
+		v_hat.set( -v.y, v.x );
+		w_hat.set( w.y, -w.x );
+
+		// pts from i
+
+		p.copy( pt_i ).add( v_hat );
+		q.copy( pt_i ).add( w_hat );
+
+		if ( p.equals( q ) ) {
+
+			//console.log("Warning: lines are straight");
+			return w_hat.clone();
+
+		}
+
+		// Points from j, k. helps prevents points cross overover most of the time
+
+		p.copy( pt_j ).add( v_hat );
+		q.copy( pt_k ).add( w_hat );
+
+		v_dot_w_hat = v.dot( w_hat );
+		q_sub_p_dot_w_hat = q.sub( p ).dot( w_hat );
+
+		// We should not reach these conditions
+
+		if ( v_dot_w_hat === 0 ) {
+
+			console.log( "Either infinite or no solutions!" );
+
+			if ( q_sub_p_dot_w_hat === 0 ) {
+
+				console.log( "Its finite solutions." );
+
+			} else {
+
+				console.log( "Too bad, no solutions." );
+
+			}
+
+		}
+
+		s = q_sub_p_dot_w_hat / v_dot_w_hat;
+
+		if ( s < 0 ) {
+
+			// in case of emergecy, revert to algorithm 1.
+
+			return getBevelVec1( pt_i, pt_j, pt_k );
+
+		}
+
+		intersection = v.multiplyScalar( s ).add( p );
+
+		return intersection.sub( pt_i ).clone(); // Don't normalize!, otherwise sharp corners become ugly
+
+	}
+
+	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 );
+			//vert = scalePt( contour[ i ], contourCentroid, bs, false );
+			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 );
+				//vert = scalePt( ahole[ i ], holesCentroids[ h ], bs, true );
+
+				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, true );
+
+			}
+
+			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, false );
+
+			}
+
+		} else {
+
+			// Bottom faces
+
+			for ( i = 0; i < flen; i++ ) {
+
+				face = faces[ i ];
+				f3( face[ 2 ], face[ 1 ], face[ 0 ], true );
+
+			}
+
+			// Top faces
+
+			for ( i = 0; i < flen; i ++ ) {
+
+				face = faces[ i ];
+				f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps, false );
+
+			}
+		}
+
+	}
+
+	// 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, isBottom ) {
+
+		a += shapesOffset;
+		b += shapesOffset;
+		c += shapesOffset;
+
+		// normal, color, material
+		scope.faces.push( new THREE.Face3( a, b, c, null, null, material ) );
+
+		var uvs = isBottom ? uvgen.generateBottomUV( scope, shape, options, a, b, c ) : uvgen.generateTopUV( scope, shape, options, 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, shape, wallContour, options, a, b, c, d,
+ 		                                    stepIndex, stepsLength, contourIndex1, contourIndex2 );
+
+ 		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, extrudedShape, extrudeOptions, indexA, indexB, indexC ) {
+		var ax = geometry.vertices[ indexA ].x,
+			ay = geometry.vertices[ indexA ].y,
+
+			bx = geometry.vertices[ indexB ].x,
+			by = geometry.vertices[ indexB ].y,
+
+			cx = geometry.vertices[ indexC ].x,
+			cy = geometry.vertices[ indexC ].y;
+
+		return [
+			new THREE.Vector2( ax, ay ),
+			new THREE.Vector2( bx, by ),
+			new THREE.Vector2( cx, cy )
+		];
+
+	},
+
+	generateBottomUV: function( geometry, extrudedShape, extrudeOptions, indexA, indexB, indexC ) {
+
+		return this.generateTopUV( geometry, extrudedShape, extrudeOptions, indexA, indexB, indexC );
+
+	},
+
+	generateSideWallUV: function( geometry, extrudedShape, wallContour, extrudeOptions,
+	                              indexA, indexB, indexC, indexD, stepIndex, stepsLength,
+	                              contourIndex1, contourIndex2 ) {
+
+		var ax = geometry.vertices[ indexA ].x,
+			ay = geometry.vertices[ indexA ].y,
+			az = geometry.vertices[ indexA ].z,
+
+			bx = geometry.vertices[ indexB ].x,
+			by = geometry.vertices[ indexB ].y,
+			bz = geometry.vertices[ indexB ].z,
+
+			cx = geometry.vertices[ indexC ].x,
+			cy = geometry.vertices[ indexC ].y,
+			cz = geometry.vertices[ indexC ].z,
+
+			dx = geometry.vertices[ indexD ].x,
+			dy = geometry.vertices[ indexD ].y,
+			dz = geometry.vertices[ indexD ].z;
+
+		if ( Math.abs( ay - by ) < 0.01 ) {
+			return [
+				new THREE.Vector2( ax, 1 - az ),
+				new THREE.Vector2( bx, 1 - bz ),
+				new THREE.Vector2( cx, 1 - cz ),
+				new THREE.Vector2( dx, 1 - dz )
+			];
+		} else {
+			return [
+				new THREE.Vector2( ay, 1 - az ),
+				new THREE.Vector2( by, 1 - bz ),
+				new THREE.Vector2( cy, 1 - cz ),
+				new THREE.Vector2( dy, 1 - dz )
+			];
+		}
+	}
+};
+
+THREE.ExtrudeGeometry.__v1 = new THREE.Vector2();
+THREE.ExtrudeGeometry.__v2 = new THREE.Vector2();
+THREE.ExtrudeGeometry.__v3 = new THREE.Vector2();
+THREE.ExtrudeGeometry.__v4 = new THREE.Vector2();
+THREE.ExtrudeGeometry.__v5 = new THREE.Vector2();
+THREE.ExtrudeGeometry.__v6 = new THREE.Vector2();
+
+/**
+ * @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 );
+
+	if ( shapes instanceof Array === false ) shapes = [ shapes ];
+
+	this.shapebb = shapes[ shapes.length - 1 ].getBoundingBox();
+
+	this.addShapeList( shapes, options );
+
+	this.computeCentroids();
+	this.computeFaceNormals();
+
+};
+
+THREE.ShapeGeometry.prototype = Object.create( THREE.Geometry.prototype );
+
+/**
+ * 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 shapebb = this.shapebb;
+
+	//
+
+	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.generateBottomUV( this, shape, options, a, b, c ) );
+
+	}
+
+};
+
+/**
+ * @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 );
+
+	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.computeCentroids();
+	this.computeFaceNormals();
+	this.computeVertexNormals();
+
+};
+
+THREE.LatheGeometry.prototype = Object.create( THREE.Geometry.prototype );
+
+/**
+ * @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 ) {
+
+	THREE.Geometry.call( this );
+
+	this.width = width;
+	this.height = height;
+
+	this.widthSegments = widthSegments || 1;
+	this.heightSegments = heightSegments || 1;
+
+	var ix, iz;
+	var width_half = width / 2;
+	var height_half = height / 2;
+
+	var gridX = this.widthSegments;
+	var gridZ = this.heightSegments;
+
+	var gridX1 = gridX + 1;
+	var gridZ1 = gridZ + 1;
+
+	var segment_width = this.width / gridX;
+	var segment_height = this.height / gridZ;
+
+	var normal = new THREE.Vector3( 0, 0, 1 );
+
+	for ( iz = 0; iz < gridZ1; iz ++ ) {
+
+		for ( ix = 0; ix < gridX1; ix ++ ) {
+
+			var x = ix * segment_width - width_half;
+			var y = iz * segment_height - height_half;
+
+			this.vertices.push( new THREE.Vector3( x, - y, 0 ) );
+
+		}
+
+	}
+
+	for ( iz = 0; iz < gridZ; iz ++ ) {
+
+		for ( ix = 0; ix < gridX; ix ++ ) {
+
+			var a = ix + gridX1 * iz;
+			var b = ix + gridX1 * ( iz + 1 );
+			var c = ( ix + 1 ) + gridX1 * ( iz + 1 );
+			var d = ( ix + 1 ) + gridX1 * iz;
+
+			var uva = new THREE.Vector2( ix / gridX, 1 - iz / gridZ );
+			var uvb = new THREE.Vector2( ix / gridX, 1 - ( iz + 1 ) / gridZ );
+			var uvc = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - ( iz + 1 ) / gridZ );
+			var uvd = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - iz / gridZ );
+
+			var face = new THREE.Face3( a, b, d );
+			face.normal.copy( normal );
+			face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() );
+
+			this.faces.push( face );
+			this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] );
+
+			face = new THREE.Face3( b, c, d );
+			face.normal.copy( normal );
+			face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() );
+
+			this.faces.push( face );
+			this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] );
+
+		}
+
+	}
+
+	this.computeCentroids();
+
+};
+
+THREE.PlaneGeometry.prototype = Object.create( THREE.Geometry.prototype );
+
+/**
+ * @author Kaleb Murphy
+ */
+
+THREE.RingGeometry = function ( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) {
+
+	THREE.Geometry.call( this );
+
+	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( 3, phiSegments ) : 8;
+
+	var i, o, uvs = [], radius = innerRadius, radiusStep = ( ( outerRadius - innerRadius ) / phiSegments );
+
+	for ( i = 0; i <= phiSegments; i ++ ) { // concentric circles inside ring
+
+		for ( o = 0; o <= thetaSegments; 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 / radius + 1 ) / 2, - ( vertex.y / radius + 1 ) / 2 + 1 ) );
+		}
+
+		radius += radiusStep;
+
+	}
+
+	var n = new THREE.Vector3( 0, 0, 1 );
+
+	for ( i = 0; i < phiSegments; i ++ ) { // concentric circles inside ring
+
+		var thetaSegment = i * thetaSegments;
+
+		for ( o = 0; o <= thetaSegments; o ++ ) { // number of segments per circle
+
+			var segment = o + thetaSegment;
+
+			var v1 = segment + i;
+			var v2 = segment + thetaSegments + i;
+			var v3 = segment + thetaSegments + 1 + i;
+
+			this.faces.push( new THREE.Face3( v1, v2, v3, [ n, n, n ] ) );
+			this.faceVertexUvs[ 0 ].push( [ uvs[ v1 ], uvs[ v2 ], uvs[ v3 ] ]);
+
+			v1 = segment + i;
+			v2 = segment + thetaSegments + 1 + i;
+			v3 = segment + 1 + i;
+
+			this.faces.push( new THREE.Face3( v1, v2, v3, [ n, n, n ] ) );
+			this.faceVertexUvs[ 0 ].push( [ uvs[ v1 ], uvs[ v2 ], uvs[ v3 ] ]);
+
+		}
+	}
+
+	this.computeCentroids();
+	this.computeFaceNormals();
+
+	this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius );
+
+};
+
+THREE.RingGeometry.prototype = Object.create( THREE.Geometry.prototype );
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.SphereGeometry = function ( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) {
+
+	THREE.Geometry.call( this );
+
+	this.radius = radius = radius || 50;
+
+	this.widthSegments = widthSegments = Math.max( 3, Math.floor( widthSegments ) || 8 );
+	this.heightSegments = heightSegments = Math.max( 2, Math.floor( heightSegments ) || 6 );
+
+	this.phiStart = phiStart = phiStart !== undefined ? phiStart : 0;
+	this.phiLength = phiLength = phiLength !== undefined ? phiLength : Math.PI * 2;
+
+	this.thetaStart = thetaStart = thetaStart !== undefined ? thetaStart : 0;
+	this.thetaLength = 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 < this.heightSegments; y ++ ) {
+
+		for ( x = 0; x < this.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 ) === this.radius ) {
+
+				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 ) === this.radius ) {
+
+				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, n3, n4 ] ) );
+				this.faceVertexUvs[ 0 ].push( [ uv2.clone(), uv3, uv4.clone() ] );
+
+			}
+
+		}
+
+	}
+
+	this.computeCentroids();
+	this.computeFaceNormals();
+
+	this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius );
+
+};
+
+THREE.SphereGeometry.prototype = Object.create( THREE.Geometry.prototype );
+
+/**
+ * @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 );
+
+};
+
+THREE.TextGeometry.prototype = Object.create( THREE.ExtrudeGeometry.prototype );
+
+/**
+ * @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 );
+
+	var scope = this;
+
+	this.radius = radius || 100;
+	this.tube = tube || 40;
+	this.radialSegments = radialSegments || 8;
+	this.tubularSegments = tubularSegments || 6;
+	this.arc = arc || Math.PI * 2;
+
+	var center = new THREE.Vector3(), uvs = [], normals = [];
+
+	for ( var j = 0; j <= this.radialSegments; j ++ ) {
+
+		for ( var i = 0; i <= this.tubularSegments; i ++ ) {
+
+			var u = i / this.tubularSegments * this.arc;
+			var v = j / this.radialSegments * Math.PI * 2;
+
+			center.x = this.radius * Math.cos( u );
+			center.y = this.radius * Math.sin( u );
+
+			var vertex = new THREE.Vector3();
+			vertex.x = ( this.radius + this.tube * Math.cos( v ) ) * Math.cos( u );
+			vertex.y = ( this.radius + this.tube * Math.cos( v ) ) * Math.sin( u );
+			vertex.z = this.tube * Math.sin( v );
+
+			this.vertices.push( vertex );
+
+			uvs.push( new THREE.Vector2( i / this.tubularSegments, j / this.radialSegments ) );
+			normals.push( vertex.clone().sub( center ).normalize() );
+
+		}
+	}
+
+
+	for ( var j = 1; j <= this.radialSegments; j ++ ) {
+
+		for ( var i = 1; i <= this.tubularSegments; i ++ ) {
+
+			var a = ( this.tubularSegments + 1 ) * j + i - 1;
+			var b = ( this.tubularSegments + 1 ) * ( j - 1 ) + i - 1;
+			var c = ( this.tubularSegments + 1 ) * ( j - 1 ) + i;
+			var d = ( this.tubularSegments + 1 ) * j + i;
+
+			var face = new THREE.Face3( a, b, d, [ normals[ a ], normals[ b ], normals[ d ] ] );
+			face.normal.add( normals[ a ] );
+			face.normal.add( normals[ b ] );
+			face.normal.add( normals[ d ] );
+			face.normal.normalize();
+
+			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 ], normals[ c ], normals[ d ] ] );
+			face.normal.add( normals[ b ] );
+			face.normal.add( normals[ c ] );
+			face.normal.add( normals[ d ] );
+			face.normal.normalize();
+
+			this.faces.push( face );
+
+			this.faceVertexUvs[ 0 ].push( [ uvs[ b ].clone(), uvs[ c ].clone(), uvs[ d ].clone() ] );
+		}
+
+	}
+
+	this.computeCentroids();
+
+};
+
+THREE.TorusGeometry.prototype = Object.create( THREE.Geometry.prototype );
+
+/**
+ * @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 );
+
+	var scope = this;
+
+	this.radius = radius || 100;
+	this.tube = tube || 40;
+	this.radialSegments = radialSegments || 64;
+	this.tubularSegments = tubularSegments || 8;
+	this.p = p || 2;
+	this.q = q || 3;
+	this.heightScale = heightScale || 1;
+	this.grid = new Array( this.radialSegments );
+
+	var tang = new THREE.Vector3();
+	var n = new THREE.Vector3();
+	var bitan = new THREE.Vector3();
+
+	for ( var i = 0; i < this.radialSegments; ++ i ) {
+
+		this.grid[ i ] = new Array( this.tubularSegments );
+		var u = i / this.radialSegments * 2 * this.p * Math.PI;
+		var p1 = getPos( u, this.q, this.p, this.radius, this.heightScale );
+		var p2 = getPos( u + 0.01, this.q, this.p, this.radius, this.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 < this.tubularSegments; ++ j ) {
+
+			var v = j / this.tubularSegments * 2 * Math.PI;
+			var cx = - this.tube * Math.cos( v ); // TODO: Hack: Negating it so it faces outside.
+			var cy = this.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;
+
+			this.grid[ i ][ j ] = scope.vertices.push( pos ) - 1;
+
+		}
+
+	}
+
+	for ( var i = 0; i < this.radialSegments; ++ i ) {
+
+		for ( var j = 0; j < this.tubularSegments; ++ j ) {
+
+			var ip = ( i + 1 ) % this.radialSegments;
+			var jp = ( j + 1 ) % this.tubularSegments;
+
+			var a = this.grid[ i ][ j ];
+			var b = this.grid[ ip ][ j ];
+			var c = this.grid[ ip ][ jp ];
+			var d = this.grid[ i ][ jp ];
+
+			var uva = new THREE.Vector2( i / this.radialSegments, j / this.tubularSegments );
+			var uvb = new THREE.Vector2( ( i + 1 ) / this.radialSegments, j / this.tubularSegments );
+			var uvc = new THREE.Vector2( ( i + 1 ) / this.radialSegments, ( j + 1 ) / this.tubularSegments );
+			var uvd = new THREE.Vector2( i / this.radialSegments, ( j + 1 ) / this.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.computeCentroids();
+	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 );
+
+/**
+ * @author WestLangley / https://github.com/WestLangley
+ * @author zz85 / https://github.com/zz85
+ * @author miningold / https://github.com/miningold
+ *
+ * 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 ) {
+
+	THREE.Geometry.call( this );
+
+	this.path = path;
+	this.segments = segments || 64;
+	this.radius = radius || 1;
+	this.radialSegments = radialSegments || 8;
+	this.closed = closed || false;
+
+	this.grid = [];
+
+	var scope = this,
+
+		tangent,
+		normal,
+		binormal,
+
+		numpoints = this.segments + 1,
+
+		x, y, z,
+		tx, ty, tz,
+		u, v,
+
+		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( this.path, this.segments, this.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++ ) {
+
+		this.grid[ i ] = [];
+
+		u = i / ( numpoints - 1 );
+
+		pos = path.getPointAt( u );
+
+		tangent = tangents[ i ];
+		normal = normals[ i ];
+		binormal = binormals[ i ];
+
+		for ( j = 0; j < this.radialSegments; j++ ) {
+
+			v = j / this.radialSegments * 2 * Math.PI;
+
+			cx = -this.radius * Math.cos( v ); // TODO: Hack: Negating it so it faces outside.
+			cy = this.radius * 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;
+
+			this.grid[ i ][ j ] = vert( pos2.x, pos2.y, pos2.z );
+
+		}
+	}
+
+
+	// construct the mesh
+
+	for ( i = 0; i < this.segments; i++ ) {
+
+		for ( j = 0; j < this.radialSegments; j++ ) {
+
+			ip = ( this.closed ) ? (i + 1) % this.segments : i + 1;
+			jp = (j + 1) % this.radialSegments;
+
+			a = this.grid[ i ][ j ];		// *** NOT NECESSARILY PLANAR ! ***
+			b = this.grid[ ip ][ j ];
+			c = this.grid[ ip ][ jp ];
+			d = this.grid[ i ][ jp ];
+
+			uva = new THREE.Vector2( i / this.segments, j / this.radialSegments );
+			uvb = new THREE.Vector2( ( i + 1 ) / this.segments, j / this.radialSegments );
+			uvc = new THREE.Vector2( ( i + 1 ) / this.segments, ( j + 1 ) / this.radialSegments );
+			uvd = new THREE.Vector2( i / this.segments, ( j + 1 ) / this.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.computeCentroids();
+	this.computeFaceNormals();
+	this.computeVertexNormals();
+
+};
+
+THREE.TubeGeometry.prototype = Object.create( THREE.Geometry.prototype );
+
+
+// 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 ] );
+
+		}
+
+	}
+};
+
+/**
+ * @author clockworkgeek / https://github.com/clockworkgeek
+ * @author timothypratley / https://github.com/timothypratley
+ * @author WestLangley / http://github.com/WestLangley
+*/
+
+THREE.PolyhedronGeometry = function ( vertices, faces, radius, detail ) {
+
+	THREE.Geometry.call( this );
+
+	radius = radius || 1;
+	detail = detail || 0;
+
+	var that = this;
+
+	for ( var i = 0, l = vertices.length; i < l; i ++ ) {
+
+		prepare( new THREE.Vector3( vertices[ i ][ 0 ], vertices[ i ][ 1 ], vertices[ i ][ 2 ] ) );
+
+	}
+
+	var midpoints = [], p = this.vertices;
+
+	var f = [];
+	for ( var i = 0, l = faces.length; i < l; i ++ ) {
+
+		var v1 = p[ faces[ i ][ 0 ] ];
+		var v2 = p[ faces[ i ][ 1 ] ];
+		var v3 = p[ faces[ i ][ 2 ] ];
+
+		f[ i ] = new THREE.Face3( v1.index, v2.index, v3.index, [ v1.clone(), v2.clone(), v3.clone() ] );
+
+	}
+
+	for ( var i = 0, l = f.length; i < l; i ++ ) {
+
+		subdivide(f[ 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.computeCentroids();
+
+	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() ] );
+		face.centroid.add( v1 ).add( v2 ).add( v3 ).divideScalar( 3 );
+		that.faces.push( face );
+
+		var azi = azimuth( face.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 );
+
+/**
+ * @author timothypratley / https://github.com/timothypratley
+ */
+
+THREE.IcosahedronGeometry = function ( radius, detail ) {
+
+	this.radius = radius;
+	this.detail = 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 faces = [
+		[ 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, faces, radius, detail );
+
+};
+
+THREE.IcosahedronGeometry.prototype = Object.create( THREE.Geometry.prototype );
+
+/**
+ * @author timothypratley / https://github.com/timothypratley
+ */
+
+THREE.OctahedronGeometry = function ( radius, detail ) {
+
+	var vertices = [
+		[ 1, 0, 0 ], [ -1, 0, 0 ], [ 0, 1, 0 ], [ 0, -1, 0 ], [ 0, 0, 1 ], [ 0, 0, -1 ]
+	];
+
+	var faces = [
+		[ 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, faces, radius, detail );
+};
+
+THREE.OctahedronGeometry.prototype = Object.create( THREE.Geometry.prototype );
+
+/**
+ * @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 faces = [
+		[ 2, 1, 0 ], [ 0, 3, 2 ], [ 1, 3, 0 ], [ 2, 3, 1 ]
+	];
+
+	THREE.PolyhedronGeometry.call( this, vertices, faces, radius, detail );
+
+};
+
+THREE.TetrahedronGeometry.prototype = Object.create( THREE.Geometry.prototype );
+
+/**
+ * @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 );
+
+	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.computeCentroids();
+	this.computeFaceNormals();
+	this.computeVertexNormals();
+
+};
+
+THREE.ParametricGeometry.prototype = Object.create( THREE.Geometry.prototype );
+
+/**
+ * @author sroucheray / http://sroucheray.org/
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.AxisHelper = function ( size ) {
+
+	size = size || 1;
+
+	var geometry = new THREE.Geometry();
+
+	geometry.vertices.push(
+		new THREE.Vector3(), new THREE.Vector3( size, 0, 0 ),
+		new THREE.Vector3(), new THREE.Vector3( 0, size, 0 ),
+		new THREE.Vector3(), new THREE.Vector3( 0, 0, size )
+	);
+
+	geometry.colors.push(
+		new THREE.Color( 0xff0000 ), new THREE.Color( 0xffaa00 ),
+		new THREE.Color( 0x00ff00 ), new THREE.Color( 0xaaff00 ),
+		new THREE.Color( 0x0000ff ), new THREE.Color( 0x00aaff )
+	);
+
+	var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } );
+
+	THREE.Line.call( this, geometry, material, THREE.LinePieces );
+
+};
+
+THREE.AxisHelper.prototype = Object.create( THREE.Line.prototype );
+
+/**
+ * @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
+ *  hex - color in hex value
+ */
+
+THREE.ArrowHelper = function ( dir, origin, length, hex ) {
+
+	// dir is assumed to be normalized
+
+	THREE.Object3D.call( this );
+
+	if ( hex === undefined ) hex = 0xffff00;
+	if ( length === undefined ) length = 1;
+
+	this.position = origin;
+
+	var lineGeometry = new THREE.Geometry();
+	lineGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ) );
+	lineGeometry.vertices.push( new THREE.Vector3( 0, 1, 0 ) );
+
+	this.line = new THREE.Line( lineGeometry, new THREE.LineBasicMaterial( { color: hex } ) );
+	this.line.matrixAutoUpdate = false;
+	this.add( this.line );
+
+	var coneGeometry = new THREE.CylinderGeometry( 0, 0.05, 0.25, 5, 1 );
+	coneGeometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, 0.875, 0 ) );
+
+	this.cone = new THREE.Mesh( coneGeometry, new THREE.MeshBasicMaterial( { color: hex } ) );
+	this.cone.matrixAutoUpdate = false;
+	this.add( this.cone );
+
+	this.setDirection( dir );
+	this.setLength( length );
+
+};
+
+THREE.ArrowHelper.prototype = Object.create( THREE.Object3D.prototype );
+
+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 ) {
+
+	this.scale.set( length, length, length );
+
+};
+
+THREE.ArrowHelper.prototype.setColor = function ( hex ) {
+
+	this.line.material.color.setHex( hex );
+	this.cone.material.color.setHex( hex );
+
+};
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.BoxHelper = function ( object ) {
+
+	//   5____4
+	// 1/___0/|
+	// | 6__|_7
+	// 2/___3/
+
+	var vertices = [
+		new THREE.Vector3(   1,   1,   1 ),
+		new THREE.Vector3( - 1,   1,   1 ),
+		new THREE.Vector3( - 1, - 1,   1 ),
+		new THREE.Vector3(   1, - 1,   1 ),
+
+		new THREE.Vector3(   1,   1, - 1 ),
+		new THREE.Vector3( - 1,   1, - 1 ),
+		new THREE.Vector3( - 1, - 1, - 1 ),
+		new THREE.Vector3(   1, - 1, - 1 )
+	];
+
+	this.vertices = vertices;
+
+	// TODO: Wouldn't be nice if Line had .segments?
+
+	var geometry = new THREE.Geometry();
+	geometry.vertices.push(
+		vertices[ 0 ], vertices[ 1 ],
+		vertices[ 1 ], vertices[ 2 ],
+		vertices[ 2 ], vertices[ 3 ],
+		vertices[ 3 ], vertices[ 0 ],
+
+		vertices[ 4 ], vertices[ 5 ],
+		vertices[ 5 ], vertices[ 6 ],
+		vertices[ 6 ], vertices[ 7 ],
+		vertices[ 7 ], vertices[ 4 ],
+
+		vertices[ 0 ], vertices[ 4 ],
+		vertices[ 1 ], vertices[ 5 ],
+		vertices[ 2 ], vertices[ 6 ],
+		vertices[ 3 ], vertices[ 7 ]
+	);
+
+	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.update = function ( object ) {
+
+	var geometry = object.geometry;
+
+	if ( geometry.boundingBox === null ) {
+
+		geometry.computeBoundingBox();
+
+	}
+
+	var min = geometry.boundingBox.min;
+	var max = geometry.boundingBox.max;
+	var vertices = this.vertices;
+
+	vertices[ 0 ].set( max.x, max.y, max.z );
+	vertices[ 1 ].set( min.x, max.y, max.z );
+	vertices[ 2 ].set( min.x, min.y, max.z );
+	vertices[ 3 ].set( max.x, min.y, max.z );
+	vertices[ 4 ].set( max.x, max.y, min.z );
+	vertices[ 5 ].set( min.x, max.y, min.z );
+	vertices[ 6 ].set( min.x, min.y, min.z );
+	vertices[ 7 ].set( max.x, min.y, min.z );
+
+	this.geometry.computeBoundingSphere();
+	this.geometry.verticesNeedUpdate = true;
+
+	this.matrixAutoUpdate = false;
+	this.matrixWorld = object.matrixWorld;
+
+};
+
+/**
+ * @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 || 0x888888;
+
+	this.object = object;
+
+	this.box = new THREE.Box3();
+
+	THREE.Mesh.call( this, new THREE.CubeGeometry( 1, 1, 1 ), new THREE.MeshBasicMaterial( { color: color, wireframe: true } ) );
+
+};
+
+THREE.BoundingBoxHelper.prototype = Object.create( THREE.Mesh.prototype );
+
+THREE.BoundingBoxHelper.prototype.update = function () {
+
+	this.box.setFromObject( this.object );
+
+	this.box.size( this.scale );
+
+	this.box.center( this.position );
+
+};
+
+/**
+ * @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.matrixWorld = camera.matrixWorld;
+	this.matrixAutoUpdate = false;
+
+	this.pointMap = pointMap;
+
+	this.update();
+
+};
+
+THREE.CameraHelper.prototype = Object.create( THREE.Line.prototype );
+
+THREE.CameraHelper.prototype.update = function () {
+
+	var vector = new THREE.Vector3();
+	var camera = new THREE.Camera();
+	var projector = new THREE.Projector();
+
+	return function () {
+
+		var scope = this;
+
+		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 );
+
+		function setPoint( point, x, y, z ) {
+
+			vector.set( x, y, z );
+			projector.unprojectVector( vector, camera );
+
+			var points = scope.pointMap[ point ];
+
+			if ( points !== undefined ) {
+
+				for ( var i = 0, il = points.length; i < il; i ++ ) {
+
+					scope.geometry.vertices[ points[ i ] ].copy( vector );
+
+				}
+
+			}
+
+		}
+
+		this.geometry.verticesNeedUpdate = true;
+
+	};
+
+}();
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.DirectionalLightHelper = function ( light, size ) {
+
+	THREE.Object3D.call( this );
+
+	this.light = light;
+	this.light.updateMatrixWorld();
+
+	this.matrixWorld = light.matrixWorld;
+	this.matrixAutoUpdate = false;
+
+	var geometry = new THREE.PlaneGeometry( size, size );
+	var material = new THREE.MeshBasicMaterial( { wireframe: true, fog: false } );
+	material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );
+
+	this.lightPlane = new THREE.Mesh( geometry, material );
+	this.add( this.lightPlane );
+
+	geometry = new THREE.Geometry();
+	geometry.vertices.push( new THREE.Vector3() );
+	geometry.vertices.push( new THREE.Vector3() );
+	geometry.computeLineDistances();
+
+	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.dispose = function () {
+
+	this.lightPlane.geometry.dispose();
+	this.lightPlane.material.dispose();
+	this.targetLine.geometry.dispose();
+	this.targetLine.material.dispose();
+};
+
+THREE.DirectionalLightHelper.prototype.update = function () {
+
+	var vector = new THREE.Vector3();
+
+	return function () {
+
+		vector.getPositionFromMatrix( this.light.matrixWorld ).negate();
+
+		this.lightPlane.lookAt( vector );
+		this.lightPlane.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );
+
+		this.targetLine.geometry.vertices[ 1 ].copy( vector );
+		this.targetLine.geometry.verticesNeedUpdate = true;
+		this.targetLine.material.color.copy( this.lightPlane.material.color );
+
+	}
+
+}();
+
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author WestLangley / http://github.com/WestLangley
+*/
+
+THREE.FaceNormalsHelper = function ( object, size, hex, linewidth ) {
+
+	this.object = object;
+
+	this.size = size || 1;
+
+	var color = hex || 0xffff00;
+
+	var width = 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() );
+		geometry.vertices.push( 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.update = ( function ( object ) {
+
+	var v1 = new THREE.Vector3();
+
+	return function ( object ) {
+
+		this.object.updateMatrixWorld( true );
+
+		this.normalMatrix.getNormalMatrix( this.object.matrixWorld );
+
+		var vertices = this.geometry.vertices;
+
+		var faces = this.object.geometry.faces;
+
+		var worldMatrix = this.object.matrixWorld;
+
+		for ( var i = 0, l = faces.length; i < l; i ++ ) {
+
+			var face = faces[ i ];
+
+			v1.copy( face.normal ).applyMatrix3( this.normalMatrix ).normalize().multiplyScalar( this.size );
+
+			var idx = 2 * i;
+
+			vertices[ idx ].copy( face.centroid ).applyMatrix4( worldMatrix );
+
+			vertices[ idx + 1 ].addVectors( vertices[ idx ], v1 );
+
+		}
+
+		this.geometry.verticesNeedUpdate = true;
+
+		return this;
+
+	}
+
+}());
+
+
+/**
+ * @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.setColors = function( colorCenterLine, colorGrid ) {
+
+	this.color1.set( colorCenterLine );
+	this.color2.set( colorGrid );
+
+	this.geometry.colorsNeedUpdate = true;
+
+}
+
+/**
+ * @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.matrixWorld = 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.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.getPositionFromMatrix( this.light.matrixWorld ).negate() );
+		this.lightSphere.geometry.colorsNeedUpdate = true;
+
+	}
+
+}();
+
+
+/**
+ * @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.matrixWorld = 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.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 );
+
+	}
+	*/
+
+};
+
+
+/**
+ * @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.matrixWorld = 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.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.getPositionFromMatrix( this.light.matrixWorld );
+		vector2.getPositionFromMatrix( this.light.target.matrixWorld );
+
+		this.cone.lookAt( vector2.sub( vector ) );
+
+		this.cone.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );
+
+	};
+
+}();
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author WestLangley / http://github.com/WestLangley
+*/
+
+THREE.VertexNormalsHelper = function ( object, size, hex, linewidth ) {
+
+	this.object = object;
+
+	this.size = size || 1;
+
+	var color = hex || 0xff0000;
+
+	var width = 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() );
+			geometry.vertices.push( 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.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;
+
+	}
+
+}());
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author WestLangley / http://github.com/WestLangley
+*/
+
+THREE.VertexTangentsHelper = function ( object, size, hex, linewidth ) {
+
+	this.object = object;
+
+	this.size = size || 1;
+
+	var color = hex || 0x0000ff;
+
+	var width = 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.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;
+
+	}
+
+}());
+
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+THREE.WireframeHelper = function ( object ) {
+
+	var edge = [ 0, 0 ], hash = {};
+	var sortFunction = function ( a, b ) { return a - b };
+
+	var keys = [ 'a', 'b', 'c', 'd' ];
+	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; 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 ) {
+
+				geometry.vertices.push( vertices[ edge[ 0 ] ] );
+				geometry.vertices.push( vertices[ edge[ 1 ] ] );
+
+				hash[ key ] = true;
+
+			}
+
+		}
+
+	}
+
+	THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: 0xffffff } ), THREE.LinePieces );
+
+	this.matrixAutoUpdate = false;
+	this.matrixWorld = object.matrixWorld;
+
+};
+
+THREE.WireframeHelper.prototype = Object.create( THREE.Line.prototype );
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.ImmediateRenderObject = function () {
+
+	THREE.Object3D.call( this );
+
+	this.render = function ( renderCallback ) { };
+
+};
+
+THREE.ImmediateRenderObject.prototype = Object.create( THREE.Object3D.prototype );
+
+/**
+ * @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 );
+
+
+/*
+ * 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;
+
+	}
+
+};
+
+
+
+
+
+
+
+
+
+
+
+
+
+/**
+ * @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.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;
+
+	}
+
+};
+
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.LensFlarePlugin = function () {
+
+	var _gl, _renderer, _precision, _lensFlare = {};
+
+	this.init = function ( renderer ) {
+
+		_gl = renderer.context;
+		_renderer = renderer;
+
+		_precision = renderer.getPrecision();
+
+		_lensFlare.vertices = new Float32Array( 8 + 8 );
+		_lensFlare.faces = new Uint16Array( 6 );
+
+		var i = 0;
+		_lensFlare.vertices[ i++ ] = -1; _lensFlare.vertices[ i++ ] = -1;	// vertex
+		_lensFlare.vertices[ i++ ] = 0;  _lensFlare.vertices[ i++ ] = 0;	// uv... etc.
+
+		_lensFlare.vertices[ i++ ] = 1;  _lensFlare.vertices[ i++ ] = -1;
+		_lensFlare.vertices[ i++ ] = 1;  _lensFlare.vertices[ i++ ] = 0;
+
+		_lensFlare.vertices[ i++ ] = 1;  _lensFlare.vertices[ i++ ] = 1;
+		_lensFlare.vertices[ i++ ] = 1;  _lensFlare.vertices[ i++ ] = 1;
+
+		_lensFlare.vertices[ i++ ] = -1; _lensFlare.vertices[ i++ ] = 1;
+		_lensFlare.vertices[ i++ ] = 0;  _lensFlare.vertices[ i++ ] = 1;
+
+		i = 0;
+		_lensFlare.faces[ i++ ] = 0; _lensFlare.faces[ i++ ] = 1; _lensFlare.faces[ i++ ] = 2;
+		_lensFlare.faces[ i++ ] = 0; _lensFlare.faces[ i++ ] = 2; _lensFlare.faces[ i++ ] = 3;
+
+		// buffers
+
+		_lensFlare.vertexBuffer     = _gl.createBuffer();
+		_lensFlare.elementBuffer    = _gl.createBuffer();
+
+		_gl.bindBuffer( _gl.ARRAY_BUFFER, _lensFlare.vertexBuffer );
+		_gl.bufferData( _gl.ARRAY_BUFFER, _lensFlare.vertices, _gl.STATIC_DRAW );
+
+		_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _lensFlare.elementBuffer );
+		_gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, _lensFlare.faces, _gl.STATIC_DRAW );
+
+		// textures
+
+		_lensFlare.tempTexture      = _gl.createTexture();
+		_lensFlare.occlusionTexture = _gl.createTexture();
+
+		_gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.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, _lensFlare.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 );
+
+		if ( _gl.getParameter( _gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ) <= 0 ) {
+
+			_lensFlare.hasVertexTexture = false;
+			_lensFlare.program = createProgram( THREE.ShaderFlares[ "lensFlare" ], _precision );
+
+		} else {
+
+			_lensFlare.hasVertexTexture = true;
+			_lensFlare.program = createProgram( THREE.ShaderFlares[ "lensFlareVertexTexture" ], _precision );
+
+		}
+
+		_lensFlare.attributes = {};
+		_lensFlare.uniforms = {};
+
+		_lensFlare.attributes.vertex       = _gl.getAttribLocation ( _lensFlare.program, "position" );
+		_lensFlare.attributes.uv           = _gl.getAttribLocation ( _lensFlare.program, "uv" );
+
+		_lensFlare.uniforms.renderType     = _gl.getUniformLocation( _lensFlare.program, "renderType" );
+		_lensFlare.uniforms.map            = _gl.getUniformLocation( _lensFlare.program, "map" );
+		_lensFlare.uniforms.occlusionMap   = _gl.getUniformLocation( _lensFlare.program, "occlusionMap" );
+		_lensFlare.uniforms.opacity        = _gl.getUniformLocation( _lensFlare.program, "opacity" );
+		_lensFlare.uniforms.color          = _gl.getUniformLocation( _lensFlare.program, "color" );
+		_lensFlare.uniforms.scale          = _gl.getUniformLocation( _lensFlare.program, "scale" );
+		_lensFlare.uniforms.rotation       = _gl.getUniformLocation( _lensFlare.program, "rotation" );
+		_lensFlare.uniforms.screenPosition = _gl.getUniformLocation( _lensFlare.program, "screenPosition" );
+
+	};
+
+
+	/*
+	 * Render lens flares
+	 * Method: renders 16x16 0xff00ff-colored points scattered over the light source area,
+	 *         reads these back and calculates occlusion.
+	 *         Then _lensFlare.update_lensFlares() is called to re-position and
+	 *         update transparency of flares. Then they are rendered.
+	 *
+	 */
+
+	this.render = function ( scene, camera, viewportWidth, viewportHeight ) {
+
+		var flares = scene.__webglFlares,
+			nFlares = flares.length;
+
+		if ( ! nFlares ) 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 );
+
+		var uniforms = _lensFlare.uniforms,
+			attributes = _lensFlare.attributes;
+
+		// set _lensFlare program and reset blending
+
+		_gl.useProgram( _lensFlare.program );
+
+		_gl.enableVertexAttribArray( _lensFlare.attributes.vertex );
+		_gl.enableVertexAttribArray( _lensFlare.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, _lensFlare.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, _lensFlare.elementBuffer );
+
+		_gl.disable( _gl.CULL_FACE );
+		_gl.depthMask( false );
+
+		var i, j, jl, flare, sprite;
+
+		for ( i = 0; i < nFlares; i ++ ) {
+
+			size = 16 / viewportHeight;
+			scale.set( size * invAspect, size );
+
+			// calc object screen position
+
+			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 ( _lensFlare.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, _lensFlare.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, _lensFlare.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, _lensFlare.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 ( j = 0, jl = flare.lensFlares.length; j < jl; j ++ ) {
+
+					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 );
+
+	};
+
+	function createProgram ( shader, precision ) {
+
+		var program = _gl.createProgram();
+
+		var fragmentShader = _gl.createShader( _gl.FRAGMENT_SHADER );
+		var vertexShader = _gl.createShader( _gl.VERTEX_SHADER );
+
+		var prefix = "precision " + precision + " 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;
+
+	};
+
+};
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.ShadowMapPlugin = function () {
+
+	var _gl,
+	_renderer,
+	_depthMaterial, _depthMaterialMorph, _depthMaterialSkin, _depthMaterialMorphSkin,
+
+	_frustum = new THREE.Frustum(),
+	_projScreenMatrix = new THREE.Matrix4(),
+
+	_min = new THREE.Vector3(),
+	_max = new THREE.Vector3(),
+
+	_matrixPosition = new THREE.Vector3();
+
+	this.init = function ( renderer ) {
+
+		_gl = renderer.context;
+		_renderer = renderer;
+
+		var depthShader = THREE.ShaderLib[ "depthRGBA" ];
+		var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms );
+
+		_depthMaterial = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms } );
+		_depthMaterialMorph = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms, morphTargets: true } );
+		_depthMaterialSkin = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms, skinning: true } );
+		_depthMaterialMorphSkin = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms, morphTargets: true, skinning: true } );
+
+		_depthMaterial._shadowPass = true;
+		_depthMaterialMorph._shadowPass = true;
+		_depthMaterialSkin._shadowPass = true;
+		_depthMaterialMorphSkin._shadowPass = true;
+
+	};
+
+	this.render = function ( scene, camera ) {
+
+		if ( ! ( _renderer.shadowMapEnabled && _renderer.shadowMapAutoUpdate ) ) return;
+
+		this.update( scene, camera );
+
+	};
+
+	this.update = function ( scene, camera ) {
+
+		var i, il, j, jl, n,
+
+		shadowMap, shadowMatrix, shadowCamera,
+		program, buffer, material,
+		webglObject, object, light,
+		renderList,
+
+		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 = scene.__lights.length; i < il; i ++ ) {
+
+			light = scene.__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 = 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 );
+				light.shadowCamera.add( light.cameraHelper );
+
+			}
+
+			if ( light.isVirtual && virtualLight.originalCamera == camera ) {
+
+				updateShadowCamera( camera, light );
+
+			}
+
+			shadowMap = light.shadowMap;
+			shadowMatrix = light.shadowMatrix;
+			shadowCamera = light.shadowCamera;
+
+			shadowCamera.position.getPositionFromMatrix( light.matrixWorld );
+			_matrixPosition.getPositionFromMatrix( 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 = scene.__webglObjects;
+
+			for ( j = 0, jl = renderList.length; j < jl; j ++ ) {
+
+				webglObject = renderList[ j ];
+				object = webglObject.object;
+
+				webglObject.render = false;
+
+				if ( object.visible && object.castShadow ) {
+
+					if ( ! ( object instanceof THREE.Mesh || object instanceof THREE.ParticleSystem ) || ! ( object.frustumCulled ) || _frustum.intersectsObject( object ) ) {
+
+						object._modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld );
+
+						webglObject.render = true;
+
+					}
+
+				}
+
+			}
+
+			// render regular objects
+
+			var objectMaterial, useMorphing, useSkinning;
+
+			for ( j = 0, jl = renderList.length; j < jl; j ++ ) {
+
+				webglObject = renderList[ j ];
+
+				if ( webglObject.render ) {
+
+					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.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;
+
+					}
+
+					if ( buffer instanceof THREE.BufferGeometry ) {
+
+						_renderer.renderBufferDirect( shadowCamera, scene.__lights, fog, material, buffer, object );
+
+					} else {
+
+						_renderer.renderBuffer( shadowCamera, scene.__lights, fog, material, buffer, object );
+
+					}
+
+				}
+
+			}
+
+			// set matrices and render immediate objects
+
+			renderList = scene.__webglObjectsImmediate;
+
+			for ( j = 0, jl = renderList.length; j < jl; j ++ ) {
+
+				webglObject = renderList[ j ];
+				object = webglObject.object;
+
+				if ( object.visible && object.castShadow ) {
+
+					object._modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld );
+
+					_renderer.renderImmediateObject( shadowCamera, scene.__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 );
+
+		}
+
+	};
+
+	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 ] );
+			THREE.ShadowMapPlugin.__projector.unprojectVector( p, 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;
+
+	};
+
+};
+
+THREE.ShadowMapPlugin.__projector = new THREE.Projector();
+
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.SpritePlugin = function () {
+
+	var _gl, _renderer, _precision, _sprite = {};
+
+	this.init = function ( renderer ) {
+
+		_gl = renderer.context;
+		_renderer = renderer;
+
+		_precision = renderer.getPrecision();
+
+		_sprite.vertices = new Float32Array( 8 + 8 );
+		_sprite.faces    = new Uint16Array( 6 );
+
+		var i = 0;
+
+		_sprite.vertices[ i++ ] = -0.5; _sprite.vertices[ i++ ] = -0.5;	// vertex 0
+		_sprite.vertices[ i++ ] = 0;  _sprite.vertices[ i++ ] = 0;	// uv 0
+
+		_sprite.vertices[ i++ ] = 0.5;  _sprite.vertices[ i++ ] = -0.5;	// vertex 1
+		_sprite.vertices[ i++ ] = 1;  _sprite.vertices[ i++ ] = 0;	// uv 1
+
+		_sprite.vertices[ i++ ] = 0.5;  _sprite.vertices[ i++ ] = 0.5;	// vertex 2
+		_sprite.vertices[ i++ ] = 1;  _sprite.vertices[ i++ ] = 1;	// uv 2
+
+		_sprite.vertices[ i++ ] = -0.5; _sprite.vertices[ i++ ] = 0.5;	// vertex 3
+		_sprite.vertices[ i++ ] = 0;  _sprite.vertices[ i++ ] = 1;	// uv 3
+
+		i = 0;
+
+		_sprite.faces[ i++ ] = 0; _sprite.faces[ i++ ] = 1; _sprite.faces[ i++ ] = 2;
+		_sprite.faces[ i++ ] = 0; _sprite.faces[ i++ ] = 2; _sprite.faces[ i++ ] = 3;
+
+		_sprite.vertexBuffer  = _gl.createBuffer();
+		_sprite.elementBuffer = _gl.createBuffer();
+
+		_gl.bindBuffer( _gl.ARRAY_BUFFER, _sprite.vertexBuffer );
+		_gl.bufferData( _gl.ARRAY_BUFFER, _sprite.vertices, _gl.STATIC_DRAW );
+
+		_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _sprite.elementBuffer );
+		_gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, _sprite.faces, _gl.STATIC_DRAW );
+
+		_sprite.program = createProgram( THREE.ShaderSprite[ "sprite" ], _precision );
+
+		_sprite.attributes = {};
+		_sprite.uniforms = {};
+
+		_sprite.attributes.position           = _gl.getAttribLocation ( _sprite.program, "position" );
+		_sprite.attributes.uv                 = _gl.getAttribLocation ( _sprite.program, "uv" );
+
+		_sprite.uniforms.uvOffset             = _gl.getUniformLocation( _sprite.program, "uvOffset" );
+		_sprite.uniforms.uvScale              = _gl.getUniformLocation( _sprite.program, "uvScale" );
+
+		_sprite.uniforms.rotation             = _gl.getUniformLocation( _sprite.program, "rotation" );
+		_sprite.uniforms.scale                = _gl.getUniformLocation( _sprite.program, "scale" );
+		_sprite.uniforms.alignment            = _gl.getUniformLocation( _sprite.program, "alignment" );
+		_sprite.uniforms.halfViewport         = _gl.getUniformLocation( _sprite.program, "halfViewport" );
+
+		_sprite.uniforms.color                = _gl.getUniformLocation( _sprite.program, "color" );
+		_sprite.uniforms.map                  = _gl.getUniformLocation( _sprite.program, "map" );
+		_sprite.uniforms.opacity              = _gl.getUniformLocation( _sprite.program, "opacity" );
+
+		_sprite.uniforms.useScreenCoordinates = _gl.getUniformLocation( _sprite.program, "useScreenCoordinates" );
+		_sprite.uniforms.sizeAttenuation   	  = _gl.getUniformLocation( _sprite.program, "sizeAttenuation" );
+		_sprite.uniforms.screenPosition    	  = _gl.getUniformLocation( _sprite.program, "screenPosition" );
+		_sprite.uniforms.modelViewMatrix      = _gl.getUniformLocation( _sprite.program, "modelViewMatrix" );
+		_sprite.uniforms.projectionMatrix     = _gl.getUniformLocation( _sprite.program, "projectionMatrix" );
+
+		_sprite.uniforms.fogType 		  	  = _gl.getUniformLocation( _sprite.program, "fogType" );
+		_sprite.uniforms.fogDensity 		  = _gl.getUniformLocation( _sprite.program, "fogDensity" );
+		_sprite.uniforms.fogNear 		  	  = _gl.getUniformLocation( _sprite.program, "fogNear" );
+		_sprite.uniforms.fogFar 		  	  = _gl.getUniformLocation( _sprite.program, "fogFar" );
+		_sprite.uniforms.fogColor 		  	  = _gl.getUniformLocation( _sprite.program, "fogColor" );
+
+		_sprite.uniforms.alphaTest 		  	  = _gl.getUniformLocation( _sprite.program, "alphaTest" );
+
+	};
+
+	this.render = function ( scene, camera, viewportWidth, viewportHeight ) {
+
+		var sprites = scene.__webglSprites,
+			nSprites = sprites.length;
+
+		if ( ! nSprites ) return;
+
+		var attributes = _sprite.attributes,
+			uniforms = _sprite.uniforms;
+
+		var halfViewportWidth = viewportWidth * 0.5,
+			halfViewportHeight = viewportHeight * 0.5;
+
+		// setup gl
+
+		_gl.useProgram( _sprite.program );
+
+		_gl.enableVertexAttribArray( attributes.position );
+		_gl.enableVertexAttribArray( attributes.uv );
+
+		_gl.disable( _gl.CULL_FACE );
+		_gl.enable( _gl.BLEND );
+
+		_gl.bindBuffer( _gl.ARRAY_BUFFER, _sprite.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, _sprite.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
+
+		var i, sprite, material, screenPosition, fogType, scale = [];
+
+		for( i = 0; i < nSprites; i ++ ) {
+
+			sprite = sprites[ i ];
+			material = sprite.material;
+
+			if ( ! sprite.visible || material.opacity === 0 ) continue;
+
+			if ( ! material.useScreenCoordinates ) {
+
+				sprite._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, sprite.matrixWorld );
+				sprite.z = - sprite._modelViewMatrix.elements[ 14 ];
+
+			} else {
+
+				sprite.z = - sprite.position.z;
+
+			}
+
+		}
+
+		sprites.sort( painterSortStable );
+
+		// render all sprites
+
+		for( i = 0; i < nSprites; i ++ ) {
+
+			sprite = sprites[ i ];
+			material = sprite.material;
+
+			if ( ! sprite.visible || material.opacity === 0 ) continue;
+
+			if ( material.map && material.map.image && material.map.image.width ) {
+
+				_gl.uniform1f( uniforms.alphaTest, material.alphaTest );
+
+				if ( material.useScreenCoordinates === true ) {
+
+					_gl.uniform1i( uniforms.useScreenCoordinates, 1 );
+					_gl.uniform3f(
+						uniforms.screenPosition,
+						( ( sprite.position.x * _renderer.devicePixelRatio ) - halfViewportWidth  ) / halfViewportWidth,
+						( halfViewportHeight - ( sprite.position.y * _renderer.devicePixelRatio ) ) / halfViewportHeight,
+						Math.max( 0, Math.min( 1, sprite.position.z ) )
+					);
+
+					scale[ 0 ] = _renderer.devicePixelRatio * sprite.scale.x;
+					scale[ 1 ] = _renderer.devicePixelRatio * sprite.scale.y;
+
+				} else {
+
+					_gl.uniform1i( uniforms.useScreenCoordinates, 0 );
+					_gl.uniform1i( uniforms.sizeAttenuation, material.sizeAttenuation ? 1 : 0 );
+					_gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, sprite._modelViewMatrix.elements );
+
+					scale[ 0 ] = sprite.scale.x;
+					scale[ 1 ] = sprite.scale.y;
+
+				}
+
+				if ( scene.fog && material.fog ) {
+
+					fogType = sceneFogType;
+
+				} else {
+
+					fogType = 0;
+
+				}
+
+				if ( oldFogType !== fogType ) {
+
+					_gl.uniform1i( uniforms.fogType, fogType );
+					oldFogType = fogType;
+
+				}
+
+				_gl.uniform2f( uniforms.uvScale, material.uvScale.x, material.uvScale.y );
+				_gl.uniform2f( uniforms.uvOffset, material.uvOffset.x, material.uvOffset.y );
+				_gl.uniform2f( uniforms.alignment, material.alignment.x, material.alignment.y );
+
+				_gl.uniform1f( uniforms.opacity, material.opacity );
+				_gl.uniform3f( uniforms.color, material.color.r, material.color.g, material.color.b );
+
+				_gl.uniform1f( uniforms.rotation, sprite.rotation );
+				_gl.uniform2fv( uniforms.scale, scale );
+				_gl.uniform2f( uniforms.halfViewport, halfViewportWidth, halfViewportHeight );
+
+				_renderer.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst );
+				_renderer.setDepthTest( material.depthTest );
+				_renderer.setDepthWrite( material.depthWrite );
+				_renderer.setTexture( material.map, 0 );
+
+				_gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 );
+
+			}
+
+		}
+
+		// restore gl
+
+		_gl.enable( _gl.CULL_FACE );
+
+	};
+
+	function createProgram ( shader, precision ) {
+
+		var program = _gl.createProgram();
+
+		var fragmentShader = _gl.createShader( _gl.FRAGMENT_SHADER );
+		var vertexShader = _gl.createShader( _gl.VERTEX_SHADER );
+
+		var prefix = "precision " + precision + " 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;
+
+	};
+
+	function painterSortStable ( a, b ) {
+
+		if ( a.z !== b.z ) {
+
+			return b.z - a.z;
+
+		} else {
+
+			return b.id - a.id;
+
+		}
+
+	};
+
+};
+
+/**
+ * @author alteredq / http://alteredqualia.com/
+ */
+
+THREE.DepthPassPlugin = function () {
+
+	this.enabled = false;
+	this.renderTarget = null;
+
+	var _gl,
+	_renderer,
+	_depthMaterial, _depthMaterialMorph, _depthMaterialSkin, _depthMaterialMorphSkin,
+
+	_frustum = new THREE.Frustum(),
+	_projScreenMatrix = new THREE.Matrix4();
+
+	this.init = function ( renderer ) {
+
+		_gl = renderer.context;
+		_renderer = renderer;
+
+		var depthShader = THREE.ShaderLib[ "depthRGBA" ];
+		var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms );
+
+		_depthMaterial = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms } );
+		_depthMaterialMorph = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms, morphTargets: true } );
+		_depthMaterialSkin = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms, skinning: true } );
+		_depthMaterialMorphSkin = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms, morphTargets: true, skinning: true } );
+
+		_depthMaterial._shadowPass = true;
+		_depthMaterialMorph._shadowPass = true;
+		_depthMaterialSkin._shadowPass = true;
+		_depthMaterialMorphSkin._shadowPass = true;
+
+	};
+
+	this.render = function ( scene, camera ) {
+
+		if ( ! this.enabled ) return;
+
+		this.update( scene, camera );
+
+	};
+
+	this.update = function ( scene, camera ) {
+
+		var i, il, j, jl, n,
+
+		program, buffer, material,
+		webglObject, object, light,
+		renderList,
+
+		fog = null;
+
+		// set GL state for depth map
+
+		_gl.clearColor( 1, 1, 1, 1 );
+		_gl.disable( _gl.BLEND );
+
+		_renderer.setDepthTest( true );
+
+		// update scene
+
+		if ( scene.autoUpdate === true ) scene.updateMatrixWorld();
+
+		// update camera matrices and frustum
+
+		camera.matrixWorldInverse.getInverse( camera.matrixWorld );
+
+		_projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );
+		_frustum.setFromMatrix( _projScreenMatrix );
+
+		// render depth map
+
+		_renderer.setRenderTarget( this.renderTarget );
+		_renderer.clear();
+
+		// set object matrices & frustum culling
+
+		renderList = scene.__webglObjects;
+
+		for ( j = 0, jl = renderList.length; j < jl; j ++ ) {
+
+			webglObject = renderList[ j ];
+			object = webglObject.object;
+
+			webglObject.render = false;
+
+			if ( object.visible ) {
+
+				if ( ! ( object instanceof THREE.Mesh || object instanceof THREE.ParticleSystem ) || ! ( object.frustumCulled ) || _frustum.intersectsObject( object ) ) {
+
+					object._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
+
+					webglObject.render = true;
+
+				}
+
+			}
+
+		}
+
+		// render regular objects
+
+		var objectMaterial, useMorphing, useSkinning;
+
+		for ( j = 0, jl = renderList.length; j < jl; j ++ ) {
+
+			webglObject = renderList[ j ];
+
+			if ( webglObject.render ) {
+
+				object = webglObject.object;
+				buffer = webglObject.buffer;
+
+				// todo: create proper depth material for particles
+
+				if ( object instanceof THREE.ParticleSystem && !object.customDepthMaterial ) continue;
+
+				objectMaterial = getObjectMaterial( object );
+
+				if ( objectMaterial ) _renderer.setMaterialFaces( object.material );
+
+				useMorphing = 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;
+
+				}
+
+				if ( buffer instanceof THREE.BufferGeometry ) {
+
+					_renderer.renderBufferDirect( camera, scene.__lights, fog, material, buffer, object );
+
+				} else {
+
+					_renderer.renderBuffer( camera, scene.__lights, fog, material, buffer, object );
+
+				}
+
+			}
+
+		}
+
+		// set matrices and render immediate objects
+
+		renderList = scene.__webglObjectsImmediate;
+
+		for ( j = 0, jl = renderList.length; j < jl; j ++ ) {
+
+			webglObject = renderList[ j ];
+			object = webglObject.object;
+
+			if ( object.visible ) {
+
+				object._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
+
+				_renderer.renderImmediateObject( camera, scene.__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 );
+
+	};
+
+	// 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
+
+	function getObjectMaterial( object ) {
+
+		return object.material instanceof THREE.MeshFaceMaterial
+			? object.material.materials[ 0 ]
+			: object.material;
+
+	};
+
+};
+
+
+/**
+ * @author mikael emtinger / http://gomo.se/
+ */
+
+THREE.ShaderFlares = {
+
+	'lensFlareVertexTexture': {
+
+		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" )
+
+	},
+
+
+	'lensFlare': {
+
+		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" )
+
+	}
+
+};
+
+/**
+ * @author mikael emtinger / http://gomo.se/
+ * @author alteredq / http://alteredqualia.com/
+ *
+ */
+
+THREE.ShaderSprite = {
+
+	'sprite': {
+
+		vertexShader: [
+
+			"uniform int useScreenCoordinates;",
+			"uniform int sizeAttenuation;",
+			"uniform vec3 screenPosition;",
+			"uniform mat4 modelViewMatrix;",
+			"uniform mat4 projectionMatrix;",
+			"uniform float rotation;",
+			"uniform vec2 scale;",
+			"uniform vec2 alignment;",
+			"uniform vec2 uvOffset;",
+			"uniform vec2 uvScale;",
+			"uniform vec2 halfViewport;",
+
+			"attribute vec2 position;",
+			"attribute vec2 uv;",
+
+			"varying vec2 vUV;",
+
+			"void main() {",
+
+				"vUV = uvOffset + uv * uvScale;",
+
+				"vec2 alignedPosition = ( position + alignment ) * 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;",
+
+				"if( useScreenCoordinates != 0 ) {",
+
+					"finalPosition = vec4( screenPosition.xy + ( rotatedPosition / halfViewport ), screenPosition.z, 1.0 );",
+
+				"} else {",
+
+					"finalPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );",
+					"finalPosition.xy += rotatedPosition * ( sizeAttenuation == 1 ? 1.0 : finalPosition.z );",
+					"finalPosition = projectionMatrix * finalPosition;",
+
+				"}",
+
+				"gl_Position = finalPosition;",
+
+			"}"
+
+		].join( "\n" ),
+
+		fragmentShader: [
+
+			"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" )
+
+	}
+
+};
diff --git a/gz3d/client/js/include/three.min.js b/gz3d/client/js/include/three.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..db4f8f5ccfae7af502d6c0c7e2877ae11ee3b233
--- /dev/null
+++ b/gz3d/client/js/include/three.min.js
@@ -0,0 +1,705 @@
+// three.js - http://github.com/mrdoob/three.js
+'use strict';var THREE={REVISION:"62"};self.console=self.console||{info:function(){},log:function(){},debug:function(){},warn:function(){},error:function(){}};String.prototype.trim=String.prototype.trim||function(){return this.replace(/^\s+|\s+$/g,"")};THREE.extend=function(a,b){if(Object.keys)for(var c=Object.keys(b),d=0,e=c.length;d<e;d++){var f=c[d];Object.defineProperty(a,f,Object.getOwnPropertyDescriptor(b,f))}else for(f in c={}.hasOwnProperty,b)c.call(b,f)&&(a[f]=b[f]);return a};
+(function(){for(var a=0,b=["ms","moz","webkit","o"],c=0;c<b.length&&!self.requestAnimationFrame;++c)self.requestAnimationFrame=self[b[c]+"RequestAnimationFrame"],self.cancelAnimationFrame=self[b[c]+"CancelAnimationFrame"]||self[b[c]+"CancelRequestAnimationFrame"];void 0===self.requestAnimationFrame&&void 0!==self.setTimeout&&(self.requestAnimationFrame=function(b){var c=Date.now(),f=Math.max(0,16-(c-a)),h=self.setTimeout(function(){b(c+f)},f);a=c+f;return h});void 0===self.cancelAnimationFrame&&void 0!==
+self.clearTimeout&&(self.cancelAnimationFrame=function(a){self.clearTimeout(a)})})();THREE.CullFaceNone=0;THREE.CullFaceBack=1;THREE.CullFaceFront=2;THREE.CullFaceFrontBack=3;THREE.FrontFaceDirectionCW=0;THREE.FrontFaceDirectionCCW=1;THREE.BasicShadowMap=0;THREE.PCFShadowMap=1;THREE.PCFSoftShadowMap=2;THREE.FrontSide=0;THREE.BackSide=1;THREE.DoubleSide=2;THREE.NoShading=0;THREE.FlatShading=1;THREE.SmoothShading=2;THREE.NoColors=0;THREE.FaceColors=1;THREE.VertexColors=2;THREE.NoBlending=0;
+THREE.NormalBlending=1;THREE.AdditiveBlending=2;THREE.SubtractiveBlending=3;THREE.MultiplyBlending=4;THREE.CustomBlending=5;THREE.AddEquation=100;THREE.SubtractEquation=101;THREE.ReverseSubtractEquation=102;THREE.ZeroFactor=200;THREE.OneFactor=201;THREE.SrcColorFactor=202;THREE.OneMinusSrcColorFactor=203;THREE.SrcAlphaFactor=204;THREE.OneMinusSrcAlphaFactor=205;THREE.DstAlphaFactor=206;THREE.OneMinusDstAlphaFactor=207;THREE.DstColorFactor=208;THREE.OneMinusDstColorFactor=209;
+THREE.SrcAlphaSaturateFactor=210;THREE.MultiplyOperation=0;THREE.MixOperation=1;THREE.AddOperation=2;THREE.UVMapping=function(){};THREE.CubeReflectionMapping=function(){};THREE.CubeRefractionMapping=function(){};THREE.SphericalReflectionMapping=function(){};THREE.SphericalRefractionMapping=function(){};THREE.RepeatWrapping=1E3;THREE.ClampToEdgeWrapping=1001;THREE.MirroredRepeatWrapping=1002;THREE.NearestFilter=1003;THREE.NearestMipMapNearestFilter=1004;THREE.NearestMipMapLinearFilter=1005;
+THREE.LinearFilter=1006;THREE.LinearMipMapNearestFilter=1007;THREE.LinearMipMapLinearFilter=1008;THREE.UnsignedByteType=1009;THREE.ByteType=1010;THREE.ShortType=1011;THREE.UnsignedShortType=1012;THREE.IntType=1013;THREE.UnsignedIntType=1014;THREE.FloatType=1015;THREE.UnsignedShort4444Type=1016;THREE.UnsignedShort5551Type=1017;THREE.UnsignedShort565Type=1018;THREE.AlphaFormat=1019;THREE.RGBFormat=1020;THREE.RGBAFormat=1021;THREE.LuminanceFormat=1022;THREE.LuminanceAlphaFormat=1023;
+THREE.RGB_S3TC_DXT1_Format=2001;THREE.RGBA_S3TC_DXT1_Format=2002;THREE.RGBA_S3TC_DXT3_Format=2003;THREE.RGBA_S3TC_DXT5_Format=2004;THREE.Color=function(a){void 0!==a&&this.set(a);return this};
+THREE.Color.prototype={constructor:THREE.Color,r:1,g:1,b:1,set:function(a){a instanceof THREE.Color?this.copy(a):"number"===typeof a?this.setHex(a):"string"===typeof a&&this.setStyle(a);return this},setHex:function(a){a=Math.floor(a);this.r=(a>>16&255)/255;this.g=(a>>8&255)/255;this.b=(a&255)/255;return this},setRGB:function(a,b,c){this.r=a;this.g=b;this.b=c;return this},setHSL:function(a,b,c){if(0===b)this.r=this.g=this.b=c;else{var d=function(a,b,c){0>c&&(c+=1);1<c&&(c-=1);return c<1/6?a+6*(b-a)*
+c:0.5>c?b:c<2/3?a+6*(b-a)*(2/3-c):a},b=0.5>=c?c*(1+b):c+b-c*b,c=2*c-b;this.r=d(c,b,a+1/3);this.g=d(c,b,a);this.b=d(c,b,a-1/3)}return this},setStyle:function(a){if(/^rgb\((\d+), ?(\d+), ?(\d+)\)$/i.test(a))return a=/^rgb\((\d+), ?(\d+), ?(\d+)\)$/i.exec(a),this.r=Math.min(255,parseInt(a[1],10))/255,this.g=Math.min(255,parseInt(a[2],10))/255,this.b=Math.min(255,parseInt(a[3],10))/255,this;if(/^rgb\((\d+)\%, ?(\d+)\%, ?(\d+)\%\)$/i.test(a))return a=/^rgb\((\d+)\%, ?(\d+)\%, ?(\d+)\%\)$/i.exec(a),this.r=
+Math.min(100,parseInt(a[1],10))/100,this.g=Math.min(100,parseInt(a[2],10))/100,this.b=Math.min(100,parseInt(a[3],10))/100,this;if(/^\#([0-9a-f]{6})$/i.test(a))return a=/^\#([0-9a-f]{6})$/i.exec(a),this.setHex(parseInt(a[1],16)),this;if(/^\#([0-9a-f])([0-9a-f])([0-9a-f])$/i.test(a))return a=/^\#([0-9a-f])([0-9a-f])([0-9a-f])$/i.exec(a),this.setHex(parseInt(a[1]+a[1]+a[2]+a[2]+a[3]+a[3],16)),this;if(/^(\w+)$/i.test(a))return this.setHex(THREE.ColorKeywords[a]),this},copy:function(a){this.r=a.r;this.g=
+a.g;this.b=a.b;return this},copyGammaToLinear:function(a){this.r=a.r*a.r;this.g=a.g*a.g;this.b=a.b*a.b;return this},copyLinearToGamma:function(a){this.r=Math.sqrt(a.r);this.g=Math.sqrt(a.g);this.b=Math.sqrt(a.b);return this},convertGammaToLinear:function(){var a=this.r,b=this.g,c=this.b;this.r=a*a;this.g=b*b;this.b=c*c;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 255*this.r<<16^255*this.g<<
+8^255*this.b<<0},getHexString:function(){return("000000"+this.getHex().toString(16)).slice(-6)},getHSL:function(){var a={h:0,s:0,l:0};return function(){var b=this.r,c=this.g,d=this.b,e=Math.max(b,c,d),f=Math.min(b,c,d),h,g=(f+e)/2;if(f===e)f=h=0;else{var i=e-f,f=0.5>=g?i/(e+f):i/(2-e-f);switch(e){case b:h=(c-d)/i+(c<d?6:0);break;case c:h=(d-b)/i+2;break;case d:h=(b-c)/i+4}h/=6}a.h=h;a.s=f;a.l=g;return a}}(),getStyle:function(){return"rgb("+(255*this.r|0)+","+(255*this.g|0)+","+(255*this.b|0)+")"},
+offsetHSL:function(a,b,c){var d=this.getHSL();d.h+=a;d.s+=b;d.l+=c;this.setHSL(d.h,d.s,d.l);return this},add:function(a){this.r+=a.r;this.g+=a.g;this.b+=a.b;return this},addColors:function(a,b){this.r=a.r+b.r;this.g=a.g+b.g;this.b=a.b+b.b;return this},addScalar:function(a){this.r+=a;this.g+=a;this.b+=a;return this},multiply:function(a){this.r*=a.r;this.g*=a.g;this.b*=a.b;return this},multiplyScalar:function(a){this.r*=a;this.g*=a;this.b*=a;return this},lerp:function(a,b){this.r+=(a.r-this.r)*b;this.g+=
+(a.g-this.g)*b;this.b+=(a.b-this.b)*b;return this},equals:function(a){return a.r===this.r&&a.g===this.g&&a.b===this.b},fromArray:function(a){this.r=a[0];this.g=a[1];this.b=a[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:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,
+darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,
+grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,
+lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,
+palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,
+tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074};THREE.Quaternion=function(a,b,c,d){this._x=a||0;this._y=b||0;this._z=c||0;this._w=void 0!==d?d:1};
+THREE.Quaternion.prototype={constructor:THREE.Quaternion,_x:0,_y:0,_z:0,_w:0,_euler:void 0,_updateEuler:function(){void 0!==this._euler&&this._euler.setFromQuaternion(this,void 0,!1)},get x(){return this._x},set x(a){this._x=a;this._updateEuler()},get y(){return this._y},set y(a){this._y=a;this._updateEuler()},get z(){return this._z},set z(a){this._z=a;this._updateEuler()},get w(){return this._w},set w(a){this._w=a;this._updateEuler()},set:function(a,b,c,d){this._x=a;this._y=b;this._z=c;this._w=d;
+this._updateEuler();return this},copy:function(a){this._x=a._x;this._y=a._y;this._z=a._z;this._w=a._w;this._updateEuler();return this},setFromEuler:function(a,b){if(!1===a instanceof THREE.Euler)throw Error("ERROR: Quaternion's .setFromEuler() now expects a Euler rotation rather than a Vector3 and order.  Please update your code.");var c=Math.cos(a._x/2),d=Math.cos(a._y/2),e=Math.cos(a._z/2),f=Math.sin(a._x/2),h=Math.sin(a._y/2),g=Math.sin(a._z/2);"XYZ"===a.order?(this._x=f*d*e+c*h*g,this._y=c*h*
+e-f*d*g,this._z=c*d*g+f*h*e,this._w=c*d*e-f*h*g):"YXZ"===a.order?(this._x=f*d*e+c*h*g,this._y=c*h*e-f*d*g,this._z=c*d*g-f*h*e,this._w=c*d*e+f*h*g):"ZXY"===a.order?(this._x=f*d*e-c*h*g,this._y=c*h*e+f*d*g,this._z=c*d*g+f*h*e,this._w=c*d*e-f*h*g):"ZYX"===a.order?(this._x=f*d*e-c*h*g,this._y=c*h*e+f*d*g,this._z=c*d*g-f*h*e,this._w=c*d*e+f*h*g):"YZX"===a.order?(this._x=f*d*e+c*h*g,this._y=c*h*e+f*d*g,this._z=c*d*g-f*h*e,this._w=c*d*e-f*h*g):"XZY"===a.order&&(this._x=f*d*e-c*h*g,this._y=c*h*e-f*d*g,this._z=
+c*d*g+f*h*e,this._w=c*d*e+f*h*g);!1!==b&&this._updateEuler();return this},setFromAxisAngle:function(a,b){var c=b/2,d=Math.sin(c);this._x=a.x*d;this._y=a.y*d;this._z=a.z*d;this._w=Math.cos(c);this._updateEuler();return this},setFromRotationMatrix:function(a){var b=a.elements,c=b[0],a=b[4],d=b[8],e=b[1],f=b[5],h=b[9],g=b[2],i=b[6],b=b[10],k=c+f+b;0<k?(c=0.5/Math.sqrt(k+1),this._w=0.25/c,this._x=(i-h)*c,this._y=(d-g)*c,this._z=(e-a)*c):c>f&&c>b?(c=2*Math.sqrt(1+c-f-b),this._w=(i-h)/c,this._x=0.25*c,
+this._y=(a+e)/c,this._z=(d+g)/c):f>b?(c=2*Math.sqrt(1+f-c-b),this._w=(d-g)/c,this._x=(a+e)/c,this._y=0.25*c,this._z=(h+i)/c):(c=2*Math.sqrt(1+b-c-f),this._w=(e-a)/c,this._x=(d+g)/c,this._y=(h+i)/c,this._z=0.25*c);this._updateEuler();return this},inverse:function(){this.conjugate().normalize();return this},conjugate:function(){this._x*=-1;this._y*=-1;this._z*=-1;this._updateEuler();return this},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 a=this.length();0===a?(this._z=this._y=this._x=0,this._w=1):(a=1/a,this._x*=a,this._y*=a,this._z*=a,this._w*=a);return this},multiply:function(a,b){return void 0!==b?(console.warn("DEPRECATED: Quaternion's .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead."),this.multiplyQuaternions(a,b)):this.multiplyQuaternions(this,a)},multiplyQuaternions:function(a,b){var c=a._x,d=a._y,e=a._z,f=
+a._w,h=b._x,g=b._y,i=b._z,k=b._w;this._x=c*k+f*h+d*i-e*g;this._y=d*k+f*g+e*h-c*i;this._z=e*k+f*i+c*g-d*h;this._w=f*k-c*h-d*g-e*i;this._updateEuler();return this},multiplyVector3:function(a){console.warn("DEPRECATED: Quaternion's .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.");return a.applyQuaternion(this)},slerp:function(a,b){var c=this._x,d=this._y,e=this._z,f=this._w,h=f*a._w+c*a._x+d*a._y+e*a._z;0>h?(this._w=-a._w,this._x=-a._x,this._y=-a._y,this._z=
+-a._z,h=-h):this.copy(a);if(1<=h)return this._w=f,this._x=c,this._y=d,this._z=e,this;var g=Math.acos(h),i=Math.sqrt(1-h*h);if(0.001>Math.abs(i))return this._w=0.5*(f+this._w),this._x=0.5*(c+this._x),this._y=0.5*(d+this._y),this._z=0.5*(e+this._z),this;h=Math.sin((1-b)*g)/i;g=Math.sin(b*g)/i;this._w=f*h+this._w*g;this._x=c*h+this._x*g;this._y=d*h+this._y*g;this._z=e*h+this._z*g;this._updateEuler();return this},equals:function(a){return a._x===this._x&&a._y===this._y&&a._z===this._z&&a._w===this._w},
+fromArray:function(a){this._x=a[0];this._y=a[1];this._z=a[2];this._w=a[3];this._updateEuler();return this},toArray:function(){return[this._x,this._y,this._z,this._w]},clone:function(){return new THREE.Quaternion(this._x,this._y,this._z,this._w)}};THREE.Quaternion.slerp=function(a,b,c,d){return c.copy(a).slerp(b,d)};THREE.Vector2=function(a,b){this.x=a||0;this.y=b||0};
+THREE.Vector2.prototype={constructor:THREE.Vector2,set:function(a,b){this.x=a;this.y=b;return this},setX:function(a){this.x=a;return this},setY:function(a){this.y=a;return this},setComponent:function(a,b){switch(a){case 0:this.x=b;break;case 1:this.y=b;break;default:throw Error("index is out of range: "+a);}},getComponent:function(a){switch(a){case 0:return this.x;case 1:return this.y;default:throw Error("index is out of range: "+a);}},copy:function(a){this.x=a.x;this.y=a.y;return this},add:function(a,
+b){if(void 0!==b)return console.warn("DEPRECATED: Vector2's .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(a,b);this.x+=a.x;this.y+=a.y;return this},addVectors:function(a,b){this.x=a.x+b.x;this.y=a.y+b.y;return this},addScalar:function(a){this.x+=a;this.y+=a;return this},sub:function(a,b){if(void 0!==b)return console.warn("DEPRECATED: Vector2's .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(a,b);this.x-=a.x;this.y-=
+a.y;return this},subVectors:function(a,b){this.x=a.x-b.x;this.y=a.y-b.y;return this},multiplyScalar:function(a){this.x*=a;this.y*=a;return this},divideScalar:function(a){0!==a?(a=1/a,this.x*=a,this.y*=a):this.y=this.x=0;return this},min:function(a){this.x>a.x&&(this.x=a.x);this.y>a.y&&(this.y=a.y);return this},max:function(a){this.x<a.x&&(this.x=a.x);this.y<a.y&&(this.y=a.y);return this},clamp:function(a,b){this.x<a.x?this.x=a.x:this.x>b.x&&(this.x=b.x);this.y<a.y?this.y=a.y:this.y>b.y&&(this.y=b.y);
+return this},negate:function(){return this.multiplyScalar(-1)},dot:function(a){return this.x*a.x+this.y*a.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(a){return Math.sqrt(this.distanceToSquared(a))},distanceToSquared:function(a){var b=this.x-a.x,a=this.y-a.y;return b*b+a*a},setLength:function(a){var b=this.length();0!==b&&a!==b&&this.multiplyScalar(a/
+b);return this},lerp:function(a,b){this.x+=(a.x-this.x)*b;this.y+=(a.y-this.y)*b;return this},equals:function(a){return a.x===this.x&&a.y===this.y},fromArray:function(a){this.x=a[0];this.y=a[1];return this},toArray:function(){return[this.x,this.y]},clone:function(){return new THREE.Vector2(this.x,this.y)}};THREE.Vector3=function(a,b,c){this.x=a||0;this.y=b||0;this.z=c||0};
+THREE.Vector3.prototype={constructor:THREE.Vector3,set:function(a,b,c){this.x=a;this.y=b;this.z=c;return this},setX:function(a){this.x=a;return this},setY:function(a){this.y=a;return this},setZ:function(a){this.z=a;return this},setComponent:function(a,b){switch(a){case 0:this.x=b;break;case 1:this.y=b;break;case 2:this.z=b;break;default:throw Error("index is out of range: "+a);}},getComponent:function(a){switch(a){case 0:return this.x;case 1:return this.y;case 2:return this.z;default:throw Error("index is out of range: "+
+a);}},copy:function(a){this.x=a.x;this.y=a.y;this.z=a.z;return this},add:function(a,b){if(void 0!==b)return console.warn("DEPRECATED: Vector3's .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(a,b);this.x+=a.x;this.y+=a.y;this.z+=a.z;return this},addScalar:function(a){this.x+=a;this.y+=a;this.z+=a;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(a,b){if(void 0!==b)return console.warn("DEPRECATED: Vector3's .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),
+this.subVectors(a,b);this.x-=a.x;this.y-=a.y;this.z-=a.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(a,b){if(void 0!==b)return console.warn("DEPRECATED: Vector3's .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead."),this.multiplyVectors(a,b);this.x*=a.x;this.y*=a.y;this.z*=a.z;return this},multiplyScalar:function(a){this.x*=a;this.y*=a;this.z*=a;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},applyMatrix3:function(a){var b=this.x,c=this.y,d=this.z,a=a.elements;this.x=a[0]*b+a[3]*c+a[6]*d;this.y=a[1]*b+a[4]*c+a[7]*d;this.z=a[2]*b+a[5]*c+a[8]*d;return this},applyMatrix4:function(a){var b=this.x,c=this.y,d=this.z,a=a.elements;this.x=a[0]*b+a[4]*c+a[8]*d+a[12];this.y=a[1]*b+a[5]*c+a[9]*d+a[13];this.z=a[2]*b+a[6]*c+a[10]*d+a[14];return this},applyProjection:function(a){var b=this.x,c=this.y,d=this.z,a=a.elements,e=1/(a[3]*b+a[7]*c+a[11]*d+a[15]);
+this.x=(a[0]*b+a[4]*c+a[8]*d+a[12])*e;this.y=(a[1]*b+a[5]*c+a[9]*d+a[13])*e;this.z=(a[2]*b+a[6]*c+a[10]*d+a[14])*e;return this},applyQuaternion:function(a){var b=this.x,c=this.y,d=this.z,e=a.x,f=a.y,h=a.z,a=a.w,g=a*b+f*d-h*c,i=a*c+h*b-e*d,k=a*d+e*c-f*b,b=-e*b-f*c-h*d;this.x=g*a+b*-e+i*-h-k*-f;this.y=i*a+b*-f+k*-e-g*-h;this.z=k*a+b*-h+g*-f-i*-e;return this},transformDirection:function(a){var b=this.x,c=this.y,d=this.z,a=a.elements;this.x=a[0]*b+a[4]*c+a[8]*d;this.y=a[1]*b+a[5]*c+a[9]*d;this.z=a[2]*
+b+a[6]*c+a[10]*d;this.normalize();return this},divide:function(a){this.x/=a.x;this.y/=a.y;this.z/=a.z;return this},divideScalar:function(a){0!==a?(a=1/a,this.x*=a,this.y*=a,this.z*=a):this.z=this.y=this.x=0;return this},min:function(a){this.x>a.x&&(this.x=a.x);this.y>a.y&&(this.y=a.y);this.z>a.z&&(this.z=a.z);return this},max:function(a){this.x<a.x&&(this.x=a.x);this.y<a.y&&(this.y=a.y);this.z<a.z&&(this.z=a.z);return this},clamp:function(a,b){this.x<a.x?this.x=a.x:this.x>b.x&&(this.x=b.x);this.y<
+a.y?this.y=a.y:this.y>b.y&&(this.y=b.y);this.z<a.z?this.z=a.z:this.z>b.z&&(this.z=b.z);return this},negate:function(){return this.multiplyScalar(-1)},dot:function(a){return this.x*a.x+this.y*a.y+this.z*a.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(a){var b=this.length();0!==b&&a!==b&&this.multiplyScalar(a/b);return this},lerp:function(a,b){this.x+=(a.x-this.x)*b;this.y+=(a.y-this.y)*b;this.z+=(a.z-this.z)*b;return this},cross:function(a,b){if(void 0!==b)return console.warn("DEPRECATED: Vector3's .cross() now only accepts one argument. Use .crossVectors( a, b ) instead."),this.crossVectors(a,b);var c=this.x,d=this.y,e=this.z;this.x=d*a.z-e*a.y;this.y=e*a.x-c*a.z;this.z=c*a.y-d*a.x;return this},crossVectors:function(a,b){var c=
+a.x,d=a.y,e=a.z,f=b.x,h=b.y,g=b.z;this.x=d*g-e*h;this.y=e*f-c*g;this.z=c*h-d*f;return this},angleTo:function(a){a=this.dot(a)/(this.length()*a.length());return Math.acos(THREE.Math.clamp(a,-1,1))},distanceTo:function(a){return Math.sqrt(this.distanceToSquared(a))},distanceToSquared:function(a){var b=this.x-a.x,c=this.y-a.y,a=this.z-a.z;return b*b+c*c+a*a},setEulerFromRotationMatrix:function(){console.error("REMOVED: Vector3's setEulerFromRotationMatrix has been removed in favor of Euler.setFromRotationMatrix(), please update your code.")},
+setEulerFromQuaternion:function(){console.error("REMOVED: Vector3's setEulerFromQuaternion: has been removed in favor of Euler.setFromQuaternion(), please update your code.")},getPositionFromMatrix:function(a){this.x=a.elements[12];this.y=a.elements[13];this.z=a.elements[14];return this},getScaleFromMatrix:function(a){var b=this.set(a.elements[0],a.elements[1],a.elements[2]).length(),c=this.set(a.elements[4],a.elements[5],a.elements[6]).length(),a=this.set(a.elements[8],a.elements[9],a.elements[10]).length();
+this.x=b;this.y=c;this.z=a;return this},getColumnFromMatrix:function(a,b){var c=4*a,d=b.elements;this.x=d[c];this.y=d[c+1];this.z=d[c+2];return this},equals:function(a){return a.x===this.x&&a.y===this.y&&a.z===this.z},fromArray:function(a){this.x=a[0];this.y=a[1];this.z=a[2];return this},toArray:function(){return[this.x,this.y,this.z]},clone:function(){return new THREE.Vector3(this.x,this.y,this.z)}};
+THREE.extend(THREE.Vector3.prototype,{applyEuler:function(){var a=new THREE.Quaternion;return function(b){!1===b instanceof THREE.Euler&&console.error("ERROR: Vector3's .applyEuler() now expects a Euler rotation rather than a Vector3 and order.  Please update your code.");this.applyQuaternion(a.setFromEuler(b));return this}}(),applyAxisAngle:function(){var a=new THREE.Quaternion;return function(b,c){this.applyQuaternion(a.setFromAxisAngle(b,c));return this}}(),projectOnVector:function(){var a=new THREE.Vector3;
+return function(b){a.copy(b).normalize();b=this.dot(a);return this.copy(a).multiplyScalar(b)}}(),projectOnPlane:function(){var a=new THREE.Vector3;return function(b){a.copy(this).projectOnVector(b);return this.sub(a)}}(),reflect:function(){var a=new THREE.Vector3;return function(b){a.copy(this).projectOnVector(b).multiplyScalar(2);return this.subVectors(a,this)}}()});THREE.Vector4=function(a,b,c,d){this.x=a||0;this.y=b||0;this.z=c||0;this.w=void 0!==d?d:1};
+THREE.Vector4.prototype={constructor:THREE.Vector4,set:function(a,b,c,d){this.x=a;this.y=b;this.z=c;this.w=d;return this},setX:function(a){this.x=a;return this},setY:function(a){this.y=a;return this},setZ:function(a){this.z=a;return this},setW:function(a){this.w=a;return this},setComponent:function(a,b){switch(a){case 0:this.x=b;break;case 1:this.y=b;break;case 2:this.z=b;break;case 3:this.w=b;break;default:throw Error("index is out of range: "+a);}},getComponent:function(a){switch(a){case 0:return this.x;
+case 1:return this.y;case 2:return this.z;case 3:return this.w;default:throw Error("index is out of range: "+a);}},copy:function(a){this.x=a.x;this.y=a.y;this.z=a.z;this.w=void 0!==a.w?a.w:1;return this},add:function(a,b){if(void 0!==b)return console.warn("DEPRECATED: Vector4's .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(a,b);this.x+=a.x;this.y+=a.y;this.z+=a.z;this.w+=a.w;return this},addScalar:function(a){this.x+=a;this.y+=a;this.z+=a;this.w+=a;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(a,b){if(void 0!==b)return console.warn("DEPRECATED: Vector4's .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(a,b);this.x-=a.x;this.y-=a.y;this.z-=a.z;this.w-=a.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(a){this.x*=a;this.y*=a;this.z*=a;this.w*=a;return this},
+applyMatrix4:function(a){var b=this.x,c=this.y,d=this.z,e=this.w,a=a.elements;this.x=a[0]*b+a[4]*c+a[8]*d+a[12]*e;this.y=a[1]*b+a[5]*c+a[9]*d+a[13]*e;this.z=a[2]*b+a[6]*c+a[10]*d+a[14]*e;this.w=a[3]*b+a[7]*c+a[11]*d+a[15]*e;return this},divideScalar:function(a){0!==a?(a=1/a,this.x*=a,this.y*=a,this.z*=a,this.w*=a):(this.z=this.y=this.x=0,this.w=1);return this},setAxisAngleFromQuaternion:function(a){this.w=2*Math.acos(a.w);var b=Math.sqrt(1-a.w*a.w);1E-4>b?(this.x=1,this.z=this.y=0):(this.x=a.x/b,
+this.y=a.y/b,this.z=a.z/b);return this},setAxisAngleFromRotationMatrix:function(a){var b,c,d,a=a.elements,e=a[0];d=a[4];var f=a[8],h=a[1],g=a[5],i=a[9];c=a[2];b=a[6];var k=a[10];if(0.01>Math.abs(d-h)&&0.01>Math.abs(f-c)&&0.01>Math.abs(i-b)){if(0.1>Math.abs(d+h)&&0.1>Math.abs(f+c)&&0.1>Math.abs(i+b)&&0.1>Math.abs(e+g+k-3))return this.set(1,0,0,0),this;a=Math.PI;e=(e+1)/2;g=(g+1)/2;k=(k+1)/2;d=(d+h)/4;f=(f+c)/4;i=(i+b)/4;e>g&&e>k?0.01>e?(b=0,d=c=0.707106781):(b=Math.sqrt(e),c=d/b,d=f/b):g>k?0.01>g?
+(b=0.707106781,c=0,d=0.707106781):(c=Math.sqrt(g),b=d/c,d=i/c):0.01>k?(c=b=0.707106781,d=0):(d=Math.sqrt(k),b=f/d,c=i/d);this.set(b,c,d,a);return this}a=Math.sqrt((b-i)*(b-i)+(f-c)*(f-c)+(h-d)*(h-d));0.001>Math.abs(a)&&(a=1);this.x=(b-i)/a;this.y=(f-c)/a;this.z=(h-d)/a;this.w=Math.acos((e+g+k-1)/2);return this},min:function(a){this.x>a.x&&(this.x=a.x);this.y>a.y&&(this.y=a.y);this.z>a.z&&(this.z=a.z);this.w>a.w&&(this.w=a.w);return this},max:function(a){this.x<a.x&&(this.x=a.x);this.y<a.y&&(this.y=
+a.y);this.z<a.z&&(this.z=a.z);this.w<a.w&&(this.w=a.w);return this},clamp:function(a,b){this.x<a.x?this.x=a.x:this.x>b.x&&(this.x=b.x);this.y<a.y?this.y=a.y:this.y>b.y&&(this.y=b.y);this.z<a.z?this.z=a.z:this.z>b.z&&(this.z=b.z);this.w<a.w?this.w=a.w:this.w>b.w&&(this.w=b.w);return this},negate:function(){return this.multiplyScalar(-1)},dot:function(a){return this.x*a.x+this.y*a.y+this.z*a.z+this.w*a.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(a){var b=this.length();0!==b&&a!==b&&this.multiplyScalar(a/b);return this},lerp:function(a,b){this.x+=(a.x-this.x)*b;this.y+=(a.y-this.y)*b;this.z+=(a.z-this.z)*b;this.w+=(a.w-this.w)*b;return this},equals:function(a){return a.x===this.x&&a.y===this.y&&a.z===this.z&&
+a.w===this.w},fromArray:function(a){this.x=a[0];this.y=a[1];this.z=a[2];this.w=a[3];return this},toArray:function(){return[this.x,this.y,this.z,this.w]},clone:function(){return new THREE.Vector4(this.x,this.y,this.z,this.w)}};THREE.Euler=function(a,b,c,d){this._x=a||0;this._y=b||0;this._z=c||0;this._order=d||THREE.Euler.DefaultOrder};THREE.Euler.RotationOrders="XYZ YZX ZXY XZY YXZ ZYX".split(" ");THREE.Euler.DefaultOrder="XYZ";
+THREE.Euler.prototype={constructor:THREE.Euler,_x:0,_y:0,_z:0,_order:THREE.Euler.DefaultOrder,_quaternion:void 0,_updateQuaternion:function(){void 0!==this._quaternion&&this._quaternion.setFromEuler(this,!1)},get x(){return this._x},set x(a){this._x=a;this._updateQuaternion()},get y(){return this._y},set y(a){this._y=a;this._updateQuaternion()},get z(){return this._z},set z(a){this._z=a;this._updateQuaternion()},get order(){return this._order},set order(a){this._order=a;this._updateQuaternion()},
+set:function(a,b,c,d){this._x=a;this._y=b;this._z=c;this._order=d||this._order;this._updateQuaternion();return this},copy:function(a){this._x=a._x;this._y=a._y;this._z=a._z;this._order=a._order;this._updateQuaternion();return this},setFromRotationMatrix:function(a,b){function c(a){return Math.min(Math.max(a,-1),1)}var d=a.elements,e=d[0],f=d[4],h=d[8],g=d[1],i=d[5],k=d[9],m=d[2],l=d[6],d=d[10],b=b||this._order;"XYZ"===b?(this._y=Math.asin(c(h)),0.99999>Math.abs(h)?(this._x=Math.atan2(-k,d),this._z=
+Math.atan2(-f,e)):(this._x=Math.atan2(l,i),this._z=0)):"YXZ"===b?(this._x=Math.asin(-c(k)),0.99999>Math.abs(k)?(this._y=Math.atan2(h,d),this._z=Math.atan2(g,i)):(this._y=Math.atan2(-m,e),this._z=0)):"ZXY"===b?(this._x=Math.asin(c(l)),0.99999>Math.abs(l)?(this._y=Math.atan2(-m,d),this._z=Math.atan2(-f,i)):(this._y=0,this._z=Math.atan2(g,e))):"ZYX"===b?(this._y=Math.asin(-c(m)),0.99999>Math.abs(m)?(this._x=Math.atan2(l,d),this._z=Math.atan2(g,e)):(this._x=0,this._z=Math.atan2(-f,i))):"YZX"===b?(this._z=
+Math.asin(c(g)),0.99999>Math.abs(g)?(this._x=Math.atan2(-k,i),this._y=Math.atan2(-m,e)):(this._x=0,this._y=Math.atan2(h,d))):"XZY"===b?(this._z=Math.asin(-c(f)),0.99999>Math.abs(f)?(this._x=Math.atan2(l,i),this._y=Math.atan2(h,e)):(this._x=Math.atan2(-k,d),this._y=0)):console.warn("WARNING: Euler.setFromRotationMatrix() given unsupported order: "+b);this._order=b;this._updateQuaternion();return this},setFromQuaternion:function(a,b,c){function d(a){return Math.min(Math.max(a,-1),1)}var e=a.x*a.x,f=
+a.y*a.y,h=a.z*a.z,g=a.w*a.w,b=b||this._order;"XYZ"===b?(this._x=Math.atan2(2*(a.x*a.w-a.y*a.z),g-e-f+h),this._y=Math.asin(d(2*(a.x*a.z+a.y*a.w))),this._z=Math.atan2(2*(a.z*a.w-a.x*a.y),g+e-f-h)):"YXZ"===b?(this._x=Math.asin(d(2*(a.x*a.w-a.y*a.z))),this._y=Math.atan2(2*(a.x*a.z+a.y*a.w),g-e-f+h),this._z=Math.atan2(2*(a.x*a.y+a.z*a.w),g-e+f-h)):"ZXY"===b?(this._x=Math.asin(d(2*(a.x*a.w+a.y*a.z))),this._y=Math.atan2(2*(a.y*a.w-a.z*a.x),g-e-f+h),this._z=Math.atan2(2*(a.z*a.w-a.x*a.y),g-e+f-h)):"ZYX"===
+b?(this._x=Math.atan2(2*(a.x*a.w+a.z*a.y),g-e-f+h),this._y=Math.asin(d(2*(a.y*a.w-a.x*a.z))),this._z=Math.atan2(2*(a.x*a.y+a.z*a.w),g+e-f-h)):"YZX"===b?(this._x=Math.atan2(2*(a.x*a.w-a.z*a.y),g-e+f-h),this._y=Math.atan2(2*(a.y*a.w-a.x*a.z),g+e-f-h),this._z=Math.asin(d(2*(a.x*a.y+a.z*a.w)))):"XZY"===b?(this._x=Math.atan2(2*(a.x*a.w+a.y*a.z),g-e+f-h),this._y=Math.atan2(2*(a.x*a.z+a.y*a.w),g+e-f-h),this._z=Math.asin(d(2*(a.z*a.w-a.x*a.y)))):console.warn("WARNING: Euler.setFromQuaternion() given unsupported order: "+
+b);this._order=b;!1!==c&&this._updateQuaternion();return this},reorder:function(){var a=new THREE.Quaternion;return function(b){a.setFromEuler(this);this.setFromQuaternion(a,b)}}(),fromArray:function(a){this._x=a[0];this._y=a[1];this._z=a[2];void 0!==a[3]&&(this._order=a[3]);this._updateQuaternion();return this},toArray:function(){return[this._x,this._y,this._z,this._order]},equals:function(a){return a._x===this._x&&a._y===this._y&&a._z===this._z&&a._order===this._order},clone:function(){return new THREE.Euler(this._x,
+this._y,this._z,this._order)}};THREE.Line3=function(a,b){this.start=void 0!==a?a:new THREE.Vector3;this.end=void 0!==b?b:new THREE.Vector3};
+THREE.Line3.prototype={constructor:THREE.Line3,set:function(a,b){this.start.copy(a);this.end.copy(b);return this},copy:function(a){this.start.copy(a.start);this.end.copy(a.end);return this},center:function(a){return(a||new THREE.Vector3).addVectors(this.start,this.end).multiplyScalar(0.5)},delta:function(a){return(a||new THREE.Vector3).subVectors(this.end,this.start)},distanceSq:function(){return this.start.distanceToSquared(this.end)},distance:function(){return this.start.distanceTo(this.end)},at:function(a,
+b){var c=b||new THREE.Vector3;return this.delta(c).multiplyScalar(a).add(this.start)},closestPointToPointParameter:function(){var a=new THREE.Vector3,b=new THREE.Vector3;return function(c,d){a.subVectors(c,this.start);b.subVectors(this.end,this.start);var e=b.dot(b),e=b.dot(a)/e;d&&(e=THREE.Math.clamp(e,0,1));return e}}(),closestPointToPoint:function(a,b,c){a=this.closestPointToPointParameter(a,b);c=c||new THREE.Vector3;return this.delta(c).multiplyScalar(a).add(this.start)},applyMatrix4:function(a){this.start.applyMatrix4(a);
+this.end.applyMatrix4(a);return this},equals:function(a){return a.start.equals(this.start)&&a.end.equals(this.end)},clone:function(){return(new THREE.Line3).copy(this)}};THREE.Box2=function(a,b){this.min=void 0!==a?a:new THREE.Vector2(Infinity,Infinity);this.max=void 0!==b?b:new THREE.Vector2(-Infinity,-Infinity)};
+THREE.Box2.prototype={constructor:THREE.Box2,set:function(a,b){this.min.copy(a);this.max.copy(b);return this},setFromPoints:function(a){if(0<a.length){var b=a[0];this.min.copy(b);this.max.copy(b);for(var c=1,d=a.length;c<d;c++)b=a[c],b.x<this.min.x?this.min.x=b.x:b.x>this.max.x&&(this.max.x=b.x),b.y<this.min.y?this.min.y=b.y:b.y>this.max.y&&(this.max.y=b.y)}else this.makeEmpty();return this},setFromCenterAndSize:function(){var a=new THREE.Vector2;return function(b,c){var d=a.copy(c).multiplyScalar(0.5);
+this.min.copy(b).sub(d);this.max.copy(b).add(d);return this}}(),copy:function(a){this.min.copy(a.min);this.max.copy(a.max);return this},makeEmpty:function(){this.min.x=this.min.y=Infinity;this.max.x=this.max.y=-Infinity;return this},empty:function(){return this.max.x<this.min.x||this.max.y<this.min.y},center:function(a){return(a||new THREE.Vector2).addVectors(this.min,this.max).multiplyScalar(0.5)},size:function(a){return(a||new THREE.Vector2).subVectors(this.max,this.min)},expandByPoint:function(a){this.min.min(a);
+this.max.max(a);return this},expandByVector:function(a){this.min.sub(a);this.max.add(a);return this},expandByScalar:function(a){this.min.addScalar(-a);this.max.addScalar(a);return this},containsPoint:function(a){return a.x<this.min.x||a.x>this.max.x||a.y<this.min.y||a.y>this.max.y?!1:!0},containsBox:function(a){return this.min.x<=a.min.x&&a.max.x<=this.max.x&&this.min.y<=a.min.y&&a.max.y<=this.max.y?!0:!1},getParameter:function(a){return new THREE.Vector2((a.x-this.min.x)/(this.max.x-this.min.x),
+(a.y-this.min.y)/(this.max.y-this.min.y))},isIntersectionBox:function(a){return a.max.x<this.min.x||a.min.x>this.max.x||a.max.y<this.min.y||a.min.y>this.max.y?!1:!0},clampPoint:function(a,b){return(b||new THREE.Vector2).copy(a).clamp(this.min,this.max)},distanceToPoint:function(){var a=new THREE.Vector2;return function(b){return a.copy(b).clamp(this.min,this.max).sub(b).length()}}(),intersect:function(a){this.min.max(a.min);this.max.min(a.max);return this},union:function(a){this.min.min(a.min);this.max.max(a.max);
+return this},translate:function(a){this.min.add(a);this.max.add(a);return this},equals:function(a){return a.min.equals(this.min)&&a.max.equals(this.max)},clone:function(){return(new THREE.Box2).copy(this)}};THREE.Box3=function(a,b){this.min=void 0!==a?a:new THREE.Vector3(Infinity,Infinity,Infinity);this.max=void 0!==b?b:new THREE.Vector3(-Infinity,-Infinity,-Infinity)};
+THREE.Box3.prototype={constructor:THREE.Box3,set:function(a,b){this.min.copy(a);this.max.copy(b);return this},addPoint:function(a){a.x<this.min.x?this.min.x=a.x:a.x>this.max.x&&(this.max.x=a.x);a.y<this.min.y?this.min.y=a.y:a.y>this.max.y&&(this.max.y=a.y);a.z<this.min.z?this.min.z=a.z:a.z>this.max.z&&(this.max.z=a.z)},setFromPoints:function(a){if(0<a.length){var b=a[0];this.min.copy(b);this.max.copy(b);for(var b=1,c=a.length;b<c;b++)this.addPoint(a[b])}else this.makeEmpty();return this},setFromCenterAndSize:function(){var a=
+new THREE.Vector3;return function(b,c){var d=a.copy(c).multiplyScalar(0.5);this.min.copy(b).sub(d);this.max.copy(b).add(d);return this}}(),setFromObject:function(){var a=new THREE.Vector3;return function(b){var c=this;b.updateMatrixWorld(!0);this.makeEmpty();b.traverse(function(b){if(void 0!==b.geometry&&void 0!==b.geometry.vertices)for(var e=b.geometry.vertices,f=0,h=e.length;f<h;f++)a.copy(e[f]),a.applyMatrix4(b.matrixWorld),c.expandByPoint(a)});return this}}(),copy:function(a){this.min.copy(a.min);
+this.max.copy(a.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(){return this.max.x<this.min.x||this.max.y<this.min.y||this.max.z<this.min.z},center:function(a){return(a||new THREE.Vector3).addVectors(this.min,this.max).multiplyScalar(0.5)},size:function(a){return(a||new THREE.Vector3).subVectors(this.max,this.min)},expandByPoint:function(a){this.min.min(a);this.max.max(a);return this},expandByVector:function(a){this.min.sub(a);
+this.max.add(a);return this},expandByScalar:function(a){this.min.addScalar(-a);this.max.addScalar(a);return this},containsPoint:function(a){return a.x<this.min.x||a.x>this.max.x||a.y<this.min.y||a.y>this.max.y||a.z<this.min.z||a.z>this.max.z?!1:!0},containsBox:function(a){return this.min.x<=a.min.x&&a.max.x<=this.max.x&&this.min.y<=a.min.y&&a.max.y<=this.max.y&&this.min.z<=a.min.z&&a.max.z<=this.max.z?!0:!1},getParameter:function(a){return new THREE.Vector3((a.x-this.min.x)/(this.max.x-this.min.x),
+(a.y-this.min.y)/(this.max.y-this.min.y),(a.z-this.min.z)/(this.max.z-this.min.z))},isIntersectionBox:function(a){return a.max.x<this.min.x||a.min.x>this.max.x||a.max.y<this.min.y||a.min.y>this.max.y||a.max.z<this.min.z||a.min.z>this.max.z?!1:!0},clampPoint:function(a,b){return(b||new THREE.Vector3).copy(a).clamp(this.min,this.max)},distanceToPoint:function(){var a=new THREE.Vector3;return function(b){return a.copy(b).clamp(this.min,this.max).sub(b).length()}}(),getBoundingSphere:function(){var a=
+new THREE.Vector3;return function(b){b=b||new THREE.Sphere;b.center=this.center();b.radius=0.5*this.size(a).length();return b}}(),intersect:function(a){this.min.max(a.min);this.max.min(a.max);return this},union:function(a){this.min.min(a.min);this.max.max(a.max);return this},applyMatrix4:function(){var a=[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(b){a[0].set(this.min.x,this.min.y,
+this.min.z).applyMatrix4(b);a[1].set(this.min.x,this.min.y,this.max.z).applyMatrix4(b);a[2].set(this.min.x,this.max.y,this.min.z).applyMatrix4(b);a[3].set(this.min.x,this.max.y,this.max.z).applyMatrix4(b);a[4].set(this.max.x,this.min.y,this.min.z).applyMatrix4(b);a[5].set(this.max.x,this.min.y,this.max.z).applyMatrix4(b);a[6].set(this.max.x,this.max.y,this.min.z).applyMatrix4(b);a[7].set(this.max.x,this.max.y,this.max.z).applyMatrix4(b);this.makeEmpty();this.setFromPoints(a);return this}}(),translate:function(a){this.min.add(a);
+this.max.add(a);return this},equals:function(a){return a.min.equals(this.min)&&a.max.equals(this.max)},clone:function(){return(new THREE.Box3).copy(this)}};THREE.Matrix3=function(a,b,c,d,e,f,h,g,i){this.elements=new Float32Array(9);this.set(void 0!==a?a:1,b||0,c||0,d||0,void 0!==e?e:1,f||0,h||0,g||0,void 0!==i?i:1)};
+THREE.Matrix3.prototype={constructor:THREE.Matrix3,set:function(a,b,c,d,e,f,h,g,i){var k=this.elements;k[0]=a;k[3]=b;k[6]=c;k[1]=d;k[4]=e;k[7]=f;k[2]=h;k[5]=g;k[8]=i;return this},identity:function(){this.set(1,0,0,0,1,0,0,0,1);return this},copy:function(a){a=a.elements;this.set(a[0],a[3],a[6],a[1],a[4],a[7],a[2],a[5],a[8]);return this},multiplyVector3:function(a){console.warn("DEPRECATED: Matrix3's .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead.");return a.applyMatrix3(this)},
+multiplyVector3Array:function(){var a=new THREE.Vector3;return function(b){for(var c=0,d=b.length;c<d;c+=3)a.x=b[c],a.y=b[c+1],a.z=b[c+2],a.applyMatrix3(this),b[c]=a.x,b[c+1]=a.y,b[c+2]=a.z;return b}}(),multiplyScalar:function(a){var b=this.elements;b[0]*=a;b[3]*=a;b[6]*=a;b[1]*=a;b[4]*=a;b[7]*=a;b[2]*=a;b[5]*=a;b[8]*=a;return this},determinant:function(){var a=this.elements,b=a[0],c=a[1],d=a[2],e=a[3],f=a[4],h=a[5],g=a[6],i=a[7],a=a[8];return b*f*a-b*h*i-c*e*a+c*h*g+d*e*i-d*f*g},getInverse:function(a,
+b){var c=a.elements,d=this.elements;d[0]=c[10]*c[5]-c[6]*c[9];d[1]=-c[10]*c[1]+c[2]*c[9];d[2]=c[6]*c[1]-c[2]*c[5];d[3]=-c[10]*c[4]+c[6]*c[8];d[4]=c[10]*c[0]-c[2]*c[8];d[5]=-c[6]*c[0]+c[2]*c[4];d[6]=c[9]*c[4]-c[5]*c[8];d[7]=-c[9]*c[0]+c[1]*c[8];d[8]=c[5]*c[0]-c[1]*c[4];c=c[0]*d[0]+c[1]*d[3]+c[2]*d[6];if(0===c){if(b)throw Error("Matrix3.getInverse(): can't invert matrix, determinant is 0");console.warn("Matrix3.getInverse(): can't invert matrix, determinant is 0");this.identity();return this}this.multiplyScalar(1/
+c);return this},transpose:function(){var a,b=this.elements;a=b[1];b[1]=b[3];b[3]=a;a=b[2];b[2]=b[6];b[6]=a;a=b[5];b[5]=b[7];b[7]=a;return this},getNormalMatrix:function(a){this.getInverse(a).transpose();return this},transposeIntoArray:function(a){var b=this.elements;a[0]=b[0];a[1]=b[3];a[2]=b[6];a[3]=b[1];a[4]=b[4];a[5]=b[7];a[6]=b[2];a[7]=b[5];a[8]=b[8];return this},clone:function(){var a=this.elements;return new THREE.Matrix3(a[0],a[3],a[6],a[1],a[4],a[7],a[2],a[5],a[8])}};THREE.Matrix4=function(a,b,c,d,e,f,h,g,i,k,m,l,p,s,t,n){var r=this.elements=new Float32Array(16);r[0]=void 0!==a?a:1;r[4]=b||0;r[8]=c||0;r[12]=d||0;r[1]=e||0;r[5]=void 0!==f?f:1;r[9]=h||0;r[13]=g||0;r[2]=i||0;r[6]=k||0;r[10]=void 0!==m?m:1;r[14]=l||0;r[3]=p||0;r[7]=s||0;r[11]=t||0;r[15]=void 0!==n?n:1};
+THREE.Matrix4.prototype={constructor:THREE.Matrix4,set:function(a,b,c,d,e,f,h,g,i,k,m,l,p,s,t,n){var r=this.elements;r[0]=a;r[4]=b;r[8]=c;r[12]=d;r[1]=e;r[5]=f;r[9]=h;r[13]=g;r[2]=i;r[6]=k;r[10]=m;r[14]=l;r[3]=p;r[7]=s;r[11]=t;r[15]=n;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(a){this.elements.set(a.elements);return this},extractPosition:function(a){console.warn("DEPRECATED: Matrix4's .extractPosition() has been renamed to .copyPosition().");
+return this.copyPosition(a)},copyPosition:function(a){var b=this.elements,a=a.elements;b[12]=a[12];b[13]=a[13];b[14]=a[14];return this},extractRotation:function(){var a=new THREE.Vector3;return function(b){var c=this.elements,b=b.elements,d=1/a.set(b[0],b[1],b[2]).length(),e=1/a.set(b[4],b[5],b[6]).length(),f=1/a.set(b[8],b[9],b[10]).length();c[0]=b[0]*d;c[1]=b[1]*d;c[2]=b[2]*d;c[4]=b[4]*e;c[5]=b[5]*e;c[6]=b[6]*e;c[8]=b[8]*f;c[9]=b[9]*f;c[10]=b[10]*f;return this}}(),makeRotationFromEuler:function(a){!1===
+a instanceof THREE.Euler&&console.error("ERROR: Matrix's .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.  Please update your code.");var b=this.elements,c=a.x,d=a.y,e=a.z,f=Math.cos(c),c=Math.sin(c),h=Math.cos(d),d=Math.sin(d),g=Math.cos(e),e=Math.sin(e);if("XYZ"===a.order){var a=f*g,i=f*e,k=c*g,m=c*e;b[0]=h*g;b[4]=-h*e;b[8]=d;b[1]=i+k*d;b[5]=a-m*d;b[9]=-c*h;b[2]=m-a*d;b[6]=k+i*d;b[10]=f*h}else"YXZ"===a.order?(a=h*g,i=h*e,k=d*g,m=d*e,b[0]=a+m*c,b[4]=k*c-i,b[8]=
+f*d,b[1]=f*e,b[5]=f*g,b[9]=-c,b[2]=i*c-k,b[6]=m+a*c,b[10]=f*h):"ZXY"===a.order?(a=h*g,i=h*e,k=d*g,m=d*e,b[0]=a-m*c,b[4]=-f*e,b[8]=k+i*c,b[1]=i+k*c,b[5]=f*g,b[9]=m-a*c,b[2]=-f*d,b[6]=c,b[10]=f*h):"ZYX"===a.order?(a=f*g,i=f*e,k=c*g,m=c*e,b[0]=h*g,b[4]=k*d-i,b[8]=a*d+m,b[1]=h*e,b[5]=m*d+a,b[9]=i*d-k,b[2]=-d,b[6]=c*h,b[10]=f*h):"YZX"===a.order?(a=f*h,i=f*d,k=c*h,m=c*d,b[0]=h*g,b[4]=m-a*e,b[8]=k*e+i,b[1]=e,b[5]=f*g,b[9]=-c*g,b[2]=-d*g,b[6]=i*e+k,b[10]=a-m*e):"XZY"===a.order&&(a=f*h,i=f*d,k=c*h,m=c*d,b[0]=
+h*g,b[4]=-e,b[8]=d*g,b[1]=a*e+m,b[5]=f*g,b[9]=i*e-k,b[2]=k*e-i,b[6]=c*g,b[10]=m*e+a);b[3]=0;b[7]=0;b[11]=0;b[12]=0;b[13]=0;b[14]=0;b[15]=1;return this},setRotationFromQuaternion:function(a){console.warn("DEPRECATED: Matrix4's .setRotationFromQuaternion() has been deprecated in favor of makeRotationFromQuaternion.  Please update your code.");return this.makeRotationFromQuaternion(a)},makeRotationFromQuaternion:function(a){var b=this.elements,c=a.x,d=a.y,e=a.z,f=a.w,h=c+c,g=d+d,i=e+e,a=c*h,k=c*g,c=
+c*i,m=d*g,d=d*i,e=e*i,h=f*h,g=f*g,f=f*i;b[0]=1-(m+e);b[4]=k-f;b[8]=c+g;b[1]=k+f;b[5]=1-(a+e);b[9]=d-h;b[2]=c-g;b[6]=d+h;b[10]=1-(a+m);b[3]=0;b[7]=0;b[11]=0;b[12]=0;b[13]=0;b[14]=0;b[15]=1;return this},lookAt:function(){var a=new THREE.Vector3,b=new THREE.Vector3,c=new THREE.Vector3;return function(d,e,f){var h=this.elements;c.subVectors(d,e).normalize();0===c.length()&&(c.z=1);a.crossVectors(f,c).normalize();0===a.length()&&(c.x+=1E-4,a.crossVectors(f,c).normalize());b.crossVectors(c,a);h[0]=a.x;
+h[4]=b.x;h[8]=c.x;h[1]=a.y;h[5]=b.y;h[9]=c.y;h[2]=a.z;h[6]=b.z;h[10]=c.z;return this}}(),multiply:function(a,b){return void 0!==b?(console.warn("DEPRECATED: Matrix4's .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead."),this.multiplyMatrices(a,b)):this.multiplyMatrices(this,a)},multiplyMatrices:function(a,b){var c=a.elements,d=b.elements,e=this.elements,f=c[0],h=c[4],g=c[8],i=c[12],k=c[1],m=c[5],l=c[9],p=c[13],s=c[2],t=c[6],n=c[10],r=c[14],q=c[3],u=c[7],w=c[11],c=c[15],
+z=d[0],B=d[4],D=d[8],x=d[12],F=d[1],A=d[5],O=d[9],C=d[13],E=d[2],I=d[6],y=d[10],v=d[14],G=d[3],R=d[7],J=d[11],d=d[15];e[0]=f*z+h*F+g*E+i*G;e[4]=f*B+h*A+g*I+i*R;e[8]=f*D+h*O+g*y+i*J;e[12]=f*x+h*C+g*v+i*d;e[1]=k*z+m*F+l*E+p*G;e[5]=k*B+m*A+l*I+p*R;e[9]=k*D+m*O+l*y+p*J;e[13]=k*x+m*C+l*v+p*d;e[2]=s*z+t*F+n*E+r*G;e[6]=s*B+t*A+n*I+r*R;e[10]=s*D+t*O+n*y+r*J;e[14]=s*x+t*C+n*v+r*d;e[3]=q*z+u*F+w*E+c*G;e[7]=q*B+u*A+w*I+c*R;e[11]=q*D+u*O+w*y+c*J;e[15]=q*x+u*C+w*v+c*d;return this},multiplyToArray:function(a,b,
+c){var d=this.elements;this.multiplyMatrices(a,b);c[0]=d[0];c[1]=d[1];c[2]=d[2];c[3]=d[3];c[4]=d[4];c[5]=d[5];c[6]=d[6];c[7]=d[7];c[8]=d[8];c[9]=d[9];c[10]=d[10];c[11]=d[11];c[12]=d[12];c[13]=d[13];c[14]=d[14];c[15]=d[15];return this},multiplyScalar:function(a){var b=this.elements;b[0]*=a;b[4]*=a;b[8]*=a;b[12]*=a;b[1]*=a;b[5]*=a;b[9]*=a;b[13]*=a;b[2]*=a;b[6]*=a;b[10]*=a;b[14]*=a;b[3]*=a;b[7]*=a;b[11]*=a;b[15]*=a;return this},multiplyVector3:function(a){console.warn("DEPRECATED: Matrix4's .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) or vector.applyProjection( matrix ) instead.");
+return a.applyProjection(this)},multiplyVector4:function(a){console.warn("DEPRECATED: Matrix4's .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead.");return a.applyMatrix4(this)},multiplyVector3Array:function(){var a=new THREE.Vector3;return function(b){for(var c=0,d=b.length;c<d;c+=3)a.x=b[c],a.y=b[c+1],a.z=b[c+2],a.applyProjection(this),b[c]=a.x,b[c+1]=a.y,b[c+2]=a.z;return b}}(),rotateAxis:function(a){console.warn("DEPRECATED: Matrix4's .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead.");
+a.transformDirection(this)},crossVector:function(a){console.warn("DEPRECATED: Matrix4's .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead.");return a.applyMatrix4(this)},determinant:function(){var a=this.elements,b=a[0],c=a[4],d=a[8],e=a[12],f=a[1],h=a[5],g=a[9],i=a[13],k=a[2],m=a[6],l=a[10],p=a[14];return a[3]*(+e*g*m-d*i*m-e*h*l+c*i*l+d*h*p-c*g*p)+a[7]*(+b*g*p-b*i*l+e*f*l-d*f*p+d*i*k-e*g*k)+a[11]*(+b*i*m-b*h*p-e*f*m+c*f*p+e*h*k-c*i*k)+a[15]*(-d*h*k-b*g*m+b*h*l+d*f*m-c*f*
+l+c*g*k)},transpose:function(){var a=this.elements,b;b=a[1];a[1]=a[4];a[4]=b;b=a[2];a[2]=a[8];a[8]=b;b=a[6];a[6]=a[9];a[9]=b;b=a[3];a[3]=a[12];a[12]=b;b=a[7];a[7]=a[13];a[13]=b;b=a[11];a[11]=a[14];a[14]=b;return this},flattenToArray:function(a){var b=this.elements;a[0]=b[0];a[1]=b[1];a[2]=b[2];a[3]=b[3];a[4]=b[4];a[5]=b[5];a[6]=b[6];a[7]=b[7];a[8]=b[8];a[9]=b[9];a[10]=b[10];a[11]=b[11];a[12]=b[12];a[13]=b[13];a[14]=b[14];a[15]=b[15];return a},flattenToArrayOffset:function(a,b){var c=this.elements;
+a[b]=c[0];a[b+1]=c[1];a[b+2]=c[2];a[b+3]=c[3];a[b+4]=c[4];a[b+5]=c[5];a[b+6]=c[6];a[b+7]=c[7];a[b+8]=c[8];a[b+9]=c[9];a[b+10]=c[10];a[b+11]=c[11];a[b+12]=c[12];a[b+13]=c[13];a[b+14]=c[14];a[b+15]=c[15];return a},getPosition:function(){var a=new THREE.Vector3;return function(){console.warn("DEPRECATED: Matrix4's .getPosition() has been removed. Use Vector3.getPositionFromMatrix( matrix ) instead.");var b=this.elements;return a.set(b[12],b[13],b[14])}}(),setPosition:function(a){var b=this.elements;
+b[12]=a.x;b[13]=a.y;b[14]=a.z;return this},getInverse:function(a,b){var c=this.elements,d=a.elements,e=d[0],f=d[4],h=d[8],g=d[12],i=d[1],k=d[5],m=d[9],l=d[13],p=d[2],s=d[6],t=d[10],n=d[14],r=d[3],q=d[7],u=d[11],d=d[15];c[0]=m*n*q-l*t*q+l*s*u-k*n*u-m*s*d+k*t*d;c[4]=g*t*q-h*n*q-g*s*u+f*n*u+h*s*d-f*t*d;c[8]=h*l*q-g*m*q+g*k*u-f*l*u-h*k*d+f*m*d;c[12]=g*m*s-h*l*s-g*k*t+f*l*t+h*k*n-f*m*n;c[1]=l*t*r-m*n*r-l*p*u+i*n*u+m*p*d-i*t*d;c[5]=h*n*r-g*t*r+g*p*u-e*n*u-h*p*d+e*t*d;c[9]=g*m*r-h*l*r-g*i*u+e*l*u+h*i*d-
+e*m*d;c[13]=h*l*p-g*m*p+g*i*t-e*l*t-h*i*n+e*m*n;c[2]=k*n*r-l*s*r+l*p*q-i*n*q-k*p*d+i*s*d;c[6]=g*s*r-f*n*r-g*p*q+e*n*q+f*p*d-e*s*d;c[10]=f*l*r-g*k*r+g*i*q-e*l*q-f*i*d+e*k*d;c[14]=g*k*p-f*l*p-g*i*s+e*l*s+f*i*n-e*k*n;c[3]=m*s*r-k*t*r-m*p*q+i*t*q+k*p*u-i*s*u;c[7]=f*t*r-h*s*r+h*p*q-e*t*q-f*p*u+e*s*u;c[11]=h*k*r-f*m*r-h*i*q+e*m*q+f*i*u-e*k*u;c[15]=f*m*p-h*k*p+h*i*s-e*m*s-f*i*t+e*k*t;c=e*c[0]+i*c[4]+p*c[8]+r*c[12];if(0==c){if(b)throw Error("Matrix4.getInverse(): can't invert matrix, determinant is 0");console.warn("Matrix4.getInverse(): can't invert matrix, determinant is 0");
+this.identity();return this}this.multiplyScalar(1/c);return this},translate:function(){console.warn("DEPRECATED: Matrix4's .translate() has been removed.")},rotateX:function(){console.warn("DEPRECATED: Matrix4's .rotateX() has been removed.")},rotateY:function(){console.warn("DEPRECATED: Matrix4's .rotateY() has been removed.")},rotateZ:function(){console.warn("DEPRECATED: Matrix4's .rotateZ() has been removed.")},rotateByAxis:function(){console.warn("DEPRECATED: Matrix4's .rotateByAxis() has been removed.")},
+scale:function(a){var b=this.elements,c=a.x,d=a.y,a=a.z;b[0]*=c;b[4]*=d;b[8]*=a;b[1]*=c;b[5]*=d;b[9]*=a;b[2]*=c;b[6]*=d;b[10]*=a;b[3]*=c;b[7]*=d;b[11]*=a;return this},getMaxScaleOnAxis:function(){var a=this.elements;return Math.sqrt(Math.max(a[0]*a[0]+a[1]*a[1]+a[2]*a[2],Math.max(a[4]*a[4]+a[5]*a[5]+a[6]*a[6],a[8]*a[8]+a[9]*a[9]+a[10]*a[10])))},makeTranslation:function(a,b,c){this.set(1,0,0,a,0,1,0,b,0,0,1,c,0,0,0,1);return this},makeRotationX:function(a){var b=Math.cos(a),a=Math.sin(a);this.set(1,
+0,0,0,0,b,-a,0,0,a,b,0,0,0,0,1);return this},makeRotationY:function(a){var b=Math.cos(a),a=Math.sin(a);this.set(b,0,a,0,0,1,0,0,-a,0,b,0,0,0,0,1);return this},makeRotationZ:function(a){var b=Math.cos(a),a=Math.sin(a);this.set(b,-a,0,0,a,b,0,0,0,0,1,0,0,0,0,1);return this},makeRotationAxis:function(a,b){var c=Math.cos(b),d=Math.sin(b),e=1-c,f=a.x,h=a.y,g=a.z,i=e*f,k=e*h;this.set(i*f+c,i*h-d*g,i*g+d*h,0,i*h+d*g,k*h+c,k*g-d*f,0,i*g-d*h,k*g+d*f,e*g*g+c,0,0,0,0,1);return this},makeScale:function(a,b,c){this.set(a,
+0,0,0,0,b,0,0,0,0,c,0,0,0,0,1);return this},compose:function(a,b,c){this.makeRotationFromQuaternion(b);this.scale(c);this.setPosition(a);return this},decompose:function(){var a=new THREE.Vector3,b=new THREE.Matrix4;return function(c,d,e){var f=this.elements,h=a.set(f[0],f[1],f[2]).length(),g=a.set(f[4],f[5],f[6]).length(),i=a.set(f[8],f[9],f[10]).length();c.x=f[12];c.y=f[13];c.z=f[14];b.elements.set(this.elements);var c=1/h,f=1/g,k=1/i;b.elements[0]*=c;b.elements[1]*=c;b.elements[2]*=c;b.elements[4]*=
+f;b.elements[5]*=f;b.elements[6]*=f;b.elements[8]*=k;b.elements[9]*=k;b.elements[10]*=k;d.setFromRotationMatrix(b);e.x=h;e.y=g;e.z=i;return this}}(),makeFrustum:function(a,b,c,d,e,f){var h=this.elements;h[0]=2*e/(b-a);h[4]=0;h[8]=(b+a)/(b-a);h[12]=0;h[1]=0;h[5]=2*e/(d-c);h[9]=(d+c)/(d-c);h[13]=0;h[2]=0;h[6]=0;h[10]=-(f+e)/(f-e);h[14]=-2*f*e/(f-e);h[3]=0;h[7]=0;h[11]=-1;h[15]=0;return this},makePerspective:function(a,b,c,d){var a=c*Math.tan(THREE.Math.degToRad(0.5*a)),e=-a;return this.makeFrustum(e*
+b,a*b,e,a,c,d)},makeOrthographic:function(a,b,c,d,e,f){var h=this.elements,g=b-a,i=c-d,k=f-e;h[0]=2/g;h[4]=0;h[8]=0;h[12]=-((b+a)/g);h[1]=0;h[5]=2/i;h[9]=0;h[13]=-((c+d)/i);h[2]=0;h[6]=0;h[10]=-2/k;h[14]=-((f+e)/k);h[3]=0;h[7]=0;h[11]=0;h[15]=1;return this},fromArray:function(a){this.elements.set(a);return this},toArray:function(){var a=this.elements;return[a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8],a[9],a[10],a[11],a[12],a[13],a[14],a[15]]},clone:function(){var a=this.elements;return new THREE.Matrix4(a[0],
+a[4],a[8],a[12],a[1],a[5],a[9],a[13],a[2],a[6],a[10],a[14],a[3],a[7],a[11],a[15])}};THREE.Ray=function(a,b){this.origin=void 0!==a?a:new THREE.Vector3;this.direction=void 0!==b?b:new THREE.Vector3};
+THREE.Ray.prototype={constructor:THREE.Ray,set:function(a,b){this.origin.copy(a);this.direction.copy(b);return this},copy:function(a){this.origin.copy(a.origin);this.direction.copy(a.direction);return this},at:function(a,b){return(b||new THREE.Vector3).copy(this.direction).multiplyScalar(a).add(this.origin)},recast:function(){var a=new THREE.Vector3;return function(b){this.origin.copy(this.at(b,a));return this}}(),closestPointToPoint:function(a,b){var c=b||new THREE.Vector3;c.subVectors(a,this.origin);
+var d=c.dot(this.direction);return 0>d?c.copy(this.origin):c.copy(this.direction).multiplyScalar(d).add(this.origin)},distanceToPoint:function(){var a=new THREE.Vector3;return function(b){var c=a.subVectors(b,this.origin).dot(this.direction);if(0>c)return this.origin.distanceTo(b);a.copy(this.direction).multiplyScalar(c).add(this.origin);return a.distanceTo(b)}}(),distanceSqToSegment:function(a,b,c,d){var e=a.clone().add(b).multiplyScalar(0.5),f=b.clone().sub(a).normalize(),h=0.5*a.distanceTo(b),
+g=this.origin.clone().sub(e),a=-this.direction.dot(f),b=g.dot(this.direction),i=-g.dot(f),k=g.lengthSq(),m=Math.abs(1-a*a),l,p;0<=m?(g=a*i-b,l=a*b-i,p=h*m,0<=g?l>=-p?l<=p?(h=1/m,g*=h,l*=h,a=g*(g+a*l+2*b)+l*(a*g+l+2*i)+k):(l=h,g=Math.max(0,-(a*l+b)),a=-g*g+l*(l+2*i)+k):(l=-h,g=Math.max(0,-(a*l+b)),a=-g*g+l*(l+2*i)+k):l<=-p?(g=Math.max(0,-(-a*h+b)),l=0<g?-h:Math.min(Math.max(-h,-i),h),a=-g*g+l*(l+2*i)+k):l<=p?(g=0,l=Math.min(Math.max(-h,-i),h),a=l*(l+2*i)+k):(g=Math.max(0,-(a*h+b)),l=0<g?h:Math.min(Math.max(-h,
+-i),h),a=-g*g+l*(l+2*i)+k)):(l=0<a?-h:h,g=Math.max(0,-(a*l+b)),a=-g*g+l*(l+2*i)+k);c&&c.copy(this.direction.clone().multiplyScalar(g).add(this.origin));d&&d.copy(f.clone().multiplyScalar(l).add(e));return a},isIntersectionSphere:function(a){return this.distanceToPoint(a.center)<=a.radius},isIntersectionPlane:function(a){var b=a.distanceToPoint(this.origin);return 0===b||0>a.normal.dot(this.direction)*b?!0:!1},distanceToPlane:function(a){var b=a.normal.dot(this.direction);if(0==b)return 0==a.distanceToPoint(this.origin)?
+0:null;a=-(this.origin.dot(a.normal)+a.constant)/b;return 0<=a?a:null},intersectPlane:function(a,b){var c=this.distanceToPlane(a);return null===c?null:this.at(c,b)},isIntersectionBox:function(){var a=new THREE.Vector3;return function(b){return null!==this.intersectBox(b,a)}}(),intersectBox:function(a,b){var c,d,e,f,h;d=1/this.direction.x;f=1/this.direction.y;h=1/this.direction.z;var g=this.origin;0<=d?(c=(a.min.x-g.x)*d,d*=a.max.x-g.x):(c=(a.max.x-g.x)*d,d*=a.min.x-g.x);0<=f?(e=(a.min.y-g.y)*f,f*=
+a.max.y-g.y):(e=(a.max.y-g.y)*f,f*=a.min.y-g.y);if(c>f||e>d)return null;if(e>c||c!==c)c=e;if(f<d||d!==d)d=f;0<=h?(e=(a.min.z-g.z)*h,h*=a.max.z-g.z):(e=(a.max.z-g.z)*h,h*=a.min.z-g.z);if(c>h||e>d)return null;if(e>c||c!==c)c=e;if(h<d||d!==d)d=h;return 0>d?null:this.at(0<=c?c:d,b)},intersectTriangle:function(){var a=new THREE.Vector3,b=new THREE.Vector3,c=new THREE.Vector3,d=new THREE.Vector3;return function(e,f,h,g,i){b.subVectors(f,e);c.subVectors(h,e);d.crossVectors(b,c);f=this.direction.dot(d);if(0<
+f){if(g)return null;g=1}else if(0>f)g=-1,f=-f;else return null;a.subVectors(this.origin,e);e=g*this.direction.dot(c.crossVectors(a,c));if(0>e)return null;h=g*this.direction.dot(b.cross(a));if(0>h||e+h>f)return null;e=-g*a.dot(d);return 0>e?null:this.at(e/f,i)}}(),applyMatrix4:function(a){this.direction.add(this.origin).applyMatrix4(a);this.origin.applyMatrix4(a);this.direction.sub(this.origin);this.direction.normalize();return this},equals:function(a){return a.origin.equals(this.origin)&&a.direction.equals(this.direction)},
+clone:function(){return(new THREE.Ray).copy(this)}};THREE.Sphere=function(a,b){this.center=void 0!==a?a:new THREE.Vector3;this.radius=void 0!==b?b:0};
+THREE.Sphere.prototype={constructor:THREE.Sphere,set:function(a,b){this.center.copy(a);this.radius=b;return this},setFromPoints:function(){var a=new THREE.Box3;return function(b,c){var d=this.center;void 0!==c?d.copy(c):a.setFromPoints(b).center(d);for(var e=0,f=0,h=b.length;f<h;f++)e=Math.max(e,d.distanceToSquared(b[f]));this.radius=Math.sqrt(e);return this}}(),copy:function(a){this.center.copy(a.center);this.radius=a.radius;return this},empty:function(){return 0>=this.radius},containsPoint:function(a){return a.distanceToSquared(this.center)<=
+this.radius*this.radius},distanceToPoint:function(a){return a.distanceTo(this.center)-this.radius},intersectsSphere:function(a){var b=this.radius+a.radius;return a.center.distanceToSquared(this.center)<=b*b},clampPoint:function(a,b){var c=this.center.distanceToSquared(a),d=b||new THREE.Vector3;d.copy(a);c>this.radius*this.radius&&(d.sub(this.center).normalize(),d.multiplyScalar(this.radius).add(this.center));return d},getBoundingBox:function(a){a=a||new THREE.Box3;a.set(this.center,this.center);a.expandByScalar(this.radius);
+return a},applyMatrix4:function(a){this.center.applyMatrix4(a);this.radius*=a.getMaxScaleOnAxis();return this},translate:function(a){this.center.add(a);return this},equals:function(a){return a.center.equals(this.center)&&a.radius===this.radius},clone:function(){return(new THREE.Sphere).copy(this)}};THREE.Frustum=function(a,b,c,d,e,f){this.planes=[void 0!==a?a:new THREE.Plane,void 0!==b?b:new THREE.Plane,void 0!==c?c:new THREE.Plane,void 0!==d?d:new THREE.Plane,void 0!==e?e:new THREE.Plane,void 0!==f?f:new THREE.Plane]};
+THREE.Frustum.prototype={constructor:THREE.Frustum,set:function(a,b,c,d,e,f){var h=this.planes;h[0].copy(a);h[1].copy(b);h[2].copy(c);h[3].copy(d);h[4].copy(e);h[5].copy(f);return this},copy:function(a){for(var b=this.planes,c=0;6>c;c++)b[c].copy(a.planes[c]);return this},setFromMatrix:function(a){var b=this.planes,c=a.elements,a=c[0],d=c[1],e=c[2],f=c[3],h=c[4],g=c[5],i=c[6],k=c[7],m=c[8],l=c[9],p=c[10],s=c[11],t=c[12],n=c[13],r=c[14],c=c[15];b[0].setComponents(f-a,k-h,s-m,c-t).normalize();b[1].setComponents(f+
+a,k+h,s+m,c+t).normalize();b[2].setComponents(f+d,k+g,s+l,c+n).normalize();b[3].setComponents(f-d,k-g,s-l,c-n).normalize();b[4].setComponents(f-e,k-i,s-p,c-r).normalize();b[5].setComponents(f+e,k+i,s+p,c+r).normalize();return this},intersectsObject:function(){var a=new THREE.Sphere;return function(b){var c=b.geometry;null===c.boundingSphere&&c.computeBoundingSphere();a.copy(c.boundingSphere);a.applyMatrix4(b.matrixWorld);return this.intersectsSphere(a)}}(),intersectsSphere:function(a){for(var b=this.planes,
+c=a.center,a=-a.radius,d=0;6>d;d++)if(b[d].distanceToPoint(c)<a)return!1;return!0},intersectsBox:function(){var a=new THREE.Vector3,b=new THREE.Vector3;return function(c){for(var d=this.planes,e=0;6>e;e++){var f=d[e];a.x=0<f.normal.x?c.min.x:c.max.x;b.x=0<f.normal.x?c.max.x:c.min.x;a.y=0<f.normal.y?c.min.y:c.max.y;b.y=0<f.normal.y?c.max.y:c.min.y;a.z=0<f.normal.z?c.min.z:c.max.z;b.z=0<f.normal.z?c.max.z:c.min.z;var h=f.distanceToPoint(a),f=f.distanceToPoint(b);if(0>h&&0>f)return!1}return!0}}(),containsPoint:function(a){for(var b=
+this.planes,c=0;6>c;c++)if(0>b[c].distanceToPoint(a))return!1;return!0},clone:function(){return(new THREE.Frustum).copy(this)}};THREE.Plane=function(a,b){this.normal=void 0!==a?a:new THREE.Vector3(1,0,0);this.constant=void 0!==b?b:0};
+THREE.Plane.prototype={constructor:THREE.Plane,set:function(a,b){this.normal.copy(a);this.constant=b;return this},setComponents:function(a,b,c,d){this.normal.set(a,b,c);this.constant=d;return this},setFromNormalAndCoplanarPoint:function(a,b){this.normal.copy(a);this.constant=-b.dot(this.normal);return this},setFromCoplanarPoints:function(){var a=new THREE.Vector3,b=new THREE.Vector3;return function(c,d,e){d=a.subVectors(e,d).cross(b.subVectors(c,d)).normalize();this.setFromNormalAndCoplanarPoint(d,
+c);return this}}(),copy:function(a){this.normal.copy(a.normal);this.constant=a.constant;return this},normalize:function(){var a=1/this.normal.length();this.normal.multiplyScalar(a);this.constant*=a;return this},negate:function(){this.constant*=-1;this.normal.negate();return this},distanceToPoint:function(a){return this.normal.dot(a)+this.constant},distanceToSphere:function(a){return this.distanceToPoint(a.center)-a.radius},projectPoint:function(a,b){return this.orthoPoint(a,b).sub(a).negate()},orthoPoint:function(a,
+b){var c=this.distanceToPoint(a);return(b||new THREE.Vector3).copy(this.normal).multiplyScalar(c)},isIntersectionLine:function(a){var b=this.distanceToPoint(a.start),a=this.distanceToPoint(a.end);return 0>b&&0<a||0>a&&0<b},intersectLine:function(){var a=new THREE.Vector3;return function(b,c){var d=c||new THREE.Vector3,e=b.delta(a),f=this.normal.dot(e);if(0==f){if(0==this.distanceToPoint(b.start))return d.copy(b.start)}else return f=-(b.start.dot(this.normal)+this.constant)/f,0>f||1<f?void 0:d.copy(e).multiplyScalar(f).add(b.start)}}(),
+coplanarPoint:function(a){return(a||new THREE.Vector3).copy(this.normal).multiplyScalar(-this.constant)},applyMatrix4:function(){var a=new THREE.Vector3,b=new THREE.Vector3;return function(c,d){var d=d||(new THREE.Matrix3).getNormalMatrix(c),e=a.copy(this.normal).applyMatrix3(d),f=this.coplanarPoint(b);f.applyMatrix4(c);this.setFromNormalAndCoplanarPoint(e,f);return this}}(),translate:function(a){this.constant-=a.dot(this.normal);return this},equals:function(a){return a.normal.equals(this.normal)&&
+a.constant==this.constant},clone:function(){return(new THREE.Plane).copy(this)}};THREE.Math={PI2:2*Math.PI,generateUUID:function(){var a="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split(""),b=Array(36),c=0,d;return function(){for(var e=0;36>e;e++)8==e||13==e||18==e||23==e?b[e]="-":14==e?b[e]="4":(2>=c&&(c=33554432+16777216*Math.random()|0),d=c&15,c>>=4,b[e]=a[19==e?d&3|8:d]);return b.join("")}}(),clamp:function(a,b,c){return a<b?b:a>c?c:a},clampBottom:function(a,b){return a<b?b:a},mapLinear:function(a,b,c,d,e){return d+(a-b)*(e-d)/(c-b)},smoothstep:function(a,
+b,c){if(a<=b)return 0;if(a>=c)return 1;a=(a-b)/(c-b);return a*a*(3-2*a)},smootherstep:function(a,b,c){if(a<=b)return 0;if(a>=c)return 1;a=(a-b)/(c-b);return a*a*a*(a*(6*a-15)+10)},random16:function(){return(65280*Math.random()+255*Math.random())/65535},randInt:function(a,b){return a+Math.floor(Math.random()*(b-a+1))},randFloat:function(a,b){return a+Math.random()*(b-a)},randFloatSpread:function(a){return a*(0.5-Math.random())},sign:function(a){return 0>a?-1:0<a?1:0},degToRad:function(){var a=Math.PI/
+180;return function(b){return b*a}}(),radToDeg:function(){var a=180/Math.PI;return function(b){return b*a}}()};THREE.Spline=function(a){function b(a,b,c,d,e,f,h){a=0.5*(c-a);d=0.5*(d-b);return(2*(b-c)+a+d)*h+(-3*(b-c)-2*a-d)*f+a*e+b}this.points=a;var c=[],d={x:0,y:0,z:0},e,f,h,g,i,k,m,l,p;this.initFromArray=function(a){this.points=[];for(var b=0;b<a.length;b++)this.points[b]={x:a[b][0],y:a[b][1],z:a[b][2]}};this.getPoint=function(a){e=(this.points.length-1)*a;f=Math.floor(e);h=e-f;c[0]=0===f?f:f-1;c[1]=f;c[2]=f>this.points.length-2?this.points.length-1:f+1;c[3]=f>this.points.length-3?this.points.length-1:
+f+2;k=this.points[c[0]];m=this.points[c[1]];l=this.points[c[2]];p=this.points[c[3]];g=h*h;i=h*g;d.x=b(k.x,m.x,l.x,p.x,h,g,i);d.y=b(k.y,m.y,l.y,p.y,h,g,i);d.z=b(k.z,m.z,l.z,p.z,h,g,i);return d};this.getControlPointsArray=function(){var a,b,c=this.points.length,d=[];for(a=0;a<c;a++)b=this.points[a],d[a]=[b.x,b.y,b.z];return d};this.getLength=function(a){var b,c,d,e=b=b=0,f=new THREE.Vector3,h=new THREE.Vector3,g=[],i=0;g[0]=0;a||(a=100);c=this.points.length*a;f.copy(this.points[0]);for(a=1;a<c;a++)b=
+a/c,d=this.getPoint(b),h.copy(d),i+=h.distanceTo(f),f.copy(d),b*=this.points.length-1,b=Math.floor(b),b!=e&&(g[b]=i,e=b);g[g.length]=i;return{chunks:g,total:i}};this.reparametrizeByArcLength=function(a){var b,c,d,e,f,h,g=[],i=new THREE.Vector3,k=this.getLength();g.push(i.copy(this.points[0]).clone());for(b=1;b<this.points.length;b++){c=k.chunks[b]-k.chunks[b-1];h=Math.ceil(a*c/k.total);e=(b-1)/(this.points.length-1);f=b/(this.points.length-1);for(c=1;c<h-1;c++)d=e+c*(1/h)*(f-e),d=this.getPoint(d),
+g.push(i.copy(d).clone());g.push(i.copy(this.points[b]).clone())}this.points=g}};THREE.Triangle=function(a,b,c){this.a=void 0!==a?a:new THREE.Vector3;this.b=void 0!==b?b:new THREE.Vector3;this.c=void 0!==c?c:new THREE.Vector3};THREE.Triangle.normal=function(){var a=new THREE.Vector3;return function(b,c,d,e){e=e||new THREE.Vector3;e.subVectors(d,c);a.subVectors(b,c);e.cross(a);b=e.lengthSq();return 0<b?e.multiplyScalar(1/Math.sqrt(b)):e.set(0,0,0)}}();
+THREE.Triangle.barycoordFromPoint=function(){var a=new THREE.Vector3,b=new THREE.Vector3,c=new THREE.Vector3;return function(d,e,f,h,g){a.subVectors(h,e);b.subVectors(f,e);c.subVectors(d,e);var d=a.dot(a),e=a.dot(b),f=a.dot(c),i=b.dot(b),h=b.dot(c),k=d*i-e*e,g=g||new THREE.Vector3;if(0==k)return g.set(-2,-1,-1);k=1/k;i=(i*f-e*h)*k;d=(d*h-e*f)*k;return g.set(1-i-d,d,i)}}();
+THREE.Triangle.containsPoint=function(){var a=new THREE.Vector3;return function(b,c,d,e){b=THREE.Triangle.barycoordFromPoint(b,c,d,e,a);return 0<=b.x&&0<=b.y&&1>=b.x+b.y}}();
+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(a,b,c,d){this.a.copy(a[b]);this.b.copy(a[c]);this.c.copy(a[d]);return this},copy:function(a){this.a.copy(a.a);this.b.copy(a.b);this.c.copy(a.c);return this},area:function(){var a=new THREE.Vector3,b=new THREE.Vector3;return function(){a.subVectors(this.c,this.b);b.subVectors(this.a,this.b);return 0.5*a.cross(b).length()}}(),midpoint:function(a){return(a||
+new THREE.Vector3).addVectors(this.a,this.b).add(this.c).multiplyScalar(1/3)},normal:function(a){return THREE.Triangle.normal(this.a,this.b,this.c,a)},plane:function(a){return(a||new THREE.Plane).setFromCoplanarPoints(this.a,this.b,this.c)},barycoordFromPoint:function(a,b){return THREE.Triangle.barycoordFromPoint(a,this.a,this.b,this.c,b)},containsPoint:function(a){return THREE.Triangle.containsPoint(a,this.a,this.b,this.c)},equals:function(a){return a.a.equals(this.a)&&a.b.equals(this.b)&&a.c.equals(this.c)},
+clone:function(){return(new THREE.Triangle).copy(this)}};THREE.Vertex=function(a){console.warn("THREE.Vertex has been DEPRECATED. Use THREE.Vector3 instead.");return a};THREE.UV=function(a,b){console.warn("THREE.UV has been DEPRECATED. Use THREE.Vector2 instead.");return new THREE.Vector2(a,b)};THREE.Clock=function(a){this.autoStart=void 0!==a?a:!0;this.elapsedTime=this.oldTime=this.startTime=0;this.running=!1};
+THREE.Clock.prototype={constructor:THREE.Clock,start:function(){this.oldTime=this.startTime=void 0!==self.performance&&void 0!==self.performance.now?self.performance.now():Date.now();this.running=!0},stop:function(){this.getElapsedTime();this.running=!1},getElapsedTime:function(){this.getDelta();return this.elapsedTime},getDelta:function(){var a=0;this.autoStart&&!this.running&&this.start();if(this.running){var b=void 0!==self.performance&&void 0!==self.performance.now?self.performance.now():Date.now(),
+a=0.001*(b-this.oldTime);this.oldTime=b;this.elapsedTime+=a}return a}};THREE.EventDispatcher=function(){};
+THREE.EventDispatcher.prototype={constructor:THREE.EventDispatcher,apply:function(a){a.addEventListener=THREE.EventDispatcher.prototype.addEventListener;a.hasEventListener=THREE.EventDispatcher.prototype.hasEventListener;a.removeEventListener=THREE.EventDispatcher.prototype.removeEventListener;a.dispatchEvent=THREE.EventDispatcher.prototype.dispatchEvent},addEventListener:function(a,b){void 0===this._listeners&&(this._listeners={});var c=this._listeners;void 0===c[a]&&(c[a]=[]);-1===c[a].indexOf(b)&&
+c[a].push(b)},hasEventListener:function(a,b){if(void 0===this._listeners)return!1;var c=this._listeners;return void 0!==c[a]&&-1!==c[a].indexOf(b)?!0:!1},removeEventListener:function(a,b){if(void 0!==this._listeners){var c=this._listeners,d=c[a].indexOf(b);-1!==d&&c[a].splice(d,1)}},dispatchEvent:function(){var a=[];return function(b){if(void 0!==this._listeners){var c=this._listeners[b.type];if(void 0!==c){b.target=this;for(var d=c.length,e=0;e<d;e++)a[e]=c[e];for(e=0;e<d;e++)a[e].call(this,b)}}}}()};(function(a){a.Raycaster=function(b,c,d,e){this.ray=new a.Ray(b,c);this.near=d||0;this.far=e||Infinity};var b=new a.Sphere,c=new a.Ray;new a.Plane;new a.Vector3;var d=new a.Vector3,e=new a.Matrix4,f=function(a,b){return a.distance-b.distance},h=new a.Vector3,g=new a.Vector3,i=new a.Vector3,k=function(f,m,s){if(f instanceof a.Sprite){d.getPositionFromMatrix(f.matrixWorld);var t=m.ray.distanceToPoint(d);if(t>f.scale.x)return s;s.push({distance:t,point:f.position,face:null,object:f})}else if(f instanceof
+a.LOD)d.getPositionFromMatrix(f.matrixWorld),t=m.ray.origin.distanceTo(d),k(f.getObjectForDistance(t),m,s);else if(f instanceof a.Mesh){var n=f.geometry;null===n.boundingSphere&&n.computeBoundingSphere();b.copy(n.boundingSphere);b.applyMatrix4(f.matrixWorld);if(!1===m.ray.isIntersectionSphere(b))return s;e.getInverse(f.matrixWorld);c.copy(m.ray).applyMatrix4(e);if(null!==n.boundingBox&&!1===c.isIntersectionBox(n.boundingBox))return s;if(n instanceof a.BufferGeometry){var r=f.material;if(void 0===
+r||!1===n.dynamic)return s;var q,u,w=m.precision;if(void 0!==n.attributes.index)for(var z=n.offsets,B=n.attributes.index.array,D=n.attributes.position.array,x=n.offsets.length,F=n.attributes.index.array.length/3,F=0;F<x;++F)for(var t=z[F].start,A=z[F].index,n=t,O=t+z[F].count;n<O;n+=3)t=A+B[n],q=A+B[n+1],u=A+B[n+2],h.set(D[3*t],D[3*t+1],D[3*t+2]),g.set(D[3*q],D[3*q+1],D[3*q+2]),i.set(D[3*u],D[3*u+1],D[3*u+2]),q=r.side===a.BackSide?c.intersectTriangle(i,g,h,!0):c.intersectTriangle(h,g,i,r.side!==a.DoubleSide),
+null!==q&&(q.applyMatrix4(f.matrixWorld),t=m.ray.origin.distanceTo(q),t<w||(t<m.near||t>m.far)||s.push({distance:t,point:q,face:null,faceIndex:null,object:f}));else{D=n.attributes.position.array;F=n.attributes.position.array.length;for(n=0;n<F;n+=3)t=n,q=n+1,u=n+2,h.set(D[3*t],D[3*t+1],D[3*t+2]),g.set(D[3*q],D[3*q+1],D[3*q+2]),i.set(D[3*u],D[3*u+1],D[3*u+2]),q=r.side===a.BackSide?c.intersectTriangle(i,g,h,!0):c.intersectTriangle(h,g,i,r.side!==a.DoubleSide),null!==q&&(q.applyMatrix4(f.matrixWorld),
+t=m.ray.origin.distanceTo(q),t<w||(t<m.near||t>m.far)||s.push({distance:t,point:q,face:null,faceIndex:null,object:f}))}}else if(n instanceof a.Geometry){B=f.material instanceof a.MeshFaceMaterial;D=!0===B?f.material.materials:null;w=m.precision;z=n.vertices;x=0;for(F=n.faces.length;x<F;x++)A=n.faces[x],r=!0===B?D[A.materialIndex]:f.material,void 0!==r&&(t=z[A.a],q=z[A.b],u=z[A.c],q=r.side===a.BackSide?c.intersectTriangle(u,q,t,!0):c.intersectTriangle(t,q,u,r.side!==a.DoubleSide),null!==q&&(q.applyMatrix4(f.matrixWorld),
+t=m.ray.origin.distanceTo(q),t<w||(t<m.near||t>m.far)||s.push({distance:t,point:q,face:A,faceIndex:x,object:f})))}}else if(f instanceof a.Line){w=m.linePrecision;r=w*w;n=f.geometry;null===n.boundingSphere&&n.computeBoundingSphere();b.copy(n.boundingSphere);b.applyMatrix4(f.matrixWorld);if(!1===m.ray.isIntersectionSphere(b))return s;e.getInverse(f.matrixWorld);c.copy(m.ray).applyMatrix4(e);if(n instanceof a.Geometry){z=n.vertices;w=z.length;q=new a.Vector3;u=new a.Vector3;F=f.type===a.LineStrip?1:
+2;for(n=0;n<w-1;n+=F)c.distanceSqToSegment(z[n],z[n+1],u,q)>r||(t=c.origin.distanceTo(u),t<m.near||t>m.far||s.push({distance:t,point:q.clone().applyMatrix4(f.matrixWorld),face:null,faceIndex:null,object:f}))}}},m=function(a,b,c){for(var a=a.getDescendants(),d=0,e=a.length;d<e;d++)k(a[d],b,c)};a.Raycaster.prototype.precision=1E-4;a.Raycaster.prototype.linePrecision=1;a.Raycaster.prototype.set=function(a,b){this.ray.set(a,b)};a.Raycaster.prototype.intersectObject=function(a,b){var c=[];!0===b&&m(a,
+this,c);k(a,this,c);c.sort(f);return c};a.Raycaster.prototype.intersectObjects=function(a,b){for(var c=[],d=0,e=a.length;d<e;d++)k(a[d],this,c),!0===b&&m(a[d],this,c);c.sort(f);return c}})(THREE);THREE.Object3D=function(){this.id=THREE.Object3DIdCount++;this.uuid=THREE.Math.generateUUID();this.name="";this.parent=void 0;this.children=[];this.up=new THREE.Vector3(0,1,0);this.position=new THREE.Vector3;this.rotation=new THREE.Euler;this.quaternion=new THREE.Quaternion;this.scale=new THREE.Vector3(1,1,1);this.rotation._quaternion=this.quaternion;this.quaternion._euler=this.rotation;this.renderDepth=null;this.rotationAutoUpdate=!0;this.matrix=new THREE.Matrix4;this.matrixWorld=new THREE.Matrix4;
+this.visible=this.matrixWorldNeedsUpdate=this.matrixAutoUpdate=!0;this.receiveShadow=this.castShadow=!1;this.frustumCulled=!0;this.userData={}};
+THREE.Object3D.prototype={constructor:THREE.Object3D,get eulerOrder(){console.warn("DEPRECATED: Object3D's .eulerOrder has been moved to Object3D's .rotation.order.");return this.rotation.order},set eulerOrder(a){console.warn("DEPRECATED: Object3D's .eulerOrder has been moved to Object3D's .rotation.order.");this.rotation.order=a},get useQuaternion(){console.warn("DEPRECATED: Object3D's .useQuaternion has been removed. The library now uses quaternions by default.")},set useQuaternion(a){console.warn("DEPRECATED: Object3D's .useQuaternion has been removed. The library now uses quaternions by default.")},
+applyMatrix:function(){var a=new THREE.Matrix4;return function(b){this.matrix.multiplyMatrices(b,this.matrix);this.position.getPositionFromMatrix(this.matrix);this.scale.getScaleFromMatrix(this.matrix);a.extractRotation(this.matrix);this.quaternion.setFromRotationMatrix(a)}}(),setRotationFromAxisAngle:function(a,b){this.quaternion.setFromAxisAngle(a,b)},setRotationFromEuler:function(a){this.quaternion.setFromEuler(a,!0)},setRotationFromMatrix:function(a){this.quaternion.setFromRotationMatrix(a)},
+setRotationFromQuaternion:function(a){this.quaternion.copy(a)},rotateOnAxis:function(){var a=new THREE.Quaternion;return function(b,c){a.setFromAxisAngle(b,c);this.quaternion.multiply(a);return this}}(),rotateX:function(){var a=new THREE.Vector3(1,0,0);return function(b){return this.rotateOnAxis(a,b)}}(),rotateY:function(){var a=new THREE.Vector3(0,1,0);return function(b){return this.rotateOnAxis(a,b)}}(),rotateZ:function(){var a=new THREE.Vector3(0,0,1);return function(b){return this.rotateOnAxis(a,
+b)}}(),translateOnAxis:function(){var a=new THREE.Vector3;return function(b,c){a.copy(b);a.applyQuaternion(this.quaternion);this.position.add(a.multiplyScalar(c));return this}}(),translate:function(a,b){console.warn("DEPRECATED: Object3D's .translate() has been removed. Use .translateOnAxis( axis, distance ) instead. Note args have been changed.");return this.translateOnAxis(b,a)},translateX:function(){var a=new THREE.Vector3(1,0,0);return function(b){return this.translateOnAxis(a,b)}}(),translateY:function(){var a=
+new THREE.Vector3(0,1,0);return function(b){return this.translateOnAxis(a,b)}}(),translateZ:function(){var a=new THREE.Vector3(0,0,1);return function(b){return this.translateOnAxis(a,b)}}(),localToWorld:function(a){return a.applyMatrix4(this.matrixWorld)},worldToLocal:function(){var a=new THREE.Matrix4;return function(b){return b.applyMatrix4(a.getInverse(this.matrixWorld))}}(),lookAt:function(){var a=new THREE.Matrix4;return function(b){a.lookAt(b,this.position,this.up);this.quaternion.setFromRotationMatrix(a)}}(),
+add:function(a){if(a===this)console.warn("THREE.Object3D.add: An object can't be added as a child of itself.");else if(a instanceof THREE.Object3D){void 0!==a.parent&&a.parent.remove(a);a.parent=this;a.dispatchEvent({type:"added"});this.children.push(a);for(var b=this;void 0!==b.parent;)b=b.parent;void 0!==b&&b instanceof THREE.Scene&&b.__addObject(a)}},remove:function(a){var b=this.children.indexOf(a);if(-1!==b){a.parent=void 0;a.dispatchEvent({type:"removed"});this.children.splice(b,1);for(b=this;void 0!==
+b.parent;)b=b.parent;void 0!==b&&b instanceof THREE.Scene&&b.__removeObject(a)}},traverse:function(a){a(this);for(var b=0,c=this.children.length;b<c;b++)this.children[b].traverse(a)},getObjectById:function(a,b){for(var c=0,d=this.children.length;c<d;c++){var e=this.children[c];if(e.id===a||!0===b&&(e=e.getObjectById(a,b),void 0!==e))return e}},getObjectByName:function(a,b){for(var c=0,d=this.children.length;c<d;c++){var e=this.children[c];if(e.name===a||!0===b&&(e=e.getObjectByName(a,b),void 0!==
+e))return e}},getChildByName:function(a,b){console.warn("DEPRECATED: Object3D's .getChildByName() has been renamed to .getObjectByName().");return this.getObjectByName(a,b)},getDescendants:function(a){void 0===a&&(a=[]);Array.prototype.push.apply(a,this.children);for(var b=0,c=this.children.length;b<c;b++)this.children[b].getDescendants(a);return a},updateMatrix:function(){this.matrix.compose(this.position,this.quaternion,this.scale);this.matrixWorldNeedsUpdate=!0},updateMatrixWorld:function(a){!0===
+this.matrixAutoUpdate&&this.updateMatrix();if(!0===this.matrixWorldNeedsUpdate||!0===a)void 0===this.parent?this.matrixWorld.copy(this.matrix):this.matrixWorld.multiplyMatrices(this.parent.matrixWorld,this.matrix),this.matrixWorldNeedsUpdate=!1,a=!0;for(var b=0,c=this.children.length;b<c;b++)this.children[b].updateMatrixWorld(a)},clone:function(a,b){void 0===a&&(a=new THREE.Object3D);void 0===b&&(b=!0);a.name=this.name;a.up.copy(this.up);a.position.copy(this.position);a.quaternion.copy(this.quaternion);
+a.scale.copy(this.scale);a.renderDepth=this.renderDepth;a.rotationAutoUpdate=this.rotationAutoUpdate;a.matrix.copy(this.matrix);a.matrixWorld.copy(this.matrixWorld);a.matrixAutoUpdate=this.matrixAutoUpdate;a.matrixWorldNeedsUpdate=this.matrixWorldNeedsUpdate;a.visible=this.visible;a.castShadow=this.castShadow;a.receiveShadow=this.receiveShadow;a.frustumCulled=this.frustumCulled;a.userData=JSON.parse(JSON.stringify(this.userData));if(!0===b)for(var c=0;c<this.children.length;c++)a.add(this.children[c].clone());
+return a}};THREE.EventDispatcher.prototype.apply(THREE.Object3D.prototype);THREE.Object3DIdCount=0;THREE.Projector=function(){function a(){if(i===m){var a=new THREE.RenderableVertex;k.push(a);m++;i++;return a}return k[i++]}function b(a,b){return a.z!==b.z?b.z-a.z:a.id!==b.id?a.id-b.id:0}function c(a,b){var c=0,d=1,e=a.z+a.w,f=b.z+b.w,h=-a.z+a.w,g=-b.z+b.w;if(0<=e&&0<=f&&0<=h&&0<=g)return!0;if(0>e&&0>f||0>h&&0>g)return!1;0>e?c=Math.max(c,e/(e-f)):0>f&&(d=Math.min(d,e/(e-f)));0>h?c=Math.max(c,h/(h-g)):0>g&&(d=Math.min(d,h/(h-g)));if(d<c)return!1;a.lerp(b,c);b.lerp(a,1-d);return!0}var d,e,f=[],h=
+0,g,i,k=[],m=0,l,p,s=[],t=0,n,r,q=[],u=0,w,z,B=[],D=0,x={objects:[],sprites:[],lights:[],elements:[]},F=new THREE.Vector3,A=new THREE.Vector4,O=new THREE.Box3(new THREE.Vector3(-1,-1,-1),new THREE.Vector3(1,1,1)),C=new THREE.Box3,E=Array(3),I=new THREE.Matrix4,y=new THREE.Matrix4,v,G=new THREE.Matrix4,R=new THREE.Matrix3,J=new THREE.Matrix3,ba=new THREE.Vector3,oa=new THREE.Frustum,pa=new THREE.Vector4,N=new THREE.Vector4;this.projectVector=function(a,b){b.matrixWorldInverse.getInverse(b.matrixWorld);
+y.multiplyMatrices(b.projectionMatrix,b.matrixWorldInverse);return a.applyProjection(y)};this.unprojectVector=function(a,b){b.projectionMatrixInverse.getInverse(b.projectionMatrix);y.multiplyMatrices(b.matrixWorld,b.projectionMatrixInverse);return a.applyProjection(y)};this.pickingRay=function(a,b){a.z=-1;var c=new THREE.Vector3(a.x,a.y,1);this.unprojectVector(a,b);this.unprojectVector(c,b);c.sub(a).normalize();return new THREE.Raycaster(a,c)};var M=function(a){if(e===h){var b=new THREE.RenderableObject;
+f.push(b);h++;e++;d=b}else d=f[e++];d.id=a.id;d.object=a;null!==a.renderDepth?d.z=a.renderDepth:(F.getPositionFromMatrix(a.matrixWorld),F.applyProjection(y),d.z=F.z);return d},Q=function(a){if(!1!==a.visible){a instanceof THREE.Light?x.lights.push(a):a instanceof THREE.Mesh||a instanceof THREE.Line?(!1===a.frustumCulled||!0===oa.intersectsObject(a))&&x.objects.push(M(a)):a instanceof THREE.Sprite&&x.sprites.push(M(a));for(var b=0,c=a.children.length;b<c;b++)Q(a.children[b])}};this.projectScene=function(d,
+f,h,m){var da=!1,F,M,ea,V,P,Z,U,ka,ta,ia,La,Ga;z=r=p=0;x.elements.length=0;!0===d.autoUpdate&&d.updateMatrixWorld();void 0===f.parent&&f.updateMatrixWorld();I.copy(f.matrixWorldInverse.getInverse(f.matrixWorld));y.multiplyMatrices(f.projectionMatrix,I);J.getNormalMatrix(I);oa.setFromMatrix(y);e=0;x.objects.length=0;x.sprites.length=0;x.lights.length=0;Q(d);!0===h&&x.objects.sort(b);d=0;for(h=x.objects.length;d<h;d++)if(U=x.objects[d].object,v=U.matrixWorld,i=0,U instanceof THREE.Mesh){ka=U.geometry;
+ea=ka.vertices;ta=ka.faces;ka=ka.faceVertexUvs;R.getNormalMatrix(v);La=U.material instanceof THREE.MeshFaceMaterial;Ga=!0===La?U.material:null;F=0;for(M=ea.length;F<M;F++){g=a();g.positionWorld.copy(ea[F]).applyMatrix4(v);g.positionScreen.copy(g.positionWorld).applyMatrix4(y);var fa=1/g.positionScreen.w;g.positionScreen.x*=fa;g.positionScreen.y*=fa;g.positionScreen.z*=fa;g.visible=!(-1>g.positionScreen.x||1<g.positionScreen.x||-1>g.positionScreen.y||1<g.positionScreen.y||-1>g.positionScreen.z||1<
+g.positionScreen.z)}ea=0;for(F=ta.length;ea<F;ea++)if(M=ta[ea],fa=!0===La?Ga.materials[M.materialIndex]:U.material,void 0!==fa&&(Z=fa.side,V=k[M.a],P=k[M.b],ia=k[M.c],E[0]=V.positionScreen,E[1]=P.positionScreen,E[2]=ia.positionScreen,!0===V.visible||!0===P.visible||!0===ia.visible||O.isIntersectionBox(C.setFromPoints(E))))if(da=0>(ia.positionScreen.x-V.positionScreen.x)*(P.positionScreen.y-V.positionScreen.y)-(ia.positionScreen.y-V.positionScreen.y)*(P.positionScreen.x-V.positionScreen.x),Z===THREE.DoubleSide||
+da===(Z===THREE.FrontSide)){if(p===t){var Da=new THREE.RenderableFace3;s.push(Da);t++;p++;l=Da}else l=s[p++];l.id=U.id;l.v1.copy(V);l.v2.copy(P);l.v3.copy(ia);l.normalModel.copy(M.normal);!1===da&&(Z===THREE.BackSide||Z===THREE.DoubleSide)&&l.normalModel.negate();l.normalModel.applyMatrix3(R).normalize();l.normalModelView.copy(l.normalModel).applyMatrix3(J);l.centroidModel.copy(M.centroid).applyMatrix4(v);ia=M.vertexNormals;V=0;for(P=Math.min(ia.length,3);V<P;V++)Da=l.vertexNormalsModel[V],Da.copy(ia[V]),
+!1===da&&(Z===THREE.BackSide||Z===THREE.DoubleSide)&&Da.negate(),Da.applyMatrix3(R).normalize(),l.vertexNormalsModelView[V].copy(Da).applyMatrix3(J);l.vertexNormalsLength=ia.length;da=0;for(V=Math.min(ka.length,3);da<V;da++)if(ia=ka[da][ea],void 0!==ia){P=0;for(Z=ia.length;P<Z;P++)l.uvs[da][P]=ia[P]}l.color=M.color;l.material=fa;ba.copy(l.centroidModel).applyProjection(y);l.z=ba.z;x.elements.push(l)}}else if(U instanceof THREE.Line){G.multiplyMatrices(y,v);ea=U.geometry.vertices;V=a();V.positionScreen.copy(ea[0]).applyMatrix4(G);
+ta=U.type===THREE.LinePieces?2:1;F=1;for(M=ea.length;F<M;F++)V=a(),V.positionScreen.copy(ea[F]).applyMatrix4(G),0<(F+1)%ta||(P=k[i-2],pa.copy(V.positionScreen),N.copy(P.positionScreen),!0===c(pa,N)&&(pa.multiplyScalar(1/pa.w),N.multiplyScalar(1/N.w),r===u?(ka=new THREE.RenderableLine,q.push(ka),u++,r++,n=ka):n=q[r++],n.id=U.id,n.v1.positionScreen.copy(pa),n.v2.positionScreen.copy(N),n.z=Math.max(pa.z,N.z),n.material=U.material,U.material.vertexColors===THREE.VertexColors&&(n.vertexColors[0].copy(U.geometry.colors[F]),
+n.vertexColors[1].copy(U.geometry.colors[F-1])),x.elements.push(n)))}d=0;for(h=x.sprites.length;d<h;d++)U=x.sprites[d].object,v=U.matrixWorld,U instanceof THREE.Sprite&&(A.set(v.elements[12],v.elements[13],v.elements[14],1),A.applyMatrix4(y),fa=1/A.w,A.z*=fa,-1<A.z&&1>A.z&&(z===D?(ta=new THREE.RenderableSprite,B.push(ta),D++,z++,w=ta):w=B[z++],w.id=U.id,w.x=A.x*fa,w.y=A.y*fa,w.z=A.z,w.object=U,w.rotation=U.rotation,w.scale.x=U.scale.x*Math.abs(w.x-(A.x+f.projectionMatrix.elements[0])/(A.w+f.projectionMatrix.elements[12])),
+w.scale.y=U.scale.y*Math.abs(w.y-(A.y+f.projectionMatrix.elements[5])/(A.w+f.projectionMatrix.elements[13])),w.material=U.material,x.elements.push(w)));!0===m&&x.elements.sort(b);return x}};THREE.Face3=function(a,b,c,d,e,f){this.a=a;this.b=b;this.c=c;this.normal=d instanceof THREE.Vector3?d:new THREE.Vector3;this.vertexNormals=d instanceof Array?d:[];this.color=e instanceof THREE.Color?e:new THREE.Color;this.vertexColors=e instanceof Array?e:[];this.vertexTangents=[];this.materialIndex=void 0!==f?f:0;this.centroid=new THREE.Vector3};
+THREE.Face3.prototype={constructor:THREE.Face3,clone:function(){var a=new THREE.Face3(this.a,this.b,this.c);a.normal.copy(this.normal);a.color.copy(this.color);a.centroid.copy(this.centroid);a.materialIndex=this.materialIndex;var b,c;b=0;for(c=this.vertexNormals.length;b<c;b++)a.vertexNormals[b]=this.vertexNormals[b].clone();b=0;for(c=this.vertexColors.length;b<c;b++)a.vertexColors[b]=this.vertexColors[b].clone();b=0;for(c=this.vertexTangents.length;b<c;b++)a.vertexTangents[b]=this.vertexTangents[b].clone();
+return a}};THREE.Face4=function(a,b,c,d,e,f,h){console.warn("THREE.Face4 has been removed. A THREE.Face3 will be created instead.");return new THREE.Face3(a,b,c,e,f,h)};THREE.Geometry=function(){this.id=THREE.GeometryIdCount++;this.uuid=THREE.Math.generateUUID();this.name="";this.vertices=[];this.colors=[];this.faces=[];this.faceVertexUvs=[[]];this.morphTargets=[];this.morphColors=[];this.morphNormals=[];this.skinWeights=[];this.skinIndices=[];this.lineDistances=[];this.boundingSphere=this.boundingBox=null;this.hasTangents=!1;this.dynamic=!0;this.buffersNeedUpdate=this.lineDistancesNeedUpdate=this.colorsNeedUpdate=this.tangentsNeedUpdate=this.normalsNeedUpdate=this.uvsNeedUpdate=
+this.elementsNeedUpdate=this.verticesNeedUpdate=!1};
+THREE.Geometry.prototype={constructor:THREE.Geometry,applyMatrix:function(a){for(var b=(new THREE.Matrix3).getNormalMatrix(a),c=0,d=this.vertices.length;c<d;c++)this.vertices[c].applyMatrix4(a);c=0;for(d=this.faces.length;c<d;c++){var e=this.faces[c];e.normal.applyMatrix3(b).normalize();for(var f=0,h=e.vertexNormals.length;f<h;f++)e.vertexNormals[f].applyMatrix3(b).normalize();e.centroid.applyMatrix4(a)}this.boundingBox instanceof THREE.Box3&&this.computeBoundingBox();this.boundingSphere instanceof
+THREE.Sphere&&this.computeBoundingSphere()},computeCentroids:function(){var a,b,c;a=0;for(b=this.faces.length;a<b;a++)c=this.faces[a],c.centroid.set(0,0,0),c.centroid.add(this.vertices[c.a]),c.centroid.add(this.vertices[c.b]),c.centroid.add(this.vertices[c.c]),c.centroid.divideScalar(3)},computeFaceNormals:function(){for(var a=new THREE.Vector3,b=new THREE.Vector3,c=0,d=this.faces.length;c<d;c++){var e=this.faces[c],f=this.vertices[e.a],h=this.vertices[e.b];a.subVectors(this.vertices[e.c],h);b.subVectors(f,
+h);a.cross(b);a.normalize();e.normal.copy(a)}},computeVertexNormals:function(a){var b,c,d,e;if(void 0===this.__tmpVertices){e=this.__tmpVertices=Array(this.vertices.length);b=0;for(c=this.vertices.length;b<c;b++)e[b]=new THREE.Vector3;b=0;for(c=this.faces.length;b<c;b++)d=this.faces[b],d.vertexNormals=[new THREE.Vector3,new THREE.Vector3,new THREE.Vector3]}else{e=this.__tmpVertices;b=0;for(c=this.vertices.length;b<c;b++)e[b].set(0,0,0)}if(a){var f,h,g=new THREE.Vector3,i=new THREE.Vector3;new THREE.Vector3;
+new THREE.Vector3;new THREE.Vector3;b=0;for(c=this.faces.length;b<c;b++)d=this.faces[b],a=this.vertices[d.a],f=this.vertices[d.b],h=this.vertices[d.c],g.subVectors(h,f),i.subVectors(a,f),g.cross(i),e[d.a].add(g),e[d.b].add(g),e[d.c].add(g)}else{b=0;for(c=this.faces.length;b<c;b++)d=this.faces[b],e[d.a].add(d.normal),e[d.b].add(d.normal),e[d.c].add(d.normal)}b=0;for(c=this.vertices.length;b<c;b++)e[b].normalize();b=0;for(c=this.faces.length;b<c;b++)d=this.faces[b],d.vertexNormals[0].copy(e[d.a]),d.vertexNormals[1].copy(e[d.b]),
+d.vertexNormals[2].copy(e[d.c])},computeMorphNormals:function(){var a,b,c,d,e;c=0;for(d=this.faces.length;c<d;c++){e=this.faces[c];e.__originalFaceNormal?e.__originalFaceNormal.copy(e.normal):e.__originalFaceNormal=e.normal.clone();e.__originalVertexNormals||(e.__originalVertexNormals=[]);a=0;for(b=e.vertexNormals.length;a<b;a++)e.__originalVertexNormals[a]?e.__originalVertexNormals[a].copy(e.vertexNormals[a]):e.__originalVertexNormals[a]=e.vertexNormals[a].clone()}var f=new THREE.Geometry;f.faces=
+this.faces;a=0;for(b=this.morphTargets.length;a<b;a++){if(!this.morphNormals[a]){this.morphNormals[a]={};this.morphNormals[a].faceNormals=[];this.morphNormals[a].vertexNormals=[];e=this.morphNormals[a].faceNormals;var h=this.morphNormals[a].vertexNormals,g,i;c=0;for(d=this.faces.length;c<d;c++)g=new THREE.Vector3,i={a:new THREE.Vector3,b:new THREE.Vector3,c:new THREE.Vector3},e.push(g),h.push(i)}h=this.morphNormals[a];f.vertices=this.morphTargets[a].vertices;f.computeFaceNormals();f.computeVertexNormals();
+c=0;for(d=this.faces.length;c<d;c++)e=this.faces[c],g=h.faceNormals[c],i=h.vertexNormals[c],g.copy(e.normal),i.a.copy(e.vertexNormals[0]),i.b.copy(e.vertexNormals[1]),i.c.copy(e.vertexNormals[2])}c=0;for(d=this.faces.length;c<d;c++)e=this.faces[c],e.normal=e.__originalFaceNormal,e.vertexNormals=e.__originalVertexNormals},computeTangents:function(){var a,b,c,d,e,f,h,g,i,k,m,l,p,s,t,n,r,q=[],u=[];c=new THREE.Vector3;var w=new THREE.Vector3,z=new THREE.Vector3,B=new THREE.Vector3,D=new THREE.Vector3;
+a=0;for(b=this.vertices.length;a<b;a++)q[a]=new THREE.Vector3,u[a]=new THREE.Vector3;a=0;for(b=this.faces.length;a<b;a++)e=this.faces[a],f=this.faceVertexUvs[0][a],d=e.a,r=e.b,e=e.c,h=this.vertices[d],g=this.vertices[r],i=this.vertices[e],k=f[0],m=f[1],l=f[2],f=g.x-h.x,p=i.x-h.x,s=g.y-h.y,t=i.y-h.y,g=g.z-h.z,h=i.z-h.z,i=m.x-k.x,n=l.x-k.x,m=m.y-k.y,k=l.y-k.y,l=1/(i*k-n*m),c.set((k*f-m*p)*l,(k*s-m*t)*l,(k*g-m*h)*l),w.set((i*p-n*f)*l,(i*t-n*s)*l,(i*h-n*g)*l),q[d].add(c),q[r].add(c),q[e].add(c),u[d].add(w),
+u[r].add(w),u[e].add(w);w=["a","b","c","d"];a=0;for(b=this.faces.length;a<b;a++){e=this.faces[a];for(c=0;c<Math.min(e.vertexNormals.length,3);c++)D.copy(e.vertexNormals[c]),d=e[w[c]],r=q[d],z.copy(r),z.sub(D.multiplyScalar(D.dot(r))).normalize(),B.crossVectors(e.vertexNormals[c],r),d=B.dot(u[d]),d=0>d?-1:1,e.vertexTangents[c]=new THREE.Vector4(z.x,z.y,z.z,d)}this.hasTangents=!0},computeLineDistances:function(){for(var a=0,b=this.vertices,c=0,d=b.length;c<d;c++)0<c&&(a+=b[c].distanceTo(b[c-1])),this.lineDistances[c]=
+a},computeBoundingBox:function(){null===this.boundingBox&&(this.boundingBox=new THREE.Box3);this.boundingBox.setFromPoints(this.vertices)},computeBoundingSphere:function(){null===this.boundingSphere&&(this.boundingSphere=new THREE.Sphere);this.boundingSphere.setFromPoints(this.vertices)},mergeVertices:function(){var a={},b=[],c=[],d,e=Math.pow(10,4),f,h;this.__tmpVertices=void 0;f=0;for(h=this.vertices.length;f<h;f++)d=this.vertices[f],d=Math.round(d.x*e)+"_"+Math.round(d.y*e)+"_"+Math.round(d.z*
+e),void 0===a[d]?(a[d]=f,b.push(this.vertices[f]),c[f]=b.length-1):c[f]=c[a[d]];a=[];f=0;for(h=this.faces.length;f<h;f++){e=this.faces[f];e.a=c[e.a];e.b=c[e.b];e.c=c[e.c];e=[e.a,e.b,e.c];for(d=0;3>d;d++)if(e[d]==e[(d+1)%3]){a.push(f);break}}for(f=a.length-1;0<=f;f--){e=a[f];this.faces.splice(e,1);c=0;for(h=this.faceVertexUvs.length;c<h;c++)this.faceVertexUvs[c].splice(e,1)}f=this.vertices.length-b.length;this.vertices=b;return f},clone:function(){for(var a=new THREE.Geometry,b=this.vertices,c=0,d=
+b.length;c<d;c++)a.vertices.push(b[c].clone());b=this.faces;c=0;for(d=b.length;c<d;c++)a.faces.push(b[c].clone());b=this.faceVertexUvs[0];c=0;for(d=b.length;c<d;c++){for(var e=b[c],f=[],h=0,g=e.length;h<g;h++)f.push(new THREE.Vector2(e[h].x,e[h].y));a.faceVertexUvs[0].push(f)}return a},dispose:function(){this.dispatchEvent({type:"dispose"})}};THREE.EventDispatcher.prototype.apply(THREE.Geometry.prototype);THREE.GeometryIdCount=0;THREE.BufferGeometry=function(){this.id=THREE.GeometryIdCount++;this.uuid=THREE.Math.generateUUID();this.name="";this.attributes={};this.dynamic=!0;this.offsets=[];this.boundingSphere=this.boundingBox=null;this.hasTangents=!1;this.morphTargets=[]};
+THREE.BufferGeometry.prototype={constructor:THREE.BufferGeometry,addAttribute:function(a,b,c,d){this.attributes[a]={itemSize:d,array:new b(c*d)}},applyMatrix:function(a){var b,c;this.attributes.position&&(b=this.attributes.position.array);this.attributes.normal&&(c=this.attributes.normal.array);void 0!==b&&(a.multiplyVector3Array(b),this.verticesNeedUpdate=!0);void 0!==c&&((new THREE.Matrix3).getNormalMatrix(a).multiplyVector3Array(c),this.normalizeNormals(),this.normalsNeedUpdate=!0)},computeBoundingBox:function(){null===
+this.boundingBox&&(this.boundingBox=new THREE.Box3);var a=this.attributes.position.array;if(a){var b=this.boundingBox,c,d,e;3<=a.length&&(b.min.x=b.max.x=a[0],b.min.y=b.max.y=a[1],b.min.z=b.max.z=a[2]);for(var f=3,h=a.length;f<h;f+=3)c=a[f],d=a[f+1],e=a[f+2],c<b.min.x?b.min.x=c:c>b.max.x&&(b.max.x=c),d<b.min.y?b.min.y=d:d>b.max.y&&(b.max.y=d),e<b.min.z?b.min.z=e:e>b.max.z&&(b.max.z=e)}if(void 0===a||0===a.length)this.boundingBox.min.set(0,0,0),this.boundingBox.max.set(0,0,0)},computeBoundingSphere:function(){var a=
+new THREE.Box3,b=new THREE.Vector3;return function(){null===this.boundingSphere&&(this.boundingSphere=new THREE.Sphere);var c=this.attributes.position.array;if(c){for(var d=this.boundingSphere.center,e=0,f=c.length;e<f;e+=3)b.set(c[e],c[e+1],c[e+2]),a.addPoint(b);a.center(d);for(var h=0,e=0,f=c.length;e<f;e+=3)b.set(c[e],c[e+1],c[e+2]),h=Math.max(h,d.distanceToSquared(b));this.boundingSphere.radius=Math.sqrt(h)}}}(),computeVertexNormals:function(){if(this.attributes.position){var a,b,c,d;a=this.attributes.position.array.length;
+if(void 0===this.attributes.normal)this.attributes.normal={itemSize:3,array:new Float32Array(a)};else{a=0;for(b=this.attributes.normal.array.length;a<b;a++)this.attributes.normal.array[a]=0}var e=this.attributes.position.array,f=this.attributes.normal.array,h,g,i,k,m,l,p=new THREE.Vector3,s=new THREE.Vector3,t=new THREE.Vector3,n=new THREE.Vector3,r=new THREE.Vector3;if(this.attributes.index){var q=this.attributes.index.array,u=this.offsets;c=0;for(d=u.length;c<d;++c){b=u[c].start;h=u[c].count;var w=
+u[c].index;a=b;for(b+=h;a<b;a+=3)h=w+q[a],g=w+q[a+1],i=w+q[a+2],k=e[3*h],m=e[3*h+1],l=e[3*h+2],p.set(k,m,l),k=e[3*g],m=e[3*g+1],l=e[3*g+2],s.set(k,m,l),k=e[3*i],m=e[3*i+1],l=e[3*i+2],t.set(k,m,l),n.subVectors(t,s),r.subVectors(p,s),n.cross(r),f[3*h]+=n.x,f[3*h+1]+=n.y,f[3*h+2]+=n.z,f[3*g]+=n.x,f[3*g+1]+=n.y,f[3*g+2]+=n.z,f[3*i]+=n.x,f[3*i+1]+=n.y,f[3*i+2]+=n.z}}else{a=0;for(b=e.length;a<b;a+=9)k=e[a],m=e[a+1],l=e[a+2],p.set(k,m,l),k=e[a+3],m=e[a+4],l=e[a+5],s.set(k,m,l),k=e[a+6],m=e[a+7],l=e[a+8],
+t.set(k,m,l),n.subVectors(t,s),r.subVectors(p,s),n.cross(r),f[a]=n.x,f[a+1]=n.y,f[a+2]=n.z,f[a+3]=n.x,f[a+4]=n.y,f[a+5]=n.z,f[a+6]=n.x,f[a+7]=n.y,f[a+8]=n.z}this.normalizeNormals();this.normalsNeedUpdate=!0}},normalizeNormals:function(){for(var a=this.attributes.normal.array,b,c,d,e=0,f=a.length;e<f;e+=3)b=a[e],c=a[e+1],d=a[e+2],b=1/Math.sqrt(b*b+c*c+d*d),a[e]*=b,a[e+1]*=b,a[e+2]*=b},computeTangents:function(){function a(a){oa.x=d[3*a];oa.y=d[3*a+1];oa.z=d[3*a+2];pa.copy(oa);M=g[a];J.copy(M);J.sub(oa.multiplyScalar(oa.dot(M))).normalize();
+ba.crossVectors(pa,M);Q=ba.dot(i[a]);N=0>Q?-1:1;h[4*a]=J.x;h[4*a+1]=J.y;h[4*a+2]=J.z;h[4*a+3]=N}if(void 0===this.attributes.index||void 0===this.attributes.position||void 0===this.attributes.normal||void 0===this.attributes.uv)console.warn("Missing required attributes (index, position, normal or uv) in BufferGeometry.computeTangents()");else{var b=this.attributes.index.array,c=this.attributes.position.array,d=this.attributes.normal.array,e=this.attributes.uv.array,f=c.length/3;void 0===this.attributes.tangent&&
+(this.attributes.tangent={itemSize:4,array:new Float32Array(4*f)});for(var h=this.attributes.tangent.array,g=[],i=[],k=0;k<f;k++)g[k]=new THREE.Vector3,i[k]=new THREE.Vector3;var m,l,p,s,t,n,r,q,u,w,z,B,D,x,F,f=new THREE.Vector3,k=new THREE.Vector3,A,O,C,E,I,y,v,G=this.offsets;C=0;for(E=G.length;C<E;++C){O=G[C].start;I=G[C].count;var R=G[C].index;A=O;for(O+=I;A<O;A+=3)I=R+b[A],y=R+b[A+1],v=R+b[A+2],m=c[3*I],l=c[3*I+1],p=c[3*I+2],s=c[3*y],t=c[3*y+1],n=c[3*y+2],r=c[3*v],q=c[3*v+1],u=c[3*v+2],w=e[2*
+I],z=e[2*I+1],B=e[2*y],D=e[2*y+1],x=e[2*v],F=e[2*v+1],s-=m,m=r-m,t-=l,l=q-l,n-=p,p=u-p,B-=w,w=x-w,D-=z,z=F-z,F=1/(B*z-w*D),f.set((z*s-D*m)*F,(z*t-D*l)*F,(z*n-D*p)*F),k.set((B*m-w*s)*F,(B*l-w*t)*F,(B*p-w*n)*F),g[I].add(f),g[y].add(f),g[v].add(f),i[I].add(k),i[y].add(k),i[v].add(k)}var J=new THREE.Vector3,ba=new THREE.Vector3,oa=new THREE.Vector3,pa=new THREE.Vector3,N,M,Q;C=0;for(E=G.length;C<E;++C){O=G[C].start;I=G[C].count;R=G[C].index;A=O;for(O+=I;A<O;A+=3)I=R+b[A],y=R+b[A+1],v=R+b[A+2],a(I),a(y),
+a(v)}this.tangentsNeedUpdate=this.hasTangents=!0}},clone:function(){var a=new THREE.BufferGeometry,b=[Int8Array,Uint8Array,Uint8ClampedArray,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array],c;for(c in this.attributes){for(var d=this.attributes[c],e=d.array,f={itemSize:d.itemSize,numItems:d.numItems,array:null},d=0,h=b.length;d<h;d++){var g=b[d];if(e instanceof g){f.array=new g(e);break}}a.attributes[c]=f}d=0;for(h=this.offsets.length;d<h;d++)b=this.offsets[d],a.offsets.push({start:b.start,
+index:b.index,count:b.count});return a},dispose:function(){this.dispatchEvent({type:"dispose"})}};THREE.EventDispatcher.prototype.apply(THREE.BufferGeometry.prototype);THREE.Camera=function(){THREE.Object3D.call(this);this.matrixWorldInverse=new THREE.Matrix4;this.projectionMatrix=new THREE.Matrix4;this.projectionMatrixInverse=new THREE.Matrix4};THREE.Camera.prototype=Object.create(THREE.Object3D.prototype);THREE.Camera.prototype.lookAt=function(){var a=new THREE.Matrix4;return function(b){a.lookAt(this.position,b,this.up);this.quaternion.setFromRotationMatrix(a)}}();
+THREE.Camera.prototype.clone=function(a){void 0===a&&(a=new THREE.Camera);THREE.Object3D.prototype.clone.call(this,a);a.matrixWorldInverse.copy(this.matrixWorldInverse);a.projectionMatrix.copy(this.projectionMatrix);a.projectionMatrixInverse.copy(this.projectionMatrixInverse);return a};THREE.OrthographicCamera=function(a,b,c,d,e,f){THREE.Camera.call(this);this.left=a;this.right=b;this.top=c;this.bottom=d;this.near=void 0!==e?e:0.1;this.far=void 0!==f?f:2E3;this.updateProjectionMatrix()};THREE.OrthographicCamera.prototype=Object.create(THREE.Camera.prototype);THREE.OrthographicCamera.prototype.updateProjectionMatrix=function(){this.projectionMatrix.makeOrthographic(this.left,this.right,this.top,this.bottom,this.near,this.far)};
+THREE.OrthographicCamera.prototype.clone=function(){var a=new THREE.OrthographicCamera;THREE.Camera.prototype.clone.call(this,a);a.left=this.left;a.right=this.right;a.top=this.top;a.bottom=this.bottom;a.near=this.near;a.far=this.far;return a};THREE.PerspectiveCamera=function(a,b,c,d){THREE.Camera.call(this);this.fov=void 0!==a?a:50;this.aspect=void 0!==b?b:1;this.near=void 0!==c?c:0.1;this.far=void 0!==d?d:2E3;this.updateProjectionMatrix()};THREE.PerspectiveCamera.prototype=Object.create(THREE.Camera.prototype);THREE.PerspectiveCamera.prototype.setLens=function(a,b){void 0===b&&(b=24);this.fov=2*THREE.Math.radToDeg(Math.atan(b/(2*a)));this.updateProjectionMatrix()};
+THREE.PerspectiveCamera.prototype.setViewOffset=function(a,b,c,d,e,f){this.fullWidth=a;this.fullHeight=b;this.x=c;this.y=d;this.width=e;this.height=f;this.updateProjectionMatrix()};
+THREE.PerspectiveCamera.prototype.updateProjectionMatrix=function(){if(this.fullWidth){var a=this.fullWidth/this.fullHeight,b=Math.tan(THREE.Math.degToRad(0.5*this.fov))*this.near,c=-b,d=a*c,a=Math.abs(a*b-d),c=Math.abs(b-c);this.projectionMatrix.makeFrustum(d+this.x*a/this.fullWidth,d+(this.x+this.width)*a/this.fullWidth,b-(this.y+this.height)*c/this.fullHeight,b-this.y*c/this.fullHeight,this.near,this.far)}else this.projectionMatrix.makePerspective(this.fov,this.aspect,this.near,this.far)};
+THREE.PerspectiveCamera.prototype.clone=function(){var a=new THREE.PerspectiveCamera;THREE.Camera.prototype.clone.call(this,a);a.fov=this.fov;a.aspect=this.aspect;a.near=this.near;a.far=this.far;return a};THREE.Light=function(a){THREE.Object3D.call(this);this.color=new THREE.Color(a)};THREE.Light.prototype=Object.create(THREE.Object3D.prototype);THREE.Light.prototype.clone=function(a){void 0===a&&(a=new THREE.Light);THREE.Object3D.prototype.clone.call(this,a);a.color.copy(this.color);return a};THREE.AmbientLight=function(a){THREE.Light.call(this,a)};THREE.AmbientLight.prototype=Object.create(THREE.Light.prototype);THREE.AmbientLight.prototype.clone=function(){var a=new THREE.AmbientLight;THREE.Light.prototype.clone.call(this,a);return a};THREE.AreaLight=function(a,b){THREE.Light.call(this,a);this.normal=new THREE.Vector3(0,-1,0);this.right=new THREE.Vector3(1,0,0);this.intensity=void 0!==b?b:1;this.height=this.width=1;this.constantAttenuation=1.5;this.linearAttenuation=0.5;this.quadraticAttenuation=0.1};THREE.AreaLight.prototype=Object.create(THREE.Light.prototype);THREE.DirectionalLight=function(a,b){THREE.Light.call(this,a);this.position.set(0,1,0);this.target=new THREE.Object3D;this.intensity=void 0!==b?b:1;this.onlyShadow=this.castShadow=!1;this.shadowCameraNear=50;this.shadowCameraFar=5E3;this.shadowCameraLeft=-500;this.shadowCameraTop=this.shadowCameraRight=500;this.shadowCameraBottom=-500;this.shadowCameraVisible=!1;this.shadowBias=0;this.shadowDarkness=0.5;this.shadowMapHeight=this.shadowMapWidth=512;this.shadowCascade=!1;this.shadowCascadeOffset=new THREE.Vector3(0,
+0,-1E3);this.shadowCascadeCount=2;this.shadowCascadeBias=[0,0,0];this.shadowCascadeWidth=[512,512,512];this.shadowCascadeHeight=[512,512,512];this.shadowCascadeNearZ=[-1,0.99,0.998];this.shadowCascadeFarZ=[0.99,0.998,1];this.shadowCascadeArray=[];this.shadowMatrix=this.shadowCamera=this.shadowMapSize=this.shadowMap=null};THREE.DirectionalLight.prototype=Object.create(THREE.Light.prototype);
+THREE.DirectionalLight.prototype.clone=function(){var a=new THREE.DirectionalLight;THREE.Light.prototype.clone.call(this,a);a.target=this.target.clone();a.intensity=this.intensity;a.castShadow=this.castShadow;a.onlyShadow=this.onlyShadow;return a};THREE.HemisphereLight=function(a,b,c){THREE.Light.call(this,a);this.position.set(0,100,0);this.groundColor=new THREE.Color(b);this.intensity=void 0!==c?c:1};THREE.HemisphereLight.prototype=Object.create(THREE.Light.prototype);THREE.HemisphereLight.prototype.clone=function(){var a=new THREE.HemisphereLight;THREE.Light.prototype.clone.call(this,a);a.groundColor.copy(this.groundColor);a.intensity=this.intensity;return a};THREE.PointLight=function(a,b,c){THREE.Light.call(this,a);this.intensity=void 0!==b?b:1;this.distance=void 0!==c?c:0};THREE.PointLight.prototype=Object.create(THREE.Light.prototype);THREE.PointLight.prototype.clone=function(){var a=new THREE.PointLight;THREE.Light.prototype.clone.call(this,a);a.intensity=this.intensity;a.distance=this.distance;return a};THREE.SpotLight=function(a,b,c,d,e){THREE.Light.call(this,a);this.position.set(0,1,0);this.target=new THREE.Object3D;this.intensity=void 0!==b?b:1;this.distance=void 0!==c?c:0;this.angle=void 0!==d?d:Math.PI/3;this.exponent=void 0!==e?e:10;this.onlyShadow=this.castShadow=!1;this.shadowCameraNear=50;this.shadowCameraFar=5E3;this.shadowCameraFov=50;this.shadowCameraVisible=!1;this.shadowBias=0;this.shadowDarkness=0.5;this.shadowMapHeight=this.shadowMapWidth=512;this.shadowMatrix=this.shadowCamera=this.shadowMapSize=
+this.shadowMap=null};THREE.SpotLight.prototype=Object.create(THREE.Light.prototype);THREE.SpotLight.prototype.clone=function(){var a=new THREE.SpotLight;THREE.Light.prototype.clone.call(this,a);a.target=this.target.clone();a.intensity=this.intensity;a.distance=this.distance;a.angle=this.angle;a.exponent=this.exponent;a.castShadow=this.castShadow;a.onlyShadow=this.onlyShadow;return a};THREE.Loader=function(a){this.statusDomElement=(this.showStatus=a)?THREE.Loader.prototype.addStatusElement():null;this.onLoadStart=function(){};this.onLoadProgress=function(){};this.onLoadComplete=function(){}};
+THREE.Loader.prototype={constructor:THREE.Loader,crossOrigin:"anonymous",addStatusElement:function(){var a=document.createElement("div");a.style.position="absolute";a.style.right="0px";a.style.top="0px";a.style.fontSize="0.8em";a.style.textAlign="left";a.style.background="rgba(0,0,0,0.25)";a.style.color="#fff";a.style.width="120px";a.style.padding="0.5em 0.5em 0.5em 0.5em";a.style.zIndex=1E3;a.innerHTML="Loading ...";return a},updateProgress:function(a){var b="Loaded ",b=a.total?b+((100*a.loaded/
+a.total).toFixed(0)+"%"):b+((a.loaded/1E3).toFixed(2)+" KB");this.statusDomElement.innerHTML=b},extractUrlBase:function(a){a=a.split("/");a.pop();return(1>a.length?".":a.join("/"))+"/"},initMaterials:function(a,b){for(var c=[],d=0;d<a.length;++d)c[d]=THREE.Loader.prototype.createMaterial(a[d],b);return c},needsTangents:function(a){for(var b=0,c=a.length;b<c;b++)if(a[b]instanceof THREE.ShaderMaterial)return!0;return!1},createMaterial:function(a,b){function c(a){a=Math.log(a)/Math.LN2;return Math.floor(a)==
+a}function d(a){a=Math.log(a)/Math.LN2;return Math.pow(2,Math.round(a))}function e(a,e,f,g,i,k,r){var q=/\.dds$/i.test(f),u=b+"/"+f;if(q){var w=THREE.ImageUtils.loadCompressedTexture(u);a[e]=w}else w=document.createElement("canvas"),a[e]=new THREE.Texture(w);a[e].sourceFile=f;g&&(a[e].repeat.set(g[0],g[1]),1!==g[0]&&(a[e].wrapS=THREE.RepeatWrapping),1!==g[1]&&(a[e].wrapT=THREE.RepeatWrapping));i&&a[e].offset.set(i[0],i[1]);k&&(f={repeat:THREE.RepeatWrapping,mirror:THREE.MirroredRepeatWrapping},void 0!==
+f[k[0]]&&(a[e].wrapS=f[k[0]]),void 0!==f[k[1]]&&(a[e].wrapT=f[k[1]]));r&&(a[e].anisotropy=r);if(!q){var z=a[e],a=new Image;a.onload=function(){if(!c(this.width)||!c(this.height)){var a=d(this.width),b=d(this.height);z.image.width=a;z.image.height=b;z.image.getContext("2d").drawImage(this,0,0,a,b)}else z.image=this;z.needsUpdate=!0};a.crossOrigin=h.crossOrigin;a.src=u}}function f(a){return(255*a[0]<<16)+(255*a[1]<<8)+255*a[2]}var h=this,g="MeshLambertMaterial",i={color:15658734,opacity:1,map:null,
+lightMap:null,normalMap:null,bumpMap:null,wireframe:!1};if(a.shading){var k=a.shading.toLowerCase();"phong"===k?g="MeshPhongMaterial":"basic"===k&&(g="MeshBasicMaterial")}void 0!==a.blending&&void 0!==THREE[a.blending]&&(i.blending=THREE[a.blending]);if(void 0!==a.transparent||1>a.opacity)i.transparent=a.transparent;void 0!==a.depthTest&&(i.depthTest=a.depthTest);void 0!==a.depthWrite&&(i.depthWrite=a.depthWrite);void 0!==a.visible&&(i.visible=a.visible);void 0!==a.flipSided&&(i.side=THREE.BackSide);
+void 0!==a.doubleSided&&(i.side=THREE.DoubleSide);void 0!==a.wireframe&&(i.wireframe=a.wireframe);void 0!==a.vertexColors&&("face"===a.vertexColors?i.vertexColors=THREE.FaceColors:a.vertexColors&&(i.vertexColors=THREE.VertexColors));a.colorDiffuse?i.color=f(a.colorDiffuse):a.DbgColor&&(i.color=a.DbgColor);a.colorSpecular&&(i.specular=f(a.colorSpecular));a.colorAmbient&&(i.ambient=f(a.colorAmbient));a.transparency&&(i.opacity=a.transparency);a.specularCoef&&(i.shininess=a.specularCoef);a.mapDiffuse&&
+b&&e(i,"map",a.mapDiffuse,a.mapDiffuseRepeat,a.mapDiffuseOffset,a.mapDiffuseWrap,a.mapDiffuseAnisotropy);a.mapLight&&b&&e(i,"lightMap",a.mapLight,a.mapLightRepeat,a.mapLightOffset,a.mapLightWrap,a.mapLightAnisotropy);a.mapBump&&b&&e(i,"bumpMap",a.mapBump,a.mapBumpRepeat,a.mapBumpOffset,a.mapBumpWrap,a.mapBumpAnisotropy);a.mapNormal&&b&&e(i,"normalMap",a.mapNormal,a.mapNormalRepeat,a.mapNormalOffset,a.mapNormalWrap,a.mapNormalAnisotropy);a.mapSpecular&&b&&e(i,"specularMap",a.mapSpecular,a.mapSpecularRepeat,
+a.mapSpecularOffset,a.mapSpecularWrap,a.mapSpecularAnisotropy);a.mapBumpScale&&(i.bumpScale=a.mapBumpScale);a.mapNormal?(g=THREE.ShaderLib.normalmap,k=THREE.UniformsUtils.clone(g.uniforms),k.tNormal.value=i.normalMap,a.mapNormalFactor&&k.uNormalScale.value.set(a.mapNormalFactor,a.mapNormalFactor),i.map&&(k.tDiffuse.value=i.map,k.enableDiffuse.value=!0),i.specularMap&&(k.tSpecular.value=i.specularMap,k.enableSpecular.value=!0),i.lightMap&&(k.tAO.value=i.lightMap,k.enableAO.value=!0),k.uDiffuseColor.value.setHex(i.color),
+k.uSpecularColor.value.setHex(i.specular),k.uAmbientColor.value.setHex(i.ambient),k.uShininess.value=i.shininess,void 0!==i.opacity&&(k.uOpacity.value=i.opacity),g=new THREE.ShaderMaterial({fragmentShader:g.fragmentShader,vertexShader:g.vertexShader,uniforms:k,lights:!0,fog:!0}),i.transparent&&(g.transparent=!0)):g=new THREE[g](i);void 0!==a.DbgName&&(g.name=a.DbgName);return g}};THREE.XHRLoader=function(a){this.manager=void 0!==a?a:THREE.DefaultLoadingManager};
+THREE.XHRLoader.prototype={constructor:THREE.XHRLoader,load:function(a,b,c,d){var e=this,f=new XMLHttpRequest;void 0!==b&&f.addEventListener("load",function(c){b(c.target.responseText);e.manager.itemEnd(a)},!1);void 0!==c&&f.addEventListener("progress",function(a){c(a)},!1);void 0!==d&&f.addEventListener("error",function(a){d(a)},!1);void 0!==this.crossOrigin&&(f.crossOrigin=this.crossOrigin);f.open("GET",a,!0);f.send(null);e.manager.itemStart(a)},setCrossOrigin:function(a){this.crossOrigin=a}};THREE.ImageLoader=function(a){this.manager=void 0!==a?a:THREE.DefaultLoadingManager};
+THREE.ImageLoader.prototype={constructor:THREE.ImageLoader,load:function(a,b,c,d){var e=this,f=document.createElement("img");void 0!==b&&f.addEventListener("load",function(){e.manager.itemEnd(a);b(this)},!1);void 0!==c&&f.addEventListener("progress",function(a){c(a)},!1);void 0!==d&&f.addEventListener("error",function(a){d(a)},!1);void 0!==this.crossOrigin&&(f.crossOrigin=this.crossOrigin);f.src=a;e.manager.itemStart(a);return f},setCrossOrigin:function(a){this.crossOrigin=a}};THREE.JSONLoader=function(a){THREE.Loader.call(this,a);this.withCredentials=!1};THREE.JSONLoader.prototype=Object.create(THREE.Loader.prototype);THREE.JSONLoader.prototype.load=function(a,b,c){c=c&&"string"===typeof c?c:this.extractUrlBase(a);this.onLoadStart();this.loadAjaxJSON(this,a,b,c)};
+THREE.JSONLoader.prototype.loadAjaxJSON=function(a,b,c,d,e){var f=new XMLHttpRequest,h=0;f.onreadystatechange=function(){if(f.readyState===f.DONE)if(200===f.status||0===f.status){if(f.responseText){var g=JSON.parse(f.responseText),g=a.parse(g,d);c(g.geometry,g.materials)}else console.warn("THREE.JSONLoader: ["+b+"] seems to be unreachable or file there is empty");a.onLoadComplete()}else console.error("THREE.JSONLoader: Couldn't load ["+b+"] ["+f.status+"]");else f.readyState===f.LOADING?e&&(0===h&&
+(h=f.getResponseHeader("Content-Length")),e({total:h,loaded:f.responseText.length})):f.readyState===f.HEADERS_RECEIVED&&void 0!==e&&(h=f.getResponseHeader("Content-Length"))};f.open("GET",b,!0);f.withCredentials=this.withCredentials;f.send(null)};
+THREE.JSONLoader.prototype.parse=function(a,b){var c=new THREE.Geometry,d=void 0!==a.scale?1/a.scale:1,e,f,h,g,i,k,m,l,p,s,t,n,r,q,u=a.faces;p=a.vertices;var w=a.normals,z=a.colors,B=0;if(void 0!==a.uvs){for(e=0;e<a.uvs.length;e++)a.uvs[e].length&&B++;for(e=0;e<B;e++)c.faceVertexUvs[e]=[]}g=0;for(i=p.length;g<i;)k=new THREE.Vector3,k.x=p[g++]*d,k.y=p[g++]*d,k.z=p[g++]*d,c.vertices.push(k);g=0;for(i=u.length;g<i;)if(p=u[g++],s=p&1,h=p&2,e=p&8,m=p&16,t=p&32,k=p&64,p&=128,s){s=new THREE.Face3;s.a=u[g];
+s.b=u[g+1];s.c=u[g+3];n=new THREE.Face3;n.a=u[g+1];n.b=u[g+2];n.c=u[g+3];g+=4;h&&(h=u[g++],s.materialIndex=h,n.materialIndex=h);h=c.faces.length;if(e)for(e=0;e<B;e++){r=a.uvs[e];c.faceVertexUvs[e][h]=[];c.faceVertexUvs[e][h+1]=[];for(f=0;4>f;f++)l=u[g++],q=r[2*l],l=r[2*l+1],q=new THREE.Vector2(q,l),2!==f&&c.faceVertexUvs[e][h].push(q),0!==f&&c.faceVertexUvs[e][h+1].push(q)}m&&(m=3*u[g++],s.normal.set(w[m++],w[m++],w[m]),n.normal.copy(s.normal));if(t)for(e=0;4>e;e++)m=3*u[g++],t=new THREE.Vector3(w[m++],
+w[m++],w[m]),2!==e&&s.vertexNormals.push(t),0!==e&&n.vertexNormals.push(t);k&&(k=u[g++],k=z[k],s.color.setHex(k),n.color.setHex(k));if(p)for(e=0;4>e;e++)k=u[g++],k=z[k],2!==e&&s.vertexColors.push(new THREE.Color(k)),0!==e&&n.vertexColors.push(new THREE.Color(k));c.faces.push(s);c.faces.push(n)}else{s=new THREE.Face3;s.a=u[g++];s.b=u[g++];s.c=u[g++];h&&(h=u[g++],s.materialIndex=h);h=c.faces.length;if(e)for(e=0;e<B;e++){r=a.uvs[e];c.faceVertexUvs[e][h]=[];for(f=0;3>f;f++)l=u[g++],q=r[2*l],l=r[2*l+1],
+q=new THREE.Vector2(q,l),c.faceVertexUvs[e][h].push(q)}m&&(m=3*u[g++],s.normal.set(w[m++],w[m++],w[m]));if(t)for(e=0;3>e;e++)m=3*u[g++],t=new THREE.Vector3(w[m++],w[m++],w[m]),s.vertexNormals.push(t);k&&(k=u[g++],s.color.setHex(z[k]));if(p)for(e=0;3>e;e++)k=u[g++],s.vertexColors.push(new THREE.Color(z[k]));c.faces.push(s)}if(a.skinWeights){g=0;for(i=a.skinWeights.length;g<i;g+=2)u=a.skinWeights[g],w=a.skinWeights[g+1],c.skinWeights.push(new THREE.Vector4(u,w,0,0))}if(a.skinIndices){g=0;for(i=a.skinIndices.length;g<
+i;g+=2)u=a.skinIndices[g],w=a.skinIndices[g+1],c.skinIndices.push(new THREE.Vector4(u,w,0,0))}c.bones=a.bones;c.animation=a.animation;c.animations=a.animations;if(void 0!==a.morphTargets){g=0;for(i=a.morphTargets.length;g<i;g++){c.morphTargets[g]={};c.morphTargets[g].name=a.morphTargets[g].name;c.morphTargets[g].vertices=[];z=c.morphTargets[g].vertices;B=a.morphTargets[g].vertices;u=0;for(w=B.length;u<w;u+=3)p=new THREE.Vector3,p.x=B[u]*d,p.y=B[u+1]*d,p.z=B[u+2]*d,z.push(p)}}if(void 0!==a.morphColors){g=
+0;for(i=a.morphColors.length;g<i;g++){c.morphColors[g]={};c.morphColors[g].name=a.morphColors[g].name;c.morphColors[g].colors=[];w=c.morphColors[g].colors;z=a.morphColors[g].colors;d=0;for(u=z.length;d<u;d+=3)B=new THREE.Color(16755200),B.setRGB(z[d],z[d+1],z[d+2]),w.push(B)}}c.computeCentroids();c.computeFaceNormals();c.computeBoundingSphere();if(void 0===a.materials)return{geometry:c};d=this.initMaterials(a.materials,b);this.needsTangents(d)&&c.computeTangents();return{geometry:c,materials:d}};THREE.LoadingManager=function(a,b,c){var d=this,e=0,f=0;this.onLoad=a;this.onProgress=b;this.onError=c;this.itemStart=function(){f++};this.itemEnd=function(a){e++;if(void 0!==d.onProgress)d.onProgress(a,e,f);if(e===f&&void 0!==d.onLoad)d.onLoad()}};THREE.DefaultLoadingManager=new THREE.LoadingManager;THREE.BufferGeometryLoader=function(a){this.manager=void 0!==a?a:THREE.DefaultLoadingManager};
+THREE.BufferGeometryLoader.prototype={constructor:THREE.BufferGeometryLoader,load:function(a,b){var c=this,d=new THREE.XHRLoader;d.setCrossOrigin(this.crossOrigin);d.load(a,function(a){b(c.parse(JSON.parse(a)))})},setCrossOrigin:function(a){this.crossOrigin=a},parse:function(a){var b=new THREE.BufferGeometry,c=a.attributes,d=a.offsets,a=a.boundingSphere,e;for(e in c){var f=c[e];b.attributes[e]={itemSize:f.itemSize,array:new self[f.type](f.array)}}void 0!==d&&(b.offsets=JSON.parse(JSON.stringify(d)));
+void 0!==a&&(b.boundingSphere=new THREE.Sphere((new THREE.Vector3).fromArray(void 0!==a.center?a.center:[0,0,0]),a.radius));return b}};THREE.GeometryLoader=function(a){this.manager=void 0!==a?a:THREE.DefaultLoadingManager};THREE.GeometryLoader.prototype={constructor:THREE.GeometryLoader,load:function(a,b){var c=this,d=new THREE.XHRLoader;d.setCrossOrigin(this.crossOrigin);d.load(a,function(a){b(c.parse(JSON.parse(a)))})},setCrossOrigin:function(a){this.crossOrigin=a},parse:function(){}};THREE.MaterialLoader=function(a){this.manager=void 0!==a?a:THREE.DefaultLoadingManager};
+THREE.MaterialLoader.prototype={constructor:THREE.MaterialLoader,load:function(a,b){var c=this,d=new THREE.XHRLoader;d.setCrossOrigin(this.crossOrigin);d.load(a,function(a){b(c.parse(JSON.parse(a)))})},setCrossOrigin:function(a){this.crossOrigin=a},parse:function(a){var b=new THREE[a.type];void 0!==a.color&&b.color.setHex(a.color);void 0!==a.ambient&&b.ambient.setHex(a.ambient);void 0!==a.emissive&&b.emissive.setHex(a.emissive);void 0!==a.specular&&b.specular.setHex(a.specular);void 0!==a.shininess&&
+(b.shininess=a.shininess);void 0!==a.vertexColors&&(b.vertexColors=a.vertexColors);void 0!==a.blending&&(b.blending=a.blending);void 0!==a.opacity&&(b.opacity=a.opacity);void 0!==a.transparent&&(b.transparent=a.transparent);void 0!==a.wireframe&&(b.wireframe=a.wireframe);if(void 0!==a.materials)for(var c=0,d=a.materials.length;c<d;c++)b.materials.push(this.parse(a.materials[c]));return b}};THREE.ObjectLoader=function(a){this.manager=void 0!==a?a:THREE.DefaultLoadingManager};
+THREE.ObjectLoader.prototype={constructor:THREE.ObjectLoader,load:function(a,b){var c=this,d=new THREE.XHRLoader(c.manager);d.setCrossOrigin(this.crossOrigin);d.load(a,function(a){b(c.parse(JSON.parse(a)))})},setCrossOrigin:function(a){this.crossOrigin=a},parse:function(a){var b=this.parseGeometries(a.geometries),c=this.parseMaterials(a.materials);return this.parseObject(a.object,b,c)},parseGeometries:function(a){var b={};if(void 0!==a)for(var c=new THREE.JSONLoader,d=new THREE.BufferGeometryLoader,
+e=0,f=a.length;e<f;e++){var h,g=a[e];switch(g.type){case "PlaneGeometry":h=new THREE.PlaneGeometry(g.width,g.height,g.widthSegments,g.heightSegments);break;case "CircleGeometry":h=new THREE.CircleGeometry(g.radius,g.segments);break;case "CubeGeometry":h=new THREE.CubeGeometry(g.width,g.height,g.depth,g.widthSegments,g.heightSegments,g.depthSegments);break;case "CylinderGeometry":h=new THREE.CylinderGeometry(g.radiusTop,g.radiusBottom,g.height,g.radiusSegments,g.heightSegments,g.openEnded);break;case "SphereGeometry":h=
+new THREE.SphereGeometry(g.radius,g.widthSegments,g.heightSegments,g.phiStart,g.phiLength,g.thetaStart,g.thetaLength);break;case "IcosahedronGeometry":h=new THREE.IcosahedronGeometry(g.radius,g.detail);break;case "TorusGeometry":h=new THREE.TorusGeometry(g.radius,g.tube,g.radialSegments,g.tubularSegments,g.arc);break;case "TorusKnotGeometry":h=new THREE.TorusKnotGeometry(g.radius,g.tube,g.radialSegments,g.tubularSegments,g.p,g.q,g.heightScale);break;case "BufferGeometry":h=d.parse(g.data);break;case "Geometry":h=
+c.parse(g.data).geometry}h.uuid=g.uuid;void 0!==g.name&&(h.name=g.name);b[g.uuid]=h}return b},parseMaterials:function(a){var b={};if(void 0!==a)for(var c=new THREE.MaterialLoader,d=0,e=a.length;d<e;d++){var f=a[d],h=c.parse(f);h.uuid=f.uuid;void 0!==f.name&&(h.name=f.name);b[f.uuid]=h}return b},parseObject:function(){var a=new THREE.Matrix4;return function(b,c,d){var e;switch(b.type){case "Scene":e=new THREE.Scene;break;case "PerspectiveCamera":e=new THREE.PerspectiveCamera(b.fov,b.aspect,b.near,
+b.far);break;case "OrthographicCamera":e=new THREE.OrthographicCamera(b.left,b.right,b.top,b.bottom,b.near,b.far);break;case "AmbientLight":e=new THREE.AmbientLight(b.color);break;case "DirectionalLight":e=new THREE.DirectionalLight(b.color,b.intensity);break;case "PointLight":e=new THREE.PointLight(b.color,b.intensity,b.distance);break;case "SpotLight":e=new THREE.SpotLight(b.color,b.intensity,b.distance,b.angle,b.exponent);break;case "HemisphereLight":e=new THREE.HemisphereLight(b.color,b.groundColor,
+b.intensity);break;case "Mesh":e=c[b.geometry];var f=d[b.material];void 0===e&&console.error("THREE.ObjectLoader: Undefined geometry "+b.geometry);void 0===f&&console.error("THREE.ObjectLoader: Undefined material "+b.material);e=new THREE.Mesh(e,f);break;default:e=new THREE.Object3D}e.uuid=b.uuid;void 0!==b.name&&(e.name=b.name);void 0!==b.matrix?(a.fromArray(b.matrix),a.decompose(e.position,e.quaternion,e.scale)):(void 0!==b.position&&e.position.fromArray(b.position),void 0!==b.rotation&&e.rotation.fromArray(b.rotation),
+void 0!==b.scale&&e.scale.fromArray(b.scale));void 0!==b.visible&&(e.visible=b.visible);void 0!==b.userData&&(e.userData=b.userData);if(void 0!==b.children)for(var h in b.children)e.add(this.parseObject(b.children[h],c,d));return e}}()};THREE.SceneLoader=function(){this.onLoadStart=function(){};this.onLoadProgress=function(){};this.onLoadComplete=function(){};this.callbackSync=function(){};this.callbackProgress=function(){};this.geometryHandlers={};this.hierarchyHandlers={};this.addGeometryHandler("ascii",THREE.JSONLoader)};
+THREE.SceneLoader.prototype={constructor:THREE.SceneLoader,load:function(a,b){var c=this,d=new THREE.XHRLoader(c.manager);d.setCrossOrigin(this.crossOrigin);d.load(a,function(d){c.parse(JSON.parse(d),b,a)})},setCrossOrigin:function(a){this.crossOrigin=a},addGeometryHandler:function(a,b){this.geometryHandlers[a]={loaderClass:b}},addHierarchyHandler:function(a,b){this.hierarchyHandlers[a]={loaderClass:b}},parse:function(a,b,c){function d(a,b){return"relativeToHTML"==b?a:p+"/"+a}function e(){f(A.scene,
+C.objects)}function f(a,b){var c,e,h,i,k,m,p;for(p in b){var r=A.objects[p],q=b[p];if(void 0===r){if(q.type&&q.type in l.hierarchyHandlers){if(void 0===q.loading){e={type:1,url:1,material:1,position:1,rotation:1,scale:1,visible:1,children:1,userData:1,skin:1,morph:1,mirroredLoop:1,duration:1};h={};for(var B in q)B in e||(h[B]=q[B]);t=A.materials[q.material];q.loading=!0;e=l.hierarchyHandlers[q.type].loaderObject;e.options?e.load(d(q.url,C.urlBaseType),g(p,a,t,q)):e.load(d(q.url,C.urlBaseType),g(p,
+a,t,q),h)}}else if(void 0!==q.geometry){if(s=A.geometries[q.geometry]){r=!1;t=A.materials[q.material];r=t instanceof THREE.ShaderMaterial;h=q.position;i=q.rotation;k=q.scale;c=q.matrix;m=q.quaternion;q.material||(t=new THREE.MeshFaceMaterial(A.face_materials[q.geometry]));t instanceof THREE.MeshFaceMaterial&&0===t.materials.length&&(t=new THREE.MeshFaceMaterial(A.face_materials[q.geometry]));if(t instanceof THREE.MeshFaceMaterial)for(e=0;e<t.materials.length;e++)r=r||t.materials[e]instanceof THREE.ShaderMaterial;
+r&&s.computeTangents();q.skin?r=new THREE.SkinnedMesh(s,t):q.morph?(r=new THREE.MorphAnimMesh(s,t),void 0!==q.duration&&(r.duration=q.duration),void 0!==q.time&&(r.time=q.time),void 0!==q.mirroredLoop&&(r.mirroredLoop=q.mirroredLoop),t.morphNormals&&s.computeMorphNormals()):r=new THREE.Mesh(s,t);r.name=p;c?(r.matrixAutoUpdate=!1,r.matrix.set(c[0],c[1],c[2],c[3],c[4],c[5],c[6],c[7],c[8],c[9],c[10],c[11],c[12],c[13],c[14],c[15])):(r.position.fromArray(h),m?r.quaternion.fromArray(m):r.rotation.fromArray(i),
+r.scale.fromArray(k));r.visible=q.visible;r.castShadow=q.castShadow;r.receiveShadow=q.receiveShadow;a.add(r);A.objects[p]=r}}else"DirectionalLight"===q.type||"PointLight"===q.type||"AmbientLight"===q.type?(w=void 0!==q.color?q.color:16777215,z=void 0!==q.intensity?q.intensity:1,"DirectionalLight"===q.type?(h=q.direction,u=new THREE.DirectionalLight(w,z),u.position.fromArray(h),q.target&&(O.push({object:u,targetName:q.target}),u.target=null)):"PointLight"===q.type?(h=q.position,e=q.distance,u=new THREE.PointLight(w,
+z,e),u.position.fromArray(h)):"AmbientLight"===q.type&&(u=new THREE.AmbientLight(w)),a.add(u),u.name=p,A.lights[p]=u,A.objects[p]=u):"PerspectiveCamera"===q.type||"OrthographicCamera"===q.type?(h=q.position,i=q.rotation,m=q.quaternion,"PerspectiveCamera"===q.type?n=new THREE.PerspectiveCamera(q.fov,q.aspect,q.near,q.far):"OrthographicCamera"===q.type&&(n=new THREE.OrthographicCamera(q.left,q.right,q.top,q.bottom,q.near,q.far)),n.name=p,n.position.fromArray(h),void 0!==m?n.quaternion.fromArray(m):
+void 0!==i&&n.rotation.fromArray(i),a.add(n),A.cameras[p]=n,A.objects[p]=n):(h=q.position,i=q.rotation,k=q.scale,m=q.quaternion,r=new THREE.Object3D,r.name=p,r.position.fromArray(h),m?r.quaternion.fromArray(m):r.rotation.fromArray(i),r.scale.fromArray(k),r.visible=void 0!==q.visible?q.visible:!1,a.add(r),A.objects[p]=r,A.empties[p]=r);if(r){if(void 0!==q.userData)for(var D in q.userData)r.userData[D]=q.userData[D];if(void 0!==q.groups)for(e=0;e<q.groups.length;e++)h=q.groups[e],void 0===A.groups[h]&&
+(A.groups[h]=[]),A.groups[h].push(p)}}void 0!==r&&void 0!==q.children&&f(r,q.children)}}function h(a){return function(b,c){b.name=a;A.geometries[a]=b;A.face_materials[a]=c;e();B-=1;l.onLoadComplete();k()}}function g(a,b,c,d){return function(f){var f=f.content?f.content:f.dae?f.scene:f,h=d.rotation,g=d.quaternion,i=d.scale;f.position.fromArray(d.position);g?f.quaternion.fromArray(g):f.rotation.fromArray(h);f.scale.fromArray(i);c&&f.traverse(function(a){a.material=c});var m=void 0!==d.visible?d.visible:
+!0;f.traverse(function(a){a.visible=m});b.add(f);f.name=a;A.objects[a]=f;e();B-=1;l.onLoadComplete();k()}}function i(a){return function(b,c){b.name=a;A.geometries[a]=b;A.face_materials[a]=c}}function k(){l.callbackProgress({totalModels:x,totalTextures:F,loadedModels:x-B,loadedTextures:F-D},A);l.onLoadProgress();if(0===B&&0===D){for(var a=0;a<O.length;a++){var c=O[a],d=A.objects[c.targetName];d?c.object.target=d:(c.object.target=new THREE.Object3D,A.scene.add(c.object.target));c.object.target.userData.targetInverse=
+c.object}b(A)}}function m(a,b){b(a);if(void 0!==a.children)for(var c in a.children)m(a.children[c],b)}var l=this,p=THREE.Loader.prototype.extractUrlBase(c),s,t,n,r,q,u,w,z,B,D,x,F,A,O=[],C=a,E;for(E in this.geometryHandlers)a=this.geometryHandlers[E].loaderClass,this.geometryHandlers[E].loaderObject=new a;for(E in this.hierarchyHandlers)a=this.hierarchyHandlers[E].loaderClass,this.hierarchyHandlers[E].loaderObject=new a;D=B=0;A={scene:new THREE.Scene,geometries:{},face_materials:{},materials:{},textures:{},
+objects:{},cameras:{},lights:{},fogs:{},empties:{},groups:{}};if(C.transform&&(E=C.transform.position,a=C.transform.rotation,c=C.transform.scale,E&&A.scene.position.fromArray(E),a&&A.scene.rotation.fromArray(a),c&&A.scene.scale.fromArray(c),E||a||c))A.scene.updateMatrix(),A.scene.updateMatrixWorld();E=function(a){return function(){D-=a;k();l.onLoadComplete()}};for(var I in C.fogs)a=C.fogs[I],"linear"===a.type?r=new THREE.Fog(0,a.near,a.far):"exp2"===a.type&&(r=new THREE.FogExp2(0,a.density)),a=a.color,
+r.color.setRGB(a[0],a[1],a[2]),A.fogs[I]=r;for(var y in C.geometries)r=C.geometries[y],r.type in this.geometryHandlers&&(B+=1,l.onLoadStart());for(var v in C.objects)m(C.objects[v],function(a){a.type&&a.type in l.hierarchyHandlers&&(B+=1,l.onLoadStart())});x=B;for(y in C.geometries)if(r=C.geometries[y],"cube"===r.type)s=new THREE.CubeGeometry(r.width,r.height,r.depth,r.widthSegments,r.heightSegments,r.depthSegments),s.name=y,A.geometries[y]=s;else if("plane"===r.type)s=new THREE.PlaneGeometry(r.width,
+r.height,r.widthSegments,r.heightSegments),s.name=y,A.geometries[y]=s;else if("sphere"===r.type)s=new THREE.SphereGeometry(r.radius,r.widthSegments,r.heightSegments),s.name=y,A.geometries[y]=s;else if("cylinder"===r.type)s=new THREE.CylinderGeometry(r.topRad,r.botRad,r.height,r.radSegs,r.heightSegs),s.name=y,A.geometries[y]=s;else if("torus"===r.type)s=new THREE.TorusGeometry(r.radius,r.tube,r.segmentsR,r.segmentsT),s.name=y,A.geometries[y]=s;else if("icosahedron"===r.type)s=new THREE.IcosahedronGeometry(r.radius,
+r.subdivisions),s.name=y,A.geometries[y]=s;else if(r.type in this.geometryHandlers){v={};for(q in r)"type"!==q&&"url"!==q&&(v[q]=r[q]);this.geometryHandlers[r.type].loaderObject.load(d(r.url,C.urlBaseType),h(y),v)}else"embedded"===r.type&&(v=C.embeds[r.id],v.metadata=C.metadata,v&&(v=this.geometryHandlers.ascii.loaderObject.parse(v,""),i(y)(v.geometry,v.materials)));for(var G in C.textures)if(y=C.textures[G],y.url instanceof Array){D+=y.url.length;for(q=0;q<y.url.length;q++)l.onLoadStart()}else D+=
+1,l.onLoadStart();F=D;for(G in C.textures){y=C.textures[G];void 0!==y.mapping&&void 0!==THREE[y.mapping]&&(y.mapping=new THREE[y.mapping]);if(y.url instanceof Array){v=y.url.length;r=[];for(q=0;q<v;q++)r[q]=d(y.url[q],C.urlBaseType);q=(q=/\.dds$/i.test(r[0]))?THREE.ImageUtils.loadCompressedTextureCube(r,y.mapping,E(v)):THREE.ImageUtils.loadTextureCube(r,y.mapping,E(v))}else q=/\.dds$/i.test(y.url),v=d(y.url,C.urlBaseType),r=E(1),q=q?THREE.ImageUtils.loadCompressedTexture(v,y.mapping,r):THREE.ImageUtils.loadTexture(v,
+y.mapping,r),void 0!==THREE[y.minFilter]&&(q.minFilter=THREE[y.minFilter]),void 0!==THREE[y.magFilter]&&(q.magFilter=THREE[y.magFilter]),y.anisotropy&&(q.anisotropy=y.anisotropy),y.repeat&&(q.repeat.set(y.repeat[0],y.repeat[1]),1!==y.repeat[0]&&(q.wrapS=THREE.RepeatWrapping),1!==y.repeat[1]&&(q.wrapT=THREE.RepeatWrapping)),y.offset&&q.offset.set(y.offset[0],y.offset[1]),y.wrap&&(v={repeat:THREE.RepeatWrapping,mirror:THREE.MirroredRepeatWrapping},void 0!==v[y.wrap[0]]&&(q.wrapS=v[y.wrap[0]]),void 0!==
+v[y.wrap[1]]&&(q.wrapT=v[y.wrap[1]]));A.textures[G]=q}var R,J;for(R in C.materials){G=C.materials[R];for(J in G.parameters)"envMap"===J||"map"===J||"lightMap"===J||"bumpMap"===J?G.parameters[J]=A.textures[G.parameters[J]]:"shading"===J?G.parameters[J]="flat"===G.parameters[J]?THREE.FlatShading:THREE.SmoothShading:"side"===J?G.parameters[J]="double"==G.parameters[J]?THREE.DoubleSide:"back"==G.parameters[J]?THREE.BackSide:THREE.FrontSide:"blending"===J?G.parameters[J]=G.parameters[J]in THREE?THREE[G.parameters[J]]:
+THREE.NormalBlending:"combine"===J?G.parameters[J]=G.parameters[J]in THREE?THREE[G.parameters[J]]:THREE.MultiplyOperation:"vertexColors"===J?"face"==G.parameters[J]?G.parameters[J]=THREE.FaceColors:G.parameters[J]&&(G.parameters[J]=THREE.VertexColors):"wrapRGB"===J&&(E=G.parameters[J],G.parameters[J]=new THREE.Vector3(E[0],E[1],E[2]));void 0!==G.parameters.opacity&&1>G.parameters.opacity&&(G.parameters.transparent=!0);G.parameters.normalMap?(E=THREE.ShaderLib.normalmap,y=THREE.UniformsUtils.clone(E.uniforms),
+q=G.parameters.color,v=G.parameters.specular,r=G.parameters.ambient,I=G.parameters.shininess,y.tNormal.value=A.textures[G.parameters.normalMap],G.parameters.normalScale&&y.uNormalScale.value.set(G.parameters.normalScale[0],G.parameters.normalScale[1]),G.parameters.map&&(y.tDiffuse.value=G.parameters.map,y.enableDiffuse.value=!0),G.parameters.envMap&&(y.tCube.value=G.parameters.envMap,y.enableReflection.value=!0,y.uReflectivity.value=G.parameters.reflectivity),G.parameters.lightMap&&(y.tAO.value=G.parameters.lightMap,
+y.enableAO.value=!0),G.parameters.specularMap&&(y.tSpecular.value=A.textures[G.parameters.specularMap],y.enableSpecular.value=!0),G.parameters.displacementMap&&(y.tDisplacement.value=A.textures[G.parameters.displacementMap],y.enableDisplacement.value=!0,y.uDisplacementBias.value=G.parameters.displacementBias,y.uDisplacementScale.value=G.parameters.displacementScale),y.uDiffuseColor.value.setHex(q),y.uSpecularColor.value.setHex(v),y.uAmbientColor.value.setHex(r),y.uShininess.value=I,G.parameters.opacity&&
+(y.uOpacity.value=G.parameters.opacity),t=new THREE.ShaderMaterial({fragmentShader:E.fragmentShader,vertexShader:E.vertexShader,uniforms:y,lights:!0,fog:!0})):t=new THREE[G.type](G.parameters);t.name=R;A.materials[R]=t}for(R in C.materials)if(G=C.materials[R],G.parameters.materials){J=[];for(q=0;q<G.parameters.materials.length;q++)J.push(A.materials[G.parameters.materials[q]]);A.materials[R].materials=J}e();A.cameras&&C.defaults.camera&&(A.currentCamera=A.cameras[C.defaults.camera]);A.fogs&&C.defaults.fog&&
+(A.scene.fog=A.fogs[C.defaults.fog]);l.callbackSync(A);k()}};THREE.TextureLoader=function(a){this.manager=void 0!==a?a:THREE.DefaultLoadingManager};THREE.TextureLoader.prototype={constructor:THREE.TextureLoader,load:function(a,b){var c=new THREE.ImageLoader(this.manager);c.setCrossOrigin(this.crossOrigin);c.load(a,function(a){a=new THREE.Texture(a);a.needsUpdate=!0;void 0!==b&&b(a)})},setCrossOrigin:function(a){this.crossOrigin=a}};THREE.Material=function(){this.id=THREE.MaterialIdCount++;this.uuid=THREE.Math.generateUUID();this.name="";this.side=THREE.FrontSide;this.opacity=1;this.transparent=!1;this.blending=THREE.NormalBlending;this.blendSrc=THREE.SrcAlphaFactor;this.blendDst=THREE.OneMinusSrcAlphaFactor;this.blendEquation=THREE.AddEquation;this.depthWrite=this.depthTest=!0;this.polygonOffset=!1;this.overdraw=this.alphaTest=this.polygonOffsetUnits=this.polygonOffsetFactor=0;this.needsUpdate=this.visible=!0};
+THREE.Material.prototype={constructor:THREE.Material,setValues:function(a){if(void 0!==a)for(var b in a){var c=a[b];if(void 0===c)console.warn("THREE.Material: '"+b+"' parameter is undefined.");else if(b in this){var d=this[b];d instanceof THREE.Color?d.set(c):d instanceof THREE.Vector3&&c instanceof THREE.Vector3?d.copy(c):this[b]="overdraw"==b?Number(c):c}}},clone:function(a){void 0===a&&(a=new THREE.Material);a.name=this.name;a.side=this.side;a.opacity=this.opacity;a.transparent=this.transparent;
+a.blending=this.blending;a.blendSrc=this.blendSrc;a.blendDst=this.blendDst;a.blendEquation=this.blendEquation;a.depthTest=this.depthTest;a.depthWrite=this.depthWrite;a.polygonOffset=this.polygonOffset;a.polygonOffsetFactor=this.polygonOffsetFactor;a.polygonOffsetUnits=this.polygonOffsetUnits;a.alphaTest=this.alphaTest;a.overdraw=this.overdraw;a.visible=this.visible;return a},dispose:function(){this.dispatchEvent({type:"dispose"})}};THREE.EventDispatcher.prototype.apply(THREE.Material.prototype);
+THREE.MaterialIdCount=0;THREE.LineBasicMaterial=function(a){THREE.Material.call(this);this.color=new THREE.Color(16777215);this.linewidth=1;this.linejoin=this.linecap="round";this.vertexColors=!1;this.fog=!0;this.setValues(a)};THREE.LineBasicMaterial.prototype=Object.create(THREE.Material.prototype);
+THREE.LineBasicMaterial.prototype.clone=function(){var a=new THREE.LineBasicMaterial;THREE.Material.prototype.clone.call(this,a);a.color.copy(this.color);a.linewidth=this.linewidth;a.linecap=this.linecap;a.linejoin=this.linejoin;a.vertexColors=this.vertexColors;a.fog=this.fog;return a};THREE.LineDashedMaterial=function(a){THREE.Material.call(this);this.color=new THREE.Color(16777215);this.scale=this.linewidth=1;this.dashSize=3;this.gapSize=1;this.vertexColors=!1;this.fog=!0;this.setValues(a)};THREE.LineDashedMaterial.prototype=Object.create(THREE.Material.prototype);
+THREE.LineDashedMaterial.prototype.clone=function(){var a=new THREE.LineDashedMaterial;THREE.Material.prototype.clone.call(this,a);a.color.copy(this.color);a.linewidth=this.linewidth;a.scale=this.scale;a.dashSize=this.dashSize;a.gapSize=this.gapSize;a.vertexColors=this.vertexColors;a.fog=this.fog;return a};THREE.MeshBasicMaterial=function(a){THREE.Material.call(this);this.color=new THREE.Color(16777215);this.envMap=this.specularMap=this.lightMap=this.map=null;this.combine=THREE.MultiplyOperation;this.reflectivity=1;this.refractionRatio=0.98;this.fog=!0;this.shading=THREE.SmoothShading;this.wireframe=!1;this.wireframeLinewidth=1;this.wireframeLinejoin=this.wireframeLinecap="round";this.vertexColors=THREE.NoColors;this.morphTargets=this.skinning=!1;this.setValues(a)};
+THREE.MeshBasicMaterial.prototype=Object.create(THREE.Material.prototype);
+THREE.MeshBasicMaterial.prototype.clone=function(){var a=new THREE.MeshBasicMaterial;THREE.Material.prototype.clone.call(this,a);a.color.copy(this.color);a.map=this.map;a.lightMap=this.lightMap;a.specularMap=this.specularMap;a.envMap=this.envMap;a.combine=this.combine;a.reflectivity=this.reflectivity;a.refractionRatio=this.refractionRatio;a.fog=this.fog;a.shading=this.shading;a.wireframe=this.wireframe;a.wireframeLinewidth=this.wireframeLinewidth;a.wireframeLinecap=this.wireframeLinecap;a.wireframeLinejoin=
+this.wireframeLinejoin;a.vertexColors=this.vertexColors;a.skinning=this.skinning;a.morphTargets=this.morphTargets;return a};THREE.MeshLambertMaterial=function(a){THREE.Material.call(this);this.color=new THREE.Color(16777215);this.ambient=new THREE.Color(16777215);this.emissive=new THREE.Color(0);this.wrapAround=!1;this.wrapRGB=new THREE.Vector3(1,1,1);this.envMap=this.specularMap=this.lightMap=this.map=null;this.combine=THREE.MultiplyOperation;this.reflectivity=1;this.refractionRatio=0.98;this.fog=!0;this.shading=THREE.SmoothShading;this.wireframe=!1;this.wireframeLinewidth=1;this.wireframeLinejoin=this.wireframeLinecap=
+"round";this.vertexColors=THREE.NoColors;this.morphNormals=this.morphTargets=this.skinning=!1;this.setValues(a)};THREE.MeshLambertMaterial.prototype=Object.create(THREE.Material.prototype);
+THREE.MeshLambertMaterial.prototype.clone=function(){var a=new THREE.MeshLambertMaterial;THREE.Material.prototype.clone.call(this,a);a.color.copy(this.color);a.ambient.copy(this.ambient);a.emissive.copy(this.emissive);a.wrapAround=this.wrapAround;a.wrapRGB.copy(this.wrapRGB);a.map=this.map;a.lightMap=this.lightMap;a.specularMap=this.specularMap;a.envMap=this.envMap;a.combine=this.combine;a.reflectivity=this.reflectivity;a.refractionRatio=this.refractionRatio;a.fog=this.fog;a.shading=this.shading;
+a.wireframe=this.wireframe;a.wireframeLinewidth=this.wireframeLinewidth;a.wireframeLinecap=this.wireframeLinecap;a.wireframeLinejoin=this.wireframeLinejoin;a.vertexColors=this.vertexColors;a.skinning=this.skinning;a.morphTargets=this.morphTargets;a.morphNormals=this.morphNormals;return a};THREE.MeshPhongMaterial=function(a){THREE.Material.call(this);this.color=new THREE.Color(16777215);this.ambient=new THREE.Color(16777215);this.emissive=new THREE.Color(0);this.specular=new THREE.Color(1118481);this.shininess=30;this.metal=!1;this.perPixel=!0;this.wrapAround=!1;this.wrapRGB=new THREE.Vector3(1,1,1);this.bumpMap=this.lightMap=this.map=null;this.bumpScale=1;this.normalMap=null;this.normalScale=new THREE.Vector2(1,1);this.envMap=this.specularMap=null;this.combine=THREE.MultiplyOperation;
+this.reflectivity=1;this.refractionRatio=0.98;this.fog=!0;this.shading=THREE.SmoothShading;this.wireframe=!1;this.wireframeLinewidth=1;this.wireframeLinejoin=this.wireframeLinecap="round";this.vertexColors=THREE.NoColors;this.morphNormals=this.morphTargets=this.skinning=!1;this.setValues(a)};THREE.MeshPhongMaterial.prototype=Object.create(THREE.Material.prototype);
+THREE.MeshPhongMaterial.prototype.clone=function(){var a=new THREE.MeshPhongMaterial;THREE.Material.prototype.clone.call(this,a);a.color.copy(this.color);a.ambient.copy(this.ambient);a.emissive.copy(this.emissive);a.specular.copy(this.specular);a.shininess=this.shininess;a.metal=this.metal;a.perPixel=this.perPixel;a.wrapAround=this.wrapAround;a.wrapRGB.copy(this.wrapRGB);a.map=this.map;a.lightMap=this.lightMap;a.bumpMap=this.bumpMap;a.bumpScale=this.bumpScale;a.normalMap=this.normalMap;a.normalScale.copy(this.normalScale);
+a.specularMap=this.specularMap;a.envMap=this.envMap;a.combine=this.combine;a.reflectivity=this.reflectivity;a.refractionRatio=this.refractionRatio;a.fog=this.fog;a.shading=this.shading;a.wireframe=this.wireframe;a.wireframeLinewidth=this.wireframeLinewidth;a.wireframeLinecap=this.wireframeLinecap;a.wireframeLinejoin=this.wireframeLinejoin;a.vertexColors=this.vertexColors;a.skinning=this.skinning;a.morphTargets=this.morphTargets;a.morphNormals=this.morphNormals;return a};THREE.MeshDepthMaterial=function(a){THREE.Material.call(this);this.wireframe=!1;this.wireframeLinewidth=1;this.setValues(a)};THREE.MeshDepthMaterial.prototype=Object.create(THREE.Material.prototype);THREE.MeshDepthMaterial.prototype.clone=function(){var a=new THREE.MeshDepthMaterial;THREE.Material.prototype.clone.call(this,a);a.wireframe=this.wireframe;a.wireframeLinewidth=this.wireframeLinewidth;return a};THREE.MeshNormalMaterial=function(a){THREE.Material.call(this,a);this.shading=THREE.FlatShading;this.wireframe=!1;this.wireframeLinewidth=1;this.morphTargets=!1;this.setValues(a)};THREE.MeshNormalMaterial.prototype=Object.create(THREE.Material.prototype);THREE.MeshNormalMaterial.prototype.clone=function(){var a=new THREE.MeshNormalMaterial;THREE.Material.prototype.clone.call(this,a);a.shading=this.shading;a.wireframe=this.wireframe;a.wireframeLinewidth=this.wireframeLinewidth;return a};THREE.MeshFaceMaterial=function(a){this.materials=a instanceof Array?a:[]};THREE.MeshFaceMaterial.prototype.clone=function(){for(var a=new THREE.MeshFaceMaterial,b=0;b<this.materials.length;b++)a.materials.push(this.materials[b].clone());return a};THREE.ParticleSystemMaterial=function(a){THREE.Material.call(this);this.color=new THREE.Color(16777215);this.map=null;this.size=1;this.sizeAttenuation=!0;this.vertexColors=!1;this.fog=!0;this.setValues(a)};THREE.ParticleSystemMaterial.prototype=Object.create(THREE.Material.prototype);
+THREE.ParticleSystemMaterial.prototype.clone=function(){var a=new THREE.ParticleSystemMaterial;THREE.Material.prototype.clone.call(this,a);a.color.copy(this.color);a.map=this.map;a.size=this.size;a.sizeAttenuation=this.sizeAttenuation;a.vertexColors=this.vertexColors;a.fog=this.fog;return a};THREE.ParticleBasicMaterial=THREE.ParticleSystemMaterial;THREE.ShaderMaterial=function(a){THREE.Material.call(this);this.vertexShader=this.fragmentShader="void main() {}";this.uniforms={};this.defines={};this.attributes=null;this.shading=THREE.SmoothShading;this.linewidth=1;this.wireframe=!1;this.wireframeLinewidth=1;this.lights=this.fog=!1;this.vertexColors=THREE.NoColors;this.morphNormals=this.morphTargets=this.skinning=!1;this.defaultAttributeValues={color:[1,1,1],uv:[0,0],uv2:[0,0]};this.index0AttributeName="position";this.setValues(a)};
+THREE.ShaderMaterial.prototype=Object.create(THREE.Material.prototype);
+THREE.ShaderMaterial.prototype.clone=function(){var a=new THREE.ShaderMaterial;THREE.Material.prototype.clone.call(this,a);a.fragmentShader=this.fragmentShader;a.vertexShader=this.vertexShader;a.uniforms=THREE.UniformsUtils.clone(this.uniforms);a.attributes=this.attributes;a.defines=this.defines;a.shading=this.shading;a.wireframe=this.wireframe;a.wireframeLinewidth=this.wireframeLinewidth;a.fog=this.fog;a.lights=this.lights;a.vertexColors=this.vertexColors;a.skinning=this.skinning;a.morphTargets=
+this.morphTargets;a.morphNormals=this.morphNormals;return a};THREE.SpriteMaterial=function(a){THREE.Material.call(this);this.color=new THREE.Color(16777215);this.map=new THREE.Texture;this.useScreenCoordinates=!0;this.depthTest=!this.useScreenCoordinates;this.sizeAttenuation=!this.useScreenCoordinates;this.alignment=THREE.SpriteAlignment.center.clone();this.fog=!1;this.uvOffset=new THREE.Vector2(0,0);this.uvScale=new THREE.Vector2(1,1);this.setValues(a);a=a||{};void 0===a.depthTest&&(this.depthTest=!this.useScreenCoordinates);void 0===a.sizeAttenuation&&(this.sizeAttenuation=
+!this.useScreenCoordinates)};THREE.SpriteMaterial.prototype=Object.create(THREE.Material.prototype);THREE.SpriteMaterial.prototype.clone=function(){var a=new THREE.SpriteMaterial;THREE.Material.prototype.clone.call(this,a);a.color.copy(this.color);a.map=this.map;a.useScreenCoordinates=this.useScreenCoordinates;a.sizeAttenuation=this.sizeAttenuation;a.alignment.copy(this.alignment);a.uvOffset.copy(this.uvOffset);a.uvScale.copy(this.uvScale);a.fog=this.fog;return a};THREE.SpriteAlignment={};
+THREE.SpriteAlignment.topLeft=new THREE.Vector2(0.5,-0.5);THREE.SpriteAlignment.topCenter=new THREE.Vector2(0,-0.5);THREE.SpriteAlignment.topRight=new THREE.Vector2(-0.5,-0.5);THREE.SpriteAlignment.centerLeft=new THREE.Vector2(0.5,0);THREE.SpriteAlignment.center=new THREE.Vector2(0,0);THREE.SpriteAlignment.centerRight=new THREE.Vector2(-0.5,0);THREE.SpriteAlignment.bottomLeft=new THREE.Vector2(0.5,0.5);THREE.SpriteAlignment.bottomCenter=new THREE.Vector2(0,0.5);
+THREE.SpriteAlignment.bottomRight=new THREE.Vector2(-0.5,0.5);THREE.SpriteCanvasMaterial=function(a){THREE.Material.call(this);this.color=new THREE.Color(16777215);this.program=function(){};this.setValues(a)};THREE.SpriteCanvasMaterial.prototype=Object.create(THREE.Material.prototype);THREE.SpriteCanvasMaterial.prototype.clone=function(){var a=new THREE.SpriteCanvasMaterial;THREE.Material.prototype.clone.call(this,a);a.color.copy(this.color);a.program=this.program;return a};THREE.ParticleCanvasMaterial=THREE.SpriteCanvasMaterial;THREE.Texture=function(a,b,c,d,e,f,h,g,i){this.id=THREE.TextureIdCount++;this.uuid=THREE.Math.generateUUID();this.name="";this.image=a;this.mipmaps=[];this.mapping=void 0!==b?b:new THREE.UVMapping;this.wrapS=void 0!==c?c:THREE.ClampToEdgeWrapping;this.wrapT=void 0!==d?d:THREE.ClampToEdgeWrapping;this.magFilter=void 0!==e?e:THREE.LinearFilter;this.minFilter=void 0!==f?f:THREE.LinearMipMapLinearFilter;this.anisotropy=void 0!==i?i:1;this.format=void 0!==h?h:THREE.RGBAFormat;this.type=void 0!==g?g:THREE.UnsignedByteType;
+this.offset=new THREE.Vector2(0,0);this.repeat=new THREE.Vector2(1,1);this.generateMipmaps=!0;this.premultiplyAlpha=!1;this.flipY=!0;this.unpackAlignment=4;this.needsUpdate=!1;this.onUpdate=null};
+THREE.Texture.prototype={constructor:THREE.Texture,clone:function(a){void 0===a&&(a=new THREE.Texture);a.image=this.image;a.mipmaps=this.mipmaps.slice(0);a.mapping=this.mapping;a.wrapS=this.wrapS;a.wrapT=this.wrapT;a.magFilter=this.magFilter;a.minFilter=this.minFilter;a.anisotropy=this.anisotropy;a.format=this.format;a.type=this.type;a.offset.copy(this.offset);a.repeat.copy(this.repeat);a.generateMipmaps=this.generateMipmaps;a.premultiplyAlpha=this.premultiplyAlpha;a.flipY=this.flipY;a.unpackAlignment=
+this.unpackAlignment;return a},dispose:function(){this.dispatchEvent({type:"dispose"})}};THREE.EventDispatcher.prototype.apply(THREE.Texture.prototype);THREE.TextureIdCount=0;THREE.CompressedTexture=function(a,b,c,d,e,f,h,g,i,k,m){THREE.Texture.call(this,null,f,h,g,i,k,d,e,m);this.image={width:b,height:c};this.mipmaps=a;this.generateMipmaps=!1};THREE.CompressedTexture.prototype=Object.create(THREE.Texture.prototype);THREE.CompressedTexture.prototype.clone=function(){var a=new THREE.CompressedTexture;THREE.Texture.prototype.clone.call(this,a);return a};THREE.DataTexture=function(a,b,c,d,e,f,h,g,i,k,m){THREE.Texture.call(this,null,f,h,g,i,k,d,e,m);this.image={data:a,width:b,height:c}};THREE.DataTexture.prototype=Object.create(THREE.Texture.prototype);THREE.DataTexture.prototype.clone=function(){var a=new THREE.DataTexture;THREE.Texture.prototype.clone.call(this,a);return a};THREE.ParticleSystem=function(a,b){THREE.Object3D.call(this);this.geometry=void 0!==a?a:new THREE.Geometry;this.material=void 0!==b?b:new THREE.ParticleSystemMaterial({color:16777215*Math.random()});this.frustumCulled=this.sortParticles=!1};THREE.ParticleSystem.prototype=Object.create(THREE.Object3D.prototype);
+THREE.ParticleSystem.prototype.clone=function(a){void 0===a&&(a=new THREE.ParticleSystem(this.geometry,this.material));a.sortParticles=this.sortParticles;THREE.Object3D.prototype.clone.call(this,a);return a};THREE.Line=function(a,b,c){THREE.Object3D.call(this);this.geometry=void 0!==a?a:new THREE.Geometry;this.material=void 0!==b?b:new THREE.LineBasicMaterial({color:16777215*Math.random()});this.type=void 0!==c?c:THREE.LineStrip};THREE.LineStrip=0;THREE.LinePieces=1;THREE.Line.prototype=Object.create(THREE.Object3D.prototype);THREE.Line.prototype.clone=function(a){void 0===a&&(a=new THREE.Line(this.geometry,this.material,this.type));THREE.Object3D.prototype.clone.call(this,a);return a};THREE.Mesh=function(a,b){THREE.Object3D.call(this);this.geometry=void 0!==a?a:new THREE.Geometry;this.material=void 0!==b?b:new THREE.MeshBasicMaterial({color:16777215*Math.random()});this.updateMorphTargets()};THREE.Mesh.prototype=Object.create(THREE.Object3D.prototype);
+THREE.Mesh.prototype.updateMorphTargets=function(){if(0<this.geometry.morphTargets.length){this.morphTargetBase=-1;this.morphTargetForcedOrder=[];this.morphTargetInfluences=[];this.morphTargetDictionary={};for(var a=0,b=this.geometry.morphTargets.length;a<b;a++)this.morphTargetInfluences.push(0),this.morphTargetDictionary[this.geometry.morphTargets[a].name]=a}};
+THREE.Mesh.prototype.getMorphTargetIndexByName=function(a){if(void 0!==this.morphTargetDictionary[a])return this.morphTargetDictionary[a];console.log("THREE.Mesh.getMorphTargetIndexByName: morph target "+a+" does not exist. Returning 0.");return 0};THREE.Mesh.prototype.clone=function(a){void 0===a&&(a=new THREE.Mesh(this.geometry,this.material));THREE.Object3D.prototype.clone.call(this,a);return a};THREE.Bone=function(a){THREE.Object3D.call(this);this.skin=a;this.skinMatrix=new THREE.Matrix4};THREE.Bone.prototype=Object.create(THREE.Object3D.prototype);THREE.Bone.prototype.update=function(a,b){this.matrixAutoUpdate&&(b|=this.updateMatrix());if(b||this.matrixWorldNeedsUpdate)a?this.skinMatrix.multiplyMatrices(a,this.matrix):this.skinMatrix.copy(this.matrix),this.matrixWorldNeedsUpdate=!1,b=!0;var c,d=this.children.length;for(c=0;c<d;c++)this.children[c].update(this.skinMatrix,b)};THREE.SkinnedMesh=function(a,b,c){THREE.Mesh.call(this,a,b);this.useVertexTexture=void 0!==c?c:!0;this.identityMatrix=new THREE.Matrix4;this.bones=[];this.boneMatrices=[];var d,e,f;if(this.geometry&&void 0!==this.geometry.bones){for(a=0;a<this.geometry.bones.length;a++)c=this.geometry.bones[a],d=c.pos,e=c.rotq,f=c.scl,b=this.addBone(),b.name=c.name,b.position.set(d[0],d[1],d[2]),b.quaternion.set(e[0],e[1],e[2],e[3]),void 0!==f?b.scale.set(f[0],f[1],f[2]):b.scale.set(1,1,1);for(a=0;a<this.bones.length;a++)c=
+this.geometry.bones[a],b=this.bones[a],-1===c.parent?this.add(b):this.bones[c.parent].add(b);a=this.bones.length;this.useVertexTexture?(this.boneTextureHeight=this.boneTextureWidth=a=256<a?64:64<a?32:16<a?16:8,this.boneMatrices=new Float32Array(4*this.boneTextureWidth*this.boneTextureHeight),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=!1,this.boneTexture.flipY=!1):this.boneMatrices=new Float32Array(16*a);this.pose()}};THREE.SkinnedMesh.prototype=Object.create(THREE.Mesh.prototype);THREE.SkinnedMesh.prototype.addBone=function(a){void 0===a&&(a=new THREE.Bone(this));this.bones.push(a);return a};
+THREE.SkinnedMesh.prototype.updateMatrixWorld=function(){var a=new THREE.Matrix4;return function(b){this.matrixAutoUpdate&&this.updateMatrix();if(this.matrixWorldNeedsUpdate||b)this.parent?this.matrixWorld.multiplyMatrices(this.parent.matrixWorld,this.matrix):this.matrixWorld.copy(this.matrix),this.matrixWorldNeedsUpdate=!1;for(var b=0,c=this.children.length;b<c;b++){var d=this.children[b];d instanceof THREE.Bone?d.update(this.identityMatrix,!1):d.updateMatrixWorld(!0)}if(void 0==this.boneInverses){this.boneInverses=
+[];b=0;for(c=this.bones.length;b<c;b++)d=new THREE.Matrix4,d.getInverse(this.bones[b].skinMatrix),this.boneInverses.push(d)}b=0;for(c=this.bones.length;b<c;b++)a.multiplyMatrices(this.bones[b].skinMatrix,this.boneInverses[b]),a.flattenToArrayOffset(this.boneMatrices,16*b);this.useVertexTexture&&(this.boneTexture.needsUpdate=!0)}}();THREE.SkinnedMesh.prototype.pose=function(){this.updateMatrixWorld(!0);this.normalizeSkinWeights()};
+THREE.SkinnedMesh.prototype.normalizeSkinWeights=function(){if(this.geometry instanceof THREE.Geometry)for(var a=0;a<this.geometry.skinIndices.length;a++){var b=this.geometry.skinWeights[a],c=1/b.lengthManhattan();Infinity!==c?b.multiplyScalar(c):b.set(1)}};THREE.SkinnedMesh.prototype.clone=function(a){void 0===a&&(a=new THREE.SkinnedMesh(this.geometry,this.material,this.useVertexTexture));THREE.Mesh.prototype.clone.call(this,a);return a};THREE.MorphAnimMesh=function(a,b){THREE.Mesh.call(this,a,b);this.duration=1E3;this.mirroredLoop=!1;this.currentKeyframe=this.lastKeyframe=this.time=0;this.direction=1;this.directionBackwards=!1;this.setFrameRange(0,this.geometry.morphTargets.length-1)};THREE.MorphAnimMesh.prototype=Object.create(THREE.Mesh.prototype);THREE.MorphAnimMesh.prototype.setFrameRange=function(a,b){this.startKeyframe=a;this.endKeyframe=b;this.length=this.endKeyframe-this.startKeyframe+1};
+THREE.MorphAnimMesh.prototype.setDirectionForward=function(){this.direction=1;this.directionBackwards=!1};THREE.MorphAnimMesh.prototype.setDirectionBackward=function(){this.direction=-1;this.directionBackwards=!0};
+THREE.MorphAnimMesh.prototype.parseAnimations=function(){var a=this.geometry;a.animations||(a.animations={});for(var b,c=a.animations,d=/([a-z]+)(\d+)/,e=0,f=a.morphTargets.length;e<f;e++){var h=a.morphTargets[e].name.match(d);if(h&&1<h.length){h=h[1];c[h]||(c[h]={start:Infinity,end:-Infinity});var g=c[h];e<g.start&&(g.start=e);e>g.end&&(g.end=e);b||(b=h)}}a.firstAnimation=b};
+THREE.MorphAnimMesh.prototype.setAnimationLabel=function(a,b,c){this.geometry.animations||(this.geometry.animations={});this.geometry.animations[a]={start:b,end:c}};THREE.MorphAnimMesh.prototype.playAnimation=function(a,b){var c=this.geometry.animations[a];c?(this.setFrameRange(c.start,c.end),this.duration=1E3*((c.end-c.start)/b),this.time=0):console.warn("animation["+a+"] undefined")};
+THREE.MorphAnimMesh.prototype.updateAnimation=function(a){var b=this.duration/this.length;this.time+=this.direction*a;if(this.mirroredLoop){if(this.time>this.duration||0>this.time)this.direction*=-1,this.time>this.duration&&(this.time=this.duration,this.directionBackwards=!0),0>this.time&&(this.time=0,this.directionBackwards=!1)}else this.time%=this.duration,0>this.time&&(this.time+=this.duration);a=this.startKeyframe+THREE.Math.clamp(Math.floor(this.time/b),0,this.length-1);a!==this.currentKeyframe&&
+(this.morphTargetInfluences[this.lastKeyframe]=0,this.morphTargetInfluences[this.currentKeyframe]=1,this.morphTargetInfluences[a]=0,this.lastKeyframe=this.currentKeyframe,this.currentKeyframe=a);b=this.time%b/b;this.directionBackwards&&(b=1-b);this.morphTargetInfluences[this.currentKeyframe]=b;this.morphTargetInfluences[this.lastKeyframe]=1-b};
+THREE.MorphAnimMesh.prototype.clone=function(a){void 0===a&&(a=new THREE.MorphAnimMesh(this.geometry,this.material));a.duration=this.duration;a.mirroredLoop=this.mirroredLoop;a.time=this.time;a.lastKeyframe=this.lastKeyframe;a.currentKeyframe=this.currentKeyframe;a.direction=this.direction;a.directionBackwards=this.directionBackwards;THREE.Mesh.prototype.clone.call(this,a);return a};THREE.LOD=function(){THREE.Object3D.call(this);this.objects=[]};THREE.LOD.prototype=Object.create(THREE.Object3D.prototype);THREE.LOD.prototype.addLevel=function(a,b){void 0===b&&(b=0);for(var b=Math.abs(b),c=0;c<this.objects.length&&!(b<this.objects[c].distance);c++);this.objects.splice(c,0,{distance:b,object:a});this.add(a)};THREE.LOD.prototype.getObjectForDistance=function(a){for(var b=1,c=this.objects.length;b<c&&!(a<this.objects[b].distance);b++);return this.objects[b-1].object};
+THREE.LOD.prototype.update=function(){var a=new THREE.Vector3,b=new THREE.Vector3;return function(c){if(1<this.objects.length){a.getPositionFromMatrix(c.matrixWorld);b.getPositionFromMatrix(this.matrixWorld);c=a.distanceTo(b);this.objects[0].object.visible=!0;for(var d=1,e=this.objects.length;d<e;d++)if(c>=this.objects[d].distance)this.objects[d-1].object.visible=!1,this.objects[d].object.visible=!0;else break;for(;d<e;d++)this.objects[d].object.visible=!1}}}();THREE.LOD.prototype.clone=function(){};THREE.Sprite=function(a){THREE.Object3D.call(this);this.material=void 0!==a?a:new THREE.SpriteMaterial;this.rotation=0};THREE.Sprite.prototype=Object.create(THREE.Object3D.prototype);THREE.Sprite.prototype.updateMatrix=function(){this.matrix.compose(this.position,this.quaternion,this.scale);this.matrixWorldNeedsUpdate=!0};THREE.Sprite.prototype.clone=function(a){void 0===a&&(a=new THREE.Sprite(this.material));THREE.Object3D.prototype.clone.call(this,a);return a};THREE.Particle=THREE.Sprite;THREE.Scene=function(){THREE.Object3D.call(this);this.overrideMaterial=this.fog=null;this.autoUpdate=!0;this.matrixAutoUpdate=!1;this.__lights=[];this.__objectsAdded=[];this.__objectsRemoved=[]};THREE.Scene.prototype=Object.create(THREE.Object3D.prototype);
+THREE.Scene.prototype.__addObject=function(a){if(a instanceof THREE.Light)-1===this.__lights.indexOf(a)&&this.__lights.push(a),a.target&&void 0===a.target.parent&&this.add(a.target);else if(!(a instanceof THREE.Camera||a instanceof THREE.Bone)){this.__objectsAdded.push(a);var b=this.__objectsRemoved.indexOf(a);-1!==b&&this.__objectsRemoved.splice(b,1)}for(b=0;b<a.children.length;b++)this.__addObject(a.children[b])};
+THREE.Scene.prototype.__removeObject=function(a){if(a instanceof THREE.Light){var b=this.__lights.indexOf(a);-1!==b&&this.__lights.splice(b,1);if(a.shadowCascadeArray)for(b=0;b<a.shadowCascadeArray.length;b++)this.__removeObject(a.shadowCascadeArray[b])}else a instanceof THREE.Camera||(this.__objectsRemoved.push(a),b=this.__objectsAdded.indexOf(a),-1!==b&&this.__objectsAdded.splice(b,1));for(b=0;b<a.children.length;b++)this.__removeObject(a.children[b])};
+THREE.Scene.prototype.clone=function(a){void 0===a&&(a=new THREE.Scene);THREE.Object3D.prototype.clone.call(this,a);null!==this.fog&&(a.fog=this.fog.clone());null!==this.overrideMaterial&&(a.overrideMaterial=this.overrideMaterial.clone());a.autoUpdate=this.autoUpdate;a.matrixAutoUpdate=this.matrixAutoUpdate;return a};THREE.Fog=function(a,b,c){this.name="";this.color=new THREE.Color(a);this.near=void 0!==b?b:1;this.far=void 0!==c?c:1E3};THREE.Fog.prototype.clone=function(){return new THREE.Fog(this.color.getHex(),this.near,this.far)};THREE.FogExp2=function(a,b){this.name="";this.color=new THREE.Color(a);this.density=void 0!==b?b:2.5E-4};THREE.FogExp2.prototype.clone=function(){return new THREE.FogExp2(this.color.getHex(),this.density)};THREE.CanvasRenderer=function(a){function b(a,b,c){for(var d=0,e=z.length;d<e;d++){var f=z[d];La.copy(f.color);if(f instanceof THREE.DirectionalLight){var h=ua.getPositionFromMatrix(f.matrixWorld).normalize(),g=b.dot(h);0>=g||(g*=f.intensity,c.add(La.multiplyScalar(g)))}else f instanceof THREE.PointLight&&(h=ua.getPositionFromMatrix(f.matrixWorld),g=b.dot(ua.subVectors(h,a).normalize()),0>=g||(g*=0==f.distance?1:1-Math.min(a.distanceTo(h)/f.distance,1),0!=g&&(g*=f.intensity,c.add(La.multiplyScalar(g)))))}}
+function c(a,b,c,d){m(b);l(c);p(d);s(a.getStyle());C.stroke();ra.expandByScalar(2*b)}function d(a){t(a.getStyle());C.fill()}function e(a,b,c,e,f,h,g,j,i,k,m,l,p){if(!(p instanceof THREE.DataTexture||void 0===p.image||0==p.image.width)){if(!0===p.needsUpdate){var n=p.wrapS==THREE.RepeatWrapping,r=p.wrapT==THREE.RepeatWrapping;Ga[p.id]=C.createPattern(p.image,!0===n&&!0===r?"repeat":!0===n&&!1===r?"repeat-x":!1===n&&!0===r?"repeat-y":"no-repeat");p.needsUpdate=!1}void 0===Ga[p.id]?t("rgba(0,0,0,1)"):
+t(Ga[p.id]);var n=p.offset.x/p.repeat.x,r=p.offset.y/p.repeat.y,s=p.image.width*p.repeat.x,q=p.image.height*p.repeat.y,g=(g+n)*s,j=(1-j+r)*q,c=c-a,e=e-b,f=f-a,h=h-b,i=(i+n)*s-g,k=(1-k+r)*q-j,m=(m+n)*s-g,l=(1-l+r)*q-j,n=i*l-m*k;0===n?(void 0===fa[p.id]&&(b=document.createElement("canvas"),b.width=p.image.width,b.height=p.image.height,b=b.getContext("2d"),b.drawImage(p.image,0,0),fa[p.id]=b.getImageData(0,0,p.image.width,p.image.height).data),b=fa[p.id],g=4*(Math.floor(g)+Math.floor(j)*p.image.width),
+V.setRGB(b[g]/255,b[g+1]/255,b[g+2]/255),d(V)):(n=1/n,p=(l*c-k*f)*n,k=(l*e-k*h)*n,c=(i*f-m*c)*n,e=(i*h-m*e)*n,a=a-p*g-c*j,g=b-k*g-e*j,C.save(),C.transform(p,k,c,e,a,g),C.fill(),C.restore())}}function f(a,b,c,d,e,f,h,g,j,i,k,m,l){var p,n;p=l.width-1;n=l.height-1;h*=p;g*=n;c-=a;d-=b;e-=a;f-=b;j=j*p-h;i=i*n-g;k=k*p-h;m=m*n-g;n=1/(j*m-k*i);p=(m*c-i*e)*n;i=(m*d-i*f)*n;c=(j*e-k*c)*n;d=(j*f-k*d)*n;a=a-p*h-c*g;b=b-i*h-d*g;C.save();C.transform(p,i,c,d,a,b);C.clip();C.drawImage(l,0,0);C.restore()}function h(a,
+b,c,d){va[0]=255*a.r|0;va[1]=255*a.g|0;va[2]=255*a.b|0;va[4]=255*b.r|0;va[5]=255*b.g|0;va[6]=255*b.b|0;va[8]=255*c.r|0;va[9]=255*c.g|0;va[10]=255*c.b|0;va[12]=255*d.r|0;va[13]=255*d.g|0;va[14]=255*d.b|0;j.putImageData(Oa,0,0);Ea.drawImage(Pa,0,0);return wa}function g(a,b,c){var d=b.x-a.x,e=b.y-a.y,f=d*d+e*e;0!==f&&(c/=Math.sqrt(f),d*=c,e*=c,b.x+=d,b.y+=e,a.x-=d,a.y-=e)}function i(a){y!==a&&(y=C.globalAlpha=a)}function k(a){v!==a&&(a===THREE.NormalBlending?C.globalCompositeOperation="source-over":
+a===THREE.AdditiveBlending?C.globalCompositeOperation="lighter":a===THREE.SubtractiveBlending&&(C.globalCompositeOperation="darker"),v=a)}function m(a){J!==a&&(J=C.lineWidth=a)}function l(a){ba!==a&&(ba=C.lineCap=a)}function p(a){oa!==a&&(oa=C.lineJoin=a)}function s(a){G!==a&&(G=C.strokeStyle=a)}function t(a){R!==a&&(R=C.fillStyle=a)}function n(a,b){if(pa!==a||N!==b)C.setLineDash([a,b]),pa=a,N=b}console.log("THREE.CanvasRenderer",THREE.REVISION);var r=THREE.Math.smoothstep,a=a||{},q=this,u,w,z,B=
+new THREE.Projector,D=void 0!==a.canvas?a.canvas:document.createElement("canvas"),x=D.width,F=D.height,A=Math.floor(x/2),O=Math.floor(F/2),C=D.getContext("2d"),E=new THREE.Color(0),I=0,y=1,v=0,G=null,R=null,J=null,ba=null,oa=null,pa=null,N=0,M,Q,K,ca;new THREE.RenderableVertex;new THREE.RenderableVertex;var Fa,Ba,da,Aa,$,ea,V=new THREE.Color,P=new THREE.Color,Z=new THREE.Color,U=new THREE.Color,ka=new THREE.Color,ta=new THREE.Color,ia=new THREE.Color,La=new THREE.Color,Ga={},fa={},Da,Ua,Qa,xa,bb,
+cb,Ma,fb,sb,pb,Ha=new THREE.Box2,la=new THREE.Box2,ra=new THREE.Box2,gb=new THREE.Color,sa=new THREE.Color,ga=new THREE.Color,ua=new THREE.Vector3,Pa,j,Oa,va,wa,Ea,Ra=16;Pa=document.createElement("canvas");Pa.width=Pa.height=2;j=Pa.getContext("2d");j.fillStyle="rgba(0,0,0,1)";j.fillRect(0,0,2,2);Oa=j.getImageData(0,0,2,2);va=Oa.data;wa=document.createElement("canvas");wa.width=wa.height=Ra;Ea=wa.getContext("2d");Ea.translate(-Ra/2,-Ra/2);Ea.scale(Ra,Ra);Ra--;void 0===C.setLineDash&&(C.setLineDash=
+void 0!==C.mozDash?function(a){C.mozDash=null!==a[0]?a:null}:function(){});this.domElement=D;this.devicePixelRatio=void 0!==a.devicePixelRatio?a.devicePixelRatio:void 0!==self.devicePixelRatio?self.devicePixelRatio:1;this.sortElements=this.sortObjects=this.autoClear=!0;this.info={render:{vertices:0,faces:0}};this.supportsVertexTextures=function(){};this.setFaceCulling=function(){};this.setSize=function(a,b,c){x=a*this.devicePixelRatio;F=b*this.devicePixelRatio;A=Math.floor(x/2);O=Math.floor(F/2);
+D.width=x;D.height=F;1!==this.devicePixelRatio&&!1!==c&&(D.style.width=a+"px",D.style.height=b+"px");Ha.set(new THREE.Vector2(-A,-O),new THREE.Vector2(A,O));la.set(new THREE.Vector2(-A,-O),new THREE.Vector2(A,O));y=1;v=0;oa=ba=J=R=G=null};this.setClearColor=function(a,b){E.set(a);I=void 0!==b?b:1;la.set(new THREE.Vector2(-A,-O),new THREE.Vector2(A,O))};this.setClearColorHex=function(a,b){console.warn("DEPRECATED: .setClearColorHex() is being removed. Use .setClearColor() instead.");this.setClearColor(a,
+b)};this.getMaxAnisotropy=function(){return 0};this.clear=function(){C.setTransform(1,0,0,-1,A,O);!1===la.empty()&&(la.intersect(Ha),la.expandByScalar(2),1>I&&C.clearRect(la.min.x|0,la.min.y|0,la.max.x-la.min.x|0,la.max.y-la.min.y|0),0<I&&(k(THREE.NormalBlending),i(1),t("rgba("+Math.floor(255*E.r)+","+Math.floor(255*E.g)+","+Math.floor(255*E.b)+","+I+")"),C.fillRect(la.min.x|0,la.min.y|0,la.max.x-la.min.x|0,la.max.y-la.min.y|0)),la.makeEmpty())};this.render=function(a,j){if(!1===j instanceof THREE.Camera)console.error("THREE.CanvasRenderer.render: camera is not an instance of THREE.Camera.");
+else{!0===this.autoClear&&this.clear();C.setTransform(1,0,0,-1,A,O);q.info.render.vertices=0;q.info.render.faces=0;u=B.projectScene(a,j,this.sortObjects,this.sortElements);w=u.elements;z=u.lights;M=j;gb.setRGB(0,0,0);sa.setRGB(0,0,0);ga.setRGB(0,0,0);for(var D=0,G=z.length;D<G;D++){var y=z[D],F=y.color;y instanceof THREE.AmbientLight?gb.add(F):y instanceof THREE.DirectionalLight?sa.add(F):y instanceof THREE.PointLight&&ga.add(F)}D=0;for(G=w.length;D<G;D++){var x=w[D],v=x.material;if(!(void 0===v||
+!1===v.visible)){ra.makeEmpty();if(x instanceof THREE.RenderableSprite){Q=x;Q.x*=A;Q.y*=O;var y=Q,F=x,J=v;i(J.opacity);k(J.blending);var N=v=x=void 0,E=void 0,I=void 0,R=void 0,ba=void 0;J instanceof THREE.SpriteMaterial||J instanceof THREE.ParticleSystemMaterial?void 0!==J.map.image?(I=J.map.image,R=I.width>>1,ba=I.height>>1,N=F.scale.x*A,E=F.scale.y*O,x=N*R,v=E*ba,ra.min.set(y.x-x,y.y-v),ra.max.set(y.x+x,y.y+v),!1===Ha.isIntersectionBox(ra)?ra.makeEmpty():(C.save(),C.translate(y.x,y.y),C.rotate(-F.rotation),
+C.scale(N,-E),C.translate(-R,-ba),C.drawImage(I,0,0),C.restore())):(N=F.object.scale.x,E=F.object.scale.y,N*=F.scale.x*A,E*=F.scale.y*O,ra.min.set(y.x-N,y.y-E),ra.max.set(y.x+N,y.y+E),!1===Ha.isIntersectionBox(ra)?ra.makeEmpty():(t(J.color.getStyle()),C.save(),C.translate(y.x,y.y),C.rotate(-F.rotation),C.scale(N,E),C.fillRect(-1,-1,2,2),C.restore())):J instanceof THREE.SpriteCanvasMaterial&&(x=F.scale.x*A,v=F.scale.y*O,ra.min.set(y.x-x,y.y-v),ra.max.set(y.x+x,y.y+v),!1===Ha.isIntersectionBox(ra)?
+ra.makeEmpty():(s(J.color.getStyle()),t(J.color.getStyle()),C.save(),C.translate(y.x,y.y),C.rotate(-F.rotation),C.scale(x,v),J.program(C),C.restore()))}else if(x instanceof THREE.RenderableLine){if(Q=x.v1,K=x.v2,Q.positionScreen.x*=A,Q.positionScreen.y*=O,K.positionScreen.x*=A,K.positionScreen.y*=O,ra.setFromPoints([Q.positionScreen,K.positionScreen]),!0===Ha.isIntersectionBox(ra))if(y=Q,F=K,J=x,x=v,i(x.opacity),k(x.blending),C.beginPath(),C.moveTo(y.positionScreen.x,y.positionScreen.y),C.lineTo(F.positionScreen.x,
+F.positionScreen.y),x instanceof THREE.LineBasicMaterial){m(x.linewidth);l(x.linecap);p(x.linejoin);if(x.vertexColors!==THREE.VertexColors)s(x.color.getStyle());else if(v=J.vertexColors[0].getStyle(),J=J.vertexColors[1].getStyle(),v===J)s(v);else{try{var fa=C.createLinearGradient(y.positionScreen.x,y.positionScreen.y,F.positionScreen.x,F.positionScreen.y);fa.addColorStop(0,v);fa.addColorStop(1,J)}catch(oa){fa=v}s(fa)}C.stroke();ra.expandByScalar(2*x.linewidth)}else x instanceof THREE.LineDashedMaterial&&
+(m(x.linewidth),l(x.linecap),p(x.linejoin),s(x.color.getStyle()),n(x.dashSize,x.gapSize),C.stroke(),ra.expandByScalar(2*x.linewidth),n(null,null))}else if(x instanceof THREE.RenderableFace3){Q=x.v1;K=x.v2;ca=x.v3;if(-1>Q.positionScreen.z||1<Q.positionScreen.z)continue;if(-1>K.positionScreen.z||1<K.positionScreen.z)continue;if(-1>ca.positionScreen.z||1<ca.positionScreen.z)continue;Q.positionScreen.x*=A;Q.positionScreen.y*=O;K.positionScreen.x*=A;K.positionScreen.y*=O;ca.positionScreen.x*=A;ca.positionScreen.y*=
+O;0<v.overdraw&&(g(Q.positionScreen,K.positionScreen,v.overdraw),g(K.positionScreen,ca.positionScreen,v.overdraw),g(ca.positionScreen,Q.positionScreen,v.overdraw));ra.setFromPoints([Q.positionScreen,K.positionScreen,ca.positionScreen]);if(!0===Ha.isIntersectionBox(ra)){y=Q;F=K;J=ca;q.info.render.vertices+=3;q.info.render.faces++;i(v.opacity);k(v.blending);Fa=y.positionScreen.x;Ba=y.positionScreen.y;da=F.positionScreen.x;Aa=F.positionScreen.y;$=J.positionScreen.x;ea=J.positionScreen.y;var N=Fa,E=Ba,
+I=da,R=Aa,ba=$,pa=ea;C.beginPath();C.moveTo(N,E);C.lineTo(I,R);C.lineTo(ba,pa);C.closePath();(v instanceof THREE.MeshLambertMaterial||v instanceof THREE.MeshPhongMaterial)&&null===v.map?(ta.copy(v.color),ia.copy(v.emissive),v.vertexColors===THREE.FaceColors&&ta.multiply(x.color),!1===v.wireframe&&v.shading==THREE.SmoothShading&&3==x.vertexNormalsLength?(P.copy(gb),Z.copy(gb),U.copy(gb),b(x.v1.positionWorld,x.vertexNormalsModel[0],P),b(x.v2.positionWorld,x.vertexNormalsModel[1],Z),b(x.v3.positionWorld,
+x.vertexNormalsModel[2],U),P.multiply(ta).add(ia),Z.multiply(ta).add(ia),U.multiply(ta).add(ia),ka.addColors(Z,U).multiplyScalar(0.5),Qa=h(P,Z,U,ka),f(Fa,Ba,da,Aa,$,ea,0,0,1,0,0,1,Qa)):(V.copy(gb),b(x.centroidModel,x.normalModel,V),V.multiply(ta).add(ia),!0===v.wireframe?c(V,v.wireframeLinewidth,v.wireframeLinecap,v.wireframeLinejoin):d(V))):v instanceof THREE.MeshBasicMaterial||v instanceof THREE.MeshLambertMaterial||v instanceof THREE.MeshPhongMaterial?null!==v.map?v.map.mapping instanceof THREE.UVMapping&&
+(xa=x.uvs[0],e(Fa,Ba,da,Aa,$,ea,xa[0].x,xa[0].y,xa[1].x,xa[1].y,xa[2].x,xa[2].y,v.map)):null!==v.envMap?v.envMap.mapping instanceof THREE.SphericalReflectionMapping&&(ua.copy(x.vertexNormalsModelView[0]),bb=0.5*ua.x+0.5,cb=0.5*ua.y+0.5,ua.copy(x.vertexNormalsModelView[1]),Ma=0.5*ua.x+0.5,fb=0.5*ua.y+0.5,ua.copy(x.vertexNormalsModelView[2]),sb=0.5*ua.x+0.5,pb=0.5*ua.y+0.5,e(Fa,Ba,da,Aa,$,ea,bb,cb,Ma,fb,sb,pb,v.envMap)):(V.copy(v.color),v.vertexColors===THREE.FaceColors&&V.multiply(x.color),!0===v.wireframe?
+c(V,v.wireframeLinewidth,v.wireframeLinecap,v.wireframeLinejoin):d(V)):v instanceof THREE.MeshDepthMaterial?(Da=M.near,Ua=M.far,P.r=P.g=P.b=1-r(y.positionScreen.z*y.positionScreen.w,Da,Ua),Z.r=Z.g=Z.b=1-r(F.positionScreen.z*F.positionScreen.w,Da,Ua),U.r=U.g=U.b=1-r(J.positionScreen.z*J.positionScreen.w,Da,Ua),ka.addColors(Z,U).multiplyScalar(0.5),Qa=h(P,Z,U,ka),f(Fa,Ba,da,Aa,$,ea,0,0,1,0,0,1,Qa)):v instanceof THREE.MeshNormalMaterial&&(y=void 0,v.shading==THREE.FlatShading?(y=x.normalModelView,V.setRGB(y.x,
+y.y,y.z).multiplyScalar(0.5).addScalar(0.5),!0===v.wireframe?c(V,v.wireframeLinewidth,v.wireframeLinecap,v.wireframeLinejoin):d(V)):v.shading==THREE.SmoothShading&&(y=x.vertexNormalsModelView[0],P.setRGB(y.x,y.y,y.z).multiplyScalar(0.5).addScalar(0.5),y=x.vertexNormalsModelView[1],Z.setRGB(y.x,y.y,y.z).multiplyScalar(0.5).addScalar(0.5),y=x.vertexNormalsModelView[2],U.setRGB(y.x,y.y,y.z).multiplyScalar(0.5).addScalar(0.5),ka.addColors(Z,U).multiplyScalar(0.5),Qa=h(P,Z,U,ka),f(Fa,Ba,da,Aa,$,ea,0,0,
+1,0,0,1,Qa)))}}la.union(ra)}}C.setTransform(1,0,0,1,0,0)}}};THREE.ShaderChunk={fog_pars_fragment:"#ifdef USE_FOG\nuniform vec3 fogColor;\n#ifdef FOG_EXP2\nuniform float fogDensity;\n#else\nuniform float fogNear;\nuniform float fogFar;\n#endif\n#endif",fog_fragment:"#ifdef USE_FOG\nfloat depth = gl_FragCoord.z / gl_FragCoord.w;\n#ifdef FOG_EXP2\nconst float LOG2 = 1.442695;\nfloat fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );\nfogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );\n#else\nfloat fogFactor = smoothstep( fogNear, fogFar, depth );\n#endif\ngl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );\n#endif",
+envmap_pars_fragment:"#ifdef USE_ENVMAP\nuniform float reflectivity;\nuniform samplerCube envMap;\nuniform float flipEnvMap;\nuniform int combine;\n#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )\nuniform bool useRefract;\nuniform float refractionRatio;\n#else\nvarying vec3 vReflect;\n#endif\n#endif",envmap_fragment:"#ifdef USE_ENVMAP\nvec3 reflectVec;\n#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )\nvec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\nif ( useRefract ) {\nreflectVec = refract( cameraToVertex, normal, refractionRatio );\n} else { \nreflectVec = reflect( cameraToVertex, normal );\n}\n#else\nreflectVec = vReflect;\n#endif\n#ifdef DOUBLE_SIDED\nfloat flipNormal = ( -1.0 + 2.0 * float( gl_FrontFacing ) );\nvec4 cubeColor = textureCube( envMap, flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n#else\nvec4 cubeColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n#endif\n#ifdef GAMMA_INPUT\ncubeColor.xyz *= cubeColor.xyz;\n#endif\nif ( combine == 1 ) {\ngl_FragColor.xyz = mix( gl_FragColor.xyz, cubeColor.xyz, specularStrength * reflectivity );\n} else if ( combine == 2 ) {\ngl_FragColor.xyz += cubeColor.xyz * specularStrength * reflectivity;\n} else {\ngl_FragColor.xyz = mix( gl_FragColor.xyz, gl_FragColor.xyz * cubeColor.xyz, specularStrength * reflectivity );\n}\n#endif",
+envmap_pars_vertex:"#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP )\nvarying vec3 vReflect;\nuniform float refractionRatio;\nuniform bool useRefract;\n#endif",worldpos_vertex:"#if defined( USE_ENVMAP ) || defined( PHONG ) || defined( LAMBERT ) || defined ( USE_SHADOWMAP )\n#ifdef USE_SKINNING\nvec4 worldPosition = modelMatrix * skinned;\n#endif\n#if defined( USE_MORPHTARGETS ) && ! defined( USE_SKINNING )\nvec4 worldPosition = modelMatrix * vec4( morphed, 1.0 );\n#endif\n#if ! defined( USE_MORPHTARGETS ) && ! defined( USE_SKINNING )\nvec4 worldPosition = modelMatrix * vec4( position, 1.0 );\n#endif\n#endif",
+envmap_vertex:"#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP )\nvec3 worldNormal = mat3( modelMatrix[ 0 ].xyz, modelMatrix[ 1 ].xyz, modelMatrix[ 2 ].xyz ) * objectNormal;\nworldNormal = normalize( worldNormal );\nvec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );\nif ( useRefract ) {\nvReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n} else {\nvReflect = reflect( cameraToVertex, worldNormal );\n}\n#endif",map_particle_pars_fragment:"#ifdef USE_MAP\nuniform sampler2D map;\n#endif",
+map_particle_fragment:"#ifdef USE_MAP\ngl_FragColor = gl_FragColor * texture2D( map, vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y ) );\n#endif",map_pars_vertex:"#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP )\nvarying vec2 vUv;\nuniform vec4 offsetRepeat;\n#endif",map_pars_fragment:"#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP )\nvarying vec2 vUv;\n#endif\n#ifdef USE_MAP\nuniform sampler2D map;\n#endif",
+map_vertex:"#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP )\nvUv = uv * offsetRepeat.zw + offsetRepeat.xy;\n#endif",map_fragment:"#ifdef USE_MAP\nvec4 texelColor = texture2D( map, vUv );\n#ifdef GAMMA_INPUT\ntexelColor.xyz *= texelColor.xyz;\n#endif\ngl_FragColor = gl_FragColor * texelColor;\n#endif",lightmap_pars_fragment:"#ifdef USE_LIGHTMAP\nvarying vec2 vUv2;\nuniform sampler2D lightMap;\n#endif",lightmap_pars_vertex:"#ifdef USE_LIGHTMAP\nvarying vec2 vUv2;\n#endif",
+lightmap_fragment:"#ifdef USE_LIGHTMAP\ngl_FragColor = gl_FragColor * texture2D( lightMap, vUv2 );\n#endif",lightmap_vertex:"#ifdef USE_LIGHTMAP\nvUv2 = uv2;\n#endif",bumpmap_pars_fragment:"#ifdef USE_BUMPMAP\nuniform sampler2D bumpMap;\nuniform float bumpScale;\nvec2 dHdxy_fwd() {\nvec2 dSTdx = dFdx( vUv );\nvec2 dSTdy = dFdy( vUv );\nfloat Hll = bumpScale * texture2D( bumpMap, vUv ).x;\nfloat dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\nfloat dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\nreturn vec2( dBx, dBy );\n}\nvec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {\nvec3 vSigmaX = dFdx( surf_pos );\nvec3 vSigmaY = dFdy( surf_pos );\nvec3 vN = surf_norm;\nvec3 R1 = cross( vSigmaY, vN );\nvec3 R2 = cross( vN, vSigmaX );\nfloat fDet = dot( vSigmaX, R1 );\nvec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\nreturn normalize( abs( fDet ) * surf_norm - vGrad );\n}\n#endif",
+normalmap_pars_fragment:"#ifdef USE_NORMALMAP\nuniform sampler2D normalMap;\nuniform vec2 normalScale;\nvec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {\nvec3 q0 = dFdx( eye_pos.xyz );\nvec3 q1 = dFdy( eye_pos.xyz );\nvec2 st0 = dFdx( vUv.st );\nvec2 st1 = dFdy( vUv.st );\nvec3 S = normalize(  q0 * st1.t - q1 * st0.t );\nvec3 T = normalize( -q0 * st1.s + q1 * st0.s );\nvec3 N = normalize( surf_norm );\nvec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\nmapN.xy = normalScale * mapN.xy;\nmat3 tsn = mat3( S, T, N );\nreturn normalize( tsn * mapN );\n}\n#endif",
+specularmap_pars_fragment:"#ifdef USE_SPECULARMAP\nuniform sampler2D specularMap;\n#endif",specularmap_fragment:"float specularStrength;\n#ifdef USE_SPECULARMAP\nvec4 texelSpecular = texture2D( specularMap, vUv );\nspecularStrength = texelSpecular.r;\n#else\nspecularStrength = 1.0;\n#endif",lights_lambert_pars_vertex:"uniform vec3 ambient;\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform vec3 ambientLightColor;\n#if MAX_DIR_LIGHTS > 0\nuniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\nuniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n#endif\n#if MAX_HEMI_LIGHTS > 0\nuniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\nuniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\nuniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n#endif\n#if MAX_POINT_LIGHTS > 0\nuniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\nuniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\nuniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n#endif\n#if MAX_SPOT_LIGHTS > 0\nuniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\nuniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\nuniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\nuniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\nuniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\nuniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n#endif\n#ifdef WRAP_AROUND\nuniform vec3 wrapRGB;\n#endif",
+lights_lambert_vertex:"vLightFront = vec3( 0.0 );\n#ifdef DOUBLE_SIDED\nvLightBack = vec3( 0.0 );\n#endif\ntransformedNormal = normalize( transformedNormal );\n#if MAX_DIR_LIGHTS > 0\nfor( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\nvec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );\nvec3 dirVector = normalize( lDirection.xyz );\nfloat dotProduct = dot( transformedNormal, dirVector );\nvec3 directionalLightWeighting = vec3( max( dotProduct, 0.0 ) );\n#ifdef DOUBLE_SIDED\nvec3 directionalLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );\n#ifdef WRAP_AROUND\nvec3 directionalLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );\n#endif\n#endif\n#ifdef WRAP_AROUND\nvec3 directionalLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );\ndirectionalLightWeighting = mix( directionalLightWeighting, directionalLightWeightingHalf, wrapRGB );\n#ifdef DOUBLE_SIDED\ndirectionalLightWeightingBack = mix( directionalLightWeightingBack, directionalLightWeightingHalfBack, wrapRGB );\n#endif\n#endif\nvLightFront += directionalLightColor[ i ] * directionalLightWeighting;\n#ifdef DOUBLE_SIDED\nvLightBack += directionalLightColor[ i ] * directionalLightWeightingBack;\n#endif\n}\n#endif\n#if MAX_POINT_LIGHTS > 0\nfor( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\nvec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );\nvec3 lVector = lPosition.xyz - mvPosition.xyz;\nfloat lDistance = 1.0;\nif ( pointLightDistance[ i ] > 0.0 )\nlDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );\nlVector = normalize( lVector );\nfloat dotProduct = dot( transformedNormal, lVector );\nvec3 pointLightWeighting = vec3( max( dotProduct, 0.0 ) );\n#ifdef DOUBLE_SIDED\nvec3 pointLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );\n#ifdef WRAP_AROUND\nvec3 pointLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );\n#endif\n#endif\n#ifdef WRAP_AROUND\nvec3 pointLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );\npointLightWeighting = mix( pointLightWeighting, pointLightWeightingHalf, wrapRGB );\n#ifdef DOUBLE_SIDED\npointLightWeightingBack = mix( pointLightWeightingBack, pointLightWeightingHalfBack, wrapRGB );\n#endif\n#endif\nvLightFront += pointLightColor[ i ] * pointLightWeighting * lDistance;\n#ifdef DOUBLE_SIDED\nvLightBack += pointLightColor[ i ] * pointLightWeightingBack * lDistance;\n#endif\n}\n#endif\n#if MAX_SPOT_LIGHTS > 0\nfor( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\nvec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );\nvec3 lVector = lPosition.xyz - mvPosition.xyz;\nfloat spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - worldPosition.xyz ) );\nif ( spotEffect > spotLightAngleCos[ i ] ) {\nspotEffect = max( pow( spotEffect, spotLightExponent[ i ] ), 0.0 );\nfloat lDistance = 1.0;\nif ( spotLightDistance[ i ] > 0.0 )\nlDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );\nlVector = normalize( lVector );\nfloat dotProduct = dot( transformedNormal, lVector );\nvec3 spotLightWeighting = vec3( max( dotProduct, 0.0 ) );\n#ifdef DOUBLE_SIDED\nvec3 spotLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );\n#ifdef WRAP_AROUND\nvec3 spotLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );\n#endif\n#endif\n#ifdef WRAP_AROUND\nvec3 spotLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );\nspotLightWeighting = mix( spotLightWeighting, spotLightWeightingHalf, wrapRGB );\n#ifdef DOUBLE_SIDED\nspotLightWeightingBack = mix( spotLightWeightingBack, spotLightWeightingHalfBack, wrapRGB );\n#endif\n#endif\nvLightFront += spotLightColor[ i ] * spotLightWeighting * lDistance * spotEffect;\n#ifdef DOUBLE_SIDED\nvLightBack += spotLightColor[ i ] * spotLightWeightingBack * lDistance * spotEffect;\n#endif\n}\n}\n#endif\n#if MAX_HEMI_LIGHTS > 0\nfor( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\nvec4 lDirection = viewMatrix * vec4( hemisphereLightDirection[ i ], 0.0 );\nvec3 lVector = normalize( lDirection.xyz );\nfloat dotProduct = dot( transformedNormal, lVector );\nfloat hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\nfloat hemiDiffuseWeightBack = -0.5 * dotProduct + 0.5;\nvLightFront += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n#ifdef DOUBLE_SIDED\nvLightBack += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeightBack );\n#endif\n}\n#endif\nvLightFront = vLightFront * diffuse + ambient * ambientLightColor + emissive;\n#ifdef DOUBLE_SIDED\nvLightBack = vLightBack * diffuse + ambient * ambientLightColor + emissive;\n#endif",
+lights_phong_pars_vertex:"#ifndef PHONG_PER_PIXEL\n#if MAX_POINT_LIGHTS > 0\nuniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\nuniform float pointLightDistance[ MAX_POINT_LIGHTS ];\nvarying vec4 vPointLight[ MAX_POINT_LIGHTS ];\n#endif\n#if MAX_SPOT_LIGHTS > 0\nuniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\nuniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\nvarying vec4 vSpotLight[ MAX_SPOT_LIGHTS ];\n#endif\n#endif\n#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP )\nvarying vec3 vWorldPosition;\n#endif",
+lights_phong_vertex:"#ifndef PHONG_PER_PIXEL\n#if MAX_POINT_LIGHTS > 0\nfor( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\nvec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );\nvec3 lVector = lPosition.xyz - mvPosition.xyz;\nfloat lDistance = 1.0;\nif ( pointLightDistance[ i ] > 0.0 )\nlDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );\nvPointLight[ i ] = vec4( lVector, lDistance );\n}\n#endif\n#if MAX_SPOT_LIGHTS > 0\nfor( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\nvec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );\nvec3 lVector = lPosition.xyz - mvPosition.xyz;\nfloat lDistance = 1.0;\nif ( spotLightDistance[ i ] > 0.0 )\nlDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );\nvSpotLight[ i ] = vec4( lVector, lDistance );\n}\n#endif\n#endif\n#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP )\nvWorldPosition = worldPosition.xyz;\n#endif",
+lights_phong_pars_fragment:"uniform vec3 ambientLightColor;\n#if MAX_DIR_LIGHTS > 0\nuniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\nuniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n#endif\n#if MAX_HEMI_LIGHTS > 0\nuniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\nuniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\nuniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n#endif\n#if MAX_POINT_LIGHTS > 0\nuniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\n#ifdef PHONG_PER_PIXEL\nuniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\nuniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n#else\nvarying vec4 vPointLight[ MAX_POINT_LIGHTS ];\n#endif\n#endif\n#if MAX_SPOT_LIGHTS > 0\nuniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\nuniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\nuniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\nuniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\nuniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n#ifdef PHONG_PER_PIXEL\nuniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n#else\nvarying vec4 vSpotLight[ MAX_SPOT_LIGHTS ];\n#endif\n#endif\n#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP )\nvarying vec3 vWorldPosition;\n#endif\n#ifdef WRAP_AROUND\nuniform vec3 wrapRGB;\n#endif\nvarying vec3 vViewPosition;\nvarying vec3 vNormal;",
+lights_phong_fragment:"vec3 normal = normalize( vNormal );\nvec3 viewPosition = normalize( vViewPosition );\n#ifdef DOUBLE_SIDED\nnormal = normal * ( -1.0 + 2.0 * float( gl_FrontFacing ) );\n#endif\n#ifdef USE_NORMALMAP\nnormal = perturbNormal2Arb( -vViewPosition, normal );\n#elif defined( USE_BUMPMAP )\nnormal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );\n#endif\n#if MAX_POINT_LIGHTS > 0\nvec3 pointDiffuse  = vec3( 0.0 );\nvec3 pointSpecular = vec3( 0.0 );\nfor ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n#ifdef PHONG_PER_PIXEL\nvec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );\nvec3 lVector = lPosition.xyz + vViewPosition.xyz;\nfloat lDistance = 1.0;\nif ( pointLightDistance[ i ] > 0.0 )\nlDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );\nlVector = normalize( lVector );\n#else\nvec3 lVector = normalize( vPointLight[ i ].xyz );\nfloat lDistance = vPointLight[ i ].w;\n#endif\nfloat dotProduct = dot( normal, lVector );\n#ifdef WRAP_AROUND\nfloat pointDiffuseWeightFull = max( dotProduct, 0.0 );\nfloat pointDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );\nvec3 pointDiffuseWeight = mix( vec3 ( pointDiffuseWeightFull ), vec3( pointDiffuseWeightHalf ), wrapRGB );\n#else\nfloat pointDiffuseWeight = max( dotProduct, 0.0 );\n#endif\npointDiffuse  += diffuse * pointLightColor[ i ] * pointDiffuseWeight * lDistance;\nvec3 pointHalfVector = normalize( lVector + viewPosition );\nfloat pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );\nfloat pointSpecularWeight = specularStrength * max( pow( pointDotNormalHalf, shininess ), 0.0 );\n#ifdef PHYSICALLY_BASED_SHADING\nfloat specularNormalization = ( shininess + 2.0001 ) / 8.0;\nvec3 schlick = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( lVector, pointHalfVector ), 5.0 );\npointSpecular += schlick * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * lDistance * specularNormalization;\n#else\npointSpecular += specular * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * lDistance;\n#endif\n}\n#endif\n#if MAX_SPOT_LIGHTS > 0\nvec3 spotDiffuse  = vec3( 0.0 );\nvec3 spotSpecular = vec3( 0.0 );\nfor ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\n#ifdef PHONG_PER_PIXEL\nvec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );\nvec3 lVector = lPosition.xyz + vViewPosition.xyz;\nfloat lDistance = 1.0;\nif ( spotLightDistance[ i ] > 0.0 )\nlDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );\nlVector = normalize( lVector );\n#else\nvec3 lVector = normalize( vSpotLight[ i ].xyz );\nfloat lDistance = vSpotLight[ i ].w;\n#endif\nfloat spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - vWorldPosition ) );\nif ( spotEffect > spotLightAngleCos[ i ] ) {\nspotEffect = max( pow( spotEffect, spotLightExponent[ i ] ), 0.0 );\nfloat dotProduct = dot( normal, lVector );\n#ifdef WRAP_AROUND\nfloat spotDiffuseWeightFull = max( dotProduct, 0.0 );\nfloat spotDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );\nvec3 spotDiffuseWeight = mix( vec3 ( spotDiffuseWeightFull ), vec3( spotDiffuseWeightHalf ), wrapRGB );\n#else\nfloat spotDiffuseWeight = max( dotProduct, 0.0 );\n#endif\nspotDiffuse += diffuse * spotLightColor[ i ] * spotDiffuseWeight * lDistance * spotEffect;\nvec3 spotHalfVector = normalize( lVector + viewPosition );\nfloat spotDotNormalHalf = max( dot( normal, spotHalfVector ), 0.0 );\nfloat spotSpecularWeight = specularStrength * max( pow( spotDotNormalHalf, shininess ), 0.0 );\n#ifdef PHYSICALLY_BASED_SHADING\nfloat specularNormalization = ( shininess + 2.0001 ) / 8.0;\nvec3 schlick = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( lVector, spotHalfVector ), 5.0 );\nspotSpecular += schlick * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * lDistance * specularNormalization * spotEffect;\n#else\nspotSpecular += specular * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * lDistance * spotEffect;\n#endif\n}\n}\n#endif\n#if MAX_DIR_LIGHTS > 0\nvec3 dirDiffuse  = vec3( 0.0 );\nvec3 dirSpecular = vec3( 0.0 );\nfor( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\nvec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );\nvec3 dirVector = normalize( lDirection.xyz );\nfloat dotProduct = dot( normal, dirVector );\n#ifdef WRAP_AROUND\nfloat dirDiffuseWeightFull = max( dotProduct, 0.0 );\nfloat dirDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );\nvec3 dirDiffuseWeight = mix( vec3( dirDiffuseWeightFull ), vec3( dirDiffuseWeightHalf ), wrapRGB );\n#else\nfloat dirDiffuseWeight = max( dotProduct, 0.0 );\n#endif\ndirDiffuse  += diffuse * directionalLightColor[ i ] * dirDiffuseWeight;\nvec3 dirHalfVector = normalize( dirVector + viewPosition );\nfloat dirDotNormalHalf = max( dot( normal, dirHalfVector ), 0.0 );\nfloat dirSpecularWeight = specularStrength * max( pow( dirDotNormalHalf, shininess ), 0.0 );\n#ifdef PHYSICALLY_BASED_SHADING\nfloat specularNormalization = ( shininess + 2.0001 ) / 8.0;\nvec3 schlick = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( dirVector, dirHalfVector ), 5.0 );\ndirSpecular += schlick * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization;\n#else\ndirSpecular += specular * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight;\n#endif\n}\n#endif\n#if MAX_HEMI_LIGHTS > 0\nvec3 hemiDiffuse  = vec3( 0.0 );\nvec3 hemiSpecular = vec3( 0.0 );\nfor( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\nvec4 lDirection = viewMatrix * vec4( hemisphereLightDirection[ i ], 0.0 );\nvec3 lVector = normalize( lDirection.xyz );\nfloat dotProduct = dot( normal, lVector );\nfloat hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\nvec3 hemiColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\nhemiDiffuse += diffuse * hemiColor;\nvec3 hemiHalfVectorSky = normalize( lVector + viewPosition );\nfloat hemiDotNormalHalfSky = 0.5 * dot( normal, hemiHalfVectorSky ) + 0.5;\nfloat hemiSpecularWeightSky = specularStrength * max( pow( hemiDotNormalHalfSky, shininess ), 0.0 );\nvec3 lVectorGround = -lVector;\nvec3 hemiHalfVectorGround = normalize( lVectorGround + viewPosition );\nfloat hemiDotNormalHalfGround = 0.5 * dot( normal, hemiHalfVectorGround ) + 0.5;\nfloat hemiSpecularWeightGround = specularStrength * max( pow( hemiDotNormalHalfGround, shininess ), 0.0 );\n#ifdef PHYSICALLY_BASED_SHADING\nfloat dotProductGround = dot( normal, lVectorGround );\nfloat specularNormalization = ( shininess + 2.0001 ) / 8.0;\nvec3 schlickSky = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( lVector, hemiHalfVectorSky ), 5.0 );\nvec3 schlickGround = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( lVectorGround, hemiHalfVectorGround ), 5.0 );\nhemiSpecular += hemiColor * specularNormalization * ( schlickSky * hemiSpecularWeightSky * max( dotProduct, 0.0 ) + schlickGround * hemiSpecularWeightGround * max( dotProductGround, 0.0 ) );\n#else\nhemiSpecular += specular * hemiColor * ( hemiSpecularWeightSky + hemiSpecularWeightGround ) * hemiDiffuseWeight;\n#endif\n}\n#endif\nvec3 totalDiffuse = vec3( 0.0 );\nvec3 totalSpecular = vec3( 0.0 );\n#if MAX_DIR_LIGHTS > 0\ntotalDiffuse += dirDiffuse;\ntotalSpecular += dirSpecular;\n#endif\n#if MAX_HEMI_LIGHTS > 0\ntotalDiffuse += hemiDiffuse;\ntotalSpecular += hemiSpecular;\n#endif\n#if MAX_POINT_LIGHTS > 0\ntotalDiffuse += pointDiffuse;\ntotalSpecular += pointSpecular;\n#endif\n#if MAX_SPOT_LIGHTS > 0\ntotalDiffuse += spotDiffuse;\ntotalSpecular += spotSpecular;\n#endif\n#ifdef METAL\ngl_FragColor.xyz = gl_FragColor.xyz * ( emissive + totalDiffuse + ambientLightColor * ambient + totalSpecular );\n#else\ngl_FragColor.xyz = gl_FragColor.xyz * ( emissive + totalDiffuse + ambientLightColor * ambient ) + totalSpecular;\n#endif",
+color_pars_fragment:"#ifdef USE_COLOR\nvarying vec3 vColor;\n#endif",color_fragment:"#ifdef USE_COLOR\ngl_FragColor = gl_FragColor * vec4( vColor, 1.0 );\n#endif",color_pars_vertex:"#ifdef USE_COLOR\nvarying vec3 vColor;\n#endif",color_vertex:"#ifdef USE_COLOR\n#ifdef GAMMA_INPUT\nvColor = color * color;\n#else\nvColor = color;\n#endif\n#endif",skinning_pars_vertex:"#ifdef USE_SKINNING\n#ifdef BONE_TEXTURE\nuniform sampler2D boneTexture;\nuniform int boneTextureWidth;\nuniform int boneTextureHeight;\nmat4 getBoneMatrix( const in float i ) {\nfloat j = i * 4.0;\nfloat x = mod( j, float( boneTextureWidth ) );\nfloat y = floor( j / float( boneTextureWidth ) );\nfloat dx = 1.0 / float( boneTextureWidth );\nfloat dy = 1.0 / float( boneTextureHeight );\ny = dy * ( y + 0.5 );\nvec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\nvec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\nvec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\nvec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\nmat4 bone = mat4( v1, v2, v3, v4 );\nreturn bone;\n}\n#else\nuniform mat4 boneGlobalMatrices[ MAX_BONES ];\nmat4 getBoneMatrix( const in float i ) {\nmat4 bone = boneGlobalMatrices[ int(i) ];\nreturn bone;\n}\n#endif\n#endif",
+skinbase_vertex:"#ifdef USE_SKINNING\nmat4 boneMatX = getBoneMatrix( skinIndex.x );\nmat4 boneMatY = getBoneMatrix( skinIndex.y );\n#endif",skinning_vertex:"#ifdef USE_SKINNING\n#ifdef USE_MORPHTARGETS\nvec4 skinVertex = vec4( morphed, 1.0 );\n#else\nvec4 skinVertex = vec4( position, 1.0 );\n#endif\nvec4 skinned  = boneMatX * skinVertex * skinWeight.x;\nskinned \t  += boneMatY * skinVertex * skinWeight.y;\n#endif",morphtarget_pars_vertex:"#ifdef USE_MORPHTARGETS\n#ifndef USE_MORPHNORMALS\nuniform float morphTargetInfluences[ 8 ];\n#else\nuniform float morphTargetInfluences[ 4 ];\n#endif\n#endif",
+morphtarget_vertex:"#ifdef USE_MORPHTARGETS\nvec3 morphed = vec3( 0.0 );\nmorphed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];\nmorphed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];\nmorphed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];\nmorphed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];\n#ifndef USE_MORPHNORMALS\nmorphed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];\nmorphed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];\nmorphed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];\nmorphed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];\n#endif\nmorphed += position;\n#endif",
+default_vertex:"vec4 mvPosition;\n#ifdef USE_SKINNING\nmvPosition = modelViewMatrix * skinned;\n#endif\n#if !defined( USE_SKINNING ) && defined( USE_MORPHTARGETS )\nmvPosition = modelViewMatrix * vec4( morphed, 1.0 );\n#endif\n#if !defined( USE_SKINNING ) && ! defined( USE_MORPHTARGETS )\nmvPosition = modelViewMatrix * vec4( position, 1.0 );\n#endif\ngl_Position = projectionMatrix * mvPosition;",morphnormal_vertex:"#ifdef USE_MORPHNORMALS\nvec3 morphedNormal = vec3( 0.0 );\nmorphedNormal +=  ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];\nmorphedNormal +=  ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];\nmorphedNormal +=  ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];\nmorphedNormal +=  ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];\nmorphedNormal += normal;\n#endif",
+skinnormal_vertex:"#ifdef USE_SKINNING\nmat4 skinMatrix = skinWeight.x * boneMatX;\nskinMatrix \t+= skinWeight.y * boneMatY;\n#ifdef USE_MORPHNORMALS\nvec4 skinnedNormal = skinMatrix * vec4( morphedNormal, 0.0 );\n#else\nvec4 skinnedNormal = skinMatrix * vec4( normal, 0.0 );\n#endif\n#endif",defaultnormal_vertex:"vec3 objectNormal;\n#ifdef USE_SKINNING\nobjectNormal = skinnedNormal.xyz;\n#endif\n#if !defined( USE_SKINNING ) && defined( USE_MORPHNORMALS )\nobjectNormal = morphedNormal;\n#endif\n#if !defined( USE_SKINNING ) && ! defined( USE_MORPHNORMALS )\nobjectNormal = normal;\n#endif\n#ifdef FLIP_SIDED\nobjectNormal = -objectNormal;\n#endif\nvec3 transformedNormal = normalMatrix * objectNormal;",
+shadowmap_pars_fragment:"#ifdef USE_SHADOWMAP\nuniform sampler2D shadowMap[ MAX_SHADOWS ];\nuniform vec2 shadowMapSize[ MAX_SHADOWS ];\nuniform float shadowDarkness[ MAX_SHADOWS ];\nuniform float shadowBias[ MAX_SHADOWS ];\nvarying vec4 vShadowCoord[ MAX_SHADOWS ];\nfloat unpackDepth( const in vec4 rgba_depth ) {\nconst vec4 bit_shift = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );\nfloat depth = dot( rgba_depth, bit_shift );\nreturn depth;\n}\n#endif",shadowmap_fragment:"#ifdef USE_SHADOWMAP\n#ifdef SHADOWMAP_DEBUG\nvec3 frustumColors[3];\nfrustumColors[0] = vec3( 1.0, 0.5, 0.0 );\nfrustumColors[1] = vec3( 0.0, 1.0, 0.8 );\nfrustumColors[2] = vec3( 0.0, 0.5, 1.0 );\n#endif\n#ifdef SHADOWMAP_CASCADE\nint inFrustumCount = 0;\n#endif\nfloat fDepth;\nvec3 shadowColor = vec3( 1.0 );\nfor( int i = 0; i < MAX_SHADOWS; i ++ ) {\nvec3 shadowCoord = vShadowCoord[ i ].xyz / vShadowCoord[ i ].w;\nbvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\nbool inFrustum = all( inFrustumVec );\n#ifdef SHADOWMAP_CASCADE\ninFrustumCount += int( inFrustum );\nbvec3 frustumTestVec = bvec3( inFrustum, inFrustumCount == 1, shadowCoord.z <= 1.0 );\n#else\nbvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\n#endif\nbool frustumTest = all( frustumTestVec );\nif ( frustumTest ) {\nshadowCoord.z += shadowBias[ i ];\n#if defined( SHADOWMAP_TYPE_PCF )\nfloat shadow = 0.0;\nconst float shadowDelta = 1.0 / 9.0;\nfloat xPixelOffset = 1.0 / shadowMapSize[ i ].x;\nfloat yPixelOffset = 1.0 / shadowMapSize[ i ].y;\nfloat dx0 = -1.25 * xPixelOffset;\nfloat dy0 = -1.25 * yPixelOffset;\nfloat dx1 = 1.25 * xPixelOffset;\nfloat dy1 = 1.25 * yPixelOffset;\nfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );\nif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\nfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );\nif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\nfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );\nif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\nfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );\nif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\nfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );\nif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\nfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );\nif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\nfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );\nif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\nfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );\nif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\nfDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );\nif ( fDepth < shadowCoord.z ) shadow += shadowDelta;\nshadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );\n#elif defined( SHADOWMAP_TYPE_PCF_SOFT )\nfloat shadow = 0.0;\nfloat xPixelOffset = 1.0 / shadowMapSize[ i ].x;\nfloat yPixelOffset = 1.0 / shadowMapSize[ i ].y;\nfloat dx0 = -1.0 * xPixelOffset;\nfloat dy0 = -1.0 * yPixelOffset;\nfloat dx1 = 1.0 * xPixelOffset;\nfloat dy1 = 1.0 * yPixelOffset;\nmat3 shadowKernel;\nmat3 depthKernel;\ndepthKernel[0][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );\ndepthKernel[0][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );\ndepthKernel[0][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );\ndepthKernel[1][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );\ndepthKernel[1][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );\ndepthKernel[1][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );\ndepthKernel[2][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );\ndepthKernel[2][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );\ndepthKernel[2][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );\nvec3 shadowZ = vec3( shadowCoord.z );\nshadowKernel[0] = vec3(lessThan(depthKernel[0], shadowZ ));\nshadowKernel[0] *= vec3(0.25);\nshadowKernel[1] = vec3(lessThan(depthKernel[1], shadowZ ));\nshadowKernel[1] *= vec3(0.25);\nshadowKernel[2] = vec3(lessThan(depthKernel[2], shadowZ ));\nshadowKernel[2] *= vec3(0.25);\nvec2 fractionalCoord = 1.0 - fract( shadowCoord.xy * shadowMapSize[i].xy );\nshadowKernel[0] = mix( shadowKernel[1], shadowKernel[0], fractionalCoord.x );\nshadowKernel[1] = mix( shadowKernel[2], shadowKernel[1], fractionalCoord.x );\nvec4 shadowValues;\nshadowValues.x = mix( shadowKernel[0][1], shadowKernel[0][0], fractionalCoord.y );\nshadowValues.y = mix( shadowKernel[0][2], shadowKernel[0][1], fractionalCoord.y );\nshadowValues.z = mix( shadowKernel[1][1], shadowKernel[1][0], fractionalCoord.y );\nshadowValues.w = mix( shadowKernel[1][2], shadowKernel[1][1], fractionalCoord.y );\nshadow = dot( shadowValues, vec4( 1.0 ) );\nshadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );\n#else\nvec4 rgbaDepth = texture2D( shadowMap[ i ], shadowCoord.xy );\nfloat fDepth = unpackDepth( rgbaDepth );\nif ( fDepth < shadowCoord.z )\nshadowColor = shadowColor * vec3( 1.0 - shadowDarkness[ i ] );\n#endif\n}\n#ifdef SHADOWMAP_DEBUG\n#ifdef SHADOWMAP_CASCADE\nif ( inFrustum && inFrustumCount == 1 ) gl_FragColor.xyz *= frustumColors[ i ];\n#else\nif ( inFrustum ) gl_FragColor.xyz *= frustumColors[ i ];\n#endif\n#endif\n}\n#ifdef GAMMA_OUTPUT\nshadowColor *= shadowColor;\n#endif\ngl_FragColor.xyz = gl_FragColor.xyz * shadowColor;\n#endif",
+shadowmap_pars_vertex:"#ifdef USE_SHADOWMAP\nvarying vec4 vShadowCoord[ MAX_SHADOWS ];\nuniform mat4 shadowMatrix[ MAX_SHADOWS ];\n#endif",shadowmap_vertex:"#ifdef USE_SHADOWMAP\nfor( int i = 0; i < MAX_SHADOWS; i ++ ) {\nvShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;\n}\n#endif",alphatest_fragment:"#ifdef ALPHATEST\nif ( gl_FragColor.a < ALPHATEST ) discard;\n#endif",linear_to_gamma_fragment:"#ifdef GAMMA_OUTPUT\ngl_FragColor.xyz = sqrt( gl_FragColor.xyz );\n#endif"};
+THREE.UniformsUtils={merge:function(a){var b,c,d,e={};for(b=0;b<a.length;b++)for(c in d=this.clone(a[b]),d)e[c]=d[c];return e},clone:function(a){var b,c,d,e={};for(b in a)for(c in e[b]={},a[b])d=a[b][c],e[b][c]=d instanceof THREE.Color||d instanceof THREE.Vector2||d instanceof THREE.Vector3||d instanceof THREE.Vector4||d instanceof THREE.Matrix4||d instanceof THREE.Texture?d.clone():d instanceof Array?d.slice():d;return e}};
+THREE.UniformsLib={common:{diffuse:{type:"c",value:new THREE.Color(15658734)},opacity:{type:"f",value:1},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},envMap:{type:"t",value:null},flipEnvMap:{type:"f",value:-1},useRefract:{type:"i",value:0},reflectivity:{type:"f",value:1},refractionRatio:{type:"f",value:0.98},combine:{type:"i",value:0},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:2.5E-4},fogNear:{type:"f",value:1},fogFar:{type:"f",value:2E3},fogColor:{type:"c",value:new THREE.Color(16777215)}},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(15658734)},opacity:{type:"f",value:1},size:{type:"f",
+value:1},scale:{type:"f",value:1},map:{type:"t",value:null},fogDensity:{type:"f",value:2.5E-4},fogNear:{type:"f",value:1},fogFar:{type:"f",value:2E3},fogColor:{type:"c",value:new THREE.Color(16777215)}},shadowmap:{shadowMap:{type:"tv",value:[]},shadowMapSize:{type:"v2v",value:[]},shadowBias:{type:"fv1",value:[]},shadowDarkness:{type:"fv1",value:[]},shadowMatrix:{type:"m4v",value:[]}}};
+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,"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.worldpos_vertex,THREE.ShaderChunk.envmap_vertex,THREE.ShaderChunk.shadowmap_vertex,"}"].join("\n"),fragmentShader:["uniform vec3 diffuse;\nuniform float opacity;",THREE.ShaderChunk.color_pars_fragment,THREE.ShaderChunk.map_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,"void main() {\ngl_FragColor = vec4( diffuse, opacity );",THREE.ShaderChunk.map_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(16777215)},emissive:{type:"c",value:new THREE.Color(0)},wrapRGB:{type:"v3",value:new THREE.Vector3(1,1,1)}}]),vertexShader:["#define LAMBERT\nvarying vec3 vLightFront;\n#ifdef DOUBLE_SIDED\nvarying vec3 vLightBack;\n#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,"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.worldpos_vertex,THREE.ShaderChunk.envmap_vertex,THREE.ShaderChunk.lights_lambert_vertex,THREE.ShaderChunk.shadowmap_vertex,"}"].join("\n"),fragmentShader:["uniform float opacity;\nvarying vec3 vLightFront;\n#ifdef DOUBLE_SIDED\nvarying vec3 vLightBack;\n#endif",THREE.ShaderChunk.color_pars_fragment,THREE.ShaderChunk.map_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,"void main() {\ngl_FragColor = vec4( vec3 ( 1.0 ), opacity );",THREE.ShaderChunk.map_fragment,THREE.ShaderChunk.alphatest_fragment,THREE.ShaderChunk.specularmap_fragment,"#ifdef DOUBLE_SIDED\nif ( gl_FrontFacing )\ngl_FragColor.xyz *= vLightFront;\nelse\ngl_FragColor.xyz *= vLightBack;\n#else\ngl_FragColor.xyz *= vLightFront;\n#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(16777215)},emissive:{type:"c",value:new THREE.Color(0)},specular:{type:"c",
+value:new THREE.Color(1118481)},shininess:{type:"f",value:30},wrapRGB:{type:"v3",value:new THREE.Vector3(1,1,1)}}]),vertexShader:["#define PHONG\nvarying vec3 vViewPosition;\nvarying 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,
+"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,"vViewPosition = -mvPosition.xyz;",THREE.ShaderChunk.worldpos_vertex,THREE.ShaderChunk.envmap_vertex,
+THREE.ShaderChunk.lights_phong_vertex,THREE.ShaderChunk.shadowmap_vertex,"}"].join("\n"),fragmentShader:["uniform vec3 diffuse;\nuniform float opacity;\nuniform vec3 ambient;\nuniform vec3 emissive;\nuniform vec3 specular;\nuniform float shininess;",THREE.ShaderChunk.color_pars_fragment,THREE.ShaderChunk.map_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,"void main() {\ngl_FragColor = vec4( vec3 ( 1.0 ), opacity );",THREE.ShaderChunk.map_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;\nuniform float scale;",THREE.ShaderChunk.color_pars_vertex,THREE.ShaderChunk.shadowmap_pars_vertex,"void main() {",THREE.ShaderChunk.color_vertex,"vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\n#ifdef USE_SIZEATTENUATION\ngl_PointSize = size * ( scale / length( mvPosition.xyz ) );\n#else\ngl_PointSize = size;\n#endif\ngl_Position = projectionMatrix * mvPosition;",
+THREE.ShaderChunk.worldpos_vertex,THREE.ShaderChunk.shadowmap_vertex,"}"].join("\n"),fragmentShader:["uniform vec3 psColor;\nuniform float opacity;",THREE.ShaderChunk.color_pars_fragment,THREE.ShaderChunk.map_particle_pars_fragment,THREE.ShaderChunk.fog_pars_fragment,THREE.ShaderChunk.shadowmap_pars_fragment,"void main() {\ngl_FragColor = vec4( psColor, opacity );",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;\nattribute float lineDistance;\nvarying float vLineDistance;",THREE.ShaderChunk.color_pars_vertex,"void main() {",THREE.ShaderChunk.color_vertex,"vLineDistance = scale * lineDistance;\nvec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\ngl_Position = projectionMatrix * mvPosition;\n}"].join("\n"),
+fragmentShader:["uniform vec3 diffuse;\nuniform float opacity;\nuniform float dashSize;\nuniform float totalSize;\nvarying float vLineDistance;",THREE.ShaderChunk.color_pars_fragment,THREE.ShaderChunk.fog_pars_fragment,"void main() {\nif ( mod( vLineDistance, totalSize ) > dashSize ) {\ndiscard;\n}\ngl_FragColor = vec4( diffuse, opacity );",THREE.ShaderChunk.color_fragment,THREE.ShaderChunk.fog_fragment,"}"].join("\n")},depth:{uniforms:{mNear:{type:"f",value:1},mFar:{type:"f",value:2E3},opacity:{type:"f",
+value:1}},vertexShader:"void main() {\ngl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}",fragmentShader:"uniform float mNear;\nuniform float mFar;\nuniform float opacity;\nvoid main() {\nfloat depth = gl_FragCoord.z / gl_FragCoord.w;\nfloat color = 1.0 - smoothstep( mNear, mFar, depth );\ngl_FragColor = vec4( vec3( color ), opacity );\n}"},normal:{uniforms:{opacity:{type:"f",value:1}},vertexShader:["varying vec3 vNormal;",THREE.ShaderChunk.morphtarget_pars_vertex,"void main() {\nvNormal = normalize( normalMatrix * normal );",
+THREE.ShaderChunk.morphtarget_vertex,THREE.ShaderChunk.default_vertex,"}"].join("\n"),fragmentShader:"uniform float opacity;\nvarying vec3 vNormal;\nvoid main() {\ngl_FragColor = vec4( 0.5 * normalize( vNormal ) + 0.5, opacity );\n}"},normalmap:{uniforms:THREE.UniformsUtils.merge([THREE.UniformsLib.fog,THREE.UniformsLib.lights,THREE.UniformsLib.shadowmap,{enableAO:{type:"i",value:0},enableDiffuse:{type:"i",value:0},enableSpecular:{type:"i",value:0},enableReflection:{type:"i",value:0},enableDisplacement:{type:"i",
+value:0},tDisplacement:{type:"t",value:null},tDiffuse:{type:"t",value:null},tCube:{type:"t",value:null},tNormal:{type:"t",value:null},tSpecular:{type:"t",value:null},tAO:{type:"t",value:null},uNormalScale:{type:"v2",value:new THREE.Vector2(1,1)},uDisplacementBias:{type:"f",value:0},uDisplacementScale:{type:"f",value:1},uDiffuseColor:{type:"c",value:new THREE.Color(16777215)},uSpecularColor:{type:"c",value:new THREE.Color(1118481)},uAmbientColor:{type:"c",value:new THREE.Color(16777215)},uShininess:{type:"f",
+value:30},uOpacity:{type:"f",value:1},useRefract:{type:"i",value:0},uRefractionRatio:{type:"f",value:0.98},uReflectivity:{type:"f",value:0.5},uOffset:{type:"v2",value:new THREE.Vector2(0,0)},uRepeat:{type:"v2",value:new THREE.Vector2(1,1)},wrapRGB:{type:"v3",value:new THREE.Vector3(1,1,1)}}]),fragmentShader:["uniform vec3 uAmbientColor;\nuniform vec3 uDiffuseColor;\nuniform vec3 uSpecularColor;\nuniform float uShininess;\nuniform float uOpacity;\nuniform bool enableDiffuse;\nuniform bool enableSpecular;\nuniform bool enableAO;\nuniform bool enableReflection;\nuniform sampler2D tDiffuse;\nuniform sampler2D tNormal;\nuniform sampler2D tSpecular;\nuniform sampler2D tAO;\nuniform samplerCube tCube;\nuniform vec2 uNormalScale;\nuniform bool useRefract;\nuniform float uRefractionRatio;\nuniform float uReflectivity;\nvarying vec3 vTangent;\nvarying vec3 vBinormal;\nvarying vec3 vNormal;\nvarying vec2 vUv;\nuniform vec3 ambientLightColor;\n#if MAX_DIR_LIGHTS > 0\nuniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\nuniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n#endif\n#if MAX_HEMI_LIGHTS > 0\nuniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\nuniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\nuniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n#endif\n#if MAX_POINT_LIGHTS > 0\nuniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\nuniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\nuniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n#endif\n#if MAX_SPOT_LIGHTS > 0\nuniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\nuniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\nuniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\nuniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\nuniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\nuniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n#endif\n#ifdef WRAP_AROUND\nuniform vec3 wrapRGB;\n#endif\nvarying vec3 vWorldPosition;\nvarying vec3 vViewPosition;",
+THREE.ShaderChunk.shadowmap_pars_fragment,THREE.ShaderChunk.fog_pars_fragment,"void main() {\ngl_FragColor = vec4( vec3( 1.0 ), uOpacity );\nvec3 specularTex = vec3( 1.0 );\nvec3 normalTex = texture2D( tNormal, vUv ).xyz * 2.0 - 1.0;\nnormalTex.xy *= uNormalScale;\nnormalTex = normalize( normalTex );\nif( enableDiffuse ) {\n#ifdef GAMMA_INPUT\nvec4 texelColor = texture2D( tDiffuse, vUv );\ntexelColor.xyz *= texelColor.xyz;\ngl_FragColor = gl_FragColor * texelColor;\n#else\ngl_FragColor = gl_FragColor * texture2D( tDiffuse, vUv );\n#endif\n}\nif( enableAO ) {\n#ifdef GAMMA_INPUT\nvec4 aoColor = texture2D( tAO, vUv );\naoColor.xyz *= aoColor.xyz;\ngl_FragColor.xyz = gl_FragColor.xyz * aoColor.xyz;\n#else\ngl_FragColor.xyz = gl_FragColor.xyz * texture2D( tAO, vUv ).xyz;\n#endif\n}\nif( enableSpecular )\nspecularTex = texture2D( tSpecular, vUv ).xyz;\nmat3 tsb = mat3( normalize( vTangent ), normalize( vBinormal ), normalize( vNormal ) );\nvec3 finalNormal = tsb * normalTex;\n#ifdef FLIP_SIDED\nfinalNormal = -finalNormal;\n#endif\nvec3 normal = normalize( finalNormal );\nvec3 viewPosition = normalize( vViewPosition );\n#if MAX_POINT_LIGHTS > 0\nvec3 pointDiffuse = vec3( 0.0 );\nvec3 pointSpecular = vec3( 0.0 );\nfor ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\nvec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );\nvec3 pointVector = lPosition.xyz + vViewPosition.xyz;\nfloat pointDistance = 1.0;\nif ( pointLightDistance[ i ] > 0.0 )\npointDistance = 1.0 - min( ( length( pointVector ) / pointLightDistance[ i ] ), 1.0 );\npointVector = normalize( pointVector );\n#ifdef WRAP_AROUND\nfloat pointDiffuseWeightFull = max( dot( normal, pointVector ), 0.0 );\nfloat pointDiffuseWeightHalf = max( 0.5 * dot( normal, pointVector ) + 0.5, 0.0 );\nvec3 pointDiffuseWeight = mix( vec3 ( pointDiffuseWeightFull ), vec3( pointDiffuseWeightHalf ), wrapRGB );\n#else\nfloat pointDiffuseWeight = max( dot( normal, pointVector ), 0.0 );\n#endif\npointDiffuse += pointDistance * pointLightColor[ i ] * uDiffuseColor * pointDiffuseWeight;\nvec3 pointHalfVector = normalize( pointVector + viewPosition );\nfloat pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );\nfloat pointSpecularWeight = specularTex.r * max( pow( pointDotNormalHalf, uShininess ), 0.0 );\n#ifdef PHYSICALLY_BASED_SHADING\nfloat specularNormalization = ( uShininess + 2.0001 ) / 8.0;\nvec3 schlick = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( pointVector, pointHalfVector ), 5.0 );\npointSpecular += schlick * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * pointDistance * specularNormalization;\n#else\npointSpecular += pointDistance * pointLightColor[ i ] * uSpecularColor * pointSpecularWeight * pointDiffuseWeight;\n#endif\n}\n#endif\n#if MAX_SPOT_LIGHTS > 0\nvec3 spotDiffuse = vec3( 0.0 );\nvec3 spotSpecular = vec3( 0.0 );\nfor ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\nvec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );\nvec3 spotVector = lPosition.xyz + vViewPosition.xyz;\nfloat spotDistance = 1.0;\nif ( spotLightDistance[ i ] > 0.0 )\nspotDistance = 1.0 - min( ( length( spotVector ) / spotLightDistance[ i ] ), 1.0 );\nspotVector = normalize( spotVector );\nfloat spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - vWorldPosition ) );\nif ( spotEffect > spotLightAngleCos[ i ] ) {\nspotEffect = max( pow( spotEffect, spotLightExponent[ i ] ), 0.0 );\n#ifdef WRAP_AROUND\nfloat spotDiffuseWeightFull = max( dot( normal, spotVector ), 0.0 );\nfloat spotDiffuseWeightHalf = max( 0.5 * dot( normal, spotVector ) + 0.5, 0.0 );\nvec3 spotDiffuseWeight = mix( vec3 ( spotDiffuseWeightFull ), vec3( spotDiffuseWeightHalf ), wrapRGB );\n#else\nfloat spotDiffuseWeight = max( dot( normal, spotVector ), 0.0 );\n#endif\nspotDiffuse += spotDistance * spotLightColor[ i ] * uDiffuseColor * spotDiffuseWeight * spotEffect;\nvec3 spotHalfVector = normalize( spotVector + viewPosition );\nfloat spotDotNormalHalf = max( dot( normal, spotHalfVector ), 0.0 );\nfloat spotSpecularWeight = specularTex.r * max( pow( spotDotNormalHalf, uShininess ), 0.0 );\n#ifdef PHYSICALLY_BASED_SHADING\nfloat specularNormalization = ( uShininess + 2.0001 ) / 8.0;\nvec3 schlick = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( spotVector, spotHalfVector ), 5.0 );\nspotSpecular += schlick * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * spotDistance * specularNormalization * spotEffect;\n#else\nspotSpecular += spotDistance * spotLightColor[ i ] * uSpecularColor * spotSpecularWeight * spotDiffuseWeight * spotEffect;\n#endif\n}\n}\n#endif\n#if MAX_DIR_LIGHTS > 0\nvec3 dirDiffuse = vec3( 0.0 );\nvec3 dirSpecular = vec3( 0.0 );\nfor( int i = 0; i < MAX_DIR_LIGHTS; i++ ) {\nvec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );\nvec3 dirVector = normalize( lDirection.xyz );\n#ifdef WRAP_AROUND\nfloat directionalLightWeightingFull = max( dot( normal, dirVector ), 0.0 );\nfloat directionalLightWeightingHalf = max( 0.5 * dot( normal, dirVector ) + 0.5, 0.0 );\nvec3 dirDiffuseWeight = mix( vec3( directionalLightWeightingFull ), vec3( directionalLightWeightingHalf ), wrapRGB );\n#else\nfloat dirDiffuseWeight = max( dot( normal, dirVector ), 0.0 );\n#endif\ndirDiffuse += directionalLightColor[ i ] * uDiffuseColor * dirDiffuseWeight;\nvec3 dirHalfVector = normalize( dirVector + viewPosition );\nfloat dirDotNormalHalf = max( dot( normal, dirHalfVector ), 0.0 );\nfloat dirSpecularWeight = specularTex.r * max( pow( dirDotNormalHalf, uShininess ), 0.0 );\n#ifdef PHYSICALLY_BASED_SHADING\nfloat specularNormalization = ( uShininess + 2.0001 ) / 8.0;\nvec3 schlick = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( dirVector, dirHalfVector ), 5.0 );\ndirSpecular += schlick * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization;\n#else\ndirSpecular += directionalLightColor[ i ] * uSpecularColor * dirSpecularWeight * dirDiffuseWeight;\n#endif\n}\n#endif\n#if MAX_HEMI_LIGHTS > 0\nvec3 hemiDiffuse  = vec3( 0.0 );\nvec3 hemiSpecular = vec3( 0.0 );\nfor( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\nvec4 lDirection = viewMatrix * vec4( hemisphereLightDirection[ i ], 0.0 );\nvec3 lVector = normalize( lDirection.xyz );\nfloat dotProduct = dot( normal, lVector );\nfloat hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\nvec3 hemiColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\nhemiDiffuse += uDiffuseColor * hemiColor;\nvec3 hemiHalfVectorSky = normalize( lVector + viewPosition );\nfloat hemiDotNormalHalfSky = 0.5 * dot( normal, hemiHalfVectorSky ) + 0.5;\nfloat hemiSpecularWeightSky = specularTex.r * max( pow( hemiDotNormalHalfSky, uShininess ), 0.0 );\nvec3 lVectorGround = -lVector;\nvec3 hemiHalfVectorGround = normalize( lVectorGround + viewPosition );\nfloat hemiDotNormalHalfGround = 0.5 * dot( normal, hemiHalfVectorGround ) + 0.5;\nfloat hemiSpecularWeightGround = specularTex.r * max( pow( hemiDotNormalHalfGround, uShininess ), 0.0 );\n#ifdef PHYSICALLY_BASED_SHADING\nfloat dotProductGround = dot( normal, lVectorGround );\nfloat specularNormalization = ( uShininess + 2.0001 ) / 8.0;\nvec3 schlickSky = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( lVector, hemiHalfVectorSky ), 5.0 );\nvec3 schlickGround = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( lVectorGround, hemiHalfVectorGround ), 5.0 );\nhemiSpecular += hemiColor * specularNormalization * ( schlickSky * hemiSpecularWeightSky * max( dotProduct, 0.0 ) + schlickGround * hemiSpecularWeightGround * max( dotProductGround, 0.0 ) );\n#else\nhemiSpecular += uSpecularColor * hemiColor * ( hemiSpecularWeightSky + hemiSpecularWeightGround ) * hemiDiffuseWeight;\n#endif\n}\n#endif\nvec3 totalDiffuse = vec3( 0.0 );\nvec3 totalSpecular = vec3( 0.0 );\n#if MAX_DIR_LIGHTS > 0\ntotalDiffuse += dirDiffuse;\ntotalSpecular += dirSpecular;\n#endif\n#if MAX_HEMI_LIGHTS > 0\ntotalDiffuse += hemiDiffuse;\ntotalSpecular += hemiSpecular;\n#endif\n#if MAX_POINT_LIGHTS > 0\ntotalDiffuse += pointDiffuse;\ntotalSpecular += pointSpecular;\n#endif\n#if MAX_SPOT_LIGHTS > 0\ntotalDiffuse += spotDiffuse;\ntotalSpecular += spotSpecular;\n#endif\n#ifdef METAL\ngl_FragColor.xyz = gl_FragColor.xyz * ( totalDiffuse + ambientLightColor * uAmbientColor + totalSpecular );\n#else\ngl_FragColor.xyz = gl_FragColor.xyz * ( totalDiffuse + ambientLightColor * uAmbientColor ) + totalSpecular;\n#endif\nif ( enableReflection ) {\nvec3 vReflect;\nvec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\nif ( useRefract ) {\nvReflect = refract( cameraToVertex, normal, uRefractionRatio );\n} else {\nvReflect = reflect( cameraToVertex, normal );\n}\nvec4 cubeColor = textureCube( tCube, vec3( -vReflect.x, vReflect.yz ) );\n#ifdef GAMMA_INPUT\ncubeColor.xyz *= cubeColor.xyz;\n#endif\ngl_FragColor.xyz = mix( gl_FragColor.xyz, cubeColor.xyz, specularTex.r * uReflectivity );\n}",
+THREE.ShaderChunk.shadowmap_fragment,THREE.ShaderChunk.linear_to_gamma_fragment,THREE.ShaderChunk.fog_fragment,"}"].join("\n"),vertexShader:["attribute vec4 tangent;\nuniform vec2 uOffset;\nuniform vec2 uRepeat;\nuniform bool enableDisplacement;\n#ifdef VERTEX_TEXTURES\nuniform sampler2D tDisplacement;\nuniform float uDisplacementScale;\nuniform float uDisplacementBias;\n#endif\nvarying vec3 vTangent;\nvarying vec3 vBinormal;\nvarying vec3 vNormal;\nvarying vec2 vUv;\nvarying vec3 vWorldPosition;\nvarying vec3 vViewPosition;",
+THREE.ShaderChunk.skinning_pars_vertex,THREE.ShaderChunk.shadowmap_pars_vertex,"void main() {",THREE.ShaderChunk.skinbase_vertex,THREE.ShaderChunk.skinnormal_vertex,"#ifdef USE_SKINNING\nvNormal = normalize( normalMatrix * skinnedNormal.xyz );\nvec4 skinnedTangent = skinMatrix * vec4( tangent.xyz, 0.0 );\nvTangent = normalize( normalMatrix * skinnedTangent.xyz );\n#else\nvNormal = normalize( normalMatrix * normal );\nvTangent = normalize( normalMatrix * tangent.xyz );\n#endif\nvBinormal = normalize( cross( vNormal, vTangent ) * tangent.w );\nvUv = uv * uRepeat + uOffset;\nvec3 displacedPosition;\n#ifdef VERTEX_TEXTURES\nif ( enableDisplacement ) {\nvec3 dv = texture2D( tDisplacement, uv ).xyz;\nfloat df = uDisplacementScale * dv.x + uDisplacementBias;\ndisplacedPosition = position + normalize( normal ) * df;\n} else {\n#ifdef USE_SKINNING\nvec4 skinVertex = vec4( position, 1.0 );\nvec4 skinned  = boneMatX * skinVertex * skinWeight.x;\nskinned \t  += boneMatY * skinVertex * skinWeight.y;\ndisplacedPosition  = skinned.xyz;\n#else\ndisplacedPosition = position;\n#endif\n}\n#else\n#ifdef USE_SKINNING\nvec4 skinVertex = vec4( position, 1.0 );\nvec4 skinned  = boneMatX * skinVertex * skinWeight.x;\nskinned \t  += boneMatY * skinVertex * skinWeight.y;\ndisplacedPosition  = skinned.xyz;\n#else\ndisplacedPosition = position;\n#endif\n#endif\nvec4 mvPosition = modelViewMatrix * vec4( displacedPosition, 1.0 );\nvec4 worldPosition = modelMatrix * vec4( displacedPosition, 1.0 );\ngl_Position = projectionMatrix * mvPosition;\nvWorldPosition = worldPosition.xyz;\nvViewPosition = -mvPosition.xyz;\n#ifdef USE_SHADOWMAP\nfor( int i = 0; i < MAX_SHADOWS; i ++ ) {\nvShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;\n}\n#endif\n}"].join("\n")},
+cube:{uniforms:{tCube:{type:"t",value:null},tFlip:{type:"f",value:-1}},vertexShader:"varying vec3 vWorldPosition;\nvoid main() {\nvec4 worldPosition = modelMatrix * vec4( position, 1.0 );\nvWorldPosition = worldPosition.xyz;\ngl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}",fragmentShader:"uniform samplerCube tCube;\nuniform float tFlip;\nvarying vec3 vWorldPosition;\nvoid main() {\ngl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );\n}"},
+depthRGBA:{uniforms:{},vertexShader:[THREE.ShaderChunk.morphtarget_pars_vertex,THREE.ShaderChunk.skinning_pars_vertex,"void main() {",THREE.ShaderChunk.skinbase_vertex,THREE.ShaderChunk.morphtarget_vertex,THREE.ShaderChunk.skinning_vertex,THREE.ShaderChunk.default_vertex,"}"].join("\n"),fragmentShader:"vec4 pack_depth( const in float depth ) {\nconst vec4 bit_shift = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 );\nconst vec4 bit_mask  = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 );\nvec4 res = fract( depth * bit_shift );\nres -= res.xxyz * bit_mask;\nreturn res;\n}\nvoid main() {\ngl_FragData[ 0 ] = pack_depth( gl_FragCoord.z );\n}"}};THREE.WebGLRenderer=function(a){function b(a,b){var c=a.vertices.length,d=b.material;if(d.attributes){void 0===a.__webglCustomAttributesList&&(a.__webglCustomAttributesList=[]);for(var e in d.attributes){var f=d.attributes[e];if(!f.__webglInitialized||f.createUniqueBuffers){f.__webglInitialized=!0;var h=1;"v2"===f.type?h=2:"v3"===f.type?h=3:"v4"===f.type?h=4:"c"===f.type&&(h=3);f.size=h;f.array=new Float32Array(c*h);f.buffer=j.createBuffer();f.buffer.belongsToAttribute=e;f.needsUpdate=!0}a.__webglCustomAttributesList.push(f)}}}
+function c(a,b){var c=b.geometry,h=a.faces3,g=3*h.length,i=1*h.length,k=3*h.length,h=d(b,a),m=f(h),l=e(h),p=h.vertexColors?h.vertexColors:!1;a.__vertexArray=new Float32Array(3*g);l&&(a.__normalArray=new Float32Array(3*g));c.hasTangents&&(a.__tangentArray=new Float32Array(4*g));p&&(a.__colorArray=new Float32Array(3*g));m&&(0<c.faceVertexUvs.length&&(a.__uvArray=new Float32Array(2*g)),1<c.faceVertexUvs.length&&(a.__uv2Array=new Float32Array(2*g)));b.geometry.skinWeights.length&&b.geometry.skinIndices.length&&
+(a.__skinIndexArray=new Float32Array(4*g),a.__skinWeightArray=new Float32Array(4*g));a.__faceArray=new Uint16Array(3*i);a.__lineArray=new Uint16Array(2*k);if(a.numMorphTargets){a.__morphTargetsArrays=[];c=0;for(m=a.numMorphTargets;c<m;c++)a.__morphTargetsArrays.push(new Float32Array(3*g))}if(a.numMorphNormals){a.__morphNormalsArrays=[];c=0;for(m=a.numMorphNormals;c<m;c++)a.__morphNormalsArrays.push(new Float32Array(3*g))}a.__webglFaceCount=3*i;a.__webglLineCount=2*k;if(h.attributes){void 0===a.__webglCustomAttributesList&&
+(a.__webglCustomAttributesList=[]);for(var n in h.attributes){var i=h.attributes[n],k={},r;for(r in i)k[r]=i[r];if(!k.__webglInitialized||k.createUniqueBuffers)k.__webglInitialized=!0,c=1,"v2"===k.type?c=2:"v3"===k.type?c=3:"v4"===k.type?c=4:"c"===k.type&&(c=3),k.size=c,k.array=new Float32Array(g*c),k.buffer=j.createBuffer(),k.buffer.belongsToAttribute=n,i.needsUpdate=!0,k.__original=i;a.__webglCustomAttributesList.push(k)}}a.__inittedArrays=!0}function d(a,b){return a.material instanceof THREE.MeshFaceMaterial?
+a.material.materials[b.materialIndex]:a.material}function e(a){return a instanceof THREE.MeshBasicMaterial&&!a.envMap||a instanceof THREE.MeshDepthMaterial?!1:a&&void 0!==a.shading&&a.shading===THREE.SmoothShading?THREE.SmoothShading:THREE.FlatShading}function f(a){return a.map||a.lightMap||a.bumpMap||a.normalMap||a.specularMap||a instanceof THREE.ShaderMaterial?!0:!1}function h(a){Ha[a]||(j.enableVertexAttribArray(a),Ha[a]=!0)}function g(){for(var a in Ha)Ha[a]&&(j.disableVertexAttribArray(a),Ha[a]=
+!1)}function i(a,b){return a.z!==b.z?b.z-a.z:a.id-b.id}function k(a,b){return b[0]-a[0]}function m(a,b,c){if(a.length)for(var d=0,e=a.length;d<e;d++)ea=Ba=null,Aa=$=U=Z=fa=Ga=ka=-1,ua=!0,a[d].render(b,c,sb,pb),ea=Ba=null,Aa=$=U=Z=fa=Ga=ka=-1,ua=!0}function l(a,b,c,d,e,f,h,g){var j,i,k,m;b?(i=a.length-1,m=b=-1):(i=0,b=a.length,m=1);for(var l=i;l!==b;l+=m)if(j=a[l],j.render){i=j.object;k=j.buffer;if(g)j=g;else{j=j[c];if(!j)continue;h&&K.setBlending(j.blending,j.blendEquation,j.blendSrc,j.blendDst);
+K.setDepthTest(j.depthTest);K.setDepthWrite(j.depthWrite);A(j.polygonOffset,j.polygonOffsetFactor,j.polygonOffsetUnits)}K.setMaterialFaces(j);k instanceof THREE.BufferGeometry?K.renderBufferDirect(d,e,f,j,k,i):K.renderBuffer(d,e,f,j,k,i)}}function p(a,b,c,d,e,f,h){for(var g,j,i=0,k=a.length;i<k;i++)if(g=a[i],j=g.object,j.visible){if(h)g=h;else{g=g[b];if(!g)continue;f&&K.setBlending(g.blending,g.blendEquation,g.blendSrc,g.blendDst);K.setDepthTest(g.depthTest);K.setDepthWrite(g.depthWrite);A(g.polygonOffset,
+g.polygonOffsetFactor,g.polygonOffsetUnits)}K.renderImmediateObject(c,d,e,g,j)}}function s(a,d){var e,f,h,g;if(void 0===a.__webglInit&&(a.__webglInit=!0,a._modelViewMatrix=new THREE.Matrix4,a._normalMatrix=new THREE.Matrix3,void 0!==a.geometry&&void 0===a.geometry.__webglInit&&(a.geometry.__webglInit=!0,a.geometry.addEventListener("dispose",Cb)),f=a.geometry,void 0!==f))if(f instanceof THREE.BufferGeometry){var i,k;for(i in f.attributes)k="index"===i?j.ELEMENT_ARRAY_BUFFER:j.ARRAY_BUFFER,g=f.attributes[i],
+void 0===g.numItems&&(g.numItems=g.array.length),g.buffer=j.createBuffer(),j.bindBuffer(k,g.buffer),j.bufferData(k,g.array,j.STATIC_DRAW)}else if(a instanceof THREE.Mesh){h=a.material;if(void 0===f.geometryGroups){i=f;var m,l,p;k={};var n=i.morphTargets.length,r=i.morphNormals.length,s=h instanceof THREE.MeshFaceMaterial;i.geometryGroups={};h=0;for(m=i.faces.length;h<m;h++)l=i.faces[h],l=s?l.materialIndex:0,void 0===k[l]&&(k[l]={hash:l,counter:0}),p=k[l].hash+"_"+k[l].counter,void 0===i.geometryGroups[p]&&
+(i.geometryGroups[p]={faces3:[],materialIndex:l,vertices:0,numMorphTargets:n,numMorphNormals:r}),65535<i.geometryGroups[p].vertices+3&&(k[l].counter+=1,p=k[l].hash+"_"+k[l].counter,void 0===i.geometryGroups[p]&&(i.geometryGroups[p]={faces3:[],materialIndex:l,vertices:0,numMorphTargets:n,numMorphNormals:r})),i.geometryGroups[p].faces3.push(h),i.geometryGroups[p].vertices+=3;i.geometryGroupsList=[];for(g in i.geometryGroups)i.geometryGroups[g].id=V++,i.geometryGroupsList.push(i.geometryGroups[g])}for(e in f.geometryGroups)if(g=
+f.geometryGroups[e],!g.__webglVertexBuffer){i=g;i.__webglVertexBuffer=j.createBuffer();i.__webglNormalBuffer=j.createBuffer();i.__webglTangentBuffer=j.createBuffer();i.__webglColorBuffer=j.createBuffer();i.__webglUVBuffer=j.createBuffer();i.__webglUV2Buffer=j.createBuffer();i.__webglSkinIndicesBuffer=j.createBuffer();i.__webglSkinWeightsBuffer=j.createBuffer();i.__webglFaceBuffer=j.createBuffer();i.__webglLineBuffer=j.createBuffer();n=k=void 0;if(i.numMorphTargets){i.__webglMorphTargetsBuffers=[];
+k=0;for(n=i.numMorphTargets;k<n;k++)i.__webglMorphTargetsBuffers.push(j.createBuffer())}if(i.numMorphNormals){i.__webglMorphNormalsBuffers=[];k=0;for(n=i.numMorphNormals;k<n;k++)i.__webglMorphNormalsBuffers.push(j.createBuffer())}K.info.memory.geometries++;c(g,a);f.verticesNeedUpdate=!0;f.morphTargetsNeedUpdate=!0;f.elementsNeedUpdate=!0;f.uvsNeedUpdate=!0;f.normalsNeedUpdate=!0;f.tangentsNeedUpdate=!0;f.colorsNeedUpdate=!0}}else a instanceof THREE.Line?f.__webglVertexBuffer||(g=f,g.__webglVertexBuffer=
+j.createBuffer(),g.__webglColorBuffer=j.createBuffer(),g.__webglLineDistanceBuffer=j.createBuffer(),K.info.memory.geometries++,g=f,i=g.vertices.length,g.__vertexArray=new Float32Array(3*i),g.__colorArray=new Float32Array(3*i),g.__lineDistanceArray=new Float32Array(1*i),g.__webglLineCount=i,b(g,a),f.verticesNeedUpdate=!0,f.colorsNeedUpdate=!0,f.lineDistancesNeedUpdate=!0):a instanceof THREE.ParticleSystem&&!f.__webglVertexBuffer&&(g=f,g.__webglVertexBuffer=j.createBuffer(),g.__webglColorBuffer=j.createBuffer(),
+K.info.memory.geometries++,g=f,i=g.vertices.length,g.__vertexArray=new Float32Array(3*i),g.__colorArray=new Float32Array(3*i),g.__sortArray=[],g.__webglParticleCount=i,b(g,a),f.verticesNeedUpdate=!0,f.colorsNeedUpdate=!0);if(void 0===a.__webglActive){if(a instanceof THREE.Mesh)if(f=a.geometry,f instanceof THREE.BufferGeometry)t(d.__webglObjects,f,a);else{if(f instanceof THREE.Geometry)for(e in f.geometryGroups)g=f.geometryGroups[e],t(d.__webglObjects,g,a)}else a instanceof THREE.Line||a instanceof
+THREE.ParticleSystem?(f=a.geometry,t(d.__webglObjects,f,a)):a instanceof THREE.ImmediateRenderObject||a.immediateRenderCallback?d.__webglObjectsImmediate.push({id:null,object:a,opaque:null,transparent:null,z:0}):a instanceof THREE.Sprite?d.__webglSprites.push(a):a instanceof THREE.LensFlare&&d.__webglFlares.push(a);a.__webglActive=!0}}function t(a,b,c){a.push({id:null,buffer:b,object:c,opaque:null,transparent:null,z:0})}function n(a){for(var b in a.attributes)if(a.attributes[b].needsUpdate)return!0;
+return!1}function r(a){for(var b in a.attributes)a.attributes[b].needsUpdate=!1}function q(a,b){a instanceof THREE.Mesh||a instanceof THREE.ParticleSystem||a instanceof THREE.Line?u(b.__webglObjects,a):a instanceof THREE.Sprite?w(b.__webglSprites,a):a instanceof THREE.LensFlare?w(b.__webglFlares,a):(a instanceof THREE.ImmediateRenderObject||a.immediateRenderCallback)&&u(b.__webglObjectsImmediate,a);delete a.__webglActive}function u(a,b){for(var c=a.length-1;0<=c;c--)a[c].object===b&&a.splice(c,1)}
+function w(a,b){for(var c=a.length-1;0<=c;c--)a[c]===b&&a.splice(c,1)}function z(a,b,c,d,e){P=0;d.needsUpdate&&(d.program&&Gb(d),K.initMaterial(d,b,c,e),d.needsUpdate=!1);d.morphTargets&&!e.__webglMorphTargetInfluences&&(e.__webglMorphTargetInfluences=new Float32Array(K.maxMorphTargets));var f=!1,g=d.program,h=g.uniforms,i=d.uniforms;g!==Ba&&(j.useProgram(g),Ba=g,f=!0);d.id!==Aa&&(Aa=d.id,f=!0);if(f||a!==ea)j.uniformMatrix4fv(h.projectionMatrix,!1,a.projectionMatrix.elements),a!==ea&&(ea=a);if(d.skinning)if(yb&&
+e.useVertexTexture){if(null!==h.boneTexture){var k=B();j.uniform1i(h.boneTexture,k);K.setTexture(e.boneTexture,k)}null!==h.boneTextureWidth&&j.uniform1i(h.boneTextureWidth,e.boneTextureWidth);null!==h.boneTextureHeight&&j.uniform1i(h.boneTextureHeight,e.boneTextureHeight)}else null!==h.boneGlobalMatrices&&j.uniformMatrix4fv(h.boneGlobalMatrices,!1,e.boneMatrices);if(f){c&&d.fog&&(i.fogColor.value=c.color,c instanceof THREE.Fog?(i.fogNear.value=c.near,i.fogFar.value=c.far):c instanceof THREE.FogExp2&&
+(i.fogDensity.value=c.density));if(d instanceof THREE.MeshPhongMaterial||d instanceof THREE.MeshLambertMaterial||d.lights){if(ua){for(var m,l=k=0,p=0,n,r,s,q=Pa,t=q.directional.colors,u=q.directional.positions,w=q.point.colors,z=q.point.positions,y=q.point.distances,A=q.spot.colors,C=q.spot.positions,F=q.spot.distances,J=q.spot.directions,N=q.spot.anglesCos,O=q.spot.exponents,I=q.hemi.skyColors,V=q.hemi.groundColors,R=q.hemi.positions,M=0,U=0,da=0,Z=0,Fa=0,dc=0,X=0,W=0,Q=m=0,c=s=Q=0,f=b.length;c<
+f;c++)m=b[c],m.onlyShadow||(n=m.color,r=m.intensity,s=m.distance,m instanceof THREE.AmbientLight?m.visible&&(K.gammaInput?(k+=n.r*n.r,l+=n.g*n.g,p+=n.b*n.b):(k+=n.r,l+=n.g,p+=n.b)):m instanceof THREE.DirectionalLight?(Fa+=1,m.visible&&(ga.getPositionFromMatrix(m.matrixWorld),sa.getPositionFromMatrix(m.target.matrixWorld),ga.sub(sa),ga.normalize(),0===ga.x&&0===ga.y&&0===ga.z||(m=3*M,u[m]=ga.x,u[m+1]=ga.y,u[m+2]=ga.z,K.gammaInput?D(t,m,n,r*r):x(t,m,n,r),M+=1))):m instanceof THREE.PointLight?(dc+=1,
+m.visible&&(Q=3*U,K.gammaInput?D(w,Q,n,r*r):x(w,Q,n,r),sa.getPositionFromMatrix(m.matrixWorld),z[Q]=sa.x,z[Q+1]=sa.y,z[Q+2]=sa.z,y[U]=s,U+=1)):m instanceof THREE.SpotLight?(X+=1,m.visible&&(Q=3*da,K.gammaInput?D(A,Q,n,r*r):x(A,Q,n,r),sa.getPositionFromMatrix(m.matrixWorld),C[Q]=sa.x,C[Q+1]=sa.y,C[Q+2]=sa.z,F[da]=s,ga.copy(sa),sa.getPositionFromMatrix(m.target.matrixWorld),ga.sub(sa),ga.normalize(),J[Q]=ga.x,J[Q+1]=ga.y,J[Q+2]=ga.z,N[da]=Math.cos(m.angle),O[da]=m.exponent,da+=1)):m instanceof THREE.HemisphereLight&&
+(W+=1,m.visible&&(ga.getPositionFromMatrix(m.matrixWorld),ga.normalize(),0===ga.x&&0===ga.y&&0===ga.z||(s=3*Z,R[s]=ga.x,R[s+1]=ga.y,R[s+2]=ga.z,n=m.color,m=m.groundColor,K.gammaInput?(r*=r,D(I,s,n,r),D(V,s,m,r)):(x(I,s,n,r),x(V,s,m,r)),Z+=1))));c=3*M;for(f=Math.max(t.length,3*Fa);c<f;c++)t[c]=0;c=3*U;for(f=Math.max(w.length,3*dc);c<f;c++)w[c]=0;c=3*da;for(f=Math.max(A.length,3*X);c<f;c++)A[c]=0;c=3*Z;for(f=Math.max(I.length,3*W);c<f;c++)I[c]=0;c=3*Z;for(f=Math.max(V.length,3*W);c<f;c++)V[c]=0;q.directional.length=
+M;q.point.length=U;q.spot.length=da;q.hemi.length=Z;q.ambient[0]=k;q.ambient[1]=l;q.ambient[2]=p;ua=!1}c=Pa;i.ambientLightColor.value=c.ambient;i.directionalLightColor.value=c.directional.colors;i.directionalLightDirection.value=c.directional.positions;i.pointLightColor.value=c.point.colors;i.pointLightPosition.value=c.point.positions;i.pointLightDistance.value=c.point.distances;i.spotLightColor.value=c.spot.colors;i.spotLightPosition.value=c.spot.positions;i.spotLightDistance.value=c.spot.distances;
+i.spotLightDirection.value=c.spot.directions;i.spotLightAngleCos.value=c.spot.anglesCos;i.spotLightExponent.value=c.spot.exponents;i.hemisphereLightSkyColor.value=c.hemi.skyColors;i.hemisphereLightGroundColor.value=c.hemi.groundColors;i.hemisphereLightDirection.value=c.hemi.positions}if(d instanceof THREE.MeshBasicMaterial||d instanceof THREE.MeshLambertMaterial||d instanceof THREE.MeshPhongMaterial){i.opacity.value=d.opacity;K.gammaInput?i.diffuse.value.copyGammaToLinear(d.color):i.diffuse.value=
+d.color;i.map.value=d.map;i.lightMap.value=d.lightMap;i.specularMap.value=d.specularMap;d.bumpMap&&(i.bumpMap.value=d.bumpMap,i.bumpScale.value=d.bumpScale);d.normalMap&&(i.normalMap.value=d.normalMap,i.normalScale.value.copy(d.normalScale));var $;d.map?$=d.map:d.specularMap?$=d.specularMap:d.normalMap?$=d.normalMap:d.bumpMap&&($=d.bumpMap);void 0!==$&&(c=$.offset,$=$.repeat,i.offsetRepeat.value.set(c.x,c.y,$.x,$.y));i.envMap.value=d.envMap;i.flipEnvMap.value=d.envMap instanceof THREE.WebGLRenderTargetCube?
+1:-1;i.reflectivity.value=d.reflectivity;i.refractionRatio.value=d.refractionRatio;i.combine.value=d.combine;i.useRefract.value=d.envMap&&d.envMap.mapping instanceof THREE.CubeRefractionMapping}d instanceof THREE.LineBasicMaterial?(i.diffuse.value=d.color,i.opacity.value=d.opacity):d instanceof THREE.LineDashedMaterial?(i.diffuse.value=d.color,i.opacity.value=d.opacity,i.dashSize.value=d.dashSize,i.totalSize.value=d.dashSize+d.gapSize,i.scale.value=d.scale):d instanceof THREE.ParticleSystemMaterial?
+(i.psColor.value=d.color,i.opacity.value=d.opacity,i.size.value=d.size,i.scale.value=G.height/2,i.map.value=d.map):d instanceof THREE.MeshPhongMaterial?(i.shininess.value=d.shininess,K.gammaInput?(i.ambient.value.copyGammaToLinear(d.ambient),i.emissive.value.copyGammaToLinear(d.emissive),i.specular.value.copyGammaToLinear(d.specular)):(i.ambient.value=d.ambient,i.emissive.value=d.emissive,i.specular.value=d.specular),d.wrapAround&&i.wrapRGB.value.copy(d.wrapRGB)):d instanceof THREE.MeshLambertMaterial?
+(K.gammaInput?(i.ambient.value.copyGammaToLinear(d.ambient),i.emissive.value.copyGammaToLinear(d.emissive)):(i.ambient.value=d.ambient,i.emissive.value=d.emissive),d.wrapAround&&i.wrapRGB.value.copy(d.wrapRGB)):d instanceof THREE.MeshDepthMaterial?(i.mNear.value=a.near,i.mFar.value=a.far,i.opacity.value=d.opacity):d instanceof THREE.MeshNormalMaterial&&(i.opacity.value=d.opacity);if(e.receiveShadow&&!d._shadowPass&&i.shadowMatrix){c=$=0;for(f=b.length;c<f;c++)if(k=b[c],k.castShadow&&(k instanceof
+THREE.SpotLight||k instanceof THREE.DirectionalLight&&!k.shadowCascade))i.shadowMap.value[$]=k.shadowMap,i.shadowMapSize.value[$]=k.shadowMapSize,i.shadowMatrix.value[$]=k.shadowMatrix,i.shadowDarkness.value[$]=k.shadowDarkness,i.shadowBias.value[$]=k.shadowBias,$++}b=d.uniformsList;i=0;for($=b.length;i<$;i++)if(f=g.uniforms[b[i][1]])if(c=b[i][0],l=c.type,k=c.value,"i"===l)j.uniform1i(f,k);else if("f"===l)j.uniform1f(f,k);else if("v2"===l)j.uniform2f(f,k.x,k.y);else if("v3"===l)j.uniform3f(f,k.x,
+k.y,k.z);else if("v4"===l)j.uniform4f(f,k.x,k.y,k.z,k.w);else if("c"===l)j.uniform3f(f,k.r,k.g,k.b);else if("iv1"===l)j.uniform1iv(f,k);else if("iv"===l)j.uniform3iv(f,k);else if("fv1"===l)j.uniform1fv(f,k);else if("fv"===l)j.uniform3fv(f,k);else if("v2v"===l){void 0===c._array&&(c._array=new Float32Array(2*k.length));l=0;for(p=k.length;l<p;l++)q=2*l,c._array[q]=k[l].x,c._array[q+1]=k[l].y;j.uniform2fv(f,c._array)}else if("v3v"===l){void 0===c._array&&(c._array=new Float32Array(3*k.length));l=0;for(p=
+k.length;l<p;l++)q=3*l,c._array[q]=k[l].x,c._array[q+1]=k[l].y,c._array[q+2]=k[l].z;j.uniform3fv(f,c._array)}else if("v4v"===l){void 0===c._array&&(c._array=new Float32Array(4*k.length));l=0;for(p=k.length;l<p;l++)q=4*l,c._array[q]=k[l].x,c._array[q+1]=k[l].y,c._array[q+2]=k[l].z,c._array[q+3]=k[l].w;j.uniform4fv(f,c._array)}else if("m4"===l)void 0===c._array&&(c._array=new Float32Array(16)),k.flattenToArray(c._array),j.uniformMatrix4fv(f,!1,c._array);else if("m4v"===l){void 0===c._array&&(c._array=
+new Float32Array(16*k.length));l=0;for(p=k.length;l<p;l++)k[l].flattenToArrayOffset(c._array,16*l);j.uniformMatrix4fv(f,!1,c._array)}else if("t"===l){if(q=k,k=B(),j.uniform1i(f,k),q)if(q.image instanceof Array&&6===q.image.length){if(c=q,f=k,6===c.image.length)if(c.needsUpdate){c.image.__webglTextureCube||(c.addEventListener("dispose",Db),c.image.__webglTextureCube=j.createTexture(),K.info.memory.textures++);j.activeTexture(j.TEXTURE0+f);j.bindTexture(j.TEXTURE_CUBE_MAP,c.image.__webglTextureCube);
+j.pixelStorei(j.UNPACK_FLIP_Y_WEBGL,c.flipY);f=c instanceof THREE.CompressedTexture;k=[];for(l=0;6>l;l++)K.autoScaleCubemaps&&!f?(p=k,q=l,t=c.image[l],w=ac,t.width<=w&&t.height<=w||(z=Math.max(t.width,t.height),u=Math.floor(t.width*w/z),w=Math.floor(t.height*w/z),z=document.createElement("canvas"),z.width=u,z.height=w,z.getContext("2d").drawImage(t,0,0,t.width,t.height,0,0,u,w),t=z),p[q]=t):k[l]=c.image[l];l=k[0];p=0===(l.width&l.width-1)&&0===(l.height&l.height-1);q=v(c.format);t=v(c.type);E(j.TEXTURE_CUBE_MAP,
+c,p);for(l=0;6>l;l++)if(f){w=k[l].mipmaps;z=0;for(y=w.length;z<y;z++)u=w[z],c.format!==THREE.RGBAFormat?j.compressedTexImage2D(j.TEXTURE_CUBE_MAP_POSITIVE_X+l,z,q,u.width,u.height,0,u.data):j.texImage2D(j.TEXTURE_CUBE_MAP_POSITIVE_X+l,z,q,u.width,u.height,0,q,t,u.data)}else j.texImage2D(j.TEXTURE_CUBE_MAP_POSITIVE_X+l,0,q,q,t,k[l]);c.generateMipmaps&&p&&j.generateMipmap(j.TEXTURE_CUBE_MAP);c.needsUpdate=!1;if(c.onUpdate)c.onUpdate()}else j.activeTexture(j.TEXTURE0+f),j.bindTexture(j.TEXTURE_CUBE_MAP,
+c.image.__webglTextureCube)}else q instanceof THREE.WebGLRenderTargetCube?(c=q,j.activeTexture(j.TEXTURE0+k),j.bindTexture(j.TEXTURE_CUBE_MAP,c.__webglTexture)):K.setTexture(q,k)}else if("tv"===l){void 0===c._array&&(c._array=[]);l=0;for(p=c.value.length;l<p;l++)c._array[l]=B();j.uniform1iv(f,c._array);l=0;for(p=c.value.length;l<p;l++)q=c.value[l],k=c._array[l],q&&K.setTexture(q,k)}else console.warn("THREE.WebGLRenderer: Unknown uniform type: "+l);if((d instanceof THREE.ShaderMaterial||d instanceof
+THREE.MeshPhongMaterial||d.envMap)&&null!==h.cameraPosition)sa.getPositionFromMatrix(a.matrixWorld),j.uniform3f(h.cameraPosition,sa.x,sa.y,sa.z);(d instanceof THREE.MeshPhongMaterial||d instanceof THREE.MeshLambertMaterial||d instanceof THREE.ShaderMaterial||d.skinning)&&null!==h.viewMatrix&&j.uniformMatrix4fv(h.viewMatrix,!1,a.matrixWorldInverse.elements)}j.uniformMatrix4fv(h.modelViewMatrix,!1,e._modelViewMatrix.elements);h.normalMatrix&&j.uniformMatrix3fv(h.normalMatrix,!1,e._normalMatrix.elements);
+null!==h.modelMatrix&&j.uniformMatrix4fv(h.modelMatrix,!1,e.matrixWorld.elements);return g}function B(){var a=P;a>=Mb&&console.warn("WebGLRenderer: trying to use "+a+" texture units while this GPU supports only "+Mb);P+=1;return a}function D(a,b,c,d){a[b]=c.r*c.r*d;a[b+1]=c.g*c.g*d;a[b+2]=c.b*c.b*d}function x(a,b,c,d){a[b]=c.r*d;a[b+1]=c.g*d;a[b+2]=c.b*d}function F(a){a!==xa&&(j.lineWidth(a),xa=a)}function A(a,b,c){Da!==a&&(a?j.enable(j.POLYGON_OFFSET_FILL):j.disable(j.POLYGON_OFFSET_FILL),Da=a);
+if(a&&(Ua!==b||Qa!==c))j.polygonOffset(b,c),Ua=b,Qa=c}function O(a){for(var a=a.split("\n"),b=0,c=a.length;b<c;b++)a[b]=b+1+": "+a[b];return a.join("\n")}function C(a,b){var c;"fragment"===a?c=j.createShader(j.FRAGMENT_SHADER):"vertex"===a&&(c=j.createShader(j.VERTEX_SHADER));j.shaderSource(c,b);j.compileShader(c);return!j.getShaderParameter(c,j.COMPILE_STATUS)?(console.error(j.getShaderInfoLog(c)),console.error(O(b)),null):c}function E(a,b,c){c?(j.texParameteri(a,j.TEXTURE_WRAP_S,v(b.wrapS)),j.texParameteri(a,
+j.TEXTURE_WRAP_T,v(b.wrapT)),j.texParameteri(a,j.TEXTURE_MAG_FILTER,v(b.magFilter)),j.texParameteri(a,j.TEXTURE_MIN_FILTER,v(b.minFilter))):(j.texParameteri(a,j.TEXTURE_WRAP_S,j.CLAMP_TO_EDGE),j.texParameteri(a,j.TEXTURE_WRAP_T,j.CLAMP_TO_EDGE),j.texParameteri(a,j.TEXTURE_MAG_FILTER,y(b.magFilter)),j.texParameteri(a,j.TEXTURE_MIN_FILTER,y(b.minFilter)));if(wa&&b.type!==THREE.FloatType&&(1<b.anisotropy||b.__oldAnisotropy))j.texParameterf(a,wa.TEXTURE_MAX_ANISOTROPY_EXT,Math.min(b.anisotropy,Nb)),b.__oldAnisotropy=
+b.anisotropy}function I(a,b){j.bindRenderbuffer(j.RENDERBUFFER,a);b.depthBuffer&&!b.stencilBuffer?(j.renderbufferStorage(j.RENDERBUFFER,j.DEPTH_COMPONENT16,b.width,b.height),j.framebufferRenderbuffer(j.FRAMEBUFFER,j.DEPTH_ATTACHMENT,j.RENDERBUFFER,a)):b.depthBuffer&&b.stencilBuffer?(j.renderbufferStorage(j.RENDERBUFFER,j.DEPTH_STENCIL,b.width,b.height),j.framebufferRenderbuffer(j.FRAMEBUFFER,j.DEPTH_STENCIL_ATTACHMENT,j.RENDERBUFFER,a)):j.renderbufferStorage(j.RENDERBUFFER,j.RGBA4,b.width,b.height)}
+function y(a){return a===THREE.NearestFilter||a===THREE.NearestMipMapNearestFilter||a===THREE.NearestMipMapLinearFilter?j.NEAREST:j.LINEAR}function v(a){if(a===THREE.RepeatWrapping)return j.REPEAT;if(a===THREE.ClampToEdgeWrapping)return j.CLAMP_TO_EDGE;if(a===THREE.MirroredRepeatWrapping)return j.MIRRORED_REPEAT;if(a===THREE.NearestFilter)return j.NEAREST;if(a===THREE.NearestMipMapNearestFilter)return j.NEAREST_MIPMAP_NEAREST;if(a===THREE.NearestMipMapLinearFilter)return j.NEAREST_MIPMAP_LINEAR;if(a===
+THREE.LinearFilter)return j.LINEAR;if(a===THREE.LinearMipMapNearestFilter)return j.LINEAR_MIPMAP_NEAREST;if(a===THREE.LinearMipMapLinearFilter)return j.LINEAR_MIPMAP_LINEAR;if(a===THREE.UnsignedByteType)return j.UNSIGNED_BYTE;if(a===THREE.UnsignedShort4444Type)return j.UNSIGNED_SHORT_4_4_4_4;if(a===THREE.UnsignedShort5551Type)return j.UNSIGNED_SHORT_5_5_5_1;if(a===THREE.UnsignedShort565Type)return j.UNSIGNED_SHORT_5_6_5;if(a===THREE.ByteType)return j.BYTE;if(a===THREE.ShortType)return j.SHORT;if(a===
+THREE.UnsignedShortType)return j.UNSIGNED_SHORT;if(a===THREE.IntType)return j.INT;if(a===THREE.UnsignedIntType)return j.UNSIGNED_INT;if(a===THREE.FloatType)return j.FLOAT;if(a===THREE.AlphaFormat)return j.ALPHA;if(a===THREE.RGBFormat)return j.RGB;if(a===THREE.RGBAFormat)return j.RGBA;if(a===THREE.LuminanceFormat)return j.LUMINANCE;if(a===THREE.LuminanceAlphaFormat)return j.LUMINANCE_ALPHA;if(a===THREE.AddEquation)return j.FUNC_ADD;if(a===THREE.SubtractEquation)return j.FUNC_SUBTRACT;if(a===THREE.ReverseSubtractEquation)return j.FUNC_REVERSE_SUBTRACT;
+if(a===THREE.ZeroFactor)return j.ZERO;if(a===THREE.OneFactor)return j.ONE;if(a===THREE.SrcColorFactor)return j.SRC_COLOR;if(a===THREE.OneMinusSrcColorFactor)return j.ONE_MINUS_SRC_COLOR;if(a===THREE.SrcAlphaFactor)return j.SRC_ALPHA;if(a===THREE.OneMinusSrcAlphaFactor)return j.ONE_MINUS_SRC_ALPHA;if(a===THREE.DstAlphaFactor)return j.DST_ALPHA;if(a===THREE.OneMinusDstAlphaFactor)return j.ONE_MINUS_DST_ALPHA;if(a===THREE.DstColorFactor)return j.DST_COLOR;if(a===THREE.OneMinusDstColorFactor)return j.ONE_MINUS_DST_COLOR;
+if(a===THREE.SrcAlphaSaturateFactor)return j.SRC_ALPHA_SATURATE;if(void 0!==Ea){if(a===THREE.RGB_S3TC_DXT1_Format)return Ea.COMPRESSED_RGB_S3TC_DXT1_EXT;if(a===THREE.RGBA_S3TC_DXT1_Format)return Ea.COMPRESSED_RGBA_S3TC_DXT1_EXT;if(a===THREE.RGBA_S3TC_DXT3_Format)return Ea.COMPRESSED_RGBA_S3TC_DXT3_EXT;if(a===THREE.RGBA_S3TC_DXT5_Format)return Ea.COMPRESSED_RGBA_S3TC_DXT5_EXT}return 0}console.log("THREE.WebGLRenderer",THREE.REVISION);var a=a||{},G=void 0!==a.canvas?a.canvas:document.createElement("canvas"),
+R=void 0!==a.precision?a.precision:"highp",J=void 0!==a.alpha?a.alpha:!0,ba=void 0!==a.premultipliedAlpha?a.premultipliedAlpha:!0,oa=void 0!==a.antialias?a.antialias:!1,pa=void 0!==a.stencil?a.stencil:!0,N=void 0!==a.preserveDrawingBuffer?a.preserveDrawingBuffer:!1,M=new THREE.Color(0),Q=0;this.domElement=G;this.context=null;this.devicePixelRatio=void 0!==a.devicePixelRatio?a.devicePixelRatio:void 0!==self.devicePixelRatio?self.devicePixelRatio:1;this.autoUpdateObjects=this.sortObjects=this.autoClearStencil=
+this.autoClearDepth=this.autoClearColor=this.autoClear=!0;this.shadowMapEnabled=this.physicallyBasedShading=this.gammaOutput=this.gammaInput=!1;this.shadowMapAutoUpdate=!0;this.shadowMapType=THREE.PCFShadowMap;this.shadowMapCullFace=THREE.CullFaceFront;this.shadowMapCascade=this.shadowMapDebug=!1;this.maxMorphTargets=8;this.maxMorphNormals=4;this.autoScaleCubemaps=!0;this.renderPluginsPre=[];this.renderPluginsPost=[];this.info={memory:{programs:0,geometries:0,textures:0},render:{calls:0,vertices:0,
+faces:0,points:0}};var K=this,ca=[],Fa=0,Ba=null,da=null,Aa=-1,$=null,ea=null,V=0,P=0,Z=-1,U=-1,ka=-1,ta=-1,ia=-1,La=-1,Ga=-1,fa=-1,Da=null,Ua=null,Qa=null,xa=null,bb=0,cb=0,Ma=G.width,fb=G.height,sb=0,pb=0,Ha={},la=new THREE.Frustum,ra=new THREE.Matrix4,gb=new THREE.Matrix4,sa=new THREE.Vector3,ga=new THREE.Vector3,ua=!0,Pa={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:[]}},j,Oa,va,wa,Ea;try{var Ra={alpha:J,premultipliedAlpha:ba,antialias:oa,stencil:pa,preserveDrawingBuffer:N};j=G.getContext("webgl",Ra)||G.getContext("experimental-webgl",Ra);if(null===j)throw"Error creating WebGL context.";}catch(Zb){console.error(Zb)}Oa=j.getExtension("OES_texture_float");j.getExtension("OES_texture_float_linear");va=j.getExtension("OES_standard_derivatives");wa=j.getExtension("EXT_texture_filter_anisotropic")||
+j.getExtension("MOZ_EXT_texture_filter_anisotropic")||j.getExtension("WEBKIT_EXT_texture_filter_anisotropic");Ea=j.getExtension("WEBGL_compressed_texture_s3tc")||j.getExtension("MOZ_WEBGL_compressed_texture_s3tc")||j.getExtension("WEBKIT_WEBGL_compressed_texture_s3tc");Oa||console.log("THREE.WebGLRenderer: Float textures not supported.");va||console.log("THREE.WebGLRenderer: Standard derivatives not supported.");wa||console.log("THREE.WebGLRenderer: Anisotropic texture filtering not supported.");
+Ea||console.log("THREE.WebGLRenderer: S3TC compressed textures not supported.");void 0===j.getShaderPrecisionFormat&&(j.getShaderPrecisionFormat=function(){return{rangeMin:1,rangeMax:1,precision:1}});j.clearColor(0,0,0,1);j.clearDepth(1);j.clearStencil(0);j.enable(j.DEPTH_TEST);j.depthFunc(j.LEQUAL);j.frontFace(j.CCW);j.cullFace(j.BACK);j.enable(j.CULL_FACE);j.enable(j.BLEND);j.blendEquation(j.FUNC_ADD);j.blendFunc(j.SRC_ALPHA,j.ONE_MINUS_SRC_ALPHA);j.viewport(bb,cb,Ma,fb);j.clearColor(M.r,M.g,M.b,
+Q);this.context=j;var Mb=j.getParameter(j.MAX_TEXTURE_IMAGE_UNITS),$b=j.getParameter(j.MAX_VERTEX_TEXTURE_IMAGE_UNITS);j.getParameter(j.MAX_TEXTURE_SIZE);var ac=j.getParameter(j.MAX_CUBE_MAP_TEXTURE_SIZE),Nb=wa?j.getParameter(wa.MAX_TEXTURE_MAX_ANISOTROPY_EXT):0,Bb=0<$b,yb=Bb&&Oa;Ea&&j.getParameter(j.COMPRESSED_TEXTURE_FORMATS);var bc=j.getShaderPrecisionFormat(j.VERTEX_SHADER,j.HIGH_FLOAT),cc=j.getShaderPrecisionFormat(j.VERTEX_SHADER,j.MEDIUM_FLOAT);j.getShaderPrecisionFormat(j.VERTEX_SHADER,j.LOW_FLOAT);
+var qc=j.getShaderPrecisionFormat(j.FRAGMENT_SHADER,j.HIGH_FLOAT),rc=j.getShaderPrecisionFormat(j.FRAGMENT_SHADER,j.MEDIUM_FLOAT);j.getShaderPrecisionFormat(j.FRAGMENT_SHADER,j.LOW_FLOAT);j.getShaderPrecisionFormat(j.VERTEX_SHADER,j.HIGH_INT);j.getShaderPrecisionFormat(j.VERTEX_SHADER,j.MEDIUM_INT);j.getShaderPrecisionFormat(j.VERTEX_SHADER,j.LOW_INT);j.getShaderPrecisionFormat(j.FRAGMENT_SHADER,j.HIGH_INT);j.getShaderPrecisionFormat(j.FRAGMENT_SHADER,j.MEDIUM_INT);j.getShaderPrecisionFormat(j.FRAGMENT_SHADER,
+j.LOW_INT);var sc=0<bc.precision&&0<qc.precision,Ob=0<cc.precision&&0<rc.precision;"highp"===R&&!sc&&(Ob?(R="mediump",console.warn("WebGLRenderer: highp not supported, using mediump")):(R="lowp",console.warn("WebGLRenderer: highp and mediump not supported, using lowp")));"mediump"===R&&!Ob&&(R="lowp",console.warn("WebGLRenderer: mediump not supported, using lowp"));this.getContext=function(){return j};this.supportsVertexTextures=function(){return Bb};this.supportsFloatTextures=function(){return Oa};
+this.supportsStandardDerivatives=function(){return va};this.supportsCompressedTextureS3TC=function(){return Ea};this.getMaxAnisotropy=function(){return Nb};this.getPrecision=function(){return R};this.setSize=function(a,b,c){G.width=a*this.devicePixelRatio;G.height=b*this.devicePixelRatio;1!==this.devicePixelRatio&&!1!==c&&(G.style.width=a+"px",G.style.height=b+"px");this.setViewport(0,0,G.width,G.height)};this.setViewport=function(a,b,c,d){bb=void 0!==a?a:0;cb=void 0!==b?b:0;Ma=void 0!==c?c:G.width;
+fb=void 0!==d?d:G.height;j.viewport(bb,cb,Ma,fb)};this.setScissor=function(a,b,c,d){j.scissor(a,b,c,d)};this.enableScissorTest=function(a){a?j.enable(j.SCISSOR_TEST):j.disable(j.SCISSOR_TEST)};this.setClearColor=function(a,b){M.set(a);Q=void 0!==b?b:1;j.clearColor(M.r,M.g,M.b,Q)};this.setClearColorHex=function(a,b){console.warn("DEPRECATED: .setClearColorHex() is being removed. Use .setClearColor() instead.");this.setClearColor(a,b)};this.getClearColor=function(){return M};this.getClearAlpha=function(){return Q};
+this.clear=function(a,b,c){var d=0;if(void 0===a||a)d|=j.COLOR_BUFFER_BIT;if(void 0===b||b)d|=j.DEPTH_BUFFER_BIT;if(void 0===c||c)d|=j.STENCIL_BUFFER_BIT;j.clear(d)};this.clearTarget=function(a,b,c,d){this.setRenderTarget(a);this.clear(b,c,d)};this.addPostPlugin=function(a){a.init(this);this.renderPluginsPost.push(a)};this.addPrePlugin=function(a){a.init(this);this.renderPluginsPre.push(a)};this.updateShadowMap=function(a,b){Ba=null;Aa=$=fa=Ga=ka=-1;ua=!0;U=Z=-1;this.shadowMapPlugin.update(a,b)};
+var Cb=function(a){a=a.target;a.removeEventListener("dispose",Cb);a.__webglInit=void 0;if(a instanceof THREE.BufferGeometry){var b=a.attributes,c;for(c in b)void 0!==b[c].buffer&&j.deleteBuffer(b[c].buffer);K.info.memory.geometries--}else if(void 0!==a.geometryGroups)for(b in a.geometryGroups){c=a.geometryGroups[b];if(void 0!==c.numMorphTargets)for(var d=0,e=c.numMorphTargets;d<e;d++)j.deleteBuffer(c.__webglMorphTargetsBuffers[d]);if(void 0!==c.numMorphNormals){d=0;for(e=c.numMorphNormals;d<e;d++)j.deleteBuffer(c.__webglMorphNormalsBuffers[d])}Hb(c)}else Hb(a)},
+Db=function(a){a=a.target;a.removeEventListener("dispose",Db);a.image&&a.image.__webglTextureCube?j.deleteTexture(a.image.__webglTextureCube):a.__webglInit&&(a.__webglInit=!1,j.deleteTexture(a.__webglTexture));K.info.memory.textures--},Eb=function(a){a=a.target;a.removeEventListener("dispose",Eb);if(a&&a.__webglTexture)if(j.deleteTexture(a.__webglTexture),a instanceof THREE.WebGLRenderTargetCube)for(var b=0;6>b;b++)j.deleteFramebuffer(a.__webglFramebuffer[b]),j.deleteRenderbuffer(a.__webglRenderbuffer[b]);
+else j.deleteFramebuffer(a.__webglFramebuffer),j.deleteRenderbuffer(a.__webglRenderbuffer);K.info.memory.textures--},Fb=function(a){a=a.target;a.removeEventListener("dispose",Fb);Gb(a)},Hb=function(a){void 0!==a.__webglVertexBuffer&&j.deleteBuffer(a.__webglVertexBuffer);void 0!==a.__webglNormalBuffer&&j.deleteBuffer(a.__webglNormalBuffer);void 0!==a.__webglTangentBuffer&&j.deleteBuffer(a.__webglTangentBuffer);void 0!==a.__webglColorBuffer&&j.deleteBuffer(a.__webglColorBuffer);void 0!==a.__webglUVBuffer&&
+j.deleteBuffer(a.__webglUVBuffer);void 0!==a.__webglUV2Buffer&&j.deleteBuffer(a.__webglUV2Buffer);void 0!==a.__webglSkinIndicesBuffer&&j.deleteBuffer(a.__webglSkinIndicesBuffer);void 0!==a.__webglSkinWeightsBuffer&&j.deleteBuffer(a.__webglSkinWeightsBuffer);void 0!==a.__webglFaceBuffer&&j.deleteBuffer(a.__webglFaceBuffer);void 0!==a.__webglLineBuffer&&j.deleteBuffer(a.__webglLineBuffer);void 0!==a.__webglLineDistanceBuffer&&j.deleteBuffer(a.__webglLineDistanceBuffer);if(void 0!==a.__webglCustomAttributesList)for(var b in a.__webglCustomAttributesList)j.deleteBuffer(a.__webglCustomAttributesList[b].buffer);
+K.info.memory.geometries--},Gb=function(a){var b=a.program;if(void 0!==b){a.program=void 0;var c,d,e=!1,a=0;for(c=ca.length;a<c;a++)if(d=ca[a],d.program===b){d.usedTimes--;0===d.usedTimes&&(e=!0);break}if(!0===e){e=[];a=0;for(c=ca.length;a<c;a++)d=ca[a],d.program!==b&&e.push(d);ca=e;j.deleteProgram(b);K.info.memory.programs--}}};this.renderBufferImmediate=function(a,b,c){a.hasPositions&&!a.__webglVertexBuffer&&(a.__webglVertexBuffer=j.createBuffer());a.hasNormals&&!a.__webglNormalBuffer&&(a.__webglNormalBuffer=
+j.createBuffer());a.hasUvs&&!a.__webglUvBuffer&&(a.__webglUvBuffer=j.createBuffer());a.hasColors&&!a.__webglColorBuffer&&(a.__webglColorBuffer=j.createBuffer());a.hasPositions&&(j.bindBuffer(j.ARRAY_BUFFER,a.__webglVertexBuffer),j.bufferData(j.ARRAY_BUFFER,a.positionArray,j.DYNAMIC_DRAW),j.enableVertexAttribArray(b.attributes.position),j.vertexAttribPointer(b.attributes.position,3,j.FLOAT,!1,0,0));if(a.hasNormals){j.bindBuffer(j.ARRAY_BUFFER,a.__webglNormalBuffer);if(c.shading===THREE.FlatShading){var d,
+e,f,h,g,i,k,l,m,p,n,q=3*a.count;for(n=0;n<q;n+=9)p=a.normalArray,d=p[n],e=p[n+1],f=p[n+2],h=p[n+3],i=p[n+4],l=p[n+5],g=p[n+6],k=p[n+7],m=p[n+8],d=(d+h+g)/3,e=(e+i+k)/3,f=(f+l+m)/3,p[n]=d,p[n+1]=e,p[n+2]=f,p[n+3]=d,p[n+4]=e,p[n+5]=f,p[n+6]=d,p[n+7]=e,p[n+8]=f}j.bufferData(j.ARRAY_BUFFER,a.normalArray,j.DYNAMIC_DRAW);j.enableVertexAttribArray(b.attributes.normal);j.vertexAttribPointer(b.attributes.normal,3,j.FLOAT,!1,0,0)}a.hasUvs&&c.map&&(j.bindBuffer(j.ARRAY_BUFFER,a.__webglUvBuffer),j.bufferData(j.ARRAY_BUFFER,
+a.uvArray,j.DYNAMIC_DRAW),j.enableVertexAttribArray(b.attributes.uv),j.vertexAttribPointer(b.attributes.uv,2,j.FLOAT,!1,0,0));a.hasColors&&c.vertexColors!==THREE.NoColors&&(j.bindBuffer(j.ARRAY_BUFFER,a.__webglColorBuffer),j.bufferData(j.ARRAY_BUFFER,a.colorArray,j.DYNAMIC_DRAW),j.enableVertexAttribArray(b.attributes.color),j.vertexAttribPointer(b.attributes.color,3,j.FLOAT,!1,0,0));j.drawArrays(j.TRIANGLES,0,a.count);a.count=0};this.renderBufferDirect=function(a,b,c,d,e,f){if(!1!==d.visible){var i,
+k,l,m;i=z(a,b,c,d,f);b=i.attributes;a=e.attributes;c=!1;i=16777215*e.id+2*i.id+(d.wireframe?1:0);i!==$&&($=i,c=!0);c&&g();if(f instanceof THREE.Mesh)if(f=a.index){e=e.offsets;1<e.length&&(c=!0);for(var p=0,n=e.length;p<n;p++){var q=e[p].index;if(c){for(k in b)l=b[k],i=a[k],0<=l&&(i?(m=i.itemSize,j.bindBuffer(j.ARRAY_BUFFER,i.buffer),h(l),j.vertexAttribPointer(l,m,j.FLOAT,!1,0,4*q*m)):d.defaultAttributeValues&&(2===d.defaultAttributeValues[k].length?j.vertexAttrib2fv(l,d.defaultAttributeValues[k]):
+3===d.defaultAttributeValues[k].length&&j.vertexAttrib3fv(l,d.defaultAttributeValues[k])));j.bindBuffer(j.ELEMENT_ARRAY_BUFFER,f.buffer)}j.drawElements(j.TRIANGLES,e[p].count,j.UNSIGNED_SHORT,2*e[p].start);K.info.render.calls++;K.info.render.vertices+=e[p].count;K.info.render.faces+=e[p].count/3}}else{if(c)for(k in b)"index"!==k&&(l=b[k],i=a[k],0<=l&&(i?(m=i.itemSize,j.bindBuffer(j.ARRAY_BUFFER,i.buffer),h(l),j.vertexAttribPointer(l,m,j.FLOAT,!1,0,0)):d.defaultAttributeValues&&d.defaultAttributeValues[k]&&
+(2===d.defaultAttributeValues[k].length?j.vertexAttrib2fv(l,d.defaultAttributeValues[k]):3===d.defaultAttributeValues[k].length&&j.vertexAttrib3fv(l,d.defaultAttributeValues[k]))));d=e.attributes.position;j.drawArrays(j.TRIANGLES,0,d.numItems/3);K.info.render.calls++;K.info.render.vertices+=d.numItems/3;K.info.render.faces+=d.numItems/3/3}else if(f instanceof THREE.ParticleSystem){if(c){for(k in b)l=b[k],i=a[k],0<=l&&(i?(m=i.itemSize,j.bindBuffer(j.ARRAY_BUFFER,i.buffer),h(l),j.vertexAttribPointer(l,
+m,j.FLOAT,!1,0,0)):d.defaultAttributeValues&&d.defaultAttributeValues[k]&&(2===d.defaultAttributeValues[k].length?j.vertexAttrib2fv(l,d.defaultAttributeValues[k]):3===d.defaultAttributeValues[k].length&&j.vertexAttrib3fv(l,d.defaultAttributeValues[k])));d=a.position;j.drawArrays(j.POINTS,0,d.numItems/3);K.info.render.calls++;K.info.render.points+=d.numItems/3}}else if(f instanceof THREE.Line&&c){for(k in b)l=b[k],i=a[k],0<=l&&(i?(m=i.itemSize,j.bindBuffer(j.ARRAY_BUFFER,i.buffer),h(l),j.vertexAttribPointer(l,
+m,j.FLOAT,!1,0,0)):d.defaultAttributeValues&&d.defaultAttributeValues[k]&&(2===d.defaultAttributeValues[k].length?j.vertexAttrib2fv(l,d.defaultAttributeValues[k]):3===d.defaultAttributeValues[k].length&&j.vertexAttrib3fv(l,d.defaultAttributeValues[k])));k=f.type===THREE.LineStrip?j.LINE_STRIP:j.LINES;F(d.linewidth);d=a.position;j.drawArrays(k,0,d.numItems/3);K.info.render.calls++;K.info.render.points+=d.numItems}}};this.renderBuffer=function(a,b,c,d,e,f){if(!1!==d.visible){var i,l,c=z(a,b,c,d,f),
+a=c.attributes,b=!1,c=16777215*e.id+2*c.id+(d.wireframe?1:0);c!==$&&($=c,b=!0);b&&g();if(!d.morphTargets&&0<=a.position)b&&(j.bindBuffer(j.ARRAY_BUFFER,e.__webglVertexBuffer),h(a.position),j.vertexAttribPointer(a.position,3,j.FLOAT,!1,0,0));else if(f.morphTargetBase){c=d.program.attributes;-1!==f.morphTargetBase&&0<=c.position?(j.bindBuffer(j.ARRAY_BUFFER,e.__webglMorphTargetsBuffers[f.morphTargetBase]),h(c.position),j.vertexAttribPointer(c.position,3,j.FLOAT,!1,0,0)):0<=c.position&&(j.bindBuffer(j.ARRAY_BUFFER,
+e.__webglVertexBuffer),h(c.position),j.vertexAttribPointer(c.position,3,j.FLOAT,!1,0,0));if(f.morphTargetForcedOrder.length){var m=0;l=f.morphTargetForcedOrder;for(i=f.morphTargetInfluences;m<d.numSupportedMorphTargets&&m<l.length;)0<=c["morphTarget"+m]&&(j.bindBuffer(j.ARRAY_BUFFER,e.__webglMorphTargetsBuffers[l[m]]),h(c["morphTarget"+m]),j.vertexAttribPointer(c["morphTarget"+m],3,j.FLOAT,!1,0,0)),0<=c["morphNormal"+m]&&d.morphNormals&&(j.bindBuffer(j.ARRAY_BUFFER,e.__webglMorphNormalsBuffers[l[m]]),
+h(c["morphNormal"+m]),j.vertexAttribPointer(c["morphNormal"+m],3,j.FLOAT,!1,0,0)),f.__webglMorphTargetInfluences[m]=i[l[m]],m++}else{l=[];i=f.morphTargetInfluences;var p,n=i.length;for(p=0;p<n;p++)m=i[p],0<m&&l.push([m,p]);l.length>d.numSupportedMorphTargets?(l.sort(k),l.length=d.numSupportedMorphTargets):l.length>d.numSupportedMorphNormals?l.sort(k):0===l.length&&l.push([0,0]);for(m=0;m<d.numSupportedMorphTargets;)l[m]?(p=l[m][1],0<=c["morphTarget"+m]&&(j.bindBuffer(j.ARRAY_BUFFER,e.__webglMorphTargetsBuffers[p]),
+h(c["morphTarget"+m]),j.vertexAttribPointer(c["morphTarget"+m],3,j.FLOAT,!1,0,0)),0<=c["morphNormal"+m]&&d.morphNormals&&(j.bindBuffer(j.ARRAY_BUFFER,e.__webglMorphNormalsBuffers[p]),h(c["morphNormal"+m]),j.vertexAttribPointer(c["morphNormal"+m],3,j.FLOAT,!1,0,0)),f.__webglMorphTargetInfluences[m]=i[p]):f.__webglMorphTargetInfluences[m]=0,m++}null!==d.program.uniforms.morphTargetInfluences&&j.uniform1fv(d.program.uniforms.morphTargetInfluences,f.__webglMorphTargetInfluences)}if(b){if(e.__webglCustomAttributesList){i=
+0;for(l=e.__webglCustomAttributesList.length;i<l;i++)c=e.__webglCustomAttributesList[i],0<=a[c.buffer.belongsToAttribute]&&(j.bindBuffer(j.ARRAY_BUFFER,c.buffer),h(a[c.buffer.belongsToAttribute]),j.vertexAttribPointer(a[c.buffer.belongsToAttribute],c.size,j.FLOAT,!1,0,0))}0<=a.color&&(0<f.geometry.colors.length||0<f.geometry.faces.length?(j.bindBuffer(j.ARRAY_BUFFER,e.__webglColorBuffer),h(a.color),j.vertexAttribPointer(a.color,3,j.FLOAT,!1,0,0)):d.defaultAttributeValues&&j.vertexAttrib3fv(a.color,
+d.defaultAttributeValues.color));0<=a.normal&&(j.bindBuffer(j.ARRAY_BUFFER,e.__webglNormalBuffer),h(a.normal),j.vertexAttribPointer(a.normal,3,j.FLOAT,!1,0,0));0<=a.tangent&&(j.bindBuffer(j.ARRAY_BUFFER,e.__webglTangentBuffer),h(a.tangent),j.vertexAttribPointer(a.tangent,4,j.FLOAT,!1,0,0));0<=a.uv&&(f.geometry.faceVertexUvs[0]?(j.bindBuffer(j.ARRAY_BUFFER,e.__webglUVBuffer),h(a.uv),j.vertexAttribPointer(a.uv,2,j.FLOAT,!1,0,0)):d.defaultAttributeValues&&j.vertexAttrib2fv(a.uv,d.defaultAttributeValues.uv));
+0<=a.uv2&&(f.geometry.faceVertexUvs[1]?(j.bindBuffer(j.ARRAY_BUFFER,e.__webglUV2Buffer),h(a.uv2),j.vertexAttribPointer(a.uv2,2,j.FLOAT,!1,0,0)):d.defaultAttributeValues&&j.vertexAttrib2fv(a.uv2,d.defaultAttributeValues.uv2));d.skinning&&(0<=a.skinIndex&&0<=a.skinWeight)&&(j.bindBuffer(j.ARRAY_BUFFER,e.__webglSkinIndicesBuffer),h(a.skinIndex),j.vertexAttribPointer(a.skinIndex,4,j.FLOAT,!1,0,0),j.bindBuffer(j.ARRAY_BUFFER,e.__webglSkinWeightsBuffer),h(a.skinWeight),j.vertexAttribPointer(a.skinWeight,
+4,j.FLOAT,!1,0,0));0<=a.lineDistance&&(j.bindBuffer(j.ARRAY_BUFFER,e.__webglLineDistanceBuffer),h(a.lineDistance),j.vertexAttribPointer(a.lineDistance,1,j.FLOAT,!1,0,0))}f instanceof THREE.Mesh?(d.wireframe?(F(d.wireframeLinewidth),b&&j.bindBuffer(j.ELEMENT_ARRAY_BUFFER,e.__webglLineBuffer),j.drawElements(j.LINES,e.__webglLineCount,j.UNSIGNED_SHORT,0)):(b&&j.bindBuffer(j.ELEMENT_ARRAY_BUFFER,e.__webglFaceBuffer),j.drawElements(j.TRIANGLES,e.__webglFaceCount,j.UNSIGNED_SHORT,0)),K.info.render.calls++,
+K.info.render.vertices+=e.__webglFaceCount,K.info.render.faces+=e.__webglFaceCount/3):f instanceof THREE.Line?(f=f.type===THREE.LineStrip?j.LINE_STRIP:j.LINES,F(d.linewidth),j.drawArrays(f,0,e.__webglLineCount),K.info.render.calls++):f instanceof THREE.ParticleSystem&&(j.drawArrays(j.POINTS,0,e.__webglParticleCount),K.info.render.calls++,K.info.render.points+=e.__webglParticleCount)}};this.render=function(a,b,c,d){if(!1===b instanceof THREE.Camera)console.error("THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.");
+else{var e,f,h,g,k=a.__lights,n=a.fog;Aa=-1;ua=!0;!0===a.autoUpdate&&a.updateMatrixWorld();void 0===b.parent&&b.updateMatrixWorld();b.matrixWorldInverse.getInverse(b.matrixWorld);ra.multiplyMatrices(b.projectionMatrix,b.matrixWorldInverse);la.setFromMatrix(ra);this.autoUpdateObjects&&this.initWebGLObjects(a);m(this.renderPluginsPre,a,b);K.info.render.calls=0;K.info.render.vertices=0;K.info.render.faces=0;K.info.render.points=0;this.setRenderTarget(c);(this.autoClear||d)&&this.clear(this.autoClearColor,
+this.autoClearDepth,this.autoClearStencil);g=a.__webglObjects;d=0;for(e=g.length;d<e;d++)if(f=g[d],h=f.object,f.id=d,f.render=!1,h.visible&&(!(h instanceof THREE.Mesh||h instanceof THREE.ParticleSystem)||!h.frustumCulled||la.intersectsObject(h))){var q=h;q._modelViewMatrix.multiplyMatrices(b.matrixWorldInverse,q.matrixWorld);q._normalMatrix.getNormalMatrix(q._modelViewMatrix);var q=f,r=q.buffer,s=void 0,t=s=void 0,t=q.object.material;if(t instanceof THREE.MeshFaceMaterial)s=r.materialIndex,s=t.materials[s],
+s.transparent?(q.transparent=s,q.opaque=null):(q.opaque=s,q.transparent=null);else if(s=t)s.transparent?(q.transparent=s,q.opaque=null):(q.opaque=s,q.transparent=null);f.render=!0;!0===this.sortObjects&&(null!==h.renderDepth?f.z=h.renderDepth:(sa.getPositionFromMatrix(h.matrixWorld),sa.applyProjection(ra),f.z=sa.z))}this.sortObjects&&g.sort(i);g=a.__webglObjectsImmediate;d=0;for(e=g.length;d<e;d++)f=g[d],h=f.object,h.visible&&(h._modelViewMatrix.multiplyMatrices(b.matrixWorldInverse,h.matrixWorld),
+h._normalMatrix.getNormalMatrix(h._modelViewMatrix),h=f.object.material,h.transparent?(f.transparent=h,f.opaque=null):(f.opaque=h,f.transparent=null));a.overrideMaterial?(d=a.overrideMaterial,this.setBlending(d.blending,d.blendEquation,d.blendSrc,d.blendDst),this.setDepthTest(d.depthTest),this.setDepthWrite(d.depthWrite),A(d.polygonOffset,d.polygonOffsetFactor,d.polygonOffsetUnits),l(a.__webglObjects,!1,"",b,k,n,!0,d),p(a.__webglObjectsImmediate,"",b,k,n,!1,d)):(d=null,this.setBlending(THREE.NoBlending),
+l(a.__webglObjects,!0,"opaque",b,k,n,!1,d),p(a.__webglObjectsImmediate,"opaque",b,k,n,!1,d),l(a.__webglObjects,!1,"transparent",b,k,n,!0,d),p(a.__webglObjectsImmediate,"transparent",b,k,n,!0,d));m(this.renderPluginsPost,a,b);c&&(c.generateMipmaps&&c.minFilter!==THREE.NearestFilter&&c.minFilter!==THREE.LinearFilter)&&(c instanceof THREE.WebGLRenderTargetCube?(j.bindTexture(j.TEXTURE_CUBE_MAP,c.__webglTexture),j.generateMipmap(j.TEXTURE_CUBE_MAP),j.bindTexture(j.TEXTURE_CUBE_MAP,null)):(j.bindTexture(j.TEXTURE_2D,
+c.__webglTexture),j.generateMipmap(j.TEXTURE_2D),j.bindTexture(j.TEXTURE_2D,null)));this.setDepthTest(!0);this.setDepthWrite(!0)}};this.renderImmediateObject=function(a,b,c,d,e){var f=z(a,b,c,d,e);$=-1;K.setMaterialFaces(d);e.immediateRenderCallback?e.immediateRenderCallback(f,j,la):e.render(function(a){K.renderBufferImmediate(a,f,d)})};this.initWebGLObjects=function(a){a.__webglObjects||(a.__webglObjects=[],a.__webglObjectsImmediate=[],a.__webglSprites=[],a.__webglFlares=[]);for(;a.__objectsAdded.length;)s(a.__objectsAdded[0],
+a),a.__objectsAdded.splice(0,1);for(;a.__objectsRemoved.length;)q(a.__objectsRemoved[0],a),a.__objectsRemoved.splice(0,1);for(var b=0,h=a.__webglObjects.length;b<h;b++){var g=a.__webglObjects[b].object;void 0===g.__webglInit&&(void 0!==g.__webglActive&&q(g,a),s(g,a));var i=g,l=i.geometry,m=void 0,p=void 0,t=void 0;if(l instanceof THREE.BufferGeometry){var u=j.DYNAMIC_DRAW,w=!l.dynamic,z=l.attributes,y=void 0,x=void 0;for(y in z)x=z[y],x.needsUpdate&&("index"===y?(j.bindBuffer(j.ELEMENT_ARRAY_BUFFER,
+x.buffer),j.bufferData(j.ELEMENT_ARRAY_BUFFER,x.array,u)):(j.bindBuffer(j.ARRAY_BUFFER,x.buffer),j.bufferData(j.ARRAY_BUFFER,x.array,u)),x.needsUpdate=!1),w&&!x.dynamic&&(x.array=null)}else if(i instanceof THREE.Mesh){for(var A=0,B=l.geometryGroupsList.length;A<B;A++)if(m=l.geometryGroupsList[A],t=d(i,m),l.buffersNeedUpdate&&c(m,i),p=t.attributes&&n(t),l.verticesNeedUpdate||l.morphTargetsNeedUpdate||l.elementsNeedUpdate||l.uvsNeedUpdate||l.normalsNeedUpdate||l.colorsNeedUpdate||l.tangentsNeedUpdate||
+p){var v=m,C=i,D=j.DYNAMIC_DRAW,F=!l.dynamic,G=t;if(v.__inittedArrays){var J=e(G),K=G.vertexColors?G.vertexColors:!1,N=f(G),O=J===THREE.SmoothShading,E=void 0,I=void 0,V=void 0,M=void 0,R=void 0,U=void 0,Q=void 0,da=void 0,Z=void 0,$=void 0,Fa=void 0,P=void 0,X=void 0,W=void 0,Ba=void 0,ea=void 0,Aa=void 0,ba=void 0,ca=void 0,ia=void 0,fa=void 0,ga=void 0,ka=void 0,la=void 0,oa=void 0,pa=void 0,ta=void 0,ua=void 0,va=void 0,Ca=void 0,Da=void 0,Ga=void 0,Ea=void 0,La=void 0,Sa=void 0,Ha=void 0,wa=
+void 0,xa=void 0,Qa=void 0,Ra=void 0,db=0,eb=0,Oa=0,Pa=0,Ua=0,hb=0,Ta=0,tb=0,Za=0,qa=0,ya=0,L=0,Na=void 0,ib=v.__vertexArray,bb=v.__uvArray,cb=v.__uv2Array,Ma=v.__normalArray,Va=v.__tangentArray,jb=v.__colorArray,Wa=v.__skinIndexArray,Xa=v.__skinWeightArray,fb=v.__morphTargetsArrays,sb=v.__morphNormalsArrays,pb=v.__webglCustomAttributesList,H=void 0,Pb=v.__faceArray,vb=v.__lineArray,Ia=C.geometry,Bb=Ia.elementsNeedUpdate,yb=Ia.uvsNeedUpdate,Db=Ia.normalsNeedUpdate,Mb=Ia.tangentsNeedUpdate,Nb=Ia.colorsNeedUpdate,
+Ob=Ia.morphTargetsNeedUpdate,ec=Ia.vertices,aa=v.faces3,kb=Ia.faces,Cb=Ia.faceVertexUvs[0],Eb=Ia.faceVertexUvs[1],fc=Ia.skinIndices,Qb=Ia.skinWeights,Rb=Ia.morphTargets,Fb=Ia.morphNormals;if(Ia.verticesNeedUpdate){E=0;for(I=aa.length;E<I;E++)M=kb[aa[E]],P=ec[M.a],X=ec[M.b],W=ec[M.c],ib[eb]=P.x,ib[eb+1]=P.y,ib[eb+2]=P.z,ib[eb+3]=X.x,ib[eb+4]=X.y,ib[eb+5]=X.z,ib[eb+6]=W.x,ib[eb+7]=W.y,ib[eb+8]=W.z,eb+=9;j.bindBuffer(j.ARRAY_BUFFER,v.__webglVertexBuffer);j.bufferData(j.ARRAY_BUFFER,ib,D)}if(Ob){Sa=0;
+for(Ha=Rb.length;Sa<Ha;Sa++){E=ya=0;for(I=aa.length;E<I;E++)Qa=aa[E],M=kb[Qa],P=Rb[Sa].vertices[M.a],X=Rb[Sa].vertices[M.b],W=Rb[Sa].vertices[M.c],wa=fb[Sa],wa[ya]=P.x,wa[ya+1]=P.y,wa[ya+2]=P.z,wa[ya+3]=X.x,wa[ya+4]=X.y,wa[ya+5]=X.z,wa[ya+6]=W.x,wa[ya+7]=W.y,wa[ya+8]=W.z,G.morphNormals&&(O?(Ra=Fb[Sa].vertexNormals[Qa],ba=Ra.a,ca=Ra.b,ia=Ra.c):ia=ca=ba=Fb[Sa].faceNormals[Qa],xa=sb[Sa],xa[ya]=ba.x,xa[ya+1]=ba.y,xa[ya+2]=ba.z,xa[ya+3]=ca.x,xa[ya+4]=ca.y,xa[ya+5]=ca.z,xa[ya+6]=ia.x,xa[ya+7]=ia.y,xa[ya+
+8]=ia.z),ya+=9;j.bindBuffer(j.ARRAY_BUFFER,v.__webglMorphTargetsBuffers[Sa]);j.bufferData(j.ARRAY_BUFFER,fb[Sa],D);G.morphNormals&&(j.bindBuffer(j.ARRAY_BUFFER,v.__webglMorphNormalsBuffers[Sa]),j.bufferData(j.ARRAY_BUFFER,sb[Sa],D))}}if(Qb.length){E=0;for(I=aa.length;E<I;E++)M=kb[aa[E]],la=Qb[M.a],oa=Qb[M.b],pa=Qb[M.c],Xa[qa]=la.x,Xa[qa+1]=la.y,Xa[qa+2]=la.z,Xa[qa+3]=la.w,Xa[qa+4]=oa.x,Xa[qa+5]=oa.y,Xa[qa+6]=oa.z,Xa[qa+7]=oa.w,Xa[qa+8]=pa.x,Xa[qa+9]=pa.y,Xa[qa+10]=pa.z,Xa[qa+11]=pa.w,ta=fc[M.a],ua=
+fc[M.b],va=fc[M.c],Wa[qa]=ta.x,Wa[qa+1]=ta.y,Wa[qa+2]=ta.z,Wa[qa+3]=ta.w,Wa[qa+4]=ua.x,Wa[qa+5]=ua.y,Wa[qa+6]=ua.z,Wa[qa+7]=ua.w,Wa[qa+8]=va.x,Wa[qa+9]=va.y,Wa[qa+10]=va.z,Wa[qa+11]=va.w,qa+=12;0<qa&&(j.bindBuffer(j.ARRAY_BUFFER,v.__webglSkinIndicesBuffer),j.bufferData(j.ARRAY_BUFFER,Wa,D),j.bindBuffer(j.ARRAY_BUFFER,v.__webglSkinWeightsBuffer),j.bufferData(j.ARRAY_BUFFER,Xa,D))}if(Nb&&K){E=0;for(I=aa.length;E<I;E++)M=kb[aa[E]],Q=M.vertexColors,da=M.color,3===Q.length&&K===THREE.VertexColors?(fa=
+Q[0],ga=Q[1],ka=Q[2]):ka=ga=fa=da,jb[Za]=fa.r,jb[Za+1]=fa.g,jb[Za+2]=fa.b,jb[Za+3]=ga.r,jb[Za+4]=ga.g,jb[Za+5]=ga.b,jb[Za+6]=ka.r,jb[Za+7]=ka.g,jb[Za+8]=ka.b,Za+=9;0<Za&&(j.bindBuffer(j.ARRAY_BUFFER,v.__webglColorBuffer),j.bufferData(j.ARRAY_BUFFER,jb,D))}if(Mb&&Ia.hasTangents){E=0;for(I=aa.length;E<I;E++)M=kb[aa[E]],Z=M.vertexTangents,Ba=Z[0],ea=Z[1],Aa=Z[2],Va[Ta]=Ba.x,Va[Ta+1]=Ba.y,Va[Ta+2]=Ba.z,Va[Ta+3]=Ba.w,Va[Ta+4]=ea.x,Va[Ta+5]=ea.y,Va[Ta+6]=ea.z,Va[Ta+7]=ea.w,Va[Ta+8]=Aa.x,Va[Ta+9]=Aa.y,Va[Ta+
+10]=Aa.z,Va[Ta+11]=Aa.w,Ta+=12;j.bindBuffer(j.ARRAY_BUFFER,v.__webglTangentBuffer);j.bufferData(j.ARRAY_BUFFER,Va,D)}if(Db&&J){E=0;for(I=aa.length;E<I;E++)if(M=kb[aa[E]],R=M.vertexNormals,U=M.normal,3===R.length&&O)for(Ca=0;3>Ca;Ca++)Ga=R[Ca],Ma[hb]=Ga.x,Ma[hb+1]=Ga.y,Ma[hb+2]=Ga.z,hb+=3;else for(Ca=0;3>Ca;Ca++)Ma[hb]=U.x,Ma[hb+1]=U.y,Ma[hb+2]=U.z,hb+=3;j.bindBuffer(j.ARRAY_BUFFER,v.__webglNormalBuffer);j.bufferData(j.ARRAY_BUFFER,Ma,D)}if(yb&&Cb&&N){E=0;for(I=aa.length;E<I;E++)if(V=aa[E],$=Cb[V],
+void 0!==$)for(Ca=0;3>Ca;Ca++)Ea=$[Ca],bb[Oa]=Ea.x,bb[Oa+1]=Ea.y,Oa+=2;0<Oa&&(j.bindBuffer(j.ARRAY_BUFFER,v.__webglUVBuffer),j.bufferData(j.ARRAY_BUFFER,bb,D))}if(yb&&Eb&&N){E=0;for(I=aa.length;E<I;E++)if(V=aa[E],Fa=Eb[V],void 0!==Fa)for(Ca=0;3>Ca;Ca++)La=Fa[Ca],cb[Pa]=La.x,cb[Pa+1]=La.y,Pa+=2;0<Pa&&(j.bindBuffer(j.ARRAY_BUFFER,v.__webglUV2Buffer),j.bufferData(j.ARRAY_BUFFER,cb,D))}if(Bb){E=0;for(I=aa.length;E<I;E++)Pb[Ua]=db,Pb[Ua+1]=db+1,Pb[Ua+2]=db+2,Ua+=3,vb[tb]=db,vb[tb+1]=db+1,vb[tb+2]=db,vb[tb+
+3]=db+2,vb[tb+4]=db+1,vb[tb+5]=db+2,tb+=6,db+=3;j.bindBuffer(j.ELEMENT_ARRAY_BUFFER,v.__webglFaceBuffer);j.bufferData(j.ELEMENT_ARRAY_BUFFER,Pb,D);j.bindBuffer(j.ELEMENT_ARRAY_BUFFER,v.__webglLineBuffer);j.bufferData(j.ELEMENT_ARRAY_BUFFER,vb,D)}if(pb){Ca=0;for(Da=pb.length;Ca<Da;Ca++)if(H=pb[Ca],H.__original.needsUpdate){L=0;if(1===H.size)if(void 0===H.boundTo||"vertices"===H.boundTo){E=0;for(I=aa.length;E<I;E++)M=kb[aa[E]],H.array[L]=H.value[M.a],H.array[L+1]=H.value[M.b],H.array[L+2]=H.value[M.c],
+L+=3}else{if("faces"===H.boundTo){E=0;for(I=aa.length;E<I;E++)Na=H.value[aa[E]],H.array[L]=Na,H.array[L+1]=Na,H.array[L+2]=Na,L+=3}}else if(2===H.size)if(void 0===H.boundTo||"vertices"===H.boundTo){E=0;for(I=aa.length;E<I;E++)M=kb[aa[E]],P=H.value[M.a],X=H.value[M.b],W=H.value[M.c],H.array[L]=P.x,H.array[L+1]=P.y,H.array[L+2]=X.x,H.array[L+3]=X.y,H.array[L+4]=W.x,H.array[L+5]=W.y,L+=6}else{if("faces"===H.boundTo){E=0;for(I=aa.length;E<I;E++)W=X=P=Na=H.value[aa[E]],H.array[L]=P.x,H.array[L+1]=P.y,
+H.array[L+2]=X.x,H.array[L+3]=X.y,H.array[L+4]=W.x,H.array[L+5]=W.y,L+=6}}else if(3===H.size){var na;na="c"===H.type?["r","g","b"]:["x","y","z"];if(void 0===H.boundTo||"vertices"===H.boundTo){E=0;for(I=aa.length;E<I;E++)M=kb[aa[E]],P=H.value[M.a],X=H.value[M.b],W=H.value[M.c],H.array[L]=P[na[0]],H.array[L+1]=P[na[1]],H.array[L+2]=P[na[2]],H.array[L+3]=X[na[0]],H.array[L+4]=X[na[1]],H.array[L+5]=X[na[2]],H.array[L+6]=W[na[0]],H.array[L+7]=W[na[1]],H.array[L+8]=W[na[2]],L+=9}else if("faces"===H.boundTo){E=
+0;for(I=aa.length;E<I;E++)W=X=P=Na=H.value[aa[E]],H.array[L]=P[na[0]],H.array[L+1]=P[na[1]],H.array[L+2]=P[na[2]],H.array[L+3]=X[na[0]],H.array[L+4]=X[na[1]],H.array[L+5]=X[na[2]],H.array[L+6]=W[na[0]],H.array[L+7]=W[na[1]],H.array[L+8]=W[na[2]],L+=9}else if("faceVertices"===H.boundTo){E=0;for(I=aa.length;E<I;E++)Na=H.value[aa[E]],P=Na[0],X=Na[1],W=Na[2],H.array[L]=P[na[0]],H.array[L+1]=P[na[1]],H.array[L+2]=P[na[2]],H.array[L+3]=X[na[0]],H.array[L+4]=X[na[1]],H.array[L+5]=X[na[2]],H.array[L+6]=W[na[0]],
+H.array[L+7]=W[na[1]],H.array[L+8]=W[na[2]],L+=9}}else if(4===H.size)if(void 0===H.boundTo||"vertices"===H.boundTo){E=0;for(I=aa.length;E<I;E++)M=kb[aa[E]],P=H.value[M.a],X=H.value[M.b],W=H.value[M.c],H.array[L]=P.x,H.array[L+1]=P.y,H.array[L+2]=P.z,H.array[L+3]=P.w,H.array[L+4]=X.x,H.array[L+5]=X.y,H.array[L+6]=X.z,H.array[L+7]=X.w,H.array[L+8]=W.x,H.array[L+9]=W.y,H.array[L+10]=W.z,H.array[L+11]=W.w,L+=12}else if("faces"===H.boundTo){E=0;for(I=aa.length;E<I;E++)W=X=P=Na=H.value[aa[E]],H.array[L]=
+P.x,H.array[L+1]=P.y,H.array[L+2]=P.z,H.array[L+3]=P.w,H.array[L+4]=X.x,H.array[L+5]=X.y,H.array[L+6]=X.z,H.array[L+7]=X.w,H.array[L+8]=W.x,H.array[L+9]=W.y,H.array[L+10]=W.z,H.array[L+11]=W.w,L+=12}else if("faceVertices"===H.boundTo){E=0;for(I=aa.length;E<I;E++)Na=H.value[aa[E]],P=Na[0],X=Na[1],W=Na[2],H.array[L]=P.x,H.array[L+1]=P.y,H.array[L+2]=P.z,H.array[L+3]=P.w,H.array[L+4]=X.x,H.array[L+5]=X.y,H.array[L+6]=X.z,H.array[L+7]=X.w,H.array[L+8]=W.x,H.array[L+9]=W.y,H.array[L+10]=W.z,H.array[L+
+11]=W.w,L+=12}j.bindBuffer(j.ARRAY_BUFFER,H.buffer);j.bufferData(j.ARRAY_BUFFER,H.array,D)}}F&&(delete v.__inittedArrays,delete v.__colorArray,delete v.__normalArray,delete v.__tangentArray,delete v.__uvArray,delete v.__uv2Array,delete v.__faceArray,delete v.__vertexArray,delete v.__lineArray,delete v.__skinIndexArray,delete v.__skinWeightArray)}}l.verticesNeedUpdate=!1;l.morphTargetsNeedUpdate=!1;l.elementsNeedUpdate=!1;l.uvsNeedUpdate=!1;l.normalsNeedUpdate=!1;l.colorsNeedUpdate=!1;l.tangentsNeedUpdate=
+!1;l.buffersNeedUpdate=!1;t.attributes&&r(t)}else if(i instanceof THREE.Line){t=d(i,l);p=t.attributes&&n(t);if(l.verticesNeedUpdate||l.colorsNeedUpdate||l.lineDistancesNeedUpdate||p){var Ya=l,Sb=j.DYNAMIC_DRAW,Ib=void 0,Jb=void 0,Kb=void 0,Tb=void 0,ma=void 0,Ub=void 0,Gb=Ya.vertices,Hb=Ya.colors,kc=Ya.lineDistances,Zb=Gb.length,$b=Hb.length,ac=kc.length,Vb=Ya.__vertexArray,Wb=Ya.__colorArray,lc=Ya.__lineDistanceArray,bc=Ya.colorsNeedUpdate,cc=Ya.lineDistancesNeedUpdate,gc=Ya.__webglCustomAttributesList,
+Xb=void 0,mc=void 0,za=void 0,zb=void 0,Ja=void 0,ja=void 0;if(Ya.verticesNeedUpdate){for(Ib=0;Ib<Zb;Ib++)Tb=Gb[Ib],ma=3*Ib,Vb[ma]=Tb.x,Vb[ma+1]=Tb.y,Vb[ma+2]=Tb.z;j.bindBuffer(j.ARRAY_BUFFER,Ya.__webglVertexBuffer);j.bufferData(j.ARRAY_BUFFER,Vb,Sb)}if(bc){for(Jb=0;Jb<$b;Jb++)Ub=Hb[Jb],ma=3*Jb,Wb[ma]=Ub.r,Wb[ma+1]=Ub.g,Wb[ma+2]=Ub.b;j.bindBuffer(j.ARRAY_BUFFER,Ya.__webglColorBuffer);j.bufferData(j.ARRAY_BUFFER,Wb,Sb)}if(cc){for(Kb=0;Kb<ac;Kb++)lc[Kb]=kc[Kb];j.bindBuffer(j.ARRAY_BUFFER,Ya.__webglLineDistanceBuffer);
+j.bufferData(j.ARRAY_BUFFER,lc,Sb)}if(gc){Xb=0;for(mc=gc.length;Xb<mc;Xb++)if(ja=gc[Xb],ja.needsUpdate&&(void 0===ja.boundTo||"vertices"===ja.boundTo)){ma=0;zb=ja.value.length;if(1===ja.size)for(za=0;za<zb;za++)ja.array[za]=ja.value[za];else if(2===ja.size)for(za=0;za<zb;za++)Ja=ja.value[za],ja.array[ma]=Ja.x,ja.array[ma+1]=Ja.y,ma+=2;else if(3===ja.size)if("c"===ja.type)for(za=0;za<zb;za++)Ja=ja.value[za],ja.array[ma]=Ja.r,ja.array[ma+1]=Ja.g,ja.array[ma+2]=Ja.b,ma+=3;else for(za=0;za<zb;za++)Ja=
+ja.value[za],ja.array[ma]=Ja.x,ja.array[ma+1]=Ja.y,ja.array[ma+2]=Ja.z,ma+=3;else if(4===ja.size)for(za=0;za<zb;za++)Ja=ja.value[za],ja.array[ma]=Ja.x,ja.array[ma+1]=Ja.y,ja.array[ma+2]=Ja.z,ja.array[ma+3]=Ja.w,ma+=4;j.bindBuffer(j.ARRAY_BUFFER,ja.buffer);j.bufferData(j.ARRAY_BUFFER,ja.array,Sb)}}}l.verticesNeedUpdate=!1;l.colorsNeedUpdate=!1;l.lineDistancesNeedUpdate=!1;t.attributes&&r(t)}else if(i instanceof THREE.ParticleSystem){t=d(i,l);p=t.attributes&&n(t);if(l.verticesNeedUpdate||l.colorsNeedUpdate||
+i.sortParticles||p){var lb=l,hc=j.DYNAMIC_DRAW,Lb=i,Ka=void 0,mb=void 0,nb=void 0,T=void 0,ob=void 0,ub=void 0,Yb=lb.vertices,ic=Yb.length,jc=lb.colors,nc=jc.length,wb=lb.__vertexArray,xb=lb.__colorArray,qb=lb.__sortArray,oc=lb.verticesNeedUpdate,pc=lb.colorsNeedUpdate,rb=lb.__webglCustomAttributesList,$a=void 0,Ab=void 0,Y=void 0,ab=void 0,ha=void 0,S=void 0;if(Lb.sortParticles){gb.copy(ra);gb.multiply(Lb.matrixWorld);for(Ka=0;Ka<ic;Ka++)nb=Yb[Ka],sa.copy(nb),sa.applyProjection(gb),qb[Ka]=[sa.z,
+Ka];qb.sort(k);for(Ka=0;Ka<ic;Ka++)nb=Yb[qb[Ka][1]],T=3*Ka,wb[T]=nb.x,wb[T+1]=nb.y,wb[T+2]=nb.z;for(mb=0;mb<nc;mb++)T=3*mb,ub=jc[qb[mb][1]],xb[T]=ub.r,xb[T+1]=ub.g,xb[T+2]=ub.b;if(rb){$a=0;for(Ab=rb.length;$a<Ab;$a++)if(S=rb[$a],void 0===S.boundTo||"vertices"===S.boundTo)if(T=0,ab=S.value.length,1===S.size)for(Y=0;Y<ab;Y++)ob=qb[Y][1],S.array[Y]=S.value[ob];else if(2===S.size)for(Y=0;Y<ab;Y++)ob=qb[Y][1],ha=S.value[ob],S.array[T]=ha.x,S.array[T+1]=ha.y,T+=2;else if(3===S.size)if("c"===S.type)for(Y=
+0;Y<ab;Y++)ob=qb[Y][1],ha=S.value[ob],S.array[T]=ha.r,S.array[T+1]=ha.g,S.array[T+2]=ha.b,T+=3;else for(Y=0;Y<ab;Y++)ob=qb[Y][1],ha=S.value[ob],S.array[T]=ha.x,S.array[T+1]=ha.y,S.array[T+2]=ha.z,T+=3;else if(4===S.size)for(Y=0;Y<ab;Y++)ob=qb[Y][1],ha=S.value[ob],S.array[T]=ha.x,S.array[T+1]=ha.y,S.array[T+2]=ha.z,S.array[T+3]=ha.w,T+=4}}else{if(oc)for(Ka=0;Ka<ic;Ka++)nb=Yb[Ka],T=3*Ka,wb[T]=nb.x,wb[T+1]=nb.y,wb[T+2]=nb.z;if(pc)for(mb=0;mb<nc;mb++)ub=jc[mb],T=3*mb,xb[T]=ub.r,xb[T+1]=ub.g,xb[T+2]=ub.b;
+if(rb){$a=0;for(Ab=rb.length;$a<Ab;$a++)if(S=rb[$a],S.needsUpdate&&(void 0===S.boundTo||"vertices"===S.boundTo))if(ab=S.value.length,T=0,1===S.size)for(Y=0;Y<ab;Y++)S.array[Y]=S.value[Y];else if(2===S.size)for(Y=0;Y<ab;Y++)ha=S.value[Y],S.array[T]=ha.x,S.array[T+1]=ha.y,T+=2;else if(3===S.size)if("c"===S.type)for(Y=0;Y<ab;Y++)ha=S.value[Y],S.array[T]=ha.r,S.array[T+1]=ha.g,S.array[T+2]=ha.b,T+=3;else for(Y=0;Y<ab;Y++)ha=S.value[Y],S.array[T]=ha.x,S.array[T+1]=ha.y,S.array[T+2]=ha.z,T+=3;else if(4===
+S.size)for(Y=0;Y<ab;Y++)ha=S.value[Y],S.array[T]=ha.x,S.array[T+1]=ha.y,S.array[T+2]=ha.z,S.array[T+3]=ha.w,T+=4}}if(oc||Lb.sortParticles)j.bindBuffer(j.ARRAY_BUFFER,lb.__webglVertexBuffer),j.bufferData(j.ARRAY_BUFFER,wb,hc);if(pc||Lb.sortParticles)j.bindBuffer(j.ARRAY_BUFFER,lb.__webglColorBuffer),j.bufferData(j.ARRAY_BUFFER,xb,hc);if(rb){$a=0;for(Ab=rb.length;$a<Ab;$a++)if(S=rb[$a],S.needsUpdate||Lb.sortParticles)j.bindBuffer(j.ARRAY_BUFFER,S.buffer),j.bufferData(j.ARRAY_BUFFER,S.array,hc)}}l.verticesNeedUpdate=
+!1;l.colorsNeedUpdate=!1;t.attributes&&r(t)}}};this.initMaterial=function(a,b,c,d){var e,f,h,g;a.addEventListener("dispose",Fb);var i,k,l,m,p;a instanceof THREE.MeshDepthMaterial?p="depth":a instanceof THREE.MeshNormalMaterial?p="normal":a instanceof THREE.MeshBasicMaterial?p="basic":a instanceof THREE.MeshLambertMaterial?p="lambert":a instanceof THREE.MeshPhongMaterial?p="phong":a instanceof THREE.LineBasicMaterial?p="basic":a instanceof THREE.LineDashedMaterial?p="dashed":a instanceof THREE.ParticleSystemMaterial&&
+(p="particle_basic");if(p){var n=THREE.ShaderLib[p];a.uniforms=THREE.UniformsUtils.clone(n.uniforms);a.vertexShader=n.vertexShader;a.fragmentShader=n.fragmentShader}var q=e=0,r=0,t=n=0;for(f=b.length;t<f;t++)h=b[t],h.onlyShadow||(h instanceof THREE.DirectionalLight&&e++,h instanceof THREE.PointLight&&q++,h instanceof THREE.SpotLight&&r++,h instanceof THREE.HemisphereLight&&n++);f=q;h=r;g=n;r=n=0;for(q=b.length;r<q;r++)t=b[r],t.castShadow&&(t instanceof THREE.SpotLight&&n++,t instanceof THREE.DirectionalLight&&
+!t.shadowCascade&&n++);m=n;yb&&d&&d.useVertexTexture?l=1024:(b=j.getParameter(j.MAX_VERTEX_UNIFORM_VECTORS),b=Math.floor((b-20)/4),void 0!==d&&d instanceof THREE.SkinnedMesh&&(b=Math.min(d.bones.length,b),b<d.bones.length&&console.warn("WebGLRenderer: too many bones - "+d.bones.length+", this GPU supports just "+b+" (try OpenGL instead of ANGLE)")),l=b);a:{var r=a.fragmentShader,q=a.vertexShader,n=a.uniforms,b=a.attributes,t=a.defines,c={map:!!a.map,envMap:!!a.envMap,lightMap:!!a.lightMap,bumpMap:!!a.bumpMap,
+normalMap:!!a.normalMap,specularMap:!!a.specularMap,vertexColors:a.vertexColors,fog:c,useFog:a.fog,fogExp:c instanceof THREE.FogExp2,sizeAttenuation:a.sizeAttenuation,skinning:a.skinning,maxBones:l,useVertexTexture:yb&&d&&d.useVertexTexture,morphTargets:a.morphTargets,morphNormals:a.morphNormals,maxMorphTargets:this.maxMorphTargets,maxMorphNormals:this.maxMorphNormals,maxDirLights:e,maxPointLights:f,maxSpotLights:h,maxHemiLights:g,maxShadows:m,shadowMapEnabled:this.shadowMapEnabled&&d.receiveShadow,
+shadowMapType:this.shadowMapType,shadowMapDebug:this.shadowMapDebug,shadowMapCascade:this.shadowMapCascade,alphaTest:a.alphaTest,metal:a.metal,perPixel:a.perPixel,wrapAround:a.wrapAround,doubleSided:a.side===THREE.DoubleSide,flipSided:a.side===THREE.BackSide},d=a.index0AttributeName,s,u,w;e=[];p?e.push(p):(e.push(r),e.push(q));for(u in t)e.push(u),e.push(t[u]);for(s in c)e.push(s),e.push(c[s]);p=e.join();s=0;for(u=ca.length;s<u;s++)if(e=ca[s],e.code===p){e.usedTimes++;k=e.program;break a}s="SHADOWMAP_TYPE_BASIC";
+c.shadowMapType===THREE.PCFShadowMap?s="SHADOWMAP_TYPE_PCF":c.shadowMapType===THREE.PCFSoftShadowMap&&(s="SHADOWMAP_TYPE_PCF_SOFT");u=[];for(w in t)e=t[w],!1!==e&&(e="#define "+w+" "+e,u.push(e));e=u.join("\n");w=j.createProgram();u=["precision "+R+" float;","precision "+R+" int;",e,Bb?"#define VERTEX_TEXTURES":"",K.gammaInput?"#define GAMMA_INPUT":"",K.gammaOutput?"#define GAMMA_OUTPUT":"",K.physicallyBasedShading?"#define PHYSICALLY_BASED_SHADING":"","#define MAX_DIR_LIGHTS "+c.maxDirLights,"#define MAX_POINT_LIGHTS "+
+c.maxPointLights,"#define MAX_SPOT_LIGHTS "+c.maxSpotLights,"#define MAX_HEMI_LIGHTS "+c.maxHemiLights,"#define MAX_SHADOWS "+c.maxShadows,"#define MAX_BONES "+c.maxBones,c.map?"#define USE_MAP":"",c.envMap?"#define USE_ENVMAP":"",c.lightMap?"#define USE_LIGHTMAP":"",c.bumpMap?"#define USE_BUMPMAP":"",c.normalMap?"#define USE_NORMALMAP":"",c.specularMap?"#define USE_SPECULARMAP":"",c.vertexColors?"#define USE_COLOR":"",c.skinning?"#define USE_SKINNING":"",c.useVertexTexture?"#define BONE_TEXTURE":
+"",c.morphTargets?"#define USE_MORPHTARGETS":"",c.morphNormals?"#define USE_MORPHNORMALS":"",c.perPixel?"#define PHONG_PER_PIXEL":"",c.wrapAround?"#define WRAP_AROUND":"",c.doubleSided?"#define DOUBLE_SIDED":"",c.flipSided?"#define FLIP_SIDED":"",c.shadowMapEnabled?"#define USE_SHADOWMAP":"",c.shadowMapEnabled?"#define "+s:"",c.shadowMapDebug?"#define SHADOWMAP_DEBUG":"",c.shadowMapCascade?"#define SHADOWMAP_CASCADE":"",c.sizeAttenuation?"#define USE_SIZEATTENUATION":"","uniform mat4 modelMatrix;\nuniform mat4 modelViewMatrix;\nuniform mat4 projectionMatrix;\nuniform mat4 viewMatrix;\nuniform mat3 normalMatrix;\nuniform vec3 cameraPosition;\nattribute vec3 position;\nattribute vec3 normal;\nattribute vec2 uv;\nattribute vec2 uv2;\n#ifdef USE_COLOR\nattribute vec3 color;\n#endif\n#ifdef USE_MORPHTARGETS\nattribute vec3 morphTarget0;\nattribute vec3 morphTarget1;\nattribute vec3 morphTarget2;\nattribute vec3 morphTarget3;\n#ifdef USE_MORPHNORMALS\nattribute vec3 morphNormal0;\nattribute vec3 morphNormal1;\nattribute vec3 morphNormal2;\nattribute vec3 morphNormal3;\n#else\nattribute vec3 morphTarget4;\nattribute vec3 morphTarget5;\nattribute vec3 morphTarget6;\nattribute vec3 morphTarget7;\n#endif\n#endif\n#ifdef USE_SKINNING\nattribute vec4 skinIndex;\nattribute vec4 skinWeight;\n#endif\n"].join("\n");
+s=["precision "+R+" float;","precision "+R+" int;",c.bumpMap||c.normalMap?"#extension GL_OES_standard_derivatives : enable":"",e,"#define MAX_DIR_LIGHTS "+c.maxDirLights,"#define MAX_POINT_LIGHTS "+c.maxPointLights,"#define MAX_SPOT_LIGHTS "+c.maxSpotLights,"#define MAX_HEMI_LIGHTS "+c.maxHemiLights,"#define MAX_SHADOWS "+c.maxShadows,c.alphaTest?"#define ALPHATEST "+c.alphaTest:"",K.gammaInput?"#define GAMMA_INPUT":"",K.gammaOutput?"#define GAMMA_OUTPUT":"",K.physicallyBasedShading?"#define PHYSICALLY_BASED_SHADING":
+"",c.useFog&&c.fog?"#define USE_FOG":"",c.useFog&&c.fogExp?"#define FOG_EXP2":"",c.map?"#define USE_MAP":"",c.envMap?"#define USE_ENVMAP":"",c.lightMap?"#define USE_LIGHTMAP":"",c.bumpMap?"#define USE_BUMPMAP":"",c.normalMap?"#define USE_NORMALMAP":"",c.specularMap?"#define USE_SPECULARMAP":"",c.vertexColors?"#define USE_COLOR":"",c.metal?"#define METAL":"",c.perPixel?"#define PHONG_PER_PIXEL":"",c.wrapAround?"#define WRAP_AROUND":"",c.doubleSided?"#define DOUBLE_SIDED":"",c.flipSided?"#define FLIP_SIDED":
+"",c.shadowMapEnabled?"#define USE_SHADOWMAP":"",c.shadowMapEnabled?"#define "+s:"",c.shadowMapDebug?"#define SHADOWMAP_DEBUG":"",c.shadowMapCascade?"#define SHADOWMAP_CASCADE":"","uniform mat4 viewMatrix;\nuniform vec3 cameraPosition;\n"].join("\n");u=C("vertex",u+q);s=C("fragment",s+r);j.attachShader(w,u);j.attachShader(w,s);d&&j.bindAttribLocation(w,0,d);j.linkProgram(w);j.getProgramParameter(w,j.LINK_STATUS)||(console.error("Could not initialise shader\nVALIDATE_STATUS: "+j.getProgramParameter(w,
+j.VALIDATE_STATUS)+", gl error ["+j.getError()+"]"),console.error("Program Info Log: "+j.getProgramInfoLog(w)));j.deleteShader(s);j.deleteShader(u);w.uniforms={};w.attributes={};var v;s="viewMatrix modelViewMatrix projectionMatrix normalMatrix modelMatrix cameraPosition morphTargetInfluences".split(" ");c.useVertexTexture?(s.push("boneTexture"),s.push("boneTextureWidth"),s.push("boneTextureHeight")):s.push("boneGlobalMatrices");for(v in n)s.push(v);v=s;s=0;for(u=v.length;s<u;s++)n=v[s],w.uniforms[n]=
+j.getUniformLocation(w,n);s="position normal uv uv2 tangent color skinIndex skinWeight lineDistance".split(" ");for(v=0;v<c.maxMorphTargets;v++)s.push("morphTarget"+v);for(v=0;v<c.maxMorphNormals;v++)s.push("morphNormal"+v);for(k in b)s.push(k);k=s;v=0;for(b=k.length;v<b;v++)s=k[v],w.attributes[s]=j.getAttribLocation(w,s);w.id=Fa++;ca.push({program:w,code:p,usedTimes:1});K.info.memory.programs=ca.length;k=w}a.program=k;v=a.program.attributes;if(a.morphTargets){a.numSupportedMorphTargets=0;b="morphTarget";
+for(k=0;k<this.maxMorphTargets;k++)w=b+k,0<=v[w]&&a.numSupportedMorphTargets++}if(a.morphNormals){a.numSupportedMorphNormals=0;b="morphNormal";for(k=0;k<this.maxMorphNormals;k++)w=b+k,0<=v[w]&&a.numSupportedMorphNormals++}a.uniformsList=[];for(i in a.uniforms)a.uniformsList.push([a.uniforms[i],i])};this.setFaceCulling=function(a,b){a===THREE.CullFaceNone?j.disable(j.CULL_FACE):(b===THREE.FrontFaceDirectionCW?j.frontFace(j.CW):j.frontFace(j.CCW),a===THREE.CullFaceBack?j.cullFace(j.BACK):a===THREE.CullFaceFront?
+j.cullFace(j.FRONT):j.cullFace(j.FRONT_AND_BACK),j.enable(j.CULL_FACE))};this.setMaterialFaces=function(a){var b=a.side===THREE.DoubleSide,a=a.side===THREE.BackSide;Z!==b&&(b?j.disable(j.CULL_FACE):j.enable(j.CULL_FACE),Z=b);U!==a&&(a?j.frontFace(j.CW):j.frontFace(j.CCW),U=a)};this.setDepthTest=function(a){Ga!==a&&(a?j.enable(j.DEPTH_TEST):j.disable(j.DEPTH_TEST),Ga=a)};this.setDepthWrite=function(a){fa!==a&&(j.depthMask(a),fa=a)};this.setBlending=function(a,b,c,d){a!==ka&&(a===THREE.NoBlending?j.disable(j.BLEND):
+a===THREE.AdditiveBlending?(j.enable(j.BLEND),j.blendEquation(j.FUNC_ADD),j.blendFunc(j.SRC_ALPHA,j.ONE)):a===THREE.SubtractiveBlending?(j.enable(j.BLEND),j.blendEquation(j.FUNC_ADD),j.blendFunc(j.ZERO,j.ONE_MINUS_SRC_COLOR)):a===THREE.MultiplyBlending?(j.enable(j.BLEND),j.blendEquation(j.FUNC_ADD),j.blendFunc(j.ZERO,j.SRC_COLOR)):a===THREE.CustomBlending?j.enable(j.BLEND):(j.enable(j.BLEND),j.blendEquationSeparate(j.FUNC_ADD,j.FUNC_ADD),j.blendFuncSeparate(j.SRC_ALPHA,j.ONE_MINUS_SRC_ALPHA,j.ONE,
+j.ONE_MINUS_SRC_ALPHA)),ka=a);if(a===THREE.CustomBlending){if(b!==ta&&(j.blendEquation(v(b)),ta=b),c!==ia||d!==La)j.blendFunc(v(c),v(d)),ia=c,La=d}else La=ia=ta=null};this.setTexture=function(a,b){if(a.needsUpdate){a.__webglInit||(a.__webglInit=!0,a.addEventListener("dispose",Db),a.__webglTexture=j.createTexture(),K.info.memory.textures++);j.activeTexture(j.TEXTURE0+b);j.bindTexture(j.TEXTURE_2D,a.__webglTexture);j.pixelStorei(j.UNPACK_FLIP_Y_WEBGL,a.flipY);j.pixelStorei(j.UNPACK_PREMULTIPLY_ALPHA_WEBGL,
+a.premultiplyAlpha);j.pixelStorei(j.UNPACK_ALIGNMENT,a.unpackAlignment);var c=a.image,d=0===(c.width&c.width-1)&&0===(c.height&c.height-1),e=v(a.format),f=v(a.type);E(j.TEXTURE_2D,a,d);var h=a.mipmaps;if(a instanceof THREE.DataTexture)if(0<h.length&&d){for(var g=0,i=h.length;g<i;g++)c=h[g],j.texImage2D(j.TEXTURE_2D,g,e,c.width,c.height,0,e,f,c.data);a.generateMipmaps=!1}else j.texImage2D(j.TEXTURE_2D,0,e,c.width,c.height,0,e,f,c.data);else if(a instanceof THREE.CompressedTexture){g=0;for(i=h.length;g<
+i;g++)c=h[g],a.format!==THREE.RGBAFormat?j.compressedTexImage2D(j.TEXTURE_2D,g,e,c.width,c.height,0,c.data):j.texImage2D(j.TEXTURE_2D,g,e,c.width,c.height,0,e,f,c.data)}else if(0<h.length&&d){g=0;for(i=h.length;g<i;g++)c=h[g],j.texImage2D(j.TEXTURE_2D,g,e,e,f,c);a.generateMipmaps=!1}else j.texImage2D(j.TEXTURE_2D,0,e,e,f,a.image);a.generateMipmaps&&d&&j.generateMipmap(j.TEXTURE_2D);a.needsUpdate=!1;if(a.onUpdate)a.onUpdate()}else j.activeTexture(j.TEXTURE0+b),j.bindTexture(j.TEXTURE_2D,a.__webglTexture)};
+this.setRenderTarget=function(a){var b=a instanceof THREE.WebGLRenderTargetCube;if(a&&!a.__webglFramebuffer){void 0===a.depthBuffer&&(a.depthBuffer=!0);void 0===a.stencilBuffer&&(a.stencilBuffer=!0);a.addEventListener("dispose",Eb);a.__webglTexture=j.createTexture();K.info.memory.textures++;var c=0===(a.width&a.width-1)&&0===(a.height&a.height-1),d=v(a.format),e=v(a.type);if(b){a.__webglFramebuffer=[];a.__webglRenderbuffer=[];j.bindTexture(j.TEXTURE_CUBE_MAP,a.__webglTexture);E(j.TEXTURE_CUBE_MAP,
+a,c);for(var f=0;6>f;f++){a.__webglFramebuffer[f]=j.createFramebuffer();a.__webglRenderbuffer[f]=j.createRenderbuffer();j.texImage2D(j.TEXTURE_CUBE_MAP_POSITIVE_X+f,0,d,a.width,a.height,0,d,e,null);var h=a,g=j.TEXTURE_CUBE_MAP_POSITIVE_X+f;j.bindFramebuffer(j.FRAMEBUFFER,a.__webglFramebuffer[f]);j.framebufferTexture2D(j.FRAMEBUFFER,j.COLOR_ATTACHMENT0,g,h.__webglTexture,0);I(a.__webglRenderbuffer[f],a)}c&&j.generateMipmap(j.TEXTURE_CUBE_MAP)}else a.__webglFramebuffer=j.createFramebuffer(),a.__webglRenderbuffer=
+a.shareDepthFrom?a.shareDepthFrom.__webglRenderbuffer:j.createRenderbuffer(),j.bindTexture(j.TEXTURE_2D,a.__webglTexture),E(j.TEXTURE_2D,a,c),j.texImage2D(j.TEXTURE_2D,0,d,a.width,a.height,0,d,e,null),d=j.TEXTURE_2D,j.bindFramebuffer(j.FRAMEBUFFER,a.__webglFramebuffer),j.framebufferTexture2D(j.FRAMEBUFFER,j.COLOR_ATTACHMENT0,d,a.__webglTexture,0),a.shareDepthFrom?a.depthBuffer&&!a.stencilBuffer?j.framebufferRenderbuffer(j.FRAMEBUFFER,j.DEPTH_ATTACHMENT,j.RENDERBUFFER,a.__webglRenderbuffer):a.depthBuffer&&
+a.stencilBuffer&&j.framebufferRenderbuffer(j.FRAMEBUFFER,j.DEPTH_STENCIL_ATTACHMENT,j.RENDERBUFFER,a.__webglRenderbuffer):I(a.__webglRenderbuffer,a),c&&j.generateMipmap(j.TEXTURE_2D);b?j.bindTexture(j.TEXTURE_CUBE_MAP,null):j.bindTexture(j.TEXTURE_2D,null);j.bindRenderbuffer(j.RENDERBUFFER,null);j.bindFramebuffer(j.FRAMEBUFFER,null)}a?(b=b?a.__webglFramebuffer[a.activeCubeFace]:a.__webglFramebuffer,c=a.width,a=a.height,e=d=0):(b=null,c=Ma,a=fb,d=bb,e=cb);b!==da&&(j.bindFramebuffer(j.FRAMEBUFFER,b),
+j.viewport(d,e,c,a),da=b);sb=c;pb=a};this.shadowMapPlugin=new THREE.ShadowMapPlugin;this.addPrePlugin(this.shadowMapPlugin);this.addPostPlugin(new THREE.SpritePlugin);this.addPostPlugin(new THREE.LensFlarePlugin)};THREE.WebGLRenderTarget=function(a,b,c){this.width=a;this.height=b;c=c||{};this.wrapS=void 0!==c.wrapS?c.wrapS:THREE.ClampToEdgeWrapping;this.wrapT=void 0!==c.wrapT?c.wrapT:THREE.ClampToEdgeWrapping;this.magFilter=void 0!==c.magFilter?c.magFilter:THREE.LinearFilter;this.minFilter=void 0!==c.minFilter?c.minFilter:THREE.LinearMipMapLinearFilter;this.anisotropy=void 0!==c.anisotropy?c.anisotropy:1;this.offset=new THREE.Vector2(0,0);this.repeat=new THREE.Vector2(1,1);this.format=void 0!==c.format?c.format:
+THREE.RGBAFormat;this.type=void 0!==c.type?c.type:THREE.UnsignedByteType;this.depthBuffer=void 0!==c.depthBuffer?c.depthBuffer:!0;this.stencilBuffer=void 0!==c.stencilBuffer?c.stencilBuffer:!0;this.generateMipmaps=!0;this.shareDepthFrom=null};
+THREE.WebGLRenderTarget.prototype={constructor:THREE.WebGLRenderTarget,clone:function(){var a=new THREE.WebGLRenderTarget(this.width,this.height);a.wrapS=this.wrapS;a.wrapT=this.wrapT;a.magFilter=this.magFilter;a.minFilter=this.minFilter;a.anisotropy=this.anisotropy;a.offset.copy(this.offset);a.repeat.copy(this.repeat);a.format=this.format;a.type=this.type;a.depthBuffer=this.depthBuffer;a.stencilBuffer=this.stencilBuffer;a.generateMipmaps=this.generateMipmaps;a.shareDepthFrom=this.shareDepthFrom;
+return a},dispose:function(){this.dispatchEvent({type:"dispose"})}};THREE.EventDispatcher.prototype.apply(THREE.WebGLRenderTarget.prototype);THREE.WebGLRenderTargetCube=function(a,b,c){THREE.WebGLRenderTarget.call(this,a,b,c);this.activeCubeFace=0};THREE.WebGLRenderTargetCube.prototype=Object.create(THREE.WebGLRenderTarget.prototype);THREE.RenderableVertex=function(){this.positionWorld=new THREE.Vector3;this.positionScreen=new THREE.Vector4;this.visible=!0};THREE.RenderableVertex.prototype.copy=function(a){this.positionWorld.copy(a.positionWorld);this.positionScreen.copy(a.positionScreen)};THREE.RenderableFace3=function(){this.id=0;this.v1=new THREE.RenderableVertex;this.v2=new THREE.RenderableVertex;this.v3=new THREE.RenderableVertex;this.centroidModel=new THREE.Vector3;this.normalModel=new THREE.Vector3;this.normalModelView=new THREE.Vector3;this.vertexNormalsLength=0;this.vertexNormalsModel=[new THREE.Vector3,new THREE.Vector3,new THREE.Vector3];this.vertexNormalsModelView=[new THREE.Vector3,new THREE.Vector3,new THREE.Vector3];this.material=this.color=null;this.uvs=[[]];this.z=
+0};THREE.RenderableObject=function(){this.id=0;this.object=null;this.z=0};THREE.RenderableSprite=function(){this.id=0;this.object=null;this.rotation=this.z=this.y=this.x=0;this.scale=new THREE.Vector2;this.material=null};THREE.RenderableLine=function(){this.id=0;this.v1=new THREE.RenderableVertex;this.v2=new THREE.RenderableVertex;this.vertexColors=[new THREE.Color,new THREE.Color];this.material=null;this.z=0};THREE.GeometryUtils={merge:function(a,b,c){var d,e,f=a.vertices.length,h=b instanceof THREE.Mesh?b.geometry:b,g=a.vertices,i=h.vertices,k=a.faces,m=h.faces,a=a.faceVertexUvs[0],h=h.faceVertexUvs[0];void 0===c&&(c=0);b instanceof THREE.Mesh&&(b.matrixAutoUpdate&&b.updateMatrix(),d=b.matrix,e=(new THREE.Matrix3).getNormalMatrix(d));for(var b=0,l=i.length;b<l;b++){var p=i[b].clone();d&&p.applyMatrix4(d);g.push(p)}b=0;for(l=m.length;b<l;b++){var p=m[b],s,t,n=p.vertexNormals,r=p.vertexColors;s=new THREE.Face3(p.a+
+f,p.b+f,p.c+f);s.normal.copy(p.normal);e&&s.normal.applyMatrix3(e).normalize();g=0;for(i=n.length;g<i;g++)t=n[g].clone(),e&&t.applyMatrix3(e).normalize(),s.vertexNormals.push(t);s.color.copy(p.color);g=0;for(i=r.length;g<i;g++)t=r[g],s.vertexColors.push(t.clone());s.materialIndex=p.materialIndex+c;s.centroid.copy(p.centroid);d&&s.centroid.applyMatrix4(d);k.push(s)}b=0;for(l=h.length;b<l;b++){c=h[b];d=[];g=0;for(i=c.length;g<i;g++)d.push(new THREE.Vector2(c[g].x,c[g].y));a.push(d)}},randomPointInTriangle:function(){var a=
+new THREE.Vector3;return function(b,c,d){var e=new THREE.Vector3,f=THREE.Math.random16(),h=THREE.Math.random16();1<f+h&&(f=1-f,h=1-h);var g=1-f-h;e.copy(b);e.multiplyScalar(f);a.copy(c);a.multiplyScalar(h);e.add(a);a.copy(d);a.multiplyScalar(g);e.add(a);return e}}(),randomPointInFace:function(a,b){return THREE.GeometryUtils.randomPointInTriangle(b.vertices[a.a],b.vertices[a.b],b.vertices[a.c])},randomPointsInGeometry:function(a,b){function c(a){function b(c,d){if(d<c)return c;var e=c+Math.floor((d-
+c)/2);return k[e]>a?b(c,e-1):k[e]<a?b(e+1,d):e}return b(0,k.length-1)}var d,e,f=a.faces,h=a.vertices,g=f.length,i=0,k=[],m,l,p;for(e=0;e<g;e++)d=f[e],m=h[d.a],l=h[d.b],p=h[d.c],d._area=THREE.GeometryUtils.triangleArea(m,l,p),i+=d._area,k[e]=i;d=[];for(e=0;e<b;e++)h=THREE.Math.random16()*i,h=c(h),d[e]=THREE.GeometryUtils.randomPointInFace(f[h],a,!0);return d},triangleArea:function(){var a=new THREE.Vector3,b=new THREE.Vector3;return function(c,d,e){a.subVectors(d,c);b.subVectors(e,c);a.cross(b);return 0.5*
+a.length()}}(),center:function(a){a.computeBoundingBox();var b=a.boundingBox,c=new THREE.Vector3;c.addVectors(b.min,b.max);c.multiplyScalar(-0.5);a.applyMatrix((new THREE.Matrix4).makeTranslation(c.x,c.y,c.z));a.computeBoundingBox();return c},triangulateQuads:function(a){var b,c,d,e,f=[],h=[];b=0;for(c=a.faceVertexUvs.length;b<c;b++)h[b]=[];b=0;for(c=a.faces.length;b<c;b++){f.push(a.faces[b]);d=0;for(e=a.faceVertexUvs.length;d<e;d++)h[d].push(a.faceVertexUvs[d][b])}a.faces=f;a.faceVertexUvs=h;a.computeCentroids();
+a.computeFaceNormals();a.computeVertexNormals();a.hasTangents&&a.computeTangents()}};THREE.ImageUtils={crossOrigin:"anonymous",loadTexture:function(a,b,c){var d=new THREE.ImageLoader;d.crossOrigin=this.crossOrigin;var e=new THREE.Texture(void 0,b),b=d.load(a,function(){e.needsUpdate=!0;c&&c(e)});e.image=b;e.sourceFile=a;return e},loadCompressedTexture:function(a,b,c,d){var e=new THREE.CompressedTexture;e.mapping=b;var f=new XMLHttpRequest;f.onload=function(){var a=THREE.ImageUtils.parseDDS(f.response,!0);e.format=a.format;e.mipmaps=a.mipmaps;e.image.width=a.width;e.image.height=a.height;
+e.generateMipmaps=!1;e.needsUpdate=!0;c&&c(e)};f.onerror=d;f.open("GET",a,!0);f.responseType="arraybuffer";f.send(null);return e},loadTextureCube:function(a,b,c,d){var e=[];e.loadCount=0;var f=new THREE.Texture;f.image=e;void 0!==b&&(f.mapping=b);f.flipY=!1;for(var b=0,h=a.length;b<h;++b){var g=new Image;e[b]=g;g.onload=function(){e.loadCount+=1;6===e.loadCount&&(f.needsUpdate=!0,c&&c(f))};g.onerror=d;g.crossOrigin=this.crossOrigin;g.src=a[b]}return f},loadCompressedTextureCube:function(a,b,c,d){var e=
+[];e.loadCount=0;var f=new THREE.CompressedTexture;f.image=e;void 0!==b&&(f.mapping=b);f.flipY=!1;f.generateMipmaps=!1;b=function(a,b){return function(){var d=THREE.ImageUtils.parseDDS(a.response,!0);b.format=d.format;b.mipmaps=d.mipmaps;b.width=d.width;b.height=d.height;e.loadCount+=1;6===e.loadCount&&(f.format=d.format,f.needsUpdate=!0,c&&c(f))}};if(a instanceof Array)for(var h=0,g=a.length;h<g;++h){var i={};e[h]=i;var k=new XMLHttpRequest;k.onload=b(k,i);k.onerror=d;i=a[h];k.open("GET",i,!0);k.responseType=
+"arraybuffer";k.send(null)}else k=new XMLHttpRequest,k.onload=function(){var a=THREE.ImageUtils.parseDDS(k.response,!0);if(a.isCubemap){for(var b=a.mipmaps.length/a.mipmapCount,d=0;d<b;d++){e[d]={mipmaps:[]};for(var h=0;h<a.mipmapCount;h++)e[d].mipmaps.push(a.mipmaps[d*a.mipmapCount+h]),e[d].format=a.format,e[d].width=a.width,e[d].height=a.height}f.format=a.format;f.needsUpdate=!0;c&&c(f)}},k.onerror=d,k.open("GET",a,!0),k.responseType="arraybuffer",k.send(null);return f},loadDDSTexture:function(a,
+b,c,d){var e=[];e.loadCount=0;var f=new THREE.CompressedTexture;f.image=e;void 0!==b&&(f.mapping=b);f.flipY=!1;f.generateMipmaps=!1;var h=new XMLHttpRequest;h.onload=function(){var a=THREE.ImageUtils.parseDDS(h.response,!0);if(a.isCubemap)for(var b=a.mipmaps.length/a.mipmapCount,d=0;d<b;d++){e[d]={mipmaps:[]};for(var m=0;m<a.mipmapCount;m++)e[d].mipmaps.push(a.mipmaps[d*a.mipmapCount+m]),e[d].format=a.format,e[d].width=a.width,e[d].height=a.height}else f.image.width=a.width,f.image.height=a.height,
+f.mipmaps=a.mipmaps;f.format=a.format;f.needsUpdate=!0;c&&c(f)};h.onerror=d;h.open("GET",a,!0);h.responseType="arraybuffer";h.send(null);return f},parseDDS:function(a,b){function c(a){return a.charCodeAt(0)+(a.charCodeAt(1)<<8)+(a.charCodeAt(2)<<16)+(a.charCodeAt(3)<<24)}var d={mipmaps:[],width:0,height:0,format:null,mipmapCount:1},e=c("DXT1"),f=c("DXT3"),h=c("DXT5"),g=new Int32Array(a,0,31);if(542327876!==g[0])return console.error("ImageUtils.parseDDS(): Invalid magic number in DDS header"),d;if(!g[20]&
+4)return console.error("ImageUtils.parseDDS(): Unsupported format, must contain a FourCC code"),d;var i=g[21],k=!1;switch(i){case e:e=8;d.format=THREE.RGB_S3TC_DXT1_Format;break;case f:e=16;d.format=THREE.RGBA_S3TC_DXT3_Format;break;case h:e=16;d.format=THREE.RGBA_S3TC_DXT5_Format;break;default:if(32==g[22]&&g[23]&16711680&&g[24]&65280&&g[25]&255&&g[26]&4278190080)k=!0,e=64,d.format=THREE.RGBAFormat;else return console.error("ImageUtils.parseDDS(): Unsupported FourCC code: ",String.fromCharCode(i&
+255,i>>8&255,i>>16&255,i>>24&255)),d}d.mipmapCount=1;g[2]&131072&&!1!==b&&(d.mipmapCount=Math.max(1,g[7]));d.isCubemap=g[28]&512?!0:!1;d.width=g[4];d.height=g[3];for(var g=g[1]+4,f=d.width,h=d.height,i=d.isCubemap?6:1,m=0;m<i;m++){for(var l=0;l<d.mipmapCount;l++){if(k){var p;p=f;for(var s=h,t=4*p*s,n=new Uint8Array(a,g,t),t=new Uint8Array(t),r=0,q=0,u=0;u<s;u++)for(var w=0;w<p;w++){var z=n[q];q++;var B=n[q];q++;var D=n[q];q++;var x=n[q];q++;t[r]=D;r++;t[r]=B;r++;t[r]=z;r++;t[r]=x;r++}p=t;s=p.length}else s=
+Math.max(4,f)/4*Math.max(4,h)/4*e,p=new Uint8Array(a,g,s);d.mipmaps.push({data:p,width:f,height:h});g+=s;f=Math.max(0.5*f,1);h=Math.max(0.5*h,1)}f=d.width;h=d.height}return d},getNormalMap:function(a,b){var c=function(a){var b=Math.sqrt(a[0]*a[0]+a[1]*a[1]+a[2]*a[2]);return[a[0]/b,a[1]/b,a[2]/b]},b=b|1,d=a.width,e=a.height,f=document.createElement("canvas");f.width=d;f.height=e;var h=f.getContext("2d");h.drawImage(a,0,0);for(var g=h.getImageData(0,0,d,e).data,i=h.createImageData(d,e),k=i.data,m=0;m<
+d;m++)for(var l=0;l<e;l++){var p=0>l-1?0:l-1,s=l+1>e-1?e-1:l+1,t=0>m-1?0:m-1,n=m+1>d-1?d-1:m+1,r=[],q=[0,0,g[4*(l*d+m)]/255*b];r.push([-1,0,g[4*(l*d+t)]/255*b]);r.push([-1,-1,g[4*(p*d+t)]/255*b]);r.push([0,-1,g[4*(p*d+m)]/255*b]);r.push([1,-1,g[4*(p*d+n)]/255*b]);r.push([1,0,g[4*(l*d+n)]/255*b]);r.push([1,1,g[4*(s*d+n)]/255*b]);r.push([0,1,g[4*(s*d+m)]/255*b]);r.push([-1,1,g[4*(s*d+t)]/255*b]);p=[];t=r.length;for(s=0;s<t;s++){var n=r[s],u=r[(s+1)%t],n=[n[0]-q[0],n[1]-q[1],n[2]-q[2]],u=[u[0]-q[0],
+u[1]-q[1],u[2]-q[2]];p.push(c([n[1]*u[2]-n[2]*u[1],n[2]*u[0]-n[0]*u[2],n[0]*u[1]-n[1]*u[0]]))}r=[0,0,0];for(s=0;s<p.length;s++)r[0]+=p[s][0],r[1]+=p[s][1],r[2]+=p[s][2];r[0]/=p.length;r[1]/=p.length;r[2]/=p.length;q=4*(l*d+m);k[q]=255*((r[0]+1)/2)|0;k[q+1]=255*((r[1]+1)/2)|0;k[q+2]=255*r[2]|0;k[q+3]=255}h.putImageData(i,0,0);return f},generateDataTexture:function(a,b,c){for(var d=a*b,e=new Uint8Array(3*d),f=Math.floor(255*c.r),h=Math.floor(255*c.g),c=Math.floor(255*c.b),g=0;g<d;g++)e[3*g]=f,e[3*g+
+1]=h,e[3*g+2]=c;a=new THREE.DataTexture(e,a,b,THREE.RGBFormat);a.needsUpdate=!0;return a}};THREE.SceneUtils={createMultiMaterialObject:function(a,b){for(var c=new THREE.Object3D,d=0,e=b.length;d<e;d++)c.add(new THREE.Mesh(a,b[d]));return c},detach:function(a,b,c){a.applyMatrix(b.matrixWorld);b.remove(a);c.add(a)},attach:function(a,b,c){var d=new THREE.Matrix4;d.getInverse(c.matrixWorld);a.applyMatrix(d);b.remove(a);c.add(a)}};THREE.FontUtils={faces:{},face:"helvetiker",weight:"normal",style:"normal",size:150,divisions:10,getFace:function(){return this.faces[this.face][this.weight][this.style]},loadFace:function(a){var b=a.familyName.toLowerCase();this.faces[b]=this.faces[b]||{};this.faces[b][a.cssFontWeight]=this.faces[b][a.cssFontWeight]||{};this.faces[b][a.cssFontWeight][a.cssFontStyle]=a;return this.faces[b][a.cssFontWeight][a.cssFontStyle]=a},drawText:function(a){for(var b=this.getFace(),c=this.size/b.resolution,d=
+0,e=String(a).split(""),f=e.length,h=[],a=0;a<f;a++){var g=new THREE.Path,g=this.extractGlyphPoints(e[a],b,c,d,g),d=d+g.offset;h.push(g.path)}return{paths:h,offset:d/2}},extractGlyphPoints:function(a,b,c,d,e){var f=[],h,g,i,k,m,l,p,s,t,n,r,q=b.glyphs[a]||b.glyphs["?"];if(q){if(q.o){b=q._cachedOutline||(q._cachedOutline=q.o.split(" "));k=b.length;for(a=0;a<k;)switch(i=b[a++],i){case "m":i=b[a++]*c+d;m=b[a++]*c;e.moveTo(i,m);break;case "l":i=b[a++]*c+d;m=b[a++]*c;e.lineTo(i,m);break;case "q":i=b[a++]*
+c+d;m=b[a++]*c;s=b[a++]*c+d;t=b[a++]*c;e.quadraticCurveTo(s,t,i,m);if(h=f[f.length-1]){l=h.x;p=h.y;h=1;for(g=this.divisions;h<=g;h++){var u=h/g;THREE.Shape.Utils.b2(u,l,s,i);THREE.Shape.Utils.b2(u,p,t,m)}}break;case "b":if(i=b[a++]*c+d,m=b[a++]*c,s=b[a++]*c+d,t=b[a++]*-c,n=b[a++]*c+d,r=b[a++]*-c,e.bezierCurveTo(i,m,s,t,n,r),h=f[f.length-1]){l=h.x;p=h.y;h=1;for(g=this.divisions;h<=g;h++)u=h/g,THREE.Shape.Utils.b3(u,l,s,n,i),THREE.Shape.Utils.b3(u,p,t,r,m)}}}return{offset:q.ha*c,path:e}}}};
+THREE.FontUtils.generateShapes=function(a,b){var b=b||{},c=void 0!==b.curveSegments?b.curveSegments:4,d=void 0!==b.font?b.font:"helvetiker",e=void 0!==b.weight?b.weight:"normal",f=void 0!==b.style?b.style:"normal";THREE.FontUtils.size=void 0!==b.size?b.size:100;THREE.FontUtils.divisions=c;THREE.FontUtils.face=d;THREE.FontUtils.weight=e;THREE.FontUtils.style=f;c=THREE.FontUtils.drawText(a).paths;d=[];e=0;for(f=c.length;e<f;e++)Array.prototype.push.apply(d,c[e].toShapes());return d};
+(function(a){var b=function(a){for(var b=a.length,e=0,f=b-1,h=0;h<b;f=h++)e+=a[f].x*a[h].y-a[h].x*a[f].y;return 0.5*e};a.Triangulate=function(a,d){var e=a.length;if(3>e)return null;var f=[],h=[],g=[],i,k,m;if(0<b(a))for(k=0;k<e;k++)h[k]=k;else for(k=0;k<e;k++)h[k]=e-1-k;var l=2*e;for(k=e-1;2<e;){if(0>=l--){console.log("Warning, unable to triangulate polygon!");break}i=k;e<=i&&(i=0);k=i+1;e<=k&&(k=0);m=k+1;e<=m&&(m=0);var p;a:{var s=p=void 0,t=void 0,n=void 0,r=void 0,q=void 0,u=void 0,w=void 0,z=
+void 0,s=a[h[i]].x,t=a[h[i]].y,n=a[h[k]].x,r=a[h[k]].y,q=a[h[m]].x,u=a[h[m]].y;if(1E-10>(n-s)*(u-t)-(r-t)*(q-s))p=!1;else{var B=void 0,D=void 0,x=void 0,F=void 0,A=void 0,O=void 0,C=void 0,E=void 0,I=void 0,y=void 0,I=E=C=z=w=void 0,B=q-n,D=u-r,x=s-q,F=t-u,A=n-s,O=r-t;for(p=0;p<e;p++)if(!(p===i||p===k||p===m))if(w=a[h[p]].x,z=a[h[p]].y,C=w-s,E=z-t,I=w-n,y=z-r,w-=q,z-=u,I=B*y-D*I,C=A*E-O*C,E=x*z-F*w,-1E-10<=I&&-1E-10<=E&&-1E-10<=C){p=!1;break a}p=!0}}if(p){f.push([a[h[i]],a[h[k]],a[h[m]]]);g.push([h[i],
+h[k],h[m]]);i=k;for(m=k+1;m<e;i++,m++)h[i]=h[m];e--;l=2*e}}return d?g:f};a.Triangulate.area=b;return a})(THREE.FontUtils);self._typeface_js={faces:THREE.FontUtils.faces,loadFace:THREE.FontUtils.loadFace};THREE.typeface_js=self._typeface_js;THREE.Curve=function(){};THREE.Curve.prototype.getPoint=function(){console.log("Warning, getPoint() not implemented!");return null};THREE.Curve.prototype.getPointAt=function(a){a=this.getUtoTmapping(a);return this.getPoint(a)};THREE.Curve.prototype.getPoints=function(a){a||(a=5);var b,c=[];for(b=0;b<=a;b++)c.push(this.getPoint(b/a));return c};THREE.Curve.prototype.getSpacedPoints=function(a){a||(a=5);var b,c=[];for(b=0;b<=a;b++)c.push(this.getPointAt(b/a));return c};
+THREE.Curve.prototype.getLength=function(){var a=this.getLengths();return a[a.length-1]};THREE.Curve.prototype.getLengths=function(a){a||(a=this.__arcLengthDivisions?this.__arcLengthDivisions:200);if(this.cacheArcLengths&&this.cacheArcLengths.length==a+1&&!this.needsUpdate)return this.cacheArcLengths;this.needsUpdate=!1;var b=[],c,d=this.getPoint(0),e,f=0;b.push(0);for(e=1;e<=a;e++)c=this.getPoint(e/a),f+=c.distanceTo(d),b.push(f),d=c;return this.cacheArcLengths=b};
+THREE.Curve.prototype.updateArcLengths=function(){this.needsUpdate=!0;this.getLengths()};THREE.Curve.prototype.getUtoTmapping=function(a,b){var c=this.getLengths(),d=0,e=c.length,f;f=b?b:a*c[e-1];for(var h=0,g=e-1,i;h<=g;)if(d=Math.floor(h+(g-h)/2),i=c[d]-f,0>i)h=d+1;else if(0<i)g=d-1;else{g=d;break}d=g;if(c[d]==f)return d/(e-1);h=c[d];return c=(d+(f-h)/(c[d+1]-h))/(e-1)};THREE.Curve.prototype.getTangent=function(a){var b=a-1E-4,a=a+1E-4;0>b&&(b=0);1<a&&(a=1);b=this.getPoint(b);return this.getPoint(a).clone().sub(b).normalize()};
+THREE.Curve.prototype.getTangentAt=function(a){a=this.getUtoTmapping(a);return this.getTangent(a)};
+THREE.Curve.Utils={tangentQuadraticBezier:function(a,b,c,d){return 2*(1-a)*(c-b)+2*a*(d-c)},tangentCubicBezier:function(a,b,c,d,e){return-3*b*(1-a)*(1-a)+3*c*(1-a)*(1-a)-6*a*c*(1-a)+6*a*d*(1-a)-3*a*a*d+3*a*a*e},tangentSpline:function(a){return 6*a*a-6*a+(3*a*a-4*a+1)+(-6*a*a+6*a)+(3*a*a-2*a)},interpolate:function(a,b,c,d,e){var a=0.5*(c-a),d=0.5*(d-b),f=e*e;return(2*b-2*c+a+d)*e*f+(-3*b+3*c-2*a-d)*f+a*e+b}};
+THREE.Curve.create=function(a,b){a.prototype=Object.create(THREE.Curve.prototype);a.prototype.getPoint=b;return a};THREE.CurvePath=function(){this.curves=[];this.bends=[];this.autoClose=!1};THREE.CurvePath.prototype=Object.create(THREE.Curve.prototype);THREE.CurvePath.prototype.add=function(a){this.curves.push(a)};THREE.CurvePath.prototype.checkConnection=function(){};THREE.CurvePath.prototype.closePath=function(){var a=this.curves[0].getPoint(0),b=this.curves[this.curves.length-1].getPoint(1);a.equals(b)||this.curves.push(new THREE.LineCurve(b,a))};
+THREE.CurvePath.prototype.getPoint=function(a){for(var b=a*this.getLength(),c=this.getCurveLengths(),a=0;a<c.length;){if(c[a]>=b)return b=c[a]-b,a=this.curves[a],b=1-b/a.getLength(),a.getPointAt(b);a++}return null};THREE.CurvePath.prototype.getLength=function(){var a=this.getCurveLengths();return a[a.length-1]};
+THREE.CurvePath.prototype.getCurveLengths=function(){if(this.cacheLengths&&this.cacheLengths.length==this.curves.length)return this.cacheLengths;var a=[],b=0,c,d=this.curves.length;for(c=0;c<d;c++)b+=this.curves[c].getLength(),a.push(b);return this.cacheLengths=a};
+THREE.CurvePath.prototype.getBoundingBox=function(){var a=this.getPoints(),b,c,d,e,f,h;b=c=Number.NEGATIVE_INFINITY;e=f=Number.POSITIVE_INFINITY;var g,i,k,m,l=a[0]instanceof THREE.Vector3;m=l?new THREE.Vector3:new THREE.Vector2;i=0;for(k=a.length;i<k;i++)g=a[i],g.x>b?b=g.x:g.x<e&&(e=g.x),g.y>c?c=g.y:g.y<f&&(f=g.y),l&&(g.z>d?d=g.z:g.z<h&&(h=g.z)),m.add(g);a={minX:e,minY:f,maxX:b,maxY:c,centroid:m.divideScalar(k)};l&&(a.maxZ=d,a.minZ=h);return a};
+THREE.CurvePath.prototype.createPointsGeometry=function(a){a=this.getPoints(a,!0);return this.createGeometry(a)};THREE.CurvePath.prototype.createSpacedPointsGeometry=function(a){a=this.getSpacedPoints(a,!0);return this.createGeometry(a)};THREE.CurvePath.prototype.createGeometry=function(a){for(var b=new THREE.Geometry,c=0;c<a.length;c++)b.vertices.push(new THREE.Vector3(a[c].x,a[c].y,a[c].z||0));return b};THREE.CurvePath.prototype.addWrapPath=function(a){this.bends.push(a)};
+THREE.CurvePath.prototype.getTransformedPoints=function(a,b){var c=this.getPoints(a),d,e;b||(b=this.bends);d=0;for(e=b.length;d<e;d++)c=this.getWrapPoints(c,b[d]);return c};THREE.CurvePath.prototype.getTransformedSpacedPoints=function(a,b){var c=this.getSpacedPoints(a),d,e;b||(b=this.bends);d=0;for(e=b.length;d<e;d++)c=this.getWrapPoints(c,b[d]);return c};
+THREE.CurvePath.prototype.getWrapPoints=function(a,b){var c=this.getBoundingBox(),d,e,f,h,g,i;d=0;for(e=a.length;d<e;d++)f=a[d],h=f.x,g=f.y,i=h/c.maxX,i=b.getUtoTmapping(i,h),h=b.getPoint(i),g=b.getNormalVector(i).multiplyScalar(g),f.x=h.x+g.x,f.y=h.y+g.y;return a};THREE.Gyroscope=function(){THREE.Object3D.call(this)};THREE.Gyroscope.prototype=Object.create(THREE.Object3D.prototype);
+THREE.Gyroscope.prototype.updateMatrixWorld=function(a){this.matrixAutoUpdate&&this.updateMatrix();if(this.matrixWorldNeedsUpdate||a)this.parent?(this.matrixWorld.multiplyMatrices(this.parent.matrixWorld,this.matrix),this.matrixWorld.decompose(this.translationWorld,this.quaternionWorld,this.scaleWorld),this.matrix.decompose(this.translationObject,this.quaternionObject,this.scaleObject),this.matrixWorld.compose(this.translationWorld,this.quaternionObject,this.scaleWorld)):this.matrixWorld.copy(this.matrix),
+this.matrixWorldNeedsUpdate=!1,a=!0;for(var b=0,c=this.children.length;b<c;b++)this.children[b].updateMatrixWorld(a)};THREE.Gyroscope.prototype.translationWorld=new THREE.Vector3;THREE.Gyroscope.prototype.translationObject=new THREE.Vector3;THREE.Gyroscope.prototype.quaternionWorld=new THREE.Quaternion;THREE.Gyroscope.prototype.quaternionObject=new THREE.Quaternion;THREE.Gyroscope.prototype.scaleWorld=new THREE.Vector3;THREE.Gyroscope.prototype.scaleObject=new THREE.Vector3;THREE.Path=function(a){THREE.CurvePath.call(this);this.actions=[];a&&this.fromPoints(a)};THREE.Path.prototype=Object.create(THREE.CurvePath.prototype);THREE.PathActions={MOVE_TO:"moveTo",LINE_TO:"lineTo",QUADRATIC_CURVE_TO:"quadraticCurveTo",BEZIER_CURVE_TO:"bezierCurveTo",CSPLINE_THRU:"splineThru",ARC:"arc",ELLIPSE:"ellipse"};THREE.Path.prototype.fromPoints=function(a){this.moveTo(a[0].x,a[0].y);for(var b=1,c=a.length;b<c;b++)this.lineTo(a[b].x,a[b].y)};
+THREE.Path.prototype.moveTo=function(a,b){var c=Array.prototype.slice.call(arguments);this.actions.push({action:THREE.PathActions.MOVE_TO,args:c})};THREE.Path.prototype.lineTo=function(a,b){var c=Array.prototype.slice.call(arguments),d=this.actions[this.actions.length-1].args,d=new THREE.LineCurve(new THREE.Vector2(d[d.length-2],d[d.length-1]),new THREE.Vector2(a,b));this.curves.push(d);this.actions.push({action:THREE.PathActions.LINE_TO,args:c})};
+THREE.Path.prototype.quadraticCurveTo=function(a,b,c,d){var e=Array.prototype.slice.call(arguments),f=this.actions[this.actions.length-1].args,f=new THREE.QuadraticBezierCurve(new THREE.Vector2(f[f.length-2],f[f.length-1]),new THREE.Vector2(a,b),new THREE.Vector2(c,d));this.curves.push(f);this.actions.push({action:THREE.PathActions.QUADRATIC_CURVE_TO,args:e})};
+THREE.Path.prototype.bezierCurveTo=function(a,b,c,d,e,f){var h=Array.prototype.slice.call(arguments),g=this.actions[this.actions.length-1].args,g=new THREE.CubicBezierCurve(new THREE.Vector2(g[g.length-2],g[g.length-1]),new THREE.Vector2(a,b),new THREE.Vector2(c,d),new THREE.Vector2(e,f));this.curves.push(g);this.actions.push({action:THREE.PathActions.BEZIER_CURVE_TO,args:h})};
+THREE.Path.prototype.splineThru=function(a){var b=Array.prototype.slice.call(arguments),c=this.actions[this.actions.length-1].args,c=[new THREE.Vector2(c[c.length-2],c[c.length-1])];Array.prototype.push.apply(c,a);c=new THREE.SplineCurve(c);this.curves.push(c);this.actions.push({action:THREE.PathActions.CSPLINE_THRU,args:b})};THREE.Path.prototype.arc=function(a,b,c,d,e,f){var h=this.actions[this.actions.length-1].args;this.absarc(a+h[h.length-2],b+h[h.length-1],c,d,e,f)};
+THREE.Path.prototype.absarc=function(a,b,c,d,e,f){this.absellipse(a,b,c,c,d,e,f)};THREE.Path.prototype.ellipse=function(a,b,c,d,e,f,h){var g=this.actions[this.actions.length-1].args;this.absellipse(a+g[g.length-2],b+g[g.length-1],c,d,e,f,h)};THREE.Path.prototype.absellipse=function(a,b,c,d,e,f,h){var g=Array.prototype.slice.call(arguments),i=new THREE.EllipseCurve(a,b,c,d,e,f,h);this.curves.push(i);i=i.getPoint(1);g.push(i.x);g.push(i.y);this.actions.push({action:THREE.PathActions.ELLIPSE,args:g})};
+THREE.Path.prototype.getSpacedPoints=function(a){a||(a=40);for(var b=[],c=0;c<a;c++)b.push(this.getPoint(c/a));return b};
+THREE.Path.prototype.getPoints=function(a,b){if(this.useSpacedPoints)return console.log("tata"),this.getSpacedPoints(a,b);var a=a||12,c=[],d,e,f,h,g,i,k,m,l,p,s,t,n;d=0;for(e=this.actions.length;d<e;d++)switch(f=this.actions[d],h=f.action,f=f.args,h){case THREE.PathActions.MOVE_TO:c.push(new THREE.Vector2(f[0],f[1]));break;case THREE.PathActions.LINE_TO:c.push(new THREE.Vector2(f[0],f[1]));break;case THREE.PathActions.QUADRATIC_CURVE_TO:g=f[2];i=f[3];l=f[0];p=f[1];0<c.length?(h=c[c.length-1],s=h.x,
+t=h.y):(h=this.actions[d-1].args,s=h[h.length-2],t=h[h.length-1]);for(f=1;f<=a;f++)n=f/a,h=THREE.Shape.Utils.b2(n,s,l,g),n=THREE.Shape.Utils.b2(n,t,p,i),c.push(new THREE.Vector2(h,n));break;case THREE.PathActions.BEZIER_CURVE_TO:g=f[4];i=f[5];l=f[0];p=f[1];k=f[2];m=f[3];0<c.length?(h=c[c.length-1],s=h.x,t=h.y):(h=this.actions[d-1].args,s=h[h.length-2],t=h[h.length-1]);for(f=1;f<=a;f++)n=f/a,h=THREE.Shape.Utils.b3(n,s,l,k,g),n=THREE.Shape.Utils.b3(n,t,p,m,i),c.push(new THREE.Vector2(h,n));break;case THREE.PathActions.CSPLINE_THRU:h=
+this.actions[d-1].args;n=[new THREE.Vector2(h[h.length-2],h[h.length-1])];h=a*f[0].length;n=n.concat(f[0]);n=new THREE.SplineCurve(n);for(f=1;f<=h;f++)c.push(n.getPointAt(f/h));break;case THREE.PathActions.ARC:g=f[0];i=f[1];p=f[2];k=f[3];h=f[4];l=!!f[5];s=h-k;t=2*a;for(f=1;f<=t;f++)n=f/t,l||(n=1-n),n=k+n*s,h=g+p*Math.cos(n),n=i+p*Math.sin(n),c.push(new THREE.Vector2(h,n));break;case THREE.PathActions.ELLIPSE:g=f[0];i=f[1];p=f[2];m=f[3];k=f[4];h=f[5];l=!!f[6];s=h-k;t=2*a;for(f=1;f<=t;f++)n=f/t,l||
+(n=1-n),n=k+n*s,h=g+p*Math.cos(n),n=i+m*Math.sin(n),c.push(new THREE.Vector2(h,n))}d=c[c.length-1];1E-10>Math.abs(d.x-c[0].x)&&1E-10>Math.abs(d.y-c[0].y)&&c.splice(c.length-1,1);b&&c.push(c[0]);return c};
+THREE.Path.prototype.toShapes=function(a){var b,c,d,e,f=[],h=new THREE.Path;b=0;for(c=this.actions.length;b<c;b++)d=this.actions[b],e=d.args,d=d.action,d==THREE.PathActions.MOVE_TO&&0!=h.actions.length&&(f.push(h),h=new THREE.Path),h[d].apply(h,e);0!=h.actions.length&&f.push(h);if(0==f.length)return[];var g;e=[];if(1==f.length)return d=f[0],g=new THREE.Shape,g.actions=d.actions,g.curves=d.curves,e.push(g),e;b=!THREE.Shape.Utils.isClockWise(f[0].getPoints());if(a?!b:b){g=new THREE.Shape;b=0;for(c=
+f.length;b<c;b++)d=f[b],h=THREE.Shape.Utils.isClockWise(d.getPoints()),(h=a?!h:h)?(g.actions=d.actions,g.curves=d.curves,e.push(g),g=new THREE.Shape):g.holes.push(d)}else{g=void 0;b=0;for(c=f.length;b<c;b++)d=f[b],h=THREE.Shape.Utils.isClockWise(d.getPoints()),(h=a?!h:h)?(g&&e.push(g),g=new THREE.Shape,g.actions=d.actions,g.curves=d.curves):g.holes.push(d);e.push(g)}return e};THREE.Shape=function(){THREE.Path.apply(this,arguments);this.holes=[]};THREE.Shape.prototype=Object.create(THREE.Path.prototype);THREE.Shape.prototype.extrude=function(a){return new THREE.ExtrudeGeometry(this,a)};THREE.Shape.prototype.makeGeometry=function(a){return new THREE.ShapeGeometry(this,a)};THREE.Shape.prototype.getPointsHoles=function(a){var b,c=this.holes.length,d=[];for(b=0;b<c;b++)d[b]=this.holes[b].getTransformedPoints(a,this.bends);return d};
+THREE.Shape.prototype.getSpacedPointsHoles=function(a){var b,c=this.holes.length,d=[];for(b=0;b<c;b++)d[b]=this.holes[b].getTransformedSpacedPoints(a,this.bends);return d};THREE.Shape.prototype.extractAllPoints=function(a){return{shape:this.getTransformedPoints(a),holes:this.getPointsHoles(a)}};THREE.Shape.prototype.extractPoints=function(a){return this.useSpacedPoints?this.extractAllSpacedPoints(a):this.extractAllPoints(a)};
+THREE.Shape.prototype.extractAllSpacedPoints=function(a){return{shape:this.getTransformedSpacedPoints(a),holes:this.getSpacedPointsHoles(a)}};
+THREE.Shape.Utils={removeHoles:function(a,b){var c=a.concat(),d=c.concat(),e,f,h,g,i,k,m,l,p,s,t=[];for(i=0;i<b.length;i++){k=b[i];Array.prototype.push.apply(d,k);f=Number.POSITIVE_INFINITY;for(e=0;e<k.length;e++){p=k[e];s=[];for(l=0;l<c.length;l++)m=c[l],m=p.distanceToSquared(m),s.push(m),m<f&&(f=m,h=e,g=l)}e=0<=g-1?g-1:c.length-1;f=0<=h-1?h-1:k.length-1;var n=[k[h],c[g],c[e]];l=THREE.FontUtils.Triangulate.area(n);var r=[k[h],k[f],c[g]];p=THREE.FontUtils.Triangulate.area(r);s=g;m=h;g+=1;h+=-1;0>
+g&&(g+=c.length);g%=c.length;0>h&&(h+=k.length);h%=k.length;e=0<=g-1?g-1:c.length-1;f=0<=h-1?h-1:k.length-1;n=[k[h],c[g],c[e]];n=THREE.FontUtils.Triangulate.area(n);r=[k[h],k[f],c[g]];r=THREE.FontUtils.Triangulate.area(r);l+p>n+r&&(g=s,h=m,0>g&&(g+=c.length),g%=c.length,0>h&&(h+=k.length),h%=k.length,e=0<=g-1?g-1:c.length-1,f=0<=h-1?h-1:k.length-1);l=c.slice(0,g);p=c.slice(g);s=k.slice(h);m=k.slice(0,h);f=[k[h],k[f],c[g]];t.push([k[h],c[g],c[e]]);t.push(f);c=l.concat(s).concat(m).concat(p)}return{shape:c,
+isolatedPts:t,allpoints:d}},triangulateShape:function(a,b){var c=THREE.Shape.Utils.removeHoles(a,b),d=c.allpoints,e=c.isolatedPts,c=THREE.FontUtils.Triangulate(c.shape,!1),f,h,g,i,k={};f=0;for(h=d.length;f<h;f++)i=d[f].x+":"+d[f].y,void 0!==k[i]&&console.log("Duplicate point",i),k[i]=f;f=0;for(h=c.length;f<h;f++){g=c[f];for(d=0;3>d;d++)i=g[d].x+":"+g[d].y,i=k[i],void 0!==i&&(g[d]=i)}f=0;for(h=e.length;f<h;f++){g=e[f];for(d=0;3>d;d++)i=g[d].x+":"+g[d].y,i=k[i],void 0!==i&&(g[d]=i)}return c.concat(e)},
+isClockWise:function(a){return 0>THREE.FontUtils.Triangulate.area(a)},b2p0:function(a,b){var c=1-a;return c*c*b},b2p1:function(a,b){return 2*(1-a)*a*b},b2p2:function(a,b){return a*a*b},b2:function(a,b,c,d){return this.b2p0(a,b)+this.b2p1(a,c)+this.b2p2(a,d)},b3p0:function(a,b){var c=1-a;return c*c*c*b},b3p1:function(a,b){var c=1-a;return 3*c*c*a*b},b3p2:function(a,b){return 3*(1-a)*a*a*b},b3p3:function(a,b){return a*a*a*b},b3:function(a,b,c,d,e){return this.b3p0(a,b)+this.b3p1(a,c)+this.b3p2(a,d)+
+this.b3p3(a,e)}};THREE.LineCurve=function(a,b){this.v1=a;this.v2=b};THREE.LineCurve.prototype=Object.create(THREE.Curve.prototype);THREE.LineCurve.prototype.getPoint=function(a){var b=this.v2.clone().sub(this.v1);b.multiplyScalar(a).add(this.v1);return b};THREE.LineCurve.prototype.getPointAt=function(a){return this.getPoint(a)};THREE.LineCurve.prototype.getTangent=function(){return this.v2.clone().sub(this.v1).normalize()};THREE.QuadraticBezierCurve=function(a,b,c){this.v0=a;this.v1=b;this.v2=c};THREE.QuadraticBezierCurve.prototype=Object.create(THREE.Curve.prototype);THREE.QuadraticBezierCurve.prototype.getPoint=function(a){var b;b=THREE.Shape.Utils.b2(a,this.v0.x,this.v1.x,this.v2.x);a=THREE.Shape.Utils.b2(a,this.v0.y,this.v1.y,this.v2.y);return new THREE.Vector2(b,a)};
+THREE.QuadraticBezierCurve.prototype.getTangent=function(a){var b;b=THREE.Curve.Utils.tangentQuadraticBezier(a,this.v0.x,this.v1.x,this.v2.x);a=THREE.Curve.Utils.tangentQuadraticBezier(a,this.v0.y,this.v1.y,this.v2.y);b=new THREE.Vector2(b,a);b.normalize();return b};THREE.CubicBezierCurve=function(a,b,c,d){this.v0=a;this.v1=b;this.v2=c;this.v3=d};THREE.CubicBezierCurve.prototype=Object.create(THREE.Curve.prototype);THREE.CubicBezierCurve.prototype.getPoint=function(a){var b;b=THREE.Shape.Utils.b3(a,this.v0.x,this.v1.x,this.v2.x,this.v3.x);a=THREE.Shape.Utils.b3(a,this.v0.y,this.v1.y,this.v2.y,this.v3.y);return new THREE.Vector2(b,a)};
+THREE.CubicBezierCurve.prototype.getTangent=function(a){var b;b=THREE.Curve.Utils.tangentCubicBezier(a,this.v0.x,this.v1.x,this.v2.x,this.v3.x);a=THREE.Curve.Utils.tangentCubicBezier(a,this.v0.y,this.v1.y,this.v2.y,this.v3.y);b=new THREE.Vector2(b,a);b.normalize();return b};THREE.SplineCurve=function(a){this.points=void 0==a?[]:a};THREE.SplineCurve.prototype=Object.create(THREE.Curve.prototype);THREE.SplineCurve.prototype.getPoint=function(a){var b=new THREE.Vector2,c=[],d=this.points,e;e=(d.length-1)*a;a=Math.floor(e);e-=a;c[0]=0==a?a:a-1;c[1]=a;c[2]=a>d.length-2?d.length-1:a+1;c[3]=a>d.length-3?d.length-1:a+2;b.x=THREE.Curve.Utils.interpolate(d[c[0]].x,d[c[1]].x,d[c[2]].x,d[c[3]].x,e);b.y=THREE.Curve.Utils.interpolate(d[c[0]].y,d[c[1]].y,d[c[2]].y,d[c[3]].y,e);return b};THREE.EllipseCurve=function(a,b,c,d,e,f,h){this.aX=a;this.aY=b;this.xRadius=c;this.yRadius=d;this.aStartAngle=e;this.aEndAngle=f;this.aClockwise=h};THREE.EllipseCurve.prototype=Object.create(THREE.Curve.prototype);
+THREE.EllipseCurve.prototype.getPoint=function(a){var b;b=this.aEndAngle-this.aStartAngle;0>b&&(b+=2*Math.PI);b>2*Math.PI&&(b-=2*Math.PI);b=!0===this.aClockwise?this.aEndAngle+(1-a)*(2*Math.PI-b):this.aStartAngle+a*b;a=this.aX+this.xRadius*Math.cos(b);b=this.aY+this.yRadius*Math.sin(b);return new THREE.Vector2(a,b)};THREE.ArcCurve=function(a,b,c,d,e,f){THREE.EllipseCurve.call(this,a,b,c,c,d,e,f)};THREE.ArcCurve.prototype=Object.create(THREE.EllipseCurve.prototype);THREE.LineCurve3=THREE.Curve.create(function(a,b){this.v1=a;this.v2=b},function(a){var b=new THREE.Vector3;b.subVectors(this.v2,this.v1);b.multiplyScalar(a);b.add(this.v1);return b});THREE.QuadraticBezierCurve3=THREE.Curve.create(function(a,b,c){this.v0=a;this.v1=b;this.v2=c},function(a){var b,c;b=THREE.Shape.Utils.b2(a,this.v0.x,this.v1.x,this.v2.x);c=THREE.Shape.Utils.b2(a,this.v0.y,this.v1.y,this.v2.y);a=THREE.Shape.Utils.b2(a,this.v0.z,this.v1.z,this.v2.z);return new THREE.Vector3(b,c,a)});THREE.CubicBezierCurve3=THREE.Curve.create(function(a,b,c,d){this.v0=a;this.v1=b;this.v2=c;this.v3=d},function(a){var b,c;b=THREE.Shape.Utils.b3(a,this.v0.x,this.v1.x,this.v2.x,this.v3.x);c=THREE.Shape.Utils.b3(a,this.v0.y,this.v1.y,this.v2.y,this.v3.y);a=THREE.Shape.Utils.b3(a,this.v0.z,this.v1.z,this.v2.z,this.v3.z);return new THREE.Vector3(b,c,a)});THREE.SplineCurve3=THREE.Curve.create(function(a){this.points=void 0==a?[]:a},function(a){var b=new THREE.Vector3,c=[],d=this.points,e,a=(d.length-1)*a;e=Math.floor(a);a-=e;c[0]=0==e?e:e-1;c[1]=e;c[2]=e>d.length-2?d.length-1:e+1;c[3]=e>d.length-3?d.length-1:e+2;e=d[c[0]];var f=d[c[1]],h=d[c[2]],c=d[c[3]];b.x=THREE.Curve.Utils.interpolate(e.x,f.x,h.x,c.x,a);b.y=THREE.Curve.Utils.interpolate(e.y,f.y,h.y,c.y,a);b.z=THREE.Curve.Utils.interpolate(e.z,f.z,h.z,c.z,a);return b});THREE.ClosedSplineCurve3=THREE.Curve.create(function(a){this.points=void 0==a?[]:a},function(a){var b=new THREE.Vector3,c=[],d=this.points,e;e=(d.length-0)*a;a=Math.floor(e);e-=a;a+=0<a?0:(Math.floor(Math.abs(a)/d.length)+1)*d.length;c[0]=(a-1)%d.length;c[1]=a%d.length;c[2]=(a+1)%d.length;c[3]=(a+2)%d.length;b.x=THREE.Curve.Utils.interpolate(d[c[0]].x,d[c[1]].x,d[c[2]].x,d[c[3]].x,e);b.y=THREE.Curve.Utils.interpolate(d[c[0]].y,d[c[1]].y,d[c[2]].y,d[c[3]].y,e);b.z=THREE.Curve.Utils.interpolate(d[c[0]].z,
+d[c[1]].z,d[c[2]].z,d[c[3]].z,e);return b});THREE.AnimationHandler=function(){var a=[],b={},c={update:function(b){for(var c=0;c<a.length;c++)a[c].update(b)},addToUpdate:function(b){-1===a.indexOf(b)&&a.push(b)},removeFromUpdate:function(b){b=a.indexOf(b);-1!==b&&a.splice(b,1)},add:function(a){void 0!==b[a.name]&&console.log("THREE.AnimationHandler.add: Warning! "+a.name+" already exists in library. Overwriting.");b[a.name]=a;if(!0!==a.initialized){for(var c=0;c<a.hierarchy.length;c++){for(var d=0;d<a.hierarchy[c].keys.length;d++)if(0>a.hierarchy[c].keys[d].time&&
+(a.hierarchy[c].keys[d].time=0),void 0!==a.hierarchy[c].keys[d].rot&&!(a.hierarchy[c].keys[d].rot instanceof THREE.Quaternion)){var g=a.hierarchy[c].keys[d].rot;a.hierarchy[c].keys[d].rot=new THREE.Quaternion(g[0],g[1],g[2],g[3])}if(a.hierarchy[c].keys.length&&void 0!==a.hierarchy[c].keys[0].morphTargets){g={};for(d=0;d<a.hierarchy[c].keys.length;d++)for(var i=0;i<a.hierarchy[c].keys[d].morphTargets.length;i++){var k=a.hierarchy[c].keys[d].morphTargets[i];g[k]=-1}a.hierarchy[c].usedMorphTargets=g;
+for(d=0;d<a.hierarchy[c].keys.length;d++){var m={};for(k in g){for(i=0;i<a.hierarchy[c].keys[d].morphTargets.length;i++)if(a.hierarchy[c].keys[d].morphTargets[i]===k){m[k]=a.hierarchy[c].keys[d].morphTargetsInfluences[i];break}i===a.hierarchy[c].keys[d].morphTargets.length&&(m[k]=0)}a.hierarchy[c].keys[d].morphTargetsInfluences=m}}for(d=1;d<a.hierarchy[c].keys.length;d++)a.hierarchy[c].keys[d].time===a.hierarchy[c].keys[d-1].time&&(a.hierarchy[c].keys.splice(d,1),d--);for(d=0;d<a.hierarchy[c].keys.length;d++)a.hierarchy[c].keys[d].index=
+d}d=parseInt(a.length*a.fps,10);a.JIT={};a.JIT.hierarchy=[];for(c=0;c<a.hierarchy.length;c++)a.JIT.hierarchy.push(Array(d));a.initialized=!0}},get:function(a){if("string"===typeof a){if(b[a])return b[a];console.log("THREE.AnimationHandler.get: Couldn't find animation "+a);return null}},parse:function(a){var b=[];if(a instanceof THREE.SkinnedMesh)for(var c=0;c<a.bones.length;c++)b.push(a.bones[c]);else d(a,b);return b}},d=function(a,b){b.push(a);for(var c=0;c<a.children.length;c++)d(a.children[c],
+b)};c.LINEAR=0;c.CATMULLROM=1;c.CATMULLROM_FORWARD=2;return c}();THREE.Animation=function(a,b,c){this.root=a;this.data=THREE.AnimationHandler.get(b);this.hierarchy=THREE.AnimationHandler.parse(a);this.currentTime=0;this.timeScale=1;this.isPlaying=!1;this.loop=this.isPaused=!0;this.interpolationType=void 0!==c?c:THREE.AnimationHandler.LINEAR;this.points=[];this.target=new THREE.Vector3};
+THREE.Animation.prototype.play=function(a,b){if(!1===this.isPlaying){this.isPlaying=!0;this.loop=void 0!==a?a:!0;this.currentTime=void 0!==b?b:0;var c,d=this.hierarchy.length,e;for(c=0;c<d;c++){e=this.hierarchy[c];e.matrixAutoUpdate=!0;void 0===e.animationCache&&(e.animationCache={},e.animationCache.prevKey={pos:0,rot:0,scl:0},e.animationCache.nextKey={pos:0,rot:0,scl:0},e.animationCache.originalMatrix=e instanceof THREE.Bone?e.skinMatrix:e.matrix);var f=e.animationCache.prevKey;e=e.animationCache.nextKey;
+f.pos=this.data.hierarchy[c].keys[0];f.rot=this.data.hierarchy[c].keys[0];f.scl=this.data.hierarchy[c].keys[0];e.pos=this.getNextKeyWith("pos",c,1);e.rot=this.getNextKeyWith("rot",c,1);e.scl=this.getNextKeyWith("scl",c,1)}this.update(0)}this.isPaused=!1;THREE.AnimationHandler.addToUpdate(this)};THREE.Animation.prototype.pause=function(){!0===this.isPaused?THREE.AnimationHandler.addToUpdate(this):THREE.AnimationHandler.removeFromUpdate(this);this.isPaused=!this.isPaused};
+THREE.Animation.prototype.stop=function(){this.isPaused=this.isPlaying=!1;THREE.AnimationHandler.removeFromUpdate(this)};
+THREE.Animation.prototype.update=function(a){if(!1!==this.isPlaying){var b=["pos","rot","scl"],c,d,e,f,h,g,i,k,m;m=this.currentTime+=a*this.timeScale;k=this.currentTime%=this.data.length;parseInt(Math.min(k*this.data.fps,this.data.length*this.data.fps),10);for(var l=0,p=this.hierarchy.length;l<p;l++){a=this.hierarchy[l];i=a.animationCache;for(var s=0;3>s;s++){c=b[s];h=i.prevKey[c];g=i.nextKey[c];if(g.time<=m){if(k<m)if(this.loop){h=this.data.hierarchy[l].keys[0];for(g=this.getNextKeyWith(c,l,1);g.time<
+k;)h=g,g=this.getNextKeyWith(c,l,g.index+1)}else{this.stop();return}else{do h=g,g=this.getNextKeyWith(c,l,g.index+1);while(g.time<k)}i.prevKey[c]=h;i.nextKey[c]=g}a.matrixAutoUpdate=!0;a.matrixWorldNeedsUpdate=!0;d=(k-h.time)/(g.time-h.time);e=h[c];f=g[c];if(0>d||1<d)console.log("THREE.Animation.update: Warning! Scale out of bounds:"+d+" on bone "+l),d=0>d?0:1;if("pos"===c)if(c=a.position,this.interpolationType===THREE.AnimationHandler.LINEAR)c.x=e[0]+(f[0]-e[0])*d,c.y=e[1]+(f[1]-e[1])*d,c.z=e[2]+
+(f[2]-e[2])*d;else{if(this.interpolationType===THREE.AnimationHandler.CATMULLROM||this.interpolationType===THREE.AnimationHandler.CATMULLROM_FORWARD)this.points[0]=this.getPrevKeyWith("pos",l,h.index-1).pos,this.points[1]=e,this.points[2]=f,this.points[3]=this.getNextKeyWith("pos",l,g.index+1).pos,d=0.33*d+0.33,e=this.interpolateCatmullRom(this.points,d),c.x=e[0],c.y=e[1],c.z=e[2],this.interpolationType===THREE.AnimationHandler.CATMULLROM_FORWARD&&(d=this.interpolateCatmullRom(this.points,1.01*d),
+this.target.set(d[0],d[1],d[2]),this.target.sub(c),this.target.y=0,this.target.normalize(),d=Math.atan2(this.target.x,this.target.z),a.rotation.set(0,d,0))}else"rot"===c?THREE.Quaternion.slerp(e,f,a.quaternion,d):"scl"===c&&(c=a.scale,c.x=e[0]+(f[0]-e[0])*d,c.y=e[1]+(f[1]-e[1])*d,c.z=e[2]+(f[2]-e[2])*d)}}}};
+THREE.Animation.prototype.interpolateCatmullRom=function(a,b){var c=[],d=[],e,f,h,g,i,k;e=(a.length-1)*b;f=Math.floor(e);e-=f;c[0]=0===f?f:f-1;c[1]=f;c[2]=f>a.length-2?f:f+1;c[3]=f>a.length-3?f:f+2;f=a[c[0]];g=a[c[1]];i=a[c[2]];k=a[c[3]];c=e*e;h=e*c;d[0]=this.interpolate(f[0],g[0],i[0],k[0],e,c,h);d[1]=this.interpolate(f[1],g[1],i[1],k[1],e,c,h);d[2]=this.interpolate(f[2],g[2],i[2],k[2],e,c,h);return d};
+THREE.Animation.prototype.interpolate=function(a,b,c,d,e,f,h){a=0.5*(c-a);d=0.5*(d-b);return(2*(b-c)+a+d)*h+(-3*(b-c)-2*a-d)*f+a*e+b};THREE.Animation.prototype.getNextKeyWith=function(a,b,c){for(var d=this.data.hierarchy[b].keys,c=this.interpolationType===THREE.AnimationHandler.CATMULLROM||this.interpolationType===THREE.AnimationHandler.CATMULLROM_FORWARD?c<d.length-1?c:d.length-1:c%d.length;c<d.length;c++)if(void 0!==d[c][a])return d[c];return this.data.hierarchy[b].keys[0]};
+THREE.Animation.prototype.getPrevKeyWith=function(a,b,c){for(var d=this.data.hierarchy[b].keys,c=this.interpolationType===THREE.AnimationHandler.CATMULLROM||this.interpolationType===THREE.AnimationHandler.CATMULLROM_FORWARD?0<c?c:0:0<=c?c:c+d.length;0<=c;c--)if(void 0!==d[c][a])return d[c];return this.data.hierarchy[b].keys[d.length-1]};THREE.KeyFrameAnimation=function(a,b,c){this.root=a;this.data=THREE.AnimationHandler.get(b);this.hierarchy=THREE.AnimationHandler.parse(a);this.currentTime=0;this.timeScale=0.001;this.isPlaying=!1;this.loop=this.isPaused=!0;this.JITCompile=void 0!==c?c:!0;a=0;for(b=this.hierarchy.length;a<b;a++){var c=this.data.hierarchy[a].sids,d=this.hierarchy[a];if(this.data.hierarchy[a].keys.length&&c){for(var e=0;e<c.length;e++){var f=c[e],h=this.getNextKeyWith(f,a,0);h&&h.apply(f)}d.matrixAutoUpdate=!1;this.data.hierarchy[a].node.updateMatrix();
+d.matrixWorldNeedsUpdate=!0}}};
+THREE.KeyFrameAnimation.prototype.play=function(a,b){if(!this.isPlaying){this.isPlaying=!0;this.loop=void 0!==a?a:!0;this.currentTime=void 0!==b?b:0;this.startTimeMs=b;this.startTime=1E7;this.endTime=-this.startTime;var c,d=this.hierarchy.length,e,f;for(c=0;c<d;c++)e=this.hierarchy[c],f=this.data.hierarchy[c],void 0===f.animationCache&&(f.animationCache={},f.animationCache.prevKey=null,f.animationCache.nextKey=null,f.animationCache.originalMatrix=e instanceof THREE.Bone?e.skinMatrix:e.matrix),e=this.data.hierarchy[c].keys,
+e.length&&(f.animationCache.prevKey=e[0],f.animationCache.nextKey=e[1],this.startTime=Math.min(e[0].time,this.startTime),this.endTime=Math.max(e[e.length-1].time,this.endTime));this.update(0)}this.isPaused=!1;THREE.AnimationHandler.addToUpdate(this)};THREE.KeyFrameAnimation.prototype.pause=function(){this.isPaused?THREE.AnimationHandler.addToUpdate(this):THREE.AnimationHandler.removeFromUpdate(this);this.isPaused=!this.isPaused};
+THREE.KeyFrameAnimation.prototype.stop=function(){this.isPaused=this.isPlaying=!1;THREE.AnimationHandler.removeFromUpdate(this);for(var a=0;a<this.data.hierarchy.length;a++){var b=this.hierarchy[a],c=this.data.hierarchy[a];if(void 0!==c.animationCache){var d=c.animationCache.originalMatrix;b instanceof THREE.Bone?(d.copy(b.skinMatrix),b.skinMatrix=d):(d.copy(b.matrix),b.matrix=d);delete c.animationCache}}};
+THREE.KeyFrameAnimation.prototype.update=function(a){if(this.isPlaying){var b,c,d,e,f=this.data.JIT.hierarchy,h,g,i;g=this.currentTime+=a*this.timeScale;h=this.currentTime%=this.data.length;h<this.startTimeMs&&(h=this.currentTime=this.startTimeMs+h);e=parseInt(Math.min(h*this.data.fps,this.data.length*this.data.fps),10);if((i=h<g)&&!this.loop){for(var a=0,k=this.hierarchy.length;a<k;a++){var m=this.data.hierarchy[a].keys,f=this.data.hierarchy[a].sids;d=m.length-1;e=this.hierarchy[a];if(m.length){for(m=
+0;m<f.length;m++)h=f[m],(g=this.getPrevKeyWith(h,a,d))&&g.apply(h);this.data.hierarchy[a].node.updateMatrix();e.matrixWorldNeedsUpdate=!0}}this.stop()}else if(!(h<this.startTime)){a=0;for(k=this.hierarchy.length;a<k;a++){d=this.hierarchy[a];b=this.data.hierarchy[a];var m=b.keys,l=b.animationCache;if(this.JITCompile&&void 0!==f[a][e])d instanceof THREE.Bone?(d.skinMatrix=f[a][e],d.matrixWorldNeedsUpdate=!1):(d.matrix=f[a][e],d.matrixWorldNeedsUpdate=!0);else if(m.length){this.JITCompile&&l&&(d instanceof
+THREE.Bone?d.skinMatrix=l.originalMatrix:d.matrix=l.originalMatrix);b=l.prevKey;c=l.nextKey;if(b&&c){if(c.time<=g){if(i&&this.loop){b=m[0];for(c=m[1];c.time<h;)b=c,c=m[b.index+1]}else if(!i)for(var p=m.length-1;c.time<h&&c.index!==p;)b=c,c=m[b.index+1];l.prevKey=b;l.nextKey=c}c.time>=h?b.interpolate(c,h):b.interpolate(c,c.time)}this.data.hierarchy[a].node.updateMatrix();d.matrixWorldNeedsUpdate=!0}}if(this.JITCompile&&void 0===f[0][e]){this.hierarchy[0].updateMatrixWorld(!0);for(a=0;a<this.hierarchy.length;a++)f[a][e]=
+this.hierarchy[a]instanceof THREE.Bone?this.hierarchy[a].skinMatrix.clone():this.hierarchy[a].matrix.clone()}}}};THREE.KeyFrameAnimation.prototype.getNextKeyWith=function(a,b,c){b=this.data.hierarchy[b].keys;for(c%=b.length;c<b.length;c++)if(b[c].hasTarget(a))return b[c];return b[0]};THREE.KeyFrameAnimation.prototype.getPrevKeyWith=function(a,b,c){b=this.data.hierarchy[b].keys;for(c=0<=c?c:c+b.length;0<=c;c--)if(b[c].hasTarget(a))return b[c];return b[b.length-1]};THREE.CubeCamera=function(a,b,c){THREE.Object3D.call(this);var d=new THREE.PerspectiveCamera(90,1,a,b);d.up.set(0,-1,0);d.lookAt(new THREE.Vector3(1,0,0));this.add(d);var e=new THREE.PerspectiveCamera(90,1,a,b);e.up.set(0,-1,0);e.lookAt(new THREE.Vector3(-1,0,0));this.add(e);var f=new THREE.PerspectiveCamera(90,1,a,b);f.up.set(0,0,1);f.lookAt(new THREE.Vector3(0,1,0));this.add(f);var h=new THREE.PerspectiveCamera(90,1,a,b);h.up.set(0,0,-1);h.lookAt(new THREE.Vector3(0,-1,0));this.add(h);var g=new THREE.PerspectiveCamera(90,
+1,a,b);g.up.set(0,-1,0);g.lookAt(new THREE.Vector3(0,0,1));this.add(g);var i=new THREE.PerspectiveCamera(90,1,a,b);i.up.set(0,-1,0);i.lookAt(new THREE.Vector3(0,0,-1));this.add(i);this.renderTarget=new THREE.WebGLRenderTargetCube(c,c,{format:THREE.RGBFormat,magFilter:THREE.LinearFilter,minFilter:THREE.LinearFilter});this.updateCubeMap=function(a,b){var c=this.renderTarget,p=c.generateMipmaps;c.generateMipmaps=!1;c.activeCubeFace=0;a.render(b,d,c);c.activeCubeFace=1;a.render(b,e,c);c.activeCubeFace=
+2;a.render(b,f,c);c.activeCubeFace=3;a.render(b,h,c);c.activeCubeFace=4;a.render(b,g,c);c.generateMipmaps=p;c.activeCubeFace=5;a.render(b,i,c)}};THREE.CubeCamera.prototype=Object.create(THREE.Object3D.prototype);THREE.CombinedCamera=function(a,b,c,d,e,f,h){THREE.Camera.call(this);this.fov=c;this.left=-a/2;this.right=a/2;this.top=b/2;this.bottom=-b/2;this.cameraO=new THREE.OrthographicCamera(a/-2,a/2,b/2,b/-2,f,h);this.cameraP=new THREE.PerspectiveCamera(c,a/b,d,e);this.zoom=1;this.toPerspective()};THREE.CombinedCamera.prototype=Object.create(THREE.Camera.prototype);
+THREE.CombinedCamera.prototype.toPerspective=function(){this.near=this.cameraP.near;this.far=this.cameraP.far;this.cameraP.fov=this.fov/this.zoom;this.cameraP.updateProjectionMatrix();this.projectionMatrix=this.cameraP.projectionMatrix;this.inPerspectiveMode=!0;this.inOrthographicMode=!1};
+THREE.CombinedCamera.prototype.toOrthographic=function(){var a=this.cameraP.aspect,b=(this.cameraP.near+this.cameraP.far)/2,b=Math.tan(this.fov/2)*b,a=2*b*a/2,b=b/this.zoom,a=a/this.zoom;this.cameraO.left=-a;this.cameraO.right=a;this.cameraO.top=b;this.cameraO.bottom=-b;this.cameraO.updateProjectionMatrix();this.near=this.cameraO.near;this.far=this.cameraO.far;this.projectionMatrix=this.cameraO.projectionMatrix;this.inPerspectiveMode=!1;this.inOrthographicMode=!0};
+THREE.CombinedCamera.prototype.setSize=function(a,b){this.cameraP.aspect=a/b;this.left=-a/2;this.right=a/2;this.top=b/2;this.bottom=-b/2};THREE.CombinedCamera.prototype.setFov=function(a){this.fov=a;this.inPerspectiveMode?this.toPerspective():this.toOrthographic()};THREE.CombinedCamera.prototype.updateProjectionMatrix=function(){this.inPerspectiveMode?this.toPerspective():(this.toPerspective(),this.toOrthographic())};
+THREE.CombinedCamera.prototype.setLens=function(a,b){void 0===b&&(b=24);var c=2*THREE.Math.radToDeg(Math.atan(b/(2*a)));this.setFov(c);return c};THREE.CombinedCamera.prototype.setZoom=function(a){this.zoom=a;this.inPerspectiveMode?this.toPerspective():this.toOrthographic()};THREE.CombinedCamera.prototype.toFrontView=function(){this.rotation.x=0;this.rotation.y=0;this.rotation.z=0;this.rotationAutoUpdate=!1};
+THREE.CombinedCamera.prototype.toBackView=function(){this.rotation.x=0;this.rotation.y=Math.PI;this.rotation.z=0;this.rotationAutoUpdate=!1};THREE.CombinedCamera.prototype.toLeftView=function(){this.rotation.x=0;this.rotation.y=-Math.PI/2;this.rotation.z=0;this.rotationAutoUpdate=!1};THREE.CombinedCamera.prototype.toRightView=function(){this.rotation.x=0;this.rotation.y=Math.PI/2;this.rotation.z=0;this.rotationAutoUpdate=!1};
+THREE.CombinedCamera.prototype.toTopView=function(){this.rotation.x=-Math.PI/2;this.rotation.y=0;this.rotation.z=0;this.rotationAutoUpdate=!1};THREE.CombinedCamera.prototype.toBottomView=function(){this.rotation.x=Math.PI/2;this.rotation.y=0;this.rotation.z=0;this.rotationAutoUpdate=!1};THREE.CircleGeometry=function(a,b,c,d){THREE.Geometry.call(this);this.radius=a=a||50;this.segments=b=void 0!==b?Math.max(3,b):8;this.thetaStart=c=void 0!==c?c:0;this.thetaLength=d=void 0!==d?d:2*Math.PI;var e,f=[];e=new THREE.Vector3;var h=new THREE.Vector2(0.5,0.5);this.vertices.push(e);f.push(h);for(e=0;e<=b;e++){var g=new THREE.Vector3,i=c+e/b*d;g.x=a*Math.cos(i);g.y=a*Math.sin(i);this.vertices.push(g);f.push(new THREE.Vector2((g.x/a+1)/2,(g.y/a+1)/2))}c=new THREE.Vector3(0,0,1);for(e=1;e<=b;e++)this.faces.push(new THREE.Face3(e,
+e+1,0,[c,c,c])),this.faceVertexUvs[0].push([f[e],f[e+1],h]);this.computeCentroids();this.computeFaceNormals();this.boundingSphere=new THREE.Sphere(new THREE.Vector3,a)};THREE.CircleGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.CubeGeometry=function(a,b,c,d,e,f){function h(a,b,c,d,e,f,h,n){var r,q=g.widthSegments,u=g.heightSegments,w=e/2,z=f/2,B=g.vertices.length;if("x"===a&&"y"===b||"y"===a&&"x"===b)r="z";else if("x"===a&&"z"===b||"z"===a&&"x"===b)r="y",u=g.depthSegments;else if("z"===a&&"y"===b||"y"===a&&"z"===b)r="x",q=g.depthSegments;var D=q+1,x=u+1,F=e/q,A=f/u,O=new THREE.Vector3;O[r]=0<h?1:-1;for(e=0;e<x;e++)for(f=0;f<D;f++){var C=new THREE.Vector3;C[a]=(f*F-w)*c;C[b]=(e*A-z)*d;C[r]=h;g.vertices.push(C)}for(e=
+0;e<u;e++)for(f=0;f<q;f++)z=f+D*e,a=f+D*(e+1),b=f+1+D*(e+1),c=f+1+D*e,d=new THREE.Vector2(f/q,1-e/u),h=new THREE.Vector2(f/q,1-(e+1)/u),r=new THREE.Vector2((f+1)/q,1-(e+1)/u),w=new THREE.Vector2((f+1)/q,1-e/u),z=new THREE.Face3(z+B,a+B,c+B),z.normal.copy(O),z.vertexNormals.push(O.clone(),O.clone(),O.clone()),z.materialIndex=n,g.faces.push(z),g.faceVertexUvs[0].push([d,h,w]),z=new THREE.Face3(a+B,b+B,c+B),z.normal.copy(O),z.vertexNormals.push(O.clone(),O.clone(),O.clone()),z.materialIndex=n,g.faces.push(z),
+g.faceVertexUvs[0].push([h.clone(),r,w.clone()])}THREE.Geometry.call(this);var g=this;this.width=a;this.height=b;this.depth=c;this.widthSegments=d||1;this.heightSegments=e||1;this.depthSegments=f||1;a=this.width/2;b=this.height/2;c=this.depth/2;h("z","y",-1,-1,this.depth,this.height,a,0);h("z","y",1,-1,this.depth,this.height,-a,1);h("x","z",1,1,this.width,this.depth,b,2);h("x","z",1,-1,this.width,this.depth,-b,3);h("x","y",1,-1,this.width,this.height,c,4);h("x","y",-1,-1,this.width,this.height,-c,
+5);this.computeCentroids();this.mergeVertices()};THREE.CubeGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.CylinderGeometry=function(a,b,c,d,e,f){THREE.Geometry.call(this);this.radiusTop=a=void 0!==a?a:20;this.radiusBottom=b=void 0!==b?b:20;this.height=c=void 0!==c?c:100;this.radialSegments=d=d||8;this.heightSegments=e=e||1;this.openEnded=f=void 0!==f?f:!1;var h=c/2,g,i,k=[],m=[];for(i=0;i<=e;i++){var l=[],p=[],s=i/e,t=s*(b-a)+a;for(g=0;g<=d;g++){var n=g/d,r=new THREE.Vector3;r.x=t*Math.sin(2*n*Math.PI);r.y=-s*c+h;r.z=t*Math.cos(2*n*Math.PI);this.vertices.push(r);l.push(this.vertices.length-1);p.push(new THREE.Vector2(n,
+1-s))}k.push(l);m.push(p)}c=(b-a)/c;for(g=0;g<d;g++){0!==a?(l=this.vertices[k[0][g]].clone(),p=this.vertices[k[0][g+1]].clone()):(l=this.vertices[k[1][g]].clone(),p=this.vertices[k[1][g+1]].clone());l.setY(Math.sqrt(l.x*l.x+l.z*l.z)*c).normalize();p.setY(Math.sqrt(p.x*p.x+p.z*p.z)*c).normalize();for(i=0;i<e;i++){var s=k[i][g],t=k[i+1][g],n=k[i+1][g+1],r=k[i][g+1],q=l.clone(),u=l.clone(),w=p.clone(),z=p.clone(),B=m[i][g].clone(),D=m[i+1][g].clone(),x=m[i+1][g+1].clone(),F=m[i][g+1].clone();this.faces.push(new THREE.Face3(s,
+t,r,[q,u,z]));this.faceVertexUvs[0].push([B,D,F]);this.faces.push(new THREE.Face3(t,n,r,[u,w,z]));this.faceVertexUvs[0].push([D,x,F])}}if(!1===f&&0<a){this.vertices.push(new THREE.Vector3(0,h,0));for(g=0;g<d;g++)s=k[0][g],t=k[0][g+1],n=this.vertices.length-1,q=new THREE.Vector3(0,1,0),u=new THREE.Vector3(0,1,0),w=new THREE.Vector3(0,1,0),B=m[0][g].clone(),D=m[0][g+1].clone(),x=new THREE.Vector2(D.u,0),this.faces.push(new THREE.Face3(s,t,n,[q,u,w])),this.faceVertexUvs[0].push([B,D,x])}if(!1===f&&0<
+b){this.vertices.push(new THREE.Vector3(0,-h,0));for(g=0;g<d;g++)s=k[i][g+1],t=k[i][g],n=this.vertices.length-1,q=new THREE.Vector3(0,-1,0),u=new THREE.Vector3(0,-1,0),w=new THREE.Vector3(0,-1,0),B=m[i][g+1].clone(),D=m[i][g].clone(),x=new THREE.Vector2(D.u,1),this.faces.push(new THREE.Face3(s,t,n,[q,u,w])),this.faceVertexUvs[0].push([B,D,x])}this.computeCentroids();this.computeFaceNormals()};THREE.CylinderGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.ExtrudeGeometry=function(a,b){"undefined"!==typeof a&&(THREE.Geometry.call(this),a=a instanceof Array?a:[a],this.shapebb=a[a.length-1].getBoundingBox(),this.addShapeList(a,b),this.computeCentroids(),this.computeFaceNormals())};THREE.ExtrudeGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.ExtrudeGeometry.prototype.addShapeList=function(a,b){for(var c=a.length,d=0;d<c;d++)this.addShape(a[d],b)};
+THREE.ExtrudeGeometry.prototype.addShape=function(a,b){function c(a,b,c){b||console.log("die");return b.clone().multiplyScalar(c).add(a)}function d(a,b,c){var d=THREE.ExtrudeGeometry.__v1,e=THREE.ExtrudeGeometry.__v2,f=THREE.ExtrudeGeometry.__v3,g=THREE.ExtrudeGeometry.__v4,h=THREE.ExtrudeGeometry.__v5,i=THREE.ExtrudeGeometry.__v6;d.set(a.x-b.x,a.y-b.y);e.set(a.x-c.x,a.y-c.y);d=d.normalize();e=e.normalize();f.set(-d.y,d.x);g.set(e.y,-e.x);h.copy(a).add(f);i.copy(a).add(g);if(h.equals(i))return g.clone();
+h.copy(b).add(f);i.copy(c).add(g);f=d.dot(g);g=i.sub(h).dot(g);0===f&&(console.log("Either infinite or no solutions!"),0===g?console.log("Its finite solutions."):console.log("Too bad, no solutions."));g/=f;return 0>g?(b=Math.atan2(b.y-a.y,b.x-a.x),a=Math.atan2(c.y-a.y,c.x-a.x),b>a&&(a+=2*Math.PI),c=(b+a)/2,a=-Math.cos(c),c=-Math.sin(c),new THREE.Vector2(a,c)):d.multiplyScalar(g).add(h).sub(a).clone()}function e(c,d){var e,f;for(N=c.length;0<=--N;){e=N;f=N-1;0>f&&(f=c.length-1);for(var g=0,h=s+2*m,
+g=0;g<h;g++){var i=ba*g,k=ba*(g+1),l=d+e+i,i=d+f+i,p=d+f+k,k=d+e+k,n=c,q=g,r=h,t=e,v=f,l=l+E,i=i+E,p=p+E,k=k+E;C.faces.push(new THREE.Face3(l,i,k,null,null,u));C.faces.push(new THREE.Face3(i,p,k,null,null,u));l=w.generateSideWallUV(C,a,n,b,l,i,p,k,q,r,t,v);C.faceVertexUvs[0].push([l[0],l[1],l[3]]);C.faceVertexUvs[0].push([l[1],l[2],l[3]])}}}function f(a,b,c){C.vertices.push(new THREE.Vector3(a,b,c))}function h(c,d,e,f){c+=E;d+=E;e+=E;C.faces.push(new THREE.Face3(c,d,e,null,null,q));c=f?w.generateBottomUV(C,
+a,b,c,d,e):w.generateTopUV(C,a,b,c,d,e);C.faceVertexUvs[0].push(c)}var g=void 0!==b.amount?b.amount:100,i=void 0!==b.bevelThickness?b.bevelThickness:6,k=void 0!==b.bevelSize?b.bevelSize:i-2,m=void 0!==b.bevelSegments?b.bevelSegments:3,l=void 0!==b.bevelEnabled?b.bevelEnabled:!0,p=void 0!==b.curveSegments?b.curveSegments:12,s=void 0!==b.steps?b.steps:1,t=b.extrudePath,n,r=!1,q=b.material,u=b.extrudeMaterial,w=void 0!==b.UVGenerator?b.UVGenerator:THREE.ExtrudeGeometry.WorldUVGenerator,z,B,D,x;t&&(n=
+t.getSpacedPoints(s),r=!0,l=!1,z=void 0!==b.frames?b.frames:new THREE.TubeGeometry.FrenetFrames(t,s,!1),B=new THREE.Vector3,D=new THREE.Vector3,x=new THREE.Vector3);l||(k=i=m=0);var F,A,O,C=this,E=this.vertices.length,p=a.extractPoints(p),I=p.shape,p=p.holes;if(t=!THREE.Shape.Utils.isClockWise(I)){I=I.reverse();A=0;for(O=p.length;A<O;A++)F=p[A],THREE.Shape.Utils.isClockWise(F)&&(p[A]=F.reverse());t=!1}var y=THREE.Shape.Utils.triangulateShape(I,p),t=I;A=0;for(O=p.length;A<O;A++)F=p[A],I=I.concat(F);
+var v,G,R,J,ba=I.length,oa=y.length,pa=[],N=0,M=t.length;v=M-1;for(G=N+1;N<M;N++,v++,G++)v===M&&(v=0),G===M&&(G=0),pa[N]=d(t[N],t[v],t[G]);var Q=[],K,ca=pa.concat();A=0;for(O=p.length;A<O;A++){F=p[A];K=[];N=0;M=F.length;v=M-1;for(G=N+1;N<M;N++,v++,G++)v===M&&(v=0),G===M&&(G=0),K[N]=d(F[N],F[v],F[G]);Q.push(K);ca=ca.concat(K)}for(v=0;v<m;v++){F=v/m;R=i*(1-F);G=k*Math.sin(F*Math.PI/2);N=0;for(M=t.length;N<M;N++)J=c(t[N],pa[N],G),f(J.x,J.y,-R);A=0;for(O=p.length;A<O;A++){F=p[A];K=Q[A];N=0;for(M=F.length;N<
+M;N++)J=c(F[N],K[N],G),f(J.x,J.y,-R)}}G=k;for(N=0;N<ba;N++)J=l?c(I[N],ca[N],G):I[N],r?(D.copy(z.normals[0]).multiplyScalar(J.x),B.copy(z.binormals[0]).multiplyScalar(J.y),x.copy(n[0]).add(D).add(B),f(x.x,x.y,x.z)):f(J.x,J.y,0);for(F=1;F<=s;F++)for(N=0;N<ba;N++)J=l?c(I[N],ca[N],G):I[N],r?(D.copy(z.normals[F]).multiplyScalar(J.x),B.copy(z.binormals[F]).multiplyScalar(J.y),x.copy(n[F]).add(D).add(B),f(x.x,x.y,x.z)):f(J.x,J.y,g/s*F);for(v=m-1;0<=v;v--){F=v/m;R=i*(1-F);G=k*Math.sin(F*Math.PI/2);N=0;for(M=
+t.length;N<M;N++)J=c(t[N],pa[N],G),f(J.x,J.y,g+R);A=0;for(O=p.length;A<O;A++){F=p[A];K=Q[A];N=0;for(M=F.length;N<M;N++)J=c(F[N],K[N],G),r?f(J.x,J.y+n[s-1].y,n[s-1].x+R):f(J.x,J.y,g+R)}}if(l){i=0*ba;for(N=0;N<oa;N++)g=y[N],h(g[2]+i,g[1]+i,g[0]+i,!0);i=ba*(s+2*m);for(N=0;N<oa;N++)g=y[N],h(g[0]+i,g[1]+i,g[2]+i,!1)}else{for(N=0;N<oa;N++)g=y[N],h(g[2],g[1],g[0],!0);for(N=0;N<oa;N++)g=y[N],h(g[0]+ba*s,g[1]+ba*s,g[2]+ba*s,!1)}g=0;e(t,g);g+=t.length;A=0;for(O=p.length;A<O;A++)F=p[A],e(F,g),g+=F.length};
+THREE.ExtrudeGeometry.WorldUVGenerator={generateTopUV:function(a,b,c,d,e,f){b=a.vertices[e].x;e=a.vertices[e].y;c=a.vertices[f].x;f=a.vertices[f].y;return[new THREE.Vector2(a.vertices[d].x,a.vertices[d].y),new THREE.Vector2(b,e),new THREE.Vector2(c,f)]},generateBottomUV:function(a,b,c,d,e,f){return this.generateTopUV(a,b,c,d,e,f)},generateSideWallUV:function(a,b,c,d,e,f,h,g){var b=a.vertices[e].x,c=a.vertices[e].y,e=a.vertices[e].z,d=a.vertices[f].x,i=a.vertices[f].y,f=a.vertices[f].z,k=a.vertices[h].x,
+m=a.vertices[h].y,h=a.vertices[h].z,l=a.vertices[g].x,p=a.vertices[g].y,a=a.vertices[g].z;return 0.01>Math.abs(c-i)?[new THREE.Vector2(b,1-e),new THREE.Vector2(d,1-f),new THREE.Vector2(k,1-h),new THREE.Vector2(l,1-a)]:[new THREE.Vector2(c,1-e),new THREE.Vector2(i,1-f),new THREE.Vector2(m,1-h),new THREE.Vector2(p,1-a)]}};THREE.ExtrudeGeometry.__v1=new THREE.Vector2;THREE.ExtrudeGeometry.__v2=new THREE.Vector2;THREE.ExtrudeGeometry.__v3=new THREE.Vector2;THREE.ExtrudeGeometry.__v4=new THREE.Vector2;
+THREE.ExtrudeGeometry.__v5=new THREE.Vector2;THREE.ExtrudeGeometry.__v6=new THREE.Vector2;THREE.ShapeGeometry=function(a,b){THREE.Geometry.call(this);!1===a instanceof Array&&(a=[a]);this.shapebb=a[a.length-1].getBoundingBox();this.addShapeList(a,b);this.computeCentroids();this.computeFaceNormals()};THREE.ShapeGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.ShapeGeometry.prototype.addShapeList=function(a,b){for(var c=0,d=a.length;c<d;c++)this.addShape(a[c],b);return this};
+THREE.ShapeGeometry.prototype.addShape=function(a,b){void 0===b&&(b={});var c=b.material,d=void 0===b.UVGenerator?THREE.ExtrudeGeometry.WorldUVGenerator:b.UVGenerator,e,f,h,g=this.vertices.length;e=a.extractPoints(void 0!==b.curveSegments?b.curveSegments:12);var i=e.shape,k=e.holes;if(!THREE.Shape.Utils.isClockWise(i)){i=i.reverse();e=0;for(f=k.length;e<f;e++)h=k[e],THREE.Shape.Utils.isClockWise(h)&&(k[e]=h.reverse())}var m=THREE.Shape.Utils.triangulateShape(i,k);e=0;for(f=k.length;e<f;e++)h=k[e],
+i=i.concat(h);k=i.length;f=m.length;for(e=0;e<k;e++)h=i[e],this.vertices.push(new THREE.Vector3(h.x,h.y,0));for(e=0;e<f;e++)k=m[e],i=k[0]+g,h=k[1]+g,k=k[2]+g,this.faces.push(new THREE.Face3(i,h,k,null,null,c)),this.faceVertexUvs[0].push(d.generateBottomUV(this,a,b,i,h,k))};THREE.LatheGeometry=function(a,b,c,d){THREE.Geometry.call(this);for(var b=b||12,c=c||0,d=d||2*Math.PI,e=1/(a.length-1),f=1/b,h=0,g=b;h<=g;h++)for(var i=c+h*f*d,k=Math.cos(i),m=Math.sin(i),i=0,l=a.length;i<l;i++){var p=a[i],s=new THREE.Vector3;s.x=k*p.x-m*p.y;s.y=m*p.x+k*p.y;s.z=p.z;this.vertices.push(s)}c=a.length;h=0;for(g=b;h<g;h++){i=0;for(l=a.length-1;i<l;i++){var b=m=i+c*h,d=m+c,k=m+1+c,m=m+1,p=h*f,s=i*e,t=p+f,n=s+e;this.faces.push(new THREE.Face3(b,d,m));this.faceVertexUvs[0].push([new THREE.Vector2(p,
+s),new THREE.Vector2(t,s),new THREE.Vector2(p,n)]);this.faces.push(new THREE.Face3(d,k,m));this.faceVertexUvs[0].push([new THREE.Vector2(t,s),new THREE.Vector2(t,n),new THREE.Vector2(p,n)])}}this.mergeVertices();this.computeCentroids();this.computeFaceNormals();this.computeVertexNormals()};THREE.LatheGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.PlaneGeometry=function(a,b,c,d){THREE.Geometry.call(this);this.width=a;this.height=b;this.widthSegments=c||1;this.heightSegments=d||1;for(var e=a/2,f=b/2,c=this.widthSegments,d=this.heightSegments,h=c+1,g=d+1,i=this.width/c,k=this.height/d,m=new THREE.Vector3(0,0,1),a=0;a<g;a++)for(b=0;b<h;b++)this.vertices.push(new THREE.Vector3(b*i-e,-(a*k-f),0));for(a=0;a<d;a++)for(b=0;b<c;b++){var l=b+h*a,e=b+h*(a+1),f=b+1+h*(a+1),g=b+1+h*a,i=new THREE.Vector2(b/c,1-a/d),k=new THREE.Vector2(b/c,1-(a+1)/
+d),p=new THREE.Vector2((b+1)/c,1-(a+1)/d),s=new THREE.Vector2((b+1)/c,1-a/d),l=new THREE.Face3(l,e,g);l.normal.copy(m);l.vertexNormals.push(m.clone(),m.clone(),m.clone());this.faces.push(l);this.faceVertexUvs[0].push([i,k,s]);l=new THREE.Face3(e,f,g);l.normal.copy(m);l.vertexNormals.push(m.clone(),m.clone(),m.clone());this.faces.push(l);this.faceVertexUvs[0].push([k.clone(),p,s.clone()])}this.computeCentroids()};THREE.PlaneGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.RingGeometry=function(a,b,c,d,e,f){THREE.Geometry.call(this);for(var a=a||0,b=b||50,e=void 0!==e?e:0,f=void 0!==f?f:2*Math.PI,c=void 0!==c?Math.max(3,c):8,d=void 0!==d?Math.max(3,d):8,h=[],g=a,i=(b-a)/d,a=0;a<=d;a++){for(b=0;b<=c;b++){var k=new THREE.Vector3,m=e+b/c*f;k.x=g*Math.cos(m);k.y=g*Math.sin(m);this.vertices.push(k);h.push(new THREE.Vector2((k.x/g+1)/2,-(k.y/g+1)/2+1))}g+=i}e=new THREE.Vector3(0,0,1);for(a=0;a<d;a++){f=a*c;for(b=0;b<=c;b++){var m=b+f,i=m+a,k=m+c+a,l=m+c+1+a;this.faces.push(new THREE.Face3(i,
+k,l,[e,e,e]));this.faceVertexUvs[0].push([h[i],h[k],h[l]]);i=m+a;k=m+c+1+a;l=m+1+a;this.faces.push(new THREE.Face3(i,k,l,[e,e,e]));this.faceVertexUvs[0].push([h[i],h[k],h[l]])}}this.computeCentroids();this.computeFaceNormals();this.boundingSphere=new THREE.Sphere(new THREE.Vector3,g)};THREE.RingGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.SphereGeometry=function(a,b,c,d,e,f,h){THREE.Geometry.call(this);this.radius=a=a||50;this.widthSegments=b=Math.max(3,Math.floor(b)||8);this.heightSegments=c=Math.max(2,Math.floor(c)||6);this.phiStart=d=void 0!==d?d:0;this.phiLength=e=void 0!==e?e:2*Math.PI;this.thetaStart=f=void 0!==f?f:0;this.thetaLength=h=void 0!==h?h:Math.PI;var g,i,k=[],m=[];for(i=0;i<=c;i++){var l=[],p=[];for(g=0;g<=b;g++){var s=g/b,t=i/c,n=new THREE.Vector3;n.x=-a*Math.cos(d+s*e)*Math.sin(f+t*h);n.y=a*Math.cos(f+t*h);
+n.z=a*Math.sin(d+s*e)*Math.sin(f+t*h);this.vertices.push(n);l.push(this.vertices.length-1);p.push(new THREE.Vector2(s,1-t))}k.push(l);m.push(p)}for(i=0;i<this.heightSegments;i++)for(g=0;g<this.widthSegments;g++){var b=k[i][g+1],c=k[i][g],d=k[i+1][g],e=k[i+1][g+1],f=this.vertices[b].clone().normalize(),h=this.vertices[c].clone().normalize(),l=this.vertices[d].clone().normalize(),p=this.vertices[e].clone().normalize(),s=m[i][g+1].clone(),t=m[i][g].clone(),n=m[i+1][g].clone(),r=m[i+1][g+1].clone();Math.abs(this.vertices[b].y)===
+this.radius?(this.faces.push(new THREE.Face3(b,d,e,[f,l,p])),this.faceVertexUvs[0].push([s,n,r])):Math.abs(this.vertices[d].y)===this.radius?(this.faces.push(new THREE.Face3(b,c,d,[f,h,l])),this.faceVertexUvs[0].push([s,t,n])):(this.faces.push(new THREE.Face3(b,c,e,[f,h,p])),this.faceVertexUvs[0].push([s,t,r]),this.faces.push(new THREE.Face3(c,d,e,[h,l,p])),this.faceVertexUvs[0].push([t.clone(),n,r.clone()]))}this.computeCentroids();this.computeFaceNormals();this.boundingSphere=new THREE.Sphere(new THREE.Vector3,
+a)};THREE.SphereGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.TextGeometry=function(a,b){var b=b||{},c=THREE.FontUtils.generateShapes(a,b);b.amount=void 0!==b.height?b.height:50;void 0===b.bevelThickness&&(b.bevelThickness=10);void 0===b.bevelSize&&(b.bevelSize=8);void 0===b.bevelEnabled&&(b.bevelEnabled=!1);THREE.ExtrudeGeometry.call(this,c,b)};THREE.TextGeometry.prototype=Object.create(THREE.ExtrudeGeometry.prototype);THREE.TorusGeometry=function(a,b,c,d,e){THREE.Geometry.call(this);this.radius=a||100;this.tube=b||40;this.radialSegments=c||8;this.tubularSegments=d||6;this.arc=e||2*Math.PI;e=new THREE.Vector3;a=[];b=[];for(c=0;c<=this.radialSegments;c++)for(d=0;d<=this.tubularSegments;d++){var f=d/this.tubularSegments*this.arc,h=2*c/this.radialSegments*Math.PI;e.x=this.radius*Math.cos(f);e.y=this.radius*Math.sin(f);var g=new THREE.Vector3;g.x=(this.radius+this.tube*Math.cos(h))*Math.cos(f);g.y=(this.radius+this.tube*
+Math.cos(h))*Math.sin(f);g.z=this.tube*Math.sin(h);this.vertices.push(g);a.push(new THREE.Vector2(d/this.tubularSegments,c/this.radialSegments));b.push(g.clone().sub(e).normalize())}for(c=1;c<=this.radialSegments;c++)for(d=1;d<=this.tubularSegments;d++){var e=(this.tubularSegments+1)*c+d-1,f=(this.tubularSegments+1)*(c-1)+d-1,h=(this.tubularSegments+1)*(c-1)+d,g=(this.tubularSegments+1)*c+d,i=new THREE.Face3(e,f,g,[b[e],b[f],b[g]]);i.normal.add(b[e]);i.normal.add(b[f]);i.normal.add(b[g]);i.normal.normalize();
+this.faces.push(i);this.faceVertexUvs[0].push([a[e].clone(),a[f].clone(),a[g].clone()]);i=new THREE.Face3(f,h,g,[b[f],b[h],b[g]]);i.normal.add(b[f]);i.normal.add(b[h]);i.normal.add(b[g]);i.normal.normalize();this.faces.push(i);this.faceVertexUvs[0].push([a[f].clone(),a[h].clone(),a[g].clone()])}this.computeCentroids()};THREE.TorusGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.TorusKnotGeometry=function(a,b,c,d,e,f,h){function g(a,b,c,d,e){var f=Math.cos(a),g=Math.sin(a),a=b/c*a,b=Math.cos(a),f=0.5*(d*(2+b))*f,g=0.5*d*(2+b)*g,d=0.5*e*d*Math.sin(a);return new THREE.Vector3(f,g,d)}THREE.Geometry.call(this);this.radius=a||100;this.tube=b||40;this.radialSegments=c||64;this.tubularSegments=d||8;this.p=e||2;this.q=f||3;this.heightScale=h||1;this.grid=Array(this.radialSegments);c=new THREE.Vector3;d=new THREE.Vector3;e=new THREE.Vector3;for(a=0;a<this.radialSegments;++a){this.grid[a]=
+Array(this.tubularSegments);b=2*(a/this.radialSegments)*this.p*Math.PI;f=g(b,this.q,this.p,this.radius,this.heightScale);b=g(b+0.01,this.q,this.p,this.radius,this.heightScale);c.subVectors(b,f);d.addVectors(b,f);e.crossVectors(c,d);d.crossVectors(e,c);e.normalize();d.normalize();for(b=0;b<this.tubularSegments;++b){var i=2*(b/this.tubularSegments)*Math.PI,h=-this.tube*Math.cos(i),i=this.tube*Math.sin(i),k=new THREE.Vector3;k.x=f.x+h*d.x+i*e.x;k.y=f.y+h*d.y+i*e.y;k.z=f.z+h*d.z+i*e.z;this.grid[a][b]=
+this.vertices.push(k)-1}}for(a=0;a<this.radialSegments;++a)for(b=0;b<this.tubularSegments;++b){var e=(a+1)%this.radialSegments,f=(b+1)%this.tubularSegments,c=this.grid[a][b],d=this.grid[e][b],e=this.grid[e][f],f=this.grid[a][f],h=new THREE.Vector2(a/this.radialSegments,b/this.tubularSegments),i=new THREE.Vector2((a+1)/this.radialSegments,b/this.tubularSegments),k=new THREE.Vector2((a+1)/this.radialSegments,(b+1)/this.tubularSegments),m=new THREE.Vector2(a/this.radialSegments,(b+1)/this.tubularSegments);
+this.faces.push(new THREE.Face3(c,d,f));this.faceVertexUvs[0].push([h,i,m]);this.faces.push(new THREE.Face3(d,e,f));this.faceVertexUvs[0].push([i.clone(),k,m.clone()])}this.computeCentroids();this.computeFaceNormals();this.computeVertexNormals()};THREE.TorusKnotGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.TubeGeometry=function(a,b,c,d,e){THREE.Geometry.call(this);this.path=a;this.segments=b||64;this.radius=c||1;this.radialSegments=d||8;this.closed=e||!1;this.grid=[];var f,h,d=this.segments+1,g,i,k,e=new THREE.Vector3,m,l,b=new THREE.TubeGeometry.FrenetFrames(this.path,this.segments,this.closed);m=b.normals;l=b.binormals;this.tangents=b.tangents;this.normals=m;this.binormals=l;for(b=0;b<d;b++){this.grid[b]=[];c=b/(d-1);k=a.getPointAt(c);f=m[b];h=l[b];for(c=0;c<this.radialSegments;c++)g=2*(c/this.radialSegments)*
+Math.PI,i=-this.radius*Math.cos(g),g=this.radius*Math.sin(g),e.copy(k),e.x+=i*f.x+g*h.x,e.y+=i*f.y+g*h.y,e.z+=i*f.z+g*h.z,this.grid[b][c]=this.vertices.push(new THREE.Vector3(e.x,e.y,e.z))-1}for(b=0;b<this.segments;b++)for(c=0;c<this.radialSegments;c++)e=this.closed?(b+1)%this.segments:b+1,m=(c+1)%this.radialSegments,a=this.grid[b][c],d=this.grid[e][c],e=this.grid[e][m],m=this.grid[b][m],l=new THREE.Vector2(b/this.segments,c/this.radialSegments),f=new THREE.Vector2((b+1)/this.segments,c/this.radialSegments),
+h=new THREE.Vector2((b+1)/this.segments,(c+1)/this.radialSegments),i=new THREE.Vector2(b/this.segments,(c+1)/this.radialSegments),this.faces.push(new THREE.Face3(a,d,m)),this.faceVertexUvs[0].push([l,f,i]),this.faces.push(new THREE.Face3(d,e,m)),this.faceVertexUvs[0].push([f.clone(),h,i.clone()]);this.computeCentroids();this.computeFaceNormals();this.computeVertexNormals()};THREE.TubeGeometry.prototype=Object.create(THREE.Geometry.prototype);
+THREE.TubeGeometry.FrenetFrames=function(a,b,c){new THREE.Vector3;var d=new THREE.Vector3;new THREE.Vector3;var e=[],f=[],h=[],g=new THREE.Vector3,i=new THREE.Matrix4,b=b+1,k,m,l;this.tangents=e;this.normals=f;this.binormals=h;for(k=0;k<b;k++)m=k/(b-1),e[k]=a.getTangentAt(m),e[k].normalize();f[0]=new THREE.Vector3;h[0]=new THREE.Vector3;a=Number.MAX_VALUE;k=Math.abs(e[0].x);m=Math.abs(e[0].y);l=Math.abs(e[0].z);k<=a&&(a=k,d.set(1,0,0));m<=a&&(a=m,d.set(0,1,0));l<=a&&d.set(0,0,1);g.crossVectors(e[0],
+d).normalize();f[0].crossVectors(e[0],g);h[0].crossVectors(e[0],f[0]);for(k=1;k<b;k++)f[k]=f[k-1].clone(),h[k]=h[k-1].clone(),g.crossVectors(e[k-1],e[k]),1E-4<g.length()&&(g.normalize(),d=Math.acos(THREE.Math.clamp(e[k-1].dot(e[k]),-1,1)),f[k].applyMatrix4(i.makeRotationAxis(g,d))),h[k].crossVectors(e[k],f[k]);if(c){d=Math.acos(THREE.Math.clamp(f[0].dot(f[b-1]),-1,1));d/=b-1;0<e[0].dot(g.crossVectors(f[0],f[b-1]))&&(d=-d);for(k=1;k<b;k++)f[k].applyMatrix4(i.makeRotationAxis(e[k],d*k)),h[k].crossVectors(e[k],
+f[k])}};THREE.PolyhedronGeometry=function(a,b,c,d){function e(a){var b=a.normalize().clone();b.index=g.vertices.push(b)-1;var c=Math.atan2(a.z,-a.x)/2/Math.PI+0.5,a=Math.atan2(-a.y,Math.sqrt(a.x*a.x+a.z*a.z))/Math.PI+0.5;b.uv=new THREE.Vector2(c,1-a);return b}function f(a,b,c){var d=new THREE.Face3(a.index,b.index,c.index,[a.clone(),b.clone(),c.clone()]);d.centroid.add(a).add(b).add(c).divideScalar(3);g.faces.push(d);d=Math.atan2(d.centroid.z,-d.centroid.x);g.faceVertexUvs[0].push([h(a.uv,a,d),h(b.uv,b,d),
+h(c.uv,c,d)])}function h(a,b,c){0>c&&1===a.x&&(a=new THREE.Vector2(a.x-1,a.y));0===b.x&&0===b.z&&(a=new THREE.Vector2(c/2/Math.PI+0.5,a.y));return a.clone()}THREE.Geometry.call(this);for(var c=c||1,d=d||0,g=this,i=0,k=a.length;i<k;i++)e(new THREE.Vector3(a[i][0],a[i][1],a[i][2]));for(var m=this.vertices,a=[],i=0,k=b.length;i<k;i++){var l=m[b[i][0]],p=m[b[i][1]],s=m[b[i][2]];a[i]=new THREE.Face3(l.index,p.index,s.index,[l.clone(),p.clone(),s.clone()])}i=0;for(k=a.length;i<k;i++){p=a[i];m=d;b=Math.pow(2,
+m);Math.pow(4,m);for(var m=e(g.vertices[p.a]),l=e(g.vertices[p.b]),t=e(g.vertices[p.c]),p=[],s=0;s<=b;s++){p[s]=[];for(var n=e(m.clone().lerp(t,s/b)),r=e(l.clone().lerp(t,s/b)),q=b-s,u=0;u<=q;u++)p[s][u]=0==u&&s==b?n:e(n.clone().lerp(r,u/q))}for(s=0;s<b;s++)for(u=0;u<2*(b-s)-1;u++)m=Math.floor(u/2),0==u%2?f(p[s][m+1],p[s+1][m],p[s][m]):f(p[s][m+1],p[s+1][m+1],p[s+1][m])}i=0;for(k=this.faceVertexUvs[0].length;i<k;i++)d=this.faceVertexUvs[0][i],a=d[0].x,b=d[1].x,m=d[2].x,l=Math.max(a,Math.max(b,m)),
+p=Math.min(a,Math.min(b,m)),0.9<l&&0.1>p&&(0.2>a&&(d[0].x+=1),0.2>b&&(d[1].x+=1),0.2>m&&(d[2].x+=1));i=0;for(k=this.vertices.length;i<k;i++)this.vertices[i].multiplyScalar(c);this.mergeVertices();this.computeCentroids();this.computeFaceNormals();this.boundingSphere=new THREE.Sphere(new THREE.Vector3,c)};THREE.PolyhedronGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.IcosahedronGeometry=function(a,b){this.radius=a;this.detail=b;var c=(1+Math.sqrt(5))/2;THREE.PolyhedronGeometry.call(this,[[-1,c,0],[1,c,0],[-1,-c,0],[1,-c,0],[0,-1,c],[0,1,c],[0,-1,-c],[0,1,-c],[c,0,-1],[c,0,1],[-c,0,-1],[-c,0,1]],[[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]],a,b)};THREE.IcosahedronGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.OctahedronGeometry=function(a,b){THREE.PolyhedronGeometry.call(this,[[1,0,0],[-1,0,0],[0,1,0],[0,-1,0],[0,0,1],[0,0,-1]],[[0,2,4],[0,4,3],[0,3,5],[0,5,2],[1,2,5],[1,5,3],[1,3,4],[1,4,2]],a,b)};THREE.OctahedronGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.TetrahedronGeometry=function(a,b){THREE.PolyhedronGeometry.call(this,[[1,1,1],[-1,-1,1],[-1,1,-1],[1,-1,-1]],[[2,1,0],[0,3,2],[1,3,0],[2,3,1]],a,b)};THREE.TetrahedronGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.ParametricGeometry=function(a,b,c){THREE.Geometry.call(this);var d=this.vertices,e=this.faces,f=this.faceVertexUvs[0],h,g,i,k,m=b+1;for(h=0;h<=c;h++){k=h/c;for(g=0;g<=b;g++)i=g/b,i=a(i,k),d.push(i)}var l,p,s,t;for(h=0;h<c;h++)for(g=0;g<b;g++)a=h*m+g,d=h*m+g+1,k=(h+1)*m+g+1,i=(h+1)*m+g,l=new THREE.Vector2(g/b,h/c),p=new THREE.Vector2((g+1)/b,h/c),s=new THREE.Vector2((g+1)/b,(h+1)/c),t=new THREE.Vector2(g/b,(h+1)/c),e.push(new THREE.Face3(a,d,i)),f.push([l,p,t]),e.push(new THREE.Face3(d,k,i)),
+f.push([p.clone(),s,t.clone()]);this.computeCentroids();this.computeFaceNormals();this.computeVertexNormals()};THREE.ParametricGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.AxisHelper=function(a){var a=a||1,b=new THREE.Geometry;b.vertices.push(new THREE.Vector3,new THREE.Vector3(a,0,0),new THREE.Vector3,new THREE.Vector3(0,a,0),new THREE.Vector3,new THREE.Vector3(0,0,a));b.colors.push(new THREE.Color(16711680),new THREE.Color(16755200),new THREE.Color(65280),new THREE.Color(11206400),new THREE.Color(255),new THREE.Color(43775));a=new THREE.LineBasicMaterial({vertexColors:THREE.VertexColors});THREE.Line.call(this,b,a,THREE.LinePieces)};
+THREE.AxisHelper.prototype=Object.create(THREE.Line.prototype);THREE.ArrowHelper=function(a,b,c,d){THREE.Object3D.call(this);void 0===d&&(d=16776960);void 0===c&&(c=1);this.position=b;b=new THREE.Geometry;b.vertices.push(new THREE.Vector3(0,0,0));b.vertices.push(new THREE.Vector3(0,1,0));this.line=new THREE.Line(b,new THREE.LineBasicMaterial({color:d}));this.line.matrixAutoUpdate=!1;this.add(this.line);b=new THREE.CylinderGeometry(0,0.05,0.25,5,1);b.applyMatrix((new THREE.Matrix4).makeTranslation(0,0.875,0));this.cone=new THREE.Mesh(b,new THREE.MeshBasicMaterial({color:d}));
+this.cone.matrixAutoUpdate=!1;this.add(this.cone);this.setDirection(a);this.setLength(c)};THREE.ArrowHelper.prototype=Object.create(THREE.Object3D.prototype);THREE.ArrowHelper.prototype.setDirection=function(){var a=new THREE.Vector3,b;return function(c){0.99999<c.y?this.quaternion.set(0,0,0,1):-0.99999>c.y?this.quaternion.set(1,0,0,0):(a.set(c.z,0,-c.x).normalize(),b=Math.acos(c.y),this.quaternion.setFromAxisAngle(a,b))}}();THREE.ArrowHelper.prototype.setLength=function(a){this.scale.set(a,a,a)};
+THREE.ArrowHelper.prototype.setColor=function(a){this.line.material.color.setHex(a);this.cone.material.color.setHex(a)};THREE.BoxHelper=function(a){var b=[new THREE.Vector3(1,1,1),new THREE.Vector3(-1,1,1),new THREE.Vector3(-1,-1,1),new THREE.Vector3(1,-1,1),new THREE.Vector3(1,1,-1),new THREE.Vector3(-1,1,-1),new THREE.Vector3(-1,-1,-1),new THREE.Vector3(1,-1,-1)];this.vertices=b;var c=new THREE.Geometry;c.vertices.push(b[0],b[1],b[1],b[2],b[2],b[3],b[3],b[0],b[4],b[5],b[5],b[6],b[6],b[7],b[7],b[4],b[0],b[4],b[1],b[5],b[2],b[6],b[3],b[7]);THREE.Line.call(this,c,new THREE.LineBasicMaterial({color:16776960}),THREE.LinePieces);
+void 0!==a&&this.update(a)};THREE.BoxHelper.prototype=Object.create(THREE.Line.prototype);
+THREE.BoxHelper.prototype.update=function(a){var b=a.geometry;null===b.boundingBox&&b.computeBoundingBox();var c=b.boundingBox.min,b=b.boundingBox.max,d=this.vertices;d[0].set(b.x,b.y,b.z);d[1].set(c.x,b.y,b.z);d[2].set(c.x,c.y,b.z);d[3].set(b.x,c.y,b.z);d[4].set(b.x,b.y,c.z);d[5].set(c.x,b.y,c.z);d[6].set(c.x,c.y,c.z);d[7].set(b.x,c.y,c.z);this.geometry.computeBoundingSphere();this.geometry.verticesNeedUpdate=!0;this.matrixAutoUpdate=!1;this.matrixWorld=a.matrixWorld};THREE.BoundingBoxHelper=function(a,b){var c=b||8947848;this.object=a;this.box=new THREE.Box3;THREE.Mesh.call(this,new THREE.CubeGeometry(1,1,1),new THREE.MeshBasicMaterial({color:c,wireframe:!0}))};THREE.BoundingBoxHelper.prototype=Object.create(THREE.Mesh.prototype);THREE.BoundingBoxHelper.prototype.update=function(){this.box.setFromObject(this.object);this.box.size(this.scale);this.box.center(this.position)};THREE.CameraHelper=function(a){function b(a,b,d){c(a,d);c(b,d)}function c(a,b){d.vertices.push(new THREE.Vector3);d.colors.push(new THREE.Color(b));void 0===f[a]&&(f[a]=[]);f[a].push(d.vertices.length-1)}var d=new THREE.Geometry,e=new THREE.LineBasicMaterial({color:16777215,vertexColors:THREE.FaceColors}),f={};b("n1","n2",16755200);b("n2","n4",16755200);b("n4","n3",16755200);b("n3","n1",16755200);b("f1","f2",16755200);b("f2","f4",16755200);b("f4","f3",16755200);b("f3","f1",16755200);b("n1","f1",16755200);
+b("n2","f2",16755200);b("n3","f3",16755200);b("n4","f4",16755200);b("p","n1",16711680);b("p","n2",16711680);b("p","n3",16711680);b("p","n4",16711680);b("u1","u2",43775);b("u2","u3",43775);b("u3","u1",43775);b("c","t",16777215);b("p","c",3355443);b("cn1","cn2",3355443);b("cn3","cn4",3355443);b("cf1","cf2",3355443);b("cf3","cf4",3355443);THREE.Line.call(this,d,e,THREE.LinePieces);this.camera=a;this.matrixWorld=a.matrixWorld;this.matrixAutoUpdate=!1;this.pointMap=f;this.update()};
+THREE.CameraHelper.prototype=Object.create(THREE.Line.prototype);
+THREE.CameraHelper.prototype.update=function(){var a=new THREE.Vector3,b=new THREE.Camera,c=new THREE.Projector;return function(){function d(d,h,g,i){a.set(h,g,i);c.unprojectVector(a,b);d=e.pointMap[d];if(void 0!==d){h=0;for(g=d.length;h<g;h++)e.geometry.vertices[d[h]].copy(a)}}var e=this;b.projectionMatrix.copy(this.camera.projectionMatrix);d("c",0,0,-1);d("t",0,0,1);d("n1",-1,-1,-1);d("n2",1,-1,-1);d("n3",-1,1,-1);d("n4",1,1,-1);d("f1",-1,-1,1);d("f2",1,-1,1);d("f3",-1,1,1);d("f4",1,1,1);d("u1",
+0.7,1.1,-1);d("u2",-0.7,1.1,-1);d("u3",0,2,-1);d("cf1",-1,0,1);d("cf2",1,0,1);d("cf3",0,-1,1);d("cf4",0,1,1);d("cn1",-1,0,-1);d("cn2",1,0,-1);d("cn3",0,-1,-1);d("cn4",0,1,-1);this.geometry.verticesNeedUpdate=!0}}();THREE.DirectionalLightHelper=function(a,b){THREE.Object3D.call(this);this.light=a;this.light.updateMatrixWorld();this.matrixWorld=a.matrixWorld;this.matrixAutoUpdate=!1;var c=new THREE.PlaneGeometry(b,b),d=new THREE.MeshBasicMaterial({wireframe:!0,fog:!1});d.color.copy(this.light.color).multiplyScalar(this.light.intensity);this.lightPlane=new THREE.Mesh(c,d);this.add(this.lightPlane);c=new THREE.Geometry;c.vertices.push(new THREE.Vector3);c.vertices.push(new THREE.Vector3);c.computeLineDistances();
+d=new THREE.LineBasicMaterial({fog:!1});d.color.copy(this.light.color).multiplyScalar(this.light.intensity);this.targetLine=new THREE.Line(c,d);this.add(this.targetLine);this.update()};THREE.DirectionalLightHelper.prototype=Object.create(THREE.Object3D.prototype);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 a=new THREE.Vector3;return function(){a.getPositionFromMatrix(this.light.matrixWorld).negate();this.lightPlane.lookAt(a);this.lightPlane.material.color.copy(this.light.color).multiplyScalar(this.light.intensity);this.targetLine.geometry.vertices[1].copy(a);this.targetLine.geometry.verticesNeedUpdate=!0;this.targetLine.material.color.copy(this.lightPlane.material.color)}}();THREE.FaceNormalsHelper=function(a,b,c,d){this.object=a;this.size=b||1;for(var a=c||16776960,d=d||1,b=new THREE.Geometry,c=0,e=this.object.geometry.faces.length;c<e;c++)b.vertices.push(new THREE.Vector3),b.vertices.push(new THREE.Vector3);THREE.Line.call(this,b,new THREE.LineBasicMaterial({color:a,linewidth:d}),THREE.LinePieces);this.matrixAutoUpdate=!1;this.normalMatrix=new THREE.Matrix3;this.update()};THREE.FaceNormalsHelper.prototype=Object.create(THREE.Line.prototype);
+THREE.FaceNormalsHelper.prototype.update=function(){var a=new THREE.Vector3;return function(){this.object.updateMatrixWorld(!0);this.normalMatrix.getNormalMatrix(this.object.matrixWorld);for(var b=this.geometry.vertices,c=this.object.geometry.faces,d=this.object.matrixWorld,e=0,f=c.length;e<f;e++){var h=c[e];a.copy(h.normal).applyMatrix3(this.normalMatrix).normalize().multiplyScalar(this.size);var g=2*e;b[g].copy(h.centroid).applyMatrix4(d);b[g+1].addVectors(b[g],a)}this.geometry.verticesNeedUpdate=
+!0;return this}}();THREE.GridHelper=function(a,b){var c=new THREE.Geometry,d=new THREE.LineBasicMaterial({vertexColors:THREE.VertexColors});this.color1=new THREE.Color(4473924);this.color2=new THREE.Color(8947848);for(var e=-a;e<=a;e+=b){c.vertices.push(new THREE.Vector3(-a,0,e),new THREE.Vector3(a,0,e),new THREE.Vector3(e,0,-a),new THREE.Vector3(e,0,a));var f=0===e?this.color1:this.color2;c.colors.push(f,f,f,f)}THREE.Line.call(this,c,d,THREE.LinePieces)};THREE.GridHelper.prototype=Object.create(THREE.Line.prototype);
+THREE.GridHelper.prototype.setColors=function(a,b){this.color1.set(a);this.color2.set(b);this.geometry.colorsNeedUpdate=!0};THREE.HemisphereLightHelper=function(a,b){THREE.Object3D.call(this);this.light=a;this.light.updateMatrixWorld();this.matrixWorld=a.matrixWorld;this.matrixAutoUpdate=!1;this.colors=[new THREE.Color,new THREE.Color];var c=new THREE.SphereGeometry(b,4,2);c.applyMatrix((new THREE.Matrix4).makeRotationX(-Math.PI/2));for(var d=0;8>d;d++)c.faces[d].color=this.colors[4>d?0:1];d=new THREE.MeshBasicMaterial({vertexColors:THREE.FaceColors,wireframe:!0});this.lightSphere=new THREE.Mesh(c,d);this.add(this.lightSphere);
+this.update()};THREE.HemisphereLightHelper.prototype=Object.create(THREE.Object3D.prototype);THREE.HemisphereLightHelper.prototype.dispose=function(){this.lightSphere.geometry.dispose();this.lightSphere.material.dispose()};
+THREE.HemisphereLightHelper.prototype.update=function(){var a=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(a.getPositionFromMatrix(this.light.matrixWorld).negate());this.lightSphere.geometry.colorsNeedUpdate=!0}}();THREE.PointLightHelper=function(a,b){this.light=a;this.light.updateMatrixWorld();var c=new THREE.SphereGeometry(b,4,2),d=new THREE.MeshBasicMaterial({wireframe:!0,fog:!1});d.color.copy(this.light.color).multiplyScalar(this.light.intensity);THREE.Mesh.call(this,c,d);this.matrixWorld=this.light.matrixWorld;this.matrixAutoUpdate=!1};THREE.PointLightHelper.prototype=Object.create(THREE.Mesh.prototype);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)};THREE.SpotLightHelper=function(a){THREE.Object3D.call(this);this.light=a;this.light.updateMatrixWorld();this.matrixWorld=a.matrixWorld;this.matrixAutoUpdate=!1;a=new THREE.CylinderGeometry(0,1,1,8,1,!0);a.applyMatrix((new THREE.Matrix4).makeTranslation(0,-0.5,0));a.applyMatrix((new THREE.Matrix4).makeRotationX(-Math.PI/2));var b=new THREE.MeshBasicMaterial({wireframe:!0,fog:!1});this.cone=new THREE.Mesh(a,b);this.add(this.cone);this.update()};THREE.SpotLightHelper.prototype=Object.create(THREE.Object3D.prototype);
+THREE.SpotLightHelper.prototype.dispose=function(){this.cone.geometry.dispose();this.cone.material.dispose()};THREE.SpotLightHelper.prototype.update=function(){var a=new THREE.Vector3,b=new THREE.Vector3;return function(){var c=this.light.distance?this.light.distance:1E4,d=c*Math.tan(this.light.angle);this.cone.scale.set(d,d,c);a.getPositionFromMatrix(this.light.matrixWorld);b.getPositionFromMatrix(this.light.target.matrixWorld);this.cone.lookAt(b.sub(a));this.cone.material.color.copy(this.light.color).multiplyScalar(this.light.intensity)}}();THREE.VertexNormalsHelper=function(a,b,c,d){this.object=a;this.size=b||1;for(var b=c||16711680,d=d||1,c=new THREE.Geometry,a=a.geometry.faces,e=0,f=a.length;e<f;e++)for(var h=0,g=a[e].vertexNormals.length;h<g;h++)c.vertices.push(new THREE.Vector3),c.vertices.push(new THREE.Vector3);THREE.Line.call(this,c,new THREE.LineBasicMaterial({color:b,linewidth:d}),THREE.LinePieces);this.matrixAutoUpdate=!1;this.normalMatrix=new THREE.Matrix3;this.update()};THREE.VertexNormalsHelper.prototype=Object.create(THREE.Line.prototype);
+THREE.VertexNormalsHelper.prototype.update=function(){var a=new THREE.Vector3;return function(){var b=["a","b","c","d"];this.object.updateMatrixWorld(!0);this.normalMatrix.getNormalMatrix(this.object.matrixWorld);for(var c=this.geometry.vertices,d=this.object.geometry.vertices,e=this.object.geometry.faces,f=this.object.matrixWorld,h=0,g=0,i=e.length;g<i;g++)for(var k=e[g],m=0,l=k.vertexNormals.length;m<l;m++){var p=k.vertexNormals[m];c[h].copy(d[k[b[m]]]).applyMatrix4(f);a.copy(p).applyMatrix3(this.normalMatrix).normalize().multiplyScalar(this.size);
+a.add(c[h]);h+=1;c[h].copy(a);h+=1}this.geometry.verticesNeedUpdate=!0;return this}}();THREE.VertexTangentsHelper=function(a,b,c,d){this.object=a;this.size=b||1;for(var b=c||255,d=d||1,c=new THREE.Geometry,a=a.geometry.faces,e=0,f=a.length;e<f;e++)for(var h=0,g=a[e].vertexTangents.length;h<g;h++)c.vertices.push(new THREE.Vector3),c.vertices.push(new THREE.Vector3);THREE.Line.call(this,c,new THREE.LineBasicMaterial({color:b,linewidth:d}),THREE.LinePieces);this.matrixAutoUpdate=!1;this.update()};THREE.VertexTangentsHelper.prototype=Object.create(THREE.Line.prototype);
+THREE.VertexTangentsHelper.prototype.update=function(){var a=new THREE.Vector3;return function(){var b=["a","b","c","d"];this.object.updateMatrixWorld(!0);for(var c=this.geometry.vertices,d=this.object.geometry.vertices,e=this.object.geometry.faces,f=this.object.matrixWorld,h=0,g=0,i=e.length;g<i;g++)for(var k=e[g],m=0,l=k.vertexTangents.length;m<l;m++){var p=k.vertexTangents[m];c[h].copy(d[k[b[m]]]).applyMatrix4(f);a.copy(p).transformDirection(f).multiplyScalar(this.size);a.add(c[h]);h+=1;c[h].copy(a);
+h+=1}this.geometry.verticesNeedUpdate=!0;return this}}();THREE.WireframeHelper=function(a){for(var b=[0,0],c={},d=function(a,b){return a-b},e=["a","b","c","d"],f=new THREE.Geometry,h=a.geometry.vertices,g=a.geometry.faces,i=0,k=g.length;i<k;i++)for(var m=g[i],l=0;3>l;l++){b[0]=m[e[l]];b[1]=m[e[(l+1)%3]];b.sort(d);var p=b.toString();void 0===c[p]&&(f.vertices.push(h[b[0]]),f.vertices.push(h[b[1]]),c[p]=!0)}THREE.Line.call(this,f,new THREE.LineBasicMaterial({color:16777215}),THREE.LinePieces);this.matrixAutoUpdate=!1;this.matrixWorld=a.matrixWorld};
+THREE.WireframeHelper.prototype=Object.create(THREE.Line.prototype);THREE.ImmediateRenderObject=function(){THREE.Object3D.call(this);this.render=function(){}};THREE.ImmediateRenderObject.prototype=Object.create(THREE.Object3D.prototype);THREE.LensFlare=function(a,b,c,d,e){THREE.Object3D.call(this);this.lensFlares=[];this.positionScreen=new THREE.Vector3;this.customUpdateCallback=void 0;void 0!==a&&this.add(a,b,c,d,e)};THREE.LensFlare.prototype=Object.create(THREE.Object3D.prototype);
+THREE.LensFlare.prototype.add=function(a,b,c,d,e,f){void 0===b&&(b=-1);void 0===c&&(c=0);void 0===f&&(f=1);void 0===e&&(e=new THREE.Color(16777215));void 0===d&&(d=THREE.NormalBlending);c=Math.min(c,Math.max(0,c));this.lensFlares.push({texture:a,size:b,distance:c,x:0,y:0,z:0,scale:1,rotation:1,opacity:f,color:e,blending:d})};
+THREE.LensFlare.prototype.updateLensFlares=function(){var a,b=this.lensFlares.length,c,d=2*-this.positionScreen.x,e=2*-this.positionScreen.y;for(a=0;a<b;a++)c=this.lensFlares[a],c.x=this.positionScreen.x+d*c.distance,c.y=this.positionScreen.y+e*c.distance,c.wantedRotation=0.25*c.x*Math.PI,c.rotation+=0.25*(c.wantedRotation-c.rotation)};THREE.MorphBlendMesh=function(a,b){THREE.Mesh.call(this,a,b);this.animationsMap={};this.animationsList=[];var c=this.geometry.morphTargets.length;this.createAnimation("__default",0,c-1,c/1);this.setAnimationWeight("__default",1)};THREE.MorphBlendMesh.prototype=Object.create(THREE.Mesh.prototype);
+THREE.MorphBlendMesh.prototype.createAnimation=function(a,b,c,d){b={startFrame:b,endFrame:c,length:c-b+1,fps:d,duration:(c-b)/d,lastFrame:0,currentFrame:0,active:!1,time:0,direction:1,weight:1,directionBackwards:!1,mirroredLoop:!1};this.animationsMap[a]=b;this.animationsList.push(b)};
+THREE.MorphBlendMesh.prototype.autoCreateAnimations=function(a){for(var b=/([a-z]+)(\d+)/,c,d={},e=this.geometry,f=0,h=e.morphTargets.length;f<h;f++){var g=e.morphTargets[f].name.match(b);if(g&&1<g.length){var i=g[1];d[i]||(d[i]={start:Infinity,end:-Infinity});g=d[i];f<g.start&&(g.start=f);f>g.end&&(g.end=f);c||(c=i)}}for(i in d)g=d[i],this.createAnimation(i,g.start,g.end,a);this.firstAnimation=c};
+THREE.MorphBlendMesh.prototype.setAnimationDirectionForward=function(a){if(a=this.animationsMap[a])a.direction=1,a.directionBackwards=!1};THREE.MorphBlendMesh.prototype.setAnimationDirectionBackward=function(a){if(a=this.animationsMap[a])a.direction=-1,a.directionBackwards=!0};THREE.MorphBlendMesh.prototype.setAnimationFPS=function(a,b){var c=this.animationsMap[a];c&&(c.fps=b,c.duration=(c.end-c.start)/c.fps)};
+THREE.MorphBlendMesh.prototype.setAnimationDuration=function(a,b){var c=this.animationsMap[a];c&&(c.duration=b,c.fps=(c.end-c.start)/c.duration)};THREE.MorphBlendMesh.prototype.setAnimationWeight=function(a,b){var c=this.animationsMap[a];c&&(c.weight=b)};THREE.MorphBlendMesh.prototype.setAnimationTime=function(a,b){var c=this.animationsMap[a];c&&(c.time=b)};THREE.MorphBlendMesh.prototype.getAnimationTime=function(a){var b=0;if(a=this.animationsMap[a])b=a.time;return b};
+THREE.MorphBlendMesh.prototype.getAnimationDuration=function(a){var b=-1;if(a=this.animationsMap[a])b=a.duration;return b};THREE.MorphBlendMesh.prototype.playAnimation=function(a){var b=this.animationsMap[a];b?(b.time=0,b.active=!0):console.warn("animation["+a+"] undefined")};THREE.MorphBlendMesh.prototype.stopAnimation=function(a){if(a=this.animationsMap[a])a.active=!1};
+THREE.MorphBlendMesh.prototype.update=function(a){for(var b=0,c=this.animationsList.length;b<c;b++){var d=this.animationsList[b];if(d.active){var e=d.duration/d.length;d.time+=d.direction*a;if(d.mirroredLoop){if(d.time>d.duration||0>d.time)d.direction*=-1,d.time>d.duration&&(d.time=d.duration,d.directionBackwards=!0),0>d.time&&(d.time=0,d.directionBackwards=!1)}else d.time%=d.duration,0>d.time&&(d.time+=d.duration);var f=d.startFrame+THREE.Math.clamp(Math.floor(d.time/e),0,d.length-1),h=d.weight;
+f!==d.currentFrame&&(this.morphTargetInfluences[d.lastFrame]=0,this.morphTargetInfluences[d.currentFrame]=1*h,this.morphTargetInfluences[f]=0,d.lastFrame=d.currentFrame,d.currentFrame=f);e=d.time%e/e;d.directionBackwards&&(e=1-e);this.morphTargetInfluences[d.currentFrame]=e*h;this.morphTargetInfluences[d.lastFrame]=(1-e)*h}}};THREE.LensFlarePlugin=function(){function a(a,c){var d=b.createProgram(),e=b.createShader(b.FRAGMENT_SHADER),f=b.createShader(b.VERTEX_SHADER),g="precision "+c+" float;\n";b.shaderSource(e,g+a.fragmentShader);b.shaderSource(f,g+a.vertexShader);b.compileShader(e);b.compileShader(f);b.attachShader(d,e);b.attachShader(d,f);b.linkProgram(d);return d}var b,c,d,e,f,h,g,i,k,m,l,p,s;this.init=function(t){b=t.context;c=t;d=t.getPrecision();e=new Float32Array(16);f=new Uint16Array(6);t=0;e[t++]=-1;e[t++]=-1;
+e[t++]=0;e[t++]=0;e[t++]=1;e[t++]=-1;e[t++]=1;e[t++]=0;e[t++]=1;e[t++]=1;e[t++]=1;e[t++]=1;e[t++]=-1;e[t++]=1;e[t++]=0;e[t++]=1;t=0;f[t++]=0;f[t++]=1;f[t++]=2;f[t++]=0;f[t++]=2;f[t++]=3;h=b.createBuffer();g=b.createBuffer();b.bindBuffer(b.ARRAY_BUFFER,h);b.bufferData(b.ARRAY_BUFFER,e,b.STATIC_DRAW);b.bindBuffer(b.ELEMENT_ARRAY_BUFFER,g);b.bufferData(b.ELEMENT_ARRAY_BUFFER,f,b.STATIC_DRAW);i=b.createTexture();k=b.createTexture();b.bindTexture(b.TEXTURE_2D,i);b.texImage2D(b.TEXTURE_2D,0,b.RGB,16,16,
+0,b.RGB,b.UNSIGNED_BYTE,null);b.texParameteri(b.TEXTURE_2D,b.TEXTURE_WRAP_S,b.CLAMP_TO_EDGE);b.texParameteri(b.TEXTURE_2D,b.TEXTURE_WRAP_T,b.CLAMP_TO_EDGE);b.texParameteri(b.TEXTURE_2D,b.TEXTURE_MAG_FILTER,b.NEAREST);b.texParameteri(b.TEXTURE_2D,b.TEXTURE_MIN_FILTER,b.NEAREST);b.bindTexture(b.TEXTURE_2D,k);b.texImage2D(b.TEXTURE_2D,0,b.RGBA,16,16,0,b.RGBA,b.UNSIGNED_BYTE,null);b.texParameteri(b.TEXTURE_2D,b.TEXTURE_WRAP_S,b.CLAMP_TO_EDGE);b.texParameteri(b.TEXTURE_2D,b.TEXTURE_WRAP_T,b.CLAMP_TO_EDGE);
+b.texParameteri(b.TEXTURE_2D,b.TEXTURE_MAG_FILTER,b.NEAREST);b.texParameteri(b.TEXTURE_2D,b.TEXTURE_MIN_FILTER,b.NEAREST);0>=b.getParameter(b.MAX_VERTEX_TEXTURE_IMAGE_UNITS)?(m=!1,l=a(THREE.ShaderFlares.lensFlare,d)):(m=!0,l=a(THREE.ShaderFlares.lensFlareVertexTexture,d));p={};s={};p.vertex=b.getAttribLocation(l,"position");p.uv=b.getAttribLocation(l,"uv");s.renderType=b.getUniformLocation(l,"renderType");s.map=b.getUniformLocation(l,"map");s.occlusionMap=b.getUniformLocation(l,"occlusionMap");s.opacity=
+b.getUniformLocation(l,"opacity");s.color=b.getUniformLocation(l,"color");s.scale=b.getUniformLocation(l,"scale");s.rotation=b.getUniformLocation(l,"rotation");s.screenPosition=b.getUniformLocation(l,"screenPosition")};this.render=function(a,d,e,f){var a=a.__webglFlares,u=a.length;if(u){var w=new THREE.Vector3,z=f/e,B=0.5*e,D=0.5*f,x=16/f,F=new THREE.Vector2(x*z,x),A=new THREE.Vector3(1,1,0),O=new THREE.Vector2(1,1),C=s,x=p;b.useProgram(l);b.enableVertexAttribArray(p.vertex);b.enableVertexAttribArray(p.uv);
+b.uniform1i(C.occlusionMap,0);b.uniform1i(C.map,1);b.bindBuffer(b.ARRAY_BUFFER,h);b.vertexAttribPointer(x.vertex,2,b.FLOAT,!1,16,0);b.vertexAttribPointer(x.uv,2,b.FLOAT,!1,16,8);b.bindBuffer(b.ELEMENT_ARRAY_BUFFER,g);b.disable(b.CULL_FACE);b.depthMask(!1);var E,I,y,v,G;for(E=0;E<u;E++)if(x=16/f,F.set(x*z,x),v=a[E],w.set(v.matrixWorld.elements[12],v.matrixWorld.elements[13],v.matrixWorld.elements[14]),w.applyMatrix4(d.matrixWorldInverse),w.applyProjection(d.projectionMatrix),A.copy(w),O.x=A.x*B+B,
+O.y=A.y*D+D,m||0<O.x&&O.x<e&&0<O.y&&O.y<f){b.activeTexture(b.TEXTURE1);b.bindTexture(b.TEXTURE_2D,i);b.copyTexImage2D(b.TEXTURE_2D,0,b.RGB,O.x-8,O.y-8,16,16,0);b.uniform1i(C.renderType,0);b.uniform2f(C.scale,F.x,F.y);b.uniform3f(C.screenPosition,A.x,A.y,A.z);b.disable(b.BLEND);b.enable(b.DEPTH_TEST);b.drawElements(b.TRIANGLES,6,b.UNSIGNED_SHORT,0);b.activeTexture(b.TEXTURE0);b.bindTexture(b.TEXTURE_2D,k);b.copyTexImage2D(b.TEXTURE_2D,0,b.RGBA,O.x-8,O.y-8,16,16,0);b.uniform1i(C.renderType,1);b.disable(b.DEPTH_TEST);
+b.activeTexture(b.TEXTURE1);b.bindTexture(b.TEXTURE_2D,i);b.drawElements(b.TRIANGLES,6,b.UNSIGNED_SHORT,0);v.positionScreen.copy(A);v.customUpdateCallback?v.customUpdateCallback(v):v.updateLensFlares();b.uniform1i(C.renderType,2);b.enable(b.BLEND);I=0;for(y=v.lensFlares.length;I<y;I++)G=v.lensFlares[I],0.001<G.opacity&&0.001<G.scale&&(A.x=G.x,A.y=G.y,A.z=G.z,x=G.size*G.scale/f,F.x=x*z,F.y=x,b.uniform3f(C.screenPosition,A.x,A.y,A.z),b.uniform2f(C.scale,F.x,F.y),b.uniform1f(C.rotation,G.rotation),b.uniform1f(C.opacity,
+G.opacity),b.uniform3f(C.color,G.color.r,G.color.g,G.color.b),c.setBlending(G.blending,G.blendEquation,G.blendSrc,G.blendDst),c.setTexture(G.texture,1),b.drawElements(b.TRIANGLES,6,b.UNSIGNED_SHORT,0))}b.enable(b.CULL_FACE);b.enable(b.DEPTH_TEST);b.depthMask(!0)}}};THREE.ShadowMapPlugin=function(){var a,b,c,d,e,f,h=new THREE.Frustum,g=new THREE.Matrix4,i=new THREE.Vector3,k=new THREE.Vector3,m=new THREE.Vector3;this.init=function(g){a=g.context;b=g;var g=THREE.ShaderLib.depthRGBA,h=THREE.UniformsUtils.clone(g.uniforms);c=new THREE.ShaderMaterial({fragmentShader:g.fragmentShader,vertexShader:g.vertexShader,uniforms:h});d=new THREE.ShaderMaterial({fragmentShader:g.fragmentShader,vertexShader:g.vertexShader,uniforms:h,morphTargets:!0});e=new THREE.ShaderMaterial({fragmentShader:g.fragmentShader,
+vertexShader:g.vertexShader,uniforms:h,skinning:!0});f=new THREE.ShaderMaterial({fragmentShader:g.fragmentShader,vertexShader:g.vertexShader,uniforms:h,morphTargets:!0,skinning:!0});c._shadowPass=!0;d._shadowPass=!0;e._shadowPass=!0;f._shadowPass=!0};this.render=function(a,c){b.shadowMapEnabled&&b.shadowMapAutoUpdate&&this.update(a,c)};this.update=function(l,p){var s,t,n,r,q,u,w,z,B,D=[];r=0;a.clearColor(1,1,1,1);a.disable(a.BLEND);a.enable(a.CULL_FACE);a.frontFace(a.CCW);b.shadowMapCullFace===THREE.CullFaceFront?
+a.cullFace(a.FRONT):a.cullFace(a.BACK);b.setDepthTest(!0);s=0;for(t=l.__lights.length;s<t;s++)if(n=l.__lights[s],n.castShadow)if(n instanceof THREE.DirectionalLight&&n.shadowCascade)for(q=0;q<n.shadowCascadeCount;q++){var x;if(n.shadowCascadeArray[q])x=n.shadowCascadeArray[q];else{B=n;w=q;x=new THREE.DirectionalLight;x.isVirtual=!0;x.onlyShadow=!0;x.castShadow=!0;x.shadowCameraNear=B.shadowCameraNear;x.shadowCameraFar=B.shadowCameraFar;x.shadowCameraLeft=B.shadowCameraLeft;x.shadowCameraRight=B.shadowCameraRight;
+x.shadowCameraBottom=B.shadowCameraBottom;x.shadowCameraTop=B.shadowCameraTop;x.shadowCameraVisible=B.shadowCameraVisible;x.shadowDarkness=B.shadowDarkness;x.shadowBias=B.shadowCascadeBias[w];x.shadowMapWidth=B.shadowCascadeWidth[w];x.shadowMapHeight=B.shadowCascadeHeight[w];x.pointsWorld=[];x.pointsFrustum=[];z=x.pointsWorld;u=x.pointsFrustum;for(var F=0;8>F;F++)z[F]=new THREE.Vector3,u[F]=new THREE.Vector3;z=B.shadowCascadeNearZ[w];B=B.shadowCascadeFarZ[w];u[0].set(-1,-1,z);u[1].set(1,-1,z);u[2].set(-1,
+1,z);u[3].set(1,1,z);u[4].set(-1,-1,B);u[5].set(1,-1,B);u[6].set(-1,1,B);u[7].set(1,1,B);x.originalCamera=p;u=new THREE.Gyroscope;u.position=n.shadowCascadeOffset;u.add(x);u.add(x.target);p.add(u);n.shadowCascadeArray[q]=x;console.log("Created virtualLight",x)}w=n;z=q;B=w.shadowCascadeArray[z];B.position.copy(w.position);B.target.position.copy(w.target.position);B.lookAt(B.target);B.shadowCameraVisible=w.shadowCameraVisible;B.shadowDarkness=w.shadowDarkness;B.shadowBias=w.shadowCascadeBias[z];u=w.shadowCascadeNearZ[z];
+w=w.shadowCascadeFarZ[z];B=B.pointsFrustum;B[0].z=u;B[1].z=u;B[2].z=u;B[3].z=u;B[4].z=w;B[5].z=w;B[6].z=w;B[7].z=w;D[r]=x;r++}else D[r]=n,r++;s=0;for(t=D.length;s<t;s++){n=D[s];n.shadowMap||(q=THREE.LinearFilter,b.shadowMapType===THREE.PCFSoftShadowMap&&(q=THREE.NearestFilter),n.shadowMap=new THREE.WebGLRenderTarget(n.shadowMapWidth,n.shadowMapHeight,{minFilter:q,magFilter:q,format:THREE.RGBAFormat}),n.shadowMapSize=new THREE.Vector2(n.shadowMapWidth,n.shadowMapHeight),n.shadowMatrix=new THREE.Matrix4);
+if(!n.shadowCamera){if(n instanceof THREE.SpotLight)n.shadowCamera=new THREE.PerspectiveCamera(n.shadowCameraFov,n.shadowMapWidth/n.shadowMapHeight,n.shadowCameraNear,n.shadowCameraFar);else if(n instanceof THREE.DirectionalLight)n.shadowCamera=new THREE.OrthographicCamera(n.shadowCameraLeft,n.shadowCameraRight,n.shadowCameraTop,n.shadowCameraBottom,n.shadowCameraNear,n.shadowCameraFar);else{console.error("Unsupported light type for shadow");continue}l.add(n.shadowCamera);!0===l.autoUpdate&&l.updateMatrixWorld()}n.shadowCameraVisible&&
+!n.cameraHelper&&(n.cameraHelper=new THREE.CameraHelper(n.shadowCamera),n.shadowCamera.add(n.cameraHelper));if(n.isVirtual&&x.originalCamera==p){q=p;r=n.shadowCamera;u=n.pointsFrustum;B=n.pointsWorld;i.set(Infinity,Infinity,Infinity);k.set(-Infinity,-Infinity,-Infinity);for(w=0;8>w;w++)z=B[w],z.copy(u[w]),THREE.ShadowMapPlugin.__projector.unprojectVector(z,q),z.applyMatrix4(r.matrixWorldInverse),z.x<i.x&&(i.x=z.x),z.x>k.x&&(k.x=z.x),z.y<i.y&&(i.y=z.y),z.y>k.y&&(k.y=z.y),z.z<i.z&&(i.z=z.z),z.z>k.z&&
+(k.z=z.z);r.left=i.x;r.right=k.x;r.top=k.y;r.bottom=i.y;r.updateProjectionMatrix()}r=n.shadowMap;u=n.shadowMatrix;q=n.shadowCamera;q.position.getPositionFromMatrix(n.matrixWorld);m.getPositionFromMatrix(n.target.matrixWorld);q.lookAt(m);q.updateMatrixWorld();q.matrixWorldInverse.getInverse(q.matrixWorld);n.cameraHelper&&(n.cameraHelper.visible=n.shadowCameraVisible);n.shadowCameraVisible&&n.cameraHelper.update();u.set(0.5,0,0,0.5,0,0.5,0,0.5,0,0,0.5,0.5,0,0,0,1);u.multiply(q.projectionMatrix);u.multiply(q.matrixWorldInverse);
+g.multiplyMatrices(q.projectionMatrix,q.matrixWorldInverse);h.setFromMatrix(g);b.setRenderTarget(r);b.clear();B=l.__webglObjects;n=0;for(r=B.length;n<r;n++)if(w=B[n],u=w.object,w.render=!1,u.visible&&u.castShadow&&(!(u instanceof THREE.Mesh||u instanceof THREE.ParticleSystem)||!u.frustumCulled||h.intersectsObject(u)))u._modelViewMatrix.multiplyMatrices(q.matrixWorldInverse,u.matrixWorld),w.render=!0;n=0;for(r=B.length;n<r;n++)w=B[n],w.render&&(u=w.object,w=w.buffer,F=u.material instanceof THREE.MeshFaceMaterial?
+u.material.materials[0]:u.material,z=0<u.geometry.morphTargets.length&&F.morphTargets,F=u instanceof THREE.SkinnedMesh&&F.skinning,z=u.customDepthMaterial?u.customDepthMaterial:F?z?f:e:z?d:c,w instanceof THREE.BufferGeometry?b.renderBufferDirect(q,l.__lights,null,z,w,u):b.renderBuffer(q,l.__lights,null,z,w,u));B=l.__webglObjectsImmediate;n=0;for(r=B.length;n<r;n++)w=B[n],u=w.object,u.visible&&u.castShadow&&(u._modelViewMatrix.multiplyMatrices(q.matrixWorldInverse,u.matrixWorld),b.renderImmediateObject(q,
+l.__lights,null,c,u))}s=b.getClearColor();t=b.getClearAlpha();a.clearColor(s.r,s.g,s.b,t);a.enable(a.BLEND);b.shadowMapCullFace===THREE.CullFaceFront&&a.cullFace(a.BACK)}};THREE.ShadowMapPlugin.__projector=new THREE.Projector;THREE.SpritePlugin=function(){function a(a,b){return a.z!==b.z?b.z-a.z:b.id-a.id}var b,c,d,e,f,h,g,i,k,m;this.init=function(a){b=a.context;c=a;d=a.getPrecision();e=new Float32Array(16);f=new Uint16Array(6);a=0;e[a++]=-0.5;e[a++]=-0.5;e[a++]=0;e[a++]=0;e[a++]=0.5;e[a++]=-0.5;e[a++]=1;e[a++]=0;e[a++]=0.5;e[a++]=0.5;e[a++]=1;e[a++]=1;e[a++]=-0.5;e[a++]=0.5;e[a++]=0;e[a++]=1;a=0;f[a++]=0;f[a++]=1;f[a++]=2;f[a++]=0;f[a++]=2;f[a++]=3;h=b.createBuffer();g=b.createBuffer();b.bindBuffer(b.ARRAY_BUFFER,h);
+b.bufferData(b.ARRAY_BUFFER,e,b.STATIC_DRAW);b.bindBuffer(b.ELEMENT_ARRAY_BUFFER,g);b.bufferData(b.ELEMENT_ARRAY_BUFFER,f,b.STATIC_DRAW);var a=THREE.ShaderSprite.sprite,p=b.createProgram(),s=b.createShader(b.FRAGMENT_SHADER),t=b.createShader(b.VERTEX_SHADER),n="precision "+d+" float;\n";b.shaderSource(s,n+a.fragmentShader);b.shaderSource(t,n+a.vertexShader);b.compileShader(s);b.compileShader(t);b.attachShader(p,s);b.attachShader(p,t);b.linkProgram(p);i=p;k={};m={};k.position=b.getAttribLocation(i,
+"position");k.uv=b.getAttribLocation(i,"uv");m.uvOffset=b.getUniformLocation(i,"uvOffset");m.uvScale=b.getUniformLocation(i,"uvScale");m.rotation=b.getUniformLocation(i,"rotation");m.scale=b.getUniformLocation(i,"scale");m.alignment=b.getUniformLocation(i,"alignment");m.halfViewport=b.getUniformLocation(i,"halfViewport");m.color=b.getUniformLocation(i,"color");m.map=b.getUniformLocation(i,"map");m.opacity=b.getUniformLocation(i,"opacity");m.useScreenCoordinates=b.getUniformLocation(i,"useScreenCoordinates");
+m.sizeAttenuation=b.getUniformLocation(i,"sizeAttenuation");m.screenPosition=b.getUniformLocation(i,"screenPosition");m.modelViewMatrix=b.getUniformLocation(i,"modelViewMatrix");m.projectionMatrix=b.getUniformLocation(i,"projectionMatrix");m.fogType=b.getUniformLocation(i,"fogType");m.fogDensity=b.getUniformLocation(i,"fogDensity");m.fogNear=b.getUniformLocation(i,"fogNear");m.fogFar=b.getUniformLocation(i,"fogFar");m.fogColor=b.getUniformLocation(i,"fogColor");m.alphaTest=b.getUniformLocation(i,
+"alphaTest")};this.render=function(d,e,f,t){var n=d.__webglSprites,r=n.length;if(r){var q=k,u=m,f=0.5*f,t=0.5*t;b.useProgram(i);b.enableVertexAttribArray(q.position);b.enableVertexAttribArray(q.uv);b.disable(b.CULL_FACE);b.enable(b.BLEND);b.bindBuffer(b.ARRAY_BUFFER,h);b.vertexAttribPointer(q.position,2,b.FLOAT,!1,16,0);b.vertexAttribPointer(q.uv,2,b.FLOAT,!1,16,8);b.bindBuffer(b.ELEMENT_ARRAY_BUFFER,g);b.uniformMatrix4fv(u.projectionMatrix,!1,e.projectionMatrix.elements);b.activeTexture(b.TEXTURE0);
+b.uniform1i(u.map,0);var w=q=0,z=d.fog;z?(b.uniform3f(u.fogColor,z.color.r,z.color.g,z.color.b),z instanceof THREE.Fog?(b.uniform1f(u.fogNear,z.near),b.uniform1f(u.fogFar,z.far),b.uniform1i(u.fogType,1),w=q=1):z instanceof THREE.FogExp2&&(b.uniform1f(u.fogDensity,z.density),b.uniform1i(u.fogType,2),w=q=2)):(b.uniform1i(u.fogType,0),w=q=0);for(var B,D,x=[],z=0;z<r;z++)B=n[z],D=B.material,B.visible&&0!==D.opacity&&(D.useScreenCoordinates?B.z=-B.position.z:(B._modelViewMatrix.multiplyMatrices(e.matrixWorldInverse,
+B.matrixWorld),B.z=-B._modelViewMatrix.elements[14]));n.sort(a);for(z=0;z<r;z++)B=n[z],D=B.material,B.visible&&0!==D.opacity&&(D.map&&D.map.image&&D.map.image.width)&&(b.uniform1f(u.alphaTest,D.alphaTest),!0===D.useScreenCoordinates?(b.uniform1i(u.useScreenCoordinates,1),b.uniform3f(u.screenPosition,(B.position.x*c.devicePixelRatio-f)/f,(t-B.position.y*c.devicePixelRatio)/t,Math.max(0,Math.min(1,B.position.z))),x[0]=c.devicePixelRatio*B.scale.x,x[1]=c.devicePixelRatio*B.scale.y):(b.uniform1i(u.useScreenCoordinates,
+0),b.uniform1i(u.sizeAttenuation,D.sizeAttenuation?1:0),b.uniformMatrix4fv(u.modelViewMatrix,!1,B._modelViewMatrix.elements),x[0]=B.scale.x,x[1]=B.scale.y),e=d.fog&&D.fog?w:0,q!==e&&(b.uniform1i(u.fogType,e),q=e),b.uniform2f(u.uvScale,D.uvScale.x,D.uvScale.y),b.uniform2f(u.uvOffset,D.uvOffset.x,D.uvOffset.y),b.uniform2f(u.alignment,D.alignment.x,D.alignment.y),b.uniform1f(u.opacity,D.opacity),b.uniform3f(u.color,D.color.r,D.color.g,D.color.b),b.uniform1f(u.rotation,B.rotation),b.uniform2fv(u.scale,
+x),b.uniform2f(u.halfViewport,f,t),c.setBlending(D.blending,D.blendEquation,D.blendSrc,D.blendDst),c.setDepthTest(D.depthTest),c.setDepthWrite(D.depthWrite),c.setTexture(D.map,0),b.drawElements(b.TRIANGLES,6,b.UNSIGNED_SHORT,0));b.enable(b.CULL_FACE)}}};THREE.DepthPassPlugin=function(){this.enabled=!1;this.renderTarget=null;var a,b,c,d,e,f,h=new THREE.Frustum,g=new THREE.Matrix4;this.init=function(g){a=g.context;b=g;var g=THREE.ShaderLib.depthRGBA,h=THREE.UniformsUtils.clone(g.uniforms);c=new THREE.ShaderMaterial({fragmentShader:g.fragmentShader,vertexShader:g.vertexShader,uniforms:h});d=new THREE.ShaderMaterial({fragmentShader:g.fragmentShader,vertexShader:g.vertexShader,uniforms:h,morphTargets:!0});e=new THREE.ShaderMaterial({fragmentShader:g.fragmentShader,
+vertexShader:g.vertexShader,uniforms:h,skinning:!0});f=new THREE.ShaderMaterial({fragmentShader:g.fragmentShader,vertexShader:g.vertexShader,uniforms:h,morphTargets:!0,skinning:!0});c._shadowPass=!0;d._shadowPass=!0;e._shadowPass=!0;f._shadowPass=!0};this.render=function(a,b){this.enabled&&this.update(a,b)};this.update=function(i,k){var m,l,p,s,t,n;a.clearColor(1,1,1,1);a.disable(a.BLEND);b.setDepthTest(!0);!0===i.autoUpdate&&i.updateMatrixWorld();k.matrixWorldInverse.getInverse(k.matrixWorld);g.multiplyMatrices(k.projectionMatrix,
+k.matrixWorldInverse);h.setFromMatrix(g);b.setRenderTarget(this.renderTarget);b.clear();n=i.__webglObjects;m=0;for(l=n.length;m<l;m++)if(p=n[m],t=p.object,p.render=!1,t.visible&&(!(t instanceof THREE.Mesh||t instanceof THREE.ParticleSystem)||!t.frustumCulled||h.intersectsObject(t)))t._modelViewMatrix.multiplyMatrices(k.matrixWorldInverse,t.matrixWorld),p.render=!0;var r;m=0;for(l=n.length;m<l;m++)if(p=n[m],p.render&&(t=p.object,p=p.buffer,!(t instanceof THREE.ParticleSystem)||t.customDepthMaterial))(r=
+t.material instanceof THREE.MeshFaceMaterial?t.material.materials[0]:t.material)&&b.setMaterialFaces(t.material),s=0<t.geometry.morphTargets.length&&r.morphTargets,r=t instanceof THREE.SkinnedMesh&&r.skinning,s=t.customDepthMaterial?t.customDepthMaterial:r?s?f:e:s?d:c,p instanceof THREE.BufferGeometry?b.renderBufferDirect(k,i.__lights,null,s,p,t):b.renderBuffer(k,i.__lights,null,s,p,t);n=i.__webglObjectsImmediate;m=0;for(l=n.length;m<l;m++)p=n[m],t=p.object,t.visible&&(t._modelViewMatrix.multiplyMatrices(k.matrixWorldInverse,
+t.matrixWorld),b.renderImmediateObject(k,i.__lights,null,c,t));m=b.getClearColor();l=b.getClearAlpha();a.clearColor(m.r,m.g,m.b,l);a.enable(a.BLEND)}};THREE.ShaderFlares={lensFlareVertexTexture:{vertexShader:"uniform lowp int renderType;\nuniform vec3 screenPosition;\nuniform vec2 scale;\nuniform float rotation;\nuniform sampler2D occlusionMap;\nattribute vec2 position;\nattribute vec2 uv;\nvarying vec2 vUV;\nvarying float vVisibility;\nvoid main() {\nvUV = uv;\nvec2 pos = position;\nif( renderType == 2 ) {\nvec4 visibility = texture2D( occlusionMap, vec2( 0.1, 0.1 ) );\nvisibility += texture2D( occlusionMap, vec2( 0.5, 0.1 ) );\nvisibility += texture2D( occlusionMap, vec2( 0.9, 0.1 ) );\nvisibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) );\nvisibility += texture2D( occlusionMap, vec2( 0.9, 0.9 ) );\nvisibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) );\nvisibility += texture2D( occlusionMap, vec2( 0.1, 0.9 ) );\nvisibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) );\nvisibility += texture2D( occlusionMap, vec2( 0.5, 0.5 ) );\nvVisibility =        visibility.r / 9.0;\nvVisibility *= 1.0 - visibility.g / 9.0;\nvVisibility *=       visibility.b / 9.0;\nvVisibility *= 1.0 - visibility.a / 9.0;\npos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;\npos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;\n}\ngl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );\n}",
+fragmentShader:"uniform lowp int renderType;\nuniform sampler2D map;\nuniform float opacity;\nuniform vec3 color;\nvarying vec2 vUV;\nvarying float vVisibility;\nvoid main() {\nif( renderType == 0 ) {\ngl_FragColor = vec4( 1.0, 0.0, 1.0, 0.0 );\n} else if( renderType == 1 ) {\ngl_FragColor = texture2D( map, vUV );\n} else {\nvec4 texture = texture2D( map, vUV );\ntexture.a *= opacity * vVisibility;\ngl_FragColor = texture;\ngl_FragColor.rgb *= color;\n}\n}"},lensFlare:{vertexShader:"uniform lowp int renderType;\nuniform vec3 screenPosition;\nuniform vec2 scale;\nuniform float rotation;\nattribute vec2 position;\nattribute vec2 uv;\nvarying vec2 vUV;\nvoid main() {\nvUV = uv;\nvec2 pos = position;\nif( renderType == 2 ) {\npos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;\npos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;\n}\ngl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );\n}",
+fragmentShader:"precision mediump float;\nuniform lowp int renderType;\nuniform sampler2D map;\nuniform sampler2D occlusionMap;\nuniform float opacity;\nuniform vec3 color;\nvarying vec2 vUV;\nvoid main() {\nif( renderType == 0 ) {\ngl_FragColor = vec4( texture2D( map, vUV ).rgb, 0.0 );\n} else if( renderType == 1 ) {\ngl_FragColor = texture2D( map, vUV );\n} else {\nfloat visibility = texture2D( occlusionMap, vec2( 0.5, 0.1 ) ).a;\nvisibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) ).a;\nvisibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) ).a;\nvisibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) ).a;\nvisibility = ( 1.0 - visibility / 4.0 );\nvec4 texture = texture2D( map, vUV );\ntexture.a *= opacity * visibility;\ngl_FragColor = texture;\ngl_FragColor.rgb *= color;\n}\n}"}};THREE.ShaderSprite={sprite:{vertexShader:"uniform int useScreenCoordinates;\nuniform int sizeAttenuation;\nuniform vec3 screenPosition;\nuniform mat4 modelViewMatrix;\nuniform mat4 projectionMatrix;\nuniform float rotation;\nuniform vec2 scale;\nuniform vec2 alignment;\nuniform vec2 uvOffset;\nuniform vec2 uvScale;\nuniform vec2 halfViewport;\nattribute vec2 position;\nattribute vec2 uv;\nvarying vec2 vUV;\nvoid main() {\nvUV = uvOffset + uv * uvScale;\nvec2 alignedPosition = ( position + alignment ) * scale;\nvec2 rotatedPosition;\nrotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;\nrotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;\nvec4 finalPosition;\nif( useScreenCoordinates != 0 ) {\nfinalPosition = vec4( screenPosition.xy + ( rotatedPosition / halfViewport ), screenPosition.z, 1.0 );\n} else {\nfinalPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );\nfinalPosition.xy += rotatedPosition * ( sizeAttenuation == 1 ? 1.0 : finalPosition.z );\nfinalPosition = projectionMatrix * finalPosition;\n}\ngl_Position = finalPosition;\n}",
+fragmentShader:"uniform vec3 color;\nuniform sampler2D map;\nuniform float opacity;\nuniform int fogType;\nuniform vec3 fogColor;\nuniform float fogDensity;\nuniform float fogNear;\nuniform float fogFar;\nuniform float alphaTest;\nvarying vec2 vUV;\nvoid main() {\nvec4 texture = texture2D( map, vUV );\nif ( texture.a < alphaTest ) discard;\ngl_FragColor = vec4( color * texture.xyz, texture.a * opacity );\nif ( fogType > 0 ) {\nfloat depth = gl_FragCoord.z / gl_FragCoord.w;\nfloat fogFactor = 0.0;\nif ( fogType == 1 ) {\nfogFactor = smoothstep( fogNear, fogFar, depth );\n} else {\nconst float LOG2 = 1.442695;\nfloat fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );\nfogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );\n}\ngl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );\n}\n}"}};
diff --git a/gz3d/client/js/include/xml2json.js b/gz3d/client/js/include/xml2json.js
new file mode 100644
index 0000000000000000000000000000000000000000..288fc5fd374fe3a20759996b1603f2f4c71bbc63
--- /dev/null
+++ b/gz3d/client/js/include/xml2json.js
@@ -0,0 +1,159 @@
+/*	This work is licensed under Creative Commons GNU LGPL License.
+
+	License: http://creativecommons.org/licenses/LGPL/2.1/
+   Version: 0.9
+	Author:  Stefan Goessner/2006
+	Web:     http://goessner.net/ 
+	Note: changed to provide even single node as an array
+*/
+function xml2json(xml, tab) {
+   var X = {
+      toObj: function(xml) {
+         var o = {};
+         if (xml.nodeType==1) {   // element node ..
+            if (xml.attributes.length)   // element with attributes  ..
+               for (var i=0; i<xml.attributes.length; i++)
+                  o["@"+xml.attributes[i].nodeName] = (xml.attributes[i].nodeValue||"").toString();
+            if (xml.firstChild) { // element has child nodes ..
+               var textChild=0, cdataChild=0, hasElementChild=false;
+               for (var n=xml.firstChild; n; n=n.nextSibling) {
+                  if (n.nodeType==1) hasElementChild = true;
+                  else if (n.nodeType==3 && n.nodeValue.match(/[^ \f\n\r\t\v]/)) textChild++; // non-whitespace text
+                  else if (n.nodeType==4) cdataChild++; // cdata section node
+               }
+               if (hasElementChild) {
+                  if (textChild < 2 && cdataChild < 2) { // structured element with evtl. a single text or/and cdata node ..
+                     X.removeWhite(xml);
+                     for (var n=xml.firstChild; n; n=n.nextSibling) {
+                        if (n.nodeType == 3)  // text node
+                           o["#text"] = X.escape(n.nodeValue);
+                        else if (n.nodeType == 4)  // cdata node
+                           o["#cdata"] = X.escape(n.nodeValue);
+                        else if (o[n.nodeName]) {  // multiple occurence of element ..
+                           if (o[n.nodeName] instanceof Array)
+                              o[n.nodeName][o[n.nodeName].length] = X.toObj(n);
+                           else
+                              o[n.nodeName] = [o[n.nodeName], X.toObj(n)];
+                        }
+                        else  // first occurence of element..
+                           o[n.nodeName] = X.toObj(n);
+                     }
+                  }
+                  else { // mixed content
+                     if (!xml.attributes.length)
+                        o = X.escape(X.innerXml(xml));
+                     else
+                        o["#text"] = X.escape(X.innerXml(xml));
+                  }
+               }
+               else if (textChild) { // pure text
+                  if (!xml.attributes.length)
+                     o = X.escape(X.innerXml(xml));
+                  else
+                     o["#text"] = X.escape(X.innerXml(xml));
+               }
+               else if (cdataChild) { // cdata
+                  if (cdataChild > 1)
+                     o = X.escape(X.innerXml(xml));
+                  else
+                     for (var n=xml.firstChild; n; n=n.nextSibling)
+                        o["#cdata"] = X.escape(n.nodeValue);
+               }
+            }
+            if (!xml.attributes.length && !xml.firstChild) o = null;
+         }
+         else if (xml.nodeType==9) { // document.node
+            o = X.toObj(xml.documentElement);
+         }
+         else if (xml.nodeType==8) {
+           //TODO: comment node. just ignore
+         }
+         else
+            alert("unhandled node type: " + xml.nodeType);
+         return o;
+      },
+      toJson: function(o, name, ind) {
+         var json = name ? ("\""+name+"\"") : "";
+         if (o instanceof Array) {
+            for (var i=0,n=o.length; i<n; i++)
+               o[i] = X.toJson(o[i], "", ind+"\t");
+            json += (name?":[":"[") + (o.length > 1 ? ("\n"+ind+"\t"+o.join(",\n"+ind+"\t")+"\n"+ind) : o.join("")) + "]";
+         }
+         else if (o == null)
+            json += (name&&":") + "null";
+         else if (typeof(o) == "object") {
+            var arr = [];
+            for (var m in o)
+               arr[arr.length] = X.toJson(o[m], m, ind+"\t");
+            json += (name?":{":"{") + (arr.length > 1 ? ("\n"+ind+"\t"+arr.join(",\n"+ind+"\t")+"\n"+ind) : arr.join("")) + "}";
+         }
+         else if (typeof(o) == "string")
+            json += (name&&":") + "\"" + o.toString() + "\"";
+         else
+            json += (name&&":") + o.toString();
+         return json;
+      },
+      innerXml: function(node) {
+         var s = ""
+         if ("innerHTML" in node)
+            s = node.innerHTML;
+         else {
+            var asXml = function(n) {
+               var s = "";
+               if (n.nodeType == 1) {
+                  s += "<" + n.nodeName;
+                  for (var i=0; i<n.attributes.length;i++)
+                     s += " " + n.attributes[i].nodeName + "=\"" + (n.attributes[i].nodeValue||"").toString() + "\"";
+                  if (n.firstChild) {
+                     s += ">";
+                     for (var c=n.firstChild; c; c=c.nextSibling)
+                        s += asXml(c);
+                     s += "</"+n.nodeName+">";
+                  }
+                  else
+                     s += "/>";
+               }
+               else if (n.nodeType == 3)
+                  s += n.nodeValue;
+               else if (n.nodeType == 4)
+                  s += "<![CDATA[" + n.nodeValue + "]]>";
+               return s;
+            };
+            for (var c=node.firstChild; c; c=c.nextSibling)
+               s += asXml(c);
+         }
+         return s;
+      },
+      escape: function(txt) {
+         return txt.replace(/[\\]/g, "\\\\")
+                   .replace(/[\"]/g, '\\"')
+                   .replace(/[\n]/g, '\\n')
+                   .replace(/[\r]/g, '\\r');
+      },
+      removeWhite: function(e) {
+         e.normalize();
+         for (var n = e.firstChild; n; ) {
+            if (n.nodeType == 3) {  // text node
+               if (!n.nodeValue.match(/[^ \f\n\r\t\v]/)) { // pure whitespace text node
+                  var nxt = n.nextSibling;
+                  e.removeChild(n);
+                  n = nxt;
+               }
+               else
+                  n = n.nextSibling;
+            }
+            else if (n.nodeType == 1) {  // element node
+               X.removeWhite(n);
+               n = n.nextSibling;
+            }
+            else                      // any other node
+               n = n.nextSibling;
+         }
+         return e;
+      }
+   };
+   if (xml.nodeType == 9) // document node
+      xml = xml.documentElement;
+   var json = X.toJson(X.toObj(X.removeWhite(xml)), xml.nodeName, "\t");
+   return "{\n" + tab + (tab ? json.replace(/\t/g, tab) : json.replace(/\t|\n/g, "")) + "\n}";
+}
diff --git a/gz3d/client/style/gz3d.css b/gz3d/client/style/gz3d.css
new file mode 100644
index 0000000000000000000000000000000000000000..3f0c952c6adb8187ed179adad7a22d67968aacc4
--- /dev/null
+++ b/gz3d/client/style/gz3d.css
@@ -0,0 +1,735 @@
+/* general */
+body
+{
+  font-size-adjust: 0.5;
+}
+input:invalid
+{
+  box-shadow: none;
+}
+input[type=number]
+{
+  -moz-appearance:textfield;
+}
+input[type=number]::-webkit-outer-spin-button,
+input[type=number]::-webkit-inner-spin-button
+{
+  -webkit-appearance: none;
+  margin: 0;
+}
+.ui-content
+{
+  overflow: hidden;
+}
+#container
+{
+  padding: 0px;
+  position : absolute;
+  top: 0px;
+  bottom: 0px;
+  right: 0px;
+  left: 0px;
+}
+/* header */
+.header-button
+{
+  float: left;
+  height: 1.45em;
+  width: 1.45em;
+  padding: 0.65em;;
+}
+.header-button img
+{
+  height: 1.45em;
+}
+#clock-touch
+{
+  z-index: 100;
+}
+#clock-touch > p
+{
+  text-align: right;
+}
+#clock-mouse > p
+{
+  margin: 0;
+  margin-right: 0.25em;
+  text-align: right;
+}
+/* tabs */
+.tab
+{
+  width: 0;
+  height: 4em;
+  border-left: 2em solid;
+  border-top: 1em solid transparent;
+  border-bottom: 1em solid transparent;
+  z-index: 1001;
+  position: absolute;
+  left: 0em;
+}
+.tab img
+{
+  position: relative;
+  left: -1.7em;
+  top: 1.2em;
+  height: 1.45em;
+}
+.tab:nth-of-type(1)
+{
+  top: 1.8em;
+}
+.tab:nth-of-type(2)
+{
+  top: 7.8em;
+}
+.tab:nth-of-type(3)
+{
+  top: 13.8em;
+}
+/* panel */
+#leftPanel
+{
+  height: 100%;
+  position: absolute;
+  left: 0;
+  z-index: 1002;
+}
+.panelsGroup
+{
+  height: 100%;
+  position: absolute;
+  top: 0;
+}
+.leftPanels
+{
+  display: none;
+  background-color: #2a2a2a;
+  height: 100%;
+  width: 23em;
+}
+.panelContent
+{
+  height: calc(100% - 2.51em);
+  overflow-y: auto;
+  overflow-x: hidden;
+  margin-top: 0.01em;
+}
+.panelTitle
+{
+  width: 100%;
+  height: 2.5em;
+  line-height: 1.8em;
+  background-color: #1d1d1d;
+}
+.panelTitle:hover
+{
+  background-color: #212121;
+}
+.panelTitle h3
+{
+  margin: 0.3em;
+  float: left;
+  width: calc(100% - 1.5em);
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+.panelTitle img
+{
+  height: 1.2em;
+  margin: 0.3em;
+  position: absolute;
+  right: 0;
+  top: 0.3em;
+}
+.leftPanels ul
+{
+  list-style: none;
+  width: 21em;
+  padding: 0em;
+  padding-left: 1em;
+  margin: 0em;
+}
+.clickable:hover
+{
+  cursor: pointer;
+}
+/* main menu*/
+#mainMenu li
+{
+  left: -1em;
+  padding: 0;
+  margin: 0;
+  width: 23em;
+}
+.collapsible_header
+{
+  font-size: 1.2em;
+}
+.collapsible_item
+{
+  font-size: 0.9em;
+  color: #bdbdbd;
+}
+/* insert menu */
+.insertMenuItem
+{
+  width: 7em;
+  height: 6.5em;
+  margin-left: 1.3em;
+  float: left;
+}
+.insertMenuItem img
+{
+  height: 5em;
+  max-width: 6em;
+}
+.insertMenuItem h1
+{
+  font-size: 1em;
+  margin: 0em;
+  width: 100%;
+}
+.insertMenuItemLight img
+{
+  height: 4em;
+  margin-top: 0.6em;
+}
+.insertMenuCategoryItem
+{
+  width: calc(100% - 5em);
+}
+.insertMenuCategoryItem img
+{
+  vertical-align: middle;
+  margin-left: 1em;
+  height: 4em;
+  max-width: 4em;
+  padding-top: 1em;
+}
+.insertMenuCategoryItemLight > img:nth-child(5)
+{
+  display: none;
+}
+/* world tree */
+.expandableTree
+{
+  font-weight: bolder;
+  padding-left: 0.5em;
+  width: calc(100% - 0.5em);
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  text-shadow: none;
+  font-size: 1.2em;
+  background-color: #333333;
+  border-top: 1px solid #222222;
+  border-bottom: 1px solid #222222;
+  line-height: 2em;
+}
+.expandableTree:hover,
+.expandableTree:active
+{
+  background-color: #373737;
+}
+.notExpandableTree
+{
+  padding-left: 1.6em;
+  width: calc(100% - 1.6em);
+}
+.treeItem
+{
+  font-size: 1em;
+  line-height: 2em;
+  padding: 0;
+  width: 23em;
+  clear: both;
+  margin-left: -1em;
+}
+.treeItemImg
+{
+  height: 2em;
+  width: 3.5em;
+  text-align: center;
+  float: left;
+}
+.treeItemImg img
+{
+  height: 1.2em;
+  vertical-align: middle;
+}
+.treeItemTitle
+{
+  height: 2em;
+  width: 10.5em;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  float: left;
+}
+.unselectedTreeItem
+{
+  background-color: #2a2a2a;
+}
+.selectedTreeItem
+{
+  background-color: #aaaaaa;
+}
+/* property panel */
+.propertyPanelImg
+{
+  text-align: center;
+  clear: both;
+}
+.propertyPanelImg > img
+{
+  height: 5em;
+}
+.propertyPanelHeader
+{
+  text-align: center;
+  clear: both;
+}
+.propertyPanelHeader > h4
+{
+  background-color: #aaaaaa;
+  float: left;
+  margin: 0;
+}
+.propertyPanelHeader > h4:first-child
+{
+  width: 60%;
+}
+.propertyPanelHeader > h4:nth-child(2)
+{
+  width: 40%;
+}
+/* Property general */
+.property div
+{
+  float: left;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  position: relative;
+  text-shadow: none;
+  font-size: 0.9em;
+}
+.property > div:first-child
+{
+  font-weight:bolder;
+  padding-left: 1.7em;
+  width: calc(60% - 1.7em);
+}
+.property > div:nth-child(2)
+{
+  width: 40%;
+}
+.propertyToggle
+{
+  text-align: right;
+  padding-right: 0.3em;
+  width: calc(40% - 0.3em) !important;
+}
+.propertyToggle > div:nth-child(2)
+{
+  float: right;
+  width: 3em;
+  text-align: left;
+  padding-left: 0.2em;
+  font-size: 1em;
+}
+.propertyToggle img
+{
+  vertical-align: middle;
+  height: 0.8em;
+}
+.propertyNumber
+{
+  text-align: right;
+  padding-right: 2em;
+  width: calc(40% - 2em) !important;
+}
+.propertyNumber div:first-child
+{
+  text-align: right;
+  float:right;
+  line-height: inherit;
+}
+.propertyNumberEdit div
+{
+  margin: 0;
+}
+.propertyNumberEdit input
+{
+  margin: 0;
+  padding: 0;
+  border: 0;
+  width: 80%;
+  background-color: #2a2a2a;
+  color: #ffffff;
+  text-align: right;
+  float:right;
+  padding-right: 2em;
+}
+.propertyUnit
+{
+  position: absolute !important;
+  right: 1em;
+}
+.propertyUnit3
+{
+  font-size: 0.75em !important;
+  line-height: inherit !important;
+}
+.propertyUnit4
+{
+  font-size: 0.6em !important;
+  right: 0.3em;
+}
+.expandableProperty
+{
+  font-weight: bolder;
+  padding-left: 0.2em;
+  width: calc(100% - 0.2em);
+  float: left;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  text-shadow: none;
+  font-size: 0.9em;
+}
+.expandableProperty
+{
+  font-size: 1.2em;
+  line-height: 1.9em;
+}
+.expandableProperty:hover,
+.expandableProperty:active
+{
+  background-color: #2e2e2e;
+}
+.expandableProperty div
+{
+  position: relative;
+  float: left;
+  width: calc(60% - 0.2em);
+}
+.expandableProperty > div:nth-child(2)
+{
+  position: relative;
+  float: left;
+  width: 40%;
+}
+.expandableProperty img
+{
+  vertical-align: middle;
+  height: 1em;
+}
+.expandedContent
+{
+  display: none;
+}
+.colorDisplay
+{
+  display: inline-block;
+  height: 1em;
+  width: calc(40% - 2em) !important;
+  border: 1px solid white;
+}
+.propertyColorEdit
+{
+  height: 100%;
+}
+.propertyColorEdit div
+{
+  background-color: #aaaaaa !important;
+  padding: 0;
+  margin: 0;
+  height: 100%;
+  border: none;
+}
+.propertyColorEdit input
+{
+  padding: 0;
+  margin: 0;
+  min-height: 1em;
+  height: 1.2em;
+}
+.colorDisplay
+{
+  margin-top: 0.3em;
+}
+/* Level 1 */
+.propertyLevel1 div
+{
+  font-size: 1.2em;
+  line-height: 1.9em;
+}
+.propertyLevel1 > div:first-child
+{
+  padding-left: 1.3em;
+  width: calc(60% - 1.3em);
+}
+/* Level 2 */
+.propertyLevel2 div
+{
+  font-size: 0.8em;
+}
+.propertyLevel2 > div:first-child
+{
+  padding-left: 2.8em;
+  width: calc(60% - 2.8em);
+}
+.expandableLevel2
+{
+  padding-left: 1.7em;
+  width: calc(100% - 1.7em);
+  font-size: 0.8em;
+}
+.expandableLevel2 img
+{
+  height: 0.8em;
+}
+.propertyLevel2 div,
+.propertyLevel2 input
+{
+  font-size: 1.1em;
+  line-height: 1.8em;
+  height: 1.8em;
+}
+.propertyLevel2 > div:first-child
+{
+  padding-left: 2.3em;
+  width: calc(60% - 2.3em);
+}
+.expandableLevel2
+{
+  font-size: 1.1em;
+  line-height: 1.8em;
+  padding-left: 1.2em;
+  width: calc(100% - 1.2em);
+}
+/* Level 3 */
+.propertyLevel3 div
+{
+  font-size: 0.7em;
+}
+.propertyLevel3 > div:first-child
+{
+  padding-left: 4.5em;
+  width: calc(60% - 4.5em);
+}
+.expandableLevel3
+{
+  padding-left: 3.5em;
+  width: calc(100% - 3.5em);
+  font-size: 0.7em;
+}
+.expandableLevel3 img
+{
+  height: 0.7em;
+}
+.propertyLevel3 div,
+.propertyLevel3 input
+{
+  font-size: 1.0em;
+  line-height: 1.7em;
+  height: 1.7em;
+}
+.propertyLevel3 > div:first-child
+{
+  padding-left: 3.3em;
+  width: calc(60% - 3.3em);
+}
+.expandableLevel3
+{
+  font-size: 1.0em;
+  line-height: 1.7em;
+  padding-left: 2.4em;
+  width: calc(100% - 2.4em);
+}
+/* Level 4 */
+.propertyLevel4 div
+{
+  font-size: 0.65em;
+}
+.propertyLevel4 > div:first-child
+{
+  padding-left: 6.2em;
+  width: calc(60% - 6.2em);
+}
+.expandableLevel4
+{
+  padding-left: 5.2em;
+  width: calc(100% - 5.2em);
+  font-size: 0.65em;
+}
+.expandableLevel4 img
+{
+  height: 0.65em;
+}
+.propertyLevel4 div,
+.propertyLevel4 input
+{
+  font-size: 0.9em;
+  line-height: 1.6em;
+  height: 1.6em;
+}
+.propertyLevel4 > div:first-child
+{
+  padding-left: 4.3em;
+  width: calc(60% - 4.3em);
+}
+.expandableLevel4
+{
+  font-size: 1.0em;
+  line-height: 1.7em;
+  padding-left: 3.0em;
+  width: calc(100% - 3.0em);
+}
+/* Level 5 */
+.propertyLevel5 div
+{
+  font-size: 0.6em;
+}
+.propertyLevel5 > div:first-child
+{
+  padding-left: 7.9em;
+  width: calc(60% - 7.9em);
+}
+.propertyLevel5 div,
+.propertyLevel5 input
+{
+  font-size: 0.8em;
+  line-height: 1.5em;
+  height: 1.5em;
+}
+.propertyLevel5 > div:first-child
+{
+  padding-left: 5.6em;
+  width: calc(60% - 5.6em);
+}
+#real-time-update-rate
+{
+  font-size: 1.1em;
+  padding-left: 1.4em;
+  width: calc(60% - 1.4em);
+}
+/* Background colors */
+.redBg div
+{
+  background-color: #772a2a !important;
+}
+.greenBg div
+{
+  background-color: #2a772a !important;
+}
+.blueBg div
+{
+  background-color: #2a2a77 !important;
+}
+.lightBg div,
+.lightBg input
+{
+  background-color: #414141 !important;
+}
+/* model popup */
+#model-popup
+{
+  width: 12em;
+  height: 0em;
+  border: none;
+}
+/* narrow screens*/
+@media (max-width: 35em)
+{
+  /* panel */
+  .leftPanels
+  {
+    width: 10.5em;
+  }
+  .leftPanels ul
+  {
+    width: 8.5em;
+  }
+  /* main menu */
+  #mainMenu li
+  {
+    width: 10.5em;
+  }
+  /* insert menu */
+  .insertMenuItem
+  {
+    width: calc(100% - 3.2em);
+    height: 4.3em;
+    margin-left: 0.5em;
+  }
+  .insertMenuItem img
+  {
+    height: 3.3em;
+    max-width: 4em;
+  }
+  .insertMenuItem h1
+  {
+    font-size: 0.7em;
+  }
+  .insertMenuItemLight img
+  {
+    height: 2.7em;
+    margin-top: 0.4em;
+  }
+  .insertMenuCategoryItem img
+  {
+    margin-left: 0;
+    padding-top: 0;
+  }
+  .insertMenuCategoryItem > img:nth-child(2),
+  .insertMenuCategoryItem > img:nth-child(3),
+  .insertMenuCategoryItemLight > img:nth-child(4)
+  {
+    display: none;
+  }
+  .insertMenuCategoryItemLight > img:nth-child(5)
+  {
+    display: initial;
+  }
+  /* world tree */
+  .treeItem
+  {
+    width: 10.5em;
+  }
+  .treeItemTitle
+  {
+    width: 6.5em;
+  }
+}
+/* short screens*/
+@media (max-height: 35em)
+{
+  /* tabs */
+  .tab
+  {
+    height: 2em;
+  }
+  .tab img
+  {
+    left: -1.6em;
+    top: 0.4em;
+    height: 1.3em;
+  }
+  .tab:nth-of-type(1)
+  {
+    top: 1.8em;
+  }
+  .tab:nth-of-type(2)
+  {
+    top: 5.8em;
+  }
+  .tab:nth-of-type(3)
+  {
+    top: 9.8em;
+  }
+}
+
diff --git a/gz3d/client/style/images/arrow.png b/gz3d/client/style/images/arrow.png
new file mode 100644
index 0000000000000000000000000000000000000000..a5e465116dfbe357c8341c58d8b839b493524904
Binary files /dev/null and b/gz3d/client/style/images/arrow.png differ
diff --git a/gz3d/client/style/images/back.png b/gz3d/client/style/images/back.png
new file mode 100644
index 0000000000000000000000000000000000000000..d7492c97ed53a519083ed38f213067751b30c6de
Binary files /dev/null and b/gz3d/client/style/images/back.png differ
diff --git a/gz3d/client/style/images/bars.png b/gz3d/client/style/images/bars.png
new file mode 100644
index 0000000000000000000000000000000000000000..bda63467d92eb0377f1c355b5102844ff65d8300
Binary files /dev/null and b/gz3d/client/style/images/bars.png differ
diff --git a/gz3d/client/style/images/box.png b/gz3d/client/style/images/box.png
new file mode 100644
index 0000000000000000000000000000000000000000..e1a70a367c7fb7eb1c7b38f5c3cbe3261f18284a
Binary files /dev/null and b/gz3d/client/style/images/box.png differ
diff --git a/gz3d/client/style/images/clock.png b/gz3d/client/style/images/clock.png
new file mode 100644
index 0000000000000000000000000000000000000000..cb0fcb612e268e60ed069aa1ff9bfa958bd7abef
Binary files /dev/null and b/gz3d/client/style/images/clock.png differ
diff --git a/gz3d/client/style/images/cylinder.png b/gz3d/client/style/images/cylinder.png
new file mode 100644
index 0000000000000000000000000000000000000000..aaa23af457fbde862f00acea51acba476d1d9a03
Binary files /dev/null and b/gz3d/client/style/images/cylinder.png differ
diff --git a/gz3d/client/style/images/directionallight.png b/gz3d/client/style/images/directionallight.png
new file mode 100644
index 0000000000000000000000000000000000000000..64acc33e66bcb89a7402b4d4e7631e59c39bd7cc
Binary files /dev/null and b/gz3d/client/style/images/directionallight.png differ
diff --git a/gz3d/client/style/images/false.png b/gz3d/client/style/images/false.png
new file mode 100644
index 0000000000000000000000000000000000000000..ecfaff7ed399736eb6d38b86a989eca977c3bdf7
Binary files /dev/null and b/gz3d/client/style/images/false.png differ
diff --git a/gz3d/client/style/images/icon_background.png b/gz3d/client/style/images/icon_background.png
new file mode 100644
index 0000000000000000000000000000000000000000..f1d375785159706e46fe364dcbb4a890f346504c
Binary files /dev/null and b/gz3d/client/style/images/icon_background.png differ
diff --git a/gz3d/client/style/images/insert.png b/gz3d/client/style/images/insert.png
new file mode 100644
index 0000000000000000000000000000000000000000..5ef24c13c3584a9e3e836afde2d2e0015cff9661
Binary files /dev/null and b/gz3d/client/style/images/insert.png differ
diff --git a/gz3d/client/style/images/joints.png b/gz3d/client/style/images/joints.png
new file mode 100644
index 0000000000000000000000000000000000000000..1827f827a7ff41e539d11931acb8493cf55750cf
Binary files /dev/null and b/gz3d/client/style/images/joints.png differ
diff --git a/gz3d/client/style/images/light_thumb.png b/gz3d/client/style/images/light_thumb.png
new file mode 100644
index 0000000000000000000000000000000000000000..e31d15cc95daf6da55f4debe5db5a3a7226174d7
Binary files /dev/null and b/gz3d/client/style/images/light_thumb.png differ
diff --git a/gz3d/client/style/images/pause.png b/gz3d/client/style/images/pause.png
new file mode 100644
index 0000000000000000000000000000000000000000..8497bd42daeed0d63216281755fb16a596c39420
Binary files /dev/null and b/gz3d/client/style/images/pause.png differ
diff --git a/gz3d/client/style/images/play.png b/gz3d/client/style/images/play.png
new file mode 100644
index 0000000000000000000000000000000000000000..264fa62619c919da31e5ec5495c36d02df79c4f0
Binary files /dev/null and b/gz3d/client/style/images/play.png differ
diff --git a/gz3d/client/style/images/pointlight.png b/gz3d/client/style/images/pointlight.png
new file mode 100644
index 0000000000000000000000000000000000000000..34703104e4cf30189fc70d3391b9be72f29f22e7
Binary files /dev/null and b/gz3d/client/style/images/pointlight.png differ
diff --git a/gz3d/client/style/images/rotate.png b/gz3d/client/style/images/rotate.png
new file mode 100644
index 0000000000000000000000000000000000000000..a724d948baa584050f463a7ba63f4ade3ab7a8d2
Binary files /dev/null and b/gz3d/client/style/images/rotate.png differ
diff --git a/gz3d/client/style/images/sphere.png b/gz3d/client/style/images/sphere.png
new file mode 100644
index 0000000000000000000000000000000000000000..ca9dd1b92d8d0c777540d9b7b857d2587adf8b49
Binary files /dev/null and b/gz3d/client/style/images/sphere.png differ
diff --git a/gz3d/client/style/images/spotlight.png b/gz3d/client/style/images/spotlight.png
new file mode 100644
index 0000000000000000000000000000000000000000..65e027bec53159f45d2deca85c87410aa2b4f893
Binary files /dev/null and b/gz3d/client/style/images/spotlight.png differ
diff --git a/gz3d/client/style/images/translate.png b/gz3d/client/style/images/translate.png
new file mode 100644
index 0000000000000000000000000000000000000000..8460bdd18d9b146d1115b89e27e98a9b5dd04c1b
Binary files /dev/null and b/gz3d/client/style/images/translate.png differ
diff --git a/gz3d/client/style/images/transparent.png b/gz3d/client/style/images/transparent.png
new file mode 100644
index 0000000000000000000000000000000000000000..69dc3361a610d94e85d699aab85bbbac4e32d17f
Binary files /dev/null and b/gz3d/client/style/images/transparent.png differ
diff --git a/gz3d/client/style/images/trash.png b/gz3d/client/style/images/trash.png
new file mode 100644
index 0000000000000000000000000000000000000000..5e4fcaa7ad3cf44e745d2f5eaf2e418ec091657f
Binary files /dev/null and b/gz3d/client/style/images/trash.png differ
diff --git a/gz3d/client/style/images/tree.png b/gz3d/client/style/images/tree.png
new file mode 100644
index 0000000000000000000000000000000000000000..8203684a67a36f225f707f29570e642e056a10b2
Binary files /dev/null and b/gz3d/client/style/images/tree.png differ
diff --git a/gz3d/client/style/images/true.png b/gz3d/client/style/images/true.png
new file mode 100644
index 0000000000000000000000000000000000000000..fd9a1c20cd6f4ce7aacda201833d6d63b2a68129
Binary files /dev/null and b/gz3d/client/style/images/true.png differ
diff --git a/gz3d/client/style/images/wireframe.png b/gz3d/client/style/images/wireframe.png
new file mode 100644
index 0000000000000000000000000000000000000000..ce3827daf6c3051a9f80ca6e64c9257955ac3155
Binary files /dev/null and b/gz3d/client/style/images/wireframe.png differ
diff --git a/gz3d/client/style/jquery-mobile/jquery.mobile-1.4.0.css b/gz3d/client/style/jquery-mobile/jquery.mobile-1.4.0.css
new file mode 100644
index 0000000000000000000000000000000000000000..f4307df1f730a25eaf13a78686bf89ab9ab9c02a
--- /dev/null
+++ b/gz3d/client/style/jquery-mobile/jquery.mobile-1.4.0.css
@@ -0,0 +1,4749 @@
+/*!
+* jQuery Mobile 1.4.0
+* Git HEAD hash: f09aae0e035d6805e461a7be246d04a0dbc98f69 <> Date: Thu Dec 19 2013 17:34:22 UTC
+* http://jquerymobile.com
+*
+* Copyright 2010, 2013 jQuery Foundation, Inc. and other contributors
+* Released under the MIT license.
+* http://jquery.org/license
+*
+*/
+
+
+/* SVG icons */
+.ui-icon-action:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2213px%22%20viewBox%3D%220%200%2014%2013%22%20style%3D%22enable-background%3Anew%200%200%2014%2013%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M9%2C5v3l5-4L9%2C0v3c0%2C0-5%2C0-5%2C7C6%2C5%2C9%2C5%2C9%2C5z%20M11%2C11H2V5h1l2-2H0v10h13V7l-2%2C2V11z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-arrow-d-l:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%2214%2C3%2011%2C0%203.5%2C7.5%200%2C4%200%2C14%2010%2C14%206.5%2C10.5%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-arrow-d-r:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%2210.5%2C7.5%203%2C0%200%2C3%207.5%2C10.5%204%2C14%2014%2C14%2014%2C4%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-arrow-d:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%229%2C7%209%2C0%205%2C0%205%2C7%200%2C7%207%2C14%2014%2C7%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-arrow-l:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%227%2C5%207%2C0%200%2C7%207%2C14%207%2C9%2014%2C9%2014%2C5%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-arrow-r:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%2214%2C7%207%2C0%207%2C5%200%2C5%200%2C9%207%2C9%207%2C14%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-arrow-u-l:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%2214%2C11%206.5%2C3.5%2010%2C0%200%2C0%200%2C10%203.5%2C6.5%2011%2C14%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-arrow-u-r:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%2214%2C0%204%2C0%207.5%2C3.5%200%2C11%203%2C14%2010.5%2C6.5%2014%2C10%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-arrow-u:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%227%2C0%200%2C7%205%2C7%205%2C14%209%2C14%209%2C7%2014%2C7%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-audio:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214.018px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014.018%2014%22%20style%3D%22enable-background%3Anew%200%200%2014.018%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M1%2C4C0.447%2C4%2C0%2C4.447%2C0%2C5v4c0%2C0.553%2C0.447%2C1%2C1%2C1h1l4%2C4V0L2%2C4H1z%20M10.346%2C7c0-1.699-1.042-3.154-2.546-3.867L6.982%2C4.68%20C7.885%2C5.107%2C8.51%2C5.98%2C8.51%2C7S7.885%2C8.893%2C6.982%2C9.32L7.8%2C10.867C9.304%2C10.154%2C10.346%2C8.699%2C10.346%2C7z%20M9.447%2C0.017L8.618%2C1.586%20C10.723%2C2.584%2C12.182%2C4.621%2C12.182%2C7s-1.459%2C4.416-3.563%2C5.414l0.829%2C1.569c2.707-1.283%2C4.57-3.925%2C4.57-6.983%20S12.154%2C1.3%2C9.447%2C0.017z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-calendar:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M0%2C8h2V6H0V8z%20M3%2C8h2V6H3V8z%20M6%2C8h2V6H6V8z%20M9%2C8h2V6H9V8z%20M12%2C8h2V6h-2V8z%20M0%2C11h2V9H0V11z%20M3%2C11h2V9H3V11z%20M6%2C11h2V9H6V11z%20%20M9%2C11h2V9H9V11z%20M12%2C11h2V9h-2V11z%20M0%2C14h2v-2H0V14z%20M3%2C14h2v-2H3V14z%20M6%2C14h2v-2H6V14z%20M9%2C14h2v-2H9V14z%20M12%2C1%20c0-0.553-0.447-1-1-1s-1%2C0.447-1%2C1H4c0-0.553-0.447-1-1-1S2%2C0.447%2C2%2C1H0v4h14V1H12z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-camera:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2211px%22%20viewBox%3D%220%200%2014%2011%22%20style%3D%22enable-background%3Anew%200%200%2014%2011%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M12%2C1H9.908C9.702%2C0.419%2C9.152%2C0%2C8.5%2C0h-3C4.848%2C0%2C4.298%2C0.419%2C4.092%2C1H2C0.896%2C1%2C0%2C1.896%2C0%2C3v6c0%2C1.104%2C0.896%2C2%2C2%2C2h10%20c1.104%2C0%2C2-0.896%2C2-2V3C14%2C1.896%2C13.104%2C1%2C12%2C1z%20M7%2C9C5.343%2C9%2C4%2C7.657%2C4%2C6s1.343-3%2C3-3s3%2C1.343%2C3%2C3S8.657%2C9%2C7%2C9z%20M7%2C4%20C5.896%2C4%2C5%2C4.896%2C5%2C6s0.896%2C2%2C2%2C2s2-0.896%2C2-2S8.104%2C4%2C7%2C4z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-carat-d:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2212px%22%20height%3D%228px%22%20viewBox%3D%220%200%2012%208%22%20style%3D%22enable-background%3Anew%200%200%2012%208%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%2210%2C0%206%2C4%202%2C0%200%2C2%206%2C8%2012%2C2%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-carat-l:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%228px%22%20height%3D%2212px%22%20viewBox%3D%220%200%208%2012%22%20style%3D%22enable-background%3Anew%200%200%208%2012%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%228%2C2%206%2C0%200%2C6%206%2C12%208%2C10%204%2C6%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-carat-r:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%228px%22%20height%3D%2212px%22%20viewBox%3D%220%200%208%2012%22%20style%3D%22enable-background%3Anew%200%200%208%2012%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%222%2C0%200%2C2%204%2C6%200%2C10%202%2C12%208%2C6%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-carat-u:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2212px%22%20height%3D%228px%22%20viewBox%3D%220%200%2012%208%22%20style%3D%22enable-background%3Anew%200%200%2012%208%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%226%2C0%200%2C6%202%2C8%206%2C4%2010%2C8%2012%2C6%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-check:after,
+/* Used ui-checkbox-on twice to increase specificity. If active state has background-image for gradient this rule overrides. */
+html .ui-btn.ui-checkbox-on.ui-checkbox-on:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2212px%22%20viewBox%3D%220%200%2014%2012%22%20style%3D%22enable-background%3Anew%200%200%2014%2012%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%2214%2C3%2011%2C0%205.003%2C5.997%203%2C4%200%2C7%204.966%2C12%204.983%2C11.983%205%2C12%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-clock:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M7%2C0C3.134%2C0%2C0%2C3.134%2C0%2C7s3.134%2C7%2C7%2C7s7-3.134%2C7-7S10.866%2C0%2C7%2C0z%20M7%2C12c-2.762%2C0-5-2.238-5-5s2.238-5%2C5-5s5%2C2.238%2C5%2C5%20S9.762%2C12%2C7%2C12z%20M9%2C6H8V4c0-0.553-0.447-1-1-1S6%2C3.447%2C6%2C4v3c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1S9.553%2C6%2C9%2C6z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-cloud:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%229px%22%20viewBox%3D%220%200%2014%209%22%20style%3D%22enable-background%3Anew%200%200%2014%209%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M14%2C7c0-0.793-0.465-1.472-1.134-1.795C12.949%2C4.984%2C13%2C4.749%2C13%2C4.5c0-1.104-0.896-2-2-2c-0.158%2C0-0.31%2C0.023-0.457%2C0.058%20C9.816%2C1.049%2C8.286%2C0%2C6.5%2C0C4.17%2C0%2C2.276%2C1.777%2C2.046%2C4.046C0.883%2C4.26%2C0%2C5.274%2C0%2C6.5C0%2C7.881%2C1.119%2C9%2C2.5%2C9h10V8.93%20C13.361%2C8.706%2C14%2C7.931%2C14%2C7z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-grid:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M3%2C0H1C0.447%2C0%2C0%2C0.447%2C0%2C1v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1V1C4%2C0.447%2C3.553%2C0%2C3%2C0z%20M8%2C0H6%20C5.447%2C0%2C5%2C0.447%2C5%2C1v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1V1C9%2C0.447%2C8.553%2C0%2C8%2C0z%20M13%2C0h-2c-0.553%2C0-1%2C0.447-1%2C1v2%20c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1V1C14%2C0.447%2C13.553%2C0%2C13%2C0z%20M3%2C5H1C0.447%2C5%2C0%2C5.447%2C0%2C6v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2%20c0.553%2C0%2C1-0.447%2C1-1V6C4%2C5.447%2C3.553%2C5%2C3%2C5z%20M8%2C5H6C5.447%2C5%2C5%2C5.447%2C5%2C6v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1V6%20C9%2C5.447%2C8.553%2C5%2C8%2C5z%20M13%2C5h-2c-0.553%2C0-1%2C0.447-1%2C1v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1V6C14%2C5.447%2C13.553%2C5%2C13%2C5z%20M3%2C10%20H1c-0.553%2C0-1%2C0.447-1%2C1v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1v-2C4%2C10.447%2C3.553%2C10%2C3%2C10z%20M8%2C10H6c-0.553%2C0-1%2C0.447-1%2C1v2%20c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1v-2C9%2C10.447%2C8.553%2C10%2C8%2C10z%20M13%2C10h-2c-0.553%2C0-1%2C0.447-1%2C1v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2%20c0.553%2C0%2C1-0.447%2C1-1v-2C14%2C10.447%2C13.553%2C10%2C13%2C10z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-mail:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2210px%22%20viewBox%3D%220%200%2014%2010%22%20style%3D%22enable-background%3Anew%200%200%2014%2010%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M0%2C1.75V10h14V1.75L7%2C7L0%2C1.75z%20M14%2C0H0l7%2C5L14%2C0z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-eye:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2210px%22%20viewBox%3D%220%200%2014%2010%22%20style%3D%22enable-background%3Anew%200%200%2014%2010%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M7%2C0C3%2C0%2C0%2C5%2C0%2C5s3%2C5%2C7%2C5s7-5%2C7-5S11%2C0%2C7%2C0z%20M7%2C8C5.343%2C8%2C4%2C6.657%2C4%2C5s1.343-3%2C3-3s3%2C1.343%2C3%2C3S8.657%2C8%2C7%2C8z%20M7%2C4%20C6.448%2C4%2C6%2C4.447%2C6%2C5s0.448%2C1%2C1%2C1s1-0.447%2C1-1S7.552%2C4%2C7%2C4z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-gear:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M13.621%2C5.904l-1.036-0.259c-0.168-0.042-0.303-0.168-0.355-0.332c-0.092-0.284-0.205-0.559-0.339-0.82%20c-0.079-0.153-0.073-0.337%2C0.017-0.486l0.549-0.915c0.118-0.196%2C0.088-0.448-0.075-0.61l-0.862-0.863%20c-0.162-0.163-0.414-0.193-0.611-0.075l-0.916%2C0.55C9.844%2C2.182%2C9.659%2C2.188%2C9.506%2C2.109C9.244%2C1.975%2C8.97%2C1.861%2C8.686%2C1.77%20c-0.165-0.052-0.29-0.187-0.332-0.354L8.095%2C0.379C8.039%2C0.156%2C7.839%2C0%2C7.609%2C0H6.391c-0.229%2C0-0.43%2C0.156-0.485%2C0.379L5.646%2C1.415%20C5.604%2C1.582%2C5.479%2C1.718%2C5.313%2C1.77c-0.284%2C0.092-0.559%2C0.206-0.82%2C0.34C4.339%2C2.188%2C4.155%2C2.182%2C4.007%2C2.093L3.092%2C1.544%20c-0.196-0.118-0.448-0.087-0.61%2C0.075L1.619%2C2.481C1.457%2C2.644%2C1.426%2C2.896%2C1.544%2C3.093l0.549%2C0.914%20c0.089%2C0.148%2C0.095%2C0.332%2C0.017%2C0.486C1.975%2C4.755%2C1.861%2C5.029%2C1.77%2C5.314c-0.053%2C0.164-0.188%2C0.29-0.354%2C0.332L0.379%2C5.905%20C0.156%2C5.961%2C0%2C6.161%2C0%2C6.391v1.219c0%2C0.229%2C0.156%2C0.43%2C0.379%2C0.485l1.036%2C0.26C1.582%2C8.396%2C1.717%2C8.521%2C1.77%2C8.687%20c0.092%2C0.284%2C0.205%2C0.559%2C0.34%2C0.82C2.188%2C9.66%2C2.182%2C9.844%2C2.093%2C9.993l-0.549%2C0.915c-0.118%2C0.195-0.087%2C0.448%2C0.075%2C0.61%20l0.862%2C0.862c0.162%2C0.163%2C0.414%2C0.193%2C0.61%2C0.075l0.915-0.549c0.148-0.089%2C0.332-0.095%2C0.486-0.017%20c0.262%2C0.135%2C0.536%2C0.248%2C0.82%2C0.34c0.165%2C0.053%2C0.291%2C0.187%2C0.332%2C0.354l0.259%2C1.036C5.96%2C13.844%2C6.16%2C14%2C6.39%2C14h1.22%20c0.229%2C0%2C0.43-0.156%2C0.485-0.379l0.259-1.036c0.042-0.167%2C0.168-0.302%2C0.333-0.354c0.284-0.092%2C0.559-0.205%2C0.82-0.34%20c0.154-0.078%2C0.338-0.072%2C0.486%2C0.017l0.914%2C0.549c0.197%2C0.118%2C0.449%2C0.088%2C0.611-0.074l0.862-0.863%20c0.163-0.162%2C0.193-0.415%2C0.075-0.611l-0.549-0.915c-0.089-0.148-0.096-0.332-0.017-0.485c0.134-0.263%2C0.248-0.536%2C0.339-0.82%20c0.053-0.165%2C0.188-0.291%2C0.355-0.333l1.036-0.259C13.844%2C8.039%2C14%2C7.839%2C14%2C7.609V6.39C14%2C6.16%2C13.844%2C5.96%2C13.621%2C5.904z%20M7%2C10%20c-1.657%2C0-3-1.343-3-3s1.343-3%2C3-3s3%2C1.343%2C3%2C3S8.657%2C10%2C7%2C10z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-heart:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2213.744px%22%20viewBox%3D%220%200%2014%2013.744%22%20style%3D%22enable-background%3Anew%200%200%2014%2013.744%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M7%2C1.744c-2-3-7-2-7%2C2c0%2C3%2C4%2C7%2C4%2C7s2.417%2C2.479%2C3%2C3c0.583-0.521%2C3-3%2C3-3s4-4%2C4-7C14-0.256%2C9-1.256%2C7%2C1.744z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-home:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%227%2C0%200%2C7%202%2C7%202%2C14%205%2C14%205%2C9%209%2C9%209%2C14%2012%2C14%2012%2C7%2014%2C7%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-info:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M7%2C0C3.134%2C0%2C0%2C3.134%2C0%2C7s3.134%2C7%2C7%2C7s7-3.134%2C7-7S10.866%2C0%2C7%2C0z%20M7%2C2c0.552%2C0%2C1%2C0.447%2C1%2C1S7.552%2C4%2C7%2C4S6%2C3.553%2C6%2C3%20S6.448%2C2%2C7%2C2z%20M9%2C11H5v-1h1V6H5V5h3v5h1V11z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-bullets:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2210px%22%20viewBox%3D%220%200%2014%2010%22%20style%3D%22enable-background%3Anew%200%200%2014%2010%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M5%2C2h8c0.553%2C0%2C1-0.447%2C1-1s-0.447-1-1-1H5C4.447%2C0%2C4%2C0.447%2C4%2C1S4.447%2C2%2C5%2C2z%20M13%2C4H5C4.447%2C4%2C4%2C4.447%2C4%2C5s0.447%2C1%2C1%2C1h8%20c0.553%2C0%2C1-0.447%2C1-1S13.553%2C4%2C13%2C4z%20M13%2C8H5C4.447%2C8%2C4%2C8.447%2C4%2C9s0.447%2C1%2C1%2C1h8c0.553%2C0%2C1-0.447%2C1-1S13.553%2C8%2C13%2C8z%20M1%2C0%20C0.447%2C0%2C0%2C0.447%2C0%2C1s0.447%2C1%2C1%2C1s1-0.447%2C1-1S1.553%2C0%2C1%2C0z%20M1%2C4C0.447%2C4%2C0%2C4.447%2C0%2C5s0.447%2C1%2C1%2C1s1-0.447%2C1-1S1.553%2C4%2C1%2C4z%20M1%2C8%20C0.447%2C8%2C0%2C8.447%2C0%2C9s0.447%2C1%2C1%2C1s1-0.447%2C1-1S1.553%2C8%2C1%2C8z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-bars:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2210px%22%20viewBox%3D%220%200%2014%2010%22%20style%3D%22enable-background%3Anew%200%200%2014%2010%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M1%2C2h12c0.553%2C0%2C1-0.447%2C1-1s-0.447-1-1-1H1C0.447%2C0%2C0%2C0.447%2C0%2C1S0.447%2C2%2C1%2C2z%20M13%2C4H1C0.447%2C4%2C0%2C4.447%2C0%2C5s0.447%2C1%2C1%2C1h12%20c0.553%2C0%2C1-0.447%2C1-1S13.553%2C4%2C13%2C4z%20M13%2C8H1C0.447%2C8%2C0%2C8.447%2C0%2C9s0.447%2C1%2C1%2C1h12c0.553%2C0%2C1-0.447%2C1-1S13.553%2C8%2C13%2C8z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-navigation:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%2214%2C0%200%2C6%208%2C6%208%2C14%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-lock:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2212px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2012%2014%22%20style%3D%22enable-background%3Anew%200%200%2012%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M11%2C6V5c0-2.762-2.239-5-5-5S1%2C2.238%2C1%2C5v1H0v8h12V6H11z%20M6.5%2C9.847V12h-1V9.847C5.207%2C9.673%2C5%2C9.366%2C5%2C9%20c0-0.553%2C0.448-1%2C1-1s1%2C0.447%2C1%2C1C7%2C9.366%2C6.793%2C9.673%2C6.5%2C9.847z%20M9%2C6H3V5c0-1.657%2C1.343-3%2C3-3s3%2C1.343%2C3%2C3V6z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-search:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2213.701px%22%20height%3D%2213.721px%22%20viewBox%3D%220%200%2013.701%2013.721%22%20style%3D%22enable-background%3Anew%200%200%2013.701%2013.721%3B%22%20%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M10.021%2C8.626C10.638%2C7.738%2C11%2C6.662%2C11%2C5.5C11%2C2.463%2C8.537%2C0%2C5.5%2C0S0%2C2.463%2C0%2C5.5S2.463%2C11%2C5.5%2C11%20c1.152%2C0%2C2.221-0.356%2C3.105-0.962l3.682%2C3.683l1.414-1.414L10.021%2C8.626z%20M5.5%2C9C3.567%2C9%2C2%2C7.433%2C2%2C5.5S3.567%2C2%2C5.5%2C2S9%2C3.567%2C9%2C5.5%20S7.433%2C9%2C5.5%2C9z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-location:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%228px%22%20height%3D%2214px%22%20viewBox%3D%220%200%208%2014%22%20style%3D%22enable-background%3Anew%200%200%208%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M4%2C0C1.791%2C0%2C0%2C1.791%2C0%2C4c0%2C2%2C4%2C10%2C4%2C10S8%2C6%2C8%2C4C8%2C1.791%2C6.209%2C0%2C4%2C0z%20M4%2C6C2.896%2C6%2C2%2C5.104%2C2%2C4s0.896-2%2C2-2s2%2C0.896%2C2%2C2%20S5.104%2C6%2C4%2C6z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-minus:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%224px%22%20viewBox%3D%220%200%2014%204%22%20style%3D%22enable-background%3Anew%200%200%2014%204%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Crect%20fill%3D%22%23FFF%22%20width%3D%2214%22%20height%3D%224%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-forbidden:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M12.601%2C11.187C13.476%2C10.018%2C14%2C8.572%2C14%2C7c0-3.866-3.134-7-7-7C5.428%2C0%2C3.982%2C0.524%2C2.813%2C1.399L2.757%2C1.343L2.053%2C2.048%20L2.048%2C2.053L1.343%2C2.758l0.056%2C0.056C0.524%2C3.982%2C0%2C5.428%2C0%2C7c0%2C3.866%2C3.134%2C7%2C7%2C7c1.572%2C0%2C3.018-0.524%2C4.187-1.399l0.056%2C0.057%20l0.705-0.705l0.005-0.005l0.705-0.705L12.601%2C11.187z%20M7%2C2c2.761%2C0%2C5%2C2.238%2C5%2C5c0%2C1.019-0.308%2C1.964-0.832%2C2.754L4.246%2C2.832%20C5.036%2C2.308%2C5.981%2C2%2C7%2C2z%20M7%2C12c-2.761%2C0-5-2.238-5-5c0-1.019%2C0.308-1.964%2C0.832-2.754l6.922%2C6.922C8.964%2C11.692%2C8.019%2C12%2C7%2C12z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-edit:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M1%2C10l-1%2C4l4-1l7-7L8%2C3L1%2C10z%20M11%2C0L9%2C2l3%2C3l2-2L11%2C0z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-user:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M8.851%2C10.101c-0.18-0.399-0.2-0.763-0.153-1.104C9.383%2C8.49%2C9.738%2C7.621%2C9.891%2C6.465C10.493%2C6.355%2C10.5%2C5.967%2C10.5%2C5.5%20c0-0.437-0.008-0.804-0.502-0.94C9.999%2C4.539%2C10%2C4.521%2C10%2C4.5c0-2.103-1-4-2-4C8%2C0.5%2C7.5%2C0%2C6.5%2C0C5%2C0%2C4%2C1.877%2C4%2C4.5%20c0%2C0.021%2C0.001%2C0.039%2C0.002%2C0.06C3.508%2C4.696%2C3.5%2C5.063%2C3.5%2C5.5c0%2C0.467%2C0.007%2C0.855%2C0.609%2C0.965%20C4.262%2C7.621%2C4.617%2C8.49%2C5.303%2C8.997c0.047%2C0.341%2C0.026%2C0.704-0.153%2C1.104C1.503%2C10.503%2C0%2C12%2C0%2C12v2h14v-2%20C14%2C12%2C12.497%2C10.503%2C8.851%2C10.101z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-phone:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2213.979px%22%20height%3D%2214.016px%22%20viewBox%3D%220%200%2013.979%2014.016%22%20style%3D%22enable-background%3Anew%200%200%2013.979%2014.016%3B%22%20%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M6.939%2C9.189C6.165%2C8.557%2C5.271%2C7.705%2C4.497%2C6.744C3.953%2C6.071%2C3.473%2C5.363%2C3.969%2C4.866l-3.482-3.48%20C-0.021%2C2.02-1.146%2C5.04%2C3.675%2C9.984c5.08%2C5.211%2C8.356%2C4.096%2C8.92%2C3.51l-3.396-3.4C8.725%2C10.568%2C8.113%2C10.146%2C6.939%2C9.189z%20%20M13.82%2C11.519v-0.004c0%2C0-2.649-2.646-2.65-2.648c-0.21-0.21-0.546-0.205-0.754%2C0.002L9.455%2C9.831l3.404%2C3.408%20c0%2C0%2C0.962-0.96%2C0.961-0.961l0.002-0.001C14.043%2C12.056%2C14.021%2C11.721%2C13.82%2C11.519z%20M5.192%2C3.644V3.642%20c0.221-0.222%2C0.2-0.557%2C0-0.758V2.881c0%2C0-2.726-2.724-2.727-2.725C2.255-0.055%2C1.92-0.05%2C1.712%2C0.157L0.751%2C1.121l3.48%2C3.483%20C4.231%2C4.604%2C5.192%2C3.645%2C5.192%2C3.644z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-plus:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%2214%2C5%209%2C5%209%2C0%205%2C0%205%2C5%200%2C5%200%2C9%205%2C9%205%2C14%209%2C14%209%2C9%2014%2C9%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-power:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2212px%22%20height%3D%2213.896px%22%20viewBox%3D%220%200%2012%2013.896%22%20style%3D%22enable-background%3Anew%200%200%2012%2013.896%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M10.243%2C3.356c-0.392-0.401-1.024-0.401-1.415%2C0c-0.39%2C0.402-0.39%2C1.054%2C0%2C1.455C9.584%2C5.59%2C10%2C6.623%2C10%2C7.722%20c0%2C1.1-0.416%2C2.133-1.172%2C2.911c-1.511%2C1.556-4.145%2C1.556-5.656%2C0C2.416%2C9.854%2C2%2C8.821%2C2%2C7.722c0-1.099%2C0.416-2.132%2C1.172-2.91%20c0.39-0.401%2C0.39-1.053%2C0-1.455c-0.391-0.401-1.024-0.401-1.415%2C0C0.624%2C4.522%2C0%2C6.073%2C0%2C7.722c0%2C1.649%2C0.624%2C3.2%2C1.757%2C4.366%20C2.891%2C13.254%2C4.397%2C13.896%2C6%2C13.896s3.109-0.643%2C4.243-1.809C11.376%2C10.922%2C12%2C9.371%2C12%2C7.722C12%2C6.073%2C11.376%2C4.522%2C10.243%2C3.356z%20%20M6%2C8c0.553%2C0%2C1-0.447%2C1-1V1c0-0.553-0.447-1-1-1S5%2C0.447%2C5%2C1v6C5%2C7.553%2C5.447%2C8%2C6%2C8z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-recycle:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2212px%22%20viewBox%3D%220%200%2014%2012%22%20style%3D%22enable-background%3Anew%200%200%2014%2012%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M3%2C6h1L2%2C3L0%2C6h1c0%2C3.313%2C2.687%2C6%2C6%2C6c0.702%2C0%2C1.374-0.127%2C2-0.349V9.445C8.41%2C9.789%2C7.732%2C10%2C7%2C10C4.791%2C10%2C3%2C8.209%2C3%2C6z%20%20M13%2C6c0-3.313-2.687-6-6-6C6.298%2C0%2C5.626%2C0.127%2C5%2C0.349v2.206C5.59%2C2.211%2C6.268%2C2%2C7%2C2c2.209%2C0%2C4%2C1.791%2C4%2C4h-1l2%2C3l2-3H13z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-forward:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2212px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2012%2014%22%20style%3D%22enable-background%3Anew%200%200%2012%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M12%2C4L8%2C0v3C5%2C3%2C0%2C4%2C0%2C8c0%2C5%2C7%2C6%2C7%2C6v-2c0%2C0-5-1-5-4s6-3%2C6-3v3L12%2C4z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-refresh:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214.001px%22%20height%3D%2214.002px%22%20viewBox%3D%220%200%2014.001%2014.002%22%20style%3D%22enable-background%3Anew%200%200%2014.001%2014.002%3B%22%20%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M14.001%2C6.001v-6l-2.06%2C2.06c-0.423-0.424-0.897-0.809-1.44-1.122C7.153-0.994%2C2.872%2C0.153%2C0.939%2C3.501%20c-1.933%2C3.348-0.786%2C7.629%2C2.562%2C9.562c3.348%2C1.933%2C7.629%2C0.785%2C9.562-2.562l-1.732-1c-1.381%2C2.392-4.438%2C3.211-6.83%2C1.83%20s-3.211-4.438-1.83-6.83s4.438-3.211%2C6.83-1.83c0.389%2C0.225%2C0.718%2C0.506%2C1.02%2C0.81l-2.52%2C2.52H14.001z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-shop:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2212px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2012%2014%22%20style%3D%22enable-background%3Anew%200%200%2012%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M9%2C4V3c0-1.657-1.343-3-3-3S3%2C1.343%2C3%2C3v1H0v10h12V4H9z%20M3.5%2C6C3.224%2C6%2C3%2C5.776%2C3%2C5.5S3.224%2C5%2C3.5%2C5S4%2C5.224%2C4%2C5.5%20S3.776%2C6%2C3.5%2C6z%20M4%2C3c0-1.104%2C0.896-2%2C2-2s2%2C0.896%2C2%2C2v1H4V3z%20M8.5%2C6C8.224%2C6%2C8%2C5.776%2C8%2C5.5S8.224%2C5%2C8.5%2C5S9%2C5.224%2C9%2C5.5%20S8.776%2C6%2C8.5%2C6z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-comment:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M12%2C0H2C0.896%2C0%2C0%2C0.896%2C0%2C2v7c0%2C1.104%2C0.896%2C2%2C2%2C2h1v3l3-3h6c1.104%2C0%2C2-0.896%2C2-2V2C14%2C0.896%2C13.104%2C0%2C12%2C0z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-star:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2213px%22%20viewBox%3D%220%200%2014%2013%22%20style%3D%22enable-background%3Anew%200%200%2014%2013%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%2214%2C5%209%2C5%207%2C0%205%2C5%200%2C5%204%2C8%202.625%2C13%207%2C10%2011.375%2C13%2010%2C8%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-tag:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M5%2C0H0v5l9%2C9l5-5L5%2C0z%20M3%2C4C2.447%2C4%2C2%2C3.553%2C2%2C3s0.447-1%2C1-1s1%2C0.447%2C1%2C1S3.553%2C4%2C3%2C4z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-back:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2212px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2012%2014%22%20style%3D%22enable-background%3Anew%200%200%2012%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M4%2C3V0L0%2C4l4%2C4V5c0%2C0%2C6%2C0%2C6%2C3s-5%2C4-5%2C4v2c0%2C0%2C7-1%2C7-6C12%2C4%2C7%2C3%2C4%2C3z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-video:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2210px%22%20viewBox%3D%220%200%2014%2010%22%20style%3D%22enable-background%3Anew%200%200%2014%2010%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M8%2C0H2C0.896%2C0%2C0%2C0.896%2C0%2C2v6c0%2C1.104%2C0.896%2C2%2C2%2C2h6c1.104%2C0%2C2-0.896%2C2-2V5V2C10%2C0.896%2C9.104%2C0%2C8%2C0z%20M10%2C5l4%2C4V1L10%2C5z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-alert:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2212px%22%20viewBox%3D%220%200%2014%2012%22%20style%3D%22enable-background%3Anew%200%200%2014%2012%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M7%2C0L0%2C12h14L7%2C0z%20M7%2C11c-0.553%2C0-1-0.447-1-1s0.447-1%2C1-1s1%2C0.447%2C1%2C1S7.553%2C11%2C7%2C11z%20M7%2C8C6.447%2C8%2C6%2C7.553%2C6%2C7V5%20c0-0.553%2C0.447-1%2C1-1s1%2C0.447%2C1%2C1v2C8%2C7.553%2C7.553%2C8%2C7%2C8z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-icon-delete:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20fill%3D%22%23FFF%22%20points%3D%2214%2C3%2011%2C0%207%2C4%203%2C0%200%2C3%204%2C7%200%2C11%203%2C14%207%2C10%2011%2C14%2014%2C11%2010%2C7%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+/* Alt icons */
+.ui-alt-icon.ui-icon-action:after,
+.ui-alt-icon .ui-icon-action:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2213px%22%20viewBox%3D%220%200%2014%2013%22%20style%3D%22enable-background%3Anew%200%200%2014%2013%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M9%2C5v3l5-4L9%2C0v3c0%2C0-5%2C0-5%2C7C6%2C5%2C9%2C5%2C9%2C5z%20M11%2C11H2V5h1l2-2H0v10h13V7l-2%2C2V11z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-arrow-d:after,
+.ui-alt-icon .ui-icon-arrow-d:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%229%2C7%209%2C0%205%2C0%205%2C7%200%2C7%207%2C14%2014%2C7%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-arrow-d-l:after,
+.ui-alt-icon .ui-icon-arrow-d-l:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2214%2C3%2011%2C0%203.5%2C7.5%200%2C4%200%2C14%2010%2C14%206.5%2C10.5%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-arrow-d-r:after,
+.ui-alt-icon .ui-icon-arrow-d-r:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2210.5%2C7.5%203%2C0%200%2C3%207.5%2C10.5%204%2C14%2014%2C14%2014%2C4%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-arrow-l:after,
+.ui-alt-icon .ui-icon-arrow-l:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%227%2C5%207%2C0%200%2C7%207%2C14%207%2C9%2014%2C9%2014%2C5%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-arrow-r:after,
+.ui-alt-icon .ui-icon-arrow-r:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2214%2C7%207%2C0%207%2C5%200%2C5%200%2C9%207%2C9%207%2C14%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-arrow-u:after,
+.ui-alt-icon .ui-icon-arrow-u:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%227%2C0%200%2C7%205%2C7%205%2C14%209%2C14%209%2C7%2014%2C7%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-arrow-u-l:after,
+.ui-alt-icon .ui-icon-arrow-u-l:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2214%2C11%206.5%2C3.5%2010%2C0%200%2C0%200%2C10%203.5%2C6.5%2011%2C14%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-arrow-u-r:after,
+.ui-alt-icon .ui-icon-arrow-u-r:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2214%2C0%204%2C0%207.5%2C3.5%200%2C11%203%2C14%2010.5%2C6.5%2014%2C10%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-audio:after,
+.ui-alt-icon .ui-icon-audio:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214.018px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014.018%2014%22%20style%3D%22enable-background%3Anew%200%200%2014.018%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M1%2C4C0.447%2C4%2C0%2C4.447%2C0%2C5v4c0%2C0.553%2C0.447%2C1%2C1%2C1h1l4%2C4V0L2%2C4H1z%20M10.346%2C7c0-1.699-1.042-3.154-2.546-3.867L6.982%2C4.68%20C7.885%2C5.107%2C8.51%2C5.98%2C8.51%2C7S7.885%2C8.893%2C6.982%2C9.32L7.8%2C10.867C9.304%2C10.154%2C10.346%2C8.699%2C10.346%2C7z%20M9.447%2C0.017L8.618%2C1.586%20C10.723%2C2.584%2C12.182%2C4.621%2C12.182%2C7s-1.459%2C4.416-3.563%2C5.414l0.829%2C1.569c2.707-1.283%2C4.57-3.925%2C4.57-6.983%20S12.154%2C1.3%2C9.447%2C0.017z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-calendar:after,
+.ui-alt-icon .ui-icon-calendar:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M0%2C8h2V6H0V8z%20M3%2C8h2V6H3V8z%20M6%2C8h2V6H6V8z%20M9%2C8h2V6H9V8z%20M12%2C8h2V6h-2V8z%20M0%2C11h2V9H0V11z%20M3%2C11h2V9H3V11z%20M6%2C11h2V9H6V11z%20%20M9%2C11h2V9H9V11z%20M12%2C11h2V9h-2V11z%20M0%2C14h2v-2H0V14z%20M3%2C14h2v-2H3V14z%20M6%2C14h2v-2H6V14z%20M9%2C14h2v-2H9V14z%20M12%2C1%20c0-0.553-0.447-1-1-1s-1%2C0.447-1%2C1H4c0-0.553-0.447-1-1-1S2%2C0.447%2C2%2C1H0v4h14V1H12z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-camera:after,
+.ui-alt-icon .ui-icon-camera:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2211px%22%20viewBox%3D%220%200%2014%2011%22%20style%3D%22enable-background%3Anew%200%200%2014%2011%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M12%2C1H9.908C9.702%2C0.419%2C9.152%2C0%2C8.5%2C0h-3C4.848%2C0%2C4.298%2C0.419%2C4.092%2C1H2C0.896%2C1%2C0%2C1.896%2C0%2C3v6c0%2C1.104%2C0.896%2C2%2C2%2C2h10%20c1.104%2C0%2C2-0.896%2C2-2V3C14%2C1.896%2C13.104%2C1%2C12%2C1z%20M7%2C9C5.343%2C9%2C4%2C7.657%2C4%2C6s1.343-3%2C3-3s3%2C1.343%2C3%2C3S8.657%2C9%2C7%2C9z%20M7%2C4%20C5.896%2C4%2C5%2C4.896%2C5%2C6s0.896%2C2%2C2%2C2s2-0.896%2C2-2S8.104%2C4%2C7%2C4z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-carat-d:after,
+.ui-alt-icon .ui-icon-carat-d:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2212px%22%20height%3D%228px%22%20viewBox%3D%220%200%2012%208%22%20style%3D%22enable-background%3Anew%200%200%2012%208%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2210%2C0%206%2C4%202%2C0%200%2C2%206%2C8%2012%2C2%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-carat-l:after,
+.ui-alt-icon .ui-icon-carat-l:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%228px%22%20height%3D%2212px%22%20viewBox%3D%220%200%208%2012%22%20style%3D%22enable-background%3Anew%200%200%208%2012%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%228%2C2%206%2C0%200%2C6%206%2C12%208%2C10%204%2C6%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-carat-r:after,
+.ui-alt-icon .ui-icon-carat-r:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%228px%22%20height%3D%2212px%22%20viewBox%3D%220%200%208%2012%22%20style%3D%22enable-background%3Anew%200%200%208%2012%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%222%2C0%200%2C2%204%2C6%200%2C10%202%2C12%208%2C6%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-carat-u:after,
+.ui-alt-icon .ui-icon-carat-u:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2212px%22%20height%3D%228px%22%20viewBox%3D%220%200%2012%208%22%20style%3D%22enable-background%3Anew%200%200%2012%208%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%226%2C0%200%2C6%202%2C8%206%2C4%2010%2C8%2012%2C6%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-check:after,
+.ui-alt-icon .ui-icon-check:after,
+html .ui-alt-icon.ui-btn.ui-checkbox-on:after,
+html .ui-alt-icon .ui-btn.ui-checkbox-on:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2212px%22%20viewBox%3D%220%200%2014%2012%22%20style%3D%22enable-background%3Anew%200%200%2014%2012%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2214%2C3%2011%2C0%205.003%2C5.997%203%2C4%200%2C7%204.966%2C12%204.983%2C11.983%205%2C12%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-clock:after,
+.ui-alt-icon .ui-icon-clock:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M7%2C0C3.134%2C0%2C0%2C3.134%2C0%2C7s3.134%2C7%2C7%2C7s7-3.134%2C7-7S10.866%2C0%2C7%2C0z%20M7%2C12c-2.762%2C0-5-2.238-5-5s2.238-5%2C5-5s5%2C2.238%2C5%2C5%20S9.762%2C12%2C7%2C12z%20M9%2C6H8V4c0-0.553-0.447-1-1-1S6%2C3.447%2C6%2C4v3c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1S9.553%2C6%2C9%2C6z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-cloud:after,
+.ui-alt-icon .ui-icon-cloud:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%229px%22%20viewBox%3D%220%200%2014%209%22%20style%3D%22enable-background%3Anew%200%200%2014%209%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M14%2C7c0-0.793-0.465-1.472-1.134-1.795C12.949%2C4.984%2C13%2C4.749%2C13%2C4.5c0-1.104-0.896-2-2-2c-0.158%2C0-0.31%2C0.023-0.457%2C0.058%20C9.816%2C1.049%2C8.286%2C0%2C6.5%2C0C4.17%2C0%2C2.276%2C1.777%2C2.046%2C4.046C0.883%2C4.26%2C0%2C5.274%2C0%2C6.5C0%2C7.881%2C1.119%2C9%2C2.5%2C9h10V8.93%20C13.361%2C8.706%2C14%2C7.931%2C14%2C7z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-grid:after,
+.ui-alt-icon .ui-icon-grid:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M3%2C0H1C0.447%2C0%2C0%2C0.447%2C0%2C1v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1V1C4%2C0.447%2C3.553%2C0%2C3%2C0z%20M8%2C0H6%20C5.447%2C0%2C5%2C0.447%2C5%2C1v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1V1C9%2C0.447%2C8.553%2C0%2C8%2C0z%20M13%2C0h-2c-0.553%2C0-1%2C0.447-1%2C1v2%20c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1V1C14%2C0.447%2C13.553%2C0%2C13%2C0z%20M3%2C5H1C0.447%2C5%2C0%2C5.447%2C0%2C6v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2%20c0.553%2C0%2C1-0.447%2C1-1V6C4%2C5.447%2C3.553%2C5%2C3%2C5z%20M8%2C5H6C5.447%2C5%2C5%2C5.447%2C5%2C6v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1V6%20C9%2C5.447%2C8.553%2C5%2C8%2C5z%20M13%2C5h-2c-0.553%2C0-1%2C0.447-1%2C1v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1V6C14%2C5.447%2C13.553%2C5%2C13%2C5z%20M3%2C10%20H1c-0.553%2C0-1%2C0.447-1%2C1v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1v-2C4%2C10.447%2C3.553%2C10%2C3%2C10z%20M8%2C10H6c-0.553%2C0-1%2C0.447-1%2C1v2%20c0%2C0.553%2C0.447%2C1%2C1%2C1h2c0.553%2C0%2C1-0.447%2C1-1v-2C9%2C10.447%2C8.553%2C10%2C8%2C10z%20M13%2C10h-2c-0.553%2C0-1%2C0.447-1%2C1v2c0%2C0.553%2C0.447%2C1%2C1%2C1h2%20c0.553%2C0%2C1-0.447%2C1-1v-2C14%2C10.447%2C13.553%2C10%2C13%2C10z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-mail:after,
+.ui-alt-icon .ui-icon-mail:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2210px%22%20viewBox%3D%220%200%2014%2010%22%20style%3D%22enable-background%3Anew%200%200%2014%2010%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M0%2C1.75V10h14V1.75L7%2C7L0%2C1.75z%20M14%2C0H0l7%2C5L14%2C0z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-eye:after,
+.ui-alt-icon .ui-icon-eye:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2210px%22%20viewBox%3D%220%200%2014%2010%22%20style%3D%22enable-background%3Anew%200%200%2014%2010%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M7%2C0C3%2C0%2C0%2C5%2C0%2C5s3%2C5%2C7%2C5s7-5%2C7-5S11%2C0%2C7%2C0z%20M7%2C8C5.343%2C8%2C4%2C6.657%2C4%2C5s1.343-3%2C3-3s3%2C1.343%2C3%2C3S8.657%2C8%2C7%2C8z%20M7%2C4%20C6.448%2C4%2C6%2C4.447%2C6%2C5s0.448%2C1%2C1%2C1s1-0.447%2C1-1S7.552%2C4%2C7%2C4z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-gear:after,
+.ui-alt-icon .ui-icon-gear:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M13.621%2C5.904l-1.036-0.259c-0.168-0.042-0.303-0.168-0.355-0.332c-0.092-0.284-0.205-0.559-0.339-0.82%20c-0.079-0.153-0.073-0.337%2C0.017-0.486l0.549-0.915c0.118-0.196%2C0.088-0.448-0.075-0.61l-0.862-0.863%20c-0.162-0.163-0.414-0.193-0.611-0.075l-0.916%2C0.55C9.844%2C2.182%2C9.659%2C2.188%2C9.506%2C2.109C9.244%2C1.975%2C8.97%2C1.861%2C8.686%2C1.77%20c-0.165-0.052-0.29-0.187-0.332-0.354L8.095%2C0.379C8.039%2C0.156%2C7.839%2C0%2C7.609%2C0H6.391c-0.229%2C0-0.43%2C0.156-0.485%2C0.379L5.646%2C1.415%20C5.604%2C1.582%2C5.479%2C1.718%2C5.313%2C1.77c-0.284%2C0.092-0.559%2C0.206-0.82%2C0.34C4.339%2C2.188%2C4.155%2C2.182%2C4.007%2C2.093L3.092%2C1.544%20c-0.196-0.118-0.448-0.087-0.61%2C0.075L1.619%2C2.481C1.457%2C2.644%2C1.426%2C2.896%2C1.544%2C3.093l0.549%2C0.914%20c0.089%2C0.148%2C0.095%2C0.332%2C0.017%2C0.486C1.975%2C4.755%2C1.861%2C5.029%2C1.77%2C5.314c-0.053%2C0.164-0.188%2C0.29-0.354%2C0.332L0.379%2C5.905%20C0.156%2C5.961%2C0%2C6.161%2C0%2C6.391v1.219c0%2C0.229%2C0.156%2C0.43%2C0.379%2C0.485l1.036%2C0.26C1.582%2C8.396%2C1.717%2C8.521%2C1.77%2C8.687%20c0.092%2C0.284%2C0.205%2C0.559%2C0.34%2C0.82C2.188%2C9.66%2C2.182%2C9.844%2C2.093%2C9.993l-0.549%2C0.915c-0.118%2C0.195-0.087%2C0.448%2C0.075%2C0.61%20l0.862%2C0.862c0.162%2C0.163%2C0.414%2C0.193%2C0.61%2C0.075l0.915-0.549c0.148-0.089%2C0.332-0.095%2C0.486-0.017%20c0.262%2C0.135%2C0.536%2C0.248%2C0.82%2C0.34c0.165%2C0.053%2C0.291%2C0.187%2C0.332%2C0.354l0.259%2C1.036C5.96%2C13.844%2C6.16%2C14%2C6.39%2C14h1.22%20c0.229%2C0%2C0.43-0.156%2C0.485-0.379l0.259-1.036c0.042-0.167%2C0.168-0.302%2C0.333-0.354c0.284-0.092%2C0.559-0.205%2C0.82-0.34%20c0.154-0.078%2C0.338-0.072%2C0.486%2C0.017l0.914%2C0.549c0.197%2C0.118%2C0.449%2C0.088%2C0.611-0.074l0.862-0.863%20c0.163-0.162%2C0.193-0.415%2C0.075-0.611l-0.549-0.915c-0.089-0.148-0.096-0.332-0.017-0.485c0.134-0.263%2C0.248-0.536%2C0.339-0.82%20c0.053-0.165%2C0.188-0.291%2C0.355-0.333l1.036-0.259C13.844%2C8.039%2C14%2C7.839%2C14%2C7.609V6.39C14%2C6.16%2C13.844%2C5.96%2C13.621%2C5.904z%20M7%2C10%20c-1.657%2C0-3-1.343-3-3s1.343-3%2C3-3s3%2C1.343%2C3%2C3S8.657%2C10%2C7%2C10z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-heart:after,
+.ui-alt-icon .ui-icon-heart:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2213.744px%22%20viewBox%3D%220%200%2014%2013.744%22%20style%3D%22enable-background%3Anew%200%200%2014%2013.744%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M7%2C1.744c-2-3-7-2-7%2C2c0%2C3%2C4%2C7%2C4%2C7s2.417%2C2.479%2C3%2C3c0.583-0.521%2C3-3%2C3-3s4-4%2C4-7C14-0.256%2C9-1.256%2C7%2C1.744z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-home:after,
+.ui-alt-icon .ui-icon-home:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%227%2C0%200%2C7%202%2C7%202%2C14%205%2C14%205%2C9%209%2C9%209%2C14%2012%2C14%2012%2C7%2014%2C7%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-info:after,
+.ui-alt-icon .ui-icon-info:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M7%2C0C3.134%2C0%2C0%2C3.134%2C0%2C7s3.134%2C7%2C7%2C7s7-3.134%2C7-7S10.866%2C0%2C7%2C0z%20M7%2C2c0.552%2C0%2C1%2C0.447%2C1%2C1S7.552%2C4%2C7%2C4S6%2C3.553%2C6%2C3%20S6.448%2C2%2C7%2C2z%20M9%2C11H5v-1h1V6H5V5h3v5h1V11z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-bars:after,
+.ui-alt-icon .ui-icon-bars:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2210px%22%20viewBox%3D%220%200%2014%2010%22%20style%3D%22enable-background%3Anew%200%200%2014%2010%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M1%2C2h12c0.553%2C0%2C1-0.447%2C1-1s-0.447-1-1-1H1C0.447%2C0%2C0%2C0.447%2C0%2C1S0.447%2C2%2C1%2C2z%20M13%2C4H1C0.447%2C4%2C0%2C4.447%2C0%2C5s0.447%2C1%2C1%2C1h12%20c0.553%2C0%2C1-0.447%2C1-1S13.553%2C4%2C13%2C4z%20M13%2C8H1C0.447%2C8%2C0%2C8.447%2C0%2C9s0.447%2C1%2C1%2C1h12c0.553%2C0%2C1-0.447%2C1-1S13.553%2C8%2C13%2C8z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-bullets:after,
+.ui-alt-icon .ui-icon-bullets:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2210px%22%20viewBox%3D%220%200%2014%2010%22%20style%3D%22enable-background%3Anew%200%200%2014%2010%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M5%2C2h8c0.553%2C0%2C1-0.447%2C1-1s-0.447-1-1-1H5C4.447%2C0%2C4%2C0.447%2C4%2C1S4.447%2C2%2C5%2C2z%20M13%2C4H5C4.447%2C4%2C4%2C4.447%2C4%2C5s0.447%2C1%2C1%2C1h8%20c0.553%2C0%2C1-0.447%2C1-1S13.553%2C4%2C13%2C4z%20M13%2C8H5C4.447%2C8%2C4%2C8.447%2C4%2C9s0.447%2C1%2C1%2C1h8c0.553%2C0%2C1-0.447%2C1-1S13.553%2C8%2C13%2C8z%20M1%2C0%20C0.447%2C0%2C0%2C0.447%2C0%2C1s0.447%2C1%2C1%2C1s1-0.447%2C1-1S1.553%2C0%2C1%2C0z%20M1%2C4C0.447%2C4%2C0%2C4.447%2C0%2C5s0.447%2C1%2C1%2C1s1-0.447%2C1-1S1.553%2C4%2C1%2C4z%20M1%2C8%20C0.447%2C8%2C0%2C8.447%2C0%2C9s0.447%2C1%2C1%2C1s1-0.447%2C1-1S1.553%2C8%2C1%2C8z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-navigation:after,
+.ui-alt-icon .ui-icon-navigation:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2214%2C0%200%2C6%208%2C6%208%2C14%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-lock:after,
+.ui-alt-icon .ui-icon-lock:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2212px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2012%2014%22%20style%3D%22enable-background%3Anew%200%200%2012%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M11%2C6V5c0-2.762-2.239-5-5-5S1%2C2.238%2C1%2C5v1H0v8h12V6H11z%20M6.5%2C9.847V12h-1V9.847C5.207%2C9.673%2C5%2C9.366%2C5%2C9%20c0-0.553%2C0.448-1%2C1-1s1%2C0.447%2C1%2C1C7%2C9.366%2C6.793%2C9.673%2C6.5%2C9.847z%20M9%2C6H3V5c0-1.657%2C1.343-3%2C3-3s3%2C1.343%2C3%2C3V6z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-search:after,
+.ui-alt-icon .ui-icon-search:after,
+.ui-input-search:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2213.701px%22%20height%3D%2213.721px%22%20viewBox%3D%220%200%2013.701%2013.721%22%20style%3D%22enable-background%3Anew%200%200%2013.701%2013.721%3B%22%20%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M10.021%2C8.626C10.638%2C7.738%2C11%2C6.662%2C11%2C5.5C11%2C2.463%2C8.537%2C0%2C5.5%2C0S0%2C2.463%2C0%2C5.5S2.463%2C11%2C5.5%2C11%20c1.152%2C0%2C2.221-0.356%2C3.105-0.962l3.682%2C3.683l1.414-1.414L10.021%2C8.626z%20M5.5%2C9C3.567%2C9%2C2%2C7.433%2C2%2C5.5S3.567%2C2%2C5.5%2C2S9%2C3.567%2C9%2C5.5%20S7.433%2C9%2C5.5%2C9z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-location:after,
+.ui-alt-icon .ui-icon-location:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%228px%22%20height%3D%2214px%22%20viewBox%3D%220%200%208%2014%22%20style%3D%22enable-background%3Anew%200%200%208%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M4%2C0C1.791%2C0%2C0%2C1.791%2C0%2C4c0%2C2%2C4%2C10%2C4%2C10S8%2C6%2C8%2C4C8%2C1.791%2C6.209%2C0%2C4%2C0z%20M4%2C6C2.896%2C6%2C2%2C5.104%2C2%2C4s0.896-2%2C2-2s2%2C0.896%2C2%2C2%20S5.104%2C6%2C4%2C6z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-minus:after,
+.ui-alt-icon .ui-icon-minus:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%224px%22%20viewBox%3D%220%200%2014%204%22%20style%3D%22enable-background%3Anew%200%200%2014%204%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Crect%20width%3D%2214%22%20height%3D%224%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-forbidden:after,
+.ui-alt-icon .ui-icon-forbidden:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M12.601%2C11.187C13.476%2C10.018%2C14%2C8.572%2C14%2C7c0-3.866-3.134-7-7-7C5.428%2C0%2C3.982%2C0.524%2C2.813%2C1.399L2.757%2C1.343L2.053%2C2.048%20L2.048%2C2.053L1.343%2C2.758l0.056%2C0.056C0.524%2C3.982%2C0%2C5.428%2C0%2C7c0%2C3.866%2C3.134%2C7%2C7%2C7c1.572%2C0%2C3.018-0.524%2C4.187-1.399l0.056%2C0.057%20l0.705-0.705l0.005-0.005l0.705-0.705L12.601%2C11.187z%20M7%2C2c2.761%2C0%2C5%2C2.238%2C5%2C5c0%2C1.019-0.308%2C1.964-0.832%2C2.754L4.246%2C2.832%20C5.036%2C2.308%2C5.981%2C2%2C7%2C2z%20M7%2C12c-2.761%2C0-5-2.238-5-5c0-1.019%2C0.308-1.964%2C0.832-2.754l6.922%2C6.922C8.964%2C11.692%2C8.019%2C12%2C7%2C12z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-edit:after,
+.ui-alt-icon .ui-icon-edit:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M1%2C10l-1%2C4l4-1l7-7L8%2C3L1%2C10z%20M11%2C0L9%2C2l3%2C3l2-2L11%2C0z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-user:after,
+.ui-alt-icon .ui-icon-user:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M8.851%2C10.101c-0.18-0.399-0.2-0.763-0.153-1.104C9.383%2C8.49%2C9.738%2C7.621%2C9.891%2C6.465C10.493%2C6.355%2C10.5%2C5.967%2C10.5%2C5.5%20c0-0.437-0.008-0.804-0.502-0.94C9.999%2C4.539%2C10%2C4.521%2C10%2C4.5c0-2.103-1-4-2-4C8%2C0.5%2C7.5%2C0%2C6.5%2C0C5%2C0%2C4%2C1.877%2C4%2C4.5%20c0%2C0.021%2C0.001%2C0.039%2C0.002%2C0.06C3.508%2C4.696%2C3.5%2C5.063%2C3.5%2C5.5c0%2C0.467%2C0.007%2C0.855%2C0.609%2C0.965%20C4.262%2C7.621%2C4.617%2C8.49%2C5.303%2C8.997c0.047%2C0.341%2C0.026%2C0.704-0.153%2C1.104C1.503%2C10.503%2C0%2C12%2C0%2C12v2h14v-2%20C14%2C12%2C12.497%2C10.503%2C8.851%2C10.101z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-phone:after,
+.ui-alt-icon .ui-icon-phone:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2213.979px%22%20height%3D%2214.016px%22%20viewBox%3D%220%200%2013.979%2014.016%22%20style%3D%22enable-background%3Anew%200%200%2013.979%2014.016%3B%22%20%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M6.939%2C9.189C6.165%2C8.557%2C5.271%2C7.705%2C4.497%2C6.744C3.953%2C6.071%2C3.473%2C5.363%2C3.969%2C4.866l-3.482-3.48%20C-0.021%2C2.02-1.146%2C5.04%2C3.675%2C9.984c5.08%2C5.211%2C8.356%2C4.096%2C8.92%2C3.51l-3.396-3.4C8.725%2C10.568%2C8.113%2C10.146%2C6.939%2C9.189z%20%20M13.82%2C11.519v-0.004c0%2C0-2.649-2.646-2.65-2.648c-0.21-0.21-0.546-0.205-0.754%2C0.002L9.455%2C9.831l3.404%2C3.408%20c0%2C0%2C0.962-0.96%2C0.961-0.961l0.002-0.001C14.043%2C12.056%2C14.021%2C11.721%2C13.82%2C11.519z%20M5.192%2C3.644V3.642%20c0.221-0.222%2C0.2-0.557%2C0-0.758V2.881c0%2C0-2.726-2.724-2.727-2.725C2.255-0.055%2C1.92-0.05%2C1.712%2C0.157L0.751%2C1.121l3.48%2C3.483%20C4.231%2C4.604%2C5.192%2C3.645%2C5.192%2C3.644z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-plus:after,
+.ui-alt-icon .ui-icon-plus:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2214%2C5%209%2C5%209%2C0%205%2C0%205%2C5%200%2C5%200%2C9%205%2C9%205%2C14%209%2C14%209%2C9%2014%2C9%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-power:after,
+.ui-alt-icon .ui-icon-power:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2212px%22%20height%3D%2213.896px%22%20viewBox%3D%220%200%2012%2013.896%22%20style%3D%22enable-background%3Anew%200%200%2012%2013.896%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M10.243%2C3.356c-0.392-0.401-1.024-0.401-1.415%2C0c-0.39%2C0.402-0.39%2C1.054%2C0%2C1.455C9.584%2C5.59%2C10%2C6.623%2C10%2C7.722%20c0%2C1.1-0.416%2C2.133-1.172%2C2.911c-1.511%2C1.556-4.145%2C1.556-5.656%2C0C2.416%2C9.854%2C2%2C8.821%2C2%2C7.722c0-1.099%2C0.416-2.132%2C1.172-2.91%20c0.39-0.401%2C0.39-1.053%2C0-1.455c-0.391-0.401-1.024-0.401-1.415%2C0C0.624%2C4.522%2C0%2C6.073%2C0%2C7.722c0%2C1.649%2C0.624%2C3.2%2C1.757%2C4.366%20C2.891%2C13.254%2C4.397%2C13.896%2C6%2C13.896s3.109-0.643%2C4.243-1.809C11.376%2C10.922%2C12%2C9.371%2C12%2C7.722C12%2C6.073%2C11.376%2C4.522%2C10.243%2C3.356z%20%20M6%2C8c0.553%2C0%2C1-0.447%2C1-1V1c0-0.553-0.447-1-1-1S5%2C0.447%2C5%2C1v6C5%2C7.553%2C5.447%2C8%2C6%2C8z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-recycle:after,
+.ui-alt-icon .ui-icon-recycle:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2212px%22%20viewBox%3D%220%200%2014%2012%22%20style%3D%22enable-background%3Anew%200%200%2014%2012%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M3%2C6h1L2%2C3L0%2C6h1c0%2C3.313%2C2.687%2C6%2C6%2C6c0.702%2C0%2C1.374-0.127%2C2-0.349V9.445C8.41%2C9.789%2C7.732%2C10%2C7%2C10C4.791%2C10%2C3%2C8.209%2C3%2C6z%20%20M13%2C6c0-3.313-2.687-6-6-6C6.298%2C0%2C5.626%2C0.127%2C5%2C0.349v2.206C5.59%2C2.211%2C6.268%2C2%2C7%2C2c2.209%2C0%2C4%2C1.791%2C4%2C4h-1l2%2C3l2-3H13z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-forward:after,
+.ui-alt-icon .ui-icon-forward:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2212px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2012%2014%22%20style%3D%22enable-background%3Anew%200%200%2012%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M12%2C4L8%2C0v3C5%2C3%2C0%2C4%2C0%2C8c0%2C5%2C7%2C6%2C7%2C6v-2c0%2C0-5-1-5-4s6-3%2C6-3v3L12%2C4z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-refresh:after,
+.ui-alt-icon .ui-icon-refresh:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214.001px%22%20height%3D%2214.002px%22%20viewBox%3D%220%200%2014.001%2014.002%22%20style%3D%22enable-background%3Anew%200%200%2014.001%2014.002%3B%22%20%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M14.001%2C6.001v-6l-2.06%2C2.06c-0.423-0.424-0.897-0.809-1.44-1.122C7.153-0.994%2C2.872%2C0.153%2C0.939%2C3.501%20c-1.933%2C3.348-0.786%2C7.629%2C2.562%2C9.562c3.348%2C1.933%2C7.629%2C0.785%2C9.562-2.562l-1.732-1c-1.381%2C2.392-4.438%2C3.211-6.83%2C1.83%20s-3.211-4.438-1.83-6.83s4.438-3.211%2C6.83-1.83c0.389%2C0.225%2C0.718%2C0.506%2C1.02%2C0.81l-2.52%2C2.52H14.001z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-shop:after,
+.ui-alt-icon .ui-icon-shop:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2212px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2012%2014%22%20style%3D%22enable-background%3Anew%200%200%2012%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M9%2C4V3c0-1.657-1.343-3-3-3S3%2C1.343%2C3%2C3v1H0v10h12V4H9z%20M3.5%2C6C3.224%2C6%2C3%2C5.776%2C3%2C5.5S3.224%2C5%2C3.5%2C5S4%2C5.224%2C4%2C5.5%20S3.776%2C6%2C3.5%2C6z%20M4%2C3c0-1.104%2C0.896-2%2C2-2s2%2C0.896%2C2%2C2v1H4V3z%20M8.5%2C6C8.224%2C6%2C8%2C5.776%2C8%2C5.5S8.224%2C5%2C8.5%2C5S9%2C5.224%2C9%2C5.5%20S8.776%2C6%2C8.5%2C6z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-comment:after,
+.ui-alt-icon .ui-icon-comment:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M12%2C0H2C0.896%2C0%2C0%2C0.896%2C0%2C2v7c0%2C1.104%2C0.896%2C2%2C2%2C2h1v3l3-3h6c1.104%2C0%2C2-0.896%2C2-2V2C14%2C0.896%2C13.104%2C0%2C12%2C0z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-star:after,
+.ui-alt-icon .ui-icon-star:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2213px%22%20viewBox%3D%220%200%2014%2013%22%20style%3D%22enable-background%3Anew%200%200%2014%2013%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2214%2C5%209%2C5%207%2C0%205%2C5%200%2C5%204%2C8%202.625%2C13%207%2C10%2011.375%2C13%2010%2C8%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-tag:after,
+.ui-alt-icon .ui-icon-tag:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M5%2C0H0v5l9%2C9l5-5L5%2C0z%20M3%2C4C2.447%2C4%2C2%2C3.553%2C2%2C3s0.447-1%2C1-1s1%2C0.447%2C1%2C1S3.553%2C4%2C3%2C4z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-back:after,
+.ui-alt-icon .ui-icon-back:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2212px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2012%2014%22%20style%3D%22enable-background%3Anew%200%200%2012%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M4%2C3V0L0%2C4l4%2C4V5c0%2C0%2C6%2C0%2C6%2C3s-5%2C4-5%2C4v2c0%2C0%2C7-1%2C7-6C12%2C4%2C7%2C3%2C4%2C3z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-video:after,
+.ui-alt-icon .ui-icon-video:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2210px%22%20viewBox%3D%220%200%2014%2010%22%20style%3D%22enable-background%3Anew%200%200%2014%2010%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M8%2C0H2C0.896%2C0%2C0%2C0.896%2C0%2C2v6c0%2C1.104%2C0.896%2C2%2C2%2C2h6c1.104%2C0%2C2-0.896%2C2-2V5V2C10%2C0.896%2C9.104%2C0%2C8%2C0z%20M10%2C5l4%2C4V1L10%2C5z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-alert:after,
+.ui-alt-icon .ui-icon-alert:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2212px%22%20viewBox%3D%220%200%2014%2012%22%20style%3D%22enable-background%3Anew%200%200%2014%2012%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M7%2C0L0%2C12h14L7%2C0z%20M7%2C11c-0.553%2C0-1-0.447-1-1s0.447-1%2C1-1s1%2C0.447%2C1%2C1S7.553%2C11%2C7%2C11z%20M7%2C8C6.447%2C8%2C6%2C7.553%2C6%2C7V5%20c0-0.553%2C0.447-1%2C1-1s1%2C0.447%2C1%2C1v2C8%2C7.553%2C7.553%2C8%2C7%2C8z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+.ui-alt-icon.ui-icon-delete:after,
+.ui-alt-icon .ui-icon-delete:after {
+	background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2214px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2014%2014%22%20style%3D%22enable-background%3Anew%200%200%2014%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpolygon%20points%3D%2214%2C3%2011%2C0%207%2C4%203%2C0%200%2C3%204%2C7%200%2C11%203%2C14%207%2C10%2011%2C14%2014%2C11%2010%2C7%20%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E');
+}
+/* PNG icons */
+.ui-nosvg .ui-icon-action:after {
+	background-image: url(images/icons-png/action-white.png);
+}
+.ui-nosvg .ui-icon-arrow-d-l:after {
+	background-image: url(images/icons-png/arrow-d-l-white.png);
+}
+.ui-nosvg .ui-icon-arrow-d-r:after {
+	background-image: url(images/icons-png/arrow-d-r-white.png);
+}
+.ui-nosvg .ui-icon-arrow-d:after {
+	background-image: url(images/icons-png/arrow-d-white.png);
+}
+.ui-nosvg .ui-icon-arrow-l:after {
+	background-image: url(images/icons-png/arrow-l-white.png);
+}
+.ui-nosvg .ui-icon-arrow-r:after {
+	background-image: url(images/icons-png/arrow-r-white.png);
+}
+.ui-nosvg .ui-icon-arrow-u-l:after {
+	background-image: url(images/icons-png/arrow-u-l-white.png);
+}
+.ui-nosvg .ui-icon-arrow-u-r:after {
+	background-image: url(images/icons-png/arrow-u-r-white.png);
+}
+.ui-nosvg .ui-icon-arrow-u:after {
+	background-image: url(images/icons-png/arrow-u-white.png);
+}
+.ui-nosvg .ui-icon-audio:after {
+	background-image: url(images/icons-png/audio-white.png);
+}
+.ui-nosvg .ui-icon-calendar:after {
+	background-image: url(images/icons-png/calendar-white.png);
+}
+.ui-nosvg .ui-icon-camera:after {
+	background-image: url(images/icons-png/camera-white.png);
+}
+.ui-nosvg .ui-icon-carat-d:after {
+	background-image: url(images/icons-png/carat-d-white.png);
+}
+.ui-nosvg .ui-icon-carat-l:after {
+	background-image: url(images/icons-png/carat-l-white.png);
+}
+.ui-nosvg .ui-icon-carat-r:after {
+	background-image: url(images/icons-png/carat-r-white.png);
+}
+.ui-nosvg .ui-icon-carat-u:after {
+	background-image: url(images/icons-png/carat-u-white.png);
+}
+.ui-nosvg .ui-icon-check:after,
+html.ui-nosvg .ui-btn.ui-checkbox-on:after {
+	background-image: url(images/icons-png/check-white.png);
+}
+.ui-nosvg .ui-icon-clock:after {
+	background-image: url(images/icons-png/clock-white.png);
+}
+.ui-nosvg .ui-icon-cloud:after {
+	background-image: url(images/icons-png/cloud-white.png);
+}
+.ui-nosvg .ui-icon-grid:after {
+	background-image: url(images/icons-png/grid-white.png);
+}
+.ui-nosvg .ui-icon-mail:after {
+	background-image: url(images/icons-png/mail-white.png);
+}
+.ui-nosvg .ui-icon-eye:after {
+	background-image: url(images/icons-png/eye-white.png);
+}
+.ui-nosvg .ui-icon-gear:after {
+	background-image: url(images/icons-png/gear-white.png);
+}
+.ui-nosvg .ui-icon-heart:after {
+	background-image: url(images/icons-png/heart-white.png);
+}
+.ui-nosvg .ui-icon-home:after {
+	background-image: url(images/icons-png/home-white.png);
+}
+.ui-nosvg .ui-icon-info:after {
+	background-image: url(images/icons-png/info-white.png);
+}
+.ui-nosvg .ui-icon-bullets:after {
+	background-image: url(images/icons-png/bullets-white.png);
+}
+.ui-nosvg .ui-icon-bars:after {
+	background-image: url(images/icons-png/bars-white.png);
+}
+.ui-nosvg .ui-icon-navigation:after {
+	background-image: url(images/icons-png/navigation-white.png);
+}
+.ui-nosvg .ui-icon-lock:after {
+	background-image: url(images/icons-png/lock-white.png);
+}
+.ui-nosvg .ui-icon-search:after {
+	background-image: url(images/icons-png/search-white.png);
+}
+.ui-nosvg .ui-icon-location:after {
+	background-image: url(images/icons-png/location-white.png);
+}
+.ui-nosvg .ui-icon-minus:after {
+	background-image: url(images/icons-png/minus-white.png);
+}
+.ui-nosvg .ui-icon-forbidden:after {
+	background-image: url(images/icons-png/forbidden-white.png);
+}
+.ui-nosvg .ui-icon-edit:after {
+	background-image: url(images/icons-png/edit-white.png);
+}
+.ui-nosvg .ui-icon-user:after {
+	background-image: url(images/icons-png/user-white.png);
+}
+.ui-nosvg .ui-icon-phone:after {
+	background-image: url(images/icons-png/phone-white.png);
+}
+.ui-nosvg .ui-icon-plus:after {
+	background-image: url(images/icons-png/plus-white.png);
+}
+.ui-nosvg .ui-icon-power:after {
+	background-image: url(images/icons-png/power-white.png);
+}
+.ui-nosvg .ui-icon-recycle:after {
+	background-image: url(images/icons-png/recycle-white.png);
+}
+.ui-nosvg .ui-icon-forward:after {
+	background-image: url(images/icons-png/forward-white.png);
+}
+.ui-nosvg .ui-icon-refresh:after {
+	background-image: url(images/icons-png/refresh-white.png);
+}
+.ui-nosvg .ui-icon-shop:after {
+	background-image: url(images/icons-png/shop-white.png);
+}
+.ui-nosvg .ui-icon-comment:after {
+	background-image: url(images/icons-png/comment-white.png);
+}
+.ui-nosvg .ui-icon-star:after {
+	background-image: url(images/icons-png/star-white.png);
+}
+.ui-nosvg .ui-icon-tag:after {
+	background-image: url(images/icons-png/tag-white.png);
+}
+.ui-nosvg .ui-icon-back:after {
+	background-image: url(images/icons-png/back-white.png);
+}
+.ui-nosvg .ui-icon-video:after {
+	background-image: url(images/icons-png/video-white.png);
+}
+.ui-nosvg .ui-icon-alert:after {
+	background-image: url(images/icons-png/alert-white.png);
+}
+.ui-nosvg .ui-icon-delete:after {
+	background-image: url(images/icons-png/delete-white.png);
+}
+/* Alt icons */
+.ui-nosvg .ui-alt-icon.ui-icon-action:after,
+.ui-nosvg .ui-alt-icon .ui-icon-action:after {
+	background-image: url(images/icons-png/action-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-arrow-d:after,
+.ui-nosvg .ui-alt-icon .ui-icon-arrow-d:after {
+	background-image: url(images/icons-png/arrow-d-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-arrow-d-l:after,
+.ui-nosvg .ui-alt-icon .ui-icon-arrow-d-l:after {
+	background-image: url(images/icons-png/arrow-d-l-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-arrow-d-r:after,
+.ui-nosvg .ui-alt-icon .ui-icon-arrow-d-r:after {
+	background-image: url(images/icons-png/arrow-d-r-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-arrow-l:after,
+.ui-nosvg .ui-alt-icon .ui-icon-arrow-l:after {
+	background-image: url(images/icons-png/arrow-l-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-arrow-r:after,
+.ui-nosvg .ui-alt-icon .ui-icon-arrow-r:after {
+	background-image: url(images/icons-png/arrow-r-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-arrow-u:after,
+.ui-nosvg .ui-alt-icon .ui-icon-arrow-u:after {
+	background-image: url(images/icons-png/arrow-u-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-arrow-u-l:after,
+.ui-nosvg .ui-alt-icon .ui-icon-arrow-u-l:after {
+	background-image: url(images/icons-png/arrow-u-l-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-arrow-u-r:after,
+.ui-nosvg .ui-alt-icon .ui-icon-arrow-u-r:after {
+	background-image: url(images/icons-png/arrow-u-r-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-audio:after,
+.ui-nosvg .ui-alt-icon .ui-icon-audio:after {
+	background-image: url(images/icons-png/audio-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-calendar:after,
+.ui-nosvg .ui-alt-icon .ui-icon-calendar:after {
+	background-image: url(images/icons-png/calendar-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-camera:after,
+.ui-nosvg .ui-alt-icon .ui-icon-camera:after {
+	background-image: url(images/icons-png/camera-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-carat-d:after,
+.ui-nosvg .ui-alt-icon .ui-icon-carat-d:after {
+	background-image: url(images/icons-png/carat-d-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-carat-l:after,
+.ui-nosvg .ui-alt-icon .ui-icon-carat-l:after {
+	background-image: url(images/icons-png/carat-l-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-carat-r:after,
+.ui-nosvg .ui-alt-icon .ui-icon-carat-r:after {
+	background-image: url(images/icons-png/carat-r-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-carat-u:after,
+.ui-nosvg .ui-alt-icon .ui-icon-carat-u:after {
+	background-image: url(images/icons-png/carat-u-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-check:after,
+.ui-nosvg .ui-alt-icon .ui-icon-check:after,
+.ui-nosvg .ui-alt-icon.ui-btn.ui-checkbox-on:after,
+.ui-nosvg .ui-alt-icon .ui-btn.ui-checkbox-on:after {
+	background-image: url(images/icons-png/check-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-clock:after,
+.ui-nosvg .ui-alt-icon .ui-icon-clock:after {
+	background-image: url(images/icons-png/clock-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-cloud:after,
+.ui-nosvg .ui-alt-icon .ui-icon-cloud:after {
+	background-image: url(images/icons-png/cloud-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-grid:after,
+.ui-nosvg .ui-alt-icon .ui-icon-grid:after {
+	background-image: url(images/icons-png/grid-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-mail:after,
+.ui-nosvg .ui-alt-icon .ui-icon-mail:after {
+	background-image: url(images/icons-png/mail-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-eye:after,
+.ui-nosvg .ui-alt-icon .ui-icon-eye:after {
+	background-image: url(images/icons-png/eye-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-gear:after,
+.ui-nosvg .ui-alt-icon .ui-icon-gear:after {
+	background-image: url(images/icons-png/gear-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-heart:after,
+.ui-nosvg .ui-alt-icon .ui-icon-heart:after {
+	background-image: url(images/icons-png/heart-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-home:after,
+.ui-nosvg .ui-alt-icon .ui-icon-home:after {
+	background-image: url(images/icons-png/home-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-info:after,
+.ui-nosvg .ui-alt-icon .ui-icon-info:after {
+	background-image: url(images/icons-png/info-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-bars:after,
+.ui-nosvg .ui-alt-icon .ui-icon-bars:after {
+	background-image: url(images/icons-png/bars-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-bullets:after,
+.ui-nosvg .ui-alt-icon .ui-icon-bullets:after {
+	background-image: url(images/icons-png/bullets-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-navigation:after,
+.ui-nosvg .ui-alt-icon .ui-icon-navigation:after {
+	background-image: url(images/icons-png/navigation-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-lock:after,
+.ui-nosvg .ui-alt-icon .ui-icon-lock:after {
+	background-image: url(images/icons-png/lock-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-search:after,
+.ui-nosvg .ui-alt-icon .ui-icon-search:after,
+.ui-nosvg .ui-input-search:after {
+	background-image: url(images/icons-png/search-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-location:after,
+.ui-nosvg .ui-alt-icon .ui-icon-location:after {
+	background-image: url(images/icons-png/location-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-minus:after,
+.ui-nosvg .ui-alt-icon .ui-icon-minus:after {
+	background-image: url(images/icons-png/minus-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-forbidden:after,
+.ui-nosvg .ui-alt-icon .ui-icon-forbidden:after {
+	background-image: url(images/icons-png/forbidden-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-edit:after,
+.ui-nosvg .ui-alt-icon .ui-icon-edit:after {
+	background-image: url(images/icons-png/edit-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-user:after,
+.ui-nosvg .ui-alt-icon .ui-icon-user:after {
+	background-image: url(images/icons-png/user-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-phone:after,
+.ui-nosvg .ui-alt-icon .ui-icon-phone:after {
+	background-image: url(images/icons-png/phone-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-plus:after,
+.ui-nosvg .ui-alt-icon .ui-icon-plus:after {
+	background-image: url(images/icons-png/plus-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-power:after,
+.ui-nosvg .ui-alt-icon .ui-icon-power:after {
+	background-image: url(images/icons-png/power-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-recycle:after,
+.ui-nosvg .ui-alt-icon .ui-icon-recycle:after {
+	background-image: url(images/icons-png/recycle-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-forward:after,
+.ui-nosvg .ui-alt-icon .ui-icon-forward:after {
+	background-image: url(images/icons-png/forward-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-refresh:after,
+.ui-nosvg .ui-alt-icon .ui-icon-refresh:after {
+	background-image: url(images/icons-png/refresh-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-shop:after,
+.ui-nosvg .ui-alt-icon .ui-icon-shop:after {
+	background-image: url(images/icons-png/shop-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-comment:after,
+.ui-nosvg .ui-alt-icon .ui-icon-comment:after {
+	background-image: url(images/icons-png/comment-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-star:after,
+.ui-nosvg .ui-alt-icon .ui-icon-star:after {
+	background-image: url(images/icons-png/star-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-tag:after,
+.ui-nosvg .ui-alt-icon .ui-icon-tag:after {
+	background-image: url(images/icons-png/tag-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-back:after,
+.ui-nosvg .ui-alt-icon .ui-icon-back:after {
+	background-image: url(images/icons-png/back-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-video:after,
+.ui-nosvg .ui-alt-icon .ui-icon-video:after {
+	background-image: url(images/icons-png/video-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-alert:after,
+.ui-nosvg .ui-alt-icon .ui-icon-alert:after {
+	background-image: url(images/icons-png/alert-black.png);
+}
+.ui-nosvg .ui-alt-icon.ui-icon-delete:after,
+.ui-nosvg .ui-alt-icon .ui-icon-delete:after {
+	background-image: url(images/icons-png/delete-black.png);
+}
+/* Globals */
+/* Font
+-----------------------------------------------------------------------------------------------------------*/
+html {
+	font-size: 100%;
+}
+body,
+input,
+select,
+textarea,
+button,
+.ui-btn {
+	font-size: 1em;
+	line-height: 1.3;
+	font-family: sans-serif /*{global-font-family}*/;
+}
+legend,
+.ui-input-text input,
+.ui-input-search input {
+	color: inherit;
+	text-shadow: inherit;
+}
+/* Form labels (overrides font-weight bold in bars, and mini font-size) */
+.ui-mobile label,
+div.ui-controlgroup-label {
+	font-weight: normal;
+	font-size: 16px;
+}
+/* Separators
+-----------------------------------------------------------------------------------------------------------*/
+/* Field contain separator (< 28em) */
+.ui-field-contain {
+	border-bottom-color: #828282;
+	border-bottom-color: rgba(0,0,0,.15);
+	border-bottom-width: 1px;
+	border-bottom-style: solid;
+}
+/* Table opt-in classes: strokes between each row, and alternating row stripes */
+/* Classes table-stroke and table-stripe are deprecated in 1.4. */
+.table-stroke thead th,
+.table-stripe thead th,
+.table-stripe tbody tr:last-child {
+	border-bottom: 1px solid #d6d6d6; /* non-RGBA fallback */
+	border-bottom: 1px solid rgba(0,0,0,.1);
+}
+.table-stroke tbody th,
+.table-stroke tbody td {
+	border-bottom: 1px solid #e6e6e6; /* non-RGBA fallback  */
+	border-bottom: 1px solid rgba(0,0,0,.05);
+}
+.table-stripe.table-stroke tbody tr:last-child th,
+.table-stripe.table-stroke tbody tr:last-child td {
+	border-bottom: 0;
+}
+.table-stripe tbody tr:nth-child(odd) td,
+.table-stripe tbody tr:nth-child(odd) th {
+	background-color: #eeeeee; /* non-RGBA fallback  */
+	background-color: rgba(0,0,0,.04);
+}
+/* Buttons
+-----------------------------------------------------------------------------------------------------------*/
+.ui-btn,
+label.ui-btn {
+	font-weight: bold;
+	border-width: 1px;
+	border-style: solid;
+}
+.ui-btn:link {
+	text-decoration: none !important;
+}
+.ui-btn-active {
+	cursor: pointer;
+}
+/* Corner rounding
+-----------------------------------------------------------------------------------------------------------*/
+/* Class ui-btn-corner-all deprecated in 1.4 */
+.ui-corner-all {
+	-webkit-border-radius: 				.3125em /*{global-radii-blocks}*/;
+	border-radius: 						.3125em /*{global-radii-blocks}*/;
+}
+/* Buttons */
+.ui-btn-corner-all,
+.ui-btn.ui-corner-all,
+/* Slider track */
+.ui-slider-track.ui-corner-all,
+/* Flipswitch */
+.ui-flipswitch.ui-corner-all,
+/* Count bubble */
+.ui-li-count {
+	-webkit-border-radius: 				.3125em /*{global-radii-buttons}*/;
+	border-radius: 						.3125em /*{global-radii-buttons}*/;
+}
+/* Icon-only buttons */
+.ui-btn-icon-notext.ui-btn-corner-all,
+.ui-btn-icon-notext.ui-corner-all {
+	-webkit-border-radius: 1em;
+	border-radius: 1em;
+}
+/* Radius clip workaround for cleaning up corner trapping */
+.ui-btn-corner-all,
+.ui-corner-all {
+	-webkit-background-clip: padding;
+	background-clip: padding-box;
+}
+/* Popup arrow */
+.ui-popup.ui-corner-all > .ui-popup-arrow-guide {
+	left: .6em /*{global-radii-blocks}*/;
+	right: .6em /*{global-radii-blocks}*/;
+	top: .6em /*{global-radii-blocks}*/;
+	bottom: .6em /*{global-radii-blocks}*/;
+}
+/* Shadow
+-----------------------------------------------------------------------------------------------------------*/
+.ui-shadow {
+	-webkit-box-shadow: 0 1px 3px /*{global-box-shadow-size}*/ 		rgba(0,0,0,.15) /*{global-box-shadow-color}*/;
+	-moz-box-shadow: 0 1px 3px /*{global-box-shadow-size}*/ 		rgba(0,0,0,.15) /*{global-box-shadow-color}*/;
+	box-shadow: 0 1px 3px /*{global-box-shadow-size}*/ 				rgba(0,0,0,.15) /*{global-box-shadow-color}*/;
+}
+.ui-shadow-inset {
+	-webkit-box-shadow: inset 0 1px 3px /*{global-box-shadow-size}*/ 	rgba(0,0,0,.2) /*{global-box-shadow-color}*/;
+	-moz-box-shadow: inset 0 1px 3px /*{global-box-shadow-size}*/ 		rgba(0,0,0,.2) /*{global-box-shadow-color}*/;
+	box-shadow: inset 0 1px 3px /*{global-box-shadow-size}*/ 	rgba(0,0,0,.2) /*{global-box-shadow-color}*/;
+}
+.ui-overlay-shadow {
+	-webkit-box-shadow: 0 0 12px 		rgba(0,0,0,.6);
+	-moz-box-shadow: 0 0 12px 			rgba(0,0,0,.6);
+	box-shadow: 0 0 12px 				rgba(0,0,0,.6);
+}
+/* Icons
+-----------------------------------------------------------------------------------------------------------*/
+.ui-btn-icon-left:after,
+.ui-btn-icon-right:after,
+.ui-btn-icon-top:after,
+.ui-btn-icon-bottom:after,
+.ui-btn-icon-notext:after {
+	background-color: 					#666 /*{global-icon-color}*/;
+	background-color: 					rgba(0,0,0,.3) /*{global-icon-disc}*/;
+	background-position: center center;
+	background-repeat: no-repeat;
+	-webkit-border-radius: 1em;
+	border-radius: 1em;
+}
+/* Alt icons */
+.ui-alt-icon.ui-btn:after,
+.ui-alt-icon .ui-btn:after,
+html .ui-alt-icon.ui-checkbox-off:after,
+html .ui-alt-icon.ui-radio-off:after,
+html .ui-alt-icon .ui-checkbox-off:after,
+html .ui-alt-icon .ui-radio-off:after {
+	background-color: 					#666 /*{global-icon-color}*/;
+	background-color: 					rgba(0,0,0,.15);
+}
+/* No disc */
+.ui-nodisc-icon.ui-btn:after,
+.ui-nodisc-icon .ui-btn:after {
+	background-color: transparent;
+}
+/* Icon shadow */
+.ui-shadow-icon.ui-btn:after,
+.ui-shadow-icon .ui-btn:after {
+	-webkit-box-shadow: 0 1px 0 			rgba(255,255,255,.3) /*{global-icon-shadow}*/;
+	-moz-box-shadow: 0 1px 0 				rgba(255,255,255,.3) /*{global-icon-shadow}*/;
+	box-shadow: 0 1px 0 					rgba(255,255,255,.3) /*{global-icon-shadow}*/;
+}
+/* Checkbox and radio */
+.ui-btn.ui-checkbox-off:after,
+.ui-btn.ui-checkbox-on:after,
+.ui-btn.ui-radio-off:after,
+.ui-btn.ui-radio-on:after {
+	display: block;
+	width: 18px;
+	height: 18px;
+	margin: -9px 2px 0 2px;
+}
+.ui-checkbox-off:after,
+.ui-btn.ui-radio-off:after {
+	filter: Alpha(Opacity=30);
+	opacity: .3;
+}
+.ui-btn.ui-checkbox-off:after,
+.ui-btn.ui-checkbox-on:after {
+	-webkit-border-radius: .1875em;
+	border-radius: .1875em;
+}
+.ui-radio .ui-btn.ui-radio-on:after {
+	background-image: none;
+	background-color: #fff;
+	width: 8px;
+	height: 8px;
+	border-width: 5px;
+	border-style: solid; 
+}
+.ui-alt-icon.ui-btn.ui-radio-on:after,
+.ui-alt-icon .ui-btn.ui-radio-on:after {
+	background-color: #000;
+}
+/* Loader */
+.ui-icon-loading {
+	background: url(images/ajax-loader.gif);
+	background-size: 2.875em 2.875em;
+}
+/* Swatches */
+/* A
+-----------------------------------------------------------------------------------------------------------*/
+/* Bar: Toolbars, dividers, slider track */
+.ui-bar-a,
+.ui-page-theme-a .ui-bar-inherit,
+html .ui-bar-a .ui-bar-inherit,
+html .ui-body-a .ui-bar-inherit,
+html body .ui-group-theme-a .ui-bar-inherit {
+	background: 			#e9e9e9 /*{a-bar-background-color}*/;
+	border-color:	 		#ddd /*{a-bar-border}*/;
+	color: 					#333 /*{a-bar-color}*/;
+	text-shadow: 0 /*{a-bar-shadow-x}*/ 1px /*{a-bar-shadow-y}*/ 0 /*{a-bar-shadow-radius}*/ 	#eee /*{a-bar-shadow-color}*/;
+	font-weight: bold;
+}
+.ui-bar-a {
+	border-width: 1px;
+	border-style: solid;
+}
+/* Page and overlay */
+.ui-overlay-a,
+.ui-page-theme-a,
+.ui-page-theme-a .ui-panel-wrapper {
+	background: 			#f9f9f9 /*{a-page-background-color}*/;
+	border-color:	 		#bbb /*{a-page-border}*/;
+	color: 					#333 /*{a-page-color}*/;
+	text-shadow: 0 /*{a-page-shadow-x}*/ 1px /*{a-page-shadow-y}*/ 0 /*{a-page-shadow-radius}*/ 	#f3f3f3 /*{a-page-shadow-color}*/;
+}
+/* Body: Read-only lists, text inputs, collapsible content */
+.ui-body-a,
+.ui-page-theme-a .ui-body-inherit,
+html .ui-bar-a .ui-body-inherit,
+html .ui-body-a .ui-body-inherit,
+html body .ui-group-theme-a .ui-body-inherit,
+html .ui-panel-page-container-a {
+	background: 			#fff /*{a-body-background-color}*/;
+	border-color:	 		#ddd /*{a-body-border}*/;
+	color: 					#333 /*{a-body-color}*/;
+	text-shadow: 0 /*{a-body-shadow-x}*/ 1px /*{a-body-shadow-y}*/ 0 /*{a-body-shadow-radius}*/ 	#f3f3f3 /*{a-body-shadow-color}*/;
+}
+.ui-body-a {
+	border-width: 1px;
+	border-style: solid;
+}
+/* Links */
+.ui-page-theme-a a,
+html .ui-bar-a a,
+html .ui-body-a a,
+html body .ui-group-theme-a a {
+	color: #3388cc /*{a-link-color}*/;
+	font-weight: bold;
+}
+.ui-page-theme-a a:visited,
+html .ui-bar-a a:visited,
+html .ui-body-a a:visited,
+html body .ui-group-theme-a a:visited {
+    color: #3388cc /*{a-link-visited}*/;
+}
+.ui-page-theme-a a:hover,
+html .ui-bar-a a:hover,
+html .ui-body-a a:hover,
+html body .ui-group-theme-a a:hover {
+	color: #005599 /*{a-link-hover}*/;
+}
+.ui-page-theme-a a:active,
+html .ui-bar-a a:active,
+html .ui-body-a a:active,
+html body .ui-group-theme-a a:active {
+	color: #005599 /*{a-link-active}*/;
+}
+/* Button up */
+.ui-page-theme-a .ui-btn,
+html .ui-bar-a .ui-btn,
+html .ui-body-a .ui-btn,
+html body .ui-group-theme-a .ui-btn,
+html head + body .ui-btn.ui-btn-a,
+/* Button visited */
+.ui-page-theme-a .ui-btn:visited,
+html .ui-bar-a .ui-btn:visited,
+html .ui-body-a .ui-btn:visited,
+html body .ui-group-theme-a .ui-btn:visited,
+html head + body .ui-btn.ui-btn-a:visited {
+	background: 			#f6f6f6 /*{a-bup-background-color}*/;
+	border-color:	 		#ddd /*{a-bup-border}*/;
+	color: 					#333 /*{a-bup-color}*/;
+	text-shadow: 0 /*{a-bup-shadow-x}*/ 1px /*{a-bup-shadow-y}*/ 0 /*{a-bup-shadow-radius}*/ #f3f3f3 /*{a-bup-shadow-color}*/;
+}
+/* Button hover */
+.ui-page-theme-a .ui-btn:hover,
+html .ui-bar-a .ui-btn:hover,
+html .ui-body-a .ui-btn:hover,
+html body .ui-group-theme-a .ui-btn:hover,
+html head + body .ui-btn.ui-btn-a:hover {
+	background: 			#ededed /*{a-bhover-background-color}*/;
+	border-color:	 		#ddd /*{a-bhover-border}*/;
+	color: 					#333 /*{a-bhover-color}*/;
+	text-shadow: 0 /*{a-bhover-shadow-x}*/ 1px /*{a-bhover-shadow-y}*/ 0 /*{a-bhover-shadow-radius}*/ #f3f3f3 /*{a-bhover-shadow-color}*/;
+}
+/* Button down */
+.ui-page-theme-a .ui-btn:active,
+html .ui-bar-a .ui-btn:active,
+html .ui-body-a .ui-btn:active,
+html body .ui-group-theme-a .ui-btn:active,
+html head + body .ui-btn.ui-btn-a:active {
+	background: 			#e8e8e8 /*{a-bdown-background-color}*/;
+	border-color:	 		#ddd /*{a-bdown-border}*/;
+	color: 					#333 /*{a-bdown-color}*/;
+	text-shadow: 0 /*{a-bdown-shadow-x}*/ 1px /*{a-bdown-shadow-y}*/ 0 /*{a-bdown-shadow-radius}*/ #f3f3f3 /*{a-bdown-shadow-color}*/;
+}
+/* Active button */
+.ui-page-theme-a .ui-btn.ui-btn-active,
+html .ui-bar-a .ui-btn.ui-btn-active,
+html .ui-body-a .ui-btn.ui-btn-active,
+html body .ui-group-theme-a .ui-btn.ui-btn-active,
+html head + body .ui-btn.ui-btn-a.ui-btn-active,
+/* Active checkbox icon */
+.ui-page-theme-a .ui-checkbox-on:after,
+html .ui-bar-a .ui-checkbox-on:after,
+html .ui-body-a .ui-checkbox-on:after,
+html body .ui-group-theme-a .ui-checkbox-on:after,
+.ui-btn.ui-checkbox-on.ui-btn-a:after,
+/* Active flipswitch background */
+.ui-page-theme-a .ui-flipswitch-active,
+html .ui-bar-a .ui-flipswitch-active,
+html .ui-body-a .ui-flipswitch-active,
+html body .ui-group-theme-a .ui-flipswitch-active,
+html body .ui-flipswitch.ui-bar-a.ui-flipswitch-active,
+/* Active slider track */
+.ui-page-theme-a .ui-slider-track .ui-btn-active,
+html .ui-bar-a .ui-slider-track .ui-btn-active,
+html .ui-body-a .ui-slider-track .ui-btn-active,
+html body .ui-group-theme-a .ui-slider-track .ui-btn-active,
+html body div.ui-slider-track.ui-body-a .ui-btn-active {
+	background-color: 		#3388cc /*{a-active-background-color}*/;
+	border-color:	 		#3388cc /*{a-active-border}*/;
+	color: 					#fff /*{a-active-color}*/;
+	text-shadow: 0 /*{a-active-shadow-x}*/ 1px /*{a-active-shadow-y}*/ 0 /*{a-active-shadow-radius}*/ #005599 /*{a-active-shadow-color}*/;
+}
+/* Active radio button icon */
+.ui-page-theme-a .ui-radio-on:after,
+html .ui-bar-a .ui-radio-on:after,
+html .ui-body-a .ui-radio-on:after,
+html body .ui-group-theme-a .ui-radio-on:after,
+.ui-btn.ui-radio-on.ui-btn-a:after {
+	border-color:			#3388cc /*{a-active-background-color}*/;
+}
+/* Focus */
+.ui-page-theme-a .ui-btn:focus,
+html .ui-bar-a .ui-btn:focus,
+html .ui-body-a .ui-btn:focus,
+html body .ui-group-theme-a .ui-btn:focus,
+html head + body .ui-btn.ui-btn-a:focus,
+/* Focus buttons and text inputs with div wrap */
+.ui-page-theme-a .ui-focus,
+html .ui-bar-a .ui-focus,
+html .ui-body-a .ui-focus,
+html body .ui-group-theme-a .ui-focus,
+html head + body .ui-btn-a.ui-focus,
+html head + body .ui-body-a.ui-focus {
+	-webkit-box-shadow: 0 0 12px 	#3388cc /*{a-active-background-color}*/;
+	-moz-box-shadow: 0 0 12px 		#3388cc /*{a-active-background-color}*/;
+	box-shadow: 0 0 12px 			#3388cc /*{a-active-background-color}*/;
+}
+/* B
+-----------------------------------------------------------------------------------------------------------*/
+/* Bar: Toolbars, dividers, slider track */
+.ui-bar-b,
+.ui-page-theme-b .ui-bar-inherit,
+html .ui-bar-b .ui-bar-inherit,
+html .ui-body-b .ui-bar-inherit,
+html body .ui-group-theme-b .ui-bar-inherit {
+	background: 			#1d1d1d /*{b-bar-background-color}*/;
+	border-color:	 		#1b1b1b /*{b-bar-border}*/;
+	color: 					#fff /*{b-bar-color}*/;
+	text-shadow: 0 /*{b-bar-shadow-x}*/ 1px /*{b-bar-shadow-y}*/ 0 /*{b-bar-shadow-radius}*/ 	#111 /*{b-bar-shadow-color}*/;
+	font-weight: bold;
+}
+.ui-bar-b {
+	border-width: 1px;
+	border-style: solid;
+}
+/* Page and overlay */
+.ui-overlay-b,
+.ui-page-theme-b,
+.ui-page-theme-b .ui-panel-wrapper {
+	background: 			#252525 /*{b-page-background-color}*/;
+	border-color:	 		#454545 /*{b-page-border}*/;
+	color: 					#fff /*{b-page-color}*/;
+	text-shadow: 0 /*{b-page-shadow-x}*/ 1px /*{b-page-shadow-y}*/ 0 /*{b-page-shadow-radius}*/ 	#111 /*{b-page-shadow-color}*/;
+}
+/* Body: Read-only lists, text inputs, collapsible content */
+.ui-body-b,
+.ui-page-theme-b .ui-body-inherit,
+html .ui-bar-b .ui-body-inherit,
+html .ui-body-b .ui-body-inherit,
+html body .ui-group-theme-b .ui-body-inherit,
+html .ui-panel-page-container-b {
+	background: 			#2a2a2a /*{b-body-background-color}*/;
+	border-color:	 		#1d1d1d /*{b-body-border}*/;
+	color: 					#fff /*{b-body-color}*/;
+	text-shadow: 0 /*{b-body-shadow-x}*/ 1px /*{b-body-shadow-y}*/ 0 /*{b-body-shadow-radius}*/ 	#111 /*{b-body-shadow-color}*/;
+}
+.ui-body-b {
+	border-width: 1px;
+	border-style: solid;
+}
+/* Links */
+.ui-page-theme-b a,
+html .ui-bar-b a,
+html .ui-body-b a,
+html body .ui-group-theme-b a {
+	color: #22aadd /*{b-link-color}*/;
+	font-weight: bold;
+}
+.ui-page-theme-b a:visited,
+html .ui-bar-b a:visited,
+html .ui-body-b a:visited,
+html body .ui-group-theme-b a:visited {
+    color: #22aadd /*{b-link-visited}*/;
+}
+.ui-page-theme-b a:hover,
+html .ui-bar-b a:hover,
+html .ui-body-b a:hover,
+html body .ui-group-theme-b a:hover {
+	color: #0088bb /*{b-link-hover}*/;
+}
+.ui-page-theme-b a:active,
+html .ui-bar-b a:active,
+html .ui-body-b a:active,
+html body .ui-group-theme-b a:active {
+	color: #0088bb /*{b-link-active}*/;
+}
+/* Button up */
+.ui-page-theme-b .ui-btn,
+html .ui-bar-b .ui-btn,
+html .ui-body-b .ui-btn,
+html body .ui-group-theme-b .ui-btn,
+html head + body .ui-btn.ui-btn-b,
+/* Button visited */
+.ui-page-theme-b .ui-btn:visited,
+html .ui-bar-b .ui-btn:visited,
+html .ui-body-b .ui-btn:visited,
+html body .ui-group-theme-b .ui-btn:visited,
+html head + body .ui-btn.ui-btn-b:visited {
+	background: 			#333 /*{b-bup-background-color}*/;
+	border-color:	 		#1f1f1f /*{b-bup-border}*/;
+	color: 					#fff /*{b-bup-color}*/;
+	text-shadow: 0 /*{b-bup-shadow-x}*/ 1px /*{b-bup-shadow-y}*/ 0 /*{b-bup-shadow-radius}*/ #111 /*{b-bup-shadow-color}*/;
+}
+/* Button hover */
+.ui-page-theme-b .ui-btn:hover,
+html .ui-bar-b .ui-btn:hover,
+html .ui-body-b .ui-btn:hover,
+html body .ui-group-theme-b .ui-btn:hover,
+html head + body .ui-btn.ui-btn-b:hover {
+	background: 			#373737 /*{b-bhover-background-color}*/;
+	border-color:	 		#1f1f1f /*{b-bhover-border}*/;
+	color: 					#fff /*{b-bhover-color}*/;
+	text-shadow: 0 /*{b-bhover-shadow-x}*/ 1px /*{b-bhover-shadow-y}*/ 0 /*{b-bhover-shadow-radius}*/ #111 /*{b-bhover-shadow-color}*/;
+}
+/* Button down */
+.ui-page-theme-b .ui-btn:active,
+html .ui-bar-b .ui-btn:active,
+html .ui-body-b .ui-btn:active,
+html body .ui-group-theme-b .ui-btn:active,
+html head + body .ui-btn.ui-btn-b:active {
+	background: 			#404040 /*{b-bdown-background-color}*/;
+	border-color:	 		#1f1f1f /*{b-bdown-border}*/;
+	color: 					#fff /*{b-bdown-color}*/;
+	text-shadow: 0 /*{b-bdown-shadow-x}*/ 1px /*{b-bdown-shadow-y}*/ 0 /*{b-bdown-shadow-radius}*/ #111 /*{b-bdown-shadow-color}*/;
+}
+/* Active button */
+.ui-page-theme-b .ui-btn.ui-btn-active,
+html .ui-bar-b .ui-btn.ui-btn-active,
+html .ui-body-b .ui-btn.ui-btn-active,
+html body .ui-group-theme-b .ui-btn.ui-btn-active,
+html head + body .ui-btn.ui-btn-b.ui-btn-active,
+/* Active checkbox icon */
+.ui-page-theme-b .ui-checkbox-on:after,
+html .ui-bar-b .ui-checkbox-on:after,
+html .ui-body-b .ui-checkbox-on:after,
+html body .ui-group-theme-b .ui-checkbox-on:after,
+.ui-btn.ui-checkbox-on.ui-btn-b:after,
+/* Active flipswitch background */
+.ui-page-theme-b .ui-flipswitch-active,
+html .ui-bar-b .ui-flipswitch-active,
+html .ui-body-b .ui-flipswitch-active,
+html body .ui-group-theme-b .ui-flipswitch-active,
+html body .ui-flipswitch.ui-bar-b.ui-flipswitch-active,
+/* Active slider track */
+.ui-page-theme-b .ui-slider-track .ui-btn-active,
+html .ui-bar-b .ui-slider-track .ui-btn-active,
+html .ui-body-b .ui-slider-track .ui-btn-active,
+html body .ui-group-theme-b .ui-slider-track .ui-btn-active,
+html body div.ui-slider-track.ui-body-b .ui-btn-active {
+	background-color: 		#22aadd /*{b-active-background-color}*/;
+	border-color:	 		#22aadd /*{b-active-border}*/;
+	color: 					#fff /*{b-active-color}*/;
+	text-shadow: 0 /*{b-active-shadow-x}*/ 1px /*{b-active-shadow-y}*/ 0 /*{b-active-shadow-radius}*/ #0088bb /*{b-active-shadow-color}*/;
+}
+/* Active radio button icon */
+.ui-page-theme-b .ui-radio-on:after,
+html .ui-bar-b .ui-radio-on:after,
+html .ui-body-b .ui-radio-on:after,
+html body .ui-group-theme-b .ui-radio-on:after,
+.ui-btn.ui-radio-on.ui-btn-b:after {
+	border-color:			#22aadd /*{b-active-background-color}*/;
+}
+/* Focus */
+.ui-page-theme-b .ui-btn:focus,
+html .ui-bar-b .ui-btn:focus,
+html .ui-body-b .ui-btn:focus,
+html body .ui-group-theme-b .ui-btn:focus,
+html head + body .ui-btn.ui-btn-b:focus,
+/* Focus buttons and text inputs with div wrap */
+.ui-page-theme-b .ui-focus,
+html .ui-bar-b .ui-focus,
+html .ui-body-b .ui-focus,
+html body .ui-group-theme-b .ui-focus,
+html head + body .ui-btn-b.ui-focus,
+html head + body .ui-body-b.ui-focus {
+	-webkit-box-shadow: 0 0 12px 	#22aadd /*{b-active-background-color}*/;
+	-moz-box-shadow: 0 0 12px 		#22aadd /*{b-active-background-color}*/;
+	box-shadow: 0 0 12px 			#22aadd /*{b-active-background-color}*/;
+}
+/* Structure */
+/* Disabled
+-----------------------------------------------------------------------------------------------------------*/
+/* Class ui-disabled deprecated in 1.4. :disabled not supported by IE8 so we use [disabled] */
+.ui-disabled,
+.ui-state-disabled,
+button[disabled],
+.ui-select .ui-btn.ui-state-disabled {
+	filter: Alpha(Opacity=30);
+	opacity: .3;
+	cursor: default !important;
+	pointer-events: none;
+}
+/* Focus state outline
+-----------------------------------------------------------------------------------------------------------*/
+.ui-btn:focus,
+.ui-btn.ui-focus {
+	outline: 0;
+}
+/* Unset box-shadow in browsers that don't do it right */
+.ui-noboxshadow .ui-shadow,
+.ui-noboxshadow .ui-shadow-inset,
+.ui-noboxshadow .ui-overlay-shadow,
+.ui-noboxshadow .ui-shadow-icon.ui-btn:after,
+.ui-noboxshadow .ui-shadow-icon .ui-btn:after,
+.ui-noboxshadow .ui-focus,
+.ui-noboxshadow .ui-btn:focus,
+.ui-noboxshadow  input:focus,
+.ui-noboxshadow .ui-panel {
+	-webkit-box-shadow: none !important;
+	-moz-box-shadow: none !important;
+	box-shadow: none !important;
+}
+.ui-noboxshadow .ui-btn:focus,
+.ui-noboxshadow .ui-focus {
+	outline-width: 1px;
+	outline-style: auto;
+}
+/* Some unsets */
+.ui-mobile,
+.ui-mobile body {
+	height: 99.9%;
+}
+.ui-mobile fieldset,
+.ui-page {
+	padding: 0;
+	margin: 0;
+}
+.ui-mobile a img,
+.ui-mobile fieldset {
+	border-width: 0;
+}
+/* Fixes for fieldset issues on IE10 and FF (see #6077) */
+.ui-mobile fieldset {
+	min-width: 0;
+}
+@-moz-document url-prefix() {
+	.ui-mobile fieldset {
+		display: table-column;
+		vertical-align: middle;
+	}
+}
+/* Viewport */
+.ui-mobile-viewport {
+	margin: 0;
+	overflow-x: visible;
+	-webkit-text-size-adjust: 100%;
+	-ms-text-size-adjust:none;
+	-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+}
+/* Issue #2066 */
+body.ui-mobile-viewport,
+div.ui-mobile-viewport {
+	overflow-x: hidden;
+}
+/* "page" containers - full-screen views, one should always be in view post-pageload */
+.ui-mobile [data-role=page],
+.ui-mobile [data-role=dialog],
+.ui-page {
+	top: 0;
+	left: 0;
+	width: 100%;
+	min-height: 100%;
+	position: absolute;
+	display: none;
+	border: 0;
+}
+/* On ios4, setting focus on the page element causes flashing during transitions when there is an outline, so we turn off outlines */
+.ui-page {
+	outline: none;
+}
+.ui-mobile .ui-page-active {
+	display: block;
+	overflow: visible;
+	overflow-x: hidden;
+}
+@media screen and (orientation: portrait) {
+	.ui-mobile .ui-page {
+		min-height: 420px;
+	}
+}
+@media screen and (orientation: landscape) {
+	.ui-mobile .ui-page {
+		min-height: 300px;
+	}
+}
+/* Fouc */
+.ui-mobile-rendering > * {
+	visibility: hidden;
+}
+/* Non-js content hiding */
+.ui-nojs {
+	position: absolute !important;
+	height: 1px;
+	width: 1px;
+	overflow: hidden;
+	clip: rect(1px,1px,1px,1px);
+}
+/* Loading screen */
+.ui-loading .ui-loader {
+	display: block;
+}
+.ui-loader {
+	display: none;
+	z-index: 9999999;
+	position: fixed;
+	top: 50%;
+	left: 50%;
+	border:0;
+}
+.ui-loader-default {
+	background: none;
+	filter: Alpha(Opacity=18);
+	opacity: .18;
+	width: 2.875em;
+	height: 2.875em;
+	margin-left: -1.4375em;
+	margin-top: -1.4375em;
+}
+.ui-loader-verbose {
+	width: 12.5em;
+	filter: Alpha(Opacity=88);
+	opacity: .88;
+	box-shadow: 0 1px 1px -1px #fff;
+	height: auto;
+	margin-left: -6.875em;
+	margin-top: -2.6875em;
+	padding: .625em;
+}
+.ui-loader-default h1 {
+	font-size: 0;
+	width: 0;
+	height: 0;
+	overflow: hidden;
+}
+.ui-loader-verbose h1 {
+	font-size: 1em;
+	margin: 0;
+	text-align: center;
+}
+.ui-loader .ui-icon-loading {
+	background-color: #000;
+	display: block;
+	margin: 0;
+	width: 2.75em;
+	height: 2.75em;
+	padding: .0625em;
+	-webkit-border-radius: 2.25em;
+	border-radius: 2.25em;
+}
+.ui-loader-verbose .ui-icon-loading {
+	margin: 0 auto .625em;
+	filter: Alpha(Opacity=75);
+	opacity: .75;
+}
+.ui-loader-textonly {
+	padding: .9375em;
+	margin-left: -7.1875em;
+}
+.ui-loader-textonly .ui-icon-loading {
+	display: none;
+}
+.ui-loader-fakefix {
+	position: absolute;
+}
+/* Headers, content panels */
+.ui-bar,
+.ui-body {
+	position: relative;
+	padding: .4em 1em;
+	overflow: hidden;
+	display: block;
+	clear: both;
+}
+.ui-bar h1,
+.ui-bar h2,
+.ui-bar h3,
+.ui-bar h4,
+.ui-bar h5,
+.ui-bar h6 {
+	margin: 0;
+	padding: 0;
+	font-size: 1em;
+	display: inline-block;
+}
+.ui-header,
+.ui-footer {
+	border-width: 1px 0;
+	border-style: solid;
+	position: relative;
+}
+.ui-header:empty,
+.ui-footer:empty {
+	min-height: 2.6875em;
+}
+.ui-header .ui-title,
+.ui-footer .ui-title {
+	font-size: 1em;
+	min-height: 1.1em;
+	text-align: center;
+	display: block;
+	margin: 0 30%;
+	padding: .7em 0;
+	text-overflow: ellipsis;
+	overflow: hidden;
+	white-space: nowrap;
+	outline: 0 !important;
+}
+.ui-footer .ui-title {
+	margin: 0 1em;
+}
+.ui-content {
+	border-width: 0;
+	overflow: visible;
+	overflow-x: hidden;
+	padding: 1em;
+}
+/* Corner styling for dialogs and popups */
+.ui-corner-all > .ui-header:first-child,
+.ui-corner-all > .ui-content:first-child,
+.ui-corner-all > .ui-footer:first-child {
+	-webkit-border-top-left-radius: inherit;
+	border-top-left-radius: inherit;
+	-webkit-border-top-right-radius: inherit;
+	border-top-right-radius: inherit;
+}
+.ui-corner-all > .ui-header:last-child,
+.ui-corner-all > .ui-content:last-child,
+.ui-corner-all > .ui-footer:last-child {
+	-webkit-border-bottom-left-radius: inherit;
+	border-bottom-left-radius: inherit;
+	-webkit-border-bottom-right-radius: inherit;
+	border-bottom-right-radius: inherit;
+}
+/* Buttons and icons */
+.ui-btn {
+	font-size: 16px;
+	margin: .5em 0;
+	padding: .7em 1em;
+	display: block;
+	position: relative;
+	text-align: center;
+	text-overflow: ellipsis;
+	overflow: hidden;
+	white-space: nowrap;
+	cursor: pointer;
+	-webkit-user-select: none;
+	-moz-user-select: none;
+	-ms-user-select: none;
+	user-select: none;
+}
+.ui-btn-icon-notext {
+	padding: 0;
+	width: 1.75em;
+	height: 1.75em;
+	text-indent: -9999px;
+	white-space: nowrap !important;
+}
+.ui-mini {
+	font-size: 12.5px;
+}
+.ui-mini .ui-btn {
+	font-size: inherit;
+}
+/* Make buttons in toolbars default to mini and inline. */
+.ui-header .ui-btn,
+.ui-footer .ui-btn {
+	font-size: 12.5px;
+	display: inline-block;
+	vertical-align: middle;
+}
+/* To ensure same top and left/right position when ui-btn-left/right are added to something other than buttons. */
+.ui-header .ui-btn-left,
+.ui-header .ui-btn-right {
+	font-size: 12.5px;
+}
+.ui-mini.ui-btn-icon-notext,
+.ui-mini .ui-btn-icon-notext,
+.ui-header .ui-btn-icon-notext,
+.ui-footer .ui-btn-icon-notext {
+	font-size: 16px;	
+	padding: 0;
+}
+.ui-btn-inline {
+	display: inline-block;
+	vertical-align: middle;
+	margin-right: .625em;
+}
+.ui-btn-icon-left {
+	padding-left: 2.5em;
+}
+.ui-btn-icon-right {
+	padding-right: 2.5em;
+}
+.ui-btn-icon-top {
+	padding-top: 2.5em;
+}
+.ui-btn-icon-bottom {
+	padding-bottom: 2.5em;
+}
+.ui-header .ui-btn-icon-top,
+.ui-footer .ui-btn-icon-top,
+.ui-header .ui-btn-icon-bottom,
+.ui-footer .ui-btn-icon-bottom {
+	padding-left: .3125em;
+	padding-right: .3125em;
+}
+.ui-btn-icon-left:after,
+.ui-btn-icon-right:after,
+.ui-btn-icon-top:after,
+.ui-btn-icon-bottom:after,
+.ui-btn-icon-notext:after {
+	content: "";
+	position: absolute;
+	display: block;
+	width: 22px;
+	height: 22px;
+}
+.ui-btn-icon-notext:after,
+.ui-btn-icon-left:after,
+.ui-btn-icon-right:after {
+	top: 50%;
+	margin-top: -11px;
+}
+.ui-btn-icon-left:after {
+	left: .5625em;
+}
+.ui-btn-icon-right:after {
+	right: .5625em;
+}
+.ui-mini.ui-btn-icon-left:after,
+.ui-mini .ui-btn-icon-left:after,
+.ui-header .ui-btn-icon-left:after,
+.ui-footer .ui-btn-icon-left:after {
+	left: .37em;
+}
+.ui-mini.ui-btn-icon-right:after,
+.ui-mini .ui-btn-icon-right:after,
+.ui-header .ui-btn-icon-right:after,
+.ui-footer .ui-btn-icon-right:after {
+	right: .37em;
+}
+.ui-btn-icon-notext:after,
+.ui-btn-icon-top:after,
+.ui-btn-icon-bottom:after {
+	left: 50%;
+	margin-left: -11px;
+}
+.ui-btn-icon-top:after {
+	top: .5625em;
+}
+.ui-btn-icon-bottom:after {
+	top: auto;
+	bottom: .5625em;
+}
+/* Buttons in header position classes */
+.ui-header .ui-btn-left,
+.ui-header .ui-btn-right,
+.ui-btn-left > [class*="ui-"],
+.ui-btn-right > [class*="ui-"] {
+	margin: 0;
+}
+.ui-btn-left,
+.ui-btn-right {
+	position: absolute;
+	top: .24em;
+}
+.ui-btn-left {
+	left: .4em;
+}
+.ui-btn-right {
+	right: .4em;
+}
+.ui-btn-icon-notext.ui-btn-left {
+	top: .3125em;
+	left: .3125em;
+}
+.ui-btn-icon-notext.ui-btn-right {
+	top: .3125em;
+	right: .3125em;
+}
+/* Button elements */
+button.ui-btn,
+.ui-controlgroup-controls button.ui-btn-icon-notext {
+	-webkit-box-sizing: border-box;
+	-moz-box-sizing: border-box;
+	box-sizing: border-box;
+	-webkit-appearance: none;
+	-moz-appearance: none;
+	width: 100%;
+}
+button.ui-btn-inline {
+	width: auto;
+}
+/* Firefox adds a 1px border in a button element. We negate this to make sure they have the same height as other buttons in controlgroups. */
+button.ui-btn::-moz-focus-inner {
+	border: 0;
+}
+button.ui-btn-icon-notext,
+.ui-controlgroup-horizontal .ui-controlgroup-controls button.ui-btn {
+	-webkit-box-sizing: content-box;
+	-moz-box-sizing: content-box;
+	box-sizing: content-box;
+	width: 1.75em;
+}
+/* Form labels */
+.ui-mobile label,
+.ui-controlgroup-label {
+	display: block;
+	margin: 0 0 .4em;
+}
+/* Accessible content hiding */
+/* ui-hide-label deprecated in 1.4. TODO: Remove in 1.5 */
+.ui-hide-label > label,
+.ui-hide-label .ui-controlgroup-label,
+.ui-hide-label .ui-rangeslider label,
+.ui-hidden-accessible {
+	position: absolute !important;
+	height: 1px;
+	width: 1px;
+	overflow: hidden;
+	clip: rect(1px,1px,1px,1px);
+}
+/* Used for hiding elements by the filterable widget. You can also use this class to hide list items or buttons in controlgroups; this ensures correct corner styling. */
+.ui-screen-hidden {
+	display: none !important;
+}
+/* Transitions originally inspired by those from jQtouch, nice work, folks */
+.ui-mobile-viewport-transitioning,
+.ui-mobile-viewport-transitioning .ui-page {
+	width: 100%;
+	height: 100%;
+	overflow: hidden;
+	-webkit-box-sizing: border-box;
+	-moz-box-sizing: border-box;
+	box-sizing: border-box;
+}
+.ui-page-pre-in {
+	opacity: 0;
+}
+.in {
+	-webkit-animation-timing-function: ease-out;
+	-webkit-animation-duration: 350ms;
+	-moz-animation-timing-function: ease-out;
+	-moz-animation-duration: 350ms;
+	animation-timing-function: ease-out;
+	animation-duration: 350ms;
+}
+.out {
+	-webkit-animation-timing-function: ease-in;
+	-webkit-animation-duration: 225ms;
+	-moz-animation-timing-function: ease-in;
+	-moz-animation-duration: 225ms;
+	animation-timing-function: ease-in;
+	animation-duration: 225ms;
+}
+@-webkit-keyframes fadein {
+    from { opacity: 0; }
+    to { opacity: 1; }
+}
+@-moz-keyframes fadein {
+    from { opacity: 0; }
+    to { opacity: 1; }
+}
+@keyframes fadein {
+    from { opacity: 0; }
+    to { opacity: 1; }
+}
+@-webkit-keyframes fadeout {
+    from { opacity: 1; }
+    to { opacity: 0; }
+}
+@-moz-keyframes fadeout {
+    from { opacity: 1; }
+    to { opacity: 0; }
+}
+@keyframes fadeout {
+    from { opacity: 1; }
+    to { opacity: 0; }
+}
+.fade.out {
+	opacity: 0;
+	-webkit-animation-duration: 125ms;
+	-webkit-animation-name: fadeout;
+	-moz-animation-duration: 125ms;
+	-moz-animation-name: fadeout;
+	animation-duration: 125ms;
+	animation-name: fadeout;
+}
+.fade.in {
+	opacity: 1;
+	-webkit-animation-duration: 225ms;
+	-webkit-animation-name: fadein;
+	-moz-animation-duration: 225ms;
+	-moz-animation-name: fadein;
+	animation-duration: 225ms;
+	animation-name: fadein;
+}
+.pop {
+	-webkit-transform-origin: 50% 50%;
+	-moz-transform-origin: 50% 50%;
+	transform-origin: 50% 50%;
+}
+.pop.in {
+	-webkit-transform: scale(1);
+	-webkit-animation-name: popin;
+	-webkit-animation-duration: 350ms;
+	-moz-transform: scale(1);
+	-moz-animation-name: popin;
+	-moz-animation-duration: 350ms;
+	transform: scale(1);
+	animation-name: popin;
+	animation-duration: 350ms;
+    opacity: 1;
+}
+.pop.out {
+	-webkit-animation-name: fadeout;
+	-webkit-animation-duration: 100ms;
+	-moz-animation-name: fadeout;
+	-moz-animation-duration: 100ms;
+	animation-name: fadeout;
+	animation-duration: 100ms;
+	opacity: 0;
+}
+.pop.in.reverse {
+	-webkit-animation-name: fadein;
+	-moz-animation-name: fadein;
+	animation-name: fadein;
+}
+.pop.out.reverse {
+	-webkit-transform: scale(.8);
+	-webkit-animation-name: popout;
+	-moz-transform: scale(.8);
+	-moz-animation-name: popout;
+	transform: scale(.8);
+	animation-name: popout;
+}
+@-webkit-keyframes popin {
+    from {
+        -webkit-transform: scale(.8);
+        opacity: 0;
+    }
+    to {
+        -webkit-transform: scale(1);
+        opacity: 1;
+    }
+}
+@-moz-keyframes popin {
+    from {
+        -moz-transform: scale(.8);
+        opacity: 0;
+    }
+    to {
+        -moz-transform: scale(1);
+        opacity: 1;
+    }
+}
+@keyframes popin {
+    from {
+        transform: scale(.8);
+        opacity: 0;
+    }
+    to {
+        transform: scale(1);
+        opacity: 1;
+    }
+}
+@-webkit-keyframes popout {
+    from {
+        -webkit-transform: scale(1);
+        opacity: 1;
+    }
+    to {
+        -webkit-transform: scale(.8);
+        opacity: 0;
+    }
+}
+@-moz-keyframes popout {
+    from {
+        -moz-transform: scale(1);
+        opacity: 1;
+    }
+    to {
+        -moz-transform: scale(.8);
+        opacity: 0;
+    }
+}
+@keyframes popout {
+    from {
+        transform: scale(1);
+        opacity: 1;
+    }
+    to {
+        transform: scale(.8);
+        opacity: 0;
+    }
+}
+/* keyframes for slidein from sides */
+@-webkit-keyframes slideinfromright {
+    from { -webkit-transform: translate3d(100%,0,0); }
+    to { -webkit-transform: translate3d(0,0,0); }
+}
+@-moz-keyframes slideinfromright {
+    from { -moz-transform: translateX(100%); }
+    to { -moz-transform: translateX(0); }
+}
+@keyframes slideinfromright {
+    from { transform: translateX(100%); }
+    to { transform: translateX(0); }
+}
+@-webkit-keyframes slideinfromleft {
+    from { -webkit-transform: translate3d(-100%,0,0); }
+    to { -webkit-transform: translate3d(0,0,0); }
+}
+@-moz-keyframes slideinfromleft {
+    from { -moz-transform: translateX(-100%); }
+    to { -moz-transform: translateX(0); }
+}
+@keyframes slideinfromleft {
+    from { transform: translateX(-100%); }
+    to { transform: translateX(0); }
+}
+/* keyframes for slideout to sides */
+@-webkit-keyframes slideouttoleft {
+    from { -webkit-transform: translate3d(0,0,0); }
+    to { -webkit-transform: translate3d(-100%,0,0); }
+}
+@-moz-keyframes slideouttoleft {
+    from { -moz-transform: translateX(0); }
+    to { -moz-transform: translateX(-100%); }
+}
+@keyframes slideouttoleft {
+    from { transform: translateX(0); }
+    to { transform: translateX(-100%); }
+}
+@-webkit-keyframes slideouttoright {
+    from { -webkit-transform: translate3d(0,0,0); }
+    to { -webkit-transform: translate3d(100%,0,0); }
+}
+@-moz-keyframes slideouttoright {
+    from { -moz-transform: translateX(0); }
+    to { -moz-transform: translateX(100%); }
+}
+@keyframes slideouttoright {
+    from { transform: translateX(0); }
+    to { transform: translateX(100%); }
+}
+.slide.out, .slide.in {
+	-webkit-animation-timing-function: ease-out;
+	-webkit-animation-duration: 350ms;
+	-moz-animation-timing-function: ease-out;
+	-moz-animation-duration: 350ms;
+	animation-timing-function: ease-out;
+	animation-duration: 350ms;
+}
+.slide.out {
+	-webkit-transform: translate3d(-100%,0,0);
+	-webkit-animation-name: slideouttoleft;
+	-moz-transform: translateX(-100%);
+	-moz-animation-name: slideouttoleft;
+	transform: translateX(-100%);
+	animation-name: slideouttoleft;
+}
+.slide.in {
+	-webkit-transform: translate3d(0,0,0);
+	-webkit-animation-name: slideinfromright;
+	-moz-transform: translateX(0);
+	-moz-animation-name: slideinfromright;
+	transform: translateX(0);
+	animation-name: slideinfromright;
+}
+.slide.out.reverse {
+	-webkit-transform: translate3d(100%,0,0);
+	-webkit-animation-name: slideouttoright;
+	-moz-transform: translateX(100%);
+	-moz-animation-name: slideouttoright;
+	transform: translateX(100%);
+	animation-name: slideouttoright;
+}
+.slide.in.reverse {
+	-webkit-transform: translate3d(0,0,0);
+	-webkit-animation-name: slideinfromleft;
+	-moz-transform: translateX(0);
+	-moz-animation-name: slideinfromleft;
+	transform: translateX(0);
+	animation-name: slideinfromleft;
+}
+.slidefade.out {
+	-webkit-transform: translateX(-100%);
+	-webkit-animation-name: slideouttoleft;
+	-webkit-animation-duration: 225ms;
+	-moz-transform: translateX(-100%);
+	-moz-animation-name: slideouttoleft;
+	-moz-animation-duration: 225ms;
+	transform: translateX(-100%);
+	animation-name: slideouttoleft;
+	animation-duration: 225ms;
+}
+.slidefade.in {
+	-webkit-transform: translateX(0);
+	-webkit-animation-name: fadein;
+	-webkit-animation-duration: 200ms;
+	-moz-transform: translateX(0);
+	-moz-animation-name: fadein;
+	-moz-animation-duration: 200ms;
+	transform: translateX(0);
+	animation-name: fadein;
+	animation-duration: 200ms;
+}
+.slidefade.out.reverse {
+	-webkit-transform: translateX(100%);
+	-webkit-animation-name: slideouttoright;
+	-webkit-animation-duration: 200ms;
+	-moz-transform: translateX(100%);
+	-moz-animation-name: slideouttoright;
+	-moz-animation-duration: 200ms;
+	transform: translateX(100%);
+	animation-name: slideouttoright;
+	animation-duration: 200ms;
+}
+.slidefade.in.reverse {
+	-webkit-transform: translateX(0);
+	-webkit-animation-name: fadein;
+	-webkit-animation-duration: 200ms;
+	-moz-transform: translateX(0);
+	-moz-animation-name: fadein;
+	-moz-animation-duration: 200ms;
+	transform: translateX(0);
+	animation-name: fadein;
+	animation-duration: 200ms;
+}
+/* slide down */
+.slidedown.out {
+	-webkit-animation-name: fadeout;
+	-webkit-animation-duration: 100ms;
+	-moz-animation-name: fadeout;
+	-moz-animation-duration: 100ms;
+	animation-name: fadeout;
+	animation-duration: 100ms;
+}
+.slidedown.in {
+	-webkit-transform: translateY(0);
+	-webkit-animation-name: slideinfromtop;
+	-webkit-animation-duration: 250ms;
+	-moz-transform: translateY(0);
+	-moz-animation-name: slideinfromtop;
+	-moz-animation-duration: 250ms;
+	transform: translateY(0);
+	animation-name: slideinfromtop;
+	animation-duration: 250ms;
+}
+.slidedown.in.reverse {
+	-webkit-animation-name: fadein;
+	-webkit-animation-duration: 150ms;
+	-moz-animation-name: fadein;
+	-moz-animation-duration: 150ms;
+	animation-name: fadein;
+	animation-duration: 150ms;
+}
+.slidedown.out.reverse {
+	-webkit-transform: translateY(-100%);
+	-webkit-animation-name: slideouttotop;
+	-webkit-animation-duration: 200ms;
+	-moz-transform: translateY(-100%);
+	-moz-animation-name: slideouttotop;
+	-moz-animation-duration: 200ms;
+	transform: translateY(-100%);
+	animation-name: slideouttotop;
+	animation-duration: 200ms;
+}
+@-webkit-keyframes slideinfromtop {
+    from { -webkit-transform: translateY(-100%); }
+    to { -webkit-transform: translateY(0); }
+}
+@-moz-keyframes slideinfromtop {
+    from { -moz-transform: translateY(-100%); }
+    to { -moz-transform: translateY(0); }
+}
+@keyframes slideinfromtop {
+    from { transform: translateY(-100%); }
+    to { transform: translateY(0); }
+}
+@-webkit-keyframes slideouttotop {
+    from { -webkit-transform: translateY(0); }
+    to { -webkit-transform: translateY(-100%); }
+}
+@-moz-keyframes slideouttotop {
+    from { -moz-transform: translateY(0); }
+    to { -moz-transform: translateY(-100%); }
+}
+@keyframes slideouttotop {
+    from { transform: translateY(0); }
+    to { transform: translateY(-100%); }
+}
+/* slide up */
+.slideup.out {
+	-webkit-animation-name: fadeout;
+	-webkit-animation-duration: 100ms;
+	-moz-animation-name: fadeout;
+	-moz-animation-duration: 100ms;
+	animation-name: fadeout;
+	animation-duration: 100ms;
+}
+.slideup.in {
+	-webkit-transform: translateY(0);
+	-webkit-animation-name: slideinfrombottom;
+	-webkit-animation-duration: 250ms;
+	-moz-transform: translateY(0);
+	-moz-animation-name: slideinfrombottom;
+	-moz-animation-duration: 250ms;
+	transform: translateY(0);
+	animation-name: slideinfrombottom;
+	animation-duration: 250ms;
+}
+.slideup.in.reverse {
+	-webkit-animation-name: fadein;
+	-webkit-animation-duration: 150ms;
+	-moz-animation-name: fadein;
+	-moz-animation-duration: 150ms;
+	animation-name: fadein;
+	animation-duration: 150ms;
+}
+.slideup.out.reverse {
+	-webkit-transform: translateY(100%);
+	-webkit-animation-name: slideouttobottom;
+	-webkit-animation-duration: 200ms;
+	-moz-transform: translateY(100%);
+	-moz-animation-name: slideouttobottom;
+	-moz-animation-duration: 200ms;
+	transform: translateY(100%);
+	animation-name: slideouttobottom;
+	animation-duration: 200ms;
+}
+@-webkit-keyframes slideinfrombottom {
+    from { -webkit-transform: translateY(100%); }
+    to { -webkit-transform: translateY(0); }
+}
+@-moz-keyframes slideinfrombottom {
+    from { -moz-transform: translateY(100%); }
+    to { -moz-transform: translateY(0); }
+}
+@keyframes slideinfrombottom {
+    from { transform: translateY(100%); }
+    to { transform: translateY(0); }
+}
+@-webkit-keyframes slideouttobottom {
+    from { -webkit-transform: translateY(0); }
+    to { -webkit-transform: translateY(100%); }
+}
+@-moz-keyframes slideouttobottom {
+    from { -moz-transform: translateY(0); }
+    to { -moz-transform: translateY(100%); }
+}
+@keyframes slideouttobottom {
+    from { transform: translateY(0); }
+    to { transform: translateY(100%); }
+}
+/* The properties in this rule are only necessary for the 'flip' transition.
+ * We need specify the perspective to create a projection matrix. This will add
+ * some depth as the element flips. The depth number represents the distance of
+ * the viewer from the z-plane. According to the CSS3 spec, 1000 is a moderate
+ * value.
+ */
+.viewport-flip {
+	-webkit-perspective: 1000;
+	-moz-perspective: 1000;
+	perspective: 1000;
+	position: absolute;
+}
+.flip {
+	-webkit-backface-visibility: hidden;
+	-webkit-transform: translateX(0); /* Needed to work around an iOS 3.1 bug that causes listview thumbs to disappear when -webkit-visibility:hidden is used. */
+	-moz-backface-visibility: hidden;
+	-moz-transform: translateX(0);
+	backface-visibility: hidden;
+	transform: translateX(0);
+}
+.flip.out {
+	-webkit-transform: rotateY(-90deg) scale(.9);
+	-webkit-animation-name: flipouttoleft;
+	-webkit-animation-duration: 175ms;
+	-moz-transform: rotateY(-90deg) scale(.9);
+	-moz-animation-name: flipouttoleft;
+	-moz-animation-duration: 175ms;
+	transform: rotateY(-90deg) scale(.9);
+	animation-name: flipouttoleft;
+	animation-duration: 175ms;
+}
+.flip.in {
+	-webkit-animation-name: flipintoright;
+	-webkit-animation-duration: 225ms;
+	-moz-animation-name: flipintoright;
+	-moz-animation-duration: 225ms;
+	animation-name: flipintoright;
+	animation-duration: 225ms;
+}
+.flip.out.reverse {
+	-webkit-transform: rotateY(90deg) scale(.9);
+	-webkit-animation-name: flipouttoright;
+	-moz-transform: rotateY(90deg) scale(.9);
+	-moz-animation-name: flipouttoright;
+	transform: rotateY(90deg) scale(.9);
+	animation-name: flipouttoright;
+}
+.flip.in.reverse {
+	-webkit-animation-name: flipintoleft;
+	-moz-animation-name: flipintoleft;
+	animation-name: flipintoleft;
+}
+@-webkit-keyframes flipouttoleft {
+    from { -webkit-transform: rotateY(0); }
+    to { -webkit-transform: rotateY(-90deg) scale(.9); }
+}
+@-moz-keyframes flipouttoleft {
+    from { -moz-transform: rotateY(0); }
+    to { -moz-transform: rotateY(-90deg) scale(.9); }
+}
+@keyframes flipouttoleft {
+    from { transform: rotateY(0); }
+    to { transform: rotateY(-90deg) scale(.9); }
+}
+@-webkit-keyframes flipouttoright {
+    from { -webkit-transform: rotateY(0) ; }
+    to { -webkit-transform: rotateY(90deg) scale(.9); }
+}
+@-moz-keyframes flipouttoright {
+    from { -moz-transform: rotateY(0); }
+    to { -moz-transform: rotateY(90deg) scale(.9); }
+}
+@keyframes flipouttoright {
+    from { transform: rotateY(0); }
+    to { transform: rotateY(90deg) scale(.9); }
+}
+@-webkit-keyframes flipintoleft {
+    from { -webkit-transform: rotateY(-90deg) scale(.9); }
+    to { -webkit-transform: rotateY(0); }
+}
+@-moz-keyframes flipintoleft {
+    from { -moz-transform: rotateY(-90deg) scale(.9); }
+    to { -moz-transform: rotateY(0); }
+}
+@keyframes flipintoleft {
+    from { transform: rotateY(-90deg) scale(.9); }
+    to { transform: rotateY(0); }
+}
+@-webkit-keyframes flipintoright {
+    from { -webkit-transform: rotateY(90deg) scale(.9); }
+    to { -webkit-transform: rotateY(0); }
+}
+@-moz-keyframes flipintoright {
+    from { -moz-transform: rotateY(90deg) scale(.9); }
+    to { -moz-transform: rotateY(0); }
+}
+@keyframes flipintoright {
+    from { transform: rotateY(90deg) scale(.9); }
+    to { transform: rotateY(0); }
+}
+/* The properties in this rule are only necessary for the 'flip' transition.
+ * We need specify the perspective to create a projection matrix. This will add
+ * some depth as the element flips. The depth number represents the distance of
+ * the viewer from the z-plane. According to the CSS3 spec, 1000 is a moderate
+ * value.
+ */
+.viewport-turn {
+	-webkit-perspective: 200px;
+	-moz-perspective: 200px;
+	-ms-perspective: 200px;
+	perspective: 200px;
+	position: absolute;
+}
+.turn {
+	-webkit-backface-visibility: hidden;
+	-webkit-transform: translateX(0); /* Needed to work around an iOS 3.1 bug that causes listview thumbs to disappear when -webkit-visibility:hidden is used. */
+	-webkit-transform-origin: 0;
+	
+	-moz-backface-visibility: hidden;
+	-moz-transform: translateX(0);
+	-moz-transform-origin: 0;
+	
+	backface-visibility :hidden;
+	transform: translateX(0);
+	transform-origin: 0;
+}
+.turn.out {
+	-webkit-transform: rotateY(-90deg) scale(.9);
+	-webkit-animation-name: flipouttoleft;
+	-webkit-animation-duration: 125ms;
+	-moz-transform: rotateY(-90deg) scale(.9);
+	-moz-animation-name: flipouttoleft;
+	-moz-animation-duration: 125ms;
+	transform: rotateY(-90deg) scale(.9);
+	animation-name: flipouttoleft;
+	animation-duration: 125ms;
+}
+.turn.in {
+	-webkit-animation-name: flipintoright;
+	-webkit-animation-duration: 250ms;
+	-moz-animation-name: flipintoright;
+	-moz-animation-duration: 250ms;
+	animation-name: flipintoright;
+	animation-duration: 250ms;
+	
+}
+.turn.out.reverse {
+	-webkit-transform: rotateY(90deg) scale(.9);
+	-webkit-animation-name: flipouttoright;
+	-moz-transform: rotateY(90deg) scale(.9);
+	-moz-animation-name: flipouttoright;
+	transform: rotateY(90deg) scale(.9);
+	animation-name: flipouttoright;
+}
+.turn.in.reverse {
+	-webkit-animation-name: flipintoleft;
+	-moz-animation-name: flipintoleft;
+	animation-name: flipintoleft;
+}
+@-webkit-keyframes flipouttoleft {
+    from { -webkit-transform: rotateY(0); }
+    to { -webkit-transform: rotateY(-90deg) scale(.9); }
+}
+@-moz-keyframes flipouttoleft {
+    from { -moz-transform: rotateY(0); }
+    to { -moz-transform: rotateY(-90deg) scale(.9); }
+}
+@keyframes flipouttoleft {
+    from { transform: rotateY(0); }
+    to { transform: rotateY(-90deg) scale(.9); }
+}
+@-webkit-keyframes flipouttoright {
+    from { -webkit-transform: rotateY(0) ; }
+    to { -webkit-transform: rotateY(90deg) scale(.9); }
+}
+@-moz-keyframes flipouttoright {
+    from { -moz-transform: rotateY(0); }
+    to { -moz-transform: rotateY(90deg) scale(.9); }
+}
+@keyframes flipouttoright {
+    from { transform: rotateY(0); }
+    to { transform: rotateY(90deg) scale(.9); }
+}
+@-webkit-keyframes flipintoleft {
+    from { -webkit-transform: rotateY(-90deg) scale(.9); }
+    to { -webkit-transform: rotateY(0); }
+}
+@-moz-keyframes flipintoleft {
+    from { -moz-transform: rotateY(-90deg) scale(.9); }
+    to { -moz-transform: rotateY(0); }
+}
+@keyframes flipintoleft {
+    from { transform: rotateY(-90deg) scale(.9); }
+    to { transform: rotateY(0); }
+}
+@-webkit-keyframes flipintoright {
+    from { -webkit-transform: rotateY(90deg) scale(.9); }
+    to { -webkit-transform: rotateY(0); }
+}
+@-moz-keyframes flipintoright {
+    from { -moz-transform: rotateY(90deg) scale(.9); }
+    to { -moz-transform: rotateY(0); }
+}
+@keyframes flipintoright {
+    from { transform: rotateY(90deg) scale(.9); }
+    to { transform: rotateY(0); }
+}
+/* flow transition */
+.flow {
+	-webkit-transform-origin: 50% 30%;
+	-webkit-box-shadow: 0 0 20px rgba(0,0,0,.4);
+	-moz-transform-origin: 50% 30%;	
+	-moz-box-shadow: 0 0 20px rgba(0,0,0,.4);
+	transform-origin: 50% 30%;	
+	box-shadow: 0 0 20px rgba(0,0,0,.4);
+}
+.ui-dialog.flow {
+	-webkit-transform-origin: none;
+	-webkit-box-shadow: none;
+	-moz-transform-origin: none;	
+	-moz-box-shadow: none;
+	transform-origin: none;	
+	box-shadow: none;
+}
+.flow.out {
+	-webkit-transform: translateX(-100%) scale(.7);
+	-webkit-animation-name: flowouttoleft;
+	-webkit-animation-timing-function: ease;
+	-webkit-animation-duration: 350ms;
+	-moz-transform: translateX(-100%) scale(.7);
+	-moz-animation-name: flowouttoleft;
+	-moz-animation-timing-function: ease;
+	-moz-animation-duration: 350ms;
+	transform: translateX(-100%) scale(.7);
+	animation-name: flowouttoleft;
+	animation-timing-function: ease;
+	animation-duration: 350ms;
+}
+.flow.in {
+	-webkit-transform: translateX(0) scale(1);
+	-webkit-animation-name: flowinfromright;
+	-webkit-animation-timing-function: ease;
+	-webkit-animation-duration: 350ms;
+	-moz-transform: translateX(0) scale(1);
+	-moz-animation-name: flowinfromright;
+	-moz-animation-timing-function: ease;
+	-moz-animation-duration: 350ms;
+	transform: translateX(0) scale(1);
+	animation-name: flowinfromright;
+	animation-timing-function: ease;
+	animation-duration: 350ms;
+}
+.flow.out.reverse {
+	-webkit-transform: translateX(100%);
+	-webkit-animation-name: flowouttoright;
+	-moz-transform: translateX(100%);
+	-moz-animation-name: flowouttoright;
+	transform: translateX(100%);
+	animation-name: flowouttoright;
+}
+.flow.in.reverse {
+	-webkit-animation-name: flowinfromleft;
+	-moz-animation-name: flowinfromleft;
+	animation-name: flowinfromleft;
+}
+@-webkit-keyframes flowouttoleft {
+    0% { -webkit-transform: translateX(0) scale(1); }
+	60%, 70% { -webkit-transform: translateX(0) scale(.7); }
+    100% { -webkit-transform: translateX(-100%) scale(.7); }
+}
+@-moz-keyframes flowouttoleft {
+    0% { -moz-transform: translateX(0) scale(1); }
+	60%, 70% { -moz-transform: translateX(0) scale(.7); }
+    100% { -moz-transform:  translateX(-100%) scale(.7); }
+}
+@keyframes flowouttoleft {
+    0% { transform: translateX(0) scale(1); }
+	60%, 70% { transform: translateX(0) scale(.7); }
+    100% { transform:  translateX(-100%) scale(.7); }
+}
+@-webkit-keyframes flowouttoright {
+    0% { -webkit-transform: translateX(0) scale(1); }
+	60%, 70% { -webkit-transform: translateX(0) scale(.7); }
+    100% { -webkit-transform:  translateX(100%) scale(.7); }
+}
+@-moz-keyframes flowouttoright {
+    0% { -moz-transform: translateX(0) scale(1); }
+	60%, 70% { -moz-transform: translateX(0) scale(.7); }
+    100% { -moz-transform:  translateX(100%) scale(.7); }
+}
+@keyframes flowouttoright {
+    0% { transform: translateX(0) scale(1); }
+	60%, 70% { transform: translateX(0) scale(.7); }
+    100% { transform:  translateX(100%) scale(.7); }
+}
+@-webkit-keyframes flowinfromleft {
+    0% { -webkit-transform: translateX(-100%) scale(.7); }
+	30%, 40% { -webkit-transform: translateX(0) scale(.7); }
+    100% { -webkit-transform: translateX(0) scale(1); }
+}
+@-moz-keyframes flowinfromleft {
+    0% { -moz-transform: translateX(-100%) scale(.7); }
+	30%, 40% { -moz-transform: translateX(0) scale(.7); }
+    100% { -moz-transform: translateX(0) scale(1); }
+}
+@keyframes flowinfromleft {
+    0% { transform: translateX(-100%) scale(.7); }
+	30%, 40% { transform: translateX(0) scale(.7); }
+    100% { transform: translateX(0) scale(1); }
+}
+@-webkit-keyframes flowinfromright {
+    0% { -webkit-transform: translateX(100%) scale(.7); }
+	30%, 40% { -webkit-transform: translateX(0) scale(.7); }
+    100% { -webkit-transform: translateX(0) scale(1); }
+}
+@-moz-keyframes flowinfromright {
+    0% { -moz-transform: translateX(100%) scale(.7); }
+	30%, 40% { -moz-transform: translateX(0) scale(.7); }
+    100% { -moz-transform: translateX(0) scale(1); }
+}
+@keyframes flowinfromright {
+    0% { transform: translateX(100%) scale(.7); }
+	30%, 40% { transform: translateX(0) scale(.7); }
+    100% { transform: translateX(0) scale(1); }
+}
+.ui-field-contain,
+.ui-mobile fieldset.ui-field-contain {
+	display: block;
+	position: relative;
+	overflow: visible;
+	clear: both;
+	padding: .8em 0;
+}
+.ui-field-contain > label + [class*="ui-"],
+.ui-field-contain .ui-controlgroup-controls {
+	margin: 0;
+}
+.ui-field-contain:last-child {
+	border-bottom-width: 0;
+}
+@media (min-width: 28em) {
+	.ui-field-contain,
+	.ui-mobile fieldset.ui-field-contain {
+		padding: 0;
+		margin: 1em 0;
+		border-bottom-width: 0;
+	}
+	.ui-field-contain:before,
+	.ui-field-contain:after {
+		content: "";
+		display: table;
+	}
+	.ui-field-contain:after {
+		clear: both;
+	}
+	.ui-field-contain > label,
+	.ui-field-contain .ui-controlgroup-label,
+	.ui-field-contain > .ui-rangeslider > label {
+		float: left;
+		width: 20%;
+		margin: .5em 2% 0 0;
+	}
+	.ui-popup .ui-field-contain > label,
+	.ui-popup .ui-field-contain .ui-controlgroup-label,
+	.ui-popup .ui-field-contain > .ui-rangeslider > label {
+		float: none;
+		width: auto;
+		margin: 0 0 .4em;
+	}
+	.ui-field-contain > label + [class*="ui-"],
+	.ui-field-contain .ui-controlgroup-controls {
+		float: left;
+		width: 78%;
+		-webkit-box-sizing: border-box;
+		-moz-box-sizing: border-box;
+		-ms-box-sizing: border-box;
+		box-sizing: border-box;
+	}
+	/* ui-hide-label deprecated in 1.4. TODO: Remove in 1.5 */
+	.ui-hide-label > label + [class*="ui-"],
+	.ui-hide-label .ui-controlgroup-controls,
+	.ui-popup .ui-field-contain > label + [class*="ui-"],
+	.ui-popup .ui-field-contain .ui-controlgroup-controls {
+		float: none;
+		width: 100%;
+	}
+	.ui-field-contain > label + .ui-btn-inline {
+		width: auto;
+		margin-right: .625em;
+	}
+}
+/* content configurations. */
+.ui-grid-a,
+.ui-grid-b,
+.ui-grid-c,
+.ui-grid-d,
+.ui-grid-solo {
+	overflow: hidden;
+}
+.ui-block-a,
+.ui-block-b,
+.ui-block-c,
+.ui-block-d,
+.ui-block-e {
+	margin: 0;
+	padding: 0;
+	border: 0;
+	float: left;
+	min-height: 1px;
+	-webkit-box-sizing: border-box;
+	-moz-box-sizing: border-box;
+	box-sizing: border-box;
+}
+/* force new row */
+.ui-block-a {
+	clear: left;
+}
+ul.ui-grid-a,
+ul.ui-grid-b,
+ul.ui-grid-c,
+ul.ui-grid-d,
+ul.ui-grid-solo,
+li.ui-block-a,
+li.ui-block-b,
+li.ui-block-c,
+li.ui-block-d,
+li.ui-block-e {
+	margin-left: 0;
+	margin-right: 0;
+	padding: 0;
+	list-style: none;
+}
+/* No margin in grids for 100% width button elements until we can use max-width: fill-available; */
+[class*="ui-block-"] > button.ui-btn,
+.ui-grid-solo > button.ui-btn {
+	margin-right: 0;
+	margin-left: 0;
+}
+[class*="ui-block-"] > .ui-btn,
+[class*="ui-block-"] > .ui-select,
+[class*="ui-block-"] > .ui-checkbox,
+[class*="ui-block-"] > .ui-radio,
+[class*="ui-block-"] > button.ui-btn-inline,
+[class*="ui-block-"] > button.ui-btn-icon-notext,
+.ui-grid-solo > [class*="ui-"] {
+	margin-right: .3125em;
+	margin-left: .3125em;
+}
+.ui-grid-a > .ui-block-a,
+.ui-grid-a > .ui-block-b {
+	/* width: 49.95%; IE7 */
+	/* margin-right: -.5px; BB5 */
+	width: 50%;
+}
+.ui-grid-b > .ui-block-a,
+.ui-grid-b > .ui-block-b,
+.ui-grid-b > .ui-block-c {
+	/* width: 33.25%; IE7 */
+	/* margin-right: -.5px; BB5 */
+	width: 33.333%;
+}
+.ui-grid-c > .ui-block-a,
+.ui-grid-c > .ui-block-b,
+.ui-grid-c > .ui-block-c,
+.ui-grid-c > .ui-block-d {
+	/* width: 24.925%; IE7 */
+	/* margin-right: -.5px; BB5 */
+	width: 25%;
+}
+.ui-grid-d > .ui-block-a,
+.ui-grid-d > .ui-block-b,
+.ui-grid-d > .ui-block-c,
+.ui-grid-d > .ui-block-d,
+.ui-grid-d > .ui-block-e {
+	/* width: 19.925%; IE7 */
+	width: 20%;
+}
+.ui-grid-solo > .ui-block-a {
+	width: 100%;
+	float: none; 
+}
+/* preset breakpoint to switch to stacked grid styles below 35em (560px) */
+@media (max-width: 35em) {
+	.ui-responsive > .ui-block-a,
+	.ui-responsive > .ui-block-b,
+	.ui-responsive > .ui-block-c,
+	.ui-responsive > .ui-block-d,
+	.ui-responsive > .ui-block-e {
+		width: 100%; 
+		float: none; 
+	}
+}
+/* fixed page header & footer configuration */
+.ui-header-fixed,
+.ui-footer-fixed {
+	left: 0;
+	right: 0;
+	width: 100%;
+	position: fixed;
+	z-index: 1000;
+}
+.ui-header-fixed {
+	top: -1px;
+	padding-top: 1px;
+}
+.ui-header-fixed.ui-fixed-hidden {
+	top: 0;
+	padding-top: 0;
+}
+.ui-header-fixed .ui-btn-left,
+.ui-header-fixed .ui-btn-right {
+	margin-top: 1px;
+}
+.ui-header-fixed.ui-fixed-hidden .ui-btn-left,
+.ui-header-fixed.ui-fixed-hidden .ui-btn-right {
+	margin-top: 0;
+}
+.ui-footer-fixed {
+	bottom: -1px;
+	padding-bottom: 1px;
+}
+.ui-footer-fixed.ui-fixed-hidden {
+	bottom: 0;
+	padding-bottom: 0;
+}
+.ui-header-fullscreen,
+.ui-footer-fullscreen {
+	filter: Alpha(Opacity=90);
+	opacity: .9;
+}
+/* updatePagePadding() will update the padding to actual height of header and footer. */
+.ui-page-header-fixed {
+	padding-top: 2.8125em;
+}
+.ui-page-footer-fixed {
+	padding-bottom: 2.8125em;
+}
+.ui-page-header-fullscreen > .ui-content,
+.ui-page-footer-fullscreen > .ui-content {
+	padding: 0;
+}
+.ui-fixed-hidden {
+	position: absolute;
+}
+/* Tap toggle: hide external fixed footer. See issue #6604 */
+.ui-footer-fixed.ui-fixed-hidden {
+  display: none;
+}
+.ui-page .ui-footer-fixed.ui-fixed-hidden {
+  display: block
+}
+.ui-page-header-fullscreen .ui-fixed-hidden,
+.ui-page-footer-fullscreen .ui-fixed-hidden {
+	position: absolute !important;
+	height: 1px;
+	width: 1px;
+	overflow: hidden;
+	clip: rect(1px,1px,1px,1px);
+}
+.ui-header-fixed .ui-btn,
+.ui-footer-fixed .ui-btn { 
+	z-index: 10;
+}
+/* workarounds for other widgets */
+.ui-android-2x-fixed .ui-li-has-thumb {
+	-webkit-transform: translate3d(0,0,0);
+}
+.ui-navbar {
+	max-width: 100%;
+}
+.ui-navbar ul:before,
+.ui-navbar ul:after {
+	content: "";
+	display: table;
+}
+.ui-navbar ul:after {
+	clear: both;
+}
+.ui-navbar ul {
+	list-style: none;
+	margin: 0;
+	padding: 0;
+	position: relative;
+	display: block;
+	border: 0;
+	max-width: 100%;
+	overflow: visible;
+}
+.ui-navbar li .ui-btn {
+	font-size: 12.5px;
+	display: block;
+	margin: 0;
+	border-right-width: 0;
+}
+.ui-navbar .ui-btn:focus {
+	z-index: 1;
+}
+/* fixes gaps caused by subpixel problem */
+.ui-navbar li:last-child .ui-btn {
+	margin-right: -4px;
+}
+.ui-navbar li:last-child .ui-btn:after {
+	margin-right: 4px;
+}
+.ui-content .ui-navbar li:last-child .ui-btn,
+.ui-content .ui-navbar .ui-grid-duo .ui-block-b .ui-btn {
+	border-right-width: 1px;
+	margin-right: 0;
+}
+.ui-content .ui-navbar li:last-child .ui-btn:after,
+.ui-content .ui-navbar .ui-grid-duo .ui-block-b .ui-btn:after {
+	margin-right: 0;
+}
+.ui-navbar .ui-grid-duo .ui-block-a:last-child .ui-btn {
+	border-right-width: 1px;
+	margin-right: -1px;
+}
+.ui-navbar .ui-grid-duo .ui-block-a:last-child .ui-btn:after {
+	margin-right: 1px;
+}
+.ui-navbar .ui-grid-duo .ui-btn {
+	border-top-width: 0;
+}
+.ui-navbar .ui-grid-duo .ui-block-a:first-child .ui-btn,
+.ui-navbar .ui-grid-duo .ui-block-a:first-child + .ui-block-b .ui-btn {
+	border-top-width: 1px;
+}
+.ui-header .ui-navbar .ui-btn,
+.ui-footer .ui-navbar .ui-btn {
+	border-top-width: 0;
+	border-bottom-width: 0;
+}
+.ui-header .ui-navbar .ui-grid-duo .ui-block-a:first-child .ui-btn,
+.ui-footer .ui-navbar .ui-grid-duo .ui-block-a:first-child .ui-btn,
+.ui-header .ui-navbar .ui-grid-duo .ui-block-a:first-child + .ui-block-b .ui-btn,
+.ui-footer .ui-navbar .ui-grid-duo .ui-block-a:first-child + .ui-block-b .ui-btn {
+	border-top-width: 0;
+}
+.ui-header .ui-title ~ .ui-navbar .ui-btn,
+.ui-footer .ui-title ~ .ui-navbar .ui-btn,
+.ui-header .ui-navbar .ui-grid-duo .ui-btn,
+.ui-footer .ui-navbar .ui-grid-duo .ui-btn,
+.ui-header .ui-title ~ .ui-navbar .ui-grid-duo .ui-block-a:first-child .ui-btn,
+.ui-footer .ui-title ~ .ui-navbar .ui-grid-duo .ui-block-a:first-child .ui-btn,
+.ui-header .ui-title ~ .ui-navbar .ui-grid-duo .ui-block-a:first-child + .ui-block-b .ui-btn,
+.ui-footer .ui-title ~ .ui-navbar .ui-grid-duo .ui-block-a:first-child + .ui-block-b .ui-btn {
+	border-top-width: 1px;
+}
+/* Hide the native input element */
+.ui-input-btn input {
+	position: absolute;
+	top: 0;
+	left: 0;
+	width: 100%;
+	height: 100%;
+	padding: 0;
+	border: 0;
+	outline: 0;
+	-webkit-border-radius: inherit;
+	border-bottom-radius: inherit;	
+	-webkit-appearance: none;
+	-moz-appearance: none;
+	cursor: pointer;
+	background: #fff;
+	background: rgba(255,255,255,0);
+	filter: Alpha(Opacity=0);
+	opacity: .1;
+	font-size: 1px;
+	text-indent: -9999px;
+	z-index: 2;
+}
+/* Fixes IE/WP filter alpha opacity bugs */
+.ui-input-btn.ui-state-disabled input {
+	position: absolute !important;
+	height: 1px;
+	width: 1px;
+	overflow: hidden;
+	clip: rect(1px,1px,1px,1px);
+}
+.ui-collapsible {
+	margin: 0 -1em;
+}
+.ui-collapsible-inset,
+.ui-collapsible-set {
+	margin: .5em 0;
+}
+.ui-collapsible-heading {
+	display: block;
+	margin: 0;
+	padding: 0;
+	position: relative;
+}
+.ui-collapsible-heading .ui-btn {
+	text-align: left;
+	margin: 0;
+	border-left-width: 0;
+	border-right-width: 0;
+}
+.ui-collapsible-heading .ui-btn-icon-top,
+.ui-collapsible-heading .ui-btn-icon-bottom {
+	text-align: center;
+}
+.ui-collapsible-inset .ui-collapsible-heading .ui-btn {
+	border-right-width: 1px;
+	border-left-width: 1px;
+}
+.ui-collapsible-collapsed + .ui-collapsible:not(.ui-collapsible-inset) > .ui-collapsible-heading .ui-btn {
+	border-top-width: 0;
+}
+.ui-collapsible-set .ui-collapsible:not(.ui-collapsible-inset) .ui-collapsible-heading .ui-btn {
+	border-top-width: 1px;
+}
+.ui-collapsible-heading-status {
+	position: absolute !important;
+	height: 1px;
+	width: 1px;
+	overflow: hidden;
+	clip: rect(1px,1px,1px,1px);
+}
+.ui-collapsible-content {
+	display: block;
+	margin: 0;	
+	padding: .5em 1em;
+}
+.ui-collapsible-themed-content .ui-collapsible-content {
+	border-left-width: 0;
+	border-right-width: 0;
+	border-top-width: 0;
+	border-bottom-width: 1px;
+	border-style: solid;
+}
+.ui-collapsible-inset.ui-collapsible-themed-content .ui-collapsible-content {
+	border-left-width: 1px;
+	border-right-width: 1px;
+}
+.ui-collapsible-inset .ui-collapsible-content {
+	margin: 0;
+}
+.ui-collapsible-content-collapsed {
+	display: none;
+}
+.ui-collapsible-set > .ui-collapsible.ui-corner-all {
+	-webkit-border-radius: 0;
+	border-radius: 0;
+}
+.ui-collapsible-heading,
+.ui-collapsible-heading > .ui-btn {
+	-webkit-border-radius: inherit;	
+	border-radius: inherit;	
+}
+.ui-collapsible-set .ui-collapsible.ui-first-child {
+	-webkit-border-top-right-radius: inherit;	
+	border-top-right-radius: inherit;
+	-webkit-border-top-left-radius: inherit;	
+	border-top-left-radius: inherit;		
+}
+.ui-collapsible-content,
+.ui-collapsible-set .ui-collapsible.ui-last-child {
+	-webkit-border-bottom-right-radius: inherit;	
+	border-bottom-right-radius: inherit;
+	-webkit-border-bottom-left-radius: inherit;	
+	border-bottom-left-radius: inherit;		
+}
+.ui-collapsible-themed-content:not(.ui-collapsible-collapsed) > .ui-collapsible-heading {
+	-webkit-border-bottom-right-radius: 0;	
+	border-bottom-right-radius: 0;
+	-webkit-border-bottom-left-radius: 0;	
+	border-bottom-left-radius: 0;		
+}
+.ui-collapsible-set .ui-collapsible {
+	margin: -1px -1em 0;
+}
+.ui-collapsible-set .ui-collapsible-inset {
+	margin: -1px 0 0;
+}
+.ui-collapsible-set .ui-collapsible.ui-first-child {
+	margin-top: 0;
+}
+.ui-controlgroup,
+fieldset.ui-controlgroup {
+	padding: 0;
+	margin: .5em 0;
+}
+.ui-field-contain .ui-controlgroup,
+.ui-field-contain fieldset.ui-controlgroup {
+	margin: 0;
+}
+.ui-mini .ui-controlgroup-label {
+	font-size: 16px;
+}
+.ui-controlgroup.ui-mini .ui-btn-icon-notext,
+.ui-controlgroup .ui-mini.ui-btn-icon-notext {
+	font-size: inherit;
+}
+.ui-controlgroup-controls .ui-btn,
+.ui-controlgroup-controls .ui-checkbox,
+.ui-controlgroup-controls .ui-radio,
+.ui-controlgroup-controls .ui-select {
+	margin: 0;
+}
+.ui-controlgroup-controls .ui-btn:focus,
+.ui-controlgroup-controls .ui-btn.ui-focus {
+	z-index: 1;
+}
+.ui-controlgroup-controls li {
+	list-style: none;
+}
+.ui-controlgroup-horizontal .ui-controlgroup-controls {
+	display: inline-block;
+	vertical-align: middle;
+}
+.ui-controlgroup-horizontal .ui-controlgroup-controls:before,
+.ui-controlgroup-horizontal .ui-controlgroup-controls:after {
+	content: "";
+	display: table;
+}
+.ui-controlgroup-horizontal .ui-controlgroup-controls:after {
+	clear: both;
+}
+.ui-controlgroup-horizontal .ui-controlgroup-controls > .ui-btn,
+.ui-controlgroup-horizontal .ui-controlgroup-controls li > .ui-btn,
+.ui-controlgroup-horizontal .ui-controlgroup-controls .ui-checkbox,
+.ui-controlgroup-horizontal .ui-controlgroup-controls .ui-radio,
+.ui-controlgroup-horizontal .ui-controlgroup-controls .ui-select {
+	float: left;
+	clear: none;
+}
+.ui-controlgroup-horizontal .ui-controlgroup-controls button.ui-btn,
+.ui-controlgroup-controls .ui-btn-icon-notext {
+	width: auto;
+}
+.ui-controlgroup-horizontal .ui-controlgroup-controls .ui-btn-icon-notext,
+.ui-controlgroup-horizontal .ui-controlgroup-controls button.ui-btn-icon-notext {
+	width: 1.5em;
+}
+ .ui-controlgroup-controls .ui-btn-icon-notext {
+	height: auto;
+	padding: .7em 1em;
+}
+.ui-controlgroup-vertical .ui-controlgroup-controls .ui-btn {
+	border-bottom-width: 0;
+}
+.ui-controlgroup-vertical .ui-controlgroup-controls .ui-btn.ui-last-child {
+	border-bottom-width: 1px;
+}
+.ui-controlgroup-horizontal .ui-controlgroup-controls .ui-btn {
+	border-right-width: 0;
+}
+.ui-controlgroup-horizontal .ui-controlgroup-controls .ui-btn.ui-last-child {
+	border-right-width: 1px;
+}
+.ui-controlgroup-controls .ui-btn-corner-all,
+.ui-controlgroup-controls .ui-btn.ui-corner-all {
+	-webkit-border-radius: 0;
+	border-radius: 0;
+}
+.ui-controlgroup-controls,
+.ui-controlgroup-controls .ui-radio,
+.ui-controlgroup-controls .ui-checkbox,
+.ui-controlgroup-controls .ui-select,
+.ui-controlgroup-controls li {
+	-webkit-border-radius: inherit;
+	border-radius: inherit;
+}
+.ui-controlgroup-vertical .ui-btn.ui-first-child {
+	-webkit-border-top-left-radius: inherit;
+	border-top-left-radius: inherit;
+	-webkit-border-top-right-radius: inherit;
+	border-top-right-radius: inherit;
+}
+.ui-controlgroup-vertical .ui-btn.ui-last-child {
+	-webkit-border-bottom-left-radius: inherit;
+	border-bottom-left-radius: inherit;
+	-webkit-border-bottom-right-radius: inherit;
+	border-bottom-right-radius: inherit;
+}
+.ui-controlgroup-horizontal .ui-btn.ui-first-child {
+	-webkit-border-top-left-radius: inherit;
+	border-top-left-radius: inherit;
+	-webkit-border-bottom-left-radius: inherit;
+	border-bottom-left-radius: inherit;
+}
+.ui-controlgroup-horizontal .ui-btn.ui-last-child {
+	-webkit-border-top-right-radius: inherit;
+	border-top-right-radius: inherit;
+	-webkit-border-bottom-right-radius: inherit;
+	border-bottom-right-radius: inherit;
+}
+.ui-controlgroup-controls a.ui-shadow:not(:focus),
+.ui-controlgroup-controls button.ui-shadow:not(:focus),
+.ui-controlgroup-controls div.ui-shadow:not(.ui-focus) {
+	-moz-box-shadow: none;
+	-webkit-box-shadow: none;
+	box-shadow: none;
+}
+/* Fixes legend not wrapping on IE10 */
+.ui-controlgroup-label legend {
+	max-width: 100%;
+}
+.ui-controlgroup-controls > label {
+	position: absolute !important;
+	height: 1px;
+	width: 1px;
+	overflow: hidden;
+	clip: rect(1px,1px,1px,1px);
+}
+.ui-dialog {
+	 background: none !important; /* this is to ensure that dialog theming does not apply (by default at least) on the page div */
+}
+.ui-dialog-contain {
+	width: 92.5%;
+	max-width: 500px;
+	margin: 10% auto 1em auto;
+	padding: 0;
+	position: relative;
+	top: -1em;
+}
+.ui-dialog-contain > .ui-header, 
+.ui-dialog-contain > .ui-content, 
+.ui-dialog-contain > .ui-footer { 
+	display: block;
+	position: relative; 
+	width: auto;
+	margin: 0;
+}
+.ui-dialog-contain > .ui-header {
+	overflow: hidden;
+	z-index: 10; 
+	padding: 0;
+	border-top-width: 0;
+}
+.ui-dialog-contain > .ui-footer {
+	z-index: 10; 
+	padding: 0 1em; 
+	border-bottom-width: 0;
+}
+.ui-popup-open .ui-header-fixed,
+.ui-popup-open .ui-footer-fixed {
+	position: absolute !important; /* See issues #4816, #4844 and #4874 and popup.js */
+}
+.ui-popup-screen {
+	background-image: url(data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==); /* Necessary to set some form of background to ensure element is clickable in IE6/7. While legacy IE won't understand the data-URI'd image, it ensures no additional requests occur in all other browsers with little overhead. */
+	top: 0;
+	left: 0;
+	right: 0;
+	bottom: 1px;
+	position: absolute;
+	filter: Alpha(Opacity=0);
+	opacity: 0;
+	z-index: 1099;
+}
+.ui-popup-screen.in {
+	opacity: 0.5;
+	filter: Alpha(Opacity=50);
+}
+.ui-popup-screen.out {
+	opacity: 0;
+	filter: Alpha(Opacity=0);
+}
+.ui-popup-container {
+	z-index: 1100;
+	display: inline-block;
+	position: absolute;
+	padding: 0;
+	outline: 0;
+}
+.ui-popup {
+	position: relative;
+}
+.ui-popup.ui-body-inherit {
+	border-width: 1px;
+	border-style: solid;
+}
+.ui-popup-hidden {
+	left: 0;
+	top: 0;
+	position: absolute !important;
+	visibility: hidden;
+}
+.ui-popup-truncate {
+	height: 1px;
+	width: 1px;
+	margin: -1px;
+	overflow: hidden;
+	clip: rect(1px,1px,1px,1px);
+}
+.ui-popup.ui-content,
+.ui-popup .ui-content {
+	overflow: visible;
+}
+.ui-popup > .ui-header {
+	border-top-width: 0;
+}
+.ui-popup > .ui-footer {
+	border-bottom-width: 0;
+}
+.ui-popup > p,
+.ui-popup > h1,
+.ui-popup > h2,
+.ui-popup > h3,
+.ui-popup > h4,
+.ui-popup > h5,
+.ui-popup > h6 {
+	margin: .5em .4375em;
+}
+.ui-popup > span {
+	display: block;
+	margin: .5em .4375em;
+}
+.ui-popup-container .ui-content > p,
+.ui-popup-container .ui-content > h1,
+.ui-popup-container .ui-content > h2,
+.ui-popup-container .ui-content > h3,
+.ui-popup-container .ui-content > h4,
+.ui-popup-container .ui-content > h5,
+.ui-popup-container .ui-content > h6 {
+	margin: .5em 0;
+}
+.ui-popup-container .ui-content > span {
+	margin: 0;
+}
+.ui-popup-container .ui-content > p:first-child,
+.ui-popup-container .ui-content > h1:first-child,
+.ui-popup-container .ui-content > h2:first-child,
+.ui-popup-container .ui-content > h3:first-child,
+.ui-popup-container .ui-content > h4:first-child,
+.ui-popup-container .ui-content > h5:first-child,
+.ui-popup-container .ui-content > h6:first-child {
+	margin-top: 0;
+}
+.ui-popup-container .ui-content > p:last-child,
+.ui-popup-container .ui-content > h1:last-child,
+.ui-popup-container .ui-content > h2:last-child,
+.ui-popup-container .ui-content > h3:last-child,
+.ui-popup-container .ui-content > h4:last-child,
+.ui-popup-container .ui-content > h5:last-child,
+.ui-popup-container .ui-content > h6:last-child {
+	margin-bottom: 0;
+}
+.ui-popup > img {
+	max-width: 100%;
+	max-height: 100%;
+	vertical-align: middle;
+}
+.ui-popup:not(.ui-content) > img:only-child,
+.ui-popup:not(.ui-content) > .ui-btn-left:first-child + img:last-child,
+.ui-popup:not(.ui-content) > .ui-btn-right:first-child + img:last-child {
+	-webkit-border-radius: inherit;
+	border-radius: inherit;
+}
+.ui-popup iframe {
+	vertical-align: middle;
+}
+.ui-popup > .ui-btn-left,
+.ui-popup > .ui-btn-right {
+	position: absolute; 
+	top: -11px;
+	margin: 0;
+	z-index: 1101;
+}
+.ui-popup > .ui-btn-left {
+	left: -11px;
+}
+.ui-popup > .ui-btn-right {
+	right: -11px;
+}
+/* Dimensions related to the popup arrow
+-----------------------------------------------------------------------------------------------------------*/
+/* desired triangle height: 10px */
+/**
+ * guide for the arrow - its width, height, and offset are theme-dependent and
+ * should be expessed as left, right, top, bottom, so that the element bearing
+ * such a class becomes stretched inside its parent position: relative element.
+ * The left/top/right/bottom specified below should reflect the corresponding
+ * border radii and so it leaves room for the shadow:
+ *     ..--------------------..
+ *   ."        ^ top           ".
+ *  /          v                 \
+ * |     +------------------+     |
+ * |     |                  |     |
+ * | left|                  |right|
+ * |<--->|                  |<--->|
+ * |     +------------------+     |
+ *  \          ^                 /
+ *   `.        v bottom        .'
+ *     ""--------------------""
+ * The idea is that the top/left of the arrow container box does not move to a
+ * coordinate smaller than the top/left of the guide and the right/bottom of
+ * the arrow container box does not move to a coordinate larger than the
+ * bottom/right of the guide. This will help us avoid the following situation:
+ *        ..--------------------..
+ *      ."        ^ top           ".
+ *   /|/          v                 \
+ *  / |     +------------------+     |
+ *  \ |     |                  |     |
+ *   \| left|                  |right|
+ *    |<--->|                  |<--->|
+ *    |     +------------------+     |
+ *     \          ^                 /
+ *      `.        v bottom        .'
+ *        ""--------------------""
+ * The arrow should not receive a top/left coordinate such that it is too close
+ * to one of the corners, because then at first the shadow of the arrow and,
+ * given a coordinate even closer to the corner, even the body of the arrow will
+ * "stick out" of the corner of the popup. The guide provides a hint to the
+ * arrow positioning code as to which range of values is acceptable for the
+ * arrow container's top/left coordinate.
+ **/
+.ui-popup-arrow-container {
+	width: 20px;
+	height: 20px;
+}
+/* aside from the "infinities" (-1000,2000), triangle height is used */
+.ui-popup-arrow-container.ui-popup-arrow-l {
+	left: -10px;
+	clip: rect(-1000px,10px,2000px,-1000px);
+}
+.ui-popup-arrow-container.ui-popup-arrow-t {
+	top: -10px;
+	clip: rect(-1000px,2000px,10px,-1000px);
+}
+.ui-popup-arrow-container.ui-popup-arrow-r {
+	right: -10px;
+	clip: rect(-1000px,2000px,2000px,10px);
+}
+.ui-popup-arrow-container.ui-popup-arrow-b {
+	bottom: -10px;
+	clip: rect(10px,2000px,1000px,-1000px);
+}
+/**
+ * For each side, the arrow is twice the desired size and its corner is aligned
+ * with the edge of the container:
+ *                                           
+ *           /\         /\                +----+	     /\
+ *          /  \       /  \ 					    | /\ |top   /  \
+ *      +----+  \     /  +----+       +-->|/  \|     /    \
+ *  left| /  |   \   /   |  \ |right  |   |    |    /      \
+ *      |/   |    \ /    |   \| 			|  /|    |\  /        \
+ *      |\   |    / \    |   /| 			| / +----+ \ \ +----+ /
+ *      | \  |   /   \   |  / |       | \        /  \|    |/
+ *      +----+  /     \  +----+       |  \      /    |    |
+ *       ^  \  /       \  /  ^        |   \    /  +->|\  /|
+ *       |   \/         \/   |        |    \  /   |  | \/ |bottom
+ *       |                   |        |     \/    |  +----+
+ *       +-------------------+--------+-----------+
+ *                           |
+ *                    arrow container
+ *                     (clips arrow)
+ **/
+.ui-popup-arrow-container .ui-popup-arrow {
+	/* (4*desired triangle height)/sqrt(2) - does not account for border - centred within the outer rectangle */
+	width: 28.284271247px;
+	height: 28.284271247px;
+	border-width: 1px;
+	border-style: solid;
+}
+.ui-popup-arrow-container.ui-popup-arrow-t .ui-popup-arrow {
+	left: -4.142135623px;
+	top: 5.857864376px;
+}
+.ui-popup-arrow-container.ui-popup-arrow-b .ui-popup-arrow {
+	left: -4.142135623px;
+	top: -14.142135623px;
+}
+.ui-popup-arrow-container.ui-popup-arrow-l .ui-popup-arrow {
+	left: 5.857864376px;
+	top: -4.142135623px;
+}
+.ui-popup-arrow-container.ui-popup-arrow-r .ui-popup-arrow {
+	left: -14.142135623px;
+	top: -4.142135623px;
+}
+/* Fix rotation center for oldIE - see http://www.useragentman.com/IETransformsTranslator/ */
+.ui-popup-arrow-container.ui-popup-arrow-t.ie .ui-popup-arrow {
+    margin-left: -5.857864376269049px;
+    margin-top: -7.0710678118654755px;
+}
+.ui-popup-arrow-container.ui-popup-arrow-b.ie .ui-popup-arrow {
+    margin-left: -5.857864376269049px;
+    margin-top: -4.142135623730951px;
+}
+ 
+.ui-popup-arrow-container.ui-popup-arrow-l.ie .ui-popup-arrow {
+    margin-left: -7.0710678118654755px;
+    margin-top: -5.857864376269049px;
+}
+.ui-popup-arrow-container.ui-popup-arrow-r.ie .ui-popup-arrow {
+    margin-left: -4.142135623730951px;
+    margin-top: -5.857864376269049px;
+}
+.ui-popup-arrow-background {
+	/* desired triangle height is used here */
+	width: 20px;
+	height: 20px;
+}
+.ui-popup-arrow-container.ui-popup-arrow-t .ui-popup-arrow-background
+.ui-popup-arrow-container.ui-popup-arrow-b .ui-popup-arrow-background {
+	background-position: 0 10px;
+}
+.ui-popup-arrow-container.ui-popup-arrow-l .ui-popup-arrow-background,
+.ui-popup-arrow-container.ui-popup-arrow-r .ui-popup-arrow-background {
+	background-position: 10px 0;
+}
+/* structure */
+.ui-popup > .ui-popup-arrow-guide {
+	position: absolute;
+	left: 0;
+	right: 0;
+	top: 0;
+	bottom: 0;
+	visibility: hidden;
+}
+.ui-popup-arrow-container {
+	position: absolute;
+}
+.ui-popup-arrow {
+	-webkit-transform: rotate(45deg);
+	-moz-transform: rotate(45deg);
+	-ms-transform: rotate(45deg);
+	transform: rotate(45deg);
+	position: absolute;
+	overflow: hidden;
+	box-sizing: border-box;
+}
+.ui-popup-arrow-container.ie .ui-popup-arrow {
+	-ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.7071067811865474, M12=-0.7071067811865477, M21=0.7071067811865477, M22=0.7071067811865474, SizingMethod='auto expand')";
+	filter: progid:DXImageTransform.Microsoft.Matrix(
+	      	M11=0.7071067811865474,
+        	M12=-0.7071067811865477,
+        	M21=0.7071067811865477,
+        	M22=0.7071067811865474,
+        	SizingMethod='auto expand');
+}
+.ui-popup-arrow-background {
+	position: absolute;
+	border: 0;
+}
+.ui-popup-arrow-container.ie .ui-popup-arrow-background {
+	background: none;
+}
+.ui-popup-arrow-container.ui-popup-arrow-t .ui-popup-arrow-background,
+.ui-popup-arrow-container.ui-popup-arrow-b .ui-popup-arrow-background {
+	/* Undo rotation and reflect in x axis */
+	-webkit-transform: rotate(-45deg) scale(1, -1);
+	-moz-transform: rotate(-45deg) scale(1, -1);
+	-ms-transform: rotate(-45deg) scale(1, -1);
+	transform: rotate(-45deg) scale(1, -1);
+}
+.ui-popup-arrow-container.ui-popup-arrow-t.ie .ui-popup-arrow-background,
+.ui-popup-arrow-container.ui-popup-arrow-b.ie .ui-popup-arrow-background {
+   -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.7071067811865483, M12=-0.7071067811865467, M21=-0.7071067811865467, M22=-0.7071067811865483, SizingMethod='auto expand')";
+   filter: progid:DXImageTransform.Microsoft.Matrix(
+            M11=0.7071067811865483,
+            M12=-0.7071067811865467,
+            M21=-0.7071067811865467,
+            M22=-0.7071067811865483,
+            SizingMethod='auto expand');
+}
+.ui-popup-arrow-container.ui-popup-arrow-l .ui-popup-arrow-background,
+.ui-popup-arrow-container.ui-popup-arrow-r .ui-popup-arrow-background {
+	/* Undo rotation and reflect in y axis */
+	-webkit-transform: rotate(-45deg) scale(-1, 1);
+	-moz-transform: rotate(-45deg) scale(-1, 1);
+	-ms-transform: rotate(-45deg) scale(-1, 1);
+	transform: rotate(-45deg) scale(-1, 1);
+}
+.ui-popup-arrow-container.ui-popup-arrow-l.ie .ui-popup-arrow-background,
+.ui-popup-arrow-container.ui-popup-arrow-r.ie .ui-popup-arrow-background {
+   -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=-0.7071067811865483, M12=0.7071067811865467, M21=0.7071067811865467, M22=0.7071067811865483, SizingMethod='auto expand')";
+   filter: progid:DXImageTransform.Microsoft.Matrix(
+            M11=-0.7071067811865483,
+            M12=0.7071067811865467,
+            M21=0.7071067811865467,
+            M22=0.7071067811865483,
+            SizingMethod='auto expand');
+}
+.ui-checkbox,
+.ui-radio {
+	margin: .5em 0;
+	position: relative;
+}
+.ui-checkbox .ui-btn,
+.ui-radio .ui-btn {
+	margin: 0;
+	text-align: left;
+	white-space: normal; /* Nowrap + ellipsis doesn't work on label. Issue #1419. */
+	z-index: 2;
+}
+.ui-controlgroup .ui-checkbox .ui-btn.ui-focus,
+.ui-controlgroup .ui-radio .ui-btn.ui-focus {
+	z-index: 3;
+}
+.ui-checkbox .ui-btn-icon-top,
+.ui-radio .ui-btn-icon-top,
+.ui-checkbox .ui-btn-icon-bottom,
+.ui-radio .ui-btn-icon-bottom {
+	text-align: center;
+}
+.ui-controlgroup-horizontal .ui-checkbox .ui-btn:after,
+.ui-controlgroup-horizontal .ui-radio .ui-btn:after {
+	content: none;
+	display: none;
+}
+/* Native input positioning */
+.ui-checkbox input,
+.ui-radio input {
+	position: absolute;
+	left: .466em;
+	top: 50%;
+	width: 22px;
+	height: 22px;
+	margin: -11px 0 0 0;
+	outline: 0 !important;
+	z-index: 1;
+}
+.ui-controlgroup-horizontal .ui-checkbox input,
+.ui-controlgroup-horizontal .ui-radio input {
+	left: 50%;
+	margin-left: -9px;
+}
+.ui-checkbox input:disabled,
+.ui-radio input:disabled {
+	position: absolute !important;
+	height: 1px;
+	width: 1px;
+	overflow: hidden;
+	clip: rect(1px,1px,1px,1px);
+}
+.ui-select {
+	margin-top: .5em;
+	margin-bottom: .5em; /* no shorthand for margin because it would override margin-right for inline selects */
+	position: relative;
+}
+.ui-select > select {
+	position: absolute !important;
+	height: 1px;
+	width: 1px;
+	overflow: hidden;
+	clip: rect(1px,1px,1px,1px);
+}
+.ui-select .ui-btn {
+	margin: 0;
+	opacity: 1; /* Fixes #2588: When Windows Phone 7.5 (Mango) tries to calculate a numeric opacity for a select (including "inherit") without explicitly specifying an opacity on the parent to give it context, a bug appears where clicking elsewhere on the page after opening the select will open the select again. */
+}
+.ui-select .ui-btn select {
+	position: absolute;
+	top: 0;
+	left: 0;
+	width: 100%;
+	min-height: 1.5em;
+	min-height: 100%;
+	height: 3em;
+	max-height: 100%;
+	outline: 0;
+	-webkit-border-radius: inherit;
+	border-radius: inherit;	
+	-webkit-appearance: none;
+	-moz-appearance: none;
+	cursor: pointer;
+	filter: Alpha(Opacity=0);
+	opacity: 0;
+	z-index: 2;
+}
+@-moz-document url-prefix() {
+	.ui-select .ui-btn select {
+		opacity: 0.0001;
+	}
+}
+/* Display none because of issues with IE/WP's filter alpha opacity */
+.ui-select .ui-state-disabled select {
+	display: none;
+}
+/* Because we add all classes of the select and option elements to the span... */ 
+.ui-select span.ui-state-disabled {
+	filter: Alpha(Opacity=100);
+	opacity: 1;
+}
+.ui-select .ui-btn.ui-select-nativeonly {
+	border-radius: 0;
+	border: 0;
+}
+.ui-select .ui-btn.ui-select-nativeonly select {
+	opacity: 1;
+	text-indent: 0;
+	display: block;
+}
+/* ui-li-count is styled in the listview CSS. We set padding and offset here because select supports icon position while listview doesn't. */
+.ui-select .ui-li-has-count.ui-btn {
+	padding-right: 2.8125em;
+}
+.ui-select .ui-li-has-count.ui-btn-icon-right {
+	padding-right: 4.6875em;
+}
+.ui-select .ui-btn-icon-right .ui-li-count {
+	right: 3.2em;
+}
+/* We set the rules for the span as well to fix an issue on Chrome with text-overflow ellipsis for the button in combination with text-align center. */
+.ui-select .ui-btn > span:not(.ui-li-count) {
+	display: block;
+	text-overflow: ellipsis;
+	overflow: hidden !important;
+	white-space: nowrap;
+}
+.ui-selectmenu.ui-popup {
+	min-width: 11em;
+}
+.ui-selectmenu .ui-dialog-contain {
+	overflow: hidden;
+}
+.ui-selectmenu .ui-header {
+	margin: 0;
+	padding: 0;
+	border-width: 0;
+}
+.ui-selectmenu.ui-dialog .ui-header {
+	z-index: 1;
+	position: relative;
+}
+.ui-selectmenu.ui-popup .ui-header {
+	-webkit-border-bottom-right-radius: 0;
+	border-bottom-right-radius: 0;
+	-webkit-border-bottom-left-radius: 0;
+	border-bottom-left-radius: 0;
+}
+/* when no placeholder is defined in a multiple select, the header height doesn't even extend past the close button.  this shim's content in there */
+.ui-selectmenu.ui-popup .ui-header h1:after {
+	content: '.';
+	visibility: hidden;
+}
+.ui-selectmenu .ui-header .ui-title {
+	margin: 0 2.875em;
+}
+.ui-selectmenu.ui-dialog .ui-content {
+	overflow: visible;
+	z-index: 1;
+}
+.ui-selectmenu .ui-selectmenu-list {
+	margin: 0;
+	-webkit-border-radius: inherit;
+	border-radius: inherit;	
+}
+.ui-header:not(.ui-screen-hidden) + .ui-selectmenu-list {
+	-webkit-border-top-right-radius: 0;
+	border-top-right-radius: 0;
+	-webkit-border-top-left-radius: 0;
+	border-top-left-radius: 0;
+}
+.ui-header.ui-screen-hidden + .ui-selectmenu-list li.ui-first-child .ui-btn {
+	border-top-width: 0;
+}
+.ui-selectmenu .ui-selectmenu-list li.ui-last-child .ui-btn {
+	border-bottom-width: 0;
+}
+.ui-selectmenu .ui-btn.ui-li-divider {
+	cursor: default;
+}
+.ui-selectmenu .ui-selectmenu-placeholder {
+	display: none;
+}
+.ui-listview,
+.ui-listview > li {
+	margin: 0;
+	padding: 0;
+	list-style: none;
+}
+.ui-content .ui-listview,
+.ui-panel-inner > .ui-listview {
+	margin: -1em;
+}
+.ui-content .ui-listview-inset,
+.ui-panel-inner > .ui-listview-inset {
+	margin: 1em 0;
+}
+.ui-collapsible-content > .ui-listview {
+	margin: -.5em -1em;
+}
+.ui-collapsible-content > .ui-listview-inset {
+	margin: .5em 0;
+}
+.ui-listview > li {
+	display: block;
+	position: relative;
+	overflow: visible;
+}
+.ui-listview > .ui-li-static,
+.ui-listview > .ui-li-divider,
+.ui-listview > li > a.ui-btn {
+	margin: 0;
+	display: block;
+	position: relative;
+	text-align: left;
+	text-overflow: ellipsis;
+	overflow: hidden;
+	white-space: nowrap;
+}
+.ui-listview > li > .ui-btn:focus {
+	z-index: 1;
+}
+.ui-listview > .ui-li-static,
+.ui-listview > .ui-li-divider,
+.ui-listview > li > a.ui-btn {
+	border-width: 1px 0 0 0;
+	border-style: solid;
+}
+.ui-listview-inset > .ui-li-static,
+.ui-listview-inset > .ui-li-divider,
+.ui-listview-inset > li > a.ui-btn {
+	border-right-width: 1px;
+	border-left-width: 1px;
+}
+.ui-listview > .ui-li-static.ui-last-child,
+.ui-listview > .ui-li-divider.ui-last-child,
+.ui-listview > li.ui-last-child > a.ui-btn {
+	border-bottom-width: 1px;
+}
+.ui-collapsible-content > .ui-listview:not(.ui-listview-inset) > li.ui-first-child,
+.ui-collapsible-content > .ui-listview:not(.ui-listview-inset) > li.ui-first-child > a.ui-btn {
+	border-top-width: 0;
+}
+.ui-collapsible-themed-content .ui-listview:not(.ui-listview-inset) > li.ui-last-child,
+.ui-collapsible-themed-content .ui-listview:not(.ui-listview-inset) > li.ui-last-child > a.ui-btn {
+	border-bottom-width: 0;
+}
+.ui-listview > li.ui-first-child,
+.ui-listview > li.ui-first-child > a.ui-btn {
+	-webkit-border-top-right-radius: inherit;	
+	border-top-right-radius: inherit;
+	-webkit-border-top-left-radius: inherit;
+	border-top-left-radius: inherit;
+}
+.ui-listview > li.ui-last-child,
+.ui-listview > li.ui-last-child > a.ui-btn {
+	-webkit-border-bottom-right-radius: inherit;
+	border-bottom-right-radius: inherit;
+	-webkit-border-bottom-left-radius: inherit;
+	border-bottom-left-radius: inherit;
+}
+.ui-listview > li.ui-li-has-alt > a.ui-btn {
+	-webkit-border-top-right-radius: 0;
+	border-top-right-radius: 0;
+	-webkit-border-bottom-right-radius: 0;
+	border-bottom-right-radius: 0;
+}
+.ui-listview > li.ui-first-child > a.ui-btn + a.ui-btn {
+	-webkit-border-top-left-radius: 0;	
+	border-top-left-radius: 0;
+	-webkit-border-top-right-radius: inherit;
+	border-top-right-radius: inherit;
+}
+.ui-listview > li.ui-last-child > a.ui-btn + a.ui-btn {
+	-webkit-border-bottom-left-radius: 0;
+	border-bottom-left-radius: 0;
+	-webkit-border-bottom-right-radius: inherit;
+	border-bottom-right-radius: inherit;
+}
+.ui-listview > li.ui-first-child img:first-child:not(.ui-li-icon) {
+	-webkit-border-top-left-radius: inherit;
+	border-top-left-radius: inherit;	
+}
+.ui-listview > li.ui-last-child img:first-child:not(.ui-li-icon) {
+	-webkit-border-bottom-left-radius: inherit;
+	border-bottom-left-radius: inherit;	
+}
+.ui-collapsible-content > .ui-listview:not(.ui-listview-inset) {
+	-webkit-border-radius: inherit;
+	border-radius: inherit;	
+}
+.ui-listview > .ui-li-static {
+	padding: .7em 1em;
+}
+.ui-listview > .ui-li-divider {
+	padding: .5em 1.143em;
+	font-size: 14px;
+	font-weight: bold;
+	cursor: default;
+	outline: 0; /* Dividers in custom selectmenus have tabindex */
+}
+.ui-listview > .ui-li-has-count > .ui-btn,
+.ui-listview > .ui-li-static.ui-li-has-count,
+.ui-listview > .ui-li-divider.ui-li-has-count {
+	padding-right: 2.8125em;
+}
+.ui-listview > .ui-li-has-count > .ui-btn-icon-right {
+	padding-right: 4.6875em;
+}
+.ui-listview > .ui-li-has-thumb > .ui-btn,
+.ui-listview > .ui-li-static.ui-li-has-thumb {
+	min-height: 3.625em;
+	padding-left: 6.25em;
+}
+/* ui-li-has-icon deprecated in 1.4. TODO: remove in 1.5 */
+.ui-listview > .ui-li-has-icon > .ui-btn,
+.ui-listview > .ui-li-static.ui-li-has-icon {
+	min-height: 1.25em;
+	padding-left: 2.5em;
+}
+/* Used by both listview and custom multiple select button */
+.ui-li-count {
+	position: absolute;
+	font-size: 12.5px;
+	font-weight: bold;
+	text-align: center;
+	border-width: 1px;
+	border-style: solid;
+	padding: 0 .48em;
+	line-height: 1.6em;
+	min-height: 1.6em;
+	min-width: .64em;
+	right: .8em;
+	top: 50%;
+	margin-top: -.88em;
+}
+.ui-listview .ui-btn-icon-right .ui-li-count {
+	right: 3.2em;
+}
+.ui-listview .ui-li-has-thumb > img:first-child,
+.ui-listview .ui-li-has-thumb > .ui-btn > img:first-child,
+.ui-listview .ui-li-has-thumb .ui-li-thumb {
+	position: absolute;
+	left: 0;
+	top: 0;
+	max-height: 5em;
+	max-width: 5em;
+}
+/* ui-li-has-icon deprecated in 1.4. TODO: remove in 1.5 */
+.ui-listview > .ui-li-has-icon > img:first-child,
+.ui-listview > .ui-li-has-icon > .ui-btn > img:first-child {
+	position: absolute;
+	left: .625em;
+	top: .9em;
+	max-height: 1em;
+	max-width: 1em;
+}
+.ui-listview > li h1,
+.ui-listview > li h2,
+.ui-listview > li h3,
+.ui-listview > li h4,
+.ui-listview > li h5,
+.ui-listview > li h6 {
+	font-size: 1em;
+	font-weight: bold;
+	display: block;
+	margin: .45em 0;
+	text-overflow: ellipsis;
+	overflow: hidden;
+	white-space: nowrap;
+}
+.ui-listview > li p {
+	font-size: .75em;
+	font-weight: normal;
+	display: block;
+	margin: .6em 0;
+	text-overflow: ellipsis;
+	overflow: hidden;
+	white-space: nowrap;
+}
+.ui-listview .ui-li-aside {
+	position: absolute;
+	top: 1em;
+	right: 3.333em;
+	margin: 0;
+	text-align: right;
+}
+.ui-listview > li.ui-li-has-alt > .ui-btn {
+	margin-right: 2.5em;
+	border-right-width: 0;
+}
+.ui-listview > li.ui-li-has-alt > .ui-btn + .ui-btn {
+	position: absolute;
+	width: 2.5em;
+	height: 100%;
+	min-height: auto;
+	-webkit-box-sizing: border-box;
+	-moz-box-sizing: border-box;
+	box-sizing: border-box;
+	border-left-width: 1px;
+	top: 0;
+	right: 0;
+	margin: 0;
+	padding: 0;
+	z-index: 2;
+}
+.ui-listview-inset > li.ui-li-has-alt > .ui-btn + .ui-btn {
+	border-right-width: 1px;
+}
+.ui-listview > li.ui-li-has-alt > .ui-btn + .ui-btn:focus {
+	z-index: 3;
+}
+ol.ui-listview,
+ol.ui-listview > .ui-li-divider {
+	counter-reset: listnumbering;
+}
+ol.ui-listview > li > .ui-btn,
+ol.ui-listview > li.ui-li-static {
+	vertical-align: middle;
+}
+ol.ui-listview > li > .ui-btn:before,
+ol.ui-listview > li.ui-li-static:before,
+ol.ui-listview > li.ui-field-contain > label:before,
+ol.ui-listview > li.ui-field-contain > .ui-controlgroup-label:before {
+	display: inline-block;
+	font-size: .9em;
+	font-weight: normal;
+	padding-right: .3em;
+	min-width: 1.4em;
+	line-height: 1.5;
+	vertical-align: middle;
+	counter-increment: listnumbering;
+	content: counter(listnumbering) ".";
+}
+ol.ui-listview > li.ui-field-contain:before {
+	content: none;
+	display: none;
+}
+ol.ui-listview > li h1:first-child,
+ol.ui-listview > li h2:first-child,
+ol.ui-listview > li h3:first-child,
+ol.ui-listview > li h4:first-child,
+ol.ui-listview > li h5:first-child,
+ol.ui-listview > li h6:first-child,
+ol.ui-listview > li p:first-child,
+ol.ui-listview > li img:first-child + * {
+	display: inline-block;
+	vertical-align: middle;
+}
+ol.ui-listview > li h1:first-child ~ *,
+ol.ui-listview > li h2:first-child ~ *,
+ol.ui-listview > li h3:first-child ~ *,
+ol.ui-listview > li h4:first-child ~ *,
+ol.ui-listview > li h5:first-child ~ *,
+ol.ui-listview > li h6:first-child ~ *,
+ol.ui-listview > li p:first-child ~ *,
+ol.ui-listview > li img:first-child + * ~ * {
+	margin-top: 0;
+	text-indent: 2.04em; /* (1.4em + .3em) * .9em / .75em */
+}
+html .ui-filterable + .ui-listview,
+html .ui-filterable.ui-listview {
+	margin-top: .5em;
+}
+.ui-collapsible-content > form.ui-filterable {
+	margin-top: -.5em;
+}
+.ui-collapsible-content > .ui-input-search.ui-filterable {
+	margin-top: 0;
+}
+.ui-collapsible-content > .ui-filterable + .ui-listview:not(.ui-listview-inset) > li.ui-first-child,
+.ui-collapsible-content > .ui-filterable + .ui-listview:not(.ui-listview-inset) > li.ui-first-child > a.ui-btn,
+.ui-collapsible-content > .ui-filterable.ui-listview:not(.ui-listview-inset) > li.ui-first-child,
+.ui-collapsible-content > .ui-filterable.ui-listview:not(.ui-listview-inset) > li.ui-first-child > a.ui-btn {
+	border-top-width: 1px;
+}
+div.ui-slider {
+	height: 30px;
+	margin: .5em 0;
+	padding: 0;
+	-ms-touch-action: pan-y pinch-zoom double-tap-zoom;
+}
+div.ui-slider:before,
+div.ui-slider:after {
+	content: "";
+	display: table;
+}
+div.ui-slider:after {
+	clear: both;
+}
+input.ui-slider-input {
+	display: block;
+	float: left;
+	font-size: 14px;
+	font-weight: bold;
+	margin: 0;
+	padding: 4px;
+	width: 40px;
+	height: 20px;
+	line-height: 20px;
+	border-width: 1px;
+	border-style: solid;
+	outline: 0;
+	text-align: center;
+	vertical-align: text-bottom;
+	-webkit-appearance: none;
+	-moz-appearance: none;
+	appearance: none;
+	-webkit-box-sizing: content-box;
+	-moz-box-sizing: content-box;
+	-ms-box-sizing: content-box;
+	box-sizing: content-box;
+}
+.ui-slider-input::-webkit-outer-spin-button,
+.ui-slider-input::-webkit-inner-spin-button {
+	-webkit-appearance: none;
+	margin: 0;
+}
+.ui-slider-track {
+	position: relative;
+	overflow: visible;
+	border-width: 1px;
+	border-style: solid;
+	height: 15px;
+	margin: 0 15px 0 68px;
+	top: 6px;
+}
+.ui-slider-track.ui-mini {
+	height: 12px;
+	top: 8px;
+}
+.ui-slider-track .ui-slider-bg {
+	height: 100%;
+}
+/* High level of specificity to override button margins in grids */
+.ui-slider-track .ui-btn.ui-slider-handle {
+	position: absolute;
+	z-index: 1;
+	top: 50%;
+	width: 28px;
+	height: 28px;
+	margin: -15px 0 0 -15px;
+	outline: 0;
+	padding: 0;
+}
+.ui-slider-track.ui-mini .ui-slider-handle {
+	height: 14px;
+	width: 14px;
+	margin: -8px 0 0 -8px;
+}
+select.ui-slider-switch {
+	position: absolute !important;
+	height: 1px;
+	width: 1px;
+	overflow: hidden;
+	clip: rect(1px,1px,1px,1px);
+}
+div.ui-slider-switch {
+	display: inline-block;
+	height: 32px;
+	width: 5.8em;
+	top: 0;
+}
+/* reset the clearfix */
+div.ui-slider-switch:before,
+div.ui-slider-switch:after {
+	display: none;
+	clear: none;
+}
+div.ui-slider-switch.ui-mini {
+	height: 29px;
+	top: 0;
+}
+.ui-slider-inneroffset {
+	margin: 0 16px;
+	position: relative;
+	z-index: 1;
+}
+.ui-slider-switch.ui-mini .ui-slider-inneroffset {
+	margin: 0 15px 0 14px;
+}
+.ui-slider-switch .ui-btn.ui-slider-handle {
+	margin: 1px 0 0 -15px;
+}
+.ui-slider-switch.ui-mini .ui-slider-handle {
+	width: 25px;
+	height: 25px;
+	margin: 1px 0 0 -13px;
+	padding: 0;
+}
+.ui-slider-handle-snapping {
+	-webkit-transition: left 70ms linear;
+	-moz-transition: left 70ms linear;
+	transition: left 70ms linear;
+}
+.ui-slider-switch .ui-slider-label {
+	position: absolute;
+	text-align: center;
+	width: 100%;
+	overflow: hidden;
+	font-size: 16px;
+	top: 0;
+	line-height: 2;
+	min-height: 100%;
+	white-space: nowrap;
+	cursor: pointer;
+}
+.ui-slider-switch.ui-mini .ui-slider-label {
+	font-size: 14px;
+}
+.ui-slider-switch .ui-slider-label-a {
+	z-index: 1;
+	left: 0;
+	text-indent: -1.5em;
+}
+.ui-slider-switch .ui-slider-label-b {
+	z-index: 0;
+	right: 0;
+	text-indent: 1.5em;
+}
+/* The corner radii for ui-slider-switch/track can be specified in theme CSS. The bg and handle inherits. */
+.ui-slider-track .ui-slider-bg,
+.ui-slider-switch .ui-slider-label,
+.ui-slider-switch .ui-slider-inneroffset,
+.ui-slider-handle {
+	-webkit-border-radius: inherit;
+	border-radius: inherit;
+}
+.ui-field-contain div.ui-slider-switch {
+	margin: 0;
+}
+@media (min-width: 28em) {
+	/* ui-hide-label deprecated in 1.4. TODO: Remove in 1.5 */
+	.ui-field-contain div.ui-slider-switch,
+	.ui-field-contain.ui-hide-label div.ui-slider-switch {
+		display: inline-block;
+		width: 5.8em;
+	}
+}	
+/* slider tooltip
+-----------------------------------------------------------------------------------------------------------*/
+.ui-slider-popup {
+	width: 64px;
+	height: 64px;
+	font-size: 36px;
+	padding-top: 14px;
+	opacity: 0.8;
+}
+.ui-slider-popup {
+	position: absolute !important;
+	text-align: center;
+	z-index: 100;
+}
+.ui-slider-track .ui-btn.ui-slider-handle {
+	font-size: .9em;
+	line-height: 30px;
+}
+.ui-rangeslider {
+	margin: .5em 0;
+}
+.ui-rangeslider:before,
+.ui-rangeslider:after {
+	content: "";
+	display: table;
+}
+.ui-rangeslider:after {
+	clear: both;
+}
+.ui-rangeslider .ui-slider-input.ui-rangeslider-last {
+	float: right;
+}
+.ui-rangeslider .ui-rangeslider-sliders {
+	position: relative;
+	overflow: visible;
+	height: 30px;
+	margin: 0 68px;
+}
+.ui-rangeslider .ui-rangeslider-sliders .ui-slider-track {
+	position: absolute;
+	top: 6px;
+	right: 0;
+	left: 0;
+	margin: 0;
+}
+.ui-rangeslider.ui-mini .ui-rangeslider-sliders .ui-slider-track {
+	top: 8px;
+}
+.ui-rangeslider .ui-slider-track:first-child .ui-slider-bg {
+	display: none;
+}
+.ui-rangeslider .ui-rangeslider-sliders .ui-slider-track:first-child {
+	background-color: transparent;
+	background: none;
+	border-width: 0;
+	height: 0;
+}
+/* this makes ie6 and ie7 set height to 0 to fix z-index problem */
+html >/**/body .ui-rangeslider .ui-rangeslider-sliders .ui-slider-track:first-child {
+	height: 15px;
+	border-width: 1px;
+}
+html >/**/body .ui-rangeslider.ui-mini .ui-rangeslider-sliders .ui-slider-track:first-child {
+	height: 12px;
+}
+/* Hide the second label (the first is moved outside the div) */
+div.ui-rangeslider label {
+	position: absolute !important;
+	height: 1px;
+	width: 1px;
+	overflow: hidden;
+	clip: rect(1px,1px,1px,1px);
+}
+.ui-field-contain .ui-rangeslider input.ui-slider-input,
+.ui-field-contain .ui-rangeslider.ui-mini input.ui-slider-input,
+.ui-field-contain .ui-rangeslider .ui-rangeslider-sliders,
+.ui-field-contain .ui-rangeslider.ui-mini .ui-rangeslider-sliders {
+	margin-top: 0;
+	margin-bottom: 0;
+}
+.ui-input-text,
+.ui-input-search {
+	margin: .5em 0;
+	border-width: 1px;
+	border-style: solid;
+}
+.ui-input-text input,
+.ui-input-search input,
+textarea.ui-input-text {
+	padding: .4em;
+	line-height: 1.4em;
+	display: block;
+	width: 100%;
+	-webkit-box-sizing: border-box;
+	-moz-box-sizing: border-box;
+	-ms-box-sizing: border-box;
+	box-sizing: border-box;
+	outline: 0;
+}
+.ui-input-text input,
+.ui-input-search input {
+	margin: 0;
+	min-height: 2.2em;
+	text-align: left; /* Opera aligns type="date" right by default */
+	border: 0;
+	background: transparent none;
+	-webkit-appearance: none;
+	-webkit-border-radius: inherit;
+	border-radius: inherit;
+}
+textarea.ui-input-text {
+	overflow: auto;
+	resize: vertical;
+}
+.ui-mini .ui-input-text input,
+.ui-mini .ui-input-search input,
+.ui-input-text.ui-mini input,
+.ui-input-search.ui-mini input,
+.ui-mini textarea.ui-input-text,
+textarea.ui-mini {
+	font-size: 14px;
+}
+/* Same margin for mini textareas as other mini sized widgets (12.5/14 * 0.5em) */
+.ui-mini textarea.ui-input-text,
+textarea.ui-mini {
+	margin: .446em 0;
+}
+.ui-input-has-clear,
+.ui-input-search {
+	position: relative;
+}
+/* Padding on the div instead of input because of browser spinners etc. */
+.ui-input-has-clear {
+	padding-right: 2.25em;
+}
+.ui-input-has-clear input {
+	padding-right: 0;
+	/* Autofill on Chrome has bg color so we unset corners right as well. */
+	-webkit-border-top-right-radius: 0;
+	border-top-right-radius: 0;
+	-webkit-border-bottom-right-radius: 0;
+	border-bottom-right-radius: 0;
+}
+/* Search icon */
+.ui-input-search input {
+	padding-left: 1.75em;
+}
+.ui-input-search:after {
+	position: absolute;
+	left: .3125em;
+	top: 50%;
+	margin-top: -7px;
+	content: "";
+	background-position: center center;
+	background-repeat: no-repeat;
+	width: 14px;
+	height: 14px;
+	filter: Alpha(Opacity=50);
+	opacity: .5;
+}
+.ui-input-search.ui-input-has-clear .ui-btn.ui-input-clear,
+.ui-input-text.ui-input-has-clear .ui-btn.ui-input-clear {
+	position: absolute;
+	right: 0;
+	top: 50%;
+	margin: -14px .3125em 0;
+	border: 0;
+	background-color: transparent;
+}
+.ui-input-search .ui-input-clear-hidden,
+.ui-input-text .ui-input-clear-hidden {
+	display: none;
+}
+/* Resolves issue #5166: Added to support issue introduced in Firefox 15. We can likely remove this in the future. */
+.ui-input-text input::-moz-placeholder,
+.ui-input-search input::-moz-placeholder,
+textarea.ui-input-text::-moz-placeholder {
+	color: #aaa;
+}
+/* Same for IE10 */
+.ui-input-text input:-ms-input-placeholder,
+.ui-input-search input:-ms-input-placeholder,
+textarea.ui-input-text:-ms-input-placeholder {
+	color: #aaa;
+}
+/* Resolves issue #5131: Width of textinput depends on its type,
+for Android 4.1 */
+.ui-input-text input[type=number]::-webkit-outer-spin-button {
+	margin: 0;
+}
+/* Resolves issue #5756: Textinput in IE10 has a default clear button */
+.ui-input-text input::-ms-clear,
+.ui-input-search input::-ms-clear {
+	display: none;
+}
+.ui-input-text input:focus,
+.ui-input-search input:focus {
+	-webkit-box-shadow: none;
+	-moz-box-shadow: none;
+	box-shadow: none;
+}
+.ui-textinput-autogrow-resize {
+	-webkit-transition: height 0.25s;
+	-o-transition: height 0.25s;
+	-moz-transition: height 0.25s;
+	transition: height 0.25s;
+}
+.ui-flipswitch {
+	display: inline-block;
+	vertical-align: middle;
+	width: 5.875em; /* Override this and padding-left in next rule if you use labels other than "on/off" and need more space */
+	height: 1.875em;
+	border-width: 1px;
+	border-style: solid;
+	margin: .5em 0;
+	overflow: hidden;
+	-webkit-transition-property: padding, width, background-color, color, border-color;
+	-moz-transition-property: padding, width, background-color, color, border-color;
+	-o-transition-property: padding, width, background-color, color, border-color;
+	transition-property: padding, width, background-color, color, border-color;
+	-webkit-transition-duration: 100ms;
+	-moz-transition-duration: 100ms;
+	-o-transition-duration: 100ms;
+	transition-duration: 100ms;
+	-webkit-touch-callout: none;
+	-webkit-user-select: none;
+	-moz-user-select: none;
+	-ms-user-select: none;
+	user-select: none;
+	cursor: pointer;
+}
+.ui-flipswitch.ui-flipswitch-active {
+	padding-left: 4em;  /* Override this and width in previous rule if you use labels other than "on/off" and need more space */
+	width: 1.875em;
+}
+.ui-flipswitch-input {
+	position: absolute;
+	height: 1px;
+	width: 1px;
+	margin: -1px;
+	overflow: hidden;
+	clip: rect(1px,1px,1px,1px);
+	border: 0;
+	outline: 0;
+	filter: Alpha(Opacity=0);
+	opacity: 0;
+}
+.ui-flipswitch .ui-btn.ui-flipswitch-on,
+.ui-flipswitch .ui-flipswitch-off {
+	float: left;
+	height: 1.75em;
+	margin: .0625em;
+	line-height: 1.65em;
+}
+.ui-flipswitch .ui-btn.ui-flipswitch-on {
+	width: 1.75em;
+	padding: 0;
+	text-indent: -2.6em; /* Override this to center text if you use a label other than "on" */
+	text-align: left;
+	border-width: 1px;
+	border-style: solid;
+	-webkit-box-sizing: border-box;
+	-moz-box-sizing: border-box;
+	-ms-box-sizing: border-box;
+	box-sizing: border-box;
+	border-radius: inherit;
+	overflow: visible;
+	color: inherit;
+	text-shadow: inherit;
+}
+.ui-flipswitch .ui-flipswitch-off {
+	padding: 1px;
+	text-indent: 1em; /* Override this to center text if you use a label other than "off" */
+}
+@media (min-width: 28em) {
+	.ui-field-contain > label + .ui-flipswitch {
+		display: inline-block;
+		width: 5.875em; /* If you override the width for .ui-flipswitch you should repeat the same value here */
+		-webkit-box-sizing: content-box;
+		-moz-box-sizing: content-box;
+		-ms-box-sizing: content-box;
+		box-sizing: content-box;
+	}
+	.ui-field-contain .ui-flipswitch.ui-flipswitch-active {
+		width: 1.875em;
+	}
+}	
+.ui-table {
+	border: 0;
+	border-collapse: collapse;
+	padding: 0;
+	width: 100%;
+}
+.ui-table th,
+.ui-table td {
+	line-height: 1.5em;
+	text-align: left;
+	padding: .4em .5em;
+	vertical-align:top;
+}
+.ui-table th .ui-btn,
+.ui-table td .ui-btn {
+	line-height: normal;
+}
+.ui-table th {
+	font-weight: bold;
+}
+.ui-table caption {
+	text-align: left;
+	margin-bottom: 1.4em;
+	opacity: .5;
+}
+/*
+ Styles for the table columntoggle mode
+*/
+.ui-table-columntoggle-btn {
+	float: right;
+	margin-bottom: .8em;
+}
+/* Remove top/bottom margins around the fieldcontain on check list */
+.ui-table-columntoggle-popup fieldset {
+	margin:0;
+}
+.ui-table-columntoggle {
+	clear: both;
+}
+/* Hide all prioritized columns by default */
+@media only all {
+	th.ui-table-priority-6,
+	td.ui-table-priority-6,
+	th.ui-table-priority-5,
+	td.ui-table-priority-5,
+	th.ui-table-priority-4,
+	td.ui-table-priority-4,
+	th.ui-table-priority-3,
+	td.ui-table-priority-3,
+	th.ui-table-priority-2,
+	td.ui-table-priority-2,
+	th.ui-table-priority-1,
+	td.ui-table-priority-1 {
+		display: none;
+	}
+}
+/* Preset breakpoints if ".ui-responsive" class added to table */
+/* Show priority 1 at 320px (20em x 16px) */
+@media screen and (min-width: 20em) {
+	.ui-table-columntoggle.ui-responsive th.ui-table-priority-1,
+	.ui-table-columntoggle.ui-responsive td.ui-table-priority-1 {
+		display: table-cell;
+	}
+}
+/* Show priority 2 at 480px (30em x 16px) */
+@media screen and (min-width: 30em) {
+	.ui-table-columntoggle.ui-responsive th.ui-table-priority-2,
+	.ui-table-columntoggle.ui-responsive td.ui-table-priority-2 {
+		display: table-cell;
+	}
+}
+/* Show priority 3 at 640px (40em x 16px) */
+@media screen and (min-width: 40em) {
+	.ui-table-columntoggle.ui-responsive th.ui-table-priority-3,
+	.ui-table-columntoggle.ui-responsive td.ui-table-priority-3 {
+		display: table-cell;
+	}
+}
+/* Show priority 4 at 800px (50em x 16px) */
+@media screen and (min-width: 50em) {
+	.ui-table-columntoggle.ui-responsive th.ui-table-priority-4,
+	.ui-table-columntoggle.ui-responsive td.ui-table-priority-4 {
+		display: table-cell;
+	}
+}
+/* Show priority 5 at 960px (60em x 16px) */
+@media screen and (min-width: 60em) {
+	.ui-table-columntoggle.ui-responsive th.ui-table-priority-5,
+	.ui-table-columntoggle.ui-responsive td.ui-table-priority-5 {
+		display: table-cell;
+	}
+}
+/* Show priority 6 at 1,120px (70em x 16px) */
+@media screen and (min-width: 70em) {
+	.ui-table-columntoggle.ui-responsive th.ui-table-priority-6,
+	.ui-table-columntoggle.ui-responsive td.ui-table-priority-6 {
+		display: table-cell;
+	}
+}
+/* Unchecked manually: Always hide */
+.ui-table-columntoggle th.ui-table-cell-hidden,
+.ui-table-columntoggle td.ui-table-cell-hidden,
+.ui-table-columntoggle.ui-responsive th.ui-table-cell-hidden,
+.ui-table-columntoggle.ui-responsive td.ui-table-cell-hidden {
+	display: none;
+}
+/* Checked manually: Always show */
+.ui-table-columntoggle th.ui-table-cell-visible,
+.ui-table-columntoggle td.ui-table-cell-visible,
+.ui-table-columntoggle.ui-responsive th.ui-table-cell-visible,
+.ui-table-columntoggle.ui-responsive td.ui-table-cell-visible {
+	display: table-cell;
+}
+/*
+ Styles for the table columntoggle mode
+*/
+.ui-table-reflow td .ui-table-cell-label,
+.ui-table-reflow th .ui-table-cell-label { 
+	display: none;
+}
+/* Mobile first styles: Begin with the stacked presentation at narrow widths */ 
+@media only all {
+	/* Hide the table headers */ 
+	.ui-table-reflow thead td, 
+	.ui-table-reflow thead th {
+		display: none;
+	}
+	/* Show the table cells as a block level element */ 
+	.ui-table-reflow td,
+	.ui-table-reflow th { 
+		text-align: left;
+		display: block;
+	}
+	/* Add a fair amount of top margin to visually separate each row when stacked */  
+	.ui-table-reflow tbody th {
+		margin-top: 3em;
+	}
+	/* Make the label elements a percentage width */ 
+	.ui-table-reflow td .ui-table-cell-label,
+	.ui-table-reflow th .ui-table-cell-label { 
+		padding: .4em; 
+		min-width: 30%; 
+		display: inline-block;
+		margin: -.4em 1em -.4em -.4em;
+	}
+	/* For grouped headers, have a different style to visually separate the levels by classing the first label in each col group */ 
+	.ui-table-reflow th .ui-table-cell-label-top,
+	.ui-table-reflow td .ui-table-cell-label-top {
+		display: block;
+		padding: .4em 0;
+		margin: .4em 0;
+		text-transform: uppercase;
+		font-size: .9em;
+		font-weight: normal;
+	}
+}
+/* Breakpoint to show as a standard table at 560px (35em x 16px) or wider */ 
+@media ( min-width: 35em ) {
+	/* Fixes table rendering when switching between breakpoints in Safari <= 5. See https://github.com/jquery/jquery-mobile/issues/5380 */
+	.ui-table-reflow.ui-responsive {
+		display: table-row-group;
+	}
+	/* Show the table header rows */ 
+	.ui-table-reflow.ui-responsive td,
+	.ui-table-reflow.ui-responsive th,
+	.ui-table-reflow.ui-responsive tbody th,
+	.ui-table-reflow.ui-responsive tbody td,
+	.ui-table-reflow.ui-responsive thead td,
+	.ui-table-reflow.ui-responsive thead th {
+		display: table-cell;
+		margin: 0;
+	}
+	/* Hide the labels in each cell */ 
+	.ui-table-reflow.ui-responsive td .ui-table-cell-label,
+	.ui-table-reflow.ui-responsive th .ui-table-cell-label { 
+		display: none;
+	}
+}
+/* Hack to make IE9 and WP7.5 treat cells like block level elements, scoped to ui-responsive class */ 
+/* Applied in a max-width media query up to the table layout breakpoint so we don't need to negate this*/ 
+@media ( max-width: 35em ) {
+	.ui-table-reflow.ui-responsive td,
+	.ui-table-reflow.ui-responsive th {
+		width: 100%;
+		-webkit-box-sizing: border-box;
+		-moz-box-sizing: border-box;
+		box-sizing: border-box;
+		float: left;
+		clear: left;
+	}
+}
+/* Panel */
+.ui-panel {
+	width: 17em;
+	min-height: 100%;
+	max-height: none;
+	border-width: 0;
+	position: absolute;
+	top: 0;
+	display: block;
+}
+.ui-panel-closed {
+	width: 0;
+	max-height: 100%;
+	overflow: hidden;
+	visibility: hidden;
+}
+.ui-panel-fixed {
+	position: fixed;
+	bottom: -1px; /* Fixes gap on Chrome for Android */
+	padding-bottom: 1px;
+}
+.ui-panel-display-reveal {
+	z-index: 1;
+}
+.ui-panel-display-push {
+	z-index: 999;
+}
+.ui-panel-display-overlay {
+	z-index: 1001; /* Fixed toolbars have z-index 1000 */
+}
+.ui-panel-inner {
+	padding: 1em;
+}
+/* Container, page and wrapper */
+.ui-panel-page-container {
+	overflow-x: visible;
+}
+.ui-panel-page-container-themed .ui-page-active {
+	background: none;
+}
+.ui-panel-wrapper {
+	position: relative;
+	min-height: inherit;
+	border: 0;
+	overflow-x: hidden;
+	z-index: 999;
+}
+/* Fixed toolbars */
+.ui-panel-fixed-toolbar {
+	overflow-x: hidden;
+}
+/* Dismiss */
+.ui-panel-dismiss {
+	position: absolute;
+	top: 0;
+	left: 0;
+	right: 0;
+	height: 100%;
+	z-index: 1002;
+	display: none;
+}
+.ui-panel-dismiss-open {
+	display: block;
+}
+/* Animate class is added to panel, wrapper and fixed toolbars */
+.ui-panel-animate {
+	-webkit-transition: -webkit-transform 300ms ease;
+	-moz-transition: -moz-transform 300ms ease;
+	transition: transform 300ms ease;
+}
+/* Fix for Windows Phone issue #6349: unset the transition for transforms in case of fixed toolbars. */
+@media screen and ( max-device-width: 768px ) {
+	.ui-page-header-fixed .ui-panel-animate.ui-panel-wrapper,
+	.ui-page-footer-fixed .ui-panel-animate.ui-panel-wrapper,
+	.ui-panel-animate.ui-panel-fixed-toolbar {
+		-ms-transition: none;
+	}
+	/* We need a transitionend event ... */
+	.ui-panel-animate.ui-panel-fixed-toolbar {
+		-ms-transition: -ms-transform 1ms;
+		-ms-transform: rotate(0deg);
+	}
+}
+/* Hardware acceleration for smoother transitions on WebKit browsers */
+.ui-panel-animate.ui-panel:not(.ui-panel-display-reveal) {
+	-webkit-backface-visibility: hidden;
+	-webkit-transform: translate3d(0,0,0);
+}
+/* Panel positioning (for overlay and push) */
+/* Panel left closed */
+.ui-panel-position-left {
+	left: -17em;
+}
+/* Panel left closed animated */
+.ui-panel-animate.ui-panel-position-left.ui-panel-display-overlay,
+.ui-panel-animate.ui-panel-position-left.ui-panel-display-push {
+	left: 0;
+	-webkit-transform: translate3d(-17em,0,0);
+	-moz-transform: translate3d(-17em,0,0);
+	transform: translate3d(-17em,0,0);
+}
+/* Panel left open */
+.ui-panel-position-left.ui-panel-display-reveal, /* Unset "panel left closed" for reveal */
+.ui-panel-open.ui-panel-position-left {
+	left: 0;
+}
+/* Panel left open animated */
+.ui-panel-animate.ui-panel-open.ui-panel-position-left.ui-panel-display-overlay,
+.ui-panel-animate.ui-panel-open.ui-panel-position-left.ui-panel-display-push {
+	-webkit-transform: translate3d(0,0,0);
+	transform: translate3d(0,0,0);
+	-moz-transform: none;
+}
+/* Panel right closed */
+.ui-panel-position-right {
+	right: -17em;
+}
+/* Panel right closed animated */
+.ui-panel-animate.ui-panel-position-right.ui-panel-display-overlay,
+.ui-panel-animate.ui-panel-position-right.ui-panel-display-push {
+	right: 0;
+	-webkit-transform: translate3d(17em,0,0);
+	-moz-transform: translate3d(17em,0,0);
+	transform: translate3d(17em,0,0);
+}
+/* Panel right open */
+.ui-panel-position-right.ui-panel-display-reveal, /* Unset "panel right closed" for reveal */
+.ui-panel-position-right.ui-panel-open {
+	right: 0;
+}
+/* Panel right open animated */
+.ui-panel-animate.ui-panel-open.ui-panel-position-right.ui-panel-display-overlay,
+.ui-panel-animate.ui-panel-open.ui-panel-position-right.ui-panel-display-push {
+	-webkit-transform: translate3d(0,0,0);
+	transform: translate3d(0,0,0);
+	-moz-transform: none;
+}
+/* Wrapper and fixed toolbars positioning (for reveal and push) */
+/* Panel left open */
+.ui-panel-page-content-position-left {
+	left: 17em;
+	right: -17em;
+}
+/* Panel left open animated */
+.ui-panel-animate.ui-panel-page-content-position-left {
+	left: 0;
+	right: 0;
+	-webkit-transform: translate3d(17em,0,0);
+	-moz-transform: translate3d(17em,0,0);
+	transform: translate3d(17em,0,0);
+}
+/* Panel right open */
+.ui-panel-page-content-position-right {
+	left: -17em;
+	right: 17em;
+}
+/* Panel right open animated */
+.ui-panel-animate.ui-panel-page-content-position-right {
+	left: 0;
+	right: 0;
+	-webkit-transform: translate3d(-17em,0,0);
+	-moz-transform: translate3d(-17em,0,0);
+	transform: translate3d(-17em,0,0);
+}
+/* Dismiss model open */
+.ui-panel-dismiss-open.ui-panel-dismiss-position-left {
+	left: 17em;
+}
+.ui-panel-dismiss-open.ui-panel-dismiss-position-right {
+	right: 17em;
+}
+/* Shadows and borders */
+.ui-panel-display-reveal {
+	-webkit-box-shadow: inset -5px 0 5px rgba(0,0,0,.15);
+	-moz-box-shadow: inset -5px 0 5px rgba(0,0,0,.15);
+	box-shadow: inset -5px 0 5px rgba(0,0,0,.15);
+}
+.ui-panel-position-right.ui-panel-display-reveal {
+	-webkit-box-shadow: inset 5px 0 5px rgba(0,0,0,.15);
+	-moz-box-shadow: inset 5px 0 5px rgba(0,0,0,.15);
+	box-shadow: inset 5px 0 5px rgba(0,0,0,.15);
+}
+.ui-panel-display-overlay {
+	-webkit-box-shadow: 5px 0 5px rgba(0,0,0,.15);
+	-moz-box-shadow: 5px 0 5px rgba(0,0,0,.15);
+	box-shadow: 5px 0 5px rgba(0,0,0,.15);
+}
+.ui-panel-position-right.ui-panel-display-overlay {
+	-webkit-box-shadow: -5px 0 5px rgba(0,0,0,.15);
+	-moz-box-shadow: -5px 0 5px rgba(0,0,0,.15);
+	box-shadow: -5px 0 5px rgba(0,0,0,.15);
+}
+.ui-panel-open.ui-panel-position-left.ui-panel-display-push {
+	border-right-width: 1px;
+	margin-right: -1px;
+}
+.ui-panel-page-content-position-left.ui-panel-page-content-display-push {
+	margin-left: 1px;
+	width: auto;
+}
+.ui-panel-open.ui-panel-position-right.ui-panel-display-push {
+	border-left-width: 1px;
+	margin-left: -1px;
+}
+.ui-panel-page-content-position-right.ui-panel-page-content-display-push {
+	margin-right: 1px;
+	width: auto;
+}
+/* Responsive: wrap on wide viewports once open */
+@media (min-width:55em) {
+	.ui-responsive-panel .ui-panel-page-content-open.ui-panel-page-content-position-left {
+		margin-right: 17em;
+	}
+	.ui-responsive-panel .ui-panel-page-content-open.ui-panel-page-content-position-right {
+		margin-left: 17em;
+	}
+	.ui-responsive-panel .ui-panel-page-content-open {
+		width: auto;	
+	}
+	.ui-responsive-panel .ui-panel-dismiss-display-push,
+	.ui-responsive-panel.ui-page-active ~ .ui-panel-dismiss-display-push {
+		display: none;
+	}
+}
+.ui-tabs {
+	position: relative;/* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */
+	padding: .2em;
+}
diff --git a/gz3d/src/gz.js b/gz3d/src/gz.js
new file mode 100644
index 0000000000000000000000000000000000000000..9aa8ac896ce571b86924cd492f9eb2944238b85d
--- /dev/null
+++ b/gz3d/src/gz.js
@@ -0,0 +1,3 @@
+var GZ3D = GZ3D || {
+  REVISION : '1'
+};
diff --git a/gz3d/src/gzcontrol.js b/gz3d/src/gzcontrol.js
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/gz3d/src/gzgui.js b/gz3d/src/gzgui.js
new file mode 100644
index 0000000000000000000000000000000000000000..559504cba34f546a21b26132605701df715dfa13
--- /dev/null
+++ b/gz3d/src/gzgui.js
@@ -0,0 +1,2074 @@
+/*global $:false */
+/*global angular*/
+
+var guiEvents = new EventEmitter2({ verbose: true });
+
+var emUnits = function(value)
+    {
+      return value*parseFloat($('body').css('font-size'));
+    };
+
+var isTouchDevice = 'ontouchstart' in window || 'onmsgesturechange' in window;
+
+var isWideScreen = function()
+    {
+      return $(window).width() / emUnits(1) > 35;
+    };
+var isTallScreen = function()
+    {
+      return $(window).height() / emUnits(1) > 35;
+    };
+var lastOpenMenu = {mainMenu: 'mainMenu', insertMenu: 'insertMenu',
+    treeMenu: 'treeMenu'};
+
+var tabColors = {selected: 'rgb(34, 170, 221)', unselected: 'rgb(42, 42, 42)'};
+
+var modelList =
+  [
+    {path:'buildings', title:'Buildings',
+    examplePath1:'fast_food', examplePath2:'kitchen_dining', examplePath3:'house_1', models:
+    [
+      {modelPath:'fast_food', modelTitle:'Fast Food'},
+      {modelPath:'gas_station', modelTitle:'Gas Station'},
+      {modelPath:'house_1', modelTitle:'House 1'},
+      {modelPath:'house_2', modelTitle:'House 2'},
+      {modelPath:'house_3', modelTitle:'House 3'},
+      {modelPath:'iss', modelTitle:'International Space Station'},
+      {modelPath:'iss_half', modelTitle:'ISS half'},
+      {modelPath:'kitchen_dining', modelTitle:'Kitchen and Dining'},
+      {modelPath:'office_building', modelTitle:'Office Building'},
+      {modelPath:'powerplant', modelTitle:'Power Plant'},
+      {modelPath:'starting_pen', modelTitle:'Starting Pen'},
+      {modelPath:'willowgarage', modelTitle:'Willow Garage'}
+    ]},
+
+    {path:'furniture', title:'Furniture',
+    examplePath1:'hinged_door', examplePath2:'bookshelf', examplePath3:'table', models:
+    [
+      {modelPath:'bookshelf', modelTitle:'Book Shelf'},
+      {modelPath:'cabinet', modelTitle:'Cabinet'},
+      {modelPath:'drc_practice_door_4x8', modelTitle:'4x8 Doorway'},
+      {modelPath:'drc_practice_ladder', modelTitle:'Ladder'},
+      {modelPath:'hinged_door', modelTitle:'Hinged Door'},
+      {modelPath:'table', modelTitle:'Table'},
+      {modelPath:'table_marble', modelTitle:'Table Marble'},
+
+      {modelPath:'drc_practice_ball_valve', modelTitle:'Ball Valve'},
+      {modelPath:'drc_practice_handle_wheel_valve', modelTitle:'Handle Wheel Valve'},
+      {modelPath:'drc_practice_hand_wheel_valve', modelTitle:'Hand Wheel Valve'},
+      {modelPath:'drc_practice_wheel_valve', modelTitle:'Wheel Valve'},
+      {modelPath:'drc_practice_wheel_valve_large', modelTitle:'Wheel Valve Large'},
+      {modelPath:'door_handle', modelTitle:'Door Handle'},
+
+      {modelPath:'drc_practice_ball_valve_wall', modelTitle:'Wall (Ball Valve)'},
+      {modelPath:'drc_practice_handle_wheel_valve_wall', modelTitle:'Wall (Handle Wheel Valve)'},
+      {modelPath:'drc_practice_hand_wheel_valve_wall', modelTitle:'Wall (Hand Wheel Valve)'},
+      {modelPath:'drc_practice_valve_wall', modelTitle:'Wall (Valve)'},
+      {modelPath:'drc_practice_wheel_valve_wall', modelTitle:'Wall (Wheel Valve)'},
+      {modelPath:'drc_practice_wheel_valve_large_wall', modelTitle:'Wall (Wheel Valve Large)'},
+      {modelPath:'grey_wall', modelTitle:'Grey Wall'},
+      {modelPath:'asphalt_plane', modelTitle:'Asphalt Plane'},
+      {modelPath:'drc_practice_base_4x8', modelTitle:'Debris base'},
+      {modelPath:'ground_plane', modelTitle:'Ground Plane'},
+      {modelPath:'nist_maze_wall_120', modelTitle:'120 Maze Wall'},
+      {modelPath:'nist_maze_wall_240', modelTitle:'240 Maze Wall'},
+      {modelPath:'nist_maze_wall_triple_holes_120', modelTitle:'120 Maze Wall Triple Holes'},
+      {modelPath:'nist_simple_ramp_120', modelTitle:'Simple Ramp'},
+      {modelPath:'nist_stairs_120', modelTitle:'Stairs'}
+    ]},
+
+    {path:'kitchen', title:'Kitchen',
+    examplePath1:'saucepan',  examplePath2:'beer',  examplePath3:'bowl', models:
+    [
+      {modelPath:'beer', modelTitle:'Beer'},
+      {modelPath:'bowl', modelTitle:'Bowl'},
+      {modelPath:'coke_can', modelTitle:'Coke Can'},
+      {modelPath:'saucepan', modelTitle:'Saucepan'}
+    ]},
+
+    {path:'robocup', title:'Robocup', examplePath1:'robocup_3Dsim_ball',
+    examplePath2:'robocup14_spl_goal', examplePath3:'robocup09_spl_field', models:
+    [
+      {modelPath:'robocup09_spl_field', modelTitle:'2009 SPL Field'},
+      {modelPath:'robocup14_spl_field', modelTitle:'2014 SPL Field'},
+      {modelPath:'robocup_3Dsim_field', modelTitle:'3D Sim. Field'},
+      {modelPath:'robocup14_spl_goal', modelTitle:'SPL Goal'},
+      {modelPath:'robocup_3Dsim_goal', modelTitle:'3D Sim. Goal'},
+      {modelPath:'robocup_spl_ball', modelTitle:'SPL Ball'},
+      {modelPath:'robocup_3Dsim_ball', modelTitle:'3D Sim. Ball'}
+    ]},
+
+    {path:'robots', title:'Robots',
+    examplePath1:'pioneer3at', examplePath2:'turtlebot', examplePath3:'pr2', models:
+    [
+      {modelPath:'create', modelTitle:'Create'},
+      {modelPath:'husky', modelTitle:'Husky'},
+      {modelPath:'irobot_hand', modelTitle:'iRobot Hand'},
+      {modelPath:'pioneer2dx', modelTitle:'Pioneer 2DX'},
+      {modelPath:'pioneer3at', modelTitle:'Pioneer 3AT'},
+      {modelPath:'pr2', modelTitle:'PR2'},
+      {modelPath:'robonaut', modelTitle:'Robonaut'},
+      {modelPath:'simple_arm', modelTitle:'Simple Arm'},
+      {modelPath:'simple_arm_gripper', modelTitle:'Simple Arm and Gripper'},
+      {modelPath:'simple_gripper', modelTitle:'Simple Gripper'},
+      {modelPath:'turtlebot', modelTitle:'TurtleBot'},
+      {modelPath:'youbot', modelTitle:'YouBot'}
+    ]},
+
+    {path:'sensors', title:'Sensors',
+    examplePath1:'camera', examplePath2:'hokuyo', examplePath3:'kinect', models:
+    [
+      {modelPath:'camera', modelTitle:'Camera'},
+      {modelPath:'stereo_camera', modelTitle:'Stereo Camera'},
+      {modelPath:'hokuyo', modelTitle:'Hokuyo'},
+      {modelPath:'kinect', modelTitle:'Kinect'}
+    ]},
+
+    {path:'street', title:'Street', examplePath1:'dumpster',
+    examplePath2:'drc_practice_angled_barrier_45', examplePath3:'fire_hydrant', models:
+    [
+      {modelPath:'cinder_block', modelTitle:'Cinder Block'},
+      {modelPath:'cinder_block_2', modelTitle:'Cinder Block 2'},
+      {modelPath:'cinder_block_wide', modelTitle:'Cinder Block Wide'},
+      {modelPath:'construction_barrel', modelTitle:'Construction Barrel'},
+      {modelPath:'construction_cone', modelTitle:'Construction Cone'},
+      {modelPath:'drc_practice_angled_barrier_45', modelTitle:'Angled Barrier 45'},
+      {modelPath:'drc_practice_angled_barrier_135', modelTitle:'Angled Barrier 135'},
+      {modelPath:'drc_practice_block_wall', modelTitle:'Block Wall'},
+      {modelPath:'drc_practice_orange_jersey_barrier', modelTitle:'Jersey Barrier (Orange)'},
+      {modelPath:'drc_practice_white_jersey_barrier', modelTitle:'Jersey Barrier (White)'},
+      {modelPath:'drc_practice_truss', modelTitle:'Truss'},
+      {modelPath:'drc_practice_yellow_parking_block', modelTitle:'Parking Block'},
+      {modelPath:'dumpster', modelTitle:'Dumpster'},
+      {modelPath:'fire_hydrant', modelTitle:'Fire Hydrant'},
+      {modelPath:'jersey_barrier', modelTitle:'Jersey Barrier'},
+      {modelPath:'lamp_post', modelTitle:'Lamp Post'},
+      {modelPath:'mailbox', modelTitle:'Mailbox'},
+      {modelPath:'mud_box', modelTitle:'Mud Box'},
+      {modelPath:'nist_fiducial_barrel', modelTitle:'Fiducial Barrel'},
+      {modelPath:'speed_limit_sign', modelTitle:'Speed Limit Sign'},
+      {modelPath:'stop_sign', modelTitle:'Stop Sign'}
+
+    ]},
+
+    {path:'tools', title:'Tools', examplePath1:'hammer',
+    examplePath2:'polaris_ranger_ev', examplePath3:'cordless_drill', models:
+    [
+      {modelPath:'cordless_drill', modelTitle:'Cordless Drill'},
+      {modelPath:'fire_hose_long', modelTitle:'Fire Hose'},
+      {modelPath:'fire_hose_long_curled', modelTitle:'Fire Hose Long Curled'},
+      {modelPath:'hammer', modelTitle:'Hammer'},
+      {modelPath:'monkey_wrench', modelTitle:'Monkey Wrench'},
+      {modelPath:'polaris_ranger_ev', modelTitle:'Polaris Ranger EV'},
+      {modelPath:'polaris_ranger_xp900', modelTitle:'Polaris Ranger XP900'},
+      {modelPath:'polaris_ranger_xp900_no_roll_cage', modelTitle:'Polaris Ranger without roll cage'},
+      {modelPath:'utility_cart', modelTitle:'Utility Cart'}
+    ]},
+
+    {path:'misc', title:'Misc.', examplePath1:'brick_box_3x1x3',
+    examplePath2:'drc_practice_4x4x20', examplePath3:'double_pendulum_with_base', models:
+    [
+      {modelPath:'double_pendulum_with_base', modelTitle:'Double Pendulum With Base'},
+      {modelPath:'breakable_test', modelTitle:'Breakable_test'},
+      {modelPath:'brick_box_3x1x3', modelTitle:'Brick Box 3x1x3'},
+      {modelPath:'cube_20k', modelTitle:'Cube 20k'},
+      {modelPath:'drc_practice_2x4', modelTitle:'2x4 Lumber'},
+      {modelPath:'drc_practice_2x6', modelTitle:'2x6 Lumber'},
+      {modelPath:'drc_practice_4x4x20', modelTitle:'4x4x20 Lumber'},
+      {modelPath:'drc_practice_4x4x40', modelTitle:'4x4x40 Lumber'},
+      {modelPath:'drc_practice_blue_cylinder', modelTitle:'Blue Cylinder'},
+      {modelPath:'drc_practice_wood_slats', modelTitle:'Wood Slats'},
+      {modelPath:'nist_elevated_floor_120', modelTitle:'Elevated Floor 120'}
+    ]}
+  ];
+
+$(function()
+{
+  //Initialize
+  if ('ontouchstart' in window || 'onmsgesturechange' in window)
+  {
+    $('body').addClass('isTouchDevice');
+  }
+
+  // Toggle items
+  $('#view-collisions').buttonMarkup({icon: 'false'});
+  $('#snap-to-grid').buttonMarkup({icon: 'false'});
+  $('#open-tree-when-selected').buttonMarkup({icon: 'false'});
+  $('#view-transparent').buttonMarkup({icon: 'false'});
+  $('#view-wireframe').buttonMarkup({icon: 'false'});
+  $('#view-joints').buttonMarkup({icon: 'false'});
+  guiEvents.emit('toggle_notifications');
+  guiEvents.emit('show_orbit_indicator');
+
+  $( '#clock-touch' ).popup('option', 'arrow', 't');
+  $('#notification-popup-screen').remove();
+  $('.tab').css('border-left-color', tabColors.unselected);
+
+  if (isWideScreen())
+  {
+    guiEvents.emit('openTab', 'mainMenu', 'mainMenu');
+  }
+
+  if (isTallScreen())
+  {
+    $('.collapsible_header').click();
+    $('#expand-MODELS').click();
+    $('#expand-LIGHTS').click();
+  }
+
+  // Touch devices
+  if (isTouchDevice)
+  {
+    $('.mouse-only')
+        .css('display','none');
+
+    $('#play-header-fieldset')
+        .css('position', 'absolute')
+        .css('right', '13.6em')
+        .css('top', '0em')
+        .css('z-index', '1000');
+
+    $('#clock-header-fieldset')
+        .css('position', 'absolute')
+        .css('right', '10.2em')
+        .css('top', '0em')
+        .css('z-index', '1000');
+
+    $('#mode-header-fieldset')
+        .css('position', 'absolute')
+        .css('right', '0.5em')
+        .css('top', '0.15em')
+        .css('z-index', '1000');
+
+    $('.gzGUI').touchstart(function(event){
+        guiEvents.emit('pointerOnMenu');
+    });
+
+    $('.gzGUI').touchend(function(event){
+        guiEvents.emit('pointerOffMenu');
+    });
+
+    // long press on canvas
+    var press_time_container = 400;
+    $('#container')
+      .on('touchstart', function (event) {
+        $(this).data('checkdown', setTimeout(function () {
+          guiEvents.emit('longpress_container_start',event);
+        }, press_time_container));
+      })
+      .on('touchend', function (event) {
+        clearTimeout($(this).data('checkdown'));
+        guiEvents.emit('longpress_container_end',event,false);
+      })
+      .on('touchmove', function (event) {
+        clearTimeout($(this).data('checkdown'));
+        $(this).data('checkdown', setTimeout(function () {
+          guiEvents.emit('longpress_container_start',event);
+        }, press_time_container));
+        guiEvents.emit('longpress_container_move',event);
+      });
+
+    // long press on insert menu item
+    var press_time_insert = 400;
+    $('[id^="insert-entity-"]')
+      .on('touchstart', function (event) {
+        var path = $(this).attr('id');
+        path = path.substring(14); // after 'insert-entity-'
+        $(this).data('checkdown', setTimeout(function () {
+          guiEvents.emit('longpress_insert_start', event, path);
+        }, press_time_insert));
+      })
+      .on('touchend', function (event) {
+        clearTimeout($(this).data('checkdown'));
+        guiEvents.emit('longpress_insert_end',event,false);
+      })
+      .on('touchmove', function (event) {
+        clearTimeout($(this).data('checkdown'));
+        guiEvents.emit('longpress_insert_move',event);
+      });
+  }
+  // Mouse devices
+  else
+  {
+    $('.touch-only')
+        .css('display','none');
+
+    $('[id^="insert-entity-"]')
+      .click(function(event) {
+        var path = $(this).attr('id');
+        path = path.substring(14); // after 'insert-entity-'
+        guiEvents.emit('spawn_entity_start', path);
+      })
+      .on('mousedown', function(event) {
+        event.preventDefault();
+      });
+
+    $('#play-header-fieldset')
+        .css('position', 'absolute')
+        .css('right', '41.2em')
+        .css('top', '0em')
+        .css('z-index', '1000');
+
+    $('#clock-mouse')
+        .css('position', 'absolute')
+        .css('right', '29.0em')
+        .css('top', '0.5em')
+        .css('z-index', '100')
+        .css('width', '11em')
+        .css('height', '2.5em')
+        .css('background-color', '#333333')
+        .css('padding', '3px')
+        .css('border-radius', '5px');
+
+    $('#mode-header-fieldset')
+        .css('position', 'absolute')
+        .css('right', '24.4em')
+        .css('top', '0.15em')
+        .css('z-index', '1000');
+
+    $('#box-header-fieldset')
+        .css('position', 'absolute')
+        .css('right', '15.5em')
+        .css('top', '0em')
+        .css('z-index', '1000');
+
+    $('#sphere-header-fieldset')
+        .css('position', 'absolute')
+        .css('right', '12.5em')
+        .css('top', '0em')
+        .css('z-index', '1000');
+
+    $('#cylinder-header-fieldset')
+        .css('position', 'absolute')
+        .css('right', '9.5em')
+        .css('top', '0em')
+        .css('z-index', '1000');
+
+    $('#pointlight-header-fieldset')
+        .css('position', 'absolute')
+        .css('right', '6.5em')
+        .css('top', '0em')
+        .css('z-index', '1000');
+
+    $('#spotlight-header-fieldset')
+        .css('position', 'absolute')
+        .css('right', '3.5em')
+        .css('top', '0em')
+        .css('z-index', '1000');
+
+    $('#directionallight-header-fieldset')
+        .css('position', 'absolute')
+        .css('right', '0.5em')
+        .css('top', '0em')
+        .css('z-index', '1000');
+
+    $('.gzGUI').mouseenter(function(event){
+        guiEvents.emit('pointerOnMenu');
+    });
+
+    $('.gzGUI').mouseleave(function(event){
+        guiEvents.emit('pointerOffMenu');
+    });
+
+    // right-click
+    $('#container').mousedown(function(event)
+        {
+          event.preventDefault();
+          if(event.which === 3)
+          {
+            guiEvents.emit('right_click', event);
+          }
+        });
+
+    $('#model-popup-screen').mousedown(function(event)
+        {
+          $('#model-popup').popup('close');
+        });
+  }
+
+  $('.tab').click(function()
+      {
+        var idTab = $(this).attr('id');
+        var idMenu = idTab.substring(0,idTab.indexOf('Tab'));
+
+        if($('#'+idTab).css('border-left-color') === tabColors.unselected)
+        {
+          guiEvents.emit('openTab', lastOpenMenu[idMenu], idMenu);
+        }
+        else
+        {
+          guiEvents.emit('closeTabs', true);
+        }
+      });
+
+  $('.closePanels').click(function()
+      {
+        guiEvents.emit('closeTabs', true);
+      });
+
+  $('#view-mode').click(function()
+      {
+        guiEvents.emit('manipulation_mode', 'view');
+      });
+  $('#translate-mode').click(function()
+      {
+        guiEvents.emit('manipulation_mode', 'translate');
+      });
+  $('#rotate-mode').click(function()
+      {
+        guiEvents.emit('manipulation_mode', 'rotate');
+      });
+
+  $('[id^="header-insert-"]').click(function()
+      {
+        var entity = $(this).attr('id');
+        entity = entity.substring(14); // after 'header-insert-'
+        guiEvents.emit('closeTabs', false);
+        guiEvents.emit('spawn_entity_start', entity);
+      });
+
+  $('#play').click(function()
+      {
+        if ( $('#playText').html().indexOf('Play') !== -1 )
+        {
+          guiEvents.emit('pause', false);
+          guiEvents.emit('notification_popup','Physics engine running');
+        }
+        else
+        {
+          guiEvents.emit('pause', true);
+          guiEvents.emit('notification_popup','Physics engine paused');
+        }
+      });
+  $('#clock').click(function()
+      {
+        if ($.mobile.activePage.find('#clock-touch').parent().
+            hasClass('ui-popup-active'))
+        {
+          $( '#clock-touch' ).popup('close');
+        }
+        else
+        {
+          var position = $('#clock').offset();
+          $('#notification-popup').popup('close');
+          $('#clock-touch').popup('open', {
+              x:position.left+emUnits(1.6),
+              y:emUnits(4)});
+        }
+      });
+
+  $('#reset-model').click(function()
+      {
+        guiEvents.emit('model_reset');
+        guiEvents.emit('closeTabs', false);
+      });
+  $('#reset-world').click(function()
+      {
+        guiEvents.emit('world_reset');
+        guiEvents.emit('closeTabs', false);
+      });
+  $('#reset-view').click(function()
+      {
+        guiEvents.emit('view_reset');
+        guiEvents.emit('closeTabs', false);
+      });
+  $('#view-grid').click(function()
+      {
+        guiEvents.emit('show_grid', 'toggle');
+        guiEvents.emit('closeTabs', false);
+      });
+  $('#view-collisions').click(function()
+      {
+        guiEvents.emit('show_collision');
+        guiEvents.emit('closeTabs', false);
+      });
+  $('#view-orbit-indicator').click(function()
+      {
+        guiEvents.emit('show_orbit_indicator');
+        guiEvents.emit('closeTabs', false);
+      });
+  $( '#snap-to-grid' ).click(function() {
+    guiEvents.emit('snap_to_grid');
+    guiEvents.emit('closeTabs', false);
+  });
+  $( '#open-tree-when-selected' ).click(function() {
+    guiEvents.emit('openTreeWhenSelected');
+    guiEvents.emit('closeTabs', false);
+  });
+  $( '#toggle-notifications' ).click(function() {
+    guiEvents.emit('toggle_notifications');
+    guiEvents.emit('closeTabs', false);
+  });
+
+  // Disable Esc key to close panel
+  $('body').on('keyup', function(event)
+      {
+        if (event.which === 27)
+        {
+          return false;
+        }
+      });
+
+  // Object menu
+  $( '#view-transparent' ).click(function() {
+    $('#model-popup').popup('close');
+    guiEvents.emit('set_view_as','transparent');
+  });
+
+  $( '#view-wireframe' ).click(function() {
+    $('#model-popup').popup('close');
+    guiEvents.emit('set_view_as','wireframe');
+  });
+
+  $( '#view-joints' ).click(function() {
+    if ($('#view-joints a').css('color') === 'rgb(255, 255, 255)')
+    {
+      $('#model-popup').popup('close');
+      guiEvents.emit('view_joints');
+    }
+  });
+
+  $( '#delete-entity' ).click(function() {
+    guiEvents.emit('delete_entity');
+  });
+
+  $(window).resize(function()
+  {
+    guiEvents.emit('resizePanel');
+  });
+});
+
+function getNameFromPath(path)
+{
+  if(path === 'box')
+  {
+    return 'Box';
+  }
+  if(path === 'sphere')
+  {
+    return 'Sphere';
+  }
+  if(path === 'cylinder')
+  {
+    return 'Cylinder';
+  }
+  if(path === 'pointlight')
+  {
+    return 'Point Light';
+  }
+  if(path === 'spotlight')
+  {
+    return 'Spot Light';
+  }
+  if(path === 'directionallight')
+  {
+    return 'Directional Light';
+  }
+
+  for(var i = 0; i < modelList.length; ++i)
+  {
+    for(var j = 0; j < modelList[i].models.length; ++j)
+    {
+      if(modelList[i].models[j].modelPath === path)
+      {
+        return modelList[i].models[j].modelTitle;
+      }
+    }
+  }
+}
+
+// World tree
+var gzangular = angular.module('gzangular',[]);
+// add ng-right-click
+gzangular.directive('ngRightClick', function($parse)
+{
+  return function(scope, element, attrs)
+      {
+        var fn = $parse(attrs.ngRightClick);
+        element.bind('contextmenu', function(event)
+            {
+              scope.$apply(function()
+                  {
+                    event.preventDefault();
+                    fn(scope, {$event:event});
+                  });
+            });
+      };
+});
+
+gzangular.controller('treeControl', ['$scope', function($scope)
+{
+  $scope.updateStats = function()
+  {
+    $scope.models = modelStats;
+    $scope.lights = lightStats;
+    $scope.scene = sceneStats;
+    $scope.physics = physicsStats;
+    if (!$scope.$$phase)
+    {
+      $scope.$apply();
+    }
+  };
+
+  $scope.selectEntity = function (name)
+  {
+    $('#model-popup').popup('close');
+    guiEvents.emit('openTab', 'propertyPanel-'+name, 'treeMenu');
+    guiEvents.emit('selectEntity', name);
+  };
+
+  $scope.openEntityMenu = function (event, name)
+  {
+    $('#model-popup').popup('close');
+    guiEvents.emit('openEntityPopup', event, name);
+  };
+
+  $scope.openTab = function (tab)
+  {
+    guiEvents.emit('openTab', tab, 'treeMenu');
+  };
+
+  $scope.expandTree = function (tree)
+  {
+    var idContent = 'expandable-' + tree;
+    var idHeader = 'expand-' + tree;
+
+    if ($('#' + idContent).is(':visible'))
+    {
+      $('#' + idContent).hide();
+      $('#' + idHeader+' img').css('transform','rotate(0deg)')
+                              .css('-webkit-transform','rotate(0deg)')
+                              .css('-ms-transform','rotate(0deg)');
+    }
+    else
+    {
+      $('#' + idContent).show();
+      $('#' + idHeader+' img').css('transform','rotate(90deg)')
+                              .css('-webkit-transform','rotate(90deg)')
+                              .css('-ms-transform','rotate(90deg)');
+    }
+  };
+
+  $scope.expandProperty = function (prop, modelName, subPropShortName, subPropName, parentProp)
+  {
+    var idContent = 'expandable-' + prop + '-' + modelName;
+    var idHeader = 'expand-' + prop + '-' + modelName;
+
+    var idContentOthers, idHeaderOthers;
+
+    if (subPropShortName)
+    {
+      idContentOthers = idContent;
+      idHeaderOthers = idHeader;
+      idContent = idContent + '-' + subPropShortName;
+      idHeader = idHeader + '-' + subPropShortName;
+    }
+
+    if ($('#' + idContent).is(':visible'))
+    {
+      $('#' + idContent).hide();
+      $('#' + idHeader+' img').css('transform','rotate(0deg)')
+                              .css('-webkit-transform','rotate(0deg)')
+                              .css('-ms-transform','rotate(0deg)');
+    }
+    else
+    {
+      if (subPropShortName && (prop === 'link' || prop === 'joint'))
+      {
+        $('[id^="' + idContentOthers + '-"]').hide();
+        $('[id^="' + idHeaderOthers + '-"] img')
+            .css('transform','rotate(0deg)')
+            .css('-webkit-transform','rotate(0deg)')
+            .css('-ms-transform','rotate(0deg)');
+      }
+
+      $('#' + idContent).show();
+      $('#' + idHeader+' img').css('transform','rotate(90deg)')
+                              .css('-webkit-transform','rotate(90deg)')
+                              .css('-ms-transform','rotate(90deg)');
+
+      if (prop === 'pose' && parentProp === 'link')
+      {
+        guiEvents.emit('setPoseStats', modelName, subPropName);
+      }
+    }
+  };
+
+  $scope.changePose = function(prop1, prop2, name, value)
+  {
+    guiEvents.emit('setPose', prop1, prop2, name, value);
+  };
+
+  $scope.changeLight = function(prop, name, value)
+  {
+    guiEvents.emit('setLight', prop, name, value);
+  };
+
+  $scope.toggleProperty = function(prop, entity, subEntity)
+  {
+    // only for links so far
+    guiEvents.emit('toggleProperty', prop, entity, subEntity);
+  };
+}]);
+
+// Insert menu
+gzangular.controller('insertControl', ['$scope', function($scope)
+{
+  $scope.categories = modelList;
+
+  $scope.spawnEntity = function(path)
+  {
+    guiEvents.emit('spawn_entity_start', path);
+  };
+
+  $scope.openTab = function (tab)
+  {
+    guiEvents.emit('openTab', tab, 'insertMenu');
+  };
+}]);
+
+
+/**
+ * Graphical user interface
+ * @constructor
+ * @param {GZ3D.Scene} scene - A scene to connect to
+ */
+GZ3D.Gui = function(scene)
+{
+  this.scene = scene;
+  this.domElement = scene.getDomElement();
+  this.init();
+  this.emitter = new EventEmitter2({verbose: true});
+  this.guiEvents = guiEvents;
+};
+
+/**
+ * Initialize GUI
+ */
+GZ3D.Gui.prototype.init = function()
+{
+  this.spawnState = null;
+  this.longPressContainerState = null;
+  this.showNotifications = false;
+  this.openTreeWhenSelected = false;
+
+  var that = this;
+
+  // On guiEvents, emitter events
+  guiEvents.on('manipulation_mode',
+      function(mode)
+      {
+        that.scene.setManipulationMode(mode);
+        var space = that.scene.modelManipulator.space;
+
+        if (mode === 'view')
+        {
+          guiEvents.emit('notification_popup', 'View mode');
+        }
+        else
+        {
+          guiEvents.emit('notification_popup',
+              mode.charAt(0).toUpperCase()+
+              mode.substring(1)+' mode in '+
+              space.charAt(0).toUpperCase()+
+              space.substring(1)+' space');
+        }
+      }
+  );
+
+  // Create temp model
+  guiEvents.on('spawn_entity_start', function(entity)
+      {
+        // manually trigger view mode
+        that.scene.setManipulationMode('view');
+        $('#view-mode').prop('checked', true);
+        $('input[type="radio"]').checkboxradio('refresh');
+
+        var name = getNameFromPath(entity);
+
+        that.spawnState = 'START';
+        that.scene.spawnModel.start(entity,function(obj)
+            {
+              that.emitter.emit('entityCreated', obj, entity);
+            });
+        guiEvents.emit('notification_popup',
+            'Place '+name+' at the desired position');
+      }
+  );
+
+  // Move temp model by touch
+  guiEvents.on('spawn_entity_move', function(event)
+      {
+        that.spawnState = 'MOVE';
+        that.scene.spawnModel.onTouchMove(event,false);
+      }
+  );
+  // Place temp model by touch
+  guiEvents.on('spawn_entity_end', function()
+      {
+        if (that.spawnState === 'MOVE')
+        {
+          that.scene.spawnModel.onTouchEnd();
+        }
+        that.spawnState = null;
+      }
+  );
+
+  guiEvents.on('world_reset', function()
+      {
+        that.emitter.emit('reset', 'world');
+        guiEvents.emit('notification_popup','Reset world');
+      }
+  );
+
+  guiEvents.on('model_reset', function()
+      {
+        that.emitter.emit('reset', 'model');
+        guiEvents.emit('notification_popup','Reset model poses');
+      }
+  );
+
+  guiEvents.on('view_reset', function()
+      {
+        that.scene.resetView();
+        guiEvents.emit('notification_popup','Reset view');
+      }
+  );
+
+  guiEvents.on('pause', function(paused)
+      {
+        that.emitter.emit('pause', paused);
+      }
+  );
+
+  guiEvents.on('show_collision', function()
+      {
+        that.scene.showCollision(!that.scene.showCollisions);
+        if(!that.scene.showCollisions)
+        {
+          $('#view-collisions').buttonMarkup({icon: 'false'});
+          guiEvents.emit('notification_popup','Hiding collisions');
+        }
+        else
+        {
+          $('#view-collisions').buttonMarkup({icon: 'check'});
+          guiEvents.emit('notification_popup','Viewing collisions');
+        }
+      }
+  );
+
+  guiEvents.on('show_grid', function(option)
+      {
+        if (option === 'show')
+        {
+          that.scene.grid.visible = true;
+        }
+        else if (option === 'hide')
+        {
+          that.scene.grid.visible = false;
+        }
+        else if (option === 'toggle')
+        {
+          that.scene.grid.visible = !that.scene.grid.visible;
+        }
+
+        if(!that.scene.grid.visible)
+        {
+          $('#view-grid').buttonMarkup({icon: 'false'});
+          guiEvents.emit('notification_popup','Hiding grid');
+        }
+        else
+        {
+          $('#view-grid').buttonMarkup({icon: 'check'});
+          guiEvents.emit('notification_popup','Viewing grid');
+        }
+      }
+  );
+
+   guiEvents.on('show_orbit_indicator', function()
+      {
+        that.scene.controls.showTargetIndicator =
+            !that.scene.controls.showTargetIndicator;
+        if(!that.scene.controls.showTargetIndicator)
+        {
+          $('#view-orbit-indicator').buttonMarkup({icon: 'false'});
+          guiEvents.emit('notification_popup','Hiding orbit indicator');
+        }
+        else
+        {
+          $('#view-orbit-indicator').buttonMarkup({icon: 'check'});
+          guiEvents.emit('notification_popup','Viewing orbit indicator');
+        }
+      }
+  );
+
+  guiEvents.on('snap_to_grid',
+      function ()
+      {
+        if(that.scene.modelManipulator.snapDist === null)
+        {
+          $('#snap-to-grid').buttonMarkup({icon: 'check'});
+          that.scene.modelManipulator.snapDist = 0.5;
+          that.scene.spawnModel.snapDist = that.scene.modelManipulator.snapDist;
+          guiEvents.emit('notification_popup','Snapping to grid');
+        }
+        else
+        {
+          $('#snap-to-grid').buttonMarkup({icon: 'false'});
+          that.scene.modelManipulator.snapDist = null;
+          that.scene.spawnModel.snapDist = null;
+          guiEvents.emit('notification_popup','Not snapping to grid');
+        }
+      }
+  );
+
+  guiEvents.on('openTreeWhenSelected', function ()
+      {
+        this.openTreeWhenSelected = !this.openTreeWhenSelected;
+        if(!this.openTreeWhenSelected)
+        {
+          $('#open-tree-when-selected').buttonMarkup({icon: 'false'});
+        }
+        else
+        {
+          $('#open-tree-when-selected').buttonMarkup({icon: 'check'});
+        }
+      }
+  );
+
+  guiEvents.on('toggle_notifications', function ()
+      {
+        this.showNotifications = !this.showNotifications;
+        if(!this.showNotifications)
+        {
+          $('#toggle-notifications').buttonMarkup({icon: 'false'});
+        }
+        else
+        {
+          $('#toggle-notifications').buttonMarkup({icon: 'check'});
+        }
+      }
+  );
+
+
+  guiEvents.on('longpress_container_start',
+      function (event)
+      {
+        if (event.originalEvent.touches.length !== 1 ||
+            that.scene.modelManipulator.hovered ||
+            that.scene.spawnModel.active)
+        {
+          guiEvents.emit('longpress_container_end', event.originalEvent,true);
+        }
+        else
+        {
+          that.scene.showRadialMenu(event);
+          that.longPressContainerState = 'START';
+        }
+      }
+  );
+
+  guiEvents.on('longpress_container_end', function(event,cancel)
+      {
+        if (that.longPressContainerState !== 'START')
+        {
+          that.longPressContainerState = 'END';
+          return;
+        }
+        that.longPressContainerState = 'END';
+        if (that.scene.radialMenu.showing)
+        {
+          if (cancel)
+          {
+            that.scene.radialMenu.hide(event);
+          }
+          else
+          {
+          that.scene.radialMenu.hide(event, function(type,entity)
+              {
+                if (type === 'delete')
+                {
+                  that.emitter.emit('deleteEntity',entity);
+                  that.scene.setManipulationMode('view');
+                  $( '#view-mode' ).prop('checked', true);
+                  $('input[type="radio"]').checkboxradio('refresh');
+                }
+                else if (type === 'translate')
+                {
+                  $('#translate-mode').click();
+                  $('input[type="radio"]').checkboxradio('refresh');
+                  that.scene.attachManipulator(entity,type);
+                }
+                else if (type === 'rotate')
+                {
+                  $( '#rotate-mode' ).click();
+                  $('input[type="radio"]').checkboxradio('refresh');
+                  that.scene.attachManipulator(entity,type);
+                }
+                else if (type === 'transparent')
+                {
+                  guiEvents.emit('set_view_as','transparent');
+                }
+                else if (type === 'wireframe')
+                {
+                  guiEvents.emit('set_view_as','wireframe');
+                }
+                else if (type === 'joints')
+                {
+                  that.scene.selectEntity(entity);
+                  guiEvents.emit('view_joints');
+                }
+
+              });
+          }
+        }
+      }
+  );
+
+  guiEvents.on('longpress_container_move', function(event)
+      {
+        if (event.originalEvent.touches.length !== 1)
+        {
+          guiEvents.emit('longpress_container_end',event.originalEvent,true);
+        }
+        else
+        {
+          if (that.longPressContainerState !== 'START')
+          {
+            return;
+          }
+          if (that.scene.radialMenu.showing)
+          {
+            that.scene.radialMenu.onLongPressMove(event);
+          }
+        }
+      }
+  );
+
+  guiEvents.on('longpress_insert_start', function (event, path)
+      {
+        navigator.vibrate(50);
+        guiEvents.emit('spawn_entity_start', path);
+        event.stopPropagation();
+      }
+  );
+
+  guiEvents.on('longpress_insert_end', function(event)
+      {
+        guiEvents.emit('spawn_entity_end');
+      }
+  );
+
+  guiEvents.on('longpress_insert_move', function(event)
+      {
+        guiEvents.emit('spawn_entity_move', event);
+        event.stopPropagation();
+      }
+  );
+
+  var notificationTimeout;
+  guiEvents.on('notification_popup',
+      function (notification, duration)
+      {
+        if (this.showNotifications)
+        {
+          clearTimeout(notificationTimeout);
+          $( '#notification-popup' ).popup('close');
+          $( '#notification-popup' ).html('&nbsp;'+notification+'&nbsp;');
+          $( '#notification-popup' ).popup('open', {
+              y:window.innerHeight-50});
+          
+          if (duration === undefined)
+          {
+            duration = 2000;
+          }
+          notificationTimeout = setTimeout(function()
+          {
+            $( '#notification-popup' ).popup('close');
+          }, duration);
+        }
+      }
+  );
+
+  guiEvents.on('right_click', function (event)
+      {
+        that.scene.onRightClick(event, function(entity)
+            {
+              that.openEntityPopup(event, entity);
+            });
+      }
+  );
+
+  guiEvents.on('set_view_as', function (viewAs)
+      {
+        that.scene.setViewAs(that.scene.selectedEntity, viewAs);
+      }
+  );
+
+  guiEvents.on('view_joints', function ()
+      {
+        that.scene.viewJoints(that.scene.selectedEntity);
+      }
+  );
+
+  guiEvents.on('delete_entity', function ()
+      {
+        that.emitter.emit('deleteEntity',that.scene.selectedEntity);
+        $('#model-popup').popup('close');
+        that.scene.selectEntity(null);
+      }
+  );
+
+  guiEvents.on('pointerOnMenu', function ()
+      {
+        that.scene.pointerOnMenu = true;
+      }
+  );
+
+  guiEvents.on('pointerOffMenu', function ()
+      {
+        that.scene.pointerOnMenu = false;
+      }
+  );
+
+  guiEvents.on('openTab', function (id, parentId)
+      {
+        lastOpenMenu[parentId] = id;
+
+        $('.leftPanels').hide();
+        $('#'+id).show();
+
+        $('.tab').css('border-left-color', tabColors.unselected);
+        $('#'+parentId+'Tab').css('border-left-color', tabColors.selected);
+
+        if (id.indexOf('propertyPanel-') >= 0)
+        {
+          var entityName = id.substring(id.indexOf('-')+1);
+          var object = that.scene.getByName(entityName);
+
+          var stats = {};
+          stats.name = entityName;
+
+          stats.pose = {};
+          stats.pose.position = {x: object.position.x,
+                                 y: object.position.y,
+                                 z: object.position.z};
+
+          stats.pose.orientation = {x: object.quaternion._x,
+                                    y: object.quaternion._y,
+                                    z: object.quaternion._z,
+                                    w: object.quaternion._w};
+        }
+
+        guiEvents.emit('resizePanel');
+      }
+  );
+
+  guiEvents.on('closeTabs', function (force)
+      {
+        // Close for narrow viewports, force to always close
+        if (force || !isWideScreen())
+        {
+          $('.leftPanels').hide();
+          $('.tab').css('left', '0em');
+          $('.tab').css('border-left-color', tabColors.unselected);
+        }
+      }
+  );
+
+  guiEvents.on('setTreeSelected', function (object)
+      {
+        for (var i = 0; i < modelStats.length; ++i)
+        {
+          if (modelStats[i].name === object)
+          {
+            modelStats[i].selected = 'selectedTreeItem';
+            if (this.openTreeWhenSelected)
+            {
+              guiEvents.emit('openTab', 'propertyPanel-'+object, 'treeMenu');
+            }
+          }
+          else
+          {
+            modelStats[i].selected = 'unselectedTreeItem';
+          }
+        }
+        for (i = 0; i < lightStats.length; ++i)
+        {
+          if (lightStats[i].name === object)
+          {
+            lightStats[i].selected = 'selectedTreeItem';
+            if (this.openTreeWhenSelected)
+            {
+              guiEvents.emit('openTab', 'propertyPanel-'+object, 'treeMenu');
+            }
+          }
+          else
+          {
+            lightStats[i].selected = 'unselectedTreeItem';
+          }
+        }
+        that.updateStats();
+      }
+  );
+
+  guiEvents.on('setTreeDeselected', function ()
+      {
+        for (var i = 0; i < modelStats.length; ++i)
+        {
+          modelStats[i].selected = 'unselectedTreeItem';
+        }
+        for (i = 0; i < lightStats.length; ++i)
+        {
+          lightStats[i].selected = 'unselectedTreeItem';
+        }
+        that.updateStats();
+      }
+  );
+
+  guiEvents.on('selectEntity', function (name)
+      {
+        var object = that.scene.getByName(name);
+        that.scene.selectEntity(object);
+      }
+  );
+
+  guiEvents.on('openEntityPopup', function (event, name)
+      {
+        if (!isTouchDevice)
+        {
+          var object = that.scene.getByName(name);
+          that.openEntityPopup(event, object);
+        }
+      }
+  );
+
+  guiEvents.on('setPoseStats', function (modelName, linkName)
+      {
+        var object;
+        if (linkName === undefined)
+        {
+          object = that.scene.getByName(modelName);
+        }
+        else
+        {
+          object = that.scene.getByName(linkName);
+        }
+
+        var stats = {};
+        stats.name = object.name;
+        stats.pose = {};
+        stats.pose.position = {x: object.position.x,
+                               y: object.position.y,
+                               z: object.position.z};
+        stats.pose.orientation = {x: object.quaternion._x,
+                                  y: object.quaternion._y,
+                                  z: object.quaternion._z,
+                                  w: object.quaternion._w};
+
+        if (object.children[0] instanceof THREE.Light)
+        {
+          that.setLightStats(stats, 'update');
+        }
+        else
+        {
+          that.setModelStats(stats, 'update');
+        }
+      }
+  );
+
+  guiEvents.on('resizePanel', function ()
+      {
+        if ($('.leftPanels').is(':visible'))
+        {
+          if (isWideScreen())
+          {
+            $('.tab').css('left', '23em');
+          }
+          else
+          {
+            $('.tab').css('left', '10.5em');
+          }
+        }
+
+        if ($('.propertyPanels').is(':visible'))
+        {
+          var maxWidth = $(window).width();
+          if (isWideScreen())
+          {
+            maxWidth = emUnits(23);
+          }
+
+          $('.propertyPanels').css('width', maxWidth);
+        }
+      }
+  );
+
+  guiEvents.on('setPose', function (prop1, prop2, name, value)
+      {
+        if (value === undefined)
+        {
+          return;
+        }
+
+        var entity = that.scene.getByName(name);
+        if (prop1 === 'orientation')
+        {
+          entity['rotation']['_'+prop2] = value;
+          entity['quaternion'].setFromEuler(entity['rotation']);
+        }
+        else
+        {
+          entity[prop1][prop2] = value;
+        }
+        entity.updateMatrixWorld();
+
+        if (entity.children[0] &&
+           (entity.children[0] instanceof THREE.SpotLight ||
+            entity.children[0] instanceof THREE.DirectionalLight))
+        {
+          var lightObj = entity.children[0];
+          var dir = new THREE.Vector3(0,0,0);
+          dir.copy(entity.direction);
+          entity.localToWorld(dir);
+          lightObj.target.position.copy(dir);
+        }
+
+        that.scene.emitter.emit('entityChanged', entity);
+      }
+  );
+
+  guiEvents.on('setLight', function (prop, name, value)
+      {
+        if (value === undefined)
+        {
+          return;
+        }
+
+        var entity = that.scene.getByName(name);
+        var lightObj = entity.children[0];
+        if (prop === 'diffuse')
+        {
+          lightObj.color = new THREE.Color(value);
+        }
+        else if (prop === 'specular')
+        {
+          entity.serverProperties.specular = new THREE.Color(value);
+        }
+        else if (prop === 'range')
+        {
+          lightObj.distance = value;
+        }
+        else if (prop === 'attenuation_constant')
+        {
+          entity.serverProperties.attenuation_constant = value;
+        }
+        else if (prop === 'attenuation_linear')
+        {
+          entity.serverProperties.attenuation_linear = value;
+          lightObj.intensity = lightObj.intensity/(1+value);
+        }
+        else if (prop === 'attenuation_quadratic')
+        {
+          entity.serverProperties.attenuation_quadratic = value;
+          lightObj.intensity = lightObj.intensity/(1+value);
+        }
+
+        // updating color too often, maybe only update when popup is closed
+        that.scene.emitter.emit('entityChanged', entity);
+      }
+  );
+
+  guiEvents.on('toggleProperty', function (prop, subEntityName)
+      {
+        var entity = that.scene.getByName(subEntityName);
+        entity.serverProperties[prop] = !entity.serverProperties[prop];
+
+        that.scene.emitter.emit('linkChanged', entity);
+      }
+  );
+};
+
+/**
+ * Play/pause simulation
+ * @param {boolean} paused
+ */
+GZ3D.Gui.prototype.setPaused = function(paused)
+{
+  if (paused)
+  {
+    $('#playText').html(
+        '<img style="height:1.2em" src="style/images/play.png" title="Play">');
+  }
+  else
+  {
+    $('#playText').html(
+        '<img style="height:1.2em" src="style/images/pause.png" title="Pause">'
+        );
+  }
+};
+
+/**
+ * Update displayed real time
+ * @param {string} realTime
+ */
+GZ3D.Gui.prototype.setRealTime = function(realTime)
+{
+  $('.real-time-value').text(realTime);
+};
+
+/**
+ * Update displayed simulation time
+ * @param {string} simTime
+ */
+GZ3D.Gui.prototype.setSimTime = function(simTime)
+{
+  $('.sim-time-value').text(simTime);
+};
+
+var sceneStats = {};
+/**
+ * Update scene stats on scene tree
+ * @param {} stats
+ */
+GZ3D.Gui.prototype.setSceneStats = function(stats)
+{
+  sceneStats['ambient'] = this.round(stats.ambient, true);
+  sceneStats['background'] = this.round(stats.background, true);
+};
+
+var physicsStats = {};
+/**
+ * Update physics stats on scene tree
+ * @param {} stats
+ */
+GZ3D.Gui.prototype.setPhysicsStats = function(stats)
+{
+  physicsStats = stats;
+  physicsStats['enable_physics'] = this.trueOrFalse(
+      physicsStats['enable_physics']);
+  physicsStats['max_step_size'] = this.round(
+      physicsStats['max_step_size'], false, 3);
+  physicsStats['gravity'] = this.round(
+      physicsStats['gravity'], false, 3);
+  physicsStats['sor'] = this.round(
+      physicsStats['sor'], false, 3);
+  physicsStats['cfm'] = this.round(
+      physicsStats['cfm'], false, 3);
+  physicsStats['erp'] = this.round(
+      physicsStats['erp'], false, 3);
+  physicsStats['contact_max_correcting_vel'] = this.round(
+      physicsStats['contact_max_correcting_vel'], false, 3);
+  physicsStats['contact_surface_layer'] = this.round(
+      physicsStats['contact_surface_layer'], false, 3);
+
+  this.updateStats();
+};
+
+var modelStats = [];
+/**
+ * Update model stats on property panel
+ * @param {} stats
+ * @param {} action: 'update' / 'delete'
+ */
+GZ3D.Gui.prototype.setModelStats = function(stats, action)
+{
+  var modelName = stats.name;
+  var linkShortName;
+
+  // if it's a link
+  if (stats.name.indexOf('::') >= 0)
+  {
+    modelName = stats.name.substring(0, stats.name.indexOf('::'));
+    linkShortName = stats.name.substring(stats.name.lastIndexOf('::')+2);
+  }
+
+  if (action === 'update')
+  {
+    var model = $.grep(modelStats, function(e)
+        {
+          return e.name === modelName;
+        });
+
+    var formatted;
+
+    // New model
+    if (model.length === 0)
+    {
+      var thumbnail = this.findModelThumbnail(modelName);
+
+      formatted = this.formatStats(stats);
+
+      modelStats.push(
+          {
+            name: modelName,
+            thumbnail: thumbnail,
+            selected: 'unselectedTreeItem',
+            is_static: this.trueOrFalse(stats.is_static),
+            position: formatted.pose.position,
+            orientation: formatted.pose.orientation,
+            links: [],
+            joints: []
+          });
+
+      var newModel = modelStats[modelStats.length-1];
+
+      // links
+      for (var l = 0; l < stats.link.length; ++l)
+      {
+        var shortName = stats.link[l].name.substring(
+            stats.link[l].name.lastIndexOf('::')+2);
+
+        formatted = this.formatStats(stats.link[l]);
+
+        newModel.links.push(
+            {
+              name: stats.link[l].name,
+              shortName: shortName,
+              self_collide: this.trueOrFalse(stats.link[l].self_collide),
+              gravity: this.trueOrFalse(stats.link[l].gravity),
+              kinematic: this.trueOrFalse(stats.link[l].kinematic),
+              canonical: this.trueOrFalse(stats.link[l].canonical),
+              position: formatted.pose.position,
+              orientation: formatted.pose.orientation,
+              inertial: formatted.inertial
+            });
+      }
+
+      // joints
+      for (var j = 0; j < stats.joint.length; ++j)
+      {
+        var jointShortName = stats.joint[j].name.substring(
+            stats.joint[j].name.lastIndexOf('::')+2);
+        var parentShortName = stats.joint[j].parent.substring(
+            stats.joint[j].parent.lastIndexOf('::')+2);
+        var childShortName = stats.joint[j].child.substring(
+            stats.joint[j].child.lastIndexOf('::')+2);
+
+        var type;
+        switch (stats.joint[j].type)
+        {
+          case 1:
+              type = 'Revolute';
+              break;
+          case 2:
+              type = 'Revolute2';
+              break;
+          case 3:
+              type = 'Prismatic';
+              break;
+          case 4:
+              type = 'Universal';
+              break;
+          case 5:
+              type = 'Ball';
+              break;
+          case 6:
+              type = 'Screw';
+              break;
+          case 7:
+              type = 'Gearbox';
+              break;
+          default:
+              type = 'Unknown';
+        }
+
+        formatted = this.formatStats(stats.joint[j]);
+
+        newModel.joints.push(
+            {
+              name: stats.joint[j].name,
+              shortName: jointShortName,
+              type: type,
+              parent: stats.joint[j].parent,
+              parentShortName: parentShortName,
+              child: stats.joint[j].child,
+              childShortName: childShortName,
+              position: formatted.pose.position,
+              orientation: formatted.pose.orientation,
+              axis1: formatted.axis1,
+              axis2: formatted.axis2
+            });
+      }
+    }
+    // Update existing model
+    else
+    {
+      var link;
+
+      if (stats.link && stats.link[0])
+      {
+        var LinkShortName = stats.link[0].name;
+
+        link = $.grep(model[0].links, function(e)
+            {
+              return e.shortName === LinkShortName;
+            });
+
+        if (link[0].self_collide)
+        {
+          link[0].self_collide = this.trueOrFalse(stats.link[0].self_collide);
+        }
+        if (link[0].gravity)
+        {
+          link[0].gravity = this.trueOrFalse(stats.link[0].gravity);
+        }
+        if (link[0].kinematic)
+        {
+          link[0].kinematic = this.trueOrFalse(stats.link[0].kinematic);
+        }
+      }
+
+      // Update pose stats only if they're being displayed and are not focused
+      if (!((linkShortName &&
+          !$('#expandable-pose-'+modelName+'-'+linkShortName).is(':visible'))||
+          (!linkShortName &&
+          !$('#expandable-pose-'+modelName).is(':visible'))||
+          $('#expandable-pose-'+modelName+' input').is(':focus')))
+      {
+
+        if (stats.position)
+        {
+          stats.pose = {};
+          stats.pose.position = stats.position;
+          stats.pose.orientation = stats.orientation;
+        }
+
+        if (stats.pose)
+        {
+          formatted = this.formatStats(stats);
+
+          if (linkShortName === undefined)
+          {
+            model[0].position = formatted.pose.position;
+            model[0].orientation = formatted.pose.orientation;
+          }
+          else
+          {
+            link = $.grep(model[0].links, function(e)
+              {
+                return e.shortName === linkShortName;
+              });
+
+            link[0].position = formatted.pose.position;
+            link[0].orientation = formatted.pose.orientation;
+          }
+        }
+      }
+    }
+  }
+  else if (action === 'delete')
+  {
+    this.deleteFromStats('model', modelName);
+  }
+
+  this.updateStats();
+};
+
+var lightStats = [];
+/**
+ * Update light stats on property panel
+ * @param {} stats
+ * @param {} action: 'update' / 'delete'
+ */
+GZ3D.Gui.prototype.setLightStats = function(stats, action)
+{
+  var name = stats.name;
+
+  if (action === 'update')
+  {
+    var light = $.grep(lightStats, function(e)
+        {
+          return e.name === name;
+        });
+
+    var formatted;
+
+    // New light
+    if (light.length === 0)
+    {
+      var type = stats.type;
+
+      var thumbnail;
+      switch(type)
+      {
+        case 2:
+            thumbnail = 'style/images/spotlight.png';
+            break;
+        case 3:
+            thumbnail = 'style/images/directionallight.png';
+            break;
+        default:
+            thumbnail = 'style/images/pointlight.png';
+      }
+
+      stats.attenuation = {constant: stats.attenuation_constant,
+                           linear: stats.attenuation_linear,
+                           quadratic: stats.attenuation_quadratic};
+
+      formatted = this.formatStats(stats);
+
+      var direction;
+      if (stats.direction)
+      {
+        direction = stats.direction;
+      }
+
+      lightStats.push(
+          {
+            name: name,
+            thumbnail: thumbnail,
+            selected: 'unselectedTreeItem',
+            position: formatted.pose.position,
+            orientation: formatted.pose.orientation,
+            diffuse: formatted.diffuse,
+            specular: formatted.specular,
+            color: formatted.color,
+            range: stats.range,
+            attenuation: this.round(stats.attenuation, false, null),
+            direction: direction
+          });
+    }
+    else
+    {
+      formatted = this.formatStats(stats);
+
+      if (stats.pose)
+      {
+        light[0].position = formatted.pose.position;
+        light[0].orientation = formatted.pose.orientation;
+      }
+
+      if (stats.diffuse)
+      {
+        light[0].diffuse = formatted.diffuse;
+      }
+
+      if (stats.specular)
+      {
+        light[0].specular = formatted.specular;
+      }
+    }
+  }
+  else if (action === 'delete')
+  {
+    this.deleteFromStats('light', name);
+  }
+
+  this.updateStats();
+};
+
+/**
+ * Find thumbnail
+ * @param {} instanceName
+ * @returns string
+ */
+GZ3D.Gui.prototype.findModelThumbnail = function(instanceName)
+{
+  for(var i = 0; i < modelList.length; ++i)
+  {
+    for(var j = 0; j < modelList[i].models.length; ++j)
+    {
+      var path = modelList[i].models[j].modelPath;
+      if(instanceName.indexOf(path) >= 0)
+      {
+        return '/assets/'+path+'/thumbnails/0.png';
+      }
+    }
+  }
+  if(instanceName.indexOf('box') >= 0)
+  {
+    return 'style/images/box.png';
+  }
+  if(instanceName.indexOf('sphere') >= 0)
+  {
+    return 'style/images/sphere.png';
+  }
+  if(instanceName.indexOf('cylinder') >= 0)
+  {
+    return 'style/images/cylinder.png';
+  }
+  return 'style/images/box.png';
+};
+
+/**
+ * Update model stats
+ */
+GZ3D.Gui.prototype.updateStats = function()
+{
+  var tree = angular.element($('#treeMenu')).scope();
+  tree.updateStats();
+};
+
+/**
+ * Open entity (model/light) context menu
+ * @param {} event
+ * @param {THREE.Object3D} entity
+ */
+GZ3D.Gui.prototype.openEntityPopup = function(event, entity)
+{
+  this.scene.selectEntity(entity);
+  $('.ui-popup').popup('close');
+
+  if (entity.children[0] instanceof THREE.Light)
+  {
+    $('#view-transparent').css('visibility','collapse');
+    $('#view-wireframe').css('visibility','collapse');
+    $('#view-joints').css('visibility','collapse');
+    $('#model-popup').popup('open',
+      {x: event.clientX + emUnits(6),
+       y: event.clientY + emUnits(-8)});
+  }
+  else
+  {
+    if (this.scene.selectedEntity.viewAs === 'transparent')
+    {
+      $('#view-transparent').buttonMarkup({icon: 'check'});
+    }
+    else
+    {
+      $('#view-transparent').buttonMarkup({icon: 'false'});
+    }
+
+    if (this.scene.selectedEntity.viewAs === 'wireframe')
+    {
+      $('#view-wireframe').buttonMarkup({icon: 'check'});
+    }
+    else
+    {
+      $('#view-wireframe').buttonMarkup({icon: 'false'});
+    }
+
+    if (entity.joint === undefined || entity.joint.length === 0)
+    {
+      $('#view-joints a').css('color', '#888888');
+      $('#view-joints').buttonMarkup({icon: 'false'});
+    }
+    else
+    {
+      $('#view-joints a').css('color', '#ffffff');
+      if (entity.getObjectByName('JOINT_VISUAL', true))
+      {
+        $('#view-joints').buttonMarkup({icon: 'check'});
+      }
+      else
+      {
+        $('#view-joints').buttonMarkup({icon: 'false'});
+      }
+    }
+
+    $('#view-transparent').css('visibility','visible');
+    $('#view-wireframe').css('visibility','visible');
+    $('#view-joints').css('visibility','visible');
+    $('#model-popup').popup('open',
+      {x: event.clientX + emUnits(6),
+       y: event.clientY + emUnits(0)});
+  }
+};
+
+/**
+ * Format stats message for proper display
+ * @param {} stats
+ * @returns {Object.<position, orientation, inertial, diffuse, specular, attenuation>}
+ */
+GZ3D.Gui.prototype.formatStats = function(stats)
+{
+  var position, orientation;
+  var quat, rpy;
+  if (stats.pose)
+  {
+    position = this.round(stats.pose.position, false, null);
+
+    quat = new THREE.Quaternion(stats.pose.orientation.x,
+        stats.pose.orientation.y, stats.pose.orientation.z,
+        stats.pose.orientation.w);
+
+    rpy = new THREE.Euler();
+    rpy.setFromQuaternion(quat);
+
+    orientation = {roll: rpy._x, pitch: rpy._y, yaw: rpy._z};
+    orientation = this.round(orientation, false, null);
+  }
+  var inertial;
+  if (stats.inertial)
+  {
+    inertial = this.round(stats.inertial, false, 3);
+
+    var inertialPose = stats.inertial.pose;
+    inertial.pose = {};
+
+    inertial.pose.position = {x: inertialPose.position.x,
+                              y: inertialPose.position.y,
+                              z: inertialPose.position.z};
+
+    inertial.pose.position = this.round(inertial.pose.position, false, 3);
+
+    quat = new THREE.Quaternion(inertialPose.orientation.x,
+        inertialPose.orientation.y, inertialPose.orientation.z,
+        inertialPose.orientation.w);
+
+    rpy = new THREE.Euler();
+    rpy.setFromQuaternion(quat);
+
+    inertial.pose.orientation = {roll: rpy._x, pitch: rpy._y, yaw: rpy._z};
+    inertial.pose.orientation = this.round(inertial.pose.orientation, false, 3);
+  }
+  var diffuse, colorHex, comp;
+  var color = {};
+  if (stats.diffuse)
+  {
+    diffuse = this.round(stats.diffuse, true);
+
+    colorHex = {};
+    for (comp in diffuse)
+    {
+      colorHex[comp] = diffuse[comp].toString(16);
+      if (colorHex[comp].length === 1)
+      {
+        colorHex[comp] = '0' + colorHex[comp];
+      }
+    }
+    color.diffuse = '#' + colorHex['r'] + colorHex['g'] + colorHex['b'];
+  }
+  var specular;
+  if (stats.specular)
+  {
+    specular = this.round(stats.specular, true);
+
+    colorHex = {};
+    for (comp in specular)
+    {
+      colorHex[comp] = specular[comp].toString(16);
+      if (colorHex[comp].length === 1)
+      {
+        colorHex[comp] = '0' + colorHex[comp];
+      }
+    }
+    color.specular = '#' + colorHex['r'] + colorHex['g'] + colorHex['b'];
+  }
+  var axis1;
+  if (stats.axis1)
+  {
+    axis1 = {};
+    axis1 = this.round(stats.axis1);
+    axis1.direction = this.round(stats.axis1.xyz, false, 3);
+  }
+  var axis2;
+  if (stats.axis2)
+  {
+    axis2 = {};
+    axis2 = this.round(stats.axis2);
+    axis2.direction = this.round(stats.axis2.xyz, false, 3);
+  }
+
+  return {pose: {position: position, orientation: orientation},
+          inertial: inertial,
+          diffuse: diffuse,
+          specular: specular,
+          color: color,
+          axis1: axis1,
+          axis2: axis2};
+};
+
+/**
+ * Round numbers and format colors
+ * @param {} stats
+ * @param {} decimals - number of decimals to display, null for input fields
+ * @returns result
+ */
+GZ3D.Gui.prototype.round = function(stats, isColor, decimals)
+{
+  var result = stats;
+  if (typeof result === 'number')
+  {
+    result = this.roundNumber(result, isColor, decimals);
+  }
+  else // array of numbers
+  {
+    result = this.roundArray(result, isColor, decimals);
+  }
+  return result;
+};
+
+/**
+ * Round number and format color
+ * @param {} stats
+ * @param {} decimals - number of decimals to display, null for input fields
+ * @returns result
+ */
+GZ3D.Gui.prototype.roundNumber = function(stats, isColor, decimals)
+{
+  var result = stats;
+  if (isColor)
+  {
+    result = Math.round(result * 255);
+  }
+  else
+  {
+    if (decimals === null)
+    {
+      result = Math.round(result*1000)/1000;
+    }
+    else
+    {
+      result = result.toFixed(decimals);
+    }
+  }
+  return result;
+};
+
+/**
+ * Round each number in an array
+ * @param {} stats
+ * @param {} decimals - number of decimals to display, null for input fields
+ * @returns result
+ */
+GZ3D.Gui.prototype.roundArray = function(stats, isColor, decimals)
+{
+  var result = stats;
+  for (var key in result)
+  {
+    if (typeof result[key] === 'number')
+    {
+      result[key] = this.roundNumber(result[key], isColor, decimals);
+    }
+  }
+  return result;
+};
+
+/**
+ * Format toggle items
+ * @param {} stats: true / false
+ * @returns {Object.<icon, title>}
+ */
+GZ3D.Gui.prototype.trueOrFalse = function(stats)
+{
+  return stats ?
+      {icon: 'true', title: 'True'} :
+      {icon: 'false', title: 'False'};
+};
+
+/**
+ * Delete an entity from stats list
+ * @param {} type: 'model' / 'light'
+ * @param {} name
+ */
+GZ3D.Gui.prototype.deleteFromStats = function(type, name)
+{
+  var list = (type === 'model') ? modelStats : lightStats;
+
+  for (var i = 0; i < list.length; ++i)
+  {
+    if (list[i].name === name)
+    {
+      if ($('#propertyPanel-'+name).is(':visible'))
+      {
+        guiEvents.emit('openTab', 'treeMenu', 'treeMenu');
+      }
+
+      list.splice(i, 1);
+      break;
+    }
+  }
+};
+
diff --git a/gz3d/src/gziface.js b/gz3d/src/gziface.js
new file mode 100644
index 0000000000000000000000000000000000000000..ccb90e0561140127b7d3dbcc2bca326fe5e52b32
--- /dev/null
+++ b/gz3d/src/gziface.js
@@ -0,0 +1,1412 @@
+//var GAZEBO_MODEL_DATABASE_URI='http://gazebosim.org/models';
+
+GZ3D.GZIface = function(scene, gui)
+{
+  this.scene = scene;
+  this.gui = gui;
+
+  this.isConnected = false;
+
+  this.emitter = new EventEmitter2({ verbose: true });
+
+  this.init();
+  this.visualsToAdd = [];
+
+  this.numConnectionTrials = 0;
+  this.maxConnectionTrials = 30; // try to connect 30 times
+  this.timeToSleepBtwTrials = 1000; // wait 1 second between connection trials
+};
+
+GZ3D.GZIface.prototype.init = function()
+{
+  this.material = [];
+  this.entityMaterial = {};
+
+  this.connect();
+};
+
+GZ3D.GZIface.prototype.connect = function()
+{
+  // connect to websocket
+  this.webSocket = new ROSLIB.Ros({
+    url : 'ws://' + location.hostname + ':7681'
+  });
+
+  var that = this;
+  this.webSocket.on('connection', function() {
+    that.onConnected();
+  });
+  this.webSocket.on('error', function() {
+    that.onError();
+  });
+
+  this.numConnectionTrials++;
+};
+
+GZ3D.GZIface.prototype.onError = function()
+{
+  // init scene and show popup only for the first connection error
+  if (this.numConnectionTrials === 1)
+  {
+    this.emitter.emit('error');
+  }
+
+  var that = this;
+  // retry to connect after certain time
+  if (this.numConnectionTrials < this.maxConnectionTrials)
+  {
+    setTimeout(function() {
+      that.connect();
+    }, this.timeToSleepBtwTrials);
+  }
+};
+
+GZ3D.GZIface.prototype.onConnected = function()
+{
+  this.isConnected = true;
+  this.emitter.emit('connection');
+
+  this.heartbeatTopic = new ROSLIB.Topic({
+    ros : this.webSocket,
+    name : '~/heartbeat',
+    messageType : 'heartbeat',
+  });
+
+  var that = this;
+  var publishHeartbeat = function()
+  {
+    var hearbeatMsg =
+    {
+      alive : 1
+    };
+    that.heartbeatTopic.publish(hearbeatMsg);
+  };
+
+  setInterval(publishHeartbeat, 5000);
+
+  var statusTopic = new ROSLIB.Topic({
+    ros: this.webSocket,
+    name: '~/status',
+    messageType : 'status',
+  });
+
+  var statusUpdate = function(message)
+  {
+    if (message.status === 'error')
+    {
+      that.isConnected = false;
+      this.emitter.emit('gzstatus', 'error');
+    }
+  };
+  statusTopic.subscribe(statusUpdate.bind(this));
+
+  var materialTopic = new ROSLIB.Topic({
+    ros : this.webSocket,
+    name : '~/material',
+    messageType : 'material',
+  });
+
+  var materialUpdate = function(message)
+  {
+    this.material = message;
+    this.emitter.emit('material', this.material);
+
+  };
+  materialTopic.subscribe(materialUpdate.bind(this));
+
+  this.sceneTopic = new ROSLIB.Topic({
+    ros : this.webSocket,
+    name : '~/scene',
+    messageType : 'scene',
+  });
+
+  var sceneUpdate = function(message)
+  {
+    if (message.name)
+    {
+      this.scene.name = message.name;
+    }
+
+    if (message.grid === true)
+    {
+      this.gui.guiEvents.emit('show_grid', 'show');
+    }
+
+    if (message.ambient)
+    {
+      var ambient = new THREE.Color();
+      ambient.r = message.ambient.r;
+      ambient.g = message.ambient.g;
+      ambient.b = message.ambient.b;
+
+      this.scene.ambient.color = ambient;
+    }
+
+    if (message.background)
+    {
+      var background = new THREE.Color();
+      background.r = message.background.r;
+      background.g = message.background.g;
+      background.b = message.background.b;
+
+      this.scene.renderer.clear();
+      this.scene.renderer.setClearColor(background, 1);
+    }
+
+    for (var i = 0; i < message.light.length; ++i)
+    {
+      var light = message.light[i];
+      var lightObj = this.createLightFromMsg(light);
+      this.scene.add(lightObj);
+      this.gui.setLightStats(light, 'update');
+    }
+
+    for (var j = 0; j < message.model.length; ++j)
+    {
+      var model = message.model[j];
+      var modelObj = this.createModelFromMsg(model);
+      this.scene.add(modelObj);
+      this.gui.setModelStats(model, 'update');
+    }
+
+    this.gui.setSceneStats(message);
+    this.sceneTopic.unsubscribe();
+  };
+  this.sceneTopic.subscribe(sceneUpdate.bind(this));
+
+  this.physicsTopic = new ROSLIB.Topic({
+    ros : this.webSocket,
+    name : '~/physics',
+    messageType : 'physics',
+  });
+
+  var physicsUpdate = function(message)
+  {
+    this.gui.setPhysicsStats(message);
+  };
+  this.physicsTopic.subscribe(physicsUpdate.bind(this));
+
+
+  // Update model pose
+  var poseTopic = new ROSLIB.Topic({
+    ros : this.webSocket,
+    name : '~/pose/info',
+    messageType : 'pose',
+  });
+
+  var poseUpdate = function(message)
+  {
+    var entity = this.scene.getByName(message.name);
+    if (entity && entity !== this.scene.modelManipulator.object
+        && entity.parent !== this.scene.modelManipulator.object)
+    {
+      this.scene.updatePose(entity, message.position, message.orientation);
+      this.gui.setModelStats(message, 'update');
+    }
+  };
+
+  poseTopic.subscribe(poseUpdate.bind(this));
+
+  // Requests - for deleting models
+  var requestTopic = new ROSLIB.Topic({
+    ros : this.webSocket,
+    name : '~/request',
+    messageType : 'request',
+  });
+
+  var requestUpdate = function(message)
+  {
+    if (message.request === 'entity_delete')
+    {
+      var entity = this.scene.getByName(message.data);
+      if (entity)
+      {
+        if (entity.children[0] instanceof THREE.Light)
+        {
+          this.gui.setLightStats({name: message.data}, 'delete');
+          guiEvents.emit('notification_popup', message.data+' deleted');
+        }
+        else
+        {
+          this.gui.setModelStats({name: message.data}, 'delete');
+          guiEvents.emit('notification_popup', message.data+' deleted');
+        }
+        this.scene.remove(entity);
+      }
+    }
+  };
+
+  requestTopic.subscribe(requestUpdate.bind(this));
+
+  // Model info messages - currently used for spawning new models
+  var modelInfoTopic = new ROSLIB.Topic({
+    ros : this.webSocket,
+    name : '~/model/info',
+    messageType : 'model',
+  });
+
+  var modelUpdate = function(message)
+  {
+    if (!this.scene.getByName(message.name))
+    {
+      var modelObj = this.createModelFromMsg(message);
+      if (modelObj)
+      {
+        this.scene.add(modelObj);
+        guiEvents.emit('notification_popup', message.name+' inserted');
+      }
+
+      // visuals may arrive out of order (before the model msg),
+      // add the visual in if we find its parent here
+      var len = this.visualsToAdd.length;
+      var i = 0;
+      var j = 0;
+      while (i < len)
+      {
+        var parentName = this.visualsToAdd[j].parent_name;
+        if (parentName.indexOf(modelObj.name) >=0)
+        {
+          var parent = this.scene.getByName(parentName);
+          var visualObj = this.createVisualFromMsg(this.visualsToAdd[j]);
+          parent.add(visualObj);
+          this.visualsToAdd.splice(j, 1);
+        }
+        else
+        {
+          j++;
+        }
+        i++;
+      }
+    }
+    this.gui.setModelStats(message, 'update');
+  };
+
+  modelInfoTopic.subscribe(modelUpdate.bind(this));
+
+  // Visual messages - currently just used for collision visuals
+  var visualTopic = new ROSLIB.Topic({
+    ros : this.webSocket,
+    name : '~/visual',
+    messageType : 'visual',
+  });
+
+  var visualUpdate = function(message)
+  {
+    if (!this.scene.getByName(message.name))
+    {
+      // accept only collision visual msgs for now
+      if (message.name.indexOf('COLLISION_VISUAL') < 0)
+      {
+        return;
+      }
+
+      // delay the add if parent not found, this array will checked in
+      // modelUpdate function
+      var parent = this.scene.getByName(message.parent_name);
+      if (message.parent_name && !parent)
+      {
+        this.visualsToAdd.push(message);
+      }
+      else
+      {
+        var visualObj = this.createVisualFromMsg(message);
+        parent.add(visualObj);
+      }
+    }
+  };
+
+  visualTopic.subscribe(visualUpdate.bind(this));
+
+  // world stats
+  var worldStatsTopic = new ROSLIB.Topic({
+    ros : this.webSocket,
+    name : '~/world_stats',
+    messageType : 'world_stats',
+  });
+
+  var worldStatsUpdate = function(message)
+  {
+    this.updateStatsGuiFromMsg(message);
+  };
+
+  worldStatsTopic.subscribe(worldStatsUpdate.bind(this));
+
+  // Lights
+  var lightTopic = new ROSLIB.Topic({
+    ros : this.webSocket,
+    name : '~/light',
+    messageType : 'light',
+  });
+
+  // equivalent to modelUpdate / poseUpdate
+  var lightUpdate = function(message)
+  {
+    var entity = this.scene.getByName(message.name);
+    if (!entity)
+    {
+      var lightObj = this.createLightFromMsg(message);
+      this.scene.add(lightObj);
+      guiEvents.emit('notification_popup', message.name+' inserted');
+    }
+    else if (entity && entity !== this.scene.modelManipulator.object
+        && entity.parent !== this.scene.modelManipulator.object)
+    {
+      this.scene.updateLight(entity, message);
+    }
+    this.gui.setLightStats(message, 'update');
+  };
+
+  lightTopic.subscribe(lightUpdate.bind(this));
+
+
+  // heightmap
+  this.heightmapDataService = new ROSLIB.Service({
+    ros : this.webSocket,
+    name : '~/heightmap_data',
+    serviceType : 'heightmap_data'
+  });
+
+  // road
+  this.roadService = new ROSLIB.Service({
+    ros : this.webSocket,
+    name : '~/roads',
+    serviceType : 'roads'
+  });
+
+  var request = new ROSLIB.ServiceRequest({
+      name : 'roads'
+  });
+
+  // send service request and load road on response
+  this.roadService.callService(request,
+  function(result)
+  {
+    var roadsObj = that.createRoadsFromMsg(result);
+    this.scene.add(roadsObj);
+  });
+
+  // Model modify messages - for modifying models
+  this.modelModifyTopic = new ROSLIB.Topic({
+    ros : this.webSocket,
+    name : '~/model/modify',
+    messageType : 'model',
+  });
+
+  // Light messages - for modifying lights
+  this.lightModifyTopic = new ROSLIB.Topic({
+    ros : this.webSocket,
+    name : '~/light',
+    messageType : 'light',
+  });
+
+  var publishEntityModify = function(entity)
+  {
+    var matrix = entity.matrixWorld;
+    var translation = new THREE.Vector3();
+    var quaternion = new THREE.Quaternion();
+    var scale = new THREE.Vector3();
+    matrix.decompose(translation, quaternion, scale);
+
+    var entityMsg =
+    {
+      name : entity.name,
+      id : entity.userData,
+      createEntity : 0,
+      position :
+      {
+        x : translation.x,
+        y : translation.y,
+        z : translation.z
+      },
+      orientation :
+      {
+        w: quaternion.w,
+        x: quaternion.x,
+        y: quaternion.y,
+        z: quaternion.z
+      }
+    };
+    if (entity.children[0] &&
+        entity.children[0] instanceof THREE.Light)
+    {
+      entityMsg.diffuse =
+      {
+        r: entity.children[0].color.r,
+        g: entity.children[0].color.g,
+        b: entity.children[0].color.b
+      };
+      entityMsg.specular =
+      {
+        r: entity.serverProperties.specular.r,
+        g: entity.serverProperties.specular.g,
+        b: entity.serverProperties.specular.b
+      };
+      entityMsg.direction = entity.direction;
+      entityMsg.range = entity.children[0].distance;
+      entityMsg.attenuation_constant = entity.serverProperties.attenuation_constant;
+      entityMsg.attenuation_linear = entity.serverProperties.attenuation_linear;
+      entityMsg.attenuation_quadratic = entity.serverProperties.attenuation_quadratic;
+
+      that.lightModifyTopic.publish(entityMsg);
+    }
+    else
+    {
+      that.modelModifyTopic.publish(entityMsg);
+    }
+  };
+
+  this.scene.emitter.on('entityChanged', publishEntityModify);
+
+  // Link messages - for modifying links
+  this.linkModifyTopic = new ROSLIB.Topic({
+    ros : this.webSocket,
+    name : '~/link',
+    messageType : 'link',
+  });
+
+  var publishLinkModify = function(entity, type)
+  {
+    var modelMsg =
+    {
+      name : entity.parent.name,
+      id : entity.parent.userData,
+      link:
+      {
+        name: entity.name,
+        id: entity.userData,
+        self_collide: entity.serverProperties.self_collide,
+        gravity: entity.serverProperties.gravity,
+        kinematic: entity.serverProperties.kinematic
+      }
+    };
+
+    that.linkModifyTopic.publish(modelMsg);
+  };
+
+  this.scene.emitter.on('linkChanged', publishLinkModify);
+
+  // Factory messages - for spawning new models
+  this.factoryTopic = new ROSLIB.Topic({
+    ros : this.webSocket,
+    name : '~/factory',
+    messageType : 'factory',
+  });
+
+  // Factory messages - for spawning new lights
+  this.lightFactoryTopic = new ROSLIB.Topic({
+    ros : this.webSocket,
+    name : '~/light',
+    messageType : 'light',
+  });
+
+  var publishFactory = function(model, type)
+  {
+    var matrix = model.matrixWorld;
+    var translation = new THREE.Vector3();
+    var quaternion = new THREE.Quaternion();
+    var scale = new THREE.Vector3();
+    matrix.decompose(translation, quaternion, scale);
+    var entityMsg =
+    {
+      name : model.name,
+      type : type,
+      createEntity : 1,
+      position :
+      {
+        x : translation.x,
+        y : translation.y,
+        z : translation.z
+      },
+      orientation :
+      {
+        w: quaternion.w,
+        x: quaternion.x,
+        y: quaternion.y,
+        z: quaternion.z
+      }
+    };
+    if (model.children[0].children[0] instanceof THREE.Light)
+    {
+      that.lightFactoryTopic.publish(entityMsg);
+    }
+    else
+    {
+      that.factoryTopic.publish(entityMsg);
+    }
+  };
+
+  // For deleting models
+  this.deleteTopic = new ROSLIB.Topic({
+    ros : this.webSocket,
+    name : '~/entity_delete',
+    messageType : 'entity_delete',
+  });
+
+  var publishDeleteEntity = function(entity)
+  {
+    var modelMsg =
+    {
+      name: entity.name
+    };
+
+    that.deleteTopic.publish(modelMsg);
+  };
+
+  this.gui.emitter.on('deleteEntity',
+      function(entity)
+      {
+        publishDeleteEntity(entity);
+      }
+  );
+
+  // World control messages - for resetting world/models
+  this.worldControlTopic = new ROSLIB.Topic({
+    ros : this.webSocket,
+    name : '~/world_control',
+    messageType : 'world_control',
+  });
+
+  var publishWorldControl = function(state, resetType)
+  {
+    var worldControlMsg = {};
+    if (state !== null)
+    {
+      worldControlMsg.pause = state;
+    }
+    if (resetType)
+    {
+      worldControlMsg.reset = resetType;
+    }
+    that.worldControlTopic.publish(worldControlMsg);
+  };
+
+  this.gui.emitter.on('entityCreated', publishFactory);
+
+  this.gui.emitter.on('reset',
+      function(resetType)
+      {
+        publishWorldControl(null, resetType);
+      }
+  );
+
+  this.gui.emitter.on('pause',
+      function(paused)
+      {
+        publishWorldControl(paused, null);
+      }
+  );
+};
+
+GZ3D.GZIface.prototype.updateStatsGuiFromMsg = function(stats)
+{
+  this.gui.setPaused(stats.paused);
+
+  var simSec = stats.sim_time.sec;
+  var simNSec = stats.sim_time.nsec;
+
+  var simDay = Math.floor(simSec / 86400);
+  simSec -= simDay * 86400;
+
+  var simHour = Math.floor(simSec / 3600);
+  simSec -= simHour * 3600;
+
+  var simMin = Math.floor(simSec / 60);
+  simSec -= simMin * 60;
+
+  var simMsec = Math.floor(simNSec * 1e-6);
+
+  var realSec = stats.real_time.sec;
+  var realNSec = stats.real_time.nsec;
+
+  var realDay = Math.floor(realSec / 86400);
+  realSec -= realDay * 86400;
+
+  var realHour = Math.floor(realSec / 3600);
+  realSec -= realHour * 3600;
+
+  var realMin = Math.floor(realSec / 60);
+  realSec -= realMin * 60;
+
+  var realMsec = Math.floor(realNSec * 1e-6);
+
+  var simTimeValue = '';
+  var realTimeValue = '';
+
+  if (realDay < 10)
+  {
+    realTimeValue += '0';
+  }
+  realTimeValue += realDay.toFixed(0) + ' ';
+  if (realHour < 10)
+  {
+    realTimeValue += '0';
+  }
+  realTimeValue += realHour.toFixed(0) + ':';
+  if (realMin < 10)
+  {
+    realTimeValue += '0';
+  }
+  realTimeValue += realMin.toFixed(0)  + ':';
+  if (realSec < 10)
+  {
+    realTimeValue += '0';
+  }
+  realTimeValue += realSec.toFixed(0);
+
+  if (simDay < 10)
+  {
+    simTimeValue += '0';
+  }
+  simTimeValue += simDay.toFixed(0)  + ' ';
+  if (simHour < 10)
+  {
+    simTimeValue += '0';
+  }
+  simTimeValue += simHour.toFixed(0) + ':';
+  if (simMin < 10)
+  {
+    simTimeValue += '0';
+  }
+  simTimeValue += simMin.toFixed(0) + ':';
+  if (simSec < 10)
+  {
+    simTimeValue += '0';
+  }
+  simTimeValue += simSec.toFixed(0);
+
+  this.gui.setRealTime(realTimeValue);
+  this.gui.setSimTime(simTimeValue);
+};
+
+GZ3D.GZIface.prototype.createModelFromMsg = function(model)
+{
+  var modelObj = new THREE.Object3D();
+  modelObj.name = model.name;
+  modelObj.userData = model.id;
+  if (model.pose)
+  {
+    this.scene.setPose(modelObj, model.pose.position, model.pose.orientation);
+  }
+  for (var j = 0; j < model.link.length; ++j)
+  {
+    var link = model.link[j];
+    var linkObj = new THREE.Object3D();
+    linkObj.name = link.name;
+    linkObj.userData = link.id;
+    linkObj.serverProperties =
+        {
+          self_collide: link.self_collide,
+          gravity: link.gravity,
+          kinematic: link.kinematic
+        };
+
+    if (link.pose)
+    {
+      this.scene.setPose(linkObj, link.pose.position,
+          link.pose.orientation);
+    }
+    modelObj.add(linkObj);
+    for (var k = 0; k < link.visual.length; ++k)
+    {
+      var visual = link.visual[k];
+      var visualObj = this.createVisualFromMsg(visual);
+      if (visualObj && !visualObj.parent)
+      {
+        linkObj.add(visualObj);
+      }
+    }
+
+    for (var l = 0; l < link.collision.length; ++l)
+    {
+      var collision = link.collision[l];
+      for (var m = 0; m < link.collision[l].visual.length; ++m)
+      {
+        var collisionVisual = link.collision[l].visual[m];
+        var collisionVisualObj = this.createVisualFromMsg(collisionVisual);
+        if (collisionVisualObj && !collisionVisualObj.parent)
+        {
+          linkObj.add(collisionVisualObj);
+        }
+      }
+    }
+  }
+  if (model.joint)
+  {
+    modelObj.joint = model.joint;
+  }
+
+  return modelObj;
+};
+
+GZ3D.GZIface.prototype.createVisualFromMsg = function(visual)
+{
+  if (visual.geometry)
+  {
+    var geom = visual.geometry;
+    var visualObj = new THREE.Object3D();
+    visualObj.name = visual.name;
+    if (visual.pose)
+    {
+      this.scene.setPose(visualObj, visual.pose.position,
+          visual.pose.orientation);
+    }
+
+    visualObj.castShadow = visual.cast_shadows;
+    visualObj.receiveShadow = visual.receive_shadows;
+
+    this.createGeom(geom, visual.material, visualObj);
+
+    return visualObj;
+  }
+};
+
+GZ3D.GZIface.prototype.createLightFromMsg = function(light)
+{
+  var obj, range, direction;
+
+  if (light.type === 1)
+  {
+    direction = null;
+    range = light.range;
+  }
+  else if (light.type === 2)
+  {
+    direction = light.direction;
+    range = light.range;
+  }
+  else if (light.type === 3)
+  {
+    direction = light.direction;
+    range = null;
+  }
+
+  // equation taken from
+  // http://wiki.blender.org/index.php/Doc:2.6/Manual/Lighting/Lights/Light_Attenuation
+  var E = 1;
+  var D = 1;
+  var r = 1;
+  var L = light.attenuation_linear;
+  var Q = light.attenuation_quadratic;
+  var intensity = E*(D/(D+L*r))*(Math.pow(D,2)/(Math.pow(D,2)+Q*Math.pow(r,2)));
+
+  obj = this.scene.createLight(light.type, light.diffuse, intensity,
+        light.pose, range, light.cast_shadows, light.name,
+        direction, light.specular, light.attenuation_constant,
+        light.attenuation_linear, light.attenuation_quadratic);
+
+  return obj;
+};
+
+GZ3D.GZIface.prototype.createRoadsFromMsg = function(roads)
+{
+  var roadObj = new THREE.Object3D();
+
+  var mat = this.material['Gazebo/Road'];
+  var texture = null;
+  if (mat)
+  {
+    texture = this.parseUri('media/materials/textures/' + mat['texture']);
+  }
+  var obj = this.scene.createRoads(roads.point, roads.width, texture);
+  roadObj.add(obj);
+  return roadObj;
+};
+
+GZ3D.GZIface.prototype.parseUri = function(uri)
+{
+  var uriPath = 'assets';
+  var idx = uri.indexOf('://');
+  if (idx > 0)
+  {
+    idx +=3;
+  }
+  return uriPath + '/' + uri.substring(idx);
+};
+
+GZ3D.GZIface.prototype.createGeom = function(geom, material, parent)
+{
+  var obj;
+  var uriPath = 'assets';
+  var that = this;
+  var mat = this.parseMaterial(material);
+  if (geom.box)
+  {
+    obj = this.scene.createBox(geom.box.size.x, geom.box.size.y,
+        geom.box.size.z);
+  }
+  else if (geom.cylinder)
+  {
+    obj = this.scene.createCylinder(geom.cylinder.radius,
+        geom.cylinder.length);
+  }
+  else if (geom.sphere)
+  {
+    obj = this.scene.createSphere(geom.sphere.radius);
+  }
+  else if (geom.plane)
+  {
+    obj = this.scene.createPlane(geom.plane.normal.x, geom.plane.normal.y,
+        geom.plane.normal.z, geom.plane.size.x, geom.plane.size.y);
+  }
+  else if (geom.mesh)
+  {
+    // get model name which the mesh is in
+    var rootModel = parent;
+    while (rootModel.parent)
+    {
+      rootModel = rootModel.parent;
+    }
+
+    // find model from database, download the mesh if it exists
+    // var manifestXML;
+    // var manifestURI = GAZEBO_MODEL_DATABASE_URI + '/manifest.xml';
+    // var request = new XMLHttpRequest();
+    // request.open('GET', manifestURI, false);
+    // request.onreadystatechange = function(){
+    //   if (request.readyState === 4)
+    //   {
+    //     if (request.status === 200 || request.status === 0)
+    //     {
+    //         manifestXML = request.responseXML;
+    //     }
+    //   }
+    // };
+    // request.send();
+
+    // var uriPath;
+    // var modelAvailable = false;
+    // var modelsElem = manifestXML.getElementsByTagName('models')[0];
+    // var i;
+    // for (i = 0; i < modelsElem.getElementsByTagName('uri').length; ++i)
+    // {
+    //   var uri = modelsElem.getElementsByTagName('uri')[i];
+    //   var model = uri.substring(uri.indexOf('://') + 3);
+    //   if (model === rootModel)
+    //   {
+    //     modelAvailable = true;
+    //   }
+    // }
+
+    // if (modelAvailable)
+    {
+      var meshUri = geom.mesh.filename;
+      var submesh = geom.mesh.submesh;
+      var centerSubmesh = geom.mesh.center_submesh;
+
+      var uriType = meshUri.substring(0, meshUri.indexOf('://'));
+      if (uriType === 'file' || uriType === 'model')
+      {
+        var modelName = meshUri.substring(meshUri.indexOf('://') + 3);
+        if (geom.mesh.scale)
+        {
+          parent.scale.x = geom.mesh.scale.x;
+          parent.scale.y = geom.mesh.scale.y;
+          parent.scale.z = geom.mesh.scale.z;
+        }
+
+        var modelUri = uriPath + '/' + modelName;
+        // Use coarse version on touch devices
+        if (modelUri.indexOf('.dae') !== -1 && isTouchDevice)
+        {
+          modelUri = modelUri.substring(0,modelUri.indexOf('.dae'));
+
+          var checkModel = new XMLHttpRequest();
+          checkModel.open('HEAD', modelUri+'_coarse.dae', false);
+          checkModel.send();
+          if (checkModel.status === 404)
+          {
+            modelUri = modelUri+'.dae';
+          }
+          else
+          {
+            modelUri = modelUri+'_coarse.dae';
+          }
+        }
+
+        var materialName = parent.name + '::' + modelUri;
+        this.entityMaterial[materialName] = mat;
+
+        this.scene.loadMesh(modelUri, submesh,
+            centerSubmesh, function(dae) {
+              if (that.entityMaterial[materialName])
+              {
+                var allChildren = [];
+                dae.getDescendants(allChildren);
+                for (var c = 0; c < allChildren.length; ++c)
+                {
+                  if (allChildren[c] instanceof THREE.Mesh)
+                  {
+                    that.scene.setMaterial(allChildren[c],
+                        that.entityMaterial[materialName]);
+                    break;
+                  }
+                }
+              }
+              parent.add(dae);
+              loadGeom(parent);
+            });
+      }
+    }
+  }
+  else if (geom.heightmap)
+  {
+    var request = new ROSLIB.ServiceRequest({
+      name : that.scene.name
+    });
+
+    // redirect the texture paths to the assets dir
+    var textures = geom.heightmap.texture;
+    for ( var k = 0; k < textures.length; ++k)
+    {
+      textures[k].diffuse = this.parseUri(textures[k].diffuse);
+      textures[k].normal = this.parseUri(textures[k].normal);
+    }
+
+    var sizes = geom.heightmap.size;
+
+    // send service request and load heightmap on response
+    this.heightmapDataService.callService(request,
+        function(result)
+        {
+          var heightmap = result.heightmap;
+          // gazebo heightmap is always square shaped,
+          // and a dimension of: 2^N + 1
+          that.scene.loadHeightmap(heightmap.heights, heightmap.size.x,
+              heightmap.size.y, heightmap.width, heightmap.height,
+              heightmap.origin, textures,
+              geom.heightmap.blend, parent);
+            //console.log('Result for service call on ' + result);
+        });
+
+    //this.scene.loadHeightmap(parent)
+  }
+
+  if (obj)
+  {
+    if (mat)
+    {
+      // texture mapping for simple shapes and planes only,
+      // not used by mesh and terrain
+      this.scene.setMaterial(obj, mat);
+    }
+    obj.updateMatrix();
+    parent.add(obj);
+    loadGeom(parent);
+  }
+
+  function loadGeom(visualObj)
+  {
+    var allChildren = [];
+    visualObj.getDescendants(allChildren);
+    for (var c = 0; c < allChildren.length; ++c)
+    {
+      if (allChildren[c] instanceof THREE.Mesh)
+      {
+        allChildren[c].castShadow = true;
+        allChildren[c].receiveShadow = true;
+
+        if (visualObj.castShadows)
+        {
+          allChildren[c].castShadow = visualObj.castShadows;
+        }
+        if (visualObj.receiveShadows)
+        {
+          allChildren[c].receiveShadow = visualObj.receiveShadows;
+        }
+
+        if (visualObj.name.indexOf('COLLISION_VISUAL') >= 0)
+        {
+          allChildren[c].castShadow = false;
+          allChildren[c].receiveShadow = false;
+
+          allChildren[c].visible = this.scene.showCollisions;
+        }
+        break;
+      }
+    }
+  }
+};
+
+GZ3D.GZIface.prototype.applyMaterial = function(obj, mat)
+{
+  if (obj)
+  {
+    if (mat)
+    {
+      obj.material = new THREE.MeshPhongMaterial();
+      var ambient = mat.ambient;
+      if (ambient)
+      {
+        obj.material.ambient.setRGB(ambient[0], ambient[1], ambient[2]);
+      }
+      var diffuse = mat.diffuse;
+      if (diffuse)
+      {
+        obj.material.color.setRGB(diffuse[0], diffuse[1], diffuse[2]);
+      }
+      var specular = mat.specular;
+      if (specular)
+      {
+        obj.material.specular.setRGB(specular[0], specular[1], specular[2]);
+      }
+      var opacity = mat.opacity;
+      if (opacity)
+      {
+        if (opacity < 1)
+        {
+          obj.material.transparent = true;
+          obj.material.opacity = opacity;
+        }
+      }
+
+      if (mat.texture)
+      {
+        obj.material.map = THREE.ImageUtils.loadTexture(mat.texture);
+      }
+      if (mat.normalMap)
+      {
+        obj.material.normalMap = THREE.ImageUtils.loadTexture(mat.normalMap);
+      }
+    }
+  }
+};
+
+GZ3D.GZIface.prototype.parseMaterial = function(material)
+{
+  if (!material)
+  {
+    return null;
+  }
+
+  var uriPath = 'assets';
+  var texture;
+  var normalMap;
+  var textureUri;
+  var ambient;
+  var diffuse;
+  var specular;
+  var opacity;
+  var scale;
+  var mat;
+
+  // get texture from material script
+  var script  = material.script;
+  if (script)
+  {
+    if (script.uri.length > 0)
+    {
+      if (script.name)
+      {
+        mat = this.material[script.name];
+        if (mat)
+        {
+          ambient = mat['ambient'];
+          diffuse = mat['diffuse'];
+          specular = mat['specular'];
+          opacity = mat['opacity'];
+          scale = mat['scale'];
+
+          var textureName = mat['texture'];
+          if (textureName)
+          {
+            for (var i = 0; i < script.uri.length; ++i)
+            {
+              var type = script.uri[i].substring(0,
+                    script.uri[i].indexOf('://'));
+
+              if (type === 'model')
+              {
+                if (script.uri[i].indexOf('textures') > 0)
+                {
+                  textureUri = script.uri[i].substring(
+                      script.uri[i].indexOf('://') + 3);
+                  break;
+                }
+              }
+              else if (type === 'file')
+              {
+                if (script.uri[i].indexOf('materials') > 0)
+                {
+                  textureUri = script.uri[i].substring(
+                      script.uri[i].indexOf('://') + 3,
+                      script.uri[i].indexOf('materials') + 9) + '/textures';
+                  break;
+                }
+              }
+            }
+            if (textureUri)
+            {
+              texture = uriPath + '/' +
+                  textureUri  + '/' + textureName;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  // normal map
+  if (material.normal_map)
+  {
+    var mapUri;
+    if (material.normal_map.indexOf('://') > 0)
+    {
+      mapUri = material.normal_map.substring(
+          material.normal_map.indexOf('://') + 3,
+          material.normal_map.lastIndexOf('/'));
+    }
+    else
+    {
+      mapUri = textureUri;
+    }
+    if (mapUri)
+    {
+      var startIndex = material.normal_map.lastIndexOf('/') + 1;
+      if (startIndex < 0)
+      {
+        startIndex = 0;
+      }
+      var normalMapName = material.normal_map.substr(startIndex,
+          material.normal_map.lastIndexOf('.') - startIndex);
+      normalMap = uriPath + '/' +
+        mapUri  + '/' + normalMapName + '.png';
+    }
+  }
+
+  return {
+      texture: texture,
+      normalMap: normalMap,
+      ambient: ambient,
+      diffuse: diffuse,
+      specular: specular,
+      opacity: opacity,
+      scale: scale
+  };
+};
+
+
+/*GZ3D.GZIface.prototype.createGeom = function(geom, material, parent)
+{
+  var obj;
+
+  var uriPath = 'assets';
+  var texture;
+  var normalMap;
+  var textureUri;
+  var mat;
+  if (material)
+  {
+    // get texture from material script
+    var script  = material.script;
+    if (script)
+    {
+      if (script.uri.length > 0)
+      {
+        if (script.name)
+        {
+          mat = this.material[script.name];
+          if (mat)
+          {
+            var textureName = mat['texture'];
+            if (textureName)
+            {
+              for (var i = 0; i < script.uri.length; ++i)
+              {
+                var type = script.uri[i].substring(0,
+                      script.uri[i].indexOf('://'));
+
+                if (type === 'model')
+                {
+                  if (script.uri[i].indexOf('textures') > 0)
+                  {
+                    textureUri = script.uri[i].substring(
+                        script.uri[i].indexOf('://') + 3);
+                    break;
+                  }
+                }
+                else if (type === 'file')
+                {
+                  if (script.uri[i].indexOf('materials') > 0)
+                  {
+                    textureUri = script.uri[i].substring(
+                        script.uri[i].indexOf('://') + 3,
+                        script.uri[i].indexOf('materials') + 9) + '/textures';
+                    break;
+                  }
+                }
+              }
+              if (textureUri)
+              {
+                texture = uriPath + '/' +
+                    textureUri  + '/' + textureName;
+              }
+            }
+          }
+        }
+      }
+    }
+    // normal map
+    if (material.normal_map)
+    {
+      var mapUri;
+      if (material.normal_map.indexOf('://') > 0)
+      {
+        mapUri = material.normal_map.substring(
+            material.normal_map.indexOf('://') + 3,
+            material.normal_map.lastIndexOf('/'));
+      }
+      else
+      {
+        mapUri = textureUri;
+      }
+      if (mapUri)
+      {
+        var startIndex = material.normal_map.lastIndexOf('/') + 1;
+        if (startIndex < 0)
+        {
+          startIndex = 0;
+        }
+        var normalMapName = material.normal_map.substr(startIndex,
+            material.normal_map.lastIndexOf('.') - startIndex);
+        normalMap = uriPath + '/' +
+          mapUri  + '/' + normalMapName + '.png';
+      }
+
+    }
+  }
+
+  if (geom.box)
+  {
+    obj = this.scene.createBox(geom.box.size.x, geom.box.size.y,
+        geom.box.size.z);
+  }
+  else if (geom.cylinder)
+  {
+    obj = this.scene.createCylinder(geom.cylinder.radius,
+        geom.cylinder.length);
+  }
+  else if (geom.sphere)
+  {
+    obj = this.scene.createSphere(geom.sphere.radius);
+  }
+  else if (geom.plane)
+  {
+    obj = this.scene.createPlane(geom.plane.normal.x, geom.plane.normal.y,
+        geom.plane.normal.z, geom.plane.size.x, geom.plane.size.y);
+  }
+  else if (geom.mesh)
+  {
+    // get model name which the mesh is in
+    var rootModel = parent;
+    while (rootModel.parent)
+    {
+      rootModel = rootModel.parent;
+    }
+
+    {
+      var meshUri = geom.mesh.filename;
+      var submesh = geom.mesh.submesh;
+      var centerSubmesh = geom.mesh.center_submesh;
+
+      console.log(geom.mesh.filename + ' ' + submesh);
+
+      var uriType = meshUri.substring(0, meshUri.indexOf('://'));
+      if (uriType === 'file' || uriType === 'model')
+      {
+        var modelName = meshUri.substring(meshUri.indexOf('://') + 3);
+        if (geom.mesh.scale)
+        {
+          parent.scale.x = geom.mesh.scale.x;
+          parent.scale.y = geom.mesh.scale.y;
+          parent.scale.z = geom.mesh.scale.z;
+        }
+
+        this.scene.loadMesh(uriPath + '/' + modelName, submesh, centerSubmesh,
+            texture, normalMap, parent);
+      }
+    }
+  }
+  else if (geom.heightmap)
+  {
+    var that = this;
+    var request = new ROSLIB.ServiceRequest({
+      name : that.scene.name
+    });
+
+    // redirect the texture paths to the assets dir
+    var textures = geom.heightmap.texture;
+    for ( var k = 0; k < textures.length; ++k)
+    {
+      textures[k].diffuse = this.parseUri(textures[k].diffuse);
+      textures[k].normal = this.parseUri(textures[k].normal);
+    }
+
+    var sizes = geom.heightmap.size;
+
+    // send service request and load heightmap on response
+    this.heightmapDataService.callService(request,
+        function(result)
+        {
+          var heightmap = result.heightmap;
+          // gazebo heightmap is always square shaped,
+          // and a dimension of: 2^N + 1
+          that.scene.loadHeightmap(heightmap.heights, heightmap.size.x,
+              heightmap.size.y, heightmap.width, heightmap.height,
+              heightmap.origin, textures,
+              geom.heightmap.blend, parent);
+            //console.log('Result for service call on ' + result);
+        });
+
+    //this.scene.loadHeightmap(parent)
+  }
+
+  // texture mapping for simple shapes and planes only,
+  // not used by mesh and terrain
+  if (obj)
+  {
+
+    if (mat)
+    {
+      obj.material = new THREE.MeshPhongMaterial();
+
+      var ambient = mat['ambient'];
+      if (ambient)
+      {
+        obj.material.ambient.setRGB(ambient[0], ambient[1], ambient[2]);
+      }
+      var diffuse = mat['diffuse'];
+      if (diffuse)
+      {
+        obj.material.color.setRGB(diffuse[0], diffuse[1], diffuse[2]);
+      }
+      var specular = mat['specular'];
+      if (specular)
+      {
+        obj.material.specular.setRGB(specular[0], specular[1], specular[2]);
+      }
+      var opacity = mat['opacity'];
+      if (opacity)
+      {
+        if (opacity < 1)
+        {
+          obj.material.transparent = true;
+          obj.material.opacity = opacity;
+        }
+      }
+
+      //this.scene.setMaterial(obj, texture, normalMap);
+
+      if (texture)
+      {
+        obj.material.map = THREE.ImageUtils.loadTexture(texture);
+      }
+      if (normalMap)
+      {
+        obj.material.normalMap = THREE.ImageUtils.loadTexture(normalMap);
+      }
+    }
+    obj.updateMatrix();
+    parent.add(obj);
+  }
+};
+*/
diff --git a/gz3d/src/gzmanipulator.js b/gz3d/src/gzmanipulator.js
new file mode 100644
index 0000000000000000000000000000000000000000..e347212ed3d31031763e55a6ab00aef78ffe51e8
--- /dev/null
+++ b/gz3d/src/gzmanipulator.js
@@ -0,0 +1,1187 @@
+// Based on TransformControls.js
+// original author: arodic / https://github.com/arodic
+
+/**
+ * Manipulator to perform translate and rotate transforms on objects
+ * within the scene.
+ * @constructor
+ */
+GZ3D.Manipulator = function(camera, mobile, domElement, doc)
+{
+  // Needs camera for perspective
+  this.camera = camera;
+
+  // For mouse/touch events
+  this.domElement = (domElement !== undefined) ? domElement : document;
+  this.document = (doc !== undefined) ? doc : document;
+
+  // Mobile / desktop
+  this.mobile = (mobile !== undefined) ? mobile : false;
+
+  // Object to be manipulated
+  this.object = undefined;
+
+  // translate / rotate
+  this.mode = 'translate';
+
+  // world / local
+  this.space = 'world';
+
+  // hovered used for backwards compatibility
+  // Whenever it wasn't an issue, hovered and active were combined
+  // into selected
+  this.hovered = false;
+  this.selected = 'null';
+
+  this.scale = 1;
+
+  this.snapDist = null;
+  this.modifierAxis = new THREE.Vector3(1, 1, 1);
+  this.gizmo = new THREE.Object3D();
+
+  this.pickerNames = [];
+
+  var scope = this;
+
+  var changeEvent = {type: 'change'};
+
+  var ray = new THREE.Raycaster();
+  var projector = new THREE.Projector();
+  var pointerVector = new THREE.Vector3();
+
+  var point = new THREE.Vector3();
+  var offset = new THREE.Vector3();
+
+  var rotation = new THREE.Vector3();
+  var offsetRotation = new THREE.Vector3();
+  var scale = 1;
+
+  var lookAtMatrix = new THREE.Matrix4();
+  var eye = new THREE.Vector3();
+
+  var tempMatrix = new THREE.Matrix4();
+  var tempVector = new THREE.Vector3();
+  var tempQuaternion = new THREE.Quaternion();
+  var unitX = new THREE.Vector3(1, 0, 0);
+  var unitY = new THREE.Vector3(0, 1, 0);
+  var unitZ = new THREE.Vector3(0, 0, 1);
+
+  var quaternionXYZ = new THREE.Quaternion();
+  var quaternionX = new THREE.Quaternion();
+  var quaternionY = new THREE.Quaternion();
+  var quaternionZ = new THREE.Quaternion();
+  var quaternionE = new THREE.Quaternion();
+
+  var oldPosition = new THREE.Vector3();
+  var oldRotationMatrix = new THREE.Matrix4();
+
+  var parentRotationMatrix  = new THREE.Matrix4();
+  var parentScale = new THREE.Vector3();
+
+  var worldPosition = new THREE.Vector3();
+  var worldRotation = new THREE.Vector3();
+  var worldRotationMatrix  = new THREE.Matrix4();
+  var camPosition = new THREE.Vector3();
+
+  var hovered = null;
+  var hoveredColor = new THREE.Color();
+
+  // Picker currently selected (highlighted)
+  var selectedPicker = null;
+  var selectedColor = new THREE.Color();
+
+  // Intersection planes
+  var intersectionPlanes = {};
+  var intersectionPlaneList = ['XY','YZ','XZ'];
+  var currentPlane = 'XY';
+
+  var planes = new THREE.Object3D();
+  this.gizmo.add(planes);
+
+  for(var i in intersectionPlaneList)
+  {
+    intersectionPlanes[intersectionPlaneList[i]] =
+        new THREE.Mesh(new THREE.PlaneGeometry(500, 500));
+    intersectionPlanes[intersectionPlaneList[i]].material.side =
+        THREE.DoubleSide;
+    intersectionPlanes[intersectionPlaneList[i]].visible = false;
+    planes.add(intersectionPlanes[intersectionPlaneList[i]]);
+  }
+
+  intersectionPlanes['YZ'].rotation.set(0, Math.PI/2, 0);
+  intersectionPlanes['XZ'].rotation.set(-Math.PI/2, 0, 0);
+  bakeTransformations(intersectionPlanes['YZ']);
+  bakeTransformations(intersectionPlanes['XZ']);
+
+  // Geometries
+
+  var pickerAxes = {};
+  var displayAxes = {};
+
+  var HandleMaterial = function(color, over, opacity)
+  {
+    var material = new THREE.MeshBasicMaterial();
+    material.color = color;
+    if(over)
+    {
+      material.side = THREE.DoubleSide;
+      material.depthTest = false;
+      material.depthWrite = false;
+    }
+    material.opacity = opacity !== undefined ? opacity : 0.5;
+    material.transparent = true;
+    return material;
+  };
+
+  var LineMaterial = function(color, opacity)
+  {
+    var material = new THREE.LineBasicMaterial();
+    material.color = color;
+    material.depthTest = false;
+    material.depthWrite = false;
+    material.opacity = opacity !== undefined ? opacity : 1;
+    material.transparent = true;
+    return material;
+  };
+
+  // Colors
+  var white = new THREE.Color(0xffffff);
+  var gray = new THREE.Color(0x808080);
+  var red = new THREE.Color(0xff0000);
+  var green = new THREE.Color(0x00ff00);
+  var blue = new THREE.Color(0x0000ff);
+  var cyan = new THREE.Color(0x00ffff);
+  var magenta = new THREE.Color(0xff00ff);
+  var yellow = new THREE.Color(0xffff00);
+
+  var geometry, mesh;
+
+  // Translate
+
+  pickerAxes['translate'] = new THREE.Object3D();
+  displayAxes['translate'] = new THREE.Object3D();
+  this.gizmo.add(pickerAxes['translate']);
+  this.gizmo.add(displayAxes['translate']);
+
+  // Picker cylinder
+  if(this.mobile)
+  {
+    geometry = new THREE.CylinderGeometry(0.5, 0.01, 1.4, 10, 1, false);
+  }
+  else
+  {
+    geometry = new THREE.CylinderGeometry(0.2, 0.1, 0.8, 4, 1, false);
+  }
+
+  mesh = new THREE.Mesh(geometry, new HandleMaterial(red, true));
+  mesh.position.x = 0.7;
+  mesh.rotation.z = -Math.PI/2;
+  bakeTransformations(mesh);
+  mesh.name = 'TX';
+  pickerAxes['translate'].add(mesh);
+  this.pickerNames.push(mesh.name);
+
+  mesh = new THREE.Mesh(geometry, new HandleMaterial(green, true));
+  mesh.position.y = 0.7;
+  bakeTransformations(mesh);
+  mesh.name = 'TY';
+  pickerAxes['translate'].add(mesh);
+  this.pickerNames.push(mesh.name);
+
+  mesh = new THREE.Mesh(geometry, new HandleMaterial(blue, true));
+  mesh.position.z = 0.7;
+  mesh.rotation.x = Math.PI/2;
+  bakeTransformations(mesh);
+  mesh.name = 'TZ';
+  pickerAxes['translate'].add(mesh);
+  this.pickerNames.push(mesh.name);
+
+  if(this.mobile)
+  {
+    // Display cylinder
+    geometry = new THREE.CylinderGeometry(0.1, 0.1, 1, 10, 1, false);
+
+    mesh = new THREE.Mesh(geometry, new HandleMaterial(red, true));
+    mesh.position.x = 0.5;
+    mesh.rotation.z = -Math.PI/2;
+    bakeTransformations(mesh);
+    mesh.name = 'TX';
+    displayAxes['translate'].add(mesh);
+
+    mesh = new THREE.Mesh(geometry, new HandleMaterial(green, true));
+    mesh.position.y = 0.5;
+    bakeTransformations(mesh);
+    mesh.name = 'TY';
+    displayAxes['translate'].add(mesh);
+
+    mesh = new THREE.Mesh(geometry, new HandleMaterial(blue, true));
+    mesh.position.z = 0.5;
+    mesh.rotation.x = Math.PI/2;
+    bakeTransformations(mesh);
+    mesh.name = 'TZ';
+    displayAxes['translate'].add(mesh);
+
+    // Display cone (arrow tip)
+    geometry = new THREE.CylinderGeometry(0, 0.15, 0.4, 10, 1, false);
+
+    mesh = new THREE.Mesh(geometry, new HandleMaterial(red, true));
+    mesh.position.x = 1.2;
+    mesh.rotation.z = -Math.PI/2;
+    bakeTransformations(mesh);
+    mesh.name = 'TX';
+    displayAxes['translate'].add(mesh);
+
+    mesh = new THREE.Mesh(geometry, new HandleMaterial(green, true));
+    mesh.position.y = 1.2;
+    bakeTransformations(mesh);
+    mesh.name = 'TY';
+    displayAxes['translate'].add(mesh);
+
+    mesh = new THREE.Mesh(geometry, new HandleMaterial(blue, true));
+    mesh.position.z = 1.2;
+    mesh.rotation.x = Math.PI/2;
+    bakeTransformations(mesh);
+    mesh.name = 'TZ';
+    displayAxes['translate'].add(mesh);
+  }
+  else
+  {
+    // Display lines
+    geometry = new THREE.Geometry();
+    geometry.vertices.push(
+        new THREE.Vector3(0, 0, 0), new THREE.Vector3(1, 0, 0),
+        new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 1, 0),
+        new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 0, 1)
+    );
+    geometry.colors.push(
+        red, red, green, green, blue, blue
+    );
+    var material = new THREE.LineBasicMaterial({
+        vertexColors: THREE.VertexColors,
+        depthTest: false,
+        depthWrite: false,
+        transparent: true
+    });
+    mesh = new THREE.Line(geometry, material, THREE.LinePieces);
+    displayAxes['translate'].add(mesh);
+
+    // Display cone (arrow tip)
+    geometry = new THREE.CylinderGeometry(0, 0.05, 0.2, 4, 1, true);
+
+    mesh = new THREE.Mesh(geometry, new HandleMaterial(red, true, 1));
+    mesh.position.x = 1.1;
+    mesh.rotation.z = -Math.PI/2;
+    bakeTransformations(mesh);
+    mesh.name = 'TX';
+    displayAxes['translate'].add(mesh);
+
+    mesh = new THREE.Mesh(geometry, new HandleMaterial(green, true, 1));
+    mesh.position.y = 1.1;
+    bakeTransformations(mesh);
+    mesh.name = 'TY';
+    displayAxes['translate'].add(mesh);
+
+    mesh = new THREE.Mesh(geometry, new HandleMaterial(blue, true, 1));
+    mesh.position.z = 1.1;
+    mesh.rotation.x = Math.PI/2;
+    bakeTransformations(mesh);
+    mesh.name = 'TZ';
+    displayAxes['translate'].add(mesh);
+
+    // Picker and display octahedron for TXYZ
+    mesh = new THREE.Mesh(new THREE.OctahedronGeometry(0.1, 0),
+        new HandleMaterial(white, true, 0.25));
+    mesh.name = 'TXYZ';
+    this.pickerNames.push(mesh.name);
+    displayAxes['translate'].add(mesh);
+    pickerAxes['translate'].add(mesh.clone());
+
+    // Picker and display planes
+    geometry = new THREE.PlaneGeometry(0.3, 0.3);
+
+    mesh = new THREE.Mesh(geometry, new HandleMaterial(yellow, 0.25));
+    mesh.position.set(0.15, 0.15, 0);
+    bakeTransformations(mesh);
+    mesh.name = 'TXY';
+    this.pickerNames.push(mesh.name);
+    displayAxes['translate'].add(mesh);
+    pickerAxes['translate'].add(mesh.clone());
+
+    mesh = new THREE.Mesh(geometry, new HandleMaterial(cyan, 0.25));
+    mesh.position.set(0, 0.15, 0.15);
+    mesh.rotation.y = Math.PI/2;
+    bakeTransformations(mesh);
+    mesh.name = 'TYZ';
+    this.pickerNames.push(mesh.name);
+    displayAxes['translate'].add(mesh);
+    pickerAxes['translate'].add(mesh.clone());
+
+    mesh = new THREE.Mesh(geometry, new HandleMaterial(magenta, 0.25));
+    mesh.position.set(0.15, 0, 0.15);
+    mesh.rotation.x = Math.PI/2;
+    bakeTransformations(mesh);
+    mesh.name = 'TXZ';
+    this.pickerNames.push(mesh.name);
+    displayAxes['translate'].add(mesh);
+    pickerAxes['translate'].add(mesh.clone());
+
+  }
+
+  // Rotate
+
+  pickerAxes['rotate'] = new THREE.Object3D();
+  displayAxes['rotate'] = new THREE.Object3D();
+  this.gizmo.add(pickerAxes['rotate']);
+  this.gizmo.add(displayAxes['rotate']);
+
+  // RX, RY, RZ
+
+  // Picker torus
+  if(this.mobile)
+  {
+    geometry = new THREE.TorusGeometry(1, 0.3, 4, 36, 2*Math.PI);
+  }
+  else
+  {
+    geometry = new THREE.TorusGeometry(1, 0.15, 4, 6, Math.PI);
+  }
+
+  mesh = new THREE.Mesh(geometry, new HandleMaterial(red, false));
+  mesh.rotation.z = -Math.PI/2;
+  mesh.rotation.y = -Math.PI/2;
+  bakeTransformations(mesh);
+  mesh.name = 'RX';
+  pickerAxes['rotate'].add(mesh);
+  this.pickerNames.push(mesh.name);
+
+  mesh = new THREE.Mesh(geometry, new HandleMaterial(green, false));
+  mesh.rotation.z = Math.PI;
+  mesh.rotation.x = -Math.PI/2;
+  bakeTransformations(mesh);
+  mesh.name = 'RY';
+  pickerAxes['rotate'].add(mesh);
+  this.pickerNames.push(mesh.name);
+
+  mesh = new THREE.Mesh(geometry, new HandleMaterial(blue, false));
+  mesh.rotation.z = -Math.PI/2;
+  bakeTransformations(mesh);
+  mesh.name = 'RZ';
+  pickerAxes['rotate'].add(mesh);
+  this.pickerNames.push(mesh.name);
+
+  if(this.mobile)
+  {
+    // Display torus
+    geometry = new THREE.TorusGeometry(1, 0.1, 4, 36, 2*Math.PI);
+
+    mesh = new THREE.Mesh(geometry, new HandleMaterial(blue, false));
+    mesh.rotation.z = -Math.PI/2;
+    bakeTransformations(mesh);
+    mesh.name = 'RZ';
+    displayAxes['rotate'].add(mesh);
+
+    mesh = new THREE.Mesh(geometry, new HandleMaterial(red, false));
+    mesh.rotation.z = -Math.PI/2;
+    mesh.rotation.y = -Math.PI/2;
+    bakeTransformations(mesh);
+    mesh.name = 'RX';
+    displayAxes['rotate'].add(mesh);
+
+    mesh = new THREE.Mesh(geometry, new HandleMaterial(green, false));
+    mesh.rotation.z = Math.PI;
+    mesh.rotation.x = -Math.PI/2;
+    bakeTransformations(mesh);
+    mesh.name = 'RY';
+    displayAxes['rotate'].add(mesh);
+  }
+  else
+  {
+    // Display circles
+    var Circle = function(radius, facing, arc)
+    {
+      geometry = new THREE.Geometry();
+      arc = arc ? arc : 1;
+      for(var i = 0; i <= 64 * arc; ++i)
+      {
+        if(facing === 'x')
+        {
+          geometry.vertices.push(new THREE.Vector3(
+              0, Math.cos(i / 32 * Math.PI), Math.sin(i / 32 * Math.PI))
+              .multiplyScalar(radius));
+        }
+        if(facing === 'y')
+        {
+          geometry.vertices.push(new THREE.Vector3(
+              Math.cos(i / 32 * Math.PI), 0, Math.sin(i / 32 * Math.PI))
+              .multiplyScalar(radius));
+        }
+        if(facing === 'z')
+        {
+          geometry.vertices.push(new THREE.Vector3(
+              Math.sin(i / 32 * Math.PI), Math.cos(i / 32 * Math.PI), 0)
+              .multiplyScalar(radius));
+        }
+      }
+      return geometry;
+    };
+
+    mesh = new THREE.Line(new Circle(1, 'x', 0.5), new LineMaterial(red));
+    mesh.name = 'RX';
+    displayAxes['rotate'].add(mesh);
+
+    mesh = new THREE.Line(new Circle(1, 'y', 0.5), new LineMaterial(green));
+    mesh.name = 'RY';
+    displayAxes['rotate'].add(mesh);
+
+    mesh = new THREE.Line(new Circle(1, 'z', 0.5), new LineMaterial(blue));
+    mesh.name = 'RZ';
+    displayAxes['rotate'].add(mesh);
+
+    mesh = new THREE.Line(new Circle(1, 'z'), new LineMaterial(gray));
+    mesh.name = 'RXYZE';
+    this.pickerNames.push(mesh.name);
+    displayAxes['rotate'].add(mesh);
+
+    mesh = new THREE.Line(new Circle(1.25, 'z'),
+        new LineMaterial(yellow, 0.25));
+    mesh.name = 'RE';
+    this.pickerNames.push(mesh.name);
+    displayAxes['rotate'].add(mesh);
+
+    // Picker spheres
+    mesh = new THREE.Mesh(new THREE.SphereGeometry(0.95, 12, 12),
+        new HandleMaterial(white, 0.25));
+    mesh.name = 'RXYZE';
+    pickerAxes['rotate'].add(mesh);
+    this.pickerNames.push(mesh.name);
+
+    intersectionPlanes['SPHERE'] = new THREE.Mesh(new
+        THREE.SphereGeometry(0.95, 12, 12));
+    intersectionPlanes['SPHERE'].visible = false;
+    planes.add(intersectionPlanes['SPHERE']);
+
+    mesh = new THREE.Mesh(new THREE.TorusGeometry(1.30, 0.15, 4, 12),
+        new HandleMaterial(yellow, 0.25));
+    mesh.name = 'RE';
+    pickerAxes['rotate'].add(mesh);
+    this.pickerNames.push(mesh.name);
+  }
+  mesh = null;
+
+  /**
+   * Attach gizmo to an object
+   * @param {THREE.Object3D} - Model to be manipulated
+   */
+  this.attach = function(object)
+  {
+    this.object = object;
+    this.setMode(scope.mode);
+
+    if(this.mobile)
+    {
+      this.domElement.addEventListener('touchstart', onTouchStart, false);
+    }
+    else
+    {
+      this.domElement.addEventListener('mousedown', onMouseDown, false);
+      this.domElement.addEventListener('mousemove', onMouseHover, false);
+    }
+  };
+
+  /**
+   * Detatch gizmo from an object
+   * @param {THREE.Object3D} - Model
+   */
+  this.detach = function(object)
+  {
+    this.object = undefined;
+    this.selected = 'null';
+
+    this.hide();
+
+    if(this.mobile)
+    {
+      this.domElement.removeEventListener('touchstart', onTouchStart, false);
+    }
+    else
+    {
+      this.domElement.removeEventListener('mousedown', onMouseDown, false);
+      this.domElement.removeEventListener('mousemove', onMouseHover, false);
+    }
+  };
+
+  /**
+   * Update gizmo's pose and scale
+   */
+  this.update = function()
+  {
+    if(this.object === undefined)
+    {
+      return;
+    }
+
+    this.object.updateMatrixWorld();
+    worldPosition.getPositionFromMatrix(this.object.matrixWorld);
+
+    this.camera.updateMatrixWorld();
+    camPosition.getPositionFromMatrix(this.camera.matrixWorld);
+
+    scale = worldPosition.distanceTo(camPosition) / 6 * this.scale;
+    this.gizmo.position.copy(worldPosition);
+    this.gizmo.scale.set(scale, scale, scale);
+
+    for(var i in this.gizmo.children)
+    {
+      for(var j in this.gizmo.children[i].children)
+      {
+        var object = this.gizmo.children[i].children[j];
+        var name = object.name;
+
+        if(name.search('E') !== -1)
+        {
+          lookAtMatrix.lookAt(camPosition, worldPosition,
+              tempVector.set(0, 1, 0));
+          object.rotation.setFromRotationMatrix(lookAtMatrix);
+        }
+        else
+        {
+          eye.copy(camPosition).sub(worldPosition).normalize();
+
+          if (this.space === 'local')
+          {
+            tempQuaternion.setFromRotationMatrix(tempMatrix
+                .extractRotation(this.object.matrixWorld));
+
+            if (name.search('R') !== -1)
+            {
+              tempMatrix.makeRotationFromQuaternion(tempQuaternion)
+                  .getInverse(tempMatrix);
+              eye.applyProjection(tempMatrix);
+
+              if (name === 'RX')
+              {
+                quaternionX.setFromAxisAngle(unitX, Math.atan2(-eye.y, eye.z));
+                tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionX);
+              }
+              if (name ==='RY')
+              {
+                quaternionY.setFromAxisAngle(unitY, Math.atan2( eye.x, eye.z));
+                tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionY);
+              }
+              if (name === 'RZ')
+              {
+                quaternionZ.setFromAxisAngle(unitZ, Math.atan2( eye.y, eye.x));
+                tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionZ);
+              }
+            }
+            object.quaternion.copy(tempQuaternion);
+          }
+          else if (this.space === 'world')
+          {
+            object.rotation.set(0, 0, 0);
+
+            if(name === 'RX')
+            {
+              object.rotation.x = Math.atan2(-eye.y, eye.z);
+            }
+            if(name === 'RY')
+            {
+              object.rotation.y = Math.atan2( eye.x, eye.z);
+            }
+            if(name === 'RZ')
+            {
+              object.rotation.z = Math.atan2( eye.y, eye.x);
+            }
+          }
+        }
+      }
+    }
+  };
+
+  /**
+   * Hide gizmo
+   */
+  this.hide = function()
+  {
+    for(var i in displayAxes)
+    {
+      for(var j in displayAxes[i].children)
+      {
+        displayAxes[i].children[j].visible = false;
+      }
+    }
+    for(var k in pickerAxes)
+    {
+      for(var l in pickerAxes[k].children)
+      {
+        pickerAxes[k].children[l].visible = false;
+      }
+    }
+
+    for(var m in intersectionPlaneList)
+    {
+      intersectionPlanes[intersectionPlaneList[m]].visible = false;
+    }
+  };
+
+  /**
+   * Set mode
+   * @param {string} value - translate | rotate
+   */
+  this.setMode = function(value)
+  {
+    scope.mode = value;
+
+    this.hide();
+
+    for(var i in displayAxes[this.mode].children)
+    {
+      displayAxes[this.mode].children[i].visible = true;
+    }
+
+    for(var j in pickerAxes[this.mode].children)
+    {
+      pickerAxes[this.mode].children[j].visible = false; // debug
+    }
+
+    for(var k in intersectionPlaneList)
+    {
+      intersectionPlanes[intersectionPlaneList[k]].visible = false; // debug
+    }
+
+    scope.update();
+  };
+
+  /**
+   * Choose intersection plane
+   */
+  this.setIntersectionPlane = function()
+  {
+    eye.copy(camPosition).sub(worldPosition).normalize();
+
+    if (this.space === 'local')
+    {
+       eye.applyMatrix4(tempMatrix.getInverse(scope.object.matrixWorld));
+    }
+
+    if (isSelected('TXYZ'))
+    {
+      if (Math.abs(eye.x) > Math.abs(eye.y) &&
+          Math.abs(eye.x) > Math.abs(eye.z))
+      {
+        currentPlane = 'YZ';
+      }
+      else if (Math.abs(eye.y) > Math.abs(eye.x) &&
+               Math.abs(eye.y) > Math.abs(eye.z))
+      {
+        currentPlane = 'XZ';
+      }
+      else
+      {
+        currentPlane = 'XY';
+      }
+    }
+    else if (isSelected('RX') || isSelected('TYZ'))
+    {
+      currentPlane = 'YZ';
+    }
+    else if (isSelected('RY') || isSelected('TXZ'))
+    {
+      currentPlane = 'XZ';
+    }
+    else if (isSelected('RZ') || isSelected('TXY'))
+    {
+      currentPlane = 'XY';
+    }
+    else if (isSelected('X'))
+    {
+      if (Math.abs(eye.y) > Math.abs(eye.z))
+      {
+        currentPlane = 'XZ';
+      }
+      else
+      {
+        currentPlane = 'XY';
+      }
+    }
+    else if (isSelected('Y'))
+    {
+      if (Math.abs(eye.x) > Math.abs(eye.z))
+      {
+        currentPlane = 'YZ';
+      }
+      else
+      {
+        currentPlane = 'XY';
+      }
+    }
+    else if (isSelected('Z'))
+    {
+      if (Math.abs(eye.x) > Math.abs(eye.y))
+      {
+        currentPlane = 'YZ';
+      }
+      else
+      {
+        currentPlane = 'XZ';
+      }
+    }
+  };
+
+  /**
+   * Window event callback
+   * @param {} event
+   */
+  function onTouchStart(event)
+  {
+    event.preventDefault();
+
+    var intersect = intersectObjects(event, pickerAxes[scope.mode].children);
+
+    // If one of the current pickers was touched
+    if(intersect)
+    {
+      if(selectedPicker !== intersect.object)
+      {
+        // Back to original color
+        if(selectedPicker !== null)
+        {
+            selectedPicker.material.color.copy(selectedColor);
+        }
+
+        selectedPicker = intersect.object;
+
+        // Save color for when it's deselected
+        selectedColor.copy(selectedPicker.material.color);
+
+        // Darken color
+        selectedPicker.material.color.offsetHSL(0, 0, -0.3);
+
+        scope.dispatchEvent(changeEvent);
+      }
+
+      scope.selected = selectedPicker.name;
+      scope.hovered = true;
+      scope.update();
+      scope.setIntersectionPlane();
+
+      var planeIntersect = intersectObjects(event,
+          [intersectionPlanes[currentPlane]]);
+
+      if(planeIntersect)
+      {
+        oldPosition.copy(scope.object.position);
+
+        oldRotationMatrix.extractRotation(scope.object.matrix);
+        worldRotationMatrix.extractRotation(scope.object.matrixWorld);
+
+        parentRotationMatrix.extractRotation(scope.object.parent.matrixWorld);
+        parentScale.getScaleFromMatrix(tempMatrix.getInverse(
+            scope.object.parent.matrixWorld));
+
+        offset.copy(planeIntersect.point);
+      }
+    }
+
+    scope.document.addEventListener('touchmove', onPointerMove, false);
+    scope.document.addEventListener('touchend', onTouchEnd, false);
+  }
+
+
+  /**
+   * Window event callback
+   * @param {} event
+   */
+  function onTouchEnd()
+  {
+    // Previously selected picker back to its color
+    if(selectedPicker)
+    {
+      selectedPicker.material.color.copy(selectedColor);
+    }
+
+    selectedPicker = null;
+
+    scope.dispatchEvent(changeEvent);
+
+    scope.selected = 'null';
+    scope.hovered = false;
+
+    scope.document.removeEventListener('touchmove', onPointerMove, false);
+    scope.document.removeEventListener('touchend', onTouchEnd, false);
+  }
+
+  /**
+   * Window event callback
+   * @param {} event
+   */
+  function onMouseHover(event)
+  {
+    event.preventDefault();
+
+    if(event.button === 0 && scope.selected === 'null')
+    {
+      var intersect = intersectObjects(event, pickerAxes[scope.mode].children);
+
+        if(intersect)
+        {
+          if(hovered !== intersect.object)
+          {
+            if(hovered !== null)
+            {
+              hovered.material.color.copy(hoveredColor);
+            }
+
+            selectedPicker = intersect.object;
+            hovered = intersect.object;
+            hoveredColor.copy(hovered.material.color);
+
+            hovered.material.color.offsetHSL(0, 0, -0.3);
+
+            scope.dispatchEvent(changeEvent);
+          }
+          scope.hovered = true;
+        }
+        else if(hovered !== null)
+        {
+          hovered.material.color.copy(hoveredColor);
+
+          hovered = null;
+
+          scope.dispatchEvent(changeEvent);
+
+          scope.hovered = false;
+      }
+    }
+    scope.document.addEventListener('mousemove', onPointerMove, false);
+    scope.document.addEventListener('mouseup', onMouseUp, false);
+  }
+
+  /**
+   * Window event callback
+   * @param {} event
+   */
+  function onMouseDown(event)
+  {
+    event.preventDefault();
+
+    if(event.button !== 0)
+    {
+      return;
+    }
+
+    var intersect = intersectObjects(event, pickerAxes[scope.mode].children);
+
+    if(intersect)
+    {
+        scope.selected = selectedPicker.name;
+
+        scope.update();
+        scope.setIntersectionPlane();
+
+        var planeIntersect = intersectObjects(event,
+            [intersectionPlanes[currentPlane]]);
+
+        if(planeIntersect)
+        {
+          oldPosition.copy(scope.object.position);
+
+          oldRotationMatrix.extractRotation(scope.object.matrix);
+          worldRotationMatrix.extractRotation(scope.object.matrixWorld);
+
+          parentRotationMatrix.extractRotation(
+              scope.object.parent.matrixWorld);
+          parentScale.getScaleFromMatrix(tempMatrix.getInverse(
+              scope.object.parent.matrixWorld));
+
+          offset.copy(planeIntersect.point);
+        }
+    }
+
+    scope.document.addEventListener('mousemove', onPointerMove, false);
+    scope.document.addEventListener('mouseup', onMouseUp, false);
+  }
+
+  /**
+   * Window event callback (mouse move and touch move)
+   * @param {} event
+   */
+  function onPointerMove(event)
+  {
+    if(scope.selected === 'null')
+    {
+      return;
+    }
+
+    event.preventDefault();
+
+    var planeIntersect = intersectObjects(event,
+        [intersectionPlanes[currentPlane]]);
+
+    if(planeIntersect)
+    {
+      point.copy(planeIntersect.point);
+
+      if((scope.mode === 'translate') && isSelected('T'))
+      {
+        point.sub(offset);
+        point.multiply(parentScale);
+
+        if (scope.space === 'local')
+        {
+          point.applyMatrix4(tempMatrix.getInverse(worldRotationMatrix));
+
+          if(!(isSelected('X')) || scope.modifierAxis.x !== 1)
+          {
+            point.x = 0;
+          }
+          if(!(isSelected('Y')) || scope.modifierAxis.y !== 1)
+          {
+            point.y = 0;
+          }
+          if(!(isSelected('Z')) || scope.modifierAxis.z !== 1)
+          {
+            point.z = 0;
+          }
+          if (isSelected('XYZ'))
+          {
+            point.set(0, 0, 0);
+          }
+          point.applyMatrix4(oldRotationMatrix);
+
+          scope.object.position.copy(oldPosition);
+          scope.object.position.add(point);
+        }
+        if (scope.space === 'world' || isSelected('XYZ'))
+        {
+          if(!(isSelected('X')) || scope.modifierAxis.x !== 1)
+          {
+            point.x = 0;
+          }
+          if(!(isSelected('Y')) || scope.modifierAxis.y !== 1)
+          {
+            point.y = 0;
+          }
+          if(!(isSelected('Z')) || scope.modifierAxis.z !== 1)
+          {
+            point.z = 0;
+          }
+
+          point.applyMatrix4(tempMatrix.getInverse(parentRotationMatrix));
+
+          scope.object.position.copy(oldPosition);
+          scope.object.position.add(point);
+
+          if(scope.snapDist)
+          {
+            if(isSelected('X'))
+            {
+              scope.object.position.x = Math.round(scope.object.position.x /
+                  scope.snapDist) * scope.snapDist;
+            }
+            if(isSelected('Y'))
+            {
+              scope.object.position.y = Math.round(scope.object.position.y /
+                  scope.snapDist) * scope.snapDist;
+            }
+            if(isSelected('Z'))
+            {
+              scope.object.position.z = Math.round(scope.object.position.z /
+                  scope.snapDist) * scope.snapDist;
+            }
+          }
+        }
+      }
+      else if((scope.mode === 'rotate') && isSelected('R'))
+      {
+        point.sub(worldPosition);
+        point.multiply(parentScale);
+        tempVector.copy(offset).sub(worldPosition);
+        tempVector.multiply(parentScale);
+
+        if(scope.selected === 'RE')
+        {
+          point.applyMatrix4(tempMatrix.getInverse(lookAtMatrix));
+          tempVector.applyMatrix4(tempMatrix.getInverse(lookAtMatrix));
+
+          rotation.set(Math.atan2(point.z, point.y),
+                       Math.atan2(point.x, point.z),
+                       Math.atan2(point.y, point.x));
+          offsetRotation.set(Math.atan2(tempVector.z, tempVector.y),
+                             Math.atan2(tempVector.x, tempVector.z),
+                             Math.atan2(tempVector.y, tempVector.x));
+
+          tempQuaternion.setFromRotationMatrix(tempMatrix.getInverse(parentRotationMatrix));
+
+          quaternionE.setFromAxisAngle(eye, rotation.z - offsetRotation.z);
+          quaternionXYZ.setFromRotationMatrix(worldRotationMatrix);
+
+          tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionE);
+          tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionXYZ);
+
+          scope.object.quaternion.copy(tempQuaternion);
+        }
+        else if(scope.selected === 'RXYZE')
+        {
+          quaternionE.setFromEuler(point.clone().cross(tempVector).normalize()); // has this ever worked?
+
+          tempQuaternion.setFromRotationMatrix(tempMatrix.getInverse(parentRotationMatrix));
+          quaternionX.setFromAxisAngle(quaternionE, - point.clone().angleTo(tempVector));
+          quaternionXYZ.setFromRotationMatrix(worldRotationMatrix);
+
+          tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionX);
+          tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionXYZ);
+
+          scope.object.quaternion.copy(tempQuaternion);
+        }
+        else
+        {
+          if (scope.space === 'local')
+          {
+            point.applyMatrix4(tempMatrix.getInverse(worldRotationMatrix));
+
+            tempVector.applyMatrix4(tempMatrix.getInverse(worldRotationMatrix));
+
+            rotation.set(Math.atan2(point.z, point.y), Math.atan2(point.x, point.z),
+                Math.atan2(point.y, point.x));
+            offsetRotation.set(Math.atan2(tempVector.z, tempVector.y), Math.atan2(
+                tempVector.x, tempVector.z), Math.atan2(tempVector.y, tempVector.x));
+
+            quaternionXYZ.setFromRotationMatrix(oldRotationMatrix);
+            quaternionX.setFromAxisAngle(unitX, rotation.x - offsetRotation.x);
+            quaternionY.setFromAxisAngle(unitY, rotation.y - offsetRotation.y);
+            quaternionZ.setFromAxisAngle(unitZ, rotation.z - offsetRotation.z);
+
+            if (scope.selected === 'RX')
+            {
+              quaternionXYZ.multiplyQuaternions(quaternionXYZ, quaternionX);
+            }
+            if (scope.selected === 'RY')
+            {
+              quaternionXYZ.multiplyQuaternions(quaternionXYZ, quaternionY);
+            }
+            if (scope.selected === 'RZ')
+            {
+              quaternionXYZ.multiplyQuaternions(quaternionXYZ, quaternionZ);
+            }
+
+            scope.object.quaternion.copy(quaternionXYZ);
+          }
+          else if (scope.space === 'world')
+          {
+            rotation.set(Math.atan2(point.z, point.y), Math.atan2(point.x, point.z),
+                Math.atan2(point.y, point.x));
+            offsetRotation.set(Math.atan2(tempVector.z, tempVector.y), Math.atan2(
+              tempVector.x, tempVector.z), Math.atan2(tempVector.y, tempVector.x));
+
+            tempQuaternion.setFromRotationMatrix(tempMatrix.getInverse(
+              parentRotationMatrix));
+
+            quaternionX.setFromAxisAngle(unitX, rotation.x - offsetRotation.x);
+            quaternionY.setFromAxisAngle(unitY, rotation.y - offsetRotation.y);
+            quaternionZ.setFromAxisAngle(unitZ, rotation.z - offsetRotation.z);
+            quaternionXYZ.setFromRotationMatrix(worldRotationMatrix);
+
+            if(scope.selected === 'RX')
+            {
+              tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionX);
+            }
+            if(scope.selected === 'RY')
+            {
+              tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionY);
+            }
+            if(scope.selected === 'RZ')
+            {
+              tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionZ);
+            }
+
+            tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionXYZ);
+
+            scope.object.quaternion.copy(tempQuaternion);
+          }
+        }
+      }
+    }
+
+    // Move light target
+    if (scope.object.children[0] &&
+       (scope.object.children[0] instanceof THREE.SpotLight ||
+        scope.object.children[0] instanceof THREE.DirectionalLight))
+    {
+      var lightObj = scope.object.children[0];
+      var dir = new THREE.Vector3(0,0,0);
+      dir.copy(scope.object.direction);
+      scope.object.localToWorld(dir);
+      lightObj.target.position.copy(dir);
+    }
+
+    scope.update();
+    scope.dispatchEvent(changeEvent);
+  }
+
+  function onMouseUp(event)
+  {
+    scope.selected = 'null';
+
+    scope.document.removeEventListener('mousemove', onPointerMove, false);
+    scope.document.removeEventListener('mouseup', onMouseUp, false);
+  }
+
+  /**
+   * intersectObjects
+   * @param {} event
+   * @param {} objects
+   * @returns {?}
+   */
+  function intersectObjects(event, objects)
+  {
+    var pointer = event.touches ? event.touches[0] : event;
+
+    var rect = domElement.getBoundingClientRect();
+    var x = (pointer.clientX - rect.left) / rect.width;
+    var y = (pointer.clientY - rect.top) / rect.height;
+    pointerVector.set((x) * 2 - 1, - (y) * 2 + 1, 0.5);
+
+    projector.unprojectVector(pointerVector, scope.camera);
+    ray.set(camPosition, pointerVector.sub(camPosition).normalize());
+
+    // checks all intersections between the ray and the objects,
+    // true to check the descendants
+    var intersections = ray.intersectObjects(objects, true);
+    return intersections[0] ? intersections[0] : false;
+  }
+
+  /**
+   * Checks if given name is currently selected
+   * @param {} name
+   * @returns {bool}
+   */
+  function isSelected(name)
+  {
+    if(scope.selected.search(name) !== -1)
+    {
+        return true;
+    }
+    else
+    {
+        return false;
+    }
+  }
+
+  /**
+   * bakeTransformations
+   * @param {} object
+   */
+  function bakeTransformations(object)
+  {
+    var tempGeometry = new THREE.Geometry();
+    THREE.GeometryUtils.merge(tempGeometry, object);
+    object.geometry = tempGeometry;
+    object.position.set(0, 0, 0);
+    object.rotation.set(0, 0, 0);
+    object.scale.set(1, 1, 1);
+  }
+};
+
+GZ3D.Manipulator.prototype = Object.create(THREE.EventDispatcher.prototype);
+
diff --git a/gz3d/src/gzradialmenu.js b/gz3d/src/gzradialmenu.js
new file mode 100644
index 0000000000000000000000000000000000000000..0fca39b3e418114a9be2950ae8cdd69d9e1713e7
--- /dev/null
+++ b/gz3d/src/gzradialmenu.js
@@ -0,0 +1,405 @@
+/**
+ * Radial menu for an object
+ * @constructor
+ */
+GZ3D.RadialMenu = function(domElement)
+{
+  this.domElement = ( domElement !== undefined ) ? domElement : document;
+
+  this.init();
+};
+
+/**
+ * Initialize radial menu
+ */
+GZ3D.RadialMenu.prototype.init = function()
+{
+  var scale = 1.2;
+  // Distance from starting point
+  this.radius = 70*scale;
+  // Speed to spread the menu
+  this.speed = 10*scale;
+  // Icon size
+  this.bgSize = 40*scale;
+  this.bgSizeSelected = 68*scale;
+  this.highlightSize = 45*scale;
+  this.iconProportion = 0.6;
+  this.bgShape = THREE.ImageUtils.loadTexture(
+      'style/images/icon_background.png' );
+  this.layers = {
+    ICON: 0,
+    BACKGROUND : 1,
+    HIGHLIGHT : 2
+  };
+
+  // For the opening motion
+  this.moving = false;
+  this.startPosition = null;
+
+  // Either moving or already stopped
+  this.showing = false;
+
+  // Colors
+  this.selectedColor = new THREE.Color(0x22aadd);
+  this.plainColor = new THREE.Color(0x333333);
+  this.highlightColor = new THREE.Color(0x22aadd);
+  this.disabledColor = new THREE.Color(0x888888);
+
+  // Selected item
+  this.selected = null;
+
+  // Selected model
+  this.model = null;
+
+  // Object containing all items
+  this.menu = new THREE.Object3D();
+
+  // Add items to the menu
+  this.addItem('delete','style/images/trash.png');
+  this.addItem('translate','style/images/translate.png');
+  this.addItem('rotate','style/images/rotate.png');
+  this.addItem('transparent','style/images/transparent.png');
+  this.addItem('wireframe','style/images/wireframe.png');
+  this.addItem('joints','style/images/joints.png');
+
+  this.setNumberOfItems(this.menu.children.length);
+
+  // Start hidden
+  this.hide();
+};
+
+/**
+ * Hide radial menu
+ * @param {} event - event which triggered hide
+ * @param {function} callback
+ */
+GZ3D.RadialMenu.prototype.hide = function(event,callback)
+{
+  for (var i = 0; i < this.numberOfItems; i++)
+  {
+    var item = this.menu.children[i];
+
+    item.children[this.layers.ICON].visible = false;
+    item.children[this.layers.ICON].scale.set(
+        this.bgSize*this.iconProportion,
+        this.bgSize*this.iconProportion, 1.0 );
+
+    item.children[this.layers.BACKGROUND].visible = false;
+    item.children[this.layers.BACKGROUND].material.color = this.plainColor;
+    item.children[this.layers.BACKGROUND].scale.set(
+        this.bgSize,
+        this.bgSize, 1.0 );
+
+    item.children[this.layers.HIGHLIGHT].visible = false;
+  }
+
+  this.showing = false;
+  this.moving = false;
+  this.startPosition = null;
+
+  if (callback && this.model)
+  {
+    if ( this.selected )
+    {
+      callback(this.selected,this.model);
+      this.model = null;
+    }
+  }
+  this.selected = null;
+};
+
+/**
+ * Show radial menu
+ * @param {} event - event which triggered show
+ * @param {THREE.Object3D} model - model to which the menu will be attached
+ */
+GZ3D.RadialMenu.prototype.show = function(event,model)
+{
+  if (this.showing)
+  {
+    return;
+  }
+
+  this.model = model;
+
+  if (model.children[0] instanceof THREE.Light)
+  {
+    this.setNumberOfItems(3);
+  }
+  else
+  {
+    this.setNumberOfItems(6);
+  }
+
+  var pointer = this.getPointer(event);
+  this.startPosition = pointer;
+
+  this.menu.getObjectByName('transparent').isHighlighted = false;
+  this.menu.getObjectByName('wireframe').isHighlighted = false;
+  this.menu.getObjectByName('joints').isHighlighted = false;
+  this.menu.getObjectByName('joints').isDisabled = false;
+  if (this.model.viewAs === 'transparent')
+  {
+    this.menu.getObjectByName('transparent').isHighlighted = true;
+  }
+  if (this.model.viewAs === 'wireframe')
+  {
+    this.menu.getObjectByName('wireframe').isHighlighted = true;
+  }
+  if (this.model.joint === undefined || this.model.joint.length === 0)
+  {
+    this.menu.getObjectByName('joints').isDisabled = true;
+  }
+  else if (this.model.getObjectByName('JOINT_VISUAL', true))
+  {
+    this.menu.getObjectByName('joints').isHighlighted = true;
+  }
+
+  for (var i = 0; i < this.numberOfItems; i++)
+  {
+    var item = this.menu.children[i];
+
+    item.children[this.layers.ICON].visible = true;
+    item.children[this.layers.ICON].position.set(pointer.x,pointer.y,0);
+
+    item.children[this.layers.BACKGROUND].visible = true;
+    item.children[this.layers.BACKGROUND].position.set(pointer.x,pointer.y,0);
+    if (item.isDisabled)
+    {
+      item.children[this.layers.BACKGROUND].material.color = this.disabledColor;
+    }
+
+    item.children[this.layers.HIGHLIGHT].visible = item.isHighlighted;
+    item.children[this.layers.HIGHLIGHT].position.set(pointer.x,pointer.y,0);
+  }
+
+  this.moving = true;
+  this.showing = true;
+};
+
+/**
+ * Update radial menu
+ */
+GZ3D.RadialMenu.prototype.update = function()
+{
+  if (!this.moving)
+  {
+    return;
+  }
+
+  // Move outwards
+  for (var i = 0; i < this.numberOfItems; i++)
+  {
+    var item = this.menu.children[i];
+
+    var X = item.children[this.layers.ICON].position.x -
+        this.startPosition.x;
+    var Y = item.children[this.layers.ICON].position.y -
+        this.startPosition.y;
+
+    var d = Math.sqrt(Math.pow(X,2) + Math.pow(Y,2));
+
+    if ( d < this.radius)
+    {
+      X = X - ( this.speed * Math.sin( ( this.offset - i ) * Math.PI/4 ) );
+      Y = Y - ( this.speed * Math.cos( ( this.offset - i ) * Math.PI/4 ) );
+    }
+    else
+    {
+      this.moving = false;
+    }
+
+    item.children[this.layers.ICON].position.x = X + this.startPosition.x;
+    item.children[this.layers.ICON].position.y = Y + this.startPosition.y;
+
+    item.children[this.layers.BACKGROUND].position.x = X + this.startPosition.x;
+    item.children[this.layers.BACKGROUND].position.y = Y + this.startPosition.y;
+
+    item.children[this.layers.HIGHLIGHT].position.x = X + this.startPosition.x;
+    item.children[this.layers.HIGHLIGHT].position.y = Y + this.startPosition.y;
+  }
+
+};
+
+/**
+ * Get pointer (mouse or touch) coordinates inside the canvas
+ * @param {} event
+ */
+GZ3D.RadialMenu.prototype.getPointer = function(event)
+{
+  if (event.originalEvent)
+  {
+    event = event.originalEvent;
+  }
+  var pointer = event.touches ? event.touches[ 0 ] : event;
+  var rect = this.domElement.getBoundingClientRect();
+  var posX = (pointer.clientX - rect.left);
+  var posY = (pointer.clientY - rect.top);
+
+  return {x: posX, y:posY};
+};
+
+/**
+ * Movement after long press to select items on menu
+ * @param {} event
+ */
+GZ3D.RadialMenu.prototype.onLongPressMove = function(event)
+{
+  var pointer = this.getPointer(event);
+  var pointerX = pointer.x - this.startPosition.x;
+  var pointerY = pointer.y - this.startPosition.y;
+
+  var angle = Math.atan2(pointerY,pointerX);
+
+  // Check angle region
+  var region = null;
+  // bottom-left
+  if (angle > 5*Math.PI/8 && angle < 7*Math.PI/8)
+  {
+    region = 1;
+  }
+  // left
+  else if ( (angle > -8*Math.PI/8 && angle < -7*Math.PI/8) ||
+      (angle > 7*Math.PI/8 && angle < 8*Math.PI/8) )
+  {
+    region = 2;
+  }
+  // top-left
+  else if (angle > -7*Math.PI/8 && angle < -5*Math.PI/8)
+  {
+    region = 3;
+  }
+  // top
+  else if (angle > -5*Math.PI/8 && angle < -3*Math.PI/8)
+  {
+    region = 4;
+  }
+  // top-right
+  else if (angle > -3*Math.PI/8 && angle < -1*Math.PI/8)
+  {
+    region = 5;
+  }
+  // right
+  else if (angle > -1*Math.PI/8 && angle < 1*Math.PI/8)
+  {
+    region = 6;
+  }
+  // bottom-right
+  else if (angle > 1*Math.PI/8 && angle < 3*Math.PI/8)
+  {
+    region = 7;
+  }
+  // bottom
+  else if (angle > 3*Math.PI/8 && angle < 5*Math.PI/8)
+  {
+    region = 8;
+  }
+
+  // Check if any existing item is in the region
+  var Selected = region - 4 + this.offset;
+
+  if (Selected >= this.numberOfItems || Selected < 0)
+  {
+    this.selected = null;
+    Selected = null;
+  }
+
+  var counter = 0;
+  for (var i = 0; i < this.numberOfItems; i++)
+  {
+    var item = this.menu.children[i];
+
+    if (counter === Selected)
+    {
+      item.children[this.layers.ICON].scale.set(
+          this.bgSizeSelected*this.iconProportion,
+          this.bgSizeSelected*this.iconProportion, 1.0 );
+      this.selected = item.children[this.layers.ICON].name;
+
+      if (!item.isDisabled)
+      {
+        item.children[this.layers.BACKGROUND].material.color =
+            this.selectedColor;
+      }
+      item.children[this.layers.BACKGROUND].scale.set(
+          this.bgSizeSelected,
+          this.bgSizeSelected, 1.0 );
+    }
+    else
+    {
+      item.children[this.layers.ICON].scale.set(
+          this.bgSize*this.iconProportion,
+          this.bgSize*this.iconProportion, 1.0 );
+
+      item.children[this.layers.BACKGROUND].scale.set(
+          this.bgSize, this.bgSize, 1.0 );
+      if (!item.isDisabled)
+      {
+        item.children[this.layers.BACKGROUND].material.color = this.plainColor;
+      }
+    }
+    counter++;
+  }
+};
+
+/**
+ * Create an item and add it to the menu.
+ * Create them in order
+ * @param {string} type - delete/translate/rotate/transparent/wireframe/joints
+ * @param {string} iconTexture - icon's uri
+ */
+GZ3D.RadialMenu.prototype.addItem = function(type, iconTexture)
+{
+  // Icon
+  iconTexture = THREE.ImageUtils.loadTexture( iconTexture );
+
+  var iconMaterial = new THREE.SpriteMaterial( { useScreenCoordinates: true,
+      alignment: THREE.SpriteAlignment.center } );
+  iconMaterial.map = iconTexture;
+
+  var icon = new THREE.Sprite( iconMaterial );
+  icon.scale.set( this.bgSize*this.iconProportion,
+      this.bgSize*this.iconProportion, 1.0 );
+  icon.name = type;
+
+  // Background
+  var bgMaterial = new THREE.SpriteMaterial( {
+      map: this.bgShape,
+      useScreenCoordinates: true,
+      alignment: THREE.SpriteAlignment.center,
+      color: this.plainColor } );
+
+  var bg = new THREE.Sprite( bgMaterial );
+  bg.scale.set( this.bgSize, this.bgSize, 1.0 );
+
+  // Highlight
+  var highlightMaterial = new THREE.SpriteMaterial({
+      map: this.bgShape,
+      useScreenCoordinates: true,
+      alignment: THREE.SpriteAlignment.center,
+      color: this.highlightColor});
+
+  var highlight = new THREE.Sprite(highlightMaterial);
+  highlight.scale.set(this.highlightSize, this.highlightSize, 1.0);
+  highlight.visible = false;
+
+  var item = new THREE.Object3D();
+  // Respect layer order
+  item.add(icon);
+  item.add(bg);
+  item.add(highlight);
+  item.isHighlighted = false;
+  item.name = type;
+
+  this.menu.add(item);
+};
+
+/**
+ * Set number of items (different for models and lights)
+ * @param {int} number
+ */
+GZ3D.RadialMenu.prototype.setNumberOfItems = function(number)
+{
+  this.numberOfItems = number;
+  this.offset = this.numberOfItems - 1 - Math.floor(this.numberOfItems/2);
+};
diff --git a/gz3d/src/gzscene.js b/gz3d/src/gzscene.js
new file mode 100644
index 0000000000000000000000000000000000000000..d69c9d100a1bac5570a3a8d84318a08a7e202650
--- /dev/null
+++ b/gz3d/src/gzscene.js
@@ -0,0 +1,2427 @@
+/**
+ * The scene is where everything is placed, from objects, to lights and cameras.
+ * @constructor
+ */
+GZ3D.Scene = function()
+{
+  this.init();
+};
+
+/**
+ * Initialize scene
+ */
+GZ3D.Scene.prototype.init = function()
+{
+  this.name = 'default';
+  this.scene = new THREE.Scene();
+  // this.scene.name = this.name;
+  this.meshes = {};
+
+  // only support one heightmap for now.
+  this.heightmap = null;
+
+  this.selectedEntity = null;
+
+  this.manipulationMode = 'view';
+  this.pointerOnMenu = false;
+
+  this.renderer = new THREE.WebGLRenderer({antialias: true });
+  this.renderer.setClearColor(0xb2b2b2, 1); // Sky
+  this.renderer.setSize( window.innerWidth, window.innerHeight);
+  // this.renderer.shadowMapEnabled = true;
+  // this.renderer.shadowMapSoft = true;
+
+  // lights
+  this.ambient = new THREE.AmbientLight( 0x666666 );
+  this.scene.add(this.ambient);
+
+  // camera
+  this.camera = new THREE.PerspectiveCamera(
+      60, window.innerWidth / window.innerHeight, 0.1, 1000 );
+  this.defaultCameraPosition = new THREE.Vector3(0, -5, 5);
+  this.resetView();
+
+  // Grid
+  this.grid = new THREE.GridHelper(10, 1);
+  this.grid.name = 'grid';
+  this.grid.position.z = 0.05;
+  this.grid.rotation.x = Math.PI * 0.5;
+  this.grid.castShadow = false;
+  this.grid.setColors(new THREE.Color( 0xCCCCCC ),new THREE.Color( 0x4D4D4D ));
+  this.grid.material.transparent = true;
+  this.grid.material.opacity = 0.5;
+  this.grid.visible = false;
+  this.scene.add(this.grid);
+
+  this.showCollisions = false;
+
+  this.spawnModel = new GZ3D.SpawnModel(
+      this, this.getDomElement());
+  // Material for simple shapes being spawned (grey transparent)
+  this.spawnedShapeMaterial = new THREE.MeshPhongMaterial(
+      {color:0xffffff, shading: THREE.SmoothShading} );
+  this.spawnedShapeMaterial.transparent = true;
+  this.spawnedShapeMaterial.opacity = 0.5;
+
+  var that = this;
+
+  // Need to use `document` instead of getDomElement in order to get events
+  // outside the webgl div element.
+  document.addEventListener( 'mouseup',
+      function(event) {that.onPointerUp(event);}, false );
+
+  this.getDomElement().addEventListener( 'mouseup',
+      function(event) {that.onPointerUp(event);}, false );
+
+  this.getDomElement().addEventListener( 'DOMMouseScroll',
+      function(event) {that.onMouseScroll(event);}, false ); //firefox
+
+  this.getDomElement().addEventListener( 'mousewheel',
+      function(event) {that.onMouseScroll(event);}, false );
+
+  document.addEventListener( 'keydown',
+      function(event) {that.onKeyDown(event);}, false );
+
+  this.getDomElement().addEventListener( 'mousedown',
+      function(event) {that.onPointerDown(event);}, false );
+  this.getDomElement().addEventListener( 'touchstart',
+      function(event) {that.onPointerDown(event);}, false );
+
+  this.getDomElement().addEventListener( 'touchend',
+      function(event) {that.onPointerUp(event);}, false );
+
+  // Handles for translating and rotating objects
+  this.modelManipulator = new GZ3D.Manipulator(this.camera, isTouchDevice,
+      this.getDomElement());
+
+  this.timeDown = null;
+
+  this.controls = new THREE.OrbitControls(this.camera);
+  this.scene.add(this.controls.targetIndicator);
+
+  this.emitter = new EventEmitter2({ verbose: true });
+
+  // SSAO
+  this.effectsEnabled = false;
+  // depth
+  var depthShader = THREE.ShaderLib[ 'depthRGBA'];
+  var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms );
+
+  this.depthMaterial = new THREE.ShaderMaterial( {
+      fragmentShader: depthShader.fragmentShader,
+      vertexShader: depthShader.vertexShader,
+      uniforms: depthUniforms } );
+  this.depthMaterial.blending = THREE.NoBlending;
+
+  // postprocessing
+  this.composer = new THREE.EffectComposer(this.renderer );
+  this.composer.addPass( new THREE.RenderPass(this.scene,this.camera));
+
+  this.depthTarget = new THREE.WebGLRenderTarget( window.innerWidth,
+      window.innerHeight, { minFilter: THREE.NearestFilter,
+      magFilter: THREE.NearestFilter, format: THREE.RGBAFormat } );
+
+  var effect = new THREE.ShaderPass( THREE.SSAOShader );
+  effect.uniforms[ 'tDepth' ].value = this.depthTarget;
+  effect.uniforms[ 'size' ].value.set( window.innerWidth, window.innerHeight );
+  effect.uniforms[ 'cameraNear' ].value = this.camera.near;
+  effect.uniforms[ 'cameraFar' ].value = this.camera.far;
+  effect.renderToScreen = true;
+  this.composer.addPass( effect );
+
+  // Radial menu (only triggered by touch)
+  this.radialMenu = new GZ3D.RadialMenu(this.getDomElement());
+  this.scene.add(this.radialMenu.menu);
+
+  // Bounding Box
+  var vertices = [
+    new THREE.Vector3(0, 0, 0),
+    new THREE.Vector3(0, 0, 0),
+    new THREE.Vector3(0, 0, 0),
+    new THREE.Vector3(0, 0, 0),
+
+    new THREE.Vector3(0, 0, 0),
+    new THREE.Vector3(0, 0, 0),
+    new THREE.Vector3(0, 0, 0),
+    new THREE.Vector3(0, 0, 0)
+  ];
+  var boxGeometry = new THREE.Geometry();
+  boxGeometry.vertices.push(
+    vertices[0], vertices[1],
+    vertices[1], vertices[2],
+    vertices[2], vertices[3],
+    vertices[3], vertices[0],
+
+    vertices[4], vertices[5],
+    vertices[5], vertices[6],
+    vertices[6], vertices[7],
+    vertices[7], vertices[4],
+
+    vertices[0], vertices[4],
+    vertices[1], vertices[5],
+    vertices[2], vertices[6],
+    vertices[3], vertices[7]
+  );
+  this.boundingBox = new THREE.Line(boxGeometry,
+      new THREE.LineBasicMaterial({color: 0xffffff}),
+      THREE.LinePieces);
+  this.boundingBox.visible = false;
+
+  // Joint visuals
+  this.jointTypes =
+      {
+        REVOLUTE: 1,
+        REVOLUTE2: 2,
+        PRISMATIC: 3,
+        UNIVERSAL: 4,
+        BALL: 5,
+        SCREW: 6,
+        GEARBOX: 7
+      };
+  this.jointAxis = new THREE.Object3D();
+  this.jointAxis.name = 'JOINT_VISUAL';
+  var geometry, material, mesh;
+
+  // XYZ
+  var XYZaxes = new THREE.Object3D();
+
+  geometry = new THREE.CylinderGeometry(0.01, 0.01, 0.3, 10, 1, false);
+
+  material = new THREE.MeshBasicMaterial({color: new THREE.Color(0xff0000)});
+  mesh = new THREE.Mesh(geometry, material);
+  mesh.position.x = 0.15;
+  mesh.rotation.z = -Math.PI/2;
+  mesh.name = 'JOINT_VISUAL';
+  XYZaxes.add(mesh);
+
+  material = new THREE.MeshBasicMaterial({color: new THREE.Color(0x00ff00)});
+  mesh = new THREE.Mesh(geometry, material);
+  mesh.position.y = 0.15;
+  mesh.name = 'JOINT_VISUAL';
+  XYZaxes.add(mesh);
+
+  material = new THREE.MeshBasicMaterial({color: new THREE.Color(0x0000ff)});
+  mesh = new THREE.Mesh(geometry, material);
+  mesh.position.z = 0.15;
+  mesh.rotation.x = Math.PI/2;
+  mesh.name = 'JOINT_VISUAL';
+  XYZaxes.add(mesh);
+
+  geometry = new THREE.CylinderGeometry(0, 0.03, 0.1, 10, 1, true);
+
+  material = new THREE.MeshBasicMaterial({color: new THREE.Color(0xff0000)});
+  mesh = new THREE.Mesh(geometry, material);
+  mesh.position.x = 0.3;
+  mesh.rotation.z = -Math.PI/2;
+  mesh.name = 'JOINT_VISUAL';
+  XYZaxes.add(mesh);
+
+  material = new THREE.MeshBasicMaterial({color: new THREE.Color(0x00ff00)});
+  mesh = new THREE.Mesh(geometry, material);
+  mesh.position.y = 0.3;
+  mesh.name = 'JOINT_VISUAL';
+  XYZaxes.add(mesh);
+
+  material = new THREE.MeshBasicMaterial({color: new THREE.Color(0x0000ff)});
+  mesh = new THREE.Mesh(geometry, material);
+  mesh.position.z = 0.3;
+  mesh.rotation.x = Math.PI/2;
+  mesh.name = 'JOINT_VISUAL';
+  XYZaxes.add(mesh);
+
+  this.jointAxis['XYZaxes'] = XYZaxes;
+
+  var mainAxis = new THREE.Object3D();
+
+  material = new THREE.MeshLambertMaterial();
+  material.color = new THREE.Color(0xffff00);
+  material.ambient = material.color;
+
+  geometry = new THREE.CylinderGeometry(0.02, 0.02, 0.25, 36, 1, false);
+
+  mesh = new THREE.Mesh(geometry, material);
+  mesh.position.z = -0.175;
+  mesh.rotation.x = Math.PI/2;
+  mesh.name = 'JOINT_VISUAL';
+  mainAxis.add(mesh);
+
+  geometry = new THREE.CylinderGeometry(0, 0.035, 0.1, 36, 1, false);
+
+  mesh = new THREE.Mesh(geometry, material);
+  mesh.rotation.x = Math.PI/2;
+  mesh.name = 'JOINT_VISUAL';
+  mainAxis.add(mesh);
+
+  this.jointAxis['mainAxis'] = mainAxis;
+
+  var rotAxis = new THREE.Object3D();
+
+  geometry = new THREE.TorusGeometry(0.04, 0.006, 10, 36, Math.PI * 3/2);
+
+  mesh = new THREE.Mesh(geometry, material);
+  mesh.name = 'JOINT_VISUAL';
+  rotAxis.add(mesh);
+
+  geometry = new THREE.CylinderGeometry(0.015, 0, 0.025, 10, 1, false);
+
+  mesh = new THREE.Mesh(geometry, material);
+  mesh.position.y = -0.04;
+  mesh.rotation.z = Math.PI/2;
+  mesh.name = 'JOINT_VISUAL';
+  rotAxis.add(mesh);
+
+  this.jointAxis['rotAxis'] = rotAxis;
+
+  var transAxis = new THREE.Object3D();
+
+  geometry = new THREE.CylinderGeometry(0.01, 0.01, 0.1, 10, 1, true);
+
+  mesh = new THREE.Mesh(geometry, material);
+  mesh.position.x = 0.03;
+  mesh.position.y = 0.03;
+  mesh.position.z = -0.15;
+  mesh.rotation.x = Math.PI/2;
+  mesh.name = 'JOINT_VISUAL';
+  transAxis.add(mesh);
+
+  geometry = new THREE.CylinderGeometry(0.02, 0, 0.0375, 10, 1, false);
+
+  mesh = new THREE.Mesh(geometry, material);
+  mesh.position.x = 0.03;
+  mesh.position.y = 0.03;
+  mesh.position.z = -0.15 + 0.05;
+  mesh.rotation.x = -Math.PI/2;
+  mesh.name = 'JOINT_VISUAL';
+  transAxis.add(mesh);
+
+  mesh = new THREE.Mesh(geometry, material);
+  mesh.position.x = 0.03;
+  mesh.position.y = 0.03;
+  mesh.position.z = -0.15 - 0.05;
+  mesh.rotation.x = Math.PI/2;
+  mesh.name = 'JOINT_VISUAL';
+  transAxis.add(mesh);
+
+  this.jointAxis['transAxis'] = transAxis;
+
+  var screwAxis = new THREE.Object3D();
+
+  mesh = new THREE.Mesh(geometry, material);
+  mesh.position.x = -0.04;
+  mesh.position.z = -0.11;
+  mesh.rotation.z = -Math.PI/4;
+  mesh.rotation.x = -Math.PI/10;
+  mesh.name = 'JOINT_VISUAL';
+  screwAxis.add(mesh);
+
+  var radius = 0.04;
+  var length = 0.02;
+  var curve = new THREE.SplineCurve3([new THREE.Vector3(radius, 0, 0*length),
+                                      new THREE.Vector3(0, radius, 1*length),
+                                      new THREE.Vector3(-radius, 0, 2*length),
+                                      new THREE.Vector3(0, -radius, 3*length),
+                                      new THREE.Vector3(radius, 0, 4*length),
+                                      new THREE.Vector3(0, radius, 5*length),
+                                      new THREE.Vector3(-radius, 0, 6*length)]);
+  geometry = new THREE.TubeGeometry(curve, 36, 0.01, 10, false, false);
+
+  mesh = new THREE.Mesh(geometry, material);
+  mesh.position.z = -0.23;
+  mesh.name = 'JOINT_VISUAL';
+  screwAxis.add(mesh);
+
+  this.jointAxis['screwAxis'] = screwAxis;
+
+  var ballVisual = new THREE.Object3D();
+
+  geometry = new THREE.SphereGeometry(0.06);
+
+  mesh = new THREE.Mesh(geometry, material);
+  mesh.name = 'JOINT_VISUAL';
+  ballVisual.add(mesh);
+
+  this.jointAxis['ballVisual'] = ballVisual;
+};
+
+GZ3D.Scene.prototype.initScene = function()
+{
+  guiEvents.emit('show_grid', 'show');
+
+  // create a sun light
+  var obj = this.createLight(3, new THREE.Color(0.8, 0.8, 0.8), 0.9,
+       {position: {x:0, y:0, z:10}, orientation: {x:0, y:0, z:0, w:1}},
+       null, true, 'sun', {x: 0.5, y: 0.1, z: -0.9});
+
+  this.add(obj);
+};
+
+GZ3D.Scene.prototype.setSDFParser = function(sdfParser)
+{
+  this.spawnModel.sdfParser = sdfParser;
+};
+
+/**
+ * Window event callback
+ * @param {} event - mousedown or touchdown events
+ */
+GZ3D.Scene.prototype.onPointerDown = function(event)
+{
+  event.preventDefault();
+
+  if (this.spawnModel.active)
+  {
+    return;
+  }
+
+  var mainPointer = true;
+  var pos;
+  if (event.touches)
+  {
+    if (event.touches.length === 1)
+    {
+      pos = new THREE.Vector2(
+          event.touches[0].clientX, event.touches[0].clientY);
+    }
+    else if (event.touches.length === 2)
+    {
+      pos = new THREE.Vector2(
+          (event.touches[0].clientX + event.touches[1].clientX)/2,
+          (event.touches[0].clientY + event.touches[1].clientY)/2);
+    }
+    else
+    {
+      return;
+    }
+  }
+  else
+  {
+    pos = new THREE.Vector2(
+          event.clientX, event.clientY);
+    if (event.which !== 1)
+    {
+      mainPointer = false;
+    }
+  }
+
+  var intersect = new THREE.Vector3();
+  var model = this.getRayCastModel(pos, intersect);
+
+  if (intersect)
+  {
+    this.controls.target = intersect;
+  }
+
+  // Cancel in case of multitouch
+  if (event.touches && event.touches.length !== 1)
+  {
+    return;
+  }
+
+  // Manipulation modes
+  // Model found
+  if (model)
+  {
+    // Do nothing to the floor plane
+    if (model.name === 'plane')
+    {
+      this.timeDown = new Date().getTime();
+    }
+    else if (this.modelManipulator.pickerNames.indexOf(model.name) >= 0)
+    {
+      // Do not attach manipulator to itself
+    }
+    // Attach manipulator to model
+    else if (model.name !== '')
+    {
+      if (mainPointer && model.parent === this.scene)
+      {
+        this.selectEntity(model);
+      }
+    }
+    // Manipulator pickers, for mouse
+    else if (this.modelManipulator.hovered)
+    {
+      this.modelManipulator.update();
+      this.modelManipulator.object.updateMatrixWorld();
+    }
+    // Sky
+    else
+    {
+      this.timeDown = new Date().getTime();
+    }
+  }
+  // Plane from below, for example
+  else
+  {
+    this.timeDown = new Date().getTime();
+  }
+};
+
+/**
+ * Window event callback
+ * @param {} event - mouseup or touchend events
+ */
+GZ3D.Scene.prototype.onPointerUp = function(event)
+{
+  event.preventDefault();
+
+  // Clicks (<150ms) outside any models trigger view mode
+  var millisecs = new Date().getTime();
+  if (millisecs - this.timeDown < 150)
+  {
+    this.setManipulationMode('view');
+    $( '#view-mode' ).click();
+    $('input[type="radio"]').checkboxradio('refresh');
+  }
+  this.timeDown = null;
+};
+
+/**
+ * Window event callback
+ * @param {} event - mousescroll event
+ */
+GZ3D.Scene.prototype.onMouseScroll = function(event)
+{
+  event.preventDefault();
+
+  var pos = new THREE.Vector2(event.clientX, event.clientY);
+
+  var intersect = new THREE.Vector3();
+  var model = this.getRayCastModel(pos, intersect);
+
+  if (intersect)
+  {
+    this.controls.target = intersect;
+  }
+};
+
+/**
+ * Window event callback
+ * @param {} event - keydown events
+ */
+GZ3D.Scene.prototype.onKeyDown = function(event)
+{
+  if (event.shiftKey)
+  {
+    // + and - for zooming
+    if (event.keyCode === 187 || event.keyCode === 189)
+    {
+      var pos = new THREE.Vector2(window.innerWidth/2.0,
+          window.innerHeight/2.0);
+
+      var intersect = new THREE.Vector3();
+      var model = this.getRayCastModel(pos, intersect);
+
+      if (intersect)
+      {
+        this.controls.target = intersect;
+      }
+
+      if (event.keyCode === 187)
+      {
+        this.controls.dollyOut();
+      }
+      else
+      {
+        this.controls.dollyIn();
+      }
+    }
+  }
+
+  // DEL to delete entities
+  if (event.keyCode === 46)
+  {
+    if (this.selectedEntity)
+    {
+      guiEvents.emit('delete_entity');
+    }
+  }
+
+  // F2 for turning on effects
+  if (event.keyCode === 113)
+  {
+    this.effectsEnabled = !this.effectsEnabled;
+  }
+
+  // Esc/R/T for changing manipulation modes
+  if (event.keyCode === 27) // Esc
+  {
+    $( '#view-mode' ).click();
+    $('input[type="radio"]').checkboxradio('refresh');
+  }
+  if (event.keyCode === 82) // R
+  {
+    $( '#rotate-mode' ).click();
+    $('input[type="radio"]').checkboxradio('refresh');
+  }
+  if (event.keyCode === 84) // T
+  {
+    $( '#translate-mode' ).click();
+    $('input[type="radio"]').checkboxradio('refresh');
+  }
+};
+
+/**
+ * Check if there's a model immediately under canvas coordinate 'pos'
+ * @param {THREE.Vector2} pos - Canvas coordinates
+ * @param {THREE.Vector3} intersect - Empty at input,
+ * contains point of intersection in 3D world coordinates at output
+ * @returns {THREE.Object3D} model - Intercepted model closest to the camera
+ */
+GZ3D.Scene.prototype.getRayCastModel = function(pos, intersect)
+{
+  var projector = new THREE.Projector();
+  var vector = new THREE.Vector3(
+      ((pos.x - this.renderer.domElement.offsetLeft)
+      / window.innerWidth) * 2 - 1,
+      -((pos.y - this.renderer.domElement.offsetTop)
+      / window.innerHeight) * 2 + 1, 1);
+  projector.unprojectVector(vector, this.camera);
+  var ray = new THREE.Raycaster( this.camera.position,
+      vector.sub(this.camera.position).normalize() );
+
+  var allObjects = [];
+  this.scene.getDescendants(allObjects);
+  var objects = ray.intersectObjects(allObjects);
+
+  var model;
+  var point;
+  if (objects.length > 0)
+  {
+    modelsloop:
+    for (var i = 0; i < objects.length; ++i)
+    {
+      model = objects[i].object;
+      if (model.name.indexOf('_lightHelper') >= 0)
+      {
+        model = model.parent;
+        break;
+      }
+
+      if (!this.modelManipulator.hovered &&
+          (model.name === 'plane'))
+      {
+        // model = null;
+        point = objects[i].point;
+        break;
+      }
+
+      if (model.name === 'grid' || model.name === 'boundingBox' ||
+          model.name === 'JOINT_VISUAL')
+      {
+        point = objects[i].point;
+        model = null;
+        continue;
+      }
+
+      while (model.parent !== this.scene)
+      {
+        // Select current mode's handle
+        if (model.parent.parent === this.modelManipulator.gizmo &&
+            ((this.manipulationMode === 'translate' &&
+              model.name.indexOf('T') >=0) ||
+             (this.manipulationMode === 'rotate' &&
+               model.name.indexOf('R') >=0)))
+        {
+          break modelsloop;
+        }
+        model = model.parent;
+      }
+
+      if (model === this.radialMenu.menu)
+      {
+        continue;
+      }
+
+      if (model.name.indexOf('COLLISION_VISUAL') >= 0)
+      {
+        model = null;
+        continue;
+      }
+
+      if (this.modelManipulator.hovered)
+      {
+        if (model === this.modelManipulator.gizmo)
+        {
+          break;
+        }
+      }
+      else if (model.name !== '')
+      {
+        point = objects[i].point;
+        break;
+      }
+    }
+  }
+  if (point)
+  {
+    intersect.x = point.x;
+    intersect.y = point.y;
+    intersect.z = point.z;
+  }
+  return model;
+};
+
+/**
+ * Get dom element
+ * @returns {domElement}
+ */
+GZ3D.Scene.prototype.getDomElement = function()
+{
+  return this.renderer.domElement;
+};
+
+/**
+ * Render scene
+ */
+GZ3D.Scene.prototype.render = function()
+{
+  // Kill camera control when:
+  // -manipulating
+  // -using radial menu
+  // -pointer over menus
+  // -spawning
+  if (this.modelManipulator.hovered ||
+      this.radialMenu.showing ||
+      this.pointerOnMenu ||
+      this.spawnModel.active)
+  {
+    this.controls.enabled = false;
+    this.controls.update();
+  }
+  else
+  {
+    this.controls.enabled = true;
+    this.controls.update();
+  }
+
+  this.modelManipulator.update();
+  this.radialMenu.update();
+
+  if (this.effectsEnabled)
+  {
+    this.scene.overrideMaterial = this.depthMaterial;
+    this.renderer.render(this.scene, this.camera, this.depthTarget);
+    this.scene.overrideMaterial = null;
+    this.composer.render();
+  }
+  else
+  {
+    this.renderer.render(this.scene, this.camera);
+  }
+};
+
+/**
+ * Set window size
+ * @param {double} width
+ * @param {double} height
+ */
+GZ3D.Scene.prototype.setWindowSize = function(width, height)
+{
+  this.camera.aspect = width / height;
+  this.camera.updateProjectionMatrix();
+
+  this.renderer.setSize( width, height);
+  this.render();
+};
+
+/**
+ * Add object to the scene
+ * @param {THREE.Object3D} model
+ */
+GZ3D.Scene.prototype.add = function(model)
+{
+  model.viewAs = 'normal';
+  this.scene.add(model);
+};
+
+/**
+ * Remove object from the scene
+ * @param {THREE.Object3D} model
+ */
+GZ3D.Scene.prototype.remove = function(model)
+{
+  this.scene.remove(model);
+};
+
+/**
+ * Returns the object which has the given name
+ * @param {string} name
+ * @returns {THREE.Object3D} model
+ */
+GZ3D.Scene.prototype.getByName = function(name)
+{
+  return this.scene.getObjectByName(name, true);
+};
+
+/**
+ * Update a model's pose
+ * @param {THREE.Object3D} model
+ * @param {} position
+ * @param {} orientation
+ */
+GZ3D.Scene.prototype.updatePose = function(model, position, orientation)
+{
+  if (this.modelManipulator && this.modelManipulator.object &&
+      this.modelManipulator.hovered)
+  {
+    return;
+  }
+
+  this.setPose(model, position, orientation);
+};
+
+/**
+ * Set a model's pose
+ * @param {THREE.Object3D} model
+ * @param {} position
+ * @param {} orientation
+ */
+GZ3D.Scene.prototype.setPose = function(model, position, orientation)
+{
+  model.position.x = position.x;
+  model.position.y = position.y;
+  model.position.z = position.z;
+  model.quaternion.w = orientation.w;
+  model.quaternion.x = orientation.x;
+  model.quaternion.y = orientation.y;
+  model.quaternion.z = orientation.z;
+};
+
+GZ3D.Scene.prototype.removeAll = function()
+{
+  while(this.scene.children.length > 0)
+  {
+    this.scene.remove(this.scene.children[0]);
+  }
+};
+
+/**
+ * Create plane
+ * @param {double} normalX
+ * @param {double} normalY
+ * @param {double} normalZ
+ * @param {double} width
+ * @param {double} height
+ * @returns {THREE.Mesh}
+ */
+GZ3D.Scene.prototype.createPlane = function(normalX, normalY, normalZ,
+    width, height)
+{
+  var geometry = new THREE.PlaneGeometry(width, height, 1, 1);
+  var material =  new THREE.MeshPhongMaterial(
+      {color:0xbbbbbb, shading: THREE.SmoothShading} ); // Later Gazebo/Grey
+  var mesh = new THREE.Mesh(geometry, material);
+  var normal = new THREE.Vector3(normalX, normalY, normalZ);
+  var cross = normal.crossVectors(normal, mesh.up);
+  mesh.rotation = normal.applyAxisAngle(cross, -(normal.angleTo(mesh.up)));
+  mesh.name = 'plane';
+  mesh.receiveShadow = true;
+  return mesh;
+};
+
+/**
+ * Create sphere
+ * @param {double} radius
+ * @returns {THREE.Mesh}
+ */
+GZ3D.Scene.prototype.createSphere = function(radius)
+{
+  var geometry = new THREE.SphereGeometry(radius, 32, 32);
+  var mesh = new THREE.Mesh(geometry, this.spawnedShapeMaterial);
+  return mesh;
+};
+
+/**
+ * Create cylinder
+ * @param {double} radius
+ * @param {double} length
+ * @returns {THREE.Mesh}
+ */
+GZ3D.Scene.prototype.createCylinder = function(radius, length)
+{
+  var geometry = new THREE.CylinderGeometry(radius, radius, length, 32, 1,
+      false);
+  var mesh = new THREE.Mesh(geometry, this.spawnedShapeMaterial);
+  mesh.rotation.x = Math.PI * 0.5;
+  return mesh;
+};
+
+/**
+ * Create box
+ * @param {double} width
+ * @param {double} height
+ * @param {double} depth
+ * @returns {THREE.Mesh}
+ */
+GZ3D.Scene.prototype.createBox = function(width, height, depth)
+{
+  var geometry = new THREE.CubeGeometry(width, height, depth, 1, 1, 1);
+
+  // Fix UVs so textures are mapped in a way that is consistent to gazebo
+  // Some face uvs need to be rotated clockwise, while others anticlockwise
+  // After updating to threejs rev 62, geometries changed from quads (6 faces)
+  // to triangles (12 faces).
+  geometry.dynamic = true;
+  var faceUVFixA = [1, 4, 5];
+  var faceUVFixB = [0];
+  for (var i = 0; i < faceUVFixA.length; ++i)
+  {
+    var idx = faceUVFixA[i]*2;
+    var uva = geometry.faceVertexUvs[0][idx][0];
+    geometry.faceVertexUvs[0][idx][0] = geometry.faceVertexUvs[0][idx][1];
+    geometry.faceVertexUvs[0][idx][1] = geometry.faceVertexUvs[0][idx+1][1];
+    geometry.faceVertexUvs[0][idx][2] = uva;
+
+    geometry.faceVertexUvs[0][idx+1][0] = geometry.faceVertexUvs[0][idx+1][1];
+    geometry.faceVertexUvs[0][idx+1][1] = geometry.faceVertexUvs[0][idx+1][2];
+    geometry.faceVertexUvs[0][idx+1][2] = geometry.faceVertexUvs[0][idx][2];
+  }
+  for (var ii = 0; ii < faceUVFixB.length; ++ii)
+  {
+    var idxB = faceUVFixB[ii]*2;
+    var uvc = geometry.faceVertexUvs[0][idxB][0];
+    geometry.faceVertexUvs[0][idxB][0] = geometry.faceVertexUvs[0][idxB][2];
+    geometry.faceVertexUvs[0][idxB][1] = uvc;
+    geometry.faceVertexUvs[0][idxB][2] =  geometry.faceVertexUvs[0][idxB+1][1];
+
+    geometry.faceVertexUvs[0][idxB+1][2] = geometry.faceVertexUvs[0][idxB][2];
+    geometry.faceVertexUvs[0][idxB+1][1] = geometry.faceVertexUvs[0][idxB+1][0];
+    geometry.faceVertexUvs[0][idxB+1][0] = geometry.faceVertexUvs[0][idxB][1];
+  }
+  geometry.uvsNeedUpdate = true;
+
+  var mesh = new THREE.Mesh(geometry, this.spawnedShapeMaterial);
+  mesh.castShadow = true;
+  return mesh;
+};
+
+/**
+ * Create light
+ * @param {} type - 1: point, 2: spot, 3: directional
+ * @param {} diffuse
+ * @param {} intensity
+ * @param {} pose
+ * @param {} distance
+ * @param {} cast_shadows
+ * @param {} name
+ * @param {} direction
+ * @param {} specular
+ * @param {} attenuation_constant
+ * @param {} attenuation_linear
+ * @param {} attenuation_quadratic
+ * @returns {THREE.Object3D}
+ */
+GZ3D.Scene.prototype.createLight = function(type, diffuse, intensity, pose,
+    distance, cast_shadows, name, direction, specular, attenuation_constant,
+    attenuation_linear, attenuation_quadratic)
+{
+  var obj = new THREE.Object3D();
+  var color = new THREE.Color();
+
+  if (typeof(diffuse) === 'undefined')
+  {
+    diffuse = 0xffffff;
+  }
+  else if (typeof(diffuse) !== THREE.Color)
+  {
+    color.r = diffuse.r;
+    color.g = diffuse.g;
+    color.b = diffuse.b;
+    diffuse = color.clone();
+  }
+  else if (typeof(specular) !== THREE.Color)
+  {
+    color.r = specular.r;
+    color.g = specular.g;
+    color.b = specular.b;
+    specular = color.clone();
+  }
+
+  var matrixWorld;
+
+  if (pose)
+  {
+    var quaternion = new THREE.Quaternion(
+        pose.orientation.x,
+        pose.orientation.y,
+        pose.orientation.z,
+        pose.orientation.w);
+
+    var translation = new THREE.Vector3(
+        pose.position.x,
+        pose.position.y,
+        pose.position.z);
+
+    matrixWorld = new THREE.Matrix4();
+    matrixWorld.compose(translation, quaternion, new THREE.Vector3(1,1,1));
+
+    this.setPose(obj, pose.position, pose.orientation);
+    obj.matrixWorldNeedsUpdate = true;
+  }
+
+  var elements;
+  if (type === 1)
+  {
+    elements = this.createPointLight(obj, diffuse, intensity,
+        distance, cast_shadows);
+  }
+  else if (type === 2)
+  {
+    elements = this.createSpotLight(obj, diffuse, intensity,
+        distance, cast_shadows);
+  }
+  else if (type === 3)
+  {
+    elements = this.createDirectionalLight(obj, diffuse, intensity,
+        cast_shadows);
+  }
+
+  var lightObj = elements[0];
+  var helper = elements[1];
+
+  if (name)
+  {
+    lightObj.name = name;
+    obj.name = name;
+    helper.name = name + '_lightHelper';
+  }
+
+  if (direction)
+  {
+    var dir = new THREE.Vector3(direction.x, direction.y,
+        direction.z);
+
+    obj.direction = new THREE.Vector3();
+    obj.direction.copy(dir);
+
+    dir.applyMatrix4(matrixWorld); // localToWorld
+    lightObj.target.position.copy(dir);
+  }
+
+  // Add properties which exist on the server but have no meaning on THREE.js
+  obj.serverProperties = {};
+  obj.serverProperties.specular = specular;
+  obj.serverProperties.attenuation_constant = attenuation_constant;
+  obj.serverProperties.attenuation_linear = attenuation_linear;
+  obj.serverProperties.attenuation_quadratic = attenuation_quadratic;
+
+  obj.add(lightObj);
+  obj.add(helper);
+  return obj;
+};
+
+/**
+ * Create point light - called by createLight
+ * @param {} obj - light object
+ * @param {} color
+ * @param {} intensity
+ * @param {} distance
+ * @param {} cast_shadows
+ * @returns {Object.<THREE.Light, THREE.Mesh>}
+ */
+GZ3D.Scene.prototype.createPointLight = function(obj, color, intensity,
+    distance, cast_shadows)
+{
+  if (typeof(intensity) === 'undefined')
+  {
+    intensity = 0.5;
+  }
+
+  var lightObj = new THREE.PointLight(color, intensity);
+  lightObj.shadowDarkness = 0.3;
+
+  if (distance)
+  {
+    lightObj.distance = distance;
+  }
+  if (cast_shadows)
+  {
+    lightObj.castShadow = cast_shadows;
+  }
+
+  var helperGeometry = new THREE.OctahedronGeometry(0.25, 0);
+  helperGeometry.applyMatrix(new THREE.Matrix4().makeRotationX(Math.PI/2));
+  var helperMaterial = new THREE.MeshBasicMaterial(
+        {wireframe: true, color: 0x00ff00});
+  var helper = new THREE.Mesh(helperGeometry, helperMaterial);
+
+  return [lightObj, helper];
+};
+
+/**
+ * Create spot light - called by createLight
+ * @param {} obj - light object
+ * @param {} color
+ * @param {} intensity
+ * @param {} distance
+ * @param {} cast_shadows
+ * @returns {Object.<THREE.Light, THREE.Mesh>}
+ */
+GZ3D.Scene.prototype.createSpotLight = function(obj, color, intensity,
+    distance, cast_shadows)
+{
+  if (typeof(intensity) === 'undefined')
+  {
+    intensity = 1;
+  }
+  if (typeof(distance) === 'undefined')
+  {
+    distance = 20;
+  }
+
+  var lightObj = new THREE.SpotLight(color, intensity);
+  lightObj.distance = distance;
+  lightObj.position.set(0,0,0);
+  lightObj.shadowDarkness = 0.3;
+
+  if (cast_shadows)
+  {
+    lightObj.castShadow = cast_shadows;
+  }
+
+  var helperGeometry = new THREE.CylinderGeometry(0, 0.3, 0.2, 4, 1, true);
+  helperGeometry.applyMatrix(new THREE.Matrix4().makeRotationX(Math.PI/2));
+  helperGeometry.applyMatrix(new THREE.Matrix4().makeRotationZ(Math.PI/4));
+  var helperMaterial = new THREE.MeshBasicMaterial(
+        {wireframe: true, color: 0x00ff00});
+  var helper = new THREE.Mesh(helperGeometry, helperMaterial);
+
+  return [lightObj, helper];
+
+};
+
+/**
+ * Create directional light - called by createLight
+ * @param {} obj - light object
+ * @param {} color
+ * @param {} intensity
+ * @param {} cast_shadows
+ * @returns {Object.<THREE.Light, THREE.Mesh>}
+ */
+GZ3D.Scene.prototype.createDirectionalLight = function(obj, color, intensity,
+    cast_shadows)
+{
+  if (typeof(intensity) === 'undefined')
+  {
+    intensity = 1;
+  }
+
+  var lightObj = new THREE.DirectionalLight(color, intensity);
+  lightObj.shadowCameraNear = 1;
+  lightObj.shadowCameraFar = 50;
+  lightObj.shadowMapWidth = 4094;
+  lightObj.shadowMapHeight = 4094;
+  lightObj.shadowCameraVisible = false;
+  lightObj.shadowCameraBottom = -100;
+  lightObj.shadowCameraLeft = -100;
+  lightObj.shadowCameraRight = 100;
+  lightObj.shadowCameraTop = 100;
+  lightObj.shadowBias = 0.0001;
+  lightObj.position.set(0,0,0);
+  lightObj.shadowDarkness = 0.3;
+
+  if (cast_shadows)
+  {
+    lightObj.castShadow = cast_shadows;
+  }
+
+  var helperGeometry = new THREE.Geometry();
+  helperGeometry.vertices.push(new THREE.Vector3(-0.5, -0.5, 0));
+  helperGeometry.vertices.push(new THREE.Vector3(-0.5,  0.5, 0));
+  helperGeometry.vertices.push(new THREE.Vector3(-0.5,  0.5, 0));
+  helperGeometry.vertices.push(new THREE.Vector3( 0.5,  0.5, 0));
+  helperGeometry.vertices.push(new THREE.Vector3( 0.5,  0.5, 0));
+  helperGeometry.vertices.push(new THREE.Vector3( 0.5, -0.5, 0));
+  helperGeometry.vertices.push(new THREE.Vector3( 0.5, -0.5, 0));
+  helperGeometry.vertices.push(new THREE.Vector3(-0.5, -0.5, 0));
+  helperGeometry.vertices.push(new THREE.Vector3(   0,    0, 0));
+  helperGeometry.vertices.push(new THREE.Vector3(   0,    0, -0.5));
+  var helperMaterial = new THREE.LineBasicMaterial({color: 0x00ff00});
+  var helper = new THREE.Line(helperGeometry, helperMaterial,
+      THREE.LinePieces);
+
+  return [lightObj, helper];
+};
+
+/**
+ * Create roads
+ * @param {} points
+ * @param {} width
+ * @param {} texture
+ * @returns {THREE.Mesh}
+ */
+GZ3D.Scene.prototype.createRoads = function(points, width, texture)
+{
+  var geometry = new THREE.Geometry();
+  geometry.dynamic = true;
+  var texCoord = 0.0;
+  var texMaxLen = width;
+  var factor = 1.0;
+  var curLen = 0.0;
+  var tangent = new THREE.Vector3(0,0,0);
+  var pA;
+  var pB;
+  var prevPt = new THREE.Vector3(0,0,0);
+  var prevTexCoord;
+  var texCoords = [];
+  var j = 0;
+  for (var i = 0; i < points.length; ++i)
+  {
+    var pt0 =  new THREE.Vector3(points[i].x, points[i].y,
+        points[i].z);
+    var pt1;
+    if (i !== points.length - 1)
+    {
+      pt1 =  new THREE.Vector3(points[i+1].x, points[i+1].y,
+          points[i+1].z);
+    }
+    factor = 1.0;
+    if (i > 0)
+    {
+      curLen += pt0.distanceTo(prevPt);
+    }
+    texCoord = curLen/texMaxLen;
+    if (i === 0)
+    {
+      tangent.x = pt1.x;
+      tangent.y = pt1.y;
+      tangent.z = pt1.z;
+      tangent.sub(pt0);
+      tangent.normalize();
+    }
+    else if (i === points.length - 1)
+    {
+      tangent.x = pt0.x;
+      tangent.y = pt0.y;
+      tangent.z = pt0.z;
+      tangent.sub(prevPt);
+      tangent.normalize();
+    }
+    else
+    {
+      var v0 = new THREE.Vector3(0,0,0);
+      var v1 = new THREE.Vector3(0,0,0);
+      v0.x = pt0.x;
+      v0.y = pt0.y;
+      v0.z = pt0.z;
+      v0.sub(prevPt);
+      v0.normalize();
+
+      v1.x = pt1.x;
+      v1.y = pt1.y;
+      v1.z = pt1.z;
+      v1.sub(pt0);
+      v1.normalize();
+
+      var dot = v0.dot(v1*-1);
+
+      tangent.x = pt1.x;
+      tangent.y = pt1.y;
+      tangent.z = pt1.z;
+      tangent.sub(prevPt);
+      tangent.normalize();
+
+      if (dot > -0.97 && dot < 0.97)
+      {
+        factor = 1.0 / Math.sin(Math.acos(dot) * 0.5);
+      }
+    }
+    var theta = Math.atan2(tangent.x, -tangent.y);
+    pA = new THREE.Vector3(pt0.x,pt0.y,pt0.z);
+    pB = new THREE.Vector3(pt0.x,pt0.y,pt0.z);
+    var w = (width * factor)*0.5;
+    pA.x += Math.cos(theta) * w;
+    pA.y += Math.sin(theta) * w;
+    pB.x -= Math.cos(theta) * w;
+    pB.y -= Math.sin(theta) * w;
+
+    geometry.vertices.push(pA);
+    geometry.vertices.push(pB);
+
+    texCoords.push([0, texCoord]);
+    texCoords.push([1, texCoord]);
+
+    // draw triangle strips
+    if (i > 0)
+    {
+      geometry.faces.push(new THREE.Face3(j, j+1, j+2,
+        new THREE.Vector3(0, 0, 1)));
+      geometry.faceVertexUvs[0].push(
+          [new THREE.Vector2(texCoords[j][0], texCoords[j][1]),
+           new THREE.Vector2(texCoords[j+1][0], texCoords[j+1][1]),
+           new THREE.Vector2(texCoords[j+2][0], texCoords[j+2][1])]);
+      j++;
+
+      geometry.faces.push(new THREE.Face3(j, j+2, j+1,
+        new THREE.Vector3(0, 0, 1)));
+      geometry.faceVertexUvs[0].push(
+          [new THREE.Vector2(texCoords[j][0], texCoords[j][1]),
+           new THREE.Vector2(texCoords[j+2][0], texCoords[j+2][1]),
+           new THREE.Vector2(texCoords[j+1][0], texCoords[j+1][1])]);
+      j++;
+
+    }
+
+    prevPt.x = pt0.x;
+    prevPt.y = pt0.y;
+    prevPt.z = pt0.z;
+
+    prevTexCoord = texCoord;
+  }
+
+  // geometry.computeTangents();
+  geometry.computeFaceNormals();
+
+  geometry.verticesNeedUpdate = true;
+  geometry.uvsNeedUpdate = true;
+
+
+  var material =  new THREE.MeshPhongMaterial();
+
+ /* var ambient = mat['ambient'];
+  if (ambient)
+  {
+    material.ambient.setRGB(ambient[0], ambient[1], ambient[2]);
+  }
+  var diffuse = mat['diffuse'];
+  if (diffuse)
+  {
+    material.color.setRGB(diffuse[0], diffuse[1], diffuse[2]);
+  }
+  var specular = mat['specular'];
+  if (specular)
+  {
+    material.specular.setRGB(specular[0], specular[1], specular[2]);
+  }*/
+  if (texture)
+  {
+    var tex = THREE.ImageUtils.loadTexture(texture);
+    tex.wrapS = tex.wrapT = THREE.RepeatWrapping;
+    material.map = tex;
+  }
+
+  var mesh = new THREE.Mesh(geometry, material);
+  mesh.castShadow = false;
+  return mesh;
+};
+
+/**
+ * Load heightmap
+ * @param {} heights
+ * @param {} width
+ * @param {} height
+ * @param {} segmentWidth
+ * @param {} segmentHeight
+ * @param {} textures
+ * @param {} blends
+ * @param {} parent
+ */
+GZ3D.Scene.prototype.loadHeightmap = function(heights, width, height,
+    segmentWidth, segmentHeight, origin, textures, blends, parent)
+{
+  if (this.heightmap)
+  {
+    return;
+  }
+  // unfortunately large heightmaps kills the fps and freeze everything so
+  // we have to scale it down
+  var scale = 1;
+  var maxHeightmapWidth = 256;
+  var maxHeightmapHeight = 256;
+
+  if ((segmentWidth-1) > maxHeightmapWidth)
+  {
+    scale = maxHeightmapWidth / (segmentWidth-1);
+  }
+
+  var geometry = new THREE.PlaneGeometry(width, height,
+      (segmentWidth-1) * scale, (segmentHeight-1) * scale);
+  geometry.dynamic = true;
+
+  // flip the heights
+  var vertices = [];
+  for (var h = segmentHeight-1; h >= 0; --h)
+  {
+    for (var w = 0; w < segmentWidth; ++w)
+    {
+      vertices[(segmentHeight-h-1)*segmentWidth  + w]
+          = heights[h*segmentWidth + w];
+    }
+  }
+
+  // sub-sample
+  var col = (segmentWidth-1) * scale;
+  var row = (segmentHeight-1) * scale;
+  for (var r = 0; r < row; ++r)
+  {
+    for (var c = 0; c < col; ++c)
+    {
+      var index = (r * col * 1/(scale*scale)) +   (c * (1/scale));
+      geometry.vertices[r*col + c].z = vertices[index];
+    }
+  }
+
+  var mesh;
+  if (textures && textures.length > 0)
+  {
+    geometry.computeFaceNormals();
+    geometry.computeVertexNormals();
+    geometry.computeTangents();
+
+    var textureLoaded = [];
+    var repeats = [];
+    for (var t = 0; t < textures.length; ++t)
+    {
+      textureLoaded[t] = THREE.ImageUtils.loadTexture(textures[t].diffuse,
+          new THREE.UVMapping());
+      textureLoaded[t].wrapS = THREE.RepeatWrapping;
+      textureLoaded[t].wrapT = THREE.RepeatWrapping;
+      repeats[t] = width/textures[t].size;
+    }
+
+    // for now, use fixed no. of textures and blends
+    // so populate the remaining ones to make the fragment shader happy
+    for (var tt = textures.length; tt< 3; ++tt)
+    {
+      textureLoaded[tt] = textureLoaded[tt-1];
+    }
+
+    for (var b = blends.length; b < 2; ++b)
+    {
+      blends[b] = blends[b-1];
+    }
+
+    for (var rr = repeats.length; rr < 3; ++rr)
+    {
+      repeats[rr] = repeats[rr-1];
+    }
+
+    // Use the same approach as gazebo scene, grab the first directional light
+    // and use it for shading the terrain
+    var lightDir = new THREE.Vector3(0, 0, 1);
+    var lightDiffuse = new THREE.Color(0xffffff);
+    var allObjects = [];
+    this.scene.getDescendants(allObjects);
+    for (var l = 0; l < allObjects.length; ++l)
+    {
+      if (allObjects[l] instanceof THREE.DirectionalLight)
+      {
+        lightDir = allObjects[l].target.position;
+        lightDiffuse = allObjects[l].color;
+        break;
+      }
+    }
+
+    var material = new THREE.ShaderMaterial({
+      uniforms:
+      {
+        texture0: { type: 't', value: textureLoaded[0]},
+        texture1: { type: 't', value: textureLoaded[1]},
+        texture2: { type: 't', value: textureLoaded[2]},
+        repeat0: { type: 'f', value: repeats[0]},
+        repeat1: { type: 'f', value: repeats[1]},
+        repeat2: { type: 'f', value: repeats[2]},
+        minHeight1: { type: 'f', value: blends[0].min_height},
+        fadeDist1: { type: 'f', value: blends[0].fade_dist},
+        minHeight2: { type: 'f', value: blends[1].min_height},
+        fadeDist2: { type: 'f', value: blends[1].fade_dist},
+        ambient: { type: 'c', value: this.ambient.color},
+        lightDiffuse: { type: 'c', value: lightDiffuse},
+        lightDir: { type: 'v3', value: lightDir}
+      },
+      attributes: {},
+      vertexShader: document.getElementById( 'heightmapVS' ).innerHTML,
+      fragmentShader: document.getElementById( 'heightmapFS' ).innerHTML
+    });
+
+    mesh = new THREE.Mesh( geometry, material);
+  }
+  else
+  {
+    mesh = new THREE.Mesh( geometry,
+        new THREE.MeshPhongMaterial( { color: 0x555555 } ) );
+  }
+
+  mesh.position.x = origin.x;
+  mesh.position.y = origin.y;
+  mesh.position.z = origin.z;
+  parent.add(mesh);
+
+  this.heightmap = parent;
+};
+
+/**
+ * Load mesh
+ * @param {string} uri
+ * @param {} submesh
+ * @param {} centerSubmesh
+ * @param {function} callback
+ */
+GZ3D.Scene.prototype.loadMesh = function(uri, submesh, centerSubmesh,
+    callback)
+{
+  var uriPath = uri.substring(0, uri.lastIndexOf('/'));
+  var uriFile = uri.substring(uri.lastIndexOf('/') + 1);
+
+  // load urdf model
+  if (uriFile.substr(-4).toLowerCase() === '.dae')
+  {
+    return this.loadCollada(uri, submesh, centerSubmesh, callback);
+  }
+  else if (uriFile.substr(-5).toLowerCase() === '.urdf')
+  {
+    /*var urdfModel = new ROSLIB.UrdfModel({
+      string : uri
+    });
+
+    // adapted from ros3djs
+    var links = urdfModel.links;
+    for ( var l in links) {
+      var link = links[l];
+      if (link.visual && link.visual.geometry) {
+        if (link.visual.geometry.type === ROSLIB.URDF_MESH) {
+          var frameID = '/' + link.name;
+          var filename = link.visual.geometry.filename;
+          var meshType = filename.substr(-4).toLowerCase();
+          var mesh = filename.substring(filename.indexOf('://') + 3);
+          // ignore mesh files which are not in Collada format
+          if (meshType === '.dae')
+          {
+            var dae = this.loadCollada(uriPath + '/' + mesh, parent);
+            // check for a scale
+            if(link.visual.geometry.scale)
+            {
+              dae.scale = new THREE.Vector3(
+                  link.visual.geometry.scale.x,
+                  link.visual.geometry.scale.y,
+                  link.visual.geometry.scale.z
+              );
+            }
+          }
+        }
+      }
+    }*/
+  }
+};
+
+/**
+ * Load collada file
+ * @param {string} uri
+ * @param {} submesh
+ * @param {} centerSubmesh
+ * @param {function} callback
+ */
+GZ3D.Scene.prototype.loadCollada = function(uri, submesh, centerSubmesh,
+    callback)
+{
+  var dae;
+  var mesh = null;
+  /*
+  // Crashes: issue #36
+  if (this.meshes[uri])
+  {
+    dae = this.meshes[uri];
+    dae = dae.clone();
+    this.useColladaSubMesh(dae, submesh, centerSubmesh);
+    callback(dae);
+    return;
+  }
+  */
+
+  var loader = new THREE.ColladaLoader();
+  // var loader = new ColladaLoader2();
+  // loader.options.convertUpAxis = true;
+  var thatURI = uri;
+  var thatSubmesh = submesh;
+  var thatCenterSubmesh = centerSubmesh;
+
+  loader.load(uri, function(collada)
+  {
+    // check for a scale factor
+    /*if(collada.dae.asset.unit)
+    {
+      var scale = collada.dae.asset.unit;
+      collada.scene.scale = new THREE.Vector3(scale, scale, scale);
+    }*/
+
+    dae = collada.scene;
+    dae.updateMatrix();
+    this.scene.prepareColladaMesh(dae);
+    this.scene.meshes[thatURI] = dae;
+    dae = dae.clone();
+    this.scene.useColladaSubMesh(dae, thatSubmesh, centerSubmesh);
+
+    dae.name = uri;
+    callback(dae);
+  });
+};
+
+/**
+ * Prepare collada by removing other non-mesh entities such as lights
+ * @param {} dae
+ */
+GZ3D.Scene.prototype.prepareColladaMesh = function(dae)
+{
+  var allChildren = [];
+  dae.getDescendants(allChildren);
+  for (var i = 0; i < allChildren.length; ++i)
+  {
+    if (allChildren[i] instanceof THREE.Light)
+    {
+      allChildren[i].parent.remove(allChildren[i]);
+    }
+  }
+};
+
+/**
+ * Prepare collada by handling submesh-only loading
+ * @param {} dae
+ * @param {} submesh
+ * @param {} centerSubmesh
+ * @returns {THREE.Mesh} mesh
+ */
+GZ3D.Scene.prototype.useColladaSubMesh = function(dae, submesh, centerSubmesh)
+{
+  if (!submesh)
+  {
+    return null;
+  }
+
+  var mesh;
+  var allChildren = [];
+  dae.getDescendants(allChildren);
+  for (var i = 0; i < allChildren.length; ++i)
+  {
+    if (allChildren[i] instanceof THREE.Mesh)
+    {
+      if (!submesh && !mesh)
+      {
+        mesh = allChildren[i];
+      }
+
+      if (submesh)
+      {
+
+        if (allChildren[i].geometry.name === submesh)
+        {
+          if (centerSubmesh)
+          {
+            var vertices = allChildren[i].geometry.vertices;
+            var vMin = new THREE.Vector3();
+            var vMax = new THREE.Vector3();
+            vMin.x = vertices[0].x;
+            vMin.y = vertices[0].y;
+            vMin.z = vertices[0].z;
+            vMax.x = vMin.x;
+            vMax.y = vMin.y;
+            vMax.z = vMin.z;
+
+            for (var j = 1; j < vertices.length; ++j)
+            {
+              vMin.x = Math.min(vMin.x, vertices[j].x);
+              vMin.y = Math.min(vMin.y, vertices[j].y);
+              vMin.z = Math.min(vMin.z, vertices[j].z);
+              vMax.x = Math.max(vMax.x, vertices[j].x);
+              vMax.y = Math.max(vMax.y, vertices[j].y);
+              vMax.z = Math.max(vMax.z, vertices[j].z);
+            }
+
+            var center  = new THREE.Vector3();
+            center.x = vMin.x + (0.5 * (vMax.x - vMin.x));
+            center.y = vMin.y + (0.5 * (vMax.y - vMin.y));
+            center.z = vMin.z + (0.5 * (vMax.z - vMin.z));
+
+            for (var k = 0; k < vertices.length; ++k)
+            {
+              vertices[k].x -= center.x;
+              vertices[k].y -= center.y;
+              vertices[k].z -= center.z;
+            }
+            allChildren[i].geometry.verticesNeedUpdate = true;
+
+            allChildren[i].position.x = 0;
+            allChildren[i].position.y = 0;
+            allChildren[i].position.z = 0;
+
+            allChildren[i].parent.position.x = 0;
+            allChildren[i].parent.position.y = 0;
+            allChildren[i].parent.position.z = 0;
+          }
+          mesh = allChildren[i];
+        }
+        else
+        {
+          allChildren[i].parent.remove(allChildren[i]);
+        }
+      }
+    }
+  }
+  return mesh;
+};
+
+/*GZ3D.Scene.prototype.setMaterial = function(mesh, texture, normalMap)
+{
+  if (!mesh)
+  {
+    return;
+  }
+
+  if (texture || normalMap)
+  {
+    // normal map shader
+    var shader = THREE.ShaderLib['normalmap'];
+    var uniforms = THREE.UniformsUtils.clone( shader.uniforms );
+    if (texture)
+    {
+      uniforms['enableDiffuse'].value = true;
+      uniforms['tDiffuse'].value = THREE.ImageUtils.loadTexture(texture);
+    }
+    if (normalMap)
+    {
+      uniforms['tNormal'].value = THREE.ImageUtils.loadTexture(normalMap);
+    }
+
+    var parameters = { fragmentShader: shader.fragmentShader,
+        vertexShader: shader.vertexShader, uniforms: uniforms,
+        lights: true, fog: false };
+    var shaderMaterial = new THREE.ShaderMaterial(parameters);
+    mesh.geometry.computeTangents();
+    mesh.material = shaderMaterial;
+  }
+};*/
+
+/**
+ * Set material for an object
+ * @param {} obj
+ * @param {} material
+ */
+GZ3D.Scene.prototype.setMaterial = function(obj, material)
+{
+  if (obj)
+  {
+    if (material)
+    {
+      obj.material = new THREE.MeshPhongMaterial();
+      var ambient = material.ambient;
+      if (ambient)
+      {
+        obj.material.ambient.setRGB(ambient[0], ambient[1], ambient[2]);
+      }
+      var diffuse = material.diffuse;
+      if (diffuse)
+      {
+        obj.material.color.setRGB(diffuse[0], diffuse[1], diffuse[2]);
+      }
+      var specular = material.specular;
+      if (specular)
+      {
+        obj.material.specular.setRGB(specular[0], specular[1], specular[2]);
+      }
+      var opacity = material.opacity;
+      if (opacity)
+      {
+        if (opacity < 1)
+        {
+          obj.material.transparent = true;
+          obj.material.opacity = opacity;
+        }
+      }
+
+      if (material.texture)
+      {
+        var texture = THREE.ImageUtils.loadTexture(material.texture);
+        if (material.scale)
+        {
+          texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
+          texture.repeat.x = 1.0 / material.scale[0];
+          texture.repeat.y = 1.0 / material.scale[1];
+        }
+        obj.material.map = texture;
+      }
+      if (material.normalMap)
+      {
+        obj.material.normalMap =
+            THREE.ImageUtils.loadTexture(material.normalMap);
+      }
+    }
+  }
+};
+
+/**
+ * Set manipulation mode (view/translate/rotate)
+ * @param {string} mode
+ */
+GZ3D.Scene.prototype.setManipulationMode = function(mode)
+{
+  this.manipulationMode = mode;
+
+  if (mode === 'view')
+  {
+    if (this.modelManipulator.object)
+    {
+      this.emitter.emit('entityChanged', this.modelManipulator.object);
+    }
+    this.selectEntity(null);
+  }
+  else
+  {
+    // Toggle manipulaion space (world / local)
+    if (this.modelManipulator.mode === this.manipulationMode)
+    {
+      this.modelManipulator.space =
+        (this.modelManipulator.space === 'world') ? 'local' : 'world';
+    }
+    this.modelManipulator.mode = this.manipulationMode;
+    this.modelManipulator.setMode(this.modelManipulator.mode);
+    // model was selected during view mode
+    if (this.selectedEntity)
+    {
+      this.selectEntity(this.selectedEntity);
+    }
+  }
+
+};
+
+/**
+ * Show collision visuals
+ * @param {boolean} show
+ */
+GZ3D.Scene.prototype.showCollision = function(show)
+{
+  if (show === this.showCollisions)
+  {
+    return;
+  }
+
+  var allObjects = [];
+  this.scene.getDescendants(allObjects);
+  for (var i = 0; i < allObjects.length; ++i)
+  {
+    if (allObjects[i] instanceof THREE.Object3D &&
+        allObjects[i].name.indexOf('COLLISION_VISUAL') >=0)
+    {
+      var allChildren = [];
+      allObjects[i].getDescendants(allChildren);
+      for (var j =0; j < allChildren.length; ++j)
+      {
+        if (allChildren[j] instanceof THREE.Mesh)
+        {
+          allChildren[j].visible = show;
+        }
+      }
+    }
+  }
+  this.showCollisions = show;
+
+};
+
+/**
+ * Attach manipulator to an object
+ * @param {THREE.Object3D} model
+ * @param {string} mode (translate/rotate)
+ */
+GZ3D.Scene.prototype.attachManipulator = function(model,mode)
+{
+  if (this.modelManipulator.object)
+  {
+    this.emitter.emit('entityChanged', this.modelManipulator.object);
+  }
+
+  if (mode !== 'view')
+  {
+    this.modelManipulator.attach(model);
+    this.modelManipulator.mode = mode;
+    this.modelManipulator.setMode( this.modelManipulator.mode );
+    this.scene.add(this.modelManipulator.gizmo);
+  }
+};
+
+/**
+ * Reset view
+ */
+GZ3D.Scene.prototype.resetView = function()
+{
+  this.camera.position.copy(this.defaultCameraPosition);
+  this.camera.up = new THREE.Vector3(0, 0, 1);
+  this.camera.lookAt(new THREE.Vector3( 0, 0, 0 ));
+  this.camera.updateMatrix();
+};
+
+/**
+ * Show radial menu
+ * @param {} event
+ */
+GZ3D.Scene.prototype.showRadialMenu = function(e)
+{
+  var event = e.originalEvent;
+
+  var pointer = event.touches ? event.touches[ 0 ] : event;
+  var pos = new THREE.Vector2(pointer.clientX, pointer.clientY);
+
+  var intersect = new THREE.Vector3();
+  var model = this.getRayCastModel(pos, intersect);
+
+  if (model && model.name !== '' && model.name !== 'plane'
+      && this.modelManipulator.pickerNames.indexOf(model.name) === -1)
+  {
+    this.radialMenu.show(event,model);
+    this.selectEntity(model);
+  }
+};
+
+/**
+ * Show bounding box for a model. The box is aligned with the world.
+ * @param {THREE.Object3D} model
+ */
+GZ3D.Scene.prototype.showBoundingBox = function(model)
+{
+  if (typeof model === 'string')
+  {
+    model = this.scene.getObjectByName(model);
+  }
+
+  if (this.boundingBox.visible)
+  {
+    if (this.boundingBox.parent === model)
+    {
+      return;
+    }
+    else
+    {
+      this.hideBoundingBox();
+    }
+  }
+  var box = new THREE.Box3();
+  // w.r.t. world
+  box.setFromObject(model);
+  // center vertices with object
+  box.min.x = box.min.x - model.position.x;
+  box.min.y = box.min.y - model.position.y;
+  box.min.z = box.min.z - model.position.z;
+  box.max.x = box.max.x - model.position.x;
+  box.max.y = box.max.y - model.position.y;
+  box.max.z = box.max.z - model.position.z;
+
+  var vertex = new THREE.Vector3(box.max.x, box.max.y, box.max.z); // 0
+  this.boundingBox.geometry.vertices[0].copy(vertex);
+  this.boundingBox.geometry.vertices[7].copy(vertex);
+  this.boundingBox.geometry.vertices[16].copy(vertex);
+
+  vertex.set(box.min.x, box.max.y, box.max.z); // 1
+  this.boundingBox.geometry.vertices[1].copy(vertex);
+  this.boundingBox.geometry.vertices[2].copy(vertex);
+  this.boundingBox.geometry.vertices[18].copy(vertex);
+
+  vertex.set(box.min.x, box.min.y, box.max.z); // 2
+  this.boundingBox.geometry.vertices[3].copy(vertex);
+  this.boundingBox.geometry.vertices[4].copy(vertex);
+  this.boundingBox.geometry.vertices[20].copy(vertex);
+
+  vertex.set(box.max.x, box.min.y, box.max.z); // 3
+  this.boundingBox.geometry.vertices[5].copy(vertex);
+  this.boundingBox.geometry.vertices[6].copy(vertex);
+  this.boundingBox.geometry.vertices[22].copy(vertex);
+
+  vertex.set(box.max.x, box.max.y, box.min.z); // 4
+  this.boundingBox.geometry.vertices[8].copy(vertex);
+  this.boundingBox.geometry.vertices[15].copy(vertex);
+  this.boundingBox.geometry.vertices[17].copy(vertex);
+
+  vertex.set(box.min.x, box.max.y, box.min.z); // 5
+  this.boundingBox.geometry.vertices[9].copy(vertex);
+  this.boundingBox.geometry.vertices[10].copy(vertex);
+  this.boundingBox.geometry.vertices[19].copy(vertex);
+
+  vertex.set(box.min.x, box.min.y, box.min.z); // 6
+  this.boundingBox.geometry.vertices[11].copy(vertex);
+  this.boundingBox.geometry.vertices[12].copy(vertex);
+  this.boundingBox.geometry.vertices[21].copy(vertex);
+
+  vertex.set(box.max.x, box.min.y, box.min.z); // 7
+  this.boundingBox.geometry.vertices[13].copy(vertex);
+  this.boundingBox.geometry.vertices[14].copy(vertex);
+  this.boundingBox.geometry.vertices[23].copy(vertex);
+
+  this.boundingBox.geometry.verticesNeedUpdate = true;
+
+  // rotate the box back to the world
+  var modelRotation = new THREE.Matrix4();
+  modelRotation.extractRotation(model.matrixWorld);
+  var modelInverse = new THREE.Matrix4();
+  modelInverse.getInverse(modelRotation);
+  this.boundingBox.quaternion.setFromRotationMatrix(modelInverse);
+  this.boundingBox.name = 'boundingBox';
+  this.boundingBox.visible = true;
+
+  // Add box as model's child
+  model.add(this.boundingBox);
+};
+
+/**
+ * Hide bounding box
+ */
+GZ3D.Scene.prototype.hideBoundingBox = function()
+{
+  if(this.boundingBox.parent)
+  {
+    this.boundingBox.parent.remove(this.boundingBox);
+  }
+  this.boundingBox.visible = false;
+};
+
+/**
+ * Mouse right click
+ * @param {} event
+ * @param {} callback - function to be executed to the clicked model
+ */
+GZ3D.Scene.prototype.onRightClick = function(event, callback)
+{
+  var pos = new THREE.Vector2(event.clientX, event.clientY);
+  var model = this.getRayCastModel(pos, new THREE.Vector3());
+
+  if(model && model.name !== '' && model.name !== 'plane' &&
+      this.modelManipulator.pickerNames.indexOf(model.name) === -1)
+  {
+    callback(model);
+  }
+};
+
+
+/**
+ * Set model's view mode
+ * @param {} model
+ * @param {} viewAs (normal/transparent/wireframe)
+ */
+GZ3D.Scene.prototype.setViewAs = function(model, viewAs)
+{
+  // Toggle
+  if (model.viewAs === viewAs)
+  {
+    viewAs = 'normal';
+  }
+
+  function materialViewAs(material)
+  {
+    if (materials.indexOf(material.id) === -1)
+    {
+      materials.push(material.id);
+      material.transparent = true;
+
+      if (viewAs === 'transparent')
+      {
+        if (material.opacity)
+        {
+          material.originalOpacity = material.opacity;
+        }
+        else
+        {
+          material.originalOpacity = 1.0;
+        }
+        material.opacity = 0.25;
+      }
+      else
+      {
+        material.opacity =
+            material.originalOpacity ?
+            material.originalOpacity : 1.0;
+      }
+
+      if (viewAs === 'wireframe')
+      {
+        material.visible = false;
+      }
+      else
+      {
+        material.visible = true;
+      }
+    }
+  }
+
+  var wireframe;
+  var descendants = [];
+  var materials = [];
+  model.getDescendants(descendants);
+  for (var i = 0; i < descendants.length; ++i)
+  {
+    if (descendants[i].material &&
+        descendants[i].name.indexOf('boundingBox') === -1 &&
+        descendants[i].name.indexOf('COLLISION_VISUAL') === -1 &&
+        !this.getParentByPartialName(descendants[i], 'COLLISION_VISUAL')&&
+        descendants[i].name.indexOf('wireframe') === -1 &&
+        descendants[i].name.indexOf('JOINT_VISUAL') === -1)
+    {
+      if (descendants[i].material instanceof THREE.MeshFaceMaterial)
+      {
+        for (var j = 0; j < descendants[i].material.materials.length; ++j)
+        {
+          materialViewAs(descendants[i].material.materials[j]);
+        }
+      }
+      else
+      {
+        materialViewAs(descendants[i].material);
+      }
+
+      if (viewAs === 'wireframe')
+      {
+        wireframe = descendants[i].getObjectByName('wireframe');
+        if (wireframe)
+        {
+          wireframe.visible = true;
+        }
+        else
+        {
+          var mesh = new THREE.Mesh( descendants[i].geometry,
+              new THREE.MeshBasicMaterial({color: 0xffffff}));
+          wireframe = new THREE.WireframeHelper( mesh );
+          wireframe.name = 'wireframe';
+          descendants[i].add( wireframe );
+        }
+      }
+      else
+      {
+        wireframe = descendants[i].getObjectByName('wireframe');
+        if (wireframe)
+        {
+          wireframe.visible = false;
+        }
+      }
+    }
+  }
+  model.viewAs = viewAs;
+};
+
+/**
+ * Returns the closest parent whose name contains the given string
+ * @param {} object
+ * @param {} name
+ */
+GZ3D.Scene.prototype.getParentByPartialName = function(object, name)
+{
+  var parent = object.parent;
+  while (parent && parent !== this.scene)
+  {
+    if (parent.name.indexOf(name) !== -1)
+    {
+      return parent;
+    }
+
+    parent = parent.parent;
+  }
+  return null;
+};
+
+/**
+ * Select entity
+ * @param {} object
+ */
+GZ3D.Scene.prototype.selectEntity = function(object)
+{
+  if (object)
+  {
+    if (object !== this.selectedEntity)
+    {
+      this.showBoundingBox(object);
+      this.selectedEntity = object;
+    }
+    this.attachManipulator(object, this.manipulationMode);
+    guiEvents.emit('setTreeSelected', object.name);
+  }
+  else
+  {
+    if (this.modelManipulator.object)
+    {
+      this.modelManipulator.detach();
+      this.scene.remove(this.modelManipulator.gizmo);
+    }
+    this.hideBoundingBox();
+    this.selectedEntity = null;
+    guiEvents.emit('setTreeDeselected');
+  }
+};
+
+/**
+ * View joints
+ * Toggle: if there are joints, hide, otherwise, show.
+ * @param {} model
+ */
+GZ3D.Scene.prototype.viewJoints = function(model)
+{
+  if (model.joint === undefined || model.joint.length === 0)
+  {
+    return;
+  }
+
+  var child;
+
+  // Visuals already exist
+  if (model.jointVisuals)
+  {
+    // Hide = remove from parent
+    if (model.jointVisuals[0].parent !== undefined)
+    {
+      for (var v = 0; v < model.jointVisuals.length; ++v)
+      {
+        model.jointVisuals[v].parent.remove(model.jointVisuals[v]);
+      }
+    }
+    // Show: attach to parent
+    else
+    {
+      for (var s = 0; s < model.joint.length; ++s)
+      {
+        child = model.getObjectByName(model.joint[s].child);
+
+        if (!child)
+        {
+          continue;
+        }
+
+        child.add(model.jointVisuals[s]);
+      }
+    }
+  }
+  // Create visuals
+  else
+  {
+    model.jointVisuals = [];
+    for (var j = 0; j < model.joint.length; ++j)
+    {
+      child = model.getObjectByName(model.joint[j].child);
+
+      if (!child)
+      {
+        continue;
+      }
+
+      // XYZ expressed w.r.t. child
+      var jointVisual = this.jointAxis['XYZaxes'].clone();
+      child.add(jointVisual);
+      model.jointVisuals.push(jointVisual);
+      jointVisual.scale.set(0.7, 0.7, 0.7);
+
+      this.setPose(jointVisual, model.joint[j].pose.position,
+          model.joint[j].pose.orientation);
+
+      var mainAxis;
+      if (model.joint[j].type !== this.jointTypes.BALL)
+      {
+        mainAxis = this.jointAxis['mainAxis'].clone();
+        jointVisual.add(mainAxis);
+      }
+
+      var secondAxis;
+      if (model.joint[j].type === this.jointTypes.REVOLUTE2 ||
+          model.joint[j].type === this.jointTypes.UNIVERSAL)
+      {
+        secondAxis = this.jointAxis['mainAxis'].clone();
+        jointVisual.add(secondAxis);
+      }
+
+      if (model.joint[j].type === this.jointTypes.REVOLUTE ||
+          model.joint[j].type === this.jointTypes.GEARBOX)
+      {
+        mainAxis.add(this.jointAxis['rotAxis'].clone());
+      }
+      else if (model.joint[j].type === this.jointTypes.REVOLUTE2 ||
+               model.joint[j].type === this.jointTypes.UNIVERSAL)
+      {
+        mainAxis.add(this.jointAxis['rotAxis'].clone());
+        secondAxis.add(this.jointAxis['rotAxis'].clone());
+      }
+      else if (model.joint[j].type === this.jointTypes.BALL)
+      {
+        jointVisual.add(this.jointAxis['ballVisual'].clone());
+      }
+      else if (model.joint[j].type === this.jointTypes.PRISMATIC)
+      {
+        mainAxis.add(this.jointAxis['transAxis'].clone());
+      }
+      else if (model.joint[j].type === this.jointTypes.SCREW)
+      {
+        mainAxis.add(this.jointAxis['screwAxis'].clone());
+      }
+
+      var direction, tempMatrix, rotMatrix;
+      if (mainAxis)
+      {
+        // main axis expressed w.r.t. parent model or joint frame
+        // needs Gazebo issue #1268 fixed, receive use_parent_model_frame on msg
+        // for now, true by default because most old models have it true
+        if (model.joint[j].axis1.use_parent_model_frame === undefined)
+        {
+          model.joint[j].axis1.use_parent_model_frame = true;
+        }
+
+        direction = new THREE.Vector3(
+            model.joint[j].axis1.xyz.x,
+            model.joint[j].axis1.xyz.y,
+            model.joint[j].axis1.xyz.z);
+        direction.normalize();
+
+        tempMatrix = new THREE.Matrix4();
+        if (model.joint[j].axis1.use_parent_model_frame)
+        {
+          tempMatrix.extractRotation(jointVisual.matrix);
+          tempMatrix.getInverse(tempMatrix);
+          direction.applyMatrix4(tempMatrix);
+          tempMatrix.extractRotation(child.matrix);
+          tempMatrix.getInverse(tempMatrix);
+          direction.applyMatrix4(tempMatrix);
+        }
+
+        mainAxis.position =  direction.multiplyScalar(0.3);
+        rotMatrix = new THREE.Matrix4();
+        rotMatrix.lookAt(direction, new THREE.Vector3(0, 0, 0), mainAxis.up);
+        mainAxis.quaternion.setFromRotationMatrix(rotMatrix);
+      }
+
+      if (secondAxis)
+      {
+        if (model.joint[j].axis2.use_parent_model_frame === undefined)
+        {
+          model.joint[j].axis2.use_parent_model_frame = true;
+        }
+
+        direction = new THREE.Vector3(
+            model.joint[j].axis2.xyz.x,
+            model.joint[j].axis2.xyz.y,
+            model.joint[j].axis2.xyz.z);
+        direction.normalize();
+
+        tempMatrix = new THREE.Matrix4();
+        if (model.joint[j].axis2.use_parent_model_frame)
+        {
+          tempMatrix.extractRotation(jointVisual.matrix);
+          tempMatrix.getInverse(tempMatrix);
+          direction.applyMatrix4(tempMatrix);
+          tempMatrix.extractRotation(child.matrix);
+          tempMatrix.getInverse(tempMatrix);
+          direction.applyMatrix4(tempMatrix);
+        }
+
+        secondAxis.position =  direction.multiplyScalar(0.3);
+        rotMatrix = new THREE.Matrix4();
+        rotMatrix.lookAt(direction, new THREE.Vector3(0, 0, 0), secondAxis.up);
+        secondAxis.quaternion.setFromRotationMatrix(rotMatrix);
+      }
+    }
+  }
+};
+
+/**
+ * Update a light entity from a message
+ * @param {} entity
+ * @param {} msg
+ */
+GZ3D.Scene.prototype.updateLight = function(entity, msg)
+{
+  // TODO: Generalize this and createLight
+  var lightObj = entity.children[0];
+  var dir;
+
+  var color = new THREE.Color();
+
+  if (msg.diffuse)
+  {
+    color.r = msg.diffuse.r;
+    color.g = msg.diffuse.g;
+    color.b = msg.diffuse.b;
+    lightObj.color = color.clone();
+  }
+  if (msg.specular)
+  {
+    color.r = msg.specular.r;
+    color.g = msg.specular.g;
+    color.b = msg.specular.b;
+    entity.serverProperties.specular = color.clone();
+  }
+
+  var matrixWorld;
+  if (msg.pose)
+  {
+    // needed to update light's direction
+    var quaternion = new THREE.Quaternion(
+        msg.pose.orientation.x,
+        msg.pose.orientation.y,
+        msg.pose.orientation.z,
+        msg.pose.orientation.w);
+
+    var translation = new THREE.Vector3(
+        msg.pose.position.x,
+        msg.pose.position.y,
+        msg.pose.position.z);
+
+    matrixWorld = new THREE.Matrix4();
+    matrixWorld.compose(translation, quaternion, new THREE.Vector3(1,1,1));
+
+    this.setPose(entity, msg.pose.position, msg.pose.orientation);
+    entity.matrixWorldNeedsUpdate = true;
+
+    if (entity.direction)
+    {
+      dir = new THREE.Vector3(entity.direction.x, entity.direction.y,
+          entity.direction.z);
+
+      entity.direction = new THREE.Vector3();
+      entity.direction.copy(dir);
+
+      dir.applyMatrix4(matrixWorld); // localToWorld
+      lightObj.target.position.copy(dir);
+    }
+  }
+
+  if (msg.range)
+  {
+    // THREE.js's light distance impacts the attenuation factor defined in the shader:
+    // attenuation factor = 1.0 - distance-to-enlighted-point / light.distance
+    // Gazebo's range (taken from OGRE 3D API) does not contribute to attenuation;
+    // it is a hard limit for light scope.
+    // Nevertheless, we identify them for sake of simplicity.
+    lightObj.distance = msg.range;
+  }
+
+  if (msg.cast_shadows)
+  {
+    lightObj.castShadow = msg.cast_shadows;
+  }
+
+  if (msg.attenuation_constant)
+  {
+    entity.serverProperties.attenuation_constant = msg.attenuation_constant;
+  }
+  if (msg.attenuation_linear)
+  {
+    entity.serverProperties.attenuation_linear = msg.attenuation_linear;
+    lightObj.intensity = lightObj.intensity/(1+msg.attenuation_linear);
+  }
+  if (msg.attenuation_quadratic)
+  {
+    entity.serverProperties.attenuation_quadratic = msg.attenuation_quadratic;
+    lightObj.intensity = lightObj.intensity/(1+msg.attenuation_quadratic);
+  }
+
+//  Not handling these on gzweb for now
+//
+//  if (lightObj instanceof THREE.SpotLight) {
+//    if (msg.spot_outer_angle) {
+//      lightObj.angle = msg.spot_outer_angle;
+//    }
+//    if (msg.spot_falloff) {
+//      lightObj.exponent = msg.spot_falloff;
+//    }
+//  }
+
+  if (msg.direction)
+  {
+    dir = new THREE.Vector3(msg.direction.x, msg.direction.y,
+        msg.direction.z);
+
+    entity.direction = new THREE.Vector3();
+    entity.direction.copy(dir);
+
+    dir.applyMatrix4(matrixWorld); // localToWorld
+    lightObj.target.position.copy(dir);
+  }
+};
diff --git a/gz3d/src/gzsdfparser.js b/gz3d/src/gzsdfparser.js
new file mode 100644
index 0000000000000000000000000000000000000000..70dbac6dc60e3da27ea5dd54bd29d917ce6520bb
--- /dev/null
+++ b/gz3d/src/gzsdfparser.js
@@ -0,0 +1,906 @@
+/**
+ * SDF parser constructor initializes SDF parser with the given parameters
+ * and defines a DOM parser function to parse SDF XML files
+ * @param {object} scene - the gz3d scene object
+ * @param {object} gui - the gz3d gui object
+ * @param {object} gziface - the gz3d gziface object
+ */
+GZ3D.SdfParser = function(scene, gui, gziface)
+{
+  // set the sdf version
+  this.SDF_VERSION = 1.5;
+  this.MATERIAL_ROOT = 'assets/';
+
+  // set the xml parser function
+  this.parseXML = function(xmlStr) {
+    return (new window.DOMParser()).parseFromString(xmlStr, 'text/xml');
+  };
+
+  this.scene = scene;
+  this.scene.setSDFParser(this);
+  this.gui = gui;
+  this.gziface = gziface;
+  this.init();
+
+  // cache materials if more than one model needs them
+  this.materials = [];
+  this.entityMaterial = {};
+
+};
+
+/**
+ * Initializes SDF parser by connecting relevant events from gziface
+ */
+GZ3D.SdfParser.prototype.init = function()
+{
+  var that = this;
+  this.gziface.emitter.on('error', function() {
+    that.gui.guiEvents.emit('notification_popup', 'GzWeb is currently running' +
+            'without a server, and materials could not be loaded.' +
+            'When connected scene will be reinitialized', 5000);
+    that.onConnectionError();
+  });
+  
+  this.gziface.emitter.on('material', function(mat) {
+    that.materials = mat;
+  });
+  
+  this.gziface.emitter.on('gzstatus', function(gzstatus) {
+    if (gzstatus === 'error')
+    {
+      that.gui.guiEvents.emit('notification_popup', 'GzWeb is currently ' +
+              'running without a GzServer, and Scene is reinitialized.', 5000);
+      that.onConnectionError();
+    }
+  });
+};
+
+/**
+ * Event callback function for gziface connection error which occurs
+ * when gziface cannot connect to gzbridge websocket
+ * this is due to 2 reasons:
+ * 1 - gzbridge websocket might not be run yet
+ * 2 - gzbridge websocket is trying to connect to gzserver which is not running currenly
+ */
+GZ3D.SdfParser.prototype.onConnectionError = function()
+{
+  this.scene.initScene();
+  
+  var that = this;
+  var entityCreated = function(model, type)
+  {
+    if (!that.gziface.isConnected)
+    {
+      that.addModelByType(model, type);
+    }
+  };
+  this.gui.emitter.on('entityCreated', entityCreated);
+  
+  var deleteEntity = function(entity)
+  {
+    var name = entity.name;
+    var obj = that.scene.getByName(name);
+    if (obj !== undefined)
+    {
+      if (obj.children[0] instanceof THREE.Light)
+      {
+        that.gui.setLightStats({name: name}, 'delete');
+      }
+      else
+      {
+        that.gui.setModelStats({name: name}, 'delete');
+      }
+      that.scene.remove(obj);
+    }
+  };
+  this.gui.emitter.on('deleteEntity', deleteEntity);
+};
+
+/**
+ * Parses string which denotes the color
+ * @param {string} colorStr - string which denotes the color where every value
+ * should be separated with single white space
+ * @returns {object} color - color object having r,g,b and alpha values
+ */
+GZ3D.SdfParser.prototype.parseColor = function(colorStr)
+{
+  var color = {};
+  var values = colorStr.split(' ');
+
+  color.r = parseFloat(values[0]);
+  color.g = parseFloat(values[1]);
+  color.b = parseFloat(values[2]);
+  color.a = parseFloat(values[3]);
+
+  return color;
+};
+
+/**
+ * Parses string which is a 3D vector
+ * @param {string} vectorStr - string which denotes the vector where every value
+ * should be separated with single white space
+ * @returns {object} vector3D - vector having x, y, z values
+ */
+GZ3D.SdfParser.prototype.parse3DVector = function(vectorStr)
+{
+  var vector3D = {};
+  var values = vectorStr.split(' ');
+  vector3D.x = parseFloat(values[0]);
+  vector3D.y = parseFloat(values[1]);
+  vector3D.z = parseFloat(values[2]);
+  return vector3D;
+};
+
+/**
+ * Creates THREE light object according to properties of sdf object
+ * which is parsed from sdf model of the light
+ * @param {object} sdfObj - object which is parsed from the sdf string
+ * @returns {THREE.Light} lightObj - THREE light object created
+ * according to given properties. The type of light object is determined
+ * according to light type
+ */
+GZ3D.SdfParser.prototype.spawnLightFromSDF = function(sdfObj)
+{
+  var light = sdfObj.light;
+  var lightObj;
+  var color = new THREE.Color();
+  var diffuseColor = this.parseColor(light.diffuse);
+  color.r = diffuseColor.r;
+  color.g = diffuseColor.g;
+  color.b = diffuseColor.b;
+
+  if (light['@type'] === 'point')
+  {
+    lightObj = new THREE.AmbientLight(color.getHex());
+    lightObj.distance = light.range;
+    this.scene.setPose(lightObj, light.pose.position, light.pose.orientation);
+  }
+  if (light['@type'] === 'spot')
+  {
+    lightObj = new THREE.SpotLight(color.getHex());
+    lightObj.distance = light.range;
+    this.scene.setPose(lightObj, light.pose.position, light.pose.orientation);
+  }
+  else if (light['@type'] === 'directional')
+  {
+    lightObj = new THREE.DirectionalLight(color.getHex());
+
+    var direction = this.parse3DVector(light.direction);
+    var dir = new THREE.Vector3(direction.x, direction.y, direction.z);
+    var target = dir;
+    var negDir = dir.negate();
+    negDir.normalize();
+    var factor = 10;
+    var pose = this.parsePose(light.pose);
+    pose.position.x += factor * negDir.x;
+    pose.position.y += factor * negDir.y;
+    pose.position.z += factor * negDir.z;
+
+    target.x -= pose.position.x;
+    target.y -= pose.position.y;
+    target.z -= pose.position.z;
+
+    lightObj.target.position = target;
+    lightObj.shadowCameraNear = 1;
+    lightObj.shadowCameraFar = 50;
+    lightObj.shadowMapWidth = 4094;
+    lightObj.shadowMapHeight = 4094;
+    lightObj.shadowCameraVisible = false;
+    lightObj.shadowCameraBottom = -100;
+    lightObj.shadowCameraLeft = -100;
+    lightObj.shadowCameraRight = 100;
+    lightObj.shadowCameraTop = 100;
+    lightObj.shadowBias = 0.0001;
+
+    lightObj.position.set(negDir.x, negDir.y, negDir.z);
+    this.scene.setPose(lightObj, pose.position, pose.orientation);
+  }
+  lightObj.intensity = parseFloat(light.attenuation.constant);
+  lightObj.castShadow = light.cast_shadows;
+  lightObj.shadowDarkness = 0.3;
+  lightObj.name = light['@name'];
+
+  return lightObj;
+};
+
+/**
+ * Parses a string which is a 3D vector
+ * @param {string} poseStr - string which denotes the pose of the object
+ * where every value should be separated with single white space and first three denotes
+ * x,y,z and values of the pose, and following three denotes euler rotation around x,y,z
+ * @returns {object} pose - pose object having position (x,y,z)(THREE.Vector3)
+ * and orientation (THREE.Quaternion) properties
+ */
+GZ3D.SdfParser.prototype.parsePose = function(poseStr)
+{
+  var values = poseStr.split(' ');
+
+  var position = new THREE.Vector3(parseFloat(values[0]),
+          parseFloat(values[1]), parseFloat(values[2]));
+
+  // get euler rotation and convert it to Quaternion
+  var quaternion = new THREE.Quaternion();
+  var euler = new THREE.Euler(parseFloat(values[3]), parseFloat(values[4]),
+          parseFloat(values[5]), 'ZYX');
+  quaternion.setFromEuler(euler);
+
+  var pose = {
+    'position': position,
+    'orientation': quaternion
+  };
+
+  return pose;
+
+};
+
+/**
+ * Parses a string which is a 3D vector
+ * @param {string} scaleStr - string which denotes scaling in x,y,z
+ * where every value should be separated with single white space
+ * @returns {THREE.Vector3} scale - THREE Vector3 object
+ * which denotes scaling of an object in x,y,z
+ */
+GZ3D.SdfParser.prototype.parseScale = function(scaleStr)
+{
+  var values = scaleStr.split(' ');
+  var scale = new THREE.Vector3(parseFloat(values[0]), parseFloat(values[1]),
+          parseFloat(values[2]));
+  return scale;
+};
+
+/**
+ * Parses SDF material element which is going to be used by THREE library
+ * It matches material scripts with the material objects which are
+ * already parsed by gzbridge and saved by SDFParser
+ * @param {object} material - SDF material object
+ * @returns {object} material - material object which has the followings:
+ * texture, normalMap, ambient, diffuse, specular, opacity
+ */
+GZ3D.SdfParser.prototype.createMaterial = function(material)
+{
+  var textureUri, texture, mat;
+  var ambient, diffuse, specular, opacity, normalMap;
+
+  if (!material) { return null; }
+
+  var script = material.script;
+  if (script)
+  {
+    if (script.uri)
+    {
+      // if there is just one uri convert it to array
+      if (!(script.uri instanceof Array))
+      {
+        script.uri = [script.uri];
+      }
+
+      if (script.name)
+      {
+        mat = this.materials[script.name];
+        // if we already cached the materials
+        if (mat)
+        {
+          ambient = mat.ambient;
+          diffuse = mat.diffuse;
+          specular = mat.specular;
+          opacity = mat.opacity;
+
+          if (mat.texture)
+          {
+            for (var i = 0; i < script.uri.length; ++i)
+            {
+              var uriType = script.uri[i].substring(0, script.uri[i]
+                      .indexOf('://'));
+              if (uriType === 'model')
+              {
+                // if texture uri
+                if (script.uri[i].indexOf('textures') > 0)
+                {
+                  textureUri = script.uri[i].substring(script.uri[i]
+                          .indexOf('://') + 3);
+                  break;
+                }
+              }
+              else if (uriType === 'file')
+              {
+                if (script.uri[i].indexOf('materials') > 0)
+                {
+                  textureUri = script.uri[i].substring(script.uri[i]
+                          .indexOf('://') + 3, script.uri[i]
+                          .indexOf('materials') + 9)
+                          + '/textures';
+                  break;
+                }
+              }
+            }
+            texture = this.MATERIAL_ROOT + textureUri + '/' + mat.texture;
+          }
+        }
+        else
+        {
+          //TODO: how to handle if material is not cached
+          console.log(script.name + ' is not cached!!!');
+        }
+      }
+    }
+  }
+
+  // normal map
+  if (material.normal_map)
+  {
+    var mapUri;
+    if (material.normal_map.indexOf('://') > 0)
+    {
+      mapUri = material.normal_map.substring(
+              material.normal_map.indexOf('://') + 3, material.normal_map
+                      .lastIndexOf('/'));
+    }
+    else
+    {
+      mapUri = textureUri;
+    }
+    if (mapUri)
+    {
+      var startIndex = material.normal_map.lastIndexOf('/') + 1;
+      if (startIndex < 0)
+      {
+        startIndex = 0;
+      }
+      var normalMapName = material.normal_map.substr(startIndex,
+              material.normal_map.lastIndexOf('.') - startIndex);
+      normalMap = this.MATERIAL_ROOT + mapUri + '/' + normalMapName + '.png';
+    }
+  }
+
+  return {
+    texture: texture,
+    normalMap: normalMap,
+    ambient: ambient,
+    diffuse: diffuse,
+    specular: specular,
+    opacity: opacity
+  };
+
+};
+
+/**
+ * Parses a string which is a size of an object
+ * @param {string} sizeStr - string which denotes size in x,y,z
+ * where every value should be separated with single white space
+ * @returns {object} size - size object which denotes
+ * size of an object in x,y,z
+ */
+GZ3D.SdfParser.prototype.parseSize = function(sizeStr)
+{
+  var sizeObj;
+  var values = sizeStr.split(' ');
+  var x = parseFloat(values[0]);
+  var y = parseFloat(values[1]);
+  var z = parseFloat(values[2]);
+  sizeObj = {
+    'x': x,
+    'y': y,
+    'z': z
+  };
+
+  return sizeObj;
+};
+
+/**
+ * Parses SDF geometry element and creates corresponding mesh,
+ * when it creates the THREE.Mesh object it directly add it to the parent
+ * object.
+ * @param {object} geom - SDF geometry object which determines the geometry
+ *  of the object and can have following properties: box, cylinder, sphere,
+ *   plane, mesh
+ * @param {object} mat - SDF material object which is going to be parsed
+ * by createMaterial function
+ * @param {object} parent - parent 3D object
+ */
+GZ3D.SdfParser.prototype.createGeom = function(geom, mat, parent)
+{
+  var that = this;
+  var obj;
+  var size, normal;
+
+  var material = this.createMaterial(mat);
+  if (geom.box)
+  {
+    size = this.parseSize(geom.box.size);
+    obj = this.scene.createBox(size.x, size.y, size.z);
+  }
+  else if (geom.cylinder)
+  {
+    obj = this.scene.createCylinder(geom.cylinder.radius, geom.cylinder.length);
+  }
+  else if (geom.sphere)
+  {
+    obj = this.scene.createSphere(geom.sphere.radius);
+  }
+  else if (geom.plane)
+  {
+    normal = this.parseSize(geom.plane.normal);
+    size = this.parseSize(geom.plane.size);
+    obj = this.scene.createPlane(normal.x, normal.y, normal.z, size.x, size.y);
+  }
+  else if (geom.mesh)
+  {
+    {
+      var meshUri = geom.mesh.uri;
+      var submesh = geom.mesh.submesh;
+      var centerSubmesh = geom.mesh.center_submesh;
+
+      var uriType = meshUri.substring(0, meshUri.indexOf('://'));
+      if (uriType === 'file' || uriType === 'model')
+      {
+        var modelName = meshUri.substring(meshUri.indexOf('://') + 3);
+        if (geom.mesh.scale)
+        {
+          var scale = this.parseScale(geom.mesh.scale);
+          parent.scale.x = scale.x;
+          parent.scale.y = scale.y;
+          parent.scale.z = scale.z;
+        }
+
+        var modelUri = this.MATERIAL_ROOT + '/' + modelName;
+        var materialName = parent.name + '::' + modelUri;
+        this.entityMaterial[materialName] = material;
+
+        this.scene.loadMesh(modelUri, submesh, centerSubmesh, function(dae){
+          if (that.entityMaterial[materialName])
+          {
+            var allChildren = [];
+            dae.getDescendants(allChildren);
+            for (var c = 0; c < allChildren.length; ++c)
+            {
+              if (allChildren[c] instanceof THREE.Mesh)
+              {
+                that.scene.setMaterial(allChildren[c],
+                        that.entityMaterial[materialName]);
+                break;
+              }
+            }
+          }
+          parent.add(dae);
+          loadGeom(parent);
+        });
+      }
+    }
+  }
+  //TODO: how to handle height map without connecting to the server
+  //  else if (geom.heightmap)
+  //  {
+  //    var request = new ROSLIB.ServiceRequest({
+  //      name : that.scene.name
+  //    });
+  //
+  //    // redirect the texture paths to the assets dir
+  //    var textures = geom.heightmap.texture;
+  //    for ( var k = 0; k < textures.length; ++k)
+  //    {
+  //      textures[k].diffuse = this.parseUri(textures[k].diffuse);
+  //      textures[k].normal = this.parseUri(textures[k].normal);
+  //    }
+  //
+  //    var sizes = geom.heightmap.size;
+  //
+  //    // send service request and load heightmap on response
+  //    this.heightmapDataService.callService(request,
+  //        function(result)
+  //        {
+  //          var heightmap = result.heightmap;
+  //          // gazebo heightmap is always square shaped,
+  //          // and a dimension of: 2^N + 1
+  //          that.scene.loadHeightmap(heightmap.heights, heightmap.size.x,
+  //              heightmap.size.y, heightmap.width, heightmap.height,
+  //              heightmap.origin, textures,
+  //              geom.heightmap.blend, parent);
+  //            //console.log('Result for service call on ' + result);
+  //        });
+  //
+  //    //this.scene.loadHeightmap(parent)
+  //  }
+
+  if (obj)
+  {
+    if (material)
+    {
+      // texture mapping for simple shapes and planes only,
+      // not used by mesh and terrain
+      this.scene.setMaterial(obj, material);
+    }
+    obj.updateMatrix();
+    parent.add(obj);
+    loadGeom(parent);
+  }
+
+  function loadGeom(visualObj)
+  {
+    var allChildren = [];
+    visualObj.getDescendants(allChildren);
+    for (var c = 0; c < allChildren.length; ++c)
+    {
+      if (allChildren[c] instanceof THREE.Mesh)
+      {
+        allChildren[c].castShadow = true;
+        allChildren[c].receiveShadow = true;
+
+        if (visualObj.castShadows)
+        {
+          allChildren[c].castShadow = visualObj.castShadows;
+        }
+        if (visualObj.receiveShadows)
+        {
+          allChildren[c].receiveShadow = visualObj.receiveShadows;
+        }
+
+        if (visualObj.name.indexOf('COLLISION_VISUAL') >= 0)
+        {
+          allChildren[c].castShadow = false;
+          allChildren[c].receiveShadow = false;
+
+          allChildren[c].visible = this.scene.showCollisions;
+        }
+        break;
+      }
+    }
+  }
+};
+
+/**
+ * Parses SDF visual element and creates THREE 3D object by parsing
+ * geometry element using createGeom function
+ * @param {object} visual - SDF visual element
+ * @returns {THREE.Object3D} visualObj - 3D object which is created
+ * according to SDF visual element.
+ */
+GZ3D.SdfParser.prototype.createVisual = function(visual)
+{
+  //TODO: handle these node values
+  // cast_shadow, receive_shadows
+  if (visual.geometry)
+  {
+    var visualObj = new THREE.Object3D();
+    visualObj.name = visual['@name'];
+
+    if (visual.pose)
+    {
+      var visualPose = this.parsePose(visual.pose);
+      this.scene
+        .setPose(visualObj, visualPose.position, visualPose.orientation);
+    }
+
+    this.createGeom(visual.geometry, visual.material, visualObj);
+
+    return visualObj;
+  }
+
+  return null;
+
+};
+
+/**
+ * Parses SDF XML string or SDF XML DOM object
+ * @param {object} sdf - It is either SDF XML string or SDF XML DOM object
+ * @returns {THREE.Object3D} object - 3D object which is created from the
+ * given SDF.
+ */
+GZ3D.SdfParser.prototype.spawnFromSDF = function(sdf)
+{
+  //parse sdfXML
+  var sdfXML;
+  if ((typeof sdf) === 'string')
+  {
+    sdfXML = this.parseXML(sdf);
+  }
+  else
+  {
+    sdfXML = sdf;
+  }
+
+  //convert SDF XML to Json string and parse JSON string to object
+  //TODO: we need better xml 2 json object convertor
+  var myjson = xml2json(sdfXML, '\t');
+  var sdfObj = JSON.parse(myjson).sdf;
+  // it is easier to manipulate json object
+
+  if (sdfObj.model)
+  {
+    return this.spawnModelFromSDF(sdfObj);
+  }
+  else if (sdfObj.light)
+  {
+    return this.spawnLightFromSDF(sdfObj);
+  }
+};
+
+/**
+ * Loads SDF file according to given model name
+ * @param {string} modelName - name of the model
+ * @returns {THREE.Object3D} modelObject - 3D object which is created
+ * according to SDF model.
+ */
+GZ3D.SdfParser.prototype.loadSDF = function(modelName)
+{
+  var sdf = this.loadModel(modelName);
+  return this.spawnFromSDF(sdf);
+};
+
+/**
+ * Creates 3D object from parsed model SDF
+ * @param {object} sdfObj - parsed SDF object
+ * @returns {THREE.Object3D} modelObject - 3D object which is created
+ * according to SDF model object.
+ */
+GZ3D.SdfParser.prototype.spawnModelFromSDF = function(sdfObj)
+{
+  // create the model
+  var modelObj = new THREE.Object3D();
+  modelObj.name = sdfObj.model['@name'];
+  //TODO: is that needed
+  //modelObj.userData = sdfObj.model.@id;
+
+  var pose;
+  var i, j, k;
+  var visualObj;
+  var linkObj, linkPose;
+
+  if (sdfObj.model.pose)
+  {
+    pose = this.parsePose(sdfObj.model.pose);
+    this.scene.setPose(modelObj, pose.position, pose.orientation);
+  }
+
+  //convert link object to link array
+  if (!(sdfObj.model.link instanceof Array))
+  {
+    sdfObj.model.link = [sdfObj.model.link];
+  }
+
+  for (i = 0; i < sdfObj.model.link.length; ++i)
+  {
+    linkObj = this.createLink(sdfObj.model.link[i]);
+    modelObj.add(linkObj);
+  }
+
+  //  this.scene.add(modelObj);
+  return modelObj;
+
+};
+
+/**
+ * Creates a link 3D object of the model. A model consists of links
+ * these links are 3D objects. The function creates only visual elements
+ * of the link by createLink function
+ * @param {object} link - parsed SDF link object
+ * @returns {THREE.Object3D} linkObject - 3D link object
+ */
+GZ3D.SdfParser.prototype.createLink = function(link)
+{
+  var linkPose, visualObj;
+  var linkObj = new THREE.Object3D();
+  linkObj.name = link['@name'];
+
+  if (link.pose)
+  {
+    linkPose = this.parsePose(link.pose);
+    this.scene.setPose(linkObj, linkPose.position, linkPose.orientation);
+  }
+
+  if (link.visual)
+  {
+    if (!(link.visual instanceof Array))
+    {
+      link.visual = [link.visual];
+    }
+
+    for (var i = 0; i < link.visual.length; ++i)
+    {
+      visualObj = this.createVisual(link.visual[i]);
+      if (visualObj && !visualObj.parent)
+      {
+        linkObj.add(visualObj);
+      }
+    }
+  }
+
+  if (link.collision)
+  {
+    if (link.collision.visual)
+    {
+      if (!(link.collision.visual instanceof Array))
+      {
+        link.collision.visual = [link.collision.visual];
+      }
+
+      for (var j = 0; j < link.collision.visual.length; ++j)
+      {
+        visualObj = this.createVisual(link.collision.visual[j]);
+        if (visualObj && !visualObj.parent)
+        {
+          linkObj.add(visualObj);
+        }
+      }
+
+    }
+  }
+
+  return linkObj;
+};
+
+/**
+ * Creates 3D object according to model name and type of the model and add
+ * the created object to the scene.
+ * @param {THREE.Object3D} model - model object which will be added to scene
+ * @param {string} type - type of the model which can be followings: box,
+ * sphere, cylinder, spotlight, directionallight, pointlight
+ */
+GZ3D.SdfParser.prototype.addModelByType = function(model, type)
+{
+  var sdf, translation, euler;
+  var quaternion = new THREE.Quaternion();
+  var modelObj;
+
+  if (model.matrixWorld)
+  {
+    var matrix = model.matrixWorld;
+    translation = new THREE.Vector3();
+    euler = new THREE.Euler();
+    var scale = new THREE.Vector3();
+    matrix.decompose(translation, euler, scale);
+    quaternion.setFromEuler(euler);
+  }
+
+  if (type === 'box')
+  {
+    sdf = this.createBoxSDF(translation, euler);
+    modelObj = this.spawnFromSDF(sdf);
+  }
+  else if (type === 'sphere')
+  {
+    sdf = this.createSphereSDF(translation, euler);
+    modelObj = this.spawnFromSDF(sdf);
+  }
+  else if (type === 'cylinder')
+  {
+    sdf = this.createCylinderSDF(translation, euler);
+    modelObj = this.spawnFromSDF(sdf);
+  }
+  else if (type === 'spotlight')
+  {
+    modelObj = this.scene.createLight(2);
+    this.scene.setPose(modelObj, translation, quaternion);
+  }
+  else if (type === 'directionallight')
+  {
+    modelObj = this.scene.createLight(3);
+    this.scene.setPose(modelObj, translation, quaternion);
+  }
+  else if (type === 'pointlight')
+  {
+    modelObj = this.scene.createLight(1);
+    this.scene.setPose(modelObj, translation, quaternion);
+  }
+  else
+  {
+    var sdfObj = this.loadSDF(type);
+    modelObj = new THREE.Object3D();
+    modelObj.add(sdfObj);
+    modelObj.name = model.name;
+    this.scene.setPose(modelObj, translation, quaternion);
+  }
+  
+  var that = this;
+  
+  var addModelFunc;
+  addModelFunc = function()
+  {
+    // check whether object is removed
+    var obj = that.scene.getByName(modelObj.name);
+    if (obj === undefined)
+    {
+      that.scene.add(modelObj);
+      that.gui.setModelStats(modelObj, 'update');
+    }
+    else
+    {
+      setTimeout(addModelFunc, 100);
+    }
+  };
+  
+  setTimeout(addModelFunc , 100);
+
+//  this.scene.add(modelObj);
+//  this.gui.setModelStats(modelObj, 'update');
+};
+
+/**
+ * Creates SDF string for simple shapes: box, cylinder, sphere.
+ * @param {string} type - type of the model which can be followings: box,
+ * sphere, cylinder
+ * @param {THREE.Vector3} translation - denotes the x,y,z position
+ * of the object
+ * @param {THREE.Euler} euler - denotes the euler rotation of the object
+ * @param {string} geomSDF - geometry element string of 3D object which is
+ * already created according to type of the object
+ * @returns {string} sdf - SDF string of the simple shape
+ */
+GZ3D.SdfParser.prototype.createSimpleShapeSDF = function(type, translation,
+        euler, geomSDF)
+  {
+  var sdf;
+
+  sdf = '<sdf version="' + this.SDF_VERSION + '">' + '<model name="' + type
+          + '">' + '<pose>' + translation.x + ' ' + translation.y + ' '
+          + translation.z + ' ' + euler.x + ' ' + euler.y + ' ' + euler.z
+          + '</pose>' + '<link name="link">'
+          + '<inertial><mass>1.0</mass></inertial>'
+          + '<collision name="collision">' + '<geometry>' + geomSDF
+          + '</geometry>' + '</collision>' + '<visual name="visual">'
+          + '<geometry>' + geomSDF + '</geometry>' + '<material>' + '<script>'
+          + '<uri>file://media/materials/scripts/gazebo.material' + '</uri>'
+          + '<name>Gazebo/Grey</name>' + '</script>' + '</material>'
+          + '</visual>' + '</link>' + '</model>' + '</sdf>';
+
+  return sdf;
+};
+
+/**
+ * Creates SDF string of box geometry element
+ * @param {THREE.Vector3} translation - the x,y,z position of
+ * the box object
+ * @param {THREE.Euler} euler - the euler rotation of the box object
+ * @returns {string} geomSDF - geometry SDF string of the box
+ */
+GZ3D.SdfParser.prototype.createBoxSDF = function(translation, euler)
+{
+  var geomSDF = '<box>' + '<size>1.0 1.0 1.0</size>' + '</box>';
+
+  return this.createSimpleShapeSDF('box', translation, euler, geomSDF);
+};
+
+/**
+ * Creates SDF string of sphere geometry element
+ * @param {THREE.Vector3} translation - the x,y,z position of
+ * the box object
+ * @param {THREE.Euler} euler - the euler rotation of the box object
+ * @returns {string} geomSDF - geometry SDF string of the sphere
+ */
+GZ3D.SdfParser.prototype.createSphereSDF = function(translation, euler)
+{
+  var geomSDF = '<sphere>' + '<radius>0.5</radius>' + '</sphere>';
+
+  return this.createSimpleShapeSDF('sphere', translation, euler, geomSDF);
+};
+
+/**
+ * Creates SDF string of cylinder geometry element
+ * @param {THREE.Vector3} translation - the x,y,z position of
+ * the box object
+ * @param {THREE.Euler} euler - the euler rotation of the cylinder object
+ * @returns {string} geomSDF - geometry SDF string of the cylinder
+ */
+GZ3D.SdfParser.prototype.createCylinderSDF = function(translation, euler)
+{
+  var geomSDF = '<cylinder>' + '<radius>0.5</radius>' + '<length>1.0</length>'
+          + '</cylinder>';
+
+  return this.createSimpleShapeSDF('cylinder', translation, euler, geomSDF);
+};
+
+/**
+ * Loads SDF of the model. It first constructs the url of the model
+ * according to modelname
+ * @param {string} modelName - name of the model
+ * @returns {XMLDocument} modelDOM - SDF DOM object of the loaded model
+ */
+GZ3D.SdfParser.prototype.loadModel = function(modelName)
+{
+  var modelFile = this.MATERIAL_ROOT + modelName + '/model.sdf';
+
+  var xhttp = new XMLHttpRequest();
+  xhttp.overrideMimeType('text/xml');
+  xhttp.open('GET', modelFile, false);
+  xhttp.send();
+  return xhttp.responseXML;
+};
diff --git a/gz3d/src/gzspawnmodel.js b/gz3d/src/gzspawnmodel.js
new file mode 100644
index 0000000000000000000000000000000000000000..9675c2257996987a4ef67640486cd5cb8b29a1f1
--- /dev/null
+++ b/gz3d/src/gzspawnmodel.js
@@ -0,0 +1,288 @@
+/**
+ * Spawn a model into the scene
+ * @constructor
+ */
+GZ3D.SpawnModel = function(scene, domElement)
+{
+  this.scene = scene;
+  this.domElement = ( domElement !== undefined ) ? domElement : document;
+  this.init();
+  this.obj = undefined;
+  this.callback = undefined;
+  this.sdfParser = undefined;
+};
+
+/**
+ * Initialize SpawnModel
+ */
+GZ3D.SpawnModel.prototype.init = function()
+{
+  this.plane = new THREE.Plane(new THREE.Vector3(0, 0, 1), 0);
+  this.projector = new THREE.Projector();
+  this.ray = new THREE.Ray();
+  this.obj = null;
+  this.active = false;
+  this.snapDist = null;
+};
+
+/**
+ * Start spawning an entity. Only simple shapes supported so far.
+ * Adds a temp object to the scene which is not registered on the server.
+ * @param {string} entity
+ * @param {function} callback
+ */
+GZ3D.SpawnModel.prototype.start = function(entity, callback)
+{
+  if (this.active)
+  {
+    this.finish();
+  }
+
+  this.callback = callback;
+
+  this.obj = new THREE.Object3D();
+  var mesh;
+  if (entity === 'box')
+  {
+    mesh = this.scene.createBox(1, 1, 1);
+  }
+  else if (entity === 'sphere')
+  {
+    mesh = this.scene.createSphere(0.5);
+  }
+  else if (entity === 'cylinder')
+  {
+    mesh = this.scene.createCylinder(0.5, 1.0);
+  }
+  else if (entity === 'pointlight')
+  {
+    mesh = this.scene.createLight(1);
+  }
+  else if (entity === 'spotlight')
+  {
+    mesh = this.scene.createLight(2);
+  }
+  else if (entity === 'directionallight')
+  {
+    mesh = this.scene.createLight(3);
+  }
+  else
+  {
+    mesh = this.sdfParser.loadSDF(entity);
+    //TODO: add transparency to the object
+  }
+
+  this.obj.name = this.generateUniqueName(entity);
+  this.obj.add(mesh);
+
+  // temp model appears within current view
+  var pos = new THREE.Vector2(window.window.innerWidth/2, window.innerHeight/2);
+  var intersect = new THREE.Vector3();
+  this.scene.getRayCastModel(pos, intersect);
+
+  this.obj.position.x = intersect.x;
+  this.obj.position.y = intersect.y;
+  this.obj.position.z += 0.5;
+  this.scene.add(this.obj);
+  // For the inserted light to have effect
+  var allObjects = [];
+  this.scene.scene.getDescendants(allObjects);
+  for (var l = 0; l < allObjects.length; ++l)
+  {
+    if (allObjects[l].material)
+    {
+      allObjects[l].material.needsUpdate = true;
+    }
+  }
+
+  var that = this;
+
+  this.mouseDown = function(event) {that.onMouseDown(event);};
+  this.mouseUp = function(event) {that.onMouseUp(event);};
+  this.mouseMove = function(event) {that.onMouseMove(event);};
+  this.keyDown = function(event) {that.onKeyDown(event);};
+  this.touchMove = function(event) {that.onTouchMove(event,true);};
+  this.touchEnd = function(event) {that.onTouchEnd(event);};
+
+  this.domElement.addEventListener('mousedown', that.mouseDown, false);
+  this.domElement.addEventListener( 'mouseup', that.mouseUp, false);
+  this.domElement.addEventListener( 'mousemove', that.mouseMove, false);
+  document.addEventListener( 'keydown', that.keyDown, false);
+
+  this.domElement.addEventListener( 'touchmove', that.touchMove, false);
+  this.domElement.addEventListener( 'touchend', that.touchEnd, false);
+
+  this.active = true;
+
+};
+
+/**
+ * Finish spawning an entity: re-enable camera controls,
+ * remove listeners, remove temp object
+ */
+GZ3D.SpawnModel.prototype.finish = function()
+{
+  var that = this;
+
+  this.domElement.removeEventListener( 'mousedown', that.mouseDown, false);
+  this.domElement.removeEventListener( 'mouseup', that.mouseUp, false);
+  this.domElement.removeEventListener( 'mousemove', that.mouseMove, false);
+  document.removeEventListener( 'keydown', that.keyDown, false);
+
+  this.scene.remove(this.obj);
+  this.obj = undefined;
+  this.active = false;
+};
+
+/**
+ * Window event callback
+ * @param {} event - not yet
+ */
+GZ3D.SpawnModel.prototype.onMouseDown = function(event)
+{
+  // Does this ever get called?
+  // Change like this:
+  // https://bitbucket.org/osrf/gzweb/pull-request/14/switch-to-arrow-mode-when-spawning-models/diff
+  event.preventDefault();
+  event.stopImmediatePropagation();
+};
+
+/**
+ * Window event callback
+ * @param {} event - mousemove events
+ */
+GZ3D.SpawnModel.prototype.onMouseMove = function(event)
+{
+  if (!this.active)
+  {
+    return;
+  }
+
+  event.preventDefault();
+
+  this.moveSpawnedModel(event.clientX,event.clientY);
+};
+
+/**
+ * Window event callback
+ * @param {} event - touchmove events
+ */
+GZ3D.SpawnModel.prototype.onTouchMove = function(event,originalEvent)
+{
+  if (!this.active)
+  {
+    return;
+  }
+
+  var e;
+
+  if (originalEvent)
+  {
+    e = event;
+  }
+  else
+  {
+    e = event.originalEvent;
+  }
+  e.preventDefault();
+
+  if (e.touches.length === 1)
+  {
+    this.moveSpawnedModel(e.touches[ 0 ].pageX,e.touches[ 0 ].pageY);
+  }
+};
+
+/**
+ * Window event callback
+ * @param {} event - touchend events
+ */
+GZ3D.SpawnModel.prototype.onTouchEnd = function()
+{
+  if (!this.active)
+  {
+    return;
+  }
+
+  this.callback(this.obj);
+  this.finish();
+};
+
+/**
+ * Window event callback
+ * @param {} event - mousedown events
+ */
+GZ3D.SpawnModel.prototype.onMouseUp = function(event)
+{
+  if (!this.active)
+  {
+    return;
+  }
+
+  this.callback(this.obj);
+  this.finish();
+};
+
+/**
+ * Window event callback
+ * @param {} event - keydown events
+ */
+GZ3D.SpawnModel.prototype.onKeyDown = function(event)
+{
+  if ( event.keyCode === 27 ) // Esc
+  {
+    this.finish();
+  }
+};
+
+/**
+ * Move temp spawned model
+ * @param {integer} positionX - Horizontal position on the canvas
+ * @param {integer} positionY - Vertical position on the canvas
+ */
+GZ3D.SpawnModel.prototype.moveSpawnedModel = function(positionX, positionY)
+{
+  var vector = new THREE.Vector3( (positionX / window.innerWidth) * 2 - 1,
+        -(positionY / window.innerHeight) * 2 + 1, 0.5);
+  this.projector.unprojectVector(vector, this.scene.camera);
+  this.ray.set(this.scene.camera.position,
+      vector.sub(this.scene.camera.position).normalize());
+  var point = this.ray.intersectPlane(this.plane);
+  point.z = this.obj.position.z;
+
+  if(this.snapDist)
+  {
+    point.x = Math.round(point.x / this.snapDist) * this.snapDist;
+    point.y = Math.round(point.y / this.snapDist) * this.snapDist;
+  }
+
+  this.scene.setPose(this.obj, point, new THREE.Quaternion());
+
+  if (this.obj.children[0].children[0] &&
+     (this.obj.children[0].children[0] instanceof THREE.SpotLight ||
+      this.obj.children[0].children[0] instanceof THREE.DirectionalLight))
+  {
+    var lightObj = this.obj.children[0].children[0];
+    lightObj.target.position.copy(this.obj.position);
+    lightObj.target.position.add(new THREE.Vector3(0,0,-0.5));
+  }
+};
+
+/**
+ * Generate unique name for spawned entity
+ * @param {string} entity - entity type
+ */
+GZ3D.SpawnModel.prototype.generateUniqueName = function(entity)
+{
+  var i = 0;
+  while (i < 1000)
+  {
+    if (this.scene.getByName(entity+'_'+i))
+    {
+      ++i;
+    }
+    else
+    {
+      return entity+'_'+i;
+    }
+  }
+};
diff --git a/gz3d/utils/.jshintrc b/gz3d/utils/.jshintrc
new file mode 100644
index 0000000000000000000000000000000000000000..e50434c8c2d88cd0ba93455a1c1fb4de8e81c462
--- /dev/null
+++ b/gz3d/utils/.jshintrc
@@ -0,0 +1,29 @@
+{
+  "globals": {
+    "module": true,
+    "EventEmitter2": true,
+    "ROSLIB": true,
+    "THREE": true,
+    "ColladaLoader2": true,
+    "requestAnimationFrame": true,
+    "xml2json": true
+  },
+  "curly": true,
+  "eqeqeq": true,
+  "immed": true,
+  "latedef": false,
+  "newcap": true,
+  "noarg": true,
+  "sub": true,
+  "undef": true,
+  "boss": false,
+  "eqnull": false,
+  "browser": true,
+  "devel": true,
+  "es5": true,
+  "strict": false,
+  "trailing": true,
+  "quotmark": "single",
+  "proto": true,
+  "laxbreak": true
+}
diff --git a/gz3d/utils/Gruntfile.js b/gz3d/utils/Gruntfile.js
new file mode 100644
index 0000000000000000000000000000000000000000..d3db5619b94108aa011b87eb802334d6d6cf04d8
--- /dev/null
+++ b/gz3d/utils/Gruntfile.js
@@ -0,0 +1,83 @@
+module.exports = function(grunt) {
+
+  grunt.initConfig({
+    pkg: grunt.file.readJSON('package.json'),
+    concat: {
+      build: {
+        src  : ['../src/*.js', '../src/**/*.js'],
+        dest : '../build/gz3d.js'
+      }
+    },
+    jshint: {
+      options: {
+        jshintrc: '.jshintrc'
+      },
+      files: [
+        'Gruntfile.js',
+        '../build/gz3d.js'
+      ]
+    },
+    uglify: {
+      options: {
+        report: 'min'
+      },
+      build: {
+        src: '../build/gz3d.js',
+        dest: '../build/gz3d.min.js'
+      }
+    },
+    watch: {
+      dev: {
+        options: {
+          interrupt: true
+        },
+        files: [
+          '../src/*.js',
+          '../src/**/*.js'
+        ],
+        tasks: ['concat']
+      },
+      build_and_watch: {
+        options: {
+          interrupt: true
+        },
+        files: [
+          'Gruntfile.js',
+          '.jshintrc',
+          '../src/*.js',
+          '../src/**/*.js'
+        ],
+        tasks: ['build']
+      }
+    },
+    clean: {
+      options: {
+        force: true
+      },
+      doc: ['../doc']
+    },
+    jsdoc: {
+      doc: {
+        src: [
+          '../src/*.js',
+          '../src/**/*.js'
+        ],
+        options: {
+          destination: '../doc'
+        }
+      }
+    },
+  });
+
+  grunt.loadNpmTasks('grunt-contrib-concat');
+  grunt.loadNpmTasks('grunt-contrib-jshint');
+  grunt.loadNpmTasks('grunt-contrib-watch');
+  grunt.loadNpmTasks('grunt-contrib-uglify');
+  grunt.loadNpmTasks('grunt-contrib-clean');
+  grunt.loadNpmTasks('grunt-jsdoc');
+
+  grunt.registerTask('dev', ['concat', 'watch']);
+  grunt.registerTask('build', ['concat', 'jshint', 'uglify']);
+  grunt.registerTask('build_and_watch', ['watch']);
+  grunt.registerTask('doc', ['clean', 'jsdoc']);
+};
diff --git a/gz3d/utils/package.json b/gz3d/utils/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..dd525f603e522591ccd6933f7eef4b01bab2ec82
--- /dev/null
+++ b/gz3d/utils/package.json
@@ -0,0 +1,18 @@
+{
+  "name": "gzweb-dev",
+  "description": "gzweb build environment setup",
+  "repository" :
+  {
+    "type" : "mercurial",
+    "url" : "https://bitbucket.org/osrf/gzweb"
+  },
+  "devDependencies": {
+    "grunt": "~0.4.1",
+    "grunt-contrib-concat": "~0.1.3",
+    "grunt-contrib-jshint": "~0.1.1",
+    "grunt-contrib-watch": "~0.3.1",
+    "grunt-contrib-uglify": "~0.2.0",
+    "grunt-jsdoc": "~0.3.0",
+    "grunt-contrib-clean": "~0.4.0"
+  }
+}
diff --git a/gzbridge/CMakeLists.txt b/gzbridge/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..42e850dc2d7e1c5fa9115765122029785c08503b
--- /dev/null
+++ b/gzbridge/CMakeLists.txt
@@ -0,0 +1,28 @@
+include_directories(
+  include_directories(${GAZEBO_INCLUDE_DIRS})
+  ${PROJECT_SOURCE_DIR}/deps/libwebsockets/lib
+)
+
+link_directories(${GAZEBO_LIBRARY_DIRS})
+
+set (sources
+  pb2json.cc
+  GazeboInterface.cc
+  pb2json.cc
+)
+
+set (headers
+  pb2json.hh
+  GazeboInterface.hh
+)
+
+# add_executable(gzbridge ${sources})
+
+# target_link_libraries(gzbridge websockets_shared ${GAZEBO_LIBRARIES} ${Boost_LIBRARIES} ${PROTOBUF_LIBRARIES} ${JANSSON_LIBRARIES})
+#add_dependencies(${TEST_NAME} websockets_shared)
+
+# Set test app specific defines.
+# set_property(TARGET gzbridge
+#      PROPERTY COMPILE_DEFINITIONS
+#        INSTALL_DATADIR="${CMAKE_INSTALL_PREFIX}/share"
+#      )
diff --git a/gzbridge/ConfigLoader.cc b/gzbridge/ConfigLoader.cc
new file mode 100644
index 0000000000000000000000000000000000000000..ff438033e77df8839d931593517f5e2683f4d8d8
--- /dev/null
+++ b/gzbridge/ConfigLoader.cc
@@ -0,0 +1,423 @@
+#include "ConfigLoader.hh"
+
+#include <vector>
+#include <map>
+#include <exception>
+#include <fstream>
+
+#include <boost/filesystem.hpp>
+
+using namespace gzweb;
+
+void ConfigLoader::loadAllFiles(ConfigLoader* c, const std::string& path)
+{
+  try
+  {
+  	for ( boost::filesystem::recursive_directory_iterator end, dir(path); dir != end; ++dir )
+  	{
+  		boost::filesystem::path p(*dir);
+  		if(p.extension() == c->m_fileEnding)
+  		{
+  			std::ifstream in((*dir).path().string().c_str(), std::ios::binary);
+  			c->parseScript(in);
+  		}
+  	}
+  }
+  catch (boost::filesystem::filesystem_error &e)
+  {
+    std::cerr << e.what() << std::endl;
+  }
+}
+
+ConfigLoader::ConfigLoader(const std::string& fileEnding)
+{
+	//Register as a ScriptLoader
+	m_fileEnding = fileEnding;
+}
+
+ConfigLoader::~ConfigLoader()
+{
+	clearScriptList();
+
+}
+
+void ConfigLoader::clearScriptList()
+{
+	std::map <std::string, ConfigNode *>::iterator i;
+	for (i = m_scriptList.begin(); i != m_scriptList.end(); i++)
+	{
+		delete i->second;
+	}
+	m_scriptList.clear();
+}
+
+ConfigNode *ConfigLoader::getConfigScript(const std::string &name)
+{
+	std::map <std::string, ConfigNode*>::iterator i;
+
+	std::string key = name;
+	i = m_scriptList.find(key);
+
+	//If found..
+	if (i != m_scriptList.end())
+	{
+		return i->second;
+	}
+	else
+	{
+		return NULL;
+	}
+}
+
+std::map <std::string, ConfigNode*> ConfigLoader::getAllConfigScripts ()
+{
+	return m_scriptList;
+}
+
+void ConfigLoader::parseScript(std::ifstream &stream)
+{
+	//Get first token
+	_nextToken(stream);
+	if (tok == TOKEN_EOF)
+	{
+		stream.close();
+		return;
+	}
+
+	//Parse the script
+	_parseNodes(stream, 0);
+
+	stream.close();
+}
+
+void ConfigLoader::_nextToken(std::ifstream &stream)
+{
+	lastTok = tok;
+	lastTokVal = tokVal;
+
+	//EOF token
+	if (stream.eof())
+	{
+		tok = TOKEN_EOF;
+		return;
+	}
+
+	//(Get next character)
+	int ch = stream.get();
+	if (ch == -1)
+	{
+		tok = TOKEN_EOF;
+		return;
+	}
+	while ((ch == ' ' || ch == 9) && !stream.eof())
+	{    //Skip leading spaces / tabs
+		ch = stream.get();
+	}
+
+	if (stream.eof())
+	{
+		tok = TOKEN_EOF;
+		return;
+	}
+
+	//Newline token
+	if (ch == '\r' || ch == '\n')
+	{
+		do
+		{
+			ch = stream.get();
+		} while ((ch == '\r' || ch == '\n') && !stream.eof());
+
+		stream.unget();
+
+		tok = TOKEN_NewLine;
+		return;
+	}
+
+	//Open brace token
+	else if (ch == '{')
+	{
+		tok = TOKEN_OpenBrace;
+		return;
+	}
+
+	//Close brace token
+	else if (ch == '}')
+	{
+		tok = TOKEN_CloseBrace;
+		return;
+	}
+
+	//Text token
+	if (ch < 32 || ch > 122)    //Verify valid char
+	{
+		throw std::runtime_error("Parse Error: Invalid character, ConfigLoader::load()");
+	}
+
+	tokVal = "";
+	tok = TOKEN_Text;
+	do
+	{
+		//Skip comments
+		if (ch == '/')
+		{
+			int ch2 = stream.peek();
+
+			//C++ style comment (//)
+			if (ch2 == '/')
+			{
+				stream.get();
+				do
+				{
+					ch = stream.get();
+				} while (ch != '\r' && ch != '\n' && !stream.eof());
+
+				tok = TOKEN_NewLine;
+				return;
+			}
+      else if (ch2 == '*')
+      {
+        stream.get();
+				do
+				{
+					ch = stream.get();
+					ch2 = stream.peek();
+				} while (!(ch == '*' && ch2 == '/') && !stream.eof());
+        stream.get();
+				do
+				{
+					ch = stream.get();
+				} while (ch != '\r' && ch != '\n' && !stream.eof());
+        continue;
+      }
+		}
+
+		//Add valid char to tokVal
+		tokVal += (char)ch;
+
+		//Next char
+		ch = stream.get();
+
+	} while (ch > 32 && ch <= 122 && !stream.eof());
+
+	stream.unget();
+
+	return;
+}
+
+void ConfigLoader::_skipNewLines(std::ifstream &stream)
+{
+	while (tok == TOKEN_NewLine)
+	{
+		_nextToken(stream);
+	}
+}
+
+void ConfigLoader::_parseNodes(std::ifstream &stream, ConfigNode *parent)
+{
+	typedef std::pair<std::string, ConfigNode*> ScriptItem;
+
+	while (true)
+	{
+		switch (tok)
+		{
+			//Node
+			case TOKEN_Text:
+				//Add the new node
+				ConfigNode *newNode;
+				if (parent)
+				{
+					newNode = parent->addChild(tokVal);
+				}
+				else
+				{
+					newNode = new ConfigNode(0, tokVal);
+				}
+
+				//Get values
+				_nextToken(stream);
+				while (tok == TOKEN_Text)
+				{
+					newNode->addValue(tokVal);
+					_nextToken(stream);
+				}
+
+				//Add root nodes to scriptList
+				if (!parent){
+					std::string key;
+
+					if (newNode->getValues().empty())
+					{
+						key = newNode->getName() + ' ';
+					}
+					else
+					{
+						key = newNode->getName() + ' ' + newNode->getValues().front();
+					}
+
+					m_scriptList.insert(ScriptItem(key, newNode));
+				}
+
+				_skipNewLines(stream);
+
+				//Add any sub-nodes
+				if (tok == TOKEN_OpenBrace)
+				{
+					//Parse nodes
+					_nextToken(stream);
+					_parseNodes(stream, newNode);
+					//Check for matching closing brace
+					if (tok != TOKEN_CloseBrace)
+					{
+						throw std::runtime_error("Parse Error: Expecting closing brace");
+					}
+					_nextToken(stream);
+					_skipNewLines(stream);
+				}
+
+				break;
+
+			//Out of place brace
+			case TOKEN_OpenBrace:
+				throw std::runtime_error("Parse Error: Opening brace out of plane");
+				break;
+
+			//Return if end of nodes have been reached
+			case TOKEN_CloseBrace:
+				return;
+
+			//Return if reached end of file
+			case TOKEN_EOF:
+				return;
+
+			case TOKEN_NewLine:
+				_nextToken(stream);
+				break;
+		}
+	};
+}
+
+ConfigNode::ConfigNode(ConfigNode *parent, const std::string &name)
+{
+	m_name = name;
+	m_parent = parent;
+	_removeSelf = true;    //For proper destruction
+	m_lastChildFound = -1;
+
+	//Add self to parent's child list (unless this is the root node being created)
+	if (parent != NULL)
+	{
+		m_parent->m_children.push_back(this);
+		_iter = --(m_parent->m_children.end());
+	}
+}
+
+ConfigNode::~ConfigNode()
+{
+	//Delete all children
+	std::vector<ConfigNode*>::iterator i;
+	for (i = m_children.begin(); i != m_children.end(); i++)
+	{
+		ConfigNode *node = *i;
+		node->_removeSelf = false;
+		delete node;
+	}
+	m_children.clear();
+
+	//Remove self from parent's child list
+	if (_removeSelf && m_parent != NULL)
+	{
+		m_parent->m_children.erase(_iter);
+	}
+}
+
+ConfigNode *ConfigNode::addChild(const std::string &name, bool replaceExisting)
+{
+	if (replaceExisting)
+	{
+		ConfigNode *node = findChild(name, false);
+		if (node)
+		{
+			return node;
+		}
+	}
+	return new ConfigNode(this, name);
+}
+
+ConfigNode *ConfigNode::findChild(const std::string &name, bool recursive)
+{
+	int indx, prevC, nextC;
+	int childCount = (int)m_children.size();
+
+	if (m_lastChildFound != -1)
+	{
+		//If possible, try checking the nodes neighboring the last successful search
+		//(often nodes searched for in sequence, so this will boost search speeds).
+		prevC = m_lastChildFound-1; if (prevC < 0) prevC = 0; else if (prevC >= childCount) prevC = childCount-1;
+		nextC = m_lastChildFound+1; if (nextC < 0) nextC = 0; else if (nextC >= childCount) nextC = childCount-1;
+		for (indx = prevC; indx <= nextC; ++indx)
+		{
+			ConfigNode *node = m_children[indx];
+			if (node->m_name == name)
+			{
+				m_lastChildFound = indx;
+				return node;
+			}
+		}
+
+		//If not found that way, search for the node from start to finish, avoiding the
+		//already searched area above.
+		for (indx = nextC + 1; indx < childCount; ++indx)
+		{
+			ConfigNode *node = m_children[indx];
+			if (node->m_name == name) {
+				m_lastChildFound = indx;
+				return node;
+			}
+		}
+		for (indx = 0; indx < prevC; ++indx)
+		{
+			ConfigNode *node = m_children[indx];
+			if (node->m_name == name) {
+				m_lastChildFound = indx;
+				return node;
+			}
+		}
+	}
+	else
+	{
+		//Search for the node from start to finish
+		for (indx = 0; indx < childCount; ++indx){
+			ConfigNode *node = m_children[indx];
+			if (node->m_name == name) {
+				m_lastChildFound = indx;
+				return node;
+			}
+		}
+	}
+
+	//If not found, search child nodes (if recursive == true)
+	if (recursive)
+	{
+		for (indx = 0; indx < childCount; ++indx)
+		{
+			m_children[indx]->findChild(name, recursive);
+		}
+	}
+
+	//Not found anywhere
+	return NULL;
+}
+
+void ConfigNode::setParent(ConfigNode *newParent)
+{
+	//Remove self from current parent
+	m_parent->m_children.erase(_iter);
+
+	//Set new parent
+	m_parent = newParent;
+
+	//Add self to new parent
+	m_parent->m_children.push_back(this);
+	_iter = --(m_parent->m_children.end());
+}
diff --git a/gzbridge/ConfigLoader.hh b/gzbridge/ConfigLoader.hh
new file mode 100644
index 0000000000000000000000000000000000000000..69a78ec46bdca7b73ccbed5d89158a7c0603c940
--- /dev/null
+++ b/gzbridge/ConfigLoader.hh
@@ -0,0 +1,136 @@
+#ifndef _CONFIG_LOADER_HH_
+#define _CONFIG_LOADER_HH_
+
+#include <map>
+#include <vector>
+#include <cassert>
+#include <string>
+#include <iostream>
+
+namespace gzweb
+{
+  class ConfigNode;
+
+	class ConfigLoader
+	{
+	public:
+		static void loadAllFiles(ConfigLoader* c, const std::string& path);
+
+		ConfigLoader(const std::string& fileEnding);
+		virtual ~ConfigLoader();
+
+		std::string m_fileEnding;
+
+		// For a line like
+		// entity animals/dog
+		// {
+		//    ...
+		// }
+		// The type is "entity" and the name is "animals/dog"
+		// Or if animal/dog was not there then name is ""
+		virtual ConfigNode *getConfigScript (const std::string &name);
+
+		virtual std::map <std::string, ConfigNode*> getAllConfigScripts ();
+
+		virtual void parseScript(std::ifstream &stream);
+
+
+	protected:
+
+		float m_LoadOrder;
+		// like "*.object"
+
+		std::map <std::string, ConfigNode*> m_scriptList;
+
+		enum Token
+		{
+			TOKEN_Text,
+			TOKEN_NewLine,
+			TOKEN_OpenBrace,
+			TOKEN_CloseBrace,
+			TOKEN_EOF,
+		};
+
+		Token tok, lastTok;
+		std::string tokVal, lastTokVal;
+
+		void _parseNodes(std::ifstream &stream, ConfigNode *parent);
+		void _nextToken(std::ifstream &stream);
+		void _skipNewLines(std::ifstream &stream);
+
+		virtual void clearScriptList();
+	};
+
+	class ConfigNode
+	{
+	public:
+		ConfigNode(ConfigNode *parent, const std::string &name = "untitled");
+		~ConfigNode();
+
+		inline void setName(const std::string &name)
+		{
+			this->m_name = name;
+		}
+
+		inline std::string &getName()
+		{
+			return m_name;
+		}
+
+		inline void addValue(const std::string &value)
+		{
+			m_values.push_back(value);
+		}
+
+		inline void clearValues()
+		{
+			m_values.clear();
+		}
+
+		inline std::vector<std::string> &getValues()
+		{
+			return m_values;
+		}
+
+		inline const std::string &getValue(unsigned int index = 0)
+		{
+			assert(index < m_values.size());
+			return m_values[index];
+		}
+
+		ConfigNode *addChild(const std::string &name = "untitled", bool replaceExisting = false);
+		ConfigNode *findChild(const std::string &name, bool recursive = false);
+
+		inline std::vector<ConfigNode*> &getChildren()
+		{
+			return m_children;
+		}
+
+		inline ConfigNode *getChild(unsigned int index = 0)
+		{
+			assert(index < m_children.size());
+			return m_children[index];
+		}
+
+		void setParent(ConfigNode *newParent);
+
+		inline ConfigNode *getParent()
+		{
+			return m_parent;
+		}
+
+	private:
+		std::string m_name;
+		std::vector<std::string> m_values;
+		std::vector<ConfigNode*> m_children;
+		ConfigNode *m_parent;
+
+		int m_lastChildFound;  //The last child node's index found with a call to findChild()
+
+		std::vector<ConfigNode*>::iterator _iter;
+		bool _removeSelf;
+	};
+
+}
+
+#endif
diff --git a/gzbridge/GZNode.cc b/gzbridge/GZNode.cc
new file mode 100644
index 0000000000000000000000000000000000000000..43f7459e0004be946c46a614eb7054c2b8268ee8
--- /dev/null
+++ b/gzbridge/GZNode.cc
@@ -0,0 +1,339 @@
+/*
+ * Copyright 2013 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+
+#include <node.h>
+#include "GZNode.hh"
+
+#include "GazeboInterface.hh"
+#include "OgreMaterialParser.hh"
+
+using namespace v8;
+using namespace gzweb;
+
+/////////////////////////////////////////////////
+GZNode::GZNode()
+{
+  isGzServerConnected = false;
+  if (!gazebo::transport::init()) {
+    return;
+  }
+
+  isGzServerConnected = true;
+  gazebo::transport::run();
+
+  this->gzIface = new GazeboInterface();
+  this->gzIface->Init();
+  this->gzIface->RunThread();
+};
+
+/////////////////////////////////////////////////
+GZNode::~GZNode()
+{
+  // Make sure to shut everything down.
+  gazebo::transport::fini();
+};
+
+/////////////////////////////////////////////////
+void GZNode::Init(Handle<Object> exports)
+{
+  // Prepare constructor template
+  Local<FunctionTemplate> tpl = FunctionTemplate::New(New);
+  tpl->SetClassName(String::NewSymbol("GZNode"));
+  tpl->InstanceTemplate()->SetInternalFieldCount(1);
+  // Prototype
+  tpl->PrototypeTemplate()->Set(String::NewSymbol("loadMaterialScripts"),
+      FunctionTemplate::New(LoadMaterialScripts)->GetFunction());
+
+  tpl->PrototypeTemplate()->Set(String::NewSymbol("setCallback"),
+      FunctionTemplate::New(Callback)->GetFunction());
+
+  tpl->PrototypeTemplate()->Set(String::NewSymbol("request"),
+      FunctionTemplate::New(Request)->GetFunction());
+
+  tpl->PrototypeTemplate()->Set(String::NewSymbol("getMessages"),
+      FunctionTemplate::New(GetMessages)->GetFunction());
+
+  tpl->PrototypeTemplate()->Set(String::NewSymbol("getPoseMsgFilterMinimumAge"),
+      FunctionTemplate::New(GetPoseMsgFilterMinimumAge)->GetFunction());
+
+  tpl->PrototypeTemplate()->Set(String::NewSymbol("setPoseMsgFilterMinimumAge"),
+      FunctionTemplate::New(SetPoseMsgFilterMinimumAge)->GetFunction());
+
+  tpl->PrototypeTemplate()->Set(
+		  String::NewSymbol("getPoseMsgFilterMinimumDistanceSquared"),
+      FunctionTemplate::New(
+    		  GetPoseMsgFilterMinimumDistanceSquared)->GetFunction());
+
+  tpl->PrototypeTemplate()->Set(
+      String::NewSymbol("setPoseMsgFilterMinimumDistanceSquared"),
+      FunctionTemplate::New(
+          SetPoseMsgFilterMinimumDistanceSquared)->GetFunction());
+
+  tpl->PrototypeTemplate()->Set(
+      String::NewSymbol("getPoseMsgFilterMinimumQuaternionSquared"),
+      FunctionTemplate::New(
+          GetPoseMsgFilterMinimumQuaternionSquared)->GetFunction());
+
+  tpl->PrototypeTemplate()->Set(
+      String::NewSymbol("setPoseMsgFilterMinimumQuaternionSquared"),
+      FunctionTemplate::New(
+          SetPoseMsgFilterMinimumQuaternionSquared)->GetFunction());
+
+  tpl->PrototypeTemplate()->Set(String::NewSymbol("setConnected"),
+      FunctionTemplate::New(
+          SetConnected)->GetFunction());
+
+  tpl->PrototypeTemplate()->Set(
+       String::NewSymbol("getIsGzServerConnected"),
+       FunctionTemplate::New(
+           GetIsGzServerConnected)->GetFunction());
+
+  tpl->PrototypeTemplate()->Set(String::NewSymbol("getMaterialScriptsMessage"),
+       FunctionTemplate::New(GetMaterialScriptsMessage)->GetFunction());
+
+  Persistent<Function> constructor =
+      Persistent<Function>::New(tpl->GetFunction());
+
+  exports->Set(String::NewSymbol("GZNode"), constructor);
+}
+
+/////////////////////////////////////////////////
+Handle<Value> GZNode::New(const Arguments& args)
+{
+  HandleScope scope;
+
+  GZNode* obj = new GZNode();
+  obj->Wrap(args.This());
+
+  return args.This();
+}
+
+/////////////////////////////////////////////////
+Handle<Value> GZNode::LoadMaterialScripts(const Arguments& args)
+{
+  HandleScope scope;
+
+  if (args.Length() < 1)
+  {
+    ThrowException(Exception::TypeError(
+        String::New("Wrong number of arguments")));
+    return scope.Close(Undefined());
+  }
+
+  if (!args[0]->IsString())
+  {
+    ThrowException(Exception::TypeError(
+        String::New("Wrong argument type. String expected.")));
+    return scope.Close(Undefined());
+  }
+
+  GZNode* obj = ObjectWrap::Unwrap<GZNode>(args.This());
+
+  String::Utf8Value path(args[0]->ToString());
+  obj->gzIface->LoadMaterialScripts(std::string(*path));
+
+  return scope.Close(Undefined());
+}
+
+Handle<Value> GZNode::GetMaterialScriptsMessage(const Arguments& args)
+{
+  HandleScope scope;
+
+  if (args.Length() < 1)
+   {
+     ThrowException(Exception::TypeError(
+         String::New("Wrong number of arguments")));
+     return scope.Close(Undefined());
+   }
+
+   if (!args[0]->IsString())
+   {
+     ThrowException(Exception::TypeError(
+         String::New("Wrong argument type. String expected.")));
+     return scope.Close(Undefined());
+   }
+
+   String::Utf8Value path(args[0]->ToString());
+
+  OgreMaterialParser materialParser;
+  materialParser.Load(std::string(*path));
+  std::string topic = "~/material";
+  std::string materialJson = materialParser.GetMaterialAsJson();
+  std::string msg;
+  msg += "{\"op\":\"publish\",\"topic\":\"" + topic + "\", \"msg\":";
+  msg += materialJson;
+  msg += "}";
+
+  return scope.Close(String::New(msg.c_str()));
+}
+
+/////////////////////////////////////////////////
+Handle<Value> GZNode::Request(const Arguments& args)
+{
+  HandleScope scope;
+
+  if (args.Length() < 1)
+  {
+    ThrowException(Exception::TypeError(
+        String::New("Wrong number of arguments")));
+    return scope.Close(Undefined());
+  }
+
+  if (!args[0]->IsString())
+  {
+    ThrowException(Exception::TypeError(
+        String::New("Wrong argument type. String expected.")));
+    return scope.Close(Undefined());
+  }
+
+  GZNode* obj = ObjectWrap::Unwrap<GZNode>(args.This());
+
+  String::Utf8Value request(args[0]->ToString());
+  obj->gzIface->PushRequest(std::string(*request));
+
+  return scope.Close(Undefined());
+}
+
+/////////////////////////////////////////////////
+Handle<Value> GZNode::Callback(const Arguments& args) {
+  HandleScope scope;
+
+  v8::Local<v8::Function> cb = Local<Function>::Cast(args[0]);
+  const unsigned argc = 1;
+  Local<Value> argv[argc] = { Local<Value>::New(String::New("hello world")) };
+  cb->Call(Context::GetCurrent()->Global(), argc, argv);
+
+  return scope.Close(Undefined());
+}
+
+/////////////////////////////////////////////////
+Handle<v8::Value> GZNode::SetPoseMsgFilterMinimumAge(const v8::Arguments& args)
+{
+  HandleScope scope;
+
+  GZNode* obj = ObjectWrap::Unwrap<GZNode>(args.This());
+  Local<Number> v = Local<Number>::Cast(args[0]);
+  double value = v->Value();
+  obj->gzIface->SetPoseFilterMinimumMsgAge(value);
+
+  return scope.Close(Undefined());
+}
+
+/////////////////////////////////////////////////
+Handle<v8::Value> GZNode::GetPoseMsgFilterMinimumAge(const \
+														v8::Arguments& args)
+{
+  HandleScope scope;
+  GZNode* obj = ObjectWrap::Unwrap<GZNode>(args.This());
+  double value  = obj->gzIface->GetPoseFilterMinimumMsgAge();
+  return scope.Close(Number::New(value));
+}
+
+/////////////////////////////////////////////////
+Handle<v8::Value> GZNode::SetPoseMsgFilterMinimumDistanceSquared(const
+    v8::Arguments& args)
+{
+  HandleScope scope;
+
+  GZNode* obj = ObjectWrap::Unwrap<GZNode>(args.This());
+  Local<Number> v = Local<Number>::Cast(args[0]);
+  double value = v->Value();
+  obj->gzIface->SetPoseFilterMinimumDistanceSquared(value);
+
+  return scope.Close(Undefined());
+}
+
+/////////////////////////////////////////////////
+Handle<v8::Value> GZNode::GetPoseMsgFilterMinimumDistanceSquared(const
+    v8::Arguments& args)
+{
+  HandleScope scope;
+  GZNode* obj = ObjectWrap::Unwrap<GZNode>(args.This());
+  double value  = obj->gzIface->GetPoseFilterMinimumDistanceSquared();
+  return scope.Close(Number::New(value));
+}
+
+/////////////////////////////////////////////////
+Handle<v8::Value> GZNode::SetPoseMsgFilterMinimumQuaternionSquared(const
+    v8::Arguments& args)
+{
+  HandleScope scope;
+
+  GZNode* obj = ObjectWrap::Unwrap<GZNode>(args.This());
+  Local<Number> v = Local<Number>::Cast(args[0]);
+  double value = v->Value();
+  obj->gzIface->SetPoseFilterMinimumQuaternionSquared(value);
+
+  return scope.Close(Undefined());
+}
+
+/////////////////////////////////////////////////
+Handle<v8::Value> GZNode::SetConnected(const v8::Arguments& args)
+{
+  HandleScope scope;
+
+  GZNode *obj = ObjectWrap::Unwrap<GZNode>(args.This());
+  bool value = args[0]->BooleanValue();
+  obj->gzIface->SetConnected(value);
+
+  return scope.Close(Undefined());
+}
+
+Handle<v8::Value> GZNode::GetIsGzServerConnected(const v8::Arguments& args)
+{
+  HandleScope scope;
+
+  GZNode *obj = ObjectWrap::Unwrap<GZNode>(args.This());
+  bool value = obj->isGzServerConnected;
+
+  return scope.Close(Boolean::New(value));
+}
+
+/////////////////////////////////////////////////
+Handle<v8::Value> GZNode::GetPoseMsgFilterMinimumQuaternionSquared(const
+    v8::Arguments& args)
+{
+  HandleScope scope;
+  GZNode* obj = ObjectWrap::Unwrap<GZNode>(args.This());
+  double value  = obj->gzIface->GetPoseFilterMinimumQuaternionSquared();
+  return scope.Close(Number::New(value));
+}
+
+/////////////////////////////////////////////////
+Handle<Value> GZNode::GetMessages(const Arguments& args)
+{
+  HandleScope scope;
+
+  GZNode* obj = ObjectWrap::Unwrap<GZNode>(args.This());
+
+  std::vector<std::string> msgs = obj->gzIface->PopOutgoingMessages();
+  Local<Array> arguments = Array::New(msgs.size());
+  for (unsigned int i = 0; i < msgs.size(); ++i) {
+    arguments->Set(i ,String::New(msgs[i].c_str()));
+  }
+
+  return scope.Close(arguments);
+}
+
+/////////////////////////////////////////////////
+void InitAll(Handle<Object> exports)
+{
+  GZNode::Init(exports);
+}
+
+/////////////////////////////////////////////////
+NODE_MODULE(gzbridge, InitAll)
diff --git a/gzbridge/GZNode.hh b/gzbridge/GZNode.hh
new file mode 100644
index 0000000000000000000000000000000000000000..5bcde2f5d3664457d2e0ddc6cda889d998776140
--- /dev/null
+++ b/gzbridge/GZNode.hh
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2013 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+
+#ifndef _GZNODE_HH_
+#define _GZNODE_HH_
+
+#include <node.h>
+#include <string>
+#include <vector>
+
+namespace gzweb
+{
+  class GazeboInterface;
+
+  class GZNode : public node::ObjectWrap
+  {
+    public: static void Init(v8::Handle<v8::Object> exports);
+
+    private: GZNode();
+
+    private: ~GZNode();
+
+    private: static v8::Handle<v8::Value> New(const v8::Arguments& args);
+
+    private: static v8::Handle<v8::Value> Callback(const v8::Arguments& args);
+
+    private: static v8::Handle<v8::Value> Request(const v8::Arguments& args);
+
+    private: static v8::Handle<v8::Value> SetPoseMsgFilterMinimumAge(const
+      v8::Arguments& args);
+
+    private: static v8::Handle<v8::Value> GetPoseMsgFilterMinimumAge(const
+      v8::Arguments& args);
+
+    private: static v8::Handle<v8::Value>
+        SetPoseMsgFilterMinimumDistanceSquared(const v8::Arguments& args);
+
+    private: static v8::Handle<v8::Value>
+        GetPoseMsgFilterMinimumDistanceSquared(const v8::Arguments& args);
+
+    private: static v8::Handle<v8::Value>
+        SetPoseMsgFilterMinimumQuaternionSquared(const v8::Arguments& args);
+
+    private: static v8::Handle<v8::Value>
+        SetConnected(const v8::Arguments& args);
+
+    private: static v8::Handle<v8::Value>
+        GetPoseMsgFilterMinimumQuaternionSquared(const v8::Arguments& args);
+
+    private: static v8::Handle<v8::Value>
+        GetMessages(const v8::Arguments& args);
+
+    private: static v8::Handle<v8::Value>
+        LoadMaterialScripts(const v8::Arguments& args);
+
+    private: static v8::Handle<v8::Value>
+        GetIsGzServerConnected(const v8::Arguments& args);
+
+    private: static v8::Handle<v8::Value>
+        GetMaterialScriptsMessage(const v8::Arguments& args);
+
+    private: GazeboInterface* gzIface;
+    private: bool isGzServerConnected;
+
+  };
+}
+
+#endif
diff --git a/gzbridge/GazeboInterface.cc b/gzbridge/GazeboInterface.cc
new file mode 100644
index 0000000000000000000000000000000000000000..5ff64fbd9c31231289fe82cd8174b915c2017985
--- /dev/null
+++ b/gzbridge/GazeboInterface.cc
@@ -0,0 +1,1117 @@
+/*
+ * Copyright 2013 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+
+#include <boost/thread.hpp>
+
+#include "pb2json.hh"
+#include "OgreMaterialParser.hh"
+#include "GazeboInterface.hh"
+
+#define MAX_NUM_MSG_SIZE 1000
+
+using namespace gzweb;
+
+std::vector<std::string> GazeboInterface::incoming;
+std::vector<std::string> GazeboInterface::outgoing;
+
+boost::recursive_mutex incomingMutex;
+boost::recursive_mutex outgoingMutex;
+
+/////////////////////////////////////////////////
+GazeboInterface::GazeboInterface()
+{
+//  this->socketServer = _server;
+  this->receiveMutex = new boost::recursive_mutex();
+  this->serviceMutex = new boost::recursive_mutex();
+  this->stop = false;
+
+  // Create our node for communication
+  this->node.reset(new gazebo::transport::Node());
+  this->node->Init();
+
+  // Gazebo topics
+  this->sensorTopic = "~/sensor";
+  this->visualTopic = "~/visual";
+  this->jointTopic = "~/joint";
+  this->modelTopic = "~/model/info";
+  this->poseTopic = "~/pose/info";
+  this->requestTopic = "~/request";
+  this->lightTopic = "~/light";
+  this->linkTopic = "~/link";
+  this->sceneTopic = "~/scene";
+  this->physicsTopic = "~/physics";
+  this->modelModifyTopic = "~/model/modify";
+  this->factoryTopic = "~/factory";
+  this->worldControlTopic = "~/world_control";
+  this->statsTopic = "~/world_stats";
+  this->roadTopic = "~/roads";
+  this->heightmapService = "~/heightmap_data";
+  this->deleteTopic = "~/entity_delete";
+
+  // material topic
+  this->materialTopic = "~/material";
+
+  this->sensorSub = this->node->Subscribe(this->sensorTopic,
+      &GazeboInterface::OnSensorMsg, this, true);
+
+  this->visSub = this->node->Subscribe(this->visualTopic,
+      &GazeboInterface::OnVisualMsg, this);
+
+  this->jointSub = this->node->Subscribe(this->jointTopic,
+      &GazeboInterface::OnJointMsg, this);
+
+  // For entity creation
+  this->modelInfoSub = node->Subscribe(this->modelTopic,
+      &GazeboInterface::OnModelMsg, this);
+
+  // For entity update
+  this->poseSub = this->node->Subscribe(this->poseTopic,
+      &GazeboInterface::OnPoseMsg, this);
+
+  // For entity delete coming from the server side
+  this->requestSub = this->node->Subscribe(this->requestTopic,
+      &GazeboInterface::OnRequest, this);
+
+  // For lights
+  this->lightSub = this->node->Subscribe(this->lightTopic,
+      &GazeboInterface::OnLightMsg, this);
+
+  this->sceneSub = this->node->Subscribe(this->sceneTopic,
+      &GazeboInterface::OnScene, this);
+
+  this->physicsSub = this->node->Subscribe(this->physicsTopic,
+      &GazeboInterface::OnPhysicsMsg, this);
+
+  this->statsSub = this->node->Subscribe(this->statsTopic,
+      &GazeboInterface::OnStats, this);
+
+  this->roadSub = this->node->Subscribe(this->roadTopic,
+      &GazeboInterface::OnRoad, this, true);
+
+  // For getting scene info on connect
+  this->requestPub =
+      this->node->Advertise<gazebo::msgs::Request>(this->requestTopic);
+
+  // For modifying models
+  this->modelPub =
+      this->node->Advertise<gazebo::msgs::Model>(this->modelModifyTopic);
+
+  // For modifying lights
+  this->lightPub =
+      this->node->Advertise<gazebo::msgs::Light>(this->lightTopic);
+
+  // For spawning models
+  this->factoryPub =
+      this->node->Advertise<gazebo::msgs::Factory>(this->factoryTopic);
+
+  // For controling world
+  this->worldControlPub =
+      this->node->Advertise<gazebo::msgs::WorldControl>(
+      this->worldControlTopic);
+
+  this->responseSub = this->node->Subscribe("~/response",
+      &GazeboInterface::OnResponse, this);
+
+  this->materialParser = new OgreMaterialParser();
+
+  this->lastStatsTime = gazebo::common::Time::Zero;
+  this->lastPausedState = true;
+
+  // message filtering apparatus
+  this->minimumDistanceSquared = 0.0001;
+  this->minimumQuaternionSquared = 0.0001;
+  this->minimumMsgAge = 0.03;
+  this->skippedMsgCount = 0;
+  this->messageWindowSize = 10000;
+  this->messageCount = 0;
+
+  this->isConnected = false;
+
+  // initialize thread variables
+  this->runThread = NULL;
+  this->serviceThread = NULL;
+  this->connectionCondition = new boost::condition_variable();
+  this->connectionMutex = new boost::mutex();
+}
+
+/////////////////////////////////////////////////
+GazeboInterface::~GazeboInterface()
+{
+  this->Fini();
+  this->node->Fini();
+
+  this->modelMsgs.clear();
+  this->poseMsgs.clear();
+  this->requestMsgs.clear();
+  this->lightMsgs.clear();
+  this->visualMsgs.clear();
+  this->sceneMsgs.clear();
+  this->physicsMsgs.clear();
+  this->jointMsgs.clear();
+  this->sensorMsgs.clear();
+
+  this->sensorSub.reset();
+  this->visSub.reset();
+  this->lightSub.reset();
+  this->sceneSub.reset();
+  this->jointSub.reset();
+  this->modelInfoSub.reset();
+  this->requestPub.reset();
+  this->modelPub.reset();
+  this->lightPub.reset();
+  this->responseSub.reset();
+  this->node.reset();
+
+  if (this->runThread)
+  {
+    this->runThread->join();
+    delete this->runThread;
+  }
+  if (this->serviceThread)
+  {
+    this->serviceThread->join();
+    delete this->serviceThread;
+  }
+
+  delete this->receiveMutex;
+  delete this->serviceMutex;
+  delete this->connectionCondition;
+  delete this->connectionMutex;
+}
+
+/////////////////////////////////////////////////
+void GazeboInterface::Init()
+{
+  this->requestPub->WaitForConnection();
+}
+
+/////////////////////////////////////////////////
+void GazeboInterface::RunThread()
+{
+  this->runThread = new boost::thread(boost::bind(&GazeboInterface::Run, this));
+  this->serviceThread = new boost::thread(
+      boost::bind(&GazeboInterface::RunService, this));
+
+}
+
+/////////////////////////////////////////////////
+void GazeboInterface::Run()
+{
+  while (!this->stop)
+  {
+    this->WaitForConnection();
+    this->ProcessMessages();
+  }
+}
+
+//////////////////////////////////////////////////
+void GazeboInterface::RunService()
+{
+  while (!this->stop)
+  {
+    this->WaitForConnection();
+    this->ProcessServiceRequests();
+  }
+}
+
+/////////////////////////////////////////////////
+void GazeboInterface::Fini()
+{
+  this->stop = true;
+}
+
+/////////////////////////////////////////////////
+void GazeboInterface::ProcessMessages()
+{
+  static RequestMsgs_L::iterator rIter;
+  static SceneMsgs_L::iterator sIter;
+  static PhysicsMsgs_L::iterator physicsIter;
+  static WorldStatsMsgs_L::iterator wIter;
+  static ModelMsgs_L::iterator modelIter;
+  static VisualMsgs_L::iterator visualIter;
+  static LightMsgs_L::iterator lightIter;
+  static PoseMsgs_L::iterator pIter;
+  // static SkeletonPoseMsgs_L::iterator spIter;
+  static JointMsgs_L::iterator jointIter;
+  static SensorMsgs_L::iterator sensorIter;
+
+  {
+    boost::recursive_mutex::scoped_lock lock(*this->receiveMutex);
+
+    // Process incoming messages.
+    std::vector<std::string> msgs = this->PopIncomingMessages();
+
+    for (unsigned int i = 0; i < msgs.size(); ++i)
+    {
+      std::string msg = msgs[i];
+
+      std::string operation = get_value(msg, "op");
+      // ignore "advertise" messages (responsible for announcing the
+      // availability of topics) as we currently don't make use of them.
+      if (operation == "advertise")
+        continue;
+
+      std::string topic = get_value(msg.c_str(), "topic");
+
+      // Process subscribe requests
+      if (!topic.empty())
+      {
+        if (topic == this->sceneTopic)
+        {
+          gazebo::msgs::Request *requestMsg;
+          requestMsg = gazebo::msgs::CreateRequest("scene_info");
+          if (this->requests.find(requestMsg->id()) != this->requests.end())
+            requests.erase(requestMsg->id());
+          this->requests[requestMsg->id()] = requestMsg;
+          this->requestPub->Publish(*requestMsg);
+        }
+        else if (topic == this->physicsTopic)
+        {
+          gazebo::msgs::Request *requestPhysicsMsg;
+          requestPhysicsMsg = gazebo::msgs::CreateRequest("physics_info", "");
+          if (this->requests.find(requestPhysicsMsg->id()) != this->requests.end())
+            requests.erase(requestPhysicsMsg->id());
+          this->requests[requestPhysicsMsg->id()] = requestPhysicsMsg;
+          this->requestPub->Publish(*requestPhysicsMsg);
+        }
+        else if (topic == this->poseTopic)
+        {
+          // TODO we currently subscribe on init,
+          // should change logic so that  we subscribe only
+          // when we receive sub msgs
+        }
+        else if (topic == this->modelModifyTopic)
+        {
+          std::string name = get_value(msg, "msg:name");
+          int id = atoi(get_value(msg, "msg:id").c_str());
+
+          if (name == "")
+            continue;
+
+          gazebo::math::Vector3 pos(
+            atof(get_value(msg, "msg:position:x").c_str()),
+            atof(get_value(msg, "msg:position:y").c_str()),
+            atof(get_value(msg, "msg:position:z").c_str()));
+          gazebo::math::Quaternion quat(
+            atof(get_value(msg, "msg:orientation:w").c_str()),
+            atof(get_value(msg, "msg:orientation:x").c_str()),
+            atof(get_value(msg, "msg:orientation:y").c_str()),
+            atof(get_value(msg, "msg:orientation:z").c_str()));
+          gazebo::math::Pose pose(pos, quat);
+
+          gazebo::msgs::Model modelMsg;
+          modelMsg.set_id(id);
+          modelMsg.set_name(name);
+          gazebo::msgs::Set(modelMsg.mutable_pose(), pose);
+
+          this->modelPub->Publish(modelMsg);
+        }
+        else if (topic == this->lightTopic)
+        {
+          std::string name = get_value(msg, "msg:name");
+          std::string type = get_value(msg, "msg:type");
+          // createEntity = 1: create new light
+          // createEntity = 0: modify existing light
+          std::string createEntity = get_value(msg, "msg:createEntity");
+
+          if (name == "")
+            continue;
+
+          gazebo::msgs::Light lightMsg;
+          lightMsg.set_name(name);
+
+          gazebo::math::Vector3 pos(
+              atof(get_value(msg, "msg:position:x").c_str()),
+              atof(get_value(msg, "msg:position:y").c_str()),
+              atof(get_value(msg, "msg:position:z").c_str()));
+          gazebo::math::Quaternion quat(
+              atof(get_value(msg, "msg:orientation:w").c_str()),
+              atof(get_value(msg, "msg:orientation:x").c_str()),
+              atof(get_value(msg, "msg:orientation:y").c_str()),
+              atof(get_value(msg, "msg:orientation:z").c_str()));
+          gazebo::math::Pose pose(pos, quat);
+          gazebo::msgs::Set(lightMsg.mutable_pose(), pose);
+
+          if (createEntity.compare("0") == 0)
+          {
+            gazebo::math::Vector3 direction(
+              atof(get_value(msg, "msg:direction:x").c_str()),
+              atof(get_value(msg, "msg:direction:y").c_str()),
+              atof(get_value(msg, "msg:direction:z").c_str()));
+            gazebo::msgs::Set(lightMsg.mutable_direction(), direction);
+
+            gazebo::common::Color diffuse(
+                atof(get_value(msg, "msg:diffuse:r").c_str()),
+                atof(get_value(msg, "msg:diffuse:g").c_str()),
+                atof(get_value(msg, "msg:diffuse:b").c_str()), 1);
+            gazebo::msgs::Set(lightMsg.mutable_diffuse(), diffuse);
+
+            gazebo::common::Color specular(
+                atof(get_value(msg, "msg:specular:r").c_str()),
+                atof(get_value(msg, "msg:specular:g").c_str()),
+                atof(get_value(msg, "msg:specular:b").c_str()), 1);
+            gazebo::msgs::Set(lightMsg.mutable_specular(), specular);
+
+            lightMsg.set_range(atof(get_value(msg, "msg:range").c_str()));
+            lightMsg.set_attenuation_constant(atof(
+                get_value(msg, "msg:attenuation_constant").c_str()));
+            lightMsg.set_attenuation_linear(atof(
+                get_value(msg, "msg:attenuation_linear").c_str()));
+            lightMsg.set_attenuation_quadratic(atof(
+                get_value(msg, "msg:attenuation_quadratic").c_str()));
+          }
+          else
+          {
+            if (type.compare("pointlight") == 0)
+            {
+              lightMsg.set_type(gazebo::msgs::Light::POINT);
+            }
+            else if (type.compare("spotlight") == 0)
+            {
+              lightMsg.set_type(gazebo::msgs::Light::SPOT);
+              gazebo::msgs::Set(lightMsg.mutable_direction(),
+                  gazebo::math::Vector3(0,0,-1));
+            }
+            else if (type.compare("directionallight") == 0)
+            {
+              lightMsg.set_type(gazebo::msgs::Light::DIRECTIONAL);
+              gazebo::msgs::Set(lightMsg.mutable_direction(),
+                  gazebo::math::Vector3(0,0,-1));
+            }
+
+            gazebo::msgs::Set(lightMsg.mutable_diffuse(),
+                gazebo::common::Color(0.5, 0.5, 0.5, 1));
+            gazebo::msgs::Set(lightMsg.mutable_specular(),
+                gazebo::common::Color(0.1, 0.1, 0.1, 1));
+            lightMsg.set_attenuation_constant(0.5);
+            lightMsg.set_attenuation_linear(0.01);
+            lightMsg.set_attenuation_quadratic(0.001);
+            lightMsg.set_range(20);
+          }
+
+          this->lightPub->Publish(lightMsg);
+        }
+        else if (topic == this->linkTopic)
+        {
+          std::string modelName = get_value(msg, "msg:name");
+          int modelId = atoi(get_value(msg, "msg:id").c_str());
+
+          std::string linkName = get_value(msg, "msg:link:name");
+          int linkId = atoi(get_value(msg, "msg:link:id").c_str());
+
+          if (modelName == "" || linkName == "")
+            continue;
+
+          gazebo::msgs::Model modelMsg;
+          modelMsg.set_id(modelId);
+          modelMsg.set_name(modelName);
+
+          gazebo::msgs::Link *linkMsg = modelMsg.add_link();
+          linkMsg->set_id(linkId);
+
+          size_t index = linkName.find_last_of("::");
+          if (index != std::string::npos)
+              linkName = linkName.substr(index+1);
+          linkMsg->set_name(linkName);
+
+          std::string self_collideStr = get_value(msg, "msg:link:self_collide").c_str();
+          bool self_collide = false;
+          if (self_collideStr == "1")
+          {
+            self_collide = true;
+          }
+          linkMsg->set_self_collide(self_collide);
+
+          std::string gravityStr = get_value(msg, "msg:link:gravity").c_str();
+          bool gravity = false;
+          if (gravityStr == "1")
+          {
+            gravity = true;
+          }
+          linkMsg->set_gravity(gravity);
+
+          std::string kinematicStr = get_value(msg, "msg:link:kinematic").c_str();
+          bool kinematic = false;
+          if (kinematicStr == "1")
+          {
+            kinematic = true;
+          }
+          linkMsg->set_kinematic(kinematic);
+
+          this->modelPub->Publish(modelMsg);
+        }
+        else if (topic == this->factoryTopic)
+        {
+          gazebo::msgs::Factory factoryMsg;
+          std::stringstream newModelStr;
+
+          std::string name = get_value(msg, "msg:name");
+          std::string type = get_value(msg, "msg:type");
+
+          gazebo::math::Vector3 pos(
+              atof(get_value(msg, "msg:position:x").c_str()),
+              atof(get_value(msg, "msg:position:y").c_str()),
+              atof(get_value(msg, "msg:position:z").c_str()));
+          gazebo::math::Quaternion quat(
+              atof(get_value(msg, "msg:orientation:w").c_str()),
+              atof(get_value(msg, "msg:orientation:x").c_str()),
+              atof(get_value(msg, "msg:orientation:y").c_str()),
+              atof(get_value(msg, "msg:orientation:z").c_str()));
+          gazebo::math::Vector3 rpy = quat.GetAsEuler();
+
+          if(type == "box" || type == "sphere" || type == "cylinder")
+          {
+            std::stringstream geom;
+            if (type == "box")
+            {
+              geom  << "<box>"
+                    <<   "<size>1.0 1.0 1.0</size>"
+                    << "</box>";
+            }
+            else if (type == "sphere")
+            {
+              geom  << "<sphere>"
+                    <<   "<radius>0.5</radius>"
+                    << "</sphere>";
+            }
+            else if (type == "cylinder")
+            {
+              geom  << "<cylinder>"
+                    <<   "<radius>0.5</radius>"
+                    <<   "<length>1.0</length>"
+                    << "</cylinder>";
+            }
+
+            newModelStr << "<sdf version ='" << SDF_VERSION << "'>"
+                << "<model name='" << name << "'>"
+                << "<pose>" << pos.x << " " << pos.y << " " << pos.z << " "
+                            << rpy.x << " " << rpy.y << " " << rpy.z << "</pose>"
+                << "<link name ='link'>"
+                <<   "<inertial><mass>1.0</mass></inertial>"
+                <<   "<collision name ='collision'>"
+                <<     "<geometry>"
+                <<        geom.str()
+                <<     "</geometry>"
+                << "</collision>"
+                << "<visual name ='visual'>"
+                <<     "<geometry>"
+                <<        geom.str()
+                <<     "</geometry>"
+                <<     "<material>"
+                <<       "<script>"
+                <<         "<uri>file://media/materials/scripts/gazebo.material"
+                <<         "</uri>"
+                <<         "<name>Gazebo/Grey</name>"
+                <<       "</script>"
+                <<     "</material>"
+                <<   "</visual>"
+                << "</link>"
+                << "</model>"
+                << "</sdf>";
+          }
+          else
+          {
+            newModelStr << "<sdf version ='" << SDF_VERSION << "'>"
+                  << "<model name='" << name << "'>"
+                  << "  <pose>" << pos.x << " " << pos.y << " "
+                                << pos.z << " " << rpy.x << " "
+                                << rpy.y << " " << rpy.z << "</pose>"
+                  << "  <include>"
+                  << "    <uri>model://" << type << "</uri>"
+                  << "  </include>"
+                  << "</model>"
+                  << "</sdf>";
+          }
+
+          // Spawn the model in the physics server
+          factoryMsg.set_sdf(newModelStr.str());
+          this->factoryPub->Publish(factoryMsg);
+        }
+        else if (topic == this->worldControlTopic)
+        {
+          gazebo::msgs::WorldControl worldControlMsg;
+          std::string pause = get_value(msg, "msg:pause");
+          std::string reset = get_value(msg, "msg:reset");
+          if (!pause.empty())
+          {
+            int pauseValue = atoi(pause.c_str());
+            worldControlMsg.set_pause(pauseValue);
+          }
+          if (!reset.empty())
+          {
+            if (reset == "model")
+            {
+              worldControlMsg.mutable_reset()->set_all(false);
+              worldControlMsg.mutable_reset()->set_time_only(false);
+              worldControlMsg.mutable_reset()->set_model_only(true);
+            }
+            else if (reset == "world")
+            {
+              worldControlMsg.mutable_reset()->set_all(true);
+
+            }
+          }
+          if (!pause.empty() || !reset.empty())
+            this->worldControlPub->Publish(worldControlMsg);
+        }
+        else if (topic == this->materialTopic)
+        {
+
+          if (this->materialParser)
+          {
+            std::string msg =
+                this->PackOutgoingTopicMsg(this->materialTopic,
+                this->materialParser->GetMaterialAsJson());
+            this->Send(msg);
+          }
+        }
+        else if (topic == this->deleteTopic)
+        {
+            std::string name = get_value(msg, "msg:name");
+            gazebo::transport::requestNoReply(this->node, "entity_delete", name);
+        }
+      }
+      else
+      {
+        // store service calls for processing later
+        std::string service = get_value(msg.c_str(), "service");
+        if (!service.empty())
+        {
+          boost::recursive_mutex::scoped_lock lock(*this->serviceMutex);
+          this->serviceRequests.push_back(msg);
+        }
+      }
+
+    }
+
+    std::string msg = "";
+    // Forward the scene messages.
+    for (sIter = this->sceneMsgs.begin(); sIter != this->sceneMsgs.end();
+        ++sIter)
+    {
+      msg = this->PackOutgoingTopicMsg(this->sceneTopic,
+          pb2json(*(*sIter).get()));
+      this->Send(msg);
+    }
+    this->sceneMsgs.clear();
+
+    // Forward the physics messages.
+    for (physicsIter = this->physicsMsgs.begin();
+        physicsIter != this->physicsMsgs.end(); ++physicsIter)
+    {
+      msg = this->PackOutgoingTopicMsg(this->physicsTopic,
+          pb2json(*(*physicsIter).get()));
+      this->Send(msg);
+    }
+    this->physicsMsgs.clear();
+
+    // Forward the model messages.
+    for (modelIter = this->modelMsgs.begin();
+        modelIter != this->modelMsgs.end(); ++modelIter)
+    {
+      msg = this->PackOutgoingTopicMsg(this->modelTopic,
+          pb2json(*(*modelIter).get()));
+      this->Send(msg);
+    }
+    this->modelMsgs.clear();
+
+    // Forward the sensor messages.
+    for (sensorIter = this->sensorMsgs.begin();
+        sensorIter != this->sensorMsgs.end(); ++sensorIter)
+    {
+      msg = this->PackOutgoingTopicMsg(this->sensorTopic,
+          pb2json(*(*sensorIter).get()));
+      this->Send(msg);
+    }
+    this->sensorMsgs.clear();
+
+    // Forward the light messages.
+    for (lightIter = this->lightMsgs.begin();
+        lightIter != this->lightMsgs.end(); ++lightIter)
+    {
+      msg = this->PackOutgoingTopicMsg(this->lightTopic,
+          pb2json(*(*lightIter).get()));
+      this->Send(msg);
+    }
+    this->lightMsgs.clear();
+
+    // Forward the visual messages.
+    for (visualIter = this->visualMsgs.begin();
+        visualIter != this->visualMsgs.end(); ++visualIter)
+    {
+      msg = this->PackOutgoingTopicMsg(this->visualTopic,
+          pb2json(*(*visualIter).get()));
+      this->Send(msg);
+      std::cerr << msg << std::endl;
+    }
+    this->visualMsgs.clear();
+
+    // Forward the joint messages.
+    for (jointIter = this->jointMsgs.begin();
+        jointIter != this->jointMsgs.end(); ++jointIter)
+    {
+      msg = this->PackOutgoingTopicMsg(this->jointTopic,
+          pb2json(*(*jointIter).get()));
+      this->Send(msg);
+    }
+    this->jointMsgs.clear();
+
+    // Forward the request messages
+    for (rIter =  this->requestMsgs.begin(); rIter != this->requestMsgs.end();
+        ++rIter)
+    {
+      msg = this->PackOutgoingTopicMsg(this->requestTopic,
+          pb2json(*(*rIter).get()));
+      this->Send(msg);
+    }
+    this->requestMsgs.clear();
+
+    // Forward the stats messages.
+    for (wIter = this->statsMsgs.begin(); wIter != this->statsMsgs.end();
+        ++wIter)
+    {
+      msg = this->PackOutgoingTopicMsg(this->statsTopic,
+          pb2json(*(*wIter).get()));
+      this->Send(msg);
+    }
+    this->statsMsgs.clear();
+
+    // Forward all the pose messages.
+    pIter = this->poseMsgs.begin();
+    while (pIter != this->poseMsgs.end())
+    {
+      msg = this->PackOutgoingTopicMsg(this->poseTopic,
+          pb2json(*pIter));
+      this->Send(msg);
+      ++pIter;
+    }
+    this->poseMsgs.clear();
+  }
+}
+
+/////////////////////////////////////////////////
+void GazeboInterface::ProcessServiceRequests()
+{
+  std::vector<std::string> services;
+  {
+    boost::recursive_mutex::scoped_lock lock(*this->serviceMutex);
+    services = this->serviceRequests;
+    this->serviceRequests.clear();
+  }
+
+  // process service request outside lock otherwise somehow it deadlocks
+  for (unsigned int i = 0; i < services.size(); ++i)
+  {
+    std::string request = services[i];
+    std::string service = get_value(request.c_str(), "service");
+    std::string id = get_value(request.c_str(), "id");
+    std::string name = get_value(request.c_str(), "args");
+    if (service == this->heightmapService)
+    {
+      boost::shared_ptr<gazebo::msgs::Response> response
+          = gazebo::transport::request(name, "heightmap_data");
+      gazebo::msgs::Geometry geomMsg;
+      if (response->response() != "error" &&
+          response->type() == geomMsg.GetTypeName())
+      {
+        geomMsg.ParseFromString(response->serialized_data());
+
+        std::string msg = this->PackOutgoingServiceMsg(id,
+            pb2json(geomMsg));
+        this->Send(msg);
+      }
+    }
+    else if (service == this->roadTopic)
+    {
+      if (!this->roadMsgs.empty())
+      {
+        std::string msg = this->PackOutgoingServiceMsg(id,
+            pb2json(*roadMsgs.front().get()));
+        this->Send(msg);
+      }
+    }
+  }
+}
+
+/////////////////////////////////////////////////
+void GazeboInterface::OnModelMsg(ConstModelPtr &_msg)
+{
+  if (!this->IsConnected())
+    return;
+
+  boost::recursive_mutex::scoped_lock lock(*this->receiveMutex);
+  this->modelMsgs.push_back(_msg);
+}
+
+/////////////////////////////////////////////////
+bool GazeboInterface::FilterPoses(const TimedPose &_old,
+    const TimedPose &_current)
+{
+  if (this->messageCount >= this->messageWindowSize)
+  {
+    // double ratio =  100.0 * this->skippedMsgCount  / this->messageWindowSize;
+    // std::cout << "Message filter: " << ratio << " %" << std::endl;
+    // std::cout << "Message count : " << this->skippedMsgCount;
+    // std::cout << " "  << << std::endl;
+    this->skippedMsgCount = 0;
+    this->messageCount = 0;
+  }
+  this->messageCount++;
+
+  bool hasMoved = false;
+  bool isTooEarly = false;
+  bool hasRotated = false;
+
+  gazebo::common::Time mininumTimeElapsed(this->minimumMsgAge);
+
+  gazebo::common::Time timeDifference =  _current.first - _old.first;
+
+  // checking > 0 because world may have been reset
+  if (timeDifference < mininumTimeElapsed && timeDifference.Double() > 0)
+  {
+    isTooEarly = true;
+  }
+
+  gazebo::math::Vector3 posDiff = _current.second.pos - _old.second.pos;
+  double translationSquared = posDiff.GetSquaredLength();
+  if (translationSquared > minimumDistanceSquared)
+  {
+    hasMoved = true;
+  }
+
+  gazebo::math::Quaternion i = _current.second.rot.GetInverse();
+  gazebo::math::Quaternion qDiff =  i * _old.second.rot;
+
+  gazebo::math::Vector3 d(qDiff.x, qDiff.y, qDiff.z);
+  double rotation = d.GetSquaredLength();
+  if (rotation > minimumQuaternionSquared)
+  {
+    hasRotated = true;
+  }
+
+  if (isTooEarly)
+  {
+    this->skippedMsgCount++;
+    return true;
+  }
+
+  if ((hasMoved == false) && (hasRotated == false))
+  {
+    this->skippedMsgCount++;
+    return true;
+  }
+
+    return false;
+}
+
+/////////////////////////////////////////////////
+void GazeboInterface::OnPoseMsg(ConstPosesStampedPtr &_msg)
+{
+  if (!this->IsConnected())
+    return;
+
+  boost::recursive_mutex::scoped_lock lock(*this->receiveMutex);
+  PoseMsgs_L::iterator iter;
+
+  for (int i = 0; i < _msg->pose_size(); ++i)
+  {
+    // Find an old model message, and remove them
+    for (iter = this->poseMsgs.begin(); iter != this->poseMsgs.end(); ++iter)
+    {
+      if ((*iter).name() == _msg->pose(i).name())
+      {
+        this->poseMsgs.erase(iter);
+        break;
+      }
+    }
+    bool filtered = false;
+
+    std::string name = _msg->pose(i).name();
+
+    gazebo::math::Pose pose = gazebo::msgs::Convert(_msg->pose(i));
+    gazebo::common::Time time = gazebo::msgs::Convert(_msg->time());
+
+    PoseMsgsFilter_M::iterator it = this->poseMsgsFilterMap.find(name);
+
+    TimedPose currentPose;
+    currentPose.first = time;
+    currentPose.second = pose;
+
+    if (it == this->poseMsgsFilterMap.end())
+    {
+      std::pair<PoseMsgsFilter_M::iterator, bool> r;
+      r = this->poseMsgsFilterMap.insert(make_pair(name, currentPose));
+    }
+    else
+    {
+      TimedPose oldPose = it->second;
+      filtered = FilterPoses(oldPose, currentPose);
+      if (!filtered)
+      {
+        // update the map
+        it->second.first = currentPose.first;
+        it->second.second = currentPose.second;
+        this->poseMsgs.push_back(_msg->pose(i));
+      }
+    }
+  }
+  std::cout.flush();
+}
+
+/////////////////////////////////////////////////
+void GazeboInterface::OnRequest(ConstRequestPtr &_msg)
+{
+  if (!this->IsConnected())
+    return;
+
+  boost::recursive_mutex::scoped_lock lock(*this->receiveMutex);
+  this->requestMsgs.push_back(_msg);
+}
+
+/////////////////////////////////////////////////
+void GazeboInterface::OnResponse(ConstResponsePtr &_msg)
+{
+  if (!this->IsConnected())
+    return;
+
+  if (this->requests.find(_msg->id()) == this->requests.end())
+    return;
+
+  if (_msg->has_type() && _msg->type() == "gazebo.msgs.Scene")
+  {
+    gazebo::msgs::Scene sceneMsg;
+    sceneMsg.ParseFromString(_msg->serialized_data());
+    boost::shared_ptr<gazebo::msgs::Scene> sm(new gazebo::msgs::Scene(sceneMsg));
+    this->sceneMsgs.push_back(sm);
+    this->requests.erase(_msg->id());
+  }
+  else if (_msg->has_type() && _msg->type() == "gazebo.msgs.Physics")
+  {
+    gazebo::msgs::Physics physicsMsg;
+    physicsMsg.ParseFromString(_msg->serialized_data());
+    boost::shared_ptr<gazebo::msgs::Physics> pm(new gazebo::msgs::Physics(physicsMsg));
+    this->physicsMsgs.push_back(pm);
+    this->requests.erase(_msg->id());
+  }
+}
+
+/////////////////////////////////////////////////
+void GazeboInterface::OnLightMsg(ConstLightPtr &_msg)
+{
+  if (!this->IsConnected())
+    return;
+
+  boost::recursive_mutex::scoped_lock lock(*this->receiveMutex);
+  this->lightMsgs.push_back(_msg);
+}
+
+/////////////////////////////////////////////////
+void GazeboInterface::OnScene(ConstScenePtr &_msg)
+{
+  if (!this->IsConnected())
+    return;
+
+  boost::recursive_mutex::scoped_lock lock(*this->receiveMutex);
+  this->sceneMsgs.push_back(_msg);
+}
+
+/////////////////////////////////////////////////
+void GazeboInterface::OnPhysicsMsg(ConstPhysicsPtr &_msg)
+{
+  if (!this->IsConnected())
+    return;
+
+  boost::recursive_mutex::scoped_lock lock(*this->receiveMutex);
+  this->physicsMsgs.push_back(_msg);
+}
+
+/////////////////////////////////////////////////
+void GazeboInterface::OnStats(ConstWorldStatisticsPtr &_msg)
+{
+  if (!this->IsConnected())
+    return;
+
+  gazebo::common::Time wallTime;
+  wallTime = gazebo::msgs::Convert(_msg->real_time());
+  bool paused = _msg->paused();
+
+  // pub at 1Hz, but force pub if world state changes
+  if (((wallTime - this->lastStatsTime).Double() >= 1.0) ||
+      wallTime < this->lastStatsTime ||
+      this->lastPausedState != paused)
+  {
+    this->lastStatsTime = wallTime;
+    this->lastPausedState = paused;
+    boost::recursive_mutex::scoped_lock lock(*this->receiveMutex);
+    this->statsMsgs.push_back(_msg);
+  }
+}
+
+/////////////////////////////////////////////////
+void GazeboInterface::OnRoad(ConstRoadPtr &_msg)
+{
+  if (!this->IsConnected())
+    return;
+
+  boost::recursive_mutex::scoped_lock lock(*this->receiveMutex);
+  this->roadMsgs.push_back(_msg);
+}
+
+/////////////////////////////////////////////////
+void GazeboInterface::OnJointMsg(ConstJointPtr &_msg)
+{
+  if (!this->IsConnected())
+    return;
+
+  boost::recursive_mutex::scoped_lock lock(*this->receiveMutex);
+  this->jointMsgs.push_back(_msg);
+}
+
+/////////////////////////////////////////////////
+void GazeboInterface::OnSensorMsg(ConstSensorPtr &_msg)
+{
+  if (!this->IsConnected())
+    return;
+
+  boost::recursive_mutex::scoped_lock lock(*this->receiveMutex);
+  this->sensorMsgs.push_back(_msg);
+}
+
+/////////////////////////////////////////////////
+void GazeboInterface::OnVisualMsg(ConstVisualPtr &_msg)
+{
+  if (!this->IsConnected())
+    return;
+
+  boost::recursive_mutex::scoped_lock lock(*this->receiveMutex);
+  this->visualMsgs.push_back(_msg);
+}
+
+/////////////////////////////////////////////////
+std::string GazeboInterface::PackOutgoingTopicMsg(const std::string &_topic,
+    const std::string &_msg)
+{
+  // Use roslibjs format for now.
+  std::string out;
+  out += "{\"op\":\"publish\",\"topic\":\"" + _topic + "\", \"msg\":";
+  out += _msg;
+  out += "}";
+  return out;
+}
+
+/////////////////////////////////////////////////
+std::string GazeboInterface::PackOutgoingServiceMsg(const std::string &_id,
+    const std::string &_msg)
+{
+  // Use roslibjs format for now.
+  std::string out;
+  out += "{\"op\":\"service_response\",\"id\":\"" + _id + "\", \"values\":";
+  out += _msg;
+  out += "}";
+  return out;
+}
+
+/////////////////////////////////////////////////
+void GazeboInterface::Send(const std::string &_msg)
+{
+//  if (this->socketServer)
+//    this->socketServer->Write(_msg);
+  boost::recursive_mutex::scoped_lock lock(outgoingMutex);
+  if (outgoing.size() < MAX_NUM_MSG_SIZE)
+    outgoing.push_back(_msg);
+}
+
+/*
+/////////////////////////////////////////////////
+void GazeboInterface::Write(const std::string &_msg)
+{
+  boost::recursive_mutex::scoped_lock lock(outgoingMutex);
+  if (outgoing.size() < MAX_NUM_MSG_SIZE)
+    outgoing.push_back(_msg);
+}*/
+
+/////////////////////////////////////////////////
+std::vector<std::string> GazeboInterface::PopIncomingMessages()
+{
+  boost::recursive_mutex::scoped_lock lock(incomingMutex);
+  std::vector<std::string> in = incoming;
+  incoming.clear();
+  return in;
+}
+
+/////////////////////////////////////////////////
+void GazeboInterface::ClearIncomingMessages()
+{
+  boost::recursive_mutex::scoped_lock lock(incomingMutex);
+  incoming.clear();
+}
+
+/////////////////////////////////////////////////
+std::vector<std::string> GazeboInterface::PopOutgoingMessages()
+{
+  boost::recursive_mutex::scoped_lock lock(outgoingMutex);
+  std::vector<std::string> out = outgoing;
+  outgoing.clear();
+  return out;
+}
+
+/////////////////////////////////////////////////
+void GazeboInterface::ClearOutgoingMessages()
+{
+  boost::recursive_mutex::scoped_lock lock(outgoingMutex);
+  outgoing.clear();
+}
+
+/////////////////////////////////////////////////
+void GazeboInterface::PushRequest(const std::string &_msg)
+{
+  boost::recursive_mutex::scoped_lock lock(incomingMutex);
+  if (incoming.size() < MAX_NUM_MSG_SIZE)
+    incoming.push_back(_msg);
+}
+
+/////////////////////////////////////////////////
+void GazeboInterface::LoadMaterialScripts(const std::string &_path)
+{
+  if (this->materialParser)
+    this->materialParser->Load(_path);
+}
+
+/////////////////////////////////////////////////
+void GazeboInterface::WaitForConnection() const
+{
+  boost::mutex::scoped_lock lock(*this->connectionMutex);
+  while (!this->isConnected)
+  {
+    this->connectionCondition->wait(lock);
+  }
+}
+
+/////////////////////////////////////////////////
+void GazeboInterface::SetConnected(bool _connected)
+{
+  boost::mutex::scoped_lock lock(*this->connectionMutex);
+  this->isConnected = _connected;
+  this->connectionCondition->notify_all();
+}
+
+/////////////////////////////////////////////////
+bool GazeboInterface::IsConnected() const
+{
+  boost::mutex::scoped_lock lock(*this->connectionMutex);
+  return this->isConnected;
+}
diff --git a/gzbridge/GazeboInterface.hh b/gzbridge/GazeboInterface.hh
new file mode 100644
index 0000000000000000000000000000000000000000..9a6f0b630bf94a5278a9fd74e22dc8b29971a500
--- /dev/null
+++ b/gzbridge/GazeboInterface.hh
@@ -0,0 +1,465 @@
+/*
+ * Copyright 2013 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+
+#ifndef _GAZEBO_INTERFACE_HH_
+#define _GAZEBO_INTERFACE_HH_
+
+#include <string>
+#include <list>
+#include <map>
+
+#include <gazebo/msgs/msgs.hh>
+#include <gazebo/transport/TransportIface.hh>
+
+namespace boost
+{
+  class thread;
+}
+
+namespace gzweb
+{
+  class OgreMaterialParser;
+
+  class GazeboInterface
+  {
+    /// \brief Constructor.
+    /// \param[in] _server Websocket server.
+    public: GazeboInterface();
+
+    /// \brief Destructor.
+    public: virtual ~GazeboInterface();
+
+    /// \brief Initialize gazebo interface.
+    public: void Init();
+
+    /// \brief Run the gazebo interface in a thread.
+    public: void RunThread();
+
+    /// \brief Stop the gazebo interace.
+    public: void Fini();
+
+    /// \brief Push a request message to incoming messages buffer.
+    /// \return Request message.
+    public: void PushRequest(const std::string &_request);
+
+    /// \brief Get incoming messages.
+    /// \return Incoming messages.
+    public: std::vector<std::string> PopIncomingMessages();
+
+    /// \brief Clear incoming messages
+    public: void ClearIncomingMessages();
+
+    /// \brief Get outgoing messages.
+    /// \return Outgoing messages.
+    public: std::vector<std::string> PopOutgoingMessages();
+
+    /// \brief Clear outgoing messages
+    public: void ClearOutgoingMessages();
+
+    /// \brief Receive message from websocket server.
+    /// \param[in] _msg Message received.
+    public: void Receive(const std::string &_msg);
+
+    /// \brief Load material scripts.
+    /// \param[in] _path Path to the material scripts.
+    public: void LoadMaterialScripts(const std::string &_path);
+
+    /// \brief Set the connected state
+    /// \param[in] _connected True if there are client connections.
+    public: void SetConnected(bool _connected);
+
+    /// \brief Get the connected state
+    /// \return True if there are client connections.
+    public: bool IsConnected() const;
+
+    /// \brief Pack topic publish message.
+    private: std::string PackOutgoingTopicMsg(const std::string &_topic,
+        const std::string &_msg);
+
+    /// \brief Pack service response message.
+    private: std::string PackOutgoingServiceMsg(const std::string &_id,
+        const std::string &_msg);
+
+    /// \brief Send message through websocket server.
+    /// \param[in] _msg Message to be sent.
+    private: void Send(const std::string &_msg);
+
+    /// \brief Run the gazebo interface.
+    private: void Run();
+
+    /// \brief Run the gazebo service handling loop.
+    private: void RunService();
+
+    /// \brief Process the messages.
+    private: void ProcessMessages();
+
+    /// \brief Process the service requests.
+    private: void ProcessServiceRequests();
+
+    /// \brief Model message callback.
+    /// \param[in] _msg The message data.
+    private: void OnModelMsg(ConstModelPtr &_msg);
+
+    /// \brief Pose message callback.
+    /// \param[in] _msg The message data.
+    private: void OnPoseMsg(ConstPosesStampedPtr &_msg);
+
+    /// \brief Request callback
+    /// \param[in] _msg The message data.
+    private: void OnRequest(ConstRequestPtr &_msg);
+
+    /// \brief Light message callback.
+    /// \param[in] _msg The message data.
+    private: void OnLightMsg(ConstLightPtr &_msg);
+
+    /// \brief Joint message callback.
+    /// \param[in] _msg The message data.
+    private: void OnJointMsg(ConstJointPtr &_msg);
+
+    /// \brief Scene message callback.
+    /// \param[in] _msg The message data.
+    private: void OnSensorMsg(ConstSensorPtr &_msg);
+
+    /// \brief Visual message callback.
+    /// \param[in] _msg The message data.
+    private: void OnVisualMsg(ConstVisualPtr &_msg);
+
+    /// \brief Called when a scene message is received on the
+    /// ~/scene topic
+    /// \param[in] _msg The message.
+    private: void OnScene(ConstScenePtr &_msg);
+
+    /// \brief Physics message callback.
+    /// \param[in] _msg The message data.
+    private: void OnPhysicsMsg(ConstPhysicsPtr &_msg);
+
+    /// \brief World stats message callback.
+    /// \param[in] _msg The message.
+    private: void OnStats(ConstWorldStatisticsPtr &_msg);
+
+    /// \brief Road message callback.
+    /// \param[in] _msg The message.
+    private: void OnRoad(ConstRoadPtr &_msg);
+
+    /// \brief Response callback
+    /// \param[in] _msg The message data.
+    private: void OnResponse(ConstResponsePtr &_msg);
+
+    /// \brief Block if there are no connections.
+    private: void WaitForConnection() const;
+
+    /// \brief a pose at a specific time
+    typedef std::pair<gazebo::common::Time, gazebo::math::Pose > TimedPose;
+
+    /// \brief True if the message is to be ignored because it is either
+    /// too old, or too similar
+    /// \param[in] _previous The previous pose
+    /// \param[in] _current The latest pose
+    private: bool FilterPoses(const TimedPose &_previous,
+        const TimedPose &_current);
+
+    /// \brief Incoming messages.
+    public: static std::vector<std::string> incoming;
+
+    /// \brief Outgoing messages.
+    public: static std::vector<std::string> outgoing;
+
+    /// \brief Thread to run the main loop.
+    private: boost::thread *runThread;
+
+    /// \brief Thread for processing services requests.
+    private: boost::thread *serviceThread;
+
+    /// \brief Gazebo transport node.
+    private: gazebo::transport::NodePtr node;
+
+    /// \brief Subscribe to model info updates
+    private: gazebo::transport::SubscriberPtr modelInfoSub;
+
+    /// \brief Subscribe to pose updates
+    private: gazebo::transport::SubscriberPtr poseSub;
+
+    /// \brief Subscribe to the request topic
+    private: gazebo::transport::SubscriberPtr requestSub;
+
+    /// \brief Subscribe to light topics
+    private: gazebo::transport::SubscriberPtr lightSub;
+
+    /// \brief Subscribe to sensor topic
+    private: gazebo::transport::SubscriberPtr sensorSub;
+
+    /// \brief Subscribe to scene topic
+    private: gazebo::transport::SubscriberPtr sceneSub;
+
+    /// \brief Subscribe to physics topic
+    private: gazebo::transport::SubscriberPtr physicsSub;
+
+    /// \brief Subscribe to world stats topic
+    private: gazebo::transport::SubscriberPtr statsSub;
+
+    /// \brief Subscribe to visual topic
+    private: gazebo::transport::SubscriberPtr visSub;
+
+    /// \brief Subscribe to joint updates.
+    private: gazebo::transport::SubscriberPtr jointSub;
+
+    /// \brief Subscribe to road msgs.
+    private: gazebo::transport::SubscriberPtr roadSub;
+
+    /// \brief Publish requests
+    private: gazebo::transport::PublisherPtr requestPub;
+
+    /// \brief Publish model modify messages
+    private: gazebo::transport::PublisherPtr modelPub;
+
+    /// \brief Publish light modify messages
+    private: gazebo::transport::PublisherPtr lightPub;
+
+    /// \brief Publish factory messages
+    private: gazebo::transport::PublisherPtr factoryPub;
+
+    /// \brief Publish world control messages
+    private: gazebo::transport::PublisherPtr worldControlPub;
+
+    /// \brief Subscribe to reponses.
+    private: gazebo::transport::SubscriberPtr responseSub;
+
+    /// \brief Request message for getting initial scene info.
+    private: std::map<int, gazebo::msgs::Request *> requests;
+
+    /// \brief Mutex to lock the various message buffers.
+    private: boost::recursive_mutex *receiveMutex;
+
+    /// \brief Mutex to lock the service request buffer.
+    private: boost::recursive_mutex *serviceMutex;
+
+    /// \brief Mutex to protect the isConnected variable.
+    private: boost::mutex *connectionMutex;
+
+    /// \brief The condition to notify when connection state changes.
+    public: boost::condition_variable *connectionCondition;
+
+    /// \def ModelMsgs_L
+    /// \brief List of model messages.
+    typedef std::list<boost::shared_ptr<gazebo::msgs::Model const> >
+        ModelMsgs_L;
+
+    /// \brief List of model message to process.
+    private: ModelMsgs_L modelMsgs;
+
+    /// \def RequestMsgs_L
+    /// \brief List of request messages.
+    typedef std::list<boost::shared_ptr<gazebo::msgs::Request const> >
+        RequestMsgs_L;
+    /// \brief List of request message to process.
+    private: RequestMsgs_L requestMsgs;
+
+    /// \def PoseMsgs_L.
+    /// \brief List of messages.
+    typedef std::list<gazebo::msgs::Pose> PoseMsgs_L;
+
+    /// \brief List of pose message to process.
+    private: PoseMsgs_L poseMsgs;
+
+    /// \def LightMsgs_L.
+    /// \brief List of light messages.
+    typedef std::list<boost::shared_ptr<gazebo::msgs::Light const> >
+        LightMsgs_L;
+
+    /// \brief List of light message to process.
+    private: LightMsgs_L lightMsgs;
+
+    /// \def SensorMsgs_L
+    /// \brief List of sensor messages.
+    typedef std::list<boost::shared_ptr<gazebo::msgs::Sensor const> >
+        SensorMsgs_L;
+
+    /// \brief List of sensor message to process.
+    private: SensorMsgs_L sensorMsgs;
+
+    /// \def VisualMsgs_L
+    /// \brief List of visual messages.
+    typedef std::list<boost::shared_ptr<gazebo::msgs::Visual const> >
+        VisualMsgs_L;
+
+    /// \brief List of visual messages to process.
+    private: VisualMsgs_L visualMsgs;
+
+    /// \def JointMsgs_L
+    /// \brief List of joint messages.
+    typedef std::list<boost::shared_ptr<gazebo::msgs::Joint const> >
+        JointMsgs_L;
+
+    /// \brief List of joint message to process.
+    private: JointMsgs_L jointMsgs;
+
+    /// \def SceneMsgs_L
+    /// \brief List of scene messages.
+    typedef std::list<boost::shared_ptr<gazebo::msgs::Scene const> >
+        SceneMsgs_L;
+
+    /// \brief List of scene message to process.
+    private: SceneMsgs_L sceneMsgs;
+
+    /// \def PhysicsMsgs_L
+    /// \brief List of physics messages.
+    typedef std::list<boost::shared_ptr<gazebo::msgs::Physics const> >
+        PhysicsMsgs_L;
+
+    /// \brief List of physics message to process.
+    private: PhysicsMsgs_L physicsMsgs;
+
+    /// \def SceneMsgs_L
+    /// \brief List of world stats messages.
+    typedef std::list<boost::shared_ptr<gazebo::msgs::WorldStatistics const> >
+        WorldStatsMsgs_L;
+
+    /// \brief List of world stats message to process.
+    private: WorldStatsMsgs_L statsMsgs;
+
+    /// \def RoadMsgs_L
+    /// \brief List of road messages.
+    typedef std::list<boost::shared_ptr<gazebo::msgs::Road const> >
+        RoadMsgs_L;
+
+    /// \brief List of road message to process.
+    private: RoadMsgs_L roadMsgs;
+
+    /// \def PoseMsgsFilter_M
+    /// \brief Map of last pose messages used for filtering
+    typedef std::map< std::string, TimedPose> PoseMsgsFilter_M;
+
+    private: PoseMsgsFilter_M poseMsgsFilterMap;
+
+    /// \brief List of service requests to process.
+    private: std::vector<std::string> serviceRequests;
+
+    /// \brief True to stop the interface.
+    private: bool stop;
+
+    /// \brief Name of sensor topic.
+    private: std::string sensorTopic;
+
+    /// \brief Name of visual topic.
+    private: std::string visualTopic;
+
+    /// \brief Name of joint topic.
+    private: std::string jointTopic;
+
+    /// \brief Name of model topic.
+    private: std::string modelTopic;
+
+    /// \brief Name of pose topic.
+    private: std::string poseTopic;
+
+    /// \brief Name of request topic.
+    private: std::string requestTopic;
+
+    /// \brief Name of light topic.
+    private: std::string lightTopic;
+
+    /// \brief Name of link topic.
+    private: std::string linkTopic;
+
+    /// \brief Name of scene topic.
+    private: std::string sceneTopic;
+
+    /// \brief Name of physics topic.
+    private: std::string physicsTopic;
+
+    /// \brief Name of world stats topic.
+    private: std::string statsTopic;
+
+    /// \brief Name of model modify topic.
+    private: std::string modelModifyTopic;
+
+    /// \brief Name of factory topic.
+    private: std::string factoryTopic;
+
+    /// \brief Name of world control topic.
+    private: std::string worldControlTopic;
+
+    /// \brief A custom topic for getting mapping of materials to textures
+    /// referenced by gazebo
+    private: std::string materialTopic;
+
+    /// \brief Name of road topic.
+    private: std::string roadTopic;
+
+    /// \brief Name of heightmap data service.
+    private: std::string heightmapService;
+
+    /// \brief Name of delete topic.
+    private: std::string deleteTopic;
+
+    /// \brief Ogre material parser.
+    private: OgreMaterialParser *materialParser;
+
+    /// \brief Last world stats time received
+    private: gazebo::common::Time lastStatsTime;
+
+    /// \brief Last world state received, play or paused.
+    private: bool lastPausedState;
+
+    /// \brief filter pose message based on minimum distance criteria
+    private: double minimumDistanceSquared;
+
+    /// \brief filter pose message based on minimum rotation criteria
+    private: double minimumQuaternionSquared;
+
+    /// \brief filter pose message based on minimum elapsed time (seconds)
+    private: double minimumMsgAge;
+
+    private: int skippedMsgCount;
+    private: int messageWindowSize;
+    private: int messageCount;
+
+    /// \brief True if there is a client connection.
+    private: bool isConnected;
+
+    public: inline void SetPoseFilterMinimumDistanceSquared(double m)
+    {
+      this->minimumDistanceSquared = m;
+    }
+    public: inline double GetPoseFilterMinimumDistanceSquared()
+    {
+      return this->minimumDistanceSquared;
+    }
+
+    public: inline void SetPoseFilterMinimumQuaternionSquared(double m)
+    {
+      this->minimumQuaternionSquared = m;
+    }
+
+    public: inline double GetPoseFilterMinimumQuaternionSquared()
+    {
+      return this->minimumQuaternionSquared;
+    }
+
+    public: inline void SetPoseFilterMinimumMsgAge(double m)
+    {
+      this->minimumMsgAge = m;
+    }
+
+    public: inline double GetPoseFilterMinimumMsgAge()
+    {
+      return this->minimumMsgAge;
+    }
+  };
+}
+
+#endif
diff --git a/gzbridge/OgreMaterialParser.cc b/gzbridge/OgreMaterialParser.cc
new file mode 100644
index 0000000000000000000000000000000000000000..8b0c54a7273d6973f87927c3306bbd91d4accf3d
--- /dev/null
+++ b/gzbridge/OgreMaterialParser.cc
@@ -0,0 +1,211 @@
+#include <iostream>
+#include <sstream>
+
+#include "ConfigLoader.hh"
+#include "OgreMaterialParser.hh"
+
+using namespace gzweb;
+
+/////////////////////////////////////////////////
+OgreMaterialParser::OgreMaterialParser()
+{
+  this->configLoader = new ConfigLoader(".material");
+}
+
+/////////////////////////////////////////////////
+OgreMaterialParser::~OgreMaterialParser()
+{
+  delete this->configLoader;
+}
+
+/////////////////////////////////////////////////
+void OgreMaterialParser::Load(const std::string &_path)
+{
+  ConfigLoader::loadAllFiles(this->configLoader, _path);
+}
+
+/////////////////////////////////////////////////
+std::string OgreMaterialParser::GetMaterialAsJson() const
+{
+  std::string jsonStr = "{";
+
+  std::map<std::string, ConfigNode *> scripts =
+      this->configLoader->getAllConfigScripts();
+
+  std::map<std::string, ConfigNode *>::iterator it;
+  bool first = true;
+  for (it = scripts.begin(); it != scripts.end(); ++it)
+  {
+    std::string name = it->first;
+    ConfigNode *node = it->second;
+
+    ConfigNode *techniqueNode = node->findChild("technique");
+    if (techniqueNode)
+    {
+      ConfigNode *passNode = techniqueNode->findChild("pass");
+      if (passNode)
+      {
+        if (!first)
+          jsonStr += ", ";
+        else
+          first = false;
+
+        std::size_t index = name.rfind(" ");
+        if (index != std::string::npos)
+        {
+          name = name.substr(index+1);
+        }
+        jsonStr += "\"" + name + "\":{";
+
+        ConfigNode *ambientNode = passNode->findChild("ambient");
+        if (ambientNode)
+        {
+          std::stringstream ss;
+          std::vector<std::string> values = ambientNode->getValues();
+          if (values.size() == 1)
+            ss << "\"";
+          for (unsigned int i = 0; i < values.size(); ++i)
+          {
+            std::string value = ambientNode->getValue(i);
+            if (value[0] == '.')
+              value = '0' + value;
+            ss << value;
+            if (i != values.size() - 1)
+              ss << ",";
+          }
+          if (values.size() == 1)
+            ss << "\"";
+          jsonStr += "\"ambient\":[" + ss.str() + "],";
+        }
+
+        ConfigNode *diffuseNode = passNode->findChild("diffuse");
+        if (diffuseNode)
+        {
+          std::stringstream ss;
+          std::vector<std::string> values = diffuseNode->getValues();
+          if (values.size() == 1)
+            ss << "\"";
+          for (unsigned int i = 0; i < values.size(); ++i)
+          {
+            std::string value = diffuseNode->getValue(i);
+            if (value[0] == '.')
+              value = '0' + value;
+            ss << value;
+            if (i != values.size() - 1)
+              ss << ",";
+          }
+          if (values.size() == 1)
+            ss << "\"";
+          jsonStr += "\"diffuse\":[" + ss.str() + "],";
+        }
+
+        ConfigNode *specularNode = passNode->findChild("specular");
+        if (specularNode)
+        {
+          std::stringstream ss;
+          std::vector<std::string> values = specularNode->getValues();
+          if (values.size() == 1)
+            ss << "\"";
+          for (unsigned int i = 0; i < values.size(); ++i)
+          {
+            std::string value = specularNode->getValue(i);
+            if (value[0] == '.')
+              value = '0' + value;
+            ss << value;
+            if (i != values.size() - 1)
+              ss << ",";
+          }
+          if (values.size() == 1)
+            ss << "\"";
+          jsonStr += "\"specular\":[" + ss.str() + "],";
+        }
+
+        ConfigNode *depthWriteNode = passNode->findChild("depth_write");
+        if (depthWriteNode)
+        {
+          std::stringstream ss;
+          std::string depthWriteStr = depthWriteNode->getValue(0);
+          if (depthWriteStr == "off")
+            ss << "false";
+          else
+            ss << "true";
+          jsonStr += "\"depth_write\":" + ss.str() + ",";
+        }
+
+        ConfigNode *depthCheckNode = passNode->findChild("depth_check");
+        if (depthCheckNode)
+        {
+          std::stringstream ss;
+          std::string depthCheckStr = depthCheckNode->getValue(0);
+          if (depthCheckStr == "off")
+            ss << "false";
+          else
+            ss << "true";
+          jsonStr += "\"depth_check\":" + ss.str() + ",";
+        }
+
+        ConfigNode *textureUnitNode = passNode->findChild("texture_unit");
+        if (textureUnitNode)
+        {
+          ConfigNode *textureNode = textureUnitNode->findChild("texture");
+          if (textureNode)
+          {
+            std::string textureStr = textureNode->getValue(0);
+            index = textureStr.rfind(".");
+            if (index != std::string::npos)
+            {
+              textureStr = textureStr.substr(0, index+1) + "png";
+            }
+
+            jsonStr += "\"texture\":\"" + textureStr + "\",";
+          }
+          ConfigNode *scaleNode = textureUnitNode->findChild("scale");
+          if (scaleNode)
+          {
+            std::stringstream ss;
+            std::vector<std::string> values = scaleNode->getValues();
+            if (values.size() == 1)
+              ss << "\"";
+            for (unsigned int i = 0; i < values.size(); ++i)
+            {
+              std::string value = scaleNode->getValue(i);
+              if (value[0] == '.')
+                value = '0' + value;
+              ss << value;
+              if (i != values.size() - 1)
+                ss << ",";
+            }
+            if (values.size() == 1)
+              ss << "\"";
+            jsonStr += "\"scale\":[" + ss.str() + "],";
+          }
+          ConfigNode *alphaOpNode = textureUnitNode->findChild("alpha_op_ex");
+          if (alphaOpNode)
+          {
+            std::stringstream ss;
+            std::vector<std::string> values = alphaOpNode->getValues();
+            // a bit hacky, just assuming there is an alpha value to use
+            // fix this to support more ogre alpha operations.
+            if (values[1] == "src_manual")
+            {
+              ss << values[3];
+            }
+            jsonStr += "\"opacity\":" + ss.str() + ",";
+          }
+        }
+        if (jsonStr[jsonStr.size()-1] == ',')
+        {
+          jsonStr = jsonStr.substr(0, jsonStr.size()-1);
+        }
+        jsonStr += "}";
+      }
+    }
+
+  }
+
+  jsonStr += "}";
+
+  // std::cout << jsonStr << std::endl;
+
+  return jsonStr;
+}
diff --git a/gzbridge/OgreMaterialParser.hh b/gzbridge/OgreMaterialParser.hh
new file mode 100644
index 0000000000000000000000000000000000000000..d5797d0f5d0988194ae7616421bec494b8c77861
--- /dev/null
+++ b/gzbridge/OgreMaterialParser.hh
@@ -0,0 +1,26 @@
+#ifndef _OGRE_MATERIAL_PARSER_HH_
+#define _OGRE_MATERIAL_PARSER_HH_
+
+#include <string>
+
+namespace gzweb
+{
+  class ConfigNode;
+  class ConfigLoader;
+
+  class OgreMaterialParser
+  {
+    public: OgreMaterialParser();
+
+    public: virtual ~OgreMaterialParser();
+
+    public: void Load(const std::string &_path);
+
+    public: std::string GetMaterialAsJson() const;
+
+    private: ConfigLoader *configLoader;
+  };
+
+}
+
+#endif
diff --git a/gzbridge/binding.gyp b/gzbridge/binding.gyp
new file mode 100644
index 0000000000000000000000000000000000000000..53d0cad3d144cbbb815f9a3bbfc11b16a9afe316
--- /dev/null
+++ b/gzbridge/binding.gyp
@@ -0,0 +1,45 @@
+{
+  "targets": [
+    {
+      "target_name": "gzbridge",
+      "sources": [ "GZNode.cc", "GZNode.hh",
+        "GazeboInterface.cc", "GazeboInterface.hh",
+        "pb2json.cc", "pb2json.hh",
+        "ConfigLoader.cc", "ConfigLoader.hh",
+        "OgreMaterialParser.cc", "OgreMaterialParser.hh"],
+      'cflags_cc!': [ '-fno-rtti', '-fno-exceptions' ],
+      'cflags!': [ '-fno-exceptions' ],
+      "conditions": [
+        ['OS=="linux"', {
+          'cflags': [
+            '<!@(pkg-config --cflags gazebo jansson protobuf)'
+          ],
+          'ldflags': [
+            '<!@(pkg-config --libs-only-L --libs-only-other gazebo jansson protobuf)'
+          ],
+          'libraries': [
+            '<!@(pkg-config --libs-only-l gazebo jansson protobuf)'
+          ]
+        }],
+        ['OS=="mac"', {
+          'libraries': [
+            '<!@(pkg-config --libs-only-l gazebo jansson protobuf)'
+          ],
+          'xcode_settings' : {
+            'GCC_ENABLE_CPP_RTTI': 'YES',
+            'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',
+            'OTHER_CFLAGS': [
+              '<!@(pkg-config --cflags gazebo jansson protobuf)'
+            ],
+            'OTHER_CPLUSPLUSFLAGS': [
+              '<!@(pkg-config --cflags gazebo jansson protobuf)'
+            ],
+            'OTHER_LDFLAGS': [
+              '<!@(pkg-config --libs-only-L --libs-only-other  gazebo jansson protobuf)'
+            ]
+          }
+        }]
+      ]
+    }
+  ]
+}
diff --git a/gzbridge/pb2json.cc b/gzbridge/pb2json.cc
new file mode 100644
index 0000000000000000000000000000000000000000..dc39e6db112ad8d69a0ebf29b6d07100527ff74e
--- /dev/null
+++ b/gzbridge/pb2json.cc
@@ -0,0 +1,306 @@
+/*
+ * Copyright 2013 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+
+
+#include <string>
+#include <sstream>
+#include <iostream>
+
+
+#include "pb2json.hh"
+
+using namespace google::protobuf;
+
+namespace gzweb {
+
+  std::string hex_encode(const std::string &input)
+  {
+    static const char* const lut = "0123456789abcdef";
+    size_t len = input.length();
+
+    std::string output;
+    output.reserve(2 * len);
+    for (size_t i = 0; i < len; ++i)
+    {
+      const unsigned char c = input[i];
+      output.push_back(lut[c >> 4]);
+      output.push_back(lut[c & 15]);
+    }
+    return output;
+  }
+
+  char *pb2json(const Message &msg)
+  {
+    json_t *root = parse_msg(&msg);
+    char *json = json_dumps(root, 0);
+    json_decref(root);
+    return json; // should be freed by caller
+  }
+
+  char *pb2json(Message *msg, const char *buf, int len)
+  {
+    std::string s (buf, len);
+    msg->ParseFromString(s);
+    json_t *root = parse_msg(msg);
+    char *json = json_dumps(root, 0);
+    json_decref(root);
+    return json; // should be freed by caller
+  }
+
+  json_t *parse_repeated_field(const Message *msg,
+      const Reflection *ref,
+      const FieldDescriptor *field)
+  {
+    size_t count = ref->FieldSize(*msg, field);
+    json_t *arr = json_array();
+    if(!arr)return NULL;
+    switch(field->cpp_type())
+    {
+      case FieldDescriptor::CPPTYPE_DOUBLE:
+        for(size_t i = 0 ; i != count ; ++i)
+        {
+          double  value1 = ref->GetRepeatedDouble(*msg, field, i);
+          json_array_append_new(arr, json_real(value1));
+        }
+        break;
+      case FieldDescriptor::CPPTYPE_FLOAT:
+        for(size_t i = 0 ; i != count ; ++i)
+        {
+          float value2 = ref->GetRepeatedFloat(*msg, field, i);
+          json_array_append_new(arr, json_real(value2));
+        }
+        break;
+      case FieldDescriptor::CPPTYPE_INT64:
+        for(size_t i = 0 ; i != count ; ++i)
+        {
+          int64_t value3 = ref->GetRepeatedInt64(*msg, field, i);
+          json_array_append_new(arr,json_integer(value3)) ;
+        }
+        break;
+      case FieldDescriptor::CPPTYPE_UINT64:
+        for(size_t i = 0 ; i != count ; ++i)
+        {
+          uint64_t value4 = ref->GetRepeatedUInt64(*msg, field, i);
+          json_array_append_new(arr, json_integer(value4));
+        }
+        break;
+      case FieldDescriptor::CPPTYPE_INT32:
+        for(size_t i = 0 ; i != count ; ++i)
+        {
+          int32_t value5 = ref->GetRepeatedInt32(*msg, field, i);
+          json_array_append_new(arr, json_integer(value5));
+        }
+        break;
+      case FieldDescriptor::CPPTYPE_UINT32:
+        for(size_t i = 0 ; i != count ; ++i)
+        {
+          uint32_t value6 = ref->GetRepeatedUInt32(*msg, field, i);
+          json_array_append_new(arr, json_integer(value6));
+        }
+        break;
+      case FieldDescriptor::CPPTYPE_BOOL:
+        for(size_t i = 0 ; i != count ; ++i)
+        {
+          bool value7 = ref->GetRepeatedBool(*msg,field,i);
+          json_array_append_new(arr, value7 ? json_true() : json_false())  ;
+        }
+        break;
+      case FieldDescriptor::CPPTYPE_STRING:
+        for(size_t i = 0 ; i != count ; ++i)
+        {
+          std::string value8 = ref->GetRepeatedString(*msg, field, i);
+          json_array_append_new(arr, json_string(value8.c_str()));
+        }
+        break;
+      case FieldDescriptor::CPPTYPE_MESSAGE:
+        for(size_t i = 0 ; i != count ; ++i)
+        {
+          const Message *value9 = &(ref->GetRepeatedMessage(*msg, field, i));
+          json_array_append_new(arr, parse_msg(value9));
+        }
+        break;
+      case FieldDescriptor::CPPTYPE_ENUM:
+        for(size_t i = 0 ; i != count ; ++i)
+        {
+          const EnumValueDescriptor* value10
+              = ref->GetRepeatedEnum(*msg, field, i);
+          json_array_append_new(arr, json_integer(value10->number()));
+        }
+        break;
+      default:
+        break;
+    }
+    return arr;
+  }
+
+  json_t *parse_msg(const Message *msg)
+  {
+    const Descriptor *d = msg->GetDescriptor();
+    if(!d)return NULL;
+    size_t count = d->field_count();
+    json_t *root = json_object();
+    if(!root)return NULL;
+    for (size_t i = 0; i != count ; ++i)
+    {
+      const FieldDescriptor *field = d->field(i);
+      if(!field)return NULL;
+
+      const Reflection *ref = msg->GetReflection();
+      if(!ref)return NULL;
+      const char *name = field->name().c_str();
+      if(field->is_repeated())
+        json_object_set_new(root, name, parse_repeated_field(msg, ref, field));
+      if(!field->is_repeated() && ref->HasField(*msg, field))
+      {
+        double value1;
+        float value2;
+        int64_t value3;
+        uint64_t value4;
+        int32_t value5;
+        uint32_t value6;
+        bool value7;
+        std::string value8;
+        const Message *value9;
+        const EnumValueDescriptor *value10;
+
+        switch (field->cpp_type())
+        {
+          case FieldDescriptor::CPPTYPE_DOUBLE:
+            value1 = ref->GetDouble(*msg,field);
+            if (value1 != value1)
+              value1 = 0;
+            json_object_set_new(root,name,json_real(value1));
+            break;
+          case FieldDescriptor::CPPTYPE_FLOAT:
+            value2 = ref->GetFloat(*msg,field);
+            if (value2 != value2)
+              value2 = 0;
+            json_object_set_new(root,name,json_real(value2));
+            break;
+          case FieldDescriptor::CPPTYPE_INT64:
+            value3 = ref->GetInt64(*msg,field);
+            json_object_set_new(root,name,json_integer(value3)) ;
+            break;
+          case FieldDescriptor::CPPTYPE_UINT64:
+            value4 = ref->GetUInt64(*msg,field);
+            json_object_set_new(root,name,json_integer(value4));
+            break;
+          case FieldDescriptor::CPPTYPE_INT32:
+            value5 = ref->GetInt32(*msg,field);
+            json_object_set_new(root,name,json_integer(value5));
+            break;
+          case FieldDescriptor::CPPTYPE_UINT32:
+            value6 = ref->GetUInt32(*msg,field);
+            json_object_set_new(root,name,json_integer(value6));
+            break;
+          case FieldDescriptor::CPPTYPE_BOOL:
+            value7 = ref->GetBool(*msg,field);
+            json_object_set_new(root,name,value7?json_true():json_false())  ;
+            break;
+          case FieldDescriptor::CPPTYPE_STRING:
+            if (field->type() == FieldDescriptor::TYPE_BYTES)
+              value8 = hex_encode(ref->GetString(*msg,field));
+            else
+              value8 = ref->GetString(*msg,field);
+            json_object_set_new(root,name,json_string(value8.c_str()));
+            break;
+          case FieldDescriptor::CPPTYPE_MESSAGE:
+            value9 = &(ref->GetMessage(*msg,field));
+            json_object_set_new(root,name,parse_msg(value9));
+            break;//FIXME : parse Message
+          case FieldDescriptor::CPPTYPE_ENUM:
+            value10 = ref->GetEnum(*msg,field);
+            json_object_set_new(root,name,json_integer(value10->number()));
+            break;
+          default:
+            break;
+        }
+      }
+    }
+    return root;
+  }
+
+  std::string get(json_t *obj, const std::string &key)
+  {
+    if (key.empty())
+      return "";
+
+    std::string keyToken = key;
+    std::string keyRest = key;
+    std::size_t pos = key.find(":");
+    if (pos != std::string::npos)
+    {
+      keyToken = key.substr(0, pos);
+      keyRest = key.substr(pos+1);
+    }
+    json_t *data = json_object_get(obj, keyToken.c_str());
+
+    if (json_is_object(data))
+    {
+      return get(data, keyRest);
+    }
+    else if(json_is_string(data))
+    {
+      const char *result = json_string_value(data);
+      return std::string(result);
+    }
+    else if (json_is_real(data))
+    {
+      std::stringstream ss;
+      ss << json_real_value(data);
+      return ss.str();
+    }
+    else if (json_is_integer(data))
+    {
+      std::stringstream ss;
+      ss << json_integer_value(data);
+      return ss.str();
+    }
+    else if (json_is_true(data))
+    {
+      std::stringstream ss;
+      ss << "1";
+      return ss.str();
+    }
+    else if (json_is_false(data))
+    {
+      std::stringstream ss;
+      ss << "0";
+      return ss.str();
+    }
+    else if (json_is_array(data))
+    {
+      // TODO: only gets the first element in array and treats it as string
+      // Need a more generic get_array function later.
+      std::stringstream ss;
+      ss << json_string_value(json_array_get(data, 0));
+      return ss.str();
+    }
+
+    return "";
+  }
+
+  std::string get_value(const std::string &msg, const std::string &key)
+  {
+    json_t *root;
+    json_error_t error;
+    root = json_loads(msg.c_str(), 0, &error);
+    std::string result = get(root, key);
+    json_decref(root);
+    return result;
+  }
+}
diff --git a/gzbridge/pb2json.hh b/gzbridge/pb2json.hh
new file mode 100644
index 0000000000000000000000000000000000000000..3ece65e59007749a0ca33a4bb91e77956704fd22
--- /dev/null
+++ b/gzbridge/pb2json.hh
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2013 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+
+#ifndef _PB2JSON_H_
+#define _PB2JSON_H_
+
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/message.h>
+#include <jansson.h>
+
+namespace gzweb
+{
+  char *pb2json(const google::protobuf::Message &msg);
+
+  char *pb2json(google::protobuf::Message *msg,const char *buf,int len);
+
+  std::string get_value(const std::string &msg, const std::string &key);
+
+  json_t *parse_msg(const google::protobuf::Message *msg);
+
+  json_t *parse_repeated_field(const google::protobuf::Message *msg,
+      const google::protobuf::Reflection *ref,
+      const google::protobuf::FieldDescriptor *field);
+}
+
+#endif
diff --git a/gzbridge/ws_server.js b/gzbridge/ws_server.js
new file mode 100755
index 0000000000000000000000000000000000000000..1315963373f77177ddfa5cfed97bca562d4b131b
--- /dev/null
+++ b/gzbridge/ws_server.js
@@ -0,0 +1,133 @@
+#!/usr/bin/env node
+
+var WebSocketServer = require('websocket').server;
+var http = require('http');
+
+var connections = [];
+var materialScriptsMessage = {};
+var addon = require('./build/Debug/gzbridge');
+var gzconnection = new addon.GZNode();
+if (gzconnection.getIsGzServerConnected())
+{
+  gzconnection.loadMaterialScripts('../http/client/assets');
+  gzconnection.setPoseMsgFilterMinimumAge(0.02);
+  gzconnection.setPoseMsgFilterMinimumDistanceSquared(0.00001);
+  gzconnection.setPoseMsgFilterMinimumQuaternionSquared(0.00001);
+
+  console.log('Pose message filter parameters: ');
+  console.log('  minimum seconds between successive messages: ' +
+      gzconnection.getPoseMsgFilterMinimumAge());
+  console.log('  minimum XYZ distance squared between successive messages: ' +
+      gzconnection.getPoseMsgFilterMinimumDistanceSquared());
+  console.log('  minimum Quartenion distance squared between successive messages:'
+      + ' ' + gzconnection.getPoseMsgFilterMinimumQuaternionSquared());
+}
+else
+{
+  materialScriptsMessage = gzconnection.getMaterialScriptsMessage('../http/client/assets');
+}
+
+var isConnected = false;
+
+var server = http.createServer(function(request, response) {
+    console.log((new Date()) + ' Received request for ' + request.url);
+    response.writeHead(404);
+    response.end();
+});
+server.listen(7681, function() {
+    console.log((new Date()) + ' Server is listening on port 7681');
+});
+
+wsServer = new WebSocketServer({
+    httpServer: server,
+    // You should not use autoAcceptConnections for production
+    // applications, as it defeats all standard cross-origin protection
+    // facilities built into the protocol and the browser.  You should
+    // *always* verify the connection's origin and decide whether or not
+    // to accept it.
+    autoAcceptConnections: false
+});
+
+function originIsAllowed(origin) {
+  // put logic here to detect whether the specified origin is allowed.
+  return true;
+}
+
+wsServer.on('request', function(request) {
+    if (!originIsAllowed(request.origin)) {
+      // Make sure we only accept requests from an allowed origin
+      request.reject();
+      console.log((new Date()) + ' Connection from origin ' +
+          request.origin + ' rejected.');
+      return;
+    }
+
+    var connection = request.accept(null, request.origin);
+
+    // if gzserver is not connected just send material scripts and status
+    if (!gzconnection.getIsGzServerConnected())
+    {
+      // create error status and send it
+      var statusMessage = '{"op":"publish","topic":"~/status","msg":{"status":"error"}}';
+      connection.sendUTF(statusMessage);
+      // send material scripts message
+      connection.sendUTF(materialScriptsMessage);
+      return;
+    }
+
+    connections.push(connection);
+
+    if (!isConnected)
+    {
+      isConnected = true;
+      gzconnection.setConnected(isConnected);
+    }
+
+    console.log((new Date()) + ' Connection accepted.');
+    connection.on('message', function(message) {
+      if (message.type === 'utf8') {
+        console.log('Received Message: ' + message.utf8Data + ' from ' +
+            request.origin + ' ' + connection.remoteAddress);
+        gzconnection.request(message.utf8Data);
+      }
+      else if (message.type === 'binary') {
+        console.log('Received Binary Message of ' +
+            message.binaryData.length + ' bytes');
+        connection.sendBytes(message.binaryData);
+      }
+    });
+    connection.on('close', function(reasonCode, description) {
+        console.log((new Date()) + ' Peer ' + connection.remoteAddress +
+            ' disconnected.');
+
+      // remove connection from array
+      var conIndex = connections.indexOf(connection);
+      connections.splice(conIndex, 1);
+
+      // if there is no connection notify server that there is no connected client
+      if (connections.length == 0) {
+        isConnected = false;
+        gzconnection.setConnected(isConnected);
+      }
+    });
+});
+
+if (gzconnection.getIsGzServerConnected())
+{
+  setInterval(update, 10);
+
+  function update()
+  {
+    if (connections.length > 0)
+    {
+      var msgs = gzconnection.getMessages();
+      for (var i = 0; i < connections.length; ++i)
+      {
+        for (var j = 0; j < msgs.length; ++j)
+        {
+          connections[i].sendUTF(msgs[j]);
+        }
+      }
+    }
+  }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..9100135338ec7c07426e05f550ca3860c625c923
--- /dev/null
+++ b/package.json
@@ -0,0 +1,20 @@
+{
+  "name": "gzweb",
+  "version": "1.0.0",
+  "description": "WebGL client for Gazebo",
+  "repository" :
+  {
+    "type" : "mercurial",
+    "url" : "https://bitbucket.org/osrf/gzweb"
+  },
+  "scripts":
+  {
+  },
+  "dependencies":
+  {
+    "websocket": "",
+    "http-server": "",
+    "node-gyp":""
+  }
+}
+
diff --git a/start_gzweb.sh b/start_gzweb.sh
new file mode 100755
index 0000000000000000000000000000000000000000..589ca8a96357ac5c015ce300642bb2b83c6011d7
--- /dev/null
+++ b/start_gzweb.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+ulimit -c unlimited
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+cd $DIR
+
+./stop_gzweb.sh
+
+if [ ! -d http/client ]
+then
+  echo "gzweb not initialized, have you run ./deploy.sh yet?"
+  exit
+fi
+
+
+./node_modules/.bin/http-server http/client &
+
+cd gzbridge
+./ws_server.js &
diff --git a/stop_gzweb.sh b/stop_gzweb.sh
new file mode 100755
index 0000000000000000000000000000000000000000..876af8d8fa66c1a27fb124cba2f94db56dc6086c
--- /dev/null
+++ b/stop_gzweb.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+killall node
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c5caabb4cd1ac1319099e7ffbefde07ea094ad4e
--- /dev/null
+++ b/tools/CMakeLists.txt
@@ -0,0 +1,21 @@
+include_directories(
+  include_directories(${GAZEBO_INCLUDE_DIRS})
+)
+
+if (HAVE_GTS)
+  include_directories(${GTS_INCLUDE_DIRS})
+  link_directories(${GTS_LIBRARY_DIRS})
+  add_definitions(${GTS_CFLAGS})
+endif()
+
+link_directories(${GAZEBO_LIBRARY_DIRS})
+
+set (sources
+  gzcoarse.cc
+)
+
+add_executable(gzcoarse ${sources})
+
+target_link_libraries(gzcoarse ${GAZEBO_LIBRARIES}
+    ${Boost_LIBRARIES} ${GTS_LIBRARIES} ${tinyxml_libraries})
+
diff --git a/tools/green.world b/tools/green.world
new file mode 100644
index 0000000000000000000000000000000000000000..c064c6f5b4688e7aedb7e82074a2b5e1a1293bf7
--- /dev/null
+++ b/tools/green.world
@@ -0,0 +1,16 @@
+<sdf version='1.5'>
+  <world name='default'>
+    <scene>
+      <ambient>0.2 0.2 0.2 1</ambient>
+      <background>0 1 0 1</background>
+      <shadows>0</shadows>
+      <grid>0</grid>
+    </scene>
+    <gui fullscreen='0'>
+      <camera name='user_camera'>
+        <pose>5.65634 -4.1009 2.6069 0 0.275643 2.35619</pose>
+        <view_controller>orbit</view_controller>
+      </camera>
+    </gui>
+  </world>
+</sdf>
diff --git a/tools/gzcoarse.cc b/tools/gzcoarse.cc
new file mode 100644
index 0000000000000000000000000000000000000000..e468fdedc94885267b7764ec522fde282054e5c3
--- /dev/null
+++ b/tools/gzcoarse.cc
@@ -0,0 +1,1296 @@
+/*
+ * Copyright 2014 Open Source Robotics Foundation
+ *
+ * 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.
+ *
+*/
+
+#include <gts.h>
+#include <tinyxml.h>
+#include <math.h>
+#include <gazebo/common/Mesh.hh>
+#include <gazebo/common/Material.hh>
+#include <gazebo/common/ColladaLoader.hh>
+#include "nanoflann.hpp"
+
+#ifndef PI
+#define PI 3.14159265359
+#endif
+
+enum GeometryType {POSITION, NORMAL, UVMAP};
+
+//////////////////////////////////////////////////
+// Custom data set for nanoflann
+template <typename T>
+struct PointCloud
+{
+  struct Point
+  {
+    T  x,y,z;
+  };
+
+  std::vector<Point>  pts;
+
+  // Must return the number of data points
+  inline size_t kdtree_get_point_count() const
+  {
+    return pts.size();
+  }
+
+  // Returns the distance between the vector "p1[0:size-1]" and the data point
+  // with index "idx_p2" stored in the class:
+  inline T kdtree_distance(const T *p1, const size_t idx_p2,
+      size_t size) const
+  {
+    const T d0=p1[0]-pts[idx_p2].x;
+    const T d1=p1[1]-pts[idx_p2].y;
+    const T d2=p1[2]-pts[idx_p2].z;
+    return d0*d0+d1*d1+d2*d2;
+  }
+
+  // Returns the dim'th component of the idx'th point in the class:
+  // Since this is inlined and the "dim" argument is typically an immediate
+  // value, the "if/else's" are actually solved at compile time.
+  inline T kdtree_get_pt(const size_t idx, int dim) const
+  {
+    if (dim==0) return pts[idx].x;
+    else if (dim==1) return pts[idx].y;
+    else return pts[idx].z;
+  }
+
+  // Optional bounding-box computation: return false to default to a standard
+  // bbox computation loop.
+  template <class BBOX>
+  bool kdtree_get_bbox(BBOX &bb) const
+  {
+    return false;
+  }
+};
+
+//////////////////////////////////////////////////
+/// \brief Export texture source, used by ExportGeometries/ConvertGztoDae.
+/// Different from that in ColladaExporter, because it uses different indices.
+/// Using nanoflann for kd-tree, but it could be changed to GTS kd-tree.
+/// \param[in] _outSubMesh Pointer to an output submesh
+/// \param[in] _inSubMesh Pointer to an input submesh
+/// \param[in] _meshXml Pointer to the mesh XML instance
+/// \param[in] _meshID Mesh ID (mesh_<number>)
+void ExportTextureSource(const gazebo::common::SubMesh *_outSubMesh,
+    const gazebo::common::SubMesh *_inSubMesh,
+    TiXmlElement *_meshXml, const char *_meshID)
+{
+  std::cout << "Calculating texture map..." << std::endl;
+
+  char sourceId[100], sourceArrayId[100];
+  unsigned int inCount = _inSubMesh->GetVertexCount();
+
+  //assert(inCount%3 == 0);
+  if (inCount%3 != 0)
+  {
+    inCount = floor(inCount/3)*3;
+  }
+
+  std::ostringstream fillData;
+  fillData.precision(5);
+  fillData<<std::fixed;
+
+  // Fill the point cloud with vertices from the original mesh
+  PointCloud<double> cloud;
+  cloud.pts.resize(inCount);
+  gazebo::math::Vector3 inVertex;
+  for (size_t i = 0; i < inCount; ++i)
+  {
+    inVertex = _inSubMesh->GetVertex(i);
+    cloud.pts[i].x = inVertex.x;
+    cloud.pts[i].y = inVertex.y;
+    cloud.pts[i].z = inVertex.z;
+  }
+
+  // construct a kd-tree index:
+  typedef nanoflann::KDTreeSingleIndexAdaptor<
+      nanoflann::L2_Simple_Adaptor<double, PointCloud<double> > ,
+      PointCloud<double>,
+      3
+      > my_kd_tree_t;
+
+  my_kd_tree_t cloudIndex(3, cloud,
+      nanoflann::KDTreeSingleIndexAdaptorParams(10));
+  cloudIndex.buildIndex();
+
+  // For each vertex of each triangle
+  unsigned int outTriIndexCount = _outSubMesh->GetIndexCount();
+  const size_t num_results = 30;
+  std::vector<size_t> result_index(num_results);
+  std::vector<double> out_dist_sqr(num_results);
+  static const int offset[] = {1,2,-1,1,-2,-1};
+  for (int i = 0; i < outTriIndexCount; ++i)
+  {
+    unsigned int outIndex = _outSubMesh->GetIndex(i);
+    gazebo::math::Vector3 outVertex = _outSubMesh->GetVertex(outIndex);
+
+    const double query_pt[3] = { outVertex.x, outVertex.y, outVertex.z};
+    // Get nearest num_results
+    cloudIndex.knnSearch(&query_pt[0], num_results, &result_index[0],
+        &out_dist_sqr[0]);
+
+    std::vector<size_t> closestIndices;
+    double closestDistance = 1000;
+    for (int j = 0; j < num_results; ++j)
+    {
+      inVertex = _inSubMesh->GetVertex(result_index[j]);
+
+      double distance = inVertex.Distance(outVertex);
+      // closer vertex
+      if (distance <  closestDistance)
+      {
+        closestDistance = distance;
+        closestIndices.clear();
+        closestIndices.push_back(result_index[j]);
+      }
+      // overlapping vertex
+      else if (distance == closestDistance)
+      {
+        closestIndices.push_back(result_index[j]);
+      }
+    }
+
+    // Choose best UV among overlapping closestIndices
+
+    // index%3 == 0: beginning of a triangle
+    // triangle 1: i == 0,1,2; triangle 2: i == 3,4,5 and so on
+    gazebo::math::Vector2d outOffset(i+offset[(i % 3)*2],
+                                     i+offset[(i % 3)*2+1]);
+    // Get other vertices in the same triangle
+    unsigned int outIndex_1 = _outSubMesh->GetIndex(outOffset.x);
+    unsigned int outIndex_2 = _outSubMesh->GetIndex(outOffset.y);
+    gazebo::math::Vector3 outVertex_1 = _outSubMesh->GetVertex(outIndex_1);
+    gazebo::math::Vector3 outVertex_2 = _outSubMesh->GetVertex(outIndex_2);
+
+    // Get directions
+    gazebo::math::Vector3 outDir_1 = (outVertex_1-outVertex).Normalize();
+    gazebo::math::Vector3 outDir_2 = (outVertex_2-outVertex).Normalize();
+
+    // Initialize closestVertex
+    size_t closestIndex = closestIndices[0];
+    gazebo::math::Vector2d
+        closestOffset(closestIndex+offset[(closestIndex % 3)*2],
+                      closestIndex+offset[(closestIndex % 3)*2+1]);
+
+    gazebo::math::Vector3 closestVertex =
+        _inSubMesh->GetVertex(closestIndex);
+    gazebo::math::Vector3 closestVertex_1 =
+        _inSubMesh->GetVertex(closestOffset.x);
+    gazebo::math::Vector3 closestVertex_2 =
+        _inSubMesh->GetVertex(closestOffset.y);
+
+    gazebo::math::Vector3 closestDir_1 =
+        (closestVertex_1-closestVertex).Normalize();
+    gazebo::math::Vector3 closestDir_2 =
+        (closestVertex_2-closestVertex).Normalize();
+
+    // Initialize sum of closest directions
+    double closestSum;
+    if (outDir_1.Distance(closestDir_1) < outDir_1.Distance(closestDir_2))
+    {
+      closestSum = outDir_1.Distance(closestDir_1) +
+                   outDir_2.Distance(closestDir_2);
+    }
+    else
+    {
+      closestSum = outDir_2.Distance(closestDir_1) +
+                   outDir_1.Distance(closestDir_2);
+    }
+
+    // Find the closest direction among all triangles containing overlapping
+    // vertices
+    for (int k = 1; k < closestIndices.size(); ++k)
+    {
+      // Current vertex
+      size_t currentIndex = closestIndices[k];
+      gazebo::math::Vector2d
+          currentOffset(currentIndex+offset[(currentIndex % 3)*2],
+                        currentIndex+offset[(currentIndex % 3)*2+1]);
+
+      gazebo::math::Vector3 currentVertex =
+          _inSubMesh->GetVertex(currentIndex);
+      gazebo::math::Vector3 currentVertex_1 =
+          _inSubMesh->GetVertex(currentOffset.x);
+      gazebo::math::Vector3 currentVertex_2 =
+          _inSubMesh->GetVertex(currentOffset.y);
+
+      gazebo::math::Vector3 currentDir_1 =
+          (currentVertex_1-currentVertex).Normalize();
+      gazebo::math::Vector3 currentDir_2 =
+          (currentVertex_2-currentVertex).Normalize();
+
+      double currentSum;
+      if (outDir_1.Distance(currentDir_1) < outDir_1.Distance(currentDir_2))
+      {
+        currentSum = outDir_1.Distance(currentDir_1) +
+                     outDir_2.Distance(currentDir_2);
+      }
+      else
+      {
+        currentSum = outDir_2.Distance(currentDir_1) +
+                     outDir_1.Distance(currentDir_2);
+      }
+
+      if (currentSum < closestSum)
+      {
+        closestSum = currentSum;
+        closestIndex = currentIndex;
+        closestDir_1 = currentDir_1;
+        closestDir_2 = currentDir_2;
+        closestVertex = currentVertex;
+      }
+    }
+
+    // Get UV coordinates
+    double U = _inSubMesh->GetTexCoord(closestIndex).x;
+    double V = _inSubMesh->GetTexCoord(closestIndex).y;
+
+    fillData << U << " " << 1.0-V << " ";
+  }
+
+  std::cout << "Texture map calculation complete." << std::endl;
+
+  snprintf(sourceId, sizeof(sourceId), "%s-UVMap", _meshID);
+  unsigned int outCount = _outSubMesh->GetVertexCount();
+  int stride = 2;
+
+  TiXmlElement *sourceXml = new TiXmlElement("source");
+  _meshXml->LinkEndChild(sourceXml);
+  sourceXml->SetAttribute("id", sourceId);
+  sourceXml->SetAttribute("name", sourceId);
+
+  snprintf(sourceArrayId, sizeof(sourceArrayId), "%s-array", sourceId);
+  TiXmlElement *floatArrayXml = new TiXmlElement("float_array");
+  floatArrayXml->SetAttribute("count", outCount *stride);
+  floatArrayXml->SetAttribute("id", sourceArrayId);
+  floatArrayXml->LinkEndChild(new TiXmlText(fillData.str().c_str()));
+  sourceXml->LinkEndChild(floatArrayXml);
+
+  TiXmlElement *techniqueCommonXml = new TiXmlElement("technique_common");
+  sourceXml->LinkEndChild(techniqueCommonXml);
+
+  snprintf(sourceArrayId, sizeof(sourceArrayId), "#%s-array", sourceId);
+  TiXmlElement *accessorXml = new TiXmlElement("accessor");
+  accessorXml->SetAttribute("count", outCount);
+  accessorXml->SetAttribute("source", sourceArrayId);
+  accessorXml->SetAttribute("stride", stride);
+  techniqueCommonXml->LinkEndChild(accessorXml);
+
+  TiXmlElement *paramXml = new TiXmlElement("param");
+
+  paramXml->SetAttribute("type", "float");
+  paramXml->SetAttribute("name", "U");
+  accessorXml->LinkEndChild(paramXml);
+
+  paramXml = new TiXmlElement("param");
+  paramXml->SetAttribute("type", "float");
+  paramXml->SetAttribute("name", "V");
+  accessorXml->LinkEndChild(paramXml);
+}
+
+//////////////////////////////////////////////////
+/// \brief Export geometry source, used by ExportGeometries/ConvertGztoDae
+/// \param[in] _subMesh Pointer to a submesh
+/// \param[in] _meshXml Pointer to the mesh XML instance
+/// \param[in] _type POSITION, NORMAL or UVMAP
+/// \param[in] _meshID Mesh ID (mesh_<number>)
+void ExportGeometrySource(const gazebo::common::SubMesh *_subMesh,
+    TiXmlElement *_meshXml, GeometryType _type, const char *_meshID)
+{
+  char sourceId[100], sourceArrayId[100];
+  std::ostringstream fillData;
+  fillData.precision(8);
+  fillData << std::fixed;
+  int stride;
+  unsigned int count = 0;
+
+  if (_type == POSITION)
+  {
+    snprintf(sourceId, sizeof(sourceId), "%s-Positions", _meshID);
+    count = _subMesh->GetVertexCount();
+    stride = 3;
+    gazebo::math::Vector3 vertex;
+    for (unsigned int i = 0; i < count; ++i)
+    {
+      vertex = _subMesh->GetVertex(i);
+      fillData << vertex.x << " " << vertex.y << " " << vertex.z << " ";
+    }
+  }
+  if (_type == NORMAL)
+  {
+    snprintf(sourceId, sizeof(sourceId), "%s-Normals", _meshID);
+    count = _subMesh->GetNormalCount();
+    stride = 3;
+    gazebo::math::Vector3 normal;
+    for (unsigned int i = 0; i < count; ++i)
+    {
+      normal = _subMesh->GetNormal(i);
+      fillData << normal.x << " " << normal.y << " " << normal.z << " ";
+    }
+  }
+
+  TiXmlElement *sourceXml = new TiXmlElement("source");
+  _meshXml->LinkEndChild(sourceXml);
+  sourceXml->SetAttribute("id", sourceId);
+  sourceXml->SetAttribute("name", sourceId);
+
+  snprintf(sourceArrayId, sizeof(sourceArrayId), "%s-array", sourceId);
+  TiXmlElement *floatArrayXml = new TiXmlElement("float_array");
+  floatArrayXml->SetAttribute("count", count *stride);
+  floatArrayXml->SetAttribute("id", sourceArrayId);
+  floatArrayXml->LinkEndChild(new TiXmlText(fillData.str().c_str()));
+  sourceXml->LinkEndChild(floatArrayXml);
+
+  TiXmlElement *techniqueCommonXml = new TiXmlElement("technique_common");
+  sourceXml->LinkEndChild(techniqueCommonXml);
+
+  snprintf(sourceArrayId, sizeof(sourceArrayId), "#%s-array", sourceId);
+  TiXmlElement *accessorXml = new TiXmlElement("accessor");
+  accessorXml->SetAttribute("count", count);
+  accessorXml->SetAttribute("source", sourceArrayId);
+  accessorXml->SetAttribute("stride", stride);
+  techniqueCommonXml->LinkEndChild(accessorXml);
+
+  TiXmlElement *paramXml = new TiXmlElement("param");
+  if (_type == POSITION || _type == NORMAL)
+  {
+    paramXml->SetAttribute("type", "float");
+    paramXml->SetAttribute("name", "X");
+    accessorXml->LinkEndChild(paramXml);
+
+    paramXml = new TiXmlElement("param");
+    paramXml->SetAttribute("type", "float");
+    paramXml->SetAttribute("name", "Y");
+    accessorXml->LinkEndChild(paramXml);
+
+    paramXml = new TiXmlElement("param");
+    paramXml->SetAttribute("type", "float");
+    paramXml->SetAttribute("name", "Z");
+    accessorXml->LinkEndChild(paramXml);
+  }
+}
+
+//////////////////////////////////////////////////
+/// \brief Export scene element, used by ConvertGzToDae
+/// \param[in] _sceneXml Pointer to the scene XML instance
+void ExportScene(TiXmlElement *_sceneXml)
+{
+  TiXmlElement *instanceVisualSceneXml =
+      new TiXmlElement("instance_visual_scene");
+  _sceneXml->LinkEndChild(instanceVisualSceneXml);
+  instanceVisualSceneXml->SetAttribute("url", "#Scene");
+}
+
+//////////////////////////////////////////////////
+/// \brief Export library visual scenes element, used by ConvertGzToDae
+/// \param[in] _libraryVisualScenesXml Pointer to the library visual
+/// scenes XML instance
+/// \param[in] _mesh Output Gazebo mesh
+void ExportVisualScenes(TiXmlElement *_libraryVisualScenesXml,
+    const gazebo::common::Mesh *_mesh)
+{
+  unsigned int subMeshCount =  _mesh->GetSubMeshCount();
+
+  TiXmlElement *visualSceneXml = new TiXmlElement("visual_scene");
+  _libraryVisualScenesXml->LinkEndChild(visualSceneXml);
+  visualSceneXml->SetAttribute("name", "Scene");
+  visualSceneXml->SetAttribute("id", "Scene");
+
+  TiXmlElement *nodeXml = new TiXmlElement("node");
+  visualSceneXml->LinkEndChild(nodeXml);
+  nodeXml->SetAttribute("name", "node");
+  nodeXml->SetAttribute("id", "node");
+
+  for (unsigned int i = 0; i < subMeshCount; ++i)
+  {
+    char meshId[100], materialId[100], attributeValue[100];
+    snprintf(meshId, sizeof(meshId), "mesh_%u", i);
+    snprintf(materialId, sizeof(materialId), "material_%u", i);
+
+    TiXmlElement *instanceGeometryXml = new TiXmlElement("instance_geometry");
+    nodeXml->LinkEndChild(instanceGeometryXml);
+    snprintf(attributeValue, sizeof(attributeValue), "#%s", meshId);
+    instanceGeometryXml->SetAttribute("url", attributeValue);
+
+    const gazebo::common::Material *material =
+        _mesh->GetMaterial(i);
+
+    if (material)
+    {
+      TiXmlElement *bindMaterialXml = new TiXmlElement("bind_material");
+      instanceGeometryXml->LinkEndChild(bindMaterialXml);
+
+      TiXmlElement *techniqueCommonXml = new TiXmlElement("technique_common");
+      bindMaterialXml->LinkEndChild(techniqueCommonXml);
+
+      TiXmlElement *instanceMaterialXml =
+          new TiXmlElement("instance_material");
+      techniqueCommonXml->LinkEndChild(instanceMaterialXml);
+      instanceMaterialXml->SetAttribute("symbol", materialId);
+      snprintf(attributeValue, sizeof(attributeValue), "#%s", materialId);
+      instanceMaterialXml->SetAttribute("target", attributeValue);
+
+      std::string imageString = material->GetTextureImage();
+
+      if (imageString.find("meshes/") != std::string::npos)
+      {
+        TiXmlElement *bindVertexInputXml =
+            new TiXmlElement("bind_vertex_input");
+        instanceMaterialXml->LinkEndChild(bindVertexInputXml);
+        bindVertexInputXml->SetAttribute("semantic", "UVSET0");
+        bindVertexInputXml->SetAttribute("input_semantic", "TEXCOORD");
+      }
+    }
+  }
+}
+
+//////////////////////////////////////////////////
+/// \brief Export library effects element, used by ConvertGzToDae
+/// \param[in] _libraryEffectsXml Pointer to the library effects XML
+/// instance
+/// \param[in] _mesh Output Gazebo mesh
+void ExportEffects(TiXmlElement *_libraryEffectsXml,
+    const gazebo::common::Mesh *_mesh)
+{
+  unsigned int materialCount =  _mesh->GetMaterialCount();
+
+  for (unsigned int i = 0; i < materialCount; ++i)
+  {
+    char id[100];
+    snprintf(id, sizeof(id), "material_%u_fx", i);
+
+    TiXmlElement *effectXml = new TiXmlElement("effect");
+    effectXml->SetAttribute("id", id);
+    _libraryEffectsXml->LinkEndChild(effectXml);
+
+    TiXmlElement *profileCommonXml = new TiXmlElement("profile_COMMON");
+    effectXml->LinkEndChild(profileCommonXml);
+
+    // Image
+    const gazebo::common::Material *material =
+        _mesh->GetMaterial(i);
+    std::string imageString = material->GetTextureImage();
+
+    if (imageString.find("meshes/") != std::string::npos)
+    {
+      TiXmlElement *newParamXml = new TiXmlElement("newparam");
+      snprintf(id, sizeof(id), "image_%u_surface", i);
+      newParamXml->SetAttribute("sid", id);
+      profileCommonXml->LinkEndChild(newParamXml);
+
+      TiXmlElement *surfaceXml = new TiXmlElement("surface");
+      surfaceXml->SetAttribute("type", "2D");
+      newParamXml->LinkEndChild(surfaceXml);
+
+      TiXmlElement *initFromXml = new TiXmlElement("init_from");
+      snprintf(id, sizeof(id), "image_%u", i);
+      initFromXml->LinkEndChild(new TiXmlText(id));
+      surfaceXml->LinkEndChild(initFromXml);
+
+      newParamXml = new TiXmlElement("newparam");
+      snprintf(id, sizeof(id), "image_%u_sampler", i);
+      newParamXml->SetAttribute("sid", id);
+      profileCommonXml->LinkEndChild(newParamXml);
+
+      TiXmlElement *sampler2dXml = new TiXmlElement("sampler2D");
+      newParamXml->LinkEndChild(sampler2dXml);
+
+      TiXmlElement *sourceXml = new TiXmlElement("source");
+      snprintf(id, sizeof(id), "image_%u_surface", i);
+      sourceXml->LinkEndChild(new TiXmlText(id));
+      sampler2dXml->LinkEndChild(sourceXml);
+
+      TiXmlElement *minFilterXml = new TiXmlElement("minfilter");
+      minFilterXml->LinkEndChild(new TiXmlText("LINEAR"));
+      sampler2dXml->LinkEndChild(minFilterXml);
+
+      TiXmlElement *magFilterXml = new TiXmlElement("magfilter");
+      magFilterXml->LinkEndChild(new TiXmlText("LINEAR"));
+      sampler2dXml->LinkEndChild(magFilterXml);
+    }
+
+    TiXmlElement *techniqueXml = new TiXmlElement("technique");
+    techniqueXml->SetAttribute("sid", "COMMON");
+    profileCommonXml->LinkEndChild(techniqueXml);
+
+    // gazebo::common::Material::ShadeMode shadeMode =
+    //    material->GetShadeMode();
+
+    // Using phong for now
+    TiXmlElement *phongXml = new TiXmlElement("phong");
+    techniqueXml->LinkEndChild(phongXml);
+
+    // ambient
+    unsigned int RGBAcolor = material->GetAmbient().GetAsRGBA();
+    float r = ((RGBAcolor >> 24) & 0xFF) / 255.0f;
+    float g = ((RGBAcolor >> 16) & 0xFF) / 255.0f;
+    float b = ((RGBAcolor >> 8) & 0xFF) / 255.0f;
+    float a = (RGBAcolor & 0xFF) / 255.0f;
+
+    TiXmlElement *ambientXml = new TiXmlElement("ambient");
+    phongXml->LinkEndChild(ambientXml);
+
+    TiXmlElement *colorXml = new TiXmlElement("color");
+    snprintf(id, sizeof(id), "%f %f %f %f", r, g, b, a);
+    colorXml->LinkEndChild(new TiXmlText(id));
+    ambientXml->LinkEndChild(colorXml);
+
+    // emission
+    RGBAcolor = material->GetEmissive().GetAsRGBA();
+    r = ((RGBAcolor >> 24) & 0xFF) / 255.0f;
+    g = ((RGBAcolor >> 16) & 0xFF) / 255.0f;
+    b = ((RGBAcolor >> 8) & 0xFF) / 255.0f;
+    a = (RGBAcolor & 0xFF) / 255.0f;
+
+    TiXmlElement *emissionXml = new TiXmlElement("emission");
+    phongXml->LinkEndChild(emissionXml);
+
+    colorXml = new TiXmlElement("color");
+    snprintf(id, sizeof(id), "%f %f %f %f", r, g, b, a);
+    colorXml->LinkEndChild(new TiXmlText(id));
+    emissionXml->LinkEndChild(colorXml);
+
+    // diffuse
+    TiXmlElement *diffuseXml = new TiXmlElement("diffuse");
+    phongXml->LinkEndChild(diffuseXml);
+
+    if (imageString.find("meshes/") != std::string::npos)
+    {
+      TiXmlElement *textureXml = new TiXmlElement("texture");
+      snprintf(id, sizeof(id), "image_%u", i);
+      textureXml->SetAttribute("texture", id);
+      textureXml->SetAttribute("texcoord", "UVSET0");
+      diffuseXml->LinkEndChild(textureXml);
+    }
+    else
+    {
+      RGBAcolor = material->GetDiffuse().GetAsRGBA();
+      r = ((RGBAcolor >> 24) & 0xFF) / 255.0f;
+      g = ((RGBAcolor >> 16) & 0xFF) / 255.0f;
+      b = ((RGBAcolor >> 8) & 0xFF) / 255.0f;
+      a = (RGBAcolor & 0xFF) / 255.0f;
+
+      colorXml = new TiXmlElement("color");
+      snprintf(id, sizeof(id), "%f %f %f %f", r, g, b, a);
+      colorXml->LinkEndChild(new TiXmlText(id));
+      diffuseXml->LinkEndChild(colorXml);
+    }
+
+    // specular
+    RGBAcolor = material->GetSpecular().GetAsRGBA();
+    r = ((RGBAcolor >> 24) & 0xFF) / 255.0f;
+    g = ((RGBAcolor >> 16) & 0xFF) / 255.0f;
+    b = ((RGBAcolor >> 8) & 0xFF) / 255.0f;
+    a = (RGBAcolor & 0xFF) / 255.0f;
+
+    TiXmlElement *specularXml = new TiXmlElement("specular");
+    phongXml->LinkEndChild(specularXml);
+
+    colorXml = new TiXmlElement("color");
+    snprintf(id, sizeof(id), "%f %f %f %f", r, g, b, a);
+    colorXml->LinkEndChild(new TiXmlText(id));
+    specularXml->LinkEndChild(colorXml);
+
+    // transparency
+    double transp = material->GetTransparency();
+
+    TiXmlElement *transparencyXml = new TiXmlElement("transparency");
+    phongXml->LinkEndChild(transparencyXml);
+
+    TiXmlElement *floatXml = new TiXmlElement("float");
+    snprintf(id, sizeof(id), "%f", transp);
+    floatXml->LinkEndChild(new TiXmlText(id));
+    transparencyXml->LinkEndChild(floatXml);
+
+    // shininess
+    double shine = material->GetShininess();
+
+    TiXmlElement *shininessXml = new TiXmlElement("shininess");
+    phongXml->LinkEndChild(shininessXml);
+
+    colorXml = new TiXmlElement("color");
+    snprintf(id, sizeof(id), "%f", shine);
+    colorXml->LinkEndChild(new TiXmlText(id));
+    shininessXml->LinkEndChild(colorXml);
+  }
+}
+
+//////////////////////////////////////////////////
+/// \brief Export library materials element, used by ConvertGzToDae
+/// \param[in] _libraryMaterialsXml Pointer to the library materials XML
+/// instance
+/// \param[in] _mesh Output Gazebo mesh
+void ExportMaterials(TiXmlElement *_libraryMaterialsXml,
+    const gazebo::common::Mesh *_mesh)
+{
+  unsigned int materialCount =  _mesh->GetMaterialCount();
+
+  for (unsigned int i = 0; i < materialCount; ++i)
+  {
+    char id[100];
+    snprintf(id, sizeof(id), "material_%u", i);
+
+    TiXmlElement *materialXml = new TiXmlElement("material");
+    materialXml->SetAttribute("id", id);
+    _libraryMaterialsXml->LinkEndChild(materialXml);
+
+    snprintf(id, sizeof(id), "#material_%u_fx", i);
+    TiXmlElement *instanceEffectXml = new TiXmlElement("instance_effect");
+    instanceEffectXml->SetAttribute("url", id);
+    materialXml->LinkEndChild(instanceEffectXml);
+  }
+}
+
+//////////////////////////////////////////////////
+/// \brief Export library images element, used by ConvertGzToDae
+/// \param[in] _libraryImagesXml Pointer to the library images XML
+/// instance
+/// \param[in] _mesh Output Gazebo mesh
+/// \return integer, number of images
+int ExportImages(TiXmlElement *_libraryImagesXml,
+    const gazebo::common::Mesh *_mesh)
+{
+  unsigned int materialCount =  _mesh->GetMaterialCount();
+
+  int imageCount = 0;
+  for (unsigned int i = 0; i < materialCount; ++i)
+  {
+    const gazebo::common::Material *material =
+        _mesh->GetMaterial(i);
+    std::string imageString = material->GetTextureImage();
+
+    if (imageString.find("meshes/") != std::string::npos)
+    {
+      char id[100];
+      snprintf(id, sizeof(id), "image_%u", i);
+
+      TiXmlElement *imageXml = new TiXmlElement("image");
+      imageXml->SetAttribute("id", id);
+      _libraryImagesXml->LinkEndChild(imageXml);
+
+      TiXmlElement *initFromXml = new TiXmlElement("init_from");
+      initFromXml->LinkEndChild(new TiXmlText(
+        imageString.substr(imageString.find(".."))));
+      imageXml->LinkEndChild(initFromXml);
+
+      imageCount++;
+    }
+  }
+
+  return imageCount;
+}
+
+//////////////////////////////////////////////////
+/// \brief Export library geometries element, used by ConvertGzToDae
+/// \param[in] libraryGeometriesXml Pointer to the library geometries
+/// XML instance
+/// \param[in] _outMesh Output Gazebo mesh
+/// \param[in] _inMesh Input Gazebo mesh
+void ExportGeometries(TiXmlElement *_libraryGeometriesXml,
+    const gazebo::common::Mesh *_outMesh,
+    const gazebo::common::Mesh *_inMesh)
+{
+  unsigned int subMeshCount =  _outMesh->GetSubMeshCount();
+  unsigned int materialCount =  _outMesh->GetMaterialCount();
+
+  for (unsigned int i = 0; i < subMeshCount; ++i)
+  {
+    char meshId[100], materialId[100];
+    snprintf(meshId, sizeof(meshId), "mesh_%u", i);
+    snprintf(materialId, sizeof(materialId), "material_%u", i);
+
+    TiXmlElement *geometryXml = new TiXmlElement("geometry");
+    geometryXml->SetAttribute("id", meshId);
+    _libraryGeometriesXml->LinkEndChild(geometryXml);
+
+    TiXmlElement *meshXml = new TiXmlElement("mesh");
+    geometryXml->LinkEndChild(meshXml);
+
+    const gazebo::common::SubMesh *outSubMesh =
+        _outMesh->GetSubMesh(i);
+    const gazebo::common::SubMesh *inSubMesh =
+        _inMesh->GetSubMesh(i);
+
+    ExportGeometrySource(outSubMesh, meshXml, POSITION, meshId);
+    ExportGeometrySource(outSubMesh, meshXml, NORMAL, meshId);
+    if (materialCount != 0)
+    {
+      // Diff from ColladaExporter
+      // ExportGeometrySource(outSubMesh, meshXml, UVMAP, meshId);
+      ExportTextureSource(outSubMesh, inSubMesh, meshXml, meshId);
+    }
+
+    char attributeValue[100];
+
+    TiXmlElement *verticesXml = new TiXmlElement("vertices");
+    meshXml->LinkEndChild(verticesXml);
+    snprintf(attributeValue, sizeof(attributeValue), "%s-Vertex", meshId);
+    verticesXml->SetAttribute("id", attributeValue);
+    verticesXml->SetAttribute("name", attributeValue);
+
+    TiXmlElement *inputXml = new TiXmlElement("input");
+    verticesXml->LinkEndChild(inputXml);
+    inputXml->SetAttribute("semantic", "POSITION");
+    snprintf(attributeValue, sizeof(attributeValue), "#%s-Positions", meshId);
+    inputXml->SetAttribute("source", attributeValue);
+
+    unsigned int indexCount = outSubMesh->GetIndexCount();
+
+    TiXmlElement *trianglesXml = new TiXmlElement("triangles");
+    meshXml->LinkEndChild(trianglesXml);
+    trianglesXml->SetAttribute("count", indexCount/3);
+    if (materialCount != 0)
+    {
+      trianglesXml->SetAttribute("material", materialId);
+    }
+
+    inputXml = new TiXmlElement("input");
+    trianglesXml->LinkEndChild(inputXml);
+    inputXml->SetAttribute("offset", 0);
+    inputXml->SetAttribute("semantic", "VERTEX");
+    snprintf(attributeValue, sizeof(attributeValue), "#%s-Vertex", meshId);
+    inputXml->SetAttribute("source", attributeValue);
+
+    inputXml = new TiXmlElement("input");
+    trianglesXml->LinkEndChild(inputXml);
+    inputXml->SetAttribute("offset", 1);
+    inputXml->SetAttribute("semantic", "NORMAL");
+    snprintf(attributeValue, sizeof(attributeValue), "#%s-Normals", meshId);
+    inputXml->SetAttribute("source", attributeValue);
+
+    if (materialCount != 0)
+    {
+      inputXml = new TiXmlElement("input");
+      trianglesXml->LinkEndChild(inputXml);
+      inputXml->SetAttribute("offset", 2);
+      inputXml->SetAttribute("semantic", "TEXCOORD");
+      snprintf(attributeValue, sizeof(attributeValue), "#%s-UVMap", meshId);
+      inputXml->SetAttribute("source", attributeValue);
+    }
+
+    std::ostringstream fillData;
+    for (unsigned int j = 0; j < indexCount; ++j)
+    {
+      fillData << outSubMesh->GetIndex(j) << " "
+               << outSubMesh->GetIndex(j) << " ";
+      if (materialCount != 0)
+      {
+        // Diff from ColladaExporter
+        // fillData << outSubMesh->GetIndex(j) << " ";
+        fillData << j << " ";
+      }
+    }
+
+    TiXmlElement *pXml = new TiXmlElement("p");
+    trianglesXml->LinkEndChild(pXml);
+    pXml->LinkEndChild(new TiXmlText(fillData.str().c_str()));
+  }
+}
+
+//////////////////////////////////////////////////
+/// \brief Export asset element, used by ConvertGzToDae
+/// \param[in] _assetXml Pointer to the asset XML instance
+void ExportAsset(TiXmlElement *_assetXml)
+{
+  TiXmlElement *unitXml = new TiXmlElement("unit");
+  unitXml->SetAttribute("meter", "1");
+  unitXml->SetAttribute("name", "meter");
+  _assetXml->LinkEndChild(unitXml);
+
+  TiXmlElement *upAxisXml = new TiXmlElement("up_axis");
+  upAxisXml->LinkEndChild(new TiXmlText("Z_UP"));
+  _assetXml->LinkEndChild(upAxisXml);
+}
+
+//////////////////////////////////////////////////
+/// \brief Convert Gazebo mesh to Collada XML Document
+/// \param[in] _inGz Input Gazebo mesh
+/// \param[in] _outGz Output Gazebo mesh
+/// \return Collada XML Document
+TiXmlDocument ConvertGzToDae(const gazebo::common::Mesh *_inGz,
+    const gazebo::common::Mesh *_outGz)
+{
+  unsigned int materialCount = _outGz->GetMaterialCount();
+
+  // Input and output collada files
+  TiXmlDocument outDae;
+
+  // XML declaration
+  TiXmlDeclaration *declarationXml = new TiXmlDeclaration("1.0", "utf-8", "");
+  outDae.LinkEndChild(declarationXml);
+
+  // Collada element
+  TiXmlElement *colladaXml = new TiXmlElement("COLLADA");
+  outDae.LinkEndChild(colladaXml);
+  colladaXml->SetAttribute("version", "1.4.1");
+  colladaXml->SetAttribute("xmlns",
+      "http://www.collada.org/2005/11/COLLADASchema");
+
+  // Asset element
+  TiXmlElement *assetXml = new TiXmlElement("asset");
+  ExportAsset(assetXml);
+  colladaXml->LinkEndChild(assetXml);
+
+  // Library geometries element
+  TiXmlElement *libraryGeometriesXml = new TiXmlElement("library_geometries");
+  ExportGeometries(libraryGeometriesXml, _outGz, _inGz);
+  colladaXml->LinkEndChild(libraryGeometriesXml);
+
+  if (materialCount != 0)
+  {
+    // Library images element
+    TiXmlElement *libraryImagesXml = new TiXmlElement("library_images");
+    int imageCount = ExportImages(libraryImagesXml, _outGz);
+    if (imageCount != 0)
+    {
+      colladaXml->LinkEndChild(libraryImagesXml);
+    }
+
+    // Library materials element
+    TiXmlElement *libraryMaterialsXml = new TiXmlElement("library_materials");
+    ExportMaterials(libraryMaterialsXml, _outGz);
+    colladaXml->LinkEndChild(libraryMaterialsXml);
+
+    // Library effects element
+    TiXmlElement *libraryEffectsXml = new TiXmlElement("library_effects");
+    ExportEffects(libraryEffectsXml, _outGz);
+    colladaXml->LinkEndChild(libraryEffectsXml);
+  }
+
+  // Library visual scenes element
+  TiXmlElement *libraryVisualScenesXml =
+      new TiXmlElement("library_visual_scenes");
+  ExportVisualScenes(libraryVisualScenesXml, _outGz);
+  colladaXml->LinkEndChild(libraryVisualScenesXml);
+
+  // Scene element
+  TiXmlElement *sceneXml = new TiXmlElement("scene");
+  ExportScene(sceneXml);
+  colladaXml->LinkEndChild(sceneXml);
+
+  return(outDae);
+}
+
+//////////////////////////////////////////////////
+/// \brief Fill Gazebo SubMesh's faces, used by ConvertGtsToGz
+/// \param[in] _t Pointer to GTS triangle
+/// \param[in] _data Pointer to data: Gazebo SubMesh, index, GHashTable
+void FillFace(GtsTriangle *_t, gpointer *_data)
+{
+  gazebo::common::SubMesh *subMesh =
+      reinterpret_cast<gazebo::common::SubMesh *>(_data[0]);
+  GHashTable*vIndex = reinterpret_cast<GHashTable *>(_data[2]);
+
+  GtsVertex *v1, *v2, *v3;
+  gts_triangle_vertices(_t, &v1, &v2, &v3);
+
+  subMesh->AddIndex(GPOINTER_TO_UINT(g_hash_table_lookup(vIndex, v1)));
+  subMesh->AddIndex(GPOINTER_TO_UINT(g_hash_table_lookup(vIndex, v2)));
+  subMesh->AddIndex(GPOINTER_TO_UINT(g_hash_table_lookup(vIndex, v3)));
+}
+
+//////////////////////////////////////////////////
+/// \brief Fill Gazebo SubMesh's vertices, used by ConvertGtsToGz
+/// \param[in] _p Pointer to GTS point (vertex)
+/// \param[in] _data Pointer to data: Gazebo SubMesh, index, GHashTable
+void FillVertex(GtsPoint *_p, gpointer *_data)
+{
+  // create a Gazebo vertex from GTS_POINT and add it to the submesh
+  gazebo::common::SubMesh *subMesh =
+      reinterpret_cast<gazebo::common::SubMesh *>(_data[0]);
+  GHashTable*vIndex = reinterpret_cast<GHashTable *>(_data[2]);
+  subMesh->AddVertex(GTS_POINT(_p)->x, GTS_POINT(_p)->y, GTS_POINT(_p)->z);
+
+  // add the normals, they are not correct now, but will be recalculated later
+  subMesh->AddNormal(0, 0, 1);
+
+  // fill the hash table which will later be used for adding indices to the
+  // submesh in the FillFace function.
+  g_hash_table_insert(vIndex, _p,
+      GUINT_TO_POINTER((*(reinterpret_cast<guint *>(_data[1])))++));
+}
+
+//////////////////////////////////////////////////
+/// \brief Convert GTS surface to Gazebo SubMesh
+/// \param[in] _surface Pointer to GTS surface
+/// \param[in] _subMesh Pointer to Gazebo SubMesh
+void ConvertGtsToGz(GtsSurface *_surface, gazebo::common::SubMesh *_subMesh)
+{
+  unsigned int n;
+  gpointer data[3];
+  GHashTable *vIndex = g_hash_table_new(NULL, NULL);
+
+  n = 0;
+  data[0] = _subMesh;
+  data[1] = &n;
+  data[2] = vIndex;
+  n = 0;
+  gts_surface_foreach_vertex(_surface, (GtsFunc)FillVertex, data);
+  n = 0;
+  gts_surface_foreach_face(_surface, (GtsFunc)FillFace, data);
+  g_hash_table_destroy(vIndex);
+}
+
+/////////////////////////////////////////////////
+/// \brief Coarsen GTS mesh
+/// \param[in] _inOutGts Pointer to GTS surface
+/// \param[in] _desiredPercentage Desired percentage of edges
+/// \return Success or failure
+int CoarsenGts(GtsSurface *_inOutGts, double _desiredPercentage)
+{
+  // Number of edges
+  guint edgesBefore = gts_surface_edge_number(_inOutGts);
+  std::cout << "Edges before: " << edgesBefore << std::endl;
+
+  if (edgesBefore < 300)
+  {
+    std::cout << "There are less than 300 edges. Not simplifying.\n";
+    return -1;
+  }
+
+  // Default cost function COST_OPTIMIZED
+  GtsKeyFunc cost_func = (GtsKeyFunc) gts_volume_optimized_cost;
+  GtsVolumeOptimizedParams params = { 0.5, 0.5, 0. };
+  gpointer cost_data = &params;
+
+  // Default coarsen function OPTIMIZED
+  GtsCoarsenFunc coarsen_func = (GtsCoarsenFunc) gts_volume_optimized_vertex;
+  gpointer coarsen_data = &params;
+
+  // Stop function STOP_NUMBER
+  GtsStopFunc stop_func = (GtsStopFunc) gts_coarsen_stop_number;
+  guint number = edgesBefore *_desiredPercentage/100;
+  gpointer stop_data = &number;
+  gdouble fold = PI/180.;
+
+  // Coarsen
+  gts_surface_coarsen (_inOutGts,
+      cost_func,    cost_data,
+      coarsen_func, coarsen_data,
+      stop_func,    stop_data,
+      fold);
+
+  // Number of edges
+  guint edgesAfter = gts_surface_edge_number(_inOutGts);
+  double obtainedPercentage = (double)100*edgesAfter/edgesBefore;
+  std::cout << "Edges after: " << edgesAfter << " ("
+            << obtainedPercentage << "%)" << std::endl;
+
+  if (obtainedPercentage > _desiredPercentage*1.5)
+  {
+    std::cout << "It wasn't possible to significantly reduce the mesh. "
+              << "Not simplifying." << std::endl;
+    return -1;
+  }
+
+  return 1;
+}
+
+//////////////////////////////////////////////////
+/// \brief Merge vertices which are close together, used by ConvertGzToGts
+/// \param[in] _vertices Pointer to vertices array
+/// \param[in] _epsilon Maximum distance to merge
+void MergeVertices(GPtrArray *_vertices, double _epsilon)
+{
+  GPtrArray *array;
+  GNode *kdtree;
+  GtsVertex **verticesData = reinterpret_cast<GtsVertex **>(_vertices->pdata);
+  array = g_ptr_array_new();
+  for (unsigned int i = 0; i < _vertices->len; ++i)
+    g_ptr_array_add(array, verticesData[i]);
+  kdtree = gts_kdtree_new(array, NULL);
+  g_ptr_array_free(array, true);
+
+  // for each vertex, do a bbox kdtree search to find nearby vertices within
+  // _epsilon, merge vertices if they are within the bbox
+  for (unsigned int i = 0; i < _vertices->len; ++i)
+  {
+    GtsVertex *v = reinterpret_cast<GtsVertex *>(verticesData[i]);
+
+    // make sure vertex v is active (because they could be marked inactive
+    // due to previous merge operation)
+    if (!GTS_OBJECT (v)->reserved)
+    {
+      GtsBBox *bbox;
+      GSList *selected, *j;
+
+      // build bounding box
+      bbox = gts_bbox_new(gts_bbox_class(),
+          v,
+          GTS_POINT(v)->x - _epsilon,
+          GTS_POINT(v)->y - _epsilon,
+          GTS_POINT(v)->z - _epsilon,
+          GTS_POINT(v)->x + _epsilon,
+          GTS_POINT(v)->y + _epsilon,
+          GTS_POINT(v)->z + _epsilon);
+
+      // select vertices which are inside bbox using kdtree
+      j = selected = gts_kdtree_range(kdtree, bbox, NULL);
+      while (j)
+      {
+        GtsVertex *sv = reinterpret_cast<GtsVertex *>(j->data);
+        // mark sv as inactive (merged)
+        if (sv != v && !GTS_OBJECT(sv)->reserved)
+          GTS_OBJECT(sv)->reserved = v;
+        j = j->next;
+      }
+      g_slist_free(selected);
+      gts_object_destroy(GTS_OBJECT(bbox));
+    }
+  }
+
+  gts_kdtree_destroy(kdtree);
+
+  // destroy inactive vertices
+  // we want to control vertex destruction
+  gts_allow_floating_vertices = true;
+  for (unsigned int i = 0; i < _vertices->len; ++i)
+  {
+    GtsVertex *v = reinterpret_cast<GtsVertex *>(verticesData[i]);
+    // v is inactive
+    if (GTS_OBJECT(v)->reserved)
+    {
+      verticesData[i] =
+          reinterpret_cast<GtsVertex *>(GTS_OBJECT(v)->reserved);
+      gts_object_destroy(GTS_OBJECT(v));
+    }
+  }
+  gts_allow_floating_vertices = false;
+}
+
+//////////////////////////////////////////////////
+/// \brief Convert Gazebo mesh to GTS mesh
+/// \param[in] _subMesh Pointer to Gazebo SubMesh
+/// \param[in] _surface Pointer to GTS surface
+void ConvertGzToGts(const gazebo::common::SubMesh *_subMesh,
+    GtsSurface *_surface)
+{
+  if (!_surface)
+  {
+    std::cout << ": Surface is NULL\n";
+    return;
+  }
+
+  GtsSurface *subSurface = gts_surface_new(
+    gts_surface_class(), gts_face_class(), gts_edge_class(),
+    gts_vertex_class());
+
+  unsigned int indexCount = _subMesh->GetIndexCount();
+  if (_subMesh->GetVertexCount() <= 2)
+    return;
+
+  GPtrArray *vertices = g_ptr_array_new();
+  for (unsigned int j = 0; j < _subMesh->GetVertexCount(); ++j)
+  {
+    gazebo::math::Vector3 vertex = _subMesh->GetVertex(j);
+    g_ptr_array_add(vertices, gts_vertex_new(gts_vertex_class(), vertex.x,
+        vertex.y, vertex.z));
+  }
+
+  MergeVertices(vertices, 1e-7);
+
+  GtsVertex **verticesData =
+      reinterpret_cast<GtsVertex **>(vertices->pdata);
+  for (unsigned int j = 0; j < indexCount/3; ++j)
+  {
+    // if vertices on the same GtsSegment, this segment. Else, NULL.
+    GtsEdge *e1 = GTS_EDGE(gts_vertices_are_connected(
+        verticesData[_subMesh->GetIndex(3*j)],
+        verticesData[_subMesh->GetIndex(3*j+1)]));
+    GtsEdge *e2 = GTS_EDGE(gts_vertices_are_connected(
+        verticesData[_subMesh->GetIndex(3*j+1)],
+        verticesData[_subMesh->GetIndex(3*j+2)]));
+    GtsEdge *e3 = GTS_EDGE(gts_vertices_are_connected(
+        verticesData[_subMesh->GetIndex(3*j+2)],
+        verticesData[_subMesh->GetIndex(3*j)]));
+
+    // If vertices are different and not connected
+    if (e1 == NULL && verticesData[_subMesh->GetIndex(3*j)]
+        != verticesData[_subMesh->GetIndex(3*j+1)])
+    {
+      e1 = gts_edge_new(subSurface->edge_class,
+          verticesData[_subMesh->GetIndex(3*j)],
+          verticesData[_subMesh->GetIndex(3*j+1)]);
+    }
+    if (e2 == NULL && verticesData[_subMesh->GetIndex(3*j+1)]
+        != verticesData[_subMesh->GetIndex(3*j+2)])
+    {
+      e2 = gts_edge_new(subSurface->edge_class,
+          verticesData[_subMesh->GetIndex(3*j+1)],
+          verticesData[_subMesh->GetIndex(3*j+2)]);
+    }
+    if (e3 == NULL && verticesData[_subMesh->GetIndex(3*j+2)]
+        != verticesData[_subMesh->GetIndex(3*j)])
+    {
+      e3 = gts_edge_new(subSurface->edge_class,
+          verticesData[_subMesh->GetIndex(3*j+2)],
+          verticesData[_subMesh->GetIndex(3*j)]);
+    }
+
+    // If all 3 edges are defined and different
+    if (e1 != NULL && e2 != NULL && e3 != NULL &&
+        e1 != e2 && e2 != e3 && e1 != e3)
+    {
+      if (GTS_SEGMENT (e1)->v1 == GTS_SEGMENT (e2)->v1)
+      {
+        if (!gts_segment_connect (GTS_SEGMENT (e3),
+            GTS_SEGMENT (e1)->v2,
+            GTS_SEGMENT (e2)->v2))
+          continue;
+      }
+      else if (GTS_SEGMENT (e1)->v2 == GTS_SEGMENT (e2)->v1)
+      {
+        if (!gts_segment_connect (GTS_SEGMENT (e3),
+            GTS_SEGMENT (e1)->v1,
+            GTS_SEGMENT (e2)->v2))
+          continue;
+      }
+      else if (GTS_SEGMENT (e1)->v2 == GTS_SEGMENT (e2)->v2)
+      {
+        if (!gts_segment_connect (GTS_SEGMENT (e3),
+            GTS_SEGMENT (e1)->v1,
+            GTS_SEGMENT (e2)->v1))
+          continue;
+      }
+      else if (GTS_SEGMENT (e1)->v1 == GTS_SEGMENT (e2)->v2)
+      {
+        if (!gts_segment_connect (GTS_SEGMENT (e3),
+            GTS_SEGMENT (e1)->v2,
+            GTS_SEGMENT (e2)->v1))
+          continue;
+      }
+
+      GtsFace *face = gts_face_new(subSurface->face_class, e1, e2, e3);
+      if (!gts_triangle_is_duplicate(&face->triangle))
+        gts_surface_add_face(subSurface, face);
+    }
+    else
+    {
+      std::cout << ": Ignoring degenerate facet!\n";
+    }
+  }
+  gts_surface_merge(_surface, subSurface);
+}
+
+/////////////////////////////////////////////////
+int main(int argc, char **argv)
+{
+  if (argc < 3)
+  {
+    std::cout << "Missing argument. Please specify a collada file and "
+              << "the desired percentage of edges. For 20%, write 20."
+              << std::endl;
+    return -1;
+  }
+  if (atoi(argv[2]) < 0 || atoi(argv[2]) > 100)
+  {
+    std::cout << "The percentage must be between 0 and 100"
+              << std::endl;
+    return -1;
+  }
+
+  TiXmlDocument inDae(argv[1]);
+  if (!inDae.LoadFile())
+  {
+    std::cout << "Could not open dae file." << std::endl;
+    return -1;
+  }
+
+  std::string filename = argv[1];
+  filename = filename.substr(0, filename.find(".dae"));
+
+  double desiredPercentage = atoi (argv[2]);
+
+  // COLLADA to GAZEBO
+  gazebo::common::ColladaLoader loader;
+  gazebo::common::Mesh *inGz = loader.Load(argv[1]);
+/*
+  // export original Gz mesh to Dae
+  TiXmlDocument exportInDae;
+  exportInDae = ConvertGzToDae(inGz,inGz);
+  exportInDae.SaveFile(filename+"_original.dae");
+
+  return 0;
+*/
+
+  gazebo::common::Mesh *outGz = new gazebo::common::Mesh();
+
+  for (int s = 0; s < inGz->GetSubMeshCount(); ++s)
+  {
+    const gazebo::common::SubMesh *inSubMesh = inGz->GetSubMesh(s);
+
+    // GAZEBO to GTS
+    GtsSurface *inOutGts;
+    inOutGts = gts_surface_new(gts_surface_class(), gts_face_class(),
+        gts_edge_class(), gts_vertex_class());
+    ConvertGzToGts(inSubMesh, inOutGts);
+
+    // SIMPLIFICATION
+    if (!CoarsenGts(inOutGts, desiredPercentage))
+    {
+      return -1;
+    }
+
+    // GTS to GAZEBO
+    // Vertices:  inOutGts
+    // Normals:   RecalculateNormals
+    // TexCoords: none
+    // Materials: inGz
+    gazebo::common::SubMesh *outSubMesh = new gazebo::common::SubMesh();
+    outGz->AddSubMesh(outSubMesh);
+    ConvertGtsToGz(inOutGts, outSubMesh);
+
+    gts_object_destroy(GTS_OBJECT(inOutGts));
+  }
+
+  // Calculate normals
+  outGz->RecalculateNormals();
+
+  // Add material
+  for (unsigned int m = 0; m < inGz->GetMaterialCount(); ++m)
+  {
+    const gazebo::common::Material *inMaterial = inGz->GetMaterial(m);
+    gazebo::common::Material *outMaterial = new gazebo::common::Material();
+
+    outMaterial->SetTextureImage(inMaterial->GetTextureImage());
+    outMaterial->SetAmbient(inMaterial->GetAmbient());
+    outMaterial->SetDiffuse(inMaterial->GetDiffuse());
+    outMaterial->SetSpecular(inMaterial->GetSpecular());
+    outMaterial->SetEmissive(inMaterial->GetEmissive());
+    outMaterial->SetTransparency(inMaterial->GetTransparency());
+    outMaterial->SetShininess(inMaterial->GetShininess());
+
+    outGz->AddMaterial(outMaterial);
+  }
+
+
+  // GAZEBO to COLLADA
+
+//#if GAZEBO_MAJOR_VERSION < 4
+// ConvertGzToDae is equal to ColladaExporter, except for ExportTextureSource,
+// which uses Collada indexation for UV coordinates
+  TiXmlDocument outDae;
+  outDae = ConvertGzToDae(inGz, outGz);
+  outDae.SaveFile(filename+"_coarse.dae");
+//#else
+//  outGz doesn't have texture coordinates, so this is incomplete
+//  gazebo::common::MeshManager::Instance()->
+//        Export(outGz, filename+"_coarse", "dae", false);
+//#endif
+
+  return 0;
+}
diff --git a/tools/gzthumbnails.sh b/tools/gzthumbnails.sh
new file mode 100755
index 0000000000000000000000000000000000000000..99cade4b04342b87db543696ecbcfc2bfcc9c090
--- /dev/null
+++ b/tools/gzthumbnails.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+ASSETS="$DIR/../http/client/assets"
+
+for dir in $ASSETS/*/
+do
+    dir=${dir%*/}
+    echo "Creating thumbnail for ${dir##*/}"
+    rm -rf $ASSETS/${dir##*/}/thumbnails
+    if [[ -f $ASSETS/${dir##*/}/model.sdf ]]; then
+	# generate thumbnails with green bg
+        gzserver -s libModelPropShop.so $DIR/green.world --propshop-save "$ASSETS/${dir##*/}/thumbnails" --propshop-model "$ASSETS/${dir##*/}/model.sdf"
+	# make green bg transparent
+	convert $ASSETS/${dir##*/}/thumbnails/1.png -fuzz 30% -transparent '#00ff00' $ASSETS/${dir##*/}/thumbnails/0.png
+	# crop transparent ends
+	convert $ASSETS/${dir##*/}/thumbnails/0.png -trim $ASSETS/${dir##*/}/thumbnails/0.png
+	# add shadow
+	convert -background none -fill black \
+              $ASSETS/${dir##*/}/thumbnails/0.png \
+          \( +clone -background black  -shadow 100x10+0+0 \) +swap \
+          -background none   -layers merge +repage  $ASSETS/${dir##*/}/thumbnails/0.png
+	# remove extra files
+	rm $ASSETS/${dir##*/}/thumbnails/1.png
+	rm $ASSETS/${dir##*/}/thumbnails/2.png
+	rm $ASSETS/${dir##*/}/thumbnails/3.png
+	rm $ASSETS/${dir##*/}/thumbnails/4.png
+	rm $ASSETS/${dir##*/}/thumbnails/5.png
+    fi
+done
diff --git a/tools/nanoflann.hpp b/tools/nanoflann.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..af0239ed93de8e183a258eab7dbd8fa108dc9141
--- /dev/null
+++ b/tools/nanoflann.hpp
@@ -0,0 +1,1449 @@
+/***********************************************************************
+ * Software License Agreement (BSD License)
+ *
+ * Copyright 2008-2009  Marius Muja (mariusm@cs.ubc.ca). All rights reserved.
+ * Copyright 2008-2009  David G. Lowe (lowe@cs.ubc.ca). All rights reserved.
+ * Copyright 2011-2013  Jose Luis Blanco (joseluisblancoc@gmail.com).
+ *   All rights reserved.
+ *
+ * THE BSD LICENSE
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *************************************************************************/
+
+#ifndef  NANOFLANN_HPP_
+#define  NANOFLANN_HPP_
+
+#include <vector>
+#include <cassert>
+#include <algorithm>
+#include <stdexcept>
+#include <cstdio>  // for fwrite()
+#include <cmath>   // for fabs(),...
+#include <limits>
+
+// Avoid conflicting declaration of min/max macros in windows headers
+#if !defined(NOMINMAX) && (defined(_WIN32) || defined(_WIN32_)  || defined(WIN32) || defined(_WIN64))
+# define NOMINMAX
+# ifdef max
+#  undef   max
+#  undef   min
+# endif
+#endif
+
+namespace nanoflann
+{
+/** @addtogroup nanoflann_grp nanoflann C++ library for ANN
+  *  @{ */
+
+    /** Library version: 0xMmP (M=Major,m=minor,P=path) */
+    #define NANOFLANN_VERSION 0x117
+
+    /** @addtogroup result_sets_grp Result set classes
+      *  @{ */
+    template <typename DistanceType, typename IndexType = size_t, typename CountType = size_t>
+    class KNNResultSet
+    {
+        IndexType * indices;
+        DistanceType* dists;
+        CountType capacity;
+        CountType count;
+
+    public:
+        inline KNNResultSet(CountType capacity_) : capacity(capacity_), count(0)
+        {
+        }
+
+        inline void init(IndexType* indices_, DistanceType* dists_)
+        {
+            indices = indices_;
+            dists = dists_;
+            count = 0;
+            dists[capacity-1] = (std::numeric_limits<DistanceType>::max)();
+        }
+
+        inline CountType size() const
+        {
+            return count;
+        }
+
+        inline bool full() const
+        {
+            return count == capacity;
+        }
+
+
+        inline void addPoint(DistanceType dist, IndexType index)
+        {
+            CountType i;
+            for (i=count; i>0; --i) {
+#ifdef NANOFLANN_FIRST_MATCH   // If defined and two poins have the same distance, the one with the lowest-index will be returned first.
+                if ( (dists[i-1]>dist) || ((dist==dists[i-1])&&(indices[i-1]>index)) ) {
+#else
+                if (dists[i-1]>dist) {
+#endif
+                    if (i<capacity) {
+                        dists[i] = dists[i-1];
+                        indices[i] = indices[i-1];
+                    }
+                }
+                else break;
+            }
+            if (i<capacity) {
+                dists[i] = dist;
+                indices[i] = index;
+            }
+            if (count<capacity) count++;
+        }
+
+        inline DistanceType worstDist() const
+        {
+            return dists[capacity-1];
+        }
+    };
+
+
+    /**
+     * A result-set class used when performing a radius based search.
+     */
+    template <typename DistanceType, typename IndexType = size_t>
+    class RadiusResultSet
+    {
+    public:
+        const DistanceType radius;
+
+        std::vector<std::pair<IndexType,DistanceType> >& m_indices_dists;
+
+        inline RadiusResultSet(DistanceType radius_, std::vector<std::pair<IndexType,DistanceType> >& indices_dists) : radius(radius_), m_indices_dists(indices_dists)
+        {
+            init();
+        }
+
+        inline ~RadiusResultSet() { }
+
+        inline void init() { clear(); }
+        inline void clear() { m_indices_dists.clear(); }
+
+        inline size_t size() const { return m_indices_dists.size(); }
+
+        inline bool full() const { return true; }
+
+        inline void addPoint(DistanceType dist, IndexType index)
+        {
+            if (dist<radius)
+                m_indices_dists.push_back(std::make_pair(index,dist));
+        }
+
+        inline DistanceType worstDist() const { return radius; }
+
+        /** Clears the result set and adjusts the search radius. */
+        inline void set_radius_and_clear( const DistanceType r )
+        {
+            radius = r;
+            clear();
+        }
+
+        /**
+         * Find the worst result (furtherest neighbor) without copying or sorting
+         * Pre-conditions: size() > 0
+         */
+        std::pair<IndexType,DistanceType> worst_item() const
+        {
+           if (m_indices_dists.empty()) throw std::runtime_error("Cannot invoke RadiusResultSet::worst_item() on an empty list of results.");
+           typedef typename std::vector<std::pair<IndexType,DistanceType> >::const_iterator DistIt;
+           DistIt it = std::max_element(m_indices_dists.begin(), m_indices_dists.end());
+           return *it;
+        }
+    };
+
+    /** operator "<" for std::sort() */
+    struct IndexDist_Sorter
+    {
+        /** PairType will be typically: std::pair<IndexType,DistanceType> */
+        template <typename PairType>
+        inline bool operator()(const PairType &p1, const PairType &p2) const {
+            return p1.second < p2.second;
+        }
+    };
+
+    /** @} */
+
+
+    /** @addtogroup loadsave_grp Load/save auxiliary functions
+      * @{ */
+    template<typename T>
+    void save_value(FILE* stream, const T& value, size_t count = 1)
+    {
+        fwrite(&value, sizeof(value),count, stream);
+    }
+
+    template<typename T>
+    void save_value(FILE* stream, const std::vector<T>& value)
+    {
+        size_t size = value.size();
+        fwrite(&size, sizeof(size_t), 1, stream);
+        fwrite(&value[0], sizeof(T), size, stream);
+    }
+
+    template<typename T>
+    void load_value(FILE* stream, T& value, size_t count = 1)
+    {
+        size_t read_cnt = fread(&value, sizeof(value), count, stream);
+        if (read_cnt != count) {
+            throw std::runtime_error("Cannot read from file");
+        }
+    }
+
+
+    template<typename T>
+    void load_value(FILE* stream, std::vector<T>& value)
+    {
+        size_t size;
+        size_t read_cnt = fread(&size, sizeof(size_t), 1, stream);
+        if (read_cnt!=1) {
+            throw std::runtime_error("Cannot read from file");
+        }
+        value.resize(size);
+        read_cnt = fread(&value[0], sizeof(T), size, stream);
+        if (read_cnt!=size) {
+            throw std::runtime_error("Cannot read from file");
+        }
+    }
+    /** @} */
+
+
+    /** @addtogroup metric_grp Metric (distance) classes
+      * @{ */
+
+    template<typename T> inline T abs(T x) { return (x<0) ? -x : x; }
+    template<> inline int abs<int>(int x) { return ::abs(x); }
+    template<> inline float abs<float>(float x) { return fabsf(x); }
+    template<> inline double abs<double>(double x) { return fabs(x); }
+    template<> inline long double abs<long double>(long double x) { return fabsl(x); }
+
+    /** Manhattan distance functor (generic version, optimized for high-dimensionality data sets).
+      *  Corresponding distance traits: nanoflann::metric_L1
+      * \tparam T Type of the elements (e.g. double, float, uint8_t)
+      * \tparam DistanceType Type of distance variables (must be signed) (e.g. float, double, int64_t)
+      */
+    template<class T, class DataSource, typename _DistanceType = T>
+    struct L1_Adaptor
+    {
+        typedef T ElementType;
+        typedef _DistanceType DistanceType;
+
+        const DataSource &data_source;
+
+        L1_Adaptor(const DataSource &_data_source) : data_source(_data_source) { }
+
+        inline DistanceType operator()(const T* a, const size_t b_idx, size_t size, DistanceType worst_dist = -1) const
+        {
+            DistanceType result = DistanceType();
+            const T* last = a + size;
+            const T* lastgroup = last - 3;
+            size_t d = 0;
+
+            /* Process 4 items with each loop for efficiency. */
+            while (a < lastgroup) {
+                const DistanceType diff0 = nanoflann::abs(a[0] - data_source.kdtree_get_pt(b_idx,d++));
+                const DistanceType diff1 = nanoflann::abs(a[1] - data_source.kdtree_get_pt(b_idx,d++));
+                const DistanceType diff2 = nanoflann::abs(a[2] - data_source.kdtree_get_pt(b_idx,d++));
+                const DistanceType diff3 = nanoflann::abs(a[3] - data_source.kdtree_get_pt(b_idx,d++));
+                result += diff0 + diff1 + diff2 + diff3;
+                a += 4;
+                if ((worst_dist>0)&&(result>worst_dist)) {
+                    return result;
+                }
+            }
+            /* Process last 0-3 components.  Not needed for standard vector lengths. */
+            while (a < last) {
+                result += nanoflann::abs( *a++ - data_source.kdtree_get_pt(b_idx,d++) );
+            }
+            return result;
+        }
+
+        template <typename U, typename V>
+        inline DistanceType accum_dist(const U a, const V b, int ) const
+        {
+            return nanoflann::abs(a-b);
+        }
+    };
+
+    /** Squared Euclidean distance functor (generic version, optimized for high-dimensionality data sets).
+      *  Corresponding distance traits: nanoflann::metric_L2
+      * \tparam T Type of the elements (e.g. double, float, uint8_t)
+      * \tparam DistanceType Type of distance variables (must be signed) (e.g. float, double, int64_t)
+      */
+    template<class T, class DataSource, typename _DistanceType = T>
+    struct L2_Adaptor
+    {
+        typedef T ElementType;
+        typedef _DistanceType DistanceType;
+
+        const DataSource &data_source;
+
+        L2_Adaptor(const DataSource &_data_source) : data_source(_data_source) { }
+
+        inline DistanceType operator()(const T* a, const size_t b_idx, size_t size, DistanceType worst_dist = -1) const
+        {
+            DistanceType result = DistanceType();
+            const T* last = a + size;
+            const T* lastgroup = last - 3;
+            size_t d = 0;
+
+            /* Process 4 items with each loop for efficiency. */
+            while (a < lastgroup) {
+                const DistanceType diff0 = a[0] - data_source.kdtree_get_pt(b_idx,d++);
+                const DistanceType diff1 = a[1] - data_source.kdtree_get_pt(b_idx,d++);
+                const DistanceType diff2 = a[2] - data_source.kdtree_get_pt(b_idx,d++);
+                const DistanceType diff3 = a[3] - data_source.kdtree_get_pt(b_idx,d++);
+                result += diff0 * diff0 + diff1 * diff1 + diff2 * diff2 + diff3 * diff3;
+                a += 4;
+                if ((worst_dist>0)&&(result>worst_dist)) {
+                    return result;
+                }
+            }
+            /* Process last 0-3 components.  Not needed for standard vector lengths. */
+            while (a < last) {
+                const DistanceType diff0 = *a++ - data_source.kdtree_get_pt(b_idx,d++);
+                result += diff0 * diff0;
+            }
+            return result;
+        }
+
+        template <typename U, typename V>
+        inline DistanceType accum_dist(const U a, const V b, int ) const
+        {
+            return (a-b)*(a-b);
+        }
+    };
+
+    /** Squared Euclidean distance functor (suitable for low-dimensionality datasets, like 2D or 3D point clouds)
+      *  Corresponding distance traits: nanoflann::metric_L2_Simple
+      * \tparam T Type of the elements (e.g. double, float, uint8_t)
+      * \tparam DistanceType Type of distance variables (must be signed) (e.g. float, double, int64_t)
+      */
+    template<class T, class DataSource, typename _DistanceType = T>
+    struct L2_Simple_Adaptor
+    {
+        typedef T ElementType;
+        typedef _DistanceType DistanceType;
+
+        const DataSource &data_source;
+
+        L2_Simple_Adaptor(const DataSource &_data_source) : data_source(_data_source) { }
+
+        inline DistanceType operator()(const T* a, const size_t b_idx, size_t size) const {
+            return data_source.kdtree_distance(a,b_idx,size);
+        }
+
+        template <typename U, typename V>
+        inline DistanceType accum_dist(const U a, const V b, int ) const
+        {
+            return (a-b)*(a-b);
+        }
+    };
+
+    /** Metaprogramming helper traits class for the L1 (Manhattan) metric */
+    struct metric_L1 {
+        template<class T, class DataSource>
+        struct traits {
+            typedef L1_Adaptor<T,DataSource> distance_t;
+        };
+    };
+    /** Metaprogramming helper traits class for the L2 (Euclidean) metric */
+    struct metric_L2 {
+        template<class T, class DataSource>
+        struct traits {
+            typedef L2_Adaptor<T,DataSource> distance_t;
+        };
+    };
+    /** Metaprogramming helper traits class for the L2_simple (Euclidean) metric */
+    struct metric_L2_Simple {
+        template<class T, class DataSource>
+        struct traits {
+            typedef L2_Simple_Adaptor<T,DataSource> distance_t;
+        };
+    };
+
+    /** @} */
+
+
+
+    /** @addtogroup param_grp Parameter structs
+      * @{ */
+
+    /**  Parameters (see http://code.google.com/p/nanoflann/ for help choosing the parameters)
+      */
+    struct KDTreeSingleIndexAdaptorParams
+    {
+        KDTreeSingleIndexAdaptorParams(size_t _leaf_max_size = 10, int dim_ = -1) :
+            leaf_max_size(_leaf_max_size), dim(dim_)
+        {}
+
+        size_t leaf_max_size;
+        int dim;
+    };
+
+    /** Search options for KDTreeSingleIndexAdaptor::findNeighbors() */
+    struct SearchParams
+    {
+        /** Note: The first argument (checks_IGNORED_) is ignored, but kept for compatibility with the FLANN interface */
+        SearchParams(int checks_IGNORED_ = 32, float eps_ = 0, bool sorted_ = true ) :
+            checks(checks_IGNORED_), eps(eps_), sorted(sorted_) {}
+
+        int   checks;  //!< Ignored parameter (Kept for compatibility with the FLANN interface).
+        float eps;  //!< search for eps-approximate neighbours (default: 0)
+        bool sorted; //!< only for radius search, require neighbours sorted by distance (default: true)
+    };
+    /** @} */
+
+
+    /** @addtogroup memalloc_grp Memory allocation
+      * @{ */
+
+    /**
+     * Allocates (using C's malloc) a generic type T.
+     *
+     * Params:
+     *     count = number of instances to allocate.
+     * Returns: pointer (of type T*) to memory buffer
+     */
+    template <typename T>
+    inline T* allocate(size_t count = 1)
+    {
+        T* mem = (T*) ::malloc(sizeof(T)*count);
+        return mem;
+    }
+
+
+    /**
+     * Pooled storage allocator
+     *
+     * The following routines allow for the efficient allocation of storage in
+     * small chunks from a specified pool.  Rather than allowing each structure
+     * to be freed individually, an entire pool of storage is freed at once.
+     * This method has two advantages over just using malloc() and free().  First,
+     * it is far more efficient for allocating small objects, as there is
+     * no overhead for remembering all the information needed to free each
+     * object or consolidating fragmented memory.  Second, the decision about
+     * how long to keep an object is made at the time of allocation, and there
+     * is no need to track down all the objects to free them.
+     *
+     */
+
+    const size_t     WORDSIZE=16;
+    const size_t     BLOCKSIZE=8192;
+
+    class PooledAllocator
+    {
+        /* We maintain memory alignment to word boundaries by requiring that all
+            allocations be in multiples of the machine wordsize.  */
+        /* Size of machine word in bytes.  Must be power of 2. */
+        /* Minimum number of bytes requested at a time from	the system.  Must be multiple of WORDSIZE. */
+
+
+        size_t  remaining;  /* Number of bytes left in current block of storage. */
+        void*   base;     /* Pointer to base of current block of storage. */
+        void*   loc;      /* Current location in block to next allocate memory. */
+        size_t  blocksize;
+
+        void internal_init()
+        {
+            remaining = 0;
+            base = NULL;
+            usedMemory = 0;
+            wastedMemory = 0;
+        }
+
+    public:
+        size_t  usedMemory;
+        size_t  wastedMemory;
+
+        /**
+            Default constructor. Initializes a new pool.
+         */
+        PooledAllocator(const size_t blocksize_ = BLOCKSIZE) : blocksize(blocksize_) {
+            internal_init();
+        }
+
+        /**
+         * Destructor. Frees all the memory allocated in this pool.
+         */
+        ~PooledAllocator() {
+            free_all();
+        }
+
+        /** Frees all allocated memory chunks */
+        void free_all()
+        {
+            while (base != NULL) {
+                void *prev = *((void**) base); /* Get pointer to prev block. */
+                ::free(base);
+                base = prev;
+            }
+            internal_init();
+        }
+
+        /**
+         * Returns a pointer to a piece of new memory of the given size in bytes
+         * allocated from the pool.
+         */
+        void* malloc(const size_t req_size)
+        {
+            /* Round size up to a multiple of wordsize.  The following expression
+                only works for WORDSIZE that is a power of 2, by masking last bits of
+                incremented size to zero.
+             */
+            const size_t size = (req_size + (WORDSIZE - 1)) & ~(WORDSIZE - 1);
+
+            /* Check whether a new block must be allocated.  Note that the first word
+                of a block is reserved for a pointer to the previous block.
+             */
+            if (size > remaining) {
+
+                wastedMemory += remaining;
+
+                /* Allocate new storage. */
+                const size_t blocksize = (size + sizeof(void*) + (WORDSIZE-1) > BLOCKSIZE) ?
+                            size + sizeof(void*) + (WORDSIZE-1) : BLOCKSIZE;
+
+                // use the standard C malloc to allocate memory
+                void* m = ::malloc(blocksize);
+                if (!m) {
+                    fprintf(stderr,"Failed to allocate memory.\n");
+                    return NULL;
+                }
+
+                /* Fill first word of new block with pointer to previous block. */
+                ((void**) m)[0] = base;
+                base = m;
+
+                size_t shift = 0;
+                //int size_t = (WORDSIZE - ( (((size_t)m) + sizeof(void*)) & (WORDSIZE-1))) & (WORDSIZE-1);
+
+                remaining = blocksize - sizeof(void*) - shift;
+                loc = ((char*)m + sizeof(void*) + shift);
+            }
+            void* rloc = loc;
+            loc = (char*)loc + size;
+            remaining -= size;
+
+            usedMemory += size;
+
+            return rloc;
+        }
+
+        /**
+         * Allocates (using this pool) a generic type T.
+         *
+         * Params:
+         *     count = number of instances to allocate.
+         * Returns: pointer (of type T*) to memory buffer
+         */
+        template <typename T>
+        T* allocate(const size_t count = 1)
+        {
+            T* mem = (T*) this->malloc(sizeof(T)*count);
+            return mem;
+        }
+
+    };
+    /** @} */
+
+    /** @addtogroup nanoflann_metaprog_grp Auxiliary metaprogramming stuff
+      * @{ */
+
+    // ----------------  CArray -------------------------
+    /** A STL container (as wrapper) for arrays of constant size defined at compile time (class imported from the MRPT project)
+     * This code is an adapted version from Boost, modifed for its integration
+     *	within MRPT (JLBC, Dec/2009) (Renamed array -> CArray to avoid possible potential conflicts).
+     * See
+     *      http://www.josuttis.com/cppcode
+     * for details and the latest version.
+     * See
+     *      http://www.boost.org/libs/array for Documentation.
+     * for documentation.
+     *
+     * (C) Copyright Nicolai M. Josuttis 2001.
+     * Permission to copy, use, modify, sell and distribute this software
+     * is granted provided this copyright notice appears in all copies.
+     * This software is provided "as is" without express or implied
+     * warranty, and with no claim as to its suitability for any purpose.
+     *
+     * 29 Jan 2004 - minor fixes (Nico Josuttis)
+     * 04 Dec 2003 - update to synch with library TR1 (Alisdair Meredith)
+     * 23 Aug 2002 - fix for Non-MSVC compilers combined with MSVC libraries.
+     * 05 Aug 2001 - minor update (Nico Josuttis)
+     * 20 Jan 2001 - STLport fix (Beman Dawes)
+     * 29 Sep 2000 - Initial Revision (Nico Josuttis)
+     *
+     * Jan 30, 2004
+     */
+    template <typename T, std::size_t N>
+    class CArray {
+      public:
+        T elems[N];    // fixed-size array of elements of type T
+
+      public:
+        // type definitions
+        typedef T              value_type;
+        typedef T*             iterator;
+        typedef const T*       const_iterator;
+        typedef T&             reference;
+        typedef const T&       const_reference;
+        typedef std::size_t    size_type;
+        typedef std::ptrdiff_t difference_type;
+
+        // iterator support
+        inline iterator begin() { return elems; }
+        inline const_iterator begin() const { return elems; }
+        inline iterator end() { return elems+N; }
+        inline const_iterator end() const { return elems+N; }
+
+        // reverse iterator support
+#if !defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) && !defined(BOOST_MSVC_STD_ITERATOR) && !defined(BOOST_NO_STD_ITERATOR_TRAITS)
+        typedef std::reverse_iterator<iterator> reverse_iterator;
+        typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+#elif defined(_MSC_VER) && (_MSC_VER == 1300) && defined(BOOST_DINKUMWARE_STDLIB) && (BOOST_DINKUMWARE_STDLIB == 310)
+        // workaround for broken reverse_iterator in VC7
+        typedef std::reverse_iterator<std::_Ptrit<value_type, difference_type, iterator,
+                                      reference, iterator, reference> > reverse_iterator;
+        typedef std::reverse_iterator<std::_Ptrit<value_type, difference_type, const_iterator,
+                                      const_reference, iterator, reference> > const_reverse_iterator;
+#else
+        // workaround for broken reverse_iterator implementations
+        typedef std::reverse_iterator<iterator,T> reverse_iterator;
+        typedef std::reverse_iterator<const_iterator,T> const_reverse_iterator;
+#endif
+
+        reverse_iterator rbegin() { return reverse_iterator(end()); }
+        const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); }
+        reverse_iterator rend() { return reverse_iterator(begin()); }
+        const_reverse_iterator rend() const { return const_reverse_iterator(begin()); }
+        // operator[]
+        inline reference operator[](size_type i) { return elems[i]; }
+        inline const_reference operator[](size_type i) const { return elems[i]; }
+        // at() with range check
+        reference at(size_type i) { rangecheck(i); return elems[i]; }
+        const_reference at(size_type i) const { rangecheck(i); return elems[i]; }
+        // front() and back()
+        reference front() { return elems[0]; }
+        const_reference front() const { return elems[0]; }
+        reference back() { return elems[N-1]; }
+        const_reference back() const { return elems[N-1]; }
+        // size is constant
+        static inline size_type size() { return N; }
+        static bool empty() { return false; }
+        static size_type max_size() { return N; }
+        enum { static_size = N };
+        /** This method has no effects in this class, but raises an exception if the expected size does not match */
+        inline void resize(const size_t nElements) { if (nElements!=N) throw std::logic_error("Try to change the size of a CArray."); }
+        // swap (note: linear complexity in N, constant for given instantiation)
+        void swap (CArray<T,N>& y) { std::swap_ranges(begin(),end(),y.begin()); }
+        // direct access to data (read-only)
+        const T* data() const { return elems; }
+        // use array as C array (direct read/write access to data)
+        T* data() { return elems; }
+        // assignment with type conversion
+        template <typename T2> CArray<T,N>& operator= (const CArray<T2,N>& rhs) {
+            std::copy(rhs.begin(),rhs.end(), begin());
+            return *this;
+        }
+        // assign one value to all elements
+        inline void assign (const T& value) { for (size_t i=0;i<N;i++) elems[i]=value; }
+        // assign (compatible with std::vector's one) (by JLBC for MRPT)
+        void assign (const size_t n, const T& value) { assert(N==n); for (size_t i=0;i<N;i++) elems[i]=value; }
+      private:
+        // check range (may be private because it is static)
+        static void rangecheck (size_type i) { if (i >= size()) { throw std::out_of_range("CArray<>: index out of range"); } }
+    }; // end of CArray
+
+    /** Used to declare fixed-size arrays when DIM>0, dynamically-allocated vectors when DIM=-1.
+      * Fixed size version for a generic DIM:
+      */
+    template <int DIM, typename T>
+    struct array_or_vector_selector
+    {
+        typedef CArray<T,DIM> container_t;
+    };
+    /** Dynamic size version */
+    template <typename T>
+    struct array_or_vector_selector<-1,T> {
+        typedef std::vector<T> container_t;
+    };
+    /** @} */
+
+    /** @addtogroup kdtrees_grp KD-tree classes and adaptors
+      * @{ */
+
+    /** kd-tree index
+     *
+     * Contains the k-d trees and other information for indexing a set of points
+     * for nearest-neighbor matching.
+     *
+     *  The class "DatasetAdaptor" must provide the following interface (can be non-virtual, inlined methods):
+     *
+     *  \code
+     *   // Must return the number of data points
+     *   inline size_t kdtree_get_point_count() const { ... }
+     *
+     *   // Must return the Euclidean (L2) distance between the vector "p1[0:size-1]" and the data point with index "idx_p2" stored in the class:
+     *   inline DistanceType kdtree_distance(const T *p1, const size_t idx_p2,size_t size) const { ... }
+     *
+     *   // Must return the dim'th component of the idx'th point in the class:
+     *   inline T kdtree_get_pt(const size_t idx, int dim) const { ... }
+     *
+     *   // Optional bounding-box computation: return false to default to a standard bbox computation loop.
+     *   //   Return true if the BBOX was already computed by the class and returned in "bb" so it can be avoided to redo it again.
+     *   //   Look at bb.size() to find out the expected dimensionality (e.g. 2 or 3 for point clouds)
+     *   template <class BBOX>
+     *   bool kdtree_get_bbox(BBOX &bb) const
+     *   {
+     *      bb[0].low = ...; bb[0].high = ...;  // 0th dimension limits
+     *      bb[1].low = ...; bb[1].high = ...;  // 1st dimension limits
+     *      ...
+     *      return true;
+     *   }
+     *
+     *  \endcode
+     *
+     * \tparam IndexType Will be typically size_t or int
+     */
+    template <typename Distance, class DatasetAdaptor,int DIM = -1, typename IndexType = size_t>
+    class KDTreeSingleIndexAdaptor
+    {
+    public:
+        typedef typename Distance::ElementType  ElementType;
+        typedef typename Distance::DistanceType DistanceType;
+    protected:
+
+        /**
+         *  Array of indices to vectors in the dataset.
+         */
+        std::vector<IndexType> vind;
+
+        size_t m_leaf_max_size;
+
+
+        /**
+         * The dataset used by this index
+         */
+        const DatasetAdaptor &dataset; //!< The source of our data
+
+        const KDTreeSingleIndexAdaptorParams index_params;
+
+        size_t m_size;
+        int dim;  //!< Dimensionality of each data point
+
+
+        /*--------------------- Internal Data Structures --------------------------*/
+        struct Node
+        {
+            union {
+                struct
+                {
+                    /**
+                     * Indices of points in leaf node
+                     */
+                    IndexType left, right;
+                } lr;
+                struct
+                {
+                    /**
+                     * Dimension used for subdivision.
+                     */
+                    int divfeat;
+                    /**
+                     * The values used for subdivision.
+                     */
+                    DistanceType divlow, divhigh;
+                } sub;
+            };
+            /**
+             * The child nodes.
+             */
+            Node* child1, * child2;
+        };
+        typedef Node* NodePtr;
+
+
+        struct Interval
+        {
+            ElementType low, high;
+        };
+
+        /** Define "BoundingBox" as a fixed-size or variable-size container depending on "DIM" */
+        typedef typename array_or_vector_selector<DIM,Interval>::container_t BoundingBox;
+
+        /** Define "distance_vector_t" as a fixed-size or variable-size container depending on "DIM" */
+        typedef typename array_or_vector_selector<DIM,DistanceType>::container_t distance_vector_t;
+
+        /** This record represents a branch point when finding neighbors in
+            the tree.  It contains a record of the minimum distance to the query
+            point, as well as the node at which the search resumes.
+         */
+        template <typename T, typename DistanceType>
+        struct BranchStruct
+        {
+            T node;           /* Tree node at which search resumes */
+            DistanceType mindist;     /* Minimum distance to query for all nodes below. */
+
+            BranchStruct() {}
+            BranchStruct(const T& aNode, DistanceType dist) : node(aNode), mindist(dist) {}
+
+            inline bool operator<(const BranchStruct<T, DistanceType>& rhs) const
+            {
+                return mindist<rhs.mindist;
+            }
+        };
+
+        /**
+         * Array of k-d trees used to find neighbours.
+         */
+        NodePtr root_node;
+        typedef BranchStruct<NodePtr, DistanceType> BranchSt;
+        typedef BranchSt* Branch;
+
+        BoundingBox root_bbox;
+
+        /**
+         * Pooled memory allocator.
+         *
+         * Using a pooled memory allocator is more efficient
+         * than allocating memory directly when there is a large
+         * number small of memory allocations.
+         */
+        PooledAllocator pool;
+
+    public:
+
+        Distance distance;
+
+        /**
+         * KDTree constructor
+         *
+         * Params:
+         *          inputData = dataset with the input features
+         *          params = parameters passed to the kdtree algorithm (see http://code.google.com/p/nanoflann/ for help choosing the parameters)
+         */
+        KDTreeSingleIndexAdaptor(const int dimensionality, const DatasetAdaptor& inputData, const KDTreeSingleIndexAdaptorParams& params = KDTreeSingleIndexAdaptorParams() ) :
+            dataset(inputData), index_params(params), root_node(NULL), distance(inputData)
+        {
+            m_size = dataset.kdtree_get_point_count();
+            dim = dimensionality;
+            if (DIM>0) dim=DIM;
+            else {
+                if (params.dim>0) dim = params.dim;
+            }
+            m_leaf_max_size = params.leaf_max_size;
+
+            // Create a permutable array of indices to the input vectors.
+            init_vind();
+        }
+
+        /**
+         * Standard destructor
+         */
+        ~KDTreeSingleIndexAdaptor()
+        {
+        }
+
+        /** Frees the previously-built index. Automatically called within buildIndex(). */
+        void freeIndex()
+        {
+            pool.free_all();
+            root_node=NULL;
+        }
+
+        /**
+         * Builds the index
+         */
+        void buildIndex()
+        {
+            init_vind();
+            computeBoundingBox(root_bbox);
+            freeIndex();
+            root_node = divideTree(0, m_size, root_bbox );   // construct the tree
+        }
+
+        /**
+         *  Returns size of index.
+         */
+        size_t size() const
+        {
+            return m_size;
+        }
+
+        /**
+         * Returns the length of an index feature.
+         */
+        size_t veclen() const
+        {
+            return static_cast<size_t>(DIM>0 ? DIM : dim);
+        }
+
+        /**
+         * Computes the inde memory usage
+         * Returns: memory used by the index
+         */
+        size_t usedMemory() const
+        {
+            return pool.usedMemory+pool.wastedMemory+dataset.kdtree_get_point_count()*sizeof(IndexType);  // pool memory and vind array memory
+        }
+
+        /** \name Query methods
+          * @{ */
+
+        /**
+         * Find set of nearest neighbors to vec[0:dim-1]. Their indices are stored inside
+         * the result object.
+         *
+         * Params:
+         *     result = the result object in which the indices of the nearest-neighbors are stored
+         *     vec = the vector for which to search the nearest neighbors
+         *
+         * \tparam RESULTSET Should be any ResultSet<DistanceType>
+         * \sa knnSearch, radiusSearch
+         */
+        template <typename RESULTSET>
+        void findNeighbors(RESULTSET& result, const ElementType* vec, const SearchParams& searchParams) const
+        {
+            assert(vec);
+            if (!root_node) throw std::runtime_error("[nanoflann] findNeighbors() called before building the index.");
+            float epsError = 1+searchParams.eps;
+
+            distance_vector_t dists; // fixed or variable-sized container (depending on DIM)
+            dists.assign((DIM>0 ? DIM : dim) ,0); // Fill it with zeros.
+            DistanceType distsq = computeInitialDistances(vec, dists);
+            searchLevel(result, vec, root_node, distsq, dists, epsError);  // "count_leaf" parameter removed since was neither used nor returned to the user.
+        }
+
+        /**
+         * Find the "num_closest" nearest neighbors to the \a query_point[0:dim-1]. Their indices are stored inside
+         * the result object.
+         *  \sa radiusSearch, findNeighbors
+         * \note nChecks_IGNORED is ignored but kept for compatibility with the original FLANN interface.
+         */
+        inline void knnSearch(const ElementType *query_point, const size_t num_closest, IndexType *out_indices, DistanceType *out_distances_sq, const int nChecks_IGNORED = 10) const
+        {
+            nanoflann::KNNResultSet<DistanceType,IndexType> resultSet(num_closest);
+            resultSet.init(out_indices, out_distances_sq);
+            this->findNeighbors(resultSet, query_point, nanoflann::SearchParams());
+        }
+
+        /**
+         * Find all the neighbors to \a query_point[0:dim-1] within a maximum radius.
+         *  The output is given as a vector of pairs, of which the first element is a point index and the second the corresponding distance.
+         *  Previous contents of \a IndicesDists are cleared.
+         *
+         *  If searchParams.sorted==true, the output list is sorted by ascending distances.
+         *
+         *  For a better performance, it is advisable to do a .reserve() on the vector if you have any wild guess about the number of expected matches.
+         *
+         *  \sa knnSearch, findNeighbors
+         * \return The number of points within the given radius (i.e. indices.size() or dists.size() )
+         */
+        size_t radiusSearch(const ElementType *query_point,const DistanceType radius, std::vector<std::pair<IndexType,DistanceType> >& IndicesDists, const SearchParams& searchParams) const
+        {
+            RadiusResultSet<DistanceType,IndexType> resultSet(radius,IndicesDists);
+            this->findNeighbors(resultSet, query_point, searchParams);
+
+            if (searchParams.sorted)
+                std::sort(IndicesDists.begin(),IndicesDists.end(), IndexDist_Sorter() );
+
+            return resultSet.size();
+        }
+
+        /** @} */
+
+    private:
+        /** Make sure the auxiliary list \a vind has the same size than the current dataset, and re-generate if size has changed. */
+        void init_vind()
+        {
+            // Create a permutable array of indices to the input vectors.
+            m_size = dataset.kdtree_get_point_count();
+            if (vind.size()!=m_size) vind.resize(m_size);
+            for (size_t i = 0; i < m_size; i++) vind[i] = i;
+        }
+
+        /// Helper accessor to the dataset points:
+        inline ElementType dataset_get(size_t idx, int component) const {
+            return dataset.kdtree_get_pt(idx,component);
+        }
+
+
+        void save_tree(FILE* stream, NodePtr tree)
+        {
+            save_value(stream, *tree);
+            if (tree->child1!=NULL) {
+                save_tree(stream, tree->child1);
+            }
+            if (tree->child2!=NULL) {
+                save_tree(stream, tree->child2);
+            }
+        }
+
+
+        void load_tree(FILE* stream, NodePtr& tree)
+        {
+            tree = pool.allocate<Node>();
+            load_value(stream, *tree);
+            if (tree->child1!=NULL) {
+                load_tree(stream, tree->child1);
+            }
+            if (tree->child2!=NULL) {
+                load_tree(stream, tree->child2);
+            }
+        }
+
+
+        void computeBoundingBox(BoundingBox& bbox)
+        {
+            bbox.resize((DIM>0 ? DIM : dim));
+            if (dataset.kdtree_get_bbox(bbox))
+            {
+                // Done! It was implemented in derived class
+            }
+            else
+            {
+                for (int i=0; i<(DIM>0 ? DIM : dim); ++i) {
+                    bbox[i].low =
+                        bbox[i].high = dataset_get(0,i);
+                }
+                const size_t N = dataset.kdtree_get_point_count();
+                for (size_t k=1; k<N; ++k) {
+                    for (int i=0; i<(DIM>0 ? DIM : dim); ++i) {
+                        if (dataset_get(k,i)<bbox[i].low) bbox[i].low = dataset_get(k,i);
+                        if (dataset_get(k,i)>bbox[i].high) bbox[i].high = dataset_get(k,i);
+                    }
+                }
+            }
+        }
+
+
+        /**
+         * Create a tree node that subdivides the list of vecs from vind[first]
+         * to vind[last].  The routine is called recursively on each sublist.
+         * Place a pointer to this new tree node in the location pTree.
+         *
+         * Params: pTree = the new node to create
+         *                  first = index of the first vector
+         *                  last = index of the last vector
+         */
+        NodePtr divideTree(const IndexType left, const IndexType right, BoundingBox& bbox)
+        {
+            NodePtr node = pool.allocate<Node>(); // allocate memory
+
+            /* If too few exemplars remain, then make this a leaf node. */
+            if ( (right-left) <= m_leaf_max_size) {
+                node->child1 = node->child2 = NULL;    /* Mark as leaf node. */
+                node->lr.left = left;
+                node->lr.right = right;
+
+                // compute bounding-box of leaf points
+                for (int i=0; i<(DIM>0 ? DIM : dim); ++i) {
+                    bbox[i].low = dataset_get(vind[left],i);
+                    bbox[i].high = dataset_get(vind[left],i);
+                }
+                for (IndexType k=left+1; k<right; ++k) {
+                    for (int i=0; i<(DIM>0 ? DIM : dim); ++i) {
+                        if (bbox[i].low>dataset_get(vind[k],i)) bbox[i].low=dataset_get(vind[k],i);
+                        if (bbox[i].high<dataset_get(vind[k],i)) bbox[i].high=dataset_get(vind[k],i);
+                    }
+                }
+            }
+            else {
+                IndexType idx;
+                int cutfeat;
+                DistanceType cutval;
+                middleSplit_(&vind[0]+left, right-left, idx, cutfeat, cutval, bbox);
+
+                node->sub.divfeat = cutfeat;
+
+                BoundingBox left_bbox(bbox);
+                left_bbox[cutfeat].high = cutval;
+                node->child1 = divideTree(left, left+idx, left_bbox);
+
+                BoundingBox right_bbox(bbox);
+                right_bbox[cutfeat].low = cutval;
+                node->child2 = divideTree(left+idx, right, right_bbox);
+
+                node->sub.divlow = left_bbox[cutfeat].high;
+                node->sub.divhigh = right_bbox[cutfeat].low;
+
+                for (int i=0; i<(DIM>0 ? DIM : dim); ++i) {
+                    bbox[i].low = std::min(left_bbox[i].low, right_bbox[i].low);
+                    bbox[i].high = std::max(left_bbox[i].high, right_bbox[i].high);
+                }
+            }
+
+            return node;
+        }
+
+        void computeMinMax(IndexType* ind, IndexType count, int element, ElementType& min_elem, ElementType& max_elem)
+        {
+            min_elem = dataset_get(ind[0],element);
+            max_elem = dataset_get(ind[0],element);
+            for (IndexType i=1; i<count; ++i) {
+                ElementType val = dataset_get(ind[i],element);
+                if (val<min_elem) min_elem = val;
+                if (val>max_elem) max_elem = val;
+            }
+        }
+
+        void middleSplit(IndexType* ind, IndexType count, IndexType& index, int& cutfeat, DistanceType& cutval, const BoundingBox& bbox)
+        {
+            // find the largest span from the approximate bounding box
+            ElementType max_span = bbox[0].high-bbox[0].low;
+            cutfeat = 0;
+            cutval = (bbox[0].high+bbox[0].low)/2;
+            for (int i=1; i<(DIM>0 ? DIM : dim); ++i) {
+                ElementType span = bbox[i].low-bbox[i].low;
+                if (span>max_span) {
+                    max_span = span;
+                    cutfeat = i;
+                    cutval = (bbox[i].high+bbox[i].low)/2;
+                }
+            }
+
+            // compute exact span on the found dimension
+            ElementType min_elem, max_elem;
+            computeMinMax(ind, count, cutfeat, min_elem, max_elem);
+            cutval = (min_elem+max_elem)/2;
+            max_span = max_elem - min_elem;
+
+            // check if a dimension of a largest span exists
+            size_t k = cutfeat;
+            for (size_t i=0; i<(DIM>0 ? DIM : dim); ++i) {
+                if (i==k) continue;
+                ElementType span = bbox[i].high-bbox[i].low;
+                if (span>max_span) {
+                    computeMinMax(ind, count, i, min_elem, max_elem);
+                    span = max_elem - min_elem;
+                    if (span>max_span) {
+                        max_span = span;
+                        cutfeat = i;
+                        cutval = (min_elem+max_elem)/2;
+                    }
+                }
+            }
+            IndexType lim1, lim2;
+            planeSplit(ind, count, cutfeat, cutval, lim1, lim2);
+
+            if (lim1>count/2) index = lim1;
+            else if (lim2<count/2) index = lim2;
+            else index = count/2;
+        }
+
+
+        void middleSplit_(IndexType* ind, IndexType count, IndexType& index, int& cutfeat, DistanceType& cutval, const BoundingBox& bbox)
+        {
+            const DistanceType EPS=static_cast<DistanceType>(0.00001);
+            ElementType max_span = bbox[0].high-bbox[0].low;
+            for (int i=1; i<(DIM>0 ? DIM : dim); ++i) {
+                ElementType span = bbox[i].high-bbox[i].low;
+                if (span>max_span) {
+                    max_span = span;
+                }
+            }
+            ElementType max_spread = -1;
+            cutfeat = 0;
+            for (int i=0; i<(DIM>0 ? DIM : dim); ++i) {
+                ElementType span = bbox[i].high-bbox[i].low;
+                if (span>(1-EPS)*max_span) {
+                    ElementType min_elem, max_elem;
+                    computeMinMax(ind, count, cutfeat, min_elem, max_elem);
+                    ElementType spread = max_elem-min_elem;;
+                    if (spread>max_spread) {
+                        cutfeat = i;
+                        max_spread = spread;
+                    }
+                }
+            }
+            // split in the middle
+            DistanceType split_val = (bbox[cutfeat].low+bbox[cutfeat].high)/2;
+            ElementType min_elem, max_elem;
+            computeMinMax(ind, count, cutfeat, min_elem, max_elem);
+
+            if (split_val<min_elem) cutval = min_elem;
+            else if (split_val>max_elem) cutval = max_elem;
+            else cutval = split_val;
+
+            IndexType lim1, lim2;
+            planeSplit(ind, count, cutfeat, cutval, lim1, lim2);
+
+            if (lim1>count/2) index = lim1;
+            else if (lim2<count/2) index = lim2;
+            else index = count/2;
+        }
+
+
+        /**
+         *  Subdivide the list of points by a plane perpendicular on axe corresponding
+         *  to the 'cutfeat' dimension at 'cutval' position.
+         *
+         *  On return:
+         *  dataset[ind[0..lim1-1]][cutfeat]<cutval
+         *  dataset[ind[lim1..lim2-1]][cutfeat]==cutval
+         *  dataset[ind[lim2..count]][cutfeat]>cutval
+         */
+        void planeSplit(IndexType* ind, const IndexType count, int cutfeat, DistanceType cutval, IndexType& lim1, IndexType& lim2)
+        {
+            /* Move vector indices for left subtree to front of list. */
+            IndexType left = 0;
+            IndexType right = count-1;
+            for (;; ) {
+                while (left<=right && dataset_get(ind[left],cutfeat)<cutval) ++left;
+                while (right && left<=right && dataset_get(ind[right],cutfeat)>=cutval) --right;
+                if (left>right || !right) break;  // "!right" was added to support unsigned Index types
+                std::swap(ind[left], ind[right]);
+                ++left;
+                --right;
+            }
+            /* If either list is empty, it means that all remaining features
+             * are identical. Split in the middle to maintain a balanced tree.
+             */
+            lim1 = left;
+            right = count-1;
+            for (;; ) {
+                while (left<=right && dataset_get(ind[left],cutfeat)<=cutval) ++left;
+                while (right && left<=right && dataset_get(ind[right],cutfeat)>cutval) --right;
+                if (left>right || !right) break;  // "!right" was added to support unsigned Index types
+                std::swap(ind[left], ind[right]);
+                ++left;
+                --right;
+            }
+            lim2 = left;
+        }
+
+        DistanceType computeInitialDistances(const ElementType* vec, distance_vector_t& dists) const
+        {
+            assert(vec);
+            DistanceType distsq = 0.0;
+
+            for (int i = 0; i < (DIM>0 ? DIM : dim); ++i) {
+                if (vec[i] < root_bbox[i].low) {
+                    dists[i] = distance.accum_dist(vec[i], root_bbox[i].low, i);
+                    distsq += dists[i];
+                }
+                if (vec[i] > root_bbox[i].high) {
+                    dists[i] = distance.accum_dist(vec[i], root_bbox[i].high, i);
+                    distsq += dists[i];
+                }
+            }
+
+            return distsq;
+        }
+
+        /**
+         * Performs an exact search in the tree starting from a node.
+         * \tparam RESULTSET Should be any ResultSet<DistanceType>
+         */
+        template <class RESULTSET>
+        void searchLevel(RESULTSET& result_set, const ElementType* vec, const NodePtr node, DistanceType mindistsq,
+                         distance_vector_t& dists, const float epsError) const
+        {
+            /* If this is a leaf node, then do check and return. */
+            if ((node->child1 == NULL)&&(node->child2 == NULL)) {
+                //count_leaf += (node->lr.right-node->lr.left);  // Removed since was neither used nor returned to the user.
+                DistanceType worst_dist = result_set.worstDist();
+                for (IndexType i=node->lr.left; i<node->lr.right; ++i) {
+                    const IndexType index = vind[i];// reorder... : i;
+                    DistanceType dist = distance(vec, index, (DIM>0 ? DIM : dim));
+                    if (dist<worst_dist) {
+                        result_set.addPoint(dist,vind[i]);
+                    }
+                }
+                return;
+            }
+
+            /* Which child branch should be taken first? */
+            int idx = node->sub.divfeat;
+            ElementType val = vec[idx];
+            DistanceType diff1 = val - node->sub.divlow;
+            DistanceType diff2 = val - node->sub.divhigh;
+
+            NodePtr bestChild;
+            NodePtr otherChild;
+            DistanceType cut_dist;
+            if ((diff1+diff2)<0) {
+                bestChild = node->child1;
+                otherChild = node->child2;
+                cut_dist = distance.accum_dist(val, node->sub.divhigh, idx);
+            }
+            else {
+                bestChild = node->child2;
+                otherChild = node->child1;
+                cut_dist = distance.accum_dist( val, node->sub.divlow, idx);
+            }
+
+            /* Call recursively to search next level down. */
+            searchLevel(result_set, vec, bestChild, mindistsq, dists, epsError);
+
+            DistanceType dst = dists[idx];
+            mindistsq = mindistsq + cut_dist - dst;
+            dists[idx] = cut_dist;
+            if (mindistsq*epsError<=result_set.worstDist()) {
+                searchLevel(result_set, vec, otherChild, mindistsq, dists, epsError);
+            }
+            dists[idx] = dst;
+        }
+
+    public:
+        /**  Stores the index in a binary file.
+          *   IMPORTANT NOTE: The set of data points is NOT stored in the file, so when loading the index object it must be constructed associated to the same source of data points used while building it.
+          * See the example: examples/saveload_example.cpp
+          * \sa loadIndex  */
+        void saveIndex(FILE* stream)
+        {
+            save_value(stream, m_size);
+            save_value(stream, dim);
+            save_value(stream, root_bbox);
+            save_value(stream, m_leaf_max_size);
+            save_value(stream, vind);
+            save_tree(stream, root_node);
+        }
+
+        /**  Loads a previous index from a binary file.
+          *   IMPORTANT NOTE: The set of data points is NOT stored in the file, so the index object must be constructed associated to the same source of data points used while building the index.
+          * See the example: examples/saveload_example.cpp
+          * \sa loadIndex  */
+        void loadIndex(FILE* stream)
+        {
+            load_value(stream, m_size);
+            load_value(stream, dim);
+            load_value(stream, root_bbox);
+            load_value(stream, m_leaf_max_size);
+            load_value(stream, vind);
+            load_tree(stream, root_node);
+        }
+
+    };   // class KDTree
+
+
+    /** A simple KD-tree adaptor for working with data directly stored in an Eigen Matrix, without duplicating the data storage.
+      *  Each row in the matrix represents a point in the state space.
+      *
+      *  Example of usage:
+      * \code
+      * 	Eigen::Matrix<num_t,Dynamic,Dynamic>  mat;
+      * 	// Fill out "mat"...
+      *
+      * 	typedef KDTreeEigenMatrixAdaptor< Eigen::Matrix<num_t,Dynamic,Dynamic> >  my_kd_tree_t;
+      * 	const int max_leaf = 10;
+      * 	my_kd_tree_t   mat_index(dimdim, mat, max_leaf );
+      * 	mat_index.index->buildIndex();
+      * 	mat_index.index->...
+      * \endcode
+      *
+      *  \tparam DIM If set to >0, it specifies a compile-time fixed dimensionality for the points in the data set, allowing more compiler optimizations.
+      *  \tparam Distance The distance metric to use: nanoflann::metric_L1, nanoflann::metric_L2, nanoflann::metric_L2_Simple, etc.
+      *  \tparam IndexType The type for indices in the KD-tree index (typically, size_t of int)
+      */
+    template <class MatrixType, int DIM = -1, class Distance = nanoflann::metric_L2, typename IndexType = size_t>
+    struct KDTreeEigenMatrixAdaptor
+    {
+        typedef KDTreeEigenMatrixAdaptor<MatrixType,DIM,Distance,IndexType> self_t;
+        typedef typename MatrixType::Scalar              num_t;
+        typedef typename Distance::template traits<num_t,self_t>::distance_t metric_t;
+        typedef KDTreeSingleIndexAdaptor< metric_t,self_t,DIM,IndexType>  index_t;
+
+        index_t* index; //! The kd-tree index for the user to call its methods as usual with any other FLANN index.
+
+        /// Constructor: takes a const ref to the matrix object with the data points
+        KDTreeEigenMatrixAdaptor(const int dimensionality, const MatrixType &mat, const int leaf_max_size = 10) : m_data_matrix(mat)
+        {
+            const size_t dims = mat.cols();
+            if (DIM>0 && static_cast<int>(dims)!=DIM)
+                throw std::runtime_error("Data set dimensionality does not match the 'DIM' template argument");
+            index = new index_t( dims, *this /* adaptor */, nanoflann::KDTreeSingleIndexAdaptorParams(leaf_max_size, dims ) );
+            index->buildIndex();
+        }
+
+        ~KDTreeEigenMatrixAdaptor() {
+            delete index;
+        }
+
+        const MatrixType &m_data_matrix;
+
+        /** Query for the \a num_closest closest points to a given point (entered as query_point[0:dim-1]).
+          *  Note that this is a short-cut method for index->findNeighbors().
+          *  The user can also call index->... methods as desired.
+          * \note nChecks_IGNORED is ignored but kept for compatibility with the original FLANN interface.
+          */
+        inline void query(const num_t *query_point, const size_t num_closest, IndexType *out_indices, num_t *out_distances_sq, const int nChecks_IGNORED = 10) const
+        {
+            nanoflann::KNNResultSet<typename MatrixType::Scalar,IndexType> resultSet(num_closest);
+            resultSet.init(out_indices, out_distances_sq);
+            index->findNeighbors(resultSet, query_point, nanoflann::SearchParams());
+        }
+
+        /** @name Interface expected by KDTreeSingleIndexAdaptor
+          * @{ */
+
+        const self_t & derived() const {
+            return *this;
+        }
+        self_t & derived()       {
+            return *this;
+        }
+
+        // Must return the number of data points
+        inline size_t kdtree_get_point_count() const {
+            return m_data_matrix.rows();
+        }
+
+        // Returns the distance between the vector "p1[0:size-1]" and the data point with index "idx_p2" stored in the class:
+        inline num_t kdtree_distance(const num_t *p1, const size_t idx_p2,size_t size) const
+        {
+            num_t s=0;
+            for (size_t i=0; i<size; i++) {
+                const num_t d= p1[i]-m_data_matrix.coeff(idx_p2,i);
+                s+=d*d;
+            }
+            return s;
+        }
+
+        // Returns the dim'th component of the idx'th point in the class:
+        inline num_t kdtree_get_pt(const size_t idx, int dim) const {
+            return m_data_matrix.coeff(idx,dim);
+        }
+
+        // Optional bounding-box computation: return false to default to a standard bbox computation loop.
+        //   Return true if the BBOX was already computed by the class and returned in "bb" so it can be avoided to redo it again.
+        //   Look at bb.size() to find out the expected dimensionality (e.g. 2 or 3 for point clouds)
+        template <class BBOX>
+        bool kdtree_get_bbox(BBOX &bb) const {
+            return false;
+        }
+
+        /** @} */
+
+    }; // end of KDTreeEigenMatrixAdaptor
+    /** @} */
+
+/** @} */ // end of grouping
+} // end of NS
+
+
+#endif /* NANOFLANN_HPP_ */
diff --git a/updateGZ3D.sh b/updateGZ3D.sh
new file mode 100755
index 0000000000000000000000000000000000000000..fe5090685efafad117f687731d2e037bfe4c4e7e
--- /dev/null
+++ b/updateGZ3D.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
+
+cd $DIR/gz3d/utils
+grunt build
+echo -e "\e[34mgz3d.js and gz3d.min.js created\e[39m"
+cd ../../build
+cmake ..
+echo -e "\e[34mFiles copied to http directory\e[39m"
+if [ "$1" == "-d" ]; then
+  cd ../gz3d/utils
+  grunt jsdoc
+  echo -e "\e[34mDocumentation updated\e[39m"
+fi
diff --git a/webify_models.py b/webify_models.py
new file mode 100755
index 0000000000000000000000000000000000000000..44efe2997946d30d344c3d6ce56ea0120aa65513
--- /dev/null
+++ b/webify_models.py
@@ -0,0 +1,61 @@
+#!/usr/bin/python
+
+# To be deprecated and replaced by webify_models_v2.py
+
+# A script to update gazebo models to be web-friendly.
+# It converts all textures to png format and make sure they are
+# stored in the [model_name]/meshes/ directory alongside the
+# dae files.
+
+import os
+import subprocess
+import sys
+import shutil
+
+print "**************************************"
+print "* 'webify_models.py' is deprecated.  *"
+print "* Use 'webify_models_v2.py' instead. *"
+print "**************************************"
+
+path = sys.argv[1]
+
+files = os.listdir(path)
+
+find_cmd = ['find', path, '-name','*']
+files = subprocess.check_output(find_cmd).split()
+
+for file in files:
+  try:
+    path, filename = os.path.split(file)
+    name, format = filename.split(".")[-2:]
+
+  except:
+    continue # not a texture
+  try:
+    # dest_dir = path.replace('materials/textures', 'meshes')
+    dest_dir = path
+    dest_path = "%s/%s.png" % (dest_dir, name)
+    cmd = None
+    if format.lower() in ['tif', 'tga', 'tiff', 'jpeg', 'jpg', 'gif', 'png']:
+      if dest_path != file:
+        cmd = ['convert', file, dest_path]
+        subprocess.check_call(cmd)
+
+      mesh_dest_dir = path.replace('materials/textures', 'meshes')
+      if mesh_dest_dir != dest_dir:
+        cmd = ['cp', dest_path, mesh_dest_dir]
+      # if format.lower() == 'png':
+      #  cmd = ['cp', file, mesh_dest_dir]
+        print cmd
+        subprocess.check_call(cmd)
+
+    if format.lower() in ['dae']:
+      sed_cmd = ["sed", "-i", "-e", 's/\.tga/\.png/g', "-e",
+          's/\.tiff/\.png/g', "-e", 's/\.tif/\.png/g',
+          "-e", 's/\.jpg/\.png/g', "-e", 's/\.jpeg/\.png/g',
+          "-e", 's/\.gif/\.png/g', file]
+      print sed_cmd
+      subprocess.check_call(sed_cmd)
+  except Exception, e:
+      print "error %s" % e
+      raise
diff --git a/webify_models_v2.py b/webify_models_v2.py
new file mode 100755
index 0000000000000000000000000000000000000000..572c0bfcd74d491246b17eeec15234a3d53a1bb9
--- /dev/null
+++ b/webify_models_v2.py
@@ -0,0 +1,99 @@
+#!/usr/bin/python
+
+# A script to update gazebo models to be web-friendly.
+# It converts all textures to png format and make sure they are
+# stored in the [model_name]/materials/textures/ directory.
+# Texture references in the collada dae files are also updated
+# to have the correct path.
+
+# Usage: ./webify_models_v2.py path_to_model_directory
+
+import os
+import subprocess
+import sys
+import shutil
+
+# explicitly set encoding in osx otherwise sometimes sed throws
+# an illegal byte sequence error
+if sys.platform == 'darwin':
+  os.environ['LANG'] = 'C'
+
+path = sys.argv[1]
+
+files = os.listdir(path)
+
+find_cmd = ['find', path, '-name','*']
+files = subprocess.check_output(find_cmd).split()
+
+for file in files:
+  try:
+    path, filename = os.path.split(file)
+    name, format = filename.split(".")[-2:]
+
+  except:
+    continue # not a texture
+  try:
+    dest_dir = path
+    dest_path = "%s/%s.png" % (dest_dir, name)
+    cmd = None
+
+    # convert texture files to png
+    if format.lower() in ['tif', 'tga', 'tiff', 'jpeg', 'jpg', 'gif', 'png']:
+      if dest_path != file:
+        cmd = ['convert', file, dest_path]
+        subprocess.check_call(cmd)
+        cmd = ['rm', file]
+        subprocess.check_call(cmd)
+
+      # make sure texture files only exist in materials/textures dir
+      texture_dir = path
+      if (texture_dir.find('materials/textures') == -1 and
+          texture_dir.find('meshes') != -1):
+        model_path, other = texture_dir.split('meshes')
+        texture_dir = os.path.join(model_path, 'materials/textures')
+        cmd = ['mkdir', '-p', texture_dir]
+        subprocess.check_call(cmd)
+
+      if texture_dir != dest_dir:
+        cmd = ['mv', dest_path, texture_dir]
+        print cmd
+        subprocess.check_call(cmd)
+
+    # update texture path in dae files
+    if format.lower() in ['dae']:
+      sed_cmd = ["sed", "-i", "-e",
+        's/\(\.tga\|\.tiff\|\.tif\|\.jpg\|\.jpeg\|\.gif\)/\.png/g', file]
+      print sed_cmd
+      subprocess.check_call(sed_cmd)
+
+      # find relatvie path to texture dir
+      texture_dir = path
+      if (texture_dir.find('materials/textures') == -1 and
+          texture_dir.find('meshes') != -1):
+        model_path, other = texture_dir.split('meshes')
+        subdir_count = len(other.split('/'))
+        relative_path = ''
+        for i in range(0, subdir_count):
+          relative_path = relative_path + '\.\.\/'
+
+        # replace dae file png references to texture path
+        sed_cmd = ["sed", "-i","-e",
+          's/\(>\)\(.*\/\)\(.*\.png\)/\\1'+ relative_path + 'materials\/textures\/\\3/g', file]
+        print sed_cmd
+        subprocess.check_call(sed_cmd)
+
+        sed_cmd = ["sed", "-i","-e",
+          '/[a-zA-Z0-9_\.\/\-]\+materials\/textures/!s/\([a-zA-Z0-9_\-]\+\)\(\.png\W\)/'+ relative_path + 'materials\/textures\/\\1\\2/g', file]
+
+        print sed_cmd
+        subprocess.check_call(sed_cmd)
+
+    if format.lower() in ['material', 'txt', 'sdf']:
+      sed_cmd = ["sed", "-i", "-e",
+        's/\(\.tga\|\.tiff\|\.tif\|\.jpg\|\.jpeg\|\.gif\)/\.png/g', file]
+      print sed_cmd
+      subprocess.check_call(sed_cmd)
+
+  except Exception, e:
+      print "error %s" % e
+      raise