[med-svn] [pdb2pqr] 02/05: Imported Upstream version 2.1.0+dfsg

Andreas Tille tille at debian.org
Fri Jan 8 10:23:06 UTC 2016


This is an automated email from the git hooks/post-receive script.

tille pushed a commit to branch master
in repository pdb2pqr.

commit 3283ea28715fd00e66d26c5080844656f4fd47d6
Author: Andreas Tille <tille at debian.org>
Date:   Fri Jan 8 10:46:17 2016 +0100

    Imported Upstream version 2.1.0+dfsg
---
 3dmol/js/3dmol.js.in                          |   420 -
 3dmol/js/foundation.min.js                    |  6081 -----
 3dmol/js/foundation/foundation.abide.js       |   340 -
 3dmol/js/foundation/foundation.accordion.js   |    67 -
 3dmol/js/foundation/foundation.alert.js       |    43 -
 3dmol/js/foundation/foundation.clearing.js    |   556 -
 3dmol/js/foundation/foundation.dropdown.js    |   448 -
 3dmol/js/foundation/foundation.equalizer.js   |    77 -
 3dmol/js/foundation/foundation.interchange.js |   354 -
 3dmol/js/foundation/foundation.joyride.js     |   932 -
 3dmol/js/foundation/foundation.js             |   703 -
 3dmol/js/foundation/foundation.magellan.js    |   203 -
 3dmol/js/foundation/foundation.offcanvas.js   |   152 -
 3dmol/js/foundation/foundation.orbit.js       |   476 -
 3dmol/js/foundation/foundation.reveal.js      |   471 -
 3dmol/js/foundation/foundation.slider.js      |   263 -
 3dmol/js/foundation/foundation.tab.js         |   237 -
 3dmol/js/foundation/foundation.tooltip.js     |   307 -
 3dmol/js/foundation/foundation.topbar.js      |   452 -
 3dmol/js/pitt_3Dmol.js                        | 31446 ------------------------
 3dmol/js/vendor/fastclick.js                  |     8 -
 3dmol/js/vendor/jquery.cookie.js              |     8 -
 3dmol/js/vendor/jquery.js                     |    26 -
 3dmol/js/vendor/modernizr.js                  |     8 -
 3dmol/js/vendor/placeholder.js                |     2 -
 3dmol/js/visualize_html.js                    |   111 -
 26 files changed, 44191 deletions(-)

diff --git a/3dmol/js/3dmol.js.in b/3dmol/js/3dmol.js.in
deleted file mode 100644
index da131fa..0000000
--- a/3dmol/js/3dmol.js.in
+++ /dev/null
@@ -1,420 +0,0 @@
-/* 3Dmol functions 
-*
-*
-*/
-
-   //protein object
-    var protein = {
-        surface: $3Dmol.SurfaceType.SAS,
-        opacity: 1,
-        min_isoval: -5,
-        max_isoval: 5,
-        colorScheme: "RWB",
-        volumedata: null
-    };
-
-    var volumedata = null;
-    var glviewer = null;
-    var labels = [];
-   
-
-    var addLabels = function() {
-        var atoms = glviewer.getModel().selectedAtoms({
-            atom : "CA"
-        });
-        for ( var a in atoms) {
-            var atom = atoms[a];
-
-            var l = glviewer.addLabel(atom.resn + " " + atom.resi, {
-                inFront : true,
-                fontSize : 12,
-                position : {
-                    x : atom.x,
-                    y : atom.y,
-                    z : atom.z
-                }
-            });
-            atom.label = l;
-            labels.push(atom);
-        }
-    };
-    
-    var removetheLabels = function() {
-        for (var i = 0; i < labels.length; i++) {
-        var atom = labels[i]
-        glviewer.removeLabel(atom.label)
-        delete atom.label
-        }
-        //console.log(labels)
-        
-        labels = []
-
-        };
-
-    /* removed until remove functionality works -- also see addpqr
-    var atomcallback = function(atom, viewer) {
-        if (atom.clickLabel === undefined
-                || !atom.clickLabel instanceof $3Dmol.Label) {
-            atom.clickLabel = viewer.addLabel(atom.elem + atom.serial, {
-                fontSize : 14,
-                position : {
-                    x : atom.x,
-                    y : atom.y,
-                    z : atom.z
-                },
-                backgroundColor: "gray"
-            });
-            atom.clicked = true;
-        }
-
-        //toggle label style
-        else {
-
-            //if (atom.clicked) {
-            //  var newstyle = atom.clickLabel.getStyle();
-            //  newstyle.backgroundColor = 0x66ccff;
-
-            //  viewer.setLabelStyle(atom.clickLabel, newstyle);
-            //  atom.clicked = !atom.clicked;
-            //}
-            if (atom.clicked) {
-                viewer.removeLabel(atom.clickLabel);
-                delete atom.clickLabel;
-                atom.clicked = false;
-            }
-
-        }
-    };
-    */
-    var glviewer;
-    $(document).ready(function() {
-        glviewer = $3Dmol.createViewer("gldiv", {
-        defaultcolors : $3Dmol.rasmolElementColors
-        });
-        glviewer.setBackgroundColor("black");
-
-    });
-    
-    var fileselected = function(files, func){
-        
-        
-        readText(files, func);
-
-        
-    };
-     
-    var addpqr = function(data){
-        
-        //moldata = data = $("#moldata_pdb_large").val();
-        //console.log(data); //see contents of file
-        receptorModel = m = glviewer.addModel(data, "pqr");
-
-        atoms = m.selectedAtoms({});
-
-        /* removed until remove atom functionality is fixed
-        for ( var i in atoms) {
-            var atom = atoms[i];
-            atom.clickable = true;
-            atom.callback = atomcallback;
-        }
-        */
-        glviewer.mapAtomProperties($3Dmol.applyPartialCharges);
-        glviewer.zoomTo();
-        glviewer.render();
-        
-        };
-        
-        
-    var addcube = function (volumedata){
-        //protein.volumedata = volumedata;
-        window.volumedata = new $3Dmol.VolumeData(volumedata, "cube");
-        //volumedata = $("#volumetric_data").val();
-        //glviewer.addIsosurface(volumedata, {isoval: -5, color:"red", smoothness: 10})
-        //glviewer.addIsosurface(volumedata, {isoval: 5, color:"blue", smoothness: 1})
-        
-        
-        glviewer.render();
-        create_surface();
-        };
-    
-    var backbone = function (){
-        var atoms = glviewer.getModel().selectedAtoms({
-            });
-        for ( var i = 0; i < atoms.length; i++) {
-            var atom = atoms[i];
-        if (atom.atom == "H")
-        //    delete atom
-        //if (atom == "O")
-        //    delete atom
-        //if (atom.atom == "CA")
-        atoms.splice(i,1);
-        }
-    }
-   
-    var readText = function(input,func) {
-        
-        if(input.length > 0) {
-            var file = input[0];
-            var reader = new FileReader();
-            reader.onload = function(evt) {
-                func(evt.target.result,file.name);
-            };
-            reader.readAsText(file); //needs to be type Blob
-            $(input).val('');
-            
-        }
-
-    };
-    
-    var distance = function(atom1, atom2) {
-        m = glviewer.getModel(0);
-        myatoms = m.selectedAtoms({});
-        //console.log(myatoms)
-        for ( var i in myatoms) {
-        var myatom = myatoms[i];
-        myatom.clickable = true;
-    }   
-        myatom.onclick = console.log(myatom)
-    };
-
-    /*update surface based on selected action 
-    * 0 -  
-    * 1 - change surface
-    * 2 - set translucent
-    * 3 - set opaque
-    */
-    function update_surface(action){
-        var e = document.getElementById("selected_surface");
-        var x = e.options[e.selectedIndex].value;
-        glviewer.removeSurface(surf);
-        switch (action){
-            case 1:
-                if (x == 'SAS')
-                   protein.surface = $3Dmol.SurfaceType.SAS;
-                else if (x == 'SES')
-                    protein.surface = $3Dmol.SurfaceType.SES;
-                else if (x == 'VDW')
-                    protein.surface = $3Dmol.SurfaceType.VDW;
-                break;
-            case 2:
-                protein.opacity = 0.70;
-                break;
-            case 3: 
-                protein.opacity = 1;
-                break;
-            case 4:
-                protein.min_isoval = -5;
-                protein.max_isoval = 5;
-                break;
-            
-            default:
-                break;
-        }
-            set_color();
-        }
-
-        function show_colorbar(){
-            var w = document.getElementById("selected_scheme");
-            var y = w.options[w.selectedIndex].value;
-            //console.log(y);
-            if(y=='RWB')
-               document.getElementById("colorbar").innerHTML ="<img src=3dmol/images/rwb.png width='250'>";
-            if(y=='RGB')
-                document.getElementById("colorbar").innerHTML ="<img src=3dmol/images/rgb.png width='250'>";
-
-        }
-
-        function surface_vis(checkbox){
-            //console.log(here);
-            if(checkbox.checked)
-                on_surface();
-            else
-                glviewer.removeSurface(surf);
-        }
-
-        function surface_opacity(checkbox){
-            //console.log(here);
-            if(checkbox.checked)
-                update_surface(3);
-            else
-                update_surface(2);
-        }
-
-        function surface_labels(checkbox){
-            //console.log(here);
-            if(checkbox.checked){
-                removetheLabels(glviewer);
-                glviewer.render();
-            }
-            else{
-                addLabels(glviewer); 
-                glviewer.render();
-            }
-        }
-
-        function set_vis(){
-            var f = document.getElementById("selected_vis");
-            var y = f.options[f.selectedIndex].value;
-            vis=y;
-
-            if(y=="stick"){ glviewer.setStyle({},{stick:{}}); glviewer.render();}
-            if(y=="line"){glviewer.setStyle({},{line:{}}); glviewer.render();}
-            if(y=="cross"){glviewer.setStyle({},{cross:{linewidth:5}}); glviewer.render();}
-            if(y=="sphere"){glviewer.setStyle({},{sphere:{}}); glviewer.render();}
-            if(y=="cartoon"){glviewer.setStyle({hetflag:false},{cartoon:{color: 'spectrum'}}); glviewer.render();}
-        }
-
-        function set_color(){
-        //inefficient -- need to fix!
-        //want to set as protein attribute
-        var f = document.getElementById("selected_scheme");
-        var y = f.options[f.selectedIndex].value;
-        protein.colorScheme=y;
-        
-        if(protein.colorScheme=="RWB")
-            volscheme_to_use = new $3Dmol.Gradient.RWB(protein.min_isoval,protein.max_isoval);
-        else if(protein.colorScheme=="RGB")
-            volscheme_to_use = new $3Dmol.Gradient.ROYGB(protein.min_isoval,protein.max_isoval);
-        else if(protein.colorScheme=="BWR")
-            volscheme_to_use = new $3Dmol.Gradient.Sinebow(protein.min_isoval,protein.max_isoval);
-        
-        surf = glviewer.addSurface(protein.surface, {opacity:protein.opacity, voldata: volumedata, volscheme: volscheme_to_use});
-        }
-        
-        //starts program with SAS surface
-        function create_surface(){
-            volscheme_to_use = new $3Dmol.Gradient.RWB(protein.min_isoval,protein.max_isoval);
-            surf = glviewer.addSurface(protein.surface, {opacity:protein.opacity, voldata: volumedata, volscheme: volscheme_to_use});
-        }
-
-        //Turn on the surface for the current selected surface
-        function on_surface(){
-            var e = document.getElementById("selected_surface");
-            var x = e.options[e.selectedIndex].value;
-            if (x == 'SAS')
-                protein.surface = $3Dmol.SurfaceType.SAS;
-            else if (x == 'SES')
-                protein.surface = $3Dmol.SurfaceType.SES;
-            else if (x == 'VDW')
-                protein.surface = $3Dmol.SurfaceType.VDW;
-
-            set_color();
-        }
-
-        //change output for min_isoval range
-        function set_min_isoval(min_val) {
-            document.querySelector('#min_isoval').value = min_val;
-            protein.min_isoval = min_val;
-            update_surface(0);
-        }
-
-        //change output for max_isoval range
-        function set_max_isoval(max_val) {
-            document.querySelector('#max_isoval').value = max_val; 
-            protein.max_isoval = max_val;
-            update_surface(0);
-        }
-
-        //reset min and max isovals
-        function reset_vals() {
-            set_min_isoval2(-5);
-            set_max_isoval2(5);
-            document.getElementById("min_isoval2").value = "-5";
-            document.getElementById("max_isoval2").value = "5";
-            update_surface(0);
-            return false;
-        }
-        
- //change output for min_isoval range, not perfect
-        function set_min_isoval2(min_val) {
-            document.getElementById("min_isoval").innerHTML = min_val;
-            protein.min_isoval = Number(min_val);
-            console.log(document.getElementById('min_isoval').value);
-            update_surface(0);
-        }
-
-        //change output for max_isoval range, not perfect
-        function set_max_isoval2(max_val) {
-            document.getElementById("max_isoval").innerHTML = max_val;
-            protein.max_isoval = Number(max_val);
-            update_surface(0);
-        }
-
-        function getpqr(jobid){
-            var xhr = new XMLHttpRequest();
-            //jobid = 14357857643;
-            url = "@website at tmp/"+jobid+"/"+jobid+".pqr";
-            //url = "../3dmol/files/1fas.pqr";
-            xhr.open("GET", url);
-            //xhr.responseType = 'blob';
-
-            xhr.onload = function(e) {
-              if (this.status == 200) {
-                // Note: .response instead of .responseText
-                //var blob = new Blob([this.response], {type: 'text/plain'});
-               //readText(this.response);
-               addpqr(this.response);
-              }
-              
-            };
-            xhr.send(null);
-            
-        }
-
-        function getcube(jobid){
-            var xhr = new XMLHttpRequest();
-            xhr.open("GET", "@website at tmp/"+jobid+"/"+jobid+".cube");
-            //xhr.open("GET", "../3dmol/files/1fas.cube");
-            //xhr.responseType = 'blob';
-
-            xhr.onload = function(e) {
-              if (this.status == 200) {
-                // Note: .response instead of .responseText
-                //var blob = new Blob([this.response], {type: 'text/plain'});
-               //readText(this.response);
-               addcube(this.response);
-              }
-              
-            };
-            xhr.send(null);
-            
-        }
-
-var surfaceOn = true
-function toggleSurface(){
-    if(surfaceOn){
-        surfaceOn = false
-        on_surface()
-    }
-    else{
-        surfaceOn = true
-        glviewer.removeSurface(surf)
-    }
-}
-
-var surfaceOpacity = true
-function toggleOpacity(){
-    if(surfaceOpacity){
-        update_surface(3)
-        surfaceOpacity = false
-    }
-    else{
-        update_surface(2)
-        surfaceOpacity = true
-    }
-}
-
-var modelLabels = false
-function toggleLabels(){
-    if(modelLabels){
-        removetheLabels(glviewer);
-        glviewer.render();
-        modelLabels = false
-    }
-    else{
-        addLabels(glviewer);
-        glviewer.render();
-        modelLabels = true
-    }
-}
-
diff --git a/3dmol/js/foundation.min.js b/3dmol/js/foundation.min.js
deleted file mode 100644
index 4a23ccc..0000000
--- a/3dmol/js/foundation.min.js
+++ /dev/null
@@ -1,6081 +0,0 @@
-/*
- * Foundation Responsive Library
- * http://foundation.zurb.com
- * Copyright 2014, ZURB
- * Free to use under the MIT license.
- * http://www.opensource.org/licenses/mit-license.php
-*/
-
-(function ($, window, document, undefined) {
-  'use strict';
-
-  var header_helpers = function (class_array) {
-    var i = class_array.length;
-    var head = $('head');
-
-    while (i--) {
-      if (head.has('.' + class_array[i]).length === 0) {
-        head.append('<meta class="' + class_array[i] + '" />');
-      }
-    }
-  };
-
-  header_helpers([
-    'foundation-mq-small',
-    'foundation-mq-small-only',
-    'foundation-mq-medium',
-    'foundation-mq-medium-only',
-    'foundation-mq-large',
-    'foundation-mq-large-only',
-    'foundation-mq-xlarge',
-    'foundation-mq-xlarge-only',
-    'foundation-mq-xxlarge',
-    'foundation-data-attribute-namespace']);
-
-  // Enable FastClick if present
-
-  $(function () {
-    if (typeof FastClick !== 'undefined') {
-      // Don't attach to body if undefined
-      if (typeof document.body !== 'undefined') {
-        FastClick.attach(document.body);
-      }
-    }
-  });
-
-  // private Fast Selector wrapper,
-  // returns jQuery object. Only use where
-  // getElementById is not available.
-  var S = function (selector, context) {
-    if (typeof selector === 'string') {
-      if (context) {
-        var cont;
-        if (context.jquery) {
-          cont = context[0];
-          if (!cont) {
-            return context;
-          }
-        } else {
-          cont = context;
-        }
-        return $(cont.querySelectorAll(selector));
-      }
-
-      return $(document.querySelectorAll(selector));
-    }
-
-    return $(selector, context);
-  };
-
-  // Namespace functions.
-
-  var attr_name = function (init) {
-    var arr = [];
-    if (!init) {
-      arr.push('data');
-    }
-    if (this.namespace.length > 0) {
-      arr.push(this.namespace);
-    }
-    arr.push(this.name);
-
-    return arr.join('-');
-  };
-
-  var add_namespace = function (str) {
-    var parts = str.split('-'),
-        i = parts.length,
-        arr = [];
-
-    while (i--) {
-      if (i !== 0) {
-        arr.push(parts[i]);
-      } else {
-        if (this.namespace.length > 0) {
-          arr.push(this.namespace, parts[i]);
-        } else {
-          arr.push(parts[i]);
-        }
-      }
-    }
-
-    return arr.reverse().join('-');
-  };
-
-  // Event binding and data-options updating.
-
-  var bindings = function (method, options) {
-    var self = this,
-        bind = function(){
-          var $this = S(this),
-              should_bind_events = !$this.data(self.attr_name(true) + '-init');
-          $this.data(self.attr_name(true) + '-init', $.extend({}, self.settings, (options || method), self.data_options($this)));
-
-          if (should_bind_events) {
-            self.events(this);
-          }
-        };
-
-    if (S(this.scope).is('[' + this.attr_name() +']')) {
-      bind.call(this.scope);
-    } else {
-      S('[' + this.attr_name() +']', this.scope).each(bind);
-    }
-    // # Patch to fix #5043 to move this *after* the if/else clause in order for Backbone and similar frameworks to have improved control over event binding and data-options updating.
-    if (typeof method === 'string') {
-      return this[method].call(this, options);
-    }
-
-  };
-
-  var single_image_loaded = function (image, callback) {
-    function loaded () {
-      callback(image[0]);
-    }
-
-    function bindLoad () {
-      this.one('load', loaded);
-
-      if (/MSIE (\d+\.\d+);/.test(navigator.userAgent)) {
-        var src = this.attr( 'src' ),
-            param = src.match( /\?/ ) ? '&' : '?';
-
-        param += 'random=' + (new Date()).getTime();
-        this.attr('src', src + param);
-      }
-    }
-
-    if (!image.attr('src')) {
-      loaded();
-      return;
-    }
-
-    if (image[0].complete || image[0].readyState === 4) {
-      loaded();
-    } else {
-      bindLoad.call(image);
-    }
-  };
-
-  /*
-    https://github.com/paulirish/matchMedia.js
-  */
-
-  window.matchMedia = window.matchMedia || (function ( doc ) {
-
-    'use strict';
-
-    var bool,
-        docElem = doc.documentElement,
-        refNode = docElem.firstElementChild || docElem.firstChild,
-        // fakeBody required for <FF4 when executed in <head>
-        fakeBody = doc.createElement( 'body' ),
-        div = doc.createElement( 'div' );
-
-    div.id = 'mq-test-1';
-    div.style.cssText = 'position:absolute;top:-100em';
-    fakeBody.style.background = 'none';
-    fakeBody.appendChild(div);
-
-    return function (q) {
-
-      div.innerHTML = '­<style media="' + q + '"> #mq-test-1 { width: 42px; }</style>';
-
-      docElem.insertBefore( fakeBody, refNode );
-      bool = div.offsetWidth === 42;
-      docElem.removeChild( fakeBody );
-
-      return {
-        matches : bool,
-        media : q
-      };
-
-    };
-
-  }( document ));
-
-  /*
-   * jquery.requestAnimationFrame
-   * https://github.com/gnarf37/jquery-requestAnimationFrame
-   * Requires jQuery 1.8+
-   *
-   * Copyright (c) 2012 Corey Frang
-   * Licensed under the MIT license.
-   */
-
-  (function(jQuery) {
-
-
-  // requestAnimationFrame polyfill adapted from Erik Möller
-  // fixes from Paul Irish and Tino Zijdel
-  // http://paulirish.com/2011/requestanimationframe-for-smart-animating/
-  // http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
-
-  var animating,
-      lastTime = 0,
-      vendors = ['webkit', 'moz'],
-      requestAnimationFrame = window.requestAnimationFrame,
-      cancelAnimationFrame = window.cancelAnimationFrame,
-      jqueryFxAvailable = 'undefined' !== typeof jQuery.fx;
-
-  for (; lastTime < vendors.length && !requestAnimationFrame; lastTime++) {
-    requestAnimationFrame = window[ vendors[lastTime] + 'RequestAnimationFrame' ];
-    cancelAnimationFrame = cancelAnimationFrame ||
-      window[ vendors[lastTime] + 'CancelAnimationFrame' ] ||
-      window[ vendors[lastTime] + 'CancelRequestAnimationFrame' ];
-  }
-
-  function raf() {
-    if (animating) {
-      requestAnimationFrame(raf);
-
-      if (jqueryFxAvailable) {
-        jQuery.fx.tick();
-      }
-    }
-  }
-
-  if (requestAnimationFrame) {
-    // use rAF
-    window.requestAnimationFrame = requestAnimationFrame;
-    window.cancelAnimationFrame = cancelAnimationFrame;
-
-    if (jqueryFxAvailable) {
-      jQuery.fx.timer = function (timer) {
-        if (timer() && jQuery.timers.push(timer) && !animating) {
-          animating = true;
-          raf();
-        }
-      };
-
-      jQuery.fx.stop = function () {
-        animating = false;
-      };
-    }
-  } else {
-    // polyfill
-    window.requestAnimationFrame = function (callback) {
-      var currTime = new Date().getTime(),
-        timeToCall = Math.max(0, 16 - (currTime - lastTime)),
-        id = window.setTimeout(function () {
-          callback(currTime + timeToCall);
-        }, timeToCall);
-      lastTime = currTime + timeToCall;
-      return id;
-    };
-
-    window.cancelAnimationFrame = function (id) {
-      clearTimeout(id);
-    };
-
-  }
-
-  }( $ ));
-
-  function removeQuotes (string) {
-    if (typeof string === 'string' || string instanceof String) {
-      string = string.replace(/^['\\/"]+|(;\s?})+|['\\/"]+$/g, '');
-    }
-
-    return string;
-  }
-
-  window.Foundation = {
-    name : 'Foundation',
-
-    version : '5.5.1',
-
-    media_queries : {
-      'small'       : S('.foundation-mq-small').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, ''),
-      'small-only'  : S('.foundation-mq-small-only').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, ''),
-      'medium'      : S('.foundation-mq-medium').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, ''),
-      'medium-only' : S('.foundation-mq-medium-only').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, ''),
-      'large'       : S('.foundation-mq-large').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, ''),
-      'large-only'  : S('.foundation-mq-large-only').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, ''),
-      'xlarge'      : S('.foundation-mq-xlarge').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, ''),
-      'xlarge-only' : S('.foundation-mq-xlarge-only').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, ''),
-      'xxlarge'     : S('.foundation-mq-xxlarge').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, '')
-    },
-
-    stylesheet : $('<style></style>').appendTo('head')[0].sheet,
-
-    global : {
-      namespace : undefined
-    },
-
-    init : function (scope, libraries, method, options, response) {
-      var args = [scope, method, options, response],
-          responses = [];
-
-      // check RTL
-      this.rtl = /rtl/i.test(S('html').attr('dir'));
-
-      // set foundation global scope
-      this.scope = scope || this.scope;
-
-      this.set_namespace();
-
-      if (libraries && typeof libraries === 'string' && !/reflow/i.test(libraries)) {
-        if (this.libs.hasOwnProperty(libraries)) {
-          responses.push(this.init_lib(libraries, args));
-        }
-      } else {
-        for (var lib in this.libs) {
-          responses.push(this.init_lib(lib, libraries));
-        }
-      }
-
-      S(window).load(function () {
-        S(window)
-          .trigger('resize.fndtn.clearing')
-          .trigger('resize.fndtn.dropdown')
-          .trigger('resize.fndtn.equalizer')
-          .trigger('resize.fndtn.interchange')
-          .trigger('resize.fndtn.joyride')
-          .trigger('resize.fndtn.magellan')
-          .trigger('resize.fndtn.topbar')
-          .trigger('resize.fndtn.slider');
-      });
-
-      return scope;
-    },
-
-    init_lib : function (lib, args) {
-      if (this.libs.hasOwnProperty(lib)) {
-        this.patch(this.libs[lib]);
-
-        if (args && args.hasOwnProperty(lib)) {
-            if (typeof this.libs[lib].settings !== 'undefined') {
-              $.extend(true, this.libs[lib].settings, args[lib]);
-            } else if (typeof this.libs[lib].defaults !== 'undefined') {
-              $.extend(true, this.libs[lib].defaults, args[lib]);
-            }
-          return this.libs[lib].init.apply(this.libs[lib], [this.scope, args[lib]]);
-        }
-
-        args = args instanceof Array ? args : new Array(args);
-        return this.libs[lib].init.apply(this.libs[lib], args);
-      }
-
-      return function () {};
-    },
-
-    patch : function (lib) {
-      lib.scope = this.scope;
-      lib.namespace = this.global.namespace;
-      lib.rtl = this.rtl;
-      lib['data_options'] = this.utils.data_options;
-      lib['attr_name'] = attr_name;
-      lib['add_namespace'] = add_namespace;
-      lib['bindings'] = bindings;
-      lib['S'] = this.utils.S;
-    },
-
-    inherit : function (scope, methods) {
-      var methods_arr = methods.split(' '),
-          i = methods_arr.length;
-
-      while (i--) {
-        if (this.utils.hasOwnProperty(methods_arr[i])) {
-          scope[methods_arr[i]] = this.utils[methods_arr[i]];
-        }
-      }
-    },
-
-    set_namespace : function () {
-
-      // Description:
-      //    Don't bother reading the namespace out of the meta tag
-      //    if the namespace has been set globally in javascript
-      //
-      // Example:
-      //    Foundation.global.namespace = 'my-namespace';
-      // or make it an empty string:
-      //    Foundation.global.namespace = '';
-      //
-      //
-
-      // If the namespace has not been set (is undefined), try to read it out of the meta element.
-      // Otherwise use the globally defined namespace, even if it's empty ('')
-      var namespace = ( this.global.namespace === undefined ) ? $('.foundation-data-attribute-namespace').css('font-family') : this.global.namespace;
-
-      // Finally, if the namsepace is either undefined or false, set it to an empty string.
-      // Otherwise use the namespace value.
-      this.global.namespace = ( namespace === undefined || /false/i.test(namespace) ) ? '' : namespace;
-    },
-
-    libs : {},
-
-    // methods that can be inherited in libraries
-    utils : {
-
-      // Description:
-      //    Fast Selector wrapper returns jQuery object. Only use where getElementById
-      //    is not available.
-      //
-      // Arguments:
-      //    Selector (String): CSS selector describing the element(s) to be
-      //    returned as a jQuery object.
-      //
-      //    Scope (String): CSS selector describing the area to be searched. Default
-      //    is document.
-      //
-      // Returns:
-      //    Element (jQuery Object): jQuery object containing elements matching the
-      //    selector within the scope.
-      S : S,
-
-      // Description:
-      //    Executes a function a max of once every n milliseconds
-      //
-      // Arguments:
-      //    Func (Function): Function to be throttled.
-      //
-      //    Delay (Integer): Function execution threshold in milliseconds.
-      //
-      // Returns:
-      //    Lazy_function (Function): Function with throttling applied.
-      throttle : function (func, delay) {
-        var timer = null;
-
-        return function () {
-          var context = this, args = arguments;
-
-          if (timer == null) {
-            timer = setTimeout(function () {
-              func.apply(context, args);
-              timer = null;
-            }, delay);
-          }
-        };
-      },
-
-      // Description:
-      //    Executes a function when it stops being invoked for n seconds
-      //    Modified version of _.debounce() http://underscorejs.org
-      //
-      // Arguments:
-      //    Func (Function): Function to be debounced.
-      //
-      //    Delay (Integer): Function execution threshold in milliseconds.
-      //
-      //    Immediate (Bool): Whether the function should be called at the beginning
-      //    of the delay instead of the end. Default is false.
-      //
-      // Returns:
-      //    Lazy_function (Function): Function with debouncing applied.
-      debounce : function (func, delay, immediate) {
-        var timeout, result;
-        return function () {
-          var context = this, args = arguments;
-          var later = function () {
-            timeout = null;
-            if (!immediate) {
-              result = func.apply(context, args);
-            }
-          };
-          var callNow = immediate && !timeout;
-          clearTimeout(timeout);
-          timeout = setTimeout(later, delay);
-          if (callNow) {
-            result = func.apply(context, args);
-          }
-          return result;
-        };
-      },
-
-      // Description:
-      //    Parses data-options attribute
-      //
-      // Arguments:
-      //    El (jQuery Object): Element to be parsed.
-      //
-      // Returns:
-      //    Options (Javascript Object): Contents of the element's data-options
-      //    attribute.
-      data_options : function (el, data_attr_name) {
-        data_attr_name = data_attr_name || 'options';
-        var opts = {}, ii, p, opts_arr,
-            data_options = function (el) {
-              var namespace = Foundation.global.namespace;
-
-              if (namespace.length > 0) {
-                return el.data(namespace + '-' + data_attr_name);
-              }
-
-              return el.data(data_attr_name);
-            };
-
-        var cached_options = data_options(el);
-
-        if (typeof cached_options === 'object') {
-          return cached_options;
-        }
-
-        opts_arr = (cached_options || ':').split(';');
-        ii = opts_arr.length;
-
-        function isNumber (o) {
-          return !isNaN (o - 0) && o !== null && o !== '' && o !== false && o !== true;
-        }
-
-        function trim (str) {
-          if (typeof str === 'string') {
-            return $.trim(str);
-          }
-          return str;
-        }
-
-        while (ii--) {
-          p = opts_arr[ii].split(':');
-          p = [p[0], p.slice(1).join(':')];
-
-          if (/true/i.test(p[1])) {
-            p[1] = true;
-          }
-          if (/false/i.test(p[1])) {
-            p[1] = false;
-          }
-          if (isNumber(p[1])) {
-            if (p[1].indexOf('.') === -1) {
-              p[1] = parseInt(p[1], 10);
-            } else {
-              p[1] = parseFloat(p[1]);
-            }
-          }
-
-          if (p.length === 2 && p[0].length > 0) {
-            opts[trim(p[0])] = trim(p[1]);
-          }
-        }
-
-        return opts;
-      },
-
-      // Description:
-      //    Adds JS-recognizable media queries
-      //
-      // Arguments:
-      //    Media (String): Key string for the media query to be stored as in
-      //    Foundation.media_queries
-      //
-      //    Class (String): Class name for the generated <meta> tag
-      register_media : function (media, media_class) {
-        if (Foundation.media_queries[media] === undefined) {
-          $('head').append('<meta class="' + media_class + '"/>');
-          Foundation.media_queries[media] = removeQuotes($('.' + media_class).css('font-family'));
-        }
-      },
-
-      // Description:
-      //    Add custom CSS within a JS-defined media query
-      //
-      // Arguments:
-      //    Rule (String): CSS rule to be appended to the document.
-      //
-      //    Media (String): Optional media query string for the CSS rule to be
-      //    nested under.
-      add_custom_rule : function (rule, media) {
-        if (media === undefined && Foundation.stylesheet) {
-          Foundation.stylesheet.insertRule(rule, Foundation.stylesheet.cssRules.length);
-        } else {
-          var query = Foundation.media_queries[media];
-
-          if (query !== undefined) {
-            Foundation.stylesheet.insertRule('@media ' +
-              Foundation.media_queries[media] + '{ ' + rule + ' }');
-          }
-        }
-      },
-
-      // Description:
-      //    Performs a callback function when an image is fully loaded
-      //
-      // Arguments:
-      //    Image (jQuery Object): Image(s) to check if loaded.
-      //
-      //    Callback (Function): Function to execute when image is fully loaded.
-      image_loaded : function (images, callback) {
-        var self = this,
-            unloaded = images.length;
-
-        if (unloaded === 0) {
-          callback(images);
-        }
-
-        images.each(function () {
-          single_image_loaded(self.S(this), function () {
-            unloaded -= 1;
-            if (unloaded === 0) {
-              callback(images);
-            }
-          });
-        });
-      },
-
-      // Description:
-      //    Returns a random, alphanumeric string
-      //
-      // Arguments:
-      //    Length (Integer): Length of string to be generated. Defaults to random
-      //    integer.
-      //
-      // Returns:
-      //    Rand (String): Pseudo-random, alphanumeric string.
-      random_str : function () {
-        if (!this.fidx) {
-          this.fidx = 0;
-        }
-        this.prefix = this.prefix || [(this.name || 'F'), (+new Date).toString(36)].join('-');
-
-        return this.prefix + (this.fidx++).toString(36);
-      },
-
-      // Description:
-      //    Helper for window.matchMedia
-      //
-      // Arguments:
-      //    mq (String): Media query
-      //
-      // Returns:
-      //    (Boolean): Whether the media query passes or not
-      match : function (mq) {
-        return window.matchMedia(mq).matches;
-      },
-
-      // Description:
-      //    Helpers for checking Foundation default media queries with JS
-      //
-      // Returns:
-      //    (Boolean): Whether the media query passes or not
-
-      is_small_up : function () {
-        return this.match(Foundation.media_queries.small);
-      },
-
-      is_medium_up : function () {
-        return this.match(Foundation.media_queries.medium);
-      },
-
-      is_large_up : function () {
-        return this.match(Foundation.media_queries.large);
-      },
-
-      is_xlarge_up : function () {
-        return this.match(Foundation.media_queries.xlarge);
-      },
-
-      is_xxlarge_up : function () {
-        return this.match(Foundation.media_queries.xxlarge);
-      },
-
-      is_small_only : function () {
-        return !this.is_medium_up() && !this.is_large_up() && !this.is_xlarge_up() && !this.is_xxlarge_up();
-      },
-
-      is_medium_only : function () {
-        return this.is_medium_up() && !this.is_large_up() && !this.is_xlarge_up() && !this.is_xxlarge_up();
-      },
-
-      is_large_only : function () {
-        return this.is_medium_up() && this.is_large_up() && !this.is_xlarge_up() && !this.is_xxlarge_up();
-      },
-
-      is_xlarge_only : function () {
-        return this.is_medium_up() && this.is_large_up() && this.is_xlarge_up() && !this.is_xxlarge_up();
-      },
-
-      is_xxlarge_only : function () {
-        return this.is_medium_up() && this.is_large_up() && this.is_xlarge_up() && this.is_xxlarge_up();
-      }
-    }
-  };
-
-  $.fn.foundation = function () {
-    var args = Array.prototype.slice.call(arguments, 0);
-
-    return this.each(function () {
-      Foundation.init.apply(Foundation, [this].concat(args));
-      return this;
-    });
-  };
-
-}(jQuery, window, window.document));
-;(function ($, window, document, undefined) {
-  'use strict';
-
-  Foundation.libs.slider = {
-    name : 'slider',
-
-    version : '5.5.1',
-
-    settings : {
-      start : 0,
-      end : 100,
-      step : 1,
-      precision : null,
-      initial : null,
-      display_selector : '',
-      vertical : false,
-      trigger_input_change : false,
-      on_change : function () {}
-    },
-
-    cache : {},
-
-    init : function (scope, method, options) {
-      Foundation.inherit(this, 'throttle');
-      this.bindings(method, options);
-      this.reflow();
-    },
-
-    events : function () {
-      var self = this;
-
-      $(this.scope)
-        .off('.slider')
-        .on('mousedown.fndtn.slider touchstart.fndtn.slider pointerdown.fndtn.slider',
-        '[' + self.attr_name() + ']:not(.disabled, [disabled]) .range-slider-handle', function (e) {
-          if (!self.cache.active) {
-            e.preventDefault();
-            self.set_active_slider($(e.target));
-          }
-        })
-        .on('mousemove.fndtn.slider touchmove.fndtn.slider pointermove.fndtn.slider', function (e) {
-          if (!!self.cache.active) {
-            e.preventDefault();
-            if ($.data(self.cache.active[0], 'settings').vertical) {
-              var scroll_offset = 0;
-              if (!e.pageY) {
-                scroll_offset = window.scrollY;
-              }
-              self.calculate_position(self.cache.active, self.get_cursor_position(e, 'y') + scroll_offset);
-            } else {
-              self.calculate_position(self.cache.active, self.get_cursor_position(e, 'x'));
-            }
-          }
-        })
-        .on('mouseup.fndtn.slider touchend.fndtn.slider pointerup.fndtn.slider', function (e) {
-          self.remove_active_slider();
-        })
-        .on('change.fndtn.slider', function (e) {
-          self.settings.on_change();
-        });
-
-      self.S(window)
-        .on('resize.fndtn.slider', self.throttle(function (e) {
-          self.reflow();
-        }, 300));
-    },
-
-    get_cursor_position : function (e, xy) {
-      var pageXY = 'page' + xy.toUpperCase(),
-          clientXY = 'client' + xy.toUpperCase(),
-          position;
-
-      if (typeof e[pageXY] !== 'undefined') {
-        position = e[pageXY];
-      } else if (typeof e.originalEvent[clientXY] !== 'undefined') {
-        position = e.originalEvent[clientXY];
-      } else if (e.originalEvent.touches && e.originalEvent.touches[0] && typeof e.originalEvent.touches[0][clientXY] !== 'undefined') {
-        position = e.originalEvent.touches[0][clientXY];
-      } else if (e.currentPoint && typeof e.currentPoint[xy] !== 'undefined') {
-        position = e.currentPoint[xy];
-      }
-
-      return position;
-    },
-
-    set_active_slider : function ($handle) {
-      this.cache.active = $handle;
-    },
-
-    remove_active_slider : function () {
-      this.cache.active = null;
-    },
-
-    calculate_position : function ($handle, cursor_x) {
-      var self = this,
-          settings = $.data($handle[0], 'settings'),
-          handle_l = $.data($handle[0], 'handle_l'),
-          handle_o = $.data($handle[0], 'handle_o'),
-          bar_l = $.data($handle[0], 'bar_l'),
-          bar_o = $.data($handle[0], 'bar_o');
-
-      requestAnimationFrame(function () {
-        var pct;
-
-        if (Foundation.rtl && !settings.vertical) {
-          pct = self.limit_to(((bar_o + bar_l - cursor_x) / bar_l), 0, 1);
-        } else {
-          pct = self.limit_to(((cursor_x - bar_o) / bar_l), 0, 1);
-        }
-
-        pct = settings.vertical ? 1 - pct : pct;
-
-        var norm = self.normalized_value(pct, settings.start, settings.end, settings.step, settings.precision);
-
-        self.set_ui($handle, norm);
-      });
-    },
-
-    set_ui : function ($handle, value) {
-      var settings = $.data($handle[0], 'settings'),
-          handle_l = $.data($handle[0], 'handle_l'),
-          bar_l = $.data($handle[0], 'bar_l'),
-          norm_pct = this.normalized_percentage(value, settings.start, settings.end),
-          handle_offset = norm_pct * (bar_l - handle_l) - 1,
-          progress_bar_length = norm_pct * 100,
-          $handle_parent = $handle.parent(),
-          $hidden_inputs = $handle.parent().children('input[type=hidden]');
-
-      if (Foundation.rtl && !settings.vertical) {
-        handle_offset = -handle_offset;
-      }
-
-      handle_offset = settings.vertical ? -handle_offset + bar_l - handle_l + 1 : handle_offset;
-      this.set_translate($handle, handle_offset, settings.vertical);
-
-      if (settings.vertical) {
-        $handle.siblings('.range-slider-active-segment').css('height', progress_bar_length + '%');
-      } else {
-        $handle.siblings('.range-slider-active-segment').css('width', progress_bar_length + '%');
-      }
-
-      $handle_parent.attr(this.attr_name(), value).trigger('change').trigger('change.fndtn.slider');
-
-      $hidden_inputs.val(value);
-      if (settings.trigger_input_change) {
-          $hidden_inputs.trigger('change');
-      }
-
-      if (!$handle[0].hasAttribute('aria-valuemin')) {
-        $handle.attr({
-          'aria-valuemin' : settings.start,
-          'aria-valuemax' : settings.end
-        });
-      }
-      $handle.attr('aria-valuenow', value);
-
-      if (settings.display_selector != '') {
-        $(settings.display_selector).each(function () {
-          if (this.hasOwnProperty('value')) {
-            $(this).val(value);
-          } else {
-            $(this).text(value);
-          }
-        });
-      }
-
-    },
-
-    normalized_percentage : function (val, start, end) {
-      return Math.min(1, (val - start) / (end - start));
-    },
-
-    normalized_value : function (val, start, end, step, precision) {
-      var range = end - start,
-          point = val * range,
-          mod = (point - (point % step)) / step,
-          rem = point % step,
-          round = ( rem >= step * 0.5 ? step : 0);
-      return ((mod * step + round) + start).toFixed(precision);
-    },
-
-    set_translate : function (ele, offset, vertical) {
-      if (vertical) {
-        $(ele)
-          .css('-webkit-transform', 'translateY(' + offset + 'px)')
-          .css('-moz-transform', 'translateY(' + offset + 'px)')
-          .css('-ms-transform', 'translateY(' + offset + 'px)')
-          .css('-o-transform', 'translateY(' + offset + 'px)')
-          .css('transform', 'translateY(' + offset + 'px)');
-      } else {
-        $(ele)
-          .css('-webkit-transform', 'translateX(' + offset + 'px)')
-          .css('-moz-transform', 'translateX(' + offset + 'px)')
-          .css('-ms-transform', 'translateX(' + offset + 'px)')
-          .css('-o-transform', 'translateX(' + offset + 'px)')
-          .css('transform', 'translateX(' + offset + 'px)');
-      }
-    },
-
-    limit_to : function (val, min, max) {
-      return Math.min(Math.max(val, min), max);
-    },
-
-    initialize_settings : function (handle) {
-      var settings = $.extend({}, this.settings, this.data_options($(handle).parent())),
-          decimal_places_match_result;
-
-      if (settings.precision === null) {
-        decimal_places_match_result = ('' + settings.step).match(/\.([\d]*)/);
-        settings.precision = decimal_places_match_result && decimal_places_match_result[1] ? decimal_places_match_result[1].length : 0;
-      }
-
-      if (settings.vertical) {
-        $.data(handle, 'bar_o', $(handle).parent().offset().top);
-        $.data(handle, 'bar_l', $(handle).parent().outerHeight());
-        $.data(handle, 'handle_o', $(handle).offset().top);
-        $.data(handle, 'handle_l', $(handle).outerHeight());
-      } else {
-        $.data(handle, 'bar_o', $(handle).parent().offset().left);
-        $.data(handle, 'bar_l', $(handle).parent().outerWidth());
-        $.data(handle, 'handle_o', $(handle).offset().left);
-        $.data(handle, 'handle_l', $(handle).outerWidth());
-      }
-
-      $.data(handle, 'bar', $(handle).parent());
-      $.data(handle, 'settings', settings);
-    },
-
-    set_initial_position : function ($ele) {
-      var settings = $.data($ele.children('.range-slider-handle')[0], 'settings'),
-          initial = ((typeof settings.initial == 'number' && !isNaN(settings.initial)) ? settings.initial : Math.floor((settings.end - settings.start) * 0.5 / settings.step) * settings.step + settings.start),
-          $handle = $ele.children('.range-slider-handle');
-      this.set_ui($handle, initial);
-    },
-
-    set_value : function (value) {
-      var self = this;
-      $('[' + self.attr_name() + ']', this.scope).each(function () {
-        $(this).attr(self.attr_name(), value);
-      });
-      if (!!$(this.scope).attr(self.attr_name())) {
-        $(this.scope).attr(self.attr_name(), value);
-      }
-      self.reflow();
-    },
-
-    reflow : function () {
-      var self = this;
-      self.S('[' + this.attr_name() + ']').each(function () {
-        var handle = $(this).children('.range-slider-handle')[0],
-            val = $(this).attr(self.attr_name());
-        self.initialize_settings(handle);
-
-        if (val) {
-          self.set_ui($(handle), parseFloat(val));
-        } else {
-          self.set_initial_position($(this));
-        }
-      });
-    }
-  };
-
-}(jQuery, window, window.document));
-;(function ($, window, document, undefined) {
-  'use strict';
-
-  var Modernizr = Modernizr || false;
-
-  Foundation.libs.joyride = {
-    name : 'joyride',
-
-    version : '5.5.1',
-
-    defaults : {
-      expose                   : false,     // turn on or off the expose feature
-      modal                    : true,      // Whether to cover page with modal during the tour
-      keyboard                 : true,      // enable left, right and esc keystrokes
-      tip_location             : 'bottom',  // 'top' or 'bottom' in relation to parent
-      nub_position             : 'auto',    // override on a per tooltip bases
-      scroll_speed             : 1500,      // Page scrolling speed in milliseconds, 0 = no scroll animation
-      scroll_animation         : 'linear',  // supports 'swing' and 'linear', extend with jQuery UI.
-      timer                    : 0,         // 0 = no timer , all other numbers = timer in milliseconds
-      start_timer_on_click     : true,      // true or false - true requires clicking the first button start the timer
-      start_offset             : 0,         // the index of the tooltip you want to start on (index of the li)
-      next_button              : true,      // true or false to control whether a next button is used
-      prev_button              : true,      // true or false to control whether a prev button is used
-      tip_animation            : 'fade',    // 'pop' or 'fade' in each tip
-      pause_after              : [],        // array of indexes where to pause the tour after
-      exposed                  : [],        // array of expose elements
-      tip_animation_fade_speed : 300,       // when tipAnimation = 'fade' this is speed in milliseconds for the transition
-      cookie_monster           : false,     // true or false to control whether cookies are used
-      cookie_name              : 'joyride', // Name the cookie you'll use
-      cookie_domain            : false,     // Will this cookie be attached to a domain, ie. '.notableapp.com'
-      cookie_expires           : 365,       // set when you would like the cookie to expire.
-      tip_container            : 'body',    // Where will the tip be attached
-      abort_on_close           : true,      // When true, the close event will not fire any callback
-      tip_location_patterns    : {
-        top : ['bottom'],
-        bottom : [], // bottom should not need to be repositioned
-        left : ['right', 'top', 'bottom'],
-        right : ['left', 'top', 'bottom']
-      },
-      post_ride_callback     : function () {},    // A method to call once the tour closes (canceled or complete)
-      post_step_callback     : function () {},    // A method to call after each step
-      pre_step_callback      : function () {},    // A method to call before each step
-      pre_ride_callback      : function () {},    // A method to call before the tour starts (passed index, tip, and cloned exposed element)
-      post_expose_callback   : function () {},    // A method to call after an element has been exposed
-      template : { // HTML segments for tip layout
-        link          : '<a href="#close" class="joyride-close-tip">×</a>',
-        timer         : '<div class="joyride-timer-indicator-wrap"><span class="joyride-timer-indicator"></span></div>',
-        tip           : '<div class="joyride-tip-guide"><span class="joyride-nub"></span></div>',
-        wrapper       : '<div class="joyride-content-wrapper"></div>',
-        button        : '<a href="#" class="small button joyride-next-tip"></a>',
-        prev_button   : '<a href="#" class="small button joyride-prev-tip"></a>',
-        modal         : '<div class="joyride-modal-bg"></div>',
-        expose        : '<div class="joyride-expose-wrapper"></div>',
-        expose_cover  : '<div class="joyride-expose-cover"></div>'
-      },
-      expose_add_class : '' // One or more space-separated class names to be added to exposed element
-    },
-
-    init : function (scope, method, options) {
-      Foundation.inherit(this, 'throttle random_str');
-
-      this.settings = this.settings || $.extend({}, this.defaults, (options || method));
-
-      this.bindings(method, options)
-    },
-
-    go_next : function () {
-      if (this.settings.$li.next().length < 1) {
-        this.end();
-      } else if (this.settings.timer > 0) {
-        clearTimeout(this.settings.automate);
-        this.hide();
-        this.show();
-        this.startTimer();
-      } else {
-        this.hide();
-        this.show();
-      }
-    },
-
-    go_prev : function () {
-      if (this.settings.$li.prev().length < 1) {
-        // Do nothing if there are no prev element
-      } else if (this.settings.timer > 0) {
-        clearTimeout(this.settings.automate);
-        this.hide();
-        this.show(null, true);
-        this.startTimer();
-      } else {
-        this.hide();
-        this.show(null, true);
-      }
-    },
-
-    events : function () {
-      var self = this;
-
-      $(this.scope)
-        .off('.joyride')
-        .on('click.fndtn.joyride', '.joyride-next-tip, .joyride-modal-bg', function (e) {
-          e.preventDefault();
-          this.go_next()
-        }.bind(this))
-        .on('click.fndtn.joyride', '.joyride-prev-tip', function (e) {
-          e.preventDefault();
-          this.go_prev();
-        }.bind(this))
-
-        .on('click.fndtn.joyride', '.joyride-close-tip', function (e) {
-          e.preventDefault();
-          this.end(this.settings.abort_on_close);
-        }.bind(this))
-
-        .on('keyup.fndtn.joyride', function (e) {
-          // Don't do anything if keystrokes are disabled
-          // or if the joyride is not being shown
-          if (!this.settings.keyboard || !this.settings.riding) {
-            return;
-          }
-
-          switch (e.which) {
-            case 39: // right arrow
-              e.preventDefault();
-              this.go_next();
-              break;
-            case 37: // left arrow
-              e.preventDefault();
-              this.go_prev();
-              break;
-            case 27: // escape
-              e.preventDefault();
-              this.end(this.settings.abort_on_close);
-          }
-        }.bind(this));
-
-      $(window)
-        .off('.joyride')
-        .on('resize.fndtn.joyride', self.throttle(function () {
-          if ($('[' + self.attr_name() + ']').length > 0 && self.settings.$next_tip && self.settings.riding) {
-            if (self.settings.exposed.length > 0) {
-              var $els = $(self.settings.exposed);
-
-              $els.each(function () {
-                var $this = $(this);
-                self.un_expose($this);
-                self.expose($this);
-              });
-            }
-
-            if (self.is_phone()) {
-              self.pos_phone();
-            } else {
-              self.pos_default(false);
-            }
-          }
-        }, 100));
-    },
-
-    start : function () {
-      var self = this,
-          $this = $('[' + this.attr_name() + ']', this.scope),
-          integer_settings = ['timer', 'scrollSpeed', 'startOffset', 'tipAnimationFadeSpeed', 'cookieExpires'],
-          int_settings_count = integer_settings.length;
-
-      if (!$this.length > 0) {
-        return;
-      }
-
-      if (!this.settings.init) {
-        this.events();
-      }
-
-      this.settings = $this.data(this.attr_name(true) + '-init');
-
-      // non configureable settings
-      this.settings.$content_el = $this;
-      this.settings.$body = $(this.settings.tip_container);
-      this.settings.body_offset = $(this.settings.tip_container).position();
-      this.settings.$tip_content = this.settings.$content_el.find('> li');
-      this.settings.paused = false;
-      this.settings.attempts = 0;
-      this.settings.riding = true;
-
-      // can we create cookies?
-      if (typeof $.cookie !== 'function') {
-        this.settings.cookie_monster = false;
-      }
-
-      // generate the tips and insert into dom.
-      if (!this.settings.cookie_monster || this.settings.cookie_monster && !$.cookie(this.settings.cookie_name)) {
-        this.settings.$tip_content.each(function (index) {
-          var $this = $(this);
-          this.settings = $.extend({}, self.defaults, self.data_options($this));
-
-          // Make sure that settings parsed from data_options are integers where necessary
-          var i = int_settings_count;
-          while (i--) {
-            self.settings[integer_settings[i]] = parseInt(self.settings[integer_settings[i]], 10);
-          }
-          self.create({$li : $this, index : index});
-        });
-
-        // show first tip
-        if (!this.settings.start_timer_on_click && this.settings.timer > 0) {
-          this.show('init');
-          this.startTimer();
-        } else {
-          this.show('init');
-        }
-
-      }
-    },
-
-    resume : function () {
-      this.set_li();
-      this.show();
-    },
-
-    tip_template : function (opts) {
-      var $blank, content;
-
-      opts.tip_class = opts.tip_class || '';
-
-      $blank = $(this.settings.template.tip).addClass(opts.tip_class);
-      content = $.trim($(opts.li).html()) +
-        this.prev_button_text(opts.prev_button_text, opts.index) +
-        this.button_text(opts.button_text) +
-        this.settings.template.link +
-        this.timer_instance(opts.index);
-
-      $blank.append($(this.settings.template.wrapper));
-      $blank.first().attr(this.add_namespace('data-index'), opts.index);
-      $('.joyride-content-wrapper', $blank).append(content);
-
-      return $blank[0];
-    },
-
-    timer_instance : function (index) {
-      var txt;
-
-      if ((index === 0 && this.settings.start_timer_on_click && this.settings.timer > 0) || this.settings.timer === 0) {
-        txt = '';
-      } else {
-        txt = $(this.settings.template.timer)[0].outerHTML;
-      }
-      return txt;
-    },
-
-    button_text : function (txt) {
-      if (this.settings.tip_settings.next_button) {
-        txt = $.trim(txt) || 'Next';
-        txt = $(this.settings.template.button).append(txt)[0].outerHTML;
-      } else {
-        txt = '';
-      }
-      return txt;
-    },
-
-    prev_button_text : function (txt, idx) {
-      if (this.settings.tip_settings.prev_button) {
-        txt = $.trim(txt) || 'Previous';
-
-        // Add the disabled class to the button if it's the first element
-        if (idx == 0) {
-          txt = $(this.settings.template.prev_button).append(txt).addClass('disabled')[0].outerHTML;
-        } else {
-          txt = $(this.settings.template.prev_button).append(txt)[0].outerHTML;
-        }
-      } else {
-        txt = '';
-      }
-      return txt;
-    },
-
-    create : function (opts) {
-      this.settings.tip_settings = $.extend({}, this.settings, this.data_options(opts.$li));
-      var buttonText = opts.$li.attr(this.add_namespace('data-button')) || opts.$li.attr(this.add_namespace('data-text')),
-          prevButtonText = opts.$li.attr(this.add_namespace('data-button-prev')) || opts.$li.attr(this.add_namespace('data-prev-text')),
-        tipClass = opts.$li.attr('class'),
-        $tip_content = $(this.tip_template({
-          tip_class : tipClass,
-          index : opts.index,
-          button_text : buttonText,
-          prev_button_text : prevButtonText,
-          li : opts.$li
-        }));
-
-      $(this.settings.tip_container).append($tip_content);
-    },
-
-    show : function (init, is_prev) {
-      var $timer = null;
-
-      // are we paused?
-      if (this.settings.$li === undefined || ($.inArray(this.settings.$li.index(), this.settings.pause_after) === -1)) {
-
-        // don't go to the next li if the tour was paused
-        if (this.settings.paused) {
-          this.settings.paused = false;
-        } else {
-          this.set_li(init, is_prev);
-        }
-
-        this.settings.attempts = 0;
-
-        if (this.settings.$li.length && this.settings.$target.length > 0) {
-          if (init) { //run when we first start
-            this.settings.pre_ride_callback(this.settings.$li.index(), this.settings.$next_tip);
-            if (this.settings.modal) {
-              this.show_modal();
-            }
-          }
-
-          this.settings.pre_step_callback(this.settings.$li.index(), this.settings.$next_tip);
-
-          if (this.settings.modal && this.settings.expose) {
-            this.expose();
-          }
-
-          this.settings.tip_settings = $.extend({}, this.settings, this.data_options(this.settings.$li));
-
-          this.settings.timer = parseInt(this.settings.timer, 10);
-
-          this.settings.tip_settings.tip_location_pattern = this.settings.tip_location_patterns[this.settings.tip_settings.tip_location];
-
-          // scroll and hide bg if not modal
-          if (!/body/i.test(this.settings.$target.selector)) {
-            var joyridemodalbg = $('.joyride-modal-bg');
-            if (/pop/i.test(this.settings.tipAnimation)) {
-                joyridemodalbg.hide();
-            } else {
-                joyridemodalbg.fadeOut(this.settings.tipAnimationFadeSpeed);
-            }
-            this.scroll_to();
-          }
-
-          if (this.is_phone()) {
-            this.pos_phone(true);
-          } else {
-            this.pos_default(true);
-          }
-
-          $timer = this.settings.$next_tip.find('.joyride-timer-indicator');
-
-          if (/pop/i.test(this.settings.tip_animation)) {
-
-            $timer.width(0);
-
-            if (this.settings.timer > 0) {
-
-              this.settings.$next_tip.show();
-
-              setTimeout(function () {
-                $timer.animate({
-                  width : $timer.parent().width()
-                }, this.settings.timer, 'linear');
-              }.bind(this), this.settings.tip_animation_fade_speed);
-
-            } else {
-              this.settings.$next_tip.show();
-
-            }
-
-          } else if (/fade/i.test(this.settings.tip_animation)) {
-
-            $timer.width(0);
-
-            if (this.settings.timer > 0) {
-
-              this.settings.$next_tip
-                .fadeIn(this.settings.tip_animation_fade_speed)
-                .show();
-
-              setTimeout(function () {
-                $timer.animate({
-                  width : $timer.parent().width()
-                }, this.settings.timer, 'linear');
-              }.bind(this), this.settings.tip_animation_fade_speed);
-
-            } else {
-              this.settings.$next_tip.fadeIn(this.settings.tip_animation_fade_speed);
-            }
-          }
-
-          this.settings.$current_tip = this.settings.$next_tip;
-
-        // skip non-existant targets
-        } else if (this.settings.$li && this.settings.$target.length < 1) {
-
-          this.show(init, is_prev);
-
-        } else {
-
-          this.end();
-
-        }
-      } else {
-
-        this.settings.paused = true;
-
-      }
-
-    },
-
-    is_phone : function () {
-      return matchMedia(Foundation.media_queries.small).matches &&
-        !matchMedia(Foundation.media_queries.medium).matches;
-    },
-
-    hide : function () {
-      if (this.settings.modal && this.settings.expose) {
-        this.un_expose();
-      }
-
-      if (!this.settings.modal) {
-        $('.joyride-modal-bg').hide();
-      }
-
-      // Prevent scroll bouncing...wait to remove from layout
-      this.settings.$current_tip.css('visibility', 'hidden');
-      setTimeout($.proxy(function () {
-        this.hide();
-        this.css('visibility', 'visible');
-      }, this.settings.$current_tip), 0);
-      this.settings.post_step_callback(this.settings.$li.index(),
-        this.settings.$current_tip);
-    },
-
-    set_li : function (init, is_prev) {
-      if (init) {
-        this.settings.$li = this.settings.$tip_content.eq(this.settings.start_offset);
-        this.set_next_tip();
-        this.settings.$current_tip = this.settings.$next_tip;
-      } else {
-        if (is_prev) {
-          this.settings.$li = this.settings.$li.prev();
-        } else {
-          this.settings.$li = this.settings.$li.next();
-        }
-        this.set_next_tip();
-      }
-
-      this.set_target();
-    },
-
-    set_next_tip : function () {
-      this.settings.$next_tip = $('.joyride-tip-guide').eq(this.settings.$li.index());
-      this.settings.$next_tip.data('closed', '');
-    },
-
-    set_target : function () {
-      var cl = this.settings.$li.attr(this.add_namespace('data-class')),
-          id = this.settings.$li.attr(this.add_namespace('data-id')),
-          $sel = function () {
-            if (id) {
-              return $(document.getElementById(id));
-            } else if (cl) {
-              return $('.' + cl).first();
-            } else {
-              return $('body');
-            }
-          };
-
-      this.settings.$target = $sel();
-    },
-
-    scroll_to : function () {
-      var window_half, tipOffset;
-
-      window_half = $(window).height() / 2;
-      tipOffset = Math.ceil(this.settings.$target.offset().top - window_half + this.settings.$next_tip.outerHeight());
-
-      if (tipOffset != 0) {
-        $('html, body').stop().animate({
-          scrollTop : tipOffset
-        }, this.settings.scroll_speed, 'swing');
-      }
-    },
-
-    paused : function () {
-      return ($.inArray((this.settings.$li.index() + 1), this.settings.pause_after) === -1);
-    },
-
-    restart : function () {
-      this.hide();
-      this.settings.$li = undefined;
-      this.show('init');
-    },
-
-    pos_default : function (init) {
-      var $nub = this.settings.$next_tip.find('.joyride-nub'),
-          nub_width = Math.ceil($nub.outerWidth() / 2),
-          nub_height = Math.ceil($nub.outerHeight() / 2),
-          toggle = init || false;
-
-      // tip must not be "display: none" to calculate position
-      if (toggle) {
-        this.settings.$next_tip.css('visibility', 'hidden');
-        this.settings.$next_tip.show();
-      }
-
-      if (!/body/i.test(this.settings.$target.selector)) {
-          var topAdjustment = this.settings.tip_settings.tipAdjustmentY ? parseInt(this.settings.tip_settings.tipAdjustmentY) : 0,
-              leftAdjustment = this.settings.tip_settings.tipAdjustmentX ? parseInt(this.settings.tip_settings.tipAdjustmentX) : 0;
-
-          if (this.bottom()) {
-            if (this.rtl) {
-              this.settings.$next_tip.css({
-                top : (this.settings.$target.offset().top + nub_height + this.settings.$target.outerHeight() + topAdjustment),
-                left : this.settings.$target.offset().left + this.settings.$target.outerWidth() - this.settings.$next_tip.outerWidth() + leftAdjustment});
-            } else {
-              this.settings.$next_tip.css({
-                top : (this.settings.$target.offset().top + nub_height + this.settings.$target.outerHeight() + topAdjustment),
-                left : this.settings.$target.offset().left + leftAdjustment});
-            }
-
-            this.nub_position($nub, this.settings.tip_settings.nub_position, 'top');
-
-          } else if (this.top()) {
-            if (this.rtl) {
-              this.settings.$next_tip.css({
-                top : (this.settings.$target.offset().top - this.settings.$next_tip.outerHeight() - nub_height + topAdjustment),
-                left : this.settings.$target.offset().left + this.settings.$target.outerWidth() - this.settings.$next_tip.outerWidth()});
-            } else {
-              this.settings.$next_tip.css({
-                top : (this.settings.$target.offset().top - this.settings.$next_tip.outerHeight() - nub_height + topAdjustment),
-                left : this.settings.$target.offset().left + leftAdjustment});
-            }
-
-            this.nub_position($nub, this.settings.tip_settings.nub_position, 'bottom');
-
-          } else if (this.right()) {
-
-            this.settings.$next_tip.css({
-              top : this.settings.$target.offset().top + topAdjustment,
-              left : (this.settings.$target.outerWidth() + this.settings.$target.offset().left + nub_width + leftAdjustment)});
-
-            this.nub_position($nub, this.settings.tip_settings.nub_position, 'left');
-
-          } else if (this.left()) {
-
-            this.settings.$next_tip.css({
-              top : this.settings.$target.offset().top + topAdjustment,
-              left : (this.settings.$target.offset().left - this.settings.$next_tip.outerWidth() - nub_width + leftAdjustment)});
-
-            this.nub_position($nub, this.settings.tip_settings.nub_position, 'right');
-
-          }
-
-          if (!this.visible(this.corners(this.settings.$next_tip)) && this.settings.attempts < this.settings.tip_settings.tip_location_pattern.length) {
-
-            $nub.removeClass('bottom')
-              .removeClass('top')
-              .removeClass('right')
-              .removeClass('left');
-
-            this.settings.tip_settings.tip_location = this.settings.tip_settings.tip_location_pattern[this.settings.attempts];
-
-            this.settings.attempts++;
-
-            this.pos_default();
-
-          }
-
-      } else if (this.settings.$li.length) {
-
-        this.pos_modal($nub);
-
-      }
-
-      if (toggle) {
-        this.settings.$next_tip.hide();
-        this.settings.$next_tip.css('visibility', 'visible');
-      }
-
-    },
-
-    pos_phone : function (init) {
-      var tip_height = this.settings.$next_tip.outerHeight(),
-          tip_offset = this.settings.$next_tip.offset(),
-          target_height = this.settings.$target.outerHeight(),
-          $nub = $('.joyride-nub', this.settings.$next_tip),
-          nub_height = Math.ceil($nub.outerHeight() / 2),
-          toggle = init || false;
-
-      $nub.removeClass('bottom')
-        .removeClass('top')
-        .removeClass('right')
-        .removeClass('left');
-
-      if (toggle) {
-        this.settings.$next_tip.css('visibility', 'hidden');
-        this.settings.$next_tip.show();
-      }
-
-      if (!/body/i.test(this.settings.$target.selector)) {
-
-        if (this.top()) {
-
-            this.settings.$next_tip.offset({top : this.settings.$target.offset().top - tip_height - nub_height});
-            $nub.addClass('bottom');
-
-        } else {
-
-          this.settings.$next_tip.offset({top : this.settings.$target.offset().top + target_height + nub_height});
-          $nub.addClass('top');
-
-        }
-
-      } else if (this.settings.$li.length) {
-        this.pos_modal($nub);
-      }
-
-      if (toggle) {
-        this.settings.$next_tip.hide();
-        this.settings.$next_tip.css('visibility', 'visible');
-      }
-    },
-
-    pos_modal : function ($nub) {
-      this.center();
-      $nub.hide();
-
-      this.show_modal();
-    },
-
-    show_modal : function () {
-      if (!this.settings.$next_tip.data('closed')) {
-        var joyridemodalbg =  $('.joyride-modal-bg');
-        if (joyridemodalbg.length < 1) {
-          var joyridemodalbg = $(this.settings.template.modal);
-          joyridemodalbg.appendTo('body');
-        }
-
-        if (/pop/i.test(this.settings.tip_animation)) {
-            joyridemodalbg.show();
-        } else {
-            joyridemodalbg.fadeIn(this.settings.tip_animation_fade_speed);
-        }
-      }
-    },
-
-    expose : function () {
-      var expose,
-          exposeCover,
-          el,
-          origCSS,
-          origClasses,
-          randId = 'expose-' + this.random_str(6);
-
-      if (arguments.length > 0 && arguments[0] instanceof $) {
-        el = arguments[0];
-      } else if (this.settings.$target && !/body/i.test(this.settings.$target.selector)) {
-        el = this.settings.$target;
-      } else {
-        return false;
-      }
-
-      if (el.length < 1) {
-        if (window.console) {
-          console.error('element not valid', el);
-        }
-        return false;
-      }
-
-      expose = $(this.settings.template.expose);
-      this.settings.$body.append(expose);
-      expose.css({
-        top : el.offset().top,
-        left : el.offset().left,
-        width : el.outerWidth(true),
-        height : el.outerHeight(true)
-      });
-
-      exposeCover = $(this.settings.template.expose_cover);
-
-      origCSS = {
-        zIndex : el.css('z-index'),
-        position : el.css('position')
-      };
-
-      origClasses = el.attr('class') == null ? '' : el.attr('class');
-
-      el.css('z-index', parseInt(expose.css('z-index')) + 1);
-
-      if (origCSS.position == 'static') {
-        el.css('position', 'relative');
-      }
-
-      el.data('expose-css', origCSS);
-      el.data('orig-class', origClasses);
-      el.attr('class', origClasses + ' ' + this.settings.expose_add_class);
-
-      exposeCover.css({
-        top : el.offset().top,
-        left : el.offset().left,
-        width : el.outerWidth(true),
-        height : el.outerHeight(true)
-      });
-
-      if (this.settings.modal) {
-        this.show_modal();
-      }
-
-      this.settings.$body.append(exposeCover);
-      expose.addClass(randId);
-      exposeCover.addClass(randId);
-      el.data('expose', randId);
-      this.settings.post_expose_callback(this.settings.$li.index(), this.settings.$next_tip, el);
-      this.add_exposed(el);
-    },
-
-    un_expose : function () {
-      var exposeId,
-          el,
-          expose,
-          origCSS,
-          origClasses,
-          clearAll = false;
-
-      if (arguments.length > 0 && arguments[0] instanceof $) {
-        el = arguments[0];
-      } else if (this.settings.$target && !/body/i.test(this.settings.$target.selector)) {
-        el = this.settings.$target;
-      } else {
-        return false;
-      }
-
-      if (el.length < 1) {
-        if (window.console) {
-          console.error('element not valid', el);
-        }
-        return false;
-      }
-
-      exposeId = el.data('expose');
-      expose = $('.' + exposeId);
-
-      if (arguments.length > 1) {
-        clearAll = arguments[1];
-      }
-
-      if (clearAll === true) {
-        $('.joyride-expose-wrapper,.joyride-expose-cover').remove();
-      } else {
-        expose.remove();
-      }
-
-      origCSS = el.data('expose-css');
-
-      if (origCSS.zIndex == 'auto') {
-        el.css('z-index', '');
-      } else {
-        el.css('z-index', origCSS.zIndex);
-      }
-
-      if (origCSS.position != el.css('position')) {
-        if (origCSS.position == 'static') {// this is default, no need to set it.
-          el.css('position', '');
-        } else {
-          el.css('position', origCSS.position);
-        }
-      }
-
-      origClasses = el.data('orig-class');
-      el.attr('class', origClasses);
-      el.removeData('orig-classes');
-
-      el.removeData('expose');
-      el.removeData('expose-z-index');
-      this.remove_exposed(el);
-    },
-
-    add_exposed : function (el) {
-      this.settings.exposed = this.settings.exposed || [];
-      if (el instanceof $ || typeof el === 'object') {
-        this.settings.exposed.push(el[0]);
-      } else if (typeof el == 'string') {
-        this.settings.exposed.push(el);
-      }
-    },
-
-    remove_exposed : function (el) {
-      var search, i;
-      if (el instanceof $) {
-        search = el[0]
-      } else if (typeof el == 'string') {
-        search = el;
-      }
-
-      this.settings.exposed = this.settings.exposed || [];
-      i = this.settings.exposed.length;
-
-      while (i--) {
-        if (this.settings.exposed[i] == search) {
-          this.settings.exposed.splice(i, 1);
-          return;
-        }
-      }
-    },
-
-    center : function () {
-      var $w = $(window);
-
-      this.settings.$next_tip.css({
-        top : ((($w.height() - this.settings.$next_tip.outerHeight()) / 2) + $w.scrollTop()),
-        left : ((($w.width() - this.settings.$next_tip.outerWidth()) / 2) + $w.scrollLeft())
-      });
-
-      return true;
-    },
-
-    bottom : function () {
-      return /bottom/i.test(this.settings.tip_settings.tip_location);
-    },
-
-    top : function () {
-      return /top/i.test(this.settings.tip_settings.tip_location);
-    },
-
-    right : function () {
-      return /right/i.test(this.settings.tip_settings.tip_location);
-    },
-
-    left : function () {
-      return /left/i.test(this.settings.tip_settings.tip_location);
-    },
-
-    corners : function (el) {
-      var w = $(window),
-          window_half = w.height() / 2,
-          //using this to calculate since scroll may not have finished yet.
-          tipOffset = Math.ceil(this.settings.$target.offset().top - window_half + this.settings.$next_tip.outerHeight()),
-          right = w.width() + w.scrollLeft(),
-          offsetBottom =  w.height() + tipOffset,
-          bottom = w.height() + w.scrollTop(),
-          top = w.scrollTop();
-
-      if (tipOffset < top) {
-        if (tipOffset < 0) {
-          top = 0;
-        } else {
-          top = tipOffset;
-        }
-      }
-
-      if (offsetBottom > bottom) {
-        bottom = offsetBottom;
-      }
-
-      return [
-        el.offset().top < top,
-        right < el.offset().left + el.outerWidth(),
-        bottom < el.offset().top + el.outerHeight(),
-        w.scrollLeft() > el.offset().left
-      ];
-    },
-
-    visible : function (hidden_corners) {
-      var i = hidden_corners.length;
-
-      while (i--) {
-        if (hidden_corners[i]) {
-          return false;
-        }
-      }
-
-      return true;
-    },
-
-    nub_position : function (nub, pos, def) {
-      if (pos === 'auto') {
-        nub.addClass(def);
-      } else {
-        nub.addClass(pos);
-      }
-    },
-
-    startTimer : function () {
-      if (this.settings.$li.length) {
-        this.settings.automate = setTimeout(function () {
-          this.hide();
-          this.show();
-          this.startTimer();
-        }.bind(this), this.settings.timer);
-      } else {
-        clearTimeout(this.settings.automate);
-      }
-    },
-
-    end : function (abort) {
-      if (this.settings.cookie_monster) {
-        $.cookie(this.settings.cookie_name, 'ridden', {expires : this.settings.cookie_expires, domain : this.settings.cookie_domain});
-      }
-
-      if (this.settings.timer > 0) {
-        clearTimeout(this.settings.automate);
-      }
-
-      if (this.settings.modal && this.settings.expose) {
-        this.un_expose();
-      }
-
-      // Unplug keystrokes listener
-      $(this.scope).off('keyup.joyride')
-
-      this.settings.$next_tip.data('closed', true);
-      this.settings.riding = false;
-
-      $('.joyride-modal-bg').hide();
-      this.settings.$current_tip.hide();
-
-      if (typeof abort === 'undefined' || abort === false) {
-        this.settings.post_step_callback(this.settings.$li.index(), this.settings.$current_tip);
-        this.settings.post_ride_callback(this.settings.$li.index(), this.settings.$current_tip);
-      }
-
-      $('.joyride-tip-guide').remove();
-    },
-
-    off : function () {
-      $(this.scope).off('.joyride');
-      $(window).off('.joyride');
-      $('.joyride-close-tip, .joyride-next-tip, .joyride-modal-bg').off('.joyride');
-      $('.joyride-tip-guide, .joyride-modal-bg').remove();
-      clearTimeout(this.settings.automate);
-      this.settings = {};
-    },
-
-    reflow : function () {}
-  };
-}(jQuery, window, window.document));
-;(function ($, window, document, undefined) {
-  'use strict';
-
-  Foundation.libs.equalizer = {
-    name : 'equalizer',
-
-    version : '5.5.1',
-
-    settings : {
-      use_tallest : true,
-      before_height_change : $.noop,
-      after_height_change : $.noop,
-      equalize_on_stack : false
-    },
-
-    init : function (scope, method, options) {
-      Foundation.inherit(this, 'image_loaded');
-      this.bindings(method, options);
-      this.reflow();
-    },
-
-    events : function () {
-      this.S(window).off('.equalizer').on('resize.fndtn.equalizer', function (e) {
-        this.reflow();
-      }.bind(this));
-    },
-
-    equalize : function (equalizer) {
-      var isStacked = false,
-          vals = equalizer.find('[' + this.attr_name() + '-watch]:visible'),
-          settings = equalizer.data(this.attr_name(true) + '-init');
-
-      if (vals.length === 0) {
-        return;
-      }
-      var firstTopOffset = vals.first().offset().top;
-      settings.before_height_change();
-      equalizer.trigger('before-height-change').trigger('before-height-change.fndth.equalizer');
-      vals.height('inherit');
-      vals.each(function () {
-        var el = $(this);
-        if (el.offset().top !== firstTopOffset) {
-          isStacked = true;
-        }
-      });
-
-      if (settings.equalize_on_stack === false) {
-        if (isStacked) {
-          return;
-        }
-      };
-
-      var heights = vals.map(function () { return $(this).outerHeight(false) }).get();
-
-      if (settings.use_tallest) {
-        var max = Math.max.apply(null, heights);
-        vals.css('height', max);
-      } else {
-        var min = Math.min.apply(null, heights);
-        vals.css('height', min);
-      }
-      settings.after_height_change();
-      equalizer.trigger('after-height-change').trigger('after-height-change.fndtn.equalizer');
-    },
-
-    reflow : function () {
-      var self = this;
-
-      this.S('[' + this.attr_name() + ']', this.scope).each(function () {
-        var $eq_target = $(this);
-        self.image_loaded(self.S('img', this), function () {
-          self.equalize($eq_target)
-        });
-      });
-    }
-  };
-})(jQuery, window, window.document);
-;(function ($, window, document, undefined) {
-  'use strict';
-
-  Foundation.libs.dropdown = {
-    name : 'dropdown',
-
-    version : '5.5.1',
-
-    settings : {
-      active_class : 'open',
-      disabled_class : 'disabled',
-      mega_class : 'mega',
-      align : 'bottom',
-      is_hover : false,
-      hover_timeout : 150,
-      opened : function () {},
-      closed : function () {}
-    },
-
-    init : function (scope, method, options) {
-      Foundation.inherit(this, 'throttle');
-
-      $.extend(true, this.settings, method, options);
-      this.bindings(method, options);
-    },
-
-    events : function (scope) {
-      var self = this,
-          S = self.S;
-
-      S(this.scope)
-        .off('.dropdown')
-        .on('click.fndtn.dropdown', '[' + this.attr_name() + ']', function (e) {
-          var settings = S(this).data(self.attr_name(true) + '-init') || self.settings;
-          if (!settings.is_hover || Modernizr.touch) {
-            e.preventDefault();
-            if (S(this).parent('[data-reveal-id]')) {
-              e.stopPropagation();
-            }
-            self.toggle($(this));
-          }
-        })
-        .on('mouseenter.fndtn.dropdown', '[' + this.attr_name() + '], [' + this.attr_name() + '-content]', function (e) {
-          var $this = S(this),
-              dropdown,
-              target;
-
-          clearTimeout(self.timeout);
-
-          if ($this.data(self.data_attr())) {
-            dropdown = S('#' + $this.data(self.data_attr()));
-            target = $this;
-          } else {
-            dropdown = $this;
-            target = S('[' + self.attr_name() + '="' + dropdown.attr('id') + '"]');
-          }
-
-          var settings = target.data(self.attr_name(true) + '-init') || self.settings;
-
-          if (S(e.currentTarget).data(self.data_attr()) && settings.is_hover) {
-            self.closeall.call(self);
-          }
-
-          if (settings.is_hover) {
-            self.open.apply(self, [dropdown, target]);
-          }
-        })
-        .on('mouseleave.fndtn.dropdown', '[' + this.attr_name() + '], [' + this.attr_name() + '-content]', function (e) {
-          var $this = S(this);
-          var settings;
-
-          if ($this.data(self.data_attr())) {
-              settings = $this.data(self.data_attr(true) + '-init') || self.settings;
-          } else {
-              var target   = S('[' + self.attr_name() + '="' + S(this).attr('id') + '"]'),
-                  settings = target.data(self.attr_name(true) + '-init') || self.settings;
-          }
-
-          self.timeout = setTimeout(function () {
-            if ($this.data(self.data_attr())) {
-              if (settings.is_hover) {
-                self.close.call(self, S('#' + $this.data(self.data_attr())));
-              }
-            } else {
-              if (settings.is_hover) {
-                self.close.call(self, $this);
-              }
-            }
-          }.bind(this), settings.hover_timeout);
-        })
-        .on('click.fndtn.dropdown', function (e) {
-          var parent = S(e.target).closest('[' + self.attr_name() + '-content]');
-          var links  = parent.find('a');
-
-          if (links.length > 0 && parent.attr('aria-autoclose') !== 'false') {
-              self.close.call(self, S('[' + self.attr_name() + '-content]'));
-          }
-
-          if (e.target !== document && !$.contains(document.documentElement, e.target)) {
-            return;
-          }
-
-          if (S(e.target).closest('[' + self.attr_name() + ']').length > 0) {
-            return;
-          }
-
-          if (!(S(e.target).data('revealId')) &&
-            (parent.length > 0 && (S(e.target).is('[' + self.attr_name() + '-content]') ||
-              $.contains(parent.first()[0], e.target)))) {
-            e.stopPropagation();
-            return;
-          }
-
-          self.close.call(self, S('[' + self.attr_name() + '-content]'));
-        })
-        .on('opened.fndtn.dropdown', '[' + self.attr_name() + '-content]', function () {
-          self.settings.opened.call(this);
-        })
-        .on('closed.fndtn.dropdown', '[' + self.attr_name() + '-content]', function () {
-          self.settings.closed.call(this);
-        });
-
-      S(window)
-        .off('.dropdown')
-        .on('resize.fndtn.dropdown', self.throttle(function () {
-          self.resize.call(self);
-        }, 50));
-
-      this.resize();
-    },
-
-    close : function (dropdown) {
-      var self = this;
-      dropdown.each(function () {
-        var original_target = $('[' + self.attr_name() + '=' + dropdown[0].id + ']') || $('aria-controls=' + dropdown[0].id + ']');
-        original_target.attr('aria-expanded', 'false');
-        if (self.S(this).hasClass(self.settings.active_class)) {
-          self.S(this)
-            .css(Foundation.rtl ? 'right' : 'left', '-99999px')
-            .attr('aria-hidden', 'true')
-            .removeClass(self.settings.active_class)
-            .prev('[' + self.attr_name() + ']')
-            .removeClass(self.settings.active_class)
-            .removeData('target');
-
-          self.S(this).trigger('closed').trigger('closed.fndtn.dropdown', [dropdown]);
-        }
-      });
-      dropdown.removeClass('f-open-' + this.attr_name(true));
-    },
-
-    closeall : function () {
-      var self = this;
-      $.each(self.S('.f-open-' + this.attr_name(true)), function () {
-        self.close.call(self, self.S(this));
-      });
-    },
-
-    open : function (dropdown, target) {
-      this
-        .css(dropdown
-        .addClass(this.settings.active_class), target);
-      dropdown.prev('[' + this.attr_name() + ']').addClass(this.settings.active_class);
-      dropdown.data('target', target.get(0)).trigger('opened').trigger('opened.fndtn.dropdown', [dropdown, target]);
-      dropdown.attr('aria-hidden', 'false');
-      target.attr('aria-expanded', 'true');
-      dropdown.focus();
-      dropdown.addClass('f-open-' + this.attr_name(true));
-    },
-
-    data_attr : function () {
-      if (this.namespace.length > 0) {
-        return this.namespace + '-' + this.name;
-      }
-
-      return this.name;
-    },
-
-    toggle : function (target) {
-      if (target.hasClass(this.settings.disabled_class)) {
-        return;
-      }
-      var dropdown = this.S('#' + target.data(this.data_attr()));
-      if (dropdown.length === 0) {
-        // No dropdown found, not continuing
-        return;
-      }
-
-      this.close.call(this, this.S('[' + this.attr_name() + '-content]').not(dropdown));
-
-      if (dropdown.hasClass(this.settings.active_class)) {
-        this.close.call(this, dropdown);
-        if (dropdown.data('target') !== target.get(0)) {
-          this.open.call(this, dropdown, target);
-        }
-      } else {
-        this.open.call(this, dropdown, target);
-      }
-    },
-
-    resize : function () {
-      var dropdown = this.S('[' + this.attr_name() + '-content].open');
-      var target = $(dropdown.data("target"));
-
-      if (dropdown.length && target.length) {
-        this.css(dropdown, target);
-      }
-    },
-
-    css : function (dropdown, target) {
-      var left_offset = Math.max((target.width() - dropdown.width()) / 2, 8),
-          settings = target.data(this.attr_name(true) + '-init') || this.settings;
-
-      this.clear_idx();
-
-      if (this.small()) {
-        var p = this.dirs.bottom.call(dropdown, target, settings);
-
-        dropdown.attr('style', '').removeClass('drop-left drop-right drop-top').css({
-          position : 'absolute',
-          width : '95%',
-          'max-width' : 'none',
-          top : p.top
-        });
-
-        dropdown.css(Foundation.rtl ? 'right' : 'left', left_offset);
-      } else {
-
-        this.style(dropdown, target, settings);
-      }
-
-      return dropdown;
-    },
-
-    style : function (dropdown, target, settings) {
-      var css = $.extend({position : 'absolute'},
-        this.dirs[settings.align].call(dropdown, target, settings));
-
-      dropdown.attr('style', '').css(css);
-    },
-
-    // return CSS property object
-    // `this` is the dropdown
-    dirs : {
-      // Calculate target offset
-      _base : function (t) {
-        var o_p = this.offsetParent(),
-            o = o_p.offset(),
-            p = t.offset();
-
-        p.top -= o.top;
-        p.left -= o.left;
-
-        //set some flags on the p object to pass along
-        p.missRight = false;
-        p.missTop = false;
-        p.missLeft = false;
-        p.leftRightFlag = false;
-
-        //lets see if the panel will be off the screen
-        //get the actual width of the page and store it
-        var actualBodyWidth;
-        if (document.getElementsByClassName('row')[0]) {
-          actualBodyWidth = document.getElementsByClassName('row')[0].clientWidth;
-        } else {
-          actualBodyWidth = window.outerWidth;
-        }
-
-        var actualMarginWidth = (window.outerWidth - actualBodyWidth) / 2;
-        var actualBoundary = actualBodyWidth;
-
-        if (!this.hasClass('mega')) {
-          //miss top
-          if (t.offset().top <= this.outerHeight()) {
-            p.missTop = true;
-            actualBoundary = window.outerWidth - actualMarginWidth;
-            p.leftRightFlag = true;
-          }
-
-          //miss right
-          if (t.offset().left + this.outerWidth() > t.offset().left + actualMarginWidth && t.offset().left - actualMarginWidth > this.outerWidth()) {
-            p.missRight = true;
-            p.missLeft = false;
-          }
-
-          //miss left
-          if (t.offset().left - this.outerWidth() <= 0) {
-            p.missLeft = true;
-            p.missRight = false;
-          }
-        }
-
-        return p;
-      },
-
-      top : function (t, s) {
-        var self = Foundation.libs.dropdown,
-            p = self.dirs._base.call(this, t);
-
-        this.addClass('drop-top');
-
-        if (p.missTop == true) {
-          p.top = p.top + t.outerHeight() + this.outerHeight();
-          this.removeClass('drop-top');
-        }
-
-        if (p.missRight == true) {
-          p.left = p.left - this.outerWidth() + t.outerWidth();
-        }
-
-        if (t.outerWidth() < this.outerWidth() || self.small() || this.hasClass(s.mega_menu)) {
-          self.adjust_pip(this, t, s, p);
-        }
-
-        if (Foundation.rtl) {
-          return {left : p.left - this.outerWidth() + t.outerWidth(),
-            top : p.top - this.outerHeight()};
-        }
-
-        return {left : p.left, top : p.top - this.outerHeight()};
-      },
-
-      bottom : function (t, s) {
-        var self = Foundation.libs.dropdown,
-            p = self.dirs._base.call(this, t);
-
-        if (p.missRight == true) {
-          p.left = p.left - this.outerWidth() + t.outerWidth();
-        }
-
-        if (t.outerWidth() < this.outerWidth() || self.small() || this.hasClass(s.mega_menu)) {
-          self.adjust_pip(this, t, s, p);
-        }
-
-        if (self.rtl) {
-          return {left : p.left - this.outerWidth() + t.outerWidth(), top : p.top + t.outerHeight()};
-        }
-
-        return {left : p.left, top : p.top + t.outerHeight()};
-      },
-
-      left : function (t, s) {
-        var p = Foundation.libs.dropdown.dirs._base.call(this, t);
-
-        this.addClass('drop-left');
-
-        if (p.missLeft == true) {
-          p.left =  p.left + this.outerWidth();
-          p.top = p.top + t.outerHeight();
-          this.removeClass('drop-left');
-        }
-
-        return {left : p.left - this.outerWidth(), top : p.top};
-      },
-
-      right : function (t, s) {
-        var p = Foundation.libs.dropdown.dirs._base.call(this, t);
-
-        this.addClass('drop-right');
-
-        if (p.missRight == true) {
-          p.left = p.left - this.outerWidth();
-          p.top = p.top + t.outerHeight();
-          this.removeClass('drop-right');
-        } else {
-          p.triggeredRight = true;
-        }
-
-        var self = Foundation.libs.dropdown;
-
-        if (t.outerWidth() < this.outerWidth() || self.small() || this.hasClass(s.mega_menu)) {
-          self.adjust_pip(this, t, s, p);
-        }
-
-        return {left : p.left + t.outerWidth(), top : p.top};
-      }
-    },
-
-    // Insert rule to style psuedo elements
-    adjust_pip : function (dropdown, target, settings, position) {
-      var sheet = Foundation.stylesheet,
-          pip_offset_base = 8;
-
-      if (dropdown.hasClass(settings.mega_class)) {
-        pip_offset_base = position.left + (target.outerWidth() / 2) - 8;
-      } else if (this.small()) {
-        pip_offset_base += position.left - 8;
-      }
-
-      this.rule_idx = sheet.cssRules.length;
-
-      //default
-      var sel_before = '.f-dropdown.open:before',
-          sel_after  = '.f-dropdown.open:after',
-          css_before = 'left: ' + pip_offset_base + 'px;',
-          css_after  = 'left: ' + (pip_offset_base - 1) + 'px;';
-
-      if (position.missRight == true) {
-        pip_offset_base = dropdown.outerWidth() - 23;
-        sel_before = '.f-dropdown.open:before',
-        sel_after  = '.f-dropdown.open:after',
-        css_before = 'left: ' + pip_offset_base + 'px;',
-        css_after  = 'left: ' + (pip_offset_base - 1) + 'px;';
-      }
-
-      //just a case where right is fired, but its not missing right
-      if (position.triggeredRight == true) {
-        sel_before = '.f-dropdown.open:before',
-        sel_after  = '.f-dropdown.open:after',
-        css_before = 'left:-12px;',
-        css_after  = 'left:-14px;';
-      }
-
-      if (sheet.insertRule) {
-        sheet.insertRule([sel_before, '{', css_before, '}'].join(' '), this.rule_idx);
-        sheet.insertRule([sel_after, '{', css_after, '}'].join(' '), this.rule_idx + 1);
-      } else {
-        sheet.addRule(sel_before, css_before, this.rule_idx);
-        sheet.addRule(sel_after, css_after, this.rule_idx + 1);
-      }
-    },
-
-    // Remove old dropdown rule index
-    clear_idx : function () {
-      var sheet = Foundation.stylesheet;
-
-      if (typeof this.rule_idx !== 'undefined') {
-        sheet.deleteRule(this.rule_idx);
-        sheet.deleteRule(this.rule_idx);
-        delete this.rule_idx;
-      }
-    },
-
-    small : function () {
-      return matchMedia(Foundation.media_queries.small).matches &&
-        !matchMedia(Foundation.media_queries.medium).matches;
-    },
-
-    off : function () {
-      this.S(this.scope).off('.fndtn.dropdown');
-      this.S('html, body').off('.fndtn.dropdown');
-      this.S(window).off('.fndtn.dropdown');
-      this.S('[data-dropdown-content]').off('.fndtn.dropdown');
-    },
-
-    reflow : function () {}
-  };
-}(jQuery, window, window.document));
-;(function ($, window, document, undefined) {
-  'use strict';
-
-  Foundation.libs.clearing = {
-    name : 'clearing',
-
-    version : '5.5.1',
-
-    settings : {
-      templates : {
-        viewing : '<a href="#" class="clearing-close">×</a>' +
-          '<div class="visible-img" style="display: none"><div class="clearing-touch-label"></div><img src="data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs%3D" alt="" />' +
-          '<p class="clearing-caption"></p><a href="#" class="clearing-main-prev"><span></span></a>' +
-          '<a href="#" class="clearing-main-next"><span></span></a></div>'
-      },
-
-      // comma delimited list of selectors that, on click, will close clearing,
-      // add 'div.clearing-blackout, div.visible-img' to close on background click
-      close_selectors : '.clearing-close, div.clearing-blackout',
-
-      // Default to the entire li element.
-      open_selectors : '',
-
-      // Image will be skipped in carousel.
-      skip_selector : '',
-
-      touch_label : '',
-
-      // event initializers and locks
-      init : false,
-      locked : false
-    },
-
-    init : function (scope, method, options) {
-      var self = this;
-      Foundation.inherit(this, 'throttle image_loaded');
-
-      this.bindings(method, options);
-
-      if (self.S(this.scope).is('[' + this.attr_name() + ']')) {
-        this.assemble(self.S('li', this.scope));
-      } else {
-        self.S('[' + this.attr_name() + ']', this.scope).each(function () {
-          self.assemble(self.S('li', this));
-        });
-      }
-    },
-
-    events : function (scope) {
-      var self = this,
-          S = self.S,
-          $scroll_container = $('.scroll-container');
-
-      if ($scroll_container.length > 0) {
-        this.scope = $scroll_container;
-      }
-
-      S(this.scope)
-        .off('.clearing')
-        .on('click.fndtn.clearing', 'ul[' + this.attr_name() + '] li ' + this.settings.open_selectors,
-          function (e, current, target) {
-            var current = current || S(this),
-                target = target || current,
-                next = current.next('li'),
-                settings = current.closest('[' + self.attr_name() + ']').data(self.attr_name(true) + '-init'),
-                image = S(e.target);
-
-            e.preventDefault();
-
-            if (!settings) {
-              self.init();
-              settings = current.closest('[' + self.attr_name() + ']').data(self.attr_name(true) + '-init');
-            }
-
-            // if clearing is open and the current image is
-            // clicked, go to the next image in sequence
-            if (target.hasClass('visible') &&
-              current[0] === target[0] &&
-              next.length > 0 && self.is_open(current)) {
-              target = next;
-              image = S('img', target);
-            }
-
-            // set current and target to the clicked li if not otherwise defined.
-            self.open(image, current, target);
-            self.update_paddles(target);
-          })
-
-        .on('click.fndtn.clearing', '.clearing-main-next',
-          function (e) { self.nav(e, 'next') })
-        .on('click.fndtn.clearing', '.clearing-main-prev',
-          function (e) { self.nav(e, 'prev') })
-        .on('click.fndtn.clearing', this.settings.close_selectors,
-          function (e) { Foundation.libs.clearing.close(e, this) });
-
-      $(document).on('keydown.fndtn.clearing',
-          function (e) { self.keydown(e) });
-
-      S(window).off('.clearing').on('resize.fndtn.clearing',
-        function () { self.resize() });
-
-      this.swipe_events(scope);
-    },
-
-    swipe_events : function (scope) {
-      var self = this,
-      S = self.S;
-
-      S(this.scope)
-        .on('touchstart.fndtn.clearing', '.visible-img', function (e) {
-          if (!e.touches) { e = e.originalEvent; }
-          var data = {
-                start_page_x : e.touches[0].pageX,
-                start_page_y : e.touches[0].pageY,
-                start_time : (new Date()).getTime(),
-                delta_x : 0,
-                is_scrolling : undefined
-              };
-
-          S(this).data('swipe-transition', data);
-          e.stopPropagation();
-        })
-        .on('touchmove.fndtn.clearing', '.visible-img', function (e) {
-          if (!e.touches) {
-            e = e.originalEvent;
-          }
-          // Ignore pinch/zoom events
-          if (e.touches.length > 1 || e.scale && e.scale !== 1) {
-            return;
-          }
-
-          var data = S(this).data('swipe-transition');
-
-          if (typeof data === 'undefined') {
-            data = {};
-          }
-
-          data.delta_x = e.touches[0].pageX - data.start_page_x;
-
-          if (Foundation.rtl) {
-            data.delta_x = -data.delta_x;
-          }
-
-          if (typeof data.is_scrolling === 'undefined') {
-            data.is_scrolling = !!( data.is_scrolling || Math.abs(data.delta_x) < Math.abs(e.touches[0].pageY - data.start_page_y) );
-          }
-
-          if (!data.is_scrolling && !data.active) {
-            e.preventDefault();
-            var direction = (data.delta_x < 0) ? 'next' : 'prev';
-            data.active = true;
-            self.nav(e, direction);
-          }
-        })
-        .on('touchend.fndtn.clearing', '.visible-img', function (e) {
-          S(this).data('swipe-transition', {});
-          e.stopPropagation();
-        });
-    },
-
-    assemble : function ($li) {
-      var $el = $li.parent();
-
-      if ($el.parent().hasClass('carousel')) {
-        return;
-      }
-
-      $el.after('<div id="foundationClearingHolder"></div>');
-
-      var grid = $el.detach(),
-          grid_outerHTML = '';
-
-      if (grid[0] == null) {
-        return;
-      } else {
-        grid_outerHTML = grid[0].outerHTML;
-      }
-
-      var holder = this.S('#foundationClearingHolder'),
-          settings = $el.data(this.attr_name(true) + '-init'),
-          data = {
-            grid : '<div class="carousel">' + grid_outerHTML + '</div>',
-            viewing : settings.templates.viewing
-          },
-          wrapper = '<div class="clearing-assembled"><div>' + data.viewing +
-            data.grid + '</div></div>',
-          touch_label = this.settings.touch_label;
-
-      if (Modernizr.touch) {
-        wrapper = $(wrapper).find('.clearing-touch-label').html(touch_label).end();
-      }
-
-      holder.after(wrapper).remove();
-    },
-
-    open : function ($image, current, target) {
-      var self = this,
-          body = $(document.body),
-          root = target.closest('.clearing-assembled'),
-          container = self.S('div', root).first(),
-          visible_image = self.S('.visible-img', container),
-          image = self.S('img', visible_image).not($image),
-          label = self.S('.clearing-touch-label', container),
-          error = false;
-
-      // Event to disable scrolling on touch devices when Clearing is activated
-      $('body').on('touchmove', function (e) {
-        e.preventDefault();
-      });
-
-      image.error(function () {
-        error = true;
-      });
-
-      function startLoad() {
-        setTimeout(function () {
-          this.image_loaded(image, function () {
-            if (image.outerWidth() === 1 && !error) {
-              startLoad.call(this);
-            } else {
-              cb.call(this, image);
-            }
-          }.bind(this));
-        }.bind(this), 100);
-      }
-
-      function cb (image) {
-        var $image = $(image);
-        $image.css('visibility', 'visible');
-        // toggle the gallery
-        body.css('overflow', 'hidden');
-        root.addClass('clearing-blackout');
-        container.addClass('clearing-container');
-        visible_image.show();
-        this.fix_height(target)
-          .caption(self.S('.clearing-caption', visible_image), self.S('img', target))
-          .center_and_label(image, label)
-          .shift(current, target, function () {
-            target.closest('li').siblings().removeClass('visible');
-            target.closest('li').addClass('visible');
-          });
-        visible_image.trigger('opened.fndtn.clearing')
-      }
-
-      if (!this.locked()) {
-        visible_image.trigger('open.fndtn.clearing');
-        // set the image to the selected thumbnail
-        image
-          .attr('src', this.load($image))
-          .css('visibility', 'hidden');
-
-        startLoad.call(this);
-      }
-    },
-
-    close : function (e, el) {
-      e.preventDefault();
-
-      var root = (function (target) {
-            if (/blackout/.test(target.selector)) {
-              return target;
-            } else {
-              return target.closest('.clearing-blackout');
-            }
-          }($(el))),
-          body = $(document.body), container, visible_image;
-
-      if (el === e.target && root) {
-        body.css('overflow', '');
-        container = $('div', root).first();
-        visible_image = $('.visible-img', container);
-        visible_image.trigger('close.fndtn.clearing');
-        this.settings.prev_index = 0;
-        $('ul[' + this.attr_name() + ']', root)
-          .attr('style', '').closest('.clearing-blackout')
-          .removeClass('clearing-blackout');
-        container.removeClass('clearing-container');
-        visible_image.hide();
-        visible_image.trigger('closed.fndtn.clearing');
-      }
-
-      // Event to re-enable scrolling on touch devices
-      $('body').off('touchmove');
-
-      return false;
-    },
-
-    is_open : function (current) {
-      return current.parent().prop('style').length > 0;
-    },
-
-    keydown : function (e) {
-      var clearing = $('.clearing-blackout ul[' + this.attr_name() + ']'),
-          NEXT_KEY = this.rtl ? 37 : 39,
-          PREV_KEY = this.rtl ? 39 : 37,
-          ESC_KEY = 27;
-
-      if (e.which === NEXT_KEY) {
-        this.go(clearing, 'next');
-      }
-      if (e.which === PREV_KEY) {
-        this.go(clearing, 'prev');
-      }
-      if (e.which === ESC_KEY) {
-        this.S('a.clearing-close').trigger('click').trigger('click.fndtn.clearing');
-      }
-    },
-
-    nav : function (e, direction) {
-      var clearing = $('ul[' + this.attr_name() + ']', '.clearing-blackout');
-
-      e.preventDefault();
-      this.go(clearing, direction);
-    },
-
-    resize : function () {
-      var image = $('img', '.clearing-blackout .visible-img'),
-          label = $('.clearing-touch-label', '.clearing-blackout');
-
-      if (image.length) {
-        this.center_and_label(image, label);
-        image.trigger('resized.fndtn.clearing')
-      }
-    },
-
-    // visual adjustments
-    fix_height : function (target) {
-      var lis = target.parent().children(),
-          self = this;
-
-      lis.each(function () {
-        var li = self.S(this),
-            image = li.find('img');
-
-        if (li.height() > image.outerHeight()) {
-          li.addClass('fix-height');
-        }
-      })
-      .closest('ul')
-      .width(lis.length * 100 + '%');
-
-      return this;
-    },
-
-    update_paddles : function (target) {
-      target = target.closest('li');
-      var visible_image = target
-        .closest('.carousel')
-        .siblings('.visible-img');
-
-      if (target.next().length > 0) {
-        this.S('.clearing-main-next', visible_image).removeClass('disabled');
-      } else {
-        this.S('.clearing-main-next', visible_image).addClass('disabled');
-      }
-
-      if (target.prev().length > 0) {
-        this.S('.clearing-main-prev', visible_image).removeClass('disabled');
-      } else {
-        this.S('.clearing-main-prev', visible_image).addClass('disabled');
-      }
-    },
-
-    center_and_label : function (target, label) {
-      if (!this.rtl && label.length > 0) {
-        label.css({
-          marginLeft : -(label.outerWidth() / 2),
-          marginTop : -(target.outerHeight() / 2)-label.outerHeight()-10
-        });
-      } else {
-        label.css({
-          marginRight : -(label.outerWidth() / 2),
-          marginTop : -(target.outerHeight() / 2)-label.outerHeight()-10,
-          left: 'auto',
-          right: '50%'
-        });
-      }
-      return this;
-    },
-
-    // image loading and preloading
-
-    load : function ($image) {
-      var href;
-
-      if ($image[0].nodeName === 'A') {
-        href = $image.attr('href');
-      } else {
-        href = $image.closest('a').attr('href');
-      }
-
-      this.preload($image);
-
-      if (href) {
-        return href;
-      }
-      return $image.attr('src');
-    },
-
-    preload : function ($image) {
-      this
-        .img($image.closest('li').next())
-        .img($image.closest('li').prev());
-    },
-
-    img : function (img) {
-      if (img.length) {
-        var new_img = new Image(),
-            new_a = this.S('a', img);
-
-        if (new_a.length) {
-          new_img.src = new_a.attr('href');
-        } else {
-          new_img.src = this.S('img', img).attr('src');
-        }
-      }
-      return this;
-    },
-
-    // image caption
-
-    caption : function (container, $image) {
-      var caption = $image.attr('data-caption');
-
-      if (caption) {
-        container
-          .html(caption)
-          .show();
-      } else {
-        container
-          .text('')
-          .hide();
-      }
-      return this;
-    },
-
-    // directional methods
-
-    go : function ($ul, direction) {
-      var current = this.S('.visible', $ul),
-          target = current[direction]();
-
-      // Check for skip selector.
-      if (this.settings.skip_selector && target.find(this.settings.skip_selector).length != 0) {
-        target = target[direction]();
-      }
-
-      if (target.length) {
-        this.S('img', target)
-          .trigger('click', [current, target]).trigger('click.fndtn.clearing', [current, target])
-          .trigger('change.fndtn.clearing');
-      }
-    },
-
-    shift : function (current, target, callback) {
-      var clearing = target.parent(),
-          old_index = this.settings.prev_index || target.index(),
-          direction = this.direction(clearing, current, target),
-          dir = this.rtl ? 'right' : 'left',
-          left = parseInt(clearing.css('left'), 10),
-          width = target.outerWidth(),
-          skip_shift;
-
-      var dir_obj = {};
-
-      // we use jQuery animate instead of CSS transitions because we
-      // need a callback to unlock the next animation
-      // needs support for RTL **
-      if (target.index() !== old_index && !/skip/.test(direction)) {
-        if (/left/.test(direction)) {
-          this.lock();
-          dir_obj[dir] = left + width;
-          clearing.animate(dir_obj, 300, this.unlock());
-        } else if (/right/.test(direction)) {
-          this.lock();
-          dir_obj[dir] = left - width;
-          clearing.animate(dir_obj, 300, this.unlock());
-        }
-      } else if (/skip/.test(direction)) {
-        // the target image is not adjacent to the current image, so
-        // do we scroll right or not
-        skip_shift = target.index() - this.settings.up_count;
-        this.lock();
-
-        if (skip_shift > 0) {
-          dir_obj[dir] = -(skip_shift * width);
-          clearing.animate(dir_obj, 300, this.unlock());
-        } else {
-          dir_obj[dir] = 0;
-          clearing.animate(dir_obj, 300, this.unlock());
-        }
-      }
-
-      callback();
-    },
-
-    direction : function ($el, current, target) {
-      var lis = this.S('li', $el),
-          li_width = lis.outerWidth() + (lis.outerWidth() / 4),
-          up_count = Math.floor(this.S('.clearing-container').outerWidth() / li_width) - 1,
-          target_index = lis.index(target),
-          response;
-
-      this.settings.up_count = up_count;
-
-      if (this.adjacent(this.settings.prev_index, target_index)) {
-        if ((target_index > up_count) && target_index > this.settings.prev_index) {
-          response = 'right';
-        } else if ((target_index > up_count - 1) && target_index <= this.settings.prev_index) {
-          response = 'left';
-        } else {
-          response = false;
-        }
-      } else {
-        response = 'skip';
-      }
-
-      this.settings.prev_index = target_index;
-
-      return response;
-    },
-
-    adjacent : function (current_index, target_index) {
-      for (var i = target_index + 1; i >= target_index - 1; i--) {
-        if (i === current_index) {
-          return true;
-        }
-      }
-      return false;
-    },
-
-    // lock management
-
-    lock : function () {
-      this.settings.locked = true;
-    },
-
-    unlock : function () {
-      this.settings.locked = false;
-    },
-
-    locked : function () {
-      return this.settings.locked;
-    },
-
-    off : function () {
-      this.S(this.scope).off('.fndtn.clearing');
-      this.S(window).off('.fndtn.clearing');
-    },
-
-    reflow : function () {
-      this.init();
-    }
-  };
-
-}(jQuery, window, window.document));
-;(function ($, window, document, undefined) {
-  'use strict';
-
-  var noop = function () {};
-
-  var Orbit = function (el, settings) {
-    // Don't reinitialize plugin
-    if (el.hasClass(settings.slides_container_class)) {
-      return this;
-    }
-
-    var self = this,
-        container,
-        slides_container = el,
-        number_container,
-        bullets_container,
-        timer_container,
-        idx = 0,
-        animate,
-        timer,
-        locked = false,
-        adjust_height_after = false;
-
-    self.slides = function () {
-      return slides_container.children(settings.slide_selector);
-    };
-
-    self.slides().first().addClass(settings.active_slide_class);
-
-    self.update_slide_number = function (index) {
-      if (settings.slide_number) {
-        number_container.find('span:first').text(parseInt(index) + 1);
-        number_container.find('span:last').text(self.slides().length);
-      }
-      if (settings.bullets) {
-        bullets_container.children().removeClass(settings.bullets_active_class);
-        $(bullets_container.children().get(index)).addClass(settings.bullets_active_class);
-      }
-    };
-
-    self.update_active_link = function (index) {
-      var link = $('[data-orbit-link="' + self.slides().eq(index).attr('data-orbit-slide') + '"]');
-      link.siblings().removeClass(settings.bullets_active_class);
-      link.addClass(settings.bullets_active_class);
-    };
-
-    self.build_markup = function () {
-      slides_container.wrap('<div class="' + settings.container_class + '"></div>');
-      container = slides_container.parent();
-      slides_container.addClass(settings.slides_container_class);
-
-      if (settings.stack_on_small) {
-        container.addClass(settings.stack_on_small_class);
-      }
-
-      if (settings.navigation_arrows) {
-        container.append($('<a href="#"><span></span></a>').addClass(settings.prev_class));
-        container.append($('<a href="#"><span></span></a>').addClass(settings.next_class));
-      }
-
-      if (settings.timer) {
-        timer_container = $('<div>').addClass(settings.timer_container_class);
-        timer_container.append('<span>');
-        timer_container.append($('<div>').addClass(settings.timer_progress_class));
-        timer_container.addClass(settings.timer_paused_class);
-        container.append(timer_container);
-      }
-
-      if (settings.slide_number) {
-        number_container = $('<div>').addClass(settings.slide_number_class);
-        number_container.append('<span></span> ' + settings.slide_number_text + ' <span></span>');
-        container.append(number_container);
-      }
-
-      if (settings.bullets) {
-        bullets_container = $('<ol>').addClass(settings.bullets_container_class);
-        container.append(bullets_container);
-        bullets_container.wrap('<div class="orbit-bullets-container"></div>');
-        self.slides().each(function (idx, el) {
-          var bullet = $('<li>').attr('data-orbit-slide', idx).on('click', self.link_bullet);;
-          bullets_container.append(bullet);
-        });
-      }
-
-    };
-
-    self._goto = function (next_idx, start_timer) {
-      // if (locked) {return false;}
-      if (next_idx === idx) {return false;}
-      if (typeof timer === 'object') {timer.restart();}
-      var slides = self.slides();
-
-      var dir = 'next';
-      locked = true;
-      if (next_idx < idx) {dir = 'prev';}
-      if (next_idx >= slides.length) {
-        if (!settings.circular) {
-          return false;
-        }
-        next_idx = 0;
-      } else if (next_idx < 0) {
-        if (!settings.circular) {
-          return false;
-        }
-        next_idx = slides.length - 1;
-      }
-
-      var current = $(slides.get(idx));
-      var next = $(slides.get(next_idx));
-
-      current.css('zIndex', 2);
-      current.removeClass(settings.active_slide_class);
-      next.css('zIndex', 4).addClass(settings.active_slide_class);
-
-      slides_container.trigger('before-slide-change.fndtn.orbit');
-      settings.before_slide_change();
-      self.update_active_link(next_idx);
-
-      var callback = function () {
-        var unlock = function () {
-          idx = next_idx;
-          locked = false;
-          if (start_timer === true) {timer = self.create_timer(); timer.start();}
-          self.update_slide_number(idx);
-          slides_container.trigger('after-slide-change.fndtn.orbit', [{slide_number : idx, total_slides : slides.length}]);
-          settings.after_slide_change(idx, slides.length);
-        };
-        if (slides_container.outerHeight() != next.outerHeight() && settings.variable_height) {
-          slides_container.animate({'height': next.outerHeight()}, 250, 'linear', unlock);
-        } else {
-          unlock();
-        }
-      };
-
-      if (slides.length === 1) {callback(); return false;}
-
-      var start_animation = function () {
-        if (dir === 'next') {animate.next(current, next, callback);}
-        if (dir === 'prev') {animate.prev(current, next, callback);}
-      };
-
-      if (next.outerHeight() > slides_container.outerHeight() && settings.variable_height) {
-        slides_container.animate({'height': next.outerHeight()}, 250, 'linear', start_animation);
-      } else {
-        start_animation();
-      }
-    };
-
-    self.next = function (e) {
-      e.stopImmediatePropagation();
-      e.preventDefault();
-      self._goto(idx + 1);
-    };
-
-    self.prev = function (e) {
-      e.stopImmediatePropagation();
-      e.preventDefault();
-      self._goto(idx - 1);
-    };
-
-    self.link_custom = function (e) {
-      e.preventDefault();
-      var link = $(this).attr('data-orbit-link');
-      if ((typeof link === 'string') && (link = $.trim(link)) != '') {
-        var slide = container.find('[data-orbit-slide=' + link + ']');
-        if (slide.index() != -1) {self._goto(slide.index());}
-      }
-    };
-
-    self.link_bullet = function (e) {
-      var index = $(this).attr('data-orbit-slide');
-      if ((typeof index === 'string') && (index = $.trim(index)) != '') {
-        if (isNaN(parseInt(index))) {
-          var slide = container.find('[data-orbit-slide=' + index + ']');
-          if (slide.index() != -1) {self._goto(slide.index() + 1);}
-        } else {
-          self._goto(parseInt(index));
-        }
-      }
-
-    }
-
-    self.timer_callback = function () {
-      self._goto(idx + 1, true);
-    }
-
-    self.compute_dimensions = function () {
-      var current = $(self.slides().get(idx));
-      var h = current.outerHeight();
-      if (!settings.variable_height) {
-        self.slides().each(function(){
-          if ($(this).outerHeight() > h) { h = $(this).outerHeight(); }
-        });
-      }
-      slides_container.height(h);
-    };
-
-    self.create_timer = function () {
-      var t = new Timer(
-        container.find('.' + settings.timer_container_class),
-        settings,
-        self.timer_callback
-      );
-      return t;
-    };
-
-    self.stop_timer = function () {
-      if (typeof timer === 'object') {
-        timer.stop();
-      }
-    };
-
-    self.toggle_timer = function () {
-      var t = container.find('.' + settings.timer_container_class);
-      if (t.hasClass(settings.timer_paused_class)) {
-        if (typeof timer === 'undefined') {timer = self.create_timer();}
-        timer.start();
-      } else {
-        if (typeof timer === 'object') {timer.stop();}
-      }
-    };
-
-    self.init = function () {
-      self.build_markup();
-      if (settings.timer) {
-        timer = self.create_timer();
-        Foundation.utils.image_loaded(this.slides().children('img'), timer.start);
-      }
-      animate = new FadeAnimation(settings, slides_container);
-      if (settings.animation === 'slide') {
-        animate = new SlideAnimation(settings, slides_container);
-      }
-
-      container.on('click', '.' + settings.next_class, self.next);
-      container.on('click', '.' + settings.prev_class, self.prev);
-
-      if (settings.next_on_click) {
-        container.on('click', '.' + settings.slides_container_class + ' [data-orbit-slide]', self.link_bullet);
-      }
-
-      container.on('click', self.toggle_timer);
-      if (settings.swipe) {
-        container.on('touchstart.fndtn.orbit', function (e) {
-          if (!e.touches) {e = e.originalEvent;}
-          var data = {
-            start_page_x : e.touches[0].pageX,
-            start_page_y : e.touches[0].pageY,
-            start_time : (new Date()).getTime(),
-            delta_x : 0,
-            is_scrolling : undefined
-          };
-          container.data('swipe-transition', data);
-          e.stopPropagation();
-        })
-        .on('touchmove.fndtn.orbit', function (e) {
-          if (!e.touches) {
-            e = e.originalEvent;
-          }
-          // Ignore pinch/zoom events
-          if (e.touches.length > 1 || e.scale && e.scale !== 1) {
-            return;
-          }
-
-          var data = container.data('swipe-transition');
-          if (typeof data === 'undefined') {data = {};}
-
-          data.delta_x = e.touches[0].pageX - data.start_page_x;
-
-          if ( typeof data.is_scrolling === 'undefined') {
-            data.is_scrolling = !!( data.is_scrolling || Math.abs(data.delta_x) < Math.abs(e.touches[0].pageY - data.start_page_y) );
-          }
-
-          if (!data.is_scrolling && !data.active) {
-            e.preventDefault();
-            var direction = (data.delta_x < 0) ? (idx + 1) : (idx - 1);
-            data.active = true;
-            self._goto(direction);
-          }
-        })
-        .on('touchend.fndtn.orbit', function (e) {
-          container.data('swipe-transition', {});
-          e.stopPropagation();
-        })
-      }
-      container.on('mouseenter.fndtn.orbit', function (e) {
-        if (settings.timer && settings.pause_on_hover) {
-          self.stop_timer();
-        }
-      })
-      .on('mouseleave.fndtn.orbit', function (e) {
-        if (settings.timer && settings.resume_on_mouseout) {
-          timer.start();
-        }
-      });
-
-      $(document).on('click', '[data-orbit-link]', self.link_custom);
-      $(window).on('load resize', self.compute_dimensions);
-      Foundation.utils.image_loaded(this.slides().children('img'), self.compute_dimensions);
-      Foundation.utils.image_loaded(this.slides().children('img'), function () {
-        container.prev('.' + settings.preloader_class).css('display', 'none');
-        self.update_slide_number(0);
-        self.update_active_link(0);
-        slides_container.trigger('ready.fndtn.orbit');
-      });
-    };
-
-    self.init();
-  };
-
-  var Timer = function (el, settings, callback) {
-    var self = this,
-        duration = settings.timer_speed,
-        progress = el.find('.' + settings.timer_progress_class),
-        start,
-        timeout,
-        left = -1;
-
-    this.update_progress = function (w) {
-      var new_progress = progress.clone();
-      new_progress.attr('style', '');
-      new_progress.css('width', w + '%');
-      progress.replaceWith(new_progress);
-      progress = new_progress;
-    };
-
-    this.restart = function () {
-      clearTimeout(timeout);
-      el.addClass(settings.timer_paused_class);
-      left = -1;
-      self.update_progress(0);
-    };
-
-    this.start = function () {
-      if (!el.hasClass(settings.timer_paused_class)) {return true;}
-      left = (left === -1) ? duration : left;
-      el.removeClass(settings.timer_paused_class);
-      start = new Date().getTime();
-      progress.animate({'width' : '100%'}, left, 'linear');
-      timeout = setTimeout(function () {
-        self.restart();
-        callback();
-      }, left);
-      el.trigger('timer-started.fndtn.orbit')
-    };
-
-    this.stop = function () {
-      if (el.hasClass(settings.timer_paused_class)) {return true;}
-      clearTimeout(timeout);
-      el.addClass(settings.timer_paused_class);
-      var end = new Date().getTime();
-      left = left - (end - start);
-      var w = 100 - ((left / duration) * 100);
-      self.update_progress(w);
-      el.trigger('timer-stopped.fndtn.orbit');
-    };
-  };
-
-  var SlideAnimation = function (settings, container) {
-    var duration = settings.animation_speed;
-    var is_rtl = ($('html[dir=rtl]').length === 1);
-    var margin = is_rtl ? 'marginRight' : 'marginLeft';
-    var animMargin = {};
-    animMargin[margin] = '0%';
-
-    this.next = function (current, next, callback) {
-      current.animate({marginLeft : '-100%'}, duration);
-      next.animate(animMargin, duration, function () {
-        current.css(margin, '100%');
-        callback();
-      });
-    };
-
-    this.prev = function (current, prev, callback) {
-      current.animate({marginLeft : '100%'}, duration);
-      prev.css(margin, '-100%');
-      prev.animate(animMargin, duration, function () {
-        current.css(margin, '100%');
-        callback();
-      });
-    };
-  };
-
-  var FadeAnimation = function (settings, container) {
-    var duration = settings.animation_speed;
-    var is_rtl = ($('html[dir=rtl]').length === 1);
-    var margin = is_rtl ? 'marginRight' : 'marginLeft';
-
-    this.next = function (current, next, callback) {
-      next.css({'margin' : '0%', 'opacity' : '0.01'});
-      next.animate({'opacity' :'1'}, duration, 'linear', function () {
-        current.css('margin', '100%');
-        callback();
-      });
-    };
-
-    this.prev = function (current, prev, callback) {
-      prev.css({'margin' : '0%', 'opacity' : '0.01'});
-      prev.animate({'opacity' : '1'}, duration, 'linear', function () {
-        current.css('margin', '100%');
-        callback();
-      });
-    };
-  };
-
-  Foundation.libs = Foundation.libs || {};
-
-  Foundation.libs.orbit = {
-    name : 'orbit',
-
-    version : '5.5.1',
-
-    settings : {
-      animation : 'slide',
-      timer_speed : 10000,
-      pause_on_hover : true,
-      resume_on_mouseout : false,
-      next_on_click : true,
-      animation_speed : 500,
-      stack_on_small : false,
-      navigation_arrows : true,
-      slide_number : true,
-      slide_number_text : 'of',
-      container_class : 'orbit-container',
-      stack_on_small_class : 'orbit-stack-on-small',
-      next_class : 'orbit-next',
-      prev_class : 'orbit-prev',
-      timer_container_class : 'orbit-timer',
-      timer_paused_class : 'paused',
-      timer_progress_class : 'orbit-progress',
-      slides_container_class : 'orbit-slides-container',
-      preloader_class : 'preloader',
-      slide_selector : '*',
-      bullets_container_class : 'orbit-bullets',
-      bullets_active_class : 'active',
-      slide_number_class : 'orbit-slide-number',
-      caption_class : 'orbit-caption',
-      active_slide_class : 'active',
-      orbit_transition_class : 'orbit-transitioning',
-      bullets : true,
-      circular : true,
-      timer : true,
-      variable_height : false,
-      swipe : true,
-      before_slide_change : noop,
-      after_slide_change : noop
-    },
-
-    init : function (scope, method, options) {
-      var self = this;
-      this.bindings(method, options);
-    },
-
-    events : function (instance) {
-      var orbit_instance = new Orbit(this.S(instance), this.S(instance).data('orbit-init'));
-      this.S(instance).data(this.name + '-instance', orbit_instance);
-    },
-
-    reflow : function () {
-      var self = this;
-
-      if (self.S(self.scope).is('[data-orbit]')) {
-        var $el = self.S(self.scope);
-        var instance = $el.data(self.name + '-instance');
-        instance.compute_dimensions();
-      } else {
-        self.S('[data-orbit]', self.scope).each(function (idx, el) {
-          var $el = self.S(el);
-          var opts = self.data_options($el);
-          var instance = $el.data(self.name + '-instance');
-          instance.compute_dimensions();
-        });
-      }
-    }
-  };
-
-}(jQuery, window, window.document));
-;(function ($, window, document, undefined) {
-  'use strict';
-
-  Foundation.libs.offcanvas = {
-    name : 'offcanvas',
-
-    version : '5.5.1',
-
-    settings : {
-      open_method : 'move',
-      close_on_click : false
-    },
-
-    init : function (scope, method, options) {
-      this.bindings(method, options);
-    },
-
-    events : function () {
-      var self = this,
-          S = self.S,
-          move_class = '',
-          right_postfix = '',
-          left_postfix = '';
-
-      if (this.settings.open_method === 'move') {
-        move_class = 'move-';
-        right_postfix = 'right';
-        left_postfix = 'left';
-      } else if (this.settings.open_method === 'overlap_single') {
-        move_class = 'offcanvas-overlap-';
-        right_postfix = 'right';
-        left_postfix = 'left';
-      } else if (this.settings.open_method === 'overlap') {
-        move_class = 'offcanvas-overlap';
-      }
-
-      S(this.scope).off('.offcanvas')
-        .on('click.fndtn.offcanvas', '.left-off-canvas-toggle', function (e) {
-          self.click_toggle_class(e, move_class + right_postfix);
-          if (self.settings.open_method !== 'overlap') {
-            S('.left-submenu').removeClass(move_class + right_postfix);
-          }
-          $('.left-off-canvas-toggle').attr('aria-expanded', 'true');
-        })
-        .on('click.fndtn.offcanvas', '.left-off-canvas-menu a', function (e) {
-          var settings = self.get_settings(e);
-          var parent = S(this).parent();
-
-          if (settings.close_on_click && !parent.hasClass('has-submenu') && !parent.hasClass('back')) {
-            self.hide.call(self, move_class + right_postfix, self.get_wrapper(e));
-            parent.parent().removeClass(move_class + right_postfix);
-          } else if (S(this).parent().hasClass('has-submenu')) {
-            e.preventDefault();
-            S(this).siblings('.left-submenu').toggleClass(move_class + right_postfix);
-          } else if (parent.hasClass('back')) {
-            e.preventDefault();
-            parent.parent().removeClass(move_class + right_postfix);
-          }
-          $('.left-off-canvas-toggle').attr('aria-expanded', 'true');
-        })
-        .on('click.fndtn.offcanvas', '.right-off-canvas-toggle', function (e) {
-          self.click_toggle_class(e, move_class + left_postfix);
-          if (self.settings.open_method !== 'overlap') {
-            S('.right-submenu').removeClass(move_class + left_postfix);
-          }
-          $('.right-off-canvas-toggle').attr('aria-expanded', 'true');
-        })
-        .on('click.fndtn.offcanvas', '.right-off-canvas-menu a', function (e) {
-          var settings = self.get_settings(e);
-          var parent = S(this).parent();
-
-          if (settings.close_on_click && !parent.hasClass('has-submenu') && !parent.hasClass('back')) {
-            self.hide.call(self, move_class + left_postfix, self.get_wrapper(e));
-            parent.parent().removeClass(move_class + left_postfix);
-          } else if (S(this).parent().hasClass('has-submenu')) {
-            e.preventDefault();
-            S(this).siblings('.right-submenu').toggleClass(move_class + left_postfix);
-          } else if (parent.hasClass('back')) {
-            e.preventDefault();
-            parent.parent().removeClass(move_class + left_postfix);
-          }
-          $('.right-off-canvas-toggle').attr('aria-expanded', 'true');
-        })
-        .on('click.fndtn.offcanvas', '.exit-off-canvas', function (e) {
-          self.click_remove_class(e, move_class + left_postfix);
-          S('.right-submenu').removeClass(move_class + left_postfix);
-          if (right_postfix) {
-            self.click_remove_class(e, move_class + right_postfix);
-            S('.left-submenu').removeClass(move_class + left_postfix);
-          }
-          $('.right-off-canvas-toggle').attr('aria-expanded', 'true');
-        })
-        .on('click.fndtn.offcanvas', '.exit-off-canvas', function (e) {
-          self.click_remove_class(e, move_class + left_postfix);
-          $('.left-off-canvas-toggle').attr('aria-expanded', 'false');
-          if (right_postfix) {
-            self.click_remove_class(e, move_class + right_postfix);
-            $('.right-off-canvas-toggle').attr('aria-expanded', 'false');
-          }
-        });
-    },
-
-    toggle : function (class_name, $off_canvas) {
-      $off_canvas = $off_canvas || this.get_wrapper();
-      if ($off_canvas.is('.' + class_name)) {
-        this.hide(class_name, $off_canvas);
-      } else {
-        this.show(class_name, $off_canvas);
-      }
-    },
-
-    show : function (class_name, $off_canvas) {
-      $off_canvas = $off_canvas || this.get_wrapper();
-      $off_canvas.trigger('open').trigger('open.fndtn.offcanvas');
-      $off_canvas.addClass(class_name);
-    },
-
-    hide : function (class_name, $off_canvas) {
-      $off_canvas = $off_canvas || this.get_wrapper();
-      $off_canvas.trigger('close').trigger('close.fndtn.offcanvas');
-      $off_canvas.removeClass(class_name);
-    },
-
-    click_toggle_class : function (e, class_name) {
-      e.preventDefault();
-      var $off_canvas = this.get_wrapper(e);
-      this.toggle(class_name, $off_canvas);
-    },
-
-    click_remove_class : function (e, class_name) {
-      e.preventDefault();
-      var $off_canvas = this.get_wrapper(e);
-      this.hide(class_name, $off_canvas);
-    },
-
-    get_settings : function (e) {
-      var offcanvas  = this.S(e.target).closest('[' + this.attr_name() + ']');
-      return offcanvas.data(this.attr_name(true) + '-init') || this.settings;
-    },
-
-    get_wrapper : function (e) {
-      var $off_canvas = this.S(e ? e.target : this.scope).closest('.off-canvas-wrap');
-
-      if ($off_canvas.length === 0) {
-        $off_canvas = this.S('.off-canvas-wrap');
-      }
-      return $off_canvas;
-    },
-
-    reflow : function () {}
-  };
-}(jQuery, window, window.document));
-;(function ($, window, document, undefined) {
-  'use strict';
-
-  Foundation.libs.alert = {
-    name : 'alert',
-
-    version : '5.5.1',
-
-    settings : {
-      callback : function () {}
-    },
-
-    init : function (scope, method, options) {
-      this.bindings(method, options);
-    },
-
-    events : function () {
-      var self = this,
-          S = this.S;
-
-      $(this.scope).off('.alert').on('click.fndtn.alert', '[' + this.attr_name() + '] .close', function (e) {
-        var alertBox = S(this).closest('[' + self.attr_name() + ']'),
-            settings = alertBox.data(self.attr_name(true) + '-init') || self.settings;
-
-        e.preventDefault();
-        if (Modernizr.csstransitions) {
-          alertBox.addClass('alert-close');
-          alertBox.on('transitionend webkitTransitionEnd oTransitionEnd', function (e) {
-            S(this).trigger('close').trigger('close.fndtn.alert').remove();
-            settings.callback();
-          });
-        } else {
-          alertBox.fadeOut(300, function () {
-            S(this).trigger('close').trigger('close.fndtn.alert').remove();
-            settings.callback();
-          });
-        }
-      });
-    },
-
-    reflow : function () {}
-  };
-}(jQuery, window, window.document));
-;(function ($, window, document, undefined) {
-  'use strict';
-
-  Foundation.libs.reveal = {
-    name : 'reveal',
-
-    version : '5.5.1',
-
-    locked : false,
-
-    settings : {
-      animation : 'fadeAndPop',
-      animation_speed : 250,
-      close_on_background_click : true,
-      close_on_esc : true,
-      dismiss_modal_class : 'close-reveal-modal',
-      multiple_opened : false,
-      bg_class : 'reveal-modal-bg',
-      root_element : 'body',
-      open : function(){},
-      opened : function(){},
-      close : function(){},
-      closed : function(){},
-      bg : $('.reveal-modal-bg'),
-      css : {
-        open : {
-          'opacity' : 0,
-          'visibility' : 'visible',
-          'display' : 'block'
-        },
-        close : {
-          'opacity' : 1,
-          'visibility' : 'hidden',
-          'display' : 'none'
-        }
-      }
-    },
-
-    init : function (scope, method, options) {
-      $.extend(true, this.settings, method, options);
-      this.bindings(method, options);
-    },
-
-    events : function (scope) {
-      var self = this,
-          S = self.S;
-
-      S(this.scope)
-        .off('.reveal')
-        .on('click.fndtn.reveal', '[' + this.add_namespace('data-reveal-id') + ']:not([disabled])', function (e) {
-          e.preventDefault();
-
-          if (!self.locked) {
-            var element = S(this),
-                ajax = element.data(self.data_attr('reveal-ajax'));
-
-            self.locked = true;
-
-            if (typeof ajax === 'undefined') {
-              self.open.call(self, element);
-            } else {
-              var url = ajax === true ? element.attr('href') : ajax;
-
-              self.open.call(self, element, {url : url});
-            }
-          }
-        });
-
-      S(document)
-        .on('click.fndtn.reveal', this.close_targets(), function (e) {
-          e.preventDefault();
-          if (!self.locked) {
-            var settings = S('[' + self.attr_name() + '].open').data(self.attr_name(true) + '-init') || self.settings,
-                bg_clicked = S(e.target)[0] === S('.' + settings.bg_class)[0];
-
-            if (bg_clicked) {
-              if (settings.close_on_background_click) {
-                e.stopPropagation();
-              } else {
-                return;
-              }
-            }
-
-            self.locked = true;
-            self.close.call(self, bg_clicked ? S('[' + self.attr_name() + '].open') : S(this).closest('[' + self.attr_name() + ']'));
-          }
-        });
-
-      if (S('[' + self.attr_name() + ']', this.scope).length > 0) {
-        S(this.scope)
-          // .off('.reveal')
-          .on('open.fndtn.reveal', this.settings.open)
-          .on('opened.fndtn.reveal', this.settings.opened)
-          .on('opened.fndtn.reveal', this.open_video)
-          .on('close.fndtn.reveal', this.settings.close)
-          .on('closed.fndtn.reveal', this.settings.closed)
-          .on('closed.fndtn.reveal', this.close_video);
-      } else {
-        S(this.scope)
-          // .off('.reveal')
-          .on('open.fndtn.reveal', '[' + self.attr_name() + ']', this.settings.open)
-          .on('opened.fndtn.reveal', '[' + self.attr_name() + ']', this.settings.opened)
-          .on('opened.fndtn.reveal', '[' + self.attr_name() + ']', this.open_video)
-          .on('close.fndtn.reveal', '[' + self.attr_name() + ']', this.settings.close)
-          .on('closed.fndtn.reveal', '[' + self.attr_name() + ']', this.settings.closed)
-          .on('closed.fndtn.reveal', '[' + self.attr_name() + ']', this.close_video);
-      }
-
-      return true;
-    },
-
-    // PATCH #3: turning on key up capture only when a reveal window is open
-    key_up_on : function (scope) {
-      var self = this;
-
-      // PATCH #1: fixing multiple keyup event trigger from single key press
-      self.S('body').off('keyup.fndtn.reveal').on('keyup.fndtn.reveal', function ( event ) {
-        var open_modal = self.S('[' + self.attr_name() + '].open'),
-            settings = open_modal.data(self.attr_name(true) + '-init') || self.settings ;
-        // PATCH #2: making sure that the close event can be called only while unlocked,
-        //           so that multiple keyup.fndtn.reveal events don't prevent clean closing of the reveal window.
-        if ( settings && event.which === 27  && settings.close_on_esc && !self.locked) { // 27 is the keycode for the Escape key
-          self.close.call(self, open_modal);
-        }
-      });
-
-      return true;
-    },
-
-    // PATCH #3: turning on key up capture only when a reveal window is open
-    key_up_off : function (scope) {
-      this.S('body').off('keyup.fndtn.reveal');
-      return true;
-    },
-
-    open : function (target, ajax_settings) {
-      var self = this,
-          modal;
-
-      if (target) {
-        if (typeof target.selector !== 'undefined') {
-          // Find the named node; only use the first one found, since the rest of the code assumes there's only one node
-          modal = self.S('#' + target.data(self.data_attr('reveal-id'))).first();
-        } else {
-          modal = self.S(this.scope);
-
-          ajax_settings = target;
-        }
-      } else {
-        modal = self.S(this.scope);
-      }
-
-      var settings = modal.data(self.attr_name(true) + '-init');
-      settings = settings || this.settings;
-
-      if (modal.hasClass('open') && target.attr('data-reveal-id') == modal.attr('id')) {
-        return self.close(modal);
-      }
-
-      if (!modal.hasClass('open')) {
-        var open_modal = self.S('[' + self.attr_name() + '].open');
-
-        if (typeof modal.data('css-top') === 'undefined') {
-          modal.data('css-top', parseInt(modal.css('top'), 10))
-            .data('offset', this.cache_offset(modal));
-        }
-
-        this.key_up_on(modal);    // PATCH #3: turning on key up capture only when a reveal window is open
-
-        modal.on('open.fndtn.reveal').trigger('open.fndtn.reveal');
-
-        if (open_modal.length < 1) {
-          this.toggle_bg(modal, true);
-        }
-
-        if (typeof ajax_settings === 'string') {
-          ajax_settings = {
-            url : ajax_settings
-          };
-        }
-
-        if (typeof ajax_settings === 'undefined' || !ajax_settings.url) {
-          if (open_modal.length > 0) {
-            if (settings.multiple_opened) {
-              this.to_back(open_modal);
-            } else {
-              this.hide(open_modal, settings.css.close);
-            }
-          }
-
-          this.show(modal, settings.css.open);
-        } else {
-          var old_success = typeof ajax_settings.success !== 'undefined' ? ajax_settings.success : null;
-
-          $.extend(ajax_settings, {
-            success : function (data, textStatus, jqXHR) {
-              if ( $.isFunction(old_success) ) {
-                var result = old_success(data, textStatus, jqXHR);
-                if (typeof result == 'string') {
-                  data = result;
-                }
-              }
-
-              modal.html(data);
-              self.S(modal).foundation('section', 'reflow');
-              self.S(modal).children().foundation();
-
-              if (open_modal.length > 0) {
-                if (settings.multiple_opened) {
-                  this.to_back(open_modal);
-                } else {
-                  this.hide(open_modal, settings.css.close);
-                }
-              }
-              self.show(modal, settings.css.open);
-            }
-          });
-
-          $.ajax(ajax_settings);
-        }
-      }
-      self.S(window).trigger('resize');
-    },
-
-    close : function (modal) {
-      var modal = modal && modal.length ? modal : this.S(this.scope),
-          open_modals = this.S('[' + this.attr_name() + '].open'),
-          settings = modal.data(this.attr_name(true) + '-init') || this.settings;
-
-      if (open_modals.length > 0) {
-        this.locked = true;
-        this.key_up_off(modal);   // PATCH #3: turning on key up capture only when a reveal window is open
-        modal.trigger('close').trigger('close.fndtn.reveal');
-        
-        if ((settings.multiple_opened && open_modals.length === 1) || !settings.multiple_opened || modal.length > 1) {
-          this.toggle_bg(modal, false);
-          this.to_front(modal);
-        }
-        
-        if (settings.multiple_opened) {
-          this.hide(modal, settings.css.close, settings);
-          this.to_front($($.makeArray(open_modals).reverse()[1]));
-        } else {
-          this.hide(open_modals, settings.css.close, settings);
-        }
-      }
-    },
-
-    close_targets : function () {
-      var base = '.' + this.settings.dismiss_modal_class;
-
-      if (this.settings.close_on_background_click) {
-        return base + ', .' + this.settings.bg_class;
-      }
-
-      return base;
-    },
-
-    toggle_bg : function (modal, state) {
-      if (this.S('.' + this.settings.bg_class).length === 0) {
-        this.settings.bg = $('<div />', {'class': this.settings.bg_class})
-          .appendTo('body').hide();
-      }
-
-      var visible = this.settings.bg.filter(':visible').length > 0;
-      if ( state != visible ) {
-        if ( state == undefined ? visible : !state ) {
-          this.hide(this.settings.bg);
-        } else {
-          this.show(this.settings.bg);
-        }
-      }
-    },
-
-    show : function (el, css) {
-      // is modal
-      if (css) {
-        var settings = el.data(this.attr_name(true) + '-init') || this.settings,
-            root_element = settings.root_element;
-
-        if (el.parent(root_element).length === 0) {
-          var placeholder = el.wrap('<div style="display: none;" />').parent();
-
-          el.on('closed.fndtn.reveal.wrapped', function () {
-            el.detach().appendTo(placeholder);
-            el.unwrap().unbind('closed.fndtn.reveal.wrapped');
-          });
-
-          el.detach().appendTo(root_element);
-        }
-
-        var animData = getAnimationData(settings.animation);
-        if (!animData.animate) {
-          this.locked = false;
-        }
-        if (animData.pop) {
-          css.top = $(window).scrollTop() - el.data('offset') + 'px';
-          var end_css = {
-            top: $(window).scrollTop() + el.data('css-top') + 'px',
-            opacity: 1
-          };
-
-          return setTimeout(function () {
-            return el
-              .css(css)
-              .animate(end_css, settings.animation_speed, 'linear', function () {
-                this.locked = false;
-                el.trigger('opened').trigger('opened.fndtn.reveal');
-              }.bind(this))
-              .addClass('open');
-          }.bind(this), settings.animation_speed / 2);
-        }
-
-        if (animData.fade) {
-          css.top = $(window).scrollTop() + el.data('css-top') + 'px';
-          var end_css = {opacity: 1};
-
-          return setTimeout(function () {
-            return el
-              .css(css)
-              .animate(end_css, settings.animation_speed, 'linear', function () {
-                this.locked = false;
-                el.trigger('opened').trigger('opened.fndtn.reveal');
-              }.bind(this))
-              .addClass('open');
-          }.bind(this), settings.animation_speed / 2);
-        }
-
-        return el.css(css).show().css({opacity : 1}).addClass('open').trigger('opened').trigger('opened.fndtn.reveal');
-      }
-
-      var settings = this.settings;
-
-      // should we animate the background?
-      if (getAnimationData(settings.animation).fade) {
-        return el.fadeIn(settings.animation_speed / 2);
-      }
-
-      this.locked = false;
-
-      return el.show();
-    },
-    
-    to_back : function(el) {
-      el.addClass('toback');
-    },
-    
-    to_front : function(el) {
-      el.removeClass('toback');
-    },
-
-    hide : function (el, css) {
-      // is modal
-      if (css) {
-        var settings = el.data(this.attr_name(true) + '-init');
-        settings = settings || this.settings;
-
-        var animData = getAnimationData(settings.animation);
-        if (!animData.animate) {
-          this.locked = false;
-        }
-        if (animData.pop) {
-          var end_css = {
-            top: - $(window).scrollTop() - el.data('offset') + 'px',
-            opacity: 0
-          };
-
-          return setTimeout(function () {
-            return el
-              .animate(end_css, settings.animation_speed, 'linear', function () {
-                this.locked = false;
-                el.css(css).trigger('closed').trigger('closed.fndtn.reveal');
-              }.bind(this))
-              .removeClass('open');
-          }.bind(this), settings.animation_speed / 2);
-        }
-
-        if (animData.fade) {
-          var end_css = {opacity : 0};
-
-          return setTimeout(function () {
-            return el
-              .animate(end_css, settings.animation_speed, 'linear', function () {
-                this.locked = false;
-                el.css(css).trigger('closed').trigger('closed.fndtn.reveal');
-              }.bind(this))
-              .removeClass('open');
-          }.bind(this), settings.animation_speed / 2);
-        }
-
-        return el.hide().css(css).removeClass('open').trigger('closed').trigger('closed.fndtn.reveal');
-      }
-
-      var settings = this.settings;
-
-      // should we animate the background?
-      if (getAnimationData(settings.animation).fade) {
-        return el.fadeOut(settings.animation_speed / 2);
-      }
-
-      return el.hide();
-    },
-
-    close_video : function (e) {
-      var video = $('.flex-video', e.target),
-          iframe = $('iframe', video);
-
-      if (iframe.length > 0) {
-        iframe.attr('data-src', iframe[0].src);
-        iframe.attr('src', iframe.attr('src'));
-        video.hide();
-      }
-    },
-
-    open_video : function (e) {
-      var video = $('.flex-video', e.target),
-          iframe = video.find('iframe');
-
-      if (iframe.length > 0) {
-        var data_src = iframe.attr('data-src');
-        if (typeof data_src === 'string') {
-          iframe[0].src = iframe.attr('data-src');
-        } else {
-          var src = iframe[0].src;
-          iframe[0].src = undefined;
-          iframe[0].src = src;
-        }
-        video.show();
-      }
-    },
-
-    data_attr : function (str) {
-      if (this.namespace.length > 0) {
-        return this.namespace + '-' + str;
-      }
-
-      return str;
-    },
-
-    cache_offset : function (modal) {
-      var offset = modal.show().height() + parseInt(modal.css('top'), 10);
-
-      modal.hide();
-
-      return offset;
-    },
-
-    off : function () {
-      $(this.scope).off('.fndtn.reveal');
-    },
-
-    reflow : function () {}
-  };
-
-  /*
-   * getAnimationData('popAndFade') // {animate: true,  pop: true,  fade: true}
-   * getAnimationData('fade')       // {animate: true,  pop: false, fade: true}
-   * getAnimationData('pop')        // {animate: true,  pop: true,  fade: false}
-   * getAnimationData('foo')        // {animate: false, pop: false, fade: false}
-   * getAnimationData(null)         // {animate: false, pop: false, fade: false}
-   */
-  function getAnimationData(str) {
-    var fade = /fade/i.test(str);
-    var pop = /pop/i.test(str);
-    return {
-      animate : fade || pop,
-      pop : pop,
-      fade : fade
-    };
-  }
-}(jQuery, window, window.document));
-;(function ($, window, document, undefined) {
-  'use strict';
-
-  Foundation.libs.interchange = {
-    name : 'interchange',
-
-    version : '5.5.1',
-
-    cache : {},
-
-    images_loaded : false,
-    nodes_loaded : false,
-
-    settings : {
-      load_attr : 'interchange',
-
-      named_queries : {
-        'default'     : 'only screen',
-        'small'       : Foundation.media_queries['small'],
-        'small-only'  : Foundation.media_queries['small-only'],
-        'medium'      : Foundation.media_queries['medium'],
-        'medium-only' : Foundation.media_queries['medium-only'],
-        'large'       : Foundation.media_queries['large'],
-        'large-only'  : Foundation.media_queries['large-only'],
-        'xlarge'      : Foundation.media_queries['xlarge'],
-        'xlarge-only' : Foundation.media_queries['xlarge-only'],
-        'xxlarge'     : Foundation.media_queries['xxlarge'],
-        'landscape'   : 'only screen and (orientation: landscape)',
-        'portrait'    : 'only screen and (orientation: portrait)',
-        'retina'      : 'only screen and (-webkit-min-device-pixel-ratio: 2),' +
-          'only screen and (min--moz-device-pixel-ratio: 2),' +
-          'only screen and (-o-min-device-pixel-ratio: 2/1),' +
-          'only screen and (min-device-pixel-ratio: 2),' +
-          'only screen and (min-resolution: 192dpi),' +
-          'only screen and (min-resolution: 2dppx)'
-      },
-
-      directives : {
-        replace : function (el, path, trigger) {
-          // The trigger argument, if called within the directive, fires
-          // an event named after the directive on the element, passing
-          // any parameters along to the event that you pass to trigger.
-          //
-          // ex. trigger(), trigger([a, b, c]), or trigger(a, b, c)
-          //
-          // This allows you to bind a callback like so:
-          // $('#interchangeContainer').on('replace', function (e, a, b, c) {
-          //   console.log($(this).html(), a, b, c);
-          // });
-
-          if (/IMG/.test(el[0].nodeName)) {
-            var orig_path = el[0].src;
-
-            if (new RegExp(path, 'i').test(orig_path)) {
-              return;
-            }
-
-            el[0].src = path;
-
-            return trigger(el[0].src);
-          }
-          var last_path = el.data(this.data_attr + '-last-path'),
-              self = this;
-
-          if (last_path == path) {
-            return;
-          }
-
-          if (/\.(gif|jpg|jpeg|tiff|png)([?#].*)?/i.test(path)) {
-            $(el).css('background-image', 'url(' + path + ')');
-            el.data('interchange-last-path', path);
-            return trigger(path);
-          }
-
-          return $.get(path, function (response) {
-            el.html(response);
-            el.data(self.data_attr + '-last-path', path);
-            trigger();
-          });
-
-        }
-      }
-    },
-
-    init : function (scope, method, options) {
-      Foundation.inherit(this, 'throttle random_str');
-
-      this.data_attr = this.set_data_attr();
-      $.extend(true, this.settings, method, options);
-      this.bindings(method, options);
-      this.load('images');
-      this.load('nodes');
-    },
-
-    get_media_hash : function () {
-        var mediaHash = '';
-        for (var queryName in this.settings.named_queries ) {
-            mediaHash += matchMedia(this.settings.named_queries[queryName]).matches.toString();
-        }
-        return mediaHash;
-    },
-
-    events : function () {
-      var self = this, prevMediaHash;
-
-      $(window)
-        .off('.interchange')
-        .on('resize.fndtn.interchange', self.throttle(function () {
-            var currMediaHash = self.get_media_hash();
-            if (currMediaHash !== prevMediaHash) {
-                self.resize();
-            }
-            prevMediaHash = currMediaHash;
-        }, 50));
-
-      return this;
-    },
-
-    resize : function () {
-      var cache = this.cache;
-
-      if (!this.images_loaded || !this.nodes_loaded) {
-        setTimeout($.proxy(this.resize, this), 50);
-        return;
-      }
-
-      for (var uuid in cache) {
-        if (cache.hasOwnProperty(uuid)) {
-          var passed = this.results(uuid, cache[uuid]);
-
-          if (passed) {
-            this.settings.directives[passed
-              .scenario[1]].call(this, passed.el, passed.scenario[0], (function (passed) {
-                if (arguments[0] instanceof Array) { 
-                  var args = arguments[0];
-                } else {
-                  var args = Array.prototype.slice.call(arguments, 0);
-                }
-
-                return function() {
-                  passed.el.trigger(passed.scenario[1], args);
-                }
-              }(passed)));
-          }
-        }
-      }
-
-    },
-
-    results : function (uuid, scenarios) {
-      var count = scenarios.length;
-
-      if (count > 0) {
-        var el = this.S('[' + this.add_namespace('data-uuid') + '="' + uuid + '"]');
-
-        while (count--) {
-          var mq, rule = scenarios[count][2];
-          if (this.settings.named_queries.hasOwnProperty(rule)) {
-            mq = matchMedia(this.settings.named_queries[rule]);
-          } else {
-            mq = matchMedia(rule);
-          }
-          if (mq.matches) {
-            return {el : el, scenario : scenarios[count]};
-          }
-        }
-      }
-
-      return false;
-    },
-
-    load : function (type, force_update) {
-      if (typeof this['cached_' + type] === 'undefined' || force_update) {
-        this['update_' + type]();
-      }
-
-      return this['cached_' + type];
-    },
-
-    update_images : function () {
-      var images = this.S('img[' + this.data_attr + ']'),
-          count = images.length,
-          i = count,
-          loaded_count = 0,
-          data_attr = this.data_attr;
-
-      this.cache = {};
-      this.cached_images = [];
-      this.images_loaded = (count === 0);
-
-      while (i--) {
-        loaded_count++;
-        if (images[i]) {
-          var str = images[i].getAttribute(data_attr) || '';
-
-          if (str.length > 0) {
-            this.cached_images.push(images[i]);
-          }
-        }
-
-        if (loaded_count === count) {
-          this.images_loaded = true;
-          this.enhance('images');
-        }
-      }
-
-      return this;
-    },
-
-    update_nodes : function () {
-      var nodes = this.S('[' + this.data_attr + ']').not('img'),
-          count = nodes.length,
-          i = count,
-          loaded_count = 0,
-          data_attr = this.data_attr;
-
-      this.cached_nodes = [];
-      this.nodes_loaded = (count === 0);
-
-      while (i--) {
-        loaded_count++;
-        var str = nodes[i].getAttribute(data_attr) || '';
-
-        if (str.length > 0) {
-          this.cached_nodes.push(nodes[i]);
-        }
-
-        if (loaded_count === count) {
-          this.nodes_loaded = true;
-          this.enhance('nodes');
-        }
-      }
-
-      return this;
-    },
-
-    enhance : function (type) {
-      var i = this['cached_' + type].length;
-
-      while (i--) {
-        this.object($(this['cached_' + type][i]));
-      }
-
-      return $(window).trigger('resize').trigger('resize.fndtn.interchange');
-    },
-
-    convert_directive : function (directive) {
-
-      var trimmed = this.trim(directive);
-
-      if (trimmed.length > 0) {
-        return trimmed;
-      }
-
-      return 'replace';
-    },
-
-    parse_scenario : function (scenario) {
-      // This logic had to be made more complex since some users were using commas in the url path
-      // So we cannot simply just split on a comma
-      var directive_match = scenario[0].match(/(.+),\s*(\w+)\s*$/),
-      media_query         = scenario[1];
-
-      if (directive_match) {
-        var path  = directive_match[1],
-        directive = directive_match[2];
-      } else {
-        var cached_split = scenario[0].split(/,\s*$/),
-        path             = cached_split[0],
-        directive        = '';
-      }
-
-      return [this.trim(path), this.convert_directive(directive), this.trim(media_query)];
-    },
-
-    object : function (el) {
-      var raw_arr = this.parse_data_attr(el),
-          scenarios = [],
-          i = raw_arr.length;
-
-      if (i > 0) {
-        while (i--) {
-          var split = raw_arr[i].split(/\(([^\)]*?)(\))$/);
-
-          if (split.length > 1) {
-            var params = this.parse_scenario(split);
-            scenarios.push(params);
-          }
-        }
-      }
-
-      return this.store(el, scenarios);
-    },
-
-    store : function (el, scenarios) {
-      var uuid = this.random_str(),
-          current_uuid = el.data(this.add_namespace('uuid', true));
-
-      if (this.cache[current_uuid]) {
-        return this.cache[current_uuid];
-      }
-
-      el.attr(this.add_namespace('data-uuid'), uuid);
-
-      return this.cache[uuid] = scenarios;
-    },
-
-    trim : function (str) {
-
-      if (typeof str === 'string') {
-        return $.trim(str);
-      }
-
-      return str;
-    },
-
-    set_data_attr : function (init) {
-      if (init) {
-        if (this.namespace.length > 0) {
-          return this.namespace + '-' + this.settings.load_attr;
-        }
-
-        return this.settings.load_attr;
-      }
-
-      if (this.namespace.length > 0) {
-        return 'data-' + this.namespace + '-' + this.settings.load_attr;
-      }
-
-      return 'data-' + this.settings.load_attr;
-    },
-
-    parse_data_attr : function (el) {
-      var raw = el.attr(this.attr_name()).split(/\[(.*?)\]/),
-          i = raw.length,
-          output = [];
-
-      while (i--) {
-        if (raw[i].replace(/[\W\d]+/, '').length > 4) {
-          output.push(raw[i]);
-        }
-      }
-
-      return output;
-    },
-
-    reflow : function () {
-      this.load('images', true);
-      this.load('nodes', true);
-    }
-
-  };
-
-}(jQuery, window, window.document));
-;(function ($, window, document, undefined) {
-  'use strict';
-
-  Foundation.libs['magellan-expedition'] = {
-    name : 'magellan-expedition',
-
-    version : '5.5.1',
-
-    settings : {
-      active_class : 'active',
-      threshold : 0, // pixels from the top of the expedition for it to become fixes
-      destination_threshold : 20, // pixels from the top of destination for it to be considered active
-      throttle_delay : 30, // calculation throttling to increase framerate
-      fixed_top : 0, // top distance in pixels assigend to the fixed element on scroll
-      offset_by_height : true,  // whether to offset the destination by the expedition height. Usually you want this to be true, unless your expedition is on the side.
-      duration : 700, // animation duration time
-      easing : 'swing' // animation easing
-    },
-
-    init : function (scope, method, options) {
-      Foundation.inherit(this, 'throttle');
-      this.bindings(method, options);
-    },
-
-    events : function () {
-      var self = this,
-          S = self.S,
-          settings = self.settings;
-
-      // initialize expedition offset
-      self.set_expedition_position();
-
-      S(self.scope)
-        .off('.magellan')
-        .on('click.fndtn.magellan', '[' + self.add_namespace('data-magellan-arrival') + '] a[href^="#"]', function (e) {
-          e.preventDefault();
-          var expedition = $(this).closest('[' + self.attr_name() + ']'),
-              settings = expedition.data('magellan-expedition-init'),
-              hash = this.hash.split('#').join(''),
-              target = $('a[name="' + hash + '"]');
-
-          if (target.length === 0) {
-            target = $('#' + hash);
-
-          }
-
-          // Account for expedition height if fixed position
-          var scroll_top = target.offset().top - settings.destination_threshold + 1;
-          if (settings.offset_by_height) {
-            scroll_top = scroll_top - expedition.outerHeight();
-          }
-
-          $('html, body').stop().animate({
-            'scrollTop' : scroll_top
-          }, settings.duration, settings.easing, function () {
-            if (history.pushState) {
-              history.pushState(null, null, '#' + hash);
-            } else {
-              location.hash = '#' + hash;
-            }
-          });
-        })
-        .on('scroll.fndtn.magellan', self.throttle(this.check_for_arrivals.bind(this), settings.throttle_delay));
-
-      $(window)
-        .on('resize.fndtn.magellan', self.throttle(this.set_expedition_position.bind(this), settings.throttle_delay));
-    },
-
-    check_for_arrivals : function () {
-      var self = this;
-      self.update_arrivals();
-      self.update_expedition_positions();
-    },
-
-    set_expedition_position : function () {
-      var self = this;
-      $('[' + this.attr_name() + '=fixed]', self.scope).each(function (idx, el) {
-        var expedition = $(this),
-            settings = expedition.data('magellan-expedition-init'),
-            styles = expedition.attr('styles'), // save styles
-            top_offset, fixed_top;
-
-        expedition.attr('style', '');
-        top_offset = expedition.offset().top + settings.threshold;
-
-        //set fixed-top by attribute
-        fixed_top = parseInt(expedition.data('magellan-fixed-top'));
-        if (!isNaN(fixed_top)) {
-          self.settings.fixed_top = fixed_top;
-        }
-
-        expedition.data(self.data_attr('magellan-top-offset'), top_offset);
-        expedition.attr('style', styles);
-      });
-    },
-
-    update_expedition_positions : function () {
-      var self = this,
-          window_top_offset = $(window).scrollTop();
-
-      $('[' + this.attr_name() + '=fixed]', self.scope).each(function () {
-        var expedition = $(this),
-            settings = expedition.data('magellan-expedition-init'),
-            styles = expedition.attr('style'), // save styles
-            top_offset = expedition.data('magellan-top-offset');
-
-        //scroll to the top distance
-        if (window_top_offset + self.settings.fixed_top >= top_offset) {
-          // Placeholder allows height calculations to be consistent even when
-          // appearing to switch between fixed/non-fixed placement
-          var placeholder = expedition.prev('[' + self.add_namespace('data-magellan-expedition-clone') + ']');
-          if (placeholder.length === 0) {
-            placeholder = expedition.clone();
-            placeholder.removeAttr(self.attr_name());
-            placeholder.attr(self.add_namespace('data-magellan-expedition-clone'), '');
-            expedition.before(placeholder);
-          }
-          expedition.css({position :'fixed', top : settings.fixed_top}).addClass('fixed');
-        } else {
-          expedition.prev('[' + self.add_namespace('data-magellan-expedition-clone') + ']').remove();
-          expedition.attr('style', styles).css('position', '').css('top', '').removeClass('fixed');
-        }
-      });
-    },
-
-    update_arrivals : function () {
-      var self = this,
-          window_top_offset = $(window).scrollTop();
-
-      $('[' + this.attr_name() + ']', self.scope).each(function () {
-        var expedition = $(this),
-            settings = expedition.data(self.attr_name(true) + '-init'),
-            offsets = self.offsets(expedition, window_top_offset),
-            arrivals = expedition.find('[' + self.add_namespace('data-magellan-arrival') + ']'),
-            active_item = false;
-        offsets.each(function (idx, item) {
-          if (item.viewport_offset >= item.top_offset) {
-            var arrivals = expedition.find('[' + self.add_namespace('data-magellan-arrival') + ']');
-            arrivals.not(item.arrival).removeClass(settings.active_class);
-            item.arrival.addClass(settings.active_class);
-            active_item = true;
-            return true;
-          }
-        });
-
-        if (!active_item) {
-          arrivals.removeClass(settings.active_class);
-        }
-      });
-    },
-
-    offsets : function (expedition, window_offset) {
-      var self = this,
-          settings = expedition.data(self.attr_name(true) + '-init'),
-          viewport_offset = window_offset;
-
-      return expedition.find('[' + self.add_namespace('data-magellan-arrival') + ']').map(function (idx, el) {
-        var name = $(this).data(self.data_attr('magellan-arrival')),
-            dest = $('[' + self.add_namespace('data-magellan-destination') + '=' + name + ']');
-        if (dest.length > 0) {
-          var top_offset = dest.offset().top - settings.destination_threshold;
-          if (settings.offset_by_height) {
-            top_offset = top_offset - expedition.outerHeight();
-          }
-          top_offset = Math.floor(top_offset);
-          return {
-            destination : dest,
-            arrival : $(this),
-            top_offset : top_offset,
-            viewport_offset : viewport_offset
-          }
-        }
-      }).sort(function (a, b) {
-        if (a.top_offset < b.top_offset) {
-          return -1;
-        }
-        if (a.top_offset > b.top_offset) {
-          return 1;
-        }
-        return 0;
-      });
-    },
-
-    data_attr : function (str) {
-      if (this.namespace.length > 0) {
-        return this.namespace + '-' + str;
-      }
-
-      return str;
-    },
-
-    off : function () {
-      this.S(this.scope).off('.magellan');
-      this.S(window).off('.magellan');
-    },
-
-    reflow : function () {
-      var self = this;
-      // remove placeholder expeditions used for height calculation purposes
-      $('[' + self.add_namespace('data-magellan-expedition-clone') + ']', self.scope).remove();
-    }
-  };
-}(jQuery, window, window.document));
-;(function ($, window, document, undefined) {
-  'use strict';
-
-  Foundation.libs.accordion = {
-    name : 'accordion',
-
-    version : '5.5.1',
-
-    settings : {
-      content_class : 'content',
-      active_class : 'active',
-      multi_expand : false,
-      toggleable : true,
-      callback : function () {}
-    },
-
-    init : function (scope, method, options) {
-      this.bindings(method, options);
-    },
-
-    events : function () {
-      var self = this;
-      var S = this.S;
-      S(this.scope)
-      .off('.fndtn.accordion')
-      .on('click.fndtn.accordion', '[' + this.attr_name() + '] > .accordion-navigation > a', function (e) {
-        var accordion = S(this).closest('[' + self.attr_name() + ']'),
-            groupSelector = self.attr_name() + '=' + accordion.attr(self.attr_name()),
-            settings = accordion.data(self.attr_name(true) + '-init') || self.settings,
-            target = S('#' + this.href.split('#')[1]),
-            aunts = $('> .accordion-navigation', accordion),
-            siblings = aunts.children('.' + settings.content_class),
-            active_content = siblings.filter('.' + settings.active_class);
-
-        e.preventDefault();
-
-        if (accordion.attr(self.attr_name())) {
-          siblings = siblings.add('[' + groupSelector + '] dd > ' + '.' + settings.content_class);
-          aunts = aunts.add('[' + groupSelector + '] .accordion-navigation');
-        }
-
-        if (settings.toggleable && target.is(active_content)) {
-          target.parent('.accordion-navigation').toggleClass(settings.active_class, false);
-          target.toggleClass(settings.active_class, false);
-          settings.callback(target);
-          target.triggerHandler('toggled', [accordion]);
-          accordion.triggerHandler('toggled', [target]);
-          return;
-        }
-
-        if (!settings.multi_expand) {
-          siblings.removeClass(settings.active_class);
-          aunts.removeClass(settings.active_class);
-        }
-
-        target.addClass(settings.active_class).parent().addClass(settings.active_class);
-        settings.callback(target);
-        target.triggerHandler('toggled', [accordion]);
-        accordion.triggerHandler('toggled', [target]);
-      });
-    },
-
-    off : function () {},
-
-    reflow : function () {}
-  };
-}(jQuery, window, window.document));
-;(function ($, window, document, undefined) {
-  'use strict';
-
-  Foundation.libs.topbar = {
-    name : 'topbar',
-
-    version : '5.5.1',
-
-    settings : {
-      index : 0,
-      sticky_class : 'sticky',
-      custom_back_text : true,
-      back_text : 'Back',
-      mobile_show_parent_link : true,
-      is_hover : true,
-      scrolltop : true, // jump to top when sticky nav menu toggle is clicked
-      sticky_on : 'all'
-    },
-
-    init : function (section, method, options) {
-      Foundation.inherit(this, 'add_custom_rule register_media throttle');
-      var self = this;
-
-      self.register_media('topbar', 'foundation-mq-topbar');
-
-      this.bindings(method, options);
-
-      self.S('[' + this.attr_name() + ']', this.scope).each(function () {
-        var topbar = $(this),
-            settings = topbar.data(self.attr_name(true) + '-init'),
-            section = self.S('section, .top-bar-section', this);
-        topbar.data('index', 0);
-        var topbarContainer = topbar.parent();
-        if (topbarContainer.hasClass('fixed') || self.is_sticky(topbar, topbarContainer, settings) ) {
-          self.settings.sticky_class = settings.sticky_class;
-          self.settings.sticky_topbar = topbar;
-          topbar.data('height', topbarContainer.outerHeight());
-          topbar.data('stickyoffset', topbarContainer.offset().top);
-        } else {
-          topbar.data('height', topbar.outerHeight());
-        }
-
-        if (!settings.assembled) {
-          self.assemble(topbar);
-        }
-
-        if (settings.is_hover) {
-          self.S('.has-dropdown', topbar).addClass('not-click');
-        } else {
-          self.S('.has-dropdown', topbar).removeClass('not-click');
-        }
-
-        // Pad body when sticky (scrolled) or fixed.
-        self.add_custom_rule('.f-topbar-fixed { padding-top: ' + topbar.data('height') + 'px }');
-
-        if (topbarContainer.hasClass('fixed')) {
-          self.S('body').addClass('f-topbar-fixed');
-        }
-      });
-
-    },
-
-    is_sticky : function (topbar, topbarContainer, settings) {
-      var sticky     = topbarContainer.hasClass(settings.sticky_class);
-      var smallMatch = matchMedia(Foundation.media_queries.small).matches;
-      var medMatch   = matchMedia(Foundation.media_queries.medium).matches;
-      var lrgMatch   = matchMedia(Foundation.media_queries.large).matches;
-      
-       if (sticky && settings.sticky_on === 'all') {
-          return true;
-       }
-       if (sticky && this.small() && settings.sticky_on.indexOf('small') !== -1) {
-           if (smallMatch && !medMatch && !lrgMatch) { return true; }
-       }
-       if (sticky && this.medium() && settings.sticky_on.indexOf('medium') !== -1) {
-           if (smallMatch && medMatch && !lrgMatch) { return true; }
-       }
-       if (sticky && this.large() && settings.sticky_on.indexOf('large') !== -1) {
-           if (smallMatch && medMatch && lrgMatch) { return true; }
-       }
-
-       // fix for iOS browsers
-       if (sticky && navigator.userAgent.match(/(iPad|iPhone|iPod)/g)) {
-        return true;
-       }
-       return false;
-    },
-
-    toggle : function (toggleEl) {
-      var self = this,
-          topbar;
-
-      if (toggleEl) {
-        topbar = self.S(toggleEl).closest('[' + this.attr_name() + ']');
-      } else {
-        topbar = self.S('[' + this.attr_name() + ']');
-      }
-
-      var settings = topbar.data(this.attr_name(true) + '-init');
-
-      var section = self.S('section, .top-bar-section', topbar);
-
-      if (self.breakpoint()) {
-        if (!self.rtl) {
-          section.css({left : '0%'});
-          $('>.name', section).css({left : '100%'});
-        } else {
-          section.css({right : '0%'});
-          $('>.name', section).css({right : '100%'});
-        }
-
-        self.S('li.moved', section).removeClass('moved');
-        topbar.data('index', 0);
-
-        topbar
-          .toggleClass('expanded')
-          .css('height', '');
-      }
-
-      if (settings.scrolltop) {
-        if (!topbar.hasClass('expanded')) {
-          if (topbar.hasClass('fixed')) {
-            topbar.parent().addClass('fixed');
-            topbar.removeClass('fixed');
-            self.S('body').addClass('f-topbar-fixed');
-          }
-        } else if (topbar.parent().hasClass('fixed')) {
-          if (settings.scrolltop) {
-            topbar.parent().removeClass('fixed');
-            topbar.addClass('fixed');
-            self.S('body').removeClass('f-topbar-fixed');
-
-            window.scrollTo(0, 0);
-          } else {
-            topbar.parent().removeClass('expanded');
-          }
-        }
-      } else {
-        if (self.is_sticky(topbar, topbar.parent(), settings)) {
-          topbar.parent().addClass('fixed');
-        }
-
-        if (topbar.parent().hasClass('fixed')) {
-          if (!topbar.hasClass('expanded')) {
-            topbar.removeClass('fixed');
-            topbar.parent().removeClass('expanded');
-            self.update_sticky_positioning();
-          } else {
-            topbar.addClass('fixed');
-            topbar.parent().addClass('expanded');
-            self.S('body').addClass('f-topbar-fixed');
-          }
-        }
-      }
-    },
-
-    timer : null,
-
-    events : function (bar) {
-      var self = this,
-          S = this.S;
-
-      S(this.scope)
-        .off('.topbar')
-        .on('click.fndtn.topbar', '[' + this.attr_name() + '] .toggle-topbar', function (e) {
-          e.preventDefault();
-          self.toggle(this);
-        })
-        .on('click.fndtn.topbar', '.top-bar .top-bar-section li a[href^="#"],[' + this.attr_name() + '] .top-bar-section li a[href^="#"]', function (e) {
-            var li = $(this).closest('li');
-            if (self.breakpoint() && !li.hasClass('back') && !li.hasClass('has-dropdown')) {
-              self.toggle();
-            }
-        })
-        .on('click.fndtn.topbar', '[' + this.attr_name() + '] li.has-dropdown', function (e) {
-          var li = S(this),
-              target = S(e.target),
-              topbar = li.closest('[' + self.attr_name() + ']'),
-              settings = topbar.data(self.attr_name(true) + '-init');
-
-          if (target.data('revealId')) {
-            self.toggle();
-            return;
-          }
-
-          if (self.breakpoint()) {
-            return;
-          }
-
-          if (settings.is_hover && !Modernizr.touch) {
-            return;
-          }
-
-          e.stopImmediatePropagation();
-
-          if (li.hasClass('hover')) {
-            li
-              .removeClass('hover')
-              .find('li')
-              .removeClass('hover');
-
-            li.parents('li.hover')
-              .removeClass('hover');
-          } else {
-            li.addClass('hover');
-
-            $(li).siblings().removeClass('hover');
-
-            if (target[0].nodeName === 'A' && target.parent().hasClass('has-dropdown')) {
-              e.preventDefault();
-            }
-          }
-        })
-        .on('click.fndtn.topbar', '[' + this.attr_name() + '] .has-dropdown>a', function (e) {
-          if (self.breakpoint()) {
-
-            e.preventDefault();
-
-            var $this = S(this),
-                topbar = $this.closest('[' + self.attr_name() + ']'),
-                section = topbar.find('section, .top-bar-section'),
-                dropdownHeight = $this.next('.dropdown').outerHeight(),
-                $selectedLi = $this.closest('li');
-
-            topbar.data('index', topbar.data('index') + 1);
-            $selectedLi.addClass('moved');
-
-            if (!self.rtl) {
-              section.css({left : -(100 * topbar.data('index')) + '%'});
-              section.find('>.name').css({left : 100 * topbar.data('index') + '%'});
-            } else {
-              section.css({right : -(100 * topbar.data('index')) + '%'});
-              section.find('>.name').css({right : 100 * topbar.data('index') + '%'});
-            }
-
-            topbar.css('height', $this.siblings('ul').outerHeight(true) + topbar.data('height'));
-          }
-        });
-
-      S(window).off('.topbar').on('resize.fndtn.topbar', self.throttle(function () {
-          self.resize.call(self);
-      }, 50)).trigger('resize').trigger('resize.fndtn.topbar').load(function () {
-          // Ensure that the offset is calculated after all of the pages resources have loaded
-          S(this).trigger('resize.fndtn.topbar');
-      });
-
-      S('body').off('.topbar').on('click.fndtn.topbar', function (e) {
-        var parent = S(e.target).closest('li').closest('li.hover');
-
-        if (parent.length > 0) {
-          return;
-        }
-
-        S('[' + self.attr_name() + '] li.hover').removeClass('hover');
-      });
-
-      // Go up a level on Click
-      S(this.scope).on('click.fndtn.topbar', '[' + this.attr_name() + '] .has-dropdown .back', function (e) {
-        e.preventDefault();
-
-        var $this = S(this),
-            topbar = $this.closest('[' + self.attr_name() + ']'),
-            section = topbar.find('section, .top-bar-section'),
-            settings = topbar.data(self.attr_name(true) + '-init'),
-            $movedLi = $this.closest('li.moved'),
-            $previousLevelUl = $movedLi.parent();
-
-        topbar.data('index', topbar.data('index') - 1);
-
-        if (!self.rtl) {
-          section.css({left : -(100 * topbar.data('index')) + '%'});
-          section.find('>.name').css({left : 100 * topbar.data('index') + '%'});
-        } else {
-          section.css({right : -(100 * topbar.data('index')) + '%'});
-          section.find('>.name').css({right : 100 * topbar.data('index') + '%'});
-        }
-
-        if (topbar.data('index') === 0) {
-          topbar.css('height', '');
-        } else {
-          topbar.css('height', $previousLevelUl.outerHeight(true) + topbar.data('height'));
-        }
-
-        setTimeout(function () {
-          $movedLi.removeClass('moved');
-        }, 300);
-      });
-
-      // Show dropdown menus when their items are focused
-      S(this.scope).find('.dropdown a')
-        .focus(function () {
-          $(this).parents('.has-dropdown').addClass('hover');
-        })
-        .blur(function () {
-          $(this).parents('.has-dropdown').removeClass('hover');
-        });
-    },
-
-    resize : function () {
-      var self = this;
-      self.S('[' + this.attr_name() + ']').each(function () {
-        var topbar = self.S(this),
-            settings = topbar.data(self.attr_name(true) + '-init');
-
-        var stickyContainer = topbar.parent('.' + self.settings.sticky_class);
-        var stickyOffset;
-
-        if (!self.breakpoint()) {
-          var doToggle = topbar.hasClass('expanded');
-          topbar
-            .css('height', '')
-            .removeClass('expanded')
-            .find('li')
-            .removeClass('hover');
-
-            if (doToggle) {
-              self.toggle(topbar);
-            }
-        }
-
-        if (self.is_sticky(topbar, stickyContainer, settings)) {
-          if (stickyContainer.hasClass('fixed')) {
-            // Remove the fixed to allow for correct calculation of the offset.
-            stickyContainer.removeClass('fixed');
-
-            stickyOffset = stickyContainer.offset().top;
-            if (self.S(document.body).hasClass('f-topbar-fixed')) {
-              stickyOffset -= topbar.data('height');
-            }
-
-            topbar.data('stickyoffset', stickyOffset);
-            stickyContainer.addClass('fixed');
-          } else {
-            stickyOffset = stickyContainer.offset().top;
-            topbar.data('stickyoffset', stickyOffset);
-          }
-        }
-
-      });
-    },
-
-    breakpoint : function () {
-      return !matchMedia(Foundation.media_queries['topbar']).matches;
-    },
-
-    small : function () {
-      return matchMedia(Foundation.media_queries['small']).matches;
-    },
-
-    medium : function () {
-      return matchMedia(Foundation.media_queries['medium']).matches;
-    },
-
-    large : function () {
-      return matchMedia(Foundation.media_queries['large']).matches;
-    },
-
-    assemble : function (topbar) {
-      var self = this,
-          settings = topbar.data(this.attr_name(true) + '-init'),
-          section = self.S('section, .top-bar-section', topbar);
-
-      // Pull element out of the DOM for manipulation
-      section.detach();
-
-      self.S('.has-dropdown>a', section).each(function () {
-        var $link = self.S(this),
-            $dropdown = $link.siblings('.dropdown'),
-            url = $link.attr('href'),
-            $titleLi;
-
-        if (!$dropdown.find('.title.back').length) {
-
-          if (settings.mobile_show_parent_link == true && url) {
-            $titleLi = $('<li class="title back js-generated"><h5><a href="javascript:void(0)"></a></h5></li><li class="parent-link hide-for-large-up"><a class="parent-link js-generated" href="' + url + '">' + $link.html() +'</a></li>');
-          } else {
-            $titleLi = $('<li class="title back js-generated"><h5><a href="javascript:void(0)"></a></h5>');
-          }
-
-          // Copy link to subnav
-          if (settings.custom_back_text == true) {
-            $('h5>a', $titleLi).html(settings.back_text);
-          } else {
-            $('h5>a', $titleLi).html('« ' + $link.html());
-          }
-          $dropdown.prepend($titleLi);
-        }
-      });
-
-      // Put element back in the DOM
-      section.appendTo(topbar);
-
-      // check for sticky
-      this.sticky();
-
-      this.assembled(topbar);
-    },
-
-    assembled : function (topbar) {
-      topbar.data(this.attr_name(true), $.extend({}, topbar.data(this.attr_name(true)), {assembled : true}));
-    },
-
-    height : function (ul) {
-      var total = 0,
-          self = this;
-
-      $('> li', ul).each(function () {
-        total += self.S(this).outerHeight(true);
-      });
-
-      return total;
-    },
-
-    sticky : function () {
-      var self = this;
-
-      this.S(window).on('scroll', function () {
-        self.update_sticky_positioning();
-      });
-    },
-
-    update_sticky_positioning : function () {
-      var klass = '.' + this.settings.sticky_class,
-          $window = this.S(window),
-          self = this;
-
-      if (self.settings.sticky_topbar && self.is_sticky(this.settings.sticky_topbar, this.settings.sticky_topbar.parent(), this.settings)) {
-        var distance = this.settings.sticky_topbar.data('stickyoffset');
-        if (!self.S(klass).hasClass('expanded')) {
-          if ($window.scrollTop() > (distance)) {
-            if (!self.S(klass).hasClass('fixed')) {
-              self.S(klass).addClass('fixed');
-              self.S('body').addClass('f-topbar-fixed');
-            }
-          } else if ($window.scrollTop() <= distance) {
-            if (self.S(klass).hasClass('fixed')) {
-              self.S(klass).removeClass('fixed');
-              self.S('body').removeClass('f-topbar-fixed');
-            }
-          }
-        }
-      }
-    },
-
-    off : function () {
-      this.S(this.scope).off('.fndtn.topbar');
-      this.S(window).off('.fndtn.topbar');
-    },
-
-    reflow : function () {}
-  };
-}(jQuery, window, window.document));
-;(function ($, window, document, undefined) {
-  'use strict';
-
-  Foundation.libs.tab = {
-    name : 'tab',
-
-    version : '5.5.1',
-
-    settings : {
-      active_class : 'active',
-      callback : function () {},
-      deep_linking : false,
-      scroll_to_content : true,
-      is_hover : false
-    },
-
-    default_tab_hashes : [],
-
-    init : function (scope, method, options) {
-      var self = this,
-          S = this.S;
-
-      this.bindings(method, options);
-
-      // store the initial href, which is used to allow correct behaviour of the
-      // browser back button when deep linking is turned on.
-      self.entry_location = window.location.href;
-
-      this.handle_location_hash_change();
-
-      // Store the default active tabs which will be referenced when the
-      // location hash is absent, as in the case of navigating the tabs and
-      // returning to the first viewing via the browser Back button.
-      S('[' + this.attr_name() + '] > .active > a', this.scope).each(function () {
-        self.default_tab_hashes.push(this.hash);
-      });
-    },
-
-    events : function () {
-      var self = this,
-          S = this.S;
-
-      var usual_tab_behavior =  function (e) {
-          var settings = S(this).closest('[' + self.attr_name() + ']').data(self.attr_name(true) + '-init');
-          if (!settings.is_hover || Modernizr.touch) {
-            e.preventDefault();
-            e.stopPropagation();
-            self.toggle_active_tab(S(this).parent());
-          }
-        };
-
-      S(this.scope)
-        .off('.tab')
-        // Click event: tab title
-        .on('focus.fndtn.tab', '[' + this.attr_name() + '] > * > a', usual_tab_behavior )
-        .on('click.fndtn.tab', '[' + this.attr_name() + '] > * > a', usual_tab_behavior )
-        // Hover event: tab title
-        .on('mouseenter.fndtn.tab', '[' + this.attr_name() + '] > * > a', function (e) {
-          var settings = S(this).closest('[' + self.attr_name() + ']').data(self.attr_name(true) + '-init');
-          if (settings.is_hover) {
-            self.toggle_active_tab(S(this).parent());
-          }
-        });
-
-      // Location hash change event
-      S(window).on('hashchange.fndtn.tab', function (e) {
-        e.preventDefault();
-        self.handle_location_hash_change();
-      });
-    },
-
-    handle_location_hash_change : function () {
-
-      var self = this,
-          S = this.S;
-
-      S('[' + this.attr_name() + ']', this.scope).each(function () {
-        var settings = S(this).data(self.attr_name(true) + '-init');
-        if (settings.deep_linking) {
-          // Match the location hash to a label
-          var hash;
-          if (settings.scroll_to_content) {
-            hash = self.scope.location.hash;
-          } else {
-            // prefix the hash to prevent anchor scrolling
-            hash = self.scope.location.hash.replace('fndtn-', '');
-          }
-          if (hash != '') {
-            // Check whether the location hash references a tab content div or
-            // another element on the page (inside or outside the tab content div)
-            var hash_element = S(hash);
-            if (hash_element.hasClass('content') && hash_element.parent().hasClass('tabs-content')) {
-              // Tab content div
-              self.toggle_active_tab($('[' + self.attr_name() + '] > * > a[href=' + hash + ']').parent());
-            } else {
-              // Not the tab content div. If inside the tab content, find the
-              // containing tab and toggle it as active.
-              var hash_tab_container_id = hash_element.closest('.content').attr('id');
-              if (hash_tab_container_id != undefined) {
-                self.toggle_active_tab($('[' + self.attr_name() + '] > * > a[href=#' + hash_tab_container_id + ']').parent(), hash);
-              }
-            }
-          } else {
-            // Reference the default tab hashes which were initialized in the init function
-            for (var ind = 0; ind < self.default_tab_hashes.length; ind++) {
-              self.toggle_active_tab($('[' + self.attr_name() + '] > * > a[href=' + self.default_tab_hashes[ind] + ']').parent());
-            }
-          }
-        }
-       });
-     },
-
-    toggle_active_tab : function (tab, location_hash) {
-      var self = this,
-          S = self.S,
-          tabs = tab.closest('[' + this.attr_name() + ']'),
-          tab_link = tab.find('a'),
-          anchor = tab.children('a').first(),
-          target_hash = '#' + anchor.attr('href').split('#')[1],
-          target = S(target_hash),
-          siblings = tab.siblings(),
-          settings = tabs.data(this.attr_name(true) + '-init'),
-          interpret_keyup_action = function (e) {
-            // Light modification of Heydon Pickering's Practical ARIA Examples: http://heydonworks.com/practical_aria_examples/js/a11y.js
-
-            // define current, previous and next (possible) tabs
-
-            var $original = $(this);
-            var $prev = $(this).parents('li').prev().children('[role="tab"]');
-            var $next = $(this).parents('li').next().children('[role="tab"]');
-            var $target;
-
-            // find the direction (prev or next)
-
-            switch (e.keyCode) {
-              case 37:
-                $target = $prev;
-                break;
-              case 39:
-                $target = $next;
-                break;
-              default:
-                $target = false
-                  break;
-            }
-
-            if ($target.length) {
-              $original.attr({
-                'tabindex' : '-1',
-                'aria-selected' : null
-              });
-              $target.attr({
-                'tabindex' : '0',
-                'aria-selected' : true
-              }).focus();
-            }
-
-            // Hide panels
-
-            $('[role="tabpanel"]')
-              .attr('aria-hidden', 'true');
-
-            // Show panel which corresponds to target
-
-            $('#' + $(document.activeElement).attr('href').substring(1))
-              .attr('aria-hidden', null);
-
-          },
-          go_to_hash = function(hash) {
-            // This function allows correct behaviour of the browser's back button when deep linking is enabled. Without it
-            // the user would get continually redirected to the default hash.
-            var is_entry_location = window.location.href === self.entry_location,
-                default_hash = settings.scroll_to_content ? self.default_tab_hashes[0] : is_entry_location ? window.location.hash :'fndtn-' + self.default_tab_hashes[0].replace('#', '')
-
-            if (!(is_entry_location && hash === default_hash)) {
-              window.location.hash = hash;
-            }
-          };
-
-      // allow usage of data-tab-content attribute instead of href
-      if (S(this).data(this.data_attr('tab-content'))) {
-        target_hash = '#' + S(this).data(this.data_attr('tab-content')).split('#')[1];
-        target = S(target_hash);
-      }
-
-      if (settings.deep_linking) {
-
-        if (settings.scroll_to_content) {
-
-          // retain current hash to scroll to content
-          go_to_hash(location_hash || target_hash);
-
-          if (location_hash == undefined || location_hash == target_hash) {
-            tab.parent()[0].scrollIntoView();
-          } else {
-            S(target_hash)[0].scrollIntoView();
-          }
-        } else {
-          // prefix the hashes so that the browser doesn't scroll down
-          if (location_hash != undefined) {
-            go_to_hash('fndtn-' + location_hash.replace('#', ''));
-          } else {
-            go_to_hash('fndtn-' + target_hash.replace('#', ''));
-          }
-        }
-      }
-
-      // WARNING: The activation and deactivation of the tab content must
-      // occur after the deep linking in order to properly refresh the browser
-      // window (notably in Chrome).
-      // Clean up multiple attr instances to done once
-      tab.addClass(settings.active_class).triggerHandler('opened');
-      tab_link.attr({'aria-selected' : 'true',  tabindex : 0});
-      siblings.removeClass(settings.active_class)
-      siblings.find('a').attr({'aria-selected' : 'false',  tabindex : -1});
-      target.siblings().removeClass(settings.active_class).attr({'aria-hidden' : 'true',  tabindex : -1});
-      target.addClass(settings.active_class).attr('aria-hidden', 'false').removeAttr('tabindex');
-      settings.callback(tab);
-      target.triggerHandler('toggled', [tab]);
-      tabs.triggerHandler('toggled', [target]);
-
-      tab_link.off('keydown').on('keydown', interpret_keyup_action );
-    },
-
-    data_attr : function (str) {
-      if (this.namespace.length > 0) {
-        return this.namespace + '-' + str;
-      }
-
-      return str;
-    },
-
-    off : function () {},
-
-    reflow : function () {}
-  };
-}(jQuery, window, window.document));
-;(function ($, window, document, undefined) {
-  'use strict';
-
-  Foundation.libs.abide = {
-    name : 'abide',
-
-    version : '5.5.1',
-
-    settings : {
-      live_validate : true,
-      validate_on_blur : true,
-      focus_on_invalid : true,
-      error_labels : true, // labels with a for="inputId" will recieve an `error` class
-      error_class : 'error',
-      timeout : 1000,
-      patterns : {
-        alpha : /^[a-zA-Z]+$/,
-        alpha_numeric : /^[a-zA-Z0-9]+$/,
-        integer : /^[-+]?\d+$/,
-        number : /^[-+]?\d*(?:[\.\,]\d+)?$/,
-
-        // amex, visa, diners
-        card : /^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11})$/,
-        cvv : /^([0-9]){3,4}$/,
-
-        // http://www.whatwg.org/specs/web-apps/current-work/multipage/states-of-the-type-attribute.html#valid-e-mail-address
-        email : /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+$/,
-
-        url : /^(https?|ftp|file|ssh):\/\/(((([a-zA-Z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-zA-Z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-zA-Z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-zA-Z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-zA-Z]|\d|[ [...]
-        // abc.de
-        domain : /^([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,8}$/,
-
-        datetime : /^([0-2][0-9]{3})\-([0-1][0-9])\-([0-3][0-9])T([0-5][0-9])\:([0-5][0-9])\:([0-5][0-9])(Z|([\-\+]([0-1][0-9])\:00))$/,
-        // YYYY-MM-DD
-        date : /(?:19|20)[0-9]{2}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-9])|(?:(?!02)(?:0[1-9]|1[0-2])-(?:30))|(?:(?:0[13578]|1[02])-31))$/,
-        // HH:MM:SS
-        time : /^(0[0-9]|1[0-9]|2[0-3])(:[0-5][0-9]){2}$/,
-        dateISO : /^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}$/,
-        // MM/DD/YYYY
-        month_day_year : /^(0[1-9]|1[012])[- \/.](0[1-9]|[12][0-9]|3[01])[- \/.]\d{4}$/,
-        // DD/MM/YYYY
-        day_month_year : /^(0[1-9]|[12][0-9]|3[01])[- \/.](0[1-9]|1[012])[- \/.]\d{4}$/,
-
-        // #FFF or #FFFFFF
-        color : /^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/
-      },
-      validators : {
-        equalTo : function (el, required, parent) {
-          var from  = document.getElementById(el.getAttribute(this.add_namespace('data-equalto'))).value,
-              to    = el.value,
-              valid = (from === to);
-
-          return valid;
-        }
-      }
-    },
-
-    timer : null,
-
-    init : function (scope, method, options) {
-      this.bindings(method, options);
-    },
-
-    events : function (scope) {
-      var self = this,
-          form = self.S(scope).attr('novalidate', 'novalidate'),
-          settings = form.data(this.attr_name(true) + '-init') || {};
-
-      this.invalid_attr = this.add_namespace('data-invalid');
-
-      form
-        .off('.abide')
-        .on('submit.fndtn.abide validate.fndtn.abide', function (e) {
-          var is_ajax = /ajax/i.test(self.S(this).attr(self.attr_name()));
-          return self.validate(self.S(this).find('input, textarea, select').get(), e, is_ajax);
-        })
-        .on('reset', function () {
-          return self.reset($(this));
-        })
-        .find('input, textarea, select')
-          .off('.abide')
-          .on('blur.fndtn.abide change.fndtn.abide', function (e) {
-            if (settings.validate_on_blur === true) {
-              self.validate([this], e);
-            }
-          })
-          .on('keydown.fndtn.abide', function (e) {
-            if (settings.live_validate === true && e.which != 9) {
-              clearTimeout(self.timer);
-              self.timer = setTimeout(function () {
-                self.validate([this], e);
-              }.bind(this), settings.timeout);
-            }
-          });
-    },
-
-    reset : function (form) {
-      form.removeAttr(this.invalid_attr);
-      $(this.invalid_attr, form).removeAttr(this.invalid_attr);
-      $('.' + this.settings.error_class, form).not('small').removeClass(this.settings.error_class);
-    },
-
-    validate : function (els, e, is_ajax) {
-      var validations = this.parse_patterns(els),
-          validation_count = validations.length,
-          form = this.S(els[0]).closest('form'),
-          submit_event = /submit/.test(e.type);
-
-      // Has to count up to make sure the focus gets applied to the top error
-      for (var i = 0; i < validation_count; i++) {
-        if (!validations[i] && (submit_event || is_ajax)) {
-          if (this.settings.focus_on_invalid) {
-            els[i].focus();
-          }
-          form.trigger('invalid').trigger('invalid.fndtn.abide');
-          this.S(els[i]).closest('form').attr(this.invalid_attr, '');
-          return false;
-        }
-      }
-
-      if (submit_event || is_ajax) {
-        form.trigger('valid').trigger('valid.fndtn.abide');
-      }
-
-      form.removeAttr(this.invalid_attr);
-
-      if (is_ajax) {
-        return false;
-      }
-
-      return true;
-    },
-
-    parse_patterns : function (els) {
-      var i = els.length,
-          el_patterns = [];
-
-      while (i--) {
-        el_patterns.push(this.pattern(els[i]));
-      }
-
-      return this.check_validation_and_apply_styles(el_patterns);
-    },
-
-    pattern : function (el) {
-      var type = el.getAttribute('type'),
-          required = typeof el.getAttribute('required') === 'string';
-
-      var pattern = el.getAttribute('pattern') || '';
-
-      if (this.settings.patterns.hasOwnProperty(pattern) && pattern.length > 0) {
-        return [el, this.settings.patterns[pattern], required];
-      } else if (pattern.length > 0) {
-        return [el, new RegExp(pattern), required];
-      }
-
-      if (this.settings.patterns.hasOwnProperty(type)) {
-        return [el, this.settings.patterns[type], required];
-      }
-
-      pattern = /.*/;
-
-      return [el, pattern, required];
-    },
-
-    // TODO: Break this up into smaller methods, getting hard to read.
-    check_validation_and_apply_styles : function (el_patterns) {
-      var i = el_patterns.length,
-          validations = [],
-          form = this.S(el_patterns[0][0]).closest('[data-' + this.attr_name(true) + ']'),
-          settings = form.data(this.attr_name(true) + '-init') || {};
-      while (i--) {
-        var el = el_patterns[i][0],
-            required = el_patterns[i][2],
-            value = el.value.trim(),
-            direct_parent = this.S(el).parent(),
-            validator = el.getAttribute(this.add_namespace('data-abide-validator')),
-            is_radio = el.type === 'radio',
-            is_checkbox = el.type === 'checkbox',
-            label = this.S('label[for="' + el.getAttribute('id') + '"]'),
-            valid_length = (required) ? (el.value.length > 0) : true,
-            el_validations = [];
-
-        var parent, valid;
-
-        // support old way to do equalTo validations
-        if (el.getAttribute(this.add_namespace('data-equalto'))) { validator = 'equalTo' }
-
-        if (!direct_parent.is('label')) {
-          parent = direct_parent;
-        } else {
-          parent = direct_parent.parent();
-        }
-
-        if (validator) {
-          valid = this.settings.validators[validator].apply(this, [el, required, parent]);
-          el_validations.push(valid);
-        }
-
-        if (is_radio && required) {
-          el_validations.push(this.valid_radio(el, required));
-        } else if (is_checkbox && required) {
-          el_validations.push(this.valid_checkbox(el, required));
-        } else {
-
-          if (el_patterns[i][1].test(value) && valid_length ||
-            !required && el.value.length < 1 || $(el).attr('disabled')) {
-            el_validations.push(true);
-          } else {
-            el_validations.push(false);
-          }
-
-          el_validations = [el_validations.every(function (valid) {return valid;})];
-
-          if (el_validations[0]) {
-            this.S(el).removeAttr(this.invalid_attr);
-            el.setAttribute('aria-invalid', 'false');
-            el.removeAttribute('aria-describedby');
-            parent.removeClass(this.settings.error_class);
-            if (label.length > 0 && this.settings.error_labels) {
-              label.removeClass(this.settings.error_class).removeAttr('role');
-            }
-            $(el).triggerHandler('valid');
-          } else {
-            this.S(el).attr(this.invalid_attr, '');
-            el.setAttribute('aria-invalid', 'true');
-
-            // Try to find the error associated with the input
-            var errorElem = parent.find('small.' + this.settings.error_class, 'span.' + this.settings.error_class);
-            var errorID = errorElem.length > 0 ? errorElem[0].id : '';
-            if (errorID.length > 0) {
-              el.setAttribute('aria-describedby', errorID);
-            }
-
-            // el.setAttribute('aria-describedby', $(el).find('.error')[0].id);
-            parent.addClass(this.settings.error_class);
-            if (label.length > 0 && this.settings.error_labels) {
-              label.addClass(this.settings.error_class).attr('role', 'alert');
-            }
-            $(el).triggerHandler('invalid');
-          }
-        }
-        validations.push(el_validations[0]);
-      }
-      validations = [validations.every(function (valid) {return valid;})];
-      return validations;
-    },
-
-    valid_checkbox : function (el, required) {
-      var el = this.S(el),
-          valid = (el.is(':checked') || !required || el.get(0).getAttribute('disabled'));
-
-      if (valid) {
-        el.removeAttr(this.invalid_attr).parent().removeClass(this.settings.error_class);
-      } else {
-        el.attr(this.invalid_attr, '').parent().addClass(this.settings.error_class);
-      }
-
-      return valid;
-    },
-
-    valid_radio : function (el, required) {
-      var name = el.getAttribute('name'),
-          group = this.S(el).closest('[data-' + this.attr_name(true) + ']').find("[name='" + name + "']"),
-          count = group.length,
-          valid = false,
-          disabled = false;
-
-      // Has to count up to make sure the focus gets applied to the top error
-        for (var i=0; i < count; i++) {
-            if( group[i].getAttribute('disabled') ){
-                disabled=true;
-                valid=true;
-            } else {
-                if (group[i].checked){
-                    valid = true;
-                } else {
-                    if( disabled ){
-                        valid = false;
-                    }
-                }
-            }
-        }
-
-      // Has to count up to make sure the focus gets applied to the top error
-      for (var i = 0; i < count; i++) {
-        if (valid) {
-          this.S(group[i]).removeAttr(this.invalid_attr).parent().removeClass(this.settings.error_class);
-        } else {
-          this.S(group[i]).attr(this.invalid_attr, '').parent().addClass(this.settings.error_class);
-        }
-      }
-
-      return valid;
-    },
-
-    valid_equal : function (el, required, parent) {
-      var from  = document.getElementById(el.getAttribute(this.add_namespace('data-equalto'))).value,
-          to    = el.value,
-          valid = (from === to);
-
-      if (valid) {
-        this.S(el).removeAttr(this.invalid_attr);
-        parent.removeClass(this.settings.error_class);
-        if (label.length > 0 && settings.error_labels) {
-          label.removeClass(this.settings.error_class);
-        }
-      } else {
-        this.S(el).attr(this.invalid_attr, '');
-        parent.addClass(this.settings.error_class);
-        if (label.length > 0 && settings.error_labels) {
-          label.addClass(this.settings.error_class);
-        }
-      }
-
-      return valid;
-    },
-
-    valid_oneof : function (el, required, parent, doNotValidateOthers) {
-      var el = this.S(el),
-        others = this.S('[' + this.add_namespace('data-oneof') + ']'),
-        valid = others.filter(':checked').length > 0;
-
-      if (valid) {
-        el.removeAttr(this.invalid_attr).parent().removeClass(this.settings.error_class);
-      } else {
-        el.attr(this.invalid_attr, '').parent().addClass(this.settings.error_class);
-      }
-
-      if (!doNotValidateOthers) {
-        var _this = this;
-        others.each(function () {
-          _this.valid_oneof.call(_this, this, null, null, true);
-        });
-      }
-
-      return valid;
-    }
-  };
-}(jQuery, window, window.document));
-;(function ($, window, document, undefined) {
-  'use strict';
-
-  Foundation.libs.tooltip = {
-    name : 'tooltip',
-
-    version : '5.5.1',
-
-    settings : {
-      additional_inheritable_classes : [],
-      tooltip_class : '.tooltip',
-      append_to : 'body',
-      touch_close_text : 'Tap To Close',
-      disable_for_touch : false,
-      hover_delay : 200,
-      show_on : 'all',
-      tip_template : function (selector, content) {
-        return '<span data-selector="' + selector + '" id="' + selector + '" class="'
-          + Foundation.libs.tooltip.settings.tooltip_class.substring(1)
-          + '" role="tooltip">' + content + '<span class="nub"></span></span>';
-      }
-    },
-
-    cache : {},
-
-    init : function (scope, method, options) {
-      Foundation.inherit(this, 'random_str');
-      this.bindings(method, options);
-    },
-
-    should_show : function (target, tip) {
-      var settings = $.extend({}, this.settings, this.data_options(target));
-
-      if (settings.show_on === 'all') {
-        return true;
-      } else if (this.small() && settings.show_on === 'small') {
-        return true;
-      } else if (this.medium() && settings.show_on === 'medium') {
-        return true;
-      } else if (this.large() && settings.show_on === 'large') {
-        return true;
-      }
-      return false;
-    },
-
-    medium : function () {
-      return matchMedia(Foundation.media_queries['medium']).matches;
-    },
-
-    large : function () {
-      return matchMedia(Foundation.media_queries['large']).matches;
-    },
-
-    events : function (instance) {
-      var self = this,
-          S = self.S;
-
-      self.create(this.S(instance));
-
-      $(this.scope)
-        .off('.tooltip')
-        .on('mouseenter.fndtn.tooltip mouseleave.fndtn.tooltip touchstart.fndtn.tooltip MSPointerDown.fndtn.tooltip',
-          '[' + this.attr_name() + ']', function (e) {
-          var $this = S(this),
-              settings = $.extend({}, self.settings, self.data_options($this)),
-              is_touch = false;
-
-          if (Modernizr.touch && /touchstart|MSPointerDown/i.test(e.type) && S(e.target).is('a')) {
-            return false;
-          }
-
-          if (/mouse/i.test(e.type) && self.ie_touch(e)) {
-            return false;
-          }
-
-          if ($this.hasClass('open')) {
-            if (Modernizr.touch && /touchstart|MSPointerDown/i.test(e.type)) {
-              e.preventDefault();
-            }
-            self.hide($this);
-          } else {
-            if (settings.disable_for_touch && Modernizr.touch && /touchstart|MSPointerDown/i.test(e.type)) {
-              return;
-            } else if (!settings.disable_for_touch && Modernizr.touch && /touchstart|MSPointerDown/i.test(e.type)) {
-              e.preventDefault();
-              S(settings.tooltip_class + '.open').hide();
-              is_touch = true;
-            }
-
-            if (/enter|over/i.test(e.type)) {
-              this.timer = setTimeout(function () {
-                var tip = self.showTip($this);
-              }.bind(this), self.settings.hover_delay);
-            } else if (e.type === 'mouseout' || e.type === 'mouseleave') {
-              clearTimeout(this.timer);
-              self.hide($this);
-            } else {
-              self.showTip($this);
-            }
-          }
-        })
-        .on('mouseleave.fndtn.tooltip touchstart.fndtn.tooltip MSPointerDown.fndtn.tooltip', '[' + this.attr_name() + '].open', function (e) {
-          if (/mouse/i.test(e.type) && self.ie_touch(e)) {
-            return false;
-          }
-
-          if ($(this).data('tooltip-open-event-type') == 'touch' && e.type == 'mouseleave') {
-            return;
-          } else if ($(this).data('tooltip-open-event-type') == 'mouse' && /MSPointerDown|touchstart/i.test(e.type)) {
-            self.convert_to_touch($(this));
-          } else {
-            self.hide($(this));
-          }
-        })
-        .on('DOMNodeRemoved DOMAttrModified', '[' + this.attr_name() + ']:not(a)', function (e) {
-          self.hide(S(this));
-        });
-    },
-
-    ie_touch : function (e) {
-      // How do I distinguish between IE11 and Windows Phone 8?????
-      return false;
-    },
-
-    showTip : function ($target) {
-      var $tip = this.getTip($target);
-      if (this.should_show($target, $tip)) {
-        return this.show($target);
-      }
-      return;
-    },
-
-    getTip : function ($target) {
-      var selector = this.selector($target),
-          settings = $.extend({}, this.settings, this.data_options($target)),
-          tip = null;
-
-      if (selector) {
-        tip = this.S('span[data-selector="' + selector + '"]' + settings.tooltip_class);
-      }
-
-      return (typeof tip === 'object') ? tip : false;
-    },
-
-    selector : function ($target) {
-      var id = $target.attr('id'),
-          dataSelector = $target.attr(this.attr_name()) || $target.attr('data-selector');
-
-      if ((id && id.length < 1 || !id) && typeof dataSelector != 'string') {
-        dataSelector = this.random_str(6);
-        $target
-          .attr('data-selector', dataSelector)
-          .attr('aria-describedby', dataSelector);
-      }
-
-      return (id && id.length > 0) ? id : dataSelector;
-    },
-
-    create : function ($target) {
-      var self = this,
-          settings = $.extend({}, this.settings, this.data_options($target)),
-          tip_template = this.settings.tip_template;
-
-      if (typeof settings.tip_template === 'string' && window.hasOwnProperty(settings.tip_template)) {
-        tip_template = window[settings.tip_template];
-      }
-
-      var $tip = $(tip_template(this.selector($target), $('<div></div>').html($target.attr('title')).html())),
-          classes = this.inheritable_classes($target);
-
-      $tip.addClass(classes).appendTo(settings.append_to);
-
-      if (Modernizr.touch) {
-        $tip.append('<span class="tap-to-close">' + settings.touch_close_text + '</span>');
-        $tip.on('touchstart.fndtn.tooltip MSPointerDown.fndtn.tooltip', function (e) {
-          self.hide($target);
-        });
-      }
-
-      $target.removeAttr('title').attr('title', '');
-    },
-
-    reposition : function (target, tip, classes) {
-      var width, nub, nubHeight, nubWidth, column, objPos;
-
-      tip.css('visibility', 'hidden').show();
-
-      width = target.data('width');
-      nub = tip.children('.nub');
-      nubHeight = nub.outerHeight();
-      nubWidth = nub.outerHeight();
-
-      if (this.small()) {
-        tip.css({'width' : '100%'});
-      } else {
-        tip.css({'width' : (width) ? width : 'auto'});
-      }
-
-      objPos = function (obj, top, right, bottom, left, width) {
-        return obj.css({
-          'top' : (top) ? top : 'auto',
-          'bottom' : (bottom) ? bottom : 'auto',
-          'left' : (left) ? left : 'auto',
-          'right' : (right) ? right : 'auto'
-        }).end();
-      };
-
-      objPos(tip, (target.offset().top + target.outerHeight() + 10), 'auto', 'auto', target.offset().left);
-
-      if (this.small()) {
-        objPos(tip, (target.offset().top + target.outerHeight() + 10), 'auto', 'auto', 12.5, $(this.scope).width());
-        tip.addClass('tip-override');
-        objPos(nub, -nubHeight, 'auto', 'auto', target.offset().left);
-      } else {
-        var left = target.offset().left;
-        if (Foundation.rtl) {
-          nub.addClass('rtl');
-          left = target.offset().left + target.outerWidth() - tip.outerWidth();
-        }
-        objPos(tip, (target.offset().top + target.outerHeight() + 10), 'auto', 'auto', left);
-        tip.removeClass('tip-override');
-        if (classes && classes.indexOf('tip-top') > -1) {
-          if (Foundation.rtl) {
-            nub.addClass('rtl');
-          }
-          objPos(tip, (target.offset().top - tip.outerHeight()), 'auto', 'auto', left)
-            .removeClass('tip-override');
-        } else if (classes && classes.indexOf('tip-left') > -1) {
-          objPos(tip, (target.offset().top + (target.outerHeight() / 2) - (tip.outerHeight() / 2)), 'auto', 'auto', (target.offset().left - tip.outerWidth() - nubHeight))
-            .removeClass('tip-override');
-          nub.removeClass('rtl');
-        } else if (classes && classes.indexOf('tip-right') > -1) {
-          objPos(tip, (target.offset().top + (target.outerHeight() / 2) - (tip.outerHeight() / 2)), 'auto', 'auto', (target.offset().left + target.outerWidth() + nubHeight))
-            .removeClass('tip-override');
-          nub.removeClass('rtl');
-        }
-      }
-
-      tip.css('visibility', 'visible').hide();
-    },
-
-    small : function () {
-      return matchMedia(Foundation.media_queries.small).matches &&
-        !matchMedia(Foundation.media_queries.medium).matches;
-    },
-
-    inheritable_classes : function ($target) {
-      var settings = $.extend({}, this.settings, this.data_options($target)),
-          inheritables = ['tip-top', 'tip-left', 'tip-bottom', 'tip-right', 'radius', 'round'].concat(settings.additional_inheritable_classes),
-          classes = $target.attr('class'),
-          filtered = classes ? $.map(classes.split(' '), function (el, i) {
-            if ($.inArray(el, inheritables) !== -1) {
-              return el;
-            }
-          }).join(' ') : '';
-
-      return $.trim(filtered);
-    },
-
-    convert_to_touch : function ($target) {
-      var self = this,
-          $tip = self.getTip($target),
-          settings = $.extend({}, self.settings, self.data_options($target));
-
-      if ($tip.find('.tap-to-close').length === 0) {
-        $tip.append('<span class="tap-to-close">' + settings.touch_close_text + '</span>');
-        $tip.on('click.fndtn.tooltip.tapclose touchstart.fndtn.tooltip.tapclose MSPointerDown.fndtn.tooltip.tapclose', function (e) {
-          self.hide($target);
-        });
-      }
-
-      $target.data('tooltip-open-event-type', 'touch');
-    },
-
-    show : function ($target) {
-      var $tip = this.getTip($target);
-
-      if ($target.data('tooltip-open-event-type') == 'touch') {
-        this.convert_to_touch($target);
-      }
-
-      this.reposition($target, $tip, $target.attr('class'));
-      $target.addClass('open');
-      $tip.fadeIn(150);
-    },
-
-    hide : function ($target) {
-      var $tip = this.getTip($target);
-
-      $tip.fadeOut(150, function () {
-        $tip.find('.tap-to-close').remove();
-        $tip.off('click.fndtn.tooltip.tapclose MSPointerDown.fndtn.tapclose');
-        $target.removeClass('open');
-      });
-    },
-
-    off : function () {
-      var self = this;
-      this.S(this.scope).off('.fndtn.tooltip');
-      this.S(this.settings.tooltip_class).each(function (i) {
-        $('[' + self.attr_name() + ']').eq(i).attr('title', $(this).text());
-      }).remove();
-    },
-
-    reflow : function () {}
-  };
-}(jQuery, window, window.document));
diff --git a/3dmol/js/foundation/foundation.abide.js b/3dmol/js/foundation/foundation.abide.js
deleted file mode 100644
index 9eefe32..0000000
--- a/3dmol/js/foundation/foundation.abide.js
+++ /dev/null
@@ -1,340 +0,0 @@
-;(function ($, window, document, undefined) {
-  'use strict';
-
-  Foundation.libs.abide = {
-    name : 'abide',
-
-    version : '5.5.1',
-
-    settings : {
-      live_validate : true,
-      validate_on_blur : true,
-      focus_on_invalid : true,
-      error_labels : true, // labels with a for="inputId" will recieve an `error` class
-      error_class : 'error',
-      timeout : 1000,
-      patterns : {
-        alpha : /^[a-zA-Z]+$/,
-        alpha_numeric : /^[a-zA-Z0-9]+$/,
-        integer : /^[-+]?\d+$/,
-        number : /^[-+]?\d*(?:[\.\,]\d+)?$/,
-
-        // amex, visa, diners
-        card : /^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11})$/,
-        cvv : /^([0-9]){3,4}$/,
-
-        // http://www.whatwg.org/specs/web-apps/current-work/multipage/states-of-the-type-attribute.html#valid-e-mail-address
-        email : /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+$/,
-
-        url : /^(https?|ftp|file|ssh):\/\/(((([a-zA-Z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-zA-Z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-zA-Z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-zA-Z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-zA-Z]|\d|[ [...]
-        // abc.de
-        domain : /^([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,8}$/,
-
-        datetime : /^([0-2][0-9]{3})\-([0-1][0-9])\-([0-3][0-9])T([0-5][0-9])\:([0-5][0-9])\:([0-5][0-9])(Z|([\-\+]([0-1][0-9])\:00))$/,
-        // YYYY-MM-DD
-        date : /(?:19|20)[0-9]{2}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-9])|(?:(?!02)(?:0[1-9]|1[0-2])-(?:30))|(?:(?:0[13578]|1[02])-31))$/,
-        // HH:MM:SS
-        time : /^(0[0-9]|1[0-9]|2[0-3])(:[0-5][0-9]){2}$/,
-        dateISO : /^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}$/,
-        // MM/DD/YYYY
-        month_day_year : /^(0[1-9]|1[012])[- \/.](0[1-9]|[12][0-9]|3[01])[- \/.]\d{4}$/,
-        // DD/MM/YYYY
-        day_month_year : /^(0[1-9]|[12][0-9]|3[01])[- \/.](0[1-9]|1[012])[- \/.]\d{4}$/,
-
-        // #FFF or #FFFFFF
-        color : /^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/
-      },
-      validators : {
-        equalTo : function (el, required, parent) {
-          var from  = document.getElementById(el.getAttribute(this.add_namespace('data-equalto'))).value,
-              to    = el.value,
-              valid = (from === to);
-
-          return valid;
-        }
-      }
-    },
-
-    timer : null,
-
-    init : function (scope, method, options) {
-      this.bindings(method, options);
-    },
-
-    events : function (scope) {
-      var self = this,
-          form = self.S(scope).attr('novalidate', 'novalidate'),
-          settings = form.data(this.attr_name(true) + '-init') || {};
-
-      this.invalid_attr = this.add_namespace('data-invalid');
-
-      form
-        .off('.abide')
-        .on('submit.fndtn.abide validate.fndtn.abide', function (e) {
-          var is_ajax = /ajax/i.test(self.S(this).attr(self.attr_name()));
-          return self.validate(self.S(this).find('input, textarea, select').get(), e, is_ajax);
-        })
-        .on('reset', function () {
-          return self.reset($(this));
-        })
-        .find('input, textarea, select')
-          .off('.abide')
-          .on('blur.fndtn.abide change.fndtn.abide', function (e) {
-            if (settings.validate_on_blur === true) {
-              self.validate([this], e);
-            }
-          })
-          .on('keydown.fndtn.abide', function (e) {
-            if (settings.live_validate === true && e.which != 9) {
-              clearTimeout(self.timer);
-              self.timer = setTimeout(function () {
-                self.validate([this], e);
-              }.bind(this), settings.timeout);
-            }
-          });
-    },
-
-    reset : function (form) {
-      form.removeAttr(this.invalid_attr);
-      $(this.invalid_attr, form).removeAttr(this.invalid_attr);
-      $('.' + this.settings.error_class, form).not('small').removeClass(this.settings.error_class);
-    },
-
-    validate : function (els, e, is_ajax) {
-      var validations = this.parse_patterns(els),
-          validation_count = validations.length,
-          form = this.S(els[0]).closest('form'),
-          submit_event = /submit/.test(e.type);
-
-      // Has to count up to make sure the focus gets applied to the top error
-      for (var i = 0; i < validation_count; i++) {
-        if (!validations[i] && (submit_event || is_ajax)) {
-          if (this.settings.focus_on_invalid) {
-            els[i].focus();
-          }
-          form.trigger('invalid').trigger('invalid.fndtn.abide');
-          this.S(els[i]).closest('form').attr(this.invalid_attr, '');
-          return false;
-        }
-      }
-
-      if (submit_event || is_ajax) {
-        form.trigger('valid').trigger('valid.fndtn.abide');
-      }
-
-      form.removeAttr(this.invalid_attr);
-
-      if (is_ajax) {
-        return false;
-      }
-
-      return true;
-    },
-
-    parse_patterns : function (els) {
-      var i = els.length,
-          el_patterns = [];
-
-      while (i--) {
-        el_patterns.push(this.pattern(els[i]));
-      }
-
-      return this.check_validation_and_apply_styles(el_patterns);
-    },
-
-    pattern : function (el) {
-      var type = el.getAttribute('type'),
-          required = typeof el.getAttribute('required') === 'string';
-
-      var pattern = el.getAttribute('pattern') || '';
-
-      if (this.settings.patterns.hasOwnProperty(pattern) && pattern.length > 0) {
-        return [el, this.settings.patterns[pattern], required];
-      } else if (pattern.length > 0) {
-        return [el, new RegExp(pattern), required];
-      }
-
-      if (this.settings.patterns.hasOwnProperty(type)) {
-        return [el, this.settings.patterns[type], required];
-      }
-
-      pattern = /.*/;
-
-      return [el, pattern, required];
-    },
-
-    // TODO: Break this up into smaller methods, getting hard to read.
-    check_validation_and_apply_styles : function (el_patterns) {
-      var i = el_patterns.length,
-          validations = [],
-          form = this.S(el_patterns[0][0]).closest('[data-' + this.attr_name(true) + ']'),
-          settings = form.data(this.attr_name(true) + '-init') || {};
-      while (i--) {
-        var el = el_patterns[i][0],
-            required = el_patterns[i][2],
-            value = el.value.trim(),
-            direct_parent = this.S(el).parent(),
-            validator = el.getAttribute(this.add_namespace('data-abide-validator')),
-            is_radio = el.type === 'radio',
-            is_checkbox = el.type === 'checkbox',
-            label = this.S('label[for="' + el.getAttribute('id') + '"]'),
-            valid_length = (required) ? (el.value.length > 0) : true,
-            el_validations = [];
-
-        var parent, valid;
-
-        // support old way to do equalTo validations
-        if (el.getAttribute(this.add_namespace('data-equalto'))) { validator = 'equalTo' }
-
-        if (!direct_parent.is('label')) {
-          parent = direct_parent;
-        } else {
-          parent = direct_parent.parent();
-        }
-
-        if (validator) {
-          valid = this.settings.validators[validator].apply(this, [el, required, parent]);
-          el_validations.push(valid);
-        }
-
-        if (is_radio && required) {
-          el_validations.push(this.valid_radio(el, required));
-        } else if (is_checkbox && required) {
-          el_validations.push(this.valid_checkbox(el, required));
-        } else {
-
-          if (el_patterns[i][1].test(value) && valid_length ||
-            !required && el.value.length < 1 || $(el).attr('disabled')) {
-            el_validations.push(true);
-          } else {
-            el_validations.push(false);
-          }
-
-          el_validations = [el_validations.every(function (valid) {return valid;})];
-
-          if (el_validations[0]) {
-            this.S(el).removeAttr(this.invalid_attr);
-            el.setAttribute('aria-invalid', 'false');
-            el.removeAttribute('aria-describedby');
-            parent.removeClass(this.settings.error_class);
-            if (label.length > 0 && this.settings.error_labels) {
-              label.removeClass(this.settings.error_class).removeAttr('role');
-            }
-            $(el).triggerHandler('valid');
-          } else {
-            this.S(el).attr(this.invalid_attr, '');
-            el.setAttribute('aria-invalid', 'true');
-
-            // Try to find the error associated with the input
-            var errorElem = parent.find('small.' + this.settings.error_class, 'span.' + this.settings.error_class);
-            var errorID = errorElem.length > 0 ? errorElem[0].id : '';
-            if (errorID.length > 0) {
-              el.setAttribute('aria-describedby', errorID);
-            }
-
-            // el.setAttribute('aria-describedby', $(el).find('.error')[0].id);
-            parent.addClass(this.settings.error_class);
-            if (label.length > 0 && this.settings.error_labels) {
-              label.addClass(this.settings.error_class).attr('role', 'alert');
-            }
-            $(el).triggerHandler('invalid');
-          }
-        }
-        validations.push(el_validations[0]);
-      }
-      validations = [validations.every(function (valid) {return valid;})];
-      return validations;
-    },
-
-    valid_checkbox : function (el, required) {
-      var el = this.S(el),
-          valid = (el.is(':checked') || !required || el.get(0).getAttribute('disabled'));
-
-      if (valid) {
-        el.removeAttr(this.invalid_attr).parent().removeClass(this.settings.error_class);
-      } else {
-        el.attr(this.invalid_attr, '').parent().addClass(this.settings.error_class);
-      }
-
-      return valid;
-    },
-
-    valid_radio : function (el, required) {
-      var name = el.getAttribute('name'),
-          group = this.S(el).closest('[data-' + this.attr_name(true) + ']').find("[name='" + name + "']"),
-          count = group.length,
-          valid = false,
-          disabled = false;
-
-      // Has to count up to make sure the focus gets applied to the top error
-        for (var i=0; i < count; i++) {
-            if( group[i].getAttribute('disabled') ){
-                disabled=true;
-                valid=true;
-            } else {
-                if (group[i].checked){
-                    valid = true;
-                } else {
-                    if( disabled ){
-                        valid = false;
-                    }
-                }
-            }
-        }
-
-      // Has to count up to make sure the focus gets applied to the top error
-      for (var i = 0; i < count; i++) {
-        if (valid) {
-          this.S(group[i]).removeAttr(this.invalid_attr).parent().removeClass(this.settings.error_class);
-        } else {
-          this.S(group[i]).attr(this.invalid_attr, '').parent().addClass(this.settings.error_class);
-        }
-      }
-
-      return valid;
-    },
-
-    valid_equal : function (el, required, parent) {
-      var from  = document.getElementById(el.getAttribute(this.add_namespace('data-equalto'))).value,
-          to    = el.value,
-          valid = (from === to);
-
-      if (valid) {
-        this.S(el).removeAttr(this.invalid_attr);
-        parent.removeClass(this.settings.error_class);
-        if (label.length > 0 && settings.error_labels) {
-          label.removeClass(this.settings.error_class);
-        }
-      } else {
-        this.S(el).attr(this.invalid_attr, '');
-        parent.addClass(this.settings.error_class);
-        if (label.length > 0 && settings.error_labels) {
-          label.addClass(this.settings.error_class);
-        }
-      }
-
-      return valid;
-    },
-
-    valid_oneof : function (el, required, parent, doNotValidateOthers) {
-      var el = this.S(el),
-        others = this.S('[' + this.add_namespace('data-oneof') + ']'),
-        valid = others.filter(':checked').length > 0;
-
-      if (valid) {
-        el.removeAttr(this.invalid_attr).parent().removeClass(this.settings.error_class);
-      } else {
-        el.attr(this.invalid_attr, '').parent().addClass(this.settings.error_class);
-      }
-
-      if (!doNotValidateOthers) {
-        var _this = this;
-        others.each(function () {
-          _this.valid_oneof.call(_this, this, null, null, true);
-        });
-      }
-
-      return valid;
-    }
-  };
-}(jQuery, window, window.document));
diff --git a/3dmol/js/foundation/foundation.accordion.js b/3dmol/js/foundation/foundation.accordion.js
deleted file mode 100644
index 483d819..0000000
--- a/3dmol/js/foundation/foundation.accordion.js
+++ /dev/null
@@ -1,67 +0,0 @@
-;(function ($, window, document, undefined) {
-  'use strict';
-
-  Foundation.libs.accordion = {
-    name : 'accordion',
-
-    version : '5.5.1',
-
-    settings : {
-      content_class : 'content',
-      active_class : 'active',
-      multi_expand : false,
-      toggleable : true,
-      callback : function () {}
-    },
-
-    init : function (scope, method, options) {
-      this.bindings(method, options);
-    },
-
-    events : function () {
-      var self = this;
-      var S = this.S;
-      S(this.scope)
-      .off('.fndtn.accordion')
-      .on('click.fndtn.accordion', '[' + this.attr_name() + '] > .accordion-navigation > a', function (e) {
-        var accordion = S(this).closest('[' + self.attr_name() + ']'),
-            groupSelector = self.attr_name() + '=' + accordion.attr(self.attr_name()),
-            settings = accordion.data(self.attr_name(true) + '-init') || self.settings,
-            target = S('#' + this.href.split('#')[1]),
-            aunts = $('> .accordion-navigation', accordion),
-            siblings = aunts.children('.' + settings.content_class),
-            active_content = siblings.filter('.' + settings.active_class);
-
-        e.preventDefault();
-
-        if (accordion.attr(self.attr_name())) {
-          siblings = siblings.add('[' + groupSelector + '] dd > ' + '.' + settings.content_class);
-          aunts = aunts.add('[' + groupSelector + '] .accordion-navigation');
-        }
-
-        if (settings.toggleable && target.is(active_content)) {
-          target.parent('.accordion-navigation').toggleClass(settings.active_class, false);
-          target.toggleClass(settings.active_class, false);
-          settings.callback(target);
-          target.triggerHandler('toggled', [accordion]);
-          accordion.triggerHandler('toggled', [target]);
-          return;
-        }
-
-        if (!settings.multi_expand) {
-          siblings.removeClass(settings.active_class);
-          aunts.removeClass(settings.active_class);
-        }
-
-        target.addClass(settings.active_class).parent().addClass(settings.active_class);
-        settings.callback(target);
-        target.triggerHandler('toggled', [accordion]);
-        accordion.triggerHandler('toggled', [target]);
-      });
-    },
-
-    off : function () {},
-
-    reflow : function () {}
-  };
-}(jQuery, window, window.document));
diff --git a/3dmol/js/foundation/foundation.alert.js b/3dmol/js/foundation/foundation.alert.js
deleted file mode 100644
index 763a22f..0000000
--- a/3dmol/js/foundation/foundation.alert.js
+++ /dev/null
@@ -1,43 +0,0 @@
-;(function ($, window, document, undefined) {
-  'use strict';
-
-  Foundation.libs.alert = {
-    name : 'alert',
-
-    version : '5.5.1',
-
-    settings : {
-      callback : function () {}
-    },
-
-    init : function (scope, method, options) {
-      this.bindings(method, options);
-    },
-
-    events : function () {
-      var self = this,
-          S = this.S;
-
-      $(this.scope).off('.alert').on('click.fndtn.alert', '[' + this.attr_name() + '] .close', function (e) {
-        var alertBox = S(this).closest('[' + self.attr_name() + ']'),
-            settings = alertBox.data(self.attr_name(true) + '-init') || self.settings;
-
-        e.preventDefault();
-        if (Modernizr.csstransitions) {
-          alertBox.addClass('alert-close');
-          alertBox.on('transitionend webkitTransitionEnd oTransitionEnd', function (e) {
-            S(this).trigger('close').trigger('close.fndtn.alert').remove();
-            settings.callback();
-          });
-        } else {
-          alertBox.fadeOut(300, function () {
-            S(this).trigger('close').trigger('close.fndtn.alert').remove();
-            settings.callback();
-          });
-        }
-      });
-    },
-
-    reflow : function () {}
-  };
-}(jQuery, window, window.document));
diff --git a/3dmol/js/foundation/foundation.clearing.js b/3dmol/js/foundation/foundation.clearing.js
deleted file mode 100644
index e7bd458..0000000
--- a/3dmol/js/foundation/foundation.clearing.js
+++ /dev/null
@@ -1,556 +0,0 @@
-;(function ($, window, document, undefined) {
-  'use strict';
-
-  Foundation.libs.clearing = {
-    name : 'clearing',
-
-    version : '5.5.1',
-
-    settings : {
-      templates : {
-        viewing : '<a href="#" class="clearing-close">×</a>' +
-          '<div class="visible-img" style="display: none"><div class="clearing-touch-label"></div><img src="data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs%3D" alt="" />' +
-          '<p class="clearing-caption"></p><a href="#" class="clearing-main-prev"><span></span></a>' +
-          '<a href="#" class="clearing-main-next"><span></span></a></div>'
-      },
-
-      // comma delimited list of selectors that, on click, will close clearing,
-      // add 'div.clearing-blackout, div.visible-img' to close on background click
-      close_selectors : '.clearing-close, div.clearing-blackout',
-
-      // Default to the entire li element.
-      open_selectors : '',
-
-      // Image will be skipped in carousel.
-      skip_selector : '',
-
-      touch_label : '',
-
-      // event initializers and locks
-      init : false,
-      locked : false
-    },
-
-    init : function (scope, method, options) {
-      var self = this;
-      Foundation.inherit(this, 'throttle image_loaded');
-
-      this.bindings(method, options);
-
-      if (self.S(this.scope).is('[' + this.attr_name() + ']')) {
-        this.assemble(self.S('li', this.scope));
-      } else {
-        self.S('[' + this.attr_name() + ']', this.scope).each(function () {
-          self.assemble(self.S('li', this));
-        });
-      }
-    },
-
-    events : function (scope) {
-      var self = this,
-          S = self.S,
-          $scroll_container = $('.scroll-container');
-
-      if ($scroll_container.length > 0) {
-        this.scope = $scroll_container;
-      }
-
-      S(this.scope)
-        .off('.clearing')
-        .on('click.fndtn.clearing', 'ul[' + this.attr_name() + '] li ' + this.settings.open_selectors,
-          function (e, current, target) {
-            var current = current || S(this),
-                target = target || current,
-                next = current.next('li'),
-                settings = current.closest('[' + self.attr_name() + ']').data(self.attr_name(true) + '-init'),
-                image = S(e.target);
-
-            e.preventDefault();
-
-            if (!settings) {
-              self.init();
-              settings = current.closest('[' + self.attr_name() + ']').data(self.attr_name(true) + '-init');
-            }
-
-            // if clearing is open and the current image is
-            // clicked, go to the next image in sequence
-            if (target.hasClass('visible') &&
-              current[0] === target[0] &&
-              next.length > 0 && self.is_open(current)) {
-              target = next;
-              image = S('img', target);
-            }
-
-            // set current and target to the clicked li if not otherwise defined.
-            self.open(image, current, target);
-            self.update_paddles(target);
-          })
-
-        .on('click.fndtn.clearing', '.clearing-main-next',
-          function (e) { self.nav(e, 'next') })
-        .on('click.fndtn.clearing', '.clearing-main-prev',
-          function (e) { self.nav(e, 'prev') })
-        .on('click.fndtn.clearing', this.settings.close_selectors,
-          function (e) { Foundation.libs.clearing.close(e, this) });
-
-      $(document).on('keydown.fndtn.clearing',
-          function (e) { self.keydown(e) });
-
-      S(window).off('.clearing').on('resize.fndtn.clearing',
-        function () { self.resize() });
-
-      this.swipe_events(scope);
-    },
-
-    swipe_events : function (scope) {
-      var self = this,
-      S = self.S;
-
-      S(this.scope)
-        .on('touchstart.fndtn.clearing', '.visible-img', function (e) {
-          if (!e.touches) { e = e.originalEvent; }
-          var data = {
-                start_page_x : e.touches[0].pageX,
-                start_page_y : e.touches[0].pageY,
-                start_time : (new Date()).getTime(),
-                delta_x : 0,
-                is_scrolling : undefined
-              };
-
-          S(this).data('swipe-transition', data);
-          e.stopPropagation();
-        })
-        .on('touchmove.fndtn.clearing', '.visible-img', function (e) {
-          if (!e.touches) {
-            e = e.originalEvent;
-          }
-          // Ignore pinch/zoom events
-          if (e.touches.length > 1 || e.scale && e.scale !== 1) {
-            return;
-          }
-
-          var data = S(this).data('swipe-transition');
-
-          if (typeof data === 'undefined') {
-            data = {};
-          }
-
-          data.delta_x = e.touches[0].pageX - data.start_page_x;
-
-          if (Foundation.rtl) {
-            data.delta_x = -data.delta_x;
-          }
-
-          if (typeof data.is_scrolling === 'undefined') {
-            data.is_scrolling = !!( data.is_scrolling || Math.abs(data.delta_x) < Math.abs(e.touches[0].pageY - data.start_page_y) );
-          }
-
-          if (!data.is_scrolling && !data.active) {
-            e.preventDefault();
-            var direction = (data.delta_x < 0) ? 'next' : 'prev';
-            data.active = true;
-            self.nav(e, direction);
-          }
-        })
-        .on('touchend.fndtn.clearing', '.visible-img', function (e) {
-          S(this).data('swipe-transition', {});
-          e.stopPropagation();
-        });
-    },
-
-    assemble : function ($li) {
-      var $el = $li.parent();
-
-      if ($el.parent().hasClass('carousel')) {
-        return;
-      }
-
-      $el.after('<div id="foundationClearingHolder"></div>');
-
-      var grid = $el.detach(),
-          grid_outerHTML = '';
-
-      if (grid[0] == null) {
-        return;
-      } else {
-        grid_outerHTML = grid[0].outerHTML;
-      }
-
-      var holder = this.S('#foundationClearingHolder'),
-          settings = $el.data(this.attr_name(true) + '-init'),
-          data = {
-            grid : '<div class="carousel">' + grid_outerHTML + '</div>',
-            viewing : settings.templates.viewing
-          },
-          wrapper = '<div class="clearing-assembled"><div>' + data.viewing +
-            data.grid + '</div></div>',
-          touch_label = this.settings.touch_label;
-
-      if (Modernizr.touch) {
-        wrapper = $(wrapper).find('.clearing-touch-label').html(touch_label).end();
-      }
-
-      holder.after(wrapper).remove();
-    },
-
-    open : function ($image, current, target) {
-      var self = this,
-          body = $(document.body),
-          root = target.closest('.clearing-assembled'),
-          container = self.S('div', root).first(),
-          visible_image = self.S('.visible-img', container),
-          image = self.S('img', visible_image).not($image),
-          label = self.S('.clearing-touch-label', container),
-          error = false;
-
-      // Event to disable scrolling on touch devices when Clearing is activated
-      $('body').on('touchmove', function (e) {
-        e.preventDefault();
-      });
-
-      image.error(function () {
-        error = true;
-      });
-
-      function startLoad() {
-        setTimeout(function () {
-          this.image_loaded(image, function () {
-            if (image.outerWidth() === 1 && !error) {
-              startLoad.call(this);
-            } else {
-              cb.call(this, image);
-            }
-          }.bind(this));
-        }.bind(this), 100);
-      }
-
-      function cb (image) {
-        var $image = $(image);
-        $image.css('visibility', 'visible');
-        // toggle the gallery
-        body.css('overflow', 'hidden');
-        root.addClass('clearing-blackout');
-        container.addClass('clearing-container');
-        visible_image.show();
-        this.fix_height(target)
-          .caption(self.S('.clearing-caption', visible_image), self.S('img', target))
-          .center_and_label(image, label)
-          .shift(current, target, function () {
-            target.closest('li').siblings().removeClass('visible');
-            target.closest('li').addClass('visible');
-          });
-        visible_image.trigger('opened.fndtn.clearing')
-      }
-
-      if (!this.locked()) {
-        visible_image.trigger('open.fndtn.clearing');
-        // set the image to the selected thumbnail
-        image
-          .attr('src', this.load($image))
-          .css('visibility', 'hidden');
-
-        startLoad.call(this);
-      }
-    },
-
-    close : function (e, el) {
-      e.preventDefault();
-
-      var root = (function (target) {
-            if (/blackout/.test(target.selector)) {
-              return target;
-            } else {
-              return target.closest('.clearing-blackout');
-            }
-          }($(el))),
-          body = $(document.body), container, visible_image;
-
-      if (el === e.target && root) {
-        body.css('overflow', '');
-        container = $('div', root).first();
-        visible_image = $('.visible-img', container);
-        visible_image.trigger('close.fndtn.clearing');
-        this.settings.prev_index = 0;
-        $('ul[' + this.attr_name() + ']', root)
-          .attr('style', '').closest('.clearing-blackout')
-          .removeClass('clearing-blackout');
-        container.removeClass('clearing-container');
-        visible_image.hide();
-        visible_image.trigger('closed.fndtn.clearing');
-      }
-
-      // Event to re-enable scrolling on touch devices
-      $('body').off('touchmove');
-
-      return false;
-    },
-
-    is_open : function (current) {
-      return current.parent().prop('style').length > 0;
-    },
-
-    keydown : function (e) {
-      var clearing = $('.clearing-blackout ul[' + this.attr_name() + ']'),
-          NEXT_KEY = this.rtl ? 37 : 39,
-          PREV_KEY = this.rtl ? 39 : 37,
-          ESC_KEY = 27;
-
-      if (e.which === NEXT_KEY) {
-        this.go(clearing, 'next');
-      }
-      if (e.which === PREV_KEY) {
-        this.go(clearing, 'prev');
-      }
-      if (e.which === ESC_KEY) {
-        this.S('a.clearing-close').trigger('click').trigger('click.fndtn.clearing');
-      }
-    },
-
-    nav : function (e, direction) {
-      var clearing = $('ul[' + this.attr_name() + ']', '.clearing-blackout');
-
-      e.preventDefault();
-      this.go(clearing, direction);
-    },
-
-    resize : function () {
-      var image = $('img', '.clearing-blackout .visible-img'),
-          label = $('.clearing-touch-label', '.clearing-blackout');
-
-      if (image.length) {
-        this.center_and_label(image, label);
-        image.trigger('resized.fndtn.clearing')
-      }
-    },
-
-    // visual adjustments
-    fix_height : function (target) {
-      var lis = target.parent().children(),
-          self = this;
-
-      lis.each(function () {
-        var li = self.S(this),
-            image = li.find('img');
-
-        if (li.height() > image.outerHeight()) {
-          li.addClass('fix-height');
-        }
-      })
-      .closest('ul')
-      .width(lis.length * 100 + '%');
-
-      return this;
-    },
-
-    update_paddles : function (target) {
-      target = target.closest('li');
-      var visible_image = target
-        .closest('.carousel')
-        .siblings('.visible-img');
-
-      if (target.next().length > 0) {
-        this.S('.clearing-main-next', visible_image).removeClass('disabled');
-      } else {
-        this.S('.clearing-main-next', visible_image).addClass('disabled');
-      }
-
-      if (target.prev().length > 0) {
-        this.S('.clearing-main-prev', visible_image).removeClass('disabled');
-      } else {
-        this.S('.clearing-main-prev', visible_image).addClass('disabled');
-      }
-    },
-
-    center_and_label : function (target, label) {
-      if (!this.rtl && label.length > 0) {
-        label.css({
-          marginLeft : -(label.outerWidth() / 2),
-          marginTop : -(target.outerHeight() / 2)-label.outerHeight()-10
-        });
-      } else {
-        label.css({
-          marginRight : -(label.outerWidth() / 2),
-          marginTop : -(target.outerHeight() / 2)-label.outerHeight()-10,
-          left: 'auto',
-          right: '50%'
-        });
-      }
-      return this;
-    },
-
-    // image loading and preloading
-
-    load : function ($image) {
-      var href;
-
-      if ($image[0].nodeName === 'A') {
-        href = $image.attr('href');
-      } else {
-        href = $image.closest('a').attr('href');
-      }
-
-      this.preload($image);
-
-      if (href) {
-        return href;
-      }
-      return $image.attr('src');
-    },
-
-    preload : function ($image) {
-      this
-        .img($image.closest('li').next())
-        .img($image.closest('li').prev());
-    },
-
-    img : function (img) {
-      if (img.length) {
-        var new_img = new Image(),
-            new_a = this.S('a', img);
-
-        if (new_a.length) {
-          new_img.src = new_a.attr('href');
-        } else {
-          new_img.src = this.S('img', img).attr('src');
-        }
-      }
-      return this;
-    },
-
-    // image caption
-
-    caption : function (container, $image) {
-      var caption = $image.attr('data-caption');
-
-      if (caption) {
-        container
-          .html(caption)
-          .show();
-      } else {
-        container
-          .text('')
-          .hide();
-      }
-      return this;
-    },
-
-    // directional methods
-
-    go : function ($ul, direction) {
-      var current = this.S('.visible', $ul),
-          target = current[direction]();
-
-      // Check for skip selector.
-      if (this.settings.skip_selector && target.find(this.settings.skip_selector).length != 0) {
-        target = target[direction]();
-      }
-
-      if (target.length) {
-        this.S('img', target)
-          .trigger('click', [current, target]).trigger('click.fndtn.clearing', [current, target])
-          .trigger('change.fndtn.clearing');
-      }
-    },
-
-    shift : function (current, target, callback) {
-      var clearing = target.parent(),
-          old_index = this.settings.prev_index || target.index(),
-          direction = this.direction(clearing, current, target),
-          dir = this.rtl ? 'right' : 'left',
-          left = parseInt(clearing.css('left'), 10),
-          width = target.outerWidth(),
-          skip_shift;
-
-      var dir_obj = {};
-
-      // we use jQuery animate instead of CSS transitions because we
-      // need a callback to unlock the next animation
-      // needs support for RTL **
-      if (target.index() !== old_index && !/skip/.test(direction)) {
-        if (/left/.test(direction)) {
-          this.lock();
-          dir_obj[dir] = left + width;
-          clearing.animate(dir_obj, 300, this.unlock());
-        } else if (/right/.test(direction)) {
-          this.lock();
-          dir_obj[dir] = left - width;
-          clearing.animate(dir_obj, 300, this.unlock());
-        }
-      } else if (/skip/.test(direction)) {
-        // the target image is not adjacent to the current image, so
-        // do we scroll right or not
-        skip_shift = target.index() - this.settings.up_count;
-        this.lock();
-
-        if (skip_shift > 0) {
-          dir_obj[dir] = -(skip_shift * width);
-          clearing.animate(dir_obj, 300, this.unlock());
-        } else {
-          dir_obj[dir] = 0;
-          clearing.animate(dir_obj, 300, this.unlock());
-        }
-      }
-
-      callback();
-    },
-
-    direction : function ($el, current, target) {
-      var lis = this.S('li', $el),
-          li_width = lis.outerWidth() + (lis.outerWidth() / 4),
-          up_count = Math.floor(this.S('.clearing-container').outerWidth() / li_width) - 1,
-          target_index = lis.index(target),
-          response;
-
-      this.settings.up_count = up_count;
-
-      if (this.adjacent(this.settings.prev_index, target_index)) {
-        if ((target_index > up_count) && target_index > this.settings.prev_index) {
-          response = 'right';
-        } else if ((target_index > up_count - 1) && target_index <= this.settings.prev_index) {
-          response = 'left';
-        } else {
-          response = false;
-        }
-      } else {
-        response = 'skip';
-      }
-
-      this.settings.prev_index = target_index;
-
-      return response;
-    },
-
-    adjacent : function (current_index, target_index) {
-      for (var i = target_index + 1; i >= target_index - 1; i--) {
-        if (i === current_index) {
-          return true;
-        }
-      }
-      return false;
-    },
-
-    // lock management
-
-    lock : function () {
-      this.settings.locked = true;
-    },
-
-    unlock : function () {
-      this.settings.locked = false;
-    },
-
-    locked : function () {
-      return this.settings.locked;
-    },
-
-    off : function () {
-      this.S(this.scope).off('.fndtn.clearing');
-      this.S(window).off('.fndtn.clearing');
-    },
-
-    reflow : function () {
-      this.init();
-    }
-  };
-
-}(jQuery, window, window.document));
diff --git a/3dmol/js/foundation/foundation.dropdown.js b/3dmol/js/foundation/foundation.dropdown.js
deleted file mode 100644
index 4fa8b31..0000000
--- a/3dmol/js/foundation/foundation.dropdown.js
+++ /dev/null
@@ -1,448 +0,0 @@
-;(function ($, window, document, undefined) {
-  'use strict';
-
-  Foundation.libs.dropdown = {
-    name : 'dropdown',
-
-    version : '5.5.1',
-
-    settings : {
-      active_class : 'open',
-      disabled_class : 'disabled',
-      mega_class : 'mega',
-      align : 'bottom',
-      is_hover : false,
-      hover_timeout : 150,
-      opened : function () {},
-      closed : function () {}
-    },
-
-    init : function (scope, method, options) {
-      Foundation.inherit(this, 'throttle');
-
-      $.extend(true, this.settings, method, options);
-      this.bindings(method, options);
-    },
-
-    events : function (scope) {
-      var self = this,
-          S = self.S;
-
-      S(this.scope)
-        .off('.dropdown')
-        .on('click.fndtn.dropdown', '[' + this.attr_name() + ']', function (e) {
-          var settings = S(this).data(self.attr_name(true) + '-init') || self.settings;
-          if (!settings.is_hover || Modernizr.touch) {
-            e.preventDefault();
-            if (S(this).parent('[data-reveal-id]')) {
-              e.stopPropagation();
-            }
-            self.toggle($(this));
-          }
-        })
-        .on('mouseenter.fndtn.dropdown', '[' + this.attr_name() + '], [' + this.attr_name() + '-content]', function (e) {
-          var $this = S(this),
-              dropdown,
-              target;
-
-          clearTimeout(self.timeout);
-
-          if ($this.data(self.data_attr())) {
-            dropdown = S('#' + $this.data(self.data_attr()));
-            target = $this;
-          } else {
-            dropdown = $this;
-            target = S('[' + self.attr_name() + '="' + dropdown.attr('id') + '"]');
-          }
-
-          var settings = target.data(self.attr_name(true) + '-init') || self.settings;
-
-          if (S(e.currentTarget).data(self.data_attr()) && settings.is_hover) {
-            self.closeall.call(self);
-          }
-
-          if (settings.is_hover) {
-            self.open.apply(self, [dropdown, target]);
-          }
-        })
-        .on('mouseleave.fndtn.dropdown', '[' + this.attr_name() + '], [' + this.attr_name() + '-content]', function (e) {
-          var $this = S(this);
-          var settings;
-
-          if ($this.data(self.data_attr())) {
-              settings = $this.data(self.data_attr(true) + '-init') || self.settings;
-          } else {
-              var target   = S('[' + self.attr_name() + '="' + S(this).attr('id') + '"]'),
-                  settings = target.data(self.attr_name(true) + '-init') || self.settings;
-          }
-
-          self.timeout = setTimeout(function () {
-            if ($this.data(self.data_attr())) {
-              if (settings.is_hover) {
-                self.close.call(self, S('#' + $this.data(self.data_attr())));
-              }
-            } else {
-              if (settings.is_hover) {
-                self.close.call(self, $this);
-              }
-            }
-          }.bind(this), settings.hover_timeout);
-        })
-        .on('click.fndtn.dropdown', function (e) {
-          var parent = S(e.target).closest('[' + self.attr_name() + '-content]');
-          var links  = parent.find('a');
-
-          if (links.length > 0 && parent.attr('aria-autoclose') !== 'false') {
-              self.close.call(self, S('[' + self.attr_name() + '-content]'));
-          }
-
-          if (e.target !== document && !$.contains(document.documentElement, e.target)) {
-            return;
-          }
-
-          if (S(e.target).closest('[' + self.attr_name() + ']').length > 0) {
-            return;
-          }
-
-          if (!(S(e.target).data('revealId')) &&
-            (parent.length > 0 && (S(e.target).is('[' + self.attr_name() + '-content]') ||
-              $.contains(parent.first()[0], e.target)))) {
-            e.stopPropagation();
-            return;
-          }
-
-          self.close.call(self, S('[' + self.attr_name() + '-content]'));
-        })
-        .on('opened.fndtn.dropdown', '[' + self.attr_name() + '-content]', function () {
-          self.settings.opened.call(this);
-        })
-        .on('closed.fndtn.dropdown', '[' + self.attr_name() + '-content]', function () {
-          self.settings.closed.call(this);
-        });
-
-      S(window)
-        .off('.dropdown')
-        .on('resize.fndtn.dropdown', self.throttle(function () {
-          self.resize.call(self);
-        }, 50));
-
-      this.resize();
-    },
-
-    close : function (dropdown) {
-      var self = this;
-      dropdown.each(function () {
-        var original_target = $('[' + self.attr_name() + '=' + dropdown[0].id + ']') || $('aria-controls=' + dropdown[0].id + ']');
-        original_target.attr('aria-expanded', 'false');
-        if (self.S(this).hasClass(self.settings.active_class)) {
-          self.S(this)
-            .css(Foundation.rtl ? 'right' : 'left', '-99999px')
-            .attr('aria-hidden', 'true')
-            .removeClass(self.settings.active_class)
-            .prev('[' + self.attr_name() + ']')
-            .removeClass(self.settings.active_class)
-            .removeData('target');
-
-          self.S(this).trigger('closed').trigger('closed.fndtn.dropdown', [dropdown]);
-        }
-      });
-      dropdown.removeClass('f-open-' + this.attr_name(true));
-    },
-
-    closeall : function () {
-      var self = this;
-      $.each(self.S('.f-open-' + this.attr_name(true)), function () {
-        self.close.call(self, self.S(this));
-      });
-    },
-
-    open : function (dropdown, target) {
-      this
-        .css(dropdown
-        .addClass(this.settings.active_class), target);
-      dropdown.prev('[' + this.attr_name() + ']').addClass(this.settings.active_class);
-      dropdown.data('target', target.get(0)).trigger('opened').trigger('opened.fndtn.dropdown', [dropdown, target]);
-      dropdown.attr('aria-hidden', 'false');
-      target.attr('aria-expanded', 'true');
-      dropdown.focus();
-      dropdown.addClass('f-open-' + this.attr_name(true));
-    },
-
-    data_attr : function () {
-      if (this.namespace.length > 0) {
-        return this.namespace + '-' + this.name;
-      }
-
-      return this.name;
-    },
-
-    toggle : function (target) {
-      if (target.hasClass(this.settings.disabled_class)) {
-        return;
-      }
-      var dropdown = this.S('#' + target.data(this.data_attr()));
-      if (dropdown.length === 0) {
-        // No dropdown found, not continuing
-        return;
-      }
-
-      this.close.call(this, this.S('[' + this.attr_name() + '-content]').not(dropdown));
-
-      if (dropdown.hasClass(this.settings.active_class)) {
-        this.close.call(this, dropdown);
-        if (dropdown.data('target') !== target.get(0)) {
-          this.open.call(this, dropdown, target);
-        }
-      } else {
-        this.open.call(this, dropdown, target);
-      }
-    },
-
-    resize : function () {
-      var dropdown = this.S('[' + this.attr_name() + '-content].open');
-      var target = $(dropdown.data("target"));
-
-      if (dropdown.length && target.length) {
-        this.css(dropdown, target);
-      }
-    },
-
-    css : function (dropdown, target) {
-      var left_offset = Math.max((target.width() - dropdown.width()) / 2, 8),
-          settings = target.data(this.attr_name(true) + '-init') || this.settings;
-
-      this.clear_idx();
-
-      if (this.small()) {
-        var p = this.dirs.bottom.call(dropdown, target, settings);
-
-        dropdown.attr('style', '').removeClass('drop-left drop-right drop-top').css({
-          position : 'absolute',
-          width : '95%',
-          'max-width' : 'none',
-          top : p.top
-        });
-
-        dropdown.css(Foundation.rtl ? 'right' : 'left', left_offset);
-      } else {
-
-        this.style(dropdown, target, settings);
-      }
-
-      return dropdown;
-    },
-
-    style : function (dropdown, target, settings) {
-      var css = $.extend({position : 'absolute'},
-        this.dirs[settings.align].call(dropdown, target, settings));
-
-      dropdown.attr('style', '').css(css);
-    },
-
-    // return CSS property object
-    // `this` is the dropdown
-    dirs : {
-      // Calculate target offset
-      _base : function (t) {
-        var o_p = this.offsetParent(),
-            o = o_p.offset(),
-            p = t.offset();
-
-        p.top -= o.top;
-        p.left -= o.left;
-
-        //set some flags on the p object to pass along
-        p.missRight = false;
-        p.missTop = false;
-        p.missLeft = false;
-        p.leftRightFlag = false;
-
-        //lets see if the panel will be off the screen
-        //get the actual width of the page and store it
-        var actualBodyWidth;
-        if (document.getElementsByClassName('row')[0]) {
-          actualBodyWidth = document.getElementsByClassName('row')[0].clientWidth;
-        } else {
-          actualBodyWidth = window.outerWidth;
-        }
-
-        var actualMarginWidth = (window.outerWidth - actualBodyWidth) / 2;
-        var actualBoundary = actualBodyWidth;
-
-        if (!this.hasClass('mega')) {
-          //miss top
-          if (t.offset().top <= this.outerHeight()) {
-            p.missTop = true;
-            actualBoundary = window.outerWidth - actualMarginWidth;
-            p.leftRightFlag = true;
-          }
-
-          //miss right
-          if (t.offset().left + this.outerWidth() > t.offset().left + actualMarginWidth && t.offset().left - actualMarginWidth > this.outerWidth()) {
-            p.missRight = true;
-            p.missLeft = false;
-          }
-
-          //miss left
-          if (t.offset().left - this.outerWidth() <= 0) {
-            p.missLeft = true;
-            p.missRight = false;
-          }
-        }
-
-        return p;
-      },
-
-      top : function (t, s) {
-        var self = Foundation.libs.dropdown,
-            p = self.dirs._base.call(this, t);
-
-        this.addClass('drop-top');
-
-        if (p.missTop == true) {
-          p.top = p.top + t.outerHeight() + this.outerHeight();
-          this.removeClass('drop-top');
-        }
-
-        if (p.missRight == true) {
-          p.left = p.left - this.outerWidth() + t.outerWidth();
-        }
-
-        if (t.outerWidth() < this.outerWidth() || self.small() || this.hasClass(s.mega_menu)) {
-          self.adjust_pip(this, t, s, p);
-        }
-
-        if (Foundation.rtl) {
-          return {left : p.left - this.outerWidth() + t.outerWidth(),
-            top : p.top - this.outerHeight()};
-        }
-
-        return {left : p.left, top : p.top - this.outerHeight()};
-      },
-
-      bottom : function (t, s) {
-        var self = Foundation.libs.dropdown,
-            p = self.dirs._base.call(this, t);
-
-        if (p.missRight == true) {
-          p.left = p.left - this.outerWidth() + t.outerWidth();
-        }
-
-        if (t.outerWidth() < this.outerWidth() || self.small() || this.hasClass(s.mega_menu)) {
-          self.adjust_pip(this, t, s, p);
-        }
-
-        if (self.rtl) {
-          return {left : p.left - this.outerWidth() + t.outerWidth(), top : p.top + t.outerHeight()};
-        }
-
-        return {left : p.left, top : p.top + t.outerHeight()};
-      },
-
-      left : function (t, s) {
-        var p = Foundation.libs.dropdown.dirs._base.call(this, t);
-
-        this.addClass('drop-left');
-
-        if (p.missLeft == true) {
-          p.left =  p.left + this.outerWidth();
-          p.top = p.top + t.outerHeight();
-          this.removeClass('drop-left');
-        }
-
-        return {left : p.left - this.outerWidth(), top : p.top};
-      },
-
-      right : function (t, s) {
-        var p = Foundation.libs.dropdown.dirs._base.call(this, t);
-
-        this.addClass('drop-right');
-
-        if (p.missRight == true) {
-          p.left = p.left - this.outerWidth();
-          p.top = p.top + t.outerHeight();
-          this.removeClass('drop-right');
-        } else {
-          p.triggeredRight = true;
-        }
-
-        var self = Foundation.libs.dropdown;
-
-        if (t.outerWidth() < this.outerWidth() || self.small() || this.hasClass(s.mega_menu)) {
-          self.adjust_pip(this, t, s, p);
-        }
-
-        return {left : p.left + t.outerWidth(), top : p.top};
-      }
-    },
-
-    // Insert rule to style psuedo elements
-    adjust_pip : function (dropdown, target, settings, position) {
-      var sheet = Foundation.stylesheet,
-          pip_offset_base = 8;
-
-      if (dropdown.hasClass(settings.mega_class)) {
-        pip_offset_base = position.left + (target.outerWidth() / 2) - 8;
-      } else if (this.small()) {
-        pip_offset_base += position.left - 8;
-      }
-
-      this.rule_idx = sheet.cssRules.length;
-
-      //default
-      var sel_before = '.f-dropdown.open:before',
-          sel_after  = '.f-dropdown.open:after',
-          css_before = 'left: ' + pip_offset_base + 'px;',
-          css_after  = 'left: ' + (pip_offset_base - 1) + 'px;';
-
-      if (position.missRight == true) {
-        pip_offset_base = dropdown.outerWidth() - 23;
-        sel_before = '.f-dropdown.open:before',
-        sel_after  = '.f-dropdown.open:after',
-        css_before = 'left: ' + pip_offset_base + 'px;',
-        css_after  = 'left: ' + (pip_offset_base - 1) + 'px;';
-      }
-
-      //just a case where right is fired, but its not missing right
-      if (position.triggeredRight == true) {
-        sel_before = '.f-dropdown.open:before',
-        sel_after  = '.f-dropdown.open:after',
-        css_before = 'left:-12px;',
-        css_after  = 'left:-14px;';
-      }
-
-      if (sheet.insertRule) {
-        sheet.insertRule([sel_before, '{', css_before, '}'].join(' '), this.rule_idx);
-        sheet.insertRule([sel_after, '{', css_after, '}'].join(' '), this.rule_idx + 1);
-      } else {
-        sheet.addRule(sel_before, css_before, this.rule_idx);
-        sheet.addRule(sel_after, css_after, this.rule_idx + 1);
-      }
-    },
-
-    // Remove old dropdown rule index
-    clear_idx : function () {
-      var sheet = Foundation.stylesheet;
-
-      if (typeof this.rule_idx !== 'undefined') {
-        sheet.deleteRule(this.rule_idx);
-        sheet.deleteRule(this.rule_idx);
-        delete this.rule_idx;
-      }
-    },
-
-    small : function () {
-      return matchMedia(Foundation.media_queries.small).matches &&
-        !matchMedia(Foundation.media_queries.medium).matches;
-    },
-
-    off : function () {
-      this.S(this.scope).off('.fndtn.dropdown');
-      this.S('html, body').off('.fndtn.dropdown');
-      this.S(window).off('.fndtn.dropdown');
-      this.S('[data-dropdown-content]').off('.fndtn.dropdown');
-    },
-
-    reflow : function () {}
-  };
-}(jQuery, window, window.document));
diff --git a/3dmol/js/foundation/foundation.equalizer.js b/3dmol/js/foundation/foundation.equalizer.js
deleted file mode 100644
index dd912a7..0000000
--- a/3dmol/js/foundation/foundation.equalizer.js
+++ /dev/null
@@ -1,77 +0,0 @@
-;(function ($, window, document, undefined) {
-  'use strict';
-
-  Foundation.libs.equalizer = {
-    name : 'equalizer',
-
-    version : '5.5.1',
-
-    settings : {
-      use_tallest : true,
-      before_height_change : $.noop,
-      after_height_change : $.noop,
-      equalize_on_stack : false
-    },
-
-    init : function (scope, method, options) {
-      Foundation.inherit(this, 'image_loaded');
-      this.bindings(method, options);
-      this.reflow();
-    },
-
-    events : function () {
-      this.S(window).off('.equalizer').on('resize.fndtn.equalizer', function (e) {
-        this.reflow();
-      }.bind(this));
-    },
-
-    equalize : function (equalizer) {
-      var isStacked = false,
-          vals = equalizer.find('[' + this.attr_name() + '-watch]:visible'),
-          settings = equalizer.data(this.attr_name(true) + '-init');
-
-      if (vals.length === 0) {
-        return;
-      }
-      var firstTopOffset = vals.first().offset().top;
-      settings.before_height_change();
-      equalizer.trigger('before-height-change').trigger('before-height-change.fndth.equalizer');
-      vals.height('inherit');
-      vals.each(function () {
-        var el = $(this);
-        if (el.offset().top !== firstTopOffset) {
-          isStacked = true;
-        }
-      });
-
-      if (settings.equalize_on_stack === false) {
-        if (isStacked) {
-          return;
-        }
-      };
-
-      var heights = vals.map(function () { return $(this).outerHeight(false) }).get();
-
-      if (settings.use_tallest) {
-        var max = Math.max.apply(null, heights);
-        vals.css('height', max);
-      } else {
-        var min = Math.min.apply(null, heights);
-        vals.css('height', min);
-      }
-      settings.after_height_change();
-      equalizer.trigger('after-height-change').trigger('after-height-change.fndtn.equalizer');
-    },
-
-    reflow : function () {
-      var self = this;
-
-      this.S('[' + this.attr_name() + ']', this.scope).each(function () {
-        var $eq_target = $(this);
-        self.image_loaded(self.S('img', this), function () {
-          self.equalize($eq_target)
-        });
-      });
-    }
-  };
-})(jQuery, window, window.document);
diff --git a/3dmol/js/foundation/foundation.interchange.js b/3dmol/js/foundation/foundation.interchange.js
deleted file mode 100644
index 9162a4c..0000000
--- a/3dmol/js/foundation/foundation.interchange.js
+++ /dev/null
@@ -1,354 +0,0 @@
-;(function ($, window, document, undefined) {
-  'use strict';
-
-  Foundation.libs.interchange = {
-    name : 'interchange',
-
-    version : '5.5.1',
-
-    cache : {},
-
-    images_loaded : false,
-    nodes_loaded : false,
-
-    settings : {
-      load_attr : 'interchange',
-
-      named_queries : {
-        'default'     : 'only screen',
-        'small'       : Foundation.media_queries['small'],
-        'small-only'  : Foundation.media_queries['small-only'],
-        'medium'      : Foundation.media_queries['medium'],
-        'medium-only' : Foundation.media_queries['medium-only'],
-        'large'       : Foundation.media_queries['large'],
-        'large-only'  : Foundation.media_queries['large-only'],
-        'xlarge'      : Foundation.media_queries['xlarge'],
-        'xlarge-only' : Foundation.media_queries['xlarge-only'],
-        'xxlarge'     : Foundation.media_queries['xxlarge'],
-        'landscape'   : 'only screen and (orientation: landscape)',
-        'portrait'    : 'only screen and (orientation: portrait)',
-        'retina'      : 'only screen and (-webkit-min-device-pixel-ratio: 2),' +
-          'only screen and (min--moz-device-pixel-ratio: 2),' +
-          'only screen and (-o-min-device-pixel-ratio: 2/1),' +
-          'only screen and (min-device-pixel-ratio: 2),' +
-          'only screen and (min-resolution: 192dpi),' +
-          'only screen and (min-resolution: 2dppx)'
-      },
-
-      directives : {
-        replace : function (el, path, trigger) {
-          // The trigger argument, if called within the directive, fires
-          // an event named after the directive on the element, passing
-          // any parameters along to the event that you pass to trigger.
-          //
-          // ex. trigger(), trigger([a, b, c]), or trigger(a, b, c)
-          //
-          // This allows you to bind a callback like so:
-          // $('#interchangeContainer').on('replace', function (e, a, b, c) {
-          //   console.log($(this).html(), a, b, c);
-          // });
-
-          if (/IMG/.test(el[0].nodeName)) {
-            var orig_path = el[0].src;
-
-            if (new RegExp(path, 'i').test(orig_path)) {
-              return;
-            }
-
-            el[0].src = path;
-
-            return trigger(el[0].src);
-          }
-          var last_path = el.data(this.data_attr + '-last-path'),
-              self = this;
-
-          if (last_path == path) {
-            return;
-          }
-
-          if (/\.(gif|jpg|jpeg|tiff|png)([?#].*)?/i.test(path)) {
-            $(el).css('background-image', 'url(' + path + ')');
-            el.data('interchange-last-path', path);
-            return trigger(path);
-          }
-
-          return $.get(path, function (response) {
-            el.html(response);
-            el.data(self.data_attr + '-last-path', path);
-            trigger();
-          });
-
-        }
-      }
-    },
-
-    init : function (scope, method, options) {
-      Foundation.inherit(this, 'throttle random_str');
-
-      this.data_attr = this.set_data_attr();
-      $.extend(true, this.settings, method, options);
-      this.bindings(method, options);
-      this.load('images');
-      this.load('nodes');
-    },
-
-    get_media_hash : function () {
-        var mediaHash = '';
-        for (var queryName in this.settings.named_queries ) {
-            mediaHash += matchMedia(this.settings.named_queries[queryName]).matches.toString();
-        }
-        return mediaHash;
-    },
-
-    events : function () {
-      var self = this, prevMediaHash;
-
-      $(window)
-        .off('.interchange')
-        .on('resize.fndtn.interchange', self.throttle(function () {
-            var currMediaHash = self.get_media_hash();
-            if (currMediaHash !== prevMediaHash) {
-                self.resize();
-            }
-            prevMediaHash = currMediaHash;
-        }, 50));
-
-      return this;
-    },
-
-    resize : function () {
-      var cache = this.cache;
-
-      if (!this.images_loaded || !this.nodes_loaded) {
-        setTimeout($.proxy(this.resize, this), 50);
-        return;
-      }
-
-      for (var uuid in cache) {
-        if (cache.hasOwnProperty(uuid)) {
-          var passed = this.results(uuid, cache[uuid]);
-
-          if (passed) {
-            this.settings.directives[passed
-              .scenario[1]].call(this, passed.el, passed.scenario[0], (function (passed) {
-                if (arguments[0] instanceof Array) { 
-                  var args = arguments[0];
-                } else {
-                  var args = Array.prototype.slice.call(arguments, 0);
-                }
-
-                return function() {
-                  passed.el.trigger(passed.scenario[1], args);
-                }
-              }(passed)));
-          }
-        }
-      }
-
-    },
-
-    results : function (uuid, scenarios) {
-      var count = scenarios.length;
-
-      if (count > 0) {
-        var el = this.S('[' + this.add_namespace('data-uuid') + '="' + uuid + '"]');
-
-        while (count--) {
-          var mq, rule = scenarios[count][2];
-          if (this.settings.named_queries.hasOwnProperty(rule)) {
-            mq = matchMedia(this.settings.named_queries[rule]);
-          } else {
-            mq = matchMedia(rule);
-          }
-          if (mq.matches) {
-            return {el : el, scenario : scenarios[count]};
-          }
-        }
-      }
-
-      return false;
-    },
-
-    load : function (type, force_update) {
-      if (typeof this['cached_' + type] === 'undefined' || force_update) {
-        this['update_' + type]();
-      }
-
-      return this['cached_' + type];
-    },
-
-    update_images : function () {
-      var images = this.S('img[' + this.data_attr + ']'),
-          count = images.length,
-          i = count,
-          loaded_count = 0,
-          data_attr = this.data_attr;
-
-      this.cache = {};
-      this.cached_images = [];
-      this.images_loaded = (count === 0);
-
-      while (i--) {
-        loaded_count++;
-        if (images[i]) {
-          var str = images[i].getAttribute(data_attr) || '';
-
-          if (str.length > 0) {
-            this.cached_images.push(images[i]);
-          }
-        }
-
-        if (loaded_count === count) {
-          this.images_loaded = true;
-          this.enhance('images');
-        }
-      }
-
-      return this;
-    },
-
-    update_nodes : function () {
-      var nodes = this.S('[' + this.data_attr + ']').not('img'),
-          count = nodes.length,
-          i = count,
-          loaded_count = 0,
-          data_attr = this.data_attr;
-
-      this.cached_nodes = [];
-      this.nodes_loaded = (count === 0);
-
-      while (i--) {
-        loaded_count++;
-        var str = nodes[i].getAttribute(data_attr) || '';
-
-        if (str.length > 0) {
-          this.cached_nodes.push(nodes[i]);
-        }
-
-        if (loaded_count === count) {
-          this.nodes_loaded = true;
-          this.enhance('nodes');
-        }
-      }
-
-      return this;
-    },
-
-    enhance : function (type) {
-      var i = this['cached_' + type].length;
-
-      while (i--) {
-        this.object($(this['cached_' + type][i]));
-      }
-
-      return $(window).trigger('resize').trigger('resize.fndtn.interchange');
-    },
-
-    convert_directive : function (directive) {
-
-      var trimmed = this.trim(directive);
-
-      if (trimmed.length > 0) {
-        return trimmed;
-      }
-
-      return 'replace';
-    },
-
-    parse_scenario : function (scenario) {
-      // This logic had to be made more complex since some users were using commas in the url path
-      // So we cannot simply just split on a comma
-      var directive_match = scenario[0].match(/(.+),\s*(\w+)\s*$/),
-      media_query         = scenario[1];
-
-      if (directive_match) {
-        var path  = directive_match[1],
-        directive = directive_match[2];
-      } else {
-        var cached_split = scenario[0].split(/,\s*$/),
-        path             = cached_split[0],
-        directive        = '';
-      }
-
-      return [this.trim(path), this.convert_directive(directive), this.trim(media_query)];
-    },
-
-    object : function (el) {
-      var raw_arr = this.parse_data_attr(el),
-          scenarios = [],
-          i = raw_arr.length;
-
-      if (i > 0) {
-        while (i--) {
-          var split = raw_arr[i].split(/\(([^\)]*?)(\))$/);
-
-          if (split.length > 1) {
-            var params = this.parse_scenario(split);
-            scenarios.push(params);
-          }
-        }
-      }
-
-      return this.store(el, scenarios);
-    },
-
-    store : function (el, scenarios) {
-      var uuid = this.random_str(),
-          current_uuid = el.data(this.add_namespace('uuid', true));
-
-      if (this.cache[current_uuid]) {
-        return this.cache[current_uuid];
-      }
-
-      el.attr(this.add_namespace('data-uuid'), uuid);
-
-      return this.cache[uuid] = scenarios;
-    },
-
-    trim : function (str) {
-
-      if (typeof str === 'string') {
-        return $.trim(str);
-      }
-
-      return str;
-    },
-
-    set_data_attr : function (init) {
-      if (init) {
-        if (this.namespace.length > 0) {
-          return this.namespace + '-' + this.settings.load_attr;
-        }
-
-        return this.settings.load_attr;
-      }
-
-      if (this.namespace.length > 0) {
-        return 'data-' + this.namespace + '-' + this.settings.load_attr;
-      }
-
-      return 'data-' + this.settings.load_attr;
-    },
-
-    parse_data_attr : function (el) {
-      var raw = el.attr(this.attr_name()).split(/\[(.*?)\]/),
-          i = raw.length,
-          output = [];
-
-      while (i--) {
-        if (raw[i].replace(/[\W\d]+/, '').length > 4) {
-          output.push(raw[i]);
-        }
-      }
-
-      return output;
-    },
-
-    reflow : function () {
-      this.load('images', true);
-      this.load('nodes', true);
-    }
-
-  };
-
-}(jQuery, window, window.document));
diff --git a/3dmol/js/foundation/foundation.joyride.js b/3dmol/js/foundation/foundation.joyride.js
deleted file mode 100644
index 7b259c3..0000000
--- a/3dmol/js/foundation/foundation.joyride.js
+++ /dev/null
@@ -1,932 +0,0 @@
-;(function ($, window, document, undefined) {
-  'use strict';
-
-  var Modernizr = Modernizr || false;
-
-  Foundation.libs.joyride = {
-    name : 'joyride',
-
-    version : '5.5.1',
-
-    defaults : {
-      expose                   : false,     // turn on or off the expose feature
-      modal                    : true,      // Whether to cover page with modal during the tour
-      keyboard                 : true,      // enable left, right and esc keystrokes
-      tip_location             : 'bottom',  // 'top' or 'bottom' in relation to parent
-      nub_position             : 'auto',    // override on a per tooltip bases
-      scroll_speed             : 1500,      // Page scrolling speed in milliseconds, 0 = no scroll animation
-      scroll_animation         : 'linear',  // supports 'swing' and 'linear', extend with jQuery UI.
-      timer                    : 0,         // 0 = no timer , all other numbers = timer in milliseconds
-      start_timer_on_click     : true,      // true or false - true requires clicking the first button start the timer
-      start_offset             : 0,         // the index of the tooltip you want to start on (index of the li)
-      next_button              : true,      // true or false to control whether a next button is used
-      prev_button              : true,      // true or false to control whether a prev button is used
-      tip_animation            : 'fade',    // 'pop' or 'fade' in each tip
-      pause_after              : [],        // array of indexes where to pause the tour after
-      exposed                  : [],        // array of expose elements
-      tip_animation_fade_speed : 300,       // when tipAnimation = 'fade' this is speed in milliseconds for the transition
-      cookie_monster           : false,     // true or false to control whether cookies are used
-      cookie_name              : 'joyride', // Name the cookie you'll use
-      cookie_domain            : false,     // Will this cookie be attached to a domain, ie. '.notableapp.com'
-      cookie_expires           : 365,       // set when you would like the cookie to expire.
-      tip_container            : 'body',    // Where will the tip be attached
-      abort_on_close           : true,      // When true, the close event will not fire any callback
-      tip_location_patterns    : {
-        top : ['bottom'],
-        bottom : [], // bottom should not need to be repositioned
-        left : ['right', 'top', 'bottom'],
-        right : ['left', 'top', 'bottom']
-      },
-      post_ride_callback     : function () {},    // A method to call once the tour closes (canceled or complete)
-      post_step_callback     : function () {},    // A method to call after each step
-      pre_step_callback      : function () {},    // A method to call before each step
-      pre_ride_callback      : function () {},    // A method to call before the tour starts (passed index, tip, and cloned exposed element)
-      post_expose_callback   : function () {},    // A method to call after an element has been exposed
-      template : { // HTML segments for tip layout
-        link          : '<a href="#close" class="joyride-close-tip">×</a>',
-        timer         : '<div class="joyride-timer-indicator-wrap"><span class="joyride-timer-indicator"></span></div>',
-        tip           : '<div class="joyride-tip-guide"><span class="joyride-nub"></span></div>',
-        wrapper       : '<div class="joyride-content-wrapper"></div>',
-        button        : '<a href="#" class="small button joyride-next-tip"></a>',
-        prev_button   : '<a href="#" class="small button joyride-prev-tip"></a>',
-        modal         : '<div class="joyride-modal-bg"></div>',
-        expose        : '<div class="joyride-expose-wrapper"></div>',
-        expose_cover  : '<div class="joyride-expose-cover"></div>'
-      },
-      expose_add_class : '' // One or more space-separated class names to be added to exposed element
-    },
-
-    init : function (scope, method, options) {
-      Foundation.inherit(this, 'throttle random_str');
-
-      this.settings = this.settings || $.extend({}, this.defaults, (options || method));
-
-      this.bindings(method, options)
-    },
-
-    go_next : function () {
-      if (this.settings.$li.next().length < 1) {
-        this.end();
-      } else if (this.settings.timer > 0) {
-        clearTimeout(this.settings.automate);
-        this.hide();
-        this.show();
-        this.startTimer();
-      } else {
-        this.hide();
-        this.show();
-      }
-    },
-
-    go_prev : function () {
-      if (this.settings.$li.prev().length < 1) {
-        // Do nothing if there are no prev element
-      } else if (this.settings.timer > 0) {
-        clearTimeout(this.settings.automate);
-        this.hide();
-        this.show(null, true);
-        this.startTimer();
-      } else {
-        this.hide();
-        this.show(null, true);
-      }
-    },
-
-    events : function () {
-      var self = this;
-
-      $(this.scope)
-        .off('.joyride')
-        .on('click.fndtn.joyride', '.joyride-next-tip, .joyride-modal-bg', function (e) {
-          e.preventDefault();
-          this.go_next()
-        }.bind(this))
-        .on('click.fndtn.joyride', '.joyride-prev-tip', function (e) {
-          e.preventDefault();
-          this.go_prev();
-        }.bind(this))
-
-        .on('click.fndtn.joyride', '.joyride-close-tip', function (e) {
-          e.preventDefault();
-          this.end(this.settings.abort_on_close);
-        }.bind(this))
-
-        .on('keyup.fndtn.joyride', function (e) {
-          // Don't do anything if keystrokes are disabled
-          // or if the joyride is not being shown
-          if (!this.settings.keyboard || !this.settings.riding) {
-            return;
-          }
-
-          switch (e.which) {
-            case 39: // right arrow
-              e.preventDefault();
-              this.go_next();
-              break;
-            case 37: // left arrow
-              e.preventDefault();
-              this.go_prev();
-              break;
-            case 27: // escape
-              e.preventDefault();
-              this.end(this.settings.abort_on_close);
-          }
-        }.bind(this));
-
-      $(window)
-        .off('.joyride')
-        .on('resize.fndtn.joyride', self.throttle(function () {
-          if ($('[' + self.attr_name() + ']').length > 0 && self.settings.$next_tip && self.settings.riding) {
-            if (self.settings.exposed.length > 0) {
-              var $els = $(self.settings.exposed);
-
-              $els.each(function () {
-                var $this = $(this);
-                self.un_expose($this);
-                self.expose($this);
-              });
-            }
-
-            if (self.is_phone()) {
-              self.pos_phone();
-            } else {
-              self.pos_default(false);
-            }
-          }
-        }, 100));
-    },
-
-    start : function () {
-      var self = this,
-          $this = $('[' + this.attr_name() + ']', this.scope),
-          integer_settings = ['timer', 'scrollSpeed', 'startOffset', 'tipAnimationFadeSpeed', 'cookieExpires'],
-          int_settings_count = integer_settings.length;
-
-      if (!$this.length > 0) {
-        return;
-      }
-
-      if (!this.settings.init) {
-        this.events();
-      }
-
-      this.settings = $this.data(this.attr_name(true) + '-init');
-
-      // non configureable settings
-      this.settings.$content_el = $this;
-      this.settings.$body = $(this.settings.tip_container);
-      this.settings.body_offset = $(this.settings.tip_container).position();
-      this.settings.$tip_content = this.settings.$content_el.find('> li');
-      this.settings.paused = false;
-      this.settings.attempts = 0;
-      this.settings.riding = true;
-
-      // can we create cookies?
-      if (typeof $.cookie !== 'function') {
-        this.settings.cookie_monster = false;
-      }
-
-      // generate the tips and insert into dom.
-      if (!this.settings.cookie_monster || this.settings.cookie_monster && !$.cookie(this.settings.cookie_name)) {
-        this.settings.$tip_content.each(function (index) {
-          var $this = $(this);
-          this.settings = $.extend({}, self.defaults, self.data_options($this));
-
-          // Make sure that settings parsed from data_options are integers where necessary
-          var i = int_settings_count;
-          while (i--) {
-            self.settings[integer_settings[i]] = parseInt(self.settings[integer_settings[i]], 10);
-          }
-          self.create({$li : $this, index : index});
-        });
-
-        // show first tip
-        if (!this.settings.start_timer_on_click && this.settings.timer > 0) {
-          this.show('init');
-          this.startTimer();
-        } else {
-          this.show('init');
-        }
-
-      }
-    },
-
-    resume : function () {
-      this.set_li();
-      this.show();
-    },
-
-    tip_template : function (opts) {
-      var $blank, content;
-
-      opts.tip_class = opts.tip_class || '';
-
-      $blank = $(this.settings.template.tip).addClass(opts.tip_class);
-      content = $.trim($(opts.li).html()) +
-        this.prev_button_text(opts.prev_button_text, opts.index) +
-        this.button_text(opts.button_text) +
-        this.settings.template.link +
-        this.timer_instance(opts.index);
-
-      $blank.append($(this.settings.template.wrapper));
-      $blank.first().attr(this.add_namespace('data-index'), opts.index);
-      $('.joyride-content-wrapper', $blank).append(content);
-
-      return $blank[0];
-    },
-
-    timer_instance : function (index) {
-      var txt;
-
-      if ((index === 0 && this.settings.start_timer_on_click && this.settings.timer > 0) || this.settings.timer === 0) {
-        txt = '';
-      } else {
-        txt = $(this.settings.template.timer)[0].outerHTML;
-      }
-      return txt;
-    },
-
-    button_text : function (txt) {
-      if (this.settings.tip_settings.next_button) {
-        txt = $.trim(txt) || 'Next';
-        txt = $(this.settings.template.button).append(txt)[0].outerHTML;
-      } else {
-        txt = '';
-      }
-      return txt;
-    },
-
-    prev_button_text : function (txt, idx) {
-      if (this.settings.tip_settings.prev_button) {
-        txt = $.trim(txt) || 'Previous';
-
-        // Add the disabled class to the button if it's the first element
-        if (idx == 0) {
-          txt = $(this.settings.template.prev_button).append(txt).addClass('disabled')[0].outerHTML;
-        } else {
-          txt = $(this.settings.template.prev_button).append(txt)[0].outerHTML;
-        }
-      } else {
-        txt = '';
-      }
-      return txt;
-    },
-
-    create : function (opts) {
-      this.settings.tip_settings = $.extend({}, this.settings, this.data_options(opts.$li));
-      var buttonText = opts.$li.attr(this.add_namespace('data-button')) || opts.$li.attr(this.add_namespace('data-text')),
-          prevButtonText = opts.$li.attr(this.add_namespace('data-button-prev')) || opts.$li.attr(this.add_namespace('data-prev-text')),
-        tipClass = opts.$li.attr('class'),
-        $tip_content = $(this.tip_template({
-          tip_class : tipClass,
-          index : opts.index,
-          button_text : buttonText,
-          prev_button_text : prevButtonText,
-          li : opts.$li
-        }));
-
-      $(this.settings.tip_container).append($tip_content);
-    },
-
-    show : function (init, is_prev) {
-      var $timer = null;
-
-      // are we paused?
-      if (this.settings.$li === undefined || ($.inArray(this.settings.$li.index(), this.settings.pause_after) === -1)) {
-
-        // don't go to the next li if the tour was paused
-        if (this.settings.paused) {
-          this.settings.paused = false;
-        } else {
-          this.set_li(init, is_prev);
-        }
-
-        this.settings.attempts = 0;
-
-        if (this.settings.$li.length && this.settings.$target.length > 0) {
-          if (init) { //run when we first start
-            this.settings.pre_ride_callback(this.settings.$li.index(), this.settings.$next_tip);
-            if (this.settings.modal) {
-              this.show_modal();
-            }
-          }
-
-          this.settings.pre_step_callback(this.settings.$li.index(), this.settings.$next_tip);
-
-          if (this.settings.modal && this.settings.expose) {
-            this.expose();
-          }
-
-          this.settings.tip_settings = $.extend({}, this.settings, this.data_options(this.settings.$li));
-
-          this.settings.timer = parseInt(this.settings.timer, 10);
-
-          this.settings.tip_settings.tip_location_pattern = this.settings.tip_location_patterns[this.settings.tip_settings.tip_location];
-
-          // scroll and hide bg if not modal
-          if (!/body/i.test(this.settings.$target.selector)) {
-            var joyridemodalbg = $('.joyride-modal-bg');
-            if (/pop/i.test(this.settings.tipAnimation)) {
-                joyridemodalbg.hide();
-            } else {
-                joyridemodalbg.fadeOut(this.settings.tipAnimationFadeSpeed);
-            }
-            this.scroll_to();
-          }
-
-          if (this.is_phone()) {
-            this.pos_phone(true);
-          } else {
-            this.pos_default(true);
-          }
-
-          $timer = this.settings.$next_tip.find('.joyride-timer-indicator');
-
-          if (/pop/i.test(this.settings.tip_animation)) {
-
-            $timer.width(0);
-
-            if (this.settings.timer > 0) {
-
-              this.settings.$next_tip.show();
-
-              setTimeout(function () {
-                $timer.animate({
-                  width : $timer.parent().width()
-                }, this.settings.timer, 'linear');
-              }.bind(this), this.settings.tip_animation_fade_speed);
-
-            } else {
-              this.settings.$next_tip.show();
-
-            }
-
-          } else if (/fade/i.test(this.settings.tip_animation)) {
-
-            $timer.width(0);
-
-            if (this.settings.timer > 0) {
-
-              this.settings.$next_tip
-                .fadeIn(this.settings.tip_animation_fade_speed)
-                .show();
-
-              setTimeout(function () {
-                $timer.animate({
-                  width : $timer.parent().width()
-                }, this.settings.timer, 'linear');
-              }.bind(this), this.settings.tip_animation_fade_speed);
-
-            } else {
-              this.settings.$next_tip.fadeIn(this.settings.tip_animation_fade_speed);
-            }
-          }
-
-          this.settings.$current_tip = this.settings.$next_tip;
-
-        // skip non-existant targets
-        } else if (this.settings.$li && this.settings.$target.length < 1) {
-
-          this.show(init, is_prev);
-
-        } else {
-
-          this.end();
-
-        }
-      } else {
-
-        this.settings.paused = true;
-
-      }
-
-    },
-
-    is_phone : function () {
-      return matchMedia(Foundation.media_queries.small).matches &&
-        !matchMedia(Foundation.media_queries.medium).matches;
-    },
-
-    hide : function () {
-      if (this.settings.modal && this.settings.expose) {
-        this.un_expose();
-      }
-
-      if (!this.settings.modal) {
-        $('.joyride-modal-bg').hide();
-      }
-
-      // Prevent scroll bouncing...wait to remove from layout
-      this.settings.$current_tip.css('visibility', 'hidden');
-      setTimeout($.proxy(function () {
-        this.hide();
-        this.css('visibility', 'visible');
-      }, this.settings.$current_tip), 0);
-      this.settings.post_step_callback(this.settings.$li.index(),
-        this.settings.$current_tip);
-    },
-
-    set_li : function (init, is_prev) {
-      if (init) {
-        this.settings.$li = this.settings.$tip_content.eq(this.settings.start_offset);
-        this.set_next_tip();
-        this.settings.$current_tip = this.settings.$next_tip;
-      } else {
-        if (is_prev) {
-          this.settings.$li = this.settings.$li.prev();
-        } else {
-          this.settings.$li = this.settings.$li.next();
-        }
-        this.set_next_tip();
-      }
-
-      this.set_target();
-    },
-
-    set_next_tip : function () {
-      this.settings.$next_tip = $('.joyride-tip-guide').eq(this.settings.$li.index());
-      this.settings.$next_tip.data('closed', '');
-    },
-
-    set_target : function () {
-      var cl = this.settings.$li.attr(this.add_namespace('data-class')),
-          id = this.settings.$li.attr(this.add_namespace('data-id')),
-          $sel = function () {
-            if (id) {
-              return $(document.getElementById(id));
-            } else if (cl) {
-              return $('.' + cl).first();
-            } else {
-              return $('body');
-            }
-          };
-
-      this.settings.$target = $sel();
-    },
-
-    scroll_to : function () {
-      var window_half, tipOffset;
-
-      window_half = $(window).height() / 2;
-      tipOffset = Math.ceil(this.settings.$target.offset().top - window_half + this.settings.$next_tip.outerHeight());
-
-      if (tipOffset != 0) {
-        $('html, body').stop().animate({
-          scrollTop : tipOffset
-        }, this.settings.scroll_speed, 'swing');
-      }
-    },
-
-    paused : function () {
-      return ($.inArray((this.settings.$li.index() + 1), this.settings.pause_after) === -1);
-    },
-
-    restart : function () {
-      this.hide();
-      this.settings.$li = undefined;
-      this.show('init');
-    },
-
-    pos_default : function (init) {
-      var $nub = this.settings.$next_tip.find('.joyride-nub'),
-          nub_width = Math.ceil($nub.outerWidth() / 2),
-          nub_height = Math.ceil($nub.outerHeight() / 2),
-          toggle = init || false;
-
-      // tip must not be "display: none" to calculate position
-      if (toggle) {
-        this.settings.$next_tip.css('visibility', 'hidden');
-        this.settings.$next_tip.show();
-      }
-
-      if (!/body/i.test(this.settings.$target.selector)) {
-          var topAdjustment = this.settings.tip_settings.tipAdjustmentY ? parseInt(this.settings.tip_settings.tipAdjustmentY) : 0,
-              leftAdjustment = this.settings.tip_settings.tipAdjustmentX ? parseInt(this.settings.tip_settings.tipAdjustmentX) : 0;
-
-          if (this.bottom()) {
-            if (this.rtl) {
-              this.settings.$next_tip.css({
-                top : (this.settings.$target.offset().top + nub_height + this.settings.$target.outerHeight() + topAdjustment),
-                left : this.settings.$target.offset().left + this.settings.$target.outerWidth() - this.settings.$next_tip.outerWidth() + leftAdjustment});
-            } else {
-              this.settings.$next_tip.css({
-                top : (this.settings.$target.offset().top + nub_height + this.settings.$target.outerHeight() + topAdjustment),
-                left : this.settings.$target.offset().left + leftAdjustment});
-            }
-
-            this.nub_position($nub, this.settings.tip_settings.nub_position, 'top');
-
-          } else if (this.top()) {
-            if (this.rtl) {
-              this.settings.$next_tip.css({
-                top : (this.settings.$target.offset().top - this.settings.$next_tip.outerHeight() - nub_height + topAdjustment),
-                left : this.settings.$target.offset().left + this.settings.$target.outerWidth() - this.settings.$next_tip.outerWidth()});
-            } else {
-              this.settings.$next_tip.css({
-                top : (this.settings.$target.offset().top - this.settings.$next_tip.outerHeight() - nub_height + topAdjustment),
-                left : this.settings.$target.offset().left + leftAdjustment});
-            }
-
-            this.nub_position($nub, this.settings.tip_settings.nub_position, 'bottom');
-
-          } else if (this.right()) {
-
-            this.settings.$next_tip.css({
-              top : this.settings.$target.offset().top + topAdjustment,
-              left : (this.settings.$target.outerWidth() + this.settings.$target.offset().left + nub_width + leftAdjustment)});
-
-            this.nub_position($nub, this.settings.tip_settings.nub_position, 'left');
-
-          } else if (this.left()) {
-
-            this.settings.$next_tip.css({
-              top : this.settings.$target.offset().top + topAdjustment,
-              left : (this.settings.$target.offset().left - this.settings.$next_tip.outerWidth() - nub_width + leftAdjustment)});
-
-            this.nub_position($nub, this.settings.tip_settings.nub_position, 'right');
-
-          }
-
-          if (!this.visible(this.corners(this.settings.$next_tip)) && this.settings.attempts < this.settings.tip_settings.tip_location_pattern.length) {
-
-            $nub.removeClass('bottom')
-              .removeClass('top')
-              .removeClass('right')
-              .removeClass('left');
-
-            this.settings.tip_settings.tip_location = this.settings.tip_settings.tip_location_pattern[this.settings.attempts];
-
-            this.settings.attempts++;
-
-            this.pos_default();
-
-          }
-
-      } else if (this.settings.$li.length) {
-
-        this.pos_modal($nub);
-
-      }
-
-      if (toggle) {
-        this.settings.$next_tip.hide();
-        this.settings.$next_tip.css('visibility', 'visible');
-      }
-
-    },
-
-    pos_phone : function (init) {
-      var tip_height = this.settings.$next_tip.outerHeight(),
-          tip_offset = this.settings.$next_tip.offset(),
-          target_height = this.settings.$target.outerHeight(),
-          $nub = $('.joyride-nub', this.settings.$next_tip),
-          nub_height = Math.ceil($nub.outerHeight() / 2),
-          toggle = init || false;
-
-      $nub.removeClass('bottom')
-        .removeClass('top')
-        .removeClass('right')
-        .removeClass('left');
-
-      if (toggle) {
-        this.settings.$next_tip.css('visibility', 'hidden');
-        this.settings.$next_tip.show();
-      }
-
-      if (!/body/i.test(this.settings.$target.selector)) {
-
-        if (this.top()) {
-
-            this.settings.$next_tip.offset({top : this.settings.$target.offset().top - tip_height - nub_height});
-            $nub.addClass('bottom');
-
-        } else {
-
-          this.settings.$next_tip.offset({top : this.settings.$target.offset().top + target_height + nub_height});
-          $nub.addClass('top');
-
-        }
-
-      } else if (this.settings.$li.length) {
-        this.pos_modal($nub);
-      }
-
-      if (toggle) {
-        this.settings.$next_tip.hide();
-        this.settings.$next_tip.css('visibility', 'visible');
-      }
-    },
-
-    pos_modal : function ($nub) {
-      this.center();
-      $nub.hide();
-
-      this.show_modal();
-    },
-
-    show_modal : function () {
-      if (!this.settings.$next_tip.data('closed')) {
-        var joyridemodalbg =  $('.joyride-modal-bg');
-        if (joyridemodalbg.length < 1) {
-          var joyridemodalbg = $(this.settings.template.modal);
-          joyridemodalbg.appendTo('body');
-        }
-
-        if (/pop/i.test(this.settings.tip_animation)) {
-            joyridemodalbg.show();
-        } else {
-            joyridemodalbg.fadeIn(this.settings.tip_animation_fade_speed);
-        }
-      }
-    },
-
-    expose : function () {
-      var expose,
-          exposeCover,
-          el,
-          origCSS,
-          origClasses,
-          randId = 'expose-' + this.random_str(6);
-
-      if (arguments.length > 0 && arguments[0] instanceof $) {
-        el = arguments[0];
-      } else if (this.settings.$target && !/body/i.test(this.settings.$target.selector)) {
-        el = this.settings.$target;
-      } else {
-        return false;
-      }
-
-      if (el.length < 1) {
-        if (window.console) {
-          console.error('element not valid', el);
-        }
-        return false;
-      }
-
-      expose = $(this.settings.template.expose);
-      this.settings.$body.append(expose);
-      expose.css({
-        top : el.offset().top,
-        left : el.offset().left,
-        width : el.outerWidth(true),
-        height : el.outerHeight(true)
-      });
-
-      exposeCover = $(this.settings.template.expose_cover);
-
-      origCSS = {
-        zIndex : el.css('z-index'),
-        position : el.css('position')
-      };
-
-      origClasses = el.attr('class') == null ? '' : el.attr('class');
-
-      el.css('z-index', parseInt(expose.css('z-index')) + 1);
-
-      if (origCSS.position == 'static') {
-        el.css('position', 'relative');
-      }
-
-      el.data('expose-css', origCSS);
-      el.data('orig-class', origClasses);
-      el.attr('class', origClasses + ' ' + this.settings.expose_add_class);
-
-      exposeCover.css({
-        top : el.offset().top,
-        left : el.offset().left,
-        width : el.outerWidth(true),
-        height : el.outerHeight(true)
-      });
-
-      if (this.settings.modal) {
-        this.show_modal();
-      }
-
-      this.settings.$body.append(exposeCover);
-      expose.addClass(randId);
-      exposeCover.addClass(randId);
-      el.data('expose', randId);
-      this.settings.post_expose_callback(this.settings.$li.index(), this.settings.$next_tip, el);
-      this.add_exposed(el);
-    },
-
-    un_expose : function () {
-      var exposeId,
-          el,
-          expose,
-          origCSS,
-          origClasses,
-          clearAll = false;
-
-      if (arguments.length > 0 && arguments[0] instanceof $) {
-        el = arguments[0];
-      } else if (this.settings.$target && !/body/i.test(this.settings.$target.selector)) {
-        el = this.settings.$target;
-      } else {
-        return false;
-      }
-
-      if (el.length < 1) {
-        if (window.console) {
-          console.error('element not valid', el);
-        }
-        return false;
-      }
-
-      exposeId = el.data('expose');
-      expose = $('.' + exposeId);
-
-      if (arguments.length > 1) {
-        clearAll = arguments[1];
-      }
-
-      if (clearAll === true) {
-        $('.joyride-expose-wrapper,.joyride-expose-cover').remove();
-      } else {
-        expose.remove();
-      }
-
-      origCSS = el.data('expose-css');
-
-      if (origCSS.zIndex == 'auto') {
-        el.css('z-index', '');
-      } else {
-        el.css('z-index', origCSS.zIndex);
-      }
-
-      if (origCSS.position != el.css('position')) {
-        if (origCSS.position == 'static') {// this is default, no need to set it.
-          el.css('position', '');
-        } else {
-          el.css('position', origCSS.position);
-        }
-      }
-
-      origClasses = el.data('orig-class');
-      el.attr('class', origClasses);
-      el.removeData('orig-classes');
-
-      el.removeData('expose');
-      el.removeData('expose-z-index');
-      this.remove_exposed(el);
-    },
-
-    add_exposed : function (el) {
-      this.settings.exposed = this.settings.exposed || [];
-      if (el instanceof $ || typeof el === 'object') {
-        this.settings.exposed.push(el[0]);
-      } else if (typeof el == 'string') {
-        this.settings.exposed.push(el);
-      }
-    },
-
-    remove_exposed : function (el) {
-      var search, i;
-      if (el instanceof $) {
-        search = el[0]
-      } else if (typeof el == 'string') {
-        search = el;
-      }
-
-      this.settings.exposed = this.settings.exposed || [];
-      i = this.settings.exposed.length;
-
-      while (i--) {
-        if (this.settings.exposed[i] == search) {
-          this.settings.exposed.splice(i, 1);
-          return;
-        }
-      }
-    },
-
-    center : function () {
-      var $w = $(window);
-
-      this.settings.$next_tip.css({
-        top : ((($w.height() - this.settings.$next_tip.outerHeight()) / 2) + $w.scrollTop()),
-        left : ((($w.width() - this.settings.$next_tip.outerWidth()) / 2) + $w.scrollLeft())
-      });
-
-      return true;
-    },
-
-    bottom : function () {
-      return /bottom/i.test(this.settings.tip_settings.tip_location);
-    },
-
-    top : function () {
-      return /top/i.test(this.settings.tip_settings.tip_location);
-    },
-
-    right : function () {
-      return /right/i.test(this.settings.tip_settings.tip_location);
-    },
-
-    left : function () {
-      return /left/i.test(this.settings.tip_settings.tip_location);
-    },
-
-    corners : function (el) {
-      var w = $(window),
-          window_half = w.height() / 2,
-          //using this to calculate since scroll may not have finished yet.
-          tipOffset = Math.ceil(this.settings.$target.offset().top - window_half + this.settings.$next_tip.outerHeight()),
-          right = w.width() + w.scrollLeft(),
-          offsetBottom =  w.height() + tipOffset,
-          bottom = w.height() + w.scrollTop(),
-          top = w.scrollTop();
-
-      if (tipOffset < top) {
-        if (tipOffset < 0) {
-          top = 0;
-        } else {
-          top = tipOffset;
-        }
-      }
-
-      if (offsetBottom > bottom) {
-        bottom = offsetBottom;
-      }
-
-      return [
-        el.offset().top < top,
-        right < el.offset().left + el.outerWidth(),
-        bottom < el.offset().top + el.outerHeight(),
-        w.scrollLeft() > el.offset().left
-      ];
-    },
-
-    visible : function (hidden_corners) {
-      var i = hidden_corners.length;
-
-      while (i--) {
-        if (hidden_corners[i]) {
-          return false;
-        }
-      }
-
-      return true;
-    },
-
-    nub_position : function (nub, pos, def) {
-      if (pos === 'auto') {
-        nub.addClass(def);
-      } else {
-        nub.addClass(pos);
-      }
-    },
-
-    startTimer : function () {
-      if (this.settings.$li.length) {
-        this.settings.automate = setTimeout(function () {
-          this.hide();
-          this.show();
-          this.startTimer();
-        }.bind(this), this.settings.timer);
-      } else {
-        clearTimeout(this.settings.automate);
-      }
-    },
-
-    end : function (abort) {
-      if (this.settings.cookie_monster) {
-        $.cookie(this.settings.cookie_name, 'ridden', {expires : this.settings.cookie_expires, domain : this.settings.cookie_domain});
-      }
-
-      if (this.settings.timer > 0) {
-        clearTimeout(this.settings.automate);
-      }
-
-      if (this.settings.modal && this.settings.expose) {
-        this.un_expose();
-      }
-
-      // Unplug keystrokes listener
-      $(this.scope).off('keyup.joyride')
-
-      this.settings.$next_tip.data('closed', true);
-      this.settings.riding = false;
-
-      $('.joyride-modal-bg').hide();
-      this.settings.$current_tip.hide();
-
-      if (typeof abort === 'undefined' || abort === false) {
-        this.settings.post_step_callback(this.settings.$li.index(), this.settings.$current_tip);
-        this.settings.post_ride_callback(this.settings.$li.index(), this.settings.$current_tip);
-      }
-
-      $('.joyride-tip-guide').remove();
-    },
-
-    off : function () {
-      $(this.scope).off('.joyride');
-      $(window).off('.joyride');
-      $('.joyride-close-tip, .joyride-next-tip, .joyride-modal-bg').off('.joyride');
-      $('.joyride-tip-guide, .joyride-modal-bg').remove();
-      clearTimeout(this.settings.automate);
-      this.settings = {};
-    },
-
-    reflow : function () {}
-  };
-}(jQuery, window, window.document));
diff --git a/3dmol/js/foundation/foundation.js b/3dmol/js/foundation/foundation.js
deleted file mode 100644
index 65e6304..0000000
--- a/3dmol/js/foundation/foundation.js
+++ /dev/null
@@ -1,703 +0,0 @@
-/*
- * Foundation Responsive Library
- * http://foundation.zurb.com
- * Copyright 2014, ZURB
- * Free to use under the MIT license.
- * http://www.opensource.org/licenses/mit-license.php
-*/
-
-(function ($, window, document, undefined) {
-  'use strict';
-
-  var header_helpers = function (class_array) {
-    var i = class_array.length;
-    var head = $('head');
-
-    while (i--) {
-      if (head.has('.' + class_array[i]).length === 0) {
-        head.append('<meta class="' + class_array[i] + '" />');
-      }
-    }
-  };
-
-  header_helpers([
-    'foundation-mq-small',
-    'foundation-mq-small-only',
-    'foundation-mq-medium',
-    'foundation-mq-medium-only',
-    'foundation-mq-large',
-    'foundation-mq-large-only',
-    'foundation-mq-xlarge',
-    'foundation-mq-xlarge-only',
-    'foundation-mq-xxlarge',
-    'foundation-data-attribute-namespace']);
-
-  // Enable FastClick if present
-
-  $(function () {
-    if (typeof FastClick !== 'undefined') {
-      // Don't attach to body if undefined
-      if (typeof document.body !== 'undefined') {
-        FastClick.attach(document.body);
-      }
-    }
-  });
-
-  // private Fast Selector wrapper,
-  // returns jQuery object. Only use where
-  // getElementById is not available.
-  var S = function (selector, context) {
-    if (typeof selector === 'string') {
-      if (context) {
-        var cont;
-        if (context.jquery) {
-          cont = context[0];
-          if (!cont) {
-            return context;
-          }
-        } else {
-          cont = context;
-        }
-        return $(cont.querySelectorAll(selector));
-      }
-
-      return $(document.querySelectorAll(selector));
-    }
-
-    return $(selector, context);
-  };
-
-  // Namespace functions.
-
-  var attr_name = function (init) {
-    var arr = [];
-    if (!init) {
-      arr.push('data');
-    }
-    if (this.namespace.length > 0) {
-      arr.push(this.namespace);
-    }
-    arr.push(this.name);
-
-    return arr.join('-');
-  };
-
-  var add_namespace = function (str) {
-    var parts = str.split('-'),
-        i = parts.length,
-        arr = [];
-
-    while (i--) {
-      if (i !== 0) {
-        arr.push(parts[i]);
-      } else {
-        if (this.namespace.length > 0) {
-          arr.push(this.namespace, parts[i]);
-        } else {
-          arr.push(parts[i]);
-        }
-      }
-    }
-
-    return arr.reverse().join('-');
-  };
-
-  // Event binding and data-options updating.
-
-  var bindings = function (method, options) {
-    var self = this,
-        bind = function(){
-          var $this = S(this),
-              should_bind_events = !$this.data(self.attr_name(true) + '-init');
-          $this.data(self.attr_name(true) + '-init', $.extend({}, self.settings, (options || method), self.data_options($this)));
-
-          if (should_bind_events) {
-            self.events(this);
-          }
-        };
-
-    if (S(this.scope).is('[' + this.attr_name() +']')) {
-      bind.call(this.scope);
-    } else {
-      S('[' + this.attr_name() +']', this.scope).each(bind);
-    }
-    // # Patch to fix #5043 to move this *after* the if/else clause in order for Backbone and similar frameworks to have improved control over event binding and data-options updating.
-    if (typeof method === 'string') {
-      return this[method].call(this, options);
-    }
-
-  };
-
-  var single_image_loaded = function (image, callback) {
-    function loaded () {
-      callback(image[0]);
-    }
-
-    function bindLoad () {
-      this.one('load', loaded);
-
-      if (/MSIE (\d+\.\d+);/.test(navigator.userAgent)) {
-        var src = this.attr( 'src' ),
-            param = src.match( /\?/ ) ? '&' : '?';
-
-        param += 'random=' + (new Date()).getTime();
-        this.attr('src', src + param);
-      }
-    }
-
-    if (!image.attr('src')) {
-      loaded();
-      return;
-    }
-
-    if (image[0].complete || image[0].readyState === 4) {
-      loaded();
-    } else {
-      bindLoad.call(image);
-    }
-  };
-
-  /*
-    https://github.com/paulirish/matchMedia.js
-  */
-
-  window.matchMedia = window.matchMedia || (function ( doc ) {
-
-    'use strict';
-
-    var bool,
-        docElem = doc.documentElement,
-        refNode = docElem.firstElementChild || docElem.firstChild,
-        // fakeBody required for <FF4 when executed in <head>
-        fakeBody = doc.createElement( 'body' ),
-        div = doc.createElement( 'div' );
-
-    div.id = 'mq-test-1';
-    div.style.cssText = 'position:absolute;top:-100em';
-    fakeBody.style.background = 'none';
-    fakeBody.appendChild(div);
-
-    return function (q) {
-
-      div.innerHTML = '­<style media="' + q + '"> #mq-test-1 { width: 42px; }</style>';
-
-      docElem.insertBefore( fakeBody, refNode );
-      bool = div.offsetWidth === 42;
-      docElem.removeChild( fakeBody );
-
-      return {
-        matches : bool,
-        media : q
-      };
-
-    };
-
-  }( document ));
-
-  /*
-   * jquery.requestAnimationFrame
-   * https://github.com/gnarf37/jquery-requestAnimationFrame
-   * Requires jQuery 1.8+
-   *
-   * Copyright (c) 2012 Corey Frang
-   * Licensed under the MIT license.
-   */
-
-  (function(jQuery) {
-
-
-  // requestAnimationFrame polyfill adapted from Erik Möller
-  // fixes from Paul Irish and Tino Zijdel
-  // http://paulirish.com/2011/requestanimationframe-for-smart-animating/
-  // http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
-
-  var animating,
-      lastTime = 0,
-      vendors = ['webkit', 'moz'],
-      requestAnimationFrame = window.requestAnimationFrame,
-      cancelAnimationFrame = window.cancelAnimationFrame,
-      jqueryFxAvailable = 'undefined' !== typeof jQuery.fx;
-
-  for (; lastTime < vendors.length && !requestAnimationFrame; lastTime++) {
-    requestAnimationFrame = window[ vendors[lastTime] + 'RequestAnimationFrame' ];
-    cancelAnimationFrame = cancelAnimationFrame ||
-      window[ vendors[lastTime] + 'CancelAnimationFrame' ] ||
-      window[ vendors[lastTime] + 'CancelRequestAnimationFrame' ];
-  }
-
-  function raf() {
-    if (animating) {
-      requestAnimationFrame(raf);
-
-      if (jqueryFxAvailable) {
-        jQuery.fx.tick();
-      }
-    }
-  }
-
-  if (requestAnimationFrame) {
-    // use rAF
-    window.requestAnimationFrame = requestAnimationFrame;
-    window.cancelAnimationFrame = cancelAnimationFrame;
-
-    if (jqueryFxAvailable) {
-      jQuery.fx.timer = function (timer) {
-        if (timer() && jQuery.timers.push(timer) && !animating) {
-          animating = true;
-          raf();
-        }
-      };
-
-      jQuery.fx.stop = function () {
-        animating = false;
-      };
-    }
-  } else {
-    // polyfill
-    window.requestAnimationFrame = function (callback) {
-      var currTime = new Date().getTime(),
-        timeToCall = Math.max(0, 16 - (currTime - lastTime)),
-        id = window.setTimeout(function () {
-          callback(currTime + timeToCall);
-        }, timeToCall);
-      lastTime = currTime + timeToCall;
-      return id;
-    };
-
-    window.cancelAnimationFrame = function (id) {
-      clearTimeout(id);
-    };
-
-  }
-
-  }( $ ));
-
-  function removeQuotes (string) {
-    if (typeof string === 'string' || string instanceof String) {
-      string = string.replace(/^['\\/"]+|(;\s?})+|['\\/"]+$/g, '');
-    }
-
-    return string;
-  }
-
-  window.Foundation = {
-    name : 'Foundation',
-
-    version : '5.5.1',
-
-    media_queries : {
-      'small'       : S('.foundation-mq-small').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, ''),
-      'small-only'  : S('.foundation-mq-small-only').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, ''),
-      'medium'      : S('.foundation-mq-medium').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, ''),
-      'medium-only' : S('.foundation-mq-medium-only').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, ''),
-      'large'       : S('.foundation-mq-large').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, ''),
-      'large-only'  : S('.foundation-mq-large-only').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, ''),
-      'xlarge'      : S('.foundation-mq-xlarge').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, ''),
-      'xlarge-only' : S('.foundation-mq-xlarge-only').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, ''),
-      'xxlarge'     : S('.foundation-mq-xxlarge').css('font-family').replace(/^[\/\\'"]+|(;\s?})+|[\/\\'"]+$/g, '')
-    },
-
-    stylesheet : $('<style></style>').appendTo('head')[0].sheet,
-
-    global : {
-      namespace : undefined
-    },
-
-    init : function (scope, libraries, method, options, response) {
-      var args = [scope, method, options, response],
-          responses = [];
-
-      // check RTL
-      this.rtl = /rtl/i.test(S('html').attr('dir'));
-
-      // set foundation global scope
-      this.scope = scope || this.scope;
-
-      this.set_namespace();
-
-      if (libraries && typeof libraries === 'string' && !/reflow/i.test(libraries)) {
-        if (this.libs.hasOwnProperty(libraries)) {
-          responses.push(this.init_lib(libraries, args));
-        }
-      } else {
-        for (var lib in this.libs) {
-          responses.push(this.init_lib(lib, libraries));
-        }
-      }
-
-      S(window).load(function () {
-        S(window)
-          .trigger('resize.fndtn.clearing')
-          .trigger('resize.fndtn.dropdown')
-          .trigger('resize.fndtn.equalizer')
-          .trigger('resize.fndtn.interchange')
-          .trigger('resize.fndtn.joyride')
-          .trigger('resize.fndtn.magellan')
-          .trigger('resize.fndtn.topbar')
-          .trigger('resize.fndtn.slider');
-      });
-
-      return scope;
-    },
-
-    init_lib : function (lib, args) {
-      if (this.libs.hasOwnProperty(lib)) {
-        this.patch(this.libs[lib]);
-
-        if (args && args.hasOwnProperty(lib)) {
-            if (typeof this.libs[lib].settings !== 'undefined') {
-              $.extend(true, this.libs[lib].settings, args[lib]);
-            } else if (typeof this.libs[lib].defaults !== 'undefined') {
-              $.extend(true, this.libs[lib].defaults, args[lib]);
-            }
-          return this.libs[lib].init.apply(this.libs[lib], [this.scope, args[lib]]);
-        }
-
-        args = args instanceof Array ? args : new Array(args);
-        return this.libs[lib].init.apply(this.libs[lib], args);
-      }
-
-      return function () {};
-    },
-
-    patch : function (lib) {
-      lib.scope = this.scope;
-      lib.namespace = this.global.namespace;
-      lib.rtl = this.rtl;
-      lib['data_options'] = this.utils.data_options;
-      lib['attr_name'] = attr_name;
-      lib['add_namespace'] = add_namespace;
-      lib['bindings'] = bindings;
-      lib['S'] = this.utils.S;
-    },
-
-    inherit : function (scope, methods) {
-      var methods_arr = methods.split(' '),
-          i = methods_arr.length;
-
-      while (i--) {
-        if (this.utils.hasOwnProperty(methods_arr[i])) {
-          scope[methods_arr[i]] = this.utils[methods_arr[i]];
-        }
-      }
-    },
-
-    set_namespace : function () {
-
-      // Description:
-      //    Don't bother reading the namespace out of the meta tag
-      //    if the namespace has been set globally in javascript
-      //
-      // Example:
-      //    Foundation.global.namespace = 'my-namespace';
-      // or make it an empty string:
-      //    Foundation.global.namespace = '';
-      //
-      //
-
-      // If the namespace has not been set (is undefined), try to read it out of the meta element.
-      // Otherwise use the globally defined namespace, even if it's empty ('')
-      var namespace = ( this.global.namespace === undefined ) ? $('.foundation-data-attribute-namespace').css('font-family') : this.global.namespace;
-
-      // Finally, if the namsepace is either undefined or false, set it to an empty string.
-      // Otherwise use the namespace value.
-      this.global.namespace = ( namespace === undefined || /false/i.test(namespace) ) ? '' : namespace;
-    },
-
-    libs : {},
-
-    // methods that can be inherited in libraries
-    utils : {
-
-      // Description:
-      //    Fast Selector wrapper returns jQuery object. Only use where getElementById
-      //    is not available.
-      //
-      // Arguments:
-      //    Selector (String): CSS selector describing the element(s) to be
-      //    returned as a jQuery object.
-      //
-      //    Scope (String): CSS selector describing the area to be searched. Default
-      //    is document.
-      //
-      // Returns:
-      //    Element (jQuery Object): jQuery object containing elements matching the
-      //    selector within the scope.
-      S : S,
-
-      // Description:
-      //    Executes a function a max of once every n milliseconds
-      //
-      // Arguments:
-      //    Func (Function): Function to be throttled.
-      //
-      //    Delay (Integer): Function execution threshold in milliseconds.
-      //
-      // Returns:
-      //    Lazy_function (Function): Function with throttling applied.
-      throttle : function (func, delay) {
-        var timer = null;
-
-        return function () {
-          var context = this, args = arguments;
-
-          if (timer == null) {
-            timer = setTimeout(function () {
-              func.apply(context, args);
-              timer = null;
-            }, delay);
-          }
-        };
-      },
-
-      // Description:
-      //    Executes a function when it stops being invoked for n seconds
-      //    Modified version of _.debounce() http://underscorejs.org
-      //
-      // Arguments:
-      //    Func (Function): Function to be debounced.
-      //
-      //    Delay (Integer): Function execution threshold in milliseconds.
-      //
-      //    Immediate (Bool): Whether the function should be called at the beginning
-      //    of the delay instead of the end. Default is false.
-      //
-      // Returns:
-      //    Lazy_function (Function): Function with debouncing applied.
-      debounce : function (func, delay, immediate) {
-        var timeout, result;
-        return function () {
-          var context = this, args = arguments;
-          var later = function () {
-            timeout = null;
-            if (!immediate) {
-              result = func.apply(context, args);
-            }
-          };
-          var callNow = immediate && !timeout;
-          clearTimeout(timeout);
-          timeout = setTimeout(later, delay);
-          if (callNow) {
-            result = func.apply(context, args);
-          }
-          return result;
-        };
-      },
-
-      // Description:
-      //    Parses data-options attribute
-      //
-      // Arguments:
-      //    El (jQuery Object): Element to be parsed.
-      //
-      // Returns:
-      //    Options (Javascript Object): Contents of the element's data-options
-      //    attribute.
-      data_options : function (el, data_attr_name) {
-        data_attr_name = data_attr_name || 'options';
-        var opts = {}, ii, p, opts_arr,
-            data_options = function (el) {
-              var namespace = Foundation.global.namespace;
-
-              if (namespace.length > 0) {
-                return el.data(namespace + '-' + data_attr_name);
-              }
-
-              return el.data(data_attr_name);
-            };
-
-        var cached_options = data_options(el);
-
-        if (typeof cached_options === 'object') {
-          return cached_options;
-        }
-
-        opts_arr = (cached_options || ':').split(';');
-        ii = opts_arr.length;
-
-        function isNumber (o) {
-          return !isNaN (o - 0) && o !== null && o !== '' && o !== false && o !== true;
-        }
-
-        function trim (str) {
-          if (typeof str === 'string') {
-            return $.trim(str);
-          }
-          return str;
-        }
-
-        while (ii--) {
-          p = opts_arr[ii].split(':');
-          p = [p[0], p.slice(1).join(':')];
-
-          if (/true/i.test(p[1])) {
-            p[1] = true;
-          }
-          if (/false/i.test(p[1])) {
-            p[1] = false;
-          }
-          if (isNumber(p[1])) {
-            if (p[1].indexOf('.') === -1) {
-              p[1] = parseInt(p[1], 10);
-            } else {
-              p[1] = parseFloat(p[1]);
-            }
-          }
-
-          if (p.length === 2 && p[0].length > 0) {
-            opts[trim(p[0])] = trim(p[1]);
-          }
-        }
-
-        return opts;
-      },
-
-      // Description:
-      //    Adds JS-recognizable media queries
-      //
-      // Arguments:
-      //    Media (String): Key string for the media query to be stored as in
-      //    Foundation.media_queries
-      //
-      //    Class (String): Class name for the generated <meta> tag
-      register_media : function (media, media_class) {
-        if (Foundation.media_queries[media] === undefined) {
-          $('head').append('<meta class="' + media_class + '"/>');
-          Foundation.media_queries[media] = removeQuotes($('.' + media_class).css('font-family'));
-        }
-      },
-
-      // Description:
-      //    Add custom CSS within a JS-defined media query
-      //
-      // Arguments:
-      //    Rule (String): CSS rule to be appended to the document.
-      //
-      //    Media (String): Optional media query string for the CSS rule to be
-      //    nested under.
-      add_custom_rule : function (rule, media) {
-        if (media === undefined && Foundation.stylesheet) {
-          Foundation.stylesheet.insertRule(rule, Foundation.stylesheet.cssRules.length);
-        } else {
-          var query = Foundation.media_queries[media];
-
-          if (query !== undefined) {
-            Foundation.stylesheet.insertRule('@media ' +
-              Foundation.media_queries[media] + '{ ' + rule + ' }');
-          }
-        }
-      },
-
-      // Description:
-      //    Performs a callback function when an image is fully loaded
-      //
-      // Arguments:
-      //    Image (jQuery Object): Image(s) to check if loaded.
-      //
-      //    Callback (Function): Function to execute when image is fully loaded.
-      image_loaded : function (images, callback) {
-        var self = this,
-            unloaded = images.length;
-
-        if (unloaded === 0) {
-          callback(images);
-        }
-
-        images.each(function () {
-          single_image_loaded(self.S(this), function () {
-            unloaded -= 1;
-            if (unloaded === 0) {
-              callback(images);
-            }
-          });
-        });
-      },
-
-      // Description:
-      //    Returns a random, alphanumeric string
-      //
-      // Arguments:
-      //    Length (Integer): Length of string to be generated. Defaults to random
-      //    integer.
-      //
-      // Returns:
-      //    Rand (String): Pseudo-random, alphanumeric string.
-      random_str : function () {
-        if (!this.fidx) {
-          this.fidx = 0;
-        }
-        this.prefix = this.prefix || [(this.name || 'F'), (+new Date).toString(36)].join('-');
-
-        return this.prefix + (this.fidx++).toString(36);
-      },
-
-      // Description:
-      //    Helper for window.matchMedia
-      //
-      // Arguments:
-      //    mq (String): Media query
-      //
-      // Returns:
-      //    (Boolean): Whether the media query passes or not
-      match : function (mq) {
-        return window.matchMedia(mq).matches;
-      },
-
-      // Description:
-      //    Helpers for checking Foundation default media queries with JS
-      //
-      // Returns:
-      //    (Boolean): Whether the media query passes or not
-
-      is_small_up : function () {
-        return this.match(Foundation.media_queries.small);
-      },
-
-      is_medium_up : function () {
-        return this.match(Foundation.media_queries.medium);
-      },
-
-      is_large_up : function () {
-        return this.match(Foundation.media_queries.large);
-      },
-
-      is_xlarge_up : function () {
-        return this.match(Foundation.media_queries.xlarge);
-      },
-
-      is_xxlarge_up : function () {
-        return this.match(Foundation.media_queries.xxlarge);
-      },
-
-      is_small_only : function () {
-        return !this.is_medium_up() && !this.is_large_up() && !this.is_xlarge_up() && !this.is_xxlarge_up();
-      },
-
-      is_medium_only : function () {
-        return this.is_medium_up() && !this.is_large_up() && !this.is_xlarge_up() && !this.is_xxlarge_up();
-      },
-
-      is_large_only : function () {
-        return this.is_medium_up() && this.is_large_up() && !this.is_xlarge_up() && !this.is_xxlarge_up();
-      },
-
-      is_xlarge_only : function () {
-        return this.is_medium_up() && this.is_large_up() && this.is_xlarge_up() && !this.is_xxlarge_up();
-      },
-
-      is_xxlarge_only : function () {
-        return this.is_medium_up() && this.is_large_up() && this.is_xlarge_up() && this.is_xxlarge_up();
-      }
-    }
-  };
-
-  $.fn.foundation = function () {
-    var args = Array.prototype.slice.call(arguments, 0);
-
-    return this.each(function () {
-      Foundation.init.apply(Foundation, [this].concat(args));
-      return this;
-    });
-  };
-
-}(jQuery, window, window.document));
diff --git a/3dmol/js/foundation/foundation.magellan.js b/3dmol/js/foundation/foundation.magellan.js
deleted file mode 100644
index d8e1ebf..0000000
--- a/3dmol/js/foundation/foundation.magellan.js
+++ /dev/null
@@ -1,203 +0,0 @@
-;(function ($, window, document, undefined) {
-  'use strict';
-
-  Foundation.libs['magellan-expedition'] = {
-    name : 'magellan-expedition',
-
-    version : '5.5.1',
-
-    settings : {
-      active_class : 'active',
-      threshold : 0, // pixels from the top of the expedition for it to become fixes
-      destination_threshold : 20, // pixels from the top of destination for it to be considered active
-      throttle_delay : 30, // calculation throttling to increase framerate
-      fixed_top : 0, // top distance in pixels assigend to the fixed element on scroll
-      offset_by_height : true,  // whether to offset the destination by the expedition height. Usually you want this to be true, unless your expedition is on the side.
-      duration : 700, // animation duration time
-      easing : 'swing' // animation easing
-    },
-
-    init : function (scope, method, options) {
-      Foundation.inherit(this, 'throttle');
-      this.bindings(method, options);
-    },
-
-    events : function () {
-      var self = this,
-          S = self.S,
-          settings = self.settings;
-
-      // initialize expedition offset
-      self.set_expedition_position();
-
-      S(self.scope)
-        .off('.magellan')
-        .on('click.fndtn.magellan', '[' + self.add_namespace('data-magellan-arrival') + '] a[href^="#"]', function (e) {
-          e.preventDefault();
-          var expedition = $(this).closest('[' + self.attr_name() + ']'),
-              settings = expedition.data('magellan-expedition-init'),
-              hash = this.hash.split('#').join(''),
-              target = $('a[name="' + hash + '"]');
-
-          if (target.length === 0) {
-            target = $('#' + hash);
-
-          }
-
-          // Account for expedition height if fixed position
-          var scroll_top = target.offset().top - settings.destination_threshold + 1;
-          if (settings.offset_by_height) {
-            scroll_top = scroll_top - expedition.outerHeight();
-          }
-
-          $('html, body').stop().animate({
-            'scrollTop' : scroll_top
-          }, settings.duration, settings.easing, function () {
-            if (history.pushState) {
-              history.pushState(null, null, '#' + hash);
-            } else {
-              location.hash = '#' + hash;
-            }
-          });
-        })
-        .on('scroll.fndtn.magellan', self.throttle(this.check_for_arrivals.bind(this), settings.throttle_delay));
-
-      $(window)
-        .on('resize.fndtn.magellan', self.throttle(this.set_expedition_position.bind(this), settings.throttle_delay));
-    },
-
-    check_for_arrivals : function () {
-      var self = this;
-      self.update_arrivals();
-      self.update_expedition_positions();
-    },
-
-    set_expedition_position : function () {
-      var self = this;
-      $('[' + this.attr_name() + '=fixed]', self.scope).each(function (idx, el) {
-        var expedition = $(this),
-            settings = expedition.data('magellan-expedition-init'),
-            styles = expedition.attr('styles'), // save styles
-            top_offset, fixed_top;
-
-        expedition.attr('style', '');
-        top_offset = expedition.offset().top + settings.threshold;
-
-        //set fixed-top by attribute
-        fixed_top = parseInt(expedition.data('magellan-fixed-top'));
-        if (!isNaN(fixed_top)) {
-          self.settings.fixed_top = fixed_top;
-        }
-
-        expedition.data(self.data_attr('magellan-top-offset'), top_offset);
-        expedition.attr('style', styles);
-      });
-    },
-
-    update_expedition_positions : function () {
-      var self = this,
-          window_top_offset = $(window).scrollTop();
-
-      $('[' + this.attr_name() + '=fixed]', self.scope).each(function () {
-        var expedition = $(this),
-            settings = expedition.data('magellan-expedition-init'),
-            styles = expedition.attr('style'), // save styles
-            top_offset = expedition.data('magellan-top-offset');
-
-        //scroll to the top distance
-        if (window_top_offset + self.settings.fixed_top >= top_offset) {
-          // Placeholder allows height calculations to be consistent even when
-          // appearing to switch between fixed/non-fixed placement
-          var placeholder = expedition.prev('[' + self.add_namespace('data-magellan-expedition-clone') + ']');
-          if (placeholder.length === 0) {
-            placeholder = expedition.clone();
-            placeholder.removeAttr(self.attr_name());
-            placeholder.attr(self.add_namespace('data-magellan-expedition-clone'), '');
-            expedition.before(placeholder);
-          }
-          expedition.css({position :'fixed', top : settings.fixed_top}).addClass('fixed');
-        } else {
-          expedition.prev('[' + self.add_namespace('data-magellan-expedition-clone') + ']').remove();
-          expedition.attr('style', styles).css('position', '').css('top', '').removeClass('fixed');
-        }
-      });
-    },
-
-    update_arrivals : function () {
-      var self = this,
-          window_top_offset = $(window).scrollTop();
-
-      $('[' + this.attr_name() + ']', self.scope).each(function () {
-        var expedition = $(this),
-            settings = expedition.data(self.attr_name(true) + '-init'),
-            offsets = self.offsets(expedition, window_top_offset),
-            arrivals = expedition.find('[' + self.add_namespace('data-magellan-arrival') + ']'),
-            active_item = false;
-        offsets.each(function (idx, item) {
-          if (item.viewport_offset >= item.top_offset) {
-            var arrivals = expedition.find('[' + self.add_namespace('data-magellan-arrival') + ']');
-            arrivals.not(item.arrival).removeClass(settings.active_class);
-            item.arrival.addClass(settings.active_class);
-            active_item = true;
-            return true;
-          }
-        });
-
-        if (!active_item) {
-          arrivals.removeClass(settings.active_class);
-        }
-      });
-    },
-
-    offsets : function (expedition, window_offset) {
-      var self = this,
-          settings = expedition.data(self.attr_name(true) + '-init'),
-          viewport_offset = window_offset;
-
-      return expedition.find('[' + self.add_namespace('data-magellan-arrival') + ']').map(function (idx, el) {
-        var name = $(this).data(self.data_attr('magellan-arrival')),
-            dest = $('[' + self.add_namespace('data-magellan-destination') + '=' + name + ']');
-        if (dest.length > 0) {
-          var top_offset = dest.offset().top - settings.destination_threshold;
-          if (settings.offset_by_height) {
-            top_offset = top_offset - expedition.outerHeight();
-          }
-          top_offset = Math.floor(top_offset);
-          return {
-            destination : dest,
-            arrival : $(this),
-            top_offset : top_offset,
-            viewport_offset : viewport_offset
-          }
-        }
-      }).sort(function (a, b) {
-        if (a.top_offset < b.top_offset) {
-          return -1;
-        }
-        if (a.top_offset > b.top_offset) {
-          return 1;
-        }
-        return 0;
-      });
-    },
-
-    data_attr : function (str) {
-      if (this.namespace.length > 0) {
-        return this.namespace + '-' + str;
-      }
-
-      return str;
-    },
-
-    off : function () {
-      this.S(this.scope).off('.magellan');
-      this.S(window).off('.magellan');
-    },
-
-    reflow : function () {
-      var self = this;
-      // remove placeholder expeditions used for height calculation purposes
-      $('[' + self.add_namespace('data-magellan-expedition-clone') + ']', self.scope).remove();
-    }
-  };
-}(jQuery, window, window.document));
diff --git a/3dmol/js/foundation/foundation.offcanvas.js b/3dmol/js/foundation/foundation.offcanvas.js
deleted file mode 100644
index 51ce353..0000000
--- a/3dmol/js/foundation/foundation.offcanvas.js
+++ /dev/null
@@ -1,152 +0,0 @@
-;(function ($, window, document, undefined) {
-  'use strict';
-
-  Foundation.libs.offcanvas = {
-    name : 'offcanvas',
-
-    version : '5.5.1',
-
-    settings : {
-      open_method : 'move',
-      close_on_click : false
-    },
-
-    init : function (scope, method, options) {
-      this.bindings(method, options);
-    },
-
-    events : function () {
-      var self = this,
-          S = self.S,
-          move_class = '',
-          right_postfix = '',
-          left_postfix = '';
-
-      if (this.settings.open_method === 'move') {
-        move_class = 'move-';
-        right_postfix = 'right';
-        left_postfix = 'left';
-      } else if (this.settings.open_method === 'overlap_single') {
-        move_class = 'offcanvas-overlap-';
-        right_postfix = 'right';
-        left_postfix = 'left';
-      } else if (this.settings.open_method === 'overlap') {
-        move_class = 'offcanvas-overlap';
-      }
-
-      S(this.scope).off('.offcanvas')
-        .on('click.fndtn.offcanvas', '.left-off-canvas-toggle', function (e) {
-          self.click_toggle_class(e, move_class + right_postfix);
-          if (self.settings.open_method !== 'overlap') {
-            S('.left-submenu').removeClass(move_class + right_postfix);
-          }
-          $('.left-off-canvas-toggle').attr('aria-expanded', 'true');
-        })
-        .on('click.fndtn.offcanvas', '.left-off-canvas-menu a', function (e) {
-          var settings = self.get_settings(e);
-          var parent = S(this).parent();
-
-          if (settings.close_on_click && !parent.hasClass('has-submenu') && !parent.hasClass('back')) {
-            self.hide.call(self, move_class + right_postfix, self.get_wrapper(e));
-            parent.parent().removeClass(move_class + right_postfix);
-          } else if (S(this).parent().hasClass('has-submenu')) {
-            e.preventDefault();
-            S(this).siblings('.left-submenu').toggleClass(move_class + right_postfix);
-          } else if (parent.hasClass('back')) {
-            e.preventDefault();
-            parent.parent().removeClass(move_class + right_postfix);
-          }
-          $('.left-off-canvas-toggle').attr('aria-expanded', 'true');
-        })
-        .on('click.fndtn.offcanvas', '.right-off-canvas-toggle', function (e) {
-          self.click_toggle_class(e, move_class + left_postfix);
-          if (self.settings.open_method !== 'overlap') {
-            S('.right-submenu').removeClass(move_class + left_postfix);
-          }
-          $('.right-off-canvas-toggle').attr('aria-expanded', 'true');
-        })
-        .on('click.fndtn.offcanvas', '.right-off-canvas-menu a', function (e) {
-          var settings = self.get_settings(e);
-          var parent = S(this).parent();
-
-          if (settings.close_on_click && !parent.hasClass('has-submenu') && !parent.hasClass('back')) {
-            self.hide.call(self, move_class + left_postfix, self.get_wrapper(e));
-            parent.parent().removeClass(move_class + left_postfix);
-          } else if (S(this).parent().hasClass('has-submenu')) {
-            e.preventDefault();
-            S(this).siblings('.right-submenu').toggleClass(move_class + left_postfix);
-          } else if (parent.hasClass('back')) {
-            e.preventDefault();
-            parent.parent().removeClass(move_class + left_postfix);
-          }
-          $('.right-off-canvas-toggle').attr('aria-expanded', 'true');
-        })
-        .on('click.fndtn.offcanvas', '.exit-off-canvas', function (e) {
-          self.click_remove_class(e, move_class + left_postfix);
-          S('.right-submenu').removeClass(move_class + left_postfix);
-          if (right_postfix) {
-            self.click_remove_class(e, move_class + right_postfix);
-            S('.left-submenu').removeClass(move_class + left_postfix);
-          }
-          $('.right-off-canvas-toggle').attr('aria-expanded', 'true');
-        })
-        .on('click.fndtn.offcanvas', '.exit-off-canvas', function (e) {
-          self.click_remove_class(e, move_class + left_postfix);
-          $('.left-off-canvas-toggle').attr('aria-expanded', 'false');
-          if (right_postfix) {
-            self.click_remove_class(e, move_class + right_postfix);
-            $('.right-off-canvas-toggle').attr('aria-expanded', 'false');
-          }
-        });
-    },
-
-    toggle : function (class_name, $off_canvas) {
-      $off_canvas = $off_canvas || this.get_wrapper();
-      if ($off_canvas.is('.' + class_name)) {
-        this.hide(class_name, $off_canvas);
-      } else {
-        this.show(class_name, $off_canvas);
-      }
-    },
-
-    show : function (class_name, $off_canvas) {
-      $off_canvas = $off_canvas || this.get_wrapper();
-      $off_canvas.trigger('open').trigger('open.fndtn.offcanvas');
-      $off_canvas.addClass(class_name);
-    },
-
-    hide : function (class_name, $off_canvas) {
-      $off_canvas = $off_canvas || this.get_wrapper();
-      $off_canvas.trigger('close').trigger('close.fndtn.offcanvas');
-      $off_canvas.removeClass(class_name);
-    },
-
-    click_toggle_class : function (e, class_name) {
-      e.preventDefault();
-      var $off_canvas = this.get_wrapper(e);
-      this.toggle(class_name, $off_canvas);
-    },
-
-    click_remove_class : function (e, class_name) {
-      e.preventDefault();
-      var $off_canvas = this.get_wrapper(e);
-      this.hide(class_name, $off_canvas);
-    },
-
-    get_settings : function (e) {
-      var offcanvas  = this.S(e.target).closest('[' + this.attr_name() + ']');
-      return offcanvas.data(this.attr_name(true) + '-init') || this.settings;
-    },
-
-    get_wrapper : function (e) {
-      var $off_canvas = this.S(e ? e.target : this.scope).closest('.off-canvas-wrap');
-
-      if ($off_canvas.length === 0) {
-        $off_canvas = this.S('.off-canvas-wrap');
-      }
-      return $off_canvas;
-    },
-
-    reflow : function () {}
-  };
-}(jQuery, window, window.document));
diff --git a/3dmol/js/foundation/foundation.orbit.js b/3dmol/js/foundation/foundation.orbit.js
deleted file mode 100644
index fb03f3d..0000000
--- a/3dmol/js/foundation/foundation.orbit.js
+++ /dev/null
@@ -1,476 +0,0 @@
-;(function ($, window, document, undefined) {
-  'use strict';
-
-  var noop = function () {};
-
-  var Orbit = function (el, settings) {
-    // Don't reinitialize plugin
-    if (el.hasClass(settings.slides_container_class)) {
-      return this;
-    }
-
-    var self = this,
-        container,
-        slides_container = el,
-        number_container,
-        bullets_container,
-        timer_container,
-        idx = 0,
-        animate,
-        timer,
-        locked = false,
-        adjust_height_after = false;
-
-    self.slides = function () {
-      return slides_container.children(settings.slide_selector);
-    };
-
-    self.slides().first().addClass(settings.active_slide_class);
-
-    self.update_slide_number = function (index) {
-      if (settings.slide_number) {
-        number_container.find('span:first').text(parseInt(index) + 1);
-        number_container.find('span:last').text(self.slides().length);
-      }
-      if (settings.bullets) {
-        bullets_container.children().removeClass(settings.bullets_active_class);
-        $(bullets_container.children().get(index)).addClass(settings.bullets_active_class);
-      }
-    };
-
-    self.update_active_link = function (index) {
-      var link = $('[data-orbit-link="' + self.slides().eq(index).attr('data-orbit-slide') + '"]');
-      link.siblings().removeClass(settings.bullets_active_class);
-      link.addClass(settings.bullets_active_class);
-    };
-
-    self.build_markup = function () {
-      slides_container.wrap('<div class="' + settings.container_class + '"></div>');
-      container = slides_container.parent();
-      slides_container.addClass(settings.slides_container_class);
-
-      if (settings.stack_on_small) {
-        container.addClass(settings.stack_on_small_class);
-      }
-
-      if (settings.navigation_arrows) {
-        container.append($('<a href="#"><span></span></a>').addClass(settings.prev_class));
-        container.append($('<a href="#"><span></span></a>').addClass(settings.next_class));
-      }
-
-      if (settings.timer) {
-        timer_container = $('<div>').addClass(settings.timer_container_class);
-        timer_container.append('<span>');
-        timer_container.append($('<div>').addClass(settings.timer_progress_class));
-        timer_container.addClass(settings.timer_paused_class);
-        container.append(timer_container);
-      }
-
-      if (settings.slide_number) {
-        number_container = $('<div>').addClass(settings.slide_number_class);
-        number_container.append('<span></span> ' + settings.slide_number_text + ' <span></span>');
-        container.append(number_container);
-      }
-
-      if (settings.bullets) {
-        bullets_container = $('<ol>').addClass(settings.bullets_container_class);
-        container.append(bullets_container);
-        bullets_container.wrap('<div class="orbit-bullets-container"></div>');
-        self.slides().each(function (idx, el) {
-          var bullet = $('<li>').attr('data-orbit-slide', idx).on('click', self.link_bullet);;
-          bullets_container.append(bullet);
-        });
-      }
-
-    };
-
-    self._goto = function (next_idx, start_timer) {
-      // if (locked) {return false;}
-      if (next_idx === idx) {return false;}
-      if (typeof timer === 'object') {timer.restart();}
-      var slides = self.slides();
-
-      var dir = 'next';
-      locked = true;
-      if (next_idx < idx) {dir = 'prev';}
-      if (next_idx >= slides.length) {
-        if (!settings.circular) {
-          return false;
-        }
-        next_idx = 0;
-      } else if (next_idx < 0) {
-        if (!settings.circular) {
-          return false;
-        }
-        next_idx = slides.length - 1;
-      }
-
-      var current = $(slides.get(idx));
-      var next = $(slides.get(next_idx));
-
-      current.css('zIndex', 2);
-      current.removeClass(settings.active_slide_class);
-      next.css('zIndex', 4).addClass(settings.active_slide_class);
-
-      slides_container.trigger('before-slide-change.fndtn.orbit');
-      settings.before_slide_change();
-      self.update_active_link(next_idx);
-
-      var callback = function () {
-        var unlock = function () {
-          idx = next_idx;
-          locked = false;
-          if (start_timer === true) {timer = self.create_timer(); timer.start();}
-          self.update_slide_number(idx);
-          slides_container.trigger('after-slide-change.fndtn.orbit', [{slide_number : idx, total_slides : slides.length}]);
-          settings.after_slide_change(idx, slides.length);
-        };
-        if (slides_container.outerHeight() != next.outerHeight() && settings.variable_height) {
-          slides_container.animate({'height': next.outerHeight()}, 250, 'linear', unlock);
-        } else {
-          unlock();
-        }
-      };
-
-      if (slides.length === 1) {callback(); return false;}
-
-      var start_animation = function () {
-        if (dir === 'next') {animate.next(current, next, callback);}
-        if (dir === 'prev') {animate.prev(current, next, callback);}
-      };
-
-      if (next.outerHeight() > slides_container.outerHeight() && settings.variable_height) {
-        slides_container.animate({'height': next.outerHeight()}, 250, 'linear', start_animation);
-      } else {
-        start_animation();
-      }
-    };
-
-    self.next = function (e) {
-      e.stopImmediatePropagation();
-      e.preventDefault();
-      self._goto(idx + 1);
-    };
-
-    self.prev = function (e) {
-      e.stopImmediatePropagation();
-      e.preventDefault();
-      self._goto(idx - 1);
-    };
-
-    self.link_custom = function (e) {
-      e.preventDefault();
-      var link = $(this).attr('data-orbit-link');
-      if ((typeof link === 'string') && (link = $.trim(link)) != '') {
-        var slide = container.find('[data-orbit-slide=' + link + ']');
-        if (slide.index() != -1) {self._goto(slide.index());}
-      }
-    };
-
-    self.link_bullet = function (e) {
-      var index = $(this).attr('data-orbit-slide');
-      if ((typeof index === 'string') && (index = $.trim(index)) != '') {
-        if (isNaN(parseInt(index))) {
-          var slide = container.find('[data-orbit-slide=' + index + ']');
-          if (slide.index() != -1) {self._goto(slide.index() + 1);}
-        } else {
-          self._goto(parseInt(index));
-        }
-      }
-
-    }
-
-    self.timer_callback = function () {
-      self._goto(idx + 1, true);
-    }
-
-    self.compute_dimensions = function () {
-      var current = $(self.slides().get(idx));
-      var h = current.outerHeight();
-      if (!settings.variable_height) {
-        self.slides().each(function(){
-          if ($(this).outerHeight() > h) { h = $(this).outerHeight(); }
-        });
-      }
-      slides_container.height(h);
-    };
-
-    self.create_timer = function () {
-      var t = new Timer(
-        container.find('.' + settings.timer_container_class),
-        settings,
-        self.timer_callback
-      );
-      return t;
-    };
-
-    self.stop_timer = function () {
-      if (typeof timer === 'object') {
-        timer.stop();
-      }
-    };
-
-    self.toggle_timer = function () {
-      var t = container.find('.' + settings.timer_container_class);
-      if (t.hasClass(settings.timer_paused_class)) {
-        if (typeof timer === 'undefined') {timer = self.create_timer();}
-        timer.start();
-      } else {
-        if (typeof timer === 'object') {timer.stop();}
-      }
-    };
-
-    self.init = function () {
-      self.build_markup();
-      if (settings.timer) {
-        timer = self.create_timer();
-        Foundation.utils.image_loaded(this.slides().children('img'), timer.start);
-      }
-      animate = new FadeAnimation(settings, slides_container);
-      if (settings.animation === 'slide') {
-        animate = new SlideAnimation(settings, slides_container);
-      }
-
-      container.on('click', '.' + settings.next_class, self.next);
-      container.on('click', '.' + settings.prev_class, self.prev);
-
-      if (settings.next_on_click) {
-        container.on('click', '.' + settings.slides_container_class + ' [data-orbit-slide]', self.link_bullet);
-      }
-
-      container.on('click', self.toggle_timer);
-      if (settings.swipe) {
-        container.on('touchstart.fndtn.orbit', function (e) {
-          if (!e.touches) {e = e.originalEvent;}
-          var data = {
-            start_page_x : e.touches[0].pageX,
-            start_page_y : e.touches[0].pageY,
-            start_time : (new Date()).getTime(),
-            delta_x : 0,
-            is_scrolling : undefined
-          };
-          container.data('swipe-transition', data);
-          e.stopPropagation();
-        })
-        .on('touchmove.fndtn.orbit', function (e) {
-          if (!e.touches) {
-            e = e.originalEvent;
-          }
-          // Ignore pinch/zoom events
-          if (e.touches.length > 1 || e.scale && e.scale !== 1) {
-            return;
-          }
-
-          var data = container.data('swipe-transition');
-          if (typeof data === 'undefined') {data = {};}
-
-          data.delta_x = e.touches[0].pageX - data.start_page_x;
-
-          if ( typeof data.is_scrolling === 'undefined') {
-            data.is_scrolling = !!( data.is_scrolling || Math.abs(data.delta_x) < Math.abs(e.touches[0].pageY - data.start_page_y) );
-          }
-
-          if (!data.is_scrolling && !data.active) {
-            e.preventDefault();
-            var direction = (data.delta_x < 0) ? (idx + 1) : (idx - 1);
-            data.active = true;
-            self._goto(direction);
-          }
-        })
-        .on('touchend.fndtn.orbit', function (e) {
-          container.data('swipe-transition', {});
-          e.stopPropagation();
-        })
-      }
-      container.on('mouseenter.fndtn.orbit', function (e) {
-        if (settings.timer && settings.pause_on_hover) {
-          self.stop_timer();
-        }
-      })
-      .on('mouseleave.fndtn.orbit', function (e) {
-        if (settings.timer && settings.resume_on_mouseout) {
-          timer.start();
-        }
-      });
-
-      $(document).on('click', '[data-orbit-link]', self.link_custom);
-      $(window).on('load resize', self.compute_dimensions);
-      Foundation.utils.image_loaded(this.slides().children('img'), self.compute_dimensions);
-      Foundation.utils.image_loaded(this.slides().children('img'), function () {
-        container.prev('.' + settings.preloader_class).css('display', 'none');
-        self.update_slide_number(0);
-        self.update_active_link(0);
-        slides_container.trigger('ready.fndtn.orbit');
-      });
-    };
-
-    self.init();
-  };
-
-  var Timer = function (el, settings, callback) {
-    var self = this,
-        duration = settings.timer_speed,
-        progress = el.find('.' + settings.timer_progress_class),
-        start,
-        timeout,
-        left = -1;
-
-    this.update_progress = function (w) {
-      var new_progress = progress.clone();
-      new_progress.attr('style', '');
-      new_progress.css('width', w + '%');
-      progress.replaceWith(new_progress);
-      progress = new_progress;
-    };
-
-    this.restart = function () {
-      clearTimeout(timeout);
-      el.addClass(settings.timer_paused_class);
-      left = -1;
-      self.update_progress(0);
-    };
-
-    this.start = function () {
-      if (!el.hasClass(settings.timer_paused_class)) {return true;}
-      left = (left === -1) ? duration : left;
-      el.removeClass(settings.timer_paused_class);
-      start = new Date().getTime();
-      progress.animate({'width' : '100%'}, left, 'linear');
-      timeout = setTimeout(function () {
-        self.restart();
-        callback();
-      }, left);
-      el.trigger('timer-started.fndtn.orbit')
-    };
-
-    this.stop = function () {
-      if (el.hasClass(settings.timer_paused_class)) {return true;}
-      clearTimeout(timeout);
-      el.addClass(settings.timer_paused_class);
-      var end = new Date().getTime();
-      left = left - (end - start);
-      var w = 100 - ((left / duration) * 100);
-      self.update_progress(w);
-      el.trigger('timer-stopped.fndtn.orbit');
-    };
-  };
-
-  var SlideAnimation = function (settings, container) {
-    var duration = settings.animation_speed;
-    var is_rtl = ($('html[dir=rtl]').length === 1);
-    var margin = is_rtl ? 'marginRight' : 'marginLeft';
-    var animMargin = {};
-    animMargin[margin] = '0%';
-
-    this.next = function (current, next, callback) {
-      current.animate({marginLeft : '-100%'}, duration);
-      next.animate(animMargin, duration, function () {
-        current.css(margin, '100%');
-        callback();
-      });
-    };
-
-    this.prev = function (current, prev, callback) {
-      current.animate({marginLeft : '100%'}, duration);
-      prev.css(margin, '-100%');
-      prev.animate(animMargin, duration, function () {
-        current.css(margin, '100%');
-        callback();
-      });
-    };
-  };
-
-  var FadeAnimation = function (settings, container) {
-    var duration = settings.animation_speed;
-    var is_rtl = ($('html[dir=rtl]').length === 1);
-    var margin = is_rtl ? 'marginRight' : 'marginLeft';
-
-    this.next = function (current, next, callback) {
-      next.css({'margin' : '0%', 'opacity' : '0.01'});
-      next.animate({'opacity' :'1'}, duration, 'linear', function () {
-        current.css('margin', '100%');
-        callback();
-      });
-    };
-
-    this.prev = function (current, prev, callback) {
-      prev.css({'margin' : '0%', 'opacity' : '0.01'});
-      prev.animate({'opacity' : '1'}, duration, 'linear', function () {
-        current.css('margin', '100%');
-        callback();
-      });
-    };
-  };
-
-  Foundation.libs = Foundation.libs || {};
-
-  Foundation.libs.orbit = {
-    name : 'orbit',
-
-    version : '5.5.1',
-
-    settings : {
-      animation : 'slide',
-      timer_speed : 10000,
-      pause_on_hover : true,
-      resume_on_mouseout : false,
-      next_on_click : true,
-      animation_speed : 500,
-      stack_on_small : false,
-      navigation_arrows : true,
-      slide_number : true,
-      slide_number_text : 'of',
-      container_class : 'orbit-container',
-      stack_on_small_class : 'orbit-stack-on-small',
-      next_class : 'orbit-next',
-      prev_class : 'orbit-prev',
-      timer_container_class : 'orbit-timer',
-      timer_paused_class : 'paused',
-      timer_progress_class : 'orbit-progress',
-      slides_container_class : 'orbit-slides-container',
-      preloader_class : 'preloader',
-      slide_selector : '*',
-      bullets_container_class : 'orbit-bullets',
-      bullets_active_class : 'active',
-      slide_number_class : 'orbit-slide-number',
-      caption_class : 'orbit-caption',
-      active_slide_class : 'active',
-      orbit_transition_class : 'orbit-transitioning',
-      bullets : true,
-      circular : true,
-      timer : true,
-      variable_height : false,
-      swipe : true,
-      before_slide_change : noop,
-      after_slide_change : noop
-    },
-
-    init : function (scope, method, options) {
-      var self = this;
-      this.bindings(method, options);
-    },
-
-    events : function (instance) {
-      var orbit_instance = new Orbit(this.S(instance), this.S(instance).data('orbit-init'));
-      this.S(instance).data(this.name + '-instance', orbit_instance);
-    },
-
-    reflow : function () {
-      var self = this;
-
-      if (self.S(self.scope).is('[data-orbit]')) {
-        var $el = self.S(self.scope);
-        var instance = $el.data(self.name + '-instance');
-        instance.compute_dimensions();
-      } else {
-        self.S('[data-orbit]', self.scope).each(function (idx, el) {
-          var $el = self.S(el);
-          var opts = self.data_options($el);
-          var instance = $el.data(self.name + '-instance');
-          instance.compute_dimensions();
-        });
-      }
-    }
-  };
-
-}(jQuery, window, window.document));
diff --git a/3dmol/js/foundation/foundation.reveal.js b/3dmol/js/foundation/foundation.reveal.js
deleted file mode 100644
index c4b95d7..0000000
--- a/3dmol/js/foundation/foundation.reveal.js
+++ /dev/null
@@ -1,471 +0,0 @@
-;(function ($, window, document, undefined) {
-  'use strict';
-
-  Foundation.libs.reveal = {
-    name : 'reveal',
-
-    version : '5.5.1',
-
-    locked : false,
-
-    settings : {
-      animation : 'fadeAndPop',
-      animation_speed : 250,
-      close_on_background_click : true,
-      close_on_esc : true,
-      dismiss_modal_class : 'close-reveal-modal',
-      multiple_opened : false,
-      bg_class : 'reveal-modal-bg',
-      root_element : 'body',
-      open : function(){},
-      opened : function(){},
-      close : function(){},
-      closed : function(){},
-      bg : $('.reveal-modal-bg'),
-      css : {
-        open : {
-          'opacity' : 0,
-          'visibility' : 'visible',
-          'display' : 'block'
-        },
-        close : {
-          'opacity' : 1,
-          'visibility' : 'hidden',
-          'display' : 'none'
-        }
-      }
-    },
-
-    init : function (scope, method, options) {
-      $.extend(true, this.settings, method, options);
-      this.bindings(method, options);
-    },
-
-    events : function (scope) {
-      var self = this,
-          S = self.S;
-
-      S(this.scope)
-        .off('.reveal')
-        .on('click.fndtn.reveal', '[' + this.add_namespace('data-reveal-id') + ']:not([disabled])', function (e) {
-          e.preventDefault();
-
-          if (!self.locked) {
-            var element = S(this),
-                ajax = element.data(self.data_attr('reveal-ajax'));
-
-            self.locked = true;
-
-            if (typeof ajax === 'undefined') {
-              self.open.call(self, element);
-            } else {
-              var url = ajax === true ? element.attr('href') : ajax;
-
-              self.open.call(self, element, {url : url});
-            }
-          }
-        });
-
-      S(document)
-        .on('click.fndtn.reveal', this.close_targets(), function (e) {
-          e.preventDefault();
-          if (!self.locked) {
-            var settings = S('[' + self.attr_name() + '].open').data(self.attr_name(true) + '-init') || self.settings,
-                bg_clicked = S(e.target)[0] === S('.' + settings.bg_class)[0];
-
-            if (bg_clicked) {
-              if (settings.close_on_background_click) {
-                e.stopPropagation();
-              } else {
-                return;
-              }
-            }
-
-            self.locked = true;
-            self.close.call(self, bg_clicked ? S('[' + self.attr_name() + '].open') : S(this).closest('[' + self.attr_name() + ']'));
-          }
-        });
-
-      if (S('[' + self.attr_name() + ']', this.scope).length > 0) {
-        S(this.scope)
-          // .off('.reveal')
-          .on('open.fndtn.reveal', this.settings.open)
-          .on('opened.fndtn.reveal', this.settings.opened)
-          .on('opened.fndtn.reveal', this.open_video)
-          .on('close.fndtn.reveal', this.settings.close)
-          .on('closed.fndtn.reveal', this.settings.closed)
-          .on('closed.fndtn.reveal', this.close_video);
-      } else {
-        S(this.scope)
-          // .off('.reveal')
-          .on('open.fndtn.reveal', '[' + self.attr_name() + ']', this.settings.open)
-          .on('opened.fndtn.reveal', '[' + self.attr_name() + ']', this.settings.opened)
-          .on('opened.fndtn.reveal', '[' + self.attr_name() + ']', this.open_video)
-          .on('close.fndtn.reveal', '[' + self.attr_name() + ']', this.settings.close)
-          .on('closed.fndtn.reveal', '[' + self.attr_name() + ']', this.settings.closed)
-          .on('closed.fndtn.reveal', '[' + self.attr_name() + ']', this.close_video);
-      }
-
-      return true;
-    },
-
-    // PATCH #3: turning on key up capture only when a reveal window is open
-    key_up_on : function (scope) {
-      var self = this;
-
-      // PATCH #1: fixing multiple keyup event trigger from single key press
-      self.S('body').off('keyup.fndtn.reveal').on('keyup.fndtn.reveal', function ( event ) {
-        var open_modal = self.S('[' + self.attr_name() + '].open'),
-            settings = open_modal.data(self.attr_name(true) + '-init') || self.settings ;
-        // PATCH #2: making sure that the close event can be called only while unlocked,
-        //           so that multiple keyup.fndtn.reveal events don't prevent clean closing of the reveal window.
-        if ( settings && event.which === 27  && settings.close_on_esc && !self.locked) { // 27 is the keycode for the Escape key
-          self.close.call(self, open_modal);
-        }
-      });
-
-      return true;
-    },
-
-    // PATCH #3: turning on key up capture only when a reveal window is open
-    key_up_off : function (scope) {
-      this.S('body').off('keyup.fndtn.reveal');
-      return true;
-    },
-
-    open : function (target, ajax_settings) {
-      var self = this,
-          modal;
-
-      if (target) {
-        if (typeof target.selector !== 'undefined') {
-          // Find the named node; only use the first one found, since the rest of the code assumes there's only one node
-          modal = self.S('#' + target.data(self.data_attr('reveal-id'))).first();
-        } else {
-          modal = self.S(this.scope);
-
-          ajax_settings = target;
-        }
-      } else {
-        modal = self.S(this.scope);
-      }
-
-      var settings = modal.data(self.attr_name(true) + '-init');
-      settings = settings || this.settings;
-
-      if (modal.hasClass('open') && target.attr('data-reveal-id') == modal.attr('id')) {
-        return self.close(modal);
-      }
-
-      if (!modal.hasClass('open')) {
-        var open_modal = self.S('[' + self.attr_name() + '].open');
-
-        if (typeof modal.data('css-top') === 'undefined') {
-          modal.data('css-top', parseInt(modal.css('top'), 10))
-            .data('offset', this.cache_offset(modal));
-        }
-
-        this.key_up_on(modal);    // PATCH #3: turning on key up capture only when a reveal window is open
-
-        modal.on('open.fndtn.reveal').trigger('open.fndtn.reveal');
-
-        if (open_modal.length < 1) {
-          this.toggle_bg(modal, true);
-        }
-
-        if (typeof ajax_settings === 'string') {
-          ajax_settings = {
-            url : ajax_settings
-          };
-        }
-
-        if (typeof ajax_settings === 'undefined' || !ajax_settings.url) {
-          if (open_modal.length > 0) {
-            if (settings.multiple_opened) {
-              this.to_back(open_modal);
-            } else {
-              this.hide(open_modal, settings.css.close);
-            }
-          }
-
-          this.show(modal, settings.css.open);
-        } else {
-          var old_success = typeof ajax_settings.success !== 'undefined' ? ajax_settings.success : null;
-
-          $.extend(ajax_settings, {
-            success : function (data, textStatus, jqXHR) {
-              if ( $.isFunction(old_success) ) {
-                var result = old_success(data, textStatus, jqXHR);
-                if (typeof result == 'string') {
-                  data = result;
-                }
-              }
-
-              modal.html(data);
-              self.S(modal).foundation('section', 'reflow');
-              self.S(modal).children().foundation();
-
-              if (open_modal.length > 0) {
-                if (settings.multiple_opened) {
-                  this.to_back(open_modal);
-                } else {
-                  this.hide(open_modal, settings.css.close);
-                }
-              }
-              self.show(modal, settings.css.open);
-            }
-          });
-
-          $.ajax(ajax_settings);
-        }
-      }
-      self.S(window).trigger('resize');
-    },
-
-    close : function (modal) {
-      var modal = modal && modal.length ? modal : this.S(this.scope),
-          open_modals = this.S('[' + this.attr_name() + '].open'),
-          settings = modal.data(this.attr_name(true) + '-init') || this.settings;
-
-      if (open_modals.length > 0) {
-        this.locked = true;
-        this.key_up_off(modal);   // PATCH #3: turning on key up capture only when a reveal window is open
-        modal.trigger('close').trigger('close.fndtn.reveal');
-        
-        if ((settings.multiple_opened && open_modals.length === 1) || !settings.multiple_opened || modal.length > 1) {
-          this.toggle_bg(modal, false);
-          this.to_front(modal);
-        }
-        
-        if (settings.multiple_opened) {
-          this.hide(modal, settings.css.close, settings);
-          this.to_front($($.makeArray(open_modals).reverse()[1]));
-        } else {
-          this.hide(open_modals, settings.css.close, settings);
-        }
-      }
-    },
-
-    close_targets : function () {
-      var base = '.' + this.settings.dismiss_modal_class;
-
-      if (this.settings.close_on_background_click) {
-        return base + ', .' + this.settings.bg_class;
-      }
-
-      return base;
-    },
-
-    toggle_bg : function (modal, state) {
-      if (this.S('.' + this.settings.bg_class).length === 0) {
-        this.settings.bg = $('<div />', {'class': this.settings.bg_class})
-          .appendTo('body').hide();
-      }
-
-      var visible = this.settings.bg.filter(':visible').length > 0;
-      if ( state != visible ) {
-        if ( state == undefined ? visible : !state ) {
-          this.hide(this.settings.bg);
-        } else {
-          this.show(this.settings.bg);
-        }
-      }
-    },
-
-    show : function (el, css) {
-      // is modal
-      if (css) {
-        var settings = el.data(this.attr_name(true) + '-init') || this.settings,
-            root_element = settings.root_element;
-
-        if (el.parent(root_element).length === 0) {
-          var placeholder = el.wrap('<div style="display: none;" />').parent();
-
-          el.on('closed.fndtn.reveal.wrapped', function () {
-            el.detach().appendTo(placeholder);
-            el.unwrap().unbind('closed.fndtn.reveal.wrapped');
-          });
-
-          el.detach().appendTo(root_element);
-        }
-
-        var animData = getAnimationData(settings.animation);
-        if (!animData.animate) {
-          this.locked = false;
-        }
-        if (animData.pop) {
-          css.top = $(window).scrollTop() - el.data('offset') + 'px';
-          var end_css = {
-            top: $(window).scrollTop() + el.data('css-top') + 'px',
-            opacity: 1
-          };
-
-          return setTimeout(function () {
-            return el
-              .css(css)
-              .animate(end_css, settings.animation_speed, 'linear', function () {
-                this.locked = false;
-                el.trigger('opened').trigger('opened.fndtn.reveal');
-              }.bind(this))
-              .addClass('open');
-          }.bind(this), settings.animation_speed / 2);
-        }
-
-        if (animData.fade) {
-          css.top = $(window).scrollTop() + el.data('css-top') + 'px';
-          var end_css = {opacity: 1};
-
-          return setTimeout(function () {
-            return el
-              .css(css)
-              .animate(end_css, settings.animation_speed, 'linear', function () {
-                this.locked = false;
-                el.trigger('opened').trigger('opened.fndtn.reveal');
-              }.bind(this))
-              .addClass('open');
-          }.bind(this), settings.animation_speed / 2);
-        }
-
-        return el.css(css).show().css({opacity : 1}).addClass('open').trigger('opened').trigger('opened.fndtn.reveal');
-      }
-
-      var settings = this.settings;
-
-      // should we animate the background?
-      if (getAnimationData(settings.animation).fade) {
-        return el.fadeIn(settings.animation_speed / 2);
-      }
-
-      this.locked = false;
-
-      return el.show();
-    },
-    
-    to_back : function(el) {
-      el.addClass('toback');
-    },
-    
-    to_front : function(el) {
-      el.removeClass('toback');
-    },
-
-    hide : function (el, css) {
-      // is modal
-      if (css) {
-        var settings = el.data(this.attr_name(true) + '-init');
-        settings = settings || this.settings;
-
-        var animData = getAnimationData(settings.animation);
-        if (!animData.animate) {
-          this.locked = false;
-        }
-        if (animData.pop) {
-          var end_css = {
-            top: - $(window).scrollTop() - el.data('offset') + 'px',
-            opacity: 0
-          };
-
-          return setTimeout(function () {
-            return el
-              .animate(end_css, settings.animation_speed, 'linear', function () {
-                this.locked = false;
-                el.css(css).trigger('closed').trigger('closed.fndtn.reveal');
-              }.bind(this))
-              .removeClass('open');
-          }.bind(this), settings.animation_speed / 2);
-        }
-
-        if (animData.fade) {
-          var end_css = {opacity : 0};
-
-          return setTimeout(function () {
-            return el
-              .animate(end_css, settings.animation_speed, 'linear', function () {
-                this.locked = false;
-                el.css(css).trigger('closed').trigger('closed.fndtn.reveal');
-              }.bind(this))
-              .removeClass('open');
-          }.bind(this), settings.animation_speed / 2);
-        }
-
-        return el.hide().css(css).removeClass('open').trigger('closed').trigger('closed.fndtn.reveal');
-      }
-
-      var settings = this.settings;
-
-      // should we animate the background?
-      if (getAnimationData(settings.animation).fade) {
-        return el.fadeOut(settings.animation_speed / 2);
-      }
-
-      return el.hide();
-    },
-
-    close_video : function (e) {
-      var video = $('.flex-video', e.target),
-          iframe = $('iframe', video);
-
-      if (iframe.length > 0) {
-        iframe.attr('data-src', iframe[0].src);
-        iframe.attr('src', iframe.attr('src'));
-        video.hide();
-      }
-    },
-
-    open_video : function (e) {
-      var video = $('.flex-video', e.target),
-          iframe = video.find('iframe');
-
-      if (iframe.length > 0) {
-        var data_src = iframe.attr('data-src');
-        if (typeof data_src === 'string') {
-          iframe[0].src = iframe.attr('data-src');
-        } else {
-          var src = iframe[0].src;
-          iframe[0].src = undefined;
-          iframe[0].src = src;
-        }
-        video.show();
-      }
-    },
-
-    data_attr : function (str) {
-      if (this.namespace.length > 0) {
-        return this.namespace + '-' + str;
-      }
-
-      return str;
-    },
-
-    cache_offset : function (modal) {
-      var offset = modal.show().height() + parseInt(modal.css('top'), 10);
-
-      modal.hide();
-
-      return offset;
-    },
-
-    off : function () {
-      $(this.scope).off('.fndtn.reveal');
-    },
-
-    reflow : function () {}
-  };
-
-  /*
-   * getAnimationData('popAndFade') // {animate: true,  pop: true,  fade: true}
-   * getAnimationData('fade')       // {animate: true,  pop: false, fade: true}
-   * getAnimationData('pop')        // {animate: true,  pop: true,  fade: false}
-   * getAnimationData('foo')        // {animate: false, pop: false, fade: false}
-   * getAnimationData(null)         // {animate: false, pop: false, fade: false}
-   */
-  function getAnimationData(str) {
-    var fade = /fade/i.test(str);
-    var pop = /pop/i.test(str);
-    return {
-      animate : fade || pop,
-      pop : pop,
-      fade : fade
-    };
-  }
-}(jQuery, window, window.document));
diff --git a/3dmol/js/foundation/foundation.slider.js b/3dmol/js/foundation/foundation.slider.js
deleted file mode 100644
index 4d069bc..0000000
--- a/3dmol/js/foundation/foundation.slider.js
+++ /dev/null
@@ -1,263 +0,0 @@
-;(function ($, window, document, undefined) {
-  'use strict';
-
-  Foundation.libs.slider = {
-    name : 'slider',
-
-    version : '5.5.1',
-
-    settings : {
-      start : 0,
-      end : 100,
-      step : 1,
-      precision : null,
-      initial : null,
-      display_selector : '',
-      vertical : false,
-      trigger_input_change : false,
-      on_change : function () {}
-    },
-
-    cache : {},
-
-    init : function (scope, method, options) {
-      Foundation.inherit(this, 'throttle');
-      this.bindings(method, options);
-      this.reflow();
-    },
-
-    events : function () {
-      var self = this;
-
-      $(this.scope)
-        .off('.slider')
-        .on('mousedown.fndtn.slider touchstart.fndtn.slider pointerdown.fndtn.slider',
-        '[' + self.attr_name() + ']:not(.disabled, [disabled]) .range-slider-handle', function (e) {
-          if (!self.cache.active) {
-            e.preventDefault();
-            self.set_active_slider($(e.target));
-          }
-        })
-        .on('mousemove.fndtn.slider touchmove.fndtn.slider pointermove.fndtn.slider', function (e) {
-          if (!!self.cache.active) {
-            e.preventDefault();
-            if ($.data(self.cache.active[0], 'settings').vertical) {
-              var scroll_offset = 0;
-              if (!e.pageY) {
-                scroll_offset = window.scrollY;
-              }
-              self.calculate_position(self.cache.active, self.get_cursor_position(e, 'y') + scroll_offset);
-            } else {
-              self.calculate_position(self.cache.active, self.get_cursor_position(e, 'x'));
-            }
-          }
-        })
-        .on('mouseup.fndtn.slider touchend.fndtn.slider pointerup.fndtn.slider', function (e) {
-          self.remove_active_slider();
-        })
-        .on('change.fndtn.slider', function (e) {
-          self.settings.on_change();
-        });
-
-      self.S(window)
-        .on('resize.fndtn.slider', self.throttle(function (e) {
-          self.reflow();
-        }, 300));
-    },
-
-    get_cursor_position : function (e, xy) {
-      var pageXY = 'page' + xy.toUpperCase(),
-          clientXY = 'client' + xy.toUpperCase(),
-          position;
-
-      if (typeof e[pageXY] !== 'undefined') {
-        position = e[pageXY];
-      } else if (typeof e.originalEvent[clientXY] !== 'undefined') {
-        position = e.originalEvent[clientXY];
-      } else if (e.originalEvent.touches && e.originalEvent.touches[0] && typeof e.originalEvent.touches[0][clientXY] !== 'undefined') {
-        position = e.originalEvent.touches[0][clientXY];
-      } else if (e.currentPoint && typeof e.currentPoint[xy] !== 'undefined') {
-        position = e.currentPoint[xy];
-      }
-
-      return position;
-    },
-
-    set_active_slider : function ($handle) {
-      this.cache.active = $handle;
-    },
-
-    remove_active_slider : function () {
-      this.cache.active = null;
-    },
-
-    calculate_position : function ($handle, cursor_x) {
-      var self = this,
-          settings = $.data($handle[0], 'settings'),
-          handle_l = $.data($handle[0], 'handle_l'),
-          handle_o = $.data($handle[0], 'handle_o'),
-          bar_l = $.data($handle[0], 'bar_l'),
-          bar_o = $.data($handle[0], 'bar_o');
-
-      requestAnimationFrame(function () {
-        var pct;
-
-        if (Foundation.rtl && !settings.vertical) {
-          pct = self.limit_to(((bar_o + bar_l - cursor_x) / bar_l), 0, 1);
-        } else {
-          pct = self.limit_to(((cursor_x - bar_o) / bar_l), 0, 1);
-        }
-
-        pct = settings.vertical ? 1 - pct : pct;
-
-        var norm = self.normalized_value(pct, settings.start, settings.end, settings.step, settings.precision);
-
-        self.set_ui($handle, norm);
-      });
-    },
-
-    set_ui : function ($handle, value) {
-      var settings = $.data($handle[0], 'settings'),
-          handle_l = $.data($handle[0], 'handle_l'),
-          bar_l = $.data($handle[0], 'bar_l'),
-          norm_pct = this.normalized_percentage(value, settings.start, settings.end),
-          handle_offset = norm_pct * (bar_l - handle_l) - 1,
-          progress_bar_length = norm_pct * 100,
-          $handle_parent = $handle.parent(),
-          $hidden_inputs = $handle.parent().children('input[type=hidden]');
-
-      if (Foundation.rtl && !settings.vertical) {
-        handle_offset = -handle_offset;
-      }
-
-      handle_offset = settings.vertical ? -handle_offset + bar_l - handle_l + 1 : handle_offset;
-      this.set_translate($handle, handle_offset, settings.vertical);
-
-      if (settings.vertical) {
-        $handle.siblings('.range-slider-active-segment').css('height', progress_bar_length + '%');
-      } else {
-        $handle.siblings('.range-slider-active-segment').css('width', progress_bar_length + '%');
-      }
-
-      $handle_parent.attr(this.attr_name(), value).trigger('change').trigger('change.fndtn.slider');
-
-      $hidden_inputs.val(value);
-      if (settings.trigger_input_change) {
-          $hidden_inputs.trigger('change');
-      }
-
-      if (!$handle[0].hasAttribute('aria-valuemin')) {
-        $handle.attr({
-          'aria-valuemin' : settings.start,
-          'aria-valuemax' : settings.end
-        });
-      }
-      $handle.attr('aria-valuenow', value);
-
-      if (settings.display_selector != '') {
-        $(settings.display_selector).each(function () {
-          if (this.hasOwnProperty('value')) {
-            $(this).val(value);
-          } else {
-            $(this).text(value);
-          }
-        });
-      }
-
-    },
-
-    normalized_percentage : function (val, start, end) {
-      return Math.min(1, (val - start) / (end - start));
-    },
-
-    normalized_value : function (val, start, end, step, precision) {
-      var range = end - start,
-          point = val * range,
-          mod = (point - (point % step)) / step,
-          rem = point % step,
-          round = ( rem >= step * 0.5 ? step : 0);
-      return ((mod * step + round) + start).toFixed(precision);
-    },
-
-    set_translate : function (ele, offset, vertical) {
-      if (vertical) {
-        $(ele)
-          .css('-webkit-transform', 'translateY(' + offset + 'px)')
-          .css('-moz-transform', 'translateY(' + offset + 'px)')
-          .css('-ms-transform', 'translateY(' + offset + 'px)')
-          .css('-o-transform', 'translateY(' + offset + 'px)')
-          .css('transform', 'translateY(' + offset + 'px)');
-      } else {
-        $(ele)
-          .css('-webkit-transform', 'translateX(' + offset + 'px)')
-          .css('-moz-transform', 'translateX(' + offset + 'px)')
-          .css('-ms-transform', 'translateX(' + offset + 'px)')
-          .css('-o-transform', 'translateX(' + offset + 'px)')
-          .css('transform', 'translateX(' + offset + 'px)');
-      }
-    },
-
-    limit_to : function (val, min, max) {
-      return Math.min(Math.max(val, min), max);
-    },
-
-    initialize_settings : function (handle) {
-      var settings = $.extend({}, this.settings, this.data_options($(handle).parent())),
-          decimal_places_match_result;
-
-      if (settings.precision === null) {
-        decimal_places_match_result = ('' + settings.step).match(/\.([\d]*)/);
-        settings.precision = decimal_places_match_result && decimal_places_match_result[1] ? decimal_places_match_result[1].length : 0;
-      }
-
-      if (settings.vertical) {
-        $.data(handle, 'bar_o', $(handle).parent().offset().top);
-        $.data(handle, 'bar_l', $(handle).parent().outerHeight());
-        $.data(handle, 'handle_o', $(handle).offset().top);
-        $.data(handle, 'handle_l', $(handle).outerHeight());
-      } else {
-        $.data(handle, 'bar_o', $(handle).parent().offset().left);
-        $.data(handle, 'bar_l', $(handle).parent().outerWidth());
-        $.data(handle, 'handle_o', $(handle).offset().left);
-        $.data(handle, 'handle_l', $(handle).outerWidth());
-      }
-
-      $.data(handle, 'bar', $(handle).parent());
-      $.data(handle, 'settings', settings);
-    },
-
-    set_initial_position : function ($ele) {
-      var settings = $.data($ele.children('.range-slider-handle')[0], 'settings'),
-          initial = ((typeof settings.initial == 'number' && !isNaN(settings.initial)) ? settings.initial : Math.floor((settings.end - settings.start) * 0.5 / settings.step) * settings.step + settings.start),
-          $handle = $ele.children('.range-slider-handle');
-      this.set_ui($handle, initial);
-    },
-
-    set_value : function (value) {
-      var self = this;
-      $('[' + self.attr_name() + ']', this.scope).each(function () {
-        $(this).attr(self.attr_name(), value);
-      });
-      if (!!$(this.scope).attr(self.attr_name())) {
-        $(this.scope).attr(self.attr_name(), value);
-      }
-      self.reflow();
-    },
-
-    reflow : function () {
-      var self = this;
-      self.S('[' + this.attr_name() + ']').each(function () {
-        var handle = $(this).children('.range-slider-handle')[0],
-            val = $(this).attr(self.attr_name());
-        self.initialize_settings(handle);
-
-        if (val) {
-          self.set_ui($(handle), parseFloat(val));
-        } else {
-          self.set_initial_position($(this));
-        }
-      });
-    }
-  };
-
-}(jQuery, window, window.document));
diff --git a/3dmol/js/foundation/foundation.tab.js b/3dmol/js/foundation/foundation.tab.js
deleted file mode 100644
index 51daa25..0000000
--- a/3dmol/js/foundation/foundation.tab.js
+++ /dev/null
@@ -1,237 +0,0 @@
-;(function ($, window, document, undefined) {
-  'use strict';
-
-  Foundation.libs.tab = {
-    name : 'tab',
-
-    version : '5.5.1',
-
-    settings : {
-      active_class : 'active',
-      callback : function () {},
-      deep_linking : false,
-      scroll_to_content : true,
-      is_hover : false
-    },
-
-    default_tab_hashes : [],
-
-    init : function (scope, method, options) {
-      var self = this,
-          S = this.S;
-
-      this.bindings(method, options);
-
-      // store the initial href, which is used to allow correct behaviour of the
-      // browser back button when deep linking is turned on.
-      self.entry_location = window.location.href;
-
-      this.handle_location_hash_change();
-
-      // Store the default active tabs which will be referenced when the
-      // location hash is absent, as in the case of navigating the tabs and
-      // returning to the first viewing via the browser Back button.
-      S('[' + this.attr_name() + '] > .active > a', this.scope).each(function () {
-        self.default_tab_hashes.push(this.hash);
-      });
-    },
-
-    events : function () {
-      var self = this,
-          S = this.S;
-
-      var usual_tab_behavior =  function (e) {
-          var settings = S(this).closest('[' + self.attr_name() + ']').data(self.attr_name(true) + '-init');
-          if (!settings.is_hover || Modernizr.touch) {
-            e.preventDefault();
-            e.stopPropagation();
-            self.toggle_active_tab(S(this).parent());
-          }
-        };
-
-      S(this.scope)
-        .off('.tab')
-        // Click event: tab title
-        .on('focus.fndtn.tab', '[' + this.attr_name() + '] > * > a', usual_tab_behavior )
-        .on('click.fndtn.tab', '[' + this.attr_name() + '] > * > a', usual_tab_behavior )
-        // Hover event: tab title
-        .on('mouseenter.fndtn.tab', '[' + this.attr_name() + '] > * > a', function (e) {
-          var settings = S(this).closest('[' + self.attr_name() + ']').data(self.attr_name(true) + '-init');
-          if (settings.is_hover) {
-            self.toggle_active_tab(S(this).parent());
-          }
-        });
-
-      // Location hash change event
-      S(window).on('hashchange.fndtn.tab', function (e) {
-        e.preventDefault();
-        self.handle_location_hash_change();
-      });
-    },
-
-    handle_location_hash_change : function () {
-
-      var self = this,
-          S = this.S;
-
-      S('[' + this.attr_name() + ']', this.scope).each(function () {
-        var settings = S(this).data(self.attr_name(true) + '-init');
-        if (settings.deep_linking) {
-          // Match the location hash to a label
-          var hash;
-          if (settings.scroll_to_content) {
-            hash = self.scope.location.hash;
-          } else {
-            // prefix the hash to prevent anchor scrolling
-            hash = self.scope.location.hash.replace('fndtn-', '');
-          }
-          if (hash != '') {
-            // Check whether the location hash references a tab content div or
-            // another element on the page (inside or outside the tab content div)
-            var hash_element = S(hash);
-            if (hash_element.hasClass('content') && hash_element.parent().hasClass('tabs-content')) {
-              // Tab content div
-              self.toggle_active_tab($('[' + self.attr_name() + '] > * > a[href=' + hash + ']').parent());
-            } else {
-              // Not the tab content div. If inside the tab content, find the
-              // containing tab and toggle it as active.
-              var hash_tab_container_id = hash_element.closest('.content').attr('id');
-              if (hash_tab_container_id != undefined) {
-                self.toggle_active_tab($('[' + self.attr_name() + '] > * > a[href=#' + hash_tab_container_id + ']').parent(), hash);
-              }
-            }
-          } else {
-            // Reference the default tab hashes which were initialized in the init function
-            for (var ind = 0; ind < self.default_tab_hashes.length; ind++) {
-              self.toggle_active_tab($('[' + self.attr_name() + '] > * > a[href=' + self.default_tab_hashes[ind] + ']').parent());
-            }
-          }
-        }
-       });
-     },
-
-    toggle_active_tab : function (tab, location_hash) {
-      var self = this,
-          S = self.S,
-          tabs = tab.closest('[' + this.attr_name() + ']'),
-          tab_link = tab.find('a'),
-          anchor = tab.children('a').first(),
-          target_hash = '#' + anchor.attr('href').split('#')[1],
-          target = S(target_hash),
-          siblings = tab.siblings(),
-          settings = tabs.data(this.attr_name(true) + '-init'),
-          interpret_keyup_action = function (e) {
-            // Light modification of Heydon Pickering's Practical ARIA Examples: http://heydonworks.com/practical_aria_examples/js/a11y.js
-
-            // define current, previous and next (possible) tabs
-
-            var $original = $(this);
-            var $prev = $(this).parents('li').prev().children('[role="tab"]');
-            var $next = $(this).parents('li').next().children('[role="tab"]');
-            var $target;
-
-            // find the direction (prev or next)
-
-            switch (e.keyCode) {
-              case 37:
-                $target = $prev;
-                break;
-              case 39:
-                $target = $next;
-                break;
-              default:
-                $target = false
-                  break;
-            }
-
-            if ($target.length) {
-              $original.attr({
-                'tabindex' : '-1',
-                'aria-selected' : null
-              });
-              $target.attr({
-                'tabindex' : '0',
-                'aria-selected' : true
-              }).focus();
-            }
-
-            // Hide panels
-
-            $('[role="tabpanel"]')
-              .attr('aria-hidden', 'true');
-
-            // Show panel which corresponds to target
-
-            $('#' + $(document.activeElement).attr('href').substring(1))
-              .attr('aria-hidden', null);
-
-          },
-          go_to_hash = function(hash) {
-            // This function allows correct behaviour of the browser's back button when deep linking is enabled. Without it
-            // the user would get continually redirected to the default hash.
-            var is_entry_location = window.location.href === self.entry_location,
-                default_hash = settings.scroll_to_content ? self.default_tab_hashes[0] : is_entry_location ? window.location.hash :'fndtn-' + self.default_tab_hashes[0].replace('#', '')
-
-            if (!(is_entry_location && hash === default_hash)) {
-              window.location.hash = hash;
-            }
-          };
-
-      // allow usage of data-tab-content attribute instead of href
-      if (S(this).data(this.data_attr('tab-content'))) {
-        target_hash = '#' + S(this).data(this.data_attr('tab-content')).split('#')[1];
-        target = S(target_hash);
-      }
-
-      if (settings.deep_linking) {
-
-        if (settings.scroll_to_content) {
-
-          // retain current hash to scroll to content
-          go_to_hash(location_hash || target_hash);
-
-          if (location_hash == undefined || location_hash == target_hash) {
-            tab.parent()[0].scrollIntoView();
-          } else {
-            S(target_hash)[0].scrollIntoView();
-          }
-        } else {
-          // prefix the hashes so that the browser doesn't scroll down
-          if (location_hash != undefined) {
-            go_to_hash('fndtn-' + location_hash.replace('#', ''));
-          } else {
-            go_to_hash('fndtn-' + target_hash.replace('#', ''));
-          }
-        }
-      }
-
-      // WARNING: The activation and deactivation of the tab content must
-      // occur after the deep linking in order to properly refresh the browser
-      // window (notably in Chrome).
-      // Clean up multiple attr instances to done once
-      tab.addClass(settings.active_class).triggerHandler('opened');
-      tab_link.attr({'aria-selected' : 'true',  tabindex : 0});
-      siblings.removeClass(settings.active_class)
-      siblings.find('a').attr({'aria-selected' : 'false',  tabindex : -1});
-      target.siblings().removeClass(settings.active_class).attr({'aria-hidden' : 'true',  tabindex : -1});
-      target.addClass(settings.active_class).attr('aria-hidden', 'false').removeAttr('tabindex');
-      settings.callback(tab);
-      target.triggerHandler('toggled', [tab]);
-      tabs.triggerHandler('toggled', [target]);
-
-      tab_link.off('keydown').on('keydown', interpret_keyup_action );
-    },
-
-    data_attr : function (str) {
-      if (this.namespace.length > 0) {
-        return this.namespace + '-' + str;
-      }
-
-      return str;
-    },
-
-    off : function () {},
-
-    reflow : function () {}
-  };
-}(jQuery, window, window.document));
diff --git a/3dmol/js/foundation/foundation.tooltip.js b/3dmol/js/foundation/foundation.tooltip.js
deleted file mode 100644
index bb8faac..0000000
--- a/3dmol/js/foundation/foundation.tooltip.js
+++ /dev/null
@@ -1,307 +0,0 @@
-;(function ($, window, document, undefined) {
-  'use strict';
-
-  Foundation.libs.tooltip = {
-    name : 'tooltip',
-
-    version : '5.5.1',
-
-    settings : {
-      additional_inheritable_classes : [],
-      tooltip_class : '.tooltip',
-      append_to : 'body',
-      touch_close_text : 'Tap To Close',
-      disable_for_touch : false,
-      hover_delay : 200,
-      show_on : 'all',
-      tip_template : function (selector, content) {
-        return '<span data-selector="' + selector + '" id="' + selector + '" class="'
-          + Foundation.libs.tooltip.settings.tooltip_class.substring(1)
-          + '" role="tooltip">' + content + '<span class="nub"></span></span>';
-      }
-    },
-
-    cache : {},
-
-    init : function (scope, method, options) {
-      Foundation.inherit(this, 'random_str');
-      this.bindings(method, options);
-    },
-
-    should_show : function (target, tip) {
-      var settings = $.extend({}, this.settings, this.data_options(target));
-
-      if (settings.show_on === 'all') {
-        return true;
-      } else if (this.small() && settings.show_on === 'small') {
-        return true;
-      } else if (this.medium() && settings.show_on === 'medium') {
-        return true;
-      } else if (this.large() && settings.show_on === 'large') {
-        return true;
-      }
-      return false;
-    },
-
-    medium : function () {
-      return matchMedia(Foundation.media_queries['medium']).matches;
-    },
-
-    large : function () {
-      return matchMedia(Foundation.media_queries['large']).matches;
-    },
-
-    events : function (instance) {
-      var self = this,
-          S = self.S;
-
-      self.create(this.S(instance));
-
-      $(this.scope)
-        .off('.tooltip')
-        .on('mouseenter.fndtn.tooltip mouseleave.fndtn.tooltip touchstart.fndtn.tooltip MSPointerDown.fndtn.tooltip',
-          '[' + this.attr_name() + ']', function (e) {
-          var $this = S(this),
-              settings = $.extend({}, self.settings, self.data_options($this)),
-              is_touch = false;
-
-          if (Modernizr.touch && /touchstart|MSPointerDown/i.test(e.type) && S(e.target).is('a')) {
-            return false;
-          }
-
-          if (/mouse/i.test(e.type) && self.ie_touch(e)) {
-            return false;
-          }
-
-          if ($this.hasClass('open')) {
-            if (Modernizr.touch && /touchstart|MSPointerDown/i.test(e.type)) {
-              e.preventDefault();
-            }
-            self.hide($this);
-          } else {
-            if (settings.disable_for_touch && Modernizr.touch && /touchstart|MSPointerDown/i.test(e.type)) {
-              return;
-            } else if (!settings.disable_for_touch && Modernizr.touch && /touchstart|MSPointerDown/i.test(e.type)) {
-              e.preventDefault();
-              S(settings.tooltip_class + '.open').hide();
-              is_touch = true;
-            }
-
-            if (/enter|over/i.test(e.type)) {
-              this.timer = setTimeout(function () {
-                var tip = self.showTip($this);
-              }.bind(this), self.settings.hover_delay);
-            } else if (e.type === 'mouseout' || e.type === 'mouseleave') {
-              clearTimeout(this.timer);
-              self.hide($this);
-            } else {
-              self.showTip($this);
-            }
-          }
-        })
-        .on('mouseleave.fndtn.tooltip touchstart.fndtn.tooltip MSPointerDown.fndtn.tooltip', '[' + this.attr_name() + '].open', function (e) {
-          if (/mouse/i.test(e.type) && self.ie_touch(e)) {
-            return false;
-          }
-
-          if ($(this).data('tooltip-open-event-type') == 'touch' && e.type == 'mouseleave') {
-            return;
-          } else if ($(this).data('tooltip-open-event-type') == 'mouse' && /MSPointerDown|touchstart/i.test(e.type)) {
-            self.convert_to_touch($(this));
-          } else {
-            self.hide($(this));
-          }
-        })
-        .on('DOMNodeRemoved DOMAttrModified', '[' + this.attr_name() + ']:not(a)', function (e) {
-          self.hide(S(this));
-        });
-    },
-
-    ie_touch : function (e) {
-      // How do I distinguish between IE11 and Windows Phone 8?????
-      return false;
-    },
-
-    showTip : function ($target) {
-      var $tip = this.getTip($target);
-      if (this.should_show($target, $tip)) {
-        return this.show($target);
-      }
-      return;
-    },
-
-    getTip : function ($target) {
-      var selector = this.selector($target),
-          settings = $.extend({}, this.settings, this.data_options($target)),
-          tip = null;
-
-      if (selector) {
-        tip = this.S('span[data-selector="' + selector + '"]' + settings.tooltip_class);
-      }
-
-      return (typeof tip === 'object') ? tip : false;
-    },
-
-    selector : function ($target) {
-      var id = $target.attr('id'),
-          dataSelector = $target.attr(this.attr_name()) || $target.attr('data-selector');
-
-      if ((id && id.length < 1 || !id) && typeof dataSelector != 'string') {
-        dataSelector = this.random_str(6);
-        $target
-          .attr('data-selector', dataSelector)
-          .attr('aria-describedby', dataSelector);
-      }
-
-      return (id && id.length > 0) ? id : dataSelector;
-    },
-
-    create : function ($target) {
-      var self = this,
-          settings = $.extend({}, this.settings, this.data_options($target)),
-          tip_template = this.settings.tip_template;
-
-      if (typeof settings.tip_template === 'string' && window.hasOwnProperty(settings.tip_template)) {
-        tip_template = window[settings.tip_template];
-      }
-
-      var $tip = $(tip_template(this.selector($target), $('<div></div>').html($target.attr('title')).html())),
-          classes = this.inheritable_classes($target);
-
-      $tip.addClass(classes).appendTo(settings.append_to);
-
-      if (Modernizr.touch) {
-        $tip.append('<span class="tap-to-close">' + settings.touch_close_text + '</span>');
-        $tip.on('touchstart.fndtn.tooltip MSPointerDown.fndtn.tooltip', function (e) {
-          self.hide($target);
-        });
-      }
-
-      $target.removeAttr('title').attr('title', '');
-    },
-
-    reposition : function (target, tip, classes) {
-      var width, nub, nubHeight, nubWidth, column, objPos;
-
-      tip.css('visibility', 'hidden').show();
-
-      width = target.data('width');
-      nub = tip.children('.nub');
-      nubHeight = nub.outerHeight();
-      nubWidth = nub.outerHeight();
-
-      if (this.small()) {
-        tip.css({'width' : '100%'});
-      } else {
-        tip.css({'width' : (width) ? width : 'auto'});
-      }
-
-      objPos = function (obj, top, right, bottom, left, width) {
-        return obj.css({
-          'top' : (top) ? top : 'auto',
-          'bottom' : (bottom) ? bottom : 'auto',
-          'left' : (left) ? left : 'auto',
-          'right' : (right) ? right : 'auto'
-        }).end();
-      };
-
-      objPos(tip, (target.offset().top + target.outerHeight() + 10), 'auto', 'auto', target.offset().left);
-
-      if (this.small()) {
-        objPos(tip, (target.offset().top + target.outerHeight() + 10), 'auto', 'auto', 12.5, $(this.scope).width());
-        tip.addClass('tip-override');
-        objPos(nub, -nubHeight, 'auto', 'auto', target.offset().left);
-      } else {
-        var left = target.offset().left;
-        if (Foundation.rtl) {
-          nub.addClass('rtl');
-          left = target.offset().left + target.outerWidth() - tip.outerWidth();
-        }
-        objPos(tip, (target.offset().top + target.outerHeight() + 10), 'auto', 'auto', left);
-        tip.removeClass('tip-override');
-        if (classes && classes.indexOf('tip-top') > -1) {
-          if (Foundation.rtl) {
-            nub.addClass('rtl');
-          }
-          objPos(tip, (target.offset().top - tip.outerHeight()), 'auto', 'auto', left)
-            .removeClass('tip-override');
-        } else if (classes && classes.indexOf('tip-left') > -1) {
-          objPos(tip, (target.offset().top + (target.outerHeight() / 2) - (tip.outerHeight() / 2)), 'auto', 'auto', (target.offset().left - tip.outerWidth() - nubHeight))
-            .removeClass('tip-override');
-          nub.removeClass('rtl');
-        } else if (classes && classes.indexOf('tip-right') > -1) {
-          objPos(tip, (target.offset().top + (target.outerHeight() / 2) - (tip.outerHeight() / 2)), 'auto', 'auto', (target.offset().left + target.outerWidth() + nubHeight))
-            .removeClass('tip-override');
-          nub.removeClass('rtl');
-        }
-      }
-
-      tip.css('visibility', 'visible').hide();
-    },
-
-    small : function () {
-      return matchMedia(Foundation.media_queries.small).matches &&
-        !matchMedia(Foundation.media_queries.medium).matches;
-    },
-
-    inheritable_classes : function ($target) {
-      var settings = $.extend({}, this.settings, this.data_options($target)),
-          inheritables = ['tip-top', 'tip-left', 'tip-bottom', 'tip-right', 'radius', 'round'].concat(settings.additional_inheritable_classes),
-          classes = $target.attr('class'),
-          filtered = classes ? $.map(classes.split(' '), function (el, i) {
-            if ($.inArray(el, inheritables) !== -1) {
-              return el;
-            }
-          }).join(' ') : '';
-
-      return $.trim(filtered);
-    },
-
-    convert_to_touch : function ($target) {
-      var self = this,
-          $tip = self.getTip($target),
-          settings = $.extend({}, self.settings, self.data_options($target));
-
-      if ($tip.find('.tap-to-close').length === 0) {
-        $tip.append('<span class="tap-to-close">' + settings.touch_close_text + '</span>');
-        $tip.on('click.fndtn.tooltip.tapclose touchstart.fndtn.tooltip.tapclose MSPointerDown.fndtn.tooltip.tapclose', function (e) {
-          self.hide($target);
-        });
-      }
-
-      $target.data('tooltip-open-event-type', 'touch');
-    },
-
-    show : function ($target) {
-      var $tip = this.getTip($target);
-
-      if ($target.data('tooltip-open-event-type') == 'touch') {
-        this.convert_to_touch($target);
-      }
-
-      this.reposition($target, $tip, $target.attr('class'));
-      $target.addClass('open');
-      $tip.fadeIn(150);
-    },
-
-    hide : function ($target) {
-      var $tip = this.getTip($target);
-
-      $tip.fadeOut(150, function () {
-        $tip.find('.tap-to-close').remove();
-        $tip.off('click.fndtn.tooltip.tapclose MSPointerDown.fndtn.tapclose');
-        $target.removeClass('open');
-      });
-    },
-
-    off : function () {
-      var self = this;
-      this.S(this.scope).off('.fndtn.tooltip');
-      this.S(this.settings.tooltip_class).each(function (i) {
-        $('[' + self.attr_name() + ']').eq(i).attr('title', $(this).text());
-      }).remove();
-    },
-
-    reflow : function () {}
-  };
-}(jQuery, window, window.document));
diff --git a/3dmol/js/foundation/foundation.topbar.js b/3dmol/js/foundation/foundation.topbar.js
deleted file mode 100644
index 30e581d..0000000
--- a/3dmol/js/foundation/foundation.topbar.js
+++ /dev/null
@@ -1,452 +0,0 @@
-;(function ($, window, document, undefined) {
-  'use strict';
-
-  Foundation.libs.topbar = {
-    name : 'topbar',
-
-    version : '5.5.1',
-
-    settings : {
-      index : 0,
-      sticky_class : 'sticky',
-      custom_back_text : true,
-      back_text : 'Back',
-      mobile_show_parent_link : true,
-      is_hover : true,
-      scrolltop : true, // jump to top when sticky nav menu toggle is clicked
-      sticky_on : 'all'
-    },
-
-    init : function (section, method, options) {
-      Foundation.inherit(this, 'add_custom_rule register_media throttle');
-      var self = this;
-
-      self.register_media('topbar', 'foundation-mq-topbar');
-
-      this.bindings(method, options);
-
-      self.S('[' + this.attr_name() + ']', this.scope).each(function () {
-        var topbar = $(this),
-            settings = topbar.data(self.attr_name(true) + '-init'),
-            section = self.S('section, .top-bar-section', this);
-        topbar.data('index', 0);
-        var topbarContainer = topbar.parent();
-        if (topbarContainer.hasClass('fixed') || self.is_sticky(topbar, topbarContainer, settings) ) {
-          self.settings.sticky_class = settings.sticky_class;
-          self.settings.sticky_topbar = topbar;
-          topbar.data('height', topbarContainer.outerHeight());
-          topbar.data('stickyoffset', topbarContainer.offset().top);
-        } else {
-          topbar.data('height', topbar.outerHeight());
-        }
-
-        if (!settings.assembled) {
-          self.assemble(topbar);
-        }
-
-        if (settings.is_hover) {
-          self.S('.has-dropdown', topbar).addClass('not-click');
-        } else {
-          self.S('.has-dropdown', topbar).removeClass('not-click');
-        }
-
-        // Pad body when sticky (scrolled) or fixed.
-        self.add_custom_rule('.f-topbar-fixed { padding-top: ' + topbar.data('height') + 'px }');
-
-        if (topbarContainer.hasClass('fixed')) {
-          self.S('body').addClass('f-topbar-fixed');
-        }
-      });
-
-    },
-
-    is_sticky : function (topbar, topbarContainer, settings) {
-      var sticky     = topbarContainer.hasClass(settings.sticky_class);
-      var smallMatch = matchMedia(Foundation.media_queries.small).matches;
-      var medMatch   = matchMedia(Foundation.media_queries.medium).matches;
-      var lrgMatch   = matchMedia(Foundation.media_queries.large).matches;
-      
-       if (sticky && settings.sticky_on === 'all') {
-          return true;
-       }
-       if (sticky && this.small() && settings.sticky_on.indexOf('small') !== -1) {
-           if (smallMatch && !medMatch && !lrgMatch) { return true; }
-       }
-       if (sticky && this.medium() && settings.sticky_on.indexOf('medium') !== -1) {
-           if (smallMatch && medMatch && !lrgMatch) { return true; }
-       }
-       if (sticky && this.large() && settings.sticky_on.indexOf('large') !== -1) {
-           if (smallMatch && medMatch && lrgMatch) { return true; }
-       }
-
-       // fix for iOS browsers
-       if (sticky && navigator.userAgent.match(/(iPad|iPhone|iPod)/g)) {
-        return true;
-       }
-       return false;
-    },
-
-    toggle : function (toggleEl) {
-      var self = this,
-          topbar;
-
-      if (toggleEl) {
-        topbar = self.S(toggleEl).closest('[' + this.attr_name() + ']');
-      } else {
-        topbar = self.S('[' + this.attr_name() + ']');
-      }
-
-      var settings = topbar.data(this.attr_name(true) + '-init');
-
-      var section = self.S('section, .top-bar-section', topbar);
-
-      if (self.breakpoint()) {
-        if (!self.rtl) {
-          section.css({left : '0%'});
-          $('>.name', section).css({left : '100%'});
-        } else {
-          section.css({right : '0%'});
-          $('>.name', section).css({right : '100%'});
-        }
-
-        self.S('li.moved', section).removeClass('moved');
-        topbar.data('index', 0);
-
-        topbar
-          .toggleClass('expanded')
-          .css('height', '');
-      }
-
-      if (settings.scrolltop) {
-        if (!topbar.hasClass('expanded')) {
-          if (topbar.hasClass('fixed')) {
-            topbar.parent().addClass('fixed');
-            topbar.removeClass('fixed');
-            self.S('body').addClass('f-topbar-fixed');
-          }
-        } else if (topbar.parent().hasClass('fixed')) {
-          if (settings.scrolltop) {
-            topbar.parent().removeClass('fixed');
-            topbar.addClass('fixed');
-            self.S('body').removeClass('f-topbar-fixed');
-
-            window.scrollTo(0, 0);
-          } else {
-            topbar.parent().removeClass('expanded');
-          }
-        }
-      } else {
-        if (self.is_sticky(topbar, topbar.parent(), settings)) {
-          topbar.parent().addClass('fixed');
-        }
-
-        if (topbar.parent().hasClass('fixed')) {
-          if (!topbar.hasClass('expanded')) {
-            topbar.removeClass('fixed');
-            topbar.parent().removeClass('expanded');
-            self.update_sticky_positioning();
-          } else {
-            topbar.addClass('fixed');
-            topbar.parent().addClass('expanded');
-            self.S('body').addClass('f-topbar-fixed');
-          }
-        }
-      }
-    },
-
-    timer : null,
-
-    events : function (bar) {
-      var self = this,
-          S = this.S;
-
-      S(this.scope)
-        .off('.topbar')
-        .on('click.fndtn.topbar', '[' + this.attr_name() + '] .toggle-topbar', function (e) {
-          e.preventDefault();
-          self.toggle(this);
-        })
-        .on('click.fndtn.topbar', '.top-bar .top-bar-section li a[href^="#"],[' + this.attr_name() + '] .top-bar-section li a[href^="#"]', function (e) {
-            var li = $(this).closest('li');
-            if (self.breakpoint() && !li.hasClass('back') && !li.hasClass('has-dropdown')) {
-              self.toggle();
-            }
-        })
-        .on('click.fndtn.topbar', '[' + this.attr_name() + '] li.has-dropdown', function (e) {
-          var li = S(this),
-              target = S(e.target),
-              topbar = li.closest('[' + self.attr_name() + ']'),
-              settings = topbar.data(self.attr_name(true) + '-init');
-
-          if (target.data('revealId')) {
-            self.toggle();
-            return;
-          }
-
-          if (self.breakpoint()) {
-            return;
-          }
-
-          if (settings.is_hover && !Modernizr.touch) {
-            return;
-          }
-
-          e.stopImmediatePropagation();
-
-          if (li.hasClass('hover')) {
-            li
-              .removeClass('hover')
-              .find('li')
-              .removeClass('hover');
-
-            li.parents('li.hover')
-              .removeClass('hover');
-          } else {
-            li.addClass('hover');
-
-            $(li).siblings().removeClass('hover');
-
-            if (target[0].nodeName === 'A' && target.parent().hasClass('has-dropdown')) {
-              e.preventDefault();
-            }
-          }
-        })
-        .on('click.fndtn.topbar', '[' + this.attr_name() + '] .has-dropdown>a', function (e) {
-          if (self.breakpoint()) {
-
-            e.preventDefault();
-
-            var $this = S(this),
-                topbar = $this.closest('[' + self.attr_name() + ']'),
-                section = topbar.find('section, .top-bar-section'),
-                dropdownHeight = $this.next('.dropdown').outerHeight(),
-                $selectedLi = $this.closest('li');
-
-            topbar.data('index', topbar.data('index') + 1);
-            $selectedLi.addClass('moved');
-
-            if (!self.rtl) {
-              section.css({left : -(100 * topbar.data('index')) + '%'});
-              section.find('>.name').css({left : 100 * topbar.data('index') + '%'});
-            } else {
-              section.css({right : -(100 * topbar.data('index')) + '%'});
-              section.find('>.name').css({right : 100 * topbar.data('index') + '%'});
-            }
-
-            topbar.css('height', $this.siblings('ul').outerHeight(true) + topbar.data('height'));
-          }
-        });
-
-      S(window).off('.topbar').on('resize.fndtn.topbar', self.throttle(function () {
-          self.resize.call(self);
-      }, 50)).trigger('resize').trigger('resize.fndtn.topbar').load(function () {
-          // Ensure that the offset is calculated after all of the pages resources have loaded
-          S(this).trigger('resize.fndtn.topbar');
-      });
-
-      S('body').off('.topbar').on('click.fndtn.topbar', function (e) {
-        var parent = S(e.target).closest('li').closest('li.hover');
-
-        if (parent.length > 0) {
-          return;
-        }
-
-        S('[' + self.attr_name() + '] li.hover').removeClass('hover');
-      });
-
-      // Go up a level on Click
-      S(this.scope).on('click.fndtn.topbar', '[' + this.attr_name() + '] .has-dropdown .back', function (e) {
-        e.preventDefault();
-
-        var $this = S(this),
-            topbar = $this.closest('[' + self.attr_name() + ']'),
-            section = topbar.find('section, .top-bar-section'),
-            settings = topbar.data(self.attr_name(true) + '-init'),
-            $movedLi = $this.closest('li.moved'),
-            $previousLevelUl = $movedLi.parent();
-
-        topbar.data('index', topbar.data('index') - 1);
-
-        if (!self.rtl) {
-          section.css({left : -(100 * topbar.data('index')) + '%'});
-          section.find('>.name').css({left : 100 * topbar.data('index') + '%'});
-        } else {
-          section.css({right : -(100 * topbar.data('index')) + '%'});
-          section.find('>.name').css({right : 100 * topbar.data('index') + '%'});
-        }
-
-        if (topbar.data('index') === 0) {
-          topbar.css('height', '');
-        } else {
-          topbar.css('height', $previousLevelUl.outerHeight(true) + topbar.data('height'));
-        }
-
-        setTimeout(function () {
-          $movedLi.removeClass('moved');
-        }, 300);
-      });
-
-      // Show dropdown menus when their items are focused
-      S(this.scope).find('.dropdown a')
-        .focus(function () {
-          $(this).parents('.has-dropdown').addClass('hover');
-        })
-        .blur(function () {
-          $(this).parents('.has-dropdown').removeClass('hover');
-        });
-    },
-
-    resize : function () {
-      var self = this;
-      self.S('[' + this.attr_name() + ']').each(function () {
-        var topbar = self.S(this),
-            settings = topbar.data(self.attr_name(true) + '-init');
-
-        var stickyContainer = topbar.parent('.' + self.settings.sticky_class);
-        var stickyOffset;
-
-        if (!self.breakpoint()) {
-          var doToggle = topbar.hasClass('expanded');
-          topbar
-            .css('height', '')
-            .removeClass('expanded')
-            .find('li')
-            .removeClass('hover');
-
-            if (doToggle) {
-              self.toggle(topbar);
-            }
-        }
-
-        if (self.is_sticky(topbar, stickyContainer, settings)) {
-          if (stickyContainer.hasClass('fixed')) {
-            // Remove the fixed to allow for correct calculation of the offset.
-            stickyContainer.removeClass('fixed');
-
-            stickyOffset = stickyContainer.offset().top;
-            if (self.S(document.body).hasClass('f-topbar-fixed')) {
-              stickyOffset -= topbar.data('height');
-            }
-
-            topbar.data('stickyoffset', stickyOffset);
-            stickyContainer.addClass('fixed');
-          } else {
-            stickyOffset = stickyContainer.offset().top;
-            topbar.data('stickyoffset', stickyOffset);
-          }
-        }
-
-      });
-    },
-
-    breakpoint : function () {
-      return !matchMedia(Foundation.media_queries['topbar']).matches;
-    },
-
-    small : function () {
-      return matchMedia(Foundation.media_queries['small']).matches;
-    },
-
-    medium : function () {
-      return matchMedia(Foundation.media_queries['medium']).matches;
-    },
-
-    large : function () {
-      return matchMedia(Foundation.media_queries['large']).matches;
-    },
-
-    assemble : function (topbar) {
-      var self = this,
-          settings = topbar.data(this.attr_name(true) + '-init'),
-          section = self.S('section, .top-bar-section', topbar);
-
-      // Pull element out of the DOM for manipulation
-      section.detach();
-
-      self.S('.has-dropdown>a', section).each(function () {
-        var $link = self.S(this),
-            $dropdown = $link.siblings('.dropdown'),
-            url = $link.attr('href'),
-            $titleLi;
-
-        if (!$dropdown.find('.title.back').length) {
-
-          if (settings.mobile_show_parent_link == true && url) {
-            $titleLi = $('<li class="title back js-generated"><h5><a href="javascript:void(0)"></a></h5></li><li class="parent-link hide-for-large-up"><a class="parent-link js-generated" href="' + url + '">' + $link.html() +'</a></li>');
-          } else {
-            $titleLi = $('<li class="title back js-generated"><h5><a href="javascript:void(0)"></a></h5>');
-          }
-
-          // Copy link to subnav
-          if (settings.custom_back_text == true) {
-            $('h5>a', $titleLi).html(settings.back_text);
-          } else {
-            $('h5>a', $titleLi).html('« ' + $link.html());
-          }
-          $dropdown.prepend($titleLi);
-        }
-      });
-
-      // Put element back in the DOM
-      section.appendTo(topbar);
-
-      // check for sticky
-      this.sticky();
-
-      this.assembled(topbar);
-    },
-
-    assembled : function (topbar) {
-      topbar.data(this.attr_name(true), $.extend({}, topbar.data(this.attr_name(true)), {assembled : true}));
-    },
-
-    height : function (ul) {
-      var total = 0,
-          self = this;
-
-      $('> li', ul).each(function () {
-        total += self.S(this).outerHeight(true);
-      });
-
-      return total;
-    },
-
-    sticky : function () {
-      var self = this;
-
-      this.S(window).on('scroll', function () {
-        self.update_sticky_positioning();
-      });
-    },
-
-    update_sticky_positioning : function () {
-      var klass = '.' + this.settings.sticky_class,
-          $window = this.S(window),
-          self = this;
-
-      if (self.settings.sticky_topbar && self.is_sticky(this.settings.sticky_topbar, this.settings.sticky_topbar.parent(), this.settings)) {
-        var distance = this.settings.sticky_topbar.data('stickyoffset');
-        if (!self.S(klass).hasClass('expanded')) {
-          if ($window.scrollTop() > (distance)) {
-            if (!self.S(klass).hasClass('fixed')) {
-              self.S(klass).addClass('fixed');
-              self.S('body').addClass('f-topbar-fixed');
-            }
-          } else if ($window.scrollTop() <= distance) {
-            if (self.S(klass).hasClass('fixed')) {
-              self.S(klass).removeClass('fixed');
-              self.S('body').removeClass('f-topbar-fixed');
-            }
-          }
-        }
-      }
-    },
-
-    off : function () {
-      this.S(this.scope).off('.fndtn.topbar');
-      this.S(window).off('.fndtn.topbar');
-    },
-
-    reflow : function () {}
-  };
-}(jQuery, window, window.document));
diff --git a/3dmol/js/pitt_3Dmol.js b/3dmol/js/pitt_3Dmol.js
deleted file mode 100644
index ce9afdd..0000000
--- a/3dmol/js/pitt_3Dmol.js
+++ /dev/null
@@ -1,31446 +0,0 @@
-/*!
- * jQuery JavaScript Library v1.11.3
- * http://jquery.com/
- *
- * Includes Sizzle.js
- * http://sizzlejs.com/
- *
- * Copyright 2005, 2014 jQuery Foundation, Inc. and other contributors
- * Released under the MIT license
- * http://jquery.org/license
- *
- * Date: 2015-04-28T16:19Z
- */
-
-(function( global, factory ) {
-
-	if ( typeof module === "object" && typeof module.exports === "object" ) {
-		// For CommonJS and CommonJS-like environments where a proper window is present,
-		// execute the factory and get jQuery
-		// For environments that do not inherently posses a window with a document
-		// (such as Node.js), expose a jQuery-making factory as module.exports
-		// This accentuates the need for the creation of a real window
-		// e.g. var jQuery = require("jquery")(window);
-		// See ticket #14549 for more info
-		module.exports = global.document ?
-			factory( global, true ) :
-			function( w ) {
-				if ( !w.document ) {
-					throw new Error( "jQuery requires a window with a document" );
-				}
-				return factory( w );
-			};
-	} else {
-		factory( global );
-	}
-
-// Pass this if window is not defined yet
-}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
-
-// 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+
-//
-
-var deletedIds = [];
-
-var slice = deletedIds.slice;
-
-var concat = deletedIds.concat;
-
-var push = deletedIds.push;
-
-var indexOf = deletedIds.indexOf;
-
-var class2type = {};
-
-var toString = class2type.toString;
-
-var hasOwn = class2type.hasOwnProperty;
-
-var support = {};
-
-
-
-var
-	version = "1.11.3",
-
-	// Define a local copy of jQuery
-	jQuery = function( selector, context ) {
-		// The jQuery object is actually just the init constructor 'enhanced'
-		// Need init if jQuery is called (just allow error to be thrown if not included)
-		return new jQuery.fn.init( selector, context );
-	},
-
-	// Support: Android<4.1, IE<9
-	// Make sure we trim BOM and NBSP
-	rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,
-
-	// Matches dashed string for camelizing
-	rmsPrefix = /^-ms-/,
-	rdashAlpha = /-([\da-z])/gi,
-
-	// Used by jQuery.camelCase as callback to replace()
-	fcamelCase = function( all, letter ) {
-		return letter.toUpperCase();
-	};
-
-jQuery.fn = jQuery.prototype = {
-	// The current version of jQuery being used
-	jquery: version,
-
-	constructor: jQuery,
-
-	// Start with an empty selector
-	selector: "",
-
-	// The default length of a jQuery object is 0
-	length: 0,
-
-	toArray: function() {
-		return slice.call( this );
-	},
-
-	// Get the Nth element in the matched element set OR
-	// Get the whole matched element set as a clean array
-	get: function( num ) {
-		return num != null ?
-
-			// Return just the one element from the set
-			( num < 0 ? this[ num + this.length ] : this[ num ] ) :
-
-			// Return all the elements in a clean array
-			slice.call( this );
-	},
-
-	// Take an array of elements and push it onto the stack
-	// (returning the new matched element set)
-	pushStack: function( elems ) {
-
-		// Build a new jQuery matched element set
-		var ret = jQuery.merge( this.constructor(), elems );
-
-		// Add the old object onto the stack (as a reference)
-		ret.prevObject = this;
-		ret.context = this.context;
-
-		// Return the newly-formed element set
-		return ret;
-	},
-
-	// Execute a callback for every element in the matched set.
-	// (You can seed the arguments with an array of args, but this is
-	// only used internally.)
-	each: function( callback, args ) {
-		return jQuery.each( this, callback, args );
-	},
-
-	map: function( callback ) {
-		return this.pushStack( jQuery.map(this, function( elem, i ) {
-			return callback.call( elem, i, elem );
-		}));
-	},
-
-	slice: function() {
-		return this.pushStack( slice.apply( this, arguments ) );
-	},
-
-	first: function() {
-		return this.eq( 0 );
-	},
-
-	last: function() {
-		return this.eq( -1 );
-	},
-
-	eq: function( i ) {
-		var len = this.length,
-			j = +i + ( i < 0 ? len : 0 );
-		return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] );
-	},
-
-	end: function() {
-		return this.prevObject || this.constructor(null);
-	},
-
-	// For internal use only.
-	// Behaves like an Array's method, not like a jQuery method.
-	push: push,
-	sort: deletedIds.sort,
-	splice: deletedIds.splice
-};
-
-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;
-
-		// skip the boolean and the target
-		target = arguments[ i ] || {};
-		i++;
-	}
-
-	// Handle case when target is a string or something (possible in deep copy)
-	if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
-		target = {};
-	}
-
-	// extend jQuery itself if only one argument is passed
-	if ( i === length ) {
-		target = this;
-		i--;
-	}
-
-	for ( ; i < length; i++ ) {
-		// Only deal with non-null/undefined values
-		if ( (options = arguments[ i ]) != null ) {
-			// Extend the base object
-			for ( name in options ) {
-				src = target[ name ];
-				copy = options[ name ];
-
-				// Prevent never-ending loop
-				if ( target === copy ) {
-					continue;
-				}
-
-				// Recurse if we're merging plain objects or arrays
-				if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
-					if ( copyIsArray ) {
-						copyIsArray = false;
-						clone = src && jQuery.isArray(src) ? src : [];
-
-					} else {
-						clone = src && jQuery.isPlainObject(src) ? src : {};
-					}
-
-					// Never move original objects, clone them
-					target[ name ] = jQuery.extend( deep, clone, copy );
-
-				// Don't bring in undefined values
-				} else if ( copy !== undefined ) {
-					target[ name ] = copy;
-				}
-			}
-		}
-	}
-
-	// Return the modified object
-	return target;
-};
-
-jQuery.extend({
-	// Unique for each copy of jQuery on the page
-	expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ),
-
-	// Assume jQuery is ready without the ready module
-	isReady: true,
-
-	error: function( msg ) {
-		throw new Error( msg );
-	},
-
-	noop: function() {},
-
-	// 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 ) {
-		/* jshint eqeqeq: false */
-		return obj != null && obj == obj.window;
-	},
-
-	isNumeric: function( obj ) {
-		// parseFloat NaNs numeric-cast false positives (null|true|false|"")
-		// ...but misinterprets leading-number strings, particularly hex literals ("0x...")
-		// subtraction forces infinities to NaN
-		// adding 1 corrects loss of precision from parseFloat (#15100)
-		return !jQuery.isArray( obj ) && (obj - parseFloat( obj ) + 1) >= 0;
-	},
-
-	isEmptyObject: function( obj ) {
-		var name;
-		for ( name in obj ) {
-			return false;
-		}
-		return true;
-	},
-
-	isPlainObject: function( obj ) {
-		var key;
-
-		// 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 &&
-				!hasOwn.call(obj, "constructor") &&
-				!hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
-				return false;
-			}
-		} catch ( e ) {
-			// IE8,9 Will throw exceptions on certain host objects #9897
-			return false;
-		}
-
-		// Support: IE<9
-		// Handle iteration over inherited properties before own properties.
-		if ( support.ownLast ) {
-			for ( key in obj ) {
-				return hasOwn.call( obj, key );
-			}
-		}
-
-		// Own properties are enumerated firstly, so to speed up,
-		// if last one is own, then all properties are own.
-		for ( key in obj ) {}
-
-		return key === undefined || hasOwn.call( obj, key );
-	},
-
-	type: function( obj ) {
-		if ( obj == null ) {
-			return obj + "";
-		}
-		return typeof obj === "object" || typeof obj === "function" ?
-			class2type[ toString.call(obj) ] || "object" :
-			typeof obj;
-	},
-
-	// 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;
-	},
-
-	// Support: Android<4.1, IE<9
-	trim: function( text ) {
-		return text == null ?
-			"" :
-			( text + "" ).replace( rtrim, "" );
-	},
-
-	// results is for internal usage only
-	makeArray: function( arr, results ) {
-		var ret = results || [];
-
-		if ( arr != null ) {
-			if ( isArraylike( Object(arr) ) ) {
-				jQuery.merge( ret,
-					typeof arr === "string" ?
-					[ arr ] : arr
-				);
-			} else {
-				push.call( ret, arr );
-			}
-		}
-
-		return ret;
-	},
-
-	inArray: function( elem, arr, i ) {
-		var len;
-
-		if ( arr ) {
-			if ( indexOf ) {
-				return 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 len = +second.length,
-			j = 0,
-			i = first.length;
-
-		while ( j < len ) {
-			first[ i++ ] = second[ j++ ];
-		}
-
-		// Support: IE<9
-		// Workaround casting of .length to NaN on otherwise arraylike objects (e.g., NodeLists)
-		if ( len !== len ) {
-			while ( second[j] !== undefined ) {
-				first[ i++ ] = second[ j++ ];
-			}
-		}
-
-		first.length = i;
-
-		return first;
-	},
-
-	grep: function( elems, callback, invert ) {
-		var callbackInverse,
-			matches = [],
-			i = 0,
-			length = elems.length,
-			callbackExpect = !invert;
-
-		// Go through the array, only saving the items
-		// that pass the validator function
-		for ( ; i < length; i++ ) {
-			callbackInverse = !callback( elems[ i ], i );
-			if ( callbackInverse !== callbackExpect ) {
-				matches.push( elems[ i ] );
-			}
-		}
-
-		return matches;
-	},
-
-	// arg is for internal usage only
-	map: function( elems, callback, arg ) {
-		var value,
-			i = 0,
-			length = elems.length,
-			isArray = isArraylike( elems ),
-			ret = [];
-
-		// Go through the array, translating each of the items to their new values
-		if ( isArray ) {
-			for ( ; i < length; i++ ) {
-				value = callback( elems[ i ], i, arg );
-
-				if ( value != null ) {
-					ret.push( value );
-				}
-			}
-
-		// Go through every key on the object,
-		} else {
-			for ( i in elems ) {
-				value = callback( elems[ i ], i, arg );
-
-				if ( value != null ) {
-					ret.push( value );
-				}
-			}
-		}
-
-		// Flatten any nested arrays
-		return concat.apply( [], ret );
-	},
-
-	// A global GUID counter for objects
-	guid: 1,
-
-	// Bind a function to a context, optionally partially applying any
-	// arguments.
-	proxy: function( fn, context ) {
-		var 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 = slice.call( arguments, 2 );
-		proxy = function() {
-			return fn.apply( context || this, args.concat( slice.call( arguments ) ) );
-		};
-
-		// Set the guid of unique handler to the same of original handler, so it can be removed
-		proxy.guid = fn.guid = fn.guid || jQuery.guid++;
-
-		return proxy;
-	},
-
-	now: function() {
-		return +( new Date() );
-	},
-
-	// jQuery.support is not used in Core but other projects attach their
-	// properties to it so it needs to exist.
-	support: support
-});
-
-// Populate the class2type map
-jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) {
-	class2type[ "[object " + name + "]" ] = name.toLowerCase();
-});
-
-function isArraylike( obj ) {
-
-	// Support: iOS 8.2 (not reproducible in simulator)
-	// `in` check used to prevent JIT error (gh-2145)
-	// hasOwn isn't used here due to false negatives
-	// regarding Nodelist length in IE
-	var length = "length" in obj && obj.length,
-		type = jQuery.type( obj );
-
-	if ( type === "function" || jQuery.isWindow( obj ) ) {
-		return false;
-	}
-
-	if ( obj.nodeType === 1 && length ) {
-		return true;
-	}
-
-	return type === "array" || length === 0 ||
-		typeof length === "number" && length > 0 && ( length - 1 ) in obj;
-}
-var Sizzle =
-/*!
- * Sizzle CSS Selector Engine v2.2.0-pre
- * http://sizzlejs.com/
- *
- * Copyright 2008, 2014 jQuery Foundation, Inc. and other contributors
- * Released under the MIT license
- * http://jquery.org/license
- *
- * Date: 2014-12-16
- */
-(function( window ) {
-
-var i,
-	support,
-	Expr,
-	getText,
-	isXML,
-	tokenize,
-	compile,
-	select,
-	outermostContext,
-	sortInput,
-	hasDuplicate,
-
-	// Local document vars
-	setDocument,
-	document,
-	docElem,
-	documentIsHTML,
-	rbuggyQSA,
-	rbuggyMatches,
-	matches,
-	contains,
-
-	// Instance-specific data
-	expando = "sizzle" + 1 * new Date(),
-	preferredDoc = window.document,
-	dirruns = 0,
-	done = 0,
-	classCache = createCache(),
-	tokenCache = createCache(),
-	compilerCache = createCache(),
-	sortOrder = function( a, b ) {
-		if ( a === b ) {
-			hasDuplicate = true;
-		}
-		return 0;
-	},
-
-	// General-purpose constants
-	MAX_NEGATIVE = 1 << 31,
-
-	// Instance methods
-	hasOwn = ({}).hasOwnProperty,
-	arr = [],
-	pop = arr.pop,
-	push_native = arr.push,
-	push = arr.push,
-	slice = arr.slice,
-	// Use a stripped-down indexOf as it's faster than native
-	// http://jsperf.com/thor-indexof-vs-for/5
-	indexOf = function( list, elem ) {
-		var i = 0,
-			len = list.length;
-		for ( ; i < len; i++ ) {
-			if ( list[i] === elem ) {
-				return i;
-			}
-		}
-		return -1;
-	},
-
-	booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",
-
-	// Regular expressions
-
-	// Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace
-	whitespace = "[\\x20\\t\\r\\n\\f]",
-	// http://www.w3.org/TR/css3-syntax/#characters
-	characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",
-
-	// Loosely modeled on CSS identifier characters
-	// An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors
-	// Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
-	identifier = characterEncoding.replace( "w", "w#" ),
-
-	// Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors
-	attributes = "\\[" + whitespace + "*(" + characterEncoding + ")(?:" + whitespace +
-		// Operator (capture 2)
-		"*([*^$|!~]?=)" + whitespace +
-		// "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]"
-		"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace +
-		"*\\]",
-
-	pseudos = ":(" + characterEncoding + ")(?:\\((" +
-		// To reduce the number of selectors needing tokenize in the preFilter, prefer arguments:
-		// 1. quoted (capture 3; capture 4 or capture 5)
-		"('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" +
-		// 2. simple (capture 6)
-		"((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" +
-		// 3. anything else (capture 2)
-		".*" +
-		")\\)|)",
-
-	// Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
-	rwhitespace = new RegExp( whitespace + "+", "g" ),
-	rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
-
-	rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
-	rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ),
-
-	rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ),
-
-	rpseudo = new RegExp( pseudos ),
-	ridentifier = new RegExp( "^" + identifier + "$" ),
-
-	matchExpr = {
-		"ID": new RegExp( "^#(" + characterEncoding + ")" ),
-		"CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ),
-		"TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ),
-		"ATTR": new RegExp( "^" + attributes ),
-		"PSEUDO": new RegExp( "^" + pseudos ),
-		"CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace +
-			"*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
-			"*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
-		"bool": new RegExp( "^(?:" + booleans + ")$", "i" ),
-		// For use in libraries implementing .is()
-		// We use this for POS matching in `select`
-		"needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
-			whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
-	},
-
-	rinputs = /^(?:input|select|textarea|button)$/i,
-	rheader = /^h\d$/i,
-
-	rnative = /^[^{]+\{\s*\[native \w/,
-
-	// Easily-parseable/retrievable ID or TAG or CLASS selectors
-	rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
-
-	rsibling = /[+~]/,
-	rescape = /'|\\/g,
-
-	// CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
-	runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ),
-	funescape = function( _, escaped, escapedWhitespace ) {
-		var high = "0x" + escaped - 0x10000;
-		// NaN means non-codepoint
-		// Support: Firefox<24
-		// Workaround erroneous numeric interpretation of +"0x"
-		return high !== high || escapedWhitespace ?
-			escaped :
-			high < 0 ?
-				// BMP codepoint
-				String.fromCharCode( high + 0x10000 ) :
-				// Supplemental Plane codepoint (surrogate pair)
-				String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
-	},
-
-	// Used for iframes
-	// See setDocument()
-	// Removing the function wrapper causes a "Permission Denied"
-	// error in IE
-	unloadHandler = function() {
-		setDocument();
-	};
-
-// Optimize for push.apply( _, NodeList )
-try {
-	push.apply(
-		(arr = slice.call( preferredDoc.childNodes )),
-		preferredDoc.childNodes
-	);
-	// Support: Android<4.0
-	// Detect silently failing push.apply
-	arr[ preferredDoc.childNodes.length ].nodeType;
-} catch ( e ) {
-	push = { apply: arr.length ?
-
-		// Leverage slice if possible
-		function( target, els ) {
-			push_native.apply( target, slice.call(els) );
-		} :
-
-		// Support: IE<9
-		// Otherwise append directly
-		function( target, els ) {
-			var j = target.length,
-				i = 0;
-			// Can't trust NodeList.length
-			while ( (target[j++] = els[i++]) ) {}
-			target.length = j - 1;
-		}
-	};
-}
-
-function Sizzle( selector, context, results, seed ) {
-	var match, elem, m, nodeType,
-		// QSA vars
-		i, groups, old, nid, newContext, newSelector;
-
-	if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {
-		setDocument( context );
-	}
-
-	context = context || document;
-	results = results || [];
-	nodeType = context.nodeType;
-
-	if ( typeof selector !== "string" || !selector ||
-		nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) {
-
-		return results;
-	}
-
-	if ( !seed && documentIsHTML ) {
-
-		// Try to shortcut find operations when possible (e.g., not under DocumentFragment)
-		if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) {
-			// Speed-up: Sizzle("#ID")
-			if ( (m = match[1]) ) {
-				if ( nodeType === 9 ) {
-					elem = context.getElementById( m );
-					// Check parentNode to catch when Blackberry 4.6 returns
-					// nodes that are no longer in the document (jQuery #6963)
-					if ( elem && elem.parentNode ) {
-						// Handle the case where IE, Opera, and Webkit return items
-						// by name instead of ID
-						if ( elem.id === m ) {
-							results.push( elem );
-							return results;
-						}
-					} else {
-						return results;
-					}
-				} else {
-					// Context is not a document
-					if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&
-						contains( context, elem ) && elem.id === m ) {
-						results.push( elem );
-						return results;
-					}
-				}
-
-			// Speed-up: Sizzle("TAG")
-			} else if ( match[2] ) {
-				push.apply( results, context.getElementsByTagName( selector ) );
-				return results;
-
-			// Speed-up: Sizzle(".CLASS")
-			} else if ( (m = match[3]) && support.getElementsByClassName ) {
-				push.apply( results, context.getElementsByClassName( m ) );
-				return results;
-			}
-		}
-
-		// QSA path
-		if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) {
-			nid = old = expando;
-			newContext = context;
-			newSelector = nodeType !== 1 && selector;
-
-			// qSA works strangely on Element-rooted queries
-			// We can work around this by specifying an extra ID on the root
-			// and working up from there (Thanks to Andrew Dupont for the technique)
-			// IE 8 doesn't work on object elements
-			if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
-				groups = tokenize( selector );
-
-				if ( (old = context.getAttribute("id")) ) {
-					nid = old.replace( rescape, "\\$&" );
-				} else {
-					context.setAttribute( "id", nid );
-				}
-				nid = "[id='" + nid + "'] ";
-
-				i = groups.length;
-				while ( i-- ) {
-					groups[i] = nid + toSelector( groups[i] );
-				}
-				newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context;
-				newSelector = groups.join(",");
-			}
-
-			if ( newSelector ) {
-				try {
-					push.apply( results,
-						newContext.querySelectorAll( newSelector )
-					);
-					return results;
-				} catch(qsaError) {
-				} finally {
-					if ( !old ) {
-						context.removeAttribute("id");
-					}
-				}
-			}
-		}
-	}
-
-	// All others
-	return select( selector.replace( rtrim, "$1" ), context, results, seed );
-}
-
-/**
- * Create key-value caches of limited size
- * @returns {Function(string, Object)} Returns the Object data after storing it on itself with
- *	property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
- *	deleting the oldest entry
- */
-function createCache() {
-	var keys = [];
-
-	function cache( key, value ) {
-		// Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
-		if ( keys.push( key + " " ) > Expr.cacheLength ) {
-			// Only keep the most recent entries
-			delete cache[ keys.shift() ];
-		}
-		return (cache[ key + " " ] = value);
-	}
-	return cache;
-}
-
-/**
- * Mark a function for special use by Sizzle
- * @param {Function} fn The function to mark
- */
-function markFunction( fn ) {
-	fn[ expando ] = true;
-	return fn;
-}
-
-/**
- * Support testing using an element
- * @param {Function} fn Passed the created div and expects a boolean result
- */
-function assert( fn ) {
-	var div = document.createElement("div");
-
-	try {
-		return !!fn( div );
-	} catch (e) {
-		return false;
-	} finally {
-		// Remove from its parent by default
-		if ( div.parentNode ) {
-			div.parentNode.removeChild( div );
-		}
-		// release memory in IE
-		div = null;
-	}
-}
-
-/**
- * Adds the same handler for all of the specified attrs
- * @param {String} attrs Pipe-separated list of attributes
- * @param {Function} handler The method that will be applied
- */
-function addHandle( attrs, handler ) {
-	var arr = attrs.split("|"),
-		i = attrs.length;
-
-	while ( i-- ) {
-		Expr.attrHandle[ arr[i] ] = handler;
-	}
-}
-
-/**
- * Checks document order of two siblings
- * @param {Element} a
- * @param {Element} b
- * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b
- */
-function siblingCheck( a, b ) {
-	var cur = b && a,
-		diff = cur && a.nodeType === 1 && b.nodeType === 1 &&
-			( ~b.sourceIndex || MAX_NEGATIVE ) -
-			( ~a.sourceIndex || MAX_NEGATIVE );
-
-	// Use IE sourceIndex if available on both nodes
-	if ( diff ) {
-		return diff;
-	}
-
-	// Check if b follows a
-	if ( cur ) {
-		while ( (cur = cur.nextSibling) ) {
-			if ( cur === b ) {
-				return -1;
-			}
-		}
-	}
-
-	return a ? 1 : -1;
-}
-
-/**
- * Returns a function to use in pseudos for input types
- * @param {String} type
- */
-function createInputPseudo( type ) {
-	return function( elem ) {
-		var name = elem.nodeName.toLowerCase();
-		return name === "input" && elem.type === type;
-	};
-}
-
-/**
- * Returns a function to use in pseudos for buttons
- * @param {String} type
- */
-function createButtonPseudo( type ) {
-	return function( elem ) {
-		var name = elem.nodeName.toLowerCase();
-		return (name === "input" || name === "button") && elem.type === type;
-	};
-}
-
-/**
- * Returns a function to use in pseudos for positionals
- * @param {Function} fn
- */
-function createPositionalPseudo( fn ) {
-	return markFunction(function( argument ) {
-		argument = +argument;
-		return markFunction(function( seed, matches ) {
-			var j,
-				matchIndexes = fn( [], seed.length, argument ),
-				i = matchIndexes.length;
-
-			// Match elements found at the specified indexes
-			while ( i-- ) {
-				if ( seed[ (j = matchIndexes[i]) ] ) {
-					seed[j] = !(matches[j] = seed[j]);
-				}
-			}
-		});
-	});
-}
-
-/**
- * Checks a node for validity as a Sizzle context
- * @param {Element|Object=} context
- * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value
- */
-function testContext( context ) {
-	return context && typeof context.getElementsByTagName !== "undefined" && context;
-}
-
-// Expose support vars for convenience
-support = Sizzle.support = {};
-
-/**
- * Detects XML nodes
- * @param {Element|Object} elem An element or a document
- * @returns {Boolean} True iff elem is a non-HTML XML node
- */
-isXML = Sizzle.isXML = function( elem ) {
-	// documentElement is verified for cases where it doesn't yet exist
-	// (such as loading iframes in IE - #4833)
-	var documentElement = elem && (elem.ownerDocument || elem).documentElement;
-	return documentElement ? documentElement.nodeName !== "HTML" : false;
-};
-
-/**
- * Sets document-related variables once based on the current document
- * @param {Element|Object} [doc] An element or document object to use to set the document
- * @returns {Object} Returns the current document
- */
-setDocument = Sizzle.setDocument = function( node ) {
-	var hasCompare, parent,
-		doc = node ? node.ownerDocument || node : preferredDoc;
-
-	// If no document and documentElement is available, return
-	if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
-		return document;
-	}
-
-	// Set our document
-	document = doc;
-	docElem = doc.documentElement;
-	parent = doc.defaultView;
-
-	// Support: IE>8
-	// If iframe document is assigned to "document" variable and if iframe has been reloaded,
-	// IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936
-	// IE6-8 do not support the defaultView property so parent will be undefined
-	if ( parent && parent !== parent.top ) {
-		// IE11 does not have attachEvent, so all must suffer
-		if ( parent.addEventListener ) {
-			parent.addEventListener( "unload", unloadHandler, false );
-		} else if ( parent.attachEvent ) {
-			parent.attachEvent( "onunload", unloadHandler );
-		}
-	}
-
-	/* Support tests
-	---------------------------------------------------------------------- */
-	documentIsHTML = !isXML( doc );
-
-	/* Attributes
-	---------------------------------------------------------------------- */
-
-	// Support: IE<8
-	// Verify that getAttribute really returns attributes and not properties
-	// (excepting IE8 booleans)
-	support.attributes = assert(function( div ) {
-		div.className = "i";
-		return !div.getAttribute("className");
-	});
-
-	/* getElement(s)By*
-	---------------------------------------------------------------------- */
-
-	// Check if getElementsByTagName("*") returns only elements
-	support.getElementsByTagName = assert(function( div ) {
-		div.appendChild( doc.createComment("") );
-		return !div.getElementsByTagName("*").length;
-	});
-
-	// Support: IE<9
-	support.getElementsByClassName = rnative.test( doc.getElementsByClassName );
-
-	// Support: IE<10
-	// Check if getElementById returns elements by name
-	// The broken getElementById methods don't pick up programatically-set names,
-	// so use a roundabout getElementsByName test
-	support.getById = assert(function( div ) {
-		docElem.appendChild( div ).id = expando;
-		return !doc.getElementsByName || !doc.getElementsByName( expando ).length;
-	});
-
-	// ID find and filter
-	if ( support.getById ) {
-		Expr.find["ID"] = function( id, context ) {
-			if ( typeof context.getElementById !== "undefined" && documentIsHTML ) {
-				var m = context.getElementById( id );
-				// Check parentNode to catch when Blackberry 4.6 returns
-				// nodes that are no longer in the document #6963
-				return m && m.parentNode ? [ m ] : [];
-			}
-		};
-		Expr.filter["ID"] = function( id ) {
-			var attrId = id.replace( runescape, funescape );
-			return function( elem ) {
-				return elem.getAttribute("id") === attrId;
-			};
-		};
-	} else {
-		// Support: IE6/7
-		// getElementById is not reliable as a find shortcut
-		delete Expr.find["ID"];
-
-		Expr.filter["ID"] =  function( id ) {
-			var attrId = id.replace( runescape, funescape );
-			return function( elem ) {
-				var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
-				return node && node.value === attrId;
-			};
-		};
-	}
-
-	// Tag
-	Expr.find["TAG"] = support.getElementsByTagName ?
-		function( tag, context ) {
-			if ( typeof context.getElementsByTagName !== "undefined" ) {
-				return context.getElementsByTagName( tag );
-
-			// DocumentFragment nodes don't have gEBTN
-			} else if ( support.qsa ) {
-				return context.querySelectorAll( tag );
-			}
-		} :
-
-		function( tag, context ) {
-			var elem,
-				tmp = [],
-				i = 0,
-				// By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too
-				results = context.getElementsByTagName( tag );
-
-			// Filter out possible comments
-			if ( tag === "*" ) {
-				while ( (elem = results[i++]) ) {
-					if ( elem.nodeType === 1 ) {
-						tmp.push( elem );
-					}
-				}
-
-				return tmp;
-			}
-			return results;
-		};
-
-	// Class
-	Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) {
-		if ( documentIsHTML ) {
-			return context.getElementsByClassName( className );
-		}
-	};
-
-	/* QSA/matchesSelector
-	---------------------------------------------------------------------- */
-
-	// QSA and matchesSelector support
-
-	// matchesSelector(:active) reports false when true (IE9/Opera 11.5)
-	rbuggyMatches = [];
-
-	// qSa(:focus) reports false when true (Chrome 21)
-	// We allow this because of a bug in IE8/9 that throws an error
-	// whenever `document.activeElement` is accessed on an iframe
-	// So, we allow :focus to pass through QSA all the time to avoid the IE error
-	// See http://bugs.jquery.com/ticket/13378
-	rbuggyQSA = [];
-
-	if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) {
-		// Build QSA regex
-		// Regex strategy adopted from Diego Perini
-		assert(function( div ) {
-			// Select is set to empty string on purpose
-			// This is to test IE's treatment of not explicitly
-			// setting a boolean content attribute,
-			// since its presence should be enough
-			// http://bugs.jquery.com/ticket/12359
-			docElem.appendChild( div ).innerHTML = "<a id='" + expando + "'></a>" +
-				"<select id='" + expando + "-\f]' msallowcapture=''>" +
-				"<option selected=''></option></select>";
-
-			// Support: IE8, Opera 11-12.16
-			// Nothing should be selected when empty strings follow ^= or $= or *=
-			// The test attribute must be unknown in Opera but "safe" for WinRT
-			// http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section
-			if ( div.querySelectorAll("[msallowcapture^='']").length ) {
-				rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" );
-			}
-
-			// Support: IE8
-			// Boolean attributes and "value" are not treated correctly
-			if ( !div.querySelectorAll("[selected]").length ) {
-				rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" );
-			}
-
-			// Support: Chrome<29, Android<4.2+, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.7+
-			if ( !div.querySelectorAll( "[id~=" + expando + "-]" ).length ) {
-				rbuggyQSA.push("~=");
-			}
-
-			// Webkit/Opera - :checked should return selected option elements
-			// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
-			// IE8 throws error here and will not see later tests
-			if ( !div.querySelectorAll(":checked").length ) {
-				rbuggyQSA.push(":checked");
-			}
-
-			// Support: Safari 8+, iOS 8+
-			// https://bugs.webkit.org/show_bug.cgi?id=136851
-			// In-page `selector#id sibing-combinator selector` fails
-			if ( !div.querySelectorAll( "a#" + expando + "+*" ).length ) {
-				rbuggyQSA.push(".#.+[+~]");
-			}
-		});
-
-		assert(function( div ) {
-			// Support: Windows 8 Native Apps
-			// The type and name attributes are restricted during .innerHTML assignment
-			var input = doc.createElement("input");
-			input.setAttribute( "type", "hidden" );
-			div.appendChild( input ).setAttribute( "name", "D" );
-
-			// Support: IE8
-			// Enforce case-sensitivity of name attribute
-			if ( div.querySelectorAll("[name=d]").length ) {
-				rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" );
-			}
-
-			// FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
-			// IE8 throws error here and will not see later tests
-			if ( !div.querySelectorAll(":enabled").length ) {
-				rbuggyQSA.push( ":enabled", ":disabled" );
-			}
-
-			// Opera 10-11 does not throw on post-comma invalid pseudos
-			div.querySelectorAll("*,:x");
-			rbuggyQSA.push(",.*:");
-		});
-	}
-
-	if ( (support.matchesSelector = rnative.test( (matches = docElem.matches ||
-		docElem.webkitMatchesSelector ||
-		docElem.mozMatchesSelector ||
-		docElem.oMatchesSelector ||
-		docElem.msMatchesSelector) )) ) {
-
-		assert(function( div ) {
-			// Check to see if it's possible to do matchesSelector
-			// on a disconnected node (IE 9)
-			support.disconnectedMatch = matches.call( div, "div" );
-
-			// This should fail with an exception
-			// Gecko does not error, returns false instead
-			matches.call( div, "[s!='']:x" );
-			rbuggyMatches.push( "!=", pseudos );
-		});
-	}
-
-	rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") );
-	rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") );
-
-	/* Contains
-	---------------------------------------------------------------------- */
-	hasCompare = rnative.test( docElem.compareDocumentPosition );
-
-	// Element contains another
-	// Purposefully does not implement inclusive descendent
-	// As in, an element does not contain itself
-	contains = hasCompare || rnative.test( docElem.contains ) ?
-		function( a, b ) {
-			var adown = a.nodeType === 9 ? a.documentElement : a,
-				bup = b && b.parentNode;
-			return a === bup || !!( bup && bup.nodeType === 1 && (
-				adown.contains ?
-					adown.contains( bup ) :
-					a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
-			));
-		} :
-		function( a, b ) {
-			if ( b ) {
-				while ( (b = b.parentNode) ) {
-					if ( b === a ) {
-						return true;
-					}
-				}
-			}
-			return false;
-		};
-
-	/* Sorting
-	---------------------------------------------------------------------- */
-
-	// Document order sorting
-	sortOrder = hasCompare ?
-	function( a, b ) {
-
-		// Flag for duplicate removal
-		if ( a === b ) {
-			hasDuplicate = true;
-			return 0;
-		}
-
-		// Sort on method existence if only one input has compareDocumentPosition
-		var compare = !a.compareDocumentPosition - !b.compareDocumentPosition;
-		if ( compare ) {
-			return compare;
-		}
-
-		// Calculate position if both inputs belong to the same document
-		compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ?
-			a.compareDocumentPosition( b ) :
-
-			// Otherwise we know they are disconnected
-			1;
-
-		// Disconnected nodes
-		if ( compare & 1 ||
-			(!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) {
-
-			// Choose the first element that is related to our preferred document
-			if ( a === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) {
-				return -1;
-			}
-			if ( b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) {
-				return 1;
-			}
-
-			// Maintain original order
-			return sortInput ?
-				( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :
-				0;
-		}
-
-		return compare & 4 ? -1 : 1;
-	} :
-	function( a, b ) {
-		// Exit early if the nodes are identical
-		if ( a === b ) {
-			hasDuplicate = true;
-			return 0;
-		}
-
-		var cur,
-			i = 0,
-			aup = a.parentNode,
-			bup = b.parentNode,
-			ap = [ a ],
-			bp = [ b ];
-
-		// Parentless nodes are either documents or disconnected
-		if ( !aup || !bup ) {
-			return a === doc ? -1 :
-				b === doc ? 1 :
-				aup ? -1 :
-				bup ? 1 :
-				sortInput ?
-				( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :
-				0;
-
-		// If the nodes are siblings, we can do a quick check
-		} else if ( aup === bup ) {
-			return siblingCheck( a, b );
-		}
-
-		// Otherwise we need full lists of their ancestors for comparison
-		cur = a;
-		while ( (cur = cur.parentNode) ) {
-			ap.unshift( cur );
-		}
-		cur = b;
-		while ( (cur = cur.parentNode) ) {
-			bp.unshift( cur );
-		}
-
-		// Walk down the tree looking for a discrepancy
-		while ( ap[i] === bp[i] ) {
-			i++;
-		}
-
-		return i ?
-			// Do a sibling check if the nodes have a common ancestor
-			siblingCheck( ap[i], bp[i] ) :
-
-			// Otherwise nodes in our document sort first
-			ap[i] === preferredDoc ? -1 :
-			bp[i] === preferredDoc ? 1 :
-			0;
-	};
-
-	return doc;
-};
-
-Sizzle.matches = function( expr, elements ) {
-	return Sizzle( expr, null, null, elements );
-};
-
-Sizzle.matchesSelector = function( elem, expr ) {
-	// Set document vars if needed
-	if ( ( elem.ownerDocument || elem ) !== document ) {
-		setDocument( elem );
-	}
-
-	// Make sure that attribute selectors are quoted
-	expr = expr.replace( rattributeQuotes, "='$1']" );
-
-	if ( support.matchesSelector && documentIsHTML &&
-		( !rbuggyMatches || !rbuggyMatches.test( expr ) ) &&
-		( !rbuggyQSA     || !rbuggyQSA.test( expr ) ) ) {
-
-		try {
-			var ret = matches.call( elem, expr );
-
-			// IE 9's matchesSelector returns false on disconnected nodes
-			if ( ret || support.disconnectedMatch ||
-					// As well, disconnected nodes are said to be in a document
-					// fragment in IE 9
-					elem.document && elem.document.nodeType !== 11 ) {
-				return ret;
-			}
-		} catch (e) {}
-	}
-
-	return Sizzle( expr, document, null, [ elem ] ).length > 0;
-};
-
-Sizzle.contains = function( context, elem ) {
-	// Set document vars if needed
-	if ( ( context.ownerDocument || context ) !== document ) {
-		setDocument( context );
-	}
-	return contains( context, elem );
-};
-
-Sizzle.attr = function( elem, name ) {
-	// Set document vars if needed
-	if ( ( elem.ownerDocument || elem ) !== document ) {
-		setDocument( elem );
-	}
-
-	var fn = Expr.attrHandle[ name.toLowerCase() ],
-		// Don't get fooled by Object.prototype properties (jQuery #13807)
-		val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ?
-			fn( elem, name, !documentIsHTML ) :
-			undefined;
-
-	return val !== undefined ?
-		val :
-		support.attributes || !documentIsHTML ?
-			elem.getAttribute( name ) :
-			(val = elem.getAttributeNode(name)) && val.specified ?
-				val.value :
-				null;
-};
-
-Sizzle.error = function( msg ) {
-	throw new Error( "Syntax error, unrecognized expression: " + msg );
-};
-
-/**
- * Document sorting and removing duplicates
- * @param {ArrayLike} results
- */
-Sizzle.uniqueSort = function( results ) {
-	var elem,
-		duplicates = [],
-		j = 0,
-		i = 0;
-
-	// Unless we *know* we can detect duplicates, assume their presence
-	hasDuplicate = !support.detectDuplicates;
-	sortInput = !support.sortStable && results.slice( 0 );
-	results.sort( sortOrder );
-
-	if ( hasDuplicate ) {
-		while ( (elem = results[i++]) ) {
-			if ( elem === results[ i ] ) {
-				j = duplicates.push( i );
-			}
-		}
-		while ( j-- ) {
-			results.splice( duplicates[ j ], 1 );
-		}
-	}
-
-	// Clear input after sorting to release objects
-	// See https://github.com/jquery/sizzle/pull/225
-	sortInput = null;
-
-	return results;
-};
-
-/**
- * Utility function for retrieving the text value of an array of DOM nodes
- * @param {Array|Element} elem
- */
-getText = Sizzle.getText = function( elem ) {
-	var node,
-		ret = "",
-		i = 0,
-		nodeType = elem.nodeType;
-
-	if ( !nodeType ) {
-		// If no nodeType, this is expected to be an array
-		while ( (node = elem[i++]) ) {
-			// Do not traverse comment nodes
-			ret += getText( node );
-		}
-	} else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
-		// Use textContent for elements
-		// innerText usage removed for consistency of new lines (jQuery #11153)
-		if ( typeof elem.textContent === "string" ) {
-			return elem.textContent;
-		} else {
-			// Traverse its children
-			for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
-				ret += getText( elem );
-			}
-		}
-	} else if ( nodeType === 3 || nodeType === 4 ) {
-		return elem.nodeValue;
-	}
-	// Do not include comment or processing instruction nodes
-
-	return ret;
-};
-
-Expr = Sizzle.selectors = {
-
-	// Can be adjusted by the user
-	cacheLength: 50,
-
-	createPseudo: markFunction,
-
-	match: matchExpr,
-
-	attrHandle: {},
-
-	find: {},
-
-	relative: {
-		">": { dir: "parentNode", first: true },
-		" ": { dir: "parentNode" },
-		"+": { dir: "previousSibling", first: true },
-		"~": { dir: "previousSibling" }
-	},
-
-	preFilter: {
-		"ATTR": function( match ) {
-			match[1] = match[1].replace( runescape, funescape );
-
-			// Move the given value to match[3] whether quoted or unquoted
-			match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape );
-
-			if ( match[2] === "~=" ) {
-				match[3] = " " + match[3] + " ";
-			}
-
-			return match.slice( 0, 4 );
-		},
-
-		"CHILD": function( match ) {
-			/* matches from matchExpr["CHILD"]
-				1 type (only|nth|...)
-				2 what (child|of-type)
-				3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
-				4 xn-component of xn+y argument ([+-]?\d*n|)
-				5 sign of xn-component
-				6 x of xn-component
-				7 sign of y-component
-				8 y of y-component
-			*/
-			match[1] = match[1].toLowerCase();
-
-			if ( match[1].slice( 0, 3 ) === "nth" ) {
-				// nth-* requires argument
-				if ( !match[3] ) {
-					Sizzle.error( match[0] );
-				}
-
-				// numeric x and y parameters for Expr.filter.CHILD
-				// remember that false/true cast respectively to 0/1
-				match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) );
-				match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" );
-
-			// other types prohibit arguments
-			} else if ( match[3] ) {
-				Sizzle.error( match[0] );
-			}
-
-			return match;
-		},
-
-		"PSEUDO": function( match ) {
-			var excess,
-				unquoted = !match[6] && match[2];
-
-			if ( matchExpr["CHILD"].test( match[0] ) ) {
-				return null;
-			}
-
-			// Accept quoted arguments as-is
-			if ( match[3] ) {
-				match[2] = match[4] || match[5] || "";
-
-			// Strip excess characters from unquoted arguments
-			} else if ( unquoted && rpseudo.test( unquoted ) &&
-				// Get excess from tokenize (recursively)
-				(excess = tokenize( unquoted, true )) &&
-				// advance to the next closing parenthesis
-				(excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {
-
-				// excess is a negative index
-				match[0] = match[0].slice( 0, excess );
-				match[2] = unquoted.slice( 0, excess );
-			}
-
-			// Return only captures needed by the pseudo filter method (type and argument)
-			return match.slice( 0, 3 );
-		}
-	},
-
-	filter: {
-
-		"TAG": function( nodeNameSelector ) {
-			var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();
-			return nodeNameSelector === "*" ?
-				function() { return true; } :
-				function( elem ) {
-					return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
-				};
-		},
-
-		"CLASS": function( className ) {
-			var pattern = classCache[ className + " " ];
-
-			return pattern ||
-				(pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
-				classCache( className, function( elem ) {
-					return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "" );
-				});
-		},
-
-		"ATTR": function( name, operator, check ) {
-			return function( elem ) {
-				var result = Sizzle.attr( elem, name );
-
-				if ( result == null ) {
-					return operator === "!=";
-				}
-				if ( !operator ) {
-					return true;
-				}
-
-				result += "";
-
-				return operator === "=" ? result === check :
-					operator === "!=" ? result !== check :
-					operator === "^=" ? check && result.indexOf( check ) === 0 :
-					operator === "*=" ? check && result.indexOf( check ) > -1 :
-					operator === "$=" ? check && result.slice( -check.length ) === check :
-					operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 :
-					operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
-					false;
-			};
-		},
-
-		"CHILD": function( type, what, argument, first, last ) {
-			var simple = type.slice( 0, 3 ) !== "nth",
-				forward = type.slice( -4 ) !== "last",
-				ofType = what === "of-type";
-
-			return first === 1 && last === 0 ?
-
-				// Shortcut for :nth-*(n)
-				function( elem ) {
-					return !!elem.parentNode;
-				} :
-
-				function( elem, context, xml ) {
-					var cache, outerCache, node, diff, nodeIndex, start,
-						dir = simple !== forward ? "nextSibling" : "previousSibling",
-						parent = elem.parentNode,
-						name = ofType && elem.nodeName.toLowerCase(),
-						useCache = !xml && !ofType;
-
-					if ( parent ) {
-
-						// :(first|last|only)-(child|of-type)
-						if ( simple ) {
-							while ( dir ) {
-								node = elem;
-								while ( (node = node[ dir ]) ) {
-									if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) {
-										return false;
-									}
-								}
-								// Reverse direction for :only-* (if we haven't yet done so)
-								start = dir = type === "only" && !start && "nextSibling";
-							}
-							return true;
-						}
-
-						start = [ forward ? parent.firstChild : parent.lastChild ];
-
-						// non-xml :nth-child(...) stores cache data on `parent`
-						if ( forward && useCache ) {
-							// Seek `elem` from a previously-cached index
-							outerCache = parent[ expando ] || (parent[ expando ] = {});
-							cache = outerCache[ type ] || [];
-							nodeIndex = cache[0] === dirruns && cache[1];
-							diff = cache[0] === dirruns && cache[2];
-							node = nodeIndex && parent.childNodes[ nodeIndex ];
-
-							while ( (node = ++nodeIndex && node && node[ dir ] ||
-
-								// Fallback to seeking `elem` from the start
-								(diff = nodeIndex = 0) || start.pop()) ) {
-
-								// When found, cache indexes on `parent` and break
-								if ( node.nodeType === 1 && ++diff && node === elem ) {
-									outerCache[ type ] = [ dirruns, nodeIndex, diff ];
-									break;
-								}
-							}
-
-						// Use previously-cached element index if available
-						} else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) {
-							diff = cache[1];
-
-						// xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...)
-						} else {
-							// Use the same loop as above to seek `elem` from the start
-							while ( (node = ++nodeIndex && node && node[ dir ] ||
-								(diff = nodeIndex = 0) || start.pop()) ) {
-
-								if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) {
-									// Cache the index of each encountered element
-									if ( useCache ) {
-										(node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ];
-									}
-
-									if ( node === elem ) {
-										break;
-									}
-								}
-							}
-						}
-
-						// Incorporate the offset, then check against cycle size
-						diff -= last;
-						return diff === first || ( diff % first === 0 && diff / first >= 0 );
-					}
-				};
-		},
-
-		"PSEUDO": function( pseudo, argument ) {
-			// pseudo-class names are case-insensitive
-			// http://www.w3.org/TR/selectors/#pseudo-classes
-			// Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
-			// Remember that setFilters inherits from pseudos
-			var args,
-				fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
-					Sizzle.error( "unsupported pseudo: " + pseudo );
-
-			// The user may use createPseudo to indicate that
-			// arguments are needed to create the filter function
-			// just as Sizzle does
-			if ( fn[ expando ] ) {
-				return fn( argument );
-			}
-
-			// But maintain support for old signatures
-			if ( fn.length > 1 ) {
-				args = [ pseudo, pseudo, "", argument ];
-				return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
-					markFunction(function( seed, matches ) {
-						var idx,
-							matched = fn( seed, argument ),
-							i = matched.length;
-						while ( i-- ) {
-							idx = indexOf( seed, matched[i] );
-							seed[ idx ] = !( matches[ idx ] = matched[i] );
-						}
-					}) :
-					function( elem ) {
-						return fn( elem, 0, args );
-					};
-			}
-
-			return fn;
-		}
-	},
-
-	pseudos: {
-		// Potentially complex pseudos
-		"not": markFunction(function( selector ) {
-			// Trim the selector passed to compile
-			// to avoid treating leading and trailing
-			// spaces as combinators
-			var input = [],
-				results = [],
-				matcher = compile( selector.replace( rtrim, "$1" ) );
-
-			return matcher[ expando ] ?
-				markFunction(function( seed, matches, context, xml ) {
-					var elem,
-						unmatched = matcher( seed, null, xml, [] ),
-						i = seed.length;
-
-					// Match elements unmatched by `matcher`
-					while ( i-- ) {
-						if ( (elem = unmatched[i]) ) {
-							seed[i] = !(matches[i] = elem);
-						}
-					}
-				}) :
-				function( elem, context, xml ) {
-					input[0] = elem;
-					matcher( input, null, xml, results );
-					// Don't keep the element (issue #299)
-					input[0] = null;
-					return !results.pop();
-				};
-		}),
-
-		"has": markFunction(function( selector ) {
-			return function( elem ) {
-				return Sizzle( selector, elem ).length > 0;
-			};
-		}),
-
-		"contains": markFunction(function( text ) {
-			text = text.replace( runescape, funescape );
-			return function( elem ) {
-				return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
-			};
-		}),
-
-		// "Whether an element is represented by a :lang() selector
-		// is based solely on the element's language value
-		// being equal to the identifier C,
-		// or beginning with the identifier C immediately followed by "-".
-		// The matching of C against the element's language value is performed case-insensitively.
-		// The identifier C does not have to be a valid language name."
-		// http://www.w3.org/TR/selectors/#lang-pseudo
-		"lang": markFunction( function( lang ) {
-			// lang value must be a valid identifier
-			if ( !ridentifier.test(lang || "") ) {
-				Sizzle.error( "unsupported lang: " + lang );
-			}
-			lang = lang.replace( runescape, funescape ).toLowerCase();
-			return function( elem ) {
-				var elemLang;
-				do {
-					if ( (elemLang = documentIsHTML ?
-						elem.lang :
-						elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) {
-
-						elemLang = elemLang.toLowerCase();
-						return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
-					}
-				} while ( (elem = elem.parentNode) && elem.nodeType === 1 );
-				return false;
-			};
-		}),
-
-		// Miscellaneous
-		"target": function( elem ) {
-			var hash = window.location && window.location.hash;
-			return hash && hash.slice( 1 ) === elem.id;
-		},
-
-		"root": function( elem ) {
-			return elem === docElem;
-		},
-
-		"focus": function( elem ) {
-			return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
-		},
-
-		// Boolean properties
-		"enabled": function( elem ) {
-			return elem.disabled === false;
-		},
-
-		"disabled": function( elem ) {
-			return elem.disabled === true;
-		},
-
-		"checked": function( elem ) {
-			// In CSS3, :checked should return both checked and selected elements
-			// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
-			var nodeName = elem.nodeName.toLowerCase();
-			return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
-		},
-
-		"selected": function( elem ) {
-			// Accessing this property makes selected-by-default
-			// options in Safari work properly
-			if ( elem.parentNode ) {
-				elem.parentNode.selectedIndex;
-			}
-
-			return elem.selected === true;
-		},
-
-		// Contents
-		"empty": function( elem ) {
-			// http://www.w3.org/TR/selectors/#empty-pseudo
-			// :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5),
-			//   but not by others (comment: 8; processing instruction: 7; etc.)
-			// nodeType < 6 works because attributes (2) do not appear as children
-			for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
-				if ( elem.nodeType < 6 ) {
-					return false;
-				}
-			}
-			return true;
-		},
-
-		"parent": function( elem ) {
-			return !Expr.pseudos["empty"]( elem );
-		},
-
-		// Element/input types
-		"header": function( elem ) {
-			return rheader.test( elem.nodeName );
-		},
-
-		"input": function( elem ) {
-			return rinputs.test( elem.nodeName );
-		},
-
-		"button": function( elem ) {
-			var name = elem.nodeName.toLowerCase();
-			return name === "input" && elem.type === "button" || name === "button";
-		},
-
-		"text": function( elem ) {
-			var attr;
-			return elem.nodeName.toLowerCase() === "input" &&
-				elem.type === "text" &&
-
-				// Support: IE<8
-				// New HTML5 attribute values (e.g., "search") appear with elem.type === "text"
-				( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" );
-		},
-
-		// Position-in-collection
-		"first": createPositionalPseudo(function() {
-			return [ 0 ];
-		}),
-
-		"last": createPositionalPseudo(function( matchIndexes, length ) {
-			return [ length - 1 ];
-		}),
-
-		"eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
-			return [ argument < 0 ? argument + length : argument ];
-		}),
-
-		"even": createPositionalPseudo(function( matchIndexes, length ) {
-			var i = 0;
-			for ( ; i < length; i += 2 ) {
-				matchIndexes.push( i );
-			}
-			return matchIndexes;
-		}),
-
-		"odd": createPositionalPseudo(function( matchIndexes, length ) {
-			var i = 1;
-			for ( ; i < length; i += 2 ) {
-				matchIndexes.push( i );
-			}
-			return matchIndexes;
-		}),
-
-		"lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
-			var i = argument < 0 ? argument + length : argument;
-			for ( ; --i >= 0; ) {
-				matchIndexes.push( i );
-			}
-			return matchIndexes;
-		}),
-
-		"gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
-			var i = argument < 0 ? argument + length : argument;
-			for ( ; ++i < length; ) {
-				matchIndexes.push( i );
-			}
-			return matchIndexes;
-		})
-	}
-};
-
-Expr.pseudos["nth"] = Expr.pseudos["eq"];
-
-// Add button/input type pseudos
-for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
-	Expr.pseudos[ i ] = createInputPseudo( i );
-}
-for ( i in { submit: true, reset: true } ) {
-	Expr.pseudos[ i ] = createButtonPseudo( i );
-}
-
-// Easy API for creating new setFilters
-function setFilters() {}
-setFilters.prototype = Expr.filters = Expr.pseudos;
-Expr.setFilters = new setFilters();
-
-tokenize = Sizzle.tokenize = function( selector, parseOnly ) {
-	var matched, match, tokens, type,
-		soFar, groups, preFilters,
-		cached = tokenCache[ selector + " " ];
-
-	if ( cached ) {
-		return parseOnly ? 0 : cached.slice( 0 );
-	}
-
-	soFar = selector;
-	groups = [];
-	preFilters = Expr.preFilter;
-
-	while ( soFar ) {
-
-		// Comma and first run
-		if ( !matched || (match = rcomma.exec( soFar )) ) {
-			if ( match ) {
-				// Don't consume trailing commas as valid
-				soFar = soFar.slice( match[0].length ) || soFar;
-			}
-			groups.push( (tokens = []) );
-		}
-
-		matched = false;
-
-		// Combinators
-		if ( (match = rcombinators.exec( soFar )) ) {
-			matched = match.shift();
-			tokens.push({
-				value: matched,
-				// Cast descendant combinators to space
-				type: match[0].replace( rtrim, " " )
-			});
-			soFar = soFar.slice( matched.length );
-		}
-
-		// Filters
-		for ( type in Expr.filter ) {
-			if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
-				(match = preFilters[ type ]( match ))) ) {
-				matched = match.shift();
-				tokens.push({
-					value: matched,
-					type: type,
-					matches: match
-				});
-				soFar = soFar.slice( matched.length );
-			}
-		}
-
-		if ( !matched ) {
-			break;
-		}
-	}
-
-	// Return the length of the invalid excess
-	// if we're just parsing
-	// Otherwise, throw an error or return tokens
-	return parseOnly ?
-		soFar.length :
-		soFar ?
-			Sizzle.error( selector ) :
-			// Cache the tokens
-			tokenCache( selector, groups ).slice( 0 );
-};
-
-function toSelector( tokens ) {
-	var i = 0,
-		len = tokens.length,
-		selector = "";
-	for ( ; i < len; i++ ) {
-		selector += tokens[i].value;
-	}
-	return selector;
-}
-
-function addCombinator( matcher, combinator, base ) {
-	var dir = combinator.dir,
-		checkNonElements = base && dir === "parentNode",
-		doneName = done++;
-
-	return combinator.first ?
-		// Check against closest ancestor/preceding element
-		function( elem, context, xml ) {
-			while ( (elem = elem[ dir ]) ) {
-				if ( elem.nodeType === 1 || checkNonElements ) {
-					return matcher( elem, context, xml );
-				}
-			}
-		} :
-
-		// Check against all ancestor/preceding elements
-		function( elem, context, xml ) {
-			var oldCache, outerCache,
-				newCache = [ dirruns, doneName ];
-
-			// We can't set arbitrary data on XML nodes, so they don't benefit from dir caching
-			if ( xml ) {
-				while ( (elem = elem[ dir ]) ) {
-					if ( elem.nodeType === 1 || checkNonElements ) {
-						if ( matcher( elem, context, xml ) ) {
-							return true;
-						}
-					}
-				}
-			} else {
-				while ( (elem = elem[ dir ]) ) {
-					if ( elem.nodeType === 1 || checkNonElements ) {
-						outerCache = elem[ expando ] || (elem[ expando ] = {});
-						if ( (oldCache = outerCache[ dir ]) &&
-							oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) {
-
-							// Assign to newCache so results back-propagate to previous elements
-							return (newCache[ 2 ] = oldCache[ 2 ]);
-						} else {
-							// Reuse newcache so results back-propagate to previous elements
-							outerCache[ dir ] = newCache;
-
-							// A match means we're done; a fail means we have to keep checking
-							if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) {
-								return true;
-							}
-						}
-					}
-				}
-			}
-		};
-}
-
-function elementMatcher( matchers ) {
-	return matchers.length > 1 ?
-		function( elem, context, xml ) {
-			var i = matchers.length;
-			while ( i-- ) {
-				if ( !matchers[i]( elem, context, xml ) ) {
-					return false;
-				}
-			}
-			return true;
-		} :
-		matchers[0];
-}
-
-function multipleContexts( selector, contexts, results ) {
-	var i = 0,
-		len = contexts.length;
-	for ( ; i < len; i++ ) {
-		Sizzle( selector, contexts[i], results );
-	}
-	return results;
-}
-
-function condense( unmatched, map, filter, context, xml ) {
-	var elem,
-		newUnmatched = [],
-		i = 0,
-		len = unmatched.length,
-		mapped = map != null;
-
-	for ( ; i < len; i++ ) {
-		if ( (elem = unmatched[i]) ) {
-			if ( !filter || filter( elem, context, xml ) ) {
-				newUnmatched.push( elem );
-				if ( mapped ) {
-					map.push( i );
-				}
-			}
-		}
-	}
-
-	return newUnmatched;
-}
-
-function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
-	if ( postFilter && !postFilter[ expando ] ) {
-		postFilter = setMatcher( postFilter );
-	}
-	if ( postFinder && !postFinder[ expando ] ) {
-		postFinder = setMatcher( postFinder, postSelector );
-	}
-	return markFunction(function( seed, results, context, xml ) {
-		var temp, i, elem,
-			preMap = [],
-			postMap = [],
-			preexisting = results.length,
-
-			// Get initial elements from seed or context
-			elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
-
-			// Prefilter to get matcher input, preserving a map for seed-results synchronization
-			matcherIn = preFilter && ( seed || !selector ) ?
-				condense( elems, preMap, preFilter, context, xml ) :
-				elems,
-
-			matcherOut = matcher ?
-				// If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
-				postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
-
-					// ...intermediate processing is necessary
-					[] :
-
-					// ...otherwise use results directly
-					results :
-				matcherIn;
-
-		// Find primary matches
-		if ( matcher ) {
-			matcher( matcherIn, matcherOut, context, xml );
-		}
-
-		// Apply postFilter
-		if ( postFilter ) {
-			temp = condense( matcherOut, postMap );
-			postFilter( temp, [], context, xml );
-
-			// Un-match failing elements by moving them back to matcherIn
-			i = temp.length;
-			while ( i-- ) {
-				if ( (elem = temp[i]) ) {
-					matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
-				}
-			}
-		}
-
-		if ( seed ) {
-			if ( postFinder || preFilter ) {
-				if ( postFinder ) {
-					// Get the final matcherOut by condensing this intermediate into postFinder contexts
-					temp = [];
-					i = matcherOut.length;
-					while ( i-- ) {
-						if ( (elem = matcherOut[i]) ) {
-							// Restore matcherIn since elem is not yet a final match
-							temp.push( (matcherIn[i] = elem) );
-						}
-					}
-					postFinder( null, (matcherOut = []), temp, xml );
-				}
-
-				// Move matched elements from seed to results to keep them synchronized
-				i = matcherOut.length;
-				while ( i-- ) {
-					if ( (elem = matcherOut[i]) &&
-						(temp = postFinder ? indexOf( seed, elem ) : preMap[i]) > -1 ) {
-
-						seed[temp] = !(results[temp] = elem);
-					}
-				}
-			}
-
-		// Add elements to results, through postFinder if defined
-		} else {
-			matcherOut = condense(
-				matcherOut === results ?
-					matcherOut.splice( preexisting, matcherOut.length ) :
-					matcherOut
-			);
-			if ( postFinder ) {
-				postFinder( null, results, matcherOut, xml );
-			} else {
-				push.apply( results, matcherOut );
-			}
-		}
-	});
-}
-
-function matcherFromTokens( tokens ) {
-	var checkContext, matcher, j,
-		len = tokens.length,
-		leadingRelative = Expr.relative[ tokens[0].type ],
-		implicitRelative = leadingRelative || Expr.relative[" "],
-		i = leadingRelative ? 1 : 0,
-
-		// The foundational matcher ensures that elements are reachable from top-level context(s)
-		matchContext = addCombinator( function( elem ) {
-			return elem === checkContext;
-		}, implicitRelative, true ),
-		matchAnyContext = addCombinator( function( elem ) {
-			return indexOf( checkContext, elem ) > -1;
-		}, implicitRelative, true ),
-		matchers = [ function( elem, context, xml ) {
-			var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
-				(checkContext = context).nodeType ?
-					matchContext( elem, context, xml ) :
-					matchAnyContext( elem, context, xml ) );
-			// Avoid hanging onto element (issue #299)
-			checkContext = null;
-			return ret;
-		} ];
-
-	for ( ; i < len; i++ ) {
-		if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
-			matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];
-		} else {
-			matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
-
-			// Return special upon seeing a positional matcher
-			if ( matcher[ expando ] ) {
-				// Find the next relative operator (if any) for proper handling
-				j = ++i;
-				for ( ; j < len; j++ ) {
-					if ( Expr.relative[ tokens[j].type ] ) {
-						break;
-					}
-				}
-				return setMatcher(
-					i > 1 && elementMatcher( matchers ),
-					i > 1 && toSelector(
-						// If the preceding token was a descendant combinator, insert an implicit any-element `*`
-						tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" })
-					).replace( rtrim, "$1" ),
-					matcher,
-					i < j && matcherFromTokens( tokens.slice( i, j ) ),
-					j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
-					j < len && toSelector( tokens )
-				);
-			}
-			matchers.push( matcher );
-		}
-	}
-
-	return elementMatcher( matchers );
-}
-
-function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
-	var bySet = setMatchers.length > 0,
-		byElement = elementMatchers.length > 0,
-		superMatcher = function( seed, context, xml, results, outermost ) {
-			var elem, j, matcher,
-				matchedCount = 0,
-				i = "0",
-				unmatched = seed && [],
-				setMatched = [],
-				contextBackup = outermostContext,
-				// We must always have either seed elements or outermost context
-				elems = seed || byElement && Expr.find["TAG"]( "*", outermost ),
-				// Use integer dirruns iff this is the outermost matcher
-				dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1),
-				len = elems.length;
-
-			if ( outermost ) {
-				outermostContext = context !== document && context;
-			}
-
-			// Add elements passing elementMatchers directly to results
-			// Keep `i` a string if there are no elements so `matchedCount` will be "00" below
-			// Support: IE<9, Safari
-			// Tolerate NodeList properties (IE: "length"; Safari: <number>) matching elements by id
-			for ( ; i !== len && (elem = elems[i]) != null; i++ ) {
-				if ( byElement && elem ) {
-					j = 0;
-					while ( (matcher = elementMatchers[j++]) ) {
-						if ( matcher( elem, context, xml ) ) {
-							results.push( elem );
-							break;
-						}
-					}
-					if ( outermost ) {
-						dirruns = dirrunsUnique;
-					}
-				}
-
-				// Track unmatched elements for set filters
-				if ( bySet ) {
-					// They will have gone through all possible matchers
-					if ( (elem = !matcher && elem) ) {
-						matchedCount--;
-					}
-
-					// Lengthen the array for every element, matched or not
-					if ( seed ) {
-						unmatched.push( elem );
-					}
-				}
-			}
-
-			// Apply set filters to unmatched elements
-			matchedCount += i;
-			if ( bySet && i !== matchedCount ) {
-				j = 0;
-				while ( (matcher = setMatchers[j++]) ) {
-					matcher( unmatched, setMatched, context, xml );
-				}
-
-				if ( seed ) {
-					// Reintegrate element matches to eliminate the need for sorting
-					if ( matchedCount > 0 ) {
-						while ( i-- ) {
-							if ( !(unmatched[i] || setMatched[i]) ) {
-								setMatched[i] = pop.call( results );
-							}
-						}
-					}
-
-					// Discard index placeholder values to get only actual matches
-					setMatched = condense( setMatched );
-				}
-
-				// Add matches to results
-				push.apply( results, setMatched );
-
-				// Seedless set matches succeeding multiple successful matchers stipulate sorting
-				if ( outermost && !seed && setMatched.length > 0 &&
-					( matchedCount + setMatchers.length ) > 1 ) {
-
-					Sizzle.uniqueSort( results );
-				}
-			}
-
-			// Override manipulation of globals by nested matchers
-			if ( outermost ) {
-				dirruns = dirrunsUnique;
-				outermostContext = contextBackup;
-			}
-
-			return unmatched;
-		};
-
-	return bySet ?
-		markFunction( superMatcher ) :
-		superMatcher;
-}
-
-compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) {
-	var i,
-		setMatchers = [],
-		elementMatchers = [],
-		cached = compilerCache[ selector + " " ];
-
-	if ( !cached ) {
-		// Generate a function of recursive functions that can be used to check each element
-		if ( !match ) {
-			match = tokenize( selector );
-		}
-		i = match.length;
-		while ( i-- ) {
-			cached = matcherFromTokens( match[i] );
-			if ( cached[ expando ] ) {
-				setMatchers.push( cached );
-			} else {
-				elementMatchers.push( cached );
-			}
-		}
-
-		// Cache the compiled function
-		cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
-
-		// Save selector and tokenization
-		cached.selector = selector;
-	}
-	return cached;
-};
-
-/**
- * A low-level selection function that works with Sizzle's compiled
- *  selector functions
- * @param {String|Function} selector A selector or a pre-compiled
- *  selector function built with Sizzle.compile
- * @param {Element} context
- * @param {Array} [results]
- * @param {Array} [seed] A set of elements to match against
- */
-select = Sizzle.select = function( selector, context, results, seed ) {
-	var i, tokens, token, type, find,
-		compiled = typeof selector === "function" && selector,
-		match = !seed && tokenize( (selector = compiled.selector || selector) );
-
-	results = results || [];
-
-	// Try to minimize operations if there is no seed and only one group
-	if ( match.length === 1 ) {
-
-		// Take a shortcut and set the context if the root selector is an ID
-		tokens = match[0] = match[0].slice( 0 );
-		if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
-				support.getById && context.nodeType === 9 && documentIsHTML &&
-				Expr.relative[ tokens[1].type ] ) {
-
-			context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0];
-			if ( !context ) {
-				return results;
-
-			// Precompiled matchers will still verify ancestry, so step up a level
-			} else if ( compiled ) {
-				context = context.parentNode;
-			}
-
-			selector = selector.slice( tokens.shift().value.length );
-		}
-
-		// Fetch a seed set for right-to-left matching
-		i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length;
-		while ( i-- ) {
-			token = tokens[i];
-
-			// Abort if we hit a combinator
-			if ( Expr.relative[ (type = token.type) ] ) {
-				break;
-			}
-			if ( (find = Expr.find[ type ]) ) {
-				// Search, expanding context for leading sibling combinators
-				if ( (seed = find(
-					token.matches[0].replace( runescape, funescape ),
-					rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context
-				)) ) {
-
-					// If seed is empty or no tokens remain, we can return early
-					tokens.splice( i, 1 );
-					selector = seed.length && toSelector( tokens );
-					if ( !selector ) {
-						push.apply( results, seed );
-						return results;
-					}
-
-					break;
-				}
-			}
-		}
-	}
-
-	// Compile and execute a filtering function if one is not provided
-	// Provide `match` to avoid retokenization if we modified the selector above
-	( compiled || compile( selector, match ) )(
-		seed,
-		context,
-		!documentIsHTML,
-		results,
-		rsibling.test( selector ) && testContext( context.parentNode ) || context
-	);
-	return results;
-};
-
-// One-time assignments
-
-// Sort stability
-support.sortStable = expando.split("").sort( sortOrder ).join("") === expando;
-
-// Support: Chrome 14-35+
-// Always assume duplicates if they aren't passed to the comparison function
-support.detectDuplicates = !!hasDuplicate;
-
-// Initialize against the default document
-setDocument();
-
-// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27)
-// Detached nodes confoundingly follow *each other*
-support.sortDetached = assert(function( div1 ) {
-	// Should return 1, but returns 4 (following)
-	return div1.compareDocumentPosition( document.createElement("div") ) & 1;
-});
-
-// Support: IE<8
-// Prevent attribute/property "interpolation"
-// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
-if ( !assert(function( div ) {
-	div.innerHTML = "<a href='#'></a>";
-	return div.firstChild.getAttribute("href") === "#" ;
-}) ) {
-	addHandle( "type|href|height|width", function( elem, name, isXML ) {
-		if ( !isXML ) {
-			return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 );
-		}
-	});
-}
-
-// Support: IE<9
-// Use defaultValue in place of getAttribute("value")
-if ( !support.attributes || !assert(function( div ) {
-	div.innerHTML = "<input/>";
-	div.firstChild.setAttribute( "value", "" );
-	return div.firstChild.getAttribute( "value" ) === "";
-}) ) {
-	addHandle( "value", function( elem, name, isXML ) {
-		if ( !isXML && elem.nodeName.toLowerCase() === "input" ) {
-			return elem.defaultValue;
-		}
-	});
-}
-
-// Support: IE<9
-// Use getAttributeNode to fetch booleans when getAttribute lies
-if ( !assert(function( div ) {
-	return div.getAttribute("disabled") == null;
-}) ) {
-	addHandle( booleans, function( elem, name, isXML ) {
-		var val;
-		if ( !isXML ) {
-			return elem[ name ] === true ? name.toLowerCase() :
-					(val = elem.getAttributeNode( name )) && val.specified ?
-					val.value :
-				null;
-		}
-	});
-}
-
-return Sizzle;
-
-})( window );
-
-
-
-jQuery.find = Sizzle;
-jQuery.expr = Sizzle.selectors;
-jQuery.expr[":"] = jQuery.expr.pseudos;
-jQuery.unique = Sizzle.uniqueSort;
-jQuery.text = Sizzle.getText;
-jQuery.isXMLDoc = Sizzle.isXML;
-jQuery.contains = Sizzle.contains;
-
-
-
-var rneedsContext = jQuery.expr.match.needsContext;
-
-var rsingleTag = (/^<(\w+)\s*\/?>(?:<\/\1>|)$/);
-
-
-
-var risSimple = /^.[^:#\[\.,]*$/;
-
-// Implement the identical functionality for filter and not
-function winnow( elements, qualifier, not ) {
-	if ( jQuery.isFunction( qualifier ) ) {
-		return jQuery.grep( elements, function( elem, i ) {
-			/* jshint -W018 */
-			return !!qualifier.call( elem, i, elem ) !== not;
-		});
-
-	}
-
-	if ( qualifier.nodeType ) {
-		return jQuery.grep( elements, function( elem ) {
-			return ( elem === qualifier ) !== not;
-		});
-
-	}
-
-	if ( typeof qualifier === "string" ) {
-		if ( risSimple.test( qualifier ) ) {
-			return jQuery.filter( qualifier, elements, not );
-		}
-
-		qualifier = jQuery.filter( qualifier, elements );
-	}
-
-	return jQuery.grep( elements, function( elem ) {
-		return ( jQuery.inArray( elem, qualifier ) >= 0 ) !== not;
-	});
-}
-
-jQuery.filter = function( expr, elems, not ) {
-	var elem = elems[ 0 ];
-
-	if ( not ) {
-		expr = ":not(" + expr + ")";
-	}
-
-	return elems.length === 1 && elem.nodeType === 1 ?
-		jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] :
-		jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {
-			return elem.nodeType === 1;
-		}));
-};
-
-jQuery.fn.extend({
-	find: function( selector ) {
-		var i,
-			ret = [],
-			self = this,
-			len = self.length;
-
-		if ( typeof selector !== "string" ) {
-			return this.pushStack( jQuery( selector ).filter(function() {
-				for ( i = 0; i < len; i++ ) {
-					if ( jQuery.contains( self[ i ], this ) ) {
-						return true;
-					}
-				}
-			}) );
-		}
-
-		for ( i = 0; i < len; i++ ) {
-			jQuery.find( selector, self[ i ], ret );
-		}
-
-		// Needed because $( selector, context ) becomes $( context ).find( selector )
-		ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret );
-		ret.selector = this.selector ? this.selector + " " + selector : selector;
-		return ret;
-	},
-	filter: function( selector ) {
-		return this.pushStack( winnow(this, selector || [], false) );
-	},
-	not: function( selector ) {
-		return this.pushStack( winnow(this, selector || [], true) );
-	},
-	is: function( selector ) {
-		return !!winnow(
-			this,
-
-			// If this is a positional/relative selector, check membership in the returned set
-			// so $("p:first").is("p:last") won't return true for a doc with two "p".
-			typeof selector === "string" && rneedsContext.test( selector ) ?
-				jQuery( selector ) :
-				selector || [],
-			false
-		).length;
-	}
-});
-
-
-// Initialize a jQuery object
-
-
-// A central reference to the root jQuery(document)
-var rootjQuery,
-
-	// Use the correct document accordingly with window argument (sandbox)
-	document = window.document,
-
-	// A simple way to check for HTML strings
-	// Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
-	// Strict HTML recognition (#11290: must start with <)
-	rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,
-
-	init = jQuery.fn.init = function( selector, context ) {
-		var match, elem;
-
-		// HANDLE: $(""), $(null), $(undefined), $(false)
-		if ( !selector ) {
-			return this;
-		}
-
-		// Handle HTML strings
-		if ( typeof selector === "string" ) {
-			if ( selector.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
-					// Intentionally let the error be thrown if parseHTML is not present
-					jQuery.merge( this, jQuery.parseHTML(
-						match[1],
-						context && context.nodeType ? context.ownerDocument || context : document,
-						true
-					) );
-
-					// HANDLE: $(html, props)
-					if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
-						for ( match in context ) {
-							// Properties of context are called as methods if possible
-							if ( jQuery.isFunction( this[ match ] ) ) {
-								this[ match ]( context[ match ] );
-
-							// ...and otherwise set as attributes
-							} else {
-								this.attr( match, context[ match ] );
-							}
-						}
-					}
-
-					return this;
-
-				// HANDLE: $(#id)
-				} else {
-					elem = document.getElementById( match[2] );
-
-					// 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 typeof rootjQuery.ready !== "undefined" ?
-				rootjQuery.ready( selector ) :
-				// Execute immediately if ready is not present
-				selector( jQuery );
-		}
-
-		if ( selector.selector !== undefined ) {
-			this.selector = selector.selector;
-			this.context = selector.context;
-		}
-
-		return jQuery.makeArray( selector, this );
-	};
-
-// Give the init function the jQuery prototype for later instantiation
-init.prototype = jQuery.fn;
-
-// Initialize central reference
-rootjQuery = jQuery( document );
-
-
-var rparentsprev = /^(?:parents|prev(?:Until|All))/,
-	// methods guaranteed to produce a unique set when starting from a unique set
-	guaranteedUnique = {
-		children: true,
-		contents: true,
-		next: true,
-		prev: true
-	};
-
-jQuery.extend({
-	dir: function( elem, dir, until ) {
-		var matched = [],
-			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;
-	}
-});
-
-jQuery.fn.extend({
-	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;
-				}
-			}
-		});
-	},
-
-	closest: function( selectors, context ) {
-		var cur,
-			i = 0,
-			l = this.length,
-			matched = [],
-			pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ?
-				jQuery( selectors, context || this.context ) :
-				0;
-
-		for ( ; i < l; i++ ) {
-			for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) {
-				// Always skip document fragments
-				if ( cur.nodeType < 11 && (pos ?
-					pos.index(cur) > -1 :
-
-					// Don't pass non-elements to Sizzle
-					cur.nodeType === 1 &&
-						jQuery.find.matchesSelector(cur, selectors)) ) {
-
-					matched.push( cur );
-					break;
-				}
-			}
-		}
-
-		return this.pushStack( matched.length > 1 ? jQuery.unique( matched ) : matched );
-	},
-
-	// Determine the position of an element within
-	// the 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 ) {
-		return this.pushStack(
-			jQuery.unique(
-				jQuery.merge( this.get(), jQuery( selector, context ) )
-			)
-		);
-	},
-
-	addBack: function( selector ) {
-		return this.add( selector == null ?
-			this.prevObject : this.prevObject.filter(selector)
-		);
-	}
-});
-
-function sibling( cur, dir ) {
-	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 ( name.slice( -5 ) !== "Until" ) {
-			selector = until;
-		}
-
-		if ( selector && typeof selector === "string" ) {
-			ret = jQuery.filter( selector, ret );
-		}
-
-		if ( this.length > 1 ) {
-			// Remove duplicates
-			if ( !guaranteedUnique[ name ] ) {
-				ret = jQuery.unique( ret );
-			}
-
-			// Reverse order for parents* and prev-derivatives
-			if ( rparentsprev.test( name ) ) {
-				ret = ret.reverse();
-			}
-		}
-
-		return this.pushStack( ret );
-	};
-});
-var rnotwhite = (/\S+/g);
-
-
-
-// String to Object options format cache
-var optionsCache = {};
-
-// Convert String-formatted options into Object-formatted ones and store in cache
-function createOptions( options ) {
-	var object = optionsCache[ options ] = {};
-	jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) {
-		object[ flag ] = true;
-	});
-	return object;
-}
-
-/*
- * Create a callback list using the following parameters:
- *
- *	options: an optional list of space-separated options that will change how
- *			the callback list behaves or a more traditional option object
- *
- * By default a callback list will act like an event callback list and can be
- * "fired" multiple times.
- *
- * Possible options:
- *
- *	once:			will ensure the callback list can only be fired once (like a Deferred)
- *
- *	memory:			will keep track of previous values and will call any callback added
- *					after the list has been fired right away with the latest "memorized"
- *					values (like a Deferred)
- *
- *	unique:			will ensure a callback can only be added once (no duplicate in the list)
- *
- *	stopOnFalse:	interrupt callings when a callback returns false
- *
- */
-jQuery.Callbacks = function( options ) {
-
-	// Convert options from String-formatted to Object-formatted if needed
-	// (we check in cache first)
-	options = typeof options === "string" ?
-		( optionsCache[ options ] || createOptions( options ) ) :
-		jQuery.extend( {}, options );
-
-	var // 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 = [];
-				firingLength = 0;
-				return this;
-			},
-			// Have the list do nothing anymore
-			disable: function() {
-				list = stack = memory = undefined;
-				return this;
-			},
-			// Is it disabled?
-			disabled: function() {
-				return !list;
-			},
-			// Lock the list in its current state
-			lock: function() {
-				stack = undefined;
-				if ( !memory ) {
-					self.disable();
-				}
-				return this;
-			},
-			// Is it locked?
-			locked: function() {
-				return !stack;
-			},
-			// Call all callbacks with the given context and arguments
-			fireWith: function( context, args ) {
-				if ( list && ( !fired || stack ) ) {
-					args = args || [];
-					args = [ context, args.slice ? args.slice() : args ];
-					if ( firing ) {
-						stack.push( args );
-					} else {
-						fire( args );
-					}
-				}
-				return this;
-			},
-			// Call all the callbacks with the given arguments
-			fire: function() {
-				self.fireWith( this, arguments );
-				return this;
-			},
-			// To know if the callbacks have already been called at least once
-			fired: function() {
-				return !!fired;
-			}
-		};
-
-	return self;
-};
-
-
-jQuery.extend({
-
-	Deferred: function( func ) {
-		var tuples = [
-				// action, add listener, listener list, final state
-				[ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
-				[ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
-				[ "notify", "progress", jQuery.Callbacks("memory") ]
-			],
-			state = "pending",
-			promise = {
-				state: function() {
-					return state;
-				},
-				always: function() {
-					deferred.done( arguments ).fail( arguments );
-					return this;
-				},
-				then: function( /* fnDone, fnFail, fnProgress */ ) {
-					var fns = arguments;
-					return jQuery.Deferred(function( newDefer ) {
-						jQuery.each( tuples, function( i, tuple ) {
-							var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
-							// deferred[ done | fail | progress ] for forwarding actions to newDefer
-							deferred[ tuple[1] ](function() {
-								var returned = fn && fn.apply( this, arguments );
-								if ( returned && jQuery.isFunction( returned.promise ) ) {
-									returned.promise()
-										.done( newDefer.resolve )
-										.fail( newDefer.reject )
-										.progress( newDefer.notify );
-								} else {
-									newDefer[ tuple[ 0 ] + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
-								}
-							});
-						});
-						fns = null;
-					}).promise();
-				},
-				// Get a promise for this deferred
-				// If obj is provided, the promise aspect is added to the object
-				promise: function( obj ) {
-					return obj != null ? jQuery.extend( obj, promise ) : promise;
-				}
-			},
-			deferred = {};
-
-		// Keep pipe for back-compat
-		promise.pipe = promise.then;
-
-		// Add list-specific methods
-		jQuery.each( tuples, function( i, tuple ) {
-			var list = tuple[ 2 ],
-				stateString = tuple[ 3 ];
-
-			// promise[ done | fail | progress ] = list.add
-			promise[ tuple[1] ] = list.add;
-
-			// Handle state
-			if ( stateString ) {
-				list.add(function() {
-					// state = [ resolved | rejected ]
-					state = stateString;
-
-				// [ reject_list | resolve_list ].disable; progress_list.lock
-				}, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
-			}
-
-			// deferred[ resolve | reject | notify ]
-			deferred[ tuple[0] ] = function() {
-				deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
-				return this;
-			};
-			deferred[ tuple[0] + "With" ] = list.fireWith;
-		});
-
-		// Make the deferred a promise
-		promise.promise( deferred );
-
-		// Call given func if any
-		if ( func ) {
-			func.call( deferred, deferred );
-		}
-
-		// All done!
-		return deferred;
-	},
-
-	// Deferred helper
-	when: function( subordinate /* , ..., subordinateN */ ) {
-		var i = 0,
-			resolveValues = slice.call( arguments ),
-			length = resolveValues.length,
-
-			// the count of uncompleted subordinates
-			remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
-
-			// the master Deferred. If resolveValues consist of only a single Deferred, just use that.
-			deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
-
-			// Update function for both resolve and progress values
-			updateFunc = function( i, contexts, values ) {
-				return function( value ) {
-					contexts[ i ] = this;
-					values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;
-					if ( values === progressValues ) {
-						deferred.notifyWith( contexts, values );
-
-					} else if ( !(--remaining) ) {
-						deferred.resolveWith( contexts, values );
-					}
-				};
-			},
-
-			progressValues, progressContexts, resolveContexts;
-
-		// add listeners to Deferred subordinates; treat others as resolved
-		if ( length > 1 ) {
-			progressValues = new Array( length );
-			progressContexts = new Array( length );
-			resolveContexts = new Array( length );
-			for ( ; i < length; i++ ) {
-				if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
-					resolveValues[ i ].promise()
-						.done( updateFunc( i, resolveContexts, resolveValues ) )
-						.fail( deferred.reject )
-						.progress( updateFunc( i, progressContexts, progressValues ) );
-				} else {
-					--remaining;
-				}
-			}
-		}
-
-		// if we're not waiting on anything, resolve the master
-		if ( !remaining ) {
-			deferred.resolveWith( resolveContexts, resolveValues );
-		}
-
-		return deferred.promise();
-	}
-});
-
-
-// The deferred used on DOM ready
-var readyList;
-
-jQuery.fn.ready = function( fn ) {
-	// Add the callback
-	jQuery.ready.promise().done( fn );
-
-	return this;
-};
-
-jQuery.extend({
-	// Is the DOM ready to be used? Set to true once it occurs.
-	isReady: false,
-
-	// A counter to track how many items to wait for before
-	// the ready event fires. See #6781
-	readyWait: 1,
-
-	// Hold (or release) the ready event
-	holdReady: function( hold ) {
-		if ( hold ) {
-			jQuery.readyWait++;
-		} else {
-			jQuery.ready( true );
-		}
-	},
-
-	// Handle when the DOM is ready
-	ready: function( wait ) {
-
-		// Abort if there are pending holds or we're already ready
-		if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
-			return;
-		}
-
-		// 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.triggerHandler ) {
-			jQuery( document ).triggerHandler( "ready" );
-			jQuery( document ).off( "ready" );
-		}
-	}
-});
-
-/**
- * Clean-up method for dom ready events
- */
-function detach() {
-	if ( document.addEventListener ) {
-		document.removeEventListener( "DOMContentLoaded", completed, false );
-		window.removeEventListener( "load", completed, false );
-
-	} else {
-		document.detachEvent( "onreadystatechange", completed );
-		window.detachEvent( "onload", completed );
-	}
-}
-
-/**
- * The ready event handler and self cleanup method
- */
-function completed() {
-	// 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();
-	}
-}
-
-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 );
-};
-
-
-var strundefined = typeof undefined;
-
-
-
-// Support: IE<9
-// Iteration over object's inherited properties before its own
-var i;
-for ( i in jQuery( support ) ) {
-	break;
-}
-support.ownLast = i !== "0";
-
-// Note: most support tests are defined in their respective modules.
-// false until the test is run
-support.inlineBlockNeedsLayout = false;
-
-// Execute ASAP in case we need to set body.style.zoom
-jQuery(function() {
-	// Minified: var a,b,c,d
-	var val, div, body, container;
-
-	body = document.getElementsByTagName( "body" )[ 0 ];
-	if ( !body || !body.style ) {
-		// Return for frameset docs that don't have a body
-		return;
-	}
-
-	// Setup
-	div = document.createElement( "div" );
-	container = document.createElement( "div" );
-	container.style.cssText = "position:absolute;border:0;width:0;height:0;top:0;left:-9999px";
-	body.appendChild( container ).appendChild( div );
-
-	if ( typeof div.style.zoom !== 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.style.cssText = "display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1";
-
-		support.inlineBlockNeedsLayout = val = div.offsetWidth === 3;
-		if ( val ) {
-			// 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 );
-});
-
-
-
-
-(function() {
-	var div = document.createElement( "div" );
-
-	// Execute the test only if not already executed in another module.
-	if (support.deleteExpando == null) {
-		// Support: IE<9
-		support.deleteExpando = true;
-		try {
-			delete div.test;
-		} catch( e ) {
-			support.deleteExpando = false;
-		}
-	}
-
-	// Null elements to avoid leaks in IE.
-	div = null;
-})();
-
-
-/**
- * Determines whether an object can have data
- */
-jQuery.acceptData = function( elem ) {
-	var noData = jQuery.noData[ (elem.nodeName + " ").toLowerCase() ],
-		nodeType = +elem.nodeType || 1;
-
-	// Do not set data on non-element DOM nodes because it will not be cleared (#8335).
-	return nodeType !== 1 && nodeType !== 9 ?
-		false :
-
-		// Nodes accept data unless otherwise specified; rejection can be conditional
-		!noData || noData !== true && elem.getAttribute("classid") === noData;
-};
-
-
-var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,
-	rmultiDash = /([A-Z])/g;
-
-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;
-}
-
-function internalData( elem, name, data, pvt /* Internal Use Only */ ) {
-	if ( !jQuery.acceptData( elem ) ) {
-		return;
-	}
-
-	var ret, thisCache,
-		internalKey = jQuery.expando,
-
-		// 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)) && data === undefined && typeof name === "string" ) {
-		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 ) {
-			id = elem[ internalKey ] = deletedIds.pop() || jQuery.guid++;
-		} else {
-			id = internalKey;
-		}
-	}
-
-	if ( !cache[ id ] ) {
-		// Avoid exposing jQuery metadata on plain JS objects when the object
-		// is serialized using JSON.stringify
-		cache[ id ] = isNode ? {} : { 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 ( typeof name === "string" ) {
-
-		// 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 thisCache, i,
-		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 ) );
-			}
-
-			i = name.length;
-			while ( 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(thisCache) : !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)
-	/* jshint eqeqeq: false */
-	} else if ( support.deleteExpando || cache != cache.window ) {
-		/* jshint eqeqeq: true */
-		delete cache[ id ];
-
-	// When all else fails, null
-	} else {
-		cache[ id ] = null;
-	}
-}
-
-jQuery.extend({
-	cache: {},
-
-	// The following elements (space-suffixed to avoid Object.prototype collisions)
-	// throw uncatchable exceptions if you attempt to set expando properties
-	noData: {
-		"applet ": true,
-		"embed ": true,
-		// ...but Flash objects (which have this classid) *can* handle expandos
-		"object ": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
-	},
-
-	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 );
-	}
-});
-
-jQuery.fn.extend({
-	data: function( key, value ) {
-		var i, name, data,
-			elem = this[0],
-			attrs = elem && elem.attributes;
-
-		// Special expections of .data basically thwart jQuery.access,
-		// so implement the relevant behavior ourselves
-
-		// Gets all values
-		if ( key === undefined ) {
-			if ( this.length ) {
-				data = jQuery.data( elem );
-
-				if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) {
-					i = attrs.length;
-					while ( i-- ) {
-
-						// Support: IE11+
-						// The attrs elements can be null (#14894)
-						if ( attrs[ i ] ) {
-							name = attrs[ i ].name;
-							if ( name.indexOf( "data-" ) === 0 ) {
-								name = jQuery.camelCase( name.slice(5) );
-								dataAttr( elem, name, data[ name ] );
-							}
-						}
-					}
-					jQuery._data( elem, "parsedAttrs", true );
-				}
-			}
-
-			return data;
-		}
-
-		// Sets multiple values
-		if ( typeof key === "object" ) {
-			return this.each(function() {
-				jQuery.data( this, key );
-			});
-		}
-
-		return arguments.length > 1 ?
-
-			// Sets one value
-			this.each(function() {
-				jQuery.data( this, key, value );
-			}) :
-
-			// Gets one value
-			// Try to fetch any internally stored data first
-			elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : undefined;
-	},
-
-	removeData: function( key ) {
-		return this.each(function() {
-			jQuery.removeData( this, key );
-		});
-	}
-});
-
-
-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--;
-		}
-
-		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 );
-		});
-	},
-	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 pnum = (/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/).source;
-
-var cssExpand = [ "Top", "Right", "Bottom", "Left" ];
-
-var isHidden = function( elem, el ) {
-		// isHidden might be called from jQuery#filter function;
-		// in that case, element will be second argument
-		elem = el || elem;
-		return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem );
-	};
-
-
-
-// Multifunctional method to get and set values of a collection
-// The value/s can optionally be executed if it's a function
-var access = jQuery.access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
-	var i = 0,
-		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;
-};
-var rcheckableType = (/^(?:checkbox|radio)$/i);
-
-
-
-(function() {
-	// Minified: var a,b,c
-	var input = document.createElement( "input" ),
-		div = document.createElement( "div" ),
-		fragment = document.createDocumentFragment();
-
-	// Setup
-	div.innerHTML = "  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>";
-
-	// IE strips leading whitespace when .innerHTML is used
-	support.leadingWhitespace = div.firstChild.nodeType === 3;
-
-	// Make sure that tbody elements aren't automatically inserted
-	// IE will insert them into empty tables
-	support.tbody = !div.getElementsByTagName( "tbody" ).length;
-
-	// Make sure that link elements get serialized correctly by innerHTML
-	// This requires a wrapper element in IE
-	support.htmlSerialize = !!div.getElementsByTagName( "link" ).length;
-
-	// Makes sure cloning an html5 element does not cause problems
-	// Where outerHTML is undefined, this still works
-	support.html5Clone =
-		document.createElement( "nav" ).cloneNode( true ).outerHTML !== "<:nav></:nav>";
-
-	// Check if a disconnected checkbox will retain its checked
-	// value of true after appended to the DOM (IE6/7)
-	input.type = "checkbox";
-	input.checked = true;
-	fragment.appendChild( input );
-	support.appendChecked = input.checked;
-
-	// Make sure textarea (and checkbox) defaultValue is properly cloned
-	// Support: IE6-IE11+
-	div.innerHTML = "<textarea>x</textarea>";
-	support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue;
-
-	// #11217 - WebKit loses check when the name is after the checked attribute
-	fragment.appendChild( div );
-	div.innerHTML = "<input type='radio' checked='checked' name='t'/>";
-
-	// Support: Safari 5.1, iOS 5.1, Android 4.x, Android 2.3
-	// old WebKit doesn't clone checked state correctly in fragments
-	support.checkClone = div.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()
-	support.noCloneEvent = true;
-	if ( div.attachEvent ) {
-		div.attachEvent( "onclick", function() {
-			support.noCloneEvent = false;
-		});
-
-		div.cloneNode( true ).click();
-	}
-
-	// Execute the test only if not already executed in another module.
-	if (support.deleteExpando == null) {
-		// Support: IE<9
-		support.deleteExpando = true;
-		try {
-			delete div.test;
-		} catch( e ) {
-			support.deleteExpando = false;
-		}
-	}
-})();
-
-
-(function() {
-	var i, eventName,
-		div = document.createElement( "div" );
-
-	// Support: IE<9 (lack submit/change bubble), Firefox 23+ (lack focusin event)
-	for ( i in { submit: true, change: true, focusin: true }) {
-		eventName = "on" + i;
-
-		if ( !(support[ i + "Bubbles" ] = eventName in window) ) {
-			// Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP)
-			div.setAttribute( eventName, "t" );
-			support[ i + "Bubbles" ] = div.attributes[ eventName ].expando === false;
-		}
-	}
-
-	// Null elements to avoid leaks in IE.
-	div = null;
-})();
-
-
-var rformElems = /^(?:input|select|textarea)$/i,
-	rkeyEvent = /^key/,
-	rmouseEvent = /^(?:mouse|pointer|contextmenu)|click/,
-	rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
-	rtypenamespace = /^([^.]*)(?:\.(.+)|)$/;
-
-function returnTrue() {
-	return true;
-}
-
-function returnFalse() {
-	return false;
-}
-
-function safeActiveElement() {
-	try {
-		return document.activeElement;
-	} catch ( err ) { }
-}
-
-/*
- * Helper functions for managing events -- not part of the public interface.
- * Props to Dean Edwards' addEvent library for many of the ideas.
- */
-jQuery.event = {
-
-	global: {},
-
-	add: function( elem, types, handler, data, selector ) {
-		var 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 !== 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
-		types = ( types || "" ).match( rnotwhite ) || [ "" ];
-		t = types.length;
-		while ( t-- ) {
-			tmp = rtypenamespace.exec( types[t] ) || [];
-			type = origType = tmp[1];
-			namespaces = ( tmp[2] || "" ).split( "." ).sort();
-
-			// There *must* be a type, no attaching namespace-only handlers
-			if ( !type ) {
-				continue;
-			}
-
-			// If event changes its type, use the special event handlers for the changed type
-			special = jQuery.event.special[ type ] || {};
-
-			// If selector defined, determine special event api type, otherwise given type
-			type = ( selector ? special.delegateType : special.bindType ) || type;
-
-			// Update special based on newly reset type
-			special = jQuery.event.special[ type ] || {};
-
-			// handleObj is passed to all event handlers
-			handleObj = jQuery.extend({
-				type: type,
-				origType: origType,
-				data: data,
-				handler: handler,
-				guid: handler.guid,
-				selector: selector,
-				needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
-				namespace: namespaces.join(".")
-			}, handleObjIn );
-
-			// Init the event handler queue if we're the first
-			if ( !(handlers = events[ type ]) ) {
-				handlers = events[ type ] = [];
-				handlers.delegateCount = 0;
-
-				// Only use addEventListener/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( 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 = hasOwn.call( event, "type" ) ? event.type : event,
-			namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : [];
-
-		cur = tmp = elem = elem || document;
-
-		// Don't do events on text and comment nodes
-		if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
-			return;
-		}
-
-		// focus/blur morphs to focusin/out; ensure we're not firing them right now
-		if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
-			return;
-		}
-
-		if ( type.indexOf(".") >= 0 ) {
-			// Namespaced trigger; create a regexp to match event type in handle()
-			namespaces = type.split(".");
-			type = namespaces.shift();
-			namespaces.sort();
-		}
-		ontype = type.indexOf(":") < 0 && "on" + type;
-
-		// Caller can pass in a jQuery.Event object, Object, or just an event type string
-		event = event[ jQuery.expando ] ?
-			event :
-			new jQuery.Event( type, typeof event === "object" && event );
-
-		// Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)
-		event.isTrigger = onlyHandlers ? 2 : 3;
-		event.namespace = namespaces.join(".");
-		event.namespace_re = event.namespace ?
-			new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) :
-			null;
-
-		// Clean up the event in case it is being reused
-		event.result = undefined;
-		if ( !event.target ) {
-			event.target = elem;
-		}
-
-		// Clone any incoming data and prepend the event, creating the handler arg list
-		data = data == null ?
-			[ event ] :
-			jQuery.makeArray( data, [ event ] );
-
-		// Allow special events to draw outside the lines
-		special = jQuery.event.special[ type ] || {};
-		if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {
-			return;
-		}
-
-		// Determine event propagation path in advance, per W3C events spec (#9951)
-		// Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
-		if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
-
-			bubbleType = special.delegateType || type;
-			if ( !rfocusMorph.test( bubbleType + type ) ) {
-				cur = cur.parentNode;
-			}
-			for ( ; cur; cur = cur.parentNode ) {
-				eventPath.push( cur );
-				tmp = cur;
-			}
-
-			// Only add window if we got to document (e.g., not plain obj or detached DOM)
-			if ( tmp === (elem.ownerDocument || document) ) {
-				eventPath.push( tmp.defaultView || tmp.parentWindow || window );
-			}
-		}
-
-		// Fire handlers on the event path
-		i = 0;
-		while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) {
-
-			event.type = i > 1 ?
-				bubbleType :
-				special.bindType || type;
-
-			// jQuery handler
-			handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" );
-			if ( handle ) {
-				handle.apply( cur, data );
-			}
-
-			// Native handler
-			handle = ontype && cur[ ontype ];
-			if ( handle && handle.apply && jQuery.acceptData( cur ) ) {
-				event.result = handle.apply( cur, data );
-				if ( event.result === false ) {
-					event.preventDefault();
-				}
-			}
-		}
-		event.type = type;
-
-		// If nobody prevented the default action, do it now
-		if ( !onlyHandlers && !event.isDefaultPrevented() ) {
-
-			if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) &&
-				jQuery.acceptData( elem ) ) {
-
-				// Call a native DOM method on the target with the same name name as the event.
-				// 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 = 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") ) {
-
-			/* jshint eqeqeq: false */
-			for ( ; cur != this; cur = cur.parentNode || this ) {
-				/* jshint eqeqeq: true */
-
-				// 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
-		},
-		focus: {
-			// Fire native event if possible so blur/focus sequence is correct
-			trigger: function() {
-				if ( this !== safeActiveElement() && 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 === safeActiveElement() && this.blur ) {
-					this.blur();
-					return false;
-				}
-			},
-			delegateType: "focusout"
-		},
-		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;
-				}
-			},
-
-			// For cross-browser consistency, don't fire native .click() on links
-			_default: function( event ) {
-				return jQuery.nodeName( event.target, "a" );
-			}
-		},
-
-		beforeunload: {
-			postDispatch: function( event ) {
-
-				// Support: Firefox 20+
-				// Firefox doesn't alert if the returnValue field is not set.
-				if ( event.result !== undefined && event.originalEvent ) {
-					event.originalEvent.returnValue = event.result;
-				}
-			}
-		}
-	},
-
-	simulate: function( type, elem, event, bubble ) {
-		// Piggyback on a donor event to simulate a different one.
-		// Fake originalEvent to avoid donor's stopPropagation, but if the
-		// simulated event prevents default then we do the same on the donor.
-		var e = jQuery.extend(
-			new jQuery.Event(),
-			event,
-			{
-				type: type,
-				isSimulated: true,
-				originalEvent: {}
-			}
-		);
-		if ( bubble ) {
-			jQuery.event.trigger( e, null, elem );
-		} else {
-			jQuery.event.dispatch.call( elem, e );
-		}
-		if ( e.isDefaultPrevented() ) {
-			event.preventDefault();
-		}
-	}
-};
-
-jQuery.removeEvent = 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 ] === 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.defaultPrevented === undefined &&
-				// Support: IE < 9, Android < 4.0
-				src.returnValue === false ?
-			returnTrue :
-			returnFalse;
-
-	// Event type
-	} else {
-		this.type = src;
-	}
-
-	// Put explicitly provided properties onto the event object
-	if ( props ) {
-		jQuery.extend( this, props );
-	}
-
-	// Create a timestamp if incoming event doesn't have one
-	this.timeStamp = src && src.timeStamp || jQuery.now();
-
-	// Mark it as fixed
-	this[ jQuery.expando ] = true;
-};
-
-// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
-// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
-jQuery.Event.prototype = {
-	isDefaultPrevented: returnFalse,
-	isPropagationStopped: returnFalse,
-	isImmediatePropagationStopped: returnFalse,
-
-	preventDefault: function() {
-		var e = this.originalEvent;
-
-		this.isDefaultPrevented = returnTrue;
-		if ( !e ) {
-			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() {
-		var e = this.originalEvent;
-
-		this.isImmediatePropagationStopped = returnTrue;
-
-		if ( e && e.stopImmediatePropagation ) {
-			e.stopImmediatePropagation();
-		}
-
-		this.stopPropagation();
-	}
-};
-
-// Create mouseenter/leave events using mouseover/out and event-time checks
-jQuery.each({
-	mouseenter: "mouseover",
-	mouseleave: "mouseout",
-	pointerenter: "pointerover",
-	pointerleave: "pointerout"
-}, function( orig, fix ) {
-	jQuery.event.special[ orig ] = {
-		delegateType: fix,
-		bindType: fix,
-
-		handle: function( event ) {
-			var ret,
-				target = this,
-				related = event.relatedTarget,
-				handleObj = event.handleObj;
-
-			// For mousenter/leave call the handler if related is outside the target.
-			// NB: No relatedTarget if the mouse left/entered the browser window
-			if ( !related || (related !== target && !jQuery.contains( target, related )) ) {
-				event.type = handleObj.origType;
-				ret = handleObj.handler.apply( this, arguments );
-				event.type = fix;
-			}
-			return ret;
-		}
-	};
-});
-
-// IE submit delegation
-if ( !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 ( !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 ( !support.focusinBubbles ) {
-	jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
-
-		// Attach a single capturing handler on the document while someone wants focusin/focusout
-		var handler = function( event ) {
-				jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );
-			};
-
-		jQuery.event.special[ fix ] = {
-			setup: function() {
-				var doc = this.ownerDocument || this,
-					attaches = jQuery._data( doc, fix );
-
-				if ( !attaches ) {
-					doc.addEventListener( orig, handler, true );
-				}
-				jQuery._data( doc, fix, ( attaches || 0 ) + 1 );
-			},
-			teardown: function() {
-				var doc = this.ownerDocument || this,
-					attaches = jQuery._data( doc, fix ) - 1;
-
-				if ( !attaches ) {
-					doc.removeEventListener( orig, handler, true );
-					jQuery._removeData( doc, fix );
-				} else {
-					jQuery._data( doc, fix, attaches );
-				}
-			}
-		};
-	});
-}
-
-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 );
-		});
-	},
-
-	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 );
-		}
-	}
-});
-
-
-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,
-	// 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: 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;
-
-function getAll( context, tag ) {
-	var elems, elem,
-		i = 0,
-		found = typeof context.getElementsByTagName !== strundefined ? context.getElementsByTagName( tag || "*" ) :
-			typeof context.querySelectorAll !== 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 ( rcheckableType.test( elem.type ) ) {
-		elem.defaultChecked = elem.checked;
-	}
-}
-
-// Support: IE<8
-// Manipulating tables requires a tbody
-function manipulationTarget( elem, content ) {
-	return jQuery.nodeName( elem, "table" ) &&
-		jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ?
-
-		elem.getElementsByTagName("tbody")[0] ||
-			elem.appendChild( elem.ownerDocument.createElement("tbody") ) :
-		elem;
-}
-
-// Replace/restore the type attribute of script elements for safe DOM manipulation
-function disableScript( elem ) {
-	elem.type = (jQuery.find.attr( elem, "type" ) !== null) + "/" + elem.type;
-	return elem;
-}
-function restoreScript( elem ) {
-	var match = rscriptTypeMasked.exec( elem.type );
-	if ( match ) {
-		elem.type = match[1];
-	} else {
-		elem.removeAttribute("type");
-	}
-	return elem;
-}
-
-// Mark scripts as having already been evaluated
-function setGlobalEval( elems, refElements ) {
-	var 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 ( !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 ( support.html5Clone && ( src.innerHTML && !jQuery.trim(dest.innerHTML) ) ) {
-			dest.innerHTML = src.innerHTML;
-		}
-
-	} else if ( nodeName === "input" && 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.extend({
-	clone: function( elem, dataAndEvents, deepDataAndEvents ) {
-		var destElements, node, clone, i, srcElements,
-			inPage = jQuery.contains( elem.ownerDocument, elem );
-
-		if ( 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 ( (!support.noCloneEvent || !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 ( !support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
-						nodes.push( context.createTextNode( rleadingWhitespace.exec( elem )[0] ) );
-					}
-
-					// Remove IE's autoinserted <tbody> from table fragments
-					if ( !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 ( !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 = 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 !== strundefined ) {
-							elem.removeAttribute( internalKey );
-
-						} else {
-							elem[ internalKey ] = null;
-						}
-
-						deletedIds.push( id );
-					}
-				}
-			}
-		}
-	}
-});
-
-jQuery.fn.extend({
-	text: function( value ) {
-		return 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 );
-	},
-
-	append: function() {
-		return this.domManip( arguments, function( elem ) {
-			if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
-				var target = manipulationTarget( this, elem );
-				target.appendChild( elem );
-			}
-		});
-	},
-
-	prepend: function() {
-		return this.domManip( arguments, function( elem ) {
-			if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
-				var target = manipulationTarget( this, elem );
-				target.insertBefore( elem, target.firstChild );
-			}
-		});
-	},
-
-	before: function() {
-		return this.domManip( arguments, function( elem ) {
-			if ( this.parentNode ) {
-				this.parentNode.insertBefore( elem, this );
-			}
-		});
-	},
-
-	after: function() {
-		return this.domManip( arguments, function( elem ) {
-			if ( this.parentNode ) {
-				this.parentNode.insertBefore( elem, this.nextSibling );
-			}
-		});
-	},
-
-	remove: function( selector, keepData /* Internal Use Only */ ) {
-		var elem,
-			elems = selector ? jQuery.filter( selector, this ) : this,
-			i = 0;
-
-		for ( ; (elem = elems[i]) != null; i++ ) {
-
-			if ( !keepData && elem.nodeType === 1 ) {
-				jQuery.cleanData( getAll( elem ) );
-			}
-
-			if ( elem.parentNode ) {
-				if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) {
-					setGlobalEval( getAll( elem, "script" ) );
-				}
-				elem.parentNode.removeChild( elem );
-			}
-		}
-
-		return this;
-	},
-
-	empty: function() {
-		var elem,
-			i = 0;
-
-		for ( ; (elem = this[i]) != null; i++ ) {
-			// 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 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 ) &&
-				( support.htmlSerialize || !rnoshimcache.test( value )  ) &&
-				( 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() {
-		var arg = arguments[ 0 ];
-
-		// Make the changes, replacing each context element with the new content
-		this.domManip( arguments, function( elem ) {
-			arg = this.parentNode;
-
-			jQuery.cleanData( getAll( this ) );
-
-			if ( arg ) {
-				arg.replaceChild( elem, this );
-			}
-		});
-
-		// Force removal if there was no new content (e.g., from empty arguments)
-		return arg && (arg.length || arg.nodeType) ? this : this.remove();
-	},
-
-	detach: function( selector ) {
-		return this.remove( selector, true );
-	},
-
-	domManip: function( args, callback ) {
-
-		// Flatten any nested arrays
-		args = concat.apply( [], args );
-
-		var 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" &&
-					!support.checkClone && rchecked.test( value ) ) ) {
-			return this.each(function( index ) {
-				var self = set.eq( index );
-				if ( isFunction ) {
-					args[0] = value.call( this, index, self.html() );
-				}
-				self.domManip( args, callback );
-			});
-		}
-
-		if ( l ) {
-			fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this );
-			first = fragment.firstChild;
-
-			if ( fragment.childNodes.length === 1 ) {
-				fragment = first;
-			}
-
-			if ( first ) {
-				scripts = jQuery.map( getAll( fragment, "script" ), disableScript );
-				hasScripts = scripts.length;
-
-				// Use the original fragment for the last item instead of the first because it can end up
-				// being emptied incorrectly in certain situations (#8070).
-				for ( ; i < l; i++ ) {
-					node = fragment;
-
-					if ( i !== iNoClone ) {
-						node = jQuery.clone( node, true, true );
-
-						// Keep references to cloned scripts for later restoration
-						if ( hasScripts ) {
-							jQuery.merge( scripts, getAll( node, "script" ) );
-						}
-					}
-
-					callback.call( this[i], node, i );
-				}
-
-				if ( hasScripts ) {
-					doc = scripts[ scripts.length - 1 ].ownerDocument;
-
-					// Reenable scripts
-					jQuery.map( scripts, restoreScript );
-
-					// Evaluate executable scripts on first document insertion
-					for ( i = 0; i < hasScripts; i++ ) {
-						node = scripts[ i ];
-						if ( rscriptType.test( node.type || "" ) &&
-							!jQuery._data( node, "globalEval" ) && jQuery.contains( doc, node ) ) {
-
-							if ( node.src ) {
-								// Optional AJAX dependency, but won't run scripts if not present
-								if ( jQuery._evalUrl ) {
-									jQuery._evalUrl( node.src );
-								}
-							} else {
-								jQuery.globalEval( ( node.text || node.textContent || node.innerHTML || "" ).replace( rcleanScript, "" ) );
-							}
-						}
-					}
-				}
-
-				// Fix #11809: Avoid leaking memory
-				fragment = first = null;
-			}
-		}
-
-		return this;
-	}
-});
-
-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()
-			push.apply( ret, elems.get() );
-		}
-
-		return this.pushStack( ret );
-	};
-});
-
-
-var iframe,
-	elemdisplay = {};
-
-/**
- * Retrieve the actual display of a element
- * @param {String} name nodeName of the element
- * @param {Object} doc Document object
- */
-// Called only from within defaultDisplay
-function actualDisplay( name, doc ) {
-	var style,
-		elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ),
-
-		// getDefaultComputedStyle might be reliably used only on attached element
-		display = window.getDefaultComputedStyle && ( style = window.getDefaultComputedStyle( elem[ 0 ] ) ) ?
-
-			// Use of this method is a temporary fix (more like optmization) until something better comes along,
-			// since it was removed from specification and supported only in FF
-			style.display : jQuery.css( elem[ 0 ], "display" );
-
-	// We don't have any data stored on the element,
-	// so use "detach" method as fast way to get rid of the element
-	elem.detach();
-
-	return display;
-}
-
-/**
- * Try to determine the default display value of an element
- * @param {String} nodeName
- */
-function defaultDisplay( nodeName ) {
-	var doc = document,
-		display = elemdisplay[ nodeName ];
-
-	if ( !display ) {
-		display = actualDisplay( nodeName, doc );
-
-		// If the simple way fails, read from inside an iframe
-		if ( display === "none" || !display ) {
-
-			// Use the already-created iframe if possible
-			iframe = (iframe || jQuery( "<iframe frameborder='0' width='0' height='0'/>" )).appendTo( doc.documentElement );
-
-			// Always write a new HTML skeleton so Webkit and Firefox don't choke on reuse
-			doc = ( iframe[ 0 ].contentWindow || iframe[ 0 ].contentDocument ).document;
-
-			// Support: IE
-			doc.write();
-			doc.close();
-
-			display = actualDisplay( nodeName, doc );
-			iframe.detach();
-		}
-
-		// Store the correct default display
-		elemdisplay[ nodeName ] = display;
-	}
-
-	return display;
-}
-
-
-(function() {
-	var shrinkWrapBlocksVal;
-
-	support.shrinkWrapBlocks = function() {
-		if ( shrinkWrapBlocksVal != null ) {
-			return shrinkWrapBlocksVal;
-		}
-
-		// Will be changed later if needed.
-		shrinkWrapBlocksVal = false;
-
-		// Minified: var b,c,d
-		var div, body, container;
-
-		body = document.getElementsByTagName( "body" )[ 0 ];
-		if ( !body || !body.style ) {
-			// Test fired too early or in an unsupported environment, exit.
-			return;
-		}
-
-		// Setup
-		div = document.createElement( "div" );
-		container = document.createElement( "div" );
-		container.style.cssText = "position:absolute;border:0;width:0;height:0;top:0;left:-9999px";
-		body.appendChild( container ).appendChild( div );
-
-		// Support: IE6
-		// Check if elements with layout shrink-wrap their children
-		if ( typeof div.style.zoom !== strundefined ) {
-			// Reset CSS: box-sizing; display; margin; border
-			div.style.cssText =
-				// Support: Firefox<29, Android 2.3
-				// Vendor-prefix box-sizing
-				"-webkit-box-sizing:content-box;-moz-box-sizing:content-box;" +
-				"box-sizing:content-box;display:block;margin:0;border:0;" +
-				"padding:1px;width:1px;zoom:1";
-			div.appendChild( document.createElement( "div" ) ).style.width = "5px";
-			shrinkWrapBlocksVal = div.offsetWidth !== 3;
-		}
-
-		body.removeChild( container );
-
-		return shrinkWrapBlocksVal;
-	};
-
-})();
-var rmargin = (/^margin/);
-
-var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" );
-
-
-
-var getStyles, curCSS,
-	rposition = /^(top|right|bottom|left)$/;
-
-if ( window.getComputedStyle ) {
-	getStyles = function( elem ) {
-		// Support: IE<=11+, Firefox<=30+ (#15098, #14150)
-		// IE throws on elements created in popups
-		// FF meanwhile throws on frame elements through "defaultView.getComputedStyle"
-		if ( elem.ownerDocument.defaultView.opener ) {
-			return elem.ownerDocument.defaultView.getComputedStyle( elem, null );
-		}
-
-		return window.getComputedStyle( elem, null );
-	};
-
-	curCSS = function( elem, name, computed ) {
-		var width, minWidth, maxWidth, ret,
-			style = elem.style;
-
-		computed = computed || getStyles( elem );
-
-		// getPropertyValue is only needed for .css('filter') in IE9, see #12537
-		ret = computed ? computed.getPropertyValue( name ) || computed[ name ] : undefined;
-
-		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;
-			}
-		}
-
-		// Support: IE
-		// IE returns zIndex value as an integer.
-		return ret === undefined ?
-			ret :
-			ret + "";
-	};
-} else if ( document.documentElement.currentStyle ) {
-	getStyles = function( elem ) {
-		return elem.currentStyle;
-	};
-
-	curCSS = function( elem, name, computed ) {
-		var left, rs, rsLeft, ret,
-			style = elem.style;
-
-		computed = computed || getStyles( elem );
-		ret = computed ? computed[ name ] : undefined;
-
-		// 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;
-			}
-		}
-
-		// Support: IE
-		// IE returns zIndex value as an integer.
-		return ret === undefined ?
-			ret :
-			ret + "" || "auto";
-	};
-}
-
-
-
-
-function addGetHookIf( conditionFn, hookFn ) {
-	// Define the hook, we'll check on the first run if it's really needed.
-	return {
-		get: function() {
-			var condition = conditionFn();
-
-			if ( condition == null ) {
-				// The test was not ready at this point; screw the hook this time
-				// but check again when needed next time.
-				return;
-			}
-
-			if ( condition ) {
-				// Hook not needed (or it's not possible to use it due to missing dependency),
-				// remove it.
-				// Since there are no other hooks for marginRight, remove the whole object.
-				delete this.get;
-				return;
-			}
-
-			// Hook needed; redefine it so that the support test is not executed again.
-
-			return (this.get = hookFn).apply( this, arguments );
-		}
-	};
-}
-
-
-(function() {
-	// Minified: var b,c,d,e,f,g, h,i
-	var div, style, a, pixelPositionVal, boxSizingReliableVal,
-		reliableHiddenOffsetsVal, reliableMarginRightVal;
-
-	// Setup
-	div = document.createElement( "div" );
-	div.innerHTML = "  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>";
-	a = div.getElementsByTagName( "a" )[ 0 ];
-	style = a && a.style;
-
-	// Finish early in limited (non-browser) environments
-	if ( !style ) {
-		return;
-	}
-
-	style.cssText = "float:left;opacity:.5";
-
-	// Support: IE<9
-	// Make sure that element opacity exists (as opposed to filter)
-	support.opacity = style.opacity === "0.5";
-
-	// Verify style float existence
-	// (IE uses styleFloat instead of cssFloat)
-	support.cssFloat = !!style.cssFloat;
-
-	div.style.backgroundClip = "content-box";
-	div.cloneNode( true ).style.backgroundClip = "";
-	support.clearCloneStyle = div.style.backgroundClip === "content-box";
-
-	// Support: Firefox<29, Android 2.3
-	// Vendor-prefix box-sizing
-	support.boxSizing = style.boxSizing === "" || style.MozBoxSizing === "" ||
-		style.WebkitBoxSizing === "";
-
-	jQuery.extend(support, {
-		reliableHiddenOffsets: function() {
-			if ( reliableHiddenOffsetsVal == null ) {
-				computeStyleTests();
-			}
-			return reliableHiddenOffsetsVal;
-		},
-
-		boxSizingReliable: function() {
-			if ( boxSizingReliableVal == null ) {
-				computeStyleTests();
-			}
-			return boxSizingReliableVal;
-		},
-
-		pixelPosition: function() {
-			if ( pixelPositionVal == null ) {
-				computeStyleTests();
-			}
-			return pixelPositionVal;
-		},
-
-		// Support: Android 2.3
-		reliableMarginRight: function() {
-			if ( reliableMarginRightVal == null ) {
-				computeStyleTests();
-			}
-			return reliableMarginRightVal;
-		}
-	});
-
-	function computeStyleTests() {
-		// Minified: var b,c,d,j
-		var div, body, container, contents;
-
-		body = document.getElementsByTagName( "body" )[ 0 ];
-		if ( !body || !body.style ) {
-			// Test fired too early or in an unsupported environment, exit.
-			return;
-		}
-
-		// Setup
-		div = document.createElement( "div" );
-		container = document.createElement( "div" );
-		container.style.cssText = "position:absolute;border:0;width:0;height:0;top:0;left:-9999px";
-		body.appendChild( container ).appendChild( div );
-
-		div.style.cssText =
-			// Support: Firefox<29, Android 2.3
-			// Vendor-prefix box-sizing
-			"-webkit-box-sizing:border-box;-moz-box-sizing:border-box;" +
-			"box-sizing:border-box;display:block;margin-top:1%;top:1%;" +
-			"border:1px;padding:1px;width:4px;position:absolute";
-
-		// Support: IE<9
-		// Assume reasonable values in the absence of getComputedStyle
-		pixelPositionVal = boxSizingReliableVal = false;
-		reliableMarginRightVal = true;
-
-		// Check for getComputedStyle so that this code is not run in IE<9.
-		if ( window.getComputedStyle ) {
-			pixelPositionVal = ( window.getComputedStyle( div, null ) || {} ).top !== "1%";
-			boxSizingReliableVal =
-				( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px";
-
-			// Support: Android 2.3
-			// Div with explicit width and no margin-right incorrectly
-			// gets computed margin-right based on width of container (#3333)
-			// WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
-			contents = div.appendChild( document.createElement( "div" ) );
-
-			// Reset CSS: box-sizing; display; margin; border; padding
-			contents.style.cssText = div.style.cssText =
-				// Support: Firefox<29, Android 2.3
-				// Vendor-prefix box-sizing
-				"-webkit-box-sizing:content-box;-moz-box-sizing:content-box;" +
-				"box-sizing:content-box;display:block;margin:0;border:0;padding:0";
-			contents.style.marginRight = contents.style.width = "0";
-			div.style.width = "1px";
-
-			reliableMarginRightVal =
-				!parseFloat( ( window.getComputedStyle( contents, null ) || {} ).marginRight );
-
-			div.removeChild( contents );
-		}
-
-		// 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>";
-		contents = div.getElementsByTagName( "td" );
-		contents[ 0 ].style.cssText = "margin:0;border:0;padding:0;display:none";
-		reliableHiddenOffsetsVal = contents[ 0 ].offsetHeight === 0;
-		if ( reliableHiddenOffsetsVal ) {
-			contents[ 0 ].style.display = "";
-			contents[ 1 ].style.display = "none";
-			reliableHiddenOffsetsVal = contents[ 0 ].offsetHeight === 0;
-		}
-
-		body.removeChild( container );
-	}
-
-})();
-
-
-// A method for quickly swapping in/out CSS properties to get correct calculations.
-jQuery.swap = function( elem, options, callback, args ) {
-	var ret, name,
-		old = {};
-
-	// Remember the old values, and insert the new ones
-	for ( name in options ) {
-		old[ name ] = elem.style[ name ];
-		elem.style[ name ] = options[ name ];
-	}
-
-	ret = callback.apply( elem, args || [] );
-
-	// Revert the old values
-	for ( name in options ) {
-		elem.style[ name ] = old[ name ];
-	}
-
-	return ret;
-};
-
-
-var
-		ralpha = /alpha\([^)]*\)/i,
-	ropacity = /opacity\s*=\s*([^)]*)/,
-
-	// swappable if display is none or starts with table except "table", "table-cell", or "table-caption"
-	// see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
-	rdisplayswap = /^(none|table(?!-c[ea]).+)/,
-	rnumsplit = new RegExp( "^(" + pnum + ")(.*)$", "i" ),
-	rrelNum = new RegExp( "^([+-])=(" + pnum + ")", "i" ),
-
-	cssShow = { position: "absolute", visibility: "hidden", display: "block" },
-	cssNormalTransform = {
-		letterSpacing: "0",
-		fontWeight: "400"
-	},
-
-	cssPrefixes = [ "Webkit", "O", "Moz", "ms" ];
-
-
-// return a css property mapped to a potentially vendor prefixed property
-function vendorPropName( style, name ) {
-
-	// shortcut for names that are not vendor prefixed
-	if ( name in style ) {
-		return name;
-	}
-
-	// check for vendor prefixed names
-	var capName = name.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 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", defaultDisplay(elem.nodeName) );
-			}
-		} else {
-			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;
-}
-
-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 = 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 && ( 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";
-}
-
-jQuery.extend({
-	// Add in style property hooks for overriding the default
-	// behavior of getting and setting a style property
-	cssHooks: {
-		opacity: {
-			get: function( elem, computed ) {
-				if ( computed ) {
-					// We should always get a number back from opacity
-					var ret = curCSS( elem, "opacity" );
-					return ret === "" ? "1" : ret;
-				}
-			}
-		}
-	},
-
-	// Don't automatically add "px" to these possibly-unitless properties
-	cssNumber: {
-		"columnCount": true,
-		"fillOpacity": true,
-		"flexGrow": true,
-		"flexShrink": true,
-		"fontWeight": true,
-		"lineHeight": true,
-		"opacity": true,
-		"order": true,
-		"orphans": true,
-		"widows": true,
-		"zIndex": true,
-		"zoom": true
-	},
-
-	// Add in properties whose names you wish to fix before
-	// setting or getting the value
-	cssProps: {
-		// normalize float css property
-		"float": 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 null and NaN values aren't set. See: #7116
-			if ( value == null || value !== 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 ( !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 ) {
-
-				// Support: IE
-				// Swallow errors from 'invalid' CSS values (#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;
-	}
-});
-
-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 rdisplayswap.test( jQuery.css( elem, "display" ) ) && elem.offsetWidth === 0 ?
-					jQuery.swap( elem, cssShow, function() {
-						return getWidthOrHeight( elem, name, extra );
-					}) :
-					getWidthOrHeight( elem, name, extra );
-			}
-		},
-
-		set: function( elem, value, extra ) {
-			var styles = extra && getStyles( elem );
-			return setPositiveNumber( elem, value, extra ?
-				augmentWidthOrHeight(
-					elem,
-					name,
-					extra,
-					support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
-					styles
-				) : 0
-			);
-		}
-	};
-});
-
-if ( !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;
-		}
-	};
-}
-
-jQuery.cssHooks.marginRight = addGetHookIf( support.reliableMarginRight,
-	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" ] );
-		}
-	}
-);
-
-// These hooks are used by animate to expand properties
-jQuery.each({
-	margin: "",
-	padding: "",
-	border: "Width"
-}, function( prefix, suffix ) {
-	jQuery.cssHooks[ prefix + suffix ] = {
-		expand: function( value ) {
-			var i = 0,
-				expanded = {},
-
-				// assumes a single number if not a string
-				parts = typeof value === "string" ? value.split(" ") : [ value ];
-
-			for ( ; i < 4; i++ ) {
-				expanded[ prefix + cssExpand[ i ] + suffix ] =
-					parts[ i ] || parts[ i - 2 ] || parts[ 0 ];
-			}
-
-			return expanded;
-		}
-	};
-
-	if ( !rmargin.test( prefix ) ) {
-		jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;
-	}
-});
-
-jQuery.fn.extend({
-	css: function( name, value ) {
-		return access( this, function( elem, name, value ) {
-			var styles, len,
-				map = {},
-				i = 0;
-
-			if ( jQuery.isArray( name ) ) {
-				styles = getStyles( elem );
-				len = name.length;
-
-				for ( ; i < len; i++ ) {
-					map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles );
-				}
-
-				return map;
-			}
-
-			return value !== undefined ?
-				jQuery.style( elem, name, value ) :
-				jQuery.css( elem, name );
-		}, name, value, arguments.length > 1 );
-	},
-	show: function() {
-		return showHide( this, true );
-	},
-	hide: function() {
-		return showHide( this );
-	},
-	toggle: function( state ) {
-		if ( typeof state === "boolean" ) {
-			return state ? this.show() : this.hide();
-		}
-
-		return this.each(function() {
-			if ( isHidden( this ) ) {
-				jQuery( this ).show();
-			} else {
-				jQuery( this ).hide();
-			}
-		});
-	}
-});
-
-
-function Tween( elem, options, prop, end, easing ) {
-	return new Tween.prototype.init( elem, options, prop, end, easing );
-}
-jQuery.Tween = Tween;
-
-Tween.prototype = {
-	constructor: Tween,
-	init: function( elem, options, prop, end, easing, unit ) {
-		this.elem = elem;
-		this.prop = prop;
-		this.easing = easing || "swing";
-		this.options = options;
-		this.start = this.now = this.cur();
-		this.end = end;
-		this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" );
-	},
-	cur: function() {
-		var hooks = Tween.propHooks[ this.prop ];
-
-		return hooks && hooks.get ?
-			hooks.get( this ) :
-			Tween.propHooks._default.get( this );
-	},
-	run: function( percent ) {
-		var eased,
-			hooks = Tween.propHooks[ this.prop ];
-
-		if ( this.options.duration ) {
-			this.pos = eased = jQuery.easing[ this.easing ](
-				percent, this.options.duration * percent, 0, 1, this.options.duration
-			);
-		} else {
-			this.pos = eased = percent;
-		}
-		this.now = ( this.end - this.start ) * eased + this.start;
-
-		if ( this.options.step ) {
-			this.options.step.call( this.elem, this.now, this );
-		}
-
-		if ( hooks && hooks.set ) {
-			hooks.set( this );
-		} else {
-			Tween.propHooks._default.set( this );
-		}
-		return this;
-	}
-};
-
-Tween.prototype.init.prototype = Tween.prototype;
-
-Tween.propHooks = {
-	_default: {
-		get: function( tween ) {
-			var result;
-
-			if ( tween.elem[ tween.prop ] != null &&
-				(!tween.elem.style || tween.elem.style[ tween.prop ] == null) ) {
-				return tween.elem[ tween.prop ];
-			}
-
-			// passing an empty string as a 3rd parameter to .css will automatically
-			// attempt a parseFloat and fallback to a string if the parse fails
-			// 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;
-			}
-		}
-	}
-};
-
-// Support: IE <=9
-// Panic based approach to setting things on disconnected nodes
-
-Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {
-	set: function( tween ) {
-		if ( tween.elem.nodeType && tween.elem.parentNode ) {
-			tween.elem[ tween.prop ] = tween.now;
-		}
-	}
-};
-
-jQuery.easing = {
-	linear: function( p ) {
-		return p;
-	},
-	swing: function( p ) {
-		return 0.5 - Math.cos( p * Math.PI ) / 2;
-	}
-};
-
-jQuery.fx = Tween.prototype.init;
-
-// Back Compat <1.8 extension point
-jQuery.fx.step = {};
-
-
-
-
-var
-	fxNow, timerId,
-	rfxtypes = /^(?:toggle|show|hide)$/,
-	rfxnum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ),
-	rrun = /queueHooks$/,
-	animationPrefilters = [ defaultPrefilter ],
-	tweeners = {
-		"*": [ function( prop, value ) {
-			var tween = this.createTween( prop, value ),
-				target = tween.cur(),
-				parts = rfxnum.exec( value ),
-				unit = parts && parts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ),
-
-				// Starting value computation is required for potential unit mismatches
-				start = ( jQuery.cssNumber[ prop ] || unit !== "px" && +target ) &&
-					rfxnum.exec( jQuery.css( tween.elem, prop ) ),
-				scale = 1,
-				maxIterations = 20;
-
-			if ( start && start[ 3 ] !== unit ) {
-				// Trust units reported by jQuery.css
-				unit = unit || start[ 3 ];
-
-				// Make sure we update the tween properties later on
-				parts = parts || [];
-
-				// Iteratively approximate from a nonzero starting point
-				start = +target || 1;
-
-				do {
-					// If previous iteration zeroed out, double until we get *something*
-					// Use 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 );
-			}
-
-			// Update tween properties
-			if ( parts ) {
-				start = tween.start = +start || +target || 0;
-				tween.unit = unit;
-				// If a +=/-= token was provided, we're doing a relative animation
-				tween.end = parts[ 1 ] ?
-					start + ( parts[ 1 ] + 1 ) * parts[ 2 ] :
-					+parts[ 2 ];
-			}
-
-			return tween;
-		} ]
-	};
-
-// Animations created synchronously will run synchronously
-function createFxNow() {
-	setTimeout(function() {
-		fxNow = undefined;
-	});
-	return ( fxNow = jQuery.now() );
-}
-
-// Generate parameters to create a standard animation
-function genFx( type, includeWidth ) {
-	var which,
-		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;
-}
-
-function createTween( value, prop, animation ) {
-	var tween,
-		collection = ( tweeners[ prop ] || [] ).concat( tweeners[ "*" ] ),
-		index = 0,
-		length = collection.length;
-	for ( ; index < length; index++ ) {
-		if ( (tween = collection[ index ].call( animation, prop, value )) ) {
-
-			// we're done with this property
-			return tween;
-		}
-	}
-}
-
-function defaultPrefilter( elem, props, opts ) {
-	/* jshint validthis: true */
-	var prop, value, toggle, tween, hooks, oldfire, display, checkDisplay,
-		anim = this,
-		orig = {},
-		style = elem.style,
-		hidden = elem.nodeType && isHidden( elem ),
-		dataShow = jQuery._data( elem, "fxshow" );
-
-	// handle queue: false promises
-	if ( !opts.queue ) {
-		hooks = jQuery._queueHooks( elem, "fx" );
-		if ( hooks.unqueued == null ) {
-			hooks.unqueued = 0;
-			oldfire = hooks.empty.fire;
-			hooks.empty.fire = function() {
-				if ( !hooks.unqueued ) {
-					oldfire();
-				}
-			};
-		}
-		hooks.unqueued++;
-
-		anim.always(function() {
-			// 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
-		display = jQuery.css( elem, "display" );
-
-		// Test default display if display is currently "none"
-		checkDisplay = display === "none" ?
-			jQuery._data( elem, "olddisplay" ) || defaultDisplay( elem.nodeName ) : display;
-
-		if ( checkDisplay === "inline" && jQuery.css( elem, "float" ) === "none" ) {
-
-			// inline-level elements accept inline-block;
-			// block-level elements need to be inline with layout
-			if ( !support.inlineBlockNeedsLayout || defaultDisplay( elem.nodeName ) === "inline" ) {
-				style.display = "inline-block";
-			} else {
-				style.zoom = 1;
-			}
-		}
-	}
-
-	if ( opts.overflow ) {
-		style.overflow = "hidden";
-		if ( !support.shrinkWrapBlocks() ) {
-			anim.always(function() {
-				style.overflow = opts.overflow[ 0 ];
-				style.overflowX = opts.overflow[ 1 ];
-				style.overflowY = opts.overflow[ 2 ];
-			});
-		}
-	}
-
-	// show/hide pass
-	for ( prop in props ) {
-		value = props[ prop ];
-		if ( rfxtypes.exec( value ) ) {
-			delete props[ prop ];
-			toggle = toggle || value === "toggle";
-			if ( value === ( hidden ? "hide" : "show" ) ) {
-
-				// If there is dataShow left over from a stopped hide or show and we are going to proceed with show, we should pretend to be hidden
-				if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) {
-					hidden = true;
-				} else {
-					continue;
-				}
-			}
-			orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop );
-
-		// Any non-fx value stops us from restoring the original display value
-		} else {
-			display = undefined;
-		}
-	}
-
-	if ( !jQuery.isEmptyObject( orig ) ) {
-		if ( dataShow ) {
-			if ( "hidden" in dataShow ) {
-				hidden = dataShow.hidden;
-			}
-		} else {
-			dataShow = jQuery._data( elem, "fxshow", {} );
-		}
-
-		// store state if its toggle - enables .stop().toggle() to "reverse"
-		if ( toggle ) {
-			dataShow.hidden = !hidden;
-		}
-		if ( hidden ) {
-			jQuery( elem ).show();
-		} else {
-			anim.done(function() {
-				jQuery( elem ).hide();
-			});
-		}
-		anim.done(function() {
-			var prop;
-			jQuery._removeData( elem, "fxshow" );
-			for ( prop in orig ) {
-				jQuery.style( elem, prop, orig[ prop ] );
-			}
-		});
-		for ( prop in orig ) {
-			tween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim );
-
-			if ( !( prop in dataShow ) ) {
-				dataShow[ prop ] = tween.start;
-				if ( hidden ) {
-					tween.end = tween.start;
-					tween.start = prop === "width" || prop === "height" ? 1 : 0;
-				}
-			}
-		}
-
-	// If this is a noop like .hide().hide(), restore an overwritten display value
-	} else if ( (display === "none" ? defaultDisplay( elem.nodeName ) : display) === "inline" ) {
-		style.display = display;
-	}
-}
-
-function propFilter( props, specialEasing ) {
-	var index, name, easing, value, hooks;
-
-	// camelCase, specialEasing and expand cssHook pass
-	for ( index in props ) {
-		name = jQuery.camelCase( index );
-		easing = specialEasing[ name ];
-		value = props[ index ];
-		if ( jQuery.isArray( value ) ) {
-			easing = value[ 1 ];
-			value = props[ index ] = value[ 0 ];
-		}
-
-		if ( index !== name ) {
-			props[ name ] = value;
-			delete props[ index ];
-		}
-
-		hooks = jQuery.cssHooks[ name ];
-		if ( hooks && "expand" in hooks ) {
-			value = hooks.expand( value );
-			delete props[ name ];
-
-			// not quite $.extend, this 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;
-		}
-	}
-}
-
-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;
-		}
-	}
-
-	jQuery.map( props, createTween, animation );
-
-	if ( jQuery.isFunction( animation.opts.start ) ) {
-		animation.opts.start.call( elem, animation );
-	}
-
-	jQuery.fx.timer(
-		jQuery.extend( tick, {
-			elem: elem,
-			anim: animation,
-			queue: animation.opts.queue
-		})
-	);
-
-	// attach callbacks from options
-	return animation.progress( animation.opts.progress )
-		.done( animation.opts.done, animation.opts.complete )
-		.fail( animation.opts.fail )
-		.always( animation.opts.always );
-}
-
-jQuery.Animation = jQuery.extend( Animation, {
-	tweener: function( props, callback ) {
-		if ( jQuery.isFunction( props ) ) {
-			callback = props;
-			props = [ "*" ];
-		} else {
-			props = props.split(" ");
-		}
-
-		var prop,
-			index = 0,
-			length = props.length;
-
-		for ( ; index < length ; index++ ) {
-			prop = props[ index ];
-			tweeners[ prop ] = tweeners[ prop ] || [];
-			tweeners[ prop ].unshift( callback );
-		}
-	},
-
-	prefilter: function( callback, prepend ) {
-		if ( prepend ) {
-			animationPrefilters.unshift( callback );
-		} else {
-			animationPrefilters.push( callback );
-		}
-	}
-});
-
-jQuery.speed = function( speed, easing, fn ) {
-	var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
-		complete: fn || !fn && easing ||
-			jQuery.isFunction( speed ) && speed,
-		duration: speed,
-		easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
-	};
-
-	opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
-		opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;
-
-	// normalize opt.queue - true/undefined/null -> "fx"
-	if ( opt.queue == null || opt.queue === true ) {
-		opt.queue = "fx";
-	}
-
-	// Queueing
-	opt.old = opt.complete;
-
-	opt.complete = function() {
-		if ( jQuery.isFunction( opt.old ) ) {
-			opt.old.call( this );
-		}
-
-		if ( opt.queue ) {
-			jQuery.dequeue( this, opt.queue );
-		}
-	};
-
-	return opt;
-};
-
-jQuery.fn.extend({
-	fadeTo: function( speed, to, easing, callback ) {
-
-		// show any hidden elements after setting opacity to 0
-		return this.filter( isHidden ).css( "opacity", 0 ).show()
-
-			// animate to the value specified
-			.end().animate({ opacity: to }, speed, easing, callback );
-	},
-	animate: function( prop, speed, easing, callback ) {
-		var empty = jQuery.isEmptyObject( prop ),
-			optall = jQuery.speed( speed, easing, callback ),
-			doAnimation = function() {
-				// Operate on a copy of prop so per-property easing won't be lost
-				var anim = Animation( this, jQuery.extend( {}, prop ), optall );
-
-				// Empty animations, or finishing resolves immediately
-				if ( empty || 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.stop ) {
-				hooks.stop.call( this, true );
-			}
-
-			// look for any active animations, and finish them
-			for ( index = timers.length; index--; ) {
-				if ( timers[ index ].elem === this && timers[ index ].queue === type ) {
-					timers[ index ].anim.stop( true );
-					timers.splice( index, 1 );
-				}
-			}
-
-			// look for any animations in the old queue and finish them
-			for ( index = 0; index < length; index++ ) {
-				if ( queue[ index ] && queue[ index ].finish ) {
-					queue[ index ].finish.call( this );
-				}
-			}
-
-			// turn off finishing flag
-			delete data.finish;
-		});
-	}
-});
-
-jQuery.each([ "toggle", "show", "hide" ], function( i, name ) {
-	var cssFn = jQuery.fn[ name ];
-	jQuery.fn[ name ] = function( speed, easing, callback ) {
-		return speed == null || typeof speed === "boolean" ?
-			cssFn.apply( this, arguments ) :
-			this.animate( genFx( name, true ), speed, easing, callback );
-	};
-});
-
-// Generate shortcuts for custom animations
-jQuery.each({
-	slideDown: genFx("show"),
-	slideUp: genFx("hide"),
-	slideToggle: genFx("toggle"),
-	fadeIn: { opacity: "show" },
-	fadeOut: { opacity: "hide" },
-	fadeToggle: { opacity: "toggle" }
-}, function( name, props ) {
-	jQuery.fn[ name ] = function( speed, easing, callback ) {
-		return this.animate( props, speed, easing, callback );
-	};
-});
-
-jQuery.timers = [];
-jQuery.fx.tick = function() {
-	var timer,
-		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 ) {
-	jQuery.timers.push( timer );
-	if ( timer() ) {
-		jQuery.fx.start();
-	} else {
-		jQuery.timers.pop();
-	}
-};
-
-jQuery.fx.interval = 13;
-
-jQuery.fx.start = function() {
-	if ( !timerId ) {
-		timerId = setInterval( jQuery.fx.tick, jQuery.fx.interval );
-	}
-};
-
-jQuery.fx.stop = function() {
-	clearInterval( timerId );
-	timerId = null;
-};
-
-jQuery.fx.speeds = {
-	slow: 600,
-	fast: 200,
-	// Default speed
-	_default: 400
-};
-
-
-// Based off of the plugin by Clint Helfers, with permission.
-// http://blindsignals.com/index.php/2009/07/jquery-delay/
-jQuery.fn.delay = function( time, type ) {
-	time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
-	type = type || "fx";
-
-	return this.queue( type, function( next, hooks ) {
-		var timeout = setTimeout( next, time );
-		hooks.stop = function() {
-			clearTimeout( timeout );
-		};
-	});
-};
-
-
-(function() {
-	// Minified: var a,b,c,d,e
-	var input, div, select, a, opt;
-
-	// Setup
-	div = document.createElement( "div" );
-	div.setAttribute( "className", "t" );
-	div.innerHTML = "  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>";
-	a = div.getElementsByTagName("a")[ 0 ];
-
-	// First batch of tests.
-	select = document.createElement("select");
-	opt = select.appendChild( document.createElement("option") );
-	input = div.getElementsByTagName("input")[ 0 ];
-
-	a.style.cssText = "top:1px";
-
-	// Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7)
-	support.getSetAttribute = div.className !== "t";
-
-	// Get the style information from getAttribute
-	// (IE uses .cssText instead)
-	support.style = /top/.test( a.getAttribute("style") );
-
-	// Make sure that URLs aren't manipulated
-	// (IE normalizes it by default)
-	support.hrefNormalized = a.getAttribute("href") === "/a";
-
-	// Check the default checkbox/radio value ("" on WebKit; "on" elsewhere)
-	support.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)
-	support.optSelected = opt.selected;
-
-	// Tests for enctype support on a form (#6743)
-	support.enctype = !!document.createElement("form").enctype;
-
-	// 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: IE8 only
-	// 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";
-})();
-
-
-var rreturn = /\r/g;
-
-jQuery.fn.extend({
-	val: function( value ) {
-		var hooks, ret, isFunction,
-			elem = this[0];
-
-		if ( !arguments.length ) {
-			if ( elem ) {
-				hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];
-
-				if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
-					return ret;
-				}
-
-				ret = elem.value;
-
-				return typeof ret === "string" ?
-					// handle most common string cases
-					ret.replace(rreturn, "") :
-					// handle cases where value is null/undef or number
-					ret == null ? "" : ret;
-			}
-
-			return;
-		}
-
-		isFunction = jQuery.isFunction( value );
-
-		return this.each(function( i ) {
-			var val;
-
-			if ( this.nodeType !== 1 ) {
-				return;
-			}
-
-			if ( isFunction ) {
-				val = value.call( this, i, jQuery( this ).val() );
-			} else {
-				val = value;
-			}
-
-			// Treat null/undefined as ""; convert numbers to string
-			if ( val == null ) {
-				val = "";
-			} else if ( typeof val === "number" ) {
-				val += "";
-			} else if ( jQuery.isArray( val ) ) {
-				val = jQuery.map( val, function( value ) {
-					return value == null ? "" : value + "";
-				});
-			}
-
-			hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];
-
-			// If set returns undefined, fall back to normal setting
-			if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
-				this.value = val;
-			}
-		});
-	}
-});
-
-jQuery.extend({
-	valHooks: {
-		option: {
-			get: function( elem ) {
-				var val = jQuery.find.attr( elem, "value" );
-				return val != null ?
-					val :
-					// Support: IE10-11+
-					// option.text throws exceptions (#14686, #14858)
-					jQuery.trim( jQuery.text( elem ) );
-			}
-		},
-		select: {
-			get: function( elem ) {
-				var value, option,
-					options = elem.options,
-					index = elem.selectedIndex,
-					one = elem.type === "select-one" || index < 0,
-					values = one ? null : [],
-					max = one ? index + 1 : options.length,
-					i = index < 0 ?
-						max :
-						one ? index : 0;
-
-				// Loop through all the selected options
-				for ( ; i < max; i++ ) {
-					option = options[ i ];
-
-					// 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
-							( support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) &&
-							( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) {
-
-						// Get the specific value for the option
-						value = jQuery( option ).val();
-
-						// We don't need an array for one selects
-						if ( one ) {
-							return value;
-						}
-
-						// Multi-Selects return an array
-						values.push( value );
-					}
-				}
-
-				return values;
-			},
-
-			set: function( elem, value ) {
-				var optionSet, option,
-					options = elem.options,
-					values = jQuery.makeArray( value ),
-					i = options.length;
-
-				while ( i-- ) {
-					option = options[ i ];
-
-					if ( jQuery.inArray( jQuery.valHooks.option.get( option ), values ) >= 0 ) {
-
-						// Support: IE6
-						// When new option element is added to select box we need to
-						// force reflow of newly added node in order to workaround delay
-						// of initialization properties
-						try {
-							option.selected = optionSet = true;
-
-						} catch ( _ ) {
-
-							// Will be executed only in IE6
-							option.scrollHeight;
-						}
-
-					} else {
-						option.selected = false;
-					}
-				}
-
-				// Force browsers to behave consistently when non-matching value is set
-				if ( !optionSet ) {
-					elem.selectedIndex = -1;
-				}
-
-				return options;
-			}
-		}
-	}
-});
-
-// Radios and checkboxes getter/setter
-jQuery.each([ "radio", "checkbox" ], function() {
-	jQuery.valHooks[ this ] = {
-		set: function( elem, value ) {
-			if ( jQuery.isArray( value ) ) {
-				return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );
-			}
-		}
-	};
-	if ( !support.checkOn ) {
-		jQuery.valHooks[ this ].get = function( elem ) {
-			// Support: Webkit
-			// "" is returned instead of "on" if a value isn't specified
-			return elem.getAttribute("value") === null ? "on" : elem.value;
-		};
-	}
-});
-
-
-
-
-var nodeHook, boolHook,
-	attrHandle = jQuery.expr.attrHandle,
-	ruseDefault = /^(?:checked|selected)$/i,
-	getSetAttribute = support.getSetAttribute,
-	getSetInput = support.input;
-
-jQuery.fn.extend({
-	attr: function( name, value ) {
-		return access( this, jQuery.attr, name, value, arguments.length > 1 );
-	},
-
-	removeAttr: function( name ) {
-		return this.each(function() {
-			jQuery.removeAttr( this, name );
-		});
-	}
-});
-
-jQuery.extend({
-	attr: function( elem, name, value ) {
-		var hooks, ret,
-			nType = elem.nodeType;
-
-		// don't get/set attributes on text, comment and attribute nodes
-		if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
-			return;
-		}
-
-		// Fallback to prop when attributes are not supported
-		if ( typeof elem.getAttribute === strundefined ) {
-			return jQuery.prop( elem, name, value );
-		}
-
-		// All attributes are lowercase
-		// Grab necessary hook if one is defined
-		if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
-			name = name.toLowerCase();
-			hooks = jQuery.attrHooks[ name ] ||
-				( jQuery.expr.match.bool.test( name ) ? boolHook : nodeHook );
-		}
-
-		if ( value !== undefined ) {
-
-			if ( value === null ) {
-				jQuery.removeAttr( elem, name );
-
-			} else if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
-				return ret;
-
-			} else {
-				elem.setAttribute( name, value + "" );
-				return value;
-			}
-
-		} else if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
-			return ret;
-
-		} else {
-			ret = jQuery.find.attr( elem, name );
-
-			// Non-existent attributes return null, we normalize to undefined
-			return ret == null ?
-				undefined :
-				ret;
-		}
-	},
-
-	removeAttr: function( elem, value ) {
-		var name, propName,
-			i = 0,
-			attrNames = value && value.match( rnotwhite );
-
-		if ( attrNames && elem.nodeType === 1 ) {
-			while ( (name = attrNames[i++]) ) {
-				propName = jQuery.propFix[ name ] || name;
-
-				// Boolean attributes get special treatment (#10870)
-				if ( jQuery.expr.match.bool.test( name ) ) {
-					// Set corresponding property to false
-					if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) {
-						elem[ propName ] = false;
-					// Support: IE<9
-					// Also clear defaultChecked/defaultSelected (if appropriate)
-					} else {
-						elem[ jQuery.camelCase( "default-" + name ) ] =
-							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 ( !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;
-				}
-			}
-		}
-	}
-});
-
-// Hook for boolean attributes
-boolHook = {
-	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;
-	}
-};
-
-// Retrieve booleans specially
-jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) {
-
-	var getter = attrHandle[ name ] || jQuery.find.attr;
-
-	attrHandle[ name ] = getSetInput && getSetAttribute || !ruseDefault.test( name ) ?
-		function( elem, name, isXML ) {
-			var ret, handle;
-			if ( !isXML ) {
-				// Avoid an infinite loop by temporarily removing this function from the getter
-				handle = attrHandle[ name ];
-				attrHandle[ name ] = ret;
-				ret = getter( elem, name, isXML ) != null ?
-					name.toLowerCase() :
-					null;
-				attrHandle[ name ] = handle;
-			}
-			return ret;
-		} :
-		function( elem, name, isXML ) {
-			if ( !isXML ) {
-				return elem[ jQuery.camelCase( "default-" + name ) ] ?
-					name.toLowerCase() :
-					null;
-			}
-		};
-});
-
-// fix oldIE attroperties
-if ( !getSetInput || !getSetAttribute ) {
-	jQuery.attrHooks.value = {
-		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 = {
-		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)
-			if ( name === "value" || value === elem.getAttribute( name ) ) {
-				return value;
-			}
-		}
-	};
-
-	// Some attributes are constructed with empty-string values when not defined
-	attrHandle.id = attrHandle.name = attrHandle.coords =
-		function( elem, name, isXML ) {
-			var ret;
-			if ( !isXML ) {
-				return (ret = elem.getAttributeNode( name )) && ret.value !== "" ?
-					ret.value :
-					null;
-			}
-		};
-
-	// Fixing value retrieval on a button requires this module
-	jQuery.valHooks.button = {
-		get: function( elem, name ) {
-			var ret = elem.getAttributeNode( name );
-			if ( ret && ret.specified ) {
-				return ret.value;
-			}
-		},
-		set: nodeHook.set
-	};
-
-	// Set contenteditable to false on removals(#10429)
-	// Setting to empty string throws an error as an invalid value
-	jQuery.attrHooks.contenteditable = {
-		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 ] = {
-			set: function( elem, value ) {
-				if ( value === "" ) {
-					elem.setAttribute( name, "auto" );
-					return value;
-				}
-			}
-		};
-	});
-}
-
-if ( !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 + "" );
-		}
-	};
-}
-
-
-
-
-var rfocusable = /^(?:input|select|textarea|button|object)$/i,
-	rclickable = /^(?:a|area)$/i;
-
-jQuery.fn.extend({
-	prop: function( name, value ) {
-		return access( this, jQuery.prop, name, value, arguments.length > 1 );
-	},
-
-	removeProp: function( name ) {
-		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 ) {}
-		});
-	}
-});
-
-jQuery.extend({
-	propFix: {
-		"for": "htmlFor",
-		"class": "className"
-	},
-
-	prop: function( elem, name, value ) {
-		var ret, hooks, notxml,
-			nType = elem.nodeType;
-
-		// don't get/set properties on text, comment and attribute nodes
-		if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
-			return;
-		}
-
-		notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
-
-		if ( notxml ) {
-			// Fix name and attach hooks
-			name = jQuery.propFix[ name ] || name;
-			hooks = jQuery.propHooks[ name ];
-		}
-
-		if ( value !== undefined ) {
-			return hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ?
-				ret :
-				( elem[ name ] = value );
-
-		} else {
-			return hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ?
-				ret :
-				elem[ name ];
-		}
-	},
-
-	propHooks: {
-		tabIndex: {
-			get: function( elem ) {
-				// 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/
-				// Use proper attribute retrieval(#12072)
-				var tabindex = jQuery.find.attr( elem, "tabindex" );
-
-				return tabindex ?
-					parseInt( tabindex, 10 ) :
-					rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
-						0 :
-						-1;
-			}
-		}
-	}
-});
-
-// Some attributes require a special call on IE
-// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
-if ( !support.hrefNormalized ) {
-	// 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 );
-			}
-		};
-	});
-}
-
-// Support: Safari, IE9+
-// mis-reports the default selected property of an option
-// Accessing the parent's selectedIndex property fixes it
-if ( !support.optSelected ) {
-	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;
-		}
-	};
-}
-
-jQuery.each([
-	"tabIndex",
-	"readOnly",
-	"maxLength",
-	"cellSpacing",
-	"cellPadding",
-	"rowSpan",
-	"colSpan",
-	"useMap",
-	"frameBorder",
-	"contentEditable"
-], function() {
-	jQuery.propFix[ this.toLowerCase() ] = this;
-});
-
-// IE6/7 call enctype encoding
-if ( !support.enctype ) {
-	jQuery.propFix.enctype = "encoding";
-}
-
-
-
-
-var rclass = /[\t\r\n\f]/g;
-
-jQuery.fn.extend({
-	addClass: function( value ) {
-		var classes, elem, cur, clazz, j, finalValue,
-			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( rnotwhite ) || [];
-
-			for ( ; i < len; i++ ) {
-				elem = this[ i ];
-				cur = elem.nodeType === 1 && ( elem.className ?
-					( " " + elem.className + " " ).replace( rclass, " " ) :
-					" "
-				);
-
-				if ( cur ) {
-					j = 0;
-					while ( (clazz = classes[j++]) ) {
-						if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
-							cur += clazz + " ";
-						}
-					}
-
-					// only assign if different to avoid unneeded rendering.
-					finalValue = jQuery.trim( cur );
-					if ( elem.className !== finalValue ) {
-						elem.className = finalValue;
-					}
-				}
-			}
-		}
-
-		return this;
-	},
-
-	removeClass: function( value ) {
-		var classes, elem, cur, clazz, j, finalValue,
-			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( rnotwhite ) || [];
-
-			for ( ; i < len; i++ ) {
-				elem = this[ i ];
-				// This expression is here for better compressibility (see addClass)
-				cur = elem.nodeType === 1 && ( elem.className ?
-					( " " + elem.className + " " ).replace( rclass, " " ) :
-					""
-				);
-
-				if ( cur ) {
-					j = 0;
-					while ( (clazz = classes[j++]) ) {
-						// Remove *all* instances
-						while ( cur.indexOf( " " + clazz + " " ) >= 0 ) {
-							cur = cur.replace( " " + clazz + " ", " " );
-						}
-					}
-
-					// only assign if different to avoid unneeded rendering.
-					finalValue = value ? jQuery.trim( cur ) : "";
-					if ( elem.className !== finalValue ) {
-						elem.className = finalValue;
-					}
-				}
-			}
-		}
-
-		return this;
-	},
-
-	toggleClass: function( value, stateVal ) {
-		var type = typeof value;
-
-		if ( typeof stateVal === "boolean" && type === "string" ) {
-			return stateVal ? this.addClass( value ) : this.removeClass( value );
-		}
-
-		if ( jQuery.isFunction( value ) ) {
-			return this.each(function( i ) {
-				jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );
-			});
-		}
-
-		return this.each(function() {
-			if ( type === "string" ) {
-				// toggle individual class names
-				var className,
-					i = 0,
-					self = jQuery( this ),
-					classNames = value.match( rnotwhite ) || [];
-
-				while ( (className = classNames[ i++ ]) ) {
-					// check each className given, space separated list
-					if ( self.hasClass( className ) ) {
-						self.removeClass( className );
-					} else {
-						self.addClass( className );
-					}
-				}
-
-			// Toggle whole class name
-			} else if ( type === strundefined || type === "boolean" ) {
-				if ( this.className ) {
-					// store className if set
-					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;
-	}
-});
-
-
-
-
-// Return jQuery for attributes-only inclusion
-
-
-jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
-	"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
-	"change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) {
-
-	// Handle event binding
-	jQuery.fn[ name ] = function( data, fn ) {
-		return arguments.length > 0 ?
-			this.on( name, null, data, fn ) :
-			this.trigger( name );
-	};
-});
-
-jQuery.fn.extend({
-	hover: function( fnOver, fnOut ) {
-		return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
-	},
-
-	bind: function( types, data, fn ) {
-		return this.on( types, null, data, fn );
-	},
-	unbind: function( types, fn ) {
-		return this.off( types, null, fn );
-	},
-
-	delegate: function( selector, types, data, fn ) {
-		return this.on( types, selector, data, fn );
-	},
-	undelegate: function( selector, types, fn ) {
-		// ( namespace ) or ( selector, types [, fn] )
-		return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn );
-	}
-});
-
-
-var nonce = jQuery.now();
-
-var rquery = (/\?/);
-
-
-
-var rvalidtokens = /(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;
-
-jQuery.parseJSON = function( data ) {
-	// Attempt to parse using the native JSON parser first
-	if ( window.JSON && window.JSON.parse ) {
-		// Support: Android 2.3
-		// Workaround failure to string-cast null input
-		return window.JSON.parse( data + "" );
-	}
-
-	var requireNonComma,
-		depth = null,
-		str = jQuery.trim( data + "" );
-
-	// Guard against invalid (and possibly dangerous) input by ensuring that nothing remains
-	// after removing valid tokens
-	return str && !jQuery.trim( str.replace( rvalidtokens, function( token, comma, open, close ) {
-
-		// Force termination if we see a misplaced comma
-		if ( requireNonComma && comma ) {
-			depth = 0;
-		}
-
-		// Perform no more replacements after returning to outermost depth
-		if ( depth === 0 ) {
-			return token;
-		}
-
-		// Commas must not follow "[", "{", or ","
-		requireNonComma = open || comma;
-
-		// Determine new depth
-		// array/object open ("[" or "{"): depth += true - false (increment)
-		// array/object close ("]" or "}"): depth += false - true (decrement)
-		// other cases ("," or primitive): depth += true - true (numeric cast)
-		depth += !close - !open;
-
-		// Remove this token
-		return "";
-	}) ) ?
-		( Function( "return " + str ) )() :
-		jQuery.error( "Invalid JSON: " + data );
-};
-
-
-// Cross-browser xml parsing
-jQuery.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;
-};
-
-
-var
-	// Document location
-	ajaxLocParts,
-	ajaxLocation,
-
-	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+)|)|)/,
-
-	/* 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( rnotwhite ) || [];
-
-		if ( jQuery.isFunction( func ) ) {
-			// For each dataType in the dataTypeExpression
-			while ( (dataType = dataTypes[i++]) ) {
-				// Prepend if requested
-				if ( dataType.charAt( 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;
-}
-
-/* Handles responses to an ajax request:
- * - finds the right dataType (mediates between content-type and expected dataType)
- * - returns the corresponding response
- */
-function ajaxHandleResponses( s, jqXHR, responses ) {
-	var firstDataType, ct, finalDataType, type,
-		contents = s.contents,
-		dataTypes = s.dataTypes;
-
-	// Remove auto dataType and get content-type in the process
-	while ( dataTypes[ 0 ] === "*" ) {
-		dataTypes.shift();
-		if ( ct === undefined ) {
-			ct = s.mimeType || jqXHR.getResponseHeader("Content-Type");
-		}
-	}
-
-	// Check if we're dealing with a known content-type
-	if ( ct ) {
-		for ( type in contents ) {
-			if ( contents[ type ] && contents[ type ].test( ct ) ) {
-				dataTypes.unshift( type );
-				break;
-			}
-		}
-	}
-
-	// Check to see if we have a response for the expected dataType
-	if ( dataTypes[ 0 ] in responses ) {
-		finalDataType = dataTypes[ 0 ];
-	} else {
-		// Try convertible dataTypes
-		for ( type in responses ) {
-			if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) {
-				finalDataType = type;
-				break;
-			}
-			if ( !firstDataType ) {
-				firstDataType = type;
-			}
-		}
-		// Or just use first one
-		finalDataType = finalDataType || firstDataType;
-	}
-
-	// If we found a dataType
-	// We add the dataType to the list if needed
-	// and return the corresponding response
-	if ( finalDataType ) {
-		if ( finalDataType !== dataTypes[ 0 ] ) {
-			dataTypes.unshift( finalDataType );
-		}
-		return responses[ finalDataType ];
-	}
-}
-
-/* Chain conversions given the request and the original response
- * Also sets the responseXXX fields on the jqXHR instance
- */
-function ajaxConvert( s, response, jqXHR, isSuccess ) {
-	var conv2, current, conv, tmp, prev,
-		converters = {},
-		// Work with a copy of dataTypes in case we need to modify it for conversion
-		dataTypes = s.dataTypes.slice();
-
-	// Create converters map with lowercased keys
-	if ( dataTypes[ 1 ] ) {
-		for ( conv in s.converters ) {
-			converters[ conv.toLowerCase() ] = s.converters[ conv ];
-		}
-	}
-
-	current = dataTypes.shift();
-
-	// Convert to each sequential dataType
-	while ( current ) {
-
-		if ( s.responseFields[ current ] ) {
-			jqXHR[ s.responseFields[ current ] ] = response;
-		}
-
-		// Apply the dataFilter if provided
-		if ( !prev && isSuccess && s.dataFilter ) {
-			response = s.dataFilter( response, s.dataType );
-		}
-
-		prev = current;
-		current = dataTypes.shift();
-
-		if ( current ) {
-
-			// There's only work to do if current dataType is non-auto
-			if ( current === "*" ) {
-
-				current = prev;
-
-			// Convert response if prev dataType is non-auto and differs from current
-			} else if ( prev !== "*" && prev !== current ) {
-
-				// Seek a direct converter
-				conv = converters[ prev + " " + current ] || converters[ "* " + current ];
-
-				// If none found, seek a pair
-				if ( !conv ) {
-					for ( conv2 in converters ) {
-
-						// If conv2 outputs current
-						tmp = conv2.split( " " );
-						if ( tmp[ 1 ] === current ) {
-
-							// If prev can be converted to accepted input
-							conv = converters[ prev + " " + tmp[ 0 ] ] ||
-								converters[ "* " + tmp[ 0 ] ];
-							if ( conv ) {
-								// Condense equivalence converters
-								if ( conv === true ) {
-									conv = converters[ conv2 ];
-
-								// Otherwise, insert the intermediate dataType
-								} else if ( converters[ conv2 ] !== true ) {
-									current = tmp[ 0 ];
-									dataTypes.unshift( tmp[ 1 ] );
-								}
-								break;
-							}
-						}
-					}
-				}
-
-				// Apply converter (if not an equivalence)
-				if ( conv !== true ) {
-
-					// Unless errors are allowed to bubble, catch and return them
-					if ( conv && s[ "throws" ] ) {
-						response = conv( response );
-					} else {
-						try {
-							response = conv( response );
-						} catch ( e ) {
-							return { state: "parsererror", error: conv ? e : "No conversion from " + prev + " to " + current };
-						}
-					}
-				}
-			}
-		}
-	}
-
-	return { state: "success", data: response };
-}
-
-jQuery.extend({
-
-	// Counter for holding the number of active queries
-	active: 0,
-
-	// Last-Modified header cache for next request
-	lastModified: {},
-	etag: {},
-
-	ajaxSettings: {
-		url: ajaxLocation,
-		type: "GET",
-		isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ),
-		global: true,
-		processData: true,
-		async: true,
-		contentType: "application/x-www-form-urlencoded; charset=UTF-8",
-		/*
-		timeout: 0,
-		data: null,
-		dataType: null,
-		username: null,
-		password: null,
-		cache: null,
-		throws: false,
-		traditional: false,
-		headers: {},
-		*/
-
-		accepts: {
-			"*": allTypes,
-			text: "text/plain",
-			html: "text/html",
-			xml: "application/xml, text/xml",
-			json: "application/json, text/javascript"
-		},
-
-		contents: {
-			xml: /xml/,
-			html: /html/,
-			json: /json/
-		},
-
-		responseFields: {
-			xml: "responseXML",
-			text: "responseText",
-			json: "responseJSON"
-		},
-
-		// Data converters
-		// Keys separate source (or catchall "*") and destination types with a single space
-		converters: {
-
-			// Convert anything to text
-			"* text": String,
-
-			// Text to html (true = no transformation)
-			"text html": true,
-
-			// Evaluate text as a json expression
-			"text json": jQuery.parseJSON,
-
-			// Parse text as xml
-			"text xml": jQuery.parseXML
-		},
-
-		// For options that shouldn't be deep extended:
-		// you can add your own custom options here if
-		// and when you create one that shouldn't be
-		// deep extended (see ajaxExtend)
-		flatOptions: {
-			url: true,
-			context: true
-		}
-	},
-
-	// Creates a full fledged settings object into target
-	// with both ajaxSettings and settings fields.
-	// If target is omitted, writes into ajaxSettings.
-	ajaxSetup: function( target, settings ) {
-		return settings ?
-
-			// Building a settings object
-			ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) :
-
-			// Extending ajaxSettings
-			ajaxExtend( jQuery.ajaxSettings, target );
-	},
-
-	ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
-	ajaxTransport: addToPrefiltersOrTransports( transports ),
-
-	// Main method
-	ajax: function( url, options ) {
-
-		// If url is an object, simulate pre-1.5 signature
-		if ( typeof url === "object" ) {
-			options = url;
-			url = undefined;
-		}
-
-		// Force options to be an object
-		options = options || {};
-
-		var // 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( rnotwhite ) || [ "" ];
-
-		// A cross-domain request is in order when we have a protocol:host:port mismatch
-		if ( s.crossDomain == null ) {
-			parts = rurl.exec( s.url.toLowerCase() );
-			s.crossDomain = !!( parts &&
-				( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] ||
-					( parts[ 3 ] || ( parts[ 1 ] === "http:" ? "80" : "443" ) ) !==
-						( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? "80" : "443" ) ) )
-			);
-		}
-
-		// Convert data if not already a string
-		if ( s.data && s.processData && typeof s.data !== "string" ) {
-			s.data = jQuery.param( s.data, s.traditional );
-		}
-
-		// Apply prefilters
-		inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
-
-		// If request was aborted inside a prefilter, stop there
-		if ( state === 2 ) {
-			return jqXHR;
-		}
-
-		// We can fire global events as of now if asked to
-		// Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118)
-		fireGlobals = jQuery.event && s.global;
-
-		// Watch for a new set of requests
-		if ( fireGlobals && jQuery.active++ === 0 ) {
-			jQuery.event.trigger("ajaxStart");
-		}
-
-		// Uppercase the type
-		s.type = s.type.toUpperCase();
-
-		// Determine if request has content
-		s.hasContent = !rnoContent.test( s.type );
-
-		// Save the URL in case we're toying with the If-Modified-Since
-		// and/or If-None-Match header later on
-		cacheURL = s.url;
-
-		// More options handling for requests with no content
-		if ( !s.hasContent ) {
-
-			// If data is available, append data to url
-			if ( s.data ) {
-				cacheURL = ( s.url += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data );
-				// #9682: remove data so that it's not used in an eventual retry
-				delete s.data;
-			}
-
-			// Add anti-cache in url if needed
-			if ( s.cache === false ) {
-				s.url = rts.test( cacheURL ) ?
-
-					// If there is already a '_' parameter, set its value
-					cacheURL.replace( rts, "$1_=" + nonce++ ) :
-
-					// Otherwise add one to the end
-					cacheURL + ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + nonce++;
-			}
-		}
-
-		// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
-		if ( s.ifModified ) {
-			if ( jQuery.lastModified[ cacheURL ] ) {
-				jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] );
-			}
-			if ( jQuery.etag[ cacheURL ] ) {
-				jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] );
-			}
-		}
-
-		// Set the correct header, if data is being sent
-		if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
-			jqXHR.setRequestHeader( "Content-Type", s.contentType );
-		}
-
-		// Set the Accepts header for the server, depending on the dataType
-		jqXHR.setRequestHeader(
-			"Accept",
-			s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
-				s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
-				s.accepts[ "*" ]
-		);
-
-		// Check for headers option
-		for ( i in s.headers ) {
-			jqXHR.setRequestHeader( i, s.headers[ i ] );
-		}
-
-		// Allow custom headers/mimetypes and early abort
-		if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
-			// Abort if not done already and return
-			return jqXHR.abort();
-		}
-
-		// aborting is no longer a cancellation
-		strAbort = "abort";
-
-		// Install callbacks on deferreds
-		for ( i in { success: 1, error: 1, complete: 1 } ) {
-			jqXHR[ i ]( s[ i ] );
-		}
-
-		// Get transport
-		transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
-
-		// If no transport, we auto-abort
-		if ( !transport ) {
-			done( -1, "No Transport" );
-		} else {
-			jqXHR.readyState = 1;
-
-			// Send global event
-			if ( fireGlobals ) {
-				globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
-			}
-			// Timeout
-			if ( s.async && s.timeout > 0 ) {
-				timeoutTimer = setTimeout(function() {
-					jqXHR.abort("timeout");
-				}, s.timeout );
-			}
-
-			try {
-				state = 1;
-				transport.send( requestHeaders, done );
-			} catch ( e ) {
-				// Propagate exception as error if not done
-				if ( state < 2 ) {
-					done( -1, e );
-				// Simply rethrow otherwise
-				} else {
-					throw e;
-				}
-			}
-		}
-
-		// Callback for when everything is done
-		function done( status, nativeStatusText, responses, headers ) {
-			var isSuccess, success, error, response, modified,
-				statusText = nativeStatusText;
-
-			// Called once
-			if ( state === 2 ) {
-				return;
-			}
-
-			// State is "done" now
-			state = 2;
-
-			// Clear timeout if it exists
-			if ( timeoutTimer ) {
-				clearTimeout( timeoutTimer );
-			}
-
-			// Dereference transport for early garbage collection
-			// (no matter how long the jqXHR object will be used)
-			transport = undefined;
-
-			// Cache response headers
-			responseHeadersString = headers || "";
-
-			// Set readyState
-			jqXHR.readyState = status > 0 ? 4 : 0;
-
-			// Determine if successful
-			isSuccess = status >= 200 && status < 300 || status === 304;
-
-			// Get response data
-			if ( responses ) {
-				response = ajaxHandleResponses( s, jqXHR, responses );
-			}
-
-			// Convert no matter what (that way responseXXX fields are always set)
-			response = ajaxConvert( s, response, jqXHR, isSuccess );
-
-			// If successful, handle type chaining
-			if ( isSuccess ) {
-
-				// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
-				if ( s.ifModified ) {
-					modified = jqXHR.getResponseHeader("Last-Modified");
-					if ( modified ) {
-						jQuery.lastModified[ cacheURL ] = modified;
-					}
-					modified = jqXHR.getResponseHeader("etag");
-					if ( modified ) {
-						jQuery.etag[ cacheURL ] = modified;
-					}
-				}
-
-				// if no content
-				if ( status === 204 || s.type === "HEAD" ) {
-					statusText = "nocontent";
-
-				// if not modified
-				} else if ( status === 304 ) {
-					statusText = "notmodified";
-
-				// If we have data, let's convert it
-				} else {
-					statusText = response.state;
-					success = response.data;
-					error = response.error;
-					isSuccess = !error;
-				}
-			} else {
-				// 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;
-	},
-
-	getJSON: function( url, data, callback ) {
-		return jQuery.get( url, data, callback, "json" );
-	},
-
-	getScript: function( url, callback ) {
-		return jQuery.get( url, undefined, callback, "script" );
-	}
-});
-
-jQuery.each( [ "get", "post" ], function( i, method ) {
-	jQuery[ method ] = function( url, data, callback, type ) {
-		// shift arguments if data argument was omitted
-		if ( jQuery.isFunction( data ) ) {
-			type = type || callback;
-			callback = data;
-			data = undefined;
-		}
-
-		return jQuery.ajax({
-			url: url,
-			type: method,
-			dataType: type,
-			data: data,
-			success: callback
-		});
-	};
-});
-
-
-jQuery._evalUrl = function( url ) {
-	return jQuery.ajax({
-		url: url,
-		type: "GET",
-		dataType: "script",
-		async: false,
-		global: false,
-		"throws": true
-	});
-};
-
-
-jQuery.fn.extend({
-	wrapAll: function( html ) {
-		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();
-	}
-});
-
-
-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 ||
-		(!support.reliableHiddenOffsets() &&
-			((elem.style && elem.style.display) || jQuery.css( elem, "display" )) === "none");
-};
-
-jQuery.expr.filters.visible = function( elem ) {
-	return !jQuery.expr.filters.hidden( elem );
-};
-
-
-
-
-var r20 = /%20/g,
-	rbracket = /\[\]$/,
-	rCRLF = /\r?\n/g,
-	rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i,
-	rsubmittable = /^(?:input|select|textarea|keygen)/i;
-
-function buildParams( prefix, obj, traditional, add ) {
-	var name;
-
-	if ( jQuery.isArray( obj ) ) {
-		// Serialize array item.
-		jQuery.each( obj, function( i, v ) {
-			if ( traditional || rbracket.test( prefix ) ) {
-				// Treat each array item as a scalar.
-				add( prefix, v );
-
-			} else {
-				// Item is non-scalar (array or object), encode its numeric index.
-				buildParams( prefix + "[" + ( typeof v === "object" ? i : "" ) + "]", v, traditional, add );
-			}
-		});
-
-	} else if ( !traditional && jQuery.type( obj ) === "object" ) {
-		// Serialize object item.
-		for ( name in obj ) {
-			buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
-		}
-
-	} else {
-		// Serialize scalar item.
-		add( prefix, obj );
-	}
-}
-
-// Serialize an array of form elements or a set of
-// key/values into a query string
-jQuery.param = function( a, traditional ) {
-	var prefix,
-		s = [],
-		add = function( key, value ) {
-			// If value is a function, invoke it and return its value
-			value = jQuery.isFunction( value ) ? value() : ( value == null ? "" : value );
-			s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
-		};
-
-	// Set traditional to true for jQuery <= 1.3.2 behavior.
-	if ( traditional === undefined ) {
-		traditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional;
-	}
-
-	// If an array was passed in, assume that it is an array of form elements.
-	if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
-		// Serialize the form elements
-		jQuery.each( a, function() {
-			add( this.name, this.value );
-		});
-
-	} else {
-		// If traditional, encode the "old" way (the way 1.3.2 or older
-		// did it), otherwise encode params recursively.
-		for ( prefix in a ) {
-			buildParams( prefix, a[ prefix ], traditional, add );
-		}
-	}
-
-	// Return the resulting serialization
-	return s.join( "&" ).replace( r20, "+" );
-};
-
-jQuery.fn.extend({
-	serialize: function() {
-		return jQuery.param( this.serializeArray() );
-	},
-	serializeArray: function() {
-		return this.map(function() {
-			// Can add propHook for "elements" to filter or add form elements
-			var elements = jQuery.prop( this, "elements" );
-			return elements ? jQuery.makeArray( elements ) : this;
-		})
-		.filter(function() {
-			var type = this.type;
-			// Use .is(":disabled") so that fieldset[disabled] works
-			return this.name && !jQuery( this ).is( ":disabled" ) &&
-				rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&
-				( this.checked || !rcheckableType.test( type ) );
-		})
-		.map(function( i, elem ) {
-			var val = jQuery( this ).val();
-
-			return val == null ?
-				null :
-				jQuery.isArray( val ) ?
-					jQuery.map( val, function( val ) {
-						return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
-					}) :
-					{ name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
-		}).get();
-	}
-});
-
-
-// Create the request object
-// (This is still attached to ajaxSettings for backward compatibility)
-jQuery.ajaxSettings.xhr = window.ActiveXObject !== undefined ?
-	// Support: IE6+
-	function() {
-
-		// XHR cannot access local files, always use ActiveX for that case
-		return !this.isLocal &&
-
-			// Support: IE7-8
-			// oldIE XHR does not support non-RFC2616 methods (#13240)
-			// See http://msdn.microsoft.com/en-us/library/ie/ms536648(v=vs.85).aspx
-			// and http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9
-			// Although this check for six methods instead of eight
-			// since IE also does not support "trace" and "connect"
-			/^(get|post|head|put|delete|options)$/i.test( this.type ) &&
-
-			createStandardXHR() || createActiveXHR();
-	} :
-	// For all other browsers, use the standard XMLHttpRequest object
-	createStandardXHR;
-
-var xhrId = 0,
-	xhrCallbacks = {},
-	xhrSupported = jQuery.ajaxSettings.xhr();
-
-// Support: IE<10
-// Open requests must be manually aborted on unload (#5280)
-// See https://support.microsoft.com/kb/2856746 for more info
-if ( window.attachEvent ) {
-	window.attachEvent( "onunload", function() {
-		for ( var key in xhrCallbacks ) {
-			xhrCallbacks[ key ]( undefined, true );
-		}
-	});
-}
-
-// Determine support properties
-support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported );
-xhrSupported = support.ajax = !!xhrSupported;
-
-// Create transport if the browser can provide an xhr
-if ( xhrSupported ) {
-
-	jQuery.ajaxTransport(function( options ) {
-		// Cross domain only allowed if supported through XMLHttpRequest
-		if ( !options.crossDomain || support.cors ) {
-
-			var callback;
-
-			return {
-				send: function( headers, complete ) {
-					var i,
-						xhr = options.xhr(),
-						id = ++xhrId;
-
-					// Open the socket
-					xhr.open( options.type, options.url, options.async, options.username, options.password );
-
-					// Apply custom fields if provided
-					if ( options.xhrFields ) {
-						for ( i in options.xhrFields ) {
-							xhr[ i ] = options.xhrFields[ i ];
-						}
-					}
-
-					// Override mime type if needed
-					if ( options.mimeType && xhr.overrideMimeType ) {
-						xhr.overrideMimeType( options.mimeType );
-					}
-
-					// X-Requested-With header
-					// For cross-domain requests, seeing as conditions for a preflight are
-					// akin to a jigsaw puzzle, we simply never set it to be sure.
-					// (it can always be set on a per-request basis or even using ajaxSetup)
-					// For same-domain requests, won't change header if already provided.
-					if ( !options.crossDomain && !headers["X-Requested-With"] ) {
-						headers["X-Requested-With"] = "XMLHttpRequest";
-					}
-
-					// Set headers
-					for ( i in headers ) {
-						// Support: IE<9
-						// IE's ActiveXObject throws a 'Type Mismatch' exception when setting
-						// request header to a null-value.
-						//
-						// To keep consistent with other XHR implementations, cast the value
-						// to string and ignore `undefined`.
-						if ( headers[ i ] !== undefined ) {
-							xhr.setRequestHeader( i, headers[ i ] + "" );
-						}
-					}
-
-					// Do send the request
-					// This may raise an exception which is actually
-					// handled in jQuery.ajax (so no try/catch here)
-					xhr.send( ( options.hasContent && options.data ) || null );
-
-					// Listener
-					callback = function( _, isAbort ) {
-						var status, statusText, responses;
-
-						// Was never called and is aborted or complete
-						if ( callback && ( isAbort || xhr.readyState === 4 ) ) {
-							// Clean up
-							delete xhrCallbacks[ id ];
-							callback = undefined;
-							xhr.onreadystatechange = jQuery.noop;
-
-							// Abort manually if needed
-							if ( isAbort ) {
-								if ( xhr.readyState !== 4 ) {
-									xhr.abort();
-								}
-							} else {
-								responses = {};
-								status = xhr.status;
-
-								// Support: IE<10
-								// Accessing binary-data responseText throws an exception
-								// (#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 && options.isLocal && !options.crossDomain ) {
-									status = responses.text ? 200 : 404;
-								// IE - #1450: sometimes returns 1223 when it should be 204
-								} else if ( status === 1223 ) {
-									status = 204;
-								}
-							}
-						}
-
-						// Call complete if needed
-						if ( responses ) {
-							complete( status, statusText, responses, xhr.getAllResponseHeaders() );
-						}
-					};
-
-					if ( !options.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 {
-						// Add to the list of active xhr callbacks
-						xhr.onreadystatechange = xhrCallbacks[ id ] = callback;
-					}
-				},
-
-				abort: function() {
-					if ( callback ) {
-						callback( 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 ) {}
-}
-
-
-
-
-// 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 + "_" + ( nonce++ ) );
-		this[ callback ] = true;
-		return callback;
-	}
-});
-
-// Detect, normalize options and install callbacks for jsonp requests
-jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
-
-	var callbackName, overwritten, responseContainer,
-		jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ?
-			"url" :
-			typeof s.data === "string" && !( s.contentType || "" ).indexOf("application/x-www-form-urlencoded") && rjsonp.test( s.data ) && "data"
-		);
-
-	// Handle iff the expected data type is "jsonp" or we have a parameter to set
-	if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) {
-
-		// Get callback name, remembering preexisting value associated with it
-		callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ?
-			s.jsonpCallback() :
-			s.jsonpCallback;
-
-		// Insert callback into url or form data
-		if ( jsonProp ) {
-			s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName );
-		} else if ( s.jsonp !== false ) {
-			s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName;
-		}
-
-		// Use data converter to retrieve json after script execution
-		s.converters["script json"] = function() {
-			if ( !responseContainer ) {
-				jQuery.error( callbackName + " was not called" );
-			}
-			return responseContainer[ 0 ];
-		};
-
-		// force json dataType
-		s.dataTypes[ 0 ] = "json";
-
-		// Install callback
-		overwritten = window[ callbackName ];
-		window[ callbackName ] = function() {
-			responseContainer = arguments;
-		};
-
-		// Clean-up function (fires after converters)
-		jqXHR.always(function() {
-			// Restore preexisting value
-			window[ callbackName ] = overwritten;
-
-			// Save back as free
-			if ( s[ callbackName ] ) {
-				// make sure that re-using the options doesn't screw things around
-				s.jsonpCallback = originalSettings.jsonpCallback;
-
-				// save the callback name for future use
-				oldCallbacks.push( callbackName );
-			}
-
-			// Call if it was a function and we have a response
-			if ( responseContainer && jQuery.isFunction( overwritten ) ) {
-				overwritten( responseContainer[ 0 ] );
-			}
-
-			responseContainer = overwritten = undefined;
-		});
-
-		// Delegate to script
-		return "script";
-	}
-});
-
-
-
-
-// data: string of html
-// context (optional): If specified, the fragment will be created in this context, defaults to document
-// keepScripts (optional): If true, will include scripts passed in the html string
-jQuery.parseHTML = function( data, context, keepScripts ) {
-	if ( !data || typeof data !== "string" ) {
-		return null;
-	}
-	if ( typeof context === "boolean" ) {
-		keepScripts = context;
-		context = false;
-	}
-	context = context || document;
-
-	var parsed = rsingleTag.exec( data ),
-		scripts = !keepScripts && [];
-
-	// Single tag
-	if ( parsed ) {
-		return [ context.createElement( parsed[1] ) ];
-	}
-
-	parsed = jQuery.buildFragment( [ data ], context, scripts );
-
-	if ( scripts && scripts.length ) {
-		jQuery( scripts ).remove();
-	}
-
-	return jQuery.merge( [], parsed.childNodes );
-};
-
-
-// Keep a copy of the old load method
-var _load = jQuery.fn.load;
-
-/**
- * Load a url into a page
- */
-jQuery.fn.load = function( url, params, callback ) {
-	if ( typeof url !== "string" && _load ) {
-		return _load.apply( this, arguments );
-	}
-
-	var selector, response, type,
-		self = this,
-		off = url.indexOf(" ");
-
-	if ( off >= 0 ) {
-		selector = jQuery.trim( 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.expr.filters.animated = function( elem ) {
-	return jQuery.grep(jQuery.timers, function( fn ) {
-		return elem === fn.elem;
-	}).length;
-};
-
-
-
-
-
-var docElem = window.document.documentElement;
-
-/**
- * Gets a window from an element
- */
-function getWindow( elem ) {
-	return jQuery.isWindow( elem ) ?
-		elem :
-		elem.nodeType === 9 ?
-			elem.defaultView || elem.parentWindow :
-			false;
-}
-
-jQuery.offset = {
-	setOffset: function( elem, options, i ) {
-		var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition,
-			position = jQuery.css( elem, "position" ),
-			curElem = jQuery( elem ),
-			props = {};
-
-		// set position first, in-case top/left are set even on static elem
-		if ( position === "static" ) {
-			elem.style.position = "relative";
-		}
-
-		curOffset = curElem.offset();
-		curCSSTop = jQuery.css( elem, "top" );
-		curCSSLeft = jQuery.css( elem, "left" );
-		calculatePosition = ( position === "absolute" || position === "fixed" ) &&
-			jQuery.inArray("auto", [ curCSSTop, curCSSLeft ] ) > -1;
-
-		// need to be able to calculate position if either top or left is auto and position is either absolute or fixed
-		if ( calculatePosition ) {
-			curPosition = curElem.position();
-			curTop = curPosition.top;
-			curLeft = curPosition.left;
-		} else {
-			curTop = parseFloat( curCSSTop ) || 0;
-			curLeft = parseFloat( curCSSLeft ) || 0;
-		}
-
-		if ( jQuery.isFunction( options ) ) {
-			options = options.call( elem, i, curOffset );
-		}
-
-		if ( options.top != null ) {
-			props.top = ( options.top - curOffset.top ) + curTop;
-		}
-		if ( options.left != null ) {
-			props.left = ( options.left - curOffset.left ) + curLeft;
-		}
-
-		if ( "using" in options ) {
-			options.using.call( elem, props );
-		} else {
-			curElem.css( props );
-		}
-	}
-};
-
-jQuery.fn.extend({
-	offset: function( options ) {
-		if ( arguments.length ) {
-			return options === undefined ?
-				this :
-				this.each(function( i ) {
-					jQuery.offset.setOffset( this, options, i );
-				});
-		}
-
-		var docElem, win,
-			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 !== 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 )
-		};
-	},
-
-	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 its 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 || docElem;
-
-			while ( offsetParent && ( !jQuery.nodeName( offsetParent, "html" ) && jQuery.css( offsetParent, "position" ) === "static" ) ) {
-				offsetParent = offsetParent.offsetParent;
-			}
-			return offsetParent || docElem;
-		});
-	}
-});
-
-// Create scrollLeft and scrollTop methods
-jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function( method, prop ) {
-	var top = /Y/.test( prop );
-
-	jQuery.fn[ method ] = function( val ) {
-		return 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 );
-	};
-});
-
-// Add the top/left cssHooks using jQuery.fn.position
-// 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
-jQuery.each( [ "top", "left" ], function( i, prop ) {
-	jQuery.cssHooks[ prop ] = addGetHookIf( support.pixelPosition,
-		function( elem, computed ) {
-			if ( computed ) {
-				computed = curCSS( elem, prop );
-				// if curCSS returns percentage, fallback to offset
-				return rnumnonpx.test( computed ) ?
-					jQuery( elem ).position()[ prop ] + "px" :
-					computed;
-			}
-		}
-	);
-});
-
-
-// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
-jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
-	jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, function( defaultExtra, funcName ) {
-		// margin is only for outerHeight, outerWidth
-		jQuery.fn[ funcName ] = function( margin, value ) {
-			var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ),
-				extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" );
-
-			return access( this, function( elem, type, value ) {
-				var doc;
-
-				if ( jQuery.isWindow( elem ) ) {
-					// As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there
-					// isn't a whole lot we can do. See pull request at this URL for discussion:
-					// https://github.com/jquery/jquery/pull/764
-					return elem.document.documentElement[ "client" + name ];
-				}
-
-				// Get document width or height
-				if ( elem.nodeType === 9 ) {
-					doc = elem.documentElement;
-
-					// Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height], whichever is greatest
-					// 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 );
-		};
-	});
-});
-
-
-// The number of elements contained in the matched element set
-jQuery.fn.size = function() {
-	return this.length;
-};
-
-jQuery.fn.andSelf = jQuery.fn.addBack;
-
-
-
-
-// Register as a named AMD module, since jQuery can be concatenated with other
-// files that may use define, but not via a proper concatenation script that
-// understands anonymous AMD modules. A named AMD is safest and most robust
-// way to register. Lowercase jquery is used because AMD module names are
-// derived from file names, and jQuery is normally delivered in a lowercase
-// file name. Do this after creating the global so that if an AMD module wants
-// to call noConflict to hide this version of jQuery, it will work.
-
-// Note that for maximum portability, libraries that are not jQuery should
-// declare themselves as anonymous modules, and avoid setting a global if an
-// AMD loader is present. jQuery is a special case. For more information, see
-// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon
-
-if ( typeof define === "function" && define.amd ) {
-	define( "jquery", [], function() {
-		return jQuery;
-	});
-}
-
-
-
-
-var
-	// Map over jQuery in case of overwrite
-	_jQuery = window.jQuery,
-
-	// Map over the $ in case of overwrite
-	_$ = window.$;
-
-jQuery.noConflict = function( deep ) {
-	if ( window.$ === jQuery ) {
-		window.$ = _$;
-	}
-
-	if ( deep && window.jQuery === jQuery ) {
-		window.jQuery = _jQuery;
-	}
-
-	return jQuery;
-};
-
-// Expose jQuery and $ identifiers, even in
-// AMD (#7102#comment:10, https://github.com/jquery/jquery/pull/557)
-// and CommonJS for browser emulators (#13566)
-if ( typeof noGlobal === strundefined ) {
-	window.jQuery = window.$ = jQuery;
-}
-
-
-
-
-return jQuery;
-
-}));
-/* pako 0.2.7 nodeca/pako */(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.pako = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if( [...]
-'use strict';
-
-
-var TYPED_OK =  (typeof Uint8Array !== 'undefined') &&
-                (typeof Uint16Array !== 'undefined') &&
-                (typeof Int32Array !== 'undefined');
-
-
-exports.assign = function (obj /*from1, from2, from3, ...*/) {
-  var sources = Array.prototype.slice.call(arguments, 1);
-  while (sources.length) {
-    var source = sources.shift();
-    if (!source) { continue; }
-
-    if (typeof source !== 'object') {
-      throw new TypeError(source + 'must be non-object');
-    }
-
-    for (var p in source) {
-      if (source.hasOwnProperty(p)) {
-        obj[p] = source[p];
-      }
-    }
-  }
-
-  return obj;
-};
-
-
-// reduce buffer size, avoiding mem copy
-exports.shrinkBuf = function (buf, size) {
-  if (buf.length === size) { return buf; }
-  if (buf.subarray) { return buf.subarray(0, size); }
-  buf.length = size;
-  return buf;
-};
-
-
-var fnTyped = {
-  arraySet: function (dest, src, src_offs, len, dest_offs) {
-    if (src.subarray && dest.subarray) {
-      dest.set(src.subarray(src_offs, src_offs+len), dest_offs);
-      return;
-    }
-    // Fallback to ordinary array
-    for (var i=0; i<len; i++) {
-      dest[dest_offs + i] = src[src_offs + i];
-    }
-  },
-  // Join array of chunks to single array.
-  flattenChunks: function(chunks) {
-    var i, l, len, pos, chunk, result;
-
-    // calculate data length
-    len = 0;
-    for (i=0, l=chunks.length; i<l; i++) {
-      len += chunks[i].length;
-    }
-
-    // join chunks
-    result = new Uint8Array(len);
-    pos = 0;
-    for (i=0, l=chunks.length; i<l; i++) {
-      chunk = chunks[i];
-      result.set(chunk, pos);
-      pos += chunk.length;
-    }
-
-    return result;
-  }
-};
-
-var fnUntyped = {
-  arraySet: function (dest, src, src_offs, len, dest_offs) {
-    for (var i=0; i<len; i++) {
-      dest[dest_offs + i] = src[src_offs + i];
-    }
-  },
-  // Join array of chunks to single array.
-  flattenChunks: function(chunks) {
-    return [].concat.apply([], chunks);
-  }
-};
-
-
-// Enable/Disable typed arrays use, for testing
-//
-exports.setTyped = function (on) {
-  if (on) {
-    exports.Buf8  = Uint8Array;
-    exports.Buf16 = Uint16Array;
-    exports.Buf32 = Int32Array;
-    exports.assign(exports, fnTyped);
-  } else {
-    exports.Buf8  = Array;
-    exports.Buf16 = Array;
-    exports.Buf32 = Array;
-    exports.assign(exports, fnUntyped);
-  }
-};
-
-exports.setTyped(TYPED_OK);
-
-},{}],2:[function(require,module,exports){
-// String encode/decode helpers
-'use strict';
-
-
-var utils = require('./common');
-
-
-// Quick check if we can use fast array to bin string conversion
-//
-// - apply(Array) can fail on Android 2.2
-// - apply(Uint8Array) can fail on iOS 5.1 Safary
-//
-var STR_APPLY_OK = true;
-var STR_APPLY_UIA_OK = true;
-
-try { String.fromCharCode.apply(null, [0]); } catch(__) { STR_APPLY_OK = false; }
-try { String.fromCharCode.apply(null, new Uint8Array(1)); } catch(__) { STR_APPLY_UIA_OK = false; }
-
-
-// Table with utf8 lengths (calculated by first byte of sequence)
-// Note, that 5 & 6-byte values and some 4-byte values can not be represented in JS,
-// because max possible codepoint is 0x10ffff
-var _utf8len = new utils.Buf8(256);
-for (var q=0; q<256; q++) {
-  _utf8len[q] = (q >= 252 ? 6 : q >= 248 ? 5 : q >= 240 ? 4 : q >= 224 ? 3 : q >= 192 ? 2 : 1);
-}
-_utf8len[254]=_utf8len[254]=1; // Invalid sequence start
-
-
-// convert string to array (typed, when possible)
-exports.string2buf = function (str) {
-  var buf, c, c2, m_pos, i, str_len = str.length, buf_len = 0;
-
-  // count binary size
-  for (m_pos = 0; m_pos < str_len; m_pos++) {
-    c = str.charCodeAt(m_pos);
-    if ((c & 0xfc00) === 0xd800 && (m_pos+1 < str_len)) {
-      c2 = str.charCodeAt(m_pos+1);
-      if ((c2 & 0xfc00) === 0xdc00) {
-        c = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00);
-        m_pos++;
-      }
-    }
-    buf_len += c < 0x80 ? 1 : c < 0x800 ? 2 : c < 0x10000 ? 3 : 4;
-  }
-
-  // allocate buffer
-  buf = new utils.Buf8(buf_len);
-
-  // convert
-  for (i=0, m_pos = 0; i < buf_len; m_pos++) {
-    c = str.charCodeAt(m_pos);
-    if ((c & 0xfc00) === 0xd800 && (m_pos+1 < str_len)) {
-      c2 = str.charCodeAt(m_pos+1);
-      if ((c2 & 0xfc00) === 0xdc00) {
-        c = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00);
-        m_pos++;
-      }
-    }
-    if (c < 0x80) {
-      /* one byte */
-      buf[i++] = c;
-    } else if (c < 0x800) {
-      /* two bytes */
-      buf[i++] = 0xC0 | (c >>> 6);
-      buf[i++] = 0x80 | (c & 0x3f);
-    } else if (c < 0x10000) {
-      /* three bytes */
-      buf[i++] = 0xE0 | (c >>> 12);
-      buf[i++] = 0x80 | (c >>> 6 & 0x3f);
-      buf[i++] = 0x80 | (c & 0x3f);
-    } else {
-      /* four bytes */
-      buf[i++] = 0xf0 | (c >>> 18);
-      buf[i++] = 0x80 | (c >>> 12 & 0x3f);
-      buf[i++] = 0x80 | (c >>> 6 & 0x3f);
-      buf[i++] = 0x80 | (c & 0x3f);
-    }
-  }
-
-  return buf;
-};
-
-// Helper (used in 2 places)
-function buf2binstring(buf, len) {
-  // use fallback for big arrays to avoid stack overflow
-  if (len < 65537) {
-    if ((buf.subarray && STR_APPLY_UIA_OK) || (!buf.subarray && STR_APPLY_OK)) {
-      return String.fromCharCode.apply(null, utils.shrinkBuf(buf, len));
-    }
-  }
-
-  var result = '';
-  for (var i=0; i < len; i++) {
-    result += String.fromCharCode(buf[i]);
-  }
-  return result;
-}
-
-
-// Convert byte array to binary string
-exports.buf2binstring = function(buf) {
-  return buf2binstring(buf, buf.length);
-};
-
-
-// Convert binary string (typed, when possible)
-exports.binstring2buf = function(str) {
-  var buf = new utils.Buf8(str.length);
-  for (var i=0, len=buf.length; i < len; i++) {
-    buf[i] = str.charCodeAt(i);
-  }
-  return buf;
-};
-
-
-// convert array to string
-exports.buf2string = function (buf, max) {
-  var i, out, c, c_len;
-  var len = max || buf.length;
-
-  // Reserve max possible length (2 words per char)
-  // NB: by unknown reasons, Array is significantly faster for
-  //     String.fromCharCode.apply than Uint16Array.
-  var utf16buf = new Array(len*2);
-
-  for (out=0, i=0; i<len;) {
-    c = buf[i++];
-    // quick process ascii
-    if (c < 0x80) { utf16buf[out++] = c; continue; }
-
-    c_len = _utf8len[c];
-    // skip 5 & 6 byte codes
-    if (c_len > 4) { utf16buf[out++] = 0xfffd; i += c_len-1; continue; }
-
-    // apply mask on first byte
-    c &= c_len === 2 ? 0x1f : c_len === 3 ? 0x0f : 0x07;
-    // join the rest
-    while (c_len > 1 && i < len) {
-      c = (c << 6) | (buf[i++] & 0x3f);
-      c_len--;
-    }
-
-    // terminated by end of string?
-    if (c_len > 1) { utf16buf[out++] = 0xfffd; continue; }
-
-    if (c < 0x10000) {
-      utf16buf[out++] = c;
-    } else {
-      c -= 0x10000;
-      utf16buf[out++] = 0xd800 | ((c >> 10) & 0x3ff);
-      utf16buf[out++] = 0xdc00 | (c & 0x3ff);
-    }
-  }
-
-  return buf2binstring(utf16buf, out);
-};
-
-
-// Calculate max possible position in utf8 buffer,
-// that will not break sequence. If that's not possible
-// - (very small limits) return max size as is.
-//
-// buf[] - utf8 bytes array
-// max   - length limit (mandatory);
-exports.utf8border = function(buf, max) {
-  var pos;
-
-  max = max || buf.length;
-  if (max > buf.length) { max = buf.length; }
-
-  // go back from last position, until start of sequence found
-  pos = max-1;
-  while (pos >= 0 && (buf[pos] & 0xC0) === 0x80) { pos--; }
-
-  // Fuckup - very small and broken sequence,
-  // return max, because we should return something anyway.
-  if (pos < 0) { return max; }
-
-  // If we came to start of buffer - that means vuffer is too small,
-  // return max too.
-  if (pos === 0) { return max; }
-
-  return (pos + _utf8len[buf[pos]] > max) ? pos : max;
-};
-
-},{"./common":1}],3:[function(require,module,exports){
-'use strict';
-
-// Note: adler32 takes 12% for level 0 and 2% for level 6.
-// It doesn't worth to make additional optimizationa as in original.
-// Small size is preferable.
-
-function adler32(adler, buf, len, pos) {
-  var s1 = (adler & 0xffff) |0,
-      s2 = ((adler >>> 16) & 0xffff) |0,
-      n = 0;
-
-  while (len !== 0) {
-    // Set limit ~ twice less than 5552, to keep
-    // s2 in 31-bits, because we force signed ints.
-    // in other case %= will fail.
-    n = len > 2000 ? 2000 : len;
-    len -= n;
-
-    do {
-      s1 = (s1 + buf[pos++]) |0;
-      s2 = (s2 + s1) |0;
-    } while (--n);
-
-    s1 %= 65521;
-    s2 %= 65521;
-  }
-
-  return (s1 | (s2 << 16)) |0;
-}
-
-
-module.exports = adler32;
-
-},{}],4:[function(require,module,exports){
-module.exports = {
-
-  /* Allowed flush values; see deflate() and inflate() below for details */
-  Z_NO_FLUSH:         0,
-  Z_PARTIAL_FLUSH:    1,
-  Z_SYNC_FLUSH:       2,
-  Z_FULL_FLUSH:       3,
-  Z_FINISH:           4,
-  Z_BLOCK:            5,
-  Z_TREES:            6,
-
-  /* Return codes for the compression/decompression functions. Negative values
-  * are errors, positive values are used for special but normal events.
-  */
-  Z_OK:               0,
-  Z_STREAM_END:       1,
-  Z_NEED_DICT:        2,
-  Z_ERRNO:           -1,
-  Z_STREAM_ERROR:    -2,
-  Z_DATA_ERROR:      -3,
-  //Z_MEM_ERROR:     -4,
-  Z_BUF_ERROR:       -5,
-  //Z_VERSION_ERROR: -6,
-
-  /* compression levels */
-  Z_NO_COMPRESSION:         0,
-  Z_BEST_SPEED:             1,
-  Z_BEST_COMPRESSION:       9,
-  Z_DEFAULT_COMPRESSION:   -1,
-
-
-  Z_FILTERED:               1,
-  Z_HUFFMAN_ONLY:           2,
-  Z_RLE:                    3,
-  Z_FIXED:                  4,
-  Z_DEFAULT_STRATEGY:       0,
-
-  /* Possible values of the data_type field (though see inflate()) */
-  Z_BINARY:                 0,
-  Z_TEXT:                   1,
-  //Z_ASCII:                1, // = Z_TEXT (deprecated)
-  Z_UNKNOWN:                2,
-
-  /* The deflate compression method */
-  Z_DEFLATED:               8
-  //Z_NULL:                 null // Use -1 or null inline, depending on var type
-};
-
-},{}],5:[function(require,module,exports){
-'use strict';
-
-// Note: we can't get significant speed boost here.
-// So write code to minimize size - no pregenerated tables
-// and array tools dependencies.
-
-
-// Use ordinary array, since untyped makes no boost here
-function makeTable() {
-  var c, table = [];
-
-  for (var n =0; n < 256; n++) {
-    c = n;
-    for (var k =0; k < 8; k++) {
-      c = ((c&1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1));
-    }
-    table[n] = c;
-  }
-
-  return table;
-}
-
-// Create table on load. Just 255 signed longs. Not a problem.
-var crcTable = makeTable();
-
-
-function crc32(crc, buf, len, pos) {
-  var t = crcTable,
-      end = pos + len;
-
-  crc = crc ^ (-1);
-
-  for (var i = pos; i < end; i++) {
-    crc = (crc >>> 8) ^ t[(crc ^ buf[i]) & 0xFF];
-  }
-
-  return (crc ^ (-1)); // >>> 0;
-}
-
-
-module.exports = crc32;
-
-},{}],6:[function(require,module,exports){
-'use strict';
-
-
-function GZheader() {
-  /* true if compressed data believed to be text */
-  this.text       = 0;
-  /* modification time */
-  this.time       = 0;
-  /* extra flags (not used when writing a gzip file) */
-  this.xflags     = 0;
-  /* operating system */
-  this.os         = 0;
-  /* pointer to extra field or Z_NULL if none */
-  this.extra      = null;
-  /* extra field length (valid if extra != Z_NULL) */
-  this.extra_len  = 0; // Actually, we don't need it in JS,
-                       // but leave for few code modifications
-
-  //
-  // Setup limits is not necessary because in js we should not preallocate memory
-  // for inflate use constant limit in 65536 bytes
-  //
-
-  /* space at extra (only when reading header) */
-  // this.extra_max  = 0;
-  /* pointer to zero-terminated file name or Z_NULL */
-  this.name       = '';
-  /* space at name (only when reading header) */
-  // this.name_max   = 0;
-  /* pointer to zero-terminated comment or Z_NULL */
-  this.comment    = '';
-  /* space at comment (only when reading header) */
-  // this.comm_max   = 0;
-  /* true if there was or will be a header crc */
-  this.hcrc       = 0;
-  /* true when done reading gzip header (not used when writing a gzip file) */
-  this.done       = false;
-}
-
-module.exports = GZheader;
-
-},{}],7:[function(require,module,exports){
-'use strict';
-
-// See state defs from inflate.js
-var BAD = 30;       /* got a data error -- remain here until reset */
-var TYPE = 12;      /* i: waiting for type bits, including last-flag bit */
-
-/*
-   Decode literal, length, and distance codes and write out the resulting
-   literal and match bytes until either not enough input or output is
-   available, an end-of-block is encountered, or a data error is encountered.
-   When large enough input and output buffers are supplied to inflate(), for
-   example, a 16K input buffer and a 64K output buffer, more than 95% of the
-   inflate execution time is spent in this routine.
-
-   Entry assumptions:
-
-        state.mode === LEN
-        strm.avail_in >= 6
-        strm.avail_out >= 258
-        start >= strm.avail_out
-        state.bits < 8
-
-   On return, state.mode is one of:
-
-        LEN -- ran out of enough output space or enough available input
-        TYPE -- reached end of block code, inflate() to interpret next block
-        BAD -- error in block data
-
-   Notes:
-
-    - The maximum input bits used by a length/distance pair is 15 bits for the
-      length code, 5 bits for the length extra, 15 bits for the distance code,
-      and 13 bits for the distance extra.  This totals 48 bits, or six bytes.
-      Therefore if strm.avail_in >= 6, then there is enough input to avoid
-      checking for available input while decoding.
-
-    - The maximum bytes that a single length/distance pair can output is 258
-      bytes, which is the maximum length that can be coded.  inflate_fast()
-      requires strm.avail_out >= 258 for each loop to avoid checking for
-      output space.
- */
-module.exports = function inflate_fast(strm, start) {
-  var state;
-  var _in;                    /* local strm.input */
-  var last;                   /* have enough input while in < last */
-  var _out;                   /* local strm.output */
-  var beg;                    /* inflate()'s initial strm.output */
-  var end;                    /* while out < end, enough space available */
-//#ifdef INFLATE_STRICT
-  var dmax;                   /* maximum distance from zlib header */
-//#endif
-  var wsize;                  /* window size or zero if not using window */
-  var whave;                  /* valid bytes in the window */
-  var wnext;                  /* window write index */
-  var window;                 /* allocated sliding window, if wsize != 0 */
-  var hold;                   /* local strm.hold */
-  var bits;                   /* local strm.bits */
-  var lcode;                  /* local strm.lencode */
-  var dcode;                  /* local strm.distcode */
-  var lmask;                  /* mask for first level of length codes */
-  var dmask;                  /* mask for first level of distance codes */
-  var here;                   /* retrieved table entry */
-  var op;                     /* code bits, operation, extra bits, or */
-                              /*  window position, window bytes to copy */
-  var len;                    /* match length, unused bytes */
-  var dist;                   /* match distance */
-  var from;                   /* where to copy match from */
-  var from_source;
-
-
-  var input, output; // JS specific, because we have no pointers
-
-  /* copy state to local variables */
-  state = strm.state;
-  //here = state.here;
-  _in = strm.next_in;
-  input = strm.input;
-  last = _in + (strm.avail_in - 5);
-  _out = strm.next_out;
-  output = strm.output;
-  beg = _out - (start - strm.avail_out);
-  end = _out + (strm.avail_out - 257);
-//#ifdef INFLATE_STRICT
-  dmax = state.dmax;
-//#endif
-  wsize = state.wsize;
-  whave = state.whave;
-  wnext = state.wnext;
-  window = state.window;
-  hold = state.hold;
-  bits = state.bits;
-  lcode = state.lencode;
-  dcode = state.distcode;
-  lmask = (1 << state.lenbits) - 1;
-  dmask = (1 << state.distbits) - 1;
-
-
-  /* decode literals and length/distances until end-of-block or not enough
-     input data or output space */
-
-  top:
-  do {
-    if (bits < 15) {
-      hold += input[_in++] << bits;
-      bits += 8;
-      hold += input[_in++] << bits;
-      bits += 8;
-    }
-
-    here = lcode[hold & lmask];
-
-    dolen:
-    for (;;) { // Goto emulation
-      op = here >>> 24/*here.bits*/;
-      hold >>>= op;
-      bits -= op;
-      op = (here >>> 16) & 0xff/*here.op*/;
-      if (op === 0) {                          /* literal */
-        //Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
-        //        "inflate:         literal '%c'\n" :
-        //        "inflate:         literal 0x%02x\n", here.val));
-        output[_out++] = here & 0xffff/*here.val*/;
-      }
-      else if (op & 16) {                     /* length base */
-        len = here & 0xffff/*here.val*/;
-        op &= 15;                           /* number of extra bits */
-        if (op) {
-          if (bits < op) {
-            hold += input[_in++] << bits;
-            bits += 8;
-          }
-          len += hold & ((1 << op) - 1);
-          hold >>>= op;
-          bits -= op;
-        }
-        //Tracevv((stderr, "inflate:         length %u\n", len));
-        if (bits < 15) {
-          hold += input[_in++] << bits;
-          bits += 8;
-          hold += input[_in++] << bits;
-          bits += 8;
-        }
-        here = dcode[hold & dmask];
-
-        dodist:
-        for (;;) { // goto emulation
-          op = here >>> 24/*here.bits*/;
-          hold >>>= op;
-          bits -= op;
-          op = (here >>> 16) & 0xff/*here.op*/;
-
-          if (op & 16) {                      /* distance base */
-            dist = here & 0xffff/*here.val*/;
-            op &= 15;                       /* number of extra bits */
-            if (bits < op) {
-              hold += input[_in++] << bits;
-              bits += 8;
-              if (bits < op) {
-                hold += input[_in++] << bits;
-                bits += 8;
-              }
-            }
-            dist += hold & ((1 << op) - 1);
-//#ifdef INFLATE_STRICT
-            if (dist > dmax) {
-              strm.msg = 'invalid distance too far back';
-              state.mode = BAD;
-              break top;
-            }
-//#endif
-            hold >>>= op;
-            bits -= op;
-            //Tracevv((stderr, "inflate:         distance %u\n", dist));
-            op = _out - beg;                /* max distance in output */
-            if (dist > op) {                /* see if copy from window */
-              op = dist - op;               /* distance back in window */
-              if (op > whave) {
-                if (state.sane) {
-                  strm.msg = 'invalid distance too far back';
-                  state.mode = BAD;
-                  break top;
-                }
-
-// (!) This block is disabled in zlib defailts,
-// don't enable it for binary compatibility
-//#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
-//                if (len <= op - whave) {
-//                  do {
-//                    output[_out++] = 0;
-//                  } while (--len);
-//                  continue top;
-//                }
-//                len -= op - whave;
-//                do {
-//                  output[_out++] = 0;
-//                } while (--op > whave);
-//                if (op === 0) {
-//                  from = _out - dist;
-//                  do {
-//                    output[_out++] = output[from++];
-//                  } while (--len);
-//                  continue top;
-//                }
-//#endif
-              }
-              from = 0; // window index
-              from_source = window;
-              if (wnext === 0) {           /* very common case */
-                from += wsize - op;
-                if (op < len) {         /* some from window */
-                  len -= op;
-                  do {
-                    output[_out++] = window[from++];
-                  } while (--op);
-                  from = _out - dist;  /* rest from output */
-                  from_source = output;
-                }
-              }
-              else if (wnext < op) {      /* wrap around window */
-                from += wsize + wnext - op;
-                op -= wnext;
-                if (op < len) {         /* some from end of window */
-                  len -= op;
-                  do {
-                    output[_out++] = window[from++];
-                  } while (--op);
-                  from = 0;
-                  if (wnext < len) {  /* some from start of window */
-                    op = wnext;
-                    len -= op;
-                    do {
-                      output[_out++] = window[from++];
-                    } while (--op);
-                    from = _out - dist;      /* rest from output */
-                    from_source = output;
-                  }
-                }
-              }
-              else {                      /* contiguous in window */
-                from += wnext - op;
-                if (op < len) {         /* some from window */
-                  len -= op;
-                  do {
-                    output[_out++] = window[from++];
-                  } while (--op);
-                  from = _out - dist;  /* rest from output */
-                  from_source = output;
-                }
-              }
-              while (len > 2) {
-                output[_out++] = from_source[from++];
-                output[_out++] = from_source[from++];
-                output[_out++] = from_source[from++];
-                len -= 3;
-              }
-              if (len) {
-                output[_out++] = from_source[from++];
-                if (len > 1) {
-                  output[_out++] = from_source[from++];
-                }
-              }
-            }
-            else {
-              from = _out - dist;          /* copy direct from output */
-              do {                        /* minimum length is three */
-                output[_out++] = output[from++];
-                output[_out++] = output[from++];
-                output[_out++] = output[from++];
-                len -= 3;
-              } while (len > 2);
-              if (len) {
-                output[_out++] = output[from++];
-                if (len > 1) {
-                  output[_out++] = output[from++];
-                }
-              }
-            }
-          }
-          else if ((op & 64) === 0) {          /* 2nd level distance code */
-            here = dcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))];
-            continue dodist;
-          }
-          else {
-            strm.msg = 'invalid distance code';
-            state.mode = BAD;
-            break top;
-          }
-
-          break; // need to emulate goto via "continue"
-        }
-      }
-      else if ((op & 64) === 0) {              /* 2nd level length code */
-        here = lcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))];
-        continue dolen;
-      }
-      else if (op & 32) {                     /* end-of-block */
-        //Tracevv((stderr, "inflate:         end of block\n"));
-        state.mode = TYPE;
-        break top;
-      }
-      else {
-        strm.msg = 'invalid literal/length code';
-        state.mode = BAD;
-        break top;
-      }
-
-      break; // need to emulate goto via "continue"
-    }
-  } while (_in < last && _out < end);
-
-  /* return unused bytes (on entry, bits < 8, so in won't go too far back) */
-  len = bits >> 3;
-  _in -= len;
-  bits -= len << 3;
-  hold &= (1 << bits) - 1;
-
-  /* update state and return */
-  strm.next_in = _in;
-  strm.next_out = _out;
-  strm.avail_in = (_in < last ? 5 + (last - _in) : 5 - (_in - last));
-  strm.avail_out = (_out < end ? 257 + (end - _out) : 257 - (_out - end));
-  state.hold = hold;
-  state.bits = bits;
-  return;
-};
-
-},{}],8:[function(require,module,exports){
-'use strict';
-
-
-var utils = require('../utils/common');
-var adler32 = require('./adler32');
-var crc32   = require('./crc32');
-var inflate_fast = require('./inffast');
-var inflate_table = require('./inftrees');
-
-var CODES = 0;
-var LENS = 1;
-var DISTS = 2;
-
-/* Public constants ==========================================================*/
-/* ===========================================================================*/
-
-
-/* Allowed flush values; see deflate() and inflate() below for details */
-//var Z_NO_FLUSH      = 0;
-//var Z_PARTIAL_FLUSH = 1;
-//var Z_SYNC_FLUSH    = 2;
-//var Z_FULL_FLUSH    = 3;
-var Z_FINISH        = 4;
-var Z_BLOCK         = 5;
-var Z_TREES         = 6;
-
-
-/* Return codes for the compression/decompression functions. Negative values
- * are errors, positive values are used for special but normal events.
- */
-var Z_OK            = 0;
-var Z_STREAM_END    = 1;
-var Z_NEED_DICT     = 2;
-//var Z_ERRNO         = -1;
-var Z_STREAM_ERROR  = -2;
-var Z_DATA_ERROR    = -3;
-var Z_MEM_ERROR     = -4;
-var Z_BUF_ERROR     = -5;
-//var Z_VERSION_ERROR = -6;
-
-/* The deflate compression method */
-var Z_DEFLATED  = 8;
-
-
-/* STATES ====================================================================*/
-/* ===========================================================================*/
-
-
-var    HEAD = 1;       /* i: waiting for magic header */
-var    FLAGS = 2;      /* i: waiting for method and flags (gzip) */
-var    TIME = 3;       /* i: waiting for modification time (gzip) */
-var    OS = 4;         /* i: waiting for extra flags and operating system (gzip) */
-var    EXLEN = 5;      /* i: waiting for extra length (gzip) */
-var    EXTRA = 6;      /* i: waiting for extra bytes (gzip) */
-var    NAME = 7;       /* i: waiting for end of file name (gzip) */
-var    COMMENT = 8;    /* i: waiting for end of comment (gzip) */
-var    HCRC = 9;       /* i: waiting for header crc (gzip) */
-var    DICTID = 10;    /* i: waiting for dictionary check value */
-var    DICT = 11;      /* waiting for inflateSetDictionary() call */
-var        TYPE = 12;      /* i: waiting for type bits, including last-flag bit */
-var        TYPEDO = 13;    /* i: same, but skip check to exit inflate on new block */
-var        STORED = 14;    /* i: waiting for stored size (length and complement) */
-var        COPY_ = 15;     /* i/o: same as COPY below, but only first time in */
-var        COPY = 16;      /* i/o: waiting for input or output to copy stored block */
-var        TABLE = 17;     /* i: waiting for dynamic block table lengths */
-var        LENLENS = 18;   /* i: waiting for code length code lengths */
-var        CODELENS = 19;  /* i: waiting for length/lit and distance code lengths */
-var            LEN_ = 20;      /* i: same as LEN below, but only first time in */
-var            LEN = 21;       /* i: waiting for length/lit/eob code */
-var            LENEXT = 22;    /* i: waiting for length extra bits */
-var            DIST = 23;      /* i: waiting for distance code */
-var            DISTEXT = 24;   /* i: waiting for distance extra bits */
-var            MATCH = 25;     /* o: waiting for output space to copy string */
-var            LIT = 26;       /* o: waiting for output space to write literal */
-var    CHECK = 27;     /* i: waiting for 32-bit check value */
-var    LENGTH = 28;    /* i: waiting for 32-bit length (gzip) */
-var    DONE = 29;      /* finished check, done -- remain here until reset */
-var    BAD = 30;       /* got a data error -- remain here until reset */
-var    MEM = 31;       /* got an inflate() memory error -- remain here until reset */
-var    SYNC = 32;      /* looking for synchronization bytes to restart inflate() */
-
-/* ===========================================================================*/
-
-
-
-var ENOUGH_LENS = 852;
-var ENOUGH_DISTS = 592;
-//var ENOUGH =  (ENOUGH_LENS+ENOUGH_DISTS);
-
-var MAX_WBITS = 15;
-/* 32K LZ77 window */
-var DEF_WBITS = MAX_WBITS;
-
-
-function ZSWAP32(q) {
-  return  (((q >>> 24) & 0xff) +
-          ((q >>> 8) & 0xff00) +
-          ((q & 0xff00) << 8) +
-          ((q & 0xff) << 24));
-}
-
-
-function InflateState() {
-  this.mode = 0;             /* current inflate mode */
-  this.last = false;          /* true if processing last block */
-  this.wrap = 0;              /* bit 0 true for zlib, bit 1 true for gzip */
-  this.havedict = false;      /* true if dictionary provided */
-  this.flags = 0;             /* gzip header method and flags (0 if zlib) */
-  this.dmax = 0;              /* zlib header max distance (INFLATE_STRICT) */
-  this.check = 0;             /* protected copy of check value */
-  this.total = 0;             /* protected copy of output count */
-  // TODO: may be {}
-  this.head = null;           /* where to save gzip header information */
-
-  /* sliding window */
-  this.wbits = 0;             /* log base 2 of requested window size */
-  this.wsize = 0;             /* window size or zero if not using window */
-  this.whave = 0;             /* valid bytes in the window */
-  this.wnext = 0;             /* window write index */
-  this.window = null;         /* allocated sliding window, if needed */
-
-  /* bit accumulator */
-  this.hold = 0;              /* input bit accumulator */
-  this.bits = 0;              /* number of bits in "in" */
-
-  /* for string and stored block copying */
-  this.length = 0;            /* literal or length of data to copy */
-  this.offset = 0;            /* distance back to copy string from */
-
-  /* for table and code decoding */
-  this.extra = 0;             /* extra bits needed */
-
-  /* fixed and dynamic code tables */
-  this.lencode = null;          /* starting table for length/literal codes */
-  this.distcode = null;         /* starting table for distance codes */
-  this.lenbits = 0;           /* index bits for lencode */
-  this.distbits = 0;          /* index bits for distcode */
-
-  /* dynamic table building */
-  this.ncode = 0;             /* number of code length code lengths */
-  this.nlen = 0;              /* number of length code lengths */
-  this.ndist = 0;             /* number of distance code lengths */
-  this.have = 0;              /* number of code lengths in lens[] */
-  this.next = null;              /* next available space in codes[] */
-
-  this.lens = new utils.Buf16(320); /* temporary storage for code lengths */
-  this.work = new utils.Buf16(288); /* work area for code table building */
-
-  /*
-   because we don't have pointers in js, we use lencode and distcode directly
-   as buffers so we don't need codes
-  */
-  //this.codes = new utils.Buf32(ENOUGH);       /* space for code tables */
-  this.lendyn = null;              /* dynamic table for length/literal codes (JS specific) */
-  this.distdyn = null;             /* dynamic table for distance codes (JS specific) */
-  this.sane = 0;                   /* if false, allow invalid distance too far */
-  this.back = 0;                   /* bits back of last unprocessed length/lit */
-  this.was = 0;                    /* initial length of match */
-}
-
-function inflateResetKeep(strm) {
-  var state;
-
-  if (!strm || !strm.state) { return Z_STREAM_ERROR; }
-  state = strm.state;
-  strm.total_in = strm.total_out = state.total = 0;
-  strm.msg = ''; /*Z_NULL*/
-  if (state.wrap) {       /* to support ill-conceived Java test suite */
-    strm.adler = state.wrap & 1;
-  }
-  state.mode = HEAD;
-  state.last = 0;
-  state.havedict = 0;
-  state.dmax = 32768;
-  state.head = null/*Z_NULL*/;
-  state.hold = 0;
-  state.bits = 0;
-  //state.lencode = state.distcode = state.next = state.codes;
-  state.lencode = state.lendyn = new utils.Buf32(ENOUGH_LENS);
-  state.distcode = state.distdyn = new utils.Buf32(ENOUGH_DISTS);
-
-  state.sane = 1;
-  state.back = -1;
-  //Tracev((stderr, "inflate: reset\n"));
-  return Z_OK;
-}
-
-function inflateReset(strm) {
-  var state;
-
-  if (!strm || !strm.state) { return Z_STREAM_ERROR; }
-  state = strm.state;
-  state.wsize = 0;
-  state.whave = 0;
-  state.wnext = 0;
-  return inflateResetKeep(strm);
-
-}
-
-function inflateReset2(strm, windowBits) {
-  var wrap;
-  var state;
-
-  /* get the state */
-  if (!strm || !strm.state) { return Z_STREAM_ERROR; }
-  state = strm.state;
-
-  /* extract wrap request from windowBits parameter */
-  if (windowBits < 0) {
-    wrap = 0;
-    windowBits = -windowBits;
-  }
-  else {
-    wrap = (windowBits >> 4) + 1;
-    if (windowBits < 48) {
-      windowBits &= 15;
-    }
-  }
-
-  /* set number of window bits, free window if different */
-  if (windowBits && (windowBits < 8 || windowBits > 15)) {
-    return Z_STREAM_ERROR;
-  }
-  if (state.window !== null && state.wbits !== windowBits) {
-    state.window = null;
-  }
-
-  /* update state and reset the rest of it */
-  state.wrap = wrap;
-  state.wbits = windowBits;
-  return inflateReset(strm);
-}
-
-function inflateInit2(strm, windowBits) {
-  var ret;
-  var state;
-
-  if (!strm) { return Z_STREAM_ERROR; }
-  //strm.msg = Z_NULL;                 /* in case we return an error */
-
-  state = new InflateState();
-
-  //if (state === Z_NULL) return Z_MEM_ERROR;
-  //Tracev((stderr, "inflate: allocated\n"));
-  strm.state = state;
-  state.window = null/*Z_NULL*/;
-  ret = inflateReset2(strm, windowBits);
-  if (ret !== Z_OK) {
-    strm.state = null/*Z_NULL*/;
-  }
-  return ret;
-}
-
-function inflateInit(strm) {
-  return inflateInit2(strm, DEF_WBITS);
-}
-
-
-/*
- Return state with length and distance decoding tables and index sizes set to
- fixed code decoding.  Normally this returns fixed tables from inffixed.h.
- If BUILDFIXED is defined, then instead this routine builds the tables the
- first time it's called, and returns those tables the first time and
- thereafter.  This reduces the size of the code by about 2K bytes, in
- exchange for a little execution time.  However, BUILDFIXED should not be
- used for threaded applications, since the rewriting of the tables and virgin
- may not be thread-safe.
- */
-var virgin = true;
-
-var lenfix, distfix; // We have no pointers in JS, so keep tables separate
-
-function fixedtables(state) {
-  /* build fixed huffman tables if first call (may not be thread safe) */
-  if (virgin) {
-    var sym;
-
-    lenfix = new utils.Buf32(512);
-    distfix = new utils.Buf32(32);
-
-    /* literal/length table */
-    sym = 0;
-    while (sym < 144) { state.lens[sym++] = 8; }
-    while (sym < 256) { state.lens[sym++] = 9; }
-    while (sym < 280) { state.lens[sym++] = 7; }
-    while (sym < 288) { state.lens[sym++] = 8; }
-
-    inflate_table(LENS,  state.lens, 0, 288, lenfix,   0, state.work, {bits: 9});
-
-    /* distance table */
-    sym = 0;
-    while (sym < 32) { state.lens[sym++] = 5; }
-
-    inflate_table(DISTS, state.lens, 0, 32,   distfix, 0, state.work, {bits: 5});
-
-    /* do this just once */
-    virgin = false;
-  }
-
-  state.lencode = lenfix;
-  state.lenbits = 9;
-  state.distcode = distfix;
-  state.distbits = 5;
-}
-
-
-/*
- Update the window with the last wsize (normally 32K) bytes written before
- returning.  If window does not exist yet, create it.  This is only called
- when a window is already in use, or when output has been written during this
- inflate call, but the end of the deflate stream has not been reached yet.
- It is also called to create a window for dictionary data when a dictionary
- is loaded.
-
- Providing output buffers larger than 32K to inflate() should provide a speed
- advantage, since only the last 32K of output is copied to the sliding window
- upon return from inflate(), and since all distances after the first 32K of
- output will fall in the output data, making match copies simpler and faster.
- The advantage may be dependent on the size of the processor's data caches.
- */
-function updatewindow(strm, src, end, copy) {
-  var dist;
-  var state = strm.state;
-
-  /* if it hasn't been done already, allocate space for the window */
-  if (state.window === null) {
-    state.wsize = 1 << state.wbits;
-    state.wnext = 0;
-    state.whave = 0;
-
-    state.window = new utils.Buf8(state.wsize);
-  }
-
-  /* copy state->wsize or less output bytes into the circular window */
-  if (copy >= state.wsize) {
-    utils.arraySet(state.window,src, end - state.wsize, state.wsize, 0);
-    state.wnext = 0;
-    state.whave = state.wsize;
-  }
-  else {
-    dist = state.wsize - state.wnext;
-    if (dist > copy) {
-      dist = copy;
-    }
-    //zmemcpy(state->window + state->wnext, end - copy, dist);
-    utils.arraySet(state.window,src, end - copy, dist, state.wnext);
-    copy -= dist;
-    if (copy) {
-      //zmemcpy(state->window, end - copy, copy);
-      utils.arraySet(state.window,src, end - copy, copy, 0);
-      state.wnext = copy;
-      state.whave = state.wsize;
-    }
-    else {
-      state.wnext += dist;
-      if (state.wnext === state.wsize) { state.wnext = 0; }
-      if (state.whave < state.wsize) { state.whave += dist; }
-    }
-  }
-  return 0;
-}
-
-function inflate(strm, flush) {
-  var state;
-  var input, output;          // input/output buffers
-  var next;                   /* next input INDEX */
-  var put;                    /* next output INDEX */
-  var have, left;             /* available input and output */
-  var hold;                   /* bit buffer */
-  var bits;                   /* bits in bit buffer */
-  var _in, _out;              /* save starting available input and output */
-  var copy;                   /* number of stored or match bytes to copy */
-  var from;                   /* where to copy match bytes from */
-  var from_source;
-  var here = 0;               /* current decoding table entry */
-  var here_bits, here_op, here_val; // paked "here" denormalized (JS specific)
-  //var last;                   /* parent table entry */
-  var last_bits, last_op, last_val; // paked "last" denormalized (JS specific)
-  var len;                    /* length to copy for repeats, bits to drop */
-  var ret;                    /* return code */
-  var hbuf = new utils.Buf8(4);    /* buffer for gzip header crc calculation */
-  var opts;
-
-  var n; // temporary var for NEED_BITS
-
-  var order = /* permutation of code lengths */
-    [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15];
-
-
-  if (!strm || !strm.state || !strm.output ||
-      (!strm.input && strm.avail_in !== 0)) {
-    return Z_STREAM_ERROR;
-  }
-
-  state = strm.state;
-  if (state.mode === TYPE) { state.mode = TYPEDO; }    /* skip check */
-
-
-  //--- LOAD() ---
-  put = strm.next_out;
-  output = strm.output;
-  left = strm.avail_out;
-  next = strm.next_in;
-  input = strm.input;
-  have = strm.avail_in;
-  hold = state.hold;
-  bits = state.bits;
-  //---
-
-  _in = have;
-  _out = left;
-  ret = Z_OK;
-
-  inf_leave: // goto emulation
-  for (;;) {
-    switch (state.mode) {
-    case HEAD:
-      if (state.wrap === 0) {
-        state.mode = TYPEDO;
-        break;
-      }
-      //=== NEEDBITS(16);
-      while (bits < 16) {
-        if (have === 0) { break inf_leave; }
-        have--;
-        hold += input[next++] << bits;
-        bits += 8;
-      }
-      //===//
-      if ((state.wrap & 2) && hold === 0x8b1f) {  /* gzip header */
-        state.check = 0/*crc32(0L, Z_NULL, 0)*/;
-        //=== CRC2(state.check, hold);
-        hbuf[0] = hold & 0xff;
-        hbuf[1] = (hold >>> 8) & 0xff;
-        state.check = crc32(state.check, hbuf, 2, 0);
-        //===//
-
-        //=== INITBITS();
-        hold = 0;
-        bits = 0;
-        //===//
-        state.mode = FLAGS;
-        break;
-      }
-      state.flags = 0;           /* expect zlib header */
-      if (state.head) {
-        state.head.done = false;
-      }
-      if (!(state.wrap & 1) ||   /* check if zlib header allowed */
-        (((hold & 0xff)/*BITS(8)*/ << 8) + (hold >> 8)) % 31) {
-        strm.msg = 'incorrect header check';
-        state.mode = BAD;
-        break;
-      }
-      if ((hold & 0x0f)/*BITS(4)*/ !== Z_DEFLATED) {
-        strm.msg = 'unknown compression method';
-        state.mode = BAD;
-        break;
-      }
-      //--- DROPBITS(4) ---//
-      hold >>>= 4;
-      bits -= 4;
-      //---//
-      len = (hold & 0x0f)/*BITS(4)*/ + 8;
-      if (state.wbits === 0) {
-        state.wbits = len;
-      }
-      else if (len > state.wbits) {
-        strm.msg = 'invalid window size';
-        state.mode = BAD;
-        break;
-      }
-      state.dmax = 1 << len;
-      //Tracev((stderr, "inflate:   zlib header ok\n"));
-      strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/;
-      state.mode = hold & 0x200 ? DICTID : TYPE;
-      //=== INITBITS();
-      hold = 0;
-      bits = 0;
-      //===//
-      break;
-    case FLAGS:
-      //=== NEEDBITS(16); */
-      while (bits < 16) {
-        if (have === 0) { break inf_leave; }
-        have--;
-        hold += input[next++] << bits;
-        bits += 8;
-      }
-      //===//
-      state.flags = hold;
-      if ((state.flags & 0xff) !== Z_DEFLATED) {
-        strm.msg = 'unknown compression method';
-        state.mode = BAD;
-        break;
-      }
-      if (state.flags & 0xe000) {
-        strm.msg = 'unknown header flags set';
-        state.mode = BAD;
-        break;
-      }
-      if (state.head) {
-        state.head.text = ((hold >> 8) & 1);
-      }
-      if (state.flags & 0x0200) {
-        //=== CRC2(state.check, hold);
-        hbuf[0] = hold & 0xff;
-        hbuf[1] = (hold >>> 8) & 0xff;
-        state.check = crc32(state.check, hbuf, 2, 0);
-        //===//
-      }
-      //=== INITBITS();
-      hold = 0;
-      bits = 0;
-      //===//
-      state.mode = TIME;
-      /* falls through */
-    case TIME:
-      //=== NEEDBITS(32); */
-      while (bits < 32) {
-        if (have === 0) { break inf_leave; }
-        have--;
-        hold += input[next++] << bits;
-        bits += 8;
-      }
-      //===//
-      if (state.head) {
-        state.head.time = hold;
-      }
-      if (state.flags & 0x0200) {
-        //=== CRC4(state.check, hold)
-        hbuf[0] = hold & 0xff;
-        hbuf[1] = (hold >>> 8) & 0xff;
-        hbuf[2] = (hold >>> 16) & 0xff;
-        hbuf[3] = (hold >>> 24) & 0xff;
-        state.check = crc32(state.check, hbuf, 4, 0);
-        //===
-      }
-      //=== INITBITS();
-      hold = 0;
-      bits = 0;
-      //===//
-      state.mode = OS;
-      /* falls through */
-    case OS:
-      //=== NEEDBITS(16); */
-      while (bits < 16) {
-        if (have === 0) { break inf_leave; }
-        have--;
-        hold += input[next++] << bits;
-        bits += 8;
-      }
-      //===//
-      if (state.head) {
-        state.head.xflags = (hold & 0xff);
-        state.head.os = (hold >> 8);
-      }
-      if (state.flags & 0x0200) {
-        //=== CRC2(state.check, hold);
-        hbuf[0] = hold & 0xff;
-        hbuf[1] = (hold >>> 8) & 0xff;
-        state.check = crc32(state.check, hbuf, 2, 0);
-        //===//
-      }
-      //=== INITBITS();
-      hold = 0;
-      bits = 0;
-      //===//
-      state.mode = EXLEN;
-      /* falls through */
-    case EXLEN:
-      if (state.flags & 0x0400) {
-        //=== NEEDBITS(16); */
-        while (bits < 16) {
-          if (have === 0) { break inf_leave; }
-          have--;
-          hold += input[next++] << bits;
-          bits += 8;
-        }
-        //===//
-        state.length = hold;
-        if (state.head) {
-          state.head.extra_len = hold;
-        }
-        if (state.flags & 0x0200) {
-          //=== CRC2(state.check, hold);
-          hbuf[0] = hold & 0xff;
-          hbuf[1] = (hold >>> 8) & 0xff;
-          state.check = crc32(state.check, hbuf, 2, 0);
-          //===//
-        }
-        //=== INITBITS();
-        hold = 0;
-        bits = 0;
-        //===//
-      }
-      else if (state.head) {
-        state.head.extra = null/*Z_NULL*/;
-      }
-      state.mode = EXTRA;
-      /* falls through */
-    case EXTRA:
-      if (state.flags & 0x0400) {
-        copy = state.length;
-        if (copy > have) { copy = have; }
-        if (copy) {
-          if (state.head) {
-            len = state.head.extra_len - state.length;
-            if (!state.head.extra) {
-              // Use untyped array for more conveniend processing later
-              state.head.extra = new Array(state.head.extra_len);
-            }
-            utils.arraySet(
-              state.head.extra,
-              input,
-              next,
-              // extra field is limited to 65536 bytes
-              // - no need for additional size check
-              copy,
-              /*len + copy > state.head.extra_max - len ? state.head.extra_max : copy,*/
-              len
-            );
-            //zmemcpy(state.head.extra + len, next,
-            //        len + copy > state.head.extra_max ?
-            //        state.head.extra_max - len : copy);
-          }
-          if (state.flags & 0x0200) {
-            state.check = crc32(state.check, input, copy, next);
-          }
-          have -= copy;
-          next += copy;
-          state.length -= copy;
-        }
-        if (state.length) { break inf_leave; }
-      }
-      state.length = 0;
-      state.mode = NAME;
-      /* falls through */
-    case NAME:
-      if (state.flags & 0x0800) {
-        if (have === 0) { break inf_leave; }
-        copy = 0;
-        do {
-          // TODO: 2 or 1 bytes?
-          len = input[next + copy++];
-          /* use constant limit because in js we should not preallocate memory */
-          if (state.head && len &&
-              (state.length < 65536 /*state.head.name_max*/)) {
-            state.head.name += String.fromCharCode(len);
-          }
-        } while (len && copy < have);
-
-        if (state.flags & 0x0200) {
-          state.check = crc32(state.check, input, copy, next);
-        }
-        have -= copy;
-        next += copy;
-        if (len) { break inf_leave; }
-      }
-      else if (state.head) {
-        state.head.name = null;
-      }
-      state.length = 0;
-      state.mode = COMMENT;
-      /* falls through */
-    case COMMENT:
-      if (state.flags & 0x1000) {
-        if (have === 0) { break inf_leave; }
-        copy = 0;
-        do {
-          len = input[next + copy++];
-          /* use constant limit because in js we should not preallocate memory */
-          if (state.head && len &&
-              (state.length < 65536 /*state.head.comm_max*/)) {
-            state.head.comment += String.fromCharCode(len);
-          }
-        } while (len && copy < have);
-        if (state.flags & 0x0200) {
-          state.check = crc32(state.check, input, copy, next);
-        }
-        have -= copy;
-        next += copy;
-        if (len) { break inf_leave; }
-      }
-      else if (state.head) {
-        state.head.comment = null;
-      }
-      state.mode = HCRC;
-      /* falls through */
-    case HCRC:
-      if (state.flags & 0x0200) {
-        //=== NEEDBITS(16); */
-        while (bits < 16) {
-          if (have === 0) { break inf_leave; }
-          have--;
-          hold += input[next++] << bits;
-          bits += 8;
-        }
-        //===//
-        if (hold !== (state.check & 0xffff)) {
-          strm.msg = 'header crc mismatch';
-          state.mode = BAD;
-          break;
-        }
-        //=== INITBITS();
-        hold = 0;
-        bits = 0;
-        //===//
-      }
-      if (state.head) {
-        state.head.hcrc = ((state.flags >> 9) & 1);
-        state.head.done = true;
-      }
-      strm.adler = state.check = 0 /*crc32(0L, Z_NULL, 0)*/;
-      state.mode = TYPE;
-      break;
-    case DICTID:
-      //=== NEEDBITS(32); */
-      while (bits < 32) {
-        if (have === 0) { break inf_leave; }
-        have--;
-        hold += input[next++] << bits;
-        bits += 8;
-      }
-      //===//
-      strm.adler = state.check = ZSWAP32(hold);
-      //=== INITBITS();
-      hold = 0;
-      bits = 0;
-      //===//
-      state.mode = DICT;
-      /* falls through */
-    case DICT:
-      if (state.havedict === 0) {
-        //--- RESTORE() ---
-        strm.next_out = put;
-        strm.avail_out = left;
-        strm.next_in = next;
-        strm.avail_in = have;
-        state.hold = hold;
-        state.bits = bits;
-        //---
-        return Z_NEED_DICT;
-      }
-      strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/;
-      state.mode = TYPE;
-      /* falls through */
-    case TYPE:
-      if (flush === Z_BLOCK || flush === Z_TREES) { break inf_leave; }
-      /* falls through */
-    case TYPEDO:
-      if (state.last) {
-        //--- BYTEBITS() ---//
-        hold >>>= bits & 7;
-        bits -= bits & 7;
-        //---//
-        state.mode = CHECK;
-        break;
-      }
-      //=== NEEDBITS(3); */
-      while (bits < 3) {
-        if (have === 0) { break inf_leave; }
-        have--;
-        hold += input[next++] << bits;
-        bits += 8;
-      }
-      //===//
-      state.last = (hold & 0x01)/*BITS(1)*/;
-      //--- DROPBITS(1) ---//
-      hold >>>= 1;
-      bits -= 1;
-      //---//
-
-      switch ((hold & 0x03)/*BITS(2)*/) {
-      case 0:                             /* stored block */
-        //Tracev((stderr, "inflate:     stored block%s\n",
-        //        state.last ? " (last)" : ""));
-        state.mode = STORED;
-        break;
-      case 1:                             /* fixed block */
-        fixedtables(state);
-        //Tracev((stderr, "inflate:     fixed codes block%s\n",
-        //        state.last ? " (last)" : ""));
-        state.mode = LEN_;             /* decode codes */
-        if (flush === Z_TREES) {
-          //--- DROPBITS(2) ---//
-          hold >>>= 2;
-          bits -= 2;
-          //---//
-          break inf_leave;
-        }
-        break;
-      case 2:                             /* dynamic block */
-        //Tracev((stderr, "inflate:     dynamic codes block%s\n",
-        //        state.last ? " (last)" : ""));
-        state.mode = TABLE;
-        break;
-      case 3:
-        strm.msg = 'invalid block type';
-        state.mode = BAD;
-      }
-      //--- DROPBITS(2) ---//
-      hold >>>= 2;
-      bits -= 2;
-      //---//
-      break;
-    case STORED:
-      //--- BYTEBITS() ---// /* go to byte boundary */
-      hold >>>= bits & 7;
-      bits -= bits & 7;
-      //---//
-      //=== NEEDBITS(32); */
-      while (bits < 32) {
-        if (have === 0) { break inf_leave; }
-        have--;
-        hold += input[next++] << bits;
-        bits += 8;
-      }
-      //===//
-      if ((hold & 0xffff) !== ((hold >>> 16) ^ 0xffff)) {
-        strm.msg = 'invalid stored block lengths';
-        state.mode = BAD;
-        break;
-      }
-      state.length = hold & 0xffff;
-      //Tracev((stderr, "inflate:       stored length %u\n",
-      //        state.length));
-      //=== INITBITS();
-      hold = 0;
-      bits = 0;
-      //===//
-      state.mode = COPY_;
-      if (flush === Z_TREES) { break inf_leave; }
-      /* falls through */
-    case COPY_:
-      state.mode = COPY;
-      /* falls through */
-    case COPY:
-      copy = state.length;
-      if (copy) {
-        if (copy > have) { copy = have; }
-        if (copy > left) { copy = left; }
-        if (copy === 0) { break inf_leave; }
-        //--- zmemcpy(put, next, copy); ---
-        utils.arraySet(output, input, next, copy, put);
-        //---//
-        have -= copy;
-        next += copy;
-        left -= copy;
-        put += copy;
-        state.length -= copy;
-        break;
-      }
-      //Tracev((stderr, "inflate:       stored end\n"));
-      state.mode = TYPE;
-      break;
-    case TABLE:
-      //=== NEEDBITS(14); */
-      while (bits < 14) {
-        if (have === 0) { break inf_leave; }
-        have--;
-        hold += input[next++] << bits;
-        bits += 8;
-      }
-      //===//
-      state.nlen = (hold & 0x1f)/*BITS(5)*/ + 257;
-      //--- DROPBITS(5) ---//
-      hold >>>= 5;
-      bits -= 5;
-      //---//
-      state.ndist = (hold & 0x1f)/*BITS(5)*/ + 1;
-      //--- DROPBITS(5) ---//
-      hold >>>= 5;
-      bits -= 5;
-      //---//
-      state.ncode = (hold & 0x0f)/*BITS(4)*/ + 4;
-      //--- DROPBITS(4) ---//
-      hold >>>= 4;
-      bits -= 4;
-      //---//
-//#ifndef PKZIP_BUG_WORKAROUND
-      if (state.nlen > 286 || state.ndist > 30) {
-        strm.msg = 'too many length or distance symbols';
-        state.mode = BAD;
-        break;
-      }
-//#endif
-      //Tracev((stderr, "inflate:       table sizes ok\n"));
-      state.have = 0;
-      state.mode = LENLENS;
-      /* falls through */
-    case LENLENS:
-      while (state.have < state.ncode) {
-        //=== NEEDBITS(3);
-        while (bits < 3) {
-          if (have === 0) { break inf_leave; }
-          have--;
-          hold += input[next++] << bits;
-          bits += 8;
-        }
-        //===//
-        state.lens[order[state.have++]] = (hold & 0x07);//BITS(3);
-        //--- DROPBITS(3) ---//
-        hold >>>= 3;
-        bits -= 3;
-        //---//
-      }
-      while (state.have < 19) {
-        state.lens[order[state.have++]] = 0;
-      }
-      // We have separate tables & no pointers. 2 commented lines below not needed.
-      //state.next = state.codes;
-      //state.lencode = state.next;
-      // Switch to use dynamic table
-      state.lencode = state.lendyn;
-      state.lenbits = 7;
-
-      opts = {bits: state.lenbits};
-      ret = inflate_table(CODES, state.lens, 0, 19, state.lencode, 0, state.work, opts);
-      state.lenbits = opts.bits;
-
-      if (ret) {
-        strm.msg = 'invalid code lengths set';
-        state.mode = BAD;
-        break;
-      }
-      //Tracev((stderr, "inflate:       code lengths ok\n"));
-      state.have = 0;
-      state.mode = CODELENS;
-      /* falls through */
-    case CODELENS:
-      while (state.have < state.nlen + state.ndist) {
-        for (;;) {
-          here = state.lencode[hold & ((1 << state.lenbits) - 1)];/*BITS(state.lenbits)*/
-          here_bits = here >>> 24;
-          here_op = (here >>> 16) & 0xff;
-          here_val = here & 0xffff;
-
-          if ((here_bits) <= bits) { break; }
-          //--- PULLBYTE() ---//
-          if (have === 0) { break inf_leave; }
-          have--;
-          hold += input[next++] << bits;
-          bits += 8;
-          //---//
-        }
-        if (here_val < 16) {
-          //--- DROPBITS(here.bits) ---//
-          hold >>>= here_bits;
-          bits -= here_bits;
-          //---//
-          state.lens[state.have++] = here_val;
-        }
-        else {
-          if (here_val === 16) {
-            //=== NEEDBITS(here.bits + 2);
-            n = here_bits + 2;
-            while (bits < n) {
-              if (have === 0) { break inf_leave; }
-              have--;
-              hold += input[next++] << bits;
-              bits += 8;
-            }
-            //===//
-            //--- DROPBITS(here.bits) ---//
-            hold >>>= here_bits;
-            bits -= here_bits;
-            //---//
-            if (state.have === 0) {
-              strm.msg = 'invalid bit length repeat';
-              state.mode = BAD;
-              break;
-            }
-            len = state.lens[state.have - 1];
-            copy = 3 + (hold & 0x03);//BITS(2);
-            //--- DROPBITS(2) ---//
-            hold >>>= 2;
-            bits -= 2;
-            //---//
-          }
-          else if (here_val === 17) {
-            //=== NEEDBITS(here.bits + 3);
-            n = here_bits + 3;
-            while (bits < n) {
-              if (have === 0) { break inf_leave; }
-              have--;
-              hold += input[next++] << bits;
-              bits += 8;
-            }
-            //===//
-            //--- DROPBITS(here.bits) ---//
-            hold >>>= here_bits;
-            bits -= here_bits;
-            //---//
-            len = 0;
-            copy = 3 + (hold & 0x07);//BITS(3);
-            //--- DROPBITS(3) ---//
-            hold >>>= 3;
-            bits -= 3;
-            //---//
-          }
-          else {
-            //=== NEEDBITS(here.bits + 7);
-            n = here_bits + 7;
-            while (bits < n) {
-              if (have === 0) { break inf_leave; }
-              have--;
-              hold += input[next++] << bits;
-              bits += 8;
-            }
-            //===//
-            //--- DROPBITS(here.bits) ---//
-            hold >>>= here_bits;
-            bits -= here_bits;
-            //---//
-            len = 0;
-            copy = 11 + (hold & 0x7f);//BITS(7);
-            //--- DROPBITS(7) ---//
-            hold >>>= 7;
-            bits -= 7;
-            //---//
-          }
-          if (state.have + copy > state.nlen + state.ndist) {
-            strm.msg = 'invalid bit length repeat';
-            state.mode = BAD;
-            break;
-          }
-          while (copy--) {
-            state.lens[state.have++] = len;
-          }
-        }
-      }
-
-      /* handle error breaks in while */
-      if (state.mode === BAD) { break; }
-
-      /* check for end-of-block code (better have one) */
-      if (state.lens[256] === 0) {
-        strm.msg = 'invalid code -- missing end-of-block';
-        state.mode = BAD;
-        break;
-      }
-
-      /* build code tables -- note: do not change the lenbits or distbits
-         values here (9 and 6) without reading the comments in inftrees.h
-         concerning the ENOUGH constants, which depend on those values */
-      state.lenbits = 9;
-
-      opts = {bits: state.lenbits};
-      ret = inflate_table(LENS, state.lens, 0, state.nlen, state.lencode, 0, state.work, opts);
-      // We have separate tables & no pointers. 2 commented lines below not needed.
-      // state.next_index = opts.table_index;
-      state.lenbits = opts.bits;
-      // state.lencode = state.next;
-
-      if (ret) {
-        strm.msg = 'invalid literal/lengths set';
-        state.mode = BAD;
-        break;
-      }
-
-      state.distbits = 6;
-      //state.distcode.copy(state.codes);
-      // Switch to use dynamic table
-      state.distcode = state.distdyn;
-      opts = {bits: state.distbits};
-      ret = inflate_table(DISTS, state.lens, state.nlen, state.ndist, state.distcode, 0, state.work, opts);
-      // We have separate tables & no pointers. 2 commented lines below not needed.
-      // state.next_index = opts.table_index;
-      state.distbits = opts.bits;
-      // state.distcode = state.next;
-
-      if (ret) {
-        strm.msg = 'invalid distances set';
-        state.mode = BAD;
-        break;
-      }
-      //Tracev((stderr, 'inflate:       codes ok\n'));
-      state.mode = LEN_;
-      if (flush === Z_TREES) { break inf_leave; }
-      /* falls through */
-    case LEN_:
-      state.mode = LEN;
-      /* falls through */
-    case LEN:
-      if (have >= 6 && left >= 258) {
-        //--- RESTORE() ---
-        strm.next_out = put;
-        strm.avail_out = left;
-        strm.next_in = next;
-        strm.avail_in = have;
-        state.hold = hold;
-        state.bits = bits;
-        //---
-        inflate_fast(strm, _out);
-        //--- LOAD() ---
-        put = strm.next_out;
-        output = strm.output;
-        left = strm.avail_out;
-        next = strm.next_in;
-        input = strm.input;
-        have = strm.avail_in;
-        hold = state.hold;
-        bits = state.bits;
-        //---
-
-        if (state.mode === TYPE) {
-          state.back = -1;
-        }
-        break;
-      }
-      state.back = 0;
-      for (;;) {
-        here = state.lencode[hold & ((1 << state.lenbits) -1)];  /*BITS(state.lenbits)*/
-        here_bits = here >>> 24;
-        here_op = (here >>> 16) & 0xff;
-        here_val = here & 0xffff;
-
-        if (here_bits <= bits) { break; }
-        //--- PULLBYTE() ---//
-        if (have === 0) { break inf_leave; }
-        have--;
-        hold += input[next++] << bits;
-        bits += 8;
-        //---//
-      }
-      if (here_op && (here_op & 0xf0) === 0) {
-        last_bits = here_bits;
-        last_op = here_op;
-        last_val = here_val;
-        for (;;) {
-          here = state.lencode[last_val +
-                  ((hold & ((1 << (last_bits + last_op)) -1))/*BITS(last.bits + last.op)*/ >> last_bits)];
-          here_bits = here >>> 24;
-          here_op = (here >>> 16) & 0xff;
-          here_val = here & 0xffff;
-
-          if ((last_bits + here_bits) <= bits) { break; }
-          //--- PULLBYTE() ---//
-          if (have === 0) { break inf_leave; }
-          have--;
-          hold += input[next++] << bits;
-          bits += 8;
-          //---//
-        }
-        //--- DROPBITS(last.bits) ---//
-        hold >>>= last_bits;
-        bits -= last_bits;
-        //---//
-        state.back += last_bits;
-      }
-      //--- DROPBITS(here.bits) ---//
-      hold >>>= here_bits;
-      bits -= here_bits;
-      //---//
-      state.back += here_bits;
-      state.length = here_val;
-      if (here_op === 0) {
-        //Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
-        //        "inflate:         literal '%c'\n" :
-        //        "inflate:         literal 0x%02x\n", here.val));
-        state.mode = LIT;
-        break;
-      }
-      if (here_op & 32) {
-        //Tracevv((stderr, "inflate:         end of block\n"));
-        state.back = -1;
-        state.mode = TYPE;
-        break;
-      }
-      if (here_op & 64) {
-        strm.msg = 'invalid literal/length code';
-        state.mode = BAD;
-        break;
-      }
-      state.extra = here_op & 15;
-      state.mode = LENEXT;
-      /* falls through */
-    case LENEXT:
-      if (state.extra) {
-        //=== NEEDBITS(state.extra);
-        n = state.extra;
-        while (bits < n) {
-          if (have === 0) { break inf_leave; }
-          have--;
-          hold += input[next++] << bits;
-          bits += 8;
-        }
-        //===//
-        state.length += hold & ((1 << state.extra) -1)/*BITS(state.extra)*/;
-        //--- DROPBITS(state.extra) ---//
-        hold >>>= state.extra;
-        bits -= state.extra;
-        //---//
-        state.back += state.extra;
-      }
-      //Tracevv((stderr, "inflate:         length %u\n", state.length));
-      state.was = state.length;
-      state.mode = DIST;
-      /* falls through */
-    case DIST:
-      for (;;) {
-        here = state.distcode[hold & ((1 << state.distbits) -1)];/*BITS(state.distbits)*/
-        here_bits = here >>> 24;
-        here_op = (here >>> 16) & 0xff;
-        here_val = here & 0xffff;
-
-        if ((here_bits) <= bits) { break; }
-        //--- PULLBYTE() ---//
-        if (have === 0) { break inf_leave; }
-        have--;
-        hold += input[next++] << bits;
-        bits += 8;
-        //---//
-      }
-      if ((here_op & 0xf0) === 0) {
-        last_bits = here_bits;
-        last_op = here_op;
-        last_val = here_val;
-        for (;;) {
-          here = state.distcode[last_val +
-                  ((hold & ((1 << (last_bits + last_op)) -1))/*BITS(last.bits + last.op)*/ >> last_bits)];
-          here_bits = here >>> 24;
-          here_op = (here >>> 16) & 0xff;
-          here_val = here & 0xffff;
-
-          if ((last_bits + here_bits) <= bits) { break; }
-          //--- PULLBYTE() ---//
-          if (have === 0) { break inf_leave; }
-          have--;
-          hold += input[next++] << bits;
-          bits += 8;
-          //---//
-        }
-        //--- DROPBITS(last.bits) ---//
-        hold >>>= last_bits;
-        bits -= last_bits;
-        //---//
-        state.back += last_bits;
-      }
-      //--- DROPBITS(here.bits) ---//
-      hold >>>= here_bits;
-      bits -= here_bits;
-      //---//
-      state.back += here_bits;
-      if (here_op & 64) {
-        strm.msg = 'invalid distance code';
-        state.mode = BAD;
-        break;
-      }
-      state.offset = here_val;
-      state.extra = (here_op) & 15;
-      state.mode = DISTEXT;
-      /* falls through */
-    case DISTEXT:
-      if (state.extra) {
-        //=== NEEDBITS(state.extra);
-        n = state.extra;
-        while (bits < n) {
-          if (have === 0) { break inf_leave; }
-          have--;
-          hold += input[next++] << bits;
-          bits += 8;
-        }
-        //===//
-        state.offset += hold & ((1 << state.extra) -1)/*BITS(state.extra)*/;
-        //--- DROPBITS(state.extra) ---//
-        hold >>>= state.extra;
-        bits -= state.extra;
-        //---//
-        state.back += state.extra;
-      }
-//#ifdef INFLATE_STRICT
-      if (state.offset > state.dmax) {
-        strm.msg = 'invalid distance too far back';
-        state.mode = BAD;
-        break;
-      }
-//#endif
-      //Tracevv((stderr, "inflate:         distance %u\n", state.offset));
-      state.mode = MATCH;
-      /* falls through */
-    case MATCH:
-      if (left === 0) { break inf_leave; }
-      copy = _out - left;
-      if (state.offset > copy) {         /* copy from window */
-        copy = state.offset - copy;
-        if (copy > state.whave) {
-          if (state.sane) {
-            strm.msg = 'invalid distance too far back';
-            state.mode = BAD;
-            break;
-          }
-// (!) This block is disabled in zlib defailts,
-// don't enable it for binary compatibility
-//#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
-//          Trace((stderr, "inflate.c too far\n"));
-//          copy -= state.whave;
-//          if (copy > state.length) { copy = state.length; }
-//          if (copy > left) { copy = left; }
-//          left -= copy;
-//          state.length -= copy;
-//          do {
-//            output[put++] = 0;
-//          } while (--copy);
-//          if (state.length === 0) { state.mode = LEN; }
-//          break;
-//#endif
-        }
-        if (copy > state.wnext) {
-          copy -= state.wnext;
-          from = state.wsize - copy;
-        }
-        else {
-          from = state.wnext - copy;
-        }
-        if (copy > state.length) { copy = state.length; }
-        from_source = state.window;
-      }
-      else {                              /* copy from output */
-        from_source = output;
-        from = put - state.offset;
-        copy = state.length;
-      }
-      if (copy > left) { copy = left; }
-      left -= copy;
-      state.length -= copy;
-      do {
-        output[put++] = from_source[from++];
-      } while (--copy);
-      if (state.length === 0) { state.mode = LEN; }
-      break;
-    case LIT:
-      if (left === 0) { break inf_leave; }
-      output[put++] = state.length;
-      left--;
-      state.mode = LEN;
-      break;
-    case CHECK:
-      if (state.wrap) {
-        //=== NEEDBITS(32);
-        while (bits < 32) {
-          if (have === 0) { break inf_leave; }
-          have--;
-          // Use '|' insdead of '+' to make sure that result is signed
-          hold |= input[next++] << bits;
-          bits += 8;
-        }
-        //===//
-        _out -= left;
-        strm.total_out += _out;
-        state.total += _out;
-        if (_out) {
-          strm.adler = state.check =
-              /*UPDATE(state.check, put - _out, _out);*/
-              (state.flags ? crc32(state.check, output, _out, put - _out) : adler32(state.check, output, _out, put - _out));
-
-        }
-        _out = left;
-        // NB: crc32 stored as signed 32-bit int, ZSWAP32 returns signed too
-        if ((state.flags ? hold : ZSWAP32(hold)) !== state.check) {
-          strm.msg = 'incorrect data check';
-          state.mode = BAD;
-          break;
-        }
-        //=== INITBITS();
-        hold = 0;
-        bits = 0;
-        //===//
-        //Tracev((stderr, "inflate:   check matches trailer\n"));
-      }
-      state.mode = LENGTH;
-      /* falls through */
-    case LENGTH:
-      if (state.wrap && state.flags) {
-        //=== NEEDBITS(32);
-        while (bits < 32) {
-          if (have === 0) { break inf_leave; }
-          have--;
-          hold += input[next++] << bits;
-          bits += 8;
-        }
-        //===//
-        if (hold !== (state.total & 0xffffffff)) {
-          strm.msg = 'incorrect length check';
-          state.mode = BAD;
-          break;
-        }
-        //=== INITBITS();
-        hold = 0;
-        bits = 0;
-        //===//
-        //Tracev((stderr, "inflate:   length matches trailer\n"));
-      }
-      state.mode = DONE;
-      /* falls through */
-    case DONE:
-      ret = Z_STREAM_END;
-      break inf_leave;
-    case BAD:
-      ret = Z_DATA_ERROR;
-      break inf_leave;
-    case MEM:
-      return Z_MEM_ERROR;
-    case SYNC:
-      /* falls through */
-    default:
-      return Z_STREAM_ERROR;
-    }
-  }
-
-  // inf_leave <- here is real place for "goto inf_leave", emulated via "break inf_leave"
-
-  /*
-     Return from inflate(), updating the total counts and the check value.
-     If there was no progress during the inflate() call, return a buffer
-     error.  Call updatewindow() to create and/or update the window state.
-     Note: a memory error from inflate() is non-recoverable.
-   */
-
-  //--- RESTORE() ---
-  strm.next_out = put;
-  strm.avail_out = left;
-  strm.next_in = next;
-  strm.avail_in = have;
-  state.hold = hold;
-  state.bits = bits;
-  //---
-
-  if (state.wsize || (_out !== strm.avail_out && state.mode < BAD &&
-                      (state.mode < CHECK || flush !== Z_FINISH))) {
-    if (updatewindow(strm, strm.output, strm.next_out, _out - strm.avail_out)) {
-      state.mode = MEM;
-      return Z_MEM_ERROR;
-    }
-  }
-  _in -= strm.avail_in;
-  _out -= strm.avail_out;
-  strm.total_in += _in;
-  strm.total_out += _out;
-  state.total += _out;
-  if (state.wrap && _out) {
-    strm.adler = state.check = /*UPDATE(state.check, strm.next_out - _out, _out);*/
-      (state.flags ? crc32(state.check, output, _out, strm.next_out - _out) : adler32(state.check, output, _out, strm.next_out - _out));
-  }
-  strm.data_type = state.bits + (state.last ? 64 : 0) +
-                    (state.mode === TYPE ? 128 : 0) +
-                    (state.mode === LEN_ || state.mode === COPY_ ? 256 : 0);
-  if (((_in === 0 && _out === 0) || flush === Z_FINISH) && ret === Z_OK) {
-    ret = Z_BUF_ERROR;
-  }
-  return ret;
-}
-
-function inflateEnd(strm) {
-
-  if (!strm || !strm.state /*|| strm->zfree == (free_func)0*/) {
-    return Z_STREAM_ERROR;
-  }
-
-  var state = strm.state;
-  if (state.window) {
-    state.window = null;
-  }
-  strm.state = null;
-  return Z_OK;
-}
-
-function inflateGetHeader(strm, head) {
-  var state;
-
-  /* check state */
-  if (!strm || !strm.state) { return Z_STREAM_ERROR; }
-  state = strm.state;
-  if ((state.wrap & 2) === 0) { return Z_STREAM_ERROR; }
-
-  /* save header structure */
-  state.head = head;
-  head.done = false;
-  return Z_OK;
-}
-
-
-exports.inflateReset = inflateReset;
-exports.inflateReset2 = inflateReset2;
-exports.inflateResetKeep = inflateResetKeep;
-exports.inflateInit = inflateInit;
-exports.inflateInit2 = inflateInit2;
-exports.inflate = inflate;
-exports.inflateEnd = inflateEnd;
-exports.inflateGetHeader = inflateGetHeader;
-exports.inflateInfo = 'pako inflate (from Nodeca project)';
-
-/* Not implemented
-exports.inflateCopy = inflateCopy;
-exports.inflateGetDictionary = inflateGetDictionary;
-exports.inflateMark = inflateMark;
-exports.inflatePrime = inflatePrime;
-exports.inflateSetDictionary = inflateSetDictionary;
-exports.inflateSync = inflateSync;
-exports.inflateSyncPoint = inflateSyncPoint;
-exports.inflateUndermine = inflateUndermine;
-*/
-
-},{"../utils/common":1,"./adler32":3,"./crc32":5,"./inffast":7,"./inftrees":9}],9:[function(require,module,exports){
-'use strict';
-
-
-var utils = require('../utils/common');
-
-var MAXBITS = 15;
-var ENOUGH_LENS = 852;
-var ENOUGH_DISTS = 592;
-//var ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS);
-
-var CODES = 0;
-var LENS = 1;
-var DISTS = 2;
-
-var lbase = [ /* Length codes 257..285 base */
-  3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
-  35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0
-];
-
-var lext = [ /* Length codes 257..285 extra */
-  16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18,
-  19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 72, 78
-];
-
-var dbase = [ /* Distance codes 0..29 base */
-  1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
-  257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
-  8193, 12289, 16385, 24577, 0, 0
-];
-
-var dext = [ /* Distance codes 0..29 extra */
-  16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22,
-  23, 23, 24, 24, 25, 25, 26, 26, 27, 27,
-  28, 28, 29, 29, 64, 64
-];
-
-module.exports = function inflate_table(type, lens, lens_index, codes, table, table_index, work, opts)
-{
-  var bits = opts.bits;
-      //here = opts.here; /* table entry for duplication */
-
-  var len = 0;               /* a code's length in bits */
-  var sym = 0;               /* index of code symbols */
-  var min = 0, max = 0;          /* minimum and maximum code lengths */
-  var root = 0;              /* number of index bits for root table */
-  var curr = 0;              /* number of index bits for current table */
-  var drop = 0;              /* code bits to drop for sub-table */
-  var left = 0;                   /* number of prefix codes available */
-  var used = 0;              /* code entries in table used */
-  var huff = 0;              /* Huffman code */
-  var incr;              /* for incrementing code, index */
-  var fill;              /* index for replicating entries */
-  var low;               /* low bits for current root entry */
-  var mask;              /* mask for low root bits */
-  var next;             /* next available space in table */
-  var base = null;     /* base value table to use */
-  var base_index = 0;
-//  var shoextra;    /* extra bits table to use */
-  var end;                    /* use base and extra for symbol > end */
-  var count = new utils.Buf16(MAXBITS+1); //[MAXBITS+1];    /* number of codes of each length */
-  var offs = new utils.Buf16(MAXBITS+1); //[MAXBITS+1];     /* offsets in table for each length */
-  var extra = null;
-  var extra_index = 0;
-
-  var here_bits, here_op, here_val;
-
-  /*
-   Process a set of code lengths to create a canonical Huffman code.  The
-   code lengths are lens[0..codes-1].  Each length corresponds to the
-   symbols 0..codes-1.  The Huffman code is generated by first sorting the
-   symbols by length from short to long, and retaining the symbol order
-   for codes with equal lengths.  Then the code starts with all zero bits
-   for the first code of the shortest length, and the codes are integer
-   increments for the same length, and zeros are appended as the length
-   increases.  For the deflate format, these bits are stored backwards
-   from their more natural integer increment ordering, and so when the
-   decoding tables are built in the large loop below, the integer codes
-   are incremented backwards.
-
-   This routine assumes, but does not check, that all of the entries in
-   lens[] are in the range 0..MAXBITS.  The caller must assure this.
-   1..MAXBITS is interpreted as that code length.  zero means that that
-   symbol does not occur in this code.
-
-   The codes are sorted by computing a count of codes for each length,
-   creating from that a table of starting indices for each length in the
-   sorted table, and then entering the symbols in order in the sorted
-   table.  The sorted table is work[], with that space being provided by
-   the caller.
-
-   The length counts are used for other purposes as well, i.e. finding
-   the minimum and maximum length codes, determining if there are any
-   codes at all, checking for a valid set of lengths, and looking ahead
-   at length counts to determine sub-table sizes when building the
-   decoding tables.
-   */
-
-  /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */
-  for (len = 0; len <= MAXBITS; len++) {
-    count[len] = 0;
-  }
-  for (sym = 0; sym < codes; sym++) {
-    count[lens[lens_index + sym]]++;
-  }
-
-  /* bound code lengths, force root to be within code lengths */
-  root = bits;
-  for (max = MAXBITS; max >= 1; max--) {
-    if (count[max] !== 0) { break; }
-  }
-  if (root > max) {
-    root = max;
-  }
-  if (max === 0) {                     /* no symbols to code at all */
-    //table.op[opts.table_index] = 64;  //here.op = (var char)64;    /* invalid code marker */
-    //table.bits[opts.table_index] = 1;   //here.bits = (var char)1;
-    //table.val[opts.table_index++] = 0;   //here.val = (var short)0;
-    table[table_index++] = (1 << 24) | (64 << 16) | 0;
-
-
-    //table.op[opts.table_index] = 64;
-    //table.bits[opts.table_index] = 1;
-    //table.val[opts.table_index++] = 0;
-    table[table_index++] = (1 << 24) | (64 << 16) | 0;
-
-    opts.bits = 1;
-    return 0;     /* no symbols, but wait for decoding to report error */
-  }
-  for (min = 1; min < max; min++) {
-    if (count[min] !== 0) { break; }
-  }
-  if (root < min) {
-    root = min;
-  }
-
-  /* check for an over-subscribed or incomplete set of lengths */
-  left = 1;
-  for (len = 1; len <= MAXBITS; len++) {
-    left <<= 1;
-    left -= count[len];
-    if (left < 0) {
-      return -1;
-    }        /* over-subscribed */
-  }
-  if (left > 0 && (type === CODES || max !== 1)) {
-    return -1;                      /* incomplete set */
-  }
-
-  /* generate offsets into symbol table for each length for sorting */
-  offs[1] = 0;
-  for (len = 1; len < MAXBITS; len++) {
-    offs[len + 1] = offs[len] + count[len];
-  }
-
-  /* sort symbols by length, by symbol order within each length */
-  for (sym = 0; sym < codes; sym++) {
-    if (lens[lens_index + sym] !== 0) {
-      work[offs[lens[lens_index + sym]]++] = sym;
-    }
-  }
-
-  /*
-   Create and fill in decoding tables.  In this loop, the table being
-   filled is at next and has curr index bits.  The code being used is huff
-   with length len.  That code is converted to an index by dropping drop
-   bits off of the bottom.  For codes where len is less than drop + curr,
-   those top drop + curr - len bits are incremented through all values to
-   fill the table with replicated entries.
-
-   root is the number of index bits for the root table.  When len exceeds
-   root, sub-tables are created pointed to by the root entry with an index
-   of the low root bits of huff.  This is saved in low to check for when a
-   new sub-table should be started.  drop is zero when the root table is
-   being filled, and drop is root when sub-tables are being filled.
-
-   When a new sub-table is needed, it is necessary to look ahead in the
-   code lengths to determine what size sub-table is needed.  The length
-   counts are used for this, and so count[] is decremented as codes are
-   entered in the tables.
-
-   used keeps track of how many table entries have been allocated from the
-   provided *table space.  It is checked for LENS and DIST tables against
-   the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in
-   the initial root table size constants.  See the comments in inftrees.h
-   for more information.
-
-   sym increments through all symbols, and the loop terminates when
-   all codes of length max, i.e. all codes, have been processed.  This
-   routine permits incomplete codes, so another loop after this one fills
-   in the rest of the decoding tables with invalid code markers.
-   */
-
-  /* set up for code type */
-  // poor man optimization - use if-else instead of switch,
-  // to avoid deopts in old v8
-  if (type === CODES) {
-    base = extra = work;    /* dummy value--not used */
-    end = 19;
-
-  } else if (type === LENS) {
-    base = lbase;
-    base_index -= 257;
-    extra = lext;
-    extra_index -= 257;
-    end = 256;
-
-  } else {                    /* DISTS */
-    base = dbase;
-    extra = dext;
-    end = -1;
-  }
-
-  /* initialize opts for loop */
-  huff = 0;                   /* starting code */
-  sym = 0;                    /* starting code symbol */
-  len = min;                  /* starting code length */
-  next = table_index;              /* current table to fill in */
-  curr = root;                /* current table index bits */
-  drop = 0;                   /* current bits to drop from code for index */
-  low = -1;                   /* trigger new sub-table when len > root */
-  used = 1 << root;          /* use root table entries */
-  mask = used - 1;            /* mask for comparing low */
-
-  /* check available table space */
-  if ((type === LENS && used > ENOUGH_LENS) ||
-    (type === DISTS && used > ENOUGH_DISTS)) {
-    return 1;
-  }
-
-  var i=0;
-  /* process all codes and make table entries */
-  for (;;) {
-    i++;
-    /* create table entry */
-    here_bits = len - drop;
-    if (work[sym] < end) {
-      here_op = 0;
-      here_val = work[sym];
-    }
-    else if (work[sym] > end) {
-      here_op = extra[extra_index + work[sym]];
-      here_val = base[base_index + work[sym]];
-    }
-    else {
-      here_op = 32 + 64;         /* end of block */
-      here_val = 0;
-    }
-
-    /* replicate for those indices with low len bits equal to huff */
-    incr = 1 << (len - drop);
-    fill = 1 << curr;
-    min = fill;                 /* save offset to next table */
-    do {
-      fill -= incr;
-      table[next + (huff >> drop) + fill] = (here_bits << 24) | (here_op << 16) | here_val |0;
-    } while (fill !== 0);
-
-    /* backwards increment the len-bit code huff */
-    incr = 1 << (len - 1);
-    while (huff & incr) {
-      incr >>= 1;
-    }
-    if (incr !== 0) {
-      huff &= incr - 1;
-      huff += incr;
-    } else {
-      huff = 0;
-    }
-
-    /* go to next symbol, update count, len */
-    sym++;
-    if (--count[len] === 0) {
-      if (len === max) { break; }
-      len = lens[lens_index + work[sym]];
-    }
-
-    /* create new sub-table if needed */
-    if (len > root && (huff & mask) !== low) {
-      /* if first time, transition to sub-tables */
-      if (drop === 0) {
-        drop = root;
-      }
-
-      /* increment past last table */
-      next += min;            /* here min is 1 << curr */
-
-      /* determine length of next table */
-      curr = len - drop;
-      left = 1 << curr;
-      while (curr + drop < max) {
-        left -= count[curr + drop];
-        if (left <= 0) { break; }
-        curr++;
-        left <<= 1;
-      }
-
-      /* check for enough space */
-      used += 1 << curr;
-      if ((type === LENS && used > ENOUGH_LENS) ||
-        (type === DISTS && used > ENOUGH_DISTS)) {
-        return 1;
-      }
-
-      /* point entry in root table to sub-table */
-      low = huff & mask;
-      /*table.op[low] = curr;
-      table.bits[low] = root;
-      table.val[low] = next - opts.table_index;*/
-      table[low] = (root << 24) | (curr << 16) | (next - table_index) |0;
-    }
-  }
-
-  /* fill in remaining table entry if code is incomplete (guaranteed to have
-   at most one remaining entry, since if the code is incomplete, the
-   maximum code length that was allowed to get this far is one bit) */
-  if (huff !== 0) {
-    //table.op[next + huff] = 64;            /* invalid code marker */
-    //table.bits[next + huff] = len - drop;
-    //table.val[next + huff] = 0;
-    table[next + huff] = ((len - drop) << 24) | (64 << 16) |0;
-  }
-
-  /* set return parameters */
-  //opts.table_index += used;
-  opts.bits = root;
-  return 0;
-};
-
-},{"../utils/common":1}],10:[function(require,module,exports){
-'use strict';
-
-module.exports = {
-  '2':    'need dictionary',     /* Z_NEED_DICT       2  */
-  '1':    'stream end',          /* Z_STREAM_END      1  */
-  '0':    '',                    /* Z_OK              0  */
-  '-1':   'file error',          /* Z_ERRNO         (-1) */
-  '-2':   'stream error',        /* Z_STREAM_ERROR  (-2) */
-  '-3':   'data error',          /* Z_DATA_ERROR    (-3) */
-  '-4':   'insufficient memory', /* Z_MEM_ERROR     (-4) */
-  '-5':   'buffer error',        /* Z_BUF_ERROR     (-5) */
-  '-6':   'incompatible version' /* Z_VERSION_ERROR (-6) */
-};
-
-},{}],11:[function(require,module,exports){
-'use strict';
-
-
-function ZStream() {
-  /* next input byte */
-  this.input = null; // JS specific, because we have no pointers
-  this.next_in = 0;
-  /* number of bytes available at input */
-  this.avail_in = 0;
-  /* total number of input bytes read so far */
-  this.total_in = 0;
-  /* next output byte should be put there */
-  this.output = null; // JS specific, because we have no pointers
-  this.next_out = 0;
-  /* remaining free space at output */
-  this.avail_out = 0;
-  /* total number of bytes output so far */
-  this.total_out = 0;
-  /* last error message, NULL if no error */
-  this.msg = ''/*Z_NULL*/;
-  /* not visible by applications */
-  this.state = null;
-  /* best guess about the data type: binary or text */
-  this.data_type = 2/*Z_UNKNOWN*/;
-  /* adler32 value of the uncompressed data */
-  this.adler = 0;
-}
-
-module.exports = ZStream;
-
-},{}],"/lib/inflate.js":[function(require,module,exports){
-'use strict';
-
-
-var zlib_inflate = require('./zlib/inflate.js');
-var utils = require('./utils/common');
-var strings = require('./utils/strings');
-var c = require('./zlib/constants');
-var msg = require('./zlib/messages');
-var zstream = require('./zlib/zstream');
-var gzheader = require('./zlib/gzheader');
-
-var toString = Object.prototype.toString;
-
-/**
- * class Inflate
- *
- * Generic JS-style wrapper for zlib calls. If you don't need
- * streaming behaviour - use more simple functions: [[inflate]]
- * and [[inflateRaw]].
- **/
-
-/* internal
- * inflate.chunks -> Array
- *
- * Chunks of output data, if [[Inflate#onData]] not overriden.
- **/
-
-/**
- * Inflate.result -> Uint8Array|Array|String
- *
- * Uncompressed result, generated by default [[Inflate#onData]]
- * and [[Inflate#onEnd]] handlers. Filled after you push last chunk
- * (call [[Inflate#push]] with `Z_FINISH` / `true` param) or if you
- * push a chunk with explicit flush (call [[Inflate#push]] with
- * `Z_SYNC_FLUSH` param).
- **/
-
-/**
- * Inflate.err -> Number
- *
- * Error code after inflate finished. 0 (Z_OK) on success.
- * Should be checked if broken data possible.
- **/
-
-/**
- * Inflate.msg -> String
- *
- * Error message, if [[Inflate.err]] != 0
- **/
-
-
-/**
- * new Inflate(options)
- * - options (Object): zlib inflate options.
- *
- * Creates new inflator instance with specified params. Throws exception
- * on bad params. Supported options:
- *
- * - `windowBits`
- *
- * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
- * for more information on these.
- *
- * Additional options, for internal needs:
- *
- * - `chunkSize` - size of generated data chunks (16K by default)
- * - `raw` (Boolean) - do raw inflate
- * - `to` (String) - if equal to 'string', then result will be converted
- *   from utf8 to utf16 (javascript) string. When string output requested,
- *   chunk length can differ from `chunkSize`, depending on content.
- *
- * By default, when no options set, autodetect deflate/gzip data format via
- * wrapper header.
- *
- * ##### Example:
- *
- * ```javascript
- * var pako = require('pako')
- *   , chunk1 = Uint8Array([1,2,3,4,5,6,7,8,9])
- *   , chunk2 = Uint8Array([10,11,12,13,14,15,16,17,18,19]);
- *
- * var inflate = new pako.Inflate({ level: 3});
- *
- * inflate.push(chunk1, false);
- * inflate.push(chunk2, true);  // true -> last chunk
- *
- * if (inflate.err) { throw new Error(inflate.err); }
- *
- * console.log(inflate.result);
- * ```
- **/
-var Inflate = function(options) {
-
-  this.options = utils.assign({
-    chunkSize: 16384,
-    windowBits: 0,
-    to: ''
-  }, options || {});
-
-  var opt = this.options;
-
-  // Force window size for `raw` data, if not set directly,
-  // because we have no header for autodetect.
-  if (opt.raw && (opt.windowBits >= 0) && (opt.windowBits < 16)) {
-    opt.windowBits = -opt.windowBits;
-    if (opt.windowBits === 0) { opt.windowBits = -15; }
-  }
-
-  // If `windowBits` not defined (and mode not raw) - set autodetect flag for gzip/deflate
-  if ((opt.windowBits >= 0) && (opt.windowBits < 16) &&
-      !(options && options.windowBits)) {
-    opt.windowBits += 32;
-  }
-
-  // Gzip header has no info about windows size, we can do autodetect only
-  // for deflate. So, if window size not set, force it to max when gzip possible
-  if ((opt.windowBits > 15) && (opt.windowBits < 48)) {
-    // bit 3 (16) -> gzipped data
-    // bit 4 (32) -> autodetect gzip/deflate
-    if ((opt.windowBits & 15) === 0) {
-      opt.windowBits |= 15;
-    }
-  }
-
-  this.err    = 0;      // error code, if happens (0 = Z_OK)
-  this.msg    = '';     // error message
-  this.ended  = false;  // used to avoid multiple onEnd() calls
-  this.chunks = [];     // chunks of compressed data
-
-  this.strm   = new zstream();
-  this.strm.avail_out = 0;
-
-  var status  = zlib_inflate.inflateInit2(
-    this.strm,
-    opt.windowBits
-  );
-
-  if (status !== c.Z_OK) {
-    throw new Error(msg[status]);
-  }
-
-  this.header = new gzheader();
-
-  zlib_inflate.inflateGetHeader(this.strm, this.header);
-};
-
-/**
- * Inflate#push(data[, mode]) -> Boolean
- * - data (Uint8Array|Array|ArrayBuffer|String): input data
- * - mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE modes.
- *   See constants. Skipped or `false` means Z_NO_FLUSH, `true` meansh Z_FINISH.
- *
- * Sends input data to inflate pipe, generating [[Inflate#onData]] calls with
- * new output chunks. Returns `true` on success. The last data block must have
- * mode Z_FINISH (or `true`). That will flush internal pending buffers and call
- * [[Inflate#onEnd]]. For interim explicit flushes (without ending the stream) you
- * can use mode Z_SYNC_FLUSH, keeping the decompression context.
- *
- * On fail call [[Inflate#onEnd]] with error code and return false.
- *
- * We strongly recommend to use `Uint8Array` on input for best speed (output
- * format is detected automatically). Also, don't skip last param and always
- * use the same type in your code (boolean or number). That will improve JS speed.
- *
- * For regular `Array`-s make sure all elements are [0..255].
- *
- * ##### Example
- *
- * ```javascript
- * push(chunk, false); // push one of data chunks
- * ...
- * push(chunk, true);  // push last chunk
- * ```
- **/
-Inflate.prototype.push = function(data, mode) {
-  var strm = this.strm;
-  var chunkSize = this.options.chunkSize;
-  var status, _mode;
-  var next_out_utf8, tail, utf8str;
-
-  if (this.ended) { return false; }
-  _mode = (mode === ~~mode) ? mode : ((mode === true) ? c.Z_FINISH : c.Z_NO_FLUSH);
-
-  // Convert data if needed
-  if (typeof data === 'string') {
-    // Only binary strings can be decompressed on practice
-    strm.input = strings.binstring2buf(data);
-  } else if (toString.call(data) === '[object ArrayBuffer]') {
-    strm.input = new Uint8Array(data);
-  } else {
-    strm.input = data;
-  }
-
-  strm.next_in = 0;
-  strm.avail_in = strm.input.length;
-
-  do {
-    if (strm.avail_out === 0) {
-      strm.output = new utils.Buf8(chunkSize);
-      strm.next_out = 0;
-      strm.avail_out = chunkSize;
-    }
-
-    status = zlib_inflate.inflate(strm, c.Z_NO_FLUSH);    /* no bad return value */
-
-    if (status !== c.Z_STREAM_END && status !== c.Z_OK) {
-      this.onEnd(status);
-      this.ended = true;
-      return false;
-    }
-
-    if (strm.next_out) {
-      if (strm.avail_out === 0 || status === c.Z_STREAM_END || (strm.avail_in === 0 && (_mode === c.Z_FINISH || _mode === c.Z_SYNC_FLUSH))) {
-
-        if (this.options.to === 'string') {
-
-          next_out_utf8 = strings.utf8border(strm.output, strm.next_out);
-
-          tail = strm.next_out - next_out_utf8;
-          utf8str = strings.buf2string(strm.output, next_out_utf8);
-
-          // move tail
-          strm.next_out = tail;
-          strm.avail_out = chunkSize - tail;
-          if (tail) { utils.arraySet(strm.output, strm.output, next_out_utf8, tail, 0); }
-
-          this.onData(utf8str);
-
-        } else {
-          this.onData(utils.shrinkBuf(strm.output, strm.next_out));
-        }
-      }
-    }
-  } while ((strm.avail_in > 0) && status !== c.Z_STREAM_END);
-
-  if (status === c.Z_STREAM_END) {
-    _mode = c.Z_FINISH;
-  }
-
-  // Finalize on the last chunk.
-  if (_mode === c.Z_FINISH) {
-    status = zlib_inflate.inflateEnd(this.strm);
-    this.onEnd(status);
-    this.ended = true;
-    return status === c.Z_OK;
-  }
-
-  // callback interim results if Z_SYNC_FLUSH.
-  if (_mode === c.Z_SYNC_FLUSH) {
-    this.onEnd(c.Z_OK);
-    strm.avail_out = 0;
-    return true;
-  }
-
-  return true;
-};
-
-
-/**
- * Inflate#onData(chunk) -> Void
- * - chunk (Uint8Array|Array|String): ouput data. Type of array depends
- *   on js engine support. When string output requested, each chunk
- *   will be string.
- *
- * By default, stores data blocks in `chunks[]` property and glue
- * those in `onEnd`. Override this handler, if you need another behaviour.
- **/
-Inflate.prototype.onData = function(chunk) {
-  this.chunks.push(chunk);
-};
-
-
-/**
- * Inflate#onEnd(status) -> Void
- * - status (Number): inflate status. 0 (Z_OK) on success,
- *   other if not.
- *
- * Called either after you tell inflate that the input stream is
- * complete (Z_FINISH) or should be flushed (Z_SYNC_FLUSH)
- * or if an error happened. By default - join collected chunks,
- * free memory and fill `results` / `err` properties.
- **/
-Inflate.prototype.onEnd = function(status) {
-  // On success - join
-  if (status === c.Z_OK) {
-    if (this.options.to === 'string') {
-      // Glue & convert here, until we teach pako to send
-      // utf8 alligned strings to onData
-      this.result = this.chunks.join('');
-    } else {
-      this.result = utils.flattenChunks(this.chunks);
-    }
-  }
-  this.chunks = [];
-  this.err = status;
-  this.msg = this.strm.msg;
-};
-
-
-/**
- * inflate(data[, options]) -> Uint8Array|Array|String
- * - data (Uint8Array|Array|String): input data to decompress.
- * - options (Object): zlib inflate options.
- *
- * Decompress `data` with inflate/ungzip and `options`. Autodetect
- * format via wrapper header by default. That's why we don't provide
- * separate `ungzip` method.
- *
- * Supported options are:
- *
- * - windowBits
- *
- * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
- * for more information.
- *
- * Sugar (options):
- *
- * - `raw` (Boolean) - say that we work with raw stream, if you don't wish to specify
- *   negative windowBits implicitly.
- * - `to` (String) - if equal to 'string', then result will be converted
- *   from utf8 to utf16 (javascript) string. When string output requested,
- *   chunk length can differ from `chunkSize`, depending on content.
- *
- *
- * ##### Example:
- *
- * ```javascript
- * var pako = require('pako')
- *   , input = pako.deflate([1,2,3,4,5,6,7,8,9])
- *   , output;
- *
- * try {
- *   output = pako.inflate(input);
- * } catch (err)
- *   console.log(err);
- * }
- * ```
- **/
-function inflate(input, options) {
-  var inflator = new Inflate(options);
-
-  inflator.push(input, true);
-
-  // That will never happens, if you don't cheat with options :)
-  if (inflator.err) { throw inflator.msg; }
-
-  return inflator.result;
-}
-
-
-/**
- * inflateRaw(data[, options]) -> Uint8Array|Array|String
- * - data (Uint8Array|Array|String): input data to decompress.
- * - options (Object): zlib inflate options.
- *
- * The same as [[inflate]], but creates raw data, without wrapper
- * (header and adler32 crc).
- **/
-function inflateRaw(input, options) {
-  options = options || {};
-  options.raw = true;
-  return inflate(input, options);
-}
-
-
-/**
- * ungzip(data[, options]) -> Uint8Array|Array|String
- * - data (Uint8Array|Array|String): input data to decompress.
- * - options (Object): zlib inflate options.
- *
- * Just shortcut to [[inflate]], because it autodetects format
- * by header.content. Done for convenience.
- **/
-
-
-exports.Inflate = Inflate;
-exports.inflate = inflate;
-exports.inflateRaw = inflateRaw;
-exports.ungzip  = inflate;
-
-},{"./utils/common":1,"./utils/strings":2,"./zlib/constants":4,"./zlib/gzheader":6,"./zlib/inflate.js":8,"./zlib/messages":10,"./zlib/zstream":11}]},{},[])("/lib/inflate.js")
-});
-//This defines the $3Dmol object which is used to create viewers
-//and configure system-wide settings
-
-/** 
- * All of the functionality of $3Dmol.js is contained within the
- * $3Dmol global namespace
- * @namespace */
-$3Dmol = (function(window) {
-    
-    var my = window['$3Dmol'] || {};
-    //var $ = window['jQuery'];
-    
-    return my;
-
-})(window);
-
-/* The following code "phones home" to register that an ip 
-   address has loaded 3Dmol.js.  Being able track this usage
-   is very helpful when reporting to funding agencies.  Please
-   leave this code in if you would like to increase the 
-   likelihood of 3Dmol.js remaining supported.
-*/
-$.get("http://3dmol.csb.pitt.edu/track/report.cgi");
-    
-/**
- * Create and initialize an appropriate viewer at supplied HTML element using specification in config
- * @param {Object | string} element - Either HTML element or string identifier
- * @param {ViewerSpec} config Viewer specification
- * @return {$3Dmol.GLViewer} GLViewer, null if unable to instantiate WebGL
- * 
- * @example
- * // Assume there exists an HTML div with id "gldiv"
- * var element = $("#gldiv");
- * 
- * // Viewer config - properties 'defaultcolors' and 'callback'
- * var config = {defaultcolors: $3Dmol.rasmolElementColors };
- * 
- * // Create GLViewer within 'gldiv' 
- * var myviewer = $3Dmol.createViewer(element, config);
- * //'data' is a string containing molecule data in pdb format  
- * myviewer.addModel(data, "pdb");
- * myviewer.zoomTo();
- * myviewer.render();                        
- *                        
- */
-$3Dmol.createViewer = function(element, config)
-{
-    if($.type(element) === "string")
-        element = $("#"+element);
-    if(!element) return;
-
-    config = config || {}; 
-
-    //try to create the  viewer
-    try {
-        return new $3Dmol.GLViewer(element, config);
-    }
-    catch(e) {
-        throw "error creating viewer: "+e;
-    }
-    
-    return null;
-};
-   
-/**
- * Contains a dictionary of embedded viewers created from HTML elements
- * with a the viewer_3Dmoljs css class indexed by their id (or numerically
- * if they do not have an id).
-*/
-$3Dmol.viewers = {};
-
-/**
- * Load a PDB/PubChem structure into existing viewer. Automatically calls 'zoomTo' and 'render' on viewer after loading model
- * 
- * @function $3Dmol.download
- * @param {string} query - String specifying pdb or pubchem id; must be prefaced with "pdb: " or "cid: ", respectively
- * @param {$3Dmol.GLViewer} viewer - Add new model to existing viewer
- * @param {Object} options - Specify additional options such as file format to download, if multiple are available
- * @example
- * var myviewer = $3Dmol.createViewer(gldiv);
- * 
- * // GLModel 'm' created and loaded into glviewer for PDB id 2POR
- * // Note that m will not contain the atomic data until after the network request is completed
- * var m = $3Dmol.download('pdb: 2POR', myviewer, {format:'cif'});
- * 
- * @return {$3Dmol.GLModel} GLModel
- */ 
-$3Dmol.download = function(query, viewer, options, callback) {
-    var baseURL = '';
-    var type = "";
-    var m = viewer.addModel();
-    if (query.substr(0, 4) === 'pdb:') {
-        type = options && options.format ? options.format : "pdb";
-        query = query.substr(4).toUpperCase();
-        if (!query.match(/^[1-9][A-Za-z0-9]{3}$/)) {
-           alert("Wrong PDB ID"); return;
-        }
-        if (options && options.format)
-            uri = "http://www.rcsb.org/pdb/files/" + query + "." + options.format;
-        else
-            uri = "http://www.rcsb.org/pdb/files/" + query + ".pdb"; 
-
-    } else if (query.substr(0, 4) == 'cid:') {
-        type = "sdf";
-        query = query.substr(4);
-        if (!query.match(/^[1-9]+$/)) {
-           alert("Wrong Compound ID"); return;
-        }
-        uri = "http://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/cid/" + query + 
-          "/SDF?record_type=3d";
-    }
-
-   $.get(uri, function(ret) {
-      m.addMolData(ret, type, options);
-      viewer.zoomTo();
-      viewer.render();
-      if(callback) callback(m);
-
-   });
-   
-   return m;
-};
-       
-
-/**
- * $3Dmol surface types
- * @enum {number}
- */
-$3Dmol.SurfaceType = {
-    VDW : 1,
-    MS : 2,
-    SAS : 3,
-    SES  : 4
-};
-
-
-//Miscellaneous functions and classes - to be incorporated into $3Dmol proper
-/**
- * 
- * @param {$3Dmol.Geometry} geometry
- * @param {$3Dmol.Mesh} mesh
- * @returns {undefined}
- */
-$3Dmol.mergeGeos = function(geometry, mesh) {
-    
-    var meshGeo = mesh.geometry;
-    
-    if (meshGeo === undefined) 
-        return;
-    
-    geometry.geometryGroups.push( meshGeo.geometryGroups[0] );
-    
-};
-
-$3Dmol.multiLineString = function(f) {
-    return f.toString()
-            .replace(/^[^\/]+\/\*!?/, '')
-            .replace(/\*\/[^\/]+$/, '');
-            
-};
-
-/** 
- * Render surface synchronously if true
- * @param {boolean} [$3Dmol.SyncSurface=false]
- * @type {boolean} */
-$3Dmol.syncSurface = false;
-
-// Internet Explorer refuses to allow webworkers in data blobs.  I can find
-// no way of checking for this feature directly, so must do a brower check
-if(window.navigator.userAgent.indexOf('MSIE ') >= 0 ||
-        window.navigator.userAgent.indexOf('Trident/') >= 0) {
-    $3Dmol.syncSurface = true; // can't use webworkers
-}
-
-/**
- * Parse a string that represents a style or atom selection and convert it
- * into an object.  The goal is to make it easier to write out these specifications
- * without resorting to json. Objects cannot be defined recursively.
- * ; - delineates fields of the object 
- * : - if the field has a value other than an empty object, it comes after a colon
- * , - delineates key/value pairs of a value object
- *     If the value object consists of ONLY keys (no = present) the keys are 
- *     converted to a list.  Otherwise a object of key/value pairs is created with
- *     any missing values set to null
- * = OR ~ - separates key/value pairs of a value object, if not provided value is null
- *     twiddle is supported since = has special meaning in URLs
- * @param (String) str
- * @returns {Object}
- */
-$3Dmol.specStringToObject = function(str) {
-    if(typeof(str) === "object") {
-        return str; //not string, assume was converted already
-    }
-    else if(typeof(str) === "undefined" || str == null) {
-        return str; 
-    }
-    
-    str = str.replace(/%7E/,'~'); //copy/pasting urls sometimes does this
-    //convert things that look like numbers into numbers
-    var massage = function(val) {
-        if($.isNumeric(val)) {
-           //hexadecimal does not parse as float
-           if(Math.floor(parseFloat(val)) == parseInt(val)) {
-              return parseFloat(val);
-           }
-           else if(val.indexOf('.') >= 0) {
-               return parseFloat(val); // ".7" for example, does not parseInt
-           }
-           else {
-               return parseInt(val);
-           }
-        }
-        return val;
-    }
-    
-    var ret = {};
-    var fields = str.split(';');
-    for(var i = 0; i < fields.length; i++) {
-        var fv = fields[i].split(':');
-        var f = fv[0];
-        var val = {};
-        var vstr = fv[1];
-        if(vstr) {
-            vstr = vstr.replace(/~/g,"=");
-            if(vstr.indexOf('=') !== -1) {
-                //has key=value pairs, must be object
-                var kvs = vstr.split(',');
-                for(var j = 0; j < kvs.length; j++) {
-                    var kv = kvs[j].split('=',2);
-                    val[kv[0]] = massage(kv[1]);
-                }
-            }
-            else if(vstr.indexOf(',') !== -1) {
-                //has multiple values, must list
-                val = vstr.split(',');
-            }
-            else {
-                val = massage(vstr); //value itself
-            }
-        }
-        ret[f] = val;
-    }
-
-return ret;
-}
-
-
-/**
- * computes the bounding box around the provided atoms
- * @param {AtomSpec[]} atomlist
- * @return {Array}
- */
-$3Dmol.getExtent = function(atomlist, ignoreSymmetries) {
-    var xmin, ymin, zmin, xmax, ymax, zmax, xsum, ysum, zsum, cnt;
-    var includeSym = !ignoreSymmetries;
-
-    xmin = ymin = zmin = 9999;
-    xmax = ymax = zmax = -9999;
-    xsum = ysum = zsum = cnt = 0;
-    
-    if (atomlist.length === 0)
-        return [ [ 0, 0, 0 ], [ 0, 0, 0 ], [ 0, 0, 0 ] ];
-    for (var i = 0; i < atomlist.length; i++) {
-        var atom = atomlist[i];
-        if (typeof atom === 'undefined' || !isFinite(atom.x) ||
-                !isFinite(atom.y) || !isFinite(atom.z))
-            continue;
-        cnt++;
-        xsum += atom.x;
-        ysum += atom.y;
-        zsum += atom.z;
-        
-        xmin = (xmin < atom.x) ? xmin : atom.x;
-        ymin = (ymin < atom.y) ? ymin : atom.y;
-        zmin = (zmin < atom.z) ? zmin : atom.z;
-        xmax = (xmax > atom.x) ? xmax : atom.x;
-        ymax = (ymax > atom.y) ? ymax : atom.y;
-        zmax = (zmax > atom.z) ? zmax : atom.z;
-        
-        if (atom.symmetries && includeSym) {
-            for (var n = 0; n < atom.symmetries.length; n++) {
-                cnt++;
-                xsum += atom.symmetries[n].x;
-                ysum += atom.symmetries[n].y;
-                zsum += atom.symmetries[n].z;
-                xmin = (xmin < atom.symmetries[n].x) ? xmin : atom.symmetries[n].x;
-                ymin = (ymin < atom.symmetries[n].y) ? ymin : atom.symmetries[n].y;
-                zmin = (zmin < atom.symmetries[n].z) ? zmin : atom.symmetries[n].z;
-                xmax = (xmax > atom.symmetries[n].x) ? xmax : atom.symmetries[n].x;
-                ymax = (ymax > atom.symmetries[n].y) ? ymax : atom.symmetries[n].y;
-                zmax = (zmax > atom.symmetries[n].z) ? zmax : atom.symmetries[n].z; 
-            }
-        }  
-    }
-
-    return [ [ xmin, ymin, zmin ], [ xmax, ymax, zmax ],
-            [ xsum / cnt, ysum / cnt, zsum / cnt ] ];
-};
-
-
-//return the value of an atom property prop, or null if non existent
-//looks first in properties, then in the atom itself
-$3Dmol.getAtomProperty = function(atom, prop) {
-    var val = null;
-    if (atom.properties
-            && typeof (atom.properties[prop]) != "undefined") {
-        val = atom.properties[prop];
-    } else if(typeof(atom[prop]) != 'undefined') {
-        val = atom[prop];
-    }
-    return val;
-};
-
-/* get the min and max values of the specified property in the provided
-* @function $3Dmol.getPropertyRange
-* @param {AtomSpec[]} atomlist - list of atoms to evaluate
-* @param {string} prop - name of property 
-* @return {Array} - [min, max] values
-*/
-$3Dmol.getPropertyRange = function (atomlist, prop) {
-    var min = Number.POSITIVE_INFINITY;
-    var max = Number.NEGATIVE_INFINITY;
-
-    for (var i = 0, n = atomlist.length; i < n; i++) {
-        var atom = atomlist[i];
-        var val = $3Dmol.getAtomProperty(atom, prop);
-        
-        if(val != null) {
-            if (val < min)
-                min = val;
-            if (val > max)
-                max = val;                
-        }
-    }
-
-    if (!isFinite(min) && !isFinite(max))
-        min = max = 0;
-    else if (!isFinite(min))
-        min = max;
-    else if (!isFinite(max))
-        max = min;
-
-    return [ min, max ];
-}
-
-/*
-* math-like functionality
-* quaternion, vector, matrix
-*/
-
-var $3Dmol = $3Dmol || {};
-$3Dmol.Math = {
-
-    clamp : function(x, min, max) {
-        return Math.min( Math.max( x, min ), max );
-    },
-
-    degToRad : function() {
-       var degreeToRadiansFactor = Math.PI / 180;
-       
-       return function(deg) {
-           return deg * degreeToRadiansFactor;
-       };
-    
-    }()
-    
-};
-
-
-// Quaternion
-/** @constructor */
-$3Dmol.Quaternion = function(x, y, z, w) {
-
-    this.x = x || 0;
-    this.y = y || 0;
-    this.z = z || 0;
-    this.w = (w !== undefined) ? w : 1;
-
-};
-
-$3Dmol.Quaternion.prototype = {
-
-    constructor : $3Dmol.Quaternion,
-
-    set : function(x, y, z, w) {
-        
-        this.x = x;
-        this.y = y;
-        this.z = z;
-        this.w = w;
-
-        return this;
-    },
-
-    copy : function(q) {
-        
-        this.x = q.x;
-        this.y = q.y;
-        this.z = q.z;
-        this.w = q.w;
-
-        return this;
-    },
-
-    conjugate : function() {
-        
-        this.x *= -1;
-        this.y *= -1;
-        this.z *= -1;
-
-        return this;
-    },
-
-    inverse : function() {
-        
-        return this.conjugate().normalize();
-    },
-
-    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 *= l;
-            this.y *= l;
-            this.z *= l;
-            this.w *= l;
-        }
-
-        return this;
-
-    },
-
-    multiply : function(q) {
-        
-        return this.multiplyQuaternions(this, q);
-    },
-
-    multiplyQuaternions : function(a, b) {
-
-        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;
-
-    }
-};
-
-//A 2 Vector
-/** @constructor */
-$3Dmol.Vector2 = function(x, y) {
-    
-    this.x = x || 0.0;
-    this.y = y || 0.0;
-};
-
-$3Dmol.Vector2.prototype = {
-    
-    constructor : $3Dmol.Vector2,
-   
-    set : function(x, y) {
-       
-        this.x = x;
-        this.y = y;
-       
-        return this;
-    },
-    
-    subVectors : function(a, b) {
-        
-      this.x = a.x - b.x;
-      this.y = a.y - b.y;
-      
-      return this;
-    },
-   
-    copy : function(v) {
-       
-        this.x = v.x;
-        this.y = v.y;
-       
-        return this;
-    },
-   
-    clone : function() {
-        
-        return new $3Dmol.Vector2(this.x, this.y);
-    }    
-   
-};
-
-//A 3 Vector
-
-$3Dmol.Vector3 = function(x, y, z) {
-    this.x = x || 0.0;
-    this.y = y || 0.0;
-    this.z = z || 0.0;
-};
-/** @this {$3Dmol.Vector3} */
-$3Dmol.Vector3.prototype =  {
-    
-    constructor : $3Dmol.Vector3,
-    
-    set : function(x, y, z) {
-        
-        this.x = x;
-        this.y = y;
-        this.z = z;
-        
-        return this;
-    },
-    
-    copy : function(v) {
-        
-        this.x = v.x;
-        this.y = v.y;
-        this.z = v.z;
-        
-        return this;  
-    },
-    
-    add : function(v) {
-        
-        this.x += v.x;
-        this.y += v.y;
-        this.z += v.z;  
-        
-        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) {
-        
-        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;
-    },
-    
-    multiplyScalar : function(s) {
-        
-        this.x *= s;
-        this.y *= s;
-        this.z *= s;
-        
-        return this;
-    },
-    
-    divideScalar : function(s) {
-        
-        if (s !== 0) {
-            this.x /= s;
-            this.y /= s;
-            this.z /= s;
-        }
-        
-        else {
-            this.x = 0;
-            this.y = 0;
-            this.z = 0;
-        }
-        
-        return this;
-    },
-    
-
-    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;
-    },
-    
-    applyMatrix4 : function(m) {
-    
-        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: $3Dmol.Matrix4 projection matrix
-        
-        var x = this.x, y = this.y, z = this.z;
-        
-        var e = m.elements;
-        var d = ( e[3]*x + e[7]*y + e[11]*z + e[15]);
-        
-        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) { 
-        
-        //save values
-        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;
-        
-        //compute this as
-        //t = 2 * cross(q.xyz, v)
-        //newv = v + q.w * t + cross(q.xyz, t)
-        //this from molecularmusings
-        //http://molecularmusings.wordpress.com/2013/05/24/a-faster-quaternion-vector-multiplication/
-        var t = {};
-        t.x = 2*(y * qz - z * qy);
-        t.y = 2*(z * qx - x * qz);
-        t.z = 2*(x * qy - y * qx);
-        
-        //cross t with q
-        var t2 = {};
-        t2.x = t.y * qz - t.z * qy;
-        t2.y = t.z * qx - t.x * qz;
-        t2.z = t.x * qy - t.y * qx;
-        
-        this.x = x + q.w*t.x + t2.x;
-        this.y = y + q.w*t.y + t2.y;
-        this.z = z + q.w*t.z + t2.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;
-    },
-    
-    length : function() {
-        
-        return Math.sqrt(this.x*this.x + this.y*this.y + this.z*this.z);
-    },
-    
-    lengthSq : function() {
-    
-        return (this.x*this.x + this.y*this.y + this.z*this.z);
-    },
-    
-    normalize : function() {
-        
-        return this.divideScalar( this.length() );
-    },
-    
-    cross : function (v) {
-        
-        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) {
-        
-        this.x = a.y * b.z - a.z * b.y;
-        this.y = a.z * b.x - a.x * b.z;
-        this.z = a.x * b.y - a.y * b.x;
-        
-        return this;
-    },
-    
-    getPositionFromMatrix : function(m) {
-        
-        this.x = m.elements[12];
-        this.y = m.elements[13];
-        this.z = m.elements[14];
-        
-        return this;
-    },
-
-    setEulerFromRotationMatrix : function (m, order) {
-
-        // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
-
-        var te = m.elements;
-        var m11 = te[0], m12 = te[4], m13 = te[8];
-        var m21 = te[1], m22 = te[5], m23 = te[9];
-        var m31 = te[2], m32 = te[6], m33 = te[10];
-
-        if ( order === undefined || order === 'XYZ' ) {
-
-            this.y = Math.asin( $3Dmol.Math.clamp( m13, -1, 1 ) );
-
-            if ( Math.abs( m13 ) < 0.99999 ) {
-
-                this.x = Math.atan2( - m23, m33 );
-                this.z = Math.atan2( - m12, m11 );
-
-            } else {
-
-                this.x = Math.atan2( m32, m22 );
-                this.z = 0;
-
-            }
-        }
-        
-        else {
-            console.error("Error with vector's setEulerFromRotationMatrix: Unknown order: " + order);
-        }
-        
-        return this;
-
-    },
-
-    rotateAboutVector : function(axis, ang) {
-
-        axis.normalize();
-        var cosang = Math.cos(ang);
-        var sinang = Math.sin(ang); 
-        // Rodrigues' rotation formula, from wikipedia
-
-        var term1 = this.clone().multiplyScalar(cosang);
-        var term2 = (axis.clone().cross(this)).multiplyScalar(sinang)
-        var term3 = axis.clone().multiplyScalar(axis.clone().dot(this)).multiplyScalar(1-cosang);
-
-        var rot = term1.add(term2).add(term3);
-    
-        this.x = rot.x;
-        this.y = rot.y;
-        this.z = rot.z;
-
-        return this;
-    },
-    
-    clone : function() {
-        return new $3Dmol.Vector3(this.x, this.y, this.z);
-    }
-    
-};
-
-//Matrices
-
-//Matrix3
-/** @constructor */
-$3Dmol.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
-    );
-    
-};
-
-$3Dmol.Matrix3.prototype = {
-    
-    constructor : $3Dmol.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]
-        );
-    },
-    
-    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;
-    },
-
-    getInverse: function ( matrix, throwOnInvertible ) {
-        // input: Matrix4
-
-        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;
-    },
-
-    //https://en.wikipedia.org/wiki/Determinant
-    getDeterminant: function() {
-        var m = this.elements;
-
-        /*|a b c|
-          |d e f|
-          |g h i|*/
-
-        var determinant
-            = m[0] * m[4] * m[8] //+aei
-            + m[1] * m[5] * m[6] //+bfg
-            + m[2] * m[3] * m[7] //+cdh
-            - m[2] * m[4] * m[6] //-ceg
-            - m[1] * m[3] * m[8] //-bdi
-            - m[0] * m[5] * m[7];//-afh
-        return determinant;
-    },
-    
-    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;
-    },
-    
-    clone: function () {
-        var te = this.elements;
-
-        return new $3Dmol.Matrix3(
-
-            te[0], te[3], te[6],
-            te[1], te[4], te[7],
-            te[2], te[5], te[8]
-
-        );
-    }
-   
-};
-
-//Matrix 4
-/** @constructor */
-$3Dmol.Matrix4 = function(n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44) {
-
-    var te = this.elements = new Float32Array( 16 );
-    
-    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;
-
-};
-
-$3Dmol.Matrix4.prototype = {
-
-    constructor : $3Dmol.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 ) {
-        var me = m.elements;
-
-        this.set(
-
-            me[0], me[4], me[8], me[12],
-            me[1], me[5], me[9], me[13],
-            me[2], me[6], me[10], me[14],
-            me[3], me[7], me[11], me[15]
-
-        );
-
-        return this;
-    },
-
-    matrix3FromTopLeft: function () {
-        var te = this.elements;
-        return new $3Dmol.Matrix3(te[0],te[4],te[8],te[1],te[5],te[9],te[2],te[6],te[10]);
-    },
-
-    setRotationFromEuler: function ( v, order ) {
-
-        var te = this.elements;
-
-        var x = v.x, y = v.y, z = v.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 ( order === undefined || 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
-            console.error("Error with matrix4 setRotationFromEuler. Order: " + order);
-
-        return this;
-
-    },
-
-    setRotationFromQuaternion: 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 );
-
-        return this;
-    },
-
-    lookAt: function() {
-        var x = new $3Dmol.Vector3();
-        var y = new $3Dmol.Vector3();
-        var z = new $3Dmol.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;
-        };
-
-    }(),
-
-    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;
-    },
-    
-    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;
-    },
-    
-    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;
-    },
-
-    getPosition: function() {
-        var v1 = new $3Dmol.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 = me[ 0 ] * te[ 0 ] + me[ 1 ] * te[ 4 ] + me[ 2 ] * te[ 8 ] + me[ 3 ] * 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;
-    },
-
-    isReflected: function() {
-        return this.matrix3FromTopLeft().getDeterminant() < 0;
-    },
-
-    compose: function() {
-        var mRotation = new $3Dmol.Matrix4(),
-            mScale = new $3Dmol.Matrix4();
-        
-        return function ( translation, rotation, scale ) {
-
-            var te = this.elements;
-
-            mRotation.identity();
-            mRotation.setRotationFromQuaternion( rotation );
-
-            mScale.makeScale( scale.x, scale.y, scale.z );
-
-            this.multiplyMatrices( mRotation, mScale );
-
-            te[12] = translation.x;
-            te[13] = translation.y;
-            te[14] = translation.z;
-
-            return this;
-
-        };
-    }(),
-
-    decompose: function() {
-        var x = new $3Dmol.Vector3(),
-            y = new $3Dmol.Vector3(),
-            z = new $3Dmol.Vector3(),
-            matrix = new $3Dmol.Matrix4();
-
-        return function ( translation, rotation, scale ) {
-
-            var te = this.elements;
-
-            // grab the axis vectors
-            x.set( te[0], te[1], te[2] );
-            y.set( te[4], te[5], te[6] );
-            z.set( te[8], te[9], te[10] );
-
-            translation = ( translation instanceof $3Dmol.Vector3 ) ? translation : new $3Dmol.Vector3();
-            rotation = ( rotation instanceof $3Dmol.Quaternion ) ? rotation : new $3Dmol.Quaternion();
-            scale = ( scale instanceof $3Dmol.Vector3 ) ? scale : new $3Dmol.Vector3();
-
-            scale.x = x.length();
-            scale.y = y.length();
-            scale.z = z.length();
-
-            translation.x = te[12];
-            translation.y = te[13];
-            translation.z = te[14];
-
-            // scale the rotation part
-
-            matrix.copy( this );
-
-            matrix.elements[0] /= scale.x;
-            matrix.elements[1] /= scale.x;
-            matrix.elements[2] /= scale.x;
-
-            matrix.elements[4] /= scale.y;
-            matrix.elements[5] /= scale.y;
-            matrix.elements[6] /= scale.y;
-
-            matrix.elements[8] /= scale.z;
-            matrix.elements[9] /= scale.z;
-            matrix.elements[10] /= scale.z;
-
-            rotation.setFromRotationMatrix( matrix );
-
-            return [ translation, rotation, scale ];
-
-        };
-    }(),
-
-    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)));
-        
-    },
-
-    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( $3Dmol.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 );
-    },
-    
-    isEqual : function (m) {
-        var me = m.elements;
-        var te = this.elements;
-        
-        if (te[0] == me[0] && te[4] == me[4] && te[8] == me[8] && te[12] == me[12] &&
-            te[1] == me[1] && te[5] == me[5] && te[9] == me[9] && te[13] == me[13] &&
-            te[2] == me[2] && te[6] == me[6] && te[10] == me[10] && te[14] == me[14] &&
-            te[3] == me[3] && te[7] == me[7] && te[11] == me[11] && te[15] == me[15]) {
-            return true;
-        }
-        else {
-            return false;
-        }
-    },
-
-    clone: function () {
-        var te = this.elements;
-
-        return new $3Dmol.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]
-
-        );
-    },
-    
-    isIdentity: function () {
-        var te = this.elements;
-        
-        if (te[0] == 1 && te[4] == 0 && te[8] == 0 && te[12] == 0 &&
-            te[1] == 0 && te[5] == 1 && te[9] == 0 && te[13] == 0 &&
-            te[2] == 0 && te[6] == 0 && te[10] == 1 && te[14] == 0 &&
-            te[3] == 0 && te[7] == 0 && te[11] == 0 && te[15] == 1) {
-            return true;
-        }
-        else {
-            return false;
-        }
-    }
-    
-};
-/** @constructor */
-$3Dmol.Ray = function(origin, direction) {
-    
-    this.origin = (origin !== undefined) ? 
-        origin : new $3Dmol.Vector3();
-        
-    this.direction = (direction !== undefined) ?
-        direction : new $3Dmol.Vector3();
-      
-};
-
-//TODO: Remove methods we don't need (intersectPlane ??)
-$3Dmol.Ray.prototype = {
-    
-    constructor : $3Dmol.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 $3Dmol.Vector3();
-        
-        return result.copy(this.direction).multiplyScalar(t).add(this.origin);
-        
-    },
-    
-    recast : function() {
-        
-        var v1 = new $3Dmol.Vector3();
-        
-        return function(t) {
-            this.origin.copy(this.at(t, v1));
-            
-            return this;
-        };
-        
-    }(),
-    
-    closestPointToPoint : function(point, optionalTarget) {
-        
-        var result = optionalTarget || new $3Dmol.Vector3();
-        result.subVectors(point, this.origin);
-        var directionDistance = result.dot(this.direction);
-        
-        //returns a point on this ray
-        return result.copy(this.direction).multiplyScalar(directionDistance).add(this.origin);
-        
-    },
-    
-    distanceToPoint : function(point) {
-        
-        var v1 = new $3Dmol.Vector3();
-        
-        return function(point) {
-            var directionDistance = v1.subVectors(point, this.origin).dot(this.direction);
-            v1.copy(this.direction).multiplyScalar(directionDistance).add(this.origin);
-            return v1.distanceTo(point);
-        };
-        
-    }(),
-    
-    isIntersectionCylinder : function() {
-        
-    },
-    
-    isIntersectionSphere : function(sphere) {
-       return (this.distanceToPoint(sphere.center) <= sphere.radius);
-          
-    },
-    
-    isIntersectionPlane : function(plane) {
-        
-        var denominator = plane.normal.dot(this.direction);
-        
-        //plane and ray are not perpendicular
-        if (denominator !== 0) 
-            return true;
-        
-        if (plane.distanceToPoint(this.origin) === 0) 
-            return true;
-        
-        return false;
-        
-    },
-    
-    distanceToPlane : function(plane) {
-       
-       var denominator = plane.normal.dot(this.direction);
-       if (denominator === 0) {
-           
-           //line is coplanar
-       if (plane.distanceToPoint(this.origin) === 0)
-           return 0;
-       
-       //ray is parallel
-           return undefined;
-       }
-       
-       var t = - (this.origin.dot(plane.normal) + plane.constant) / denominator;
-       
-       return t;
-       
-    },
-    
-    intersectPlane : function(plane, optionalTarget) {
-       
-       var t = this.distanceToPlane(plane);
-       
-       if (t === undefined)
-           return undefined;
-       
-       return this.at(t, optionalTarget);
-       
-    },
-    
-    applyMatrix4 : function(matrix4) {
-       
-       this.direction.add(this.origin).applyMatrix4(matrix4);
-       this.origin.applyMatrix4(matrix4);
-       this.direction.sub(this.origin);
-       
-       return this;
-       
-    },
-    
-    equals : function(ray) {
-       
-       return ray.origin.equals(this.origin) && ray.direction.equals(this.direction);
-       
-    },
-    
-    clone : function() {
-    
-       return new $3Dmol.Ray().copy(this);
-    
-    }
- 
-     
-};
-
-//Intersection sphere and box shapes.  
-
-
-//Intersection sphere for sphere, stick render
-/** @constructor */
-$3Dmol.Sphere = function(center, radius) {
-
-    this.center = (center !== undefined) ? 
-        center : new $3Dmol.Vector3();
-        
-    this.radius = (radius !== undefined) ?
-        radius : 0;
-        
-};
-
-$3Dmol.Sphere.prototype = {
-    
-    constructor : $3Dmol.Sphere,
-    
-    set : function(center, radius) {
-        
-        this.center.copy(center);
-        this.radius = radius;
-        
-        return this;
-        
-    },
-    
-    copy : function(sphere) {
-        
-        this.center.copy(sphere.center);
-        this.radius = sphere.radius;
-        
-        return this;
-        
-    },
-    
-    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 $3Dmol.Sphere().copy(this);
-        
-    }
-
-};
-
-
-//Bounding cylinder for stick render  
-/** @constructor */
-$3Dmol.Cylinder = function(c1, c2, radius) {
-
-    this.c1 = (c1 !== undefined) ?
-        c1 : new $3Dmol.Vector3();
-
-    this.c2 = (c2 !== undefined) ?
-        c2 : new $3Dmol.Vector3();
-        
-    this.direction = new $3Dmol.Vector3().subVectors(this.c2, this.c1).normalize();
-
-    this.radius = (radius !== undefined) ?
-        radius : 0;
-    
-};
-
-$3Dmol.Cylinder.prototype = {
-
-    constructor : $3Dmol.Cylinder,
-
-    copy : function(cylinder) {
-
-        this.c1.copy(cylinder.c1);
-        this.c2.copy(cylinder.c2);
-        this.direction.copy(cylinder.direction);
-        this.radius = cylinder.radius;
-
-        return this;
-
-    },
-    
-    lengthSq : function() {
-    
-        var vector = new $3Dmol.Vector3();
-        
-        return function(){
-            return vector.subVectors(this.c2, this.c1).lengthSq();
-        };
-        
-    }(),
-
-    applyMatrix4 : function(matrix) {
-        
-        this.direction.add(this.c1).applyMatrix4(matrix);
-        this.c1.applyMatrix4(matrix);
-        this.c2.applyMatrix4(matrix);
-        this.direction.sub(this.c1).normalize();
-        this.radius = this.radius * matrix.getMaxScaleOnAxis();
-
-        return this;
-
-    }
-
-};
-
-
-//plane specified by three points
-/** @constructor */
-$3Dmol.Triangle = function(a, b, c){
-   
-    this.a = (a !== undefined) ?
-        a : new $3Dmol.Vector3();
-
-    this.b = (b !== undefined) ?
-        b : new $3Dmol.Vector3();
-    
-    this.c = (c !== undefined) ?
-        c : new $3Dmol.Vector3();   
-  
-};
-
-$3Dmol.Triangle.prototype = {
-
-    constructor : $3Dmol.Triangle,
-    
-    copy : function(triangle) {
-        
-        this.a.copy(triangle.a);
-        this.b.copy(triangle.b);
-        this.c.copy(triangle.c);
-        
-        return this;
-        
-    },
-    
-    applyMatrix4 : function(matrix) {
-        
-        this.a.applyMatrix4(matrix);
-        this.b.applyMatrix4(matrix);
-        this.c.applyMatrix4(matrix);
-        
-        return this;
-        
-    },
-    
-    getNormal : function() {
-        
-        var v1 = new $3Dmol.Vector3();
-        
-        return function() {
-            
-            var norm = this.a.clone();
-            norm.sub(this.b);
-            v1.subVectors(this.c, this.b);
-            
-            norm.cross(v1);
-            norm.normalize();
-            
-            return norm;
-            
-        };
-        
-    }()
-
-};
-
-
-/* core Object3D
- * Base class for Scene, Camera, Geometry
- * Geometry class
- */
-
-//Event Handling
-/** @this {$3Dmol.EventDispatcher} */
-$3Dmol.EventDispatcher = function() {
-  
-    var listeners = {};
-    
-    this.addEventListener = function(type, listener) {
-        if (listeners[type] === undefined)
-            listeners[type] = [];
-        
-        if (listeners[type].indexOf(listener) === -1)
-            listeners[type].push(listener);
-    };  
-    
-    this.removeEventListener = function(type, listener) {
-        
-        var index = listeners[type].indexOf(listener);
-        
-        if (index !== -1)
-            listeners[type].splice(index, 1);
-              
-    };
-    
-    this.dispatchEvent = function(event) {
-        
-        var listenerArray = listeners[event.type];
-        
-        if (listenerArray !== undefined) {
-            event.target = this;
-            
-            for (var i = 0, l = listenerArray.length; i < l; i++)
-                listenerArray[i].call(this, event);
-                
-        }
-            
-    };
-    
-};
-
-$3Dmol.Color = function( color ){
-    
-    if ( arguments.length > 1) {
-            this.r = arguments[0] || 0.0;
-            this.g = arguments[1] || 0.0;
-            this.b = arguments[2] || 0.0;
-
-            return this;
-    }
-    
-    return this.set(color);
-                
-};
-
-$3Dmol.Color.prototype = {
-    
-    constructor: $3Dmol.Color,
-    
-    r: 0.0, g: 0.0, b: 0.0,
-    
-    set : function(val) {
-        
-            if (val instanceof $3Dmol.Color) 
-                return val.clone();
-
-            else if (typeof val === 'number')
-                this.setHex(val);
-            
-            else if (typeof val === 'object' && "r" in val && "g" in val && "b" in val) {
-                this.r = val.r;
-                this.g = val.g;
-                this.b = val.b;
-            }
-    },
-    
-    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;
-    },
-    
-    getHex: function() {
-        var R = Math.round(this.r*255);
-        var G = Math.round(this.g*255);
-        var B = Math.round(this.b*255);
-        return R<<16 | G << 8 | B;
-    },
-    
-    clone : function() {
-            return new $3Dmol.Color(this.r, this.g, this.b);
-    },
-        
-    copy : function(color) {
-        this.r = color.r;
-        this.g = color.g;
-        this.b = color.b;
-        
-        return this;
-    },
-    
-    //return object that represents color components from 0 to 255
-    scaled : function() {
-        var ret = {};
-        ret.r = Math.round(this.r*255);
-        ret.g = Math.round(this.g*255);
-        ret.b = Math.round(this.b*255);
-        ret.a = 1.0;
-        return ret;
-    }
-    
-};
-
-//Object3D base constructor function
-/** @this {$3Dmol.Object3D} */
-$3Dmol.Object3D = function() {
-    
-    this.id = $3Dmol.Object3DIDCount++;
-    
-    this.name = "";
-    
-    this.parent = undefined;
-    this.children = [];
-    
-    this.position = new $3Dmol.Vector3();
-    this.rotation = new $3Dmol.Vector3();
-    this.matrix = new $3Dmol.Matrix4();
-    this.matrixWorld = new $3Dmol.Matrix4();
-    this.quaternion = new $3Dmol.Quaternion();
-    this.eulerOrder = 'XYZ';
-    
-    this.up = new $3Dmol.Vector3(0, 1, 0);
-    this.scale = new $3Dmol.Vector3(1, 1, 1);
-    
-    this.matrixAutoUpdate = true;
-    this.matrixWorldNeedsUpdate = true;
-    this.rotationAutoUpdate = true;
-    this.useQuaternion = false;
-    
-    this.visible = true;
-    
-};
-
-$3Dmol.Object3D.prototype = {
-    
-    constructor : $3Dmol.Object3D,
-    
-    lookAt : function(vector) {
-        
-        this.matrix.lookAt(vector, this.position, this.up);
-        
-        if (this.rotationAutoUpdate) {
-            
-            if (this.useQuaternion === true) 
-                this.quaternion.copy(this.matrix.decompose()[1]);
-            else
-                this.rotation.setEulerFromRotationMatrix(this.matrix, this.eulerOrder);
-        }
-    },
-    
-    //add child object
-    add : function(object) {
-        if (object === this){
-            console.error("Can't add $3Dmol.Object3D to itself");
-            return;
-        }
-        
-        object.parent = this;
-        this.children.push(object);
-        
-        //add to the scene (i.e. follow up this instance's parents until reach the top)
-        
-        var scene = this;
-        
-        while (scene.parent !== undefined)
-            scene = scene.parent;
-            
-        if (scene !== undefined && scene instanceof $3Dmol.Scene) 
-            scene.__addObject(object);
-        
-    },
-    
-    remove : function(object) {
-        
-        var index = this.children.indexOf(object);
-        
-        if (index !== -1) {
-            
-            object.parent = undefined;
-            this.children.splice(index, 1);
-            
-            //Remove from scene
-            
-            var scene = this;
-            
-            while (scene.parent !== undefined)
-                scene = scene.parent;
-                
-            if (scene !== undefined && scene instanceof $3Dmol.Scene)
-                scene.__removeObject(object);
-                
-        }
-    },
-    
-    updateMatrix : function() {
-        
-        this.matrix.setPosition(this.position);
-        
-        if (this.useQuaternion === false) 
-            this.matrix.setRotationFromEuler(this.rotation, this.eulerOrder);
-        else
-            this.matrix.setRotationFromQuaternion(this.quaternion);
-        
-        //TODO: Do I need this??
-        if (this.scale.x !== 1 || this.scale.y !== 1 || this.scale.z !== 1)
-            this.matrix.scale(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;
-        
-        //Update matrices of all children
-        for (var i = 0; i < this.children.length; i++) {
-            this.children[i].updateMatrixWorld(true);
-        }
-    },
-    
-    clone : function(object) {
-        
-        if (object === undefined)
-            object = new $3Dmol.Object3D();
-            
-        object.name = this.name;
-        
-        object.up.copy(this.up);
-        object.position.copy(this.position);
-        object.rotation.copy(this.rotation);
-        object.eulerOrder = this.eulerOrder;
-        object.scale.copy(this.scale);
-
-        object.rotationAutoUpdate = this.rotationAutoUpdate;
-        object.matrix.copy(this.matrix);
-        object.matrixWorld.copy(this.matrixWorld);
-        object.quaternion.copy(this.quaternion);
-        object.matrixAutoUpdate = this.matrixAutoUpdate;
-        object.matrixWorldNeedsUpdate = this.matrixWorldNeedsUpdate;
-        
-        object.useQuaternion = this.useQuaternion;
-        
-        object.visible = this.visible;
-        
-        for (var i = 0; i < this.children.length; i++) {
-            var child = this.children[i];
-            object.add(child.clone());
-        }
-        
-        return object;
-        
-    },
-    
-    setVisible: function(val) { //recursively set visibility
-        this.visible = val;
-        for (var i = 0; i < this.children.length; i++) {
-            var child = this.children[i];
-            child.setVisible(val);
-        }
-    }
-};
-
-$3Dmol.Object3DIDCount = 0;
-
-//Geometry class
-//TODO: What can I remove - how can I optimize ?
-$3Dmol.Geometry = (function() {
-   
-    var BUFFERSIZE = 65535; //limited to 16bit indices
-    
-    
-    /** @constructor */
-    var geometryGroup = function(id) {
-        this.id = id || 0;
-        //for performance reasons, callers must directly modify these
-        this.vertexArray = null;
-        this.colorArray = null;
-        this.normalArray = null;
-        this.faceArray = null;
-        //this.adjFaceArray=null;
-        this.lineArray = null;
-        this.vertices = 0;
-        this.faceidx = 0;
-        this.lineidx = 0;
-        
-    };
-    
-    geometryGroup.prototype.getNumVertices = function() {
-        return this.vertices;
-    };
-    
-    geometryGroup.prototype.getVertices = function() {
-        return this.vertexArray;
-    };
-    
-    
-    geometryGroup.prototype.getCentroid = function() {
-        
-        var centroid = new $3Dmol.Vector3();
-        var offset, x, y, z;
-        
-        for (var i = 0; i < this.vertices; ++i) {
-            offset = i*3;
-            
-            x = this.vertexArray[offset]; y = this.vertexArray[offset+1]; z = this.vertexArray[offset+2];
-            
-            centroid.x += x; centroid.y += y; centroid.z += z;
-        }
-        
-        //divideScalar checks for 0 denom
-        centroid.divideScalar(this.vertices);
-        
-        return centroid;
-    };
-    
-    //setup normals - vertex and face array must exist
-    geometryGroup.prototype.setNormals = function() {        
-        
-        var faces = this.faceArray;
-        var verts = this.vertexArray;
-        var norms = this.normalArray;
-        
-        if (! this.vertices || ! this.faceidx) 
-            return;
-        
-        //vertex indices
-        var a, b, c, d,
-        //and actual vertices
-        vA, vB, vC, norm;
-            
-        for (var i = 0; i < faces.length / 3; ++i) {
-            
-            a = faces[i * 3] * 3;
-            b = faces[i * 3 + 1] * 3;
-            c = faces[i * 3 + 2] * 3;
-            
-            vA = new $3Dmol.Vector3(verts[a], verts[a+1], verts[a+2]);
-            vB = new $3Dmol.Vector3(verts[b], verts[b+1], verts[b+2]);
-            vC = new $3Dmol.Vector3(verts[c], verts[c+1], verts[c+2]);
-            
-            vA.subVectors(vA, vB);
-            vC.subVectors(vC, vB);
-            vC.cross(vA);
-            
-            //face normal
-            norm = vC;
-            norm.normalize();
-            
-            norms[a] += norm.x; norms[b] += norm.x; norms[c] += norm.x;
-            norms[a + 1] += norm.y; norms[b + 1] += norm.y; norms[c + 1] += norm.y;
-            norms[a + 2] += norm.z; norms[b + 2] += norm.z; norms[c + 2] += norm.z;
-            
-        }             
-                
-    };
-    
-    //sets line index array from face arr
-    //Note - assumes all faces are triangles (i.e. there will
-    //be an extra diagonal for four-sided faces - user should 
-    //specify linearr for custom shape generation to show wireframe squares
-    //as rectangles rather than two triangles)
-    geometryGroup.prototype.setLineIndices = function() {
-        
-        if (! this.faceidx)
-            return;
-                    
-        var faceArr = this.faceArray, lineArr = this.lineArray = new Uint16Array(this.faceidx*2);      
-        this.lineidx = this.faceidx*2;         
-        var faceoffset;
-            
-        for (var i = 0; i < this.faceidx / 3; ++i) {
-            
-            faceoffset = i*3; lineoffset = faceoffset*2;          
-            var a = faceArr[faceoffset], b = faceArr[faceoffset+1], c = faceArr[faceoffset+2];
-            
-            lineArr[lineoffset] = a; lineArr[lineoffset+1] = b;
-            lineArr[lineoffset+2] = a; lineArr[lineoffset+3] = c;
-            lineArr[lineoffset+4] = b; lineArr[lineoffset+5] = c;
-            
-        }
-    };
-    
-    geometryGroup.prototype.truncateArrayBuffers = function(mesh, reallocatemem) {
-        
-        mesh = (mesh === true) ? true : false;
-        
-        var vertexArr = this.vertexArray,
-            colorArr = this.colorArray,
-            normalArr = this.normalArray,
-            faceArr = this.faceArray,
-            lineArr = this.lineArray;
-
-        //subarray to avoid copying and reallocating memory
-        this.vertexArray = vertexArr.subarray(0,this.vertices*3);
-        this.colorArray = colorArr.subarray(0,this.vertices*3);
-        
-        if (mesh) {
-            this.normalArray = normalArr.subarray(0,this.vertices*3);
-            this.faceArray = faceArr.subarray(0,this.faceidx); 
-            
-            if(this.lineidx > 0) //not always set so reclaim memory
-                this.lineArray = lineArr.subarray(0,this.lineidx); 
-            else
-                this.lineArray = new Uint16Array();
-        }
-        else {
-            this.normalArray = new Float32Array(); 
-            this.faceArray = new Uint16Array(); 
-            this.lineArray = new Uint16Array(); 
-        }
-        
-        if(reallocatemem) { 
-            //actually copy smaller arrays to save memory
-            if(this.normalArray) this.normalArray = new Float32Array(this.normalArray);
-            if(this.faceArray) this.faceArray = new Uint16Array(this.faceArray);
-            if(this.lineArray) this.lineArray = new Uint16Array(this.lineArray);
-            if(this.vertexArray) this.vertexArray = new Float32Array(this.vertexArray);
-            if(this.colorArray) this.colorArray = new Float32Array(this.colorArray);
-            
-        }
-        this.__inittedArrays = true;        
-        
-    };
-    
-    var addGroup = function(geo) {
-        var ret = new geometryGroup(geo.geometryGroups.length);
-        geo.geometryGroups.push(ret);
-        geo.groups = geo.geometryGroups.length;
-        
-        ret.vertexArray = new Float32Array(BUFFERSIZE*3);
-        ret.colorArray = new Float32Array(BUFFERSIZE*3);
-        
-        //TODO: instantiating uint arrays according to max number of vertices
-        // is dangerous, since there exists the possibility that there will be 
-        // more face or line indices than vertex points - but so far that doesn't
-        // seem to be the case for any of the renders 
-        if (geo.mesh) {
-            ret.normalArray = new Float32Array(BUFFERSIZE*3);
-            ret.faceArray = new Uint16Array(BUFFERSIZE*6);
-            ret.lineArray = new Uint16Array(BUFFERSIZE*6);
-        }
-        
-        
-        return ret;
-    };
-    /** @constructor */
-    var Geometry = function(mesh) {
-        
-        $3Dmol.EventDispatcher.call(this);
-        
-        this.id = $3Dmol.GeometryIDCount++;
-    
-        this.name = '';
-    
-        this.hasTangents = false;
-    
-        this.dynamic = true; // the intermediate typed arrays will be deleted when set to false
-        this.mesh = (mesh === true) ? true : false; // Does this geometry represent a mesh (i.e. do we need Face/Line index buffers?)
-        // update flags
-    
-        this.verticesNeedUpdate = false;
-        this.elementsNeedUpdate = false;
-        this.normalsNeedUpdate = false;
-        this.colorsNeedUpdate = false;
-    
-        this.buffersNeedUpdate = false;
-        
-        this.geometryGroups = [];
-        this.groups = 0;
-        
-    };
-    
-    Geometry.prototype = {
-        
-        constructor : Geometry,
-
-        //Get geometry group to accomodate addVertices new vertices - create 
-        // new group if necessary       
-        updateGeoGroup : function(addVertices) {
-        
-            addVertices = addVertices || 0;
-            
-            var retGroup = this.groups > 0 ? this.geometryGroups[ this.groups - 1 ] : null;
-            
-            if (!retGroup || retGroup.vertices + addVertices > BUFFERSIZE) 
-                retGroup = addGroup(this);
-                
-            return retGroup;
-            
-        },
-        
-        addGeoGroup : function() {
-            return addGroup(this);  
-        },
-        
-        setUpNormals : function(three) {
-            
-            three = three || false;
-            
-            for (var g = 0; g < this.groups; g++) {
-            
-                var geoGroup = this.geometryGroups[g];            
-                
-                geoGroup.setNormals(three);
-                
-            }  
-                      
-        },
-        
-        setUpWireframe : function() {
-            for (var g = 0; g < this.groups; g++) {
-                var geoGroup = this.geometryGroups[g];
-                
-                geoGroup.setLineIndices();
-            }
-        },
-        
-        //After vertices, colors, etc are collected in regular or typed arrays,
-        //  create typed arrays from regular arrays if they don't already exist,
-        initTypedArrays : function() {
-                
-            for (var g = 0; g < this.groups; g++) {
-                
-                var group = this.geometryGroups[g];
-                
-                if (group.__inittedArrays === true)
-                    continue;
-                
-                //do not actually reallocate smaller memory here because
-                //of the performance hit - if you know your geometry is small,
-                //truncate manually with the second parameter true
-                group.truncateArrayBuffers(this.mesh, false);
-            }
-            
-        
-        },
-        
-        dispose : function() {
-            this.dispatchEvent( {type : 'dispose'} );
-        }
-    };
-
-    
-    return Geometry;
-    
-})();
-
-Object.defineProperty($3Dmol.Geometry.prototype, "vertices", {
-    
-    /** @this {$3Dmol.Geometry} */
-    get : function() {
-        var vertices = 0;
-        for (var g = 0; g < this.groups; g++)
-            vertices += this.geometryGroups[g].vertices;
-            
-        return vertices;
-    } 
-        
-});
-
-$3Dmol.GeometryIDCount = 0;
-
-
-//Raycaster
-/** @constructor */
-$3Dmol.Raycaster = (function() {
-    
-    var Raycaster = function(origin, direction, far, near) {
-        
-        this.ray = new $3Dmol.Ray(origin, direction);
-        
-        if (this.ray.direction.lengthSq() > 0) 
-            this.ray.direction.normalize();
-        
-        this.near = near || 0;
-        this.far = far || Infinity;
-    
-    };
-    
-    var sphere = new $3Dmol.Sphere();
-    var cylinder = new $3Dmol.Cylinder();
-    var triangle = new $3Dmol.Triangle();
-    var w_0 = new $3Dmol.Vector3(); // for cylinders, cylinder.c1 - ray.origin
-    var v1 = new $3Dmol.Vector3(); // all purpose local vector
-    var v2 = new $3Dmol.Vector3();
-    var v3 = new $3Dmol.Vector3();
-    //var facePlane = new $3Dmol.Plane();
-    var localRay = new $3Dmol.Ray();
-    var intersectPoint = new $3Dmol.Vector3();
-    var matrixPosition = new $3Dmol.Vector3();
-    
-    var inverseMatrix = new $3Dmol.Matrix4();
-        
-    var descSort = function(a, b) {
-        return a.distance - b.distance;
-    };
-
-    // [-1, 1]
-    var clamp = function(x) {
-        return Math.min(Math.max(x, -1), 1);
-    };
-    
-    //object is a Sphere or (Bounding) Box
-    var intersectObject = function(group, clickable, raycaster, intersects) {
-        
-        matrixPosition.getPositionFromMatrix(group.matrixWorld);
-        
-        if ((clickable.clickable !== true) || (clickable.intersectionShape === undefined))
-            return intersects;       
-        var intersectionShape = clickable.intersectionShape;
-        var precision = raycaster.linePrecision;
-        precision *= group.matrixWorld.getMaxScaleOnAxis();
-        var precisionSq = precision*precision;
-
-        //Check for intersection with clickable's bounding sphere, if it exists
-        if (clickable.boundingSphere !== undefined && clickable.boundingSphere instanceof $3Dmol.Sphere) {
-            sphere.copy(clickable.boundingSphere);
-            sphere.applyMatrix4(group.matrixWorld);          
-            if (!raycaster.ray.isIntersectionSphere(sphere)) {             
-				return intersects;
-            }
-        }      
-        //Iterate through intersection objects
-        var i, il,
-            norm, normProj, cylProj, rayProj,
-            distance, closestDistSq, denom, discriminant,
-            s, t, s_c, t_c;
-        //triangle faces
-        for (i = 0, il = intersectionShape.triangle.length; i < il; i++) {
-            
-            if (intersectionShape.triangle[i] instanceof $3Dmol.Triangle) {
-                
-                triangle.copy(intersectionShape.triangle[i]);
-                triangle.applyMatrix4(group.matrixWorld);
-                
-                norm = triangle.getNormal();
-                
-                normProj = raycaster.ray.direction.dot(norm);
-                
-                //face culling
-                if (normProj >= 0)
-                    continue;
-                
-                w_0.subVectors(triangle.a, raycaster.ray.origin);
-                
-                distance = (norm.dot(w_0)) / normProj;
-                
-                if (distance < 0)
-                    continue;
-                    
-                //intersects with plane, check if P inside triangle
-                v1.copy(raycaster.ray.direction).multiplyScalar(distance).add(raycaster.ray.origin);
-                v1.sub(triangle.a); // from pt a to intersection point P
-                
-                v2.copy(triangle.b).sub(triangle.a); // from pt a to b
-                v3.copy(triangle.c).sub(triangle.a); // from pt a to c
-                var b_dot_c = v2.dot(v3);
-                var b_sq = v2.lengthSq();
-                var c_sq = v3.lengthSq();
-                
-                // P = A + s(v2) + t(v3), inside trianle if 0 <= s, t <=1  and (s + t) <=0
-                
-                t = ( b_sq*v1.dot(v3) - b_dot_c*v1.dot(v2) ) / ( b_sq*c_sq - b_dot_c*b_dot_c );
-                
-                if (t < 0 || t > 1)
-                    continue;
-                
-                s = ( v1.dot(v2) - t*b_dot_c ) / b_sq;
-                
-                if ( (s < 0 || s > 1) || s + t > 1)
-                    continue;
-                    
-                else{
-                    intersects.push({clickable : clickable,
-                                     distance : distance});
-				}  
-            }
-        }    
-        //cylinders
-        for (i = 0, il = intersectionShape.cylinder.length; i < il; i++) {
-            
-            if (intersectionShape.cylinder[i] instanceof $3Dmol.Cylinder){
-                
-                cylinder.copy(intersectionShape.cylinder[i]);
-                cylinder.applyMatrix4(group.matrixWorld);
-                
-                w_0.subVectors(cylinder.c1, raycaster.ray.origin); 
-                
-                cylProj = w_0.dot(cylinder.direction); // Dela
-                rayProj = w_0.dot(raycaster.ray.direction); // Epsilon
-                
-                normProj = clamp(raycaster.ray.direction.dot(cylinder.direction)); // Beta
-                
-                denom = 1 - normProj*normProj;
-                
-                if (denom === 0.0)
-                    continue;
-                
-                s_c = (normProj*rayProj - cylProj) / denom;
-                t_c = (rayProj - normProj*cylProj) / denom;
-                
-                v1.copy(cylinder.direction).multiplyScalar(s_c).add(cylinder.c1);  // Q_c
-                v2.copy(raycaster.ray.direction).multiplyScalar(t_c).add(raycaster.ray.origin); // P_c
-                
-                closestDistSq = v3.subVectors(v1, v2).lengthSq();
-                var radiusSq = cylinder.radius*cylinder.radius;
-                
-                //Smoothing?
-                //if (closestDistSq > radiusSq) radiusSq += precisionSq;
-                
-                // closest distance between ray and cylinder axis not greater than cylinder radius;
-                // might intersect this cylinder between atom and bond midpoint
-                if (closestDistSq <= radiusSq){
-
-                    //Find points where ray intersects sides of cylinder
-                    discriminant = (normProj*cylProj - rayProj)*(normProj*cylProj - rayProj) - 
-                            denom*(w_0.lengthSq() - cylProj*cylProj - radiusSq);
-                    
-                    // ray tangent to cylinder?
-                    if (discriminant <= 0)
-                        t = distance = Math.sqrt(closestDistSq);
-                    else
-                        t = distance = ( (rayProj - normProj*cylProj) - Math.sqrt(discriminant) ) / denom; 
-                    
-                    //find closest intersection point; make sure it's between atom's position and cylinder midpoint
-                    
-                    s = normProj*t - cylProj;
-                    
-                    //does not intersect cylinder between atom and midpoint,
-                    // or intersects cylinder behind camera
-                    if (s < 0 || s*s > cylinder.lengthSq() || t < 0)
-                        continue;
-                    
-                    else
-                        intersects.push({clickable : clickable,
-                                         distance : distance});
-                    
-                }
-                    
-                
-            }
-            
-        }       
-        //lines
-        for (i = 0, il = intersectionShape.line.length; i < il; i += 2) {
-            
-            v1.copy(intersectionShape.line[i]);
-            v1.applyMatrix4(group.matrixWorld);
-            v2.copy(intersectionShape.line[i+1]);
-            v2.applyMatrix4(group.matrixWorld);
-            
-            v3.subVectors(v2, v1);
-            var bondLengthSq = v3.lengthSq();
-            v3.normalize();
-            
-            w_0.subVectors(v1, raycaster.ray.origin);
-            
-            lineProj = w_0.dot(v3);
-            rayProj = w_0.dot(raycaster.ray.direction);
-            
-            normProj = clamp(raycaster.ray.direction.dot(v3));
-            
-            denom = 1 - normProj*normProj;
-            
-            if (denom === 0.0)
-                continue;
-            
-            s_c = (normProj*rayProj - lineProj) / denom;
-            t_c = (rayProj - normProj*lineProj) / denom;
-            
-            v1.add(v3.multiplyScalar(s_c)); // Q_c
-            v2.copy(raycaster.ray.direction).multiplyScalar(t_c).add(raycaster.ray.origin); // P_c
-            
-            closestDistSq = v3.subVectors(v2, v1).lengthSq();
-            
-            if (closestDistSq < precisionSq && s_c*s_c < bondLengthSq)
-                intersects.push({clickable : clickable,
-                                 distance : t_c
-                                });
-            
-        }
-        for (i = 0, il = intersectionShape.sphere.length; i < il; i++) {
-            //sphere
-            if (intersectionShape.sphere[i] instanceof $3Dmol.Sphere) {
-                
-                sphere.copy(intersectionShape.sphere[i]);
-                sphere.applyMatrix4(group.matrixWorld);
-                
-                if (raycaster.ray.isIntersectionSphere(sphere)) {
-                    
-                    v1.subVectors(sphere.center, raycaster.ray.origin);
-                    
-                    //distance from ray origin to point on the ray normal to sphere's center
-                    //must be less than sphere's radius (since ray intersects sphere)
-                    var distanceToCenter = v1.dot(raycaster.ray.direction);
-                    
-                    discriminant = distanceToCenter*distanceToCenter - (v1.lengthSq() - sphere.radius*sphere.radius);
-                    
-                    //Don't select if sphere center behind camera
-                    if (distanceToCenter < 0) 
-                        return intersects;
-                    
-                    //ray tangent to sphere?
-                    if (discriminant <= 0)
-                        distance = distanceToCenter;
-                    
-                    //This is reversed if sphere is closer than ray origin.  Do we have 
-                    //to worry about handling that case?
-                    else 
-                        distance = distanceToCenter - Math.sqrt(discriminant);
-    
-                    intersects.push({clickable : clickable, 
-                                     distance : distance});
-                    return intersects;
-                }
-            }        
-       }
-        
-    };   
-       
-    Raycaster.prototype.precision = 0.0001;
-    Raycaster.prototype.linePrecision = 0.2;
-    
-    Raycaster.prototype.set = function(origin, direction) {
-        
-        this.ray.set(origin, direction);
-          
-    };
-    
-    Raycaster.prototype.intersectObjects = function(group, objects) {     
-        var intersects = [];
-        
-        for (var i = 0, l = objects.length; i < l; i++)            
-            intersectObject(group, objects[i], this, intersects);
-            
-        intersects.sort(descSort);
-        
-        return intersects;
-        
-    };
-    
-    return Raycaster;
-    
-})();
-
-
-//$3Dmol Projecion 
-//TODO: can probably strip this down a lot (only used for selection handling)
-/** @constructor */
-$3Dmol.Projector = function () {
-
-    var _viewMatrix = new $3Dmol.Matrix4(),
-    _viewProjectionMatrix = new $3Dmol.Matrix4();
-
-    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 );
-
-    };
-
-};
-/*
- * Simplified Perspective Camera
- */
-
-/** @constructor */
-$3Dmol.Camera = function(fov, aspect, near, far) {
-    
-    $3Dmol.Object3D.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.projectionMatrix = new $3Dmol.Matrix4();
-    this.projectionMatrixInverse = new $3Dmol.Matrix4();
-    this.matrixWorldInverse = new $3Dmol.Matrix4();
-    
-    this.updateProjectionMatrix();
-        
-};
-
-//Inherit Object3D's prototyped methods
-$3Dmol.Camera.prototype = Object.create($3Dmol.Object3D.prototype);
-
-$3Dmol.Camera.prototype.lookAt = function(vector){
-    
-    //Why is the parameter order switched (compared to Object3D)?
-    this.matrix.lookAt(this.position, vector, this.up);
-    
-    if (this.rotationAutoUpdate) {    
-        
-        if (this.useQuaternion === false) 
-            this.rotation.setEulerFromRotationMatrix( this.matrix, this.eulerOrder );
-        else
-            this.quaternion.copy( this.matrix.decompose()[ 1 ] );    
-            
-    }
-    
-};
-
-$3Dmol.Camera.prototype.updateProjectionMatrix = function () {
-
-    this.projectionMatrix.makePerspective( this.fov, this.aspect, this.near, this.far );
-
-};
-
-
-//Render plugins go here
-
-/**
- * Sprite render plugin
- * @this {$3Dmol.SpritePlugin}
- */
-
-$3Dmol.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++ ] = -1; _sprite.vertices[ i++ ] = -1; // vertex 0
-        _sprite.vertices[ i++ ] = 0;  _sprite.vertices[ i++ ] = 0;  // uv 0
-
-        _sprite.vertices[ i++ ] = 1;  _sprite.vertices[ i++ ] = -1; // vertex 1
-        _sprite.vertices[ i++ ] = 1;  _sprite.vertices[ i++ ] = 0;  // uv 1
-
-        _sprite.vertices[ i++ ] = 1;  _sprite.vertices[ i++ ] = 1;  // vertex 2
-        _sprite.vertices[ i++ ] = 1;  _sprite.vertices[ i++ ] = 1;  // uv 2
-
-        _sprite.vertices[ i++ ] = -1; _sprite.vertices[ i++ ] = 1;  // 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( $3Dmol.ShaderLib.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.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.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 invAspect = viewportHeight / viewportWidth;
-
-        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 );
-
-            _gl.uniform1f( uniforms.fogNear, fog.near );
-            _gl.uniform1f( uniforms.fogFar, fog.far );
-
-            _gl.uniform1i( uniforms.fogType, 1 );
-            oldFogType = 1;
-            sceneFogType = 1;
-
-
-        } 
-        
-        else {
-
-            _gl.uniform1i( uniforms.fogType, 0 );
-            oldFogType = 0;
-            sceneFogType = 0;
-
-        }
-
-
-        // update positions and sort
-
-        var i, sprite, material, screenPosition, size, 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 );
-                var w = material.map.image.width;
-                var h = material.map.image.height;
-                
-                scale[ 0 ] = w*_renderer.devicePixelRatio/viewportWidth;
-                scale[ 1 ] = h*_renderer.devicePixelRatio/viewportHeight;
-                
-                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 ) )
-                    );
-
-                } else {
-
-                    _gl.uniform1i( uniforms.useScreenCoordinates, 0 );
-                    _gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, sprite._modelViewMatrix.elements );
-                }
-
-                if ( scene.fog && material.fog ) {
-
-                    fogType = sceneFogType;
-
-                } else {
-
-                    fogType = 0;
-
-                }
-
-                if ( oldFogType !== fogType ) {
-
-                    _gl.uniform1i( uniforms.fogType, fogType );
-                    oldFogType = fogType;
-
-                }
-
-                size = 1 / ( material.scaleByViewport ? viewportHeight : 1 );
-
-                scale[ 0 ] *= size * sprite.scale.x;
-                scale[ 1 ] *= size * sprite.scale.y;
-
-                _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 );
-
-                //_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 );
-        
-        if ( ! _gl.getShaderParameter(fragmentShader, _gl.COMPILE_STATUS) || ! _gl.getShaderParameter(vertexShader,_gl.COMPILE_STATUS) ) {
-
-                console.error(_gl.getShaderInfoLog(fragmentShader));
-                console.error("could not initialize shader");
-                return null;
-        }
-
-        _gl.attachShader( program, fragmentShader );
-        _gl.attachShader( program, vertexShader );
-
-        _gl.linkProgram( program );
-
-        if (! _gl.getProgramParameter(program, _gl.LINK_STATUS) )
-                console.error("Could not initialize shader");
-
-        return program;
-
-    }
-
-    function painterSortStable ( a, b ) {
-
-        if ( a.z !== b.z ) {
-
-            return b.z - a.z;
-
-        } else {
-
-            return b.id - a.id;
-
-        }
-
-    }
-
-};
-
-$3Dmol.Light = function(hex, intensity) {
-    
-    $3Dmol.Object3D.call(this);
-    
-    this.color = new $3Dmol.Color(hex);
-    this.position = new $3Dmol.Vector3( 0, 1, 0 );
-    this.target = new $3Dmol.Object3D();
-
-    this.intensity = ( intensity !== undefined ) ? intensity : 1;
-
-    this.castShadow = false;
-    this.onlyShadow = false;
-    
-};
-
-$3Dmol.Light.prototype = Object.create($3Dmol.Object3D.prototype);
-/**
- * Line and Mesh material types
- * @constructor
- */
-$3Dmol.Material = function () {
-
-    $3Dmol.EventDispatcher.call( this );
-
-    this.id = $3Dmol.MaterialIdCount ++;
-
-    this.name = '';
-    
-    //TODO: Which of these instance variables can I remove??
-    this.side = $3Dmol.FrontSide;
-
-    this.opacity = 1;
-    this.transparent = false;
-
-    this.depthTest = true;
-    this.depthWrite = true;
-    
-    this.stencilTest = true;
-
-    this.polygonOffset = false;
-    this.polygonOffsetFactor = 0;
-    this.polygonOffsetUnits = 0;
-
-    this.alphaTest = 0;
-
-    this.visible = true;
-
-    this.needsUpdate = true;
-
-};
-
-
-$3Dmol.Material.prototype.setValues = function ( values ) {
-
-    if ( values === undefined ) return;
-
-    for ( var key in values ) {
-
-        var newValue = values[ key ];
-
-        if ( newValue === undefined ) {
-
-            console.warn( '$3Dmol.Material: \'' + key + '\' parameter is undefined.' );
-            continue;
-
-        }
-
-        if ( key in this ) {
-
-            var currentValue = this[ key ];
-
-            if ( currentValue instanceof $3Dmol.Color && newValue instanceof $3Dmol.Color ) {
-
-                currentValue.copy( newValue );
-
-            } else if ( currentValue instanceof $3Dmol.Color ) {
-
-                currentValue.set( newValue );
-
-            } else if ( currentValue instanceof $3Dmol.Vector3 && newValue instanceof $3Dmol.Vector3 ) {
-
-                currentValue.copy( newValue );
-
-            } else {
-
-                this[ key ] = newValue;
-
-            }
-
-        }
-
-    }
-
-};
-//TODO: might want to look into blending equations
-$3Dmol.Material.prototype.clone = function ( material ) {
-
-    if ( material === undefined ) material = new $3Dmol.Material();
-
-    material.name = this.name;
-
-    material.side = this.side;
-
-    material.opacity = this.opacity;
-    material.transparent = this.transparent;
-
-    material.depthTest = this.depthTest;
-    material.depthWrite = this.depthWrite;
-    material.stencilTest = this.stencilTest;
-
-    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;
-
-};
-
-$3Dmol.Material.prototype.dispose = function () {
-
-    this.dispatchEvent( { type: 'dispose' } );
-
-};
-
-$3Dmol.MaterialIdCount = 0;
-
-//Line basic material
-/** @constructor */
-$3Dmol.LineBasicMaterial = function(parameters) {
-    
-    $3Dmol.Material.call(this);
-    
-    this.color = new $3Dmol.Color(0xffffff);
-    
-    this.linewidth = 1;
-    this.linecap = 'round';
-    this.linejoin = 'round';
-    
-    this.vertexColors = false;
-    
-    this.fog = true;
-    this.shaderID = "basic";
-    this.setValues(parameters);
-    
-};
-
-$3Dmol.LineBasicMaterial.prototype = Object.create($3Dmol.Material.prototype);
-
-$3Dmol.LineBasicMaterial.prototype.clone = function() {
-  
-    var material = new $3Dmol.LineBasicMaterial();
-    
-    $3Dmol.Material.prototype.clone.call(this, material);
-    
-    material.color.copy(this.color);
-    return material;
-};
-
-//Mesh Lambert material
-/** @constructor */
-$3Dmol.MeshLambertMaterial = function(parameters) {
-    
-    $3Dmol.Material.call(this);
-    
-    this.color = new $3Dmol.Color(0xffffff);
-    this.ambient = new $3Dmol.Color(0xfffff);
-    this.emissive = new $3Dmol.Color(0x000000);
-    
-    //TODO: Which of these instance variables do I really need?
-    this.wrapAround = false;
-    this.wrapRGB = new $3Dmol.Vector3(1,1,1);
-    
-    this.map = null;
-    
-    this.lightMap = null;
-    
-    this.specularMap = null;
-    
-    this.envMap = null;
-    this.reflectivity = 1;
-    this.refractionRatio = 0.98;
-    
-    this.fog = true;
-    
-    this.wireframe = false;
-    this.wireframeLinewidth = 1;
-    this.wireframeLinecap = 'round';
-    this.wireframeLinejoin = 'round';
-    
-    this.shading = $3Dmol.SmoothShading;
-    this.shaderID = "lambert";
-    this.vertexColors = $3Dmol.NoColors;
-    
-    this.skinning = false;
-    
-    this.setValues(parameters);
-    
-};
-
-$3Dmol.MeshLambertMaterial.prototype = Object.create($3Dmol.Material.prototype);
-
-$3Dmol.MeshLambertMaterial.prototype.clone = function(material) {
-  
-    if ( typeof material === "undefined" ) material = new $3Dmol.MeshLambertMaterial();
-    
-    $3Dmol.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.shaderID = this.shaderID;
-    material.vertexColors = this.vertexColors;
-    
-    material.skinning = this.skinning;
-    material.morphTargets = this.morphTargets;
-    material.morphNormals = this.morphNormals;
-    
-    return material;
-    
-};
-
-//Double sided Mesh Lambert material
-/** @constructor */
-$3Dmol.MeshDoubleLambertMaterial = function(parameters) {
-    
-    $3Dmol.MeshLambertMaterial.call(this, parameters);
-
-    this.shaderID = "lambertdouble";
-    this.side = $3Dmol.DoubleSide;    
-    
-};
-
-$3Dmol.MeshDoubleLambertMaterial.prototype = Object.create($3Dmol.MeshLambertMaterial.prototype);
-
-$3Dmol.MeshDoubleLambertMaterial.prototype.clone = function() {
-  
-    var material = new $3Dmol.MeshDoubleLambertMaterial();
-    
-    $3Dmol.MeshLambertMaterial.prototype.clone.call(this, material);
-        
-    return material;
-    
-};
-
-//Outlined Mesh Lamert material
-/** @constructor */
-$3Dmol.MeshOutlineMaterial = function(parameters) {
-    $3Dmol.Material.call(this);
-    parameters = parameters || {};
-    this.fog = true;
-    this.shaderID = "outline";
-    this.wireframe=false;
-    this.outlineColor= parameters.color || new $3Dmol.Color(0.0,0.0,0.0);
-    this.outlineWidth= parameters.width || 0.1;
-    this.outlinePushback= parameters.pushback || 1.0;
-    
-};
-
-$3Dmol.MeshOutlineMaterial.prototype = Object.create($3Dmol.Material.prototype);
-
-$3Dmol.MeshOutlineMaterial.prototype.clone = function(material) {
-    if ( typeof material === "undefined" ) material = new $3Dmol.MeshOutlineMaterial();
-    $3Dmol.Material.prototype.clone.call(this, material);
-    material.fog = this.fog;
-    material.shaderID = this.shaderID;
-    material.wireframe = this.wireframe;
-    return material;
-};
-
-
-//Imposter material
-/** @constructor */
-$3Dmol.ImposterMaterial = function(parameters) {
-  
-  $3Dmol.Material.call(this);
-  
-  this.color = new $3Dmol.Color(0xffffff);
-  this.ambient = new $3Dmol.Color(0xfffff);
-  this.emissive = new $3Dmol.Color(0x000000);
-  this.imposter = true;
-  
-  //TODO: Which of these instance variables do I really need?
-  this.wrapAround = false;
-  this.wrapRGB = new $3Dmol.Vector3(1,1,1);
-  
-  this.map = null;
-  
-  this.lightMap = null;
-  
-  this.specularMap = null;
-  
-  this.envMap = null;
-  this.reflectivity = 1;
-  this.refractionRatio = 0.98;
-  
-  this.fog = true;
-  
-  this.wireframe = false;
-  this.wireframeLinewidth = 1;
-  this.wireframeLinecap = 'round';
-  this.wireframeLinejoin = 'round';
-  
-  this.shading = $3Dmol.SmoothShading;
-  this.shaderID = "sphereimposter";
-  this.vertexColors = $3Dmol.NoColors;
-  
-  this.skinning = false;
-  
-  this.setValues(parameters);
-  
-};
-
-$3Dmol.ImposterMaterial.prototype = Object.create($3Dmol.Material.prototype);
-
-$3Dmol.ImposterMaterial.prototype.clone = function() {
-
-  var material = new $3Dmol.ImposterMaterial();
-  
-  $3Dmol.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.shaderID = this.shaderID;
-  material.vertexColors = this.vertexColors;
-  
-  material.skinning = this.skinning;
-  material.morphTargets = this.morphTargets;
-  material.morphNormals = this.morphNormals;
-  
-  return material;
-  
-};
-
-
-//Sprite material
-/** @constructor */
-$3Dmol.SpriteMaterial = function(parameters) {
-    
-    $3Dmol.Material.call(this);
-    
-    this.color = new $3Dmol.Color(0xffffff);
-    this.map = new $3Dmol.Texture();
-    
-    this.useScreenCoordinates = true;
-    this.depthTest = !this.useScreenCoordinates;
-    this.sizeAttenuation = !this.useScreenCoordinates;
-    this.scaleByViewPort = !this.sizeAttenuation;
-    this.alignment = $3Dmol.SpriteAlignment.center.clone();
-    
-    this.fog = false; // use scene fog
-    
-    this.uvOffset = new $3Dmol.Vector2(0, 0);
-    this.uvScale = new $3Dmol.Vector2(1, 1);
-    
-    this.setValues(parameters);
-    
-    parameters = parameters || {};
-    
-    if (parameters.depthTest === undefined)
-        this.depthTest = !this.useScreenCoordinates;
-    if (parameters.sizeAttenuation === undefined)
-        this.sizeAttenuation = !this.useScreenCoordinates;
-    if (parameters.scaleByViewPort === undefined)
-        this.scaleByViewPort = !this.sizeAttenuation;
-    
-};
-
-$3Dmol.SpriteMaterial.prototype = Object.create($3Dmol.Material.prototype);
-
-$3Dmol.SpriteMaterial.prototype.clone = function() {
-    
-    var material = new $3Dmol.SpriteMaterial();
-    
-    $3Dmol.Material.prototype.clone.call(this, material);
-    
-    material.color.copy(this.color);
-    material.map = this.map;
-    
-    material.useScreenCoordinates = useScreenCoordinates;
-    material.sizeAttenuation = this.sizeAttenuation;
-    material.scaleByViewport = this.scaleByViewPort;
-    material.alignment.copy(this.alignment);
-    
-    material.uvOffset.copy(this.uvOffset);
-    
-    return material;
-    
-};
-
-//Alignment for Sprites
-
-$3Dmol.SpriteAlignment = {};
-$3Dmol.SpriteAlignment.topLeft = new $3Dmol.Vector2(1, -1);
-$3Dmol.SpriteAlignment.topCenter = new $3Dmol.Vector2(0, -1);
-$3Dmol.SpriteAlignment.topRight = new $3Dmol.Vector2(-1, -1);
-$3Dmol.SpriteAlignment.centerLeft = new $3Dmol.Vector2(1, 0);
-$3Dmol.SpriteAlignment.center = new $3Dmol.Vector2(0, 0);
-$3Dmol.SpriteAlignment.centerRight = new $3Dmol.Vector2(-1, 0);
-$3Dmol.SpriteAlignment.bottomLeft = new $3Dmol.Vector2(1, 1);
-$3Dmol.SpriteAlignment.bottomCenter = new $3Dmol.Vector2(0, 1);
-$3Dmol.SpriteAlignment.bottomRight = new $3Dmol.Vector2(-1, 1);
-
-
-//Texture
-//We really only create textures from 2d rendering contexts (to display text labels)
-/** @constructor */
-$3Dmol.Texture = function(image) {
-
-    $3Dmol.EventDispatcher.call(this);
-    
-    this.id = $3Dmol.TextureIdCount++;
-    
-    this.name = "";
-    
-    this.image = image;
-    this.mipmaps = [];
-    
-    this.mapping = new $3Dmol.UVMapping();
-    
-    this.wrapS = $3Dmol.ClampToEdgeWrapping;
-    this.wrapT = $3Dmol.ClampToEdgeWrapping;
-    
-    this.magFilter = $3Dmol.LinearFilter;
-    this.minFilter = $3Dmol.LinearMipMapLinearFilter;
-    
-    this.anisotropy = 1;
-    
-    this.format = $3Dmol.RGBAFormat;
-    this.type = $3Dmol.UnsignedByteType;
-    
-    this.offset = new $3Dmol.Vector2(0, 0);
-    this.repeat = new $3Dmol.Vector2(1, 1);
-    
-    this.generateMipmaps = true;
-    this.premultiplyAlpha = false;
-    this.flipY = true;
-    this.unpackAlignment = 4;
-    
-    this.needsUpdate = false;
-    this.onUpdate = null;
-    
-};
-
-$3Dmol.Texture.prototype = {
-
-    constructor : $3Dmol.Texture,
-    
-    clone : function(texture) {
-        
-        if (texture === undefined)
-            texture = new $3Dmol.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'});
-        
-    }    
-    
-};
-
-$3Dmol.TextureIdCount = 0;
-
-
-// sides
-$3Dmol.FrontSide = 0;
-$3Dmol.BackSide = 1;
-$3Dmol.DoubleSide = 2;
-
-// shading
-$3Dmol.NoShading = 0;
-$3Dmol.FlatShading = 1;
-$3Dmol.SmoothShading = 2;
-
-// colors
-$3Dmol.NoColors = 0;
-$3Dmol.FaceColors = 1;
-$3Dmol.VertexColors = 2;
-
-//Texture constants
-//TODO: Which of these do I need (since I only use textures to display label sprites) ?
-$3Dmol.MultiplyOperation = 0;
-$3Dmol.MixOperation = 1;
-$3Dmol.AddOperation = 2;
-
-// mapping modes
-
-$3Dmol.UVMapping = function() {};
-
-// wrapping modes
-$3Dmol.ClampToEdgeWrapping = 1001;
-
-//Filters
-$3Dmol.LinearFilter = 1006;
-$3Dmol.LinearMipMapLinearFilter = 1008;
-
-//Data types
-$3Dmol.UnsignedByteType = 1009;
-
-//Pixel formats
-$3Dmol.RGBAFormat = 1021;
-/* 
- * $3Dmol Mesh and Line objects
- */
-
-
-//Line Object
-/** @constructor */
-$3Dmol.Line = function (geometry, material, type) {
-
-    $3Dmol.Object3D.call(this);
-
-    this.geometry = geometry;
-        //TODO: update material and type to webgl
-    this.material = (material !== undefined) ? material : new $3Dmol.LineBasicMaterial( { color: Math.random() * 0xffffff } );
-    this.type = (type !== undefined) ? type : $3Dmol.LineStrip;
-
-};
-
-$3Dmol.LineStrip = 0;
-$3Dmol.LinePieces = 1;
-
-$3Dmol.Line.prototype = Object.create($3Dmol.Object3D.prototype);
-
-$3Dmol.Line.prototype.clone = function (object) {
-
-    if (object === undefined) object = new $3Dmol.Line(this.geometry, this.material, this.type);
-
-    $3Dmol.Object3D.prototype.clone.call(this, object);
-
-    return object;
-
-};
-
-
-//Mesh Object
-/** @constructor */
-$3Dmol.Mesh = function(geometry, material) {
-
-    $3Dmol.Object3D.call(this);
-
-    this.geometry = geometry;
-    this.material = (material !== undefined) ? material : new $3Dmol.MeshBasicMaterial( { color: Math.random() * 0xffffff, wireframe: true } );
-
-};
-
-$3Dmol.Mesh.prototype = Object.create($3Dmol.Object3D.prototype);
-
-$3Dmol.Mesh.prototype.clone = function (object) {
-
-    if (object === undefined) object = new $3Dmol.Mesh(this.geometry, this.material);
-
-    $3Dmol.Object3D.prototype.clone.call(this, object);
-
-    return object;
-
-};
-
-
-//Sprite object
-/** @constructor */
-$3Dmol.Sprite = function(material) {
-    
-    $3Dmol.Object3D.call(this);
-    
-    this.material = (material !== undefined) ? material : new $3Dmol.SpriteMaterial();
-
-    this.rotation3d = this.rotation;
-    this.rotation = 0;
-    
-};
-
-$3Dmol.Sprite.prototype = Object.create($3Dmol.Object3D.prototype);
-
-$3Dmol.Sprite.prototype.updateMatrix = function() {
-    
-    this.matrix.setPosition(this.position);
-    
-    this.rotation3d.set(0, 0, this.rotation);
-    this.matrix.setRotationFromEuler(this.rotation3d);
-    
-    if (this.scale.x !== 1 || this.scale.y !== 1)
-        this.matrix.scale(this.scale);
-    
-    this.matrixWorldNeedsUpdate = true;
-    
-};
-
-$3Dmol.Sprite.prototype.clone = function(object) {
-    
-    if (object === undefined)
-        object = new $3Dmol.Sprite(this.material);
-    
-    $3Dmol.Object3D.prototype.clone.call(this, object);
-    
-    return object;
-    
-};
-/**
- * Simplified webGL renderer
- */
-
-$3Dmol.Renderer = function(parameters) {
-
-    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 = parameters.clearColor !== undefined ? new $3Dmol.Color(parameters.clearColor) : new $3Dmol.Color(0x000000), 
-    _clearAlpha = parameters.clearAlpha !== undefined ? parameters.clearAlpha : 0, 
-    _outlineMaterial = parameters.outline !== undefined ? new $3Dmol.MeshOutlineMaterial(parameters.outline) : null;
-
-    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;
-    this.autoUpdateScene = true;
-
-    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 = 0, _viewportHeight = 0, _currentWidth = 0, _currentHeight = 0,
-
-    _enabledAttributes = {},
-
-    // camera matrices cache
-
-    _projScreenMatrix = new $3Dmol.Matrix4(),
-
-    _vector3 = new $3Dmol.Vector3(),
-
-    // light arrays cache
-
-    _direction = new $3Dmol.Vector3(),
-
-    _lightsNeedUpdate = true,
-
-    _lights = {
-
-        ambient : [ 0, 0, 0 ],
-        directional : {
-            length : 0,
-            colors : [],
-            positions : []
-        },
-        point : {
-            length : 0,
-            colors : [],
-            positions : [],
-            distances : []
-        },
-        spot : {
-            length : 0,
-            colors : [],
-            positions : [],
-            distances : [],
-            directions : [],
-            anglesCos : [],
-            exponents : []
-        },
-        hemi : {
-            length : 0,
-            skyColors : [],
-            groundColors : [],
-            positions : []
-        }
-
-    };
-
-    // initialize
-
-    var _gl;
-    var _ext;
-
-    initGL();
-
-    setDefaultGLState();
-
-    this.context = _gl;
-
-    // API
-
-    this.getContext = function() {
-
-        return _gl;
-
-    };
-
-    this.getPrecision = function() {
-
-        return _precision;
-
-    };
-
-    this.setClearColorHex = function(hex, alpha) {
-
-        _clearColor.setHex(hex);
-        _clearAlpha = alpha;
-
-        _gl
-                .clearColor(_clearColor.r, _clearColor.g, _clearColor.b,
-                        _clearAlpha);
-
-    };
-
-    this.enableOutline = function(parameters) {
-        _outlineMaterial = new $3Dmol.MeshOutlineMaterial(parameters);
-    }
-
-    this.disableOutline = function() {
-        _outlineMaterial = null;
-    }
-
-    this.setSize = function(width, height) {
-
-        _canvas.width = width * this.devicePixelRatio;
-        _canvas.height = height * this.devicePixelRatio;
-
-        _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.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(color, depth, stencil) {
-
-        this.clear(color, depth, stencil);
-
-    };
-
-    this.setMaterialFaces = function(material, reflected) {
-
-        var doubleSided = material.side === $3Dmol.DoubleSide;
-        var flipSided = material.side === $3Dmol.BackSide;
-        flipSided = reflected ? !flipSided : flipSided;
-
-        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;
-
-        }
-
-    };
-
-    this.setBlending = function(blending) {
-
-        if (!blending) {
-            _gl.disable(_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;
-    };
-
-    // Plugins
-
-    this.addPostPlugin = function(plugin) {
-
-        plugin.init(this);
-        this.renderPluginsPost.push(plugin);
-
-    };
-
-    // Sorting
-
-    function numericalSort(a, b) {
-
-        return b[0] - a[0];
-
-    }
-
-    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 setPolygonOffset(polygonOffset, factor, units) {
-
-        if (_oldPolygonOffset !== polygonOffset) {
-
-            if (polygonOffset)
-                _gl.enable(_gl.POLYGON_OFFSET_FILL);
-            else
-                _gl.disable(_gl.POLYGON_OFFSET_FILL);
-        }
-    }
-
-    function setLineWidth(width) {
-
-        if (width !== _oldLineWidth) {
-            _gl.lineWidth(width);
-            _oldLineWidth = width;
-        }
-
-    }
-
-    var onGeometryDispose = function(event) {
-
-        var geometry = event.target;
-        geometry.removeEventListener('dispose', onGeometryDispose);
-
-        deallocateGeometry(geometry);
-
-        _this.info.memory.geometries--;
-
-    };
-
-    var onTextureDispose = function(event) {
-
-        var texture = event.target;
-
-        texture.removeEventListener('dispose', onTextureDispose);
-
-        deallocateTexture(texture);
-
-        _this.info.memory.textures--;
-
-    };
-
-    var onMaterialDispose = function(event) {
-
-        var material = event.target;
-        material.removeEventListener('dispose', onMaterialDispose);
-
-        deallocateMaterial(material);
-
-    };
-
-    var deallocateGeometry = function(geometry) {
-
-        geometry.__webglInit = undefined;
-
-        if (geometry.__webglVertexBuffer !== undefined)
-            _gl.deleteBuffer(geometry.__webglVertexBuffer);
-
-        if (geometry.__webglColorBuffer !== undefined)
-            _gl.deleteBuffer(geometry.__webglColorBuffer);
-
-        if (geometry.geometryGroups !== undefined) {
-
-            for (var g = 0, gl = geometry.groups; g < gl; g++) {
-
-                var geometryGroup = geometry.geometryGroups[g];
-
-                if (geometryGroup.__webglVertexBuffer !== undefined)
-                    _gl.deleteBuffer(geometryGroup.__webglVertexBuffer);
-
-                if (geometryGroup.__webglColorBuffer !== undefined)
-                    _gl.deleteBuffer(geometryGroup.__webglColorBuffer);
-
-                if (geometryGroup.__webglNormalBuffer !== undefined)
-                    _gl.deleteBuffer(geometryGroup.__webglNormalBuffer);
-
-                if (geometryGroup.__webglFaceBuffer !== undefined)
-                    _gl.deleteBuffer(geometryGroup.__webglFaceBuffer);
-
-                if (geometryGroup.__webglLineBuffer !== undefined)
-                    _gl.deleteBuffer(geometryGroup.__webglLineBuffer);
-
-            }
-        }
-    };
-
-    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--;
-
-        }
-
-    };
-
-    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);
-
-        }
-
-    };
-
-    // Compile and return shader
-    function getShader(type, str) {
-
-        var shader;
-
-        if (type === "fragment")
-            shader = _gl.createShader(_gl.FRAGMENT_SHADER);
-        else if (type === "vertex")
-            shader = _gl.createShader(_gl.VERTEX_SHADER);
-
-        _gl.shaderSource(shader, str);
-        _gl.compileShader(shader);
-
-        if (!_gl.getShaderParameter(shader, _gl.COMPILE_STATUS)) {
-
-            console.error(_gl.getShaderInfoLog(shader));
-            console.error("could not initialize shader");
-            return null;
-
-        }
-
-        return shader;
-
-    }
-
-    // Compile appropriate shaders (if necessary) from source code and attach to
-    // gl program.
-    function buildProgram(fragmentShader, vertexShader, uniforms, parameters) {
-
-        var p, pl, d, program, code;
-        var chunks = [];
-
-        chunks.push(fragmentShader);
-        chunks.push(vertexShader);
-
-        for (p in parameters) {
-            chunks.push(p);
-            chunks.push(parameters[p]);
-        }
-
-        code = chunks.join();
-
-        // check if program has already been compiled
-
-        for (p = 0, pl = _programs.length; p < pl; p++) {
-
-            var programInfo = _programs[p];
-
-            if (programInfo.code === code) {
-
-                programInfo.usedTimes++;
-
-                return programInfo.program;
-            }
-        }
-
-        // Set up new program and compile shaders
-
-        program = _gl.createProgram();
-
-        // set up precision
-        var precision = _precision;
-        var prefix = "precision " + precision + " float;";
-
-        var prefix_vertex = [ prefix ].join("\n");
-
-        var prefix_fragment = [
-                parameters.fragdepth ? "#extension GL_EXT_frag_depth: enable" : "",
-                parameters.wireframe ? "#define WIREFRAME 1" : "", prefix ]
-                .join("\n");
-
-        var glFragmentShader = getShader("fragment", prefix_fragment
-                + fragmentShader);
-        var glVertexShader = getShader("vertex", prefix_vertex + vertexShader);
-
-        _gl.attachShader(program, glVertexShader);
-        _gl.attachShader(program, glFragmentShader);
-
-        _gl.linkProgram(program);
-
-        if (!_gl.getProgramParameter(program, _gl.LINK_STATUS))
-            console.error("Could not initialize shader");
-
-        // gather and cache uniform variables and attributes
-
-        program.uniforms = {};
-        program.attributes = {};
-
-        var identifiers, u, a, i;
-
-        // uniform vars
-        identifiers = [ 'viewMatrix', 'modelViewMatrix', 'projectionMatrix',
-                'normalMatrix', 'modelMatrix', 'cameraPosition' ];
-
-        // custom uniform vars
-        for (u in uniforms)
-            identifiers.push(u);
-
-        for (i = 0; i < identifiers.length; i++) {
-
-            var uniformVar = identifiers[i];
-            program.uniforms[uniformVar] = _gl.getUniformLocation(program,
-                    uniformVar);
-
-        }
-
-        // attributes
-        identifiers = [ 'position', 'normal', 'color', 'lineDistance' ];
-
-        /*
-         * for (a in attributes) identifiers.push(a);
-         */
-
-        for (i = 0; i < identifiers.length; i++) {
-
-            var attributeVar = identifiers[i];
-            program.attributes[attributeVar] = _gl.getAttribLocation(program,
-                    attributeVar);
-        }
-
-        program.id = _programs_counter++;
-        _programs.push({
-            program : program,
-            code : code,
-            usedTimes : 1
-        });
-        _this.info.memory.programs = _programs.length;
-
-        return program;
-    }
-
-    // TODO: need to set up shader attributes and uniforms as attributes on
-    // material object after attaching prgm
-    // We need to attach appropriate uniform variables to material after shaders
-    // have been chosen
-    this.initMaterial = function(material, lights, fog, object) {
-
-        material.addEventListener('dispose', onMaterialDispose);
-
-        var u, a, identifiers, i, parameters, maxLightCount, maxBones, maxShadows, shaderID;
-
-        shaderID = material.shaderID;
-
-        if (shaderID) {
-
-            var shader = $3Dmol.ShaderLib[shaderID];
-            material.vertexShader = shader.vertexShader;
-            material.fragmentShader = shader.fragmentShader;
-            material.uniforms = $3Dmol.ShaderUtils.clone(shader.uniforms);
-            // TODO: set material uniforms to shader uniform variables
-
-        }
-
-        parameters = {
-            wireframe : material.wireframe,
-            fragdepth : material.imposter
-        };
-
-        material.program = buildProgram(material.fragmentShader,
-                material.vertexShader, material.uniforms, parameters);
-
-    };
-
-    function setProgram(camera, lights, fog, material, object) {
-
-        if (material.needsUpdate) {
-
-            if (material.program)
-                deallocateMaterial(material);
-
-            _this.initMaterial(material, lights, fog, object);
-            material.needsUpdate = false;
-        }
-
-        var refreshMaterial = false;
-
-        // p_uniforms: uniformVarName => uniformLocation
-        // m_uniforms: uniformVarName => uniformJsVal
-        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 (camera != _currentCamera) {
-            _currentCamera = camera;
-            refreshMaterial = true;
-        }
-
-        _gl.uniformMatrix4fv(p_uniforms.projectionMatrix, false,
-                camera.projectionMatrix.elements);
-        _gl.uniformMatrix4fv(p_uniforms.modelViewMatrix, false,
-                object._modelViewMatrix.elements);
-        _gl.uniformMatrix3fv(p_uniforms.normalMatrix, false,
-                object._normalMatrix.elements);
-        
-        // Send projection matrix to uniform variable in shader
-        if (refreshMaterial) {
-
-            // Load projection, model-view matrices for perspective
-
-            // Set up correct fog uniform vals
-            m_uniforms.fogColor.value = fog.color;
-            m_uniforms.fogNear.value = fog.near;
-            m_uniforms.fogFar.value = fog.far;
-
-            // Set up lights for lambert shader
-            if (material.shaderID.lastIndexOf("lambert", 0) === 0) {
-
-                // load view and normal matrices for directional and object
-                // lighting
-                _gl.uniformMatrix4fv(p_uniforms.viewMatrix, false,
-                        camera.matrixWorldInverse.elements);
-
-                if (_lightsNeedUpdate) {
-                    setupLights(program, lights);
-                    _lightsNeedUpdate = false;
-                }
-
-                // Set up correct light uniform var vals
-                m_uniforms.ambientLightColor.value = _lights.ambient;
-                m_uniforms.directionalLightColor.value = _lights.directional.colors;
-                m_uniforms.directionalLightDirection.value = _lights.directional.positions;
-                m_uniforms.ambient.value = material.ambient;
-                m_uniforms.emissive.value = material.emissive;
-
-            } else if (material.shaderID === "outline") {
-                m_uniforms.outlineColor.value = material.outlineColor;
-                m_uniforms.outlineWidth.value = material.outlineWidth;
-                m_uniforms.outlinePushback.value = material.outlinePushback;
-            } else if (material.shaderID === "sphereimposter") {
-                _gl.uniformMatrix4fv(p_uniforms.viewMatrix, false,
-                        camera.matrixWorldInverse.elements);
-                _gl.uniformMatrix3fv(p_uniforms.normalMatrix, false,
-                        object._normalMatrix.elements);
-            }
-
-            // opacity, diffuse, emissive, etc
-            m_uniforms.opacity.value = material.opacity;
-            m_uniforms.diffuse.value = material.color;
-
-            // Load any other material specific uniform variables to gl shaders
-            loadMaterialUniforms(p_uniforms, m_uniforms);
-
-        }
-
-        return program;
-
-    }
-
-    function loadMaterialUniforms(p_uniforms, m_uniforms) {
-        var uniformVar, type, uniformVal, uniformLoc;
-
-        for (uniformVar in m_uniforms) {
-            if (!p_uniforms[uniformVar])
-                continue;
-
-            type = m_uniforms[uniformVar].type;
-            uniformVal = m_uniforms[uniformVar].value;
-            uniformLoc = p_uniforms[uniformVar];
-
-            // single float
-            if (type === 'f')
-                _gl.uniform1f(uniformLoc, uniformVal);
-            // array of floats
-            else if (type === 'fv')
-                _gl.uniform3fv(uniformLoc, uniformVal);
-            // color - r,g,b floats
-            else if (type === 'c')
-                _gl.uniform3f(uniformLoc, uniformVal.r, uniformVal.g,
-                        uniformVal.b);
-
-        }
-
-    }
-
-    this.renderBuffer = function(camera, lights, fog, material, geometryGroup,
-            object) {
-
-        if (!material.visible)
-            return;
-
-        var program, attributes, linewidth, primitives, a, attribute, i, il;
-
-        // Sets up proper vertex and fragment shaders and attaches them to webGL
-        // program
-        // Also sets appropriate uniform variables
-        program = setProgram(camera, lights, fog, material, object);
-
-        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;
-        }
-
-        // rebind shader attributes to appropriate (and already initialized) gl
-        // buffers
-        if (updateBuffers) {
-
-            disableAttributes();
-
-            // Vertices
-            if (attributes.position >= 0) {
-                _gl.bindBuffer(_gl.ARRAY_BUFFER,
-                        geometryGroup.__webglVertexBuffer);
-                enableAttribute(attributes.position);
-                _gl.vertexAttribPointer(attributes.position, 3, _gl.FLOAT,
-                        false, 0, 0);
-            }
-
-            // Colors
-            if (attributes.color >= 0) {
-                _gl.bindBuffer(_gl.ARRAY_BUFFER,
-                        geometryGroup.__webglColorBuffer);
-                enableAttribute(attributes.color);
-                _gl.vertexAttribPointer(attributes.color, 3, _gl.FLOAT, false,
-                        0, 0);
-            }
-
-            // Normals (lambert shader only)
-            if (attributes.normal >= 0) {
-                _gl.bindBuffer(_gl.ARRAY_BUFFER,
-                        geometryGroup.__webglNormalBuffer);
-                enableAttribute(attributes.normal);
-                _gl.vertexAttribPointer(attributes.normal, 3, _gl.FLOAT, false,
-                        0, 0);
-            }
-
-        }
-
-        // Render
-        var faceCount, lineCount;
-        // lambert shaders - draw triangles
-        // TODO: make sure geometryGroup's face count is setup correctly
-        if (object instanceof $3Dmol.Mesh) {
-
-            if (material.wireframe) {
-                lineCount = geometryGroup.lineidx;
-                setLineWidth(material.wireframeLinewidth);
-
-                if (updateBuffers)
-                    _gl.bindBuffer(_gl.ELEMENT_ARRAY_BUFFER,
-                            geometryGroup.__webglLineBuffer);
-
-                _gl.drawElements(_gl.LINES, lineCount, _gl.UNSIGNED_SHORT, 0);
-            }
-
-            else {
-                faceCount = geometryGroup.faceidx;
-
-                if (updateBuffers)
-                    _gl.bindBuffer(_gl.ELEMENT_ARRAY_BUFFER,
-                            geometryGroup.__webglFaceBuffer);
-                _gl.drawElements(_gl.TRIANGLES, faceCount, _gl.UNSIGNED_SHORT,
-                        0);
-
-            }
-
-            _this.info.render.calls++;
-            _this.info.render.vertices += faceCount;
-            _this.info.render.faces += faceCount / 3;
-        }
-
-        // basic shaders - draw lines
-        else if (object instanceof $3Dmol.Line) {
-            lineCount = geometryGroup.vertices;
-
-            setLineWidth(material.linewidth);
-            _gl.drawArrays(_gl.LINES, 0, lineCount);
-
-            _this.info.render.calls++;
-        }
-
-    };
-
-    // rendering
-    function renderObjects(renderList, reverse, materialType, camera, lights,
-            fog, useBlending, overrideMaterial) {
-
-        var webglObject, object, buffer, material, start, end, delta;
-
-        // Forward or backward render
-
-        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;
-                material = webglObject[materialType];
-
-                if (!material)
-                    continue;
-
-                if (useBlending)
-                    _this.setBlending(true);
-
-                _this.setDepthTest(material.depthTest);
-                _this.setDepthWrite(material.depthWrite);
-                setPolygonOffset(material.polygonOffset,
-                        material.polygonOffsetFactor,
-                        material.polygonOffsetUnits);
-
-                var reflected = object._modelViewMatrix.isReflected();
-
-                _this.setMaterialFaces(material, reflected);
-
-                _this.renderBuffer(camera, lights, fog, material, buffer,
-                        object);
-                if (_outlineMaterial && !material.wireframe
-                        && material.shaderID !== 'basic'
-                        && material.opacity !== 0.0) {
-                    _this.renderBuffer(camera, lights, fog, _outlineMaterial,
-                            buffer, object);
-                }
-            }
-        }
-
-    }
-
-    this.render = function(scene, camera, forceClear) {
-
-        if (camera instanceof $3Dmol.Camera === false) {
-
-            console
-                    .error('$3Dmol.Renderer.render: camera is not an instance of $3Dmol.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 (this.autoUpdateScene)
-            scene.updateMatrixWorld();
-
-        // update camera matrices
-        // Pretty sure camera's parent is always going to be undefined for our
-        // purposes...
-        if (camera.parent === undefined)
-            camera.updateMatrixWorld();
-
-        camera.matrixWorldInverse.getInverse(camera.matrixWorld);
-
-        _projScreenMatrix.multiplyMatrices(camera.projectionMatrix,
-                camera.matrixWorldInverse);
-
-        // update WebGL objects
-
-        if (this.autoUpdateObjects)
-            this.initWebGLObjects(scene);
-
-        _this.info.render.calls = 0;
-        _this.info.render.vertices = 0;
-        _this.info.render.faces = 0;
-        _this.info.render.points = 0;
-
-        _currentWidth = _viewportWidth;
-        _currentHeight = _viewportHeight;
-
-        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.render = false;
-
-            if (object.visible) {
-                setupMatrices(object, camera);
-                unrollBufferMaterial(webglObject);
-                webglObject.render = true;
-            }
-        }
-
-        // set matrices for immediate objects
-
-        var material = null;
-
-        // opaque pass (front-to-back order)
-
-        this.setBlending(false);
-
-        renderObjects(scene.__webglObjects, true, "opaque", camera, lights,
-                fog, false, material);
-
-        // prime depth buffer
-        renderObjects(scene.__webglObjects, true, "blank", camera, lights, fog,
-                true, material);
-
-        // transparent pass (back-to-front order)
-
-        renderObjects(scene.__webglObjects, false, "transparent", camera,
-                lights, fog, true, material);
-
-        // Render plugins (e.g. sprites), and reset state
-
-        renderPlugins(this.renderPluginsPost, scene, camera);
-
-        // 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) {
-
-        // Reset state once regardless
-        // This should also fix cartoon render bug (after transparent surface
-        // render)
-
-        _currentGeometryGroupHash = -1;
-        _currentProgram = null;
-        _currentCamera = null;
-        _oldBlending = -1;
-        _oldDepthWrite = -1;
-        _oldDepthTest = -1;
-        _oldDoubleSided = -1;
-        _currentMaterialId = -1;
-        _oldFlipSided = -1;
-
-        if (!plugins.length)
-            return;
-
-        for (var i = 0, il = plugins.length; i < il; i++) {
-
-            _lightsNeedUpdate = true;
-
-            plugins[i].render(scene, camera, _currentWidth, _currentHeight);
-
-            // Reset state after plugin render
-            _currentGeometryGroupHash = -1;
-            _currentProgram = null;
-            _currentCamera = null;
-            _oldBlending = -1;
-            _oldDepthWrite = -1;
-            _oldDepthTest = -1;
-            _oldDoubleSided = -1;
-            _currentMaterialId = -1;
-            _oldFlipSided = -1;
-
-        }
-
-    }
-
-    this.initWebGLObjects = function(scene) {
-
-        if (!scene.__webglObjects) {
-
-            scene.__webglObjects = [];
-            scene.__webglObjectsImmediate = [];
-            scene.__webglSprites = [];
-            scene.__webglFlares = [];
-
-        }
-
-        // Add objects; this sets up buffers for each geometryGroup
-        if (scene.__objectsAdded.length) {
-
-            while (scene.__objectsAdded.length) {
-                addObject(scene.__objectsAdded[0], scene);
-                scene.__objectsAdded.splice(0, 1);
-            }
-
-            // Force buffer update during render
-            // Hackish fix for initial cartoon-render-then-transparent-surface
-            // bug
-            _currentGeometryGroupHash = -1;
-
-        }
-
-        while (scene.__objectsRemoved.length) {
-
-            removeObject(scene.__objectsRemoved[0], scene);
-            scene.__objectsRemoved.splice(0, 1);
-
-        }
-
-        // update must be called after objects adding / removal
-        // This sends typed arrays to GL buffers for each geometryGroup
-        for (var o = 0, ol = scene.__webglObjects.length; o < ol; o++) {
-
-            updateObject(scene.__webglObjects[o].object);
-
-        }
-
-    };
-
-    // Objects adding
-
-    function addObject(object, scene) {
-
-        var g, gl, geometry, material, geometryGroup;
-
-        if (!object.__webglInit) {
-
-            object.__webglInit = true;
-
-            object._modelViewMatrix = new $3Dmol.Matrix4();
-            object._normalMatrix = new $3Dmol.Matrix3();
-
-            if (object.geometry !== undefined
-                    && object.geometry.__webglInit === undefined) {
-
-                object.geometry.__webglInit = true;
-                object.geometry.addEventListener('dispose', onGeometryDispose);
-
-            }
-
-            if (object instanceof $3Dmol.Mesh || object instanceof $3Dmol.Line) {
-                geometry = object.geometry;
-                material = object.material;
-
-                for (g = 0, gl = geometry.geometryGroups.length; g < gl; g++) {
-
-                    geometryGroup = geometry.geometryGroups[g];
-
-                    geometryGroup.id = _geometryGroupCounter++;
-
-                    // initialise VBO on the first access
-
-                    if (!geometryGroup.__webglVertexBuffer) {
-
-                        if (object instanceof $3Dmol.Mesh) {
-                            createMeshBuffers(geometryGroup);
-                            geometry.elementsNeedUpdate = true;
-                            geometry.normalsNeedUpdate = true;
-                        }
-
-                        else if (object instanceof $3Dmol.Line)
-                            createLineBuffers(geometryGroup);
-
-                        geometry.verticesNeedUpdate = true;
-                        geometry.colorsNeedUpdate = true;
-
-                    }
-
-                }
-
-            }
-
-        }
-
-        if (!object.__webglActive) {
-
-            if (object instanceof $3Dmol.Mesh || object instanceof $3Dmol.Line) {
-
-                geometry = object.geometry;
-
-                for (g = 0, gl = geometry.geometryGroups.length; g < gl; g++) {
-                    geometryGroup = geometry.geometryGroups[g];
-
-                    addBuffer(scene.__webglObjects, geometryGroup, object);
-                }
-
-            }
-
-            // Sprite
-            else if (object instanceof $3Dmol.Sprite)
-                scene.__webglSprites.push(object);
-
-            object.__webglActive = true;
-
-        }
-
-    }
-
-    function updateObject(object) {
-
-        var geometry = object.geometry, material = object.material, geometryGroup, customAttributesDirty;
-
-        if (object instanceof $3Dmol.Mesh || object instanceof $3Dmol.Line) {
-
-            for (var g = 0, gl = geometry.geometryGroups.length; g < gl; g++) {
-
-                geometryGroup = geometry.geometryGroups[g];
-
-                if (geometry.verticesNeedUpdate || geometry.elementsNeedUpdate
-                        || geometry.colorsNeedUpdate
-                        || geometry.normalsNeedUpdate) {
-                    setBuffers(geometryGroup, _gl.STATIC_DRAW);
-                }
-            }
-
-            geometry.verticesNeedUpdate = false;
-            geometry.elementsNeedUpdate = false;
-            geometry.normalsNeedUpdate = false;
-            geometry.colorsNeedUpdate = false;
-
-            geometry.buffersNeedUpdate = false;
-
-        }
-
-    }
-
-    function removeObject(object, scene) {
-
-        if (object instanceof $3Dmol.Mesh || object instanceof $3Dmol.Line)
-            removeInstances(scene.__webglObjects, object);
-
-        else if (object instanceof $3Dmol.Sprite)
-            removeInstancesDirect(scene.__webglSprites, object);
-
-        object.__webglActive = false;
-
-    }
-
-    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);
-
-        }
-    }
-
-    function unrollBufferMaterial(globject) {
-
-        var object = globject.object;
-        var material = object.material;
-
-        if (material.transparent) {
-            globject.opaque = null;
-            globject.transparent = material;
-            if (!material.wireframe) {
-                var blankMaterial = material.clone();
-                blankMaterial.opacity = 0.0;
-                globject.blank = blankMaterial;
-            }
-        }
-
-        else {
-            globject.opaque = material;
-            globject.transparent = null;
-
-        }
-
-    }
-
-    function setBuffers(geometryGroup, hint, line) {
-
-        var vertexArray = geometryGroup.vertexArray;
-        var colorArray = geometryGroup.colorArray;
-
-        // vertex buffers
-        _gl.bindBuffer(_gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer);
-        _gl.bufferData(_gl.ARRAY_BUFFER, vertexArray, hint);
-
-        // color buffers
-        _gl.bindBuffer(_gl.ARRAY_BUFFER, geometryGroup.__webglColorBuffer);
-        _gl.bufferData(_gl.ARRAY_BUFFER, colorArray, hint);
-
-        // normal buffers
-        if (geometryGroup.normalArray !== undefined
-                && geometryGroup.__webglNormalBuffer !== undefined) {
-            var normalArray = geometryGroup.normalArray;
-            _gl.bindBuffer(_gl.ARRAY_BUFFER, geometryGroup.__webglNormalBuffer);
-            _gl.bufferData(_gl.ARRAY_BUFFER, normalArray, hint);
-
-        }
-
-        // face (index) buffers
-        if (geometryGroup.faceArray !== undefined
-                && geometryGroup.__webglFaceBuffer !== undefined) {
-            var faceArray = geometryGroup.faceArray;
-            _gl.bindBuffer(_gl.ELEMENT_ARRAY_BUFFER,
-                    geometryGroup.__webglFaceBuffer);
-            _gl.bufferData(_gl.ELEMENT_ARRAY_BUFFER, faceArray, hint);
-
-        }
-
-        // line (index) buffers (for wireframe)
-        if (geometryGroup.lineArray !== undefined
-                && geometryGroup.__webglLineBuffer !== undefined) {
-            var lineArray = geometryGroup.lineArray;
-            _gl.bindBuffer(_gl.ELEMENT_ARRAY_BUFFER,
-                    geometryGroup.__webglLineBuffer);
-            _gl.bufferData(_gl.ELEMENT_ARRAY_BUFFER, lineArray, hint);
-        }
-
-    }
-
-    // Creates appropriate gl buffers for geometry chunk
-    // TODO: do we need line buffer for mesh objects?
-    // Also, can we integrate this with createLineBuffers?
-    function createMeshBuffers(geometryGroup) {
-
-        geometryGroup.__webglVertexBuffer = _gl.createBuffer();
-        geometryGroup.__webglNormalBuffer = _gl.createBuffer();
-        geometryGroup.__webglColorBuffer = _gl.createBuffer();
-
-        geometryGroup.__webglFaceBuffer = _gl.createBuffer();
-        geometryGroup.__webglLineBuffer = _gl.createBuffer();
-
-        _this.info.memory.geometries++;
-    }
-
-    function createLineBuffers(geometry) {
-
-        geometry.__webglVertexBuffer = _gl.createBuffer();
-        geometry.__webglColorBuffer = _gl.createBuffer();
-
-        _this.info.memory.geometries++;
-    }
-
-    function addBuffer(objlist, buffer, object) {
-
-        objlist.push({
-            buffer : buffer,
-            object : object,
-            opaque : null,
-            transparent : null
-        });
-
-    }
-
-    function setupMatrices(object, camera) {
-
-        object._modelViewMatrix.multiplyMatrices(camera.matrixWorldInverse,
-                object.matrixWorld);
-
-        object._normalMatrix.getInverse(object._modelViewMatrix);
-        object._normalMatrix.transpose();
-
-    }
-
-    function isPowerOfTwo(value) {
-
-        return (value & (value - 1)) === 0;
-
-    }
-
-    // Fallback filters for non-power-of-2 textures
-
-    function filterFallback(f) {
-
-        return _gl.LINEAR;
-
-    }
-
-    function setTextureParameters(textureType, texture, isImagePowerOfTwo) {
-
-        if (isImagePowerOfTwo) {
-
-            _gl.texParameteri(textureType, _gl.TEXTURE_WRAP_S,
-                    paramToGL(texture.wrapS));
-            _gl.texParameteri(textureType, _gl.TEXTURE_WRAP_T,
-                    paramToGL(texture.wrapT));
-
-            _gl.texParameteri(textureType, _gl.TEXTURE_MAG_FILTER,
-                    paramToGL(texture.magFilter));
-            _gl.texParameteri(textureType, _gl.TEXTURE_MIN_FILTER,
-                    paramToGL(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));
-
-        }
-
-    }
-
-    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 = paramToGL(texture.format), glType = paramToGL(texture.type);
-
-            setTextureParameters(_gl.TEXTURE_2D, texture, isImagePowerOfTwo);
-
-            var mipmap, mipmaps = texture.mipmaps;
-
-            // 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);
-
-        }
-
-    };
-
-    // Map constants to WebGL constants
-
-    function paramToGL(p) {
-
-        if (p === $3Dmol.UnsignedByteType)
-            return _gl.UNSIGNED_BYTE;
-        if (p === $3Dmol.RGBAFormat)
-            return _gl.RGBA;
-
-        return 0;
-
-    }
-
-    function setupLights(program, lights) {
-        var l, ll, light, n, r = 0, g = 0, b = 0, color, position, intensity, distance,
-
-        zlights = _lights,
-
-        dirColors = zlights.directional.colors, dirPositions = zlights.directional.positions,
-
-        dirCount = 0, dirLength = 0, dirOffset = 0;
-
-        for (l = 0, ll = lights.length; l < ll; l++) {
-
-            light = lights[l];
-
-            color = light.color;
-            intensity = light.intensity;
-            distance = light.distance;
-
-            if (light instanceof $3Dmol.Light) {
-
-                dirCount++;
-
-                _direction.getPositionFromMatrix(light.matrixWorld);
-                _vector3.getPositionFromMatrix(light.target.matrixWorld);
-                _direction.sub(_vector3);
-                _direction.normalize();
-
-                if (_direction.x === 0 && _direction.y === 0
-                        && _direction.z === 0)
-                    continue;
-
-                dirPositions[dirOffset] = _direction.x;
-                dirPositions[dirOffset + 1] = _direction.y;
-                dirPositions[dirOffset + 2] = _direction.z;
-
-                dirColors[dirOffset] = color.r * intensity;
-                dirColors[dirOffset + 1] = color.g * intensity;
-                dirColors[dirOffset + 2] = color.b * intensity;
-
-                dirOffset += 3;
-
-                dirLength++;
-            }
-
-        }
-
-        zlights.ambient[0] = r;
-        zlights.ambient[1] = g;
-        zlights.ambient[2] = b;
-        zlights.directional.length = dirLength;
-    }
-
-    function initGL() {
-
-        try {
-
-            if (!(_gl = _canvas.getContext('experimental-webgl', {
-                alpha : _alpha,
-                premultipliedAlpha : _premultipliedAlpha,
-                antialias : _antialias,
-                stencil : _stencil,
-                preserveDrawingBuffer : _preserveDrawingBuffer
-            }))) {
-                if (!(_gl = _canvas.getContext('webgl', {
-                    alpha : _alpha,
-                    premultipliedAlpha : _premultipliedAlpha,
-                    antialias : _antialias,
-                    stencil : _stencil,
-                    preserveDrawingBuffer : _preserveDrawingBuffer
-                }))) {
-                    throw 'Error creating WebGL context.';
-                }
-            }
-
-        } catch (error) {
-
-            console.error(error);
-        }
-        _ext = _gl.getExtension("EXT_frag_depth");
-    }
-
-    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
-                .clearColor(_clearColor.r, _clearColor.g, _clearColor.b,
-                        _clearAlpha);
-        var ext = _gl.getExtension('EXT_frag_depth');
-
-    }
-
-    this.addPostPlugin(new $3Dmol.SpritePlugin());
-
-};
-/*
- * Scene class
- */
-/** @constructor */
-$3Dmol.Scene = function() {
-    
-    $3Dmol.Object3D.call(this);
-    
-    this.fog = null;
-    
-    //May not need...
-    this.overrideMaterial = null;
-    
-    this.matrixAutoUpdate = false;
-    
-    this.__objects = [];
-    this.__lights = [];
-    
-    this.__objectsAdded = [];
-    this.__objectsRemoved = [];
-    
-};
-
-$3Dmol.Scene.prototype = Object.create($3Dmol.Object3D.prototype);
-
-$3Dmol.Scene.prototype.__addObject = function(object) {
-    
-    //Directional Lighting
-    if (object instanceof $3Dmol.Light) {
-        
-        if (this.__lights.indexOf(object) === -1)
-            this.__lights.push(object);
-        
-        //TODO: Do I need this??
-        if (object.target && object.target.parent === undefined)
-            this.add(object.target);
-            
-    }
-    
-    //Rotation group
-    else {
-        
-        if (this.__objects.indexOf(object) === -1) {
-            
-            this.__objects.push(object);
-            this.__objectsAdded.push(object);
-            
-            //Check if previously removed
-            
-            var idx = this.__objectsRemoved.indexOf(object);
-            
-            if (idx !== -1)
-                this.__objectsRemoved.splice(i, 1);
-                
-        }
-    }
-    
-    //Add object's children
-    
-    for (var i = 0; i < object.children.length; i++) 
-        this.__addObject(object.children[i]);
-    
-};
-
-$3Dmol.Scene.prototype.__removeObject = function(object) {
-    
-    var idx;
-    if (object instanceof $3Dmol.Light) {
-        
-        idx = this.__lights.indexOf(object);
-        
-        if (idx !== -1)
-            this.__lights.splice(idx, 1);
-            
-    }
-    
-    //Object3D
-    else {
-        
-        idx = this.__objects.indexOf(object);
-        
-        if (idx !== -1) {
-            
-            this.__objects.splice(idx, 1);
-            this.__objectsRemoved.push(object);
-            
-            //Check if previously added
-            
-            var ai = this.__objectsAdded.indexOf(object);
-            
-            if (ai !== -1) 
-                this.__objectsAdded.splice(idx, 1);
-                
-        }
-    
-    }
-    
-    //Remove object's children
-    for (var i = 0; i < object.children.length; i++)
-        this.__removeObject(object.children[i]);
-    
-};
-
-
-/*
- * Fog Class
- */
-
-/** @constructor */
-$3Dmol.Fog = function ( hex, near, far ) {
-
-    this.name = '';
-
-    this.color = new $3Dmol.Color( hex );
-
-    this.near = ( near !== undefined ) ? near : 1;
-    this.far = ( far !== undefined ) ? far : 1000;
-
-};
-
-$3Dmol.Fog.prototype.clone = function () {
-
-    return new $3Dmol.Fog( this.color.getHex(), this.near, this.far );
-
-};/* 
- * To change this license header, choose License Headers in Project Properties.
- * To change this template file, choose Tools | Templates
- * and open the template in the editor.
- */
-
-$3Dmol.ShaderUtils = {
-    
-    clone: function ( uniforms_src ) {
-        
-        var u, p, parameter, parameter_src, uniforms_clone = {};
-        
-        for (u in uniforms_src) {
-            uniforms_clone[u] = {};
-            uniforms_clone[u].type = uniforms_src[u].type;
-            
-            var srcValue = uniforms_src[u].value;
-            
-            if (srcValue instanceof $3Dmol.Color)
-                uniforms_clone[u].value = srcValue.clone();
-            else if (typeof srcValue === "number")
-                uniforms_clone[u].value = srcValue;
-            else if (srcValue instanceof Array) 
-                uniforms_clone[u].value = [];
-            else
-                console.error("Error copying shader uniforms from ShaderLib: unknown type for uniform");
-            
-        }
-        
-        return uniforms_clone;
-    }
-};
-
-$3Dmol.ShaderLib = { 
-    'basic' : {
-        fragmentShader : [                    
-"uniform mat4 viewMatrix;",
-"uniform vec3 cameraPosition;",
-"uniform vec3 diffuse;",
-"uniform float opacity;",
-
-"uniform vec3 fogColor;",
-"uniform float fogNear;",
-"uniform float fogFar;",
-
-"varying vec3 vColor;",
-
-"void main() {",
-    
-"    gl_FragColor = vec4( diffuse, opacity );",
-"    gl_FragColor = gl_FragColor * vec4( vColor, opacity );",
-    
-"    float depth = gl_FragCoord.z / gl_FragCoord.w;",    
-"    float fogFactor = smoothstep( fogNear, fogFar, depth );",
-    
-"    gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );",
-
-"}"
-                                                     
-].join("\n"),
-        
-        vertexShader : [
-
-"uniform mat4 modelViewMatrix;",
-"uniform mat4 projectionMatrix;",
-"uniform mat4 viewMatrix;",
-"uniform mat3 normalMatrix;",
-"uniform vec3 cameraPosition;",
-
-"attribute vec3 position;",
-"attribute vec3 color;",
-
-"varying vec3 vColor;",
-
-"void main() {",
-
-"    vColor = color;",
-"    vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
-"    gl_Position = projectionMatrix * mvPosition;",
-
-"}"
-        
-].join("\n"),
-    
-        uniforms : {
-            opacity: { type: 'f', value: 1.0 },
-            diffuse: { type: 'c', value: new $3Dmol.Color(1.0, 1.0, 1.0) },
-            fogColor: { type: 'c', value: new $3Dmol.Color(1.0, 1.0, 1.0) },
-            fogNear: { type: 'f', value: 1.0 },
-            fogFar: { type: 'f', value: 2000}
-        }
-
-    },
-    
- 'sphereimposter' : {
-        fragmentShader : [
-"uniform mat4 viewMatrix;",
-"uniform vec3 cameraPosition;",
-"uniform vec3 diffuse;",
-"uniform float opacity;",
-
-"uniform vec3 fogColor;",
-"uniform float fogNear;",
-"uniform float fogFar;",
-"uniform float uDepth;",
-
-"varying vec3 vColor;",
-"varying vec2 mapping;",
-"varying float rval;",
-
-"void main() {",
-"    float lensqr = dot(mapping,mapping);",
-"    float rsqr = rval*rval;",
-"    if(lensqr > rsqr)",
-"       discard;",
-"    float z = sqrt(rsqr-lensqr);",
-"    float zscale = z/rval;",
-"    gl_FragColor = vec4(zscale*vColor, 1 );", //TODO: directional lighting
-"    float depth = gl_FragCoord.z / gl_FragCoord.w;",
-"    gl_FragDepthEXT = gl_FragCoord.z-z/depth;",
-"    float fogFactor = smoothstep( fogNear, fogFar, depth );",
-
-"    gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );",
-
-
-"}"
-                                                     
-].join("\n"),
-        
-        vertexShader : [
-
-"uniform mat4 modelViewMatrix;",
-"uniform mat4 projectionMatrix;",
-"uniform mat4 viewMatrix;",
-"uniform mat3 normalMatrix;",
-"uniform vec3 cameraPosition;",
-
-"attribute vec3 position;",
-"attribute vec3 normal;",
-"attribute vec3 color;",
-
-"varying vec2 mapping;",
-"varying vec3 vColor;",
-"varying float rval;",
-
-"void main() {",
-
-"    vColor = color;",
-"    vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
-"    vec4 projPosition = projectionMatrix * mvPosition;",
-"    vec4 adjust = projectionMatrix* vec4(normal,0.0); adjust.z = 0.0; adjust.w = 0.0;",
-"    mapping = normal.xy;",
-"    rval = abs(normal.x);",
-"    gl_Position = projPosition+adjust;",
-
-"}"
-        
-].join("\n"),
-    
-        uniforms : {
-            opacity: { type: 'f', value: 1.0 },
-            diffuse: { type: 'c', value: new $3Dmol.Color(1.0, 1.0, 1.0) },
-            fogColor: { type: 'c', value: new $3Dmol.Color(1.0, 1.0, 1.0) },
-            fogNear: { type: 'f', value: 1.0 },
-            fogFar: { type: 'f', value: 2000},
-        }
-
-    },
-    
-    
-    'lambert' : { 
-        fragmentShader : [
-
-"uniform mat4 viewMatrix;",
-"uniform vec3 cameraPosition;",
-"uniform float opacity;",
-
-"uniform vec3 fogColor;",
-"uniform float fogNear;",
-"uniform float fogFar;",
-
-"varying vec3 vLightFront;",
-"varying vec3 vColor;",
-
-"void main() {",
-    
-"    gl_FragColor = vec4( vec3 ( 1.0 ), opacity );",
-    
-"    #ifndef WIREFRAME",
-"    gl_FragColor.xyz *= vLightFront;",
-"    #endif",
-    
-"    gl_FragColor = gl_FragColor * vec4( vColor, opacity );",
-"    float depth = gl_FragCoord.z / gl_FragCoord.w;",
-    
-"    float fogFactor = smoothstep( fogNear, fogFar, depth );",
-    
-"    gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );",
-
-"}"
-
-
-].join("\n"),
-       
-       vertexShader : [
-
-"uniform mat4 modelViewMatrix;",
-"uniform mat4 projectionMatrix;",
-"uniform mat4 viewMatrix;",
-"uniform mat3 normalMatrix;",
-"uniform vec3 cameraPosition;",
-"uniform vec3 ambient;",
-"uniform vec3 diffuse;",
-"uniform vec3 emissive;",
-"uniform vec3 ambientLightColor;",
-"uniform vec3 directionalLightColor[ 1 ];",
-"uniform vec3 directionalLightDirection[ 1 ];",
-
-"attribute vec3 position;",
-"attribute vec3 normal;",
-"attribute vec3 color;",
-
-"varying vec3 vColor;",
-"varying vec3 vLightFront;",
-
-"void main() {",
-    
-"    vColor = color;",
-    
-"    vec3 objectNormal = normal;",  
-"    vec3 transformedNormal = normalMatrix * objectNormal;",    
-"    vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
-    
-"    vLightFront = vec3( 0.0 );",
-    
-"    transformedNormal = normalize( transformedNormal );",
-    
-"    vec4 lDirection = viewMatrix * vec4( directionalLightDirection[ 0 ], 0.0 );",
-"    vec3 dirVector = normalize( lDirection.xyz );",
-"    float dotProduct = dot( transformedNormal, dirVector );",
-"    vec3 directionalLightWeighting = vec3( max( dotProduct, 0.0 ) );",
-    
-"    vLightFront += directionalLightColor[ 0 ] * directionalLightWeighting;",
-"    vLightFront = vLightFront * diffuse + ambient * ambientLightColor + emissive;",
-    
-"    gl_Position = projectionMatrix * mvPosition;",
-"}"
-           
-].join("\n"),
-
-        uniforms : {
-            opacity: { type: 'f', value: 1.0 },
-            diffuse: { type: 'c', value: new $3Dmol.Color(1.0, 1.0, 1.0) },
-            fogColor: { type: 'c', value: new $3Dmol.Color(1.0, 1.0, 1.0) },
-            fogNear: { type: 'f', value: 1.0 },
-            fogFar: { type: 'f', value: 2000},           
-            ambient: { type: 'c', value: new $3Dmol.Color(1.0, 1.0, 1.0) },
-            emissive: { type: 'c', value: new $3Dmol.Color(1.0, 1.0, 1.0) },
-            ambientLightColor: { type: 'fv', value: [] },
-            directionalLightColor: { type: 'fv', value: [] },
-            directionalLightDirection: { type: 'fv', value: [] }
-        }
-
-    },
- 
-//for outline
-     'outline' : { 
-        fragmentShader : [
-
-"uniform float opacity;",
-"uniform vec3 outlineColor;",
-"uniform vec3 fogColor;",
-"uniform float fogNear;",
-"uniform float fogFar;",
-
-"void main() {",
-    
-"    gl_FragColor = vec4( outlineColor, 1 );",
-"}"
-
-
-].join("\n"),
-       
-       vertexShader : [
-
-"uniform mat4 modelViewMatrix;",
-"uniform mat4 projectionMatrix;",
-"uniform float outlineWidth;",
-"uniform float outlinePushback;",
-
-"attribute vec3 position;",
-"attribute vec3 normal;",
-"attribute vec3 color;",
-
-"void main() {",
-
-"    vec4 norm = modelViewMatrix*vec4(normalize(normal),0.0);",
-"    vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
-"    mvPosition.xy += norm.xy*outlineWidth;",
-"    gl_Position = projectionMatrix * mvPosition;",
-"    mvPosition.z -= outlinePushback;", //go backwards in model space
-"    vec4 pushpos = projectionMatrix*mvPosition;", //project to get z in projection space, I'm probably missing some simple math to do the same thing..
-"    gl_Position.z = gl_Position.w*pushpos.z/pushpos.w;",
-"}"
-           
-].join("\n"),
-
-        uniforms : {
-            opacity: { type: 'f', value: 1.0 },
-            diffuse: { type: 'c', value: new $3Dmol.Color(1.0, 1.0, 1.0) },
-            outlineColor: { type: 'c', value: new $3Dmol.Color(0.0, 0.0, 0.0) },
-            fogColor: { type: 'c', value: new $3Dmol.Color(1.0, 1.0, 1.0) },
-            fogNear: { type: 'f', value: 1.0 },
-            fogFar: { type: 'f', value: 2000},           
-            outlineWidth: { type: 'f', value: 0.1 },
-            outlinePushback: { type: 'f', value: 1.0 },
-        }
-
-    },
-    
-    //for double sided lighting
-    'lambertdouble' : { 
-        fragmentShader : [
-
-"uniform mat4 viewMatrix;",
-"uniform vec3 cameraPosition;",
-"uniform float opacity;",
-
-"uniform vec3 fogColor;",
-"uniform float fogNear;",
-"uniform float fogFar;",
-
-"varying vec3 vLightFront;",
-"varying vec3 vLightBack;",
-
-"varying vec3 vColor;",
-
-"void main() {",
-    
-"    gl_FragColor = vec4( vec3 ( 1.0 ), opacity );",
-    
-"    #ifndef WIREFRAME",
-"    if ( gl_FrontFacing )",
-"       gl_FragColor.xyz *= vLightFront;",
-"    else",
-"       gl_FragColor.xyz *= vLightBack;",
-"    #endif",
-    
-"    gl_FragColor = gl_FragColor * vec4( vColor, opacity );",
-"    float depth = gl_FragCoord.z / gl_FragCoord.w;",
-    
-"    float fogFactor = smoothstep( fogNear, fogFar, depth );",
-    
-"    gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );",
-
-"}"
-
-
-].join("\n"),
-       
-       vertexShader : [
-
-"uniform mat4 modelViewMatrix;",
-"uniform mat4 projectionMatrix;",
-"uniform mat4 viewMatrix;",
-"uniform mat3 normalMatrix;",
-"uniform vec3 cameraPosition;",
-"uniform vec3 ambient;",
-"uniform vec3 diffuse;",
-"uniform vec3 emissive;",
-"uniform vec3 ambientLightColor;",
-"uniform vec3 directionalLightColor[ 1 ];",
-"uniform vec3 directionalLightDirection[ 1 ];",
-
-"attribute vec3 position;",
-"attribute vec3 normal;",
-"attribute vec3 color;",
-
-"varying vec3 vColor;",
-"varying vec3 vLightFront;",
-"varying vec3 vLightBack;",
-
-"void main() {",
-    
-"    vColor = color;",
-    
-"    vec3 objectNormal = normal;",  
-"    vec3 transformedNormal = normalMatrix * objectNormal;",    
-"    vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
-    
-"    vLightFront = vec3( 0.0 );",
-"    vLightBack = vec3( 0.0 );",
-    
-"    transformedNormal = normalize( transformedNormal );",
-    
-"    vec4 lDirection = viewMatrix * vec4( directionalLightDirection[ 0 ], 0.0 );",
-"    vec3 dirVector = normalize( lDirection.xyz );",
-"    float dotProduct = dot( transformedNormal, dirVector );",
-"    vec3 directionalLightWeighting = vec3( max( dotProduct, 0.0 ) );",
-"    vec3 directionalLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );",
-
-"    vLightFront += directionalLightColor[ 0 ] * directionalLightWeighting;",
-"    vLightBack += directionalLightColor[ 0 ] * directionalLightWeightingBack;",
-
-"    vLightFront = vLightFront * diffuse + ambient * ambientLightColor + emissive;",
-"    vLightBack = vLightBack * diffuse + ambient * ambientLightColor + emissive;",
-
-"    gl_Position = projectionMatrix * mvPosition;",
-"}"
-           
-].join("\n"),
-
-        uniforms : {
-            opacity: { type: 'f', value: 1.0 },
-            diffuse: { type: 'c', value: new $3Dmol.Color(1.0, 1.0, 1.0) },
-            fogColor: { type: 'c', value: new $3Dmol.Color(1.0, 1.0, 1.0) },
-            fogNear: { type: 'f', value: 1.0 },
-            fogFar: { type: 'f', value: 2000},           
-            ambient: { type: 'c', value: new $3Dmol.Color(1.0, 1.0, 1.0) },
-            emissive: { type: 'c', value: new $3Dmol.Color(1.0, 1.0, 1.0) },
-            ambientLightColor: { type: 'fv', value: [] },
-            directionalLightColor: { type: 'fv', value: [] },
-            directionalLightDirection: { type: 'fv', value: [] }
-        }
-
-    },
-    
-    
-    'sprite': {
-        
-        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"),
-        
-        vertexShader: [
-
-"uniform int useScreenCoordinates;",
-"uniform vec3 screenPosition;",
-"uniform mat4 modelViewMatrix;",
-"uniform mat4 projectionMatrix;",
-"uniform float rotation;",
-"uniform vec2 scale;",
-"uniform vec2 alignment;",
-"uniform vec2 uvOffset;",
-"uniform vec2 uvScale;",
-
-"attribute vec2 position;",
-"attribute vec2 uv;",
-
-"varying vec2 vUV;",
-
-"void main() {",
-    
-"    vUV = uvOffset + uv * uvScale;",
-    
-"    vec2 alignedPosition = position + alignment;",
-    
-"    vec2 rotatedPosition;",
-"    rotatedPosition.x = ( cos(rotation) * alignedPosition.x - sin(rotation) * alignedPosition.y ) * scale.x;",
-"    rotatedPosition.y = ( sin(rotation) * alignedPosition.x + cos(rotation) * alignedPosition.y ) * scale.y;",
-    
-"    vec4 finalPosition;",
-    
-"    if(useScreenCoordinates != 0) {",
-"        finalPosition = vec4(screenPosition.xy + rotatedPosition, screenPosition.z, 1.0);",
-"    }",
-    
-"    else {",
-"        finalPosition = projectionMatrix * modelViewMatrix * vec4(0.0, 0.0, 0.0, 1.0); finalPosition /= finalPosition.w;",
-"        finalPosition.xy += rotatedPosition; ",
-"    }",
-    
-"    gl_Position = finalPosition;",
-    
-"}"
-       
-].join("\n"),
-
-        uniforms : {
-            
-        }
-        
-    }
-    
-};
-/*  ProteinSurface.js by biochem_fan
-
-Ported and modified for Javascript based on EDTSurf,
-  whose license is as follows.
-
-Permission to use, copy, modify, and distribute this program for any
-purpose, with or without fee, is hereby granted, provided that this
-copyright notice and the reference information appear in all copies or
-substantial portions of the Software. It is provided "as is" without
-express or implied warranty. 
-
-Reference:
-http://zhanglab.ccmb.med.umich.edu/EDTSurf/
-D. Xu, Y. Zhang (2009) Generating Triangulated Macromolecular Surfaces
-by Euclidean Distance Transform. PLoS ONE 4(12): e8140.
-
-=======
-
-TODO: Improved performance on Firefox
-      Reduce memory consumption
-      Refactor!
- */
-
-
-// dkoes
-// Surface calculations.  This must be safe to use within a web worker.
-if (typeof console === 'undefined') {
-    // this should only be true inside of a webworker
-    console = {
-        log : function() {
-        }
-    };
-}
-
-$3Dmol.ProteinSurface = function() {
-
-    // constants for vpbits bitmasks
-    /** @const */
-    var INOUT = 1;
-    /** @const */
-    var ISDONE = 2;
-    /** @const */
-    var ISBOUND = 4;
-
-    var ptranx = 0, ptrany = 0, ptranz = 0;
-    var probeRadius = 1.4;
-    var defaultScaleFactor = 2;
-    var scaleFactor = defaultScaleFactor; // 2 is .5A grid; if this is made user configurable,
-                            // also have to adjust offset used to find non-shown
-                            // atoms
-    var pHeight = 0, pWidth = 0, pLength = 0;
-    var cutRadius = 0;
-    var vpBits = null; // uint8 array of bitmasks
-    var vpDistance = null; // floatarray of _squared_ distances
-    var vpAtomID = null; // intarray
-    var vertnumber = 0, facenumber = 0;
-    var pminx = 0, pminy = 0, pminz = 0, pmaxx = 0, pmaxy = 0, pmaxz = 0;
-
-    var vdwRadii = {
-            "H" : 1.2,
-            "Li" : 1.82,
-            "Na" : 2.27,
-            "K" : 2.75,
-            "C" : 1.7,
-            "N" : 1.55,
-            "O" : 1.52,
-            "F" : 1.47,
-            "P" : 1.80,
-            "S" : 1.80,
-            "CL" : 1.75,
-            "BR" : 1.85,
-            "SE" : 1.90,
-            "ZN" : 1.39,
-            "CU" : 1.4,
-            "NI" : 1.63,
-            "X" : 2
-        };
-    
-    /** @param {AtomSpec} atom */
-    var getVDWIndex = function(atom) {
-        if(!atom.elem || typeof(vdwRadii[atom.elem]) == "undefined") {
-            return "X";
-        }
-        return atom.elem;
-    };
-    
-    var depty = {}, widxz = {};
-    var faces, verts;
-    var nb = [ new Int32Array([ 1, 0, 0 ]), new Int32Array([ -1, 0, 0 ]), 
-               new Int32Array([ 0, 1, 0 ]), new Int32Array([ 0, -1, 0 ]),
-               new Int32Array([ 0, 0, 1 ]), 
-               new Int32Array([ 0, 0, -1 ]), 
-               new Int32Array([ 1, 1, 0 ]), 
-               new Int32Array([ 1, -1, 0 ]), 
-               new Int32Array([ -1, 1, 0 ]),
-               new Int32Array([ -1, -1, 0 ]), 
-               new Int32Array([ 1, 0, 1 ]), 
-               new Int32Array([ 1, 0, -1 ]), 
-               new Int32Array([ -1, 0, 1 ]),
-               new Int32Array([ -1, 0, -1 ]), 
-               new Int32Array([ 0, 1, 1 ]), 
-               new Int32Array([ 0, 1, -1 ]), 
-               new Int32Array([ 0, -1, 1 ]),
-               new Int32Array([ 0, -1, -1 ]), 
-               new Int32Array([ 1, 1, 1 ]), 
-               new Int32Array([ 1, 1, -1 ]), 
-               new Int32Array([ 1, -1, 1 ]),
-               new Int32Array([ -1, 1, 1 ]), 
-               new Int32Array([ 1, -1, -1 ]), 
-               new Int32Array([ -1, -1, 1 ]), 
-               new Int32Array([ -1, 1, -1 ]),
-               new Int32Array([ -1, -1, -1 ]) ];
-
-    var origextent;
-
-    var inOrigExtent = function(x, y, z) {
-        if (x < origextent[0][0] || x > origextent[1][0])
-            return false;
-        if (y < origextent[0][1] || y > origextent[1][1])
-            return false;
-        if (z < origextent[0][2] || z > origextent[1][2])
-            return false;
-        return true;
-    };
-
-    this.getFacesAndVertices = function(atomlist) {
-        var atomsToShow = {};
-        var i, il;
-        for (i = 0, il = atomlist.length; i < il; i++)
-            atomsToShow[atomlist[i]] = true;
-        var vertices = verts;
-        for (i = 0, il = vertices.length; i < il; i++) {
-            vertices[i].x = vertices[i].x / scaleFactor - ptranx;
-            vertices[i].y = vertices[i].y / scaleFactor - ptrany;
-            vertices[i].z = vertices[i].z / scaleFactor - ptranz;
-        }
-
-        var finalfaces = [];
-        for (i = 0, il = faces.length; i < il; i += 3) {
-            //var f = faces[i];
-            var fa = faces[i], fb = faces[i+1], fc = faces[i+2];
-            var a = vertices[fa]['atomid'], b = vertices[fb]['atomid'], c = vertices[fc]['atomid'];
-
-            // must be a unique face for each atom
-            var which = a;
-            if (b < which)
-                which = b;
-            if (c < which)
-                which = c;
-            if (!atomsToShow[which]) {
-                continue;
-            }
-            var av = vertices[faces[i]];
-            var bv = vertices[faces[i+1]];
-            var cv = vertices[faces[i+2]];
-
-            if (fa !== fb && fb !== fc && fa !== fc){
-                finalfaces.push(fa); 
-                finalfaces.push(fb); 
-                finalfaces.push(fc); 
-            }
-               
-        }
-
-        //try to help the garbage collector
-        vpBits = null; // uint8 array of bitmasks
-        vpDistance = null; // floatarray
-        vpAtomID = null; // intarray
-        
-        return {
-            'vertices' : vertices,
-            'faces' : finalfaces
-        };
-    };
-
-
-    this.initparm = function(extent, btype, volume) {
-        if(volume > 1000000) //heuristical decrease resolution to avoid large memory consumption
-            scaleFactor = defaultScaleFactor/2;
-        
-        var margin = (1 / scaleFactor) * 5.5; // need margin to avoid
-                                                // boundary/round off effects
-        origextent = extent;
-        pminx = extent[0][0]; pmaxx = extent[1][0];
-        pminy = extent[0][1]; pmaxy = extent[1][1];
-        pminz = extent[0][2]; pmaxz = extent[1][2];
-
-        if (!btype) {
-            pminx -= margin;
-            pminy -= margin;
-            pminz -= margin;
-            pmaxx += margin;
-            pmaxy += margin;
-            pmaxz += margin;
-        } else {
-            pminx -= probeRadius + margin;
-            pminy -= probeRadius + margin;
-            pminz -= probeRadius + margin;
-            pmaxx += probeRadius + margin;
-            pmaxy += probeRadius + margin;
-            pmaxz += probeRadius + margin;
-        }
-
-        pminx = Math.floor(pminx * scaleFactor) / scaleFactor;
-        pminy = Math.floor(pminy * scaleFactor) / scaleFactor;
-        pminz = Math.floor(pminz * scaleFactor) / scaleFactor;
-        pmaxx = Math.ceil(pmaxx * scaleFactor) / scaleFactor;
-        pmaxy = Math.ceil(pmaxy * scaleFactor) / scaleFactor;
-        pmaxz = Math.ceil(pmaxz * scaleFactor) / scaleFactor;
-
-        ptranx = -pminx;
-        ptrany = -pminy;
-        ptranz = -pminz;
-
-        pLength = Math.ceil(scaleFactor * (pmaxx - pminx)) + 1;
-        pWidth = Math.ceil(scaleFactor * (pmaxy - pminy)) + 1;
-        pHeight = Math.ceil(scaleFactor * (pmaxz - pminz)) + 1;
-
-        this.boundingatom(btype);
-        cutRadius = probeRadius * scaleFactor;
-
-        vpBits = new Uint8Array(pLength * pWidth * pHeight);
-        vpDistance = new Float64Array(pLength * pWidth * pHeight); // float 32
-        // doesn't
-        // play
-        // nicely
-        // with
-        // native
-        // floats
-        vpAtomID = new Int32Array(pLength * pWidth * pHeight);
-        //console.log("Box size: ", pLength, pWidth, pHeight, vpBits.length);
-    };
-
-    this.boundingatom = function(btype) {
-        var tradius = [];
-        var txz, tdept, sradius, idx;
-        flagradius = btype;
-
-        for ( var i in vdwRadii) {
-            if(!vdwRadii.hasOwnProperty(i))
-                continue;
-            var r = vdwRadii[i];
-            if (!btype)
-                tradius[i] = r * scaleFactor + 0.5;
-            else
-                tradius[i] = (r + probeRadius) * scaleFactor + 0.5;
-
-            sradius = tradius[i] * tradius[i];
-            widxz[i] = Math.floor(tradius[i]) + 1;
-            depty[i] = new Int32Array(widxz[i] * widxz[i]);
-            indx = 0;
-            for (j = 0; j < widxz[i]; j++) {
-                for (k = 0; k < widxz[i]; k++) {
-                    txz = j * j + k * k;
-                    if (txz > sradius)
-                        depty[i][indx] = -1; // outside
-                    else {
-                        tdept = Math.sqrt(sradius - txz);
-                        depty[i][indx] = Math.floor(tdept);
-                    }
-                    indx++;
-                }
-            }
-        }
-    };
-
-    this.fillvoxels = function(atoms, atomlist) { // (int seqinit,int
-        // seqterm,bool
-        // atomtype,atom*
-        // proseq,bool bcolor)
-        var i, il;
-        for (i = 0, il = vpBits.length; i < il; i++) {
-            vpBits[i] = 0;
-            vpDistance[i] = -1.0;
-            vpAtomID[i] = -1;
-        }
-
-        for (i in atomlist) {
-            var atom = atoms[atomlist[i]];
-            if (atom === undefined)
-                continue;
-            this.fillAtom(atom, atoms);
-        }
-
-        for (i = 0, il = vpBits.length; i < il; i++)
-            if (vpBits[i] & INOUT)
-                vpBits[i] |= ISDONE;
-
-    };
-
-
-    this.fillAtom = function(atom, atoms) {
-        var cx, cy, cz, ox, oy, oz, mi, mj, mk, i, j, k, si, sj, sk;
-        var ii, jj, kk, n;
-        cx = Math.floor(0.5 + scaleFactor * (atom.x + ptranx));
-        cy = Math.floor(0.5 + scaleFactor * (atom.y + ptrany));
-        cz = Math.floor(0.5 + scaleFactor * (atom.z + ptranz));
-
-        var at = getVDWIndex(atom);
-        var nind = 0;
-        var cnt = 0;
-        var pWH = pWidth*pHeight;
-        
-        for (i = 0, n = widxz[at]; i < n; i++) {
-            for (j = 0; j < n; j++) {
-                if (depty[at][nind] != -1) {
-                    for (ii = -1; ii < 2; ii++) {
-                        for (jj = -1; jj < 2; jj++) {
-                            for (kk = -1; kk < 2; kk++) {
-                                if (ii !== 0 && jj !== 0 && kk !== 0) {
-                                    mi = ii * i;
-                                    mk = kk * j;
-                                    for (k = 0; k <= depty[at][nind]; k++) {
-                                        mj = k * jj;
-                                        si = cx + mi;
-                                        sj = cy + mj;
-                                        sk = cz + mk;
-                                        if (si < 0 || sj < 0 || 
-                                                sk < 0 ||
-                                                si >= pLength || 
-                                                sj >= pWidth || 
-                                                sk >= pHeight)
-                                            continue;
-                                        var index = si * pWH + sj * pHeight + sk;
-
-                                        if (!(vpBits[index] & INOUT)) {
-                                            vpBits[index] |= INOUT;
-                                            vpAtomID[index] = atom.serial;
-                                        } else {
-                                            var atom2 = atoms[vpAtomID[index]];
-                                            if(atom2.serial != atom.serial) {
-                                                ox = cx + mi - Math.floor(0.5 + scaleFactor *
-                                                        (atom2.x + ptranx));
-                                                oy = cy + mj - Math.floor(0.5 + scaleFactor *
-                                                        (atom2.y + ptrany));
-                                                oz = cz + mk - Math.floor(0.5 + scaleFactor *
-                                                        (atom2.z + ptranz));
-                                                if (mi * mi + mj * mj + mk * mk < ox *
-                                                        ox + oy * oy + oz * oz)
-                                                    vpAtomID[index] = atom.serial;
-                                            }
-                                        }
-
-                                    }// k
-                                }// if
-                            }// kk
-                        }// jj
-                    }// ii
-                }// if
-                nind++;
-            }// j
-        }// i
-    };
-
-    this.fillvoxelswaals = function(atoms, atomlist) {
-        var i, il;
-        for (i = 0, il = vpBits.length; i < il; i++)
-            vpBits[i] &= ~ISDONE; // not isdone
-
-        for (i in atomlist) {
-            var atom = atoms[atomlist[i]];
-            if (atom === undefined)
-                continue;
-
-            this.fillAtomWaals(atom, atoms);
-        }
-    };
-
-    this.fillAtomWaals = function(atom, atoms) {
-        var cx, cy, cz, ox, oy, oz, nind = 0;
-        var mi, mj, mk, si, sj, sk, i, j, k, ii, jj, kk, n;
-        cx = Math.floor(0.5 + scaleFactor * (atom.x + ptranx));
-        cy = Math.floor(0.5 + scaleFactor * (atom.y + ptrany));
-        cz = Math.floor(0.5 + scaleFactor * (atom.z + ptranz));
-
-        var at = getVDWIndex(atom);
-        var pWH = pWidth*pHeight;
-        for (i = 0, n = widxz[at]; i < n; i++) {
-            for (j = 0; j < n; j++) {
-                if (depty[at][nind] != -1) {
-                    for (ii = -1; ii < 2; ii++) {
-                        for (jj = -1; jj < 2; jj++) {
-                            for (kk = -1; kk < 2; kk++) {
-                                if (ii !== 0 && jj !== 0 && kk !== 0) {
-                                    mi = ii * i;
-                                    mk = kk * j;
-                                    for (k = 0; k <= depty[at][nind]; k++) {
-                                        mj = k * jj;
-                                        si = cx + mi;
-                                        sj = cy + mj;
-                                        sk = cz + mk;
-                                        if (si < 0 || sj < 0 || 
-                                                sk < 0 || 
-                                                si >= pLength || 
-                                                sj >= pWidth || 
-                                                sk >= pHeight)
-                                            continue;
-                                        var index = si * pWH + sj * pHeight + sk;
-                                        if (!(vpBits[index] & ISDONE)) {
-                                            vpBits[index] |= ISDONE;
-                                            vpAtomID[index] = atom.serial;
-                                        }  else {
-                                            var atom2 = atoms[vpAtomID[index]];
-                                            if(atom2.serial != atom.serial) {
-                                                ox = cx + mi - Math.floor(0.5 + scaleFactor *
-                                                        (atom2.x + ptranx));
-                                                oy = cy + mj - Math.floor(0.5 + scaleFactor *
-                                                        (atom2.y + ptrany));
-                                                oz = cz + mk - Math.floor(0.5 + scaleFactor *
-                                                        (atom2.z + ptranz));
-                                                if (mi * mi + mj * mj + mk * mk < ox *
-                                                        ox + oy * oy + oz * oz)
-                                                    vpAtomID[index] = atom.serial;
-                                            }
-                                        }
-                                    }// k
-                                }// if
-                            }// kk
-                        }// jj
-                    }// ii
-                }// if
-                nind++;
-            }// j
-        }// i
-    };
-
-    this.buildboundary = function() {
-        var pWH = pWidth*pHeight;
-        for (i = 0; i < pLength; i++) {
-            for (j = 0; j < pHeight; j++) {
-                for (k = 0; k < pWidth; k++) {
-                    var index = i * pWH + k * pHeight + j;
-                    if (vpBits[index] & INOUT) {
-                        var flagbound = false;
-                        var ii = 0;
-                        while (ii < 26) {
-                            var ti = i + nb[ii][0], tj = j + nb[ii][2], tk = k +
-                                    nb[ii][1];
-                            if (ti > -1 && 
-                                ti < pLength && 
-                                tk > -1 && 
-                                tk < pWidth && 
-                                tj > -1 && 
-                                tj < pHeight && 
-                                !(vpBits[ti * pWH + tk * pHeight + tj] & INOUT)) {
-                                vpBits[index] |= ISBOUND;
-                                break;
-                            } else
-                                ii++;
-                        }
-                    }
-                }
-            }
-        }
-    };
-
-    // a little class for 3d array, should really generalize this and
-    // use throughout...
-    var PointGrid = function(length, width, height) {
-        // the standard says this is zero initialized
-        var data = new Int32Array(length * width * height * 3);
-
-        // set position x,y,z to pt, which has ix,iy,and iz
-        this.set = function(x, y, z, pt) {
-            var index = ((((x * width) + y) * height) + z) * 3;
-            data[index] = pt.ix;
-            data[index + 1] = pt.iy;
-            data[index + 2] = pt.iz;
-        };
-
-        // return point at x,y,z
-        this.get = function(x, y, z) {
-            var index = ((((x * width) + y) * height) + z) * 3;
-            return {
-                ix : data[index],
-                iy : data[index + 1],
-                iz : data[index + 2]
-            };
-        };
-    };
-
-    this.fastdistancemap = function() {
-        var eliminate = 0;
-        var certificate;
-        var i, j, k, n;
-
-        var boundPoint = new PointGrid(pLength, pWidth, pHeight);
-        var pWH = pWidth*pHeight;
-        var cutRSq = cutRadius*cutRadius;
-        
-        var inarray = [];
-        var outarray = [];
-        
-        var index;
-        
-        for (i = 0; i < pLength; i++) {
-            for (j = 0; j < pWidth; j++) {
-                for (k = 0; k < pHeight; k++) {
-                    index = i * pWH + j * pHeight + k;
-                    vpBits[index] &= ~ISDONE; // isdone = false
-                    if (vpBits[index] & INOUT) {
-                        if (vpBits[index] & ISBOUND) {
-                            var triple = {
-                                ix : i,
-                                iy : j,
-                                iz : k
-                            };
-                            boundPoint.set(i, j, k, triple);
-                            inarray.push(triple);
-                            vpDistance[index] = 0;
-                            vpBits[index] |= ISDONE;
-                            vpBits[index] &= ~ISBOUND;
-                        } 
-                    }
-                }
-            }
-        }
-
-        do {
-            outarray = this.fastoneshell(inarray, boundPoint);
-            inarray = [];
-            for (i = 0, n = outarray.length; i < n; i++) {
-                index = pWH * outarray[i].ix + pHeight *
-                    outarray[i].iy + outarray[i].iz;
-                vpBits[index] &= ~ISBOUND;
-                if (vpDistance[index] <= 1.0404 * cutRSq) {
-                    inarray.push({
-                        ix : outarray[i].ix,
-                        iy : outarray[i].iy,
-                        iz : outarray[i].iz
-                    });
-                }
-            }
-        } while (inarray.length !== 0);
-
-        inarray = [];
-        outarray = [];
-        boundPoint = null;
-        
-        var cutsf = scaleFactor - 0.5;
-        if (cutsf < 0)
-            cutsf = 0;
-        var cutoff = cutRSq - 0.50 / (0.1 + cutsf);
-        for (i = 0; i < pLength; i++) {
-            for (j = 0; j < pWidth; j++) {
-                for (k = 0; k < pHeight; k++) {
-                    index = i * pWH + j * pHeight + k;
-                    vpBits[index] &= ~ISBOUND;
-                    // ses solid
-                    if (vpBits[index] & INOUT) {
-                        if (!(vpBits[index] & ISDONE) ||
-                                ((vpBits[index] & ISDONE) && vpDistance[index] >= cutoff)) {
-                            vpBits[index] |= ISBOUND;
-                        }
-                    }
-                }
-            }
-        }
-
-    };
-
-    this.fastoneshell = function(inarray, boundPoint) { // (int* innum,int
-        // *allocout,voxel2
-        // ***boundPoint, int*
-        // outnum, int *elimi)
-        var tx, ty, tz;
-        var dx, dy, dz;
-        var i, j, n;
-        var square;
-        var bp, index;
-        var outarray = [];
-        if (inarray.length === 0)
-            return outarray;
-
-        tnv = {
-            ix : -1,
-            iy : -1,
-            iz : -1
-        };
-        var pWH = pWidth*pHeight;
-        for ( i = 0, n = inarray.length; i < n; i++) {
-            tx = inarray[i].ix;
-            ty = inarray[i].iy;
-            tz = inarray[i].iz;
-            bp = boundPoint.get(tx, ty, tz);
-
-            for (j = 0; j < 6; j++) {
-                tnv.ix = tx + nb[j][0];
-                tnv.iy = ty + nb[j][1];
-                tnv.iz = tz + nb[j][2];
-                
-                if (tnv.ix < pLength && tnv.ix > -1 && tnv.iy < pWidth &&
-                        tnv.iy > -1 && tnv.iz < pHeight && tnv.iz > -1) {
-                    index = tnv.ix * pWH + pHeight * tnv.iy + tnv.iz;
-                    
-                    if ((vpBits[index] & INOUT) && !(vpBits[index] & ISDONE)) {
-    
-                        boundPoint.set(tnv.ix, tnv.iy, tz + nb[j][2], bp);
-                        dx = tnv.ix - bp.ix;
-                        dy = tnv.iy - bp.iy;
-                        dz = tnv.iz - bp.iz;
-                        square = dx * dx + dy * dy + dz * dz;
-                        vpDistance[index] = square;
-                        vpBits[index] |= ISDONE;
-                        vpBits[index] |= ISBOUND;
-    
-                        outarray.push({
-                            ix : tnv.ix,
-                            iy : tnv.iy,
-                            iz : tnv.iz
-                        });
-                    } else if ((vpBits[index] & INOUT) && (vpBits[index] & ISDONE)) {
-    
-                        dx = tnv.ix - bp.ix;
-                        dy = tnv.iy - bp.iy;
-                        dz = tnv.iz - bp.iz;
-                        square = dx * dx + dy * dy + dz * dz;
-                        if (square < vpDistance[index]) {
-                            boundPoint.set(tnv.ix, tnv.iy, tnv.iz, bp);
-    
-                            vpDistance[index] = square;
-                            if (!(vpBits[index] & ISBOUND)) {
-                                vpBits[index] |= ISBOUND;
-                                outarray.push({
-                                    ix : tnv.ix,
-                                    iy : tnv.iy,
-                                    iz : tnv.iz
-                                });
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        // console.log("part1", positout);
-
-        for (i = 0, n = inarray.length; i < n; i++) {
-            tx = inarray[i].ix;
-            ty = inarray[i].iy;
-            tz = inarray[i].iz;
-            bp = boundPoint.get(tx, ty, tz);
-
-            for (j = 6; j < 18; j++) {
-                tnv.ix = tx + nb[j][0];
-                tnv.iy = ty + nb[j][1];
-                tnv.iz = tz + nb[j][2];
-
-                if(tnv.ix < pLength && tnv.ix > -1 && tnv.iy < pWidth &&
-                        tnv.iy > -1 && tnv.iz < pHeight && tnv.iz > -1) {
-                    index = tnv.ix * pWH + pHeight * tnv.iy + tnv.iz;
-                    
-                    if ((vpBits[index] & INOUT) && !(vpBits[index] & ISDONE)) {
-                        boundPoint.set(tnv.ix, tnv.iy, tz + nb[j][2], bp);
-    
-                        dx = tnv.ix - bp.ix;
-                        dy = tnv.iy - bp.iy;
-                        dz = tnv.iz - bp.iz;
-                        square = dx * dx + dy * dy + dz * dz;
-                        vpDistance[index] = square;
-                        vpBits[index] |= ISDONE;
-                        vpBits[index] |= ISBOUND;
-    
-                        outarray.push({
-                            ix : tnv.ix,
-                            iy : tnv.iy,
-                            iz : tnv.iz
-                        });
-                    } else if ((vpBits[index] & INOUT) && (vpBits[index] & ISDONE)) {
-                        dx = tnv.ix - bp.ix;
-                        dy = tnv.iy - bp.iy;
-                        dz = tnv.iz - bp.iz;
-                        square = dx * dx + dy * dy + dz * dz;
-                        if (square < vpDistance[index]) {
-                            boundPoint.set(tnv.ix, tnv.iy, tnv.iz, bp);
-                            vpDistance[index] = square;
-                            if (!(vpBits[index] & ISBOUND)) {
-                                vpBits[index] |= ISBOUND;
-                                outarray.push({
-                                    ix : tnv.ix,
-                                    iy : tnv.iy,
-                                    iz : tnv.iz
-                                });
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        // console.log("part2", positout);
-
-        for (i = 0, n = inarray.length; i < n; i++) {
-            tx = inarray[i].ix;
-            ty = inarray[i].iy;
-            tz = inarray[i].iz;
-            bp = boundPoint.get(tx, ty, tz);
-
-            for (j = 18; j < 26; j++) {
-                tnv.ix = tx + nb[j][0];
-                tnv.iy = ty + nb[j][1];
-                tnv.iz = tz + nb[j][2];
-
-                if (tnv.ix < pLength && tnv.ix > -1 && tnv.iy < pWidth &&
-                        tnv.iy > -1 && tnv.iz < pHeight && tnv.iz > -1) {
-                    index = tnv.ix * pWH + pHeight * tnv.iy + tnv.iz;
-
-                    if ((vpBits[index] & INOUT) && !(vpBits[index] & ISDONE)) {
-                        boundPoint.set(tnv.ix, tnv.iy, tz + nb[j][2], bp);
-
-                        dx = tnv.ix - bp.ix;
-                        dy = tnv.iy - bp.iy;
-                        dz = tnv.iz - bp.iz;
-                        square = dx * dx + dy * dy + dz * dz;
-                        vpDistance[index] = square;
-                        vpBits[index] |= ISDONE;
-                        vpBits[index] |= ISBOUND;
-
-                        outarray.push({
-                            ix : tnv.ix,
-                            iy : tnv.iy,
-                            iz : tnv.iz
-                        });
-                    } else if ((vpBits[index] & INOUT)  && (vpBits[index] & ISDONE)) {
-                        dx = tnv.ix - bp.ix;
-                        dy = tnv.iy - bp.iy;
-                        dz = tnv.iz - bp.iz;
-                        square = dx * dx + dy * dy + dz * dz;
-                        if (square < vpDistance[index]) {
-                            boundPoint.set(tnv.ix, tnv.iy, tnv.iz, bp);
-
-                            vpDistance[index] = square;
-                            if (!(vpBits[index] & ISBOUND)) {
-                                vpBits[index] |= ISBOUND;
-                                outarray.push({
-                                    ix : tnv.ix,
-                                    iy : tnv.iy,
-                                    iz : tnv.iz
-                                });
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        // console.log("part3", positout);
-        return outarray;
-    };
-
-    this.marchingcubeinit = function(stype) {
-        for ( var i = 0, lim = vpBits.length; i < lim; i++) {
-            if (stype == 1) {// vdw
-                vpBits[i] &= ~ISBOUND;
-            } else if (stype == 4) { // ses
-                vpBits[i] &= ~ISDONE;
-                if (vpBits[i] & ISBOUND)
-                    vpBits[i] |= ISDONE;
-                vpBits[i] &= ~ISBOUND;
-            } else if (stype == 2) {// after vdw
-                if ((vpBits[i] & ISBOUND) && (vpBits[i] & ISDONE))
-                    vpBits[i] &= ~ISBOUND;
-                else if ((vpBits[i] & ISBOUND) && !(vpBits[i] & ISDONE))
-                    vpBits[i] |= ISDONE;
-            } else if (stype == 3) { // sas
-                vpBits[i] &= ~ISBOUND;
-            }
-        }
-    };
-
-    // this code allows me to empirically prune the marching cubes code tables
-    // to more efficiently handle discrete data
-    var counter = function() {
-        var data = Array(256);
-        for ( var i = 0; i < 256; i++)
-            data[i] = [];
-
-        this.incrementUsed = function(i, j) {
-            if (typeof data[i][j] === 'undefined')
-                data[i][j] = {
-                    used : 0,
-                    unused : 0
-                };
-            data[i][j].used++;
-        };
-
-        this.incrementUnused = function(i, j) {
-            if (typeof data[i][j] === 'undefined')
-                data[i][j] = {
-                    used : 0,
-                    unused : 0
-                };
-            data[i][j].unused++;
-
-        };
-
-        var redoTable = function(triTable) {
-            var str = "[";
-            for ( var i = 0; i < triTable.length; i++) {
-                var code = 0;
-                var table = triTable[i];
-                for ( var j = 0; j < table.length; j++) {
-                    code |= (1 << (table[j]));
-                }
-                str += "0x" + code.toString(16) + ", ";
-            }
-            str += "]";
-            console.log(str);
-        };
-
-        this.print = function() {
-
-            var table = MarchingCube.triTable;
-            var str;
-            var newtable = [];
-            for ( var i = 0; i < table.length; i++) {
-                var newarr = [];
-                for ( var j = 0; j < table[i].length; j += 3) {
-                    var k = j / 3;
-                    if (typeof data[i][k] === 'undefined' || !data[i][k].unused) {
-                        newarr.push(table[i][j]);
-                        newarr.push(table[i][j + 1]);
-                        newarr.push(table[i][j + 2]);
-                    }
-                    if (typeof data[i][k] === 'undefined')
-                        console.log("undef " + i + "," + k);
-                }
-                newtable.push(newarr);
-            }
-            console.log(JSON.stringify(newtable));
-            redoTable(newtable);
-        };
-    };
-    
-    this.marchingcube = function(stype) {
-        this.marchingcubeinit(stype);
-        verts = []; faces = [];   
-        $3Dmol.MarchingCube.march(vpBits, verts, faces, {
-            smooth : 1,
-            nX : pLength,
-            nY : pWidth,
-            nZ : pHeight        
-        });      
-
-        var pWH = pWidth*pHeight;
-        for (var i = 0, vlen = verts.length; i < vlen; i++) {
-            verts[i]['atomid'] = vpAtomID[verts[i].x * pWH + pHeight *
-                    verts[i].y + verts[i].z];
-        }  
-
-        $3Dmol.MarchingCube.laplacianSmooth(1, verts, faces);
-
-    };
-
-
-};
-//auto-initialization
-//Create embedded viewer from HTML attributes if true
-
-$(document).ready(function() {
-
-    if ($(".viewer_3Dmoljs")[0] !== undefined)
-        $3Dmol.autoinit = true;
-        
-    if ($3Dmol.autoinit) { 
-        $3Dmol.viewers = {};
-        var nviewers = 0;
-        $(".viewer_3Dmoljs").each( function() {
-            var viewerdiv = $(this);
-            var datauri = null;
-            
-        
-            var callback = (typeof(window[viewerdiv.data("callback")]) === 'function') ? 
-                    window[viewerdiv.data("callback")] : null;
-            
-            var type = null;
-            if (viewerdiv.data("pdb")) {
-                datauri = "http://www.rcsb.org/pdb/files/" + viewerdiv.data("pdb") + ".pdb";
-                type = "pdb";
-            } else if(viewerdiv.data("cid")) {
-                //this doesn't actually work since pubchem does have CORS enabled
-                type = "sdf";
-                datauri = "http://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/cid/" + viewerdiv.data("cid") + 
-                "/SDF?record_type=3d";
-            }
-            else if (viewerdiv.data("href"))
-                datauri = viewerdiv.data("href");
-                
-            var bgcolor = $3Dmol.CC.color(viewerdiv.data("backgroundcolor"));
-            var style = {line:{}};
-            if(viewerdiv.data("style")) style = $3Dmol.specStringToObject(viewerdiv.data("style"));
-            var select = {};
-            if(viewerdiv.data("select")) select = $3Dmol.specStringToObject(viewerdiv.data("select"));
-            var selectstylelist = [];
-            var surfaces = [];
-            var labels = [];
-            var d = viewerdiv.data();
-            
-            //let users specify individual but matching select/style tags, eg.
-            //data-select1 data-style1
-            var stylere = /style(.+)/;
-            var surfre = /surface(.*)/;
-            var reslabre = /labelres(.*)/;
-            var keys = [];
-            for(var dataname in d) {
-                if(d.hasOwnProperty(dataname)) {
-                    keys.push(dataname);
-                }
-            }
-            keys.sort();
-            for(var i = 0; i < keys.length; i++) {
-                var dataname = keys[i];
-                var m = stylere.exec(dataname);
-                if(m) {
-                    var selname = "select"+m[1];
-                    var newsel = $3Dmol.specStringToObject(d[selname]);
-                    var styleobj = $3Dmol.specStringToObject(d[dataname]);
-                    selectstylelist.push([newsel,styleobj]);
-                }         
-                m = surfre.exec(dataname);
-                if(m) {
-                    var selname = "select"+m[1];
-                    var newsel = $3Dmol.specStringToObject(d[selname]);
-                    var styleobj = $3Dmol.specStringToObject(d[dataname]);
-                    surfaces.push([newsel,styleobj]);
-                }
-                m = reslabre.exec(dataname);
-                if(m) {
-                    var selname = "select"+m[1];
-                    var newsel = $3Dmol.specStringToObject(d[selname]);
-                    var styleobj = $3Dmol.specStringToObject(d[dataname]);
-                    labels.push([newsel,styleobj]);
-                }
-            }
-            
-            //apply all the selections/styles parsed out above to the passed viewer
-            var applyStyles = function(glviewer) {
-                glviewer.setStyle(select,style);
-                for(var i = 0; i < selectstylelist.length; i++) {
-                    var sel = selectstylelist[i][0] || {};
-                    var sty = selectstylelist[i][1] || {"line":{}}
-                    glviewer.setStyle(sel, sty);
-                }
-                for(var i = 0; i < surfaces.length; i++) {
-                    var sel = surfaces[i][0] || {};
-                    var sty = surfaces[i][1] || {}
-                    glviewer.addSurface($3Dmol.SurfaceType.VDW, sty, sel, sel);
-                }
-                for(var i = 0; i < labels.length; i++) {
-                    var sel = labels[i][0] || {};
-                    var sty = labels[i][1] || {}
-                    glviewer.addResLabels(sel, sty);
-                }               
-                
-                glviewer.zoomTo();
-                glviewer.render();             
-            }
-            
-            
-            var glviewer = null;
-            try {
-                glviewer = $3Dmol.viewers[this.id || nviewers++] = $3Dmol.createViewer(viewerdiv, {defaultcolors: $3Dmol.rasmolElementColors});
-                glviewer.setBackgroundColor(bgcolor);                            
-            } catch ( error ) {
-                //for autoload, provide a useful error message
-                window.location = "http://get.webgl.org";                    
-            }           
-            
-            if (datauri) {  
-                
-                type = viewerdiv.data("type") || viewerdiv.data("datatype") || type;
-                if(!type) {
-                    type = datauri.substr(datauri.lastIndexOf('.')+1);
-                }
-                                
-                $.get(datauri, function(ret) {
-                    glviewer.addModel(ret, type);
-                    applyStyles(glviewer);       
-                    if (callback) 
-                        callback(glviewer);
-                }, 'text');         
-            }
-            
-            else {
-                
-                if (viewerdiv.data("element")) {
-                    var moldata = $("#" + viewerdiv.data("element")).val() || "";
-                    var type = viewerdiv.data("type") || viewerdiv.data("datatype");
-
-                    if (!type){
-
-                        console.log("Warning: No type specified for embedded viewer with moldata from " + viewerdiv.data("element") +
-                                    "\n assuming type 'pdb'")
-
-                        type = 'pdb';
-                    }
-
-                    glviewer.addModel(moldata, type);        
-                }
-
-                applyStyles(glviewer);                
-                if (callback) 
-                    callback(glviewer);
-            }
-            
-        });              
-    }
-});
-    
-//this is only used for create the enum documentation in JSDoc
-(function() {
-/**
- * Color representation. 
- * @typedef ColorSpec
- * @prop {string} 0xAF10AB - any hex number
- * @prop {string} white   - 0xFFFFFF
- * @prop {string} silver  - 0xC0C0C0
- * @prop {string} gray    - 0x808080
- * @prop {string} grey    - 0x808080
- * @prop {string} black   - 0x000000
- * @prop {string} red     - 0xFF0000
- * @prop {string} maroon  - 0x800000
- * @prop {string} yellow  - 0xFFFF00
- * @prop {string} orange  - 0xFF6600
- * @prop {string} olive   - 0x808000
- * @prop {string} lime    - 0x00FF00
- * @prop {string} green   - 0x008000
- * @prop {string} aqua    - 0x00FFFF
- * @prop {string} cyan    - 0x00FFFF
- * @prop {string} teal    - 0x008080
- * @prop {string} blue    - 0x0000FF
- * @prop {string} navy    - 0x000080
- * @prop {string} fuchsia - 0xFF00FF
- * @prop {string} magenta - 0xFF00FF
- * @prop {string} purple  - 0x800080
- */
-
-$3Dmol.elementColors.greenCarbon['C'] = 0x00ff00;
-
-
-$3Dmol.elementColors.cyanCarbon['C'] = 0x00ffff;
-
-
-$3Dmol.elementColors.magentaCarbon['C'] = 0xff00ff;
-
-
-$3Dmol.elementColors.yellowCarbon['C'] = 0xffff00;
-
-
-$3Dmol.elementColors.whiteCarbon['C'] = 0xffffff;
-
-
-$3Dmol.elementColors.orangeCarbon['C'] = 0xff6600;
-
-
-$3Dmol.elementColors.purpleCarbon['C'] = 0x800080;
-
-$3Dmol.elementColors.blueCarbon['C'] = 0x0000ff;
-
- /**
- * Color scheme representation. 
- * @typedef ColorschemeSpec
- * @prop {string} greenCarbon   - 0x00FF00
- * @prop {string} cyanCarbon    - 0x00FFFF
- * @prop {string} magentaCarbon - 0xFF00FF
- * @prop {string} yellowCarbon  - 0xFFFF00
- * @prop {string} whiteCarbon   - 0xFFFFFF
- * @prop {string} orangeCarbon  - 0xFF6600
- * @prop {string} purpleCarbon  - 0x100080
- * @prop {string} blueCarbon    - 0x0000FF
- */
- 
-});
-
-// in an attempt to reduce memory overhead, cache all $3Dmol.Colors
-// this makes things a little faster
-$3Dmol.CC = {
-    cache : {0:new $3Dmol.Color(0)},
-    color : function color_(hex) {
-        // Undefined values default to black
-        if(!hex)
-            return this.cache[0];
-        // cache hits
-        if(typeof(this.cache[hex]) !== "undefined") {
-            return this.cache[hex];
-        }
-        // arrays
-        else if(hex && hex.constructor === Array) {
-            // parse elements recursively
-            return hex.map(color_,this);
-        }
-        // numbers and hex strings
-        hex = this.getHex(hex);
-        if(typeof hex === 'number') {
-            var c = new $3Dmol.Color(hex);
-            this.cache[hex] = c;
-            return c;
-        } else {
-            // pass through $3Dmol.Color & other objects
-            return hex;
-        }
-    },
- 
-    colorTab : {
-        'white' : 0xFFFFFF,
-        'silver' : 0xC0C0C0,
-        'gray' : 0x808080,
-        'grey' : 0x808080,
-        'black' : 0x000000,
-        'red' : 0xFF0000,
-        'maroon' : 0x800000,
-        'yellow' : 0xFFFF00,
-        'orange' : 0xFF6600,
-        'olive' : 0x808000,
-        'lime' : 0x00FF00,
-        'green' : 0x008000,
-        'aqua' : 0x00FFFF,
-        'cyan' : 0x00FFFF,
-        'teal' : 0x008080,
-        'blue' : 0x0000FF,
-        'navy' : 0x000080,
-        'fuchsia' : 0xFF00FF,
-        'magenta' : 0xFF00FF,
-        'purple' : 0x800080
-    },    
-    getHex : function(hex) {
-        if (!isNaN(parseInt(hex)))
-            return parseInt(hex);
-        
-        else if (typeof(hex) === 'string') {
-            
-            return this.colorTab[hex.trim().toLowerCase()] || 0x000000;
-        }
-        return hex;
-    }
-    
-};
-
-
-$3Dmol['CC'] = $3Dmol.CC;
-$3Dmol['CC']['color'] = $3Dmol.CC.color;
-
-
-
-/** Return proper color for atom given style
- * @param {AtomSpec} atom
- * @param {AtomStyle} style
- * @return {$3Dmol.Color}
- */
-$3Dmol.getColorFromStyle = function(atom, style) {
-    var color = atom.color;
-    if (typeof (style.color) != "undefined" && style.color != "spectrum")
-        color = style.color;
-    if(typeof(style.colorscheme) != "undefined") {
-        if(typeof($3Dmol.elementColors[style.colorscheme]) != "undefined") {
-            //name of builtin colorscheme
-            var scheme = $3Dmol.elementColors[style.colorscheme];
-            if(typeof(scheme[atom.elem]) != "undefined") {
-                color = scheme[atom.elem];
-            }
-        } else if(typeof(style.colorscheme[atom.elem]) != 'undefined') {
-            //actual color scheme provided
-            color = style.colorscheme[atom.elem];
-        } else if(typeof(style.colorscheme.prop) != 'undefined' &&
-                typeof(style.colorscheme.gradient) != 'undefined') {         
-            //apply a property mapping
-            var prop = style.colorscheme.prop;
-            var scheme = style.colorscheme.gradient;
-            var range = scheme.range() || [-1,1]; //sensible default
-            var val = $3Dmol.getAtomProperty(atom, prop);
-            if(val != null) {
-                color = scheme.valueToHex(val, range);
-            }
-        }
-    }
-    var C = $3Dmol.CC.color(color);
-    return C;
-}
-
-/** Preset element coloring - from individual element colors to entire mappings (e.g. '$3Dmol.elementColors.Jmol' colors atoms with Jmol stylings)
- * @struct
- */
-$3Dmol.elementColors = $3Dmol.elementColors || {};
-
-$3Dmol.elementColors.defaultColor = 0xff1493;
-
-/** @property Jmol-like element colors*/
-$3Dmol.elementColors.Jmol = {
-        'H': 0xFFFFFF,
-        'He': 0xD9FFFF,
-        'HE': 0xD9FFFF,
-        'Li': 0xCC80FF,
-        'LI': 0xCC80FF,
-        'B': 0xFFB5B5,
-        'C': 0x909090,
-        'N': 0x3050F8,
-        'O': 0xFF0D0D,
-        'F': 0x90E050,
-        'Na': 0xAB5CF2,
-        'NA': 0xAB5CF2,
-        'Mg': 0x8AFF00,
-        'MG': 0x8AFF00,
-        'Al': 0xBFA6A6,
-        'AL': 0xBFA6A6,
-        'Si': 0xF0C8A0,
-        'SI': 0xF0C8A0,
-        'P': 0xFF8000,
-        'S': 0xFFFF30,
-        'Cl': 0x1FF01F,
-        'CL': 0x1FF01F,
-        'Ca': 0x3DFF00,
-        'CA': 0x3DFF00,
-        'Ti': 0xBFC2C7,
-        'TI': 0xBFC2C7,
-        'Cr': 0x8A99C7,
-        'CR': 0x8A99C7,
-        'Mn': 0x9C7AC7,
-        'MN': 0x9C7AC7,
-        'Fe': 0xE06633,
-        'FE': 0xE06633,
-        'Ni': 0x50D050,
-        'NI': 0x50D050,
-        'Cu': 0xC88033,
-        'CU': 0xC88033,
-        'Zn': 0x7D80B0,
-        'ZN': 0x7D80B0,
-        'Br': 0xA62929,
-        'BR': 0xA62929,
-        'Ag': 0xC0C0C0,
-        'AG': 0xC0C0C0,
-        'I': 0x940094,
-        'Ba': 0x00C900,
-        'BA': 0x00C900,
-        'Au': 0xFFD123,
-        'AU': 0xFFD123
-};
-
-/** @property rasmol-like element colors */
-$3Dmol.elementColors.rasmol = {
-        'H': 0xFFFFFF,
-        'He': 0xFFC0CB,
-        'HE': 0xFFC0CB,
-        'Li': 0xB22222,
-        'LI': 0xB22222,
-        'B': 0x00FF00,
-        'C': 0xC8C8C8,
-        'N': 0x8F8FFF,
-        'O': 0xF00000,
-        'F': 0xDAA520,
-        'Na': 0x0000FF,
-        'NA': 0x0000FF,
-        'Mg': 0x228B22,
-        'MG': 0x228B22,
-        'Al': 0x808090,
-        'AL': 0x808090,
-        'Si': 0xDAA520,
-        'SI': 0xDAA520,
-        'P': 0xFFA500,
-        'S': 0xFFC832,
-        'Cl': 0x00FF00,
-        'CL': 0x00FF00,
-        'Ca': 0x808090,
-        'CA': 0x808090,
-        'Ti': 0x808090,
-        'TI': 0x808090,
-        'Cr': 0x808090,
-        'CR': 0x808090,
-        'Mn': 0x808090,
-        'MN': 0x808090,
-        'Fe': 0xFFA500,
-        'FE': 0xFFA500,
-        'Ni': 0xA52A2A,
-        'NI': 0xA52A2A,
-        'Cu': 0xA52A2A,
-        'CU': 0xA52A2A,
-        'Zn': 0xA52A2A,
-        'ZN': 0xA52A2A,
-        'Br': 0xA52A2A,
-        'BR': 0xA52A2A,
-        'Ag': 0x808090,
-        'AG': 0x808090,
-        'I': 0xA020F0,
-        'Ba': 0xFFA500,
-        'BA': 0xFFA500,
-        'Au': 0xDAA520,
-        'AU': 0xDAA520    
-};
-
-$3Dmol.elementColors.defaultColors = $3Dmol.elementColors.rasmol;
-
-$3Dmol.elementColors.greenCarbon = $.extend({},$3Dmol.elementColors.defaultColors);
-$3Dmol.elementColors.greenCarbon['C'] = 0x00ff00;
-
-$3Dmol.elementColors.cyanCarbon =  $.extend({},$3Dmol.elementColors.defaultColors);
-$3Dmol.elementColors.cyanCarbon['C'] = 0x00ffff;
-
-$3Dmol.elementColors.magentaCarbon =  $.extend({},$3Dmol.elementColors.defaultColors);
-$3Dmol.elementColors.magentaCarbon['C'] = 0xff00ff;
-
-$3Dmol.elementColors.yellowCarbon =  $.extend({},$3Dmol.elementColors.defaultColors);
-$3Dmol.elementColors.yellowCarbon['C'] = 0xffff00;
-
-$3Dmol.elementColors.whiteCarbon =  $.extend({},$3Dmol.elementColors.defaultColors);
-$3Dmol.elementColors.whiteCarbon['C'] = 0xffffff;
-
-$3Dmol.elementColors.orangeCarbon =  $.extend({},$3Dmol.elementColors.defaultColors);
-$3Dmol.elementColors.orangeCarbon['C'] = 0xff6600;
-
-$3Dmol.elementColors.purpleCarbon =  $.extend({},$3Dmol.elementColors.defaultColors);
-$3Dmol.elementColors.purpleCarbon['C'] = 0x800080;
-
-$3Dmol.elementColors.blueCarbon =  $.extend({},$3Dmol.elementColors.defaultColors);
-$3Dmol.elementColors.blueCarbon['C'] = 0x0000ff;
-//glcartoon.js
-//This contains all the routines for rendering a cartoon given a set
-//of atoms with assigned secondary structure
-
-//TODO: generate normals directly in drawStrip and drawThinStrip
-
-var $3Dmol = $3Dmol || {};
-
-
-/**@typedef CartoonStyleSpec
- * @prop {ColorSpec} color - strand color, may specify as 'spectrum'
- * @prop {string} style - style of cartoon rendering (trace, oval, rectangle (default), parabola, edged)
- * @prop {boolean} ribbon - whether to use constant strand width, disregarding secondary structure; use thickness to adjust radius
- * @prop {boolean} arrows - whether to add arrows showing beta-sheet directionality; does not apply to trace or ribbon
- * @prop {boolean} tubes - whether to display alpha helices as simple cylinders; does not apply to trace
- * @prop {number} thickness - cartoon strand thickness, default is 0.4
- * @prop {number} width - cartoon strand width, default is secondary structure-dependent; does not apply to trace or ribbon
- * @prop {number} opacity - set opacity from 0-1; transparency is set per-chain with a warning outputted in the event of ambiguity
- * @prop {}
- * In nucleic acids, the base cylinders obtain their color from the atom to which the cylinder is drawn, which
- * is 'N1' for purines (resn: 'A', 'G', 'DA', 'DG') and 'N3' for pyrimidines (resn: 'C', 'U', 'DC', 'DT').
- * The different nucleobases can therefore be distinguished as follows:
- * @example
- * viewer.setStyle({resn:'DA', atom:'N1'}, {cartoon:{color:'red'}});
- * viewer.setStyle({resn:'DG', atom:'N1'}, {cartoon:{color:'green'}});
- * viewer.setStyle({resn:'DC', atom:'N3'}, {cartoon:{color:'blue'}});
- * viewer.setStyle({resn:'DT', atom:'N3'}, {cartoon:{color:'yellow'}});
- */
-
-/**
- * @ignore
- * @param {$3Dmol.Object3D} group
- * @param {AtomSpec} atomlist
- * @param {$3Dmol.Gradient} gradientscheme
- */
-$3Dmol.drawCartoon = (function() {
-
-    var defaultNum = 5; // for cross-sectional shape
-    var defaultDiv = 5; // for length-wise splicing
-
-    var coilWidth = 0.5;
-    var helixSheetWidth = 1.3;
-    var nucleicAcidWidth = 0.8;
-    var arrowTipWidth = 0.1;
-    var defaultThickness = 0.4; 
-
-    // helper functions
-
-    // Catmull-Rom subdivision
-    var subdivide = function(_points, DIV) { // points as Vector3
-        var ret = [];
-        var points = _points;
-        points = []; // Smoothing test
-        points.push(_points[0]);
-        
-        var i, lim, size;
-        var p0, p1, p2, p3, v0, v1;
-        
-        for (i = 1, lim = _points.length - 1; i < lim; i++) {
-            p1 = _points[i]; p2 = _points[i + 1];
-            if (p1.smoothen)
-                points.push(new $3Dmol.Vector3((p1.x + p2.x) / 2, (p1.y + p2.y) / 2,
-                        (p1.z + p2.z) / 2));
-            else
-                points.push(p1);
-        }
-        points.push(_points[_points.length - 1]);
-
-        
-        for (i = -1, size = points.length; i <= size - 3; i++) {
-            p0 = points[(i === -1) ? 0 : i];
-            p1 = points[i + 1]; p2 = points[i + 2];
-            p3 = points[(i === size - 3) ? size - 1 : i + 3];
-            v0 = new $3Dmol.Vector3().subVectors(p2, p0).multiplyScalar(0.5);
-            v1 = new $3Dmol.Vector3().subVectors(p3, p1).multiplyScalar(0.5);
-            if (p2.skip) continue;
-
-            for ( var j = 0; j < DIV; j++) {
-                var t = 1.0 / DIV * j;
-                var x = p1.x + t * v0.x + t * t * 
-                        (-3 * p1.x + 3 * p2.x - 2 * v0.x - v1.x) + t * t * t *
-                        (2 * p1.x - 2 * p2.x + v0.x + v1.x);
-                var y = p1.y + t * v0.y + t * t * 
-                        (-3 * p1.y + 3 * p2.y - 2 * v0.y - v1.y) + t * t * t *
-                        (2 * p1.y - 2 * p2.y + v0.y + v1.y);
-                var z = p1.z + t * v0.z + t * t * 
-                        (-3 * p1.z + 3 * p2.z - 2 * v0.z - v1.z) + t * t * t * 
-                        (2 * p1.z - 2 * p2.z + v0.z + v1.z);
-                        
-                var pt = new $3Dmol.Vector3(x, y, z);
-                
-                var atomIndex = Math.floor( (ret.length+2) / DIV);
-                
-                if (_points[atomIndex] !== undefined && _points[atomIndex].atom !== undefined)
-                    pt.atom = _points[atomIndex].atom;
-                    
-                ret.push(pt);
-            }
-        }
-        ret.push(points[points.length - 1]);
-        return ret;
-    };
-
-    var drawThinStrip = function(group, p1, p2, colors, div, opacity) {
-    
-        var geo = new $3Dmol.Geometry(true);       
-        var offset, vertoffset;
-        var color, colori;
-
-        
-        for ( var i = 0, lim = p1.length; i < lim; i++) {
-            
-            colori = Math.round(i*(colors.length-1)/lim);
-            color = $3Dmol.CC.color(colors[colori]);
-           
-            geoGroup = geo.updateGeoGroup(2);
-            var vertexArray = geoGroup.vertexArray;
-            var colorArray = geoGroup.colorArray;
-            var faceArray = geoGroup.faceArray;
-            offset = geoGroup.vertices; vertoffset = offset*3;
-            
-            vertexArray[vertoffset] = p1[i].x;
-            vertexArray[vertoffset+1] = p1[i].y;
-            vertexArray[vertoffset+2] = p1[i].z;
-            
-            vertexArray[vertoffset+3] = p2[i].x;
-            vertexArray[vertoffset+4] = p2[i].y;
-            vertexArray[vertoffset+5] = p2[i].z;
-            
-            for (var j = 0; j < 6; ++j)
-            {
-                colorArray[vertoffset+3*j] = color.r;
-                colorArray[vertoffset+1+3*j] = color.g;
-                colorArray[vertoffset+2+3*j] = color.b;
-            }
-           
-            if (i > 0) {
-                var faces = [offset, offset + 1, offset - 1, offset - 2];
-                var faceoffset = geoGroup.faceidx;
-                
-                faceArray[faceoffset] = faces[0]; faceArray[faceoffset+1] = faces[1]; faceArray[faceoffset+2] = faces[3];
-                faceArray[faceoffset+3] = faces[1]; faceArray[faceoffset+4] = faces[2]; faceArray[faceoffset+5] = faces[3];
-
-                geoGroup.faceidx += 6;
-            }
-            
-            geoGroup.vertices += 2;
-        }
-        
-        geo.initTypedArrays();
-        geo.setUpNormals();
-        
-        var material = new $3Dmol.MeshDoubleLambertMaterial();
-        if(typeof(opacity) === 'number' && opacity >= 0 && opacity < 1) {
-            material.transparent = true;
-            material.opacity = opacity;
-        }
-        material.vertexColors = $3Dmol.FaceColors;
-        var mesh = new $3Dmol.Mesh(geo, material);
-
-        group.add(mesh);
-    };
-
-    var drawShapeStrip = function(group, points, colors, div, thickness, opacity, shape) {
-
-        // points is a 2D array, dimensionality given by [num = cross-sectional resolution][len = length of strip]
-        var i, j, num, len;
-        num = points.length;
-        if (num < 2 || points[0].length < 2) return;
-
-        div = div || axisDIV;
-        for (i = 0; i < num; i++) { // spline to generate greater length-wise resolution
-            points[i] = subdivide(points[i], div)
-        }
-        len = points[0].length;
-
-        if (!thickness) // if thickness is 0, we can use a smaller geometry than this function generates
-            return drawThinStrip(group, points[0], points[num-1], colors, div, opacity);
-
-
-        var geo = new $3Dmol.Geometry(true);
-        var axis, cs_shape, cs_bottom, cs_top, last_cs_bottom, last_cs_top;
-
-        // cache the available cross-sectional shapes
-        var cs_ellipse = [], cs_rectangle = [], cs_parabola = [];
-        for (j=0; j < num; j++) {
-            cs_ellipse.push(0.25 + 1.5*Math.sqrt((num-1)*j - Math.pow(j, 2))/(num-1));
-            cs_rectangle.push(0.5);
-            cs_parabola.push(2*(Math.pow(j/num, 2) - j/num) + 0.6);
-        }
-
-        /* face_refs array is used to generate faces from vertexArray iteratively.
-           As we move through each cross-sectional segment of points, we draw lateral
-           faces backwards to the previous cross-sectional segment.
-
-           To correctly identify the points needed to make each face we use this
-           array as a lookup table for the relative indices of each needed point
-           in the vertices array.
-
-           4 points are used to create 2 faces.
-        */
-
-        var face_refs = [];
-        for (j = 0; j < num*2-1; j++) {
-            /* [curr vertex in curr cross-section,
-                next vertex in curr cross-section,
-                next vertex in prev cross-section,
-                curr vertex in prev cross-section] */
-            face_refs[j] = [j, j+1, j+1-2*num, j-2*num];
-        }
-        // last face is different. easier to conceptualize this by drawing a diagram
-        face_refs[num*2-1] = [j, j+1-2*num, j+1-4*num, j-2*num];
-
-                
-        var v_offset, va_offset, f_offset;
-        var currentAtom, lastAtom
-        var color, colori;
-        var geoGroup = geo.updateGeoGroup(2*num*len); // ensure vertex capacity
-        
-        for (i = 0; i < len; i++) {
-        
-            colori = Math.round(i*(colors.length-1)/len);
-            color = $3Dmol.CC.color(colors[colori]);
-            
-            last_cs_bottom = cs_bottom;
-            last_cs_top = cs_top;
-            cs_bottom = [];
-            cs_top = [];
-            axis = [];
-
-            if (points[0][i].atom !== undefined) // TODO better edge case handling
-            {
-                currentAtom = points[0][i].atom;
-                if (shape === "oval")
-                    cs_shape = cs_ellipse;
-                else if (shape === "rectangle")
-                    cs_shape = cs_rectangle;
-                else if (shape === "parabola")
-                    cs_shape = cs_parabola;
-            }
-            if (!cs_shape) cs_shape = cs_rectangle;
-
-            
-            // calculate thickness at each width point, from cross-sectional shape
-            var toNext, toSide;
-            for (j = 0; j < num; j++)
-            {
-                if (i < len-1)
-                    toNext = points[j][i+1].clone().sub(points[j][i]);
-                else
-                    toNext = points[j][i-1].clone().sub(points[j][i]).negate();
-
-                if (j < num-1)
-                    toSide = points[j+1][i].clone().sub(points[j][i]);
-                else
-                    toSide = points[j-1][i].clone().sub(points[j][i]).negate();
-
-                axis[j] = toSide.cross(toNext).normalize().multiplyScalar(thickness*cs_shape[j]);
-            }
-
-            // generate vertices by applying cross-sectional shape thickness to input points
-            for (j = 0; j < num; j++)
-                cs_bottom[j] = points[j][i].clone().add(axis[j].clone().negate());
-            for (j = 0; j < num; j++)
-                cs_top[j] = points[j][i].clone().add(axis[j]);
-            
-
-            /* Until this point the vertices have been dealt with as $3Dmol.Vector3() objects,
-               but we need to serialize them into the geoGroup.vertexArray, where every three
-               indices represents the next vertex. The colorArray is analogous.
-
-               In the following for-loops, j iterates through VERTICES so we need to index
-               them in vertexArray by 3*j + either 0, 1, or 2 for xyz or rgb component.
-            */
-
-            var vertexArray = geoGroup.vertexArray;
-            var colorArray = geoGroup.colorArray;
-            var faceArray = geoGroup.faceArray;
-            v_offset = geoGroup.vertices; va_offset = v_offset*3; // in case geoGroup already contains vertices
-
-            // bottom edge of cross-section, vertices [0, num)
-            for (j = 0; j < num; j++) {
-                vertexArray[va_offset + 3*j + 0] = cs_bottom[j].x;
-                vertexArray[va_offset + 3*j + 1] = cs_bottom[j].y;
-                vertexArray[va_offset + 3*j + 2] = cs_bottom[j].z;
-            }
-
-            // top edge of cross-section, vertices [num, 2*num)
-            // add these backwards, so that each cross-section's vertices are added sequentially to vertexArray
-            for (j = 0; j < num; j++) {
-                vertexArray[va_offset + 3*j + 0 + 3*num] = cs_top[num-1-j].x;
-                vertexArray[va_offset + 3*j + 1 + 3*num] = cs_top[num-1-j].y;
-                vertexArray[va_offset + 3*j + 2 + 3*num] = cs_top[num-1-j].z;
-            }
-            
-            for (j = 0; j < 2*num; ++j) {
-                colorArray[va_offset + 3*j + 0] = color.r;
-                colorArray[va_offset + 3*j + 1] = color.g;
-                colorArray[va_offset + 3*j + 2] = color.b;
-            }
-            
-            if (i > 0) {
-             
-                for (j = 0; j < num*2; j++) {
-                
-                    // get VERTEX indices of the 4 points of a rectangular face (as opposed to literal vertexArray indices)
-                    var face = [v_offset + face_refs[j][0],
-                                v_offset + face_refs[j][1],
-                                v_offset + face_refs[j][2],
-                                v_offset + face_refs[j][3]];
-                    
-                    f_offset = geoGroup.faceidx;    
-                    
-                    // need 2 triangles to draw a face between 4 points
-                    faceArray[f_offset]   = face[0];
-                    faceArray[f_offset+1] = face[1]; 
-                    faceArray[f_offset+2] = face[3];
-
-                    faceArray[f_offset+3] = face[1];
-                    faceArray[f_offset+4] = face[2];
-                    faceArray[f_offset+5] = face[3];
-                    
-                    geoGroup.faceidx += 6;
-
-                    // TODO implement clickable the right way. midpoints of strand between consecutive atoms
-                }
-                    
-                if (currentAtom.clickable)
-                {
-                    var faces = [];
-
-                    faces.push(new $3Dmol.Triangle(last_cs_bottom[0], cs_bottom[0], cs_bottom[num-1]));
-                    faces.push(new $3Dmol.Triangle(last_cs_bottom[0], cs_bottom[num-1], last_cs_bottom[num-1]));
-
-                    faces.push(new $3Dmol.Triangle(last_cs_bottom[num-1], cs_bottom[num-1], cs_top[num-1]));
-                    faces.push(new $3Dmol.Triangle(last_cs_bottom[num-1], cs_top[num-1], last_cs_top[num-1]));
-
-                    faces.push(new $3Dmol.Triangle(cs_top[0], last_cs_top[0], last_cs_top[num-1]));
-                    faces.push(new $3Dmol.Triangle(cs_top[num-1], cs_top[0], last_cs_top[num-1]));
-
-                    faces.push(new $3Dmol.Triangle(cs_bottom[0], last_cs_bottom[0], last_cs_top[0]));
-                    faces.push(new $3Dmol.Triangle(cs_top[0], cs_bottom[0], last_cs_top[0]));
-
-                    for (j in faces)
-                    {
-                        currentAtom.intersectionShape.triangle.push(faces[j]);
-                        
-                    }
-                }
-
-            }
-            
-            geoGroup.vertices += 2*num;
-            lastAtom = currentAtom;
-        }
-
-        // for terminal faces
-        var vertexArray = geoGroup.vertexArray;
-        var colorArray = geoGroup.colorArray;
-        var faceArray = geoGroup.faceArray;
-        v_offset = geoGroup.vertices; va_offset = v_offset*3; f_offset = geoGroup.faceidx;
-
-        for (i = 0; i<num-1; i++) // "rear" face
-        {
-            var face = [i, i+1, 2*num-2-i, 2*num-1-i];
-
-            f_offset = geoGroup.faceidx;
-
-            faceArray[f_offset]   = face[0];
-            faceArray[f_offset+1] = face[1]; 
-            faceArray[f_offset+2] = face[3];
-
-            faceArray[f_offset+3] = face[1];
-            faceArray[f_offset+4] = face[2];
-            faceArray[f_offset+5] = face[3];
-
-            geoGroup.faceidx += 6;
-        }
-
-        for (i = 0; i<num-1; i++) // "front" face
-        {
-            var face = [v_offset-1-i, v_offset-2-i, v_offset-2*num+i+1, v_offset-2*num+i];
-
-            f_offset = geoGroup.faceidx;
-
-            faceArray[f_offset]   = face[0];
-            faceArray[f_offset+1] = face[1]; 
-            faceArray[f_offset+2] = face[3];
-
-            faceArray[f_offset+3] = face[1];
-            faceArray[f_offset+4] = face[2];
-            faceArray[f_offset+5] = face[3];
-
-            geoGroup.faceidx += 6;
-        }
-        
-        geo.initTypedArrays();
-        geo.setUpNormals();
-        
-        var material = new $3Dmol.MeshDoubleLambertMaterial();
-        material.vertexColors = $3Dmol.FaceColors;
-        if (typeof(opacity) === 'number' && opacity >= 0 && opacity < 1) {
-            material.transparent = true;
-            material.opacity = opacity;
-        }
-        var mesh = new $3Dmol.Mesh(geo, material);
-        group.add(mesh);
-    };
-
-    var drawPlainStrip = function(group, points, colors, div, thickness, opacity) {
-        if ((points.length) < 2)
-            return;
-
-        var p1, p2;
-        p1 = points[0];
-        p2 = points[points.length-1];
-
-        div = div || axisDIV;
-        p1 = subdivide(p1, div);
-        p2 = subdivide(p2, div);
-        if (!thickness)
-            return drawThinStrip(group, p1, p2, colors, div, opacity);
-
-        var geo = new $3Dmol.Geometry(true);
-        
-        //var vs = geo.vertices, fs = geo.faces;
-        var vs = [], fs = [];
-        var axis, p1v, p2v, a1v, a2v;
-        
-        var faces = [ [ 0, 2, -6, -8 ],
-                      [ -4, -2, 6, 4 ],
-                      [ 7, -1, -5, 3 ],
-                      [ -3, 5, 1, -7 ] ];
-                
-        var offset, vertoffset, faceoffset;
-        var color, colori;
-        var currentAtom, lastAtom;
-        var i, lim, j;
-        var face1, face2, face3;
-        var geoGroup;
-        
-        for (i = 0, lim = p1.length; i < lim; i++) {
-        
-            colori = Math.round(i*(colors.length-1)/lim);
-            color = $3Dmol.CC.color(colors[colori]);
-            
-            vs.push(p1v = p1[i]); // 0
-            vs.push(p1v);         // 1
-            vs.push(p2v = p2[i]); // 2
-            vs.push(p2v);         // 3
-            if (i < lim - 1) {
-                var toNext = p1[i + 1].clone().sub(p1[i]);
-                var toSide = p2[i].clone().sub(p1[i]);
-                axis = toSide.cross(toNext).normalize().multiplyScalar(
-                        thickness);
-            }
-            vs.push(a1v = p1[i].clone().add(axis)); // 4
-            vs.push(a1v); // 5
-            vs.push(a2v = p2[i].clone().add(axis)); // 6
-            vs.push(a2v); // 7
-            
-            if (p1v.atom !== undefined)
-                currentAtom = p1v.atom;
-            
-            geoGroup = geo.updateGeoGroup(8);
-            var vertexArray = geoGroup.vertexArray;
-            var colorArray = geoGroup.colorArray;
-            var faceArray = geoGroup.faceArray;
-            offset = geoGroup.vertices; vertoffset = offset*3;
-            
-            vertexArray[vertoffset]    = p1v.x; vertexArray[vertoffset+1]  = p1v.y; vertexArray[vertoffset+2]  = p1v.z;
-            vertexArray[vertoffset+3]  = p1v.x; vertexArray[vertoffset+4]  = p1v.y; vertexArray[vertoffset+5]  = p1v.z;
-            vertexArray[vertoffset+6]  = p2v.x; vertexArray[vertoffset+7]  = p2v.y; vertexArray[vertoffset+8]  = p2v.z;
-            vertexArray[vertoffset+9]  = p2v.x; vertexArray[vertoffset+10] = p2v.y; vertexArray[vertoffset+11] = p2v.z;
-            vertexArray[vertoffset+12] = a1v.x; vertexArray[vertoffset+13] = a1v.y; vertexArray[vertoffset+14] = a1v.z;
-            vertexArray[vertoffset+15] = a1v.x; vertexArray[vertoffset+16] = a1v.y; vertexArray[vertoffset+17] = a1v.z;
-            vertexArray[vertoffset+18] = a2v.x; vertexArray[vertoffset+19] = a2v.y; vertexArray[vertoffset+20] = a2v.z;
-            vertexArray[vertoffset+21] = a2v.x; vertexArray[vertoffset+22] = a2v.y; vertexArray[vertoffset+23] = a2v.z;
-            
-            for (j = 0; j < 8; ++j) {                
-                colorArray[vertoffset+3*j] = color.r; colorArray[vertoffset+1+3*j] = color.g; colorArray[vertoffset+2+3*j] = color.b;
-            }
-            
-            if (i > 0) {
-             
-                //both points have distinct atoms
-                var diffAtoms = ((lastAtom !== undefined && currentAtom !== undefined) && lastAtom.serial !== currentAtom.serial);
-                
-                for (j = 0; j < 4; j++ ) {
-                
-                    var face = [offset + faces[j][0], offset + faces[j][1], offset + faces[j][2], offset + faces[j][3]];
-                    
-                    faceoffset = geoGroup.faceidx;    
-                    
-                    faceArray[faceoffset] = face[0]; faceArray[faceoffset+1] = face[1]; faceArray[faceoffset+2] = face[3];
-                    faceArray[faceoffset+3] = face[1]; faceArray[faceoffset+4] = face[2]; faceArray[faceoffset+5] = face[3];
-                    
-                    geoGroup.faceidx += 6;
-                    
-                    if (currentAtom.clickable || lastAtom.clickable) {
-                        
-                        var p1a = vs[face[3]].clone(), p1b = vs[face[0]].clone(),
-                            p2a = vs[face[2]].clone(), p2b = vs[face[1]].clone();
-                        
-                        p1a.atom = vs[face[3]].atom || null; //should be same
-                        p2a.atom = vs[face[2]].atom || null; 
-                        
-                        
-                        p1b.atom = vs[face[0]].atom || null; //should be same                      
-                        p2b.atom = vs[face[1]].atom || null; 
-                        
-                        if (diffAtoms) {
-                            var m1 = p1a.clone().add(p1b).multiplyScalar(0.5);
-                            var m2 = p2a.clone().add(p2b).multiplyScalar(0.5);
-                            var m = p1a.clone().add(p2b).multiplyScalar(0.5);
-                            
-                            if (j % 2 === 0)
-                            {
-                                if (lastAtom.clickable) {
-                                    face1 = new $3Dmol.Triangle(m1, m, p1a);
-                                    face2 = new $3Dmol.Triangle(m2, p2a, m);
-                                    face3 = new $3Dmol.Triangle(m, p2a, p1a);
-                                    lastAtom.intersectionShape.triangle.push(face1);
-                                    lastAtom.intersectionShape.triangle.push(face2);
-                                    lastAtom.intersectionShape.triangle.push(face3);
-                                }
-                                
-                                if (currentAtom.clickable) {
-                                    face1 = new $3Dmol.Triangle(p1b, p2b, m);
-                                    face2 = new $3Dmol.Triangle(p2b, m2, m);
-                                    face3 = new $3Dmol.Triangle(p1b, m, m1);
-                                    currentAtom.intersectionShape.triangle.push(face1);
-                                    currentAtom.intersectionShape.triangle.push(face2);
-                                    currentAtom.intersectionShape.triangle.push(face3);
-                                }
-                            }
-                            else {
-                                if (currentAtom.clickable) {
-                                    face1 = new $3Dmol.Triangle(m1, m, p1a);
-                                    face2 = new $3Dmol.Triangle(m2, p2a, m);
-                                    face3 = new $3Dmol.Triangle(m, p2a, p1a);
-                                    currentAtom.intersectionShape.triangle.push(face1);
-                                    currentAtom.intersectionShape.triangle.push(face2);
-                                    currentAtom.intersectionShape.triangle.push(face3);
-                                }
-                                
-                                if (lastAtom.clickable) {
-                                    face1 = new $3Dmol.Triangle(p1b, p2b, m);
-                                    face2 = new $3Dmol.Triangle(p2b, m2, m);
-                                    face3 = new $3Dmol.Triangle(p1b, m, m1);
-                                    lastAtom.intersectionShape.triangle.push(face1);
-                                    lastAtom.intersectionShape.triangle.push(face2);
-                                    lastAtom.intersectionShape.triangle.push(face3);
-                                }                          
-                            }
-                            
-                        }
-                        
-                        //face for single atom
-                        else if (currentAtom.clickable) {
-                            face1 = new $3Dmol.Triangle(p1b, p2b, p1a);
-                            face2 = new $3Dmol.Triangle(p2b, p2a, p1a);
-                            currentAtom.intersectionShape.triangle.push(face1);
-                            currentAtom.intersectionShape.triangle.push(face2);
-                        }
-                        
-                    }
-                    
-                }
-            }
-            
-            geoGroup.vertices += 8;
-            lastAtom = currentAtom;
-        }
-        
-
-        var vsize = vs.length - 8; // Cap
-        
-        geoGroup = geo.updateGeoGroup(8);
-        var vertexArray = geoGroup.vertexArray;
-        var colorArray = geoGroup.colorArray;
-        var faceArray = geoGroup.faceArray;
-        offset = geoGroup.vertices; vertoffset = offset*3; faceoffset = geoGroup.faceidx;
-        
-        for (i = 0; i < 4; i++) {
-            vs.push(vs[i * 2]);
-            vs.push(vs[vsize + i * 2]);
-            
-            var v1 = vs[i * 2], v2 = vs[vsize + i * 2];
-            
-            vertexArray[vertoffset+6*i] = v1.x; vertexArray[vertoffset+1+6*i] = v1.y; vertexArray[vertoffset+2+6*i] = v1.z;
-            vertexArray[vertoffset+3+6*i] = v2.x; vertexArray[vertoffset+4+6*i] = v2.y; vertexArray[vertoffset+5+6*i] = v2.z;
-            
-            colorArray[vertoffset+6*i] = color.r; colorArray[vertoffset+1+6*i] = color.g; colorArray[vertoffset+2+6*i] = color.b;
-            colorArray[vertoffset+3+6*i] = color.r; colorArray[vertoffset+4+6*i] = color.g; colorArray[vertoffset+5+6*i] = color.b;
-
-        }
-        
-        vsize += 8;
-                
-        face1 = [offset, offset + 2, offset + 6, offset + 4];
-        face2 = [offset + 1, offset + 5, offset + 7, offset + 3];
-        
-        faceArray[faceoffset] = face1[0]; faceArray[faceoffset+1] = face1[1]; faceArray[faceoffset+2] = face1[3];
-        faceArray[faceoffset+3] = face1[1]; faceArray[faceoffset+4] = face1[2]; faceArray[faceoffset+5] = face1[3];
-        faceArray[faceoffset+6] = face2[0]; faceArray[faceoffset+7] = face2[1]; faceArray[faceoffset+8] = face2[3];
-        faceArray[faceoffset+9] = face2[1]; faceArray[faceoffset+10] = face2[2]; faceArray[faceoffset+11] = face2[3];
-        
-        geoGroup.faceidx += 12;
-        geoGroup.vertices += 8;
-        
-        //TODO: Add intersection planes for caps
-        
-        geo.initTypedArrays();
-        geo.setUpNormals();
-        
-                // HalfEdgeRec used to store adjacency info of mesh
-        var HalfEdge=function(vertIdx){
-            this.vert=vertIdx; // Vertex index at the end of this half-edge
-            this.twin=null;    // Oppositely oriented adjacent half-edge
-            this.next=null;    //Next half-edge around the face
-        };
-        
-        var computeAdjacency=function(faces,faceCount,vertCount){
-            //all pieces of the half-edge data structure
-            edges=[];
-            
-            // a hash table to hold the adjaceney info
-            // - Keys are pairs of vertex indices
-            // - Values are pointers to half-edge
-            var edgeTable={};
-            var len=0;
-            
-            //Plow through faces and fill all half-edge info except twin pointers:
-            for(var i=0;i<faceCount;i+=3){
-                var A=faces[i];
-                var B=faces[i+1];
-                var C=faces[i+2];
-               // console.log("A="+A+ " B="+ B+ " C="+C);
-                
-                //create the half-edge that goes from C to A
-                var CA=new HalfEdge(A);
-                edges.push(CA);
-                //create the half-edge that goes from A to B
-                var AB=new HalfEdge(B);
-                edges.push(AB);
-                //create the half-edge that goes from B to C
-                var BC=new HalfEdge(C);
-                edges.push(BC);
-                
-                CA.next=AB;
-                AB.next=BC;
-                BC.next=CA;
-                
-                edgeTable[C|(A<<16)]=CA; 
-                edgeTable[A|(B<<16)]=AB; 
-                edgeTable[B|(C<<16)]=BC;
-            }
-            
-            //verify that the mesh is clean
-            for(var key in edgeTable){
-                if(edgeTable.hasOwnProperty(key)){
-                    len++;
-                }
-            }
-            if(len!=faceCount*3){
-                console.warn("Bad mesh: duplicated edges or inconsistent winding.len="+len+" faceCount="+faceCount+" vertCount="+vertCount);
-            }
-            
-            //Populate the twin pointers by iterating over the hash table
-            var boundaryCount=0;
-            for(var key in edgeTable){
-                if(edgeTable.hasOwnProperty(key)){
-                    var twinKey=((key&0xffff)<<16)|(key>>16);
-                    if(edgeTable.hasOwnProperty(twinKey)){
-                        edgeTable[key].twin=edgeTable[twinKey];
-                        edgeTable[twinKey].twin=edgeTable[key];
-                    }else{
-                        boundaryCount+=1;
-                    }
-                }
-            }
-            
-            var ret=new Uint16Array(faceCount*6);
-            // Now that we have a half-edge structure, it's easy to create adjacency info for WebGL
-            if(boundaryCount>0){
-                console.log("Mesh is not watertight. Contains "+boundaryCount +" edges");
-                
-                for(var i=0;i<faceCount;i+=3){
-                    ret[i*2+0]=edges[i+2].vert;
-                    ret[i*2+1]=edges[i+0].twin==null?ret[i*2+0]:edges[i+0].twin.next.vert;
-                    ret[i*2+2]=edges[i+0].vert;
-                    ret[i*2+3]=edges[i+1].twin==null?ret[i*2+1]:edges[i+1].twin.next.vert;                  
-                    ret[i*2+4]=edges[i+1].vert;
-                    ret[i*2+5]=edges[i+2].twin==null?ret[i*2+2]:edges[i+2].twin.next.vert;
-                }
-            }
-            else{
-                for(var i=0;i<faceCount;i+=3){
-                    ret[i*2+0]=edges[i+2].vert;
-                    ret[i*2+1]=edges[i+0].twin.next.vert;
-                    ret[i*2+2]=edges[i+0].vert;
-                    ret[i*2+3]=edges[i+1].twin.next.vert;                   
-                    ret[i*2+4]=edges[i+1].vert;
-                    ret[i*2+5]=edges[i+2].twin.next.vert;
-                } 
-            }
-            
-            return ret;
-        };
-        
-        //geoGroup.adjFaceArray = computeAdjacency(faceArray,faceArray.length,offset);
-        
-        var material = new $3Dmol.MeshDoubleLambertMaterial();
-        material.vertexColors = $3Dmol.FaceColors;
-        if(typeof(opacity) === 'number' && opacity >= 0 && opacity < 1) {
-            material.transparent = true;
-            material.opacity = opacity;
-        }
-        var mesh = new $3Dmol.Mesh(geo, material);
-        group.add(mesh);   
-    };
-
-    //TODO: Need to update this (will we ever use this?)
-    var drawSmoothCurve = function(group, _points, width, colors, div) {
-        if (_points.length === 0)
-            return;
-
-        div = (div === undefined) ? 5 : div;
-
-        var geo = new $3Dmol.Geometry();
-        var points = subdivide(_points, div);
-                /*
-        for ( var i = 0; i < points.length; i++) {
-            geo.vertices.push(points[i]);
-            geo.colors.push($3Dmol.color(colors[(i == 0) ? 0 : Math.round((i - 1)
-                    / div)]));
-        }
-                */
-        var lineMaterial = new $3Dmol.LineBasicMaterial({
-            linewidth : width
-        });
-        lineMaterial.vertexColors = true;
-        var line = new $3Dmol.Line(geo, lineMaterial);
-        line.type = $3Dmol.LineStrip;
-        group.add(line);
-    };
-
-    var drawStrip = function(group, points, colors, div, thickness, opacity, shape)
-    {    
-        if (!shape || shape === "default")
-            shape = "rectangle";
-        if(shape === 'edged')
-            drawPlainStrip(group, points, colors, div, thickness, opacity);
-        else if (shape === "rectangle" || shape === "oval" || shape === "parabola")
-            drawShapeStrip(group, points, colors, div, thickness, opacity, shape);
-    }
-
-    // check if given atom is an alpha carbon
-    var isAlphaCarbon = function(atom)
-    {
-        return atom && atom.elem === "C" && atom.atom === "CA"; // note that calcium is also CA
-    }
-
-    // check whether two atoms are members of the same residue or subsequent, connected residues (a before b)
-    var inConnectedResidues = function(a, b)
-    {
-        return a && b && a.chain === b.chain && a.reschain === b.reschain &&
-                (a.resi === b.resi || a.resi === b.resi-1)
-    }
-
-    var drawCartoon = function(group, atomList, gradientScheme, fill, doNotSmoothen, num, div)
-    {
-        num = num || defaultNum;
-        div = div || defaultDiv;
-
-                        //  proteins    na backbone  na terminus                  nucleobases
-        var cartoonAtoms = ["CA", "O",  "P", "OP2",  "O5'", "O3'", "C5'", "C2'",  "N1", "N3"];
-        var purResns = ["DA", "DG", "A", "G"];
-        var pyrResns = ["DT", "DC", "U", "C"];
-        var naResns  =  purResns.concat(pyrResns);
-
-        var geo, cartoon, prev, curr, next, currColor, nextColor, thickness, i, nextResAtom, arrow;
-        var backbonePt, orientPt, prevOrientPt, terminalPt, termOrientPt, baseStartPt, baseEndPt;
-        var tubeStart, tubeEnd, drawingTube;
-        var traceGeo = null;
-        var colors = [];
-        var points = [];
-        for (var i = 0; i < num; i++)
-            points[i] = [];
-
-        // first determine where beta sheet arrows and alpha helix tubes belong
-        var inSheet = false;
-        var inHelix = false;
-        i = 0;
-        for (i in atomList)
-        {
-            next = atomList[i];
-            if (next.elem === 'C' && next.atom === 'CA')
-            {
-                var connected = inConnectedResidues(curr, next);
-
-                // last two residues in a beta sheet become arrowhead
-                if (connected && next.ss === "s")
-                {
-                    inSheet = true;
-                }
-                else if (inSheet)
-                {   
-                    if (curr && prev && curr.style.cartoon.arrows && prev.style.cartoon.arrows)
-                    {
-                        curr.ss = "arrow end";
-                        prev.ss = "arrow start";
-                    }
-                    inSheet = false; 
-                }
-
-                // first and last residues in a helix are used to draw tube
-                if (connected && curr.ss === "h")
-                {
-                    if (!inHelix && next.style.cartoon.tubes) next.ss = "tube start";
-                    inHelix = true;
-                }
-                else if (inHelix && curr.ss !== "tube start")
-                {
-                    if (prev && prev.style.cartoon.tubes) prev.ss = "tube end";
-                    inHelix = false;
-                }
-                prev = curr;
-                curr = next;
-            }
-        }
-
-        // then accumulate points
-        curr = undefined;
-        for (i in atomList)
-        {
-            next = atomList[i];
-            
-            if (next === undefined || $.inArray(next.atom, cartoonAtoms) === -1 || next.hetflag)
-                continue; // skip array holes, heteroatoms, and atoms not involved in cartoon drawing
-
-            var inNucleicAcid = ($.inArray(next.resn.trim(), naResns) != -1)
-
-            // determine cartoon style
-            cartoon = next.style.cartoon;
-            if (cartoon.style === "trace") // draw cylinders connecting consecutive 'backbone' atoms
-            {
-                /* "trace" style just draws cylinders between consecutive 'backbone' atoms,
-                    such as alpha carbon for polypeptides and phosphorus for DNA. */
-
-                if (!traceGeo) traceGeo = new $3Dmol.Geometry(true);
-
-                if (next.elem === 'C' && next.atom === 'CA' || inNucleicAcid && next.atom === "P")
-                {
-                    // determine cylinder color
-                    if (gradientScheme && cartoon.color === 'spectrum')
-                        nextColor = gradientScheme.valueToHex(next.resi, gradientScheme.range());
-                    else
-                        nextColor = $3Dmol.getColorFromStyle(next, cartoon).getHex();
-                    colors.push(nextColor);
-
-                    // determine cylinder thickness
-                    if ($.isNumeric(cartoon.thickness))
-                        thickness = cartoon.thickness;
-                    else
-                        thickness = defaultThickness;
-
-                    /* do not draw connections between different chains, but ignore
-                       differences in reschain to properly support CA-only files */
-                    if (curr && curr.chain === next.chain && curr.resi + 1 === next.resi)
-                    {
-                        // if both atoms are same color, draw single cylinder
-                        if (nextColor == currColor)
-                        {
-                            var color = $3Dmol.CC.color(nextColor);
-                            $3Dmol.GLDraw.drawCylinder(traceGeo, curr, next, thickness, color, 2, 2);
-                        }
-
-                        else // otherwise draw cylinders for each color (split down the middle)
-                        {
-                            var midpoint = new $3Dmol.Vector3().addVectors(curr, next).multiplyScalar(0.5);
-                            var color1 = $3Dmol.CC.color(currColor);
-                            var color2 = $3Dmol.CC.color(nextColor);
-                            $3Dmol.GLDraw.drawCylinder(traceGeo, curr, midpoint, thickness, color1, 2, 0);
-                            $3Dmol.GLDraw.drawCylinder(traceGeo, midpoint, next, thickness, color2, 0, 2);
-                        } // note that an atom object can be duck-typed as a $3Dmol.Vector3 in this case
-                    }
-
-                    if (curr && traceGeo && (curr.style.cartoon && curr.style.cartoon.style != "trace"
-                        || curr.chain != next.chain))
-                    {
-                        var traceMaterial = new $3Dmol.MeshDoubleLambertMaterial();
-                        traceMaterial.vertexColors = $3Dmol.FaceColors;
-                        if ( typeof(traceGeo.opacity) === "number" && traceGeo.opacity >= 0 && traceGeo.opacity < 1) {
-                            traceMaterial.transparent = true;
-                            traceMaterial.opacity = traceGeo.opacity;
-                            delete traceGeo.opacity;
-                        }
-                        var traceMesh = new $3Dmol.Mesh(traceGeo, traceMaterial);
-                        group.add(traceMesh);
-                        traceGeo = null;
-                    } else if (curr) // make sure whole chain is same opacity
-                    {
-                        if (traceGeo.opacity && curr.style.cartoon.opacity)
-                        {
-                            if (traceGeo.opacity != curr.style.cartoon.opacity)
-                            {
-                                console.log("Warning: a trace-style chain's opacity is ambiguous");
-                                traceGeo.opacity = 1;
-                            }
-                        }
-                        else traceGeo.opacity = parseFloat(curr.style.cartoon.opacity) || 1;
-                    }
-
-                    curr = next;
-                    currColor = nextColor;
-                }
-            }
-
-            else // draw default-style cartoons based on secondary structure
-            {
-                if (!geo) geo = new $3Dmol.Geometry(true);
-
-                // draw backbone through these atoms
-                if (isAlphaCarbon(next) ||
-                    inNucleicAcid && (next.atom === "P" || next.atom === "O5'"))
-                {
-                    if (drawingTube)
-                    {
-                        if (next.ss === "tube end")
-                        {
-                            drawingTube = false;
-                            tubeEnd = new $3Dmol.Vector3(next.x, next.y, next.z);
-                            $3Dmol.GLDraw.drawCylinder(geo, tubeStart, tubeEnd, 2, $3Dmol.CC.color(currColor), 1, 1);
-                            next.ss = "h";
-
-                        }
-                        else continue; // don't accumulate strand points while in the middle of drawing a tube
-                    }
-
-                    // end of a chain of connected residues (of same style)
-                    if (curr && (!inConnectedResidues(curr, next) || curr.style.cartoon.style !== next.style.cartoon.style ||
-                        curr.ss === "tube start"))
-                    { 
-                        if (curr.ss === "tube start")
-                        {
-                            drawingTube = true;
-                            tubeStart = new $3Dmol.Vector3(curr.x, curr.y, curr.z);
-                            curr.ss = "h";
-                        }
-
-                        if (baseEndPt) // draw the last base if it's a NA chain
-                        {
-                            if (terminalPt)
-                                baseStartPt = new $3Dmol.Vector3().addVectors(curr, terminalPt).multiplyScalar(0.5);
-                            else
-                                baseStartPt = new $3Dmol.Vector3(curr.x, curr.y, curr.z);
-
-                            $3Dmol.GLDraw.drawCylinder(geo, baseStartPt, baseEndPt, 0.4, $3Dmol.CC.color(baseEndPt.color), 0, 2);
-                            arrow = addBackbonePoints(points, num, !doNotSmoothen, terminalPt, termOrientPt, prevOrientPt, curr, atomList, i);
-                            colors.push(nextColor);
-                            if (arrow) colors.push(nextColor);
-                            
-                            baseStartPt = null;
-                            baseEndPt = null;
-                        }
-
-                        // draw accumulated strand points
-                        for (i = 0; !thickness && i < num; i++)
-                            drawSmoothCurve(group, points[i], 1, colors, div, points.opacity);
-                        if (fill && points[0].length > 0) 
-                            drawStrip(group, points, colors, div, thickness, points.opacity, points.style);
-                        
-                        if (geo != null && geo.vertices > 0)
-                        {
-                            var cartoonMaterial = new $3Dmol.MeshDoubleLambertMaterial();
-                            cartoonMaterial.vertexColors = $3Dmol.FaceColors;
-                            if (typeof(points.opacity) === "number" && points.opacity >= 0 && points.opacity < 1) {
-                                cartoonMaterial.transparent = true;
-                                cartoonMaterial.opacity = points.opacity;
-                            }
-                            var cartoonMesh = new $3Dmol.Mesh(geo, cartoonMaterial);
-                            group.add(cartoonMesh);
-                            geo = null;
-                        }
-
-                        // clear arrays for points and colors
-                        points = [];
-                        for (i = 0; i < num; i++)
-                            points[i] = [];
-                        colors = [];
-                    }
-
-                    // reached next residue (potentially the first residue)
-                    if (curr === undefined || curr.resi != next.resi)
-                    {
-                        if (baseEndPt) // draw last NA residue's base
-                        {
-                            // start the cylinder at the midpoint between consecutive backbone atoms
-                            baseStartPt = new $3Dmol.Vector3().addVectors(curr, next).multiplyScalar(0.5);
-                            var startFix = baseStartPt.clone().sub(baseEndPt).multiplyScalar(0.02); //TODO: apply this as function of thickness
-                            baseStartPt.add(startFix);
-
-                            $3Dmol.GLDraw.drawCylinder(geo, baseStartPt, baseEndPt, 0.4, $3Dmol.CC.color(baseEndPt.color), 0, 2);
-                            baseStartPt = null;
-                            baseEndPt = null;   
-                        }
-
-                        // determine color and thickness of the next strand segment
-                        if (gradientScheme && cartoon.color === 'spectrum')
-                            nextColor = gradientScheme.valueToHex(next.resi, gradientScheme.range());
-                        else
-                            nextColor = $3Dmol.getColorFromStyle(next, cartoon).getHex();
-                        colors.push(nextColor);
-                        if ($.isNumeric(cartoon.thickness))
-                            thickness = cartoon.thickness;
-                        else
-                            thickness = defaultThickness;
-
-                        
-                        curr = next; // advance backbone
-                        //nextResAtom = atomList[parseInt(i) + resSize[curr.resn]];
-                        backbonePt = new $3Dmol.Vector3(curr.x, curr.y, curr.z);
-                        backbonePt.resi = curr.resi;
-                        currColor = nextColor;
-                    }
-
-                    // click handling
-                    if (next.clickable === true &&
-                        (next.intersectionShape === undefined || next.intersectionShape.triangle === undefined)) 
-                        next.intersectionShape = {sphere : null, cylinder : [], line : [], triangle : []};
-                }
-
-                // atoms used to orient the backbone strand
-                else if (isAlphaCarbon(curr) && next.atom === "O"
-                      || inNucleicAcid && curr.atom === "P" && next.atom === "OP2"
-                      || inNucleicAcid && curr.atom === "O5'" && next.atom === "C5'")
-                {
-                    orientPt = new $3Dmol.Vector3(next.x, next.y, next.z);
-                    orientPt.resi = next.resi;
-                    if (next.atom === "OP2") // for NA 3' terminus
-                        termOrientPt = new $3Dmol.Vector3(next.x, next.y, next.z);
-                }
-
-                // NA 3' terminus is an edge case, need a vector for most recent O3'
-                else if (inNucleicAcid && next.atom === "O3'")
-                {
-                    terminalPt = new $3Dmol.Vector3(next.x, next.y, next.z);
-                }
-
-                // atoms used for drawing the NA base cylinders (diff for purines and pyramidines)
-                else if ((next.atom === "N1" && $.inArray(next.resn.trim(), purResns) != -1) ||
-                         (next.atom === "N3" && $.inArray(next.resn.trim(), pyrResns) != -1))
-                {
-                    baseEndPt = new $3Dmol.Vector3(next.x, next.y, next.z);
-                    baseEndPt.color = $3Dmol.getColorFromStyle(next, cartoon).getHex();
-                }
-
-                // when we have a backbone point and orientation point in the same residue, accumulate strand points
-                if (orientPt && backbonePt && orientPt.resi === backbonePt.resi)
-                {
-                    arrow = addBackbonePoints(points, num, !doNotSmoothen, backbonePt, orientPt, prevOrientPt, curr, atomList, i);
-                    prevOrientPt = orientPt;
-                    backbonePt = null;
-                    orientPt = null;
-                    colors.push(nextColor);
-                    if (arrow) colors.push(nextColor);
-                }
-            }
-        }
-
-        if (baseEndPt) // draw last NA base if needed
-        {
-            if (terminalPt)
-                baseStartPt = new $3Dmol.Vector3().addVectors(curr, terminalPt).multiplyScalar(0.5);
-            else
-                baseStartPt = new $3Dmol.Vector3(curr.x, curr.y, curr.z);
-
-            $3Dmol.GLDraw.drawCylinder(geo, baseStartPt, baseEndPt, 0.4, $3Dmol.CC.color(baseEndPt.color), 0, 2);
-            arrow = addBackbonePoints(points, num, !doNotSmoothen, terminalPt, termOrientPt, prevOrientPt, curr, atomList, i);
-            colors.push(nextColor);
-            if (arrow) colors.push(nextColor);
-        }
-
-        // for default style, draw the last strand
-        for (i = 0; !thickness && i < num; i++)
-            drawSmoothCurve(group, points[i], 1, colors, div, points.opacity);
-        if (fill && points[0].length > 0)
-        {
-            drawStrip(group, points, colors, div, thickness, points.opacity, points.style);
-        }
-        if (geo != null && geo.vertices > 0)
-        {
-            var cartoonMaterial = new $3Dmol.MeshDoubleLambertMaterial();
-            cartoonMaterial.vertexColors = $3Dmol.FaceColors;
-            if (typeof(points.opacity) === "number" && points.opacity >= 0 && points.opacity < 1) {
-                cartoonMaterial.transparent = true;
-                cartoonMaterial.opacity = points.opacity;
-            }
-            var cartoonMesh = new $3Dmol.Mesh(geo, cartoonMaterial);
-            group.add(cartoonMesh);
-            geo = null;
-        }
-
-        if (traceGeo != null && traceGeo.vertices > 0) // generate last mesh for trace geometry
-        {
-            var traceMaterial = new $3Dmol.MeshDoubleLambertMaterial();
-            traceMaterial.vertexColors = $3Dmol.FaceColors;
-            if (typeof(traceGeo.opacity) === "number" && traceGeo.opacity >= 0 && traceGeo.opacity < 1) {
-                traceMaterial.transparent = true;
-                traceMaterial.opacity = traceGeo.opacity;
-                delete traceGeo.opacity;
-            }
-            var traceMesh = new $3Dmol.Mesh(traceGeo, traceMaterial);
-            group.add(traceMesh);
-        }
-    };
-
-    var addBackbonePoints = function(points, num, smoothen, backbonePt, orientPt, prevOrientPt, backboneAtom, atomList, atomi)
-    {
-        var widthScalar, i, delta, v, addArrowPoints, testOpacity, testStyle;
-        
-        // dictionary of standard amino acid sizes, in number of atoms
-        var resSize = {ALA: 5, ARG: 11, ASN:8, ASP:8, CYS:6, GLN:9, GLU: 9, GLY:4, HIS:10,
-            ILE:8, LEU: 8, LYS: 9, MET:8, PHE:11, PRO:7, SER:6, THR:7, TRP:14, TYR:12, VAL:7}
-
-        // the side vector points along the axis from backbone atom to orientation atom (eg. CA to O, in peptides)
-        var sideVec = orientPt.sub(backbonePt);
-        sideVec.normalize();
-
-        // the forward vector points along the axis from backbone atom to next backbone atom
-        var forwardVec = atomList[parseInt(atomi) + resSize[backboneAtom.resn]];
-        forwardVec = forwardVec ? new $3Dmol.Vector3(forwardVec.x, forwardVec.y, forwardVec.z) : new $3Dmol.Vector3(0, 0, 0);
-        forwardVec.sub(backbonePt);
-
-        // adjustments for proper beta arrow appearance
-        if (backboneAtom.ss === "arrow start")
-        {
-            var adjustment = forwardVec.clone().multiplyScalar(0.3).cross(orientPt); // adjust perpendicularly to strand face
-            backbonePt.add(adjustment);
-
-            var upVec = forwardVec.clone().cross(sideVec).normalize();
-            sideVec.rotateAboutVector(upVec, 0.43);
-        }
-
-        // determine from cartoon style or secondary structure how wide the strand should be here
-        // ribbon shape should have same width as thickness
-        if (backboneAtom.style.cartoon.ribbon)
-        {
-            widthScalar = backboneAtom.style.cartoon.thickness || defaultThickness;
-
-        } else // depending on secondary structure, multiply the orientation vector by some scalar
-        {
-            if (!backboneAtom.style.cartoon.width)
-            {
-                if (backboneAtom.ss === "c")
-                {
-                    if (backboneAtom.atom === "P")
-                        widthScalar = nucleicAcidWidth;
-                    else
-                        widthScalar = coilWidth;
-                } else if (backboneAtom.ss === "arrow start")
-                {
-                    widthScalar = helixSheetWidth;
-                    addArrowPoints = true;
-
-                } else if (backboneAtom.ss === "arrow end")
-                    widthScalar = coilWidth;
-                
-                else if (backboneAtom.ss === "h" && backboneAtom.style.cartoon.tubes || backboneAtom.ss === "tube start")
-                    widthScalar = coilWidth;
-
-                else
-                    widthScalar = helixSheetWidth;
-            }
-            else widthScalar = backboneAtom.style.cartoon.width;  
-        }
-
-        // make sure the strand orientation doesn't twist more than 90 degrees
-        if (prevOrientPt != null && sideVec.dot(prevOrientPt) < 0)
-            sideVec.negate();
-
-
-        sideVec.multiplyScalar(widthScalar);
-        for (i = 0; i < num; i++)
-        {
-            // produces NUM incremental points from backbone atom minus orientation vector
-            //  to backbone atom plus orientation vector
-            delta = -1 + i * 2/(num - 1); // -1 to 1 incrementing by num
-            v = new $3Dmol.Vector3(backbonePt.x + delta * sideVec.x,
-                                   backbonePt.y + delta * sideVec.y,
-                                   backbonePt.z + delta * sideVec.z);
-            v.atom = backboneAtom;
-            if (smoothen && backboneAtom.ss === "s") 
-                v.smoothen = true;
-            points[i].push(v); // a num-length array of arrays, where each inner array contains length-wise points
-                               // along the backbone offset by some constant pertaining to its cell in the outer array
-        }
-
-        if (addArrowPoints)
-        {
-
-            sideVec.multiplyScalar(2);
-            for (i = 0; i < num; i++)
-            {
-                delta = -1 + i * 2/(num - 1); // -1 to 1 incrementing by num
-                v = new $3Dmol.Vector3(backbonePt.x + delta * sideVec.x,
-                                       backbonePt.y + delta * sideVec.y,
-                                       backbonePt.z + delta * sideVec.z);
-                v.atom = backboneAtom;
-                v.smoothen = false;
-                v.skip = true;
-                points[i].push(v);
-            }
-        }
-
-        // make sure the strand is all the same opacity and style
-        testOpacity = parseFloat(backboneAtom.style.cartoon.opacity) || 1;
-        if (points.opacity)
-        {
-            if (points.opacity != testOpacity)
-            {
-                console.log("Warning: a cartoon-style chain's opacity is ambiguous");
-                points.opacity = 1;
-            }
-
-        } else points.opacity = testOpacity;
-
-        testStyle = backboneAtom.style.cartoon.style || 'default';
-        if (points.style)
-        {
-            if (points.style != testStyle)
-            {
-                console.log("Warning: a cartoon chain's strand-style is ambiguous");
-                points.style = 'default';
-            }
-
-        } else points.style = testStyle;
-
-        // revert ss keywords used for arrow rendering back to original value
-        if (backboneAtom.ss === "arrow start" || backboneAtom.ss === "arrow end")
-            backboneAtom.ss = "s";
-
-        return addArrowPoints;
-    };
-
-    var defaultDrawCartoon = function(group, atomList, gradientScheme, quality)
-    {
-        quality = parseInt(parseFloat(quality)*5) || 5;
-        drawCartoon(group, atomList, gradientScheme, fill=true, doNotSmoothen=false, num=quality, div=quality);
-    }
-
-    return defaultDrawCartoon;
-})();
-//
-
-var $3Dmol = $3Dmol || {};
-
-/**
- * Lower level utilities for creating WebGL shape geometries.
- * These are not intended for general consumption.
- * @namespace $3Dmol.GLDraw
-  */
-$3Dmol.GLDraw = (function() {
-
-    var draw = {}; // object for exporting functions
-
-    // Rotation matrix around z and x axis -
-    // according to y basis vector
-    // TODO: Try to optimize this (square roots?)
-    var getRotationMatrix = function() {
-
-        var d = new $3Dmol.Vector3();
-        // var rot = new Float32Array(9);
-
-        return function(dir) {
-
-            d.set(dir[0], dir[1], dir[2]);
-
-            var dx = d.x, dy = d.y, dz = d.z;
-
-            var dxy = Math.sqrt(dx * dx + dy * dy);
-            var dxz, dyz;
-
-            var sinA, cosA, sinB, cosB, sinC, cosC;
-
-            // about z axis - Phi
-            if (dxy < 0.0001) {
-                sinA = 0;
-                cosA = 1;
-            }
-
-            else {
-                sinA = -dx / dxy;
-                cosA = dy / dxy;
-            }
-
-            // recast dy in terms of new axes - z is the same
-
-            dy = -sinA * dx + cosA * dy;
-            dyz = Math.sqrt(dy * dy + dz * dz);
-
-            // about new x axis - Theta
-
-            if (dyz < 0.0001) {
-                sinB = 0;
-                cosB = 1;
-            }
-
-            else {
-                sinB = dz / dyz;
-                cosB = dy / dyz;
-            }
-
-            var rot = new Float32Array(9);
-            rot[0] = cosA;
-            rot[1] = sinA;
-            rot[2] = 0;
-            rot[3] = -sinA * cosB;
-            rot[4] = cosA * cosB;
-            rot[5] = sinB;
-            rot[6] = sinA * sinB;
-            rot[7] = -cosA * sinB;
-            rot[8] = cosB;
-
-            return rot;
-
-        };
-
-    }();
-    
-    // Ortho normal vectors for cylinder radius/ sphere cap equator and cones
-    // Direction is j basis (0,1,0)
-    var basisVectors = function() {
-
-        var ret = {
-            vertices : [],
-            norms : []
-        };
-
-        var nvecs = [];
-
-        var subdivisions = 4; // including the initial 2, eg. 4 => 16 subintervals
-        var N = Math.pow(2, subdivisions);  // eg. 2**4 = 16 subintervals in total
-        var i = 2;  // start with 2 subdivisions already done
-        var M = Math.pow(2, i); // 4
-        var spacing = N/M;  // 16/4 = 4; if there were 5 subdivs, then 32/4 = 8.
-        var j;
-
-        nvecs[0] = new $3Dmol.Vector3(-1, 0, 0);
-        nvecs[spacing] = new $3Dmol.Vector3(0, 0, 1);
-        nvecs[spacing*2] = new $3Dmol.Vector3(1, 0, 0);
-        nvecs[spacing*3] = new $3Dmol.Vector3(0, 0, -1);
-
-        for ( i = 3; i <= subdivisions; i ++ ) {
-            // eg. i=3, we need to add 2**(3-1) = 4 new vecs. Call it M.
-            // their spacing is N/M, eg. N=16, M=4, N/M=4; M=8, N/M=2.
-            // they start off at half this spacing
-            // and are equal to the average of the two vectors on either side
-            M = Math.pow(2, (i-1));
-            spacing = N/M;
-            for ( j = 0; j < (M-1); j ++ ) {
-                nvecs[spacing/2 + j*spacing] = nvecs[j*spacing].clone().add(nvecs[(j+1)*spacing]).normalize();
-            }
-            // treat the last one specially so it wraps around to zero
-            j = M - 1;
-            nvecs[spacing/2 + j*spacing] = nvecs[j*spacing].clone().add(nvecs[0]).normalize();
-        }
-
-        /*
-         * nvecs[0] = new $3Dmol.Vector3(-1,0,0); nvecs[1] = new
-         * $3Dmol.Vector3(0,0,1); nvecs[2] = new $3Dmol.Vector3(1,0,0);
-         * nvecs[3] = new $3Dmol.Vector3(0,0,-1);
-         */
-        return nvecs;
-
-    }();
-
-    // memoize capped cylinder for given radius
-    var cylVertexCache = {
-
-        // memoize both rounded and flat caps (hemisphere and circle)
-        cache : {false:{}, true:{}},
-
-        getVerticesForRadius : function(radius, flat) {
-
-            if (this.cache[flat][radius] !== undefined)
-                return this.cache[flat][radius];
-
-            var dir = new $3Dmol.Vector3(0, 1, 0);
-            var w = basisVectors.length;
-            var nvecs = [], norms = [];
-            var n;
-
-            for (var i = 0; i < w; i++) {
-                // bottom
-                nvecs.push(basisVectors[i].clone().multiplyScalar(radius));
-                // top
-                nvecs.push(basisVectors[i].clone().multiplyScalar(radius));
-
-                // NOTE: this normal is used for constructing sphere caps -
-                // cylinder normals taken care of in drawCylinder
-                n = basisVectors[i].clone().normalize();
-                norms.push(n);
-                norms.push(n);
-            }
-
-            // norms[0]
-
-            var verticesRows = [];
-
-            // Require that heightSegments is even and >= 2
-            // Equator points at h/2 (theta = pi/2)
-            // (repeated) polar points at 0 and h (theta = 0 and pi)
-            var heightSegments = 10, widthSegments = w; // 16 or however many
-                                                        // basis vectors for
-                                                        // cylinder
-
-            if (heightSegments % 2 !== 0 || !heightSegments) {
-                console.error("heightSegments must be even");
-
-                return null;
-            }
-
-            var phiStart = 0;
-            var phiLength = Math.PI * 2;
-
-            var thetaStart = 0;
-            var thetaLength = Math.PI;
-
-            var x, y;
-            var polar = false, equator = false;
-
-            for (y = 0; y <= heightSegments; y++) {
-
-                polar = (y === 0 || y === heightSegments) ? true : false;
-                equator = (y === heightSegments / 2) ? true : false;
-
-                var verticesRow = [], toRow = [];
-
-                for (x = 0; x <= widthSegments; x++) {
-
-                    // Two vertices rows for equator pointing to previously
-                    // constructed cyl points
-                    if (equator) {
-                        var xi = (x < widthSegments) ? 2 * x : 0;
-                        toRow.push(xi + 1);
-                        verticesRow.push(xi);
-
-                        continue;
-                    }
-
-                    var u = x / widthSegments;
-                    var v = y / heightSegments;
-
-                    // Only push first polar point
-
-                    if (!polar || x === 0) {
-
-                        if (x < widthSegments) {
-                            var vertex = new $3Dmol.Vector3();
-                            vertex.x = -radius
-                                    * Math.cos(phiStart + u * phiLength)
-                                    * Math.sin(thetaStart + v * thetaLength);
-                            vertex.y = flat ? 0 : radius * Math.cos(thetaStart + v * thetaLength);
-                            vertex.z = radius
-                                    * Math.sin(phiStart + u * phiLength)
-                                    * Math.sin(thetaStart + v * thetaLength);
-
-                            if (Math.abs(vertex.x) < 1e-5)
-                                vertex.x = 0;
-                            if (Math.abs(vertex.y) < 1e-5)
-                                vertex.y = 0;
-                            if (Math.abs(vertex.z) < 1e-5)
-                                vertex.z = 0;
-
-                            if (flat) {
-                                n = new $3Dmol.Vector3(0, Math.cos(thetaStart + v * thetaLength), 0);
-                                n.normalize();
-                            }
-                            else {
-                                n = new $3Dmol.Vector3(vertex.x, vertex.y, vertex.z);
-                                n.normalize();
-                            }
-
-                            nvecs.push(vertex);
-                            norms.push(n);
-
-                            verticesRow.push(nvecs.length - 1);
-                        }
-
-                        // last point is just the first point for this row
-                        else {
-                            verticesRow.push(nvecs.length - widthSegments);
-                        }
-
-                    }
-
-                    // x > 0; index to already added point
-                    else if (polar)
-                        verticesRow.push(nvecs.length - 1);
-
-                }
-
-                // extra equator row
-                if (equator)
-                    verticesRows.push(toRow);
-
-                verticesRows.push(verticesRow);
-
-            }
-
-            var obj = {
-                vertices : nvecs,
-                normals : norms,
-                verticesRows : verticesRows,
-                w : widthSegments,
-                h : heightSegments
-            };
-
-            this.cache[flat][radius] = obj;
-
-            return obj;
-
-        }
-    };
-
-    // creates a cylinder
-    var drawnC = 0;
-    
-    /** Create a cylinder 
-     * @function $3Dmol.GLDraw.drawCylinder
-     * @param {geometry}
-     *            geo
-     * @param {Point}
-     *            from
-     * @param {Point}
-     *            to
-     * @param {float}
-     *            radius
-     * @param {$3Dmol.Color}
-     *            color
-     * @param {integer} fromCap - 0 for none, 1 for flat, 2 for round
-     * @param {integer} toCap = 0 for none, 1 for flat, 2 for round
-     *            
-     * */
-    draw.drawCylinder = function(geo, from, to, radius, color, fromCap, toCap) {
-        if (!from || !to)
-            return;
-        drawnC++;
-        // vertices
-        var drawcaps = fromCap || toCap;
-
-        if (fromCap == 1 && toCap == 1) // 0 is none, 1 is flat, 2 is round
-          	var flat = true;
-        else var flat = false;
-
-        color = color || {r:0, g:0, b:0};
-
-        /** @type {Array.<number>} */
-        var dir = [ to.x, to.y, to.z ];
-        dir[0] -= from.x;
-        dir[1] -= from.y;
-        dir[2] -= from.z;
-
-        var e = getRotationMatrix(dir);
-        // get orthonormal vectors from cache
-        // TODO: Will have orient with model view matrix according to direction
-        var vobj = cylVertexCache.getVerticesForRadius(radius, flat);
-
-        // w (n) corresponds to the number of orthonormal vectors for cylinder
-        // (default 16)
-        var n = vobj.w, h = vobj.h;
-        var w = n;
-        // get orthonormal vector
-        var n_verts = (drawcaps) ? h * n + 2 : 2 * n;
-
-        var geoGroup = geo.updateGeoGroup(n_verts);
-
-        var vertices = vobj.vertices, normals = vobj.normals, verticesRows = vobj.verticesRows;
-        var toRow = verticesRows[h / 2], fromRow = verticesRows[h / 2 + 1];
-
-        var start = geoGroup.vertices;
-        var offset, faceoffset;
-        var i, x, y, z;
-
-        var vertexArray = geoGroup.vertexArray;
-        var normalArray = geoGroup.normalArray;
-        var colorArray = geoGroup.colorArray;
-        var faceArray = geoGroup.faceArray;
-        // add vertices, opposing vertices paired together
-        for (i = 0; i < n; ++i) {
-
-            var vi = 2 * i;
-
-            x = e[0] * vertices[vi].x + e[3] * vertices[vi].y + e[6]
-                    * vertices[vi].z;
-            y = e[1] * vertices[vi].x + e[4] * vertices[vi].y + e[7]
-                    * vertices[vi].z;
-            z = e[5] * vertices[vi].y + e[8] * vertices[vi].z;
-
-            // var xn = x/radius, yn = y/radius, zn = z/radius;
-
-            offset = 3 * (start + vi);
-            faceoffset = geoGroup.faceidx;
-
-            // from
-            vertexArray[offset] = x + from.x;
-            vertexArray[offset + 1] = y + from.y;
-            vertexArray[offset + 2] = z + from.z;
-            // to
-            vertexArray[offset + 3] = x + to.x;
-            vertexArray[offset + 4] = y + to.y;
-            vertexArray[offset + 5] = z + to.z;
-
-            // normals
-            normalArray[offset] = x;
-            normalArray[offset + 3] = x;
-            normalArray[offset + 1] = y;
-            normalArray[offset + 4] = y;
-            normalArray[offset + 2] = z;
-            normalArray[offset + 5] = z;
-
-            // colors
-            colorArray[offset] = color.r;
-            colorArray[offset + 3] = color.r;
-            colorArray[offset + 1] = color.g;
-            colorArray[offset + 4] = color.g;
-            colorArray[offset + 2] = color.b;
-            colorArray[offset + 5] = color.b;
-
-            // faces
-            // 0 - 2 - 1
-            faceArray[faceoffset] = fromRow[i] + start;
-            faceArray[faceoffset + 1] = fromRow[i + 1] + start;
-            faceArray[faceoffset + 2] = toRow[i] + start;
-            // 1 - 2 - 3
-            faceArray[faceoffset + 3] = toRow[i] + start;
-            faceArray[faceoffset + 4] = fromRow[i + 1] + start;
-            faceArray[faceoffset + 5] = toRow[i + 1] + start;
-
-            geoGroup.faceidx += 6;
-
-        }
-
-        // SPHERE CAPS
-
-        if (drawcaps) {
-
-            // h - sphere rows, verticesRows.length - 2
-            var ystart = (toCap) ? 0 : h / 2;
-            var yend = (fromCap) ? h + 1 : h / 2 + 1;
-
-            var v1, v2, v3, v4, x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4, nx1, nx2, nx3, nx4, ny1, ny2, ny3, ny4, nz1, nz2, nz3, nz4, v1offset, v2offset, v3offset, v4offset;
-
-            for (y = ystart; y < yend; y++) {
-                if (y === h / 2)
-                    continue;
-                // n number of points for each level (verticesRows[i].length -
-                // 1)
-                var cap = (y <= h / 2) ? to : from;
-
-                for (x = 0; x < n; x++) {
-
-                    faceoffset = geoGroup.faceidx;
-
-                    v1 = verticesRows[y][x + 1];
-                    v1offset = (v1 + start) * 3;
-                    v2 = verticesRows[y][x];
-                    v2offset = (v2 + start) * 3;
-                    v3 = verticesRows[y + 1][x];
-                    v3offset = (v3 + start) * 3;
-                    v4 = verticesRows[y + 1][x + 1];
-                    v4offset = (v4 + start) * 3;
-
-                    // rotate sphere vectors
-                    x1 = e[0] * vertices[v1].x + e[3] * vertices[v1].y + e[6]
-                            * vertices[v1].z;
-                    x2 = e[0] * vertices[v2].x + e[3] * vertices[v2].y + e[6]
-                            * vertices[v2].z;
-                    x3 = e[0] * vertices[v3].x + e[3] * vertices[v3].y + e[6]
-                            * vertices[v3].z;
-                    x4 = e[0] * vertices[v4].x + e[3] * vertices[v4].y + e[6]
-                            * vertices[v4].z;
-
-                    y1 = e[1] * vertices[v1].x + e[4] * vertices[v1].y + e[7]
-                            * vertices[v1].z;
-                    y2 = e[1] * vertices[v2].x + e[4] * vertices[v2].y + e[7]
-                            * vertices[v2].z;
-                    y3 = e[1] * vertices[v3].x + e[4] * vertices[v3].y + e[7]
-                            * vertices[v3].z;
-                    y4 = e[1] * vertices[v4].x + e[4] * vertices[v4].y + e[7]
-                            * vertices[v4].z;
-
-                    z1 = e[5] * vertices[v1].y + e[8] * vertices[v1].z;
-                    z2 = e[5] * vertices[v2].y + e[8] * vertices[v2].z;
-                    z3 = e[5] * vertices[v3].y + e[8] * vertices[v3].z;
-                    z4 = e[5] * vertices[v4].y + e[8] * vertices[v4].z;
-
-                    vertexArray[v1offset] = x1 + cap.x;
-                    vertexArray[v2offset] = x2 + cap.x;
-                    vertexArray[v3offset] = x3 + cap.x;
-                    vertexArray[v4offset] = x4 + cap.x;
-
-                    vertexArray[v1offset + 1] = y1 + cap.y;
-                    vertexArray[v2offset + 1] = y2 + cap.y;
-                    vertexArray[v3offset + 1] = y3 + cap.y;
-                    vertexArray[v4offset + 1] = y4 + cap.y;
-
-                    vertexArray[v1offset + 2] = z1 + cap.z;
-                    vertexArray[v2offset + 2] = z2 + cap.z;
-                    vertexArray[v3offset + 2] = z3 + cap.z;
-                    vertexArray[v4offset + 2] = z4 + cap.z;
-
-                    colorArray[v1offset] = color.r;
-                    colorArray[v2offset] = color.r;
-                    colorArray[v3offset] = color.r;
-                    colorArray[v4offset] = color.r;
-
-                    colorArray[v1offset + 1] = color.g;
-                    colorArray[v2offset + 1] = color.g;
-                    colorArray[v3offset + 1] = color.g;
-                    colorArray[v4offset + 1] = color.g;
-
-                    colorArray[v1offset + 2] = color.b;
-                    colorArray[v2offset + 2] = color.b;
-                    colorArray[v3offset + 2] = color.b;
-                    colorArray[v4offset + 2] = color.b;
-
-                    nx1 = e[0] * normals[v1].x + e[3] * normals[v1].y + e[6]
-                            * normals[v1].z;
-                    nx2 = e[0] * normals[v2].x + e[3] * normals[v2].y + e[6]
-                            * normals[v2].z;
-                    nx3 = e[0] * normals[v3].x + e[3] * normals[v3].y + e[6]
-                            * normals[v3].z;
-                    nx4 = e[0] * normals[v4].x + e[3] * normals[v4].y + e[6]
-                            * normals[v4].z;
-
-                    ny1 = e[1] * normals[v1].x + e[4] * normals[v1].y + e[7]
-                            * normals[v1].z;
-                    ny2 = e[1] * normals[v2].x + e[4] * normals[v2].y + e[7]
-                            * normals[v2].z;
-                    ny3 = e[1] * normals[v3].x + e[4] * normals[v3].y + e[7]
-                            * normals[v3].z;
-                    ny4 = e[1] * normals[v4].x + e[4] * normals[v4].y + e[7]
-                            * normals[v4].z;
-
-                    nz1 = e[5] * normals[v1].y + e[8] * normals[v1].z;
-                    nz2 = e[5] * normals[v2].y + e[8] * normals[v2].z;
-                    nz3 = e[5] * normals[v3].y + e[8] * normals[v3].z;
-                    nz4 = e[5] * normals[v4].y + e[8] * normals[v4].z;
-
-                    // if (Math.abs(vobj.sphereVertices[v1].y) === radius) {
-                    if (y === 0) {
-                        // face = [v1, v3, v4];
-                        // norm = [n1, n3, n4];
-
-                        normalArray[v1offset] = nx1;
-                        normalArray[v3offset] = nx3;
-                        normalArray[v4offset] = nx4;
-                        normalArray[v1offset + 1] = ny1;
-                        normalArray[v3offset + 1] = ny3;
-                        normalArray[v4offset + 1] = ny4;
-                        normalArray[v1offset + 2] = nz1;
-                        normalArray[v3offset + 2] = nz3;
-                        normalArray[v4offset + 2] = nz4;
-
-                        faceArray[faceoffset] = v1 + start;
-                        faceArray[faceoffset + 1] = v3 + start;
-                        faceArray[faceoffset + 2] = v4 + start;
-
-                        geoGroup.faceidx += 3;
-
-                    }
-
-                    // else if (Math.abs(vobj.sphereVertices[v3].y) === radius)
-                    // {
-                    else if (y === yend - 1) {
-                        // face = [v1, v2, v3];
-                        // norm = [n1, n2, n3];
-
-                        normalArray[v1offset] = nx1;
-                        normalArray[v2offset] = nx2;
-                        normalArray[v3offset] = nx3;
-                        normalArray[v1offset + 1] = ny1;
-                        normalArray[v2offset + 1] = ny2;
-                        normalArray[v3offset + 1] = ny3;
-                        normalArray[v1offset + 2] = nz1;
-                        normalArray[v2offset + 2] = nz2;
-                        normalArray[v3offset + 2] = nz3;
-
-                        faceArray[faceoffset] = v1 + start;
-                        faceArray[faceoffset + 1] = v2 + start;
-                        faceArray[faceoffset + 2] = v3 + start;
-
-                        geoGroup.faceidx += 3;
-
-                    }
-
-                    else {
-                        // face = [v1, v2, v3, v4];
-                        // norm = [n1, n2, n3, n4];
-
-                        normalArray[v1offset] = nx1;
-                        normalArray[v2offset] = nx2;
-                        normalArray[v4offset] = nx4;
-                        normalArray[v1offset + 1] = ny1;
-                        normalArray[v2offset + 1] = ny2;
-                        normalArray[v4offset + 1] = ny4;
-                        normalArray[v1offset + 2] = nz1;
-                        normalArray[v2offset + 2] = nz2;
-                        normalArray[v4offset + 2] = nz4;
-
-                        normalArray[v2offset] = nx2;
-                        normalArray[v3offset] = nx3;
-                        normalArray[v4offset] = nx4;
-                        normalArray[v2offset + 1] = ny2;
-                        normalArray[v3offset + 1] = ny3;
-                        normalArray[v4offset + 1] = ny4;
-                        normalArray[v2offset + 2] = nz2;
-                        normalArray[v3offset + 2] = nz3;
-                        normalArray[v4offset + 2] = nz4;
-
-                        faceArray[faceoffset] = v1 + start;
-                        faceArray[faceoffset + 1] = v2 + start;
-                        faceArray[faceoffset + 2] = v4 + start;
-
-                        faceArray[faceoffset + 3] = v2 + start;
-                        faceArray[faceoffset + 4] = v3 + start;
-                        faceArray[faceoffset + 5] = v4 + start;
-
-                        geoGroup.faceidx += 6;
-                    }
-
-                }
-            }
-
-        }
-
-        geoGroup.vertices += n_verts;
-    };
-
-    /** Create a cone 
-     * @function $3Dmol.GLDraw.drawCone
-     * @param {geometry}
-     *            geo
-     * @param {Point}
-     *            from
-     * @param {Point}
-     *            to
-     * @param {float}
-     *            radius
-     * @param {$3Dmol.Color}
-     *            color
-     *            */
-    draw.drawCone = function(geo, from, to, radius, color) {
-        if (!from || !to)
-            return;
-
-        color = color || {r:0, g:0, b:0};
-
-        var dir =[to.x, to.y, to.z ];        
-        dir.x -= from.x;
-        dir.y -= from.y;
-        dir.z -= from.z;
-
-        var e = getRotationMatrix(dir);
-
-
-        // n vertices around bottom plust the two points
-        var n = basisVectors.length;
-        var basis = basisVectors;
-        var n_verts =  n + 2;
-
-        
-        //setup geo structures
-        var geoGroup = geo.updateGeoGroup(n_verts);
-        var start = geoGroup.vertices;    
-        var offset, faceoffset;
-        var i, x, y, z;
-        var vertexArray = geoGroup.vertexArray;
-        var normalArray = geoGroup.normalArray;
-        var colorArray = geoGroup.colorArray;
-        var faceArray = geoGroup.faceArray;
-        
-        var offset = start*3;
-        var ndir = new $3Dmol.Vector3(dir[0],dir[1],dir[2]).normalize();
-        //base point first vertex
-        vertexArray[offset] = from.x;
-        vertexArray[offset+1] = from.y;
-        vertexArray[offset+2] = from.z;
-        normalArray[offset] = -ndir.x;
-        normalArray[offset + 1] = -ndir.y;
-        normalArray[offset + 2] = -ndir.z;
-        colorArray[offset] = color.r;
-        colorArray[offset + 1] = color.g;
-        colorArray[offset + 2] = color.b;
-        
-        //second vertex top
-        vertexArray[offset+3] = to.x;
-        vertexArray[offset+4] = to.y;
-        vertexArray[offset+5] = to.z;
-        
-        normalArray[offset+3] = ndir.x;
-        normalArray[offset+4] = ndir.y;
-        normalArray[offset+5] = ndir.z;
-        colorArray[offset+3] = color.r;
-        colorArray[offset + 4] = color.g;
-        colorArray[offset + 5] = color.b;
-        
-        offset += 6;
-        
-        // add circle vertices
-        for (i = 0; i < n; ++i) {
-            var vec = basis[i].clone();
-            vec.multiplyScalar(radius);
-            x = e[0] * vec.x + e[3] * vec.y + e[6]
-                    * vec.z;
-            y = e[1] * vec.x + e[4] * vec.y + e[7]
-                    * vec.z;
-            z = e[5] * vec.y + e[8] * vec.z;
-
-            // from
-            vertexArray[offset] = x + from.x;
-            vertexArray[offset + 1] = y + from.y;
-            vertexArray[offset + 2] = z + from.z;
-
-            // normals
-            normalArray[offset] = x;
-            normalArray[offset + 1] = y;
-            normalArray[offset + 2] = z;
-
-            // colors
-            colorArray[offset] = color.r;
-            colorArray[offset + 1] = color.g;
-            colorArray[offset + 2] = color.b;
-            
-            offset += 3;
-
-        }
-        geoGroup.vertices += (n+2);
-        //faces
-        var faceoffset = geoGroup.faceidx;
-        for( i = 0; i < n; i++) {
-            //two neighboring circle vertices
-            var v1 = start+2+i;
-            var v2 = start+2+ ((i+1)%n);
-            
-            faceArray[faceoffset] = v1;
-            faceArray[faceoffset+1] = v2;
-            faceArray[faceoffset+2] = start;
-            faceoffset += 3;
-            faceArray[faceoffset] = v1;
-            faceArray[faceoffset+1] = v2;
-            faceArray[faceoffset+2] = start+1;
-            faceoffset += 3;
-        }
-        geoGroup.faceidx += 6*n;
-    };
-
-    
-    // Sphere component
-    var sphereVertexCache = {
-        cache : {},
-        getVerticesForRadius : function(radius) {
-
-            if (typeof (this.cache[radius]) !== "undefined")
-                return this.cache[radius];
-
-            var obj = {
-                vertices : [],
-                verticesRows : [],
-                normals : []
-            };
-            // scale quality with radius heuristically
-            var sphereQuality = 1;
-            var widthSegments = 16 * sphereQuality;
-            var heightSegments = 10 * sphereQuality;
-            if (radius < 1) {
-                widthSegments = 10 * sphereQuality;
-                heightSegments = 8 * sphereQuality;
-            }
-
-            var phiStart = 0;
-            var phiLength = Math.PI * 2;
-
-            var thetaStart = 0;
-            var thetaLength = Math.PI;
-
-            var x, y, vertices = [], uvs = [];
-
-            for (y = 0; y <= heightSegments; y++) {
-
-                var verticesRow = [];
-                for (x = 0; x <= widthSegments; x++) {
-
-                    var u = x / widthSegments;
-                    var v = y / heightSegments;
-
-                    var vertex = {};
-                    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);
-
-                    var n = new $3Dmol.Vector3(vertex.x, vertex.y, vertex.z);
-                    n.normalize();
-
-                    obj.vertices.push(vertex);
-                    obj.normals.push(n);
-
-                    verticesRow.push(obj.vertices.length - 1);
-
-                }
-
-                obj.verticesRows.push(verticesRow);
-
-            }
-
-            this.cache[radius] = obj;
-            return obj;
-        }
-
-    };
-
-    /** Create a sphere.
-     * @function $3Dmol.GLDraw.drawSphere
-     * @param {geometry}
-     *            geo
-     * @param {Point}
-     *            pos
-     * @param {float}
-     *            radius
-     * @param {$3Dmol.Color}
-     *            color
-     */
-    draw.drawSphere = function(geo, pos, radius, color) {
-
-        var center = new $3Dmol.Vector3(pos.x, pos.y, pos.z);
-
-        var x, y;
-        var vobj = sphereVertexCache.getVerticesForRadius(radius);
-
-        var vertices = vobj.vertices;
-        var normals = vobj.normals;
-
-        var geoGroup = geo.updateGeoGroup(vertices.length);
-
-        var start = geoGroup.vertices;
-        var vertexArray = geoGroup.vertexArray;
-        var colorArray = geoGroup.colorArray;
-        var faceArray = geoGroup.faceArray;
-        var lineArray = geoGroup.lineArray;
-        var normalArray = geoGroup.normalArray;
-
-        for (var i = 0, il = vertices.length; i < il; ++i) {
-            var offset = 3 * (start + i);
-            var v = vertices[i];
-
-            vertexArray[offset] = (v.x + pos.x);
-            vertexArray[offset + 1] = (v.y + pos.y);
-            vertexArray[offset + 2] = (v.z + pos.z);
-
-            colorArray[offset] = color.r;
-            colorArray[offset + 1] = color.g;
-            colorArray[offset + 2] = color.b;
-
-        }
-
-        geoGroup.vertices += vertices.length;
-
-        var verticesRows = vobj.verticesRows;
-        var h = verticesRows.length - 1;
-
-        for (y = 0; y < h; y++) {
-            var w = verticesRows[y].length - 1;
-            for (x = 0; x < w; x++) {
-
-                var faceoffset = geoGroup.faceidx, lineoffset = geoGroup.lineidx;
-
-                var v1 = verticesRows[y][x + 1] + start, v1offset = v1 * 3;
-                var v2 = verticesRows[y][x] + start, v2offset = v2 * 3;
-                var v3 = verticesRows[y + 1][x] + start, v3offset = v3 * 3;
-                var v4 = verticesRows[y + 1][x + 1] + start, v4offset = v4 * 3;
-
-                var n1 = normals[v1 - start];
-                var n2 = normals[v2 - start];
-                var n3 = normals[v3 - start];
-                var n4 = normals[v4 - start];
-                var face, norm;
-                if (Math.abs(vertices[v1 - start].y) === radius) {
-                    // face = [v1, v3, v4];
-                    // norm = [n1, n3, n4];
-
-                    normalArray[v1offset] = n1.x;
-                    normalArray[v3offset] = n3.x;
-                    normalArray[v4offset] = n4.x;
-                    normalArray[v1offset + 1] = n1.y;
-                    normalArray[v3offset + 1] = n3.y;
-                    normalArray[v4offset + 1] = n4.y;
-                    normalArray[v1offset + 2] = n1.z;
-                    normalArray[v3offset + 2] = n3.z;
-                    normalArray[v4offset + 2] = n4.z;
-
-                    faceArray[faceoffset] = v1;
-                    faceArray[faceoffset + 1] = v3;
-                    faceArray[faceoffset + 2] = v4;
-
-                    lineArray[lineoffset] = v1;
-                    lineArray[lineoffset + 1] = v3;
-                    lineArray[lineoffset + 2] = v1;
-                    lineArray[lineoffset + 3] = v4;
-                    lineArray[lineoffset + 4] = v3;
-                    lineArray[lineoffset + 5] = v4;
-
-                    geoGroup.faceidx += 3;
-                    geoGroup.lineidx += 6;
-
-                } else if (Math.abs(vertices[v3 - start].y) === radius) {
-                    // face = [v1, v2, v3];
-                    // norm = [n1, n2, n3];
-
-                    normalArray[v1offset] = n1.x;
-                    normalArray[v2offset] = n2.x;
-                    normalArray[v3offset] = n3.x;
-                    normalArray[v1offset + 1] = n1.y;
-                    normalArray[v2offset + 1] = n2.y;
-                    normalArray[v3offset + 1] = n3.y;
-                    normalArray[v1offset + 2] = n1.z;
-                    normalArray[v2offset + 2] = n2.z;
-                    normalArray[v3offset + 2] = n3.z;
-
-                    faceArray[faceoffset] = v1;
-                    faceArray[faceoffset + 1] = v2;
-                    faceArray[faceoffset + 2] = v3;
-
-                    lineArray[lineoffset] = v1;
-                    lineArray[lineoffset + 1] = v2;
-                    lineArray[lineoffset + 2] = v1;
-                    lineArray[lineoffset + 3] = v3;
-                    lineArray[lineoffset + 4] = v2;
-                    lineArray[lineoffset + 5] = v3;
-
-                    geoGroup.faceidx += 3;
-                    geoGroup.lineidx += 6;
-
-                } else {
-                    // face = [v1, v2, v3, v4];
-                    // norm = [n1, n2, n3, n4];
-
-                    normalArray[v1offset] = n1.x;
-                    normalArray[v2offset] = n2.x;
-                    normalArray[v4offset] = n4.x;
-                    normalArray[v1offset + 1] = n1.y;
-                    normalArray[v2offset + 1] = n2.y;
-                    normalArray[v4offset + 1] = n4.y;
-                    normalArray[v1offset + 2] = n1.z;
-                    normalArray[v2offset + 2] = n2.z;
-                    normalArray[v4offset + 2] = n4.z;
-
-                    normalArray[v2offset] = n2.x;
-                    normalArray[v3offset] = n3.x;
-                    normalArray[v4offset] = n4.x;
-                    normalArray[v2offset + 1] = n2.y;
-                    normalArray[v3offset + 1] = n3.y;
-                    normalArray[v4offset + 1] = n4.y;
-                    normalArray[v2offset + 2] = n2.z;
-                    normalArray[v3offset + 2] = n3.z;
-                    normalArray[v4offset + 2] = n4.z;
-
-                    faceArray[faceoffset] = v1;
-                    faceArray[faceoffset + 1] = v2;
-                    faceArray[faceoffset + 2] = v4;
-
-                    faceArray[faceoffset + 3] = v2;
-                    faceArray[faceoffset + 4] = v3;
-                    faceArray[faceoffset + 5] = v4;
-
-                    lineArray[lineoffset] = v1;
-                    lineArray[lineoffset + 1] = v2;
-                    lineArray[lineoffset + 2] = v1;
-                    lineArray[lineoffset + 3] = v4;
-
-                    lineArray[lineoffset + 4] = v2;
-                    lineArray[lineoffset + 5] = v3;
-                    lineArray[lineoffset + 6] = v3;
-                    lineArray[lineoffset + 7] = v4;
-
-                    geoGroup.faceidx += 6;
-                    geoGroup.lineidx += 8;
-
-                }
-
-            }
-        }
-
-    };
-
-    return draw;
-
-})();// A model is a collection of related atoms.  Bonds are only allowed between
-//atoms in the same model.  An atom is uniquely specified by its model id and
-//its serial number.
-//A glmodel knows how to apply the styles on each atom to create a gl object
-
-var $3Dmol = $3Dmol || {};
-
-/**
- * GLModel represents a group of related atoms
- * @constructor 
- * @param {number=} mid 
- * @param {Object=} defaultcolors Object defining default atom colors as atom => color property value pairs
- * @see $3Dmol.download
- */
-$3Dmol.GLModel = (function() {
-
-    // class variables go here
-    var defaultAtomStyle = {
-        line : {}
-    };
-
-    var Nucleotides = [ '  G', '  A', '  T', '  C', '  U', ' DG', ' DA', ' DT',
-            ' DC', ' DU' ];
-
-    var defaultlineWidth = 1.0;
-
-    // Reference: A. Bondi, J. Phys. Chem., 1964, 68, 441.
-    var vdwRadii = {
-        "H" : 1.2,
-        "Li" : 1.82,
-        "LI" : 1.82,
-        "Na" : 2.27,
-        "NA" : 2.27,
-        "K" : 2.75,
-        "C" : 1.7,
-        "N" : 1.55,
-        "O" : 1.52,
-        "F" : 1.47,
-        "P" : 1.80,
-        "S" : 1.80,
-        "CL" : 1.75,
-        "Cl" : 1.75,
-        "BR" : 1.85,
-        "Br" : 1.85,
-        "SE" : 1.90,
-        "Se" : 1.90,
-        "ZN" : 1.39,
-        "Zn" : 1.39,
-        "CU" : 1.4,
-        "Cu" : 1.4,
-        "NI" : 1.63,
-        "Ni" : 1.63
-    };
-
-    var validAtomSpecs = [
-        "resn", // Parent residue name
-        "x", // Atom's x coordinate
-        "y", // Atom's y coordinate
-        "z", // Atom's z coordinate
-        "color", // Atom's color, as hex code
-        "surfaceColor", // Hex code for color to be used for surface patch over this atom
-        "elem", // Element abbreviation (e.g. 'H', 'Ca', etc)
-        "hetflag", // Set to true if atom is a heteroatom
-        "chain", // Chain this atom belongs to, if specified in input file (e.g 'A' for chain A)
-        "resi", // Residue number 
-        "icode",
-        "rescode",
-        "serial", // Atom's serial id numbermodels
-        "atom", // Atom name; may be more specific than 'elem' (e.g 'CA' for alpha carbon)
-        "bonds", // Array of atom ids this atom is bonded to
-        "ss", // Secondary structure identifier (for cartoon render; e.g. 'h' for helix)
-        "singleBonds", // true if this atom forms only single bonds or no bonds at all
-        "bondOrder", // Array of this atom's bond orders, corresponding to bonds identfied by 'bonds'
-        "properties", // Optional mapping of additional properties
-        "b", // Atom b factor data
-        "pdbline", // If applicable, this atom's record entry from the input PDB file (used to output new PDB from models)
-        "clickable", // Set this flag to true to enable click selection handling for this atom
-        "callback", // Callback click handler function to be executed on this atom and its parent viewer
-        "invert" // for selection, inverts the meaning of the selection
-    ];
-
-    var validAtomSelectionSpecs = validAtomSpecs.concat([  // valid atom specs are ok too
-        "model", // a single model or list of models from which atoms should be selected
-        "bonds", // overloaded to select number of bonds, e.g. {bonds: 0} will select all nonbonded atoms
-        "predicate", // user supplied function that gets passed an {AtomSpec} and should return true if the atom should be selected
-        "invert", // if set, inverts the meaning of the selection
-        "byres", // if set, expands the selection to include all atoms of any residue that has any atom selected
-        "expand", // expands the selection to include all atoms within a given distance from the selection
-        "within" // intersects the selection with the set of atoms within a given distance from another selection
-    ]);
-
-    var validAtomStyleSpecs = [
-        "line", // draw bonds as lines
-        "cross", // draw atoms as crossed lines (aka stars)
-        "stick", // draw bonds as capped cylinders
-        "sphere", // draw atoms as spheres
-        "cartoon" // draw cartoon representation of secondary structure
-    ];
-
-    // class functions
-
-    // return true if a and b represent the same style
-    var sameObj = function(a,b) {
-        if(a && b)
-            return JSON.stringify(a) == JSON.stringify(b);
-        else
-            return a == b;
-    };    
-
-   
-    function GLModel(mid, defaultcolors) {
-        // private variables
-        var atoms = [];
-        var frames = [];
-        var id = mid;
-        var hidden = false;
-        var molObj = null;
-        var renderedMolObj = null;
-        var lastColors = null;
-        var modelData = {};
-        var idMatrix = new $3Dmol.Matrix4();
-        var dontDuplicateAtoms;
-        var defaultColor = $3Dmol.elementColors.defaultColor;
-        
-        var ElementColors = (defaultcolors) ? defaultcolors : $3Dmol.elementColors.defaultColors;
-
-
-        // drawing functions must be associated with model object since
-        // geometries can't span multiple canvases
-
-        // sphere drawing
-        var defaultSphereRadius = 1.5;
-
-        // return proper radius for atom given style
-        /** 
-         * 
-         * @param {AtomSpec} atom
-         * @param {atomstyle} style
-         * @return {number} 
-         * 
-         */
-        var getRadiusFromStyle = function(atom, style) {
-            var r = defaultSphereRadius;
-            if (typeof (style.radius) != "undefined")
-                r = style.radius;
-            else if (vdwRadii[atom.elem])
-                r = vdwRadii[atom.elem];
-
-            if (typeof (style.scale) != "undefined")
-                r *= style.scale;
-            return r;
-        };
-
-        // cross drawing
-        /** @typedef CrossStyleSpec
-         * @prop {boolean} hidden - do not show 
-         * @prop {number} linewidth 
-         * @prop {number} radius 
-         * @prop {number} scale - scale radius by specified amount
-         * @prop {ColorschemeSpec} colorscheme - element based coloring
-         * @prop {ColorSpec} color - fixed coloring, overrides colorscheme
-         */
-        
-        /**
-         * 
-         * @param {AtomSpec} atom
-         * @param {$3Dmol.Geometry[]} geos
-         */
-        var drawAtomCross = function(atom, geos) {
-            if (!atom.style.cross)
-                return;
-            var style = atom.style.cross;
-            if (style.hidden)
-                return;
-            var linewidth = (style.linewidth || defaultlineWidth);
-            if (!geos[linewidth])
-                geos[linewidth] = new $3Dmol.Geometry();
-                
-            var geoGroup = geos[linewidth].updateGeoGroup(6);
-            
-            var delta = getRadiusFromStyle(atom, style);
-
-            var points = [ [ delta, 0, 0 ], [ -delta, 0, 0 ], [ 0, delta, 0 ],
-                    [ 0, -delta, 0 ], [ 0, 0, delta ], [ 0, 0, -delta ] ];
-
-            var clickable = atom.clickable;
-            if (clickable && atom.intersectionShape === undefined)
-                atom.intersectionShape = {sphere : [], cylinder : [], line : []};
-            
-            var c = $3Dmol.getColorFromStyle(atom, style);
-            
-            var vertexArray = geoGroup.vertexArray;
-            var colorArray = geoGroup.colorArray;
-            
-            for ( var j = 0; j < 6; j++) {
-                
-                var offset = geoGroup.vertices*3;
-                
-                geoGroup.vertices++;
-                vertexArray[offset] = atom.x + points[j][0];
-                vertexArray[offset+1] = atom.y + points[j][1];
-                vertexArray[offset+2] = atom.z + points[j][2];
-                colorArray[offset] = c.r;
-                colorArray[offset+1] = c.g;
-                colorArray[offset+2] = c.b;
-                
-                if (clickable){
-                    var point = new $3Dmol.Vector3(points[j][0], points[j][1], points[j][2]);
-                    
-                    //decrease cross size for selection to prevent misselection from atom overlap
-                    point.multiplyScalar(0.1);
-                    point.set(point.x+atom.x, point.y+atom.y, point.z+atom.z);
-                    atom.intersectionShape.line.push(point);
-                }
-
-            }
-                        
-        };
-
-        //from atom, return a normalized vector v that is orthogonal and along which
-        //it is appropraite to draw multiple bonds
-        var getSideBondV = function(atom, atom2, i) {
-
-            var p1 = new $3Dmol.Vector3(atom.x, atom.y, atom.z);
-            var p2 = new $3Dmol.Vector3(atom2.x, atom2.y, atom2.z);
-
-            var dir = p2.clone();
-            var v = null;
-            dir.sub(p1);
-
-            var p1a, p1b, p2a, p2b;
-            var i2, j2, atom3, p3, dir2;
-            if (atom.bonds.length === 1) {
-                if (atom2.bonds.length === 1) {
-                    v = dir.clone();
-                    if (Math.abs(v.x) > 0.0001)
-                        v.y += 1;
-                    else
-                        v.x += 1;
-                } else {
-                    i2 = (i + 1) % atom2.bonds.length;
-                    j2 = atom2.bonds[i2];
-                    atom3 = atoms[j2];
-                    p3 = new $3Dmol.Vector3(atom3.x, atom3.y, atom3.z);
-
-                    dir2 = p3.clone();
-                    dir2.sub(p1);
-
-                    v = dir2.clone();
-                    v.cross(dir);
-                }
-            } else {
-                // get vector 2 different neighboring atom
-                i2 = (i + 1) % atom.bonds.length;
-                j2 = atom.bonds[i2];
-                atom3 = atoms[j2];
-                p3 = new $3Dmol.Vector3(atom3.x, atom3.y, atom3.z);
-
-                dir2 = p3.clone();
-                dir2.sub(p1);
-
-                v = dir2.clone();
-                v.cross(dir);
-            }
-
-            // especially for C#C (triple bond) dir and dir2
-            // may be opposites resulting in a zero v
-            if (v.lengthSq() < 0.01) {
-                v = dir.clone();
-                if (Math.abs(v.x) > 0.0001)
-                    v.y += 1;
-                else
-                    v.x += 1;
-            }
-
-            v.cross(dir);
-            v.normalize();
-            
-            return v;
-            
-            //v.multiplyScalar(r * 1.5);
-
-        }
-        
-        var getTripleBondPoints = function() {
-            
-            v.cross(dir);
-            v.normalize();
-            v.multiplyScalar(r * 3);
-
-            p1a = p1.clone();
-            p1a.add(v);
-            p1b = p1.clone();
-            p1b.sub(v);
-
-            p2a = p1a.clone();
-            p2a.add(dir);
-            p2b = p1b.clone();
-            p2b.add(dir);
-        }
-        
-        var addLine = function(vertexArray, colorArray, offset, p1, p2, c1) {
-            //make line from p1 to p2, does not incremeant counts
-            vertexArray[offset] = p1.x; vertexArray[offset+1] = p1.y; vertexArray[offset+2] = p1.z;
-            colorArray[offset] = c1.r; colorArray[offset+1] = c1.g; colorArray[offset+2] = c1.b;
-            vertexArray[offset+3] = p2.x; vertexArray[offset+4] = p2.y; vertexArray[offset+5] = p2.z;
-            colorArray[offset+3] = c1.r; colorArray[offset+4] = c1.g; colorArray[offset+5] = c1.b;            
-        }
-        
-        /**@typedef LineStyleSpec
-         * @prop {boolean} hidden - do not show line
-         * @prop {number} linewidth 
-         * @prop {ColorschemeSpec} colorscheme - element based coloring
-         * @prop {ColorSpec} color - fixed coloring, overrides colorscheme
-         */
-        
-        // bonds - both atoms must match bond style
-        // standardize on only drawing for lowest to highest
-        /**
-         * 
-         * @param {AtomSpec}
-         *            atom
-         * @param {AtomSpec[]} atoms
-         * @param {$3Dmol.Geometry[]} geos
-         */
-        var drawBondLines = function(atom, atoms, geos) {
-            if (!atom.style.line)
-                return;
-            var style = atom.style.line;
-            if (style.hidden)
-                return;
-
-            // have a separate geometry for each linewidth
-            var linewidth = (style.linewidth || defaultlineWidth);
-
-            if (!geos[linewidth])
-                geos[linewidth] = new $3Dmol.Geometry();
-            /** @type {geometryGroup} */
-            var geoGroup = geos[linewidth].updateGeoGroup(6*atom.bonds.length); //reserve enough space even for triple bonds
-            
-            var vertexArray = geoGroup.vertexArray;
-            var colorArray = geoGroup.colorArray;
-            
-            for ( var i = 0; i < atom.bonds.length; i++) {
-                var j = atom.bonds[i]; // our neighbor
-                
-                var atom2 = atoms[j];
-                if (!atom2.style.line)
-                    continue; // don't sweat the details
-
-                if (atom.serial >= atom2.serial) // only draw if less, this way we can do multi bonds correctly
-                    continue;
-                var p1 = new $3Dmol.Vector3(atom.x, atom.y, atom.z);
-                var p2 = new $3Dmol.Vector3(atom2.x, atom2.y, atom2.z);                
-                var mp = p1.clone().add(p2).multiplyScalar(0.5);
-                var singleBond = false;               
-                
-                if (atom.clickable){
-                    if (atom.intersectionShape === undefined)
-                        atom.intersectionShape = {sphere : [], cylinder : [], line : [], triangle : []};
-                    atom.intersectionShape.line.push(p1);
-                    atom.intersectionShape.line.push(p2);
-                }
-
-                var c1 = $3Dmol.getColorFromStyle(atom, atom.style.line);
-                var c2 = $3Dmol.getColorFromStyle(atom2, atom2.style.line);
-               
-                if(atom.bondStyles && atom.bondStyles[i]) {
-                    var bstyle = atom.bondStyles[i];
-                    if(!bstyle.iswire) {
-                        continue;
-                    }
-                    if(bstyle.radius) bondR = bstyle.radius;
-                    if(bstyle.singleBond) singleBond = true;
-                    if(typeof(bstyle.color1) != "undefined") {
-                        c1 = $3Dmol.CC.color(bstyle.color1);
-                    }
-                    if(typeof(bstyle.color2) != "undefined") {
-                        c2 = $3Dmol.CC.color(bstyle.color2);
-                    }
-                }
-
-                var offset = geoGroup.vertices*3;
-                
-                if(atom.bondOrder[i] > 1 && atom.bondOrder[i] < 4 && !singleBond) {
-                    var v = getSideBondV(atom, atom2, i);
-                    var dir = p2.clone();
-                    dir.sub(p1);
-                    
-                    if(atom.bondOrder[i] == 2) { //double
-                        
-                        v.multiplyScalar(.1);
-                           p1a = p1.clone();
-                        p1a.add(v);
-                        p1b = p1.clone();
-                        p1b.sub(v);
-
-                        p2a = p1a.clone();
-                        p2a.add(dir);
-                        p2b = p1b.clone();
-                        p2b.add(dir);
-                        
-                        if(c1 == c2) {
-                            geoGroup.vertices += 4;
-                            addLine(vertexArray, colorArray, offset, p1a, p2a, c1);                            
-                            addLine(vertexArray, colorArray, offset+6, p1b, p2b, c1);                            
-                        }
-                        else {
-                            geoGroup.vertices += 8;
-                            dir.multiplyScalar(0.5);
-                            var mpa = p1a.clone();
-                            mpa.add(dir);
-                            var mpb = p1b.clone();
-                            mpb.add(dir);
-                            
-                            addLine(vertexArray, colorArray, offset, p1a, mpa, c1);                            
-                            addLine(vertexArray, colorArray, offset+6, mpa, p2a, c2);                            
-                            addLine(vertexArray, colorArray, offset+12, p1b, mpb, c1); 
-                            addLine(vertexArray, colorArray, offset+18, mpb, p2b, c2); 
-                        }
-                    }
-                    else if(atom.bondOrder[i] == 3) { //triple
-                        
-                        v.multiplyScalar(.1);
-                           p1a = p1.clone();
-                        p1a.add(v);
-                        p1b = p1.clone();
-                        p1b.sub(v);
-
-                        p2a = p1a.clone();
-                        p2a.add(dir);
-                        p2b = p1b.clone();
-                        p2b.add(dir);
-                        
-                        if(c1 == c2) {
-                            geoGroup.vertices += 6;
-                            addLine(vertexArray, colorArray, offset, p1, p2, c1);                            
-                            addLine(vertexArray, colorArray, offset+6, p1a, p2a, c1);                            
-                            addLine(vertexArray, colorArray, offset+12, p1b, p2b, c1);                            
-                        }
-                        else {
-                            geoGroup.vertices += 12;
-                            dir.multiplyScalar(0.5);
-                            var mpa = p1a.clone();
-                            mpa.add(dir);
-                            var mpb = p1b.clone();
-                            mpb.add(dir);
-
-                            addLine(vertexArray, colorArray, offset, p1, mp, c1);                            
-                            addLine(vertexArray, colorArray, offset+6, mp, p2, c2);
-                            addLine(vertexArray, colorArray, offset+12, p1a, mpa, c1);                            
-                            addLine(vertexArray, colorArray, offset+18, mpa, p2a, c2);                            
-                            addLine(vertexArray, colorArray, offset+24, p1b, mpb, c1); 
-                            addLine(vertexArray, colorArray, offset+30, mpb, p2b, c2); 
-                        }
-                    }
-                }
-                else { //single bond                                    
-                    if(c1 == c2) {
-                        geoGroup.vertices += 2;
-                        addLine(vertexArray, colorArray, offset, p1, p2, c1);
-                    } else {
-                        geoGroup.vertices += 4;
-                        addLine(vertexArray, colorArray, offset, p1, mp, c1);
-                        addLine(vertexArray, colorArray, offset+6, mp, p2, c2);                        
-                    }
-                    
-                }
-            }
-
-        };
-
-        // bonds as cylinders
-        var defaultStickRadius = 0.25;
-
-        /**@typedef SphereStyleSpec
-         * @prop {boolean} hidden - do not show atom
-         * @prop {number} radius - override van der waals radius
-         * @prop {number} scale - scale radius by specified amount
-         * @prop {ColorschemeSpec} colorscheme - element based coloring
-         * @prop {ColorSpec} color - fixed coloring, overrides colorscheme
-         */
-        
-        //sphere drawing
-        //See also: drawCylinder
-        /** 
-         * 
-         * @param {AtomSpec} atom
-         * @param {$3Dmol.Geometry} geo
-         */
-        var drawAtomSphere = function(atom, geo) {
-            
-            if (!atom.style.sphere)
-                return;
-            var style = atom.style.sphere;
-            if (style.hidden)
-                return;
-                                                                 
-            var C = $3Dmol.getColorFromStyle(atom, style);
-            
-            var x, y;
-            var radius = getRadiusFromStyle(atom, style);
-            
-            if ((atom.clickable === true) && (atom.intersectionShape !== undefined)) {
-                var center = new $3Dmol.Vector3(atom.x, atom.y, atom.z);
-                atom.intersectionShape.sphere.push(new $3Dmol.Sphere(center, radius));
-            }
-            
-            $3Dmol.GLDraw.drawSphere(geo, atom, radius, C);    
-            
-        };
-        
-        //dkoes - test code for sphere imposters
-        var drawAtomImposter = function(atom, geo) {
-            
-            if (!atom.style.spherei)
-                return;
-            var style = atom.style.spherei;
-            if (style.hidden)
-                return;
-            
-            var radius = getRadiusFromStyle(atom, style);
-            var C = $3Dmol.getColorFromStyle(atom, style);
-            
-            //create flat square                       
-            
-            var geoGroup = geo.updateGeoGroup(4);
-            var startv =  geoGroup.vertices;
-            var start = startv*3;
-            var vertexArray = geoGroup.vertexArray;
-            var colorArray = geoGroup.colorArray;
-            
-            //use center point for each vertex
-            for(var i = 0; i < 4; i++) {
-                vertexArray[start+3*i] = atom.x;
-                vertexArray[start+3*i+1] = atom.y ;
-                vertexArray[start+3*i+2] = atom.z;                           
-            }
-            
-
-            //same colors for all 4 vertices
-            var normalArray = geoGroup.normalArray;
-            var colorArray = geoGroup.colorArray;
-            for(var i = 0; i < 4; i++) {
-                colorArray[start+3*i] = C.r;
-                colorArray[start+3*i+1] = C.g;
-                colorArray[start+3*i+2] = C.b;
-                
-            }
-            
-            normalArray[start+0] = -radius;
-            normalArray[start+1] = radius;
-            normalArray[start+2] = 0;
-            
-            normalArray[start+3] = -radius;
-            normalArray[start+4] = -radius;
-            normalArray[start+5] = 0;
-            
-            normalArray[start+6] = radius;
-            normalArray[start+7] = -radius;
-            normalArray[start+8] = 0;
-            
-            normalArray[start+9] = radius;
-            normalArray[start+10] = radius;
-            normalArray[start+11] = 0;
-            
-            geoGroup.vertices += 4;
-            
-            //two faces
-            var faceArray = geoGroup.faceArray;
-            var faceoffset = geoGroup.faceidx; //not number faces, but index
-            faceArray[faceoffset+0] = startv;
-            faceArray[faceoffset+1] = startv+1;
-            faceArray[faceoffset+2] = startv+2;
-            faceArray[faceoffset+3] = startv+2;
-            faceArray[faceoffset+4] = startv+3;
-            faceArray[faceoffset+5] = startv;
-            geoGroup.faceidx += 6;
-            
-        };
-                
-           
-        /**@typedef StickStyleSpec
-         * @prop {boolean} hidden - do not show 
-         * @prop {number} radius 
-         * @prop {boolean} singleBonds - draw all bonds as single bonds if set
-         * @prop {ColorschemeSpec} colorscheme - element based coloring
-         * @prop {ColorSpec} color - fixed coloring, overrides colorscheme
-         */
-        
-        // draws cylinders and small spheres (at bond radius)
-        var drawBondSticks = function(atom, atoms, geo) {
-            if (!atom.style.stick)
-                return;
-            var style = atom.style.stick;
-            if (style.hidden)
-                return;
-
-            var atomBondR = style.radius || defaultStickRadius;
-            var bondR = atomBondR;
-            var atomSingleBond = style.singleBonds || false;
-            var fromCap = 0, toCap = 0;
-
-            var C1 = $3Dmol.getColorFromStyle(atom, style);
-
-            var mp, mp1, mp2;
-            
-            if (!atom.capDrawn && atom.bonds.length < 4)
-                fromCap = 2;              
-                
-            for (var i = 0; i < atom.bonds.length; i++) {
-                var j = atom.bonds[i]; // our neighbor
-                var atom2 = atoms[j]; //parsePDB, etc should only add defined bonds
-                
-                if (atom.serial < atom2.serial) {// only draw if less, this
-                    // lets us combine
-                    // cylinders of the same
-                    // color
-                    var style2 = atom2.style;
-                    if (!style2.stick)
-                        continue; // don't sweat the details                     
-                   
-                    var C2 = $3Dmol.getColorFromStyle(atom2, style2.stick);
-                    
-                    //support bond specific styles
-                    bondR = atomBondR;                    
-                    var singleBond = atomSingleBond;
-                    if(atom.bondStyles && atom.bondStyles[i]) {
-                        var bstyle = atom.bondStyles[i];
-                        if(bstyle.iswire) {
-                            continue;
-                        }
-                        if(bstyle.radius) bondR = bstyle.radius;
-                        if(bstyle.singleBond) singleBond = true;
-                        if(typeof(bstyle.color1) != "undefined") {
-                            C1 = $3Dmol.CC.color(bstyle.color1);
-                        }
-                        if(typeof(bstyle.color2) != "undefined") {
-                            C2 = $3Dmol.CC.color(bstyle.color2);
-                        }
-                    }
-                    var p1 = new $3Dmol.Vector3(atom.x, atom.y, atom.z);
-                    var p2 = new $3Dmol.Vector3(atom2.x, atom2.y, atom2.z);
-
-                    // draw cylinders
-                    if (atom.bondOrder[i] === 1 || singleBond) {
-
-                        if (!atom2.capDrawn && atom2.bonds.length < 4)
-                            toCap = 2;       
-                                                
-                        if (C1 != C2) {
-                            mp = new $3Dmol.Vector3().addVectors(p1, p2)
-                                    .multiplyScalar(0.5);
-                            $3Dmol.GLDraw.drawCylinder(geo, p1, mp, bondR, C1, fromCap, 0);
-                            $3Dmol.GLDraw.drawCylinder(geo, mp, p2, bondR, C2, 0, toCap);
-                        } else {
-                            $3Dmol.GLDraw.drawCylinder(geo, p1, p2, bondR, C1, fromCap, toCap);
-                        }
-                        
-                        if (atom.clickable || atom2.clickable) {
-                            mp = new $3Dmol.Vector3().addVectors(p1, p2).multiplyScalar(0.5);
-                            if (atom.clickable){
-                                var cylinder1 = new $3Dmol.Cylinder(p1 , mp , bondR);
-                                var sphere1 = new $3Dmol.Sphere(p1 , bondR);
-                                atom.intersectionShape.cylinder.push(cylinder1);   
-                                atom.intersectionShape.sphere.push(sphere1);                             
-                            }
-                            if (atom2.clickable){
-                                var cylinder2 = new $3Dmol.Cylinder(p2 , mp , bondR);
-                                var sphere2 = new $3Dmol.Sphere(p2 , bondR);
-                                atom2.intersectionShape.cylinder.push(cylinder2);
-                                atom2.intersectionShape.sphere.push(sphere2);
-                            }
-
-                        }
-                        
-                    } 
-                    
-                    else if (atom.bondOrder[i] > 1) {
-                        var mfromCap = 0; mtoCap = 0; //multi bond caps
-                        
-                        if(bondR != atomBondR) {
-                            //assume jmol style multiple bonds - the radius doesn't fit within atom sphere
-                            mfromCap = 2;
-                            mtoCap = 2;
-                        }
-                        
-                        var dir = p2.clone();
-                        var v = null;
-                        dir.sub(p1);
-                        
-                        var r, p1a, p1b, p2a, p2b;
-                        var v = getSideBondV(atom, atom2, i);
-                        
-                        if (atom.bondOrder[i] == 2) {
-                            var r = bondR/2.5;
-                            var v = getSideBondV(atom, atom2, i);
-                            
-                            v.multiplyScalar(r*1.5);
-                            p1a = p1.clone();
-                            p1a.add(v);
-                            p1b = p1.clone();
-                            p1b.sub(v);
-
-                            p2a = p1a.clone();
-                            p2a.add(dir);
-                            p2b = p1b.clone();
-                            p2b.add(dir);
-
-                                                                 
-                            if (C1 != C2) {
-                                mp = new $3Dmol.Vector3().addVectors(p1a, p2a)
-                                        .multiplyScalar(0.5);
-                                mp2 = new $3Dmol.Vector3().addVectors(p1b, p2b)
-                                        .multiplyScalar(0.5);
-                                $3Dmol.GLDraw.drawCylinder(geo, p1a, mp, r, C1, mfromCap, 0);
-                                $3Dmol.GLDraw.drawCylinder(geo, mp, p2a, r, C2, 0, mtoCap);
-                                $3Dmol.GLDraw.drawCylinder(geo, p1b, mp2, r, C1, mfromCap, 0);
-                                $3Dmol.GLDraw.drawCylinder(geo, mp2, p2b, r, C2, 0, mtoCap);
-                            } else {
-                                $3Dmol.GLDraw.drawCylinder(geo, p1a, p2a, r, C1, mfromCap, mtoCap);
-                                $3Dmol.GLDraw.drawCylinder(geo, p1b, p2b, r, C1, mfromCap, mtoCap);
-                            }
-                            if (atom.clickable || atom2.clickable){
-                                mp = new $3Dmol.Vector3().addVectors(p1a, p2a)
-                                               .multiplyScalar(0.5);
-                                mp2 = new $3Dmol.Vector3().addVectors(p1b, p2b)
-                                                .multiplyScalar(0.5);
-                                if (atom.clickable) {
-                                    cylinder1a = new $3Dmol.Cylinder(p1a , mp , r);
-                                    cylinder1b = new $3Dmol.Cylinder(p1b , mp2 , r);
-                                    atom.intersectionShape.cylinder.push(cylinder1a);
-                                    atom.intersectionShape.cylinder.push(cylinder1b);
-                                }
-                                if (atom2.clickable) {
-                                    cylinder2a = new $3Dmol.Cylinder(p2a , mp , r);
-                                    cylinder2b = new $3Dmol.Cylinder(p2b , mp2 , r);
-                                    atom2.intersectionShape.cylinder.push(cylinder2a);
-                                    atom2.intersectionShape.cylinder.push(cylinder2b);                               
-                                }
-                            }
-                        } 
-                        else if (atom.bondOrder[i] == 3) {
-                            r = bondR / 4;
-                            v.cross(dir);
-                            v.normalize();
-                            v.multiplyScalar(r * 3);
-
-                            p1a = p1.clone();
-                            p1a.add(v);
-                            p1b = p1.clone();
-                            p1b.sub(v);
-
-                            p2a = p1a.clone();
-                            p2a.add(dir);
-                            p2b = p1b.clone();
-                            p2b.add(dir);
-
-                            if (C1 != C2) {
-                                mp = new $3Dmol.Vector3().addVectors(p1a, p2a)
-                                        .multiplyScalar(0.5);
-                                mp2 = new $3Dmol.Vector3().addVectors(p1b, p2b)
-                                        .multiplyScalar(0.5);
-                                mp3 = new $3Dmol.Vector3().addVectors(p1, p2)
-                                        .multiplyScalar(0.5);
-                                $3Dmol.GLDraw.drawCylinder(geo, p1a, mp, r, C1, mfromCap, 0);
-                                $3Dmol.GLDraw.drawCylinder(geo, mp, p2a, r, C2, 0, mtoCap);
-                                $3Dmol.GLDraw.drawCylinder(geo, p1, mp3, r, C1, fromCap, 0);
-                                $3Dmol.GLDraw.drawCylinder(geo, mp3, p2, r, C2, 0, toCap);
-                                $3Dmol.GLDraw.drawCylinder(geo, p1b, mp2, r, C1, mfromCap, 0);
-                                $3Dmol.GLDraw.drawCylinder(geo, mp2, p2b, r, C2, 0, mtoCap);
-                            } else {
-                                $3Dmol.GLDraw.drawCylinder(geo, p1a, p2a, r, C1, mfromCap, mtoCap);
-                                $3Dmol.GLDraw.drawCylinder(geo, p1, p2, r, C1, fromCap, toCap);
-                                $3Dmol.GLDraw.drawCylinder(geo, p1b, p2b, r, C1, mfromCap, mtoCap);
-
-                            }
-                            if (atom.clickable || atom2.clickable) {
-                                mp = new $3Dmol.Vector3().addVectors(p1a, p2a)
-                                        .multiplyScalar(0.5);
-                                mp2 = new $3Dmol.Vector3().addVectors(p1b, p2b)
-                                        .multiplyScalar(0.5);
-                                mp3 = new $3Dmol.Vector3().addVectors(p1, p2)
-                                        .multiplyScalar(0.5);
-                                                                
-                                if (atom.clickable) {
-                                    cylinder1a = new $3Dmol.Cylinder(p1a.clone(), mp.clone(), r);
-                                    cylinder1b = new $3Dmol.Cylinder(p1b.clone(), mp2.clone(), r);
-                                    cylinder1c = new $3Dmol.Cylinder(p1.clone(), mp3.clone(), r);
-                                    atom.intersectionShape.cylinder.push(cylinder1a);
-                                    atom.intersectionShape.cylinder.push(cylinder1b);
-                                    atom.intersectionShape.cylinder.push(cylinder1c);
-                                } 
-                                if (atom2.clickable) {                               
-                                    cylinder2a = new $3Dmol.Cylinder(p2a.clone(), mp.clone(), r);
-                                    cylinder2b = new $3Dmol.Cylinder(p2b.clone(), mp2.clone(), r);
-                                    cylinder2c = new $3Dmol.Cylinder(p2.clone(), mp3.clone(), r);
-                                    atom2.intersectionShape.cylinder.push(cylinder2a);
-                                    atom2.intersectionShape.cylinder.push(cylinder2b);
-                                    atom2.intersectionShape.cylinder.push(cylinder2c);                                
-                                }
-                            }
-                        }
-                    }
-                     
-                }                   
-                                 
-            }            
-
-            // draw non bonded heteroatoms as spheres
-            var drawSphere = false;
-            var numsinglebonds = 0;
-            var differentradii = false;
-            //also, if any bonds were drawn as multiples, need sphere
-            for(var i = 0; i < atom.bonds.length; i++) {
-                var singleBond = atomSingleBond;
-                if(atom.bondStyles && atom.bondStyles[i]) {
-                    var bstyle = atom.bondStyles[i];
-                    if(bstyle.singleBond) singleBond = true;
-                    if(bstyle.radius && bstyle.radius != atomBondR) {
-                        differentradii = true;
-                    }
-                }
-                if(singleBond || atom.bondOrder[i] == 1) {
-                    numsinglebonds++;
-                }
-            }
-            
-            if(differentradii) { //jmol style double/triple bonds - no sphere
-                if(numsinglebonds > 0) drawSphere = true; //unless needed as a cap
-            }
-            else if(numsinglebonds == 0 && atom.bonds.length > 0) {
-                drawSphere = true;
-            }
-           
-            if (drawSphere) {
-                var savedstyle = atom.style;
-                bondR = atomBondR;
-                //do not use bond style as this can be variable, particularly
-                //with jmol export of double/triple bonds
-                $3Dmol.GLDraw.drawSphere(geo, atom, bondR, C1);    
-            }
-            
-        };
-
-        // go through all the atoms and regenerate their geometries
-        // we try to have one geometry for each style since this is much much
-        // faster
-        // at some point we should optimize this to avoid unnecessary
-        // recalculation
-        /** param {AtomSpec[]} atoms */
-        var createMolObj = function(atoms) {
-
-            var ret = new $3Dmol.Object3D();
-            var cartoonAtoms = [];
-            var lineGeometries = {};
-            var crossGeometries = {};
-            var sphereGeometry = new $3Dmol.Geometry(true);                                                         
-            var imposterGeometry = new $3Dmol.Geometry(true);                                                         
-            var stickGeometry = new $3Dmol.Geometry(true);
-            var i, j, n, testOpacities;
-            var opacities = {};
-            var range = [Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY];
-            for (i = 0, n = atoms.length; i < n; i++) {
-                var atom = atoms[i];
-                // recreate gl info for each atom as necessary
-                // set up appropriate intersection spheres for clickable atoms
-
-                if (atom && atom.style) {
-
-                    if (atom.clickable && atom.intersectionShape === undefined)
-                        atom.intersectionShape = {sphere: [], cylinder: [], line: [], triangle : []};
-
-                    testOpacities = {line:undefined, cross:undefined, stick:undefined, sphere:undefined};
-                    for (j in testOpacities)
-                    {
-                        if (atom.style[j])
-                        {
-                            if (atom.style[j].opacity)
-                                testOpacities[j] = parseFloat(atom.style[j].opacity);
-                            else
-                                testOpacities[j] = 1;
-
-                        } else testOpacities[j] = undefined;
-
-                        if (opacities[j])
-                        {
-                            if (testOpacities[j] != undefined && opacities[j] != testOpacities[j])
-                            {
-                                console.log("Warning: " + j + " opacity is ambiguous");
-                                opacities[j] = 1;
-                            }
-
-                        } else opacities[j] = testOpacities[j];
-                    }
-                    
-                    drawAtomSphere(atom, sphereGeometry);
-                    drawAtomImposter(atom, imposterGeometry);
-                    drawAtomCross(atom, crossGeometries);
-                    drawBondLines(atom, atoms, lineGeometries);
-                    drawBondSticks(atom, atoms, stickGeometry);
-
-                    if (typeof (atom.style.cartoon) !== "undefined" && !atom.style.cartoon.hidden) {
-                        //gradient color scheme range
-                        if (atom.style.cartoon.color === "spectrum" && typeof(atom.resi) === "number" && !atom.hetflag) {                            
-                            if (atom.resi < range[0])
-                                range[0] = atom.resi;
-                            if (atom.resi > range[1])
-                                range[1] = atom.resi;
-                        }
-
-                        cartoonAtoms.push(atom);
-                    }                   
-                }
-            }
-            // create cartoon if needed - this is a whole model analysis
-            if (cartoonAtoms.length > 0) {
-                var gradientscheme = null;
-                //TODO: Should have an option to choose gradient type
-                if (range[0] < range[1])
-                    gradientscheme = new $3Dmol.Gradient.Sinebow(range[0], range[1]);
-
-                $3Dmol.drawCartoon(ret, cartoonAtoms, gradientscheme);
-
-            }
-
-            // add sphere geometry
-            if (sphereGeometry.vertices > 0) {
-                var sphereMaterial = new $3Dmol.MeshLambertMaterial({
-                    ambient : 0x000000,
-                    vertexColors : true,
-                    reflectivity : 0,
-                });
-                if (opacities.sphere < 1 && opacities.sphere >= 0)
-                {
-                    sphereMaterial.transparent = true;
-                    sphereMaterial.opacity = opacities.sphere;
-                }
-                
-                //Initialize buffers in geometry                
-                sphereGeometry.initTypedArrays();   
-				
-                var sphere = new $3Dmol.Mesh(sphereGeometry, sphereMaterial);
-                ret.add(sphere);
-            }
-
-            // add imposter geometry
-            if (imposterGeometry.vertices > 0) {
-                var imposterMaterial = new $3Dmol.ImposterMaterial({
-                    ambient : 0x000000,
-                    vertexColors : true,
-                    reflectivity : 0
-                });
-                
-                //Initialize buffers in geometry                
-                imposterGeometry.initTypedArrays();
-                
-                var spherei = new $3Dmol.Mesh(imposterGeometry, imposterMaterial);
-                console.log("spherei geometry " + imposterGeometry.vertices.length);
-
-                ret.add(spherei);
-            }
-            
-            // add stick geometry
-            if (stickGeometry.vertices > 0) {
-                var cylinderMaterial = new $3Dmol.MeshLambertMaterial({
-                    vertexColors : true,
-                    ambient : 0x000000,
-                    reflectivity : 0
-                });
-                if (opacities.stick < 1 && opacities.stick >= 0)
-                {
-                    cylinderMaterial.transparent = true;
-                    cylinderMaterial.opacity = opacities.stick;
-                }
-
-                //Initialize buffers in geometry                
-                stickGeometry.initTypedArrays();
-                
-                if (cylinderMaterial.wireframe)
-                    stickGeometry.setUpWireframe();
-            
-                var sticks = new $3Dmol.Mesh(stickGeometry, cylinderMaterial);
-                ret.add(sticks);
-            }
-            
-            //var linewidth;
-            // add any line geometries, distinguished by line width
-            for (i in lineGeometries) {
-                if (lineGeometries.hasOwnProperty(i)) {
-                    var linewidth = i;
-                    var lineMaterial = new $3Dmol.LineBasicMaterial({
-                        linewidth : linewidth,
-                        vertexColors : true
-                    });
-                    if (opacities.line < 1 && opacities.line >= 0)
-                    {
-                        lineMaterial.transparent = true;
-                        lineMaterial.opacity = opacities.line;
-                    }
-                    
-                    lineGeometries[i].initTypedArrays();
-                    
-                    var line = new $3Dmol.Line(lineGeometries[i], lineMaterial,
-                            $3Dmol.LinePieces);
-
-                    ret.add(line);
-                }
-            }
-
-            // add any cross geometries
-            for (i in crossGeometries) {
-                if (crossGeometries.hasOwnProperty(i)) {
-                    var linewidth = i;
-                    var crossMaterial = new $3Dmol.LineBasicMaterial({
-                        linewidth : linewidth,
-                        vertexColors : true
-                    });
-                    if (opacities.cross < 1 && opacities.cross >= 0)
-                    {
-                        crossMaterial.transparent = true;
-                        crossMaterial.opacity = opacities.cross;
-                    }
-
-                    crossGeometries[i].initTypedArrays();
-                    
-                    var cross = new $3Dmol.Line(crossGeometries[i], crossMaterial,
-                            $3Dmol.LinePieces);
-
-                    ret.add(cross);
-                }
-            }
-            
-            //for BIOMT assembly
-            if (dontDuplicateAtoms && modelData.symmetries && modelData.symmetries.length > 0) {
-                var finalRet = new $3Dmol.Object3D();
-                var t;
-                for (t = 0; t < modelData.symmetries.length; t++) {
-                    var transformedRet = new $3Dmol.Object3D();
-                    transformedRet = ret.clone();
-                    transformedRet.matrix.copy(modelData.symmetries[t]);
-                    transformedRet.matrixAutoUpdate = false;
-                    finalRet.add(transformedRet);
-                }
-                return finalRet;
-            }
-
-            return ret;
-        };
-        
-        
-        this.getCrystData = function() {
-            if (modelData.cryst) {
-                return modelData.cryst;
-            }
-            else {
-                return null;
-            }
-        }
-        
-        /**
-         * Returns list of rotational/translational matrices if there is BIOMT data
-         * Otherwise returns a list of just the ID matrix
-         *
-         * @function $3Dmol.GlModel#getSymmetries
-         * @return {Array<$3Dmol.Matrix4>}
-         *
-         */
-        this.getSymmetries = function() {
-            
-            if (typeof(modelData.symmetries) == 'undefined') {
-                modelData.symmetries = [idMatrix];
-            }
-            return modelData.symmetries; 
-        };
-        
-        /**
-         * Sets symmetries based on specified matrices in list
-         *
-         * @function $3Dmol.GlModel#setSymmetries
-         * @param {Array<$3Dmol.Matrix4>} list
-         *
-         */
-        this.setSymmetries = function(list) {
-            if (typeof(list) == "undefined") { //delete sym data
-                modelData.symmetries = [idMatrix];
-            }
-            else {
-                modelData.symmetries = list;
-            }
-        };
-
-        /**
-         * Returns model id number
-         * 
-         * @function $3Dmol.GLModel#getID
-         * @return {number} Model ID
-         */
-        this.getID = function() {
-            return id;
-        };
-        
-        /**
-         * Returns model's frames property, a list of atom lists
-         * 
-         * @function $3Dmol.GLModel#getFrames
-         * @return {Array.<Object>}
-         */
-        this.getFrames = function() {
-            return frames;
-        };
-        
-        /**
-         * Sets model's atomlist to specified frame
-         * Sets to last frame if framenum out of range
-         * 
-         * @function $3Dmol.GLModel#setFrame
-         * @param {number} framenum - model's atoms are set to this index in frames list
-         */
-        this.setFrame = function(framenum) {
-            if (frames.length == 0) {
-                return;
-            }
-            if (framenum >= 0 && framenum < frames.length) {
-                atoms = frames[framenum];
-            }
-            else {
-                atoms = frames[frames.length-1];
-            }
-            molObj = null;
-        };
-        
-        /**
-         * Add atoms as frames of model
-         * 
-         * @function $3Dmol.GLModel#addFrame
-         * @param {AtomSpec} atom - atoms to be added
-         */
-        this.addFrame = function(atoms) {
-            frames.push(atoms);
-        };
-
-        
-        /**
-         * If model atoms have dx, dy, dz properties (in some xyz files), vibrate populates the model's frame property based on parameters.
-         * Model can then be animated
-         * 
-         * @function $3Dmol.GLModel#vibrate
-         * @param {number} numFrames - number of frames to be created, default to 10
-         * @param {number} amplitude - amplitude of distortion, default to 1 (full)
-         */
-        this.vibrate = function(numFrames, amplitude) {
-            var vectors = [];
-            var amplitude = amplitude || 1;
-            var numFrames = numFrames || 10; 
-            for (var i = 0; i < atoms.length; i++) {
-                var vector = new $3Dmol.Vector3(atoms[i].dx, atoms[i].dy, atoms[i].dz);
-                vectors.push(vector);
-            }
-            numFrames--;
-            for (var i = 1; i <= numFrames; i++) {
-                var newAtoms = [];
-                for (var j = 0; j < atoms.length; j++) {
-                    var newVector = new $3Dmol.Vector3(atoms[j].dx, atoms[j].dy, atoms[j].dz);
-                    var starting = new $3Dmol.Vector3(atoms[j].x, atoms[j].y, atoms[j].z);
-                    newVector.sub(starting);
-                    newVector.multiplyScalar((i*amplitude)/numFrames);
-                    starting.add(newVector);
-                    var newAtom = {};
-                    for (var k in atoms[j]) {
-                        newAtom[k] = atoms[j][k];
-                    }
-                    newAtom.x = starting.x;
-                    newAtom.y = starting.y;
-                    newAtom.z = starting.z;
-                    newAtoms.push(newAtom);
-                }
-                frames.push(newAtoms);
-            }
-            frames.unshift(atoms); //add 1st frame
-        };
-        
-        // set default style and colors for atoms
-        var setAtomDefaults = function(atoms, id) {
-            for ( var i = 0; i < atoms.length; i++) {
-                var atom = atoms[i];
-                if (atom) {
-                    atom.style = atom.style || defaultAtomStyle;
-                    atom.color = atom.color || ElementColors[atom.elem] || defaultColor;
-                    atom.model = id;
-                    if (atom.clickable)
-                        atom.intersectionShape = {sphere : [], cylinder : [], line : [], triangle : []};
-                }
-            }
-        };
-
-        /** add atoms to this model from molecular data string
-         * 
-         * @function $3Dmol.GLModel#addMolData
-         * @param {string|ArrayBuffer} data - atom structure file input data string, for gzipped input use ArrayBuffer
-         * @param {string} format - input file string format (e.g 'pdb', 'sdf', 'sdf.gz', etc.)
-         * @param {Object} options - format dependent options (e.g. 'options.keepH' to keep hydrogens)
-         */
-        this.addMolData = function(data, format, options) {
-            options = options || {}; 
-            format = format || "";
-            dontDuplicateAtoms = !options.duplicateAssemblyAtoms;
-            
-            if (!data)
-                return; //leave an empty model
-            
-            if(/\.gz$/.test(format)) {
-                //unzip gzipped files
-                format = format.replace(/\.gz$/,'');
-                try {
-                    data = pako.inflate(data, {to: 'string'});
-                } catch(err) {
-                    console.log(err);
-                }
-            }
-            
-            if(typeof($3Dmol.Parsers[format]) == "undefined") {
-            	//let someone provide a file name and get format from extension
-            	format = format.split('.').pop();
-            	if(typeof($3Dmol.Parsers[format]) == "undefined") {            	
-	                console.log("Unknown format: "+format);
-	                //try to guess correct format from data contents
-	                if(data.match(/^@<TRIPOS>MOLECULE/gm)) {
-	                    format = "mol2";
-	                } else if(data.match(/^HETATM/gm) || data.match(/^ATOM/gm)) {
-	                    format = "pdb";
-	                } else if(data.match(/^.*\n.*\n.\s*(\d+)\s+(\d+)/gm)){
-	                    format = "sdf"; //could look at line 3
-	                } else {
-	                    format = "xyz";
-	                }
-	                console.log("Best guess: "+format);
-            	}
-            }
-            var parse = $3Dmol.Parsers[format];
-            var parsedAtoms = parse(data, options, modelData);
-
-            if (frames.length == 0) { //first call
-                for (var i = 0; i < parsedAtoms.length; i++) {
-                    if (parsedAtoms[i].length != 0)
-                        frames.push(parsedAtoms[i]);
-                }
-                if(frames[0])
-                    atoms = frames[0];
-            }
-            
-            else { //subsequent calls
-                if (options.frames) { //add to new frame
-                    for (var i = 0; i < parsedAtoms.length; i++) {
-                        frames.push(parsedAtoms[i]);
-                    }
-                }
-                else { //add atoms to current frame
-                    for (var i = 0; i < parsedAtoms.length; i++) {
-                        this.addAtoms(parsedAtoms[i]); 
-                    }
-                }
-            }
-            
-            for (var i = 0; i < frames.length; i++) {
-                setAtomDefaults(frames[i], id);
-            }
-
-        };
-        
-        /** given a selection specification, return true if atom is selected
-         * 
-         * @function $3Dmol.GLModel#atomIsSelected
-         * @param {AtomSpec} atom
-         * @param {AtomSelectionSpec} sel
-         * @return {boolean}
-         */
-        this.atomIsSelected = function(atom, sel) {
-            if (typeof (sel) === "undefined")
-                return true; // undef gets all
-            var invert = !!sel.invert;
-            var ret = true;
-            for ( var key in sel) {
-                if(key === 'predicate') { //a user supplied function for evaluating atoms
-                    if(!sel['predicate'](atom) ) {
-                        ret = false;
-                        break;
-                    }
-                }
-
-                else if (sel.hasOwnProperty(key) && key != "props" && key != "invert" && key != "model" && key != "byres" && key != "expand" && key != "within") {
-
-                    // if something is in sel, atom must have it                    
-                    if (typeof (atom[key]) === "undefined") {
-                        ret = false;
-                        break;
-                    }
-                    var isokay = false;
-                    if(key === "bonds") {
-                        //special case counting number of bonds, for selecting nonbonded mostly
-                        var val = sel[key];
-                        if(val != atom.bonds.length) {
-                            ret = false;
-                            break;
-                        }
-                    }
-                    else if ($.isArray(sel[key])) {
-                        // can be any of the listed values
-                        var valarr = sel[key];
-                        for ( var i = 0; i < valarr.length; i++) {
-                            if (atom[key] == valarr[i]) {
-                                isokay = true;
-                                break;
-                            }
-                        }
-                        if (!isokay) {
-                            ret = false;
-                            break;
-                        }
-                    } else { // single match
-                        var val = sel[key];
-                        if (atom[key] != val) {
-                            ret = false;
-                            break;
-                        }
-                    }
-                }
-            }
-            
-            return invert ? !ret : ret;
-        };
-
-
-        /** return list of atoms selected by sel, this is specific to glmodel
-         * 
-         * @function $3Dmol.GLModel#selectedAtoms
-         * @param {AtomSelectionSpec} sel
-         * @return {Array.<Object>}
-         */
-        this.selectedAtoms = function(sel, from) {
-            var ret = [];
-            sel = sel || {};
-            if (!from) from = atoms;
-            var aLength = from.length;
-            for ( var i = 0; i < aLength; i++) {
-                var atom = from[i];
-                if (atom) {
-                    if (this.atomIsSelected(atom, sel))
-                        ret.push(atom);
-                }
-            }
-
-            // expand selection by some distance
-            if (sel.hasOwnProperty("expand")) {
-
-                // get atoms in expanded bounding box
-
-                var expand = expandAtomList(ret, parseFloat(sel.expand));
-                var retlen = ret.length;
-                for (var i = 0; i < expand.length; i++) {
-                    for (var j = 0; j < retlen; j++) {
-
-                        var dist = squaredDistance(expand[i], ret[j]);
-                        var thresh = Math.pow(sel.expand, 2);
-                        if (dist < thresh && dist > 0) {
-                            ret.push(expand[i]);
-                        }
-                    }
-                }
-            }
-
-            // selection within distance of sub-selection
-            if (sel.hasOwnProperty("within") && sel.within.hasOwnProperty("sel") && sel.within.hasOwnProperty("distance")) {
-
-                // get atoms in second selection
-                var sel2 = this.selectedAtoms(sel.within.sel, atoms);
-                var within = [];
-                for (var i = 0; i < sel2.length; i++) {
-                    for (var j = 0; j < ret.length; j++) {
-
-                        var dist = squaredDistance(sel2[i], ret[j]);
-                        var thresh = Math.pow(parseFloat(sel.within.distance), 2);
-                        if (dist < thresh && dist > 0) {
-                            within.push(ret[j]);
-                        }
-                    }
-                }
-                ret = within;
-            }
-
-            // byres selection flag
-            if (sel.hasOwnProperty("byres")) {
-
-                // Keep track of visited residues, visited atoms, and atom stack
-                var vResis = {};
-                var vAtoms = [];
-                var stack = [];
-
-                for (var i = 0; i < ret.length; i++) {
-                    
-                    // Check if atom is part of a residue, and that the residue hasn't been traversed yet
-                    var atom = ret[i];
-                    var c = atom.chain;
-                    var r = atom.resi;
-                    if (vResis[c] === undefined) vResis[c] = {};
-                    if (atom.hasOwnProperty("resi") && vResis[c][r] === undefined) {
-
-                        // Perform a depth-first search of atoms with the same resi
-                        vResis[c][r] = true;
-                        stack.push(atom);
-                        while(stack.length > 0) {
-                            atom = stack.pop();
-                            c = atom.chain;
-                            r = atom.resi;
-                            if (vAtoms[atom.index] === undefined) {
-                                vAtoms[atom.index] = true;
-                                for (var j = 0; j < atom.bonds.length; j++) {
-                                    var atom2 = atoms[atom.bonds[j]];
-                                    if (vAtoms[atom2.index] === undefined && atom2.hasOwnProperty("resi") && atom2.chain == c && atom2.resi == r) {
-                                        stack.push(atom2);
-                                        ret.push(atom2);
-                                    }
-                                }
-                            }
-                        }
-                    }   
-                }
-            }
-
-            return ret;
-        };
-
-        var squaredDistance = function(atom1, atom2) {
-            var xd = atom2.x - atom1.x;
-            var yd = atom2.y - atom1.y;
-            var zd = atom2.z - atom1.z;
-            return (Math.pow(xd, 2) + Math.pow(yd, 2) + Math.pow(zd, 2));
-        };
-
-        /** returns a list of atoms in the expanded bounding box, but not in the current one
-         *
-         *  Bounding box:
-         *
-         *    [ [ xmin, ymin, zmin ],
-         *      [ xmax, ymax, zmax ],
-         *      [ xctr, yctr, zctr ] ]
-         *
-         **/
-        var expandAtomList = function(atomList, amt) {
-
-            if (amt <= 0) return atomList;
-
-            var pb = $3Dmol.getExtent(atomList); // previous bounding box
-            var nb = [[], [], []]; // expanded bounding box
-
-            for (var i = 0; i < 3; i++)
-            {
-                nb[0][i] = pb[0][i]-amt;
-                nb[1][i] = pb[1][i]+amt;
-                nb[2][i] = pb[2][i];
-            }
-
-            // look in added box "shell" for new atoms
-            var expand = [];
-            for (var i = 0; i < atoms.length; i++) {
-
-                var x = atoms[i].x;
-                var y = atoms[i].y;
-                var z = atoms[i].z;
-
-                if (x >= nb[0][0] && x <= nb[1][0] && y >= nb[0][1] && y <= nb[1][1] && z >= nb[0][2] && z <= nb[1][2]) {
-                    if (!(x >= pb[0][0] && x <= pb[1][0] && y >= pb[0][1] && y <= pb[1][1] && z >= pb[0][2] && z <= pb[1][2])) {
-                        expand.push(atoms[i]);
-                    }
-                }
-            }
-            return expand;
-        };
-        
-        /** Add list of new atoms to model.  Adjusts bonds appropriately.
-         * 
-         * @function $3Dmol.GLModel#addAtoms
-         * @param {type} newatoms
-         */        
-        this.addAtoms = function(newatoms) {
-            molObj = null;
-            var start = atoms.length;
-            var indexmap = [];
-            // mapping from old index to new index
-            var i;
-            for(i = 0; i < newatoms.length; i++) {
-                if(typeof(newatoms[i].index) == "undefined")
-                    newatoms[i].index = i;
-                if(typeof(newatoms[i].serial) == "undefined")
-                    newatoms[i].serial = i;
-                indexmap[newatoms[i].index] = start+i;
-            }
-            
-            // copy and push newatoms onto atoms
-            for(i = 0; i < newatoms.length; i++) {
-                var olda = newatoms[i];
-                var nindex = indexmap[olda.index];
-                var a = $.extend(false, {}, olda);
-                a.index = nindex;
-                a.bonds = [];
-                a.bondOrder = [];
-                a.model = id;
-                a.style = a.style || defaultAtomStyle;
-                if(typeof(a.color) == "undefined")
-                    a.color = ElementColors[a.elem] || defaultColor;                
-                // copy over all bonds contained in selection,
-                // updating indices appropriately
-                var nbonds = olda.bonds ? olda.bonds.length : 0;
-                for(var j = 0; j < nbonds; j++) {
-                    var neigh = indexmap[olda.bonds[j]];
-                    if(typeof(neigh) != "undefined") {
-                        a.bonds.push(neigh);
-                        a.bondOrder.push(olda.bondOrder ? olda.bondOrder[j] : 1);
-                    }                
-                }
-                atoms.push(a);
-            }
-        };
-
-        /** Remove specified atoms from model
-         * 
-         * @function $3Dmol.GLModel#removeAtoms
-         * @param {type} badatoms
-         * @return {removeAtoms}
-         */
-        this.removeAtoms = function(badatoms) {
-            molObj = null;
-            // make map of all baddies
-            var baddies = [];
-            var i;
-            for(i = 0; i < badatoms.length; i++) {
-                baddies[badatoms[i].index] = true;
-            }
-            
-            // create list of good atoms
-            var newatoms = [];
-            for(i = 0; i < atoms.length; i++) {
-                var a = atoms[i];
-                if(!baddies[a.index])
-                    newatoms.push(a);
-            }
-            
-            // clear it all out
-            atoms = [];
-            // and add back in to get updated bonds
-            this.addAtoms(newatoms);
-        };
-        
-        
-        /** Set atom style of selected atoms
-         * 
-         * @function $3Dmol.GLModel#setStyle
-         * @param {AtomSelectionSpec} sel
-         * @param {AtomStyleSpec} style
-         * @param {boolean} add - if true, add to current style, don't replace
-         */
-        this.setStyle = function(sel, style, add) {
-            
-            if(typeof(style) === 'undefined' && typeof(add) == 'undefined') {
-                //if a single argument is provided, assume it is a style and select all
-                style = sel;
-                sel = {};
-            }
-            
-            // report to console if this is not a valid selector
-            var s;
-            for (s in sel) {
-                if(validAtomSelectionSpecs.indexOf(s) === -1) {
-                    console.log('Unknown selector ' + s);
-                }
-            }
-            // report to console if this is not a valid style
-            for (s in style) {
-                if(validAtomStyleSpecs.indexOf(s) === -1) {
-                    console.log('Unknown style ' + s);
-                }
-            }
-
-            var changedAtoms = false;
-            // somethings we only calculate if there is a change in a certain
-            // style, although these checks will only catch cases where both
-            // are either null or undefined
-            
-            var setStyleHelper = function(atomArr) {
-                var selected = that.selectedAtoms(sel, atomArr);
-                for (var i = 0; i < atomArr.length; i++) {
-                    if (atomArr[i]) atomArr[i].capDrawn = false; //reset for proper stick render
-                }
-            
-                for ( var i = 0; i < selected.length; i++) {                
-                    changedAtoms = true;
-                    if (selected[i].clickable) 
-                        selected[i].intersectionShape = {sphere : [], cylinder : [], line : [], triangle : []};                    
-                   
-
-                    if(!add) selected[i].style = {};
-                    for(s in style) {
-                        if(style.hasOwnProperty(s)) {
-                            selected[i].style[s]=selected[i].style[s]||{}; //create distinct object for each atom
-                            for(var prop in style[s]){
-                                selected[i].style[s][prop]=style[s][prop];
-                            }
-                        }
-                    }
-                }
-            }
-            
-            var that = this;
-            setStyleHelper(atoms);
-            for (var i = 0; i < frames.length; i++) {
-                setStyleHelper(frames[i]);
-            }
-            
-            if (changedAtoms)
-                molObj = null; //force rebuild
-            
-        };
-
-        /** Set clickable and callback of selected atoms
-         * 
-         * @function $3Dmol.GLModel#setClickable
-         * @param {AtomSelectionSpec} sel - atom selection to apply clickable settings to
-         * @param {boolean} clickable - whether click-handling is enabled for the selection
-         * @param {function} callback - function called when an atom in the selection is clicked
-         */
-        this.setClickable = function(sel, clickable, callback) {           
-
-            // report to console if this is not a valid selector
-            var s;
-            for (s in sel) {
-                if (validAtomSelectionSpecs.indexOf(s) === -1) {
-                    console.log('Unknown selector ' + s);
-                }
-            }
-
-            // make sure clickable is a boolean
-            clickable = !!clickable;
-
-            // report to console if callback is not a valid function
-            if (callback && typeof callback != "function") {
-                console.log("Callback is not a function");
-                return;
-            }
-
-            var i;
-            var selected = this.selectedAtoms(sel, atoms);
-            var len = selected.length;
-            for (i = 0; i < len; i++) {                
-
-                selected[i].intersectionShape = {sphere : [], cylinder : [], line : [], triangle : []};
-                selected[i].clickable = clickable;
-                if (callback) selected[i].callback = callback;
-
-            }
-
-            if (len > 0) molObj = null; // force rebuild to get correct intersection shapes         
-        };
-        
-        /** given a mapping from element to color, set atom colors
-         * 
-         * @function $3Dmol.GLModel#setColorByElement
-         * @param {type} sel
-         * @param {type} colors
-         */
-        this.setColorByElement = function(sel, colors) {
-            
-            if(molObj !== null && sameObj(colors,lastColors))
-                return; // don't recompute
-            lastColors = colors;
-            var atoms = this.selectedAtoms(sel, atoms);
-            if(atoms.length > 0)
-                molObj = null; // force rebuild
-            for ( var i = 0; i < atoms.length; i++) {
-                var a = atoms[i];
-                if(typeof(colors[a.elem]) !== "undefined") {
-                    a.color = colors[a.elem];
-                }
-            }
-        };
-        
-        /**
-         * @function $3Dmol.GLModel.setColorByProperty
-         * @param {type} sel
-         * @param {type} prop
-         * @param {type} scheme
-         */
-        this.setColorByProperty = function(sel, prop, scheme, range) {
-            var atoms = this.selectedAtoms(sel, atoms);
-            lastColors = null; // don't bother memoizing
-            if(atoms.length > 0)
-                molObj = null; // force rebuild
-            var min =  Number.POSITIVE_INFINITY;
-            var max =  Number.NEGATIVE_INFINITY;
-            var i, a;
-            
-            if(!range) { //no explicit range, get from scheme
-                range = scheme.range();
-            }
-            
-            if(!range) { //no range in scheme, compute the range for this model
-                range = $3Dmol.getPropertyRange(atoms, prop);
-            }
-            // now apply colors using scheme
-            for (i = 0; i < atoms.length; i++) {
-                a = atoms[i];
-                var val = $3Dmol.getAtomProperty(a, prop);
-                if(val != null) {
-                    a.color = scheme.valueToHex(parseFloat(a.properties[prop]), [range[0],range[1]]);
-                }                    
-            }
-        };
-
-        /** Convert the model into an object in the format of a ChemDoodle JSON model.
-         *
-         * @function $3Dmol.GLModel#toCDObject
-         * @param {boolean} whether or not to include style information. Defaults to false.
-         * @return {Object}
-         */
-        this.toCDObject = function(includeStyles) {
-            var out = { a:[], b:[] };
-            if (includeStyles) {
-                out.s = [];
-            }
-            for (var i = 0; i < atoms.length; i++) {
-                var atomJSON = {};
-                var atom = atoms[i];
-                atomJSON.x = atom.x;
-                atomJSON.y = atom.y;
-                atomJSON.z = atom.z;
-                if (atom.elem != "C") {
-                    atomJSON.l = atom.elem;
-                }
-                if (includeStyles) {
-                    var s = 0;
-                    while (s < out.s.length &&
-                          (JSON.stringify(atom.style) !== JSON.stringify(out.s[s]))) {
-                        s++;
-                    }
-                    if (s === out.s.length) {
-                        out.s.push(atom.style);
-                    }
-                    if (s !== 0) {
-                        atomJSON.s = s;
-                    }
-                }
-                
-                out.a.push(atomJSON);
-
-                for (var b = 0; b < atom.bonds.length; b++) {
-                    var firstAtom = i;
-                    var secondAtom = atom.bonds[b];
-                    if (firstAtom >= secondAtom)
-                        continue;
-                    var bond = {
-                        b: firstAtom,
-                        e: secondAtom
-                    };
-                    var bondOrder =  atom.bondOrder[b];
-                    if (bondOrder != 1) {
-                        bond.o = bondOrder;
-                    }
-                    out.b.push(bond);
-                }
-            }
-            return out;
-        }
-
-
-        /** manage the globj for this model in the possed modelGroup - if it has to be regenerated, remove and add
-         * 
-         * @function $3Dmol.GLModel#globj
-         * @param {$3Dmol.Object3D} group
-         */
-        this.globj = function(group) {
-            var time = new Date();
-            if(molObj === null) { // have to regenerate
-                molObj = createMolObj(atoms);
-                var time2 = new Date();
-                //console.log("object creation time: " + (time2 - time));
-                if(renderedMolObj) { // previously rendered, remove
-                    group.remove(renderedMolObj);
-                    renderedMolObj = null;
-                }
-                renderedMolObj = molObj.clone();
-                if(hidden) {
-                    renderedMolObj.setVisible(false);
-                    molObj.setVisible(false);
-                }
-                group.add(renderedMolObj);              
-            }
-        };
-        
-        /** Remove any renderable mol object from scene
-         * 
-         * @function $3Dmol.GLModel#removegl
-         * @param {$3Dmol.Object3D} group
-         */
-        this.removegl = function(group) {
-            if(renderedMolObj) {
-                //dispose of geos and materials
-                if (renderedMolObj.geometry !== undefined) renderedMolObj.geometry.dispose();             
-                if (renderedMolObj.material !== undefined) renderedMolObj.material.dispose();
-                group.remove(renderedMolObj);
-                renderedMolObj = null;
-            }
-            molObj = null;
-        };
-        
-        /** Don't show this model is future renderings.  Keep all styles and state
-         * so it can be efficiencly shown again.
-         * 
-         * @function $3Dmol.GLModel#hide
-         */
-        this.hide = function() {
-            hidden = true;
-            if(renderedMolObj) renderedMolObj.setVisible(false);
-            if(molObj) molObj.setVisible(false);
-        }
-        
-        this.show = function() {
-            hidden = false;
-            if(renderedMolObj) renderedMolObj.setVisible(true);
-            if(molObj) molObj.setVisible(true);
-        }
-        
-        /** Create labels for residues of selected atoms.
-         * Will create a single label at the center of mass of all atoms
-         * with the same chain,resn, and resi.
-         * @function $3Dmol.GLModel#addResLabels
-         * 
-         * @param {AtomSelectionSpec} sel
-         * @param {$3Dmol.GLViewer} viewer
-         */
-        this.addResLabels = function(sel, viewer, style) {
-            var atoms = this.selectedAtoms(sel, atoms);
-            var bylabel = {}
-            //collect by chain:resn:resi
-            for(var i = 0; i < atoms.length; i++) {
-                var a = atoms[i];
-                var c = a.chain;
-                var resn = a.resn;
-                var resi = a.resi;
-                var label =  resn + '' + resi;
-                if(!bylabel[c]) bylabel[c] = {};
-                if(!bylabel[c][label]) bylabel[c][label] = []
-                bylabel[c][label].push(a);
-            }
-            
-            var mystyle = $.extend(true, {}, style);
-            //now compute centers of mass
-            for(var c in bylabel) {
-                if(bylabel.hasOwnProperty(c)) {
-                    var labels = bylabel[c];
-                    for(var label in labels) {
-                        if(labels.hasOwnProperty(label)) {
-                            var atoms = labels[label];
-                            var sum = new $3Dmol.Vector3(0,0,0);
-                            for(var i = 0; i < atoms.length; i++) {
-                                var a = atoms[i];
-                                sum.x += a.x;
-                                sum.y += a.y;
-                                sum.z += a.z;
-                            }
-                            sum.divideScalar(atoms.length);
-                            mystyle.position = sum;
-                            viewer.addLabel(label, mystyle);
-                        }                        
-                    }
-                }
-            }
-        }
-
-    }
-
-    return GLModel;
-    
-})();
-/**
- * A GLShape is a collection of user specified shapes.
- * 
- * @constructor $3Dmol.GLShape
- * @extends {ShapeSpec}
- * @param {number} sid - Unique identifier
- * @param {ShapeSpec} stylespec - shape style specification
- */
-$3Dmol.GLShape = (function() {
-
-    // Marching cube, to match with protein surface generation
-    var ISDONE = 2;
-
-    /**
-     * 
-     * @param {$3Dmol.Geometry}
-     *            geo
-     * @param {$3Dmol.Color |
-     *            colorlike} color
-     */
-    var updateColor = function(geo, color) {
-
-        var C = color || $3Dmol.CC.color(color);
-        geo.colorsNeedUpdate = true;
-        
-        var r,g,b;
-        if(! (color.constructor === Array)) {
-            r = color.r;
-            g = color.g;
-            b = color.b;
-        }
-
-
-        for ( var gg in geo.geometryGroups) {
-
-            var geoGroup = geo.geometryGroups[gg];
-            var colorArr = geoGroup.colorArray;
-
-            for (var i = 0, il = geoGroup.vertices; i < il; ++i) {
-            
-                if( color.constructor === Array) {
-                    var c = color[i];
-                    r = c.r;
-                    g = c.g;
-                    b = c.b;
-                }
-
-                colorArr[i * 3] = r;
-                colorArr[i * 3 + 1] = g;
-                colorArr[i * 3 + 2] = b;
-            }
-        }
-
-    };
-
-
-    /**
-     * @param {$3Dmol.GLShape}
-     *            shape
-     * @param {geometryGroup}
-     *            geoGroup
-     * @param {ArrowSpec}
-     *            spec
-     */
-    var drawArrow = function(shape, geoGroup, spec) {
-
-        var from = spec.start, end = spec.end, radius = spec.radius, radiusRatio = spec.radiusRatio, mid = spec.mid;
-
-        if (!(from && end))
-            return;
-
-        // vertices
-
-        var dir = end.clone();
-        dir.sub(from).multiplyScalar(mid);
-        var to = from.clone().add(dir);
-        var negDir = dir.clone().negate();
-
-        shape.intersectionShape.cylinder.push(new $3Dmol.Cylinder(from.clone(),
-                to.clone(), radius));
-        shape.intersectionShape.sphere.push(new $3Dmol.Sphere(from.clone(),
-                radius));
-
-        // get orthonormal vector
-        var nvecs = [];
-        nvecs[0] = dir.clone();
-        if (Math.abs(nvecs[0].x) > 0.0001)
-            nvecs[0].y += 1;
-        else
-            nvecs[0].x += 1;
-        nvecs[0].cross(dir);
-        nvecs[0].normalize();
-
-        nvecs[0] = nvecs[0];
-        // another orth vector
-        nvecs[4] = nvecs[0].clone();
-        nvecs[4].crossVectors(nvecs[0], dir);
-        nvecs[4].normalize();
-        nvecs[8] = nvecs[0].clone().negate();
-        nvecs[12] = nvecs[4].clone().negate();
-
-        // now quarter positions
-        nvecs[2] = nvecs[0].clone().add(nvecs[4]).normalize();
-        nvecs[6] = nvecs[4].clone().add(nvecs[8]).normalize();
-        nvecs[10] = nvecs[8].clone().add(nvecs[12]).normalize();
-        nvecs[14] = nvecs[12].clone().add(nvecs[0]).normalize();
-
-        // eights
-        nvecs[1] = nvecs[0].clone().add(nvecs[2]).normalize();
-        nvecs[3] = nvecs[2].clone().add(nvecs[4]).normalize();
-        nvecs[5] = nvecs[4].clone().add(nvecs[6]).normalize();
-        nvecs[7] = nvecs[6].clone().add(nvecs[8]).normalize();
-        nvecs[9] = nvecs[8].clone().add(nvecs[10]).normalize();
-        nvecs[11] = nvecs[10].clone().add(nvecs[12]).normalize();
-        nvecs[13] = nvecs[12].clone().add(nvecs[14]).normalize();
-        nvecs[15] = nvecs[14].clone().add(nvecs[0]).normalize();
-
-        // var start = geo.vertices.length;
-        var start = geoGroup.vertices;
-        var vertexArray = geoGroup.vertexArray;
-        var colorArray = geoGroup.colorArray;
-        var faceArray = geoGroup.faceArray;
-        var normalArray = geoGroup.normalArray;
-        var lineArray = geoGroup.lineArray;
-
-        var offset, i, n;
-        // add vertices, opposing vertices paired together
-        for (i = 0, n = nvecs.length; i < n; ++i) {
-            offset = 3 * (start + 3 * i);
-            var bottom = nvecs[i].clone().multiplyScalar(radius).add(from);
-            var top = nvecs[i].clone().multiplyScalar(radius).add(to);
-            var conebase = nvecs[i].clone()
-                    .multiplyScalar(radius * radiusRatio).add(to);
-
-            vertexArray[offset] = bottom.x;
-            vertexArray[offset + 1] = bottom.y;
-            vertexArray[offset + 2] = bottom.z;
-
-            vertexArray[offset + 3] = top.x;
-            vertexArray[offset + 4] = top.y;
-            vertexArray[offset + 5] = top.z;
-
-            vertexArray[offset + 6] = conebase.x;
-            vertexArray[offset + 7] = conebase.y;
-            vertexArray[offset + 8] = conebase.z;
-
-            if (i > 0) {
-                var prev_x = vertexArray[offset - 3];
-                var prev_y = vertexArray[offset - 2];
-                var prev_z = vertexArray[offset - 1];
-
-                var c = new $3Dmol.Vector3(prev_x, prev_y, prev_z);
-                var b = end.clone(), b2 = to.clone();
-                var a = new $3Dmol.Vector3(conebase.x, conebase.y, conebase.z);
-
-                shape.intersectionShape.triangle.push(new $3Dmol.Triangle(a, b,
-                        c));
-                shape.intersectionShape.triangle.push(new $3Dmol.Triangle(c
-                        .clone(), b2, a.clone()));
-            }
-        }
-
-        geoGroup.vertices += 48;
-        offset = geoGroup.vertices * 3;
-
-        // caps
-        vertexArray[offset] = from.x;
-        vertexArray[offset + 1] = from.y;
-        vertexArray[offset + 2] = from.z;
-
-        vertexArray[offset + 3] = to.x;
-        vertexArray[offset + 4] = to.y;
-        vertexArray[offset + 5] = to.z;
-
-        vertexArray[offset + 6] = end.x;
-        vertexArray[offset + 7] = end.y;
-        vertexArray[offset + 8] = end.z;
-
-        geoGroup.vertices += 3;
-
-        // now faces
-        var face, norm, faceoffset, lineoffset;
-        var t1, t2, t2b, t3, t3b, t4, t1offset, t2offset, t2boffset, t3offset, t3boffset, t4offset;
-        var n1, n2, n3, n4;
-        var n_vertices = 0;
-        var fromi = geoGroup.vertices - 3, toi = geoGroup.vertices - 2, endi = geoGroup.vertices - 1;
-        var fromoffset = fromi * 3, tooffset = toi * 3, endoffset = endi * 3;
-        for (i = 0, n = nvecs.length - 1; i < n; ++i) {
-
-            var ti = start + 3 * i;
-            offset = ti * 3;
-            faceoffset = geoGroup.faceidx;
-            lineoffset = geoGroup.lineidx;
-
-            t1 = ti;
-            t1offset = t1 * 3;
-            t2 = ti + 1;
-            t2offset = t2 * 3;
-            t2b = ti + 2;
-            t2boffset = t2b * 3;
-            t3 = ti + 4;
-            t3offset = t3 * 3;
-            t3b = ti + 5;
-            t3boffset = t3b * 3;
-            t4 = ti + 3;
-            t4offset = t4 * 3;
-
-            // face = [t1, t2, t4], [t2, t3, t4];
-            // face = [t1, t2, t3, t4];
-
-            norm = [ nvecs[i], nvecs[i], nvecs[i + 1], nvecs[i + 1] ];
-
-            n1 = n2 = nvecs[i];
-            n3 = n4 = nvecs[i + 1];
-
-            normalArray[t1offset] = n1.x;
-            normalArray[t2offset] = n2.x;
-            normalArray[t4offset] = n4.x;
-            normalArray[t1offset + 1] = n1.y;
-            normalArray[t2offset + 1] = n2.y;
-            normalArray[t4offset + 1] = n4.y;
-            normalArray[t1offset + 2] = n1.z;
-            normalArray[t2offset + 2] = n2.z;
-            normalArray[t4offset + 2] = n4.z;
-
-            normalArray[t2offset] = n2.x;
-            normalArray[t3offset] = n3.x;
-            normalArray[t4offset] = n4.x;
-            normalArray[t2offset + 1] = n2.y;
-            normalArray[t3offset + 1] = n3.y;
-            normalArray[t4offset + 1] = n4.y;
-            normalArray[t2offset + 2] = n2.z;
-            normalArray[t3offset + 2] = n3.z;
-            normalArray[t4offset + 2] = n4.z;
-
-            normalArray[t2boffset] = n2.x;
-            normalArray[t3boffset] = n3.x;
-            normalArray[t2boffset + 1] = n2.y;
-            normalArray[t3boffset + 1] = n3.y;
-            normalArray[t2boffset + 2] = n2.z;
-            normalArray[t3boffset + 2] = n3.z;
-
-            // sides
-            faceArray[faceoffset] = t1;
-            faceArray[faceoffset + 1] = t2;
-            faceArray[faceoffset + 2] = t4;
-            faceArray[faceoffset + 3] = t2;
-            faceArray[faceoffset + 4] = t3;
-            faceArray[faceoffset + 5] = t4;
-            // caps
-            faceArray[faceoffset + 6] = t1;
-            faceArray[faceoffset + 7] = t4;
-            faceArray[faceoffset + 8] = fromi;
-            faceArray[faceoffset + 9] = t2b;
-            faceArray[faceoffset + 10] = toi;
-            faceArray[faceoffset + 11] = t3b;
-            // arrowhead
-            faceArray[faceoffset + 12] = t2b;
-            faceArray[faceoffset + 13] = endi;
-            faceArray[faceoffset + 14] = t3b;
-
-            // sides
-            lineArray[lineoffset] = t1;
-            lineArray[lineoffset + 1] = t2;
-            lineArray[lineoffset + 2] = t1;
-            lineArray[lineoffset + 3] = t4;
-            // lineArray[lineoffset+4] = t2, lineArray[lineoffset+5] = t3;
-            lineArray[lineoffset + 4] = t3;
-            lineArray[lineoffset + 5] = t4;
-            // caps
-            lineArray[lineoffset + 6] = t1;
-            lineArray[lineoffset + 7] = t4;
-            // lineArray[lineoffset+10] = t1, lineArray[lineoffset+11] = fromi;
-            // lineArray[lineoffset+12] = t4, lineArray[lineoffset+13] = fromi;
-
-            lineArray[lineoffset + 8] = t2b;
-            lineArray[lineoffset + 9] = t2; // toi
-            lineArray[lineoffset + 10] = t2b;
-            lineArray[lineoffset + 11] = t3b;
-            lineArray[lineoffset + 12] = t3;
-            lineArray[lineoffset + 13] = t3b; // toi
-            // arrowhead
-            lineArray[lineoffset + 14] = t2b;
-            lineArray[lineoffset + 15] = endi;
-            lineArray[lineoffset + 16] = t2b;
-            lineArray[lineoffset + 17] = t3b;
-            lineArray[lineoffset + 18] = endi;
-            lineArray[lineoffset + 19] = t3b;
-
-            geoGroup.faceidx += 15;
-            geoGroup.lineidx += 20;
-
-        }
-        // final face
-
-        face = [ start + 45, start + 46, start + 1, start, start + 47,
-                start + 2 ];
-        norm = [ nvecs[15], nvecs[15], nvecs[0], nvecs[0] ];
-
-        faceoffset = geoGroup.faceidx;
-        lineoffset = geoGroup.lineidx;
-
-        t1 = face[0];
-        t1offset = t1 * 3;
-        t2 = face[1];
-        t2offset = t2 * 3;
-        t2b = face[4];
-        t2boffset = t2b * 3;
-        t3 = face[2];
-        t3offset = t3 * 3;
-        t3b = face[5];
-        t3boffset = t3b * 3;
-        t4 = face[3];
-        t4offset = t4 * 3;
-
-        n1 = n2 = nvecs[15];
-        n3 = n4 = nvecs[0];
-
-        normalArray[t1offset] = n1.x;
-        normalArray[t2offset] = n2.x;
-        normalArray[t4offset] = n4.x;
-        normalArray[t1offset + 1] = n1.y;
-        normalArray[t2offset + 1] = n2.y;
-        normalArray[t4offset + 1] = n4.y;
-        normalArray[t1offset + 2] = n1.z;
-        normalArray[t2offset + 2] = n2.z;
-        normalArray[t4offset + 2] = n4.z;
-
-        normalArray[t2offset] = n2.x;
-        normalArray[t3offset] = n3.x;
-        normalArray[t4offset] = n4.x;
-        normalArray[t2offset + 1] = n2.y;
-        normalArray[t3offset + 1] = n3.y;
-        normalArray[t4offset + 1] = n4.y;
-        normalArray[t2offset + 2] = n2.z;
-        normalArray[t3offset + 2] = n3.z;
-        normalArray[t4offset + 2] = n4.z;
-
-        normalArray[t2boffset] = n2.x;
-        normalArray[t3boffset] = n3.x;
-        normalArray[t2boffset + 1] = n2.y;
-        normalArray[t3boffset + 1] = n3.y;
-        normalArray[t2boffset + 2] = n2.z;
-        normalArray[t3boffset + 2] = n3.z;
-
-        // Cap normals
-        dir.normalize();
-        negDir.normalize();
-        normalArray[fromoffset] = negDir.x;
-        normalArray[tooffset] = normalArray[endoffset] = dir.x;
-        normalArray[fromoffset + 1] = negDir.y;
-        normalArray[tooffset + 1] = normalArray[endoffset + 1] = dir.y;
-        normalArray[fromoffset + 2] = negDir.z;
-        normalArray[tooffset + 2] = normalArray[endoffset + 2] = dir.z;
-
-        // Final side
-        faceArray[faceoffset] = t1;
-        faceArray[faceoffset + 1] = t2;
-        faceArray[faceoffset + 2] = t4;
-        faceArray[faceoffset + 3] = t2;
-        faceArray[faceoffset + 4] = t3;
-        faceArray[faceoffset + 5] = t4;
-        // final caps
-        faceArray[faceoffset + 6] = t1;
-        faceArray[faceoffset + 7] = t4;
-        faceArray[faceoffset + 8] = fromi;
-        faceArray[faceoffset + 9] = t2b;
-        faceArray[faceoffset + 10] = toi;
-        faceArray[faceoffset + 11] = t3b;
-        // final arrowhead
-        faceArray[faceoffset + 12] = t2b;
-        faceArray[faceoffset + 13] = endi;
-        faceArray[faceoffset + 14] = t3b;
-
-        // sides
-        lineArray[lineoffset] = t1;
-        lineArray[lineoffset + 1] = t2;
-        lineArray[lineoffset + 2] = t1;
-        lineArray[lineoffset + 3] = t4;
-        // lineArray[lineoffset+4] = t2, lineArray[lineoffset+5] = t3;
-        lineArray[lineoffset + 4] = t3;
-        lineArray[lineoffset + 5] = t4;
-        // caps
-        lineArray[lineoffset + 6] = t1;
-        lineArray[lineoffset + 7] = t4;
-        // lineArray[lineoffset+10] = t1, lineArray[lineoffset+11] = fromi;
-        // lineArray[lineoffset+12] = t4, lineArray[lineoffset+13] = fromi;
-
-        lineArray[lineoffset + 8] = t2b;
-        lineArray[lineoffset + 9] = t2; // toi
-        lineArray[lineoffset + 10] = t2b;
-        lineArray[lineoffset + 11] = t3b;
-        lineArray[lineoffset + 12] = t3;
-        lineArray[lineoffset + 13] = t3b; // toi
-        // arrowhead
-        lineArray[lineoffset + 14] = t2b;
-        lineArray[lineoffset + 15] = endi;
-        lineArray[lineoffset + 16] = t2b;
-        lineArray[lineoffset + 17] = t3b;
-        lineArray[lineoffset + 18] = endi;
-        lineArray[lineoffset + 19] = t3b;
-
-        geoGroup.faceidx += 15;
-        geoGroup.lineidx += 20;
-
-    };
-
-    //helper function for adding an appropriately sized mesh
-    var addCustomGeo = function(shape, geo, mesh, color, clickable) {
-        var geoGroup = geo.addGeoGroup();
-        var vertexArr = mesh.vertexArr, normalArr = mesh.normalArr, 
-            faceArr = mesh.faceArr;
-
-        geoGroup.vertices = vertexArr.length;
-        geoGroup.faceidx = faceArr.length;
-
-        var offset, v, a, b, c, i, il;
-        var vertexArray = geoGroup.vertexArray;
-        var colorArray = geoGroup.colorArray;
-        
-        if(! (color.constructor === Array)) {
-            var r = color.r;
-            var g = color.g;
-            var b = color.b;
-        }
-        for (i = 0, il = geoGroup.vertices; i < il; ++i) {
-            offset = i * 3;
-            v = vertexArr[i];
-            vertexArray[offset] = v.x;
-            vertexArray[offset + 1] = v.y;
-            vertexArray[offset + 2] = v.z;
-            
-            if( color.constructor === Array) {
-                var c = color[i];
-                var r = c.r;
-                var g = c.g;
-                var b = c.b;
-            }
-            
-            colorArray[offset] = r;
-            colorArray[offset + 1] = g;
-            colorArray[offset + 2] = b;
-        }
-        geoGroup.truncateArrayBuffers(true, true);
-
-        
-        if(clickable) {
-            for (i = 0, il = geoGroup.faceidx / 3; i < il; ++i) {
-                offset = i * 3;
-                a = faceArr[offset];
-                b = faceArr[offset + 1];
-                c = faceArr[offset + 2];
-                var vA = new $3Dmol.Vector3(), vB = new $3Dmol.Vector3(), vC = new $3Dmol.Vector3();
-                shape.intersectionShape.triangle.push(new $3Dmol.Triangle(vA
-                        .copy(vertexArr[a]), vB.copy(vertexArr[b]), vC
-                        .copy(vertexArr[c])));
-            }
-        }
-        
-        if(clickable) {
-            
-            var center = new $3Dmol.Vector3(0,0,0);
-            var cnt = 0;
-            for(var g = 0; g < geo.geometryGroups.length; g++) {
-                center.add(geo.geometryGroups[g].getCentroid());
-                cnt++;
-            }
-            center.divideScalar(cnt);
-            
-            
-            updateBoundingFromPoints(shape.boundingSphere, {centroid: center}, vertexArray);
-        }
-
-        geoGroup.faceArray = new Uint16Array(faceArr);
-
-        geoGroup.truncateArrayBuffers(true, true);
-
-        if (normalArr.length < geoGroup.vertices)
-            geoGroup.setNormals();
-        else {
-
-            var normalArray = geoGroup.normalArray = new Float32Array(geoGroup.vertices * 3);
-            var n;
-            for (i = 0, il = geoGroup.vertices; i < il; ++i) {
-                offset = i * 3;
-                n = normalArr[i];
-                normalArray[offset] = n.x;
-                normalArray[offset + 1] = n.y;
-                normalArray[offset + 2] = n.z;
-            }
-        }
-        
-        geoGroup.setLineIndices();
-        geoGroup.lineidx = geoGroup.lineArray.length;
-    };
-    
-
-    
-    // handles custom shape generation from user supplied arrays
-    // May need to generate normal and/or line indices
-    /**
-     * @param {$3Dmol.GLShape}
-     *            shape
-     * @param {geometry}
-     *            geo
-     * @param {CustomSpec}
-     *            customSpec
-     */
-    var drawCustom = function(shape, geo, customSpec) {
-        var mesh = customSpec;
-        var vertexArr = mesh.vertexArr, normalArr = mesh.normalArr, 
-        faceArr = mesh.faceArr;
-        if (vertexArr.length === 0 || faceArr.length === 0) {
-            console
-                    .warn("Error adding custom shape component: No vertices and/or face indices supplied!");
-        }
-
-        var color = customSpec.color;
-        if(typeof(color) == 'undefined') {
-            color = shape.color;
-        }
-        color =  $3Dmol.CC.color(color);
-
-        //var firstgeo = geo.geometryGroups.length;
-        var splits = $3Dmol.splitMesh(mesh);
-        for(var i = 0, n = splits.length; i < n; i++) {
-            addCustomGeo(shape, geo, splits[i], splits[i].colorArr ? splits[i].colorArr : color, customSpec.clickable);
-        } 
-    }; 
-
-    // Update a bounding sphere's position and radius
-    // from list of centroids and new points
-    /**
-     * @param {$3Dmol.Sphere}
-     *            sphere
-     * @param {Object}
-     *            components
-     * @param {Array}
-     *            points
-     */
-    var updateBoundingFromPoints = function(sphere, components, points) {
-
-        sphere.center.set(0, 0, 0);
-
-        var i, il;
-
-        if (components.length > 0) {
-
-            for (i = 0, il = components.length; i < il; ++i) {
-                var centroid = components[i].centroid;
-                sphere.center.add(centroid);
-            }
-
-            sphere.center.divideScalar(components.length);
-        }
-
-        var maxRadiusSq = sphere.radius * sphere.radius;
-
-        for (i = 0, il = points.length / 3; i < il; i++) {
-            var x = points[i * 3], y = points[i * 3 + 1], z = points[i * 3 + 2];
-            var radiusSq = sphere.center.distanceToSquared({
-                x : x,
-                y : y,
-                z : z
-            });
-            maxRadiusSq = Math.max(maxRadiusSq, radiusSq);
-        }
-
-        sphere.radius = Math.sqrt(maxRadiusSq);
-
-    };
-
-    /**
-     * 
-     * @param {$3Dmol.GLShape}
-     *            shape
-     * @param {ShapeSpec}
-     *            stylespec
-     * @returns {undefined}
-     */
-    var updateFromStyle = function(shape, stylespec) {
-        shape.color = stylespec.color || new $3Dmol.Color();
-        if(! (stylespec.color instanceof $3Dmol.Color))
-            shape.color = $3Dmol.CC.color(stylespec.color);
-        shape.wireframe = stylespec.wireframe ? true : false;
-        //opacity is the preferred nomenclature, support alpha for backwards compat
-        shape.opacity = stylespec.alpha ? $3Dmol.Math.clamp(stylespec.alpha, 0.0,
-                1.0) : 1.0;
-        if(typeof(stylespec.opacity) != 'undefined') {
-            shape.opacity = $3Dmol.Math.clamp(stylespec.opacity, 0.0, 1.0);
-        }
-        shape.side = (stylespec.side !== undefined) ? stylespec.side
-                : $3Dmol.DoubleSide;
-
-        shape.linewidth = typeof(stylespec.linewidth) == 'undefined' ? 1 : stylespec.linewidth;
-        // Click handling
-        shape.clickable = stylespec.clickable ? true : false;
-        shape.callback = typeof (stylespec.callback) === "function" ? stylespec.callback
-                : null;
-        shape.hidden = stylespec.hidden;
-    };
-
-    /**
-     * Custom renderable shape
-     * 
-     * @constructor $3Dmol.GLShape
-     * 
-     * @param {Object}
-     *            stylespec
-     * @returns {$3Dmol.GLShape}
-     */
-    function GLShape(stylespec) {
-
-        stylespec = stylespec || {};
-        $3Dmol.ShapeIDCount++;
-
-        this.boundingSphere = new $3Dmol.Sphere();
-        /** @type {IntersectionShapes} */
-        this.intersectionShape = {
-            sphere : [],
-            cylinder : [],
-            line : [],
-            triangle : []
-        };
-
-        updateFromStyle(this, stylespec);
-
-        // Keep track of shape components and their centroids
-        var components = [];
-        var shapeObj = null;
-        var renderedShapeObj = null;
-
-        var geo = new $3Dmol.Geometry(true);
-        var linegeo = new $3Dmol.Geometry(true);
-
-        /** Update shape with new style specification
-         * @param {ShapeSpec} newspec
-         * @return {$3Dmol.GLShape}
-         */
-        this.updateStyle = function(newspec) {
-
-            for ( var prop in newspec) {
-                stylespec[prop] = newspec[prop];
-            }
-
-            updateFromStyle(this, stylespec);
-        };
-
-        /**
-         * Creates a custom shape from supplied vertex and face arrays
-         * @function $3Dmol.GLShape#addCustom
-         * @param {CustomSpec} customSpec
-         * @return {$3Dmol.GLShape}
-         */
-        this.addCustom = function(customSpec) {
-
-            customSpec.vertexArr = customSpec.vertexArr || [];
-            customSpec.faceArr = customSpec.faceArr || [];
-            customSpec.normalArr = customSpec.normalArr || [];
-
-            var firstgeo = geo.geometryGroups.length;
-            // will split mesh as needed
-            drawCustom(this, geo, customSpec);
-        };
-
-        /**
-         * Creates a sphere shape
-         * @function $3Dmol.GLShape#addSphere
-         * @param {SphereSpec} sphereSpec
-         * @return {$3Dmol.GLShape}
-         */
-        this.addSphere = function(sphereSpec) {
-
-            sphereSpec.center = sphereSpec.center || {
-                x : 0,
-                y : 0,
-                z : 0
-            };
-            sphereSpec.radius = sphereSpec.radius ? $3Dmol.Math.clamp(
-                    sphereSpec.radius, 0, Infinity) : 1.5;
-            sphereSpec.color = $3Dmol.CC.color(sphereSpec.color);
-            
-            this.intersectionShape.sphere.push(new $3Dmol.Sphere(
-                    sphereSpec.center, sphereSpec.radius));
-
-            var geoGroup = geo.addGeoGroup();
-            $3Dmol.GLDraw.drawSphere(geo, sphereSpec.center,
-                    sphereSpec.radius, sphereSpec.color);
-            geoGroup.truncateArrayBuffers(true, true);
-
-            components.push({
-                id : geoGroup.id,
-                geoGroup : geoGroup, // has to be last group added
-                centroid : new $3Dmol.Vector3(sphereSpec.center.x,
-                        sphereSpec.center.y, sphereSpec.center.z)
-            });
-
-            updateBoundingFromPoints(this.boundingSphere, components,
-                    geoGroup.vertexArray);
-        };
-
-        /**
-         * Creates a cylinder shape
-         * @function $3Dmol.GLShape#addCylinder
-         * @param {CylinderSpec} cylinderSpec
-         * @return {$3Dmol.GLShape}
-         */
-        this.addCylinder = function(cylinderSpec) {
-
-            cylinderSpec.start = cylinderSpec.start || {};
-            cylinderSpec.end = cylinderSpec.end || {};
-
-            var start = new $3Dmol.Vector3(cylinderSpec.start.x || 0,
-                    cylinderSpec.start.y || 0, cylinderSpec.start.z || 0);
-            var end = new $3Dmol.Vector3(cylinderSpec.end.x,
-                    cylinderSpec.end.y || 0, cylinderSpec.end.z || 0);
-        	if(typeof(end.x) == 'undefined') end.x = 3; //show something even if undefined
-
-            var radius = cylinderSpec.radius || 0.1;
-            var color = $3Dmol.CC.color(cylinderSpec.color);
-            
-            this.intersectionShape.cylinder.push(new $3Dmol.Cylinder(start, end, radius));
-
-            var geoGroup = geo.addGeoGroup();
-            $3Dmol.GLDraw.drawCylinder(geo, start, end, radius, color, cylinderSpec.fromCap, cylinderSpec.toCap);            
-            geoGroup.truncateArrayBuffers(true, true);
-            
-            var centroid = new $3Dmol.Vector3();
-            components.push({
-                id : geoGroup.id,
-                geoGroup : geoGroup,
-                centroid : centroid.addVectors(start,end).multiplyScalar(0.5)
-            });
-
-            updateBoundingFromPoints(this.boundingSphere, components,
-                    geoGroup.vertexArray);
-
-        };
-
-        /**
-         * Creates a line shape
-         * @function $3Dmol.GLShape#addLine         
-         * @param {LineSpec} lineSpec
-         * @return {$3Dmol.GLShape}
-         */
-        this.addLine = function(lineSpec) {
-        	lineSpec.start = lineSpec.start || {};
-        	lineSpec.end = lineSpec.end || {};
-
-        	var start = new $3Dmol.Vector3(lineSpec.start.x || 0,
-        			lineSpec.start.y || 0, lineSpec.start.z || 0);
-        	var end = new $3Dmol.Vector3(lineSpec.end.x,
-        			lineSpec.end.y || 0, lineSpec.end.z || 0);            
-        	if(typeof(end.x) == 'undefined') end.x = 3; //show something even if undefined
-    
-            var geoGroup = geo.addGeoGroup();
-
-            //make line from start to end
-            //for consistency with rest of shapes, uses vertices and lines rather
-            //than a separate line geometry
-            var vstart = geoGroup.vertices;
-            var i = vstart*3;
-            var vertexArray = geoGroup.vertexArray;
-            vertexArray[i] = start.x;
-            vertexArray[i+1] = start.y;
-            vertexArray[i+2] = start.z;
-            vertexArray[i+3] = end.x;
-            vertexArray[i+4] = end.y;
-            vertexArray[i+5] = end.z;
-            geoGroup.vertices += 2;
-            
-            var lineArray = geoGroup.lineArray;
-            var li =  geoGroup.lineidx;
-            lineArray[li] = vstart;
-            lineArray[li+1] = vstart+1;
-            geoGroup.lineidx += 2;
-            
-            geoGroup.truncateArrayBuffers(true, true);
-        }
-        /**
-         * Creates an arrow shape
-         * @function $3Dmol.GLShape#addArrow        
-         * @param {ArrowSpec} arrowSpec
-         * @return {$3Dmol.GLShape}
-         */
-        this.addArrow = function(arrowSpec) {
-
-            arrowSpec.start = arrowSpec.start || {};
-            arrowSpec.end = arrowSpec.end || {};
-
-            arrowSpec.start = new $3Dmol.Vector3(arrowSpec.start.x || 0,
-                    arrowSpec.start.y || 0, arrowSpec.start.z || 0);
-
-            if (arrowSpec.dir instanceof $3Dmol.Vector3
-                    && arrowSpec.length instanceof number) {
-                var end = arrowSpec.dir.clone().multiplyScalar(arrowSpec.length).add(
-                        start);
-                arrowSpec.end = end;
-            }
-
-            else {
-                arrowSpec.end = new $3Dmol.Vector3(arrowSpec.end.x,
-                        arrowSpec.end.y || 0, arrowSpec.end.z || 0);
-            	if(typeof(arrowSpec.end.x) == 'undefined') arrowSpec.end.x = 3; //show something even if undefined
-            }
-
-            arrowSpec.radius = arrowSpec.radius || 0.1;
-
-            arrowSpec.radiusRatio = arrowSpec.radiusRatio || 1.618034;
-            arrowSpec.mid = (0 < arrowSpec.mid && arrowSpec.mid < 1) ? arrowSpec.mid
-                    : 0.618034;
-
-            var geoGroup = geo.addGeoGroup();
-
-            drawArrow(this, geoGroup, arrowSpec);
-            geoGroup.truncateArrayBuffers(true, true);
-
-            var centroid = new $3Dmol.Vector3();
-            components.push({
-                id : geoGroup.id,
-                geoGroup : geoGroup,
-                centroid : centroid.addVectors(arrowSpec.start, arrowSpec.end)
-                        .multiplyScalar(0.5)
-            });
-
-            updateBoundingFromPoints(this.boundingSphere, components,
-                    geoGroup.vertexArray);
-
-        };
-        
-        /**
-         * Create isosurface from voluemetric data.
-         * @function $3Dmol.GLShape#addIsosurface         
-         * @param {$3Dmol.VolumeData} data - volumetric input data
-         * @param {IsoSurfaceSpec} isoSpec - volumetric data shape specification
-         */
-        this.addIsosurface = function(data, volSpec) {
-            var isoval = (volSpec.isoval !== undefined && typeof (volSpec.isoval) === "number") ? volSpec.isoval
-                    : 0.0;
-            var voxel = (volSpec.voxel) ? true : false;
-            var smoothness = (volSpec.smoothness === undefined) ? 1 : volSpec.smoothness;
-
-            var nX = data.size.x;
-            var nY = data.size.y;
-            var nZ = data.size.z;
-            var vertnums = new Int16Array(nX * nY * nZ);
-            var vals = data.data;
-            var i, il;
-
-            for (i = 0, il = vertnums.length; i < il; ++i)
-                vertnums[i] = -1;
-
-            //mark locations partitioned by isoval
-            var bitdata = new Uint8Array(nX * nY * nZ);
-
-            for (i = 0, il = vals.length; i < il; ++i) {
-                var val = (isoval >= 0) ? vals[i] - isoval : isoval - vals[i];
-
-                if (val > 0)
-                    bitdata[i] |= ISDONE;
-
-            }
-
-            var verts = [], faces = [];
-
-            $3Dmol.MarchingCube.march(bitdata, verts, faces, {
-                fulltable : true,
-                voxel : voxel,
-                unitCube : data.unit,
-                origin : data.origin,
-                nX : nX,
-                nY : nY,
-                nZ : nZ
-            });
-
-            if (!voxel)
-                $3Dmol.MarchingCube.laplacianSmooth(smoothness, verts, faces);
-
-            drawCustom(this, geo, {
-                vertexArr : verts,
-                faceArr : faces,
-                normalArr : [],
-                clickable : volSpec.clickable
-            });
-           
-            this.updateStyle(volSpec);
-        };
-        
-        /** 
-         * @deprecated Use addIsosurface instead
-         * Creates custom shape from volumetric data 
-         * @param {string} data - Volumetric input data 
-         * @param {string} fmt - Input data format (e.g. 'cube' for cube file format)
-         * @param {IsoSurfaceSpec} isoSpec - Volumetric data shape specification
-         * @return {$3Dmol.GLShape}
-         */
-        this.addVolumetricData = function(data, fmt, volSpec) {
-            var data = new $3Dmol.VolumeData(data, fmt);
-            this.addIsosurface(data, volSpec);
-        };
-
-        /**
-         * Initialize webgl objects for rendering
-         * @param {$3Dmol.Object3D} group
-         * 
-         */  
-        this.globj = function(group) {
-
-            if (renderedShapeObj) {
-                group.remove(renderedShapeObj);
-                renderedShapeObj = null;
-            }
-            
-            if(this.hidden)
-                return;
-            geo.initTypedArrays();
-
-            updateColor(geo, this.color);
-
-            shapeObj = new $3Dmol.Object3D();
-            var material = null;
-            if(this.side == $3Dmol.DoubleSide) {
-                var material = new $3Dmol.MeshDoubleLambertMaterial({
-                    wireframe : this.wireframe,
-                    side : this.side,
-                    transparent : (this.opacity < 1) ? true : false,
-                    opacity : this.opacity,
-                    wireframeLinewidth: this.linewidth
-                });
-            } else {
-                var material = new $3Dmol.MeshLambertMaterial({
-                    wireframe : this.wireframe,
-                    side : this.side,
-                    transparent : (this.opacity < 1) ? true : false,
-                    opacity : this.opacity,
-                    wireframeLinewidth: this.linewidth
-                });
-            }
-            
-            var mesh = new $3Dmol.Mesh(geo, material);
-
-            shapeObj.add(mesh);
-            
-            var lineMaterial = new $3Dmol.LineBasicMaterial({
-                linewidth : this.linewidth,
-                color: this.color
-            });
-            var line = new $3Dmol.Line(linegeo, lineMaterial,
-                    $3Dmol.LinePieces);
-            shapeObj.add(line);
-
-            renderedShapeObj = shapeObj.clone();
-            group.add(renderedShapeObj);
-
-        };
-
-        this.removegl = function(group) {
-            if (renderedShapeObj) {
-                // dispose of geos and materials
-                if (renderedShapeObj.geometry !== undefined)
-                    renderedShapeObj.geometry.dispose();
-                if (renderedShapeObj.material !== undefined)
-                    renderedShapeObj.material.dispose();
-                group.remove(renderedShapeObj);
-                renderedShapeObj = null;
-            }
-            shapeObj = null;
-        };
-
-    };
-
-    Object.defineProperty(GLShape.prototype, "position", {
-
-        get : function() {
-            return this.boundingSphere.center;
-        }
-
-    });
-
-    Object.defineProperty(GLShape.prototype, "x", {
-
-        get : function() {
-            return this.boundingSphere.center.x;
-        }
-
-    });
-
-    Object.defineProperty(GLShape.prototype, "y", {
-
-        get : function() {
-            return this.boundingSphere.center.y;
-        }
-
-    });
-
-    Object.defineProperty(GLShape.prototype, "z", {
-
-        get : function() {
-            return this.boundingSphere.center.z;
-        }
-
-    });
-
-    return GLShape;
-
-}());
-
-$3Dmol.ShapeIDCount = 0;
-
-
-$3Dmol.splitMesh = function(mesh) {
-	    var MAXVERT = 64000; //webgl only supports 2^16 elements, leave a little breathing room (require at least 2)
-    //peel off 64k vertices rsvh into their own mesh
-    //duplicating vertices and normals as necessary to preserve faces and lines
-	
-        if(mesh.vertexArr.length < MAXVERT) return [mesh]; //typical case
-        
-        var nverts = mesh.vertexArr.length;
-        var slices = [{vertexArr: [], normalArr: [], faceArr: []}];
-        if(mesh.colorArr) slices.colorArr = [];
-        var vertSlice = []; //indexed by original vertex to get current slice
-        var vertIndex =[]; //indexed by original vertex to get index within slice
-        var currentSlice = 0;
-        
-        //for each face, make sure all three vertices (or copies) are in the same slice
-        var faces = mesh.faceArr;
-        var vs = [0,0,0];
-        for(var i = 0, nf = faces.length; i < nf; i += 3) {
-            var slice = slices[currentSlice];
-            for(var j = 0; j < 3; j++) {
-                //process each vertex to make sure it is assigned a slice
-                //all vertices of a face must belong to the same slice
-                var v = faces[i+j];
-                if(vertSlice[v] !== currentSlice) { //true if undefined
-                    vertSlice[v] = currentSlice;
-                    vertIndex[v] = slice.vertexArr.length;
-                    slice.vertexArr.push(mesh.vertexArr[v]);
-                    if(mesh.normalArr && mesh.normalArr[v]) slice.normalArr.push(mesh.normalArr[v]);
-                    if(mesh.colorArr && mesh.colorArr[v]) slice.colorArr.push(mesh.colorArr[v]);
-                }
-                slice.faceArr.push(vertIndex[v]);
-            }
-            
-            if(slice.vertexArr.length >= MAXVERT) {
-                //new slice
-                slices.push({vertexArr: [], normalArr: [], faceArr: []});
-                if(mesh.colorArr) slices.colorArr = [];
-                currentSlice++;
-            }
-        }
-        return slices;
-    }
-//a molecular viewer based on GLMol
-
-
-
-/**
- * WebGL-based 3Dmol.js viewer
- * Note: The preferred method of instantiating a GLViewer is through {@link $3Dmol.createViewer} 
- * 
- * @constructor 
- * @param {Object} element HTML element within which to create viewer
- * @param {function} callback - Callback function to be immediately executed on this viewer
- * @param {Object} defaultcolors - Object defining default atom colors as atom => color property value pairs for all models within this viewer
- */
-$3Dmol.GLViewer = (function() {
-    // private class variables
-    var numWorkers = 4; // number of threads for surface generation
-    var maxVolume = 64000; // how much to break up surface calculations
-
-    // private class helper functions
-
-    function GLViewer(element, config) { 
-        // set variables
-        config = config || {};
-        var callback = config.callback;
-        var defaultcolors = config.defaultcolors;    	
-        if(!defaultcolors)
-            defaultcolors = $3Dmol.elementColors.defaultColors;
-        var nomouse = config.nomouse;
-        var bgColor = 0;
-
-        if(typeof(config.backgroundColor) != undefined) {
-            bgColor = $3Dmol.CC.color(config.backgroundColor).getHex();
-        }
-
-        var camerax = 0;
-        if(typeof(config.camerax) != undefined) {
-            camerax = parseFloat(config.camerax);
-        }
-        var _viewer = this;
-        var container = element;
-        var id = container.id;
-        var glDOM = null;
-
-        var models = []; // atomistic molecular models
-        var surfaces = {};
-        var shapes = []; // Generic shapes
-        var labels = [];
-        var clickables = []; //things you can click on
-        
-        var WIDTH = container.width();
-        var HEIGHT = container.height();
-
-        // set dimensions
-        // $(container).width(WIDTH);
-        // $(container).height(HEIGHT);
-
-        var ASPECT = WIDTH / HEIGHT;
-        var NEAR = 1, FAR = 800;
-        var CAMERA_Z = 150;
-        var fov = 20;
-
-        var linkedViewers = [];
-
-        var renderer = new $3Dmol.Renderer({
-            antialias : true,
-            preserveDrawingBuffer: true, //so we can export images
-            premultipliedAlpha : false/* more traditional compositing with background */
-        });
-
-        renderer.domElement.style.width = "100%";
-        renderer.domElement.style.height = "100%";
-        renderer.domElement.style.padding = "0";
-        renderer.domElement.style.position = "absolute"; //TODO: get rid of this
-        renderer.domElement.style.top = "0px";
-        renderer.domElement.style.left = "0px";
-        renderer.domElement.style.zIndex = "0";
-
-        var camera = new $3Dmol.Camera(fov, ASPECT, NEAR, FAR);
-        camera.position = new $3Dmol.Vector3(camerax, 0, CAMERA_Z);
-        var lookingAt = new $3Dmol.Vector3();
-        camera.lookAt(lookingAt);
-
-        var raycaster = new $3Dmol.Raycaster(new $3Dmol.Vector3(0, 0, 0),
-                new $3Dmol.Vector3(0, 0, 0));
-        var projector = new $3Dmol.Projector();
-        var mouseVector = new $3Dmol.Vector3(0, 0, 0);
-
-        var scene = null;
-        var rotationGroup = null; // which contains modelGroup
-        var modelGroup = null;
-
-        var fogStart = 0.4;
-        var slabNear = -50; // relative to the center of rotationGroup
-        var slabFar = 50;
-
-        // UI variables
-        var cq = new $3Dmol.Quaternion(0, 0, 0, 1);
-        var dq = new $3Dmol.Quaternion(0, 0, 0, 1);
-        var animated = false;
-        var isDragging = false;
-        var mouseStartX = 0;
-        var mouseStartY = 0;
-        var touchDistanceStart = 0;
-        var currentModelPos = 0;
-        var cz = 0;
-        var cslabNear = 0;
-        var cslabFar = 0;      
-        
-        var nextSurfID = function() {
-            //compute the next highest surface id directly from surfaces
-            //this is necessary to support linking of model data
-            var max = 0;
-            for (i in surfaces) { // this is an object with possible holes
-                if(!surfaces.hasOwnProperty(i)) continue;
-                if(i > max) max = i;
-            }
-            return max+1;
-        };
-
-        var setSlabAndFog = function() {
-            var center = camera.position.z - rotationGroup.position.z;
-            if (center < 1)
-                center = 1;
-            camera.near = center + slabNear;
-            if (camera.near < 1)
-                camera.near = 1;
-            camera.far = center + slabFar;
-            if (camera.near + 1 > camera.far)
-                camera.far = camera.near + 1;
-            if (camera instanceof $3Dmol.Camera) {
-                camera.fov = fov;
-            } else {
-                camera.right = center * Math.tan(Math.PI / 180 * fov);
-                camera.left = -camera.right;
-                camera.top = camera.right / ASPECT;
-                camera.bottom = -camera.top;
-            }
-            camera.updateProjectionMatrix();
-            scene.fog.near = camera.near + fogStart
-                    * (camera.far - camera.near);
-            // if (scene.fog.near > center) scene.fog.near = center;
-            scene.fog.far = camera.far;
-        };
-
-        // display scene
-        //if nolink is set/true, don't propagate changes to linked viewers
-        var show = function(nolink) {
-            if (!scene)
-                return;
-
-            // var time = new Date();
-            setSlabAndFog();
-            renderer.render(scene, camera);
-            // console.log("rendered in " + (+new Date() - time) + "ms");
-            
-            if(!nolink && linkedViewers.length > 0) {
-                var view = _viewer.getView();
-                for(var i = 0; i < linkedViewers.length; i++) {
-                    var other = linkedViewers[i];
-                    other.setView(view, true);
-                }
-            }
-        };
-
-        var initializeScene = function() {
-
-            scene = new $3Dmol.Scene();
-            scene.fog = new $3Dmol.Fog(bgColor, 100, 200);
-
-            modelGroup = new $3Dmol.Object3D();
-            rotationGroup = new $3Dmol.Object3D();
-            rotationGroup.useQuaternion = true;
-            rotationGroup.quaternion = new $3Dmol.Quaternion(0, 0, 0, 1);
-            rotationGroup.add(modelGroup);
-
-            scene.add(rotationGroup);
-
-            // setup lights
-            var directionalLight = new $3Dmol.Light(0xFFFFFF);
-            directionalLight.position = new $3Dmol.Vector3(0.2, 0.2, 1)
-                    .normalize();
-            directionalLight.intensity = 1.0;
-            scene.add(directionalLight);
-        };
-
-        initializeScene();
-
-        renderer.setClearColorHex(bgColor, 1.0);
-        scene.fog.color = $3Dmol.CC.color(bgColor);
-
-        var clickedAtom = null;
-
-        // enable mouse support
-
-        //regenerate the list of clickables
-        var updateClickables = function() {
-            clickables.splice(0,clickables.length);
-            var i, il;
-
-            for (i = 0, il = models.length; i < il; i++) {
-                var model = models[i];
-                if(model) {
-                    var atoms = model.selectedAtoms({
-                        clickable : true
-                    });
-                    Array.prototype.push.apply(clickables, atoms); //add atoms into clickables
-                }
-            }
-
-            for (i = 0, il = shapes.length; i < il; i++) {
-
-                var shape = shapes[i];
-                if (shape && shape.clickable) {
-                    clickables.push(shape);
-                }
-            }
-        };
-        
-        // Checks for selection intersects on mousedown
-        var handleClickSelection = function(mouseX, mouseY, event) {
-            if(clickables.length == 0) return;
-            var mouse = {
-                x : mouseX,
-                y : mouseY,
-                z : -1.0
-            };
-            mouseVector.set(mouse.x, mouse.y, mouse.z);
-            projector.unprojectVector(mouseVector, camera);
-            mouseVector.sub(camera.position).normalize();
-
-            raycaster.set(camera.position, mouseVector);
-
-            var intersects = [];
-
-            intersects = raycaster.intersectObjects(modelGroup, clickables);
-            if (intersects.length) {
-                var selected = intersects[0].clickable;
-                if (selected.callback !== undefined
-                        && typeof (selected.callback) === "function") {
-                    selected.callback(selected, _viewer, event, container);
-                }
-            }
-        };
-
-        var calcTouchDistance = function(ev) { // distance between first two
-                                                // fingers
-            var xdiff = ev.originalEvent.targetTouches[0].pageX
-                    - ev.originalEvent.targetTouches[1].pageX;
-            var ydiff = ev.originalEvent.targetTouches[0].pageY
-                    - ev.originalEvent.targetTouches[1].pageY;
-            return Math.sqrt(xdiff * xdiff + ydiff * ydiff);
-        }
-        
-        //check targetTouches as well
-        var getXY = function(ev) {
-            var x = ev.pageX, y = ev.pageY;
-            if (ev.originalEvent.targetTouches
-                    && ev.originalEvent.targetTouches[0]) {
-                x = ev.originalEvent.targetTouches[0].pageX;
-                y = ev.originalEvent.targetTouches[0].pageY;
-            }
-            else if (ev.originalEvent.changedTouches
-                    && ev.originalEvent.changedTouches[0]) {
-                x = ev.originalEvent.changedTouches[0].pageX;
-                y = ev.originalEvent.changedTouches[0].pageY;
-            }            
-            return [x,y];
-        };
-
-        //for a given screen (x,y) displacement return model displacement 
-        var screenXY2model = function(x,y) {
-            var dx = x/WIDTH;
-            var dy = y/HEIGHT;
-            var zpos = rotationGroup.position.z; 
-            var q = rotationGroup.quaternion;                        
-            var t = new $3Dmol.Vector3(0,0,zpos);
-            projector.projectVector(t, camera);
-            t.x += dx*2;
-            t.y -= dy*2;
-            projector.unprojectVector(t, camera);
-            t.z = 0;                            
-            t.applyQuaternion(q);
-            return t;
-        }
-
-        // this event is bound to the body element, not the container,
-        // so no need to put it inside initContainer()
-        $('body').bind('mouseup touchend', function(ev) {
-            // handle selection
-            if(isDragging && scene) { //saw mousedown, haven't moved
-                var xy = getXY(ev);
-                var x = xy[0];
-                var y = xy[1];
-                if(x == mouseStartX && y == mouseStartY) {
-                    var offset = $('canvas',container).offset();
-                    var mouseX = ((x - offset.left) / WIDTH) * 2 - 1;
-                    var mouseY = -((y - offset.top) / HEIGHT) * 2 + 1;
-
-                    handleClickSelection(mouseX, mouseY, ev, container);
-                }
-            }
-            
-            isDragging = false;
-
-        });
-        
-        var _handleMouseDown = this._handleMouseDown = function(ev) {
-            ev.preventDefault();
-            if (!scene)
-                return;
-            var xy = getXY(ev);
-            var x = xy[0];
-            var y = xy[1];
-            
-            if (x === undefined)
-                return;
-            isDragging = true;
-            clickedAtom = null;
-            mouseButton = ev.which;
-            mouseStartX = x;
-            mouseStartY = y;
-            touchDistanceStart = 0;
-            if (ev.originalEvent.targetTouches
-                    && ev.originalEvent.targetTouches.length == 2) {
-                touchDistanceStart = calcTouchDistance(ev);
-            }
-            cq = rotationGroup.quaternion;
-            cz = rotationGroup.position.z;
-            currentModelPos = modelGroup.position.clone();
-            cslabNear = slabNear;
-            cslabFar = slabFar;
-
-        };
-        
-        var _handleMouseScroll  = this._handleMouseScroll = function(ev) { // Zoom
-            ev.preventDefault();
-            if (!scene)
-                return;
-            var scaleFactor = (CAMERA_Z - rotationGroup.position.z) * 0.85;
-            if (ev.originalEvent.detail) { // Webkit
-                rotationGroup.position.z += scaleFactor
-                        * ev.originalEvent.detail / 10;
-            } else if (ev.originalEvent.wheelDelta) { // Firefox
-                rotationGroup.position.z -= scaleFactor
-                        * ev.originalEvent.wheelDelta / 400;
-            }
-            if(rotationGroup.position.z > CAMERA_Z) rotationGroup.position.z = CAMERA_Z*0.999; //avoid getting stuck
-
-            show();
-        };
-        
-        var _handleMouseMove = this._handleMouseMove = function(ev) { // touchmove
-            WIDTH = container.width();
-            HEIGHT = container.height();
-            ev.preventDefault();
-            if (!scene)
-                return;
-            if (!isDragging)
-                return;
-            var mode = 0;
-
-            var xy = getXY(ev);
-            var x = xy[0];
-            var y = xy[1];
-            if (x === undefined)
-                return;
-            var dx = (x - mouseStartX) / WIDTH;
-            var dy = (y - mouseStartY) / HEIGHT;
-            // check for pinch
-            if (touchDistanceStart != 0
-                    && ev.originalEvent.targetTouches
-                    && ev.originalEvent.targetTouches.length == 2) {
-                var newdist = calcTouchDistance(ev);
-                // change to zoom
-                mode = 2;
-                dy = (touchDistanceStart - newdist) * 2
-                        / (WIDTH + HEIGHT);
-            } else if (ev.originalEvent.targetTouches
-                    && ev.originalEvent.targetTouches.length == 3) {
-                // translate
-                mode = 1;
-            }
-
-            var r = Math.sqrt(dx * dx + dy * dy);
-            var scaleFactor;
-            if (mode == 3
-                    || (mouseButton == 3 && ev.ctrlKey)) { // Slab
-                slabNear = cslabNear + dx * 100;
-                slabFar = cslabFar + dy * 100;
-            } else if (mode == 2 || mouseButton == 3
-                    || ev.shiftKey) { // Zoom
-                scaleFactor = (CAMERA_Z - rotationGroup.position.z) * 0.85;
-                if (scaleFactor < 80)
-                    scaleFactor = 80;
-                rotationGroup.position.z = cz - dy
-                        * scaleFactor;
-                if(rotationGroup.position.z > CAMERA_Z) rotationGroup.position.z = CAMERA_Z*0.999; //avoid getting stuck
-            } else if (mode == 1 || mouseButton == 2
-                    || ev.ctrlKey) { // Translate
-                var t = screenXY2model(x-mouseStartX, y-mouseStartY);
-                modelGroup.position.addVectors(currentModelPos,t);
-                
-            } else if ((mode === 0 || mouseButton == 1)
-                    && r !== 0) { // Rotate
-                var rs = Math.sin(r * Math.PI) / r;
-                dq.x = Math.cos(r * Math.PI);
-                dq.y = 0;
-                dq.z = rs * dx;
-                dq.w = -rs * dy;
-                rotationGroup.quaternion = new $3Dmol.Quaternion(
-                        1, 0, 0, 0);
-                rotationGroup.quaternion.multiply(dq);
-                rotationGroup.quaternion.multiply(cq);
-            }
-            show();
-        };
-        
-        var initContainer = function(element) {
-            container = element;
-            WIDTH = container.width();
-            HEIGHT = container.height();
-            ASPECT = WIDTH / HEIGHT;
-            renderer.setSize(WIDTH, HEIGHT);
-            container.append(renderer.domElement);
-            glDOM = $(renderer.domElement);
-
-            if (!nomouse) {
-                // user can request that the mouse handlers not be installed
-                glDOM.bind('mousedown touchstart', _handleMouseDown);
-                glDOM.bind('DOMMouseScroll mousewheel', _handleMouseScroll);
-                glDOM.bind('mousemove touchmove', _handleMouseMove);
-                
-                glDOM.bind("contextmenu", function(ev) {
-                    ev.preventDefault();
-                });
-            }
-        };
-        initContainer(container);
-
-        // public methods
-        /**
-         * Change the viewer's container element 
-         * Also useful if the original container element was removed from the DOM.
-         * 
-         * @function $3Dmol.GLViewer#resetContainer
-         *
-         * @param {Object | string} element
-         *            Either HTML element or string identifier. Defaults to the element used to initialize the viewer.
-
-         * @example
-         * // Assume there exist HTML divs with ids "gldiv", "gldiv2"
-         * var element = $("#gldiv"), element2 = $("#gldiv2");
-         * // Create GLViewer within 'gldiv'
-         * var myviewer = $3Dmol.createViewer(element);
-         * // Move the canvas to the other div
-         * myviewer.setContainer(element2)
-         *
-         * @example
-         * // Assume there exists an HTML div with id "gldiv"
-         * var element = $("#gldiv");
-         * // Create GLViewer within 'gldiv'
-         * var myviewer = $3Dmol.createViewer(element);
-         * // Remove the element from the DOM, and add a new element
-         * element.remove()
-         * $('body').prepend("<div id='newdiv'></div>")
-         * // Show the canvas in the new element
-         * myviewer.setContainer('newdiv')
-         */
-        this.setContainer = function(element) {
-            if($.type(element) === "string")
-                element = $("#"+element);
-            if(!element) {
-                element = container
-            };
-            initContainer(element);
-        };
-        
-        /**
-         * Set the background color (default white)
-         * 
-         * @function $3Dmol.GLViewer#setBackgroundColor
-         * @param {number}
-         *            hex Hexcode specified background color, or standard color spec
-         * @param {number}
-         *            a Alpha level (default 1.0)
-         * 
-         * @example
-         * 
-         * //Set 'myviewer' background color to white
-         * myviewer.setBackgroundColor(0xffffff)
-         * 
-         */
-        this.setBackgroundColor = function(hex, a) {
-            if(typeof(a) == "undefined") {
-                a = 1.0;
-            }
-            else if(a < 0 || a > 1.0) {
-                a = 1.0;
-            }
-            var c = $3Dmol.CC.color(hex);
-            scene.fog.color = c;
-            bgColor = c.getHex();
-            renderer.setClearColorHex(c.getHex(), a);
-            show();
-        };
-        
-        /**
-         * Enable outline 
-         * @function $eDmol.GLViewer#outline
-         * 
-         * @example
-         * myviewer.outline()
-         * 
-         */
-         this.setViewStyle = function(parameters) {
-            if (parameters["style"] === "outline") {
-                var params = {};
-                if(parameters.color) params.color =  $3Dmol.CC.color(parameters.color);
-                if(parameters.width) params.width = parameters.width;
-                renderer.enableOutline(params);
-            } else {
-                renderer.disableOutline();
-            }
-        }
-         
-         if(config.style) { //enable setting style in constructor
-             this.setViewStyle(config.style);
-         }
-
-        /**
-         * Set viewer width
-         * 
-         * @function $3Dmol.GLViewer#setWidth
-         * @param {number}
-         *            w Width in pixels
-         */
-        this.setWidth = function(w) {
-            WIDTH = w || WIDTH;
-            renderer.setSize(WIDTH, HEIGHT);
-        };
-
-        /**
-         * Set viewer height
-         * 
-         * @function $3Dmol.GLViewer#setHeight
-         * @param {number}
-         *            h Height in pixels
-         */
-        this.setHeight = function(h) {
-            HEIGHT = h || HEIGHT;
-            renderer.setSize(WIDTH, HEIGHT);
-        };
-
-        /**
-         * Resize viewer according to containing HTML element's dimensions
-         * 
-         * @function $3Dmol.GLViewer#resize
-         */
-        this.resize = function() {
-            WIDTH = container.width();
-            HEIGHT = container.height();
-            ASPECT = WIDTH / HEIGHT;
-            renderer.setSize(WIDTH, HEIGHT);
-            camera.aspect = ASPECT;
-            camera.updateProjectionMatrix();
-            show();
-        };
-
-        $(window).resize(this.resize);
-
-        /**
-         * Return specified model
-         * 
-         * @function $3Dmol.GLViewer#getModel
-         * @param {number}
-         *            [id=last model id] - Retrieve model with specified id
-         * @default Returns last model added to viewer
-         * @return {GLModel}
-         * 
-         * @example // Retrieve reference to first GLModel added var m =
-         *          glviewer.getModel(0);
-         */
-        this.getModel = function(id) {
-            id = id || models.length - 1;
-            return models[id];
-        };
-
-        /**
-         * Rotate scene by angle degrees around axis
-         * 
-         * @function $3Dmol.GLViewer#rotate
-         * @param {number}
-         *            [angle] - Angle, in degrees, to rotate by.
-         * @param {string}
-         *            [angle] - Axis ("x", "y", or "z") to rotate around.
-         *            Default "y"
-         * 
-         */
-        this.rotate = function(angle, axis) {
-            if (typeof (axis) === "undefined") {
-                axis = "y";
-            }
-            var i = 0, j = 0, k = 0;
-            var rangle = Math.PI * angle / 180.0;
-            var s = Math.sin(rangle / 2.0);
-            var c = Math.cos(rangle / 2.0);
-            if (axis == "x")
-                i = s;
-            if (axis == "y")
-                j = s;
-            if (axis == "z")
-                k = s;
-
-            var q = new $3Dmol.Quaternion(i, j, k, c).normalize();
-            rotationGroup.quaternion.multiply(q);
-            show();
-        };
-
-        /** Returns an array representing the current viewpoint.
-         * Translation, zoom, and rotation quaternion. 
-         * @function $3Dmol.GLViewer#getView
-         * @returns {Array.<number>} arg
-         *  */
-        this.getView = function() {
-            if (!modelGroup)
-                return [ 0, 0, 0, 0, 0, 0, 0, 1 ];
-            var pos = modelGroup.position;
-            var q = rotationGroup.quaternion;
-            return [ pos.x, pos.y, pos.z, rotationGroup.position.z, q.x, q.y,
-                    q.z, q.w ];
-        };
-
-        /** Sets the view to the specified translation, zoom, and rotation.
-         * 
-         * @function $3Dmol.GLViewer#setView
-         * @param {Array.<number>} arg Array formatted identically to the return value of getView */
-        this.setView = function(arg, nolink) {
-
-            if (arg === undefined
-                    || !(arg instanceof Array || arg.length !== 8))
-                return;
-
-            if (!modelGroup || !rotationGroup)
-                return;
-            modelGroup.position.x = arg[0];
-            modelGroup.position.y = arg[1];
-            modelGroup.position.z = arg[2];
-            rotationGroup.position.z = arg[3];
-            rotationGroup.quaternion.x = arg[4];
-            rotationGroup.quaternion.y = arg[5];
-            rotationGroup.quaternion.z = arg[6];
-            rotationGroup.quaternion.w = arg[7];
-            if(typeof(arg[8]) != "undefined") {
-                rotationGroup.position.x = arg[8];
-                rotationGroup.position.y = arg[9];
-            }
-            show(nolink);
-        };
-
-        // apply styles, models, etc in viewer
-        /**
-         * Render current state of viewer, after 
-         * adding/removing models, applying styles, etc.
-         * 
-         * @function $3Dmol.GLViewer#render
-         */
-        this.render = function() {
-
-            updateClickables(); //must render for clickable styles to take effect
-            var time1 = new Date();
-            var view = this.getView();
-            
-            var i, n;
-            for (i = 0; i < models.length; i++) {
-                if (models[i]) {
-                    models[i].globj(modelGroup);
-                }
-            }
-
-            for (i = 0; i < shapes.length; i++) {
-                if (shapes[i]) {
-                    shapes[i].globj(modelGroup);
-                }
-            }
-            
-            for (i in surfaces) { // this is an object with possible holes
-                if(!surfaces.hasOwnProperty(i)) continue;
-                var surfArr = surfaces[i];
-                for (n = 0; n < surfArr.length; n++) {
-                    if (surfArr.hasOwnProperty(n)) {
-                        var geo = surfArr[n].geo;
-                        // async surface generation can cause
-                        // the geometry to be webgl initialized before it is fully
-                        // formed; force various recalculations until full surface
-                        // is
-                        // available
-                        if (!surfArr[n].finished) {
-                            geo.verticesNeedUpdate = true;
-                            geo.elementsNeedUpdate = true;
-                            geo.normalsNeedUpdate = true;
-                            geo.colorsNeedUpdate = true;
-                            geo.buffersNeedUpdate = true;
-                            geo.boundingSphere = null;
-
-                            if (surfArr[n].done)
-                                surfArr[n].finished = true;
-
-                            // remove partially rendered surface
-                            if (surfArr[n].lastGL)
-                                modelGroup.remove(surfArr[n].lastGL);
-
-                            // create new surface
-                            var smesh = null;
-
-                            if(surfArr[n].mat instanceof $3Dmol.LineBasicMaterial) {
-                                //special case line meshes
-                                smesh = new $3Dmol.Line(geo, surfArr[n].mat);
-                            }
-                            else {
-                                smesh = new $3Dmol.Mesh(geo, surfArr[n].mat);
-                            }
-                            if(surfArr[n].mat.transparent && surfArr[n].mat.opacity == 0) {
-                                //don't bother with hidden surfaces
-                                smesh.visible = false;
-                            } else {
-                                smesh.visible = true;
-                            }
-                            if (surfArr[n].symmetries.length > 1 || 
-                            (surfArr[n].symmetries.length == 1 && 
-                            !(surfArr[n].symmetries[n].isIdentity()))) {
-                                var j;
-                                var tmeshes = new $3Dmol.Object3D(); //transformed meshes
-                                for (j = 0; j < surfArr[n].symmetries.length; j++) {
-                                    var tmesh = smesh.clone();
-                                    tmesh.matrix = surfArr[n].symmetries[j];
-                                    tmesh.matrixAutoUpdate = false;
-                                    tmeshes.add(tmesh);
-                                }
-                                surfArr[n].lastGL = tmeshes;
-                                modelGroup.add(tmeshes);
-                            }
-                            else {
-                                surfArr[n].lastGL = smesh;
-                                modelGroup.add(smesh);
-                            }
-                        } // else final surface already there
-                    }
-                }
-            }
-            
-            this.setView(view); // Calls show() => renderer render
-            var time2 = new Date();
-            //console.log("render time: " + (time2 - time1));
-        };
-
-        /**
-         * 
-         * @param {AtomSelectionSpec}
-         *            sel
-         * @return {AtomSpec[]}
-         */
-        function getAtomsFromSel(sel) {
-            var atoms = [];
-            if (typeof (sel) === "undefined")
-                sel = {};
-
-            var ms = [];
-            var i;
-
-            if (typeof sel.model === "undefined") {
-                for (i = 0; i < models.length; i++) {
-                    if (models[i])
-                        ms.push(models[i]);
-                }
-            } else { // specific to some models
-                ms = sel.model;
-                if (!$.isArray(ms))
-                    ms = [ ms ];
-            }
-
-            for (i = 0; i < ms.length; i++) {
-                atoms = atoms.concat(ms[i].selectedAtoms(sel));
-            }
-
-            return atoms;
-        }
-
-        /**
-         * 
-         * @param {AtomSpec}
-         *            atom
-         * @param {AtomSpec}
-         *            sel
-         * @return {boolean}
-         */
-        function atomIsSelected(atom, sel) {
-            if (typeof (sel) === "undefined")
-                sel = {};
-
-            var ms = [];
-            var i;
-
-            if (typeof sel.model === "undefined") {
-                for (i = 0; i < models.length; i++) {
-                    if (models[i])
-                        ms.push(models[i]);
-                }
-            } else { // specific to some models
-                ms = sel.model;
-                if (!$.isArray(ms))
-                    ms = [ ms ];
-            }
-
-            for (i = 0; i < ms.length; i++) {
-                if (ms[i].atomIsSelected(atom, sel))
-                    return true;
-            }
-
-            return false;
-        }
-
-        
-        /** return list of atoms selected by sel
-         * 
-         * @function $3Dmol.GLViewer#selectedAtoms
-         * @param {AtomSelectionSpec} sel
-         * @return {Array.<Object>}
-         */
-        this.selectedAtoms = function(sel) {
-            return getAtomsFromSel(sel);
-        };
-        
-        /**
-         * Return pdb output of selected atoms (if atoms from pdb input)
-         * 
-         * @function $3Dmol.GLViewer#pdbData  
-         * @param {Object=} [sel] - Selection specification specifying model and atom properties to select.  Default: all atoms in viewer
-         * @return {string} PDB string of selected atoms
-         */
-        this.pdbData = function(sel) {
-            var atoms = getAtomsFromSel(sel);
-            var ret = "";
-            for (var i = 0, n = atoms.length; i < n; ++i) {
-                ret += atoms[i].pdbline + "\n";
-            }
-            return ret;
-        };
-
-        /**
-         * Zoom current view by a constant factor
-         * 
-         * @function $3Dmol.GLViewer#zoom
-         * @param {number}
-         *            [factor] - Magnification factor. Values greater than 1
-         *            will zoom in, less than one will zoom out. Default 2.
-         * 
-         */
-        this.zoom = function(factor) {
-            var factor = factor || 2;
-            var scale = (CAMERA_Z - rotationGroup.position.z) / factor;
-            rotationGroup.position.z = CAMERA_Z - scale;
-            show();
-        };
-        
-        /**
-         * Translate current view by x,y screen coordinates
-         * This pans the camera rather than translating the model.
-         * 
-         * @function $3Dmol.GLViewer#translate
-         * @param {number} x
-         * @param {number} y
-         * 
-         */
-        this.translate = function(x, y) {
-            
-            var dx = x/WIDTH;
-            var dy = y/HEIGHT;
-            var v = new $3Dmol.Vector3(0,0,-CAMERA_Z);
-
-            projector.projectVector(v, camera);
-            v.x -= dx;
-            v.y -= dy;
-            projector.unprojectVector(v, camera);
-            v.z = 0;            
-            lookingAt.add(v);
-            camera.lookAt(lookingAt);
-            show();
-        };
-        
-
-        /**
-         * Zoom to center of atom selection
-         * 
-         * @function $3Dmol.GLViewer#zoomTo
-         * @param {Object}
-         *            [sel] - Selection specification specifying model and atom
-         *            properties to select. Default: all atoms in viewer
-         * @example // Assuming we have created a model of a protein with
-         *          multiple chains (e.g. from a PDB file), focus on atoms in
-         *          chain B glviewer.zoomTo({chain: 'B'});
-         *  // Focus on centroid of all atoms of all models in this
-         * viewer glviewer.zoomTo(); // (equivalent to glviewer.zoomTo({}) )
-         */
-        this.zoomTo = function(sel) {
-            var allatoms, alltmp;
-            sel = sel || {};
-            var atoms = getAtomsFromSel(sel);
-            var tmp = $3Dmol.getExtent(atoms);
-
-            if($.isEmptyObject(sel)) {
-                //include shapes when zooming to full scene
-                //TODO: figure out a good way to specify shapes as part of a selection
-                $.each(shapes, function(i, shape) {
-                	if(shape && shape.boundingSphere && shape.boundingSphere.center)
-                		atoms.push(shape.boundingSphere.center);
-                });
-                tmp = $3Dmol.getExtent(atoms);
-                allatoms = atoms;
-                alltmp = tmp;
-
-            }
-            else {
-                allatoms = getAtomsFromSel({});
-                alltmp = $3Dmol.getExtent(allatoms);
-            }
-
-            // use selection for center
-            var center = new $3Dmol.Vector3(tmp[2][0], tmp[2][1], tmp[2][2]);
-            modelGroup.position = center.clone().multiplyScalar(-1);
-            // but all for bounding box
-            var x = alltmp[1][0] - alltmp[0][0], y = alltmp[1][1]
-                    - alltmp[0][1], z = alltmp[1][2] - alltmp[0][2];
-
-            var maxD = Math.sqrt(x * x + y * y + z * z);
-            if (maxD < 5)
-                maxD = 5;
-
-            // use full bounding box for slab/fog
-            slabNear = -maxD / 1.9;
-            slabFar = maxD / 2;
-
-            // for zoom, use selection box
-            x = tmp[1][0] - tmp[0][0];
-            y = tmp[1][1] - tmp[0][1];
-            z = tmp[1][2] - tmp[0][2];
-            maxD = Math.sqrt(x * x + y * y + z * z);
-            if (maxD < 5)
-                maxD = 5;
-            
-            //find the farthest atom from center to get max distance needed for view
-            var maxDsq = 25;
-            for (var i = 0; i < atoms.length; i++) {
-                if(atoms[i]) {
-                    var dsq = center.distanceToSquared(atoms[i]);
-                    if(dsq > maxDsq)
-                        maxDsq = dsq;
-                }
-            }
-            
-            var maxD = Math.sqrt(maxDsq)*2;
-
-            rotationGroup.position.z = -(maxD * 0.5
-                    / Math.tan(Math.PI / 180.0 * camera.fov / 2) - CAMERA_Z);
-            show();
-        };
-        /**
-         * Add label to viewer
-         * 
-         * @function $3Dmol.GLViewer#addLabel
-         * @param {string}
-         *            text - Label text
-         * @param {Object}
-         *            data - Label style specification
-         * @return {$3Dmol.Label}
-         * 
-         * @example
-         *  // Assuming glviewer contains a model representing a protein, label
-         * all alpha carbons with their residue name
-         *  // Select all alpha carbons (have property atom : "CA") from last
-         * model added var atoms =
-         * glviewer.getModel().selectedAtoms({atom:"CA"}); var labels = [];
-         * 
-         * for (var a in atoms) { var atom = atoms[a];
-         *  // Create label at alpha carbon's position displaying atom's residue
-         * and residue number var labelText = atom.resname + " " + atom.resi;
-         * 
-         * var l = glviewer.createLabel(labelText, {fontSize: 12, position: {x:
-         * atom.x, y: atom.y, z: atom.z});
-         * 
-         * labels.push(l); }
-
-         *  // Render labels glviewer.render();
-         */
-        this.addLabel = function(text, data) {
-            var label = new $3Dmol.Label(text, data);
-            label.setContext();
-            modelGroup.add(label.sprite);
-            labels.push(label);
-            show();
-            return label;
-        };
-        
-        /** Add residue labels.  This will generate one label per a
-         * residue within the selected atoms.  The label will be at the
-         * centroid of the atoms and styled according to the passed style.
-         * The label text will be [resn][resi]
-         * 
-         * @param {Object} sel
-         * @param {Object} style
-         */
-        this.addResLabels = function(sel, style) {
-            applyToModels("addResLabels", sel, this, style);
-        }
-
-        /**
-         * Remove label from viewer
-         * 
-         * @function $3Dmol.GLViewer#removeLabel
-         * @param {$3Dmol.Label}
-         *            label - $3Dmol label
-         * 
-         * @example // Remove labels created in [addLabel example]{@link $3Dmol.GLViewer#addLabel}
-         * 
-         * for (var i = 0; i < labels.length; i++) {
-         * glviewer.removeLabel(label); }
-         * 
-         * glviewer.render();
-         */
-        this.removeLabel = function(label) {
-            //todo: don't do the linear search
-            for(var i = 0; i < labels.length; i++) {
-                if(labels[i] == label) {
-                    labels.splice(i,1);
-                    label.dispose();
-                    modelGroup.remove(label.sprite);
-                    break;
-                }
-            }
-
-        };
-
-        /**
-         * Remove all labels from viewer
-         * 
-         * @function $3Dmol.GLViewer#removeAllLabels
-         */
-        this.removeAllLabels = function() {
-            for (var i = 0; i < labels.length; i++) {
-                modelGroup.remove(labels[i].sprite);
-            }
-            labels.splice(0,labels.length); //don't overwrite in case linked
-        };
-        
-        // Modify label style
-        /**
-         * Modify existing label's style
-         * 
-         * @function $3Dmol.GLViewer#setLabelStyle
-         * @param {$3Dmol.Label}
-         *            label - $3Dmol label
-         * @param {Object}
-         *            stylespec - Label style specification
-         * @return {$3Dmol.Label}
-         */
-        this.setLabelStyle = function(label, stylespec) {
-            modelGroup.remove(label.sprite);
-            label.dispose();
-            label.stylespec = stylespec;
-            label.setContext();
-            modelGroup.add(label.sprite);
-            show();
-            return label;
-
-        };
-
-        // Change label text
-        /**
-         * Modify existing label's text
-         * 
-         * @function $3Dmol.GLViewer#setLabelText
-         * @param {$3Dmol.Label}
-         *            label - $3Dmol label
-         * @param {String}
-         *            text - Label text
-         * @return {$3Dmol.Label}
-         */
-        this.setLabelText = function(label, text) {
-            modelGroup.remove(label.sprite);
-            label.dispose();
-            label.text = text;
-            label.setContext();
-            modelGroup.add(label.sprite);
-            show();
-            return label;
-
-        };
-
-        /**
-         * Add shape object to viewer 
-         * @see {@link $3Dmol.GLShape}
-         * 
-         * @function $3Dmol.GLViewer#addShape
-         * @param {ShapeSpec} shapeSpec - style specification for label
-         * @return {$3Dmol.GLShape}
-         */
-        this.addShape = function(shapeSpec) {
-            shapeSpec = shapeSpec || {};
-            var shape = new $3Dmol.GLShape(shapeSpec);
-            shape.shapePosition = shapes.length;
-            shapes.push(shape);
-
-            return shape;
-
-        };
-
-        /**
-         * Remove shape object from viewer
-         *
-         * @function $3Dmol.GLViewer#removeShape
-         * @param {$3Dmol.GLShape} shape - Reference to shape object to remove
-         */
-        this.removeShape = function(shape) {
-            if (!shape)
-                return;
-            shape.removegl(modelGroup);
-            delete shapes[shape.shapePosition];
-            // clear off back of model array
-            while (shapes.length > 0
-                    && typeof (shapes[shapes.length - 1]) === "undefined")
-                shapes.pop();
-        };
-        
-        /**
-         * Remove all shape objects from viewer
-         * @function $3Dmol.GLViewer#removeAllShapes
-         */
-        this.removeAllShapes = function() {
-            for (var i = 0; i < shapes.length; i++) {
-                var shape = shapes[i];
-                shape.removegl(modelGroup);
-            }
-            shapes.splice(0,shapes.length);
-        }
-
-        /**
-         * Create and add sphere shape. This method provides a shorthand 
-         * way to create a spherical shape object
-         * 
-         * @function $3Dmol.GLViewer#addSphere
-         * @param {SphereSpec} spec - Sphere shape style specification
-         * @return {$3Dmol.GLShape}
-         */
-        this.addSphere = function(spec) {
-            spec = spec || {};
-            var s = new $3Dmol.GLShape(spec);
-            s.shapePosition = shapes.length;
-            s.addSphere(spec);
-            shapes.push(s);
-
-            return s;
-        };
-
-        /**
-         * Create and add arrow shape
-         * 
-         * @function $3Dmol.GLViewer#addArrow
-         * @param {ArrowSpec} spec - Style specification
-         * @return {$3Dmol.GLShape}
-         */
-        this.addArrow = function(spec) {
-            spec = spec || {};
-            var s = new $3Dmol.GLShape(spec);
-            s.shapePosition = shapes.length;
-            s.addArrow(spec);
-            shapes.push(s);
-
-            return s;
-        };
-        
-        /**
-         * Create and add cylinder shape
-         * 
-         * @function $3Dmol.GLViewer#addCylinder
-         * @param {CylinderSpec} spec - Style specification
-         * @return {$3Dmol.GLShape}
-         */
-        this.addCylinder = function(spec) {
-            spec = spec || {};
-            var s = new $3Dmol.GLShape(spec);
-            s.shapePosition = shapes.length;
-            s.addCylinder(spec);
-            shapes.push(s);
-
-            return s;
-        };
-
-        /**
-         * Create and add line shape
-         * 
-         * @function $3Dmol.GLViewer#addLine
-         * @param {LineSpec} spec - Style specification
-         * @return {$3Dmol.GLShape}
-         */
-        this.addLine = function(spec) {
-            spec = spec || {};
-            spec.wireframe = true;
-            var s = new $3Dmol.GLShape(spec);
-            s.shapePosition = shapes.length;
-            if (spec.dashed)
-                s = addLineDashed(spec, s);
-            else
-                s.addLine(spec);
-            shapes.push(s);
-
-            return s;
-        };
-        
-        
-        /**
-         * Create and add unit cell
-         *
-         * @function $3Dmol.GLViewer#addUnitCell
-         * @param {GLModel} Model with unit cell information (e.g., pdb derived).
-         * @return {$3Dmol.GLShape}  Line shape delineating unit cell.
-         */
-        this.addUnitCell = function(model) {
-
-            var s = new $3Dmol.GLShape({'wireframe' : true});
-            s.shapePosition = shapes.length;
-            var data = model.getCrystData();
-            if (data) {
-                var a = data.a, b = data.b, c = data.c, alpha = data.alpha, beta = data.beta, gamma = data.gamma;
-                alpha = alpha * Math.PI/180.0;
-                beta = beta * Math.PI/180.0;
-                gamma = gamma * Math.PI/180.0;
-            
-                var u, v, w;
-            
-                u = Math.cos(beta);
-                v = (Math.cos(alpha) - Math.cos(beta)*Math.cos(gamma))/Math.sin(gamma);
-                w = Math.sqrt(Math.max(0, 1-u*u-v*v));
-            
-                var matrix = new $3Dmol.Matrix4(a, b*Math.cos(gamma), c*u, 0, 
-                                                0, b*Math.sin(gamma), c*v, 0,
-                                                0, 0,                 c*w, 0,
-                                                0, 0,                 0,   1); 
-         
-                var points = [  new $3Dmol.Vector3(0, 0, 0),
-                                new $3Dmol.Vector3(1, 0, 0),
-                                new $3Dmol.Vector3(0, 1, 0),
-                                new $3Dmol.Vector3(0, 0, 1),
-                                new $3Dmol.Vector3(1, 1, 0),
-                                new $3Dmol.Vector3(0, 1, 1),
-                                new $3Dmol.Vector3(1, 0, 1),
-                                new $3Dmol.Vector3(1, 1, 1)  ];
-                            
-                for (var i = 0; i < points.length; i++) {
-                    points[i] = points[i].applyMatrix4(matrix);
-                }
-            
-                s.addLine({start: points[0], end: points[1]});
-                s.addLine({start: points[0], end: points[2]});
-                s.addLine({start: points[1], end: points[4]});
-                s.addLine({start: points[2], end: points[4]});
-            
-                s.addLine({start: points[0], end: points[3]});
-                s.addLine({start: points[3], end: points[5]});
-                s.addLine({start: points[2], end: points[5]});
-            
-                s.addLine({start: points[1], end: points[6]});
-                s.addLine({start: points[4], end: points[7]});
-                s.addLine({start: points[6], end: points[7]});
-            
-                s.addLine({start: points[3], end: points[6]});
-                s.addLine({start: points[5], end: points[7]});
-            }
-            
-            shapes.push(s);
-            return s;
-        };
-
-        function addLineDashed(spec, s) {
-            spec.dashLength = spec.dashLength || 0.5;
-            spec.gapLength = spec.gapLength || 0.5;
-            spec.start = spec.start || {};
-            spec.end = spec.end || {};
-            
-            var p1 = new $3Dmol.Vector3(spec.start.x || 0,
-        			spec.start.y || 0, spec.start.z || 0)
-        	var p2 = new $3Dmol.Vector3(spec.end.x,
-        			spec.end.y || 0, spec.end.z || 0);
-        			
-            var dir = new $3Dmol.Vector3();
-            var dash = new $3Dmol.Vector3();
-            var gap = new $3Dmol.Vector3();
-            var length, dashAmt, gapAmt;
-            var temp = p1.clone();
-            var drawn = 0;
-
-            dir.subVectors(p2, p1);
-            length = dir.length();
-            dir.normalize();
-            dash = dir.clone();
-            gap = dir.clone();
-            dash.multiplyScalar(spec.dashLength);
-            gap.multiplyScalar(spec.gapLength);
-            dashAmt = dash.length();
-            gapAmt = gap.length();
-
-            while (drawn < length) {
-                if ((drawn + dashAmt) > length) { 
-                    spec.start = p1;
-                    spec.end = p2;
-                    s.addLine(spec);
-                    break;
-                }
-                temp.addVectors(p1, dash); 
-                spec.start = p1;
-                spec.end = temp;
-                s.addLine(spec);
-                p1 = temp.clone();
-                drawn += dashAmt;
-
-                temp.addVectors(p1, gap);
-                p1 = temp.clone();   
-                drawn += gapAmt;
-            }
-        			
-        	return s;
-        }
-
-        /**
-         * Add custom shape component from user supplied function
-         * 
-         * @function $3Dmol.GLViewer#addCustom
-         * @param {CustomSpec} spec - Style specification
-         * @return {$3Dmol.GLShape}
-         */
-        this.addCustom = function(spec) {
-            spec = spec || {};
-            var s = new $3Dmol.GLShape(spec);
-            s.shapePosition = shapes.length;
-            s.addCustom(spec);
-            shapes.push(s);
-
-            return s;
-        };
-
-        /**
-         * Construct isosurface from volumetric data in gaussian cube format
-         * @deprecated
-         * @function $3Dmol.GLViewer#addVolumetricData
-         * @param {String} data - Input file contents 
-         * @param {String} format - Input file format (currently only supports "cube")
-         * @param {IsoSurfaceSpec} spec - Shape style specification
-         * @return {$3Dmol.GLShape}
-         * 
-         * @example
-         * viewer.addVolumetricData(data, "cube", {isoval: 0.01, color: "blue", opacity: 0.95});              
-         * viewer.addVolumetricData(data, "cube", {isoval: -0.01, color: "red", opacity: 0.95}); 
-         */
-        this.addVolumetricData = function(data, format, spec) {
-            spec = spec || {};
-            var s = new $3Dmol.GLShape(spec);
-            s.shapePosition = shapes.length;
-            s.addVolumetricData(data, format, spec);
-            shapes.push(s);
-
-            return s;
-        };
-        
-        /**
-         * Construct isosurface from volumetric data
-         * @function $3Dmol.GLViewer#addIsosurface
-         * @param {$3Dmol.VolumeData} data - volumetric data
-         * @param {IsoSurfaceSpec} spec - Shape style specification
-         * @return {$3Dmol.GLShape}
-         * 
-         * @example
-         * var data = new $3Dmol.VolumeData(str,"cube");
-         * viewer.addIsosurface(data, {isoval: 0.01, color: "blue", opacity: 0.95});              
-         * viewer.addIsosurface(data, {isoval: -0.01, color: "red", opacity: 0.95}); 
-         */
-        this.addIsosurface = function(data,  spec) {
-            spec = spec || {};
-            var s = new $3Dmol.GLShape(spec);
-            s.shapePosition = shapes.length;
-            s.addIsosurface(data, spec);
-            shapes.push(s);
-
-            return s;
-        };
-        
-        /**
-         * Sets the atomlists of all models in the viewer to specified frame
-         * Sets to last frame if framenum out of range
-         * 
-         * @function $3Dmol.GLViewer#setFrame
-         * @param {number} framenum - each model in viewer has their atoms set to this index in frames list
-         */
-        this.setFrame = function(framenum) {
-            for (var i = 0; i < models.length; i++) {
-                models[i].setFrame(framenum);
-            }
-        };
-        
-        /**
-         * Returns the number of frames that the model with the most frames in the viewer has
-         * 
-         * @function $3Dmol.GLViewer#getFrames
-         * @return {number}
-         */
-        this.getFrames = function() {
-            var mostFrames = 0;
-            var modelNum = 0;
-            for (var i = 0; i < models.length; i++) {
-                if (models[i].getFrames().length > mostFrames) {
-                    modelNum = i;
-                    mostFrames = models[i].getFrames().length;
-                }
-            }
-            return mostFrames;
-        };
-        
-
-        /**
-         * Animate all models in viewer from their respective frames
-         * @function $3Dmol.GLViewer#animate
-         * @param {Object} options - can specify interval (speed of animation), loop (direction
-         * of looping, 'backward', 'forward' or 'backAndForth') and reps (numer of repetitions, 0 indicates infinite loop)
-         *      
-         * @example
-         * viewer.addModelAsFrames(data, "pdb");
-         * viewer.animate({interval: 75, loop: "backward", reps: 30});
-         */
-        this.animate = function(options) {
-            animated = true;
-            var interval = 100;
-            var loop = "forward";
-            var reps = 0;
-            options = options || {};
-            if (options.interval) {
-                interval = options.interval;
-            }
-            if (options.loop) {
-                loop = options.loop;
-            }
-            if (options.reps) {
-                reps = options.reps;
-            }
-            var mostFrames = this.getFrames();
-            var that = this;
-            var currFrame = 0;
-            var inc = 1;
-            var displayCount = 0;
-            var displayMax = mostFrames * reps;
-            var display = function(direction) {
-                if (direction == "forward") {
-                    that.setFrame(currFrame);
-                    currFrame = (currFrame + inc) % mostFrames;
-                }
-                else if (direction == "backward") {
-                    that.setFrame((mostFrames-1) - currFrame);
-                    currFrame = (currFrame + inc) % mostFrames;
-                }
-                else { //back and forth
-                    that.setFrame(currFrame);
-                    currFrame += inc;
-                    inc *= (((currFrame % (mostFrames-1)) == 0) ? -1 : 1);
-                }
-                that.render();
-                if (++displayCount == displayMax || !that.isAnimated()) {
-                    clearInterval(intervalID);
-                }
-            };
-            var intervalID = setInterval( function() { display(loop); }, interval);
-        };
-        
-        /**
-         * Stop animation of all models in viewer
-         * @function $3Dmol.GLViewer#stopAnimate
-         */
-        this.stopAnimate = function() {
-            animated = false;
-        };
-        
-        /**
-         * Return true if viewer is currently being animated, false otherwise
-         * @function $3Dmol.GLViewer#isAnimated
-         * @return {boolean}
-         */
-        this.isAnimated = function() {
-            return animated;
-        };
-        
-
-        /**
-         * Create and add model to viewer, given molecular data and its format 
-         * (pdb, sdf, xyz, or mol2)
-         * 
-         * @function $3Dmol.GLViewer#addModel
-         * @param {string} data - Input data
-         * @param {string} format - Input format ('pdb', 'sdf', 'xyz', or 'mol2')
-         * @return {$3Dmol.GLModel}
-         */
-        this.addModel = function(data, format, options) {
-            var m = new $3Dmol.GLModel(models.length, defaultcolors);
-            m.addMolData(data, format, options);
-            models.push(m);
-
-            return m;
-        };
-        
-        /**
-         * Given multimodel file and its format, add atom data to the viewer as separate models
-         * and return list of these models
-         * 
-         * @function $3Dmol.GLViewer#addModels
-         * @param {string} data - Input data
-         * @param {string} format - Input format ('pdb', 'sdf', 'xyz', or 'mol2')
-         * @return {Array<$3Dmol.GLModel>}
-         */
-        this.addModels = function(data, format, options) {
-            options = options || {};
-            options.multimodel = true;
-            options.frames = true;
-
-            var m = new $3Dmol.GLModel(models.length, defaultcolors);
-            m.addMolData(data, format, options);
-            var modelatoms = m.getFrames();
-            this.removeModel(m);
-            
-            for (var i = 0; i < modelatoms.length; i++) {
-                var newModel = new $3Dmol.GLModel(models.length, defaultcolors);
-                newModel.addFrame(modelatoms[i]);
-                newModel.setFrame(0);
-                models.push(newModel);
-            }
-            
-            return models;
-        };
-        
-        /**
-         * Create and add model to viewer. Given multimodel file and its format, 
-         * different atomlists are stored in model's frame
-         * property and model's atoms are set to the 0th frame
-         * 
-         * @function $3Dmol.GLViewer#addModelsAsFrames
-         * @param {string} data - Input data
-         * @param {string} format - Input format ('pdb', 'sdf', 'xyz', or 'mol2')
-         * @return {$3Dmol.GLModel}
-         */
-        this.addModelsAsFrames = function(data, format, options) {
-            options = options || {};
-            options.multimodel = true;
-            options.frames = true;
-            var m = new $3Dmol.GLModel(models.length, defaultcolors);
-            m.addMolData(data, format, options);
-            models.push(m);
-
-            return m;
-        };
-        
-        /**
-         * Create and add model to viewer. Given multimodel file and its format,
-         * all atoms are added to one model
-         * 
-         * @function $3Dmol.GLViewer#addAsOneMolecule
-         * @param {string} data - Input data
-         * @param {string} format - Input format ('pdb', 'sdf', 'xyz', or 'mol2')
-         * @return {$3Dmol.GLModel}
-         */
-        this.addAsOneMolecule = function(data, format, options) {
-            options = options || {};
-            options.multimodel = true;
-            options.onemol = true;
-            var m = new $3Dmol.GLModel(models.length, defaultcolors);
-            m.addMolData(data, format, options);
-            models.push(m);
-            
-            return m;
-        };
-        
-
-        /**
-         * Delete specified model from viewer
-         * 
-         * @function $3Dmol.GLViewer#removeModel
-         * @param {$3Dmol.GLModel} model
-         */
-        this.removeModel = function(model) {
-            if (!model)
-                return;
-            model.removegl(modelGroup);
-            delete models[model.getID()];
-            // clear off back of model array
-            while (models.length > 0
-                    && typeof (models[models.length - 1]) === "undefined")
-                models.pop();
-        };
-
-        /** 
-         * Delete all existing models
-         * @function $3Dmol.GLViewer#removeAllModels
-         */
-        this.removeAllModels = function() {
-            for (var i = 0; i < models.length; i++) {
-                var model = models[i];
-                model.removegl(modelGroup);
-
-            }
-            models.splice(0,models.length); //don't simply overwrite array in case linked
-        };
-
-        /**
-         * Export one or all of the loaded models into ChemDoodle compatible JSON.
-         * @function $3Dmol.GLViewer#exportJSON
-         * @param {boolean} includeStyles - Whether or not to include style information.
-         * @param {number} modelID - Optional parameter for which model to export. If left out, export all of them.
-         * @return {string}
-         */
-        this.exportJSON = function(includeStyles, modelID) {
-            var object = {};
-            if (modelID === undefined) {
-                object.m = models.map(function(model) {
-                    return model.toCDObject(includeStyles);
-                });
-            } else {
-                object.m = [ model[modelID].toCDObject() ];
-            }
-            return JSON.stringify(object);
-        }
-
-        /**
-         * Create a new model from atoms specified by sel.
-         * If extract, removes selected atoms from existing models 
-         * 
-         * @function $3Dmol.GLViewer#createModelFrom
-         * @param {Object} sel - Atom selection specification
-         * @param {boolean=} extract - If true, remove selected atoms from existing models
-         * @return {$3Dmol.GLModel}
-         */
-        this.createModelFrom = function(sel, extract) {
-            var m = new $3Dmol.GLModel(models.length, defaultcolors);
-            for (var i = 0; i < models.length; i++) {
-                if (models[i]) {
-                    var atoms = models[i].selectedAtoms(sel);
-                    m.addAtoms(atoms);
-                    if (extract)
-                        models[i].removeAtoms(atoms);
-                }
-            }
-            models.push(m);
-            return m;
-        };
-
-        function applyToModels(func, sel, value1, value2) {
-            
-            //apply func to all models that are selected by sel with value1 and 2
-            var ms = []
-            if (typeof sel.model === "undefined") {
-                for (i = 0; i < models.length; i++) {
-                    if (models[i])
-                        ms.push(models[i]);
-                }
-            } else { // specific to some models
-                ms = sel.model;
-                if (!$.isArray(ms))
-                    ms = [ ms ];
-            }
-            
-            
-            for (var i = 0; i < ms.length; i++) {
-                if (ms[i]) {
-                    ms[i][func](sel, value1, value2);
-                }
-            }
-        }
-
-        /**
-         * Set style properties to all selected atoms
-         * 
-         * @function $3Dmol.GLViewer#setStyle
-         * @param {AtomSelectionSpec} sel - Atom selection specification
-         * @param {AtomStyleSpec} style - Style spec to apply to specified atoms
-         * 
-         * @example
-         * viewer.setStyle({}, {stick:{}}); //set all atoms to stick
-         * viewer.setStyle({chain: 'B'}, {cartoon: {color: 'spectrum'}}); //set chain B to rainbow cartoon
-         */
-        this.setStyle = function(sel, style) {
-            if(typeof(style) === 'undefined') {
-                //if a single argument is provided, assume it is a style and select all
-                style = sel;
-                sel = {};
-            }
-            
-            applyToModels("setStyle", sel, style, false);
-        };
-
-        /**
-         * Add style properties to all selected atoms
-         * 
-         * @function $3Dmol.GLViewer#addStyle
-         * @param {AtomSelectionSpec} sel - Atom selection specification
-         * @param {AtomStyleSpec} style - style spec to add to specified atoms
-         */
-        this.addStyle = function(sel, style) {
-            if(typeof(style) === 'undefined') {
-                //if a single argument is provided, assume it is a style and select all
-                style = sel;
-                sel = {};
-            }
-            applyToModels("setStyle", sel, style, true);
-        };
-
-        /**
-         * Set click-handling properties to all selected atoms
-         * 
-         * @function $3Dmol.GLViewer#setClickable
-         * @param {AtomSelectionSpec} sel - atom selection to apply clickable settings to
-         * @param {boolean} clickable - whether click-handling is enabled for the selection
-         * @param {function} callback - function called when an atom in the selection is clicked
-         * 
-         * @example
-         * viewer.setClickable({}, false); // disable click-handling for entire viewer
-         * viewer.setClickable({chain: 'B'}, true, function(){ console.log(this.elem); }); // chain B prints the clicked element to console
-         */
-        this.setClickable = function(sel, clickable, callback) {
-            applyToModels("setClickable", sel, clickable, callback);
-        };
-
-        /**
-         * @function $3Dmol.GLViewer#setColorByProperty
-         * @param {AtomSelectionSpec} sel
-         * @param {type} prop
-         * @param {type} scheme
-         */
-        this.setColorByProperty = function(sel, prop, scheme) {
-            applyToModels("setColorByProperty", sel, prop, scheme);
-        };
-
-        /**
-         * @function $3Dmol.GLViewer#setColorByElement
-         * @param {AtomSelectionSpec} sel
-         * @param {type} colors
-         */
-        this.setColorByElement = function(sel, colors) {
-            applyToModels("setColorByElement", sel, colors);
-        };
-
-        /**
-         * 
-         * @param {AtomSpec[]} atomlist
-         * @param {Array}
-         *            extent
-         * @return {Array}
-         */
-        var getAtomsWithin = function(atomlist, extent) {
-            var ret = [];
-
-            for (var i = 0; i < atomlist.length; i++) {
-                var atom = atomlist[i];
-                if (typeof (atom) == "undefined")
-                    continue;
-
-                if (atom.x < extent[0][0] || atom.x > extent[1][0])
-                    continue;
-                if (atom.y < extent[0][1] || atom.y > extent[1][1])
-                    continue;
-                if (atom.z < extent[0][2] || atom.z > extent[1][2])
-                    continue;
-                ret.push(i);
-            }
-            return ret;
-        };
-
-        // return volume of extent
-        var volume = function(extent) {
-            var w = extent[1][0] - extent[0][0];
-            var h = extent[1][1] - extent[0][1];
-            var d = extent[1][2] - extent[0][2];
-            return w * h * d;
-        }; // volume
-        /*
-         * Break up bounding box/atoms into smaller pieces so we can parallelize
-         * with webworkers and also limit the size of the working memory Returns
-         * a list of bounding boxes with the corresponding atoms. These extents
-         * are expanded by 4 angstroms on each side.
-         */
-        /**
-         * 
-         * @param {Array}
-         *            extent
-         * @param {AtomSpec[]} atomlist
-         * @param {AtomSpec[]} atomstoshow
-         * @return {Array}
-         */
-        var carveUpExtent = function(extent, atomlist, atomstoshow) {
-            var ret = [];
-
-            var copyExtent = function(extent) {
-                // copy just the dimensions
-                var ret = [];
-                ret[0] = [ extent[0][0], extent[0][1], extent[0][2] ];
-                ret[1] = [ extent[1][0], extent[1][1], extent[1][2] ];
-                return ret;
-            }; // copyExtent
-            var splitExtentR = function(extent) {
-                // recursively split until volume is below maxVol
-                if (volume(extent) < maxVolume) {
-                    return [ extent ];
-                } else {
-                    // find longest edge
-                    var w = extent[1][0] - extent[0][0];
-                    var h = extent[1][1] - extent[0][1];
-                    var d = extent[1][2] - extent[0][2];
-
-                    var index;
-
-                    if (w > h && w > d) {
-                        index = 0;
-                    } else if (h > w && h > d) {
-                        index = 1;
-                    } else {
-                        index = 2;
-                    }
-
-                    // create two halves, splitting at index
-                    var a = copyExtent(extent);
-                    var b = copyExtent(extent);
-                    var mid = (extent[1][index] - extent[0][index]) / 2
-                            + extent[0][index];
-                    a[1][index] = mid;
-                    b[0][index] = mid;
-
-                    var alist = splitExtentR(a);
-                    var blist = splitExtentR(b);
-                    return alist.concat(blist);
-                }
-            }; // splitExtentR
-
-            // divide up extent
-            var splits = splitExtentR(extent);
-            // now compute atoms within expanded (this could be more efficient)
-            var off = 6; // enough for water and 2*r, also depends on scale
-            // factor
-            for (var i = 0, n = splits.length; i < n; i++) {
-                var e = copyExtent(splits[i]);
-                e[0][0] -= off;
-                e[0][1] -= off;
-                e[0][2] -= off;
-                e[1][0] += off;
-                e[1][1] += off;
-                e[1][2] += off;
-
-                var atoms = getAtomsWithin(atomlist, e);
-                var toshow = getAtomsWithin(atomstoshow, splits[i]);
-
-                // ultimately, divide up by atom for best meshing
-                ret.push({
-                    extent : splits[i],
-                    atoms : atoms,
-                    toshow : toshow
-                });
-            }
-
-            return ret;
-        };
-
-        // create a mesh defined from the passed vertices and faces and material
-        // Just create a single geometry chunk - broken up whether sync or not
-        /**
-         * 
-         * @param {AtomSpec[]} atoms
-         * @param {{vertices:number,faces:number}}
-         *            VandF
-         * @param {$3Dmol.MeshLambertMaterial}
-         *            mat
-         * @return {$3Dmol.Mesh}
-         */
-        var generateSurfaceMesh = function(atoms, VandF, mat) {
-            var geo = new $3Dmol.Geometry(true);
-            // Only one group per call to generate surface mesh (addSurface
-            // should split up mesh render)
-            var geoGroup = geo.updateGeoGroup(0);
-
-            // set colors for vertices
-            var colors = [];
-            for (i = 0, il = atoms.length; i < il; i++) {
-                var atom = atoms[i];
-                if (atom) {
-                    if (typeof (atom.surfaceColor) != "undefined") {
-                        colors[i] = atom.surfaceColor;
-                    } else if (atom.color) // map from atom
-                        colors[i] = $3Dmol.CC.color(atom.color);
-                }
-            }
-            
-            var vertexArray = geoGroup.vertexArray;
-
-            // reconstruct vertices and faces
-            var v = VandF['vertices'];
-            var offset;
-            var i, il;
-            for (i = 0, il = v.length; i < il; i++) {
-                offset = geoGroup.vertices * 3;
-                vertexArray[offset] = v[i].x;
-                vertexArray[offset + 1] = v[i].y;
-                vertexArray[offset + 2] = v[i].z;
-                geoGroup.vertices++;                
-            }
-
-            //set colorArray of there are per-atom colors
-            var colorArray = geoGroup.colorArray;
-            
-            if(mat.voldata && mat.volscheme) {
-                //convert volumetric data into colors
-                var scheme = mat.volscheme;
-                var voldata = mat.voldata;
-                var range = scheme.range() || [-1,1];
-                for (i = 0, il = v.length; i < il; i++) {
-                    var val = voldata.getVal(v[i].x,v[i].y,v[i].z);
-                    var col =  $3Dmol.CC.color(scheme.valueToHex(val, range));
-                    var offset = i * 3;
-                    colorArray[offset] = col.r;
-                    colorArray[offset + 1] = col.g;
-                    colorArray[offset + 2] = col.b;
-                }
-            }
-            else if(colors.length > 0) { //have atom colors
-                for (i = 0, il = v.length; i < il; i++) {
-                    var A = v[i].atomid;
-                    var offsetA = i * 3;
-
-                    colorArray[offsetA] = colors[A].r;
-                    colorArray[offsetA + 1] = colors[A].g;
-                    colorArray[offsetA + 2] = colors[A].b;
-                }
-            }
-            
-            var faces = VandF['faces'];
-            geoGroup.faceidx = faces.length;// *3;
-            geo.initTypedArrays();
-
-            var verts = geoGroup.vertexArray;
-            var normalArray = geoGroup.normalArray;
-            var vA, vB, vC, norm;
-
-            // Setup colors, faces, and normals
-            for (i = 0, il = faces.length; i < il; i += 3) {
-
-                // var a = faces[i].a, b = faces[i].b, c = faces[i].c;
-                var a = faces[i], b = faces[i + 1], c = faces[i + 2];
-                var A = v[a]['atomid'];
-                var B = v[b]['atomid'];
-                var C = v[c]['atomid'];
-
-                var offsetA = a * 3, offsetB = b * 3, offsetC = c * 3;
-
-                // setup Normals
-                // todo - calculate normals in parallel code
-                vA = new $3Dmol.Vector3(verts[offsetA], verts[offsetA + 1],
-                        verts[offsetA + 2]);
-                vB = new $3Dmol.Vector3(verts[offsetB], verts[offsetB + 1],
-                        verts[offsetB + 2]);
-                vC = new $3Dmol.Vector3(verts[offsetC], verts[offsetC + 1],
-                        verts[offsetC + 2]);
-
-                vC.subVectors(vC, vB);
-                vA.subVectors(vA, vB);
-                vC.cross(vA);
-
-                // face normal
-                norm = vC;
-                norm.normalize();
-
-                normalArray[offsetA] += norm.x;
-                normalArray[offsetB] += norm.x;
-                normalArray[offsetC] += norm.x;
-                normalArray[offsetA + 1] += norm.y;
-                normalArray[offsetB + 1] += norm.y;
-                normalArray[offsetC + 1] += norm.y;
-                normalArray[offsetA + 2] += norm.z;
-                normalArray[offsetB + 2] += norm.z;
-                normalArray[offsetC + 2] += norm.z;
-
-            }
-            geoGroup.faceArray = new Uint16Array(faces);
-            var mesh = new $3Dmol.Mesh(geo, mat);
-            mesh.doubleSided = true;        
-            return mesh;
-        };
-
-        // do same thing as worker in main thread
-        /**
-         * 
-         * @param {$3Dmol.SurfaceType}
-         *            type
-         * @param {Array}
-         *            expandedExtent
-         * @param {Array}
-         *            extendedAtoms
-         * @param {Array}
-         *            atomsToShow
-         * @param {AtomSpec[]} atoms
-         * @param {number}
-         *            vol
-         * @return {Object}
-         */
-        var generateMeshSyncHelper = function(type, expandedExtent,
-                extendedAtoms, atomsToShow, atoms, vol) {
-            var time = new Date();
-            var ps = new $3Dmol.ProteinSurface();
-            ps.initparm(expandedExtent, (type === 1) ? false : true, vol);
-
-            var time2 = new Date();
-            //console.log("initialize " + (time2 - time) + "ms");
-
-            ps.fillvoxels(atoms, extendedAtoms);
-
-            var time3 = new Date();
-            //console.log("fillvoxels " + (time3 - time2) + "  " + (time3 - time) + "ms");
-
-            ps.buildboundary();
-
-            if (type == $3Dmol.SurfaceType.SES) {
-                ps.fastdistancemap();
-                ps.boundingatom(false);
-                ps.fillvoxelswaals(atoms, extendedAtoms);
-            }
-
-            var time4 = new Date();
-            //console.log("buildboundaryetc " + (time4 - time3) + "  " + (time4 - time) + "ms");
-
-            ps.marchingcube(type);
-
-            var time5 = new Date();
-            //console.log("marching cube " + (time5 - time4) + "  "+ (time5 - time) + "ms");
-
-            return ps.getFacesAndVertices(atomsToShow);
-        };
-
-        /**
-         * 
-         * @param {matSpec}
-         *            style
-         * @return {$3Dmol.MeshLambertMaterial}
-         */
-        function getMatWithStyle(style) {
-            var mat = new $3Dmol.MeshLambertMaterial();
-            mat.vertexColors = $3Dmol.VertexColors;
-
-            for ( var prop in style) {
-                if (prop === "color" || prop === "map") {
-                    // ignore
-                } else if (style.hasOwnProperty(prop))
-                    mat[prop] = style[prop];
-            }
-            if (style.opacity !== undefined) {
-                if (style.opacity === 1)
-                    mat.transparent = false;
-                else
-                    mat.transparent = true;
-            }
-
-            return mat;
-        }
-
-        
-        /**
-         * Adds an explicit mesh as a surface object.
-         * 
-         * @param {$3Dmol.Mesh}
-         *            mesh
-         * @param {Object}
-         *            style
-         * @returns {Number} surfid
-         */
-        this.addMesh = function(mesh) {
-            var surfobj = {
-                geo : mesh.geometry,
-                mat : mesh.material,
-                done : true,
-                finished : false //the rendered finishes surfaces when they are done
-            };
-            var surfid = nextSurfID();
-            surfaces[surfid] = surfobj;
-            return surfid;
-        }
-
-        //return a shallow copy of list l, e.g., for atoms so we can
-        //ignore superficial changes (ie surfacecolor, position) that happen
-        //while we're surface building
-        var shallowCopy = function(l) {
-            var ret = [];
-            $.each(l, function(k,v) {
-                ret[k] = $.extend({},v);
-            });
-            return ret;
-        }
-        /**
-         * Add surface representation to atoms
-         *  @function $3Dmol.GLViewer#addSurface
-         * @param {$3Dmol.SurfaceType} type - Surface type
-         * @param {SurfaceStyleSpec} style - optional style specification for surface material (e.g. for different coloring scheme, etc)
-         * @param {AtomSelectionSpec} atomsel - Show surface for atoms in this selection
-         * @param {AtomSelectionSpec} allsel - Use atoms in this selection to calculate surface; may be larger group than 'atomsel' 
-         * @param {AtomSelectionSpec} focus - Optionally begin rendering surface specified atoms
-         * 
-         * @return {number} surfid - Identifying number for this surface
-         */
-        this.addSurface = function(type, style, atomsel, allsel, focus) {
-            // type 1: VDW 3: SAS 4: MS 2: SES
-            // if sync is true, does all work in main thread, otherwise uses
-            // workers
-            // with workers, must ensure group is the actual modelgroup since
-            // surface
-            // will get added asynchronously
-            // all atoms in atomlist are used to compute surfaces, but only the
-            // surfaces
-            // of atomsToShow are displayed (e.g., for showing cavities)
-            // if focusSele is specified, will start rending surface around the
-            // atoms specified by this selection
-            var atomlist = null, focusSele = null;
-            //TODO: currently generating a shallow copy to avoid problems when atoms are chagned
-            //during surface generation - come up with a better solution
-            var atomsToShow = shallowCopy(getAtomsFromSel(atomsel));
-            if(!allsel) {
-                atomlist = atomsToShow;
-            }
-            else {
-                atomlist = shallowCopy(getAtomsFromSel(allsel));
-            }
-            
-            var symmetries = false;
-            var n;
-            for (n = 0; n < models.length; n++) { 
-                if(models[n]) {
-                    var symMatrices = models[n].getSymmetries();
-                    if (symMatrices.length > 1 || (symMatrices.length == 1 && !(symMatrices[0].isIdentity()))) {
-                        symmetries = true;
-                        break;
-                    }
-                }
-            }
-
-            var addSurfaceHelper = function addSurfaceHelper(surfobj, atomlist, atomsToShow) {
-            
-                if(!focus) {
-                    focusSele = atomsToShow;
-                } else {
-                    focusSele = shallowCopy(getAtomsFromSel(focus));
-                }
-
-                var atom;
-                var time = new Date();
-                var extent = $3Dmol.getExtent(atomsToShow, true);
-
-                var i, il;
-                if (style['map'] && style['map']['prop']) {
-                    // map color space using already set atom properties
-                    /** @type {AtomSpec} */
-                    var prop = style['map']['prop'];
-                    /** @type {Gradient} */
-                    var scheme = style['map']['scheme'] || style['map']['gradient'] || new $3Dmol.Gradient.RWB();
-                    var range = scheme.range();
-                    if (!range) {
-                        range = $3Dmol.getPropertyRange(atomsToShow, prop);
-                    }
-                    style.colorscheme = {prop: prop, gradient: scheme};
-
-                }
-                
-                //cache surface color on each atom
-                for (i = 0, il = atomlist.length; i < il; i++) {
-                    atom = atomlist[i];
-                    atom.surfaceColor = $3Dmol.getColorFromStyle(atom, style);
-                }                
-
-                var totalVol = volume(extent); // used to scale resolution
-                var extents = carveUpExtent(extent, atomlist, atomsToShow);
-
-                if (focusSele && focusSele.length && focusSele.length > 0) {
-                    var seleExtent = $3Dmol.getExtent(focusSele, true);
-                    // sort by how close to center of seleExtent
-                    var sortFunc = function(a, b) {
-                        var distSq = function(ex, sele) {
-                            // distance from e (which has no center of mass) and
-                            // sele which does
-                            var e = ex.extent;
-                            var x = e[1][0] - e[0][0];
-                            var y = e[1][1] - e[0][1];
-                            var z = e[1][2] - e[0][2];
-                            var dx = (x - sele[2][0]);
-                            dx *= dx;
-                            var dy = (y - sele[2][1]);
-                            dy *= dy;
-                            var dz = (z - sele[2][2]);
-                            dz *= dz;
-
-                            return dx + dy + dz;
-                        };
-                        var d1 = distSq(a, seleExtent);
-                        var d2 = distSq(b, seleExtent);
-                        return d1 - d2;
-                    };
-                    extents.sort(sortFunc);
-                }
-
-                //console.log("Extents " + extents.length + "  "+ (+new Date() - time) + "ms");
-
-
-                var reducedAtoms = [];
-                // to reduce amount data transfered, just pass x,y,z,serial and elem
-                for (i = 0, il = atomlist.length; i < il; i++) {
-                    atom = atomlist[i];
-                    reducedAtoms[i] = {
-                        x : atom.x,
-                        y : atom.y,
-                        z : atom.z,
-                        serial : i,
-                        elem : atom.elem
-                    };
-                }
-
-                var sync = !!($3Dmol.syncSurface);
-                if (sync) { // don't use worker, still break up for memory purposes
-
-                    // to keep the browser from locking up, call through setTimeout
-                    var callSyncHelper = function callSyncHelper(i) {
-                        if (i >= extents.length)
-                            return;
-
-                        var VandF = generateMeshSyncHelper(type, extents[i].extent,
-                                extents[i].atoms, extents[i].toshow, reducedAtoms,
-                                totalVol);
-                        var mesh = generateSurfaceMesh(atomlist, VandF, mat);
-                        $3Dmol.mergeGeos(surfobj.geo, mesh);
-                        _viewer.render();
-
-                        setTimeout(callSyncHelper, 1, i + 1);
-                    }
-
-                    setTimeout(callSyncHelper, 1, 0);
-
-                    // TODO: Asynchronously generate geometryGroups (not separate
-                    // meshes) and merge them into a single geometry
-                } else { // use worker
-
-                    var workers = [];
-                    if (type < 0)
-                        type = 0; // negative reserved for atom data
-                    for (i = 0, il = numWorkers; i < il; i++) {
-                        // var w = new Worker('3Dmol/SurfaceWorker.js');
-                        var w = new Worker($3Dmol.SurfaceWorker);
-                        workers.push(w);
-                        w.postMessage({
-                            'type' : -1,
-                            'atoms' : reducedAtoms,
-                            'volume' : totalVol
-                        });
-                    }
-                    var cnt = 0;
-
-                    var rfunction = function(event) {
-                        var VandFs = $3Dmol.splitMesh({vertexArr:event.data.vertices,
-							                           faceArr:event.data.faces});
-					    for(var i=0,vl=VandFs.length;i<vl;i++){
-                            var VandF={vertices:VandFs[i].vertexArr,
-								       faces:VandFs[i].faceArr};
-                            var mesh = generateSurfaceMesh(atomlist, VandF, mat);
-                            $3Dmol.mergeGeos(surfobj.geo, mesh);
-                            _viewer.render();
-						}
-
-                    //    console.log("async mesh generation " + (+new Date() - time) + "ms");
-                        cnt++;
-                        if (cnt == extents.length)
-                            surfobj.done = true;
-                    };
-
-                    var efunction = function(event) {
-                        console.log(event.message + " (" + event.filename + ":" + event.lineno + ")");
-                    };
-
-                    for (i = 0; i < extents.length; i++) {
-                        var worker = workers[i % workers.length];
-                        worker.onmessage = rfunction;
-
-                        worker.onerror = efunction;
-
-                        worker.postMessage({
-                            'type' : type,
-                            'expandedExtent' : extents[i].extent,
-                            'extendedAtoms' : extents[i].atoms,
-                            'atomsToShow' : extents[i].toshow
-                        });
-                    }
-                }
-
-                // NOTE: This is misleading if 'async' mesh generation - returns
-                // immediately
-                //console.log("full mesh generation " + (+new Date() - time) + "ms");
-            }
-            
-            style = style || {};
-            var mat = getMatWithStyle(style);
-            var surfobj = [];
-            
-            if (symmetries) { //do preprocessing
-                var modelsAtomList = {};
-                var modelsAtomsToShow = {};
-                for (n = 0; n < models.length; n++) {
-                    modelsAtomList[n] = [];
-                    modelsAtomsToShow[n] = [];
-                }
-                for (n = 0; n < atomlist.length; n++) {
-                    modelsAtomList[atomlist[n].model].push(atomlist[n]);
-                }
-                for (n = 0; n < atomsToShow.length; n++) {
-                    modelsAtomsToShow[atomsToShow[n].model].push(atomsToShow[n]);
-                }
-                for (n = 0; n < models.length; n++) {
-                    surfobj.push({
-                        geo : new $3Dmol.Geometry(true),
-                        mat : mat,
-                        done : false,
-                        finished : false,
-                        symmetries : models[n].getSymmetries()
-                    // also webgl initialized
-                    });
-                    addSurfaceHelper(surfobj[n], modelsAtomList[n], modelsAtomsToShow[n]);
-                }
-            }
-            else {
-                surfobj.push({
-                    geo : new $3Dmol.Geometry(true),
-                    mat : mat,
-                    done : false,
-                    finished : false,
-                    symmetries : [new $3Dmol.Matrix4()]
-                });
-                addSurfaceHelper(surfobj[surfobj.length-1], atomlist, atomsToShow);
-            } 
-            var surfid = nextSurfID();
-            surfaces[surfid] = surfobj;
-            
-            return surfid;
-
-        };
-
-        /**
-         * Set the surface material to something else, must render change
-         * 
-         * @param {number} surf - Surface ID to apply changes to
-         * @param {matSpec} style - new material style specification
-         */ 
-        this.setSurfaceMaterialStyle = function(surf, style) {
-            if (surfaces[surf]) {
-                surfArr = surfaces[surf];
-                for (var i = 0; i < surfArr.length; i++) {
-                    surfArr[i].mat = getMatWithStyle(style);
-                    surfArr[i].mat.side = $3Dmol.FrontSide;
-                    surfArr[i].finished = false; // trigger redraw
-                }
-            }
-        };
-
-        /**
-         * Remove surface with given ID
-         * 
-         * @param {number} surf - surface id
-         */
-        this.removeSurface = function(surf) {
-            var surfArr = surfaces[surf];
-            for (var i = 0; i < surfArr.length; i++) {
-                if (surfArr[i] && surfArr[i].lastGL) {
-                    if (surfArr[i].geo !== undefined)
-                        surfArr[i].geo.dispose();
-                    if (surfArr[i].mat !== undefined)
-                        surfArr[i].mat.dispose();
-                    modelGroup.remove(surfArr[i].lastGL); // remove from scene
-                }
-            }
-            delete surfaces[surf];
-            show();
-        };
-        
-        /** Remove all surfaces.
-         * @function $3Dmol.GLViewer#removeAllSurfaces */
-        this.removeAllSurfaces = function() {
-            for (n in  surfaces) {
-                if(!surfaces.hasOwnProperty(n)) continue;
-                var surfArr = surfaces[n];
-                for(var i = 0; i < surfArr.length; i++) {
-                    if (surfArr[i] && surfArr[i].lastGL) {
-                        if (surfArr[i].geo !== undefined)
-                            surfArr[i].geo.dispose();
-                        if (surfArr[i].mat !== undefined)
-                            surfArr[i].mat.dispose();
-                        modelGroup.remove(surfArr[i].lastGL); // remove from scene
-                    }
-                }
-                delete surfaces[n];
-            }
-            show();
-            
-        };
-
-        /** return Jmol moveto command to position this scene */
-        this.jmolMoveTo = function() {
-            var pos = modelGroup.position;
-            // center on same position
-            var ret = "center { " + (-pos.x) + " " + (-pos.y) + " " + (-pos.z)
-                    + " }; ";
-            // apply rotation
-            var q = rotationGroup.quaternion;
-            ret += "moveto .5 quaternion { " + q.x + " " + q.y + " " + q.z
-                    + " " + q.w + " };";
-            // zoom is tricky.. maybe i would be best to let callee zoom on
-            // selection?
-            // can either do a bunch of math, or maybe zoom to the center with a
-            // fixed
-            // but reasonable percentage
-
-            return ret;
-        };
-
-        /** Clear scene of all objects 
-         * @function $3Dmol.GLViewer#clear
-         * */
-        this.clear = function() {
-            this.removeAllSurfaces();
-            this.removeAllModels();
-            this.removeAllLabels();
-            this.removeAllShapes();
-            show();
-        };
-
-        // props is a list of objects that select certain atoms and enumerate
-        // properties for those atoms
-        /**
-         * @function $3Dmol.GLViewer#mapAtomProperties
-         * Add specified properties to all atoms matching input argument
-         * @function $3Dmol.GLViewer#mapAtomProperties
-         * @param {Object} props, either array of atom selectors with associated props, or function that takes atom and sets its properties
-         * @param {AtomSelectionSpec} sel
-         */
-        this.mapAtomProperties = function(props, sel) {
-            sel = sel || {};
-            var atoms = getAtomsFromSel(sel);
-            
-            if(typeof(props) == "function") {
-                for (var a = 0, numa = atoms.length; a < numa; a++) {
-                    var atom = atoms[a];
-                    props(atom);
-                }
-            }
-            else {
-                for (var a = 0, numa = atoms.length; a < numa; a++) {
-                    var atom = atoms[a];
-                    for (var i = 0, n = props.length; i < n; i++) {
-                        var prop = props[i];
-                        if (prop.props) {
-                            for ( var p in prop.props) {
-                                if (prop.props.hasOwnProperty(p)) {
-                                    // check the atom
-                                    if (atomIsSelected(atom, prop)) {
-                                        if (!atom.properties)
-                                            atom.properties = {};
-                                        atom.properties[p] = prop.props[p];
-                                    }
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-        };
-
-        /**
-         * @function $3Dmol.GLViewer#linkViewer
-         * Synchronize this view matrix of this viewer to the passed viewer.
-         * When the viewpoint of this viewer changes, the other viewer will
-         * be set to this viewer's view.
-         * @function $3Dmol.GLViewer#linkViewer
-         */
-        this.linkViewer = function(otherviewer) {
-           linkedViewers.push(otherviewer);
-        };
-        
-
-        try {
-            if (typeof (callback) === "function")
-                callback(this);
-        } catch (e) {
-            // errors in callback shouldn't invalidate the viewer
-            console.log("error with glviewer callback: " + e);
-        }
-    }
-
-    return GLViewer;
-
-})();
-
-$3Dmol['glmolViewer'] = $3Dmol.GLViewer;
-//color scheme mappings
-var $3Dmol = $3Dmol || {};
-
-/** Color mapping gradiens
- * @interface
- * @param {number} min
- * @param {number} max
- */
-$3Dmol.Gradient = function(min, max) {};
-
-/**
- * Map value to hex color
- * @param {number} val
- * @param {number} range
- * @returns {number}
- */
-$3Dmol.Gradient.valueToHex = function(val, range) {};
-//return range used for color mapping, null if none set
-$3Dmol.Gradient.range = function() {};
-
-
-
-/**
- * Color scheme red to white to blue, for charges
- * @constructor
- * @implements {$3Dmol.Gradient}
- */
-$3Dmol.Gradient.RWB = function(min, max,mid) {
-    var mult = 1.0;
-    if(typeof(max) == 'undefined' && $.isArray(min) && min.length >= 2) {
-        //we were passed a single range
-        max = min[1];
-        min = min[0];
-    }
-    if(max < min) { //reverse the order
-        mult = -1.0;
-        min *= -1.0;
-        max *= -1.0;
-    }
-        
-    //map value to hex color, range is provided
-    this.valueToHex = function(val, range) {
-        var lo, hi;
-        val = mult*val; //reverse if necessary
-        if(range) {
-            lo = range[0];
-            hi = range[1];
-        }
-        else {
-            lo = min;
-            hi = max;
-        }
-    
-        if(val === undefined)
-            return 0xffffff;
-        
-        if(val < lo) val = lo;
-        if(val > hi) val = hi;
-        
-        var middle = (hi+lo)/2;
-        if(typeof(mid) != 'undefined')
-            middle = mid; //allow user to specify midpoint
-        var scale, color;
-        
-        //scale bottom from red to white
-        if(val <= middle) {
-            scale = Math.floor(255*Math.sqrt((val-lo)/(middle-lo)));
-            color = 0xff0000 + 0x100*scale + scale;
-            return color;
-        }
-        else { //form white to blue
-            scale = Math.floor(255*Math.sqrt((1-(val-middle)/(hi-middle))));
-            color =  0x10000*scale+0x100*scale+0xff;
-            return color;
-        }
-    };
-    
-
-    //return range used for color mapping, null if none set
-    this.range = function() {
-        if(typeof(min) != "undefined" && typeof(max) != "undefined") {
-            return [min,max];
-        }
-        return null;
-    };
-
-};
-
-/**
- * rainbow gradient, but without purple to match jmol
- * @constructor
- * @implements {$3Dmol.Gradient}
- */
-$3Dmol.Gradient.ROYGB = function(min, max) {
-    var mult = 1.0;
-    if(typeof(max) == 'undefined' && $.isArray(min) && min.length >= 2) {
-        //we were passed a single range
-        max = min[1];
-        min = min[0];
-    }
-    if(max < min) { //reverse the order
-        mult = -1.0;
-        min *= -1.0;
-        max *= -1.0;
-    }
-    
-    //map value to hex color, range is provided
-    this.valueToHex = function(val, range) {
-        var lo, hi;
-        val = mult*val;
-        if(range) {
-            lo = range[0];
-            hi = range[1];
-        }
-        else {
-            lo = min;
-            hi = max;
-        }
-    
-        if(typeof(val) == "undefined")
-            return 0xffffff;
-        
-        if(val < lo) val = lo;
-        if(val > hi) val = hi;
-        
-        var mid = (lo+hi)/2;
-        var q1 = (lo+mid)/2;
-        var q3 = (mid+hi)/2;
-        
-        var scale, color;
-        
-        if(val < q1) { //scale green up, red up, blue down
-            scale = Math.floor(255*Math.sqrt((val-lo)/(q1-lo)));
-            color = 0xff0000 + 0x100*scale + 0;
-            return color;
-        }
-        else if(val < mid) { //scale red down, green up, blue down
-            scale = Math.floor(255*Math.sqrt((1-(val-q1)/(mid-q1))));
-            color =  0x010000*scale+0xff00+0x0;
-            return color;
-        }
-        else if(val < q3) { //scale blue up, red down, green up
-            scale = Math.floor(255*Math.sqrt((val-mid)/(q3-mid)));
-            color = 0x000000 + 0xff00 + 0x1*scale;
-            return color;
-        }
-        else { //scale green down, blue up, red down
-            scale = Math.floor(255*Math.sqrt((1-(val-q3)/(hi-q3))));
-            color =  0x000000+0x0100*scale+0xff;
-            return color;
-        }        
-    };
-   
-
-    //return range used for color mapping, null if none set
-    this.range = function() {
-        if(typeof(min) != "undefined" && typeof(max) != "undefined") {
-            return [min,max];
-        }
-        return null;
-    };
-
-};
-
-/**
- * rainbow gradient with constant saturation, all the way to purple!
- * @constructor
- * @implements {$3Dmol.Gradient}
- */
-$3Dmol.Gradient.Sinebow = function(min, max) {
-    var mult = 1.0;
-    if(typeof(max) == 'undefined' && $.isArray(min) && min.length >= 2) {
-        //we were passed a single range
-        max = min[1];
-        min = min[0];
-    }
-    if(max < min) { //reverse the order
-        mult = -1.0;
-        min *= -1.0;
-        max *= -1.0;
-    }
-    //map value to hex color, range is provided
-    this.valueToHex = function(val, range) {
-        var lo, hi;
-        val = mult*val;
-        if(range) {
-            lo = range[0];
-            hi = range[1];
-        }
-        else {
-            lo = min;
-            hi = max;
-        }
-    
-        if(typeof(val) == "undefined")
-            return 0xffffff;
-        
-        if(val < lo) val = lo;
-        if(val > hi) val = hi;
-        
-        var scale = (val-lo)/(hi-lo);
-        var h = (5*scale/6.0+0.5);
-        var r = Math.sin(Math.PI*h);
-        r *= r*255;
-        var g = Math.sin(Math.PI*(h+1/3.0));
-        g *= g*255;
-        var b = Math.sin(Math.PI*(h+2/3.0));
-        b *= b*255;
-        
-        return 0x10000*Math.floor(r)+0x100*Math.floor(b)+0x1*Math.floor(g);
-    };
-    
-
-    //return range used for color mapping, null if none set
-    this.range = function() {
-        if(typeof(min) != "undefined" && typeof(max) != "undefined") {
-            return [min,max];
-        }
-        return null;
-    };
-
-};
-//Adapted from the text sprite example from http://stemkoski.github.io/Three.js/index.html
-
-$3Dmol.LabelCount = 0;
-
-/**
- * Renderable labels
- * @constructor $3Dmol.Label
- * @param {string} tag - Label text
- * @param {LabelSpec} parameters Label style and font specifications
- */
-$3Dmol.Label = function(text, parameters) {
-
-    this.id = $3Dmol.LabelCount++;
-    this.stylespec = parameters || {};
-
-    this.canvas = document.createElement('canvas');
-    //todo: implement resizing canvas..
-    this.canvas.width = 134;
-    this.canvas.height = 35;
-    this.context = this.canvas.getContext('2d');
-    this.sprite = new $3Dmol.Sprite();
-    this.text = text;
-
-};
-
-$3Dmol.Label.prototype = {
-
-    constructor : $3Dmol.Label,
-
-    getStyle : function () { return this.stylespec; }, 
-    
-    setContext : function() {
-        // function for drawing rounded rectangles - for Label drawing
-        var roundRect = function(ctx, x, y, w, h, r, drawBorder) {
-
-            ctx.beginPath();
-            ctx.moveTo(x + r, y);
-            ctx.lineTo(x + w - r, y);
-            ctx.quadraticCurveTo(x + w, y, x + w, y + r);
-            ctx.lineTo(x + w, y + h - r);
-            ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
-            ctx.lineTo(x + r, y + h);
-            ctx.quadraticCurveTo(x, y + h, x, y + h - r);
-            ctx.lineTo(x, y + r);
-            ctx.quadraticCurveTo(x, y, x + r, y);
-            ctx.closePath();
-            ctx.fill();
-            if(drawBorder)
-                ctx.stroke();
-
-        };
-        
-        //do all the checks to figure out what color is desired
-        var getColor = function(style, stylealpha, init) {
-            var ret = init;
-            if(typeof(style) != 'undefined') {
-                //convet regular colors
-                 if(style instanceof $3Dmol.Color) 
-                     ret = style.scaled();
-                 else //hex or name
-                    ret = $3Dmol.CC.color(style).scaled();                    
-            }
-            if(typeof(stylealpha) != 'undefined') {
-                ret.a = parseFloat(stylealpha);
-            }
-            return ret;
-        }
-
-        /**
-         * Label type specification
-         * @typedef LabelSpec
-         * @struct
-         * @prop {string} font - font name, default sans-serif
-         * @prop {number} fontSize - height of text, default 18
-         * @prop {ColorSpec} fontColor - font color, default white
-         * @prop {number} fontOpacity - font opacity, default 1
-         * @prop {number} borderThickness - line width of border around label, default 0
-         * @prop {ColorSpec} borderColor - color of border, default backgroundColor
-         * @prop {string} borderOpacity - color of border
-         * @prop {ColorSpec} backgroundColor - color of background, default black
-         * @prop {string} backgroundOpacity - opacity of background, default 1
-         * @prop {Object} position - x,y,z coordinates for label
-         * @prop {boolean} inFront - always put labels in from of model
-         * @prop {boolean} showBackground - show background rounded rectangle, default true
-         */
-        return function() {
-            
-            var style = this.stylespec;
-            var useScreen =  typeof(style.useScreen) == "undefined" ? false : style.useScreen;
-            
-            var showBackground = style.showBackground;
-            if(showBackground === '0' || showBackground === 'false') showBackground = false;
-            if(typeof(showBackground) == "undefined") showBackground = true; //default
-            var font = style.font ? style.font : "sans-serif";
-
-            var fontSize = parseInt(style.fontSize) ? parseInt(style.fontSize) : 18;
-
-            var fontColor = getColor(style.fontColor, style.fontOpacity,
-                     {
-                        r : 255,
-                        g : 255,
-                        b : 255,
-                        a : 1.0
-                    });
-
-            var padding = style.padding ? style.padding : 4;
-            var borderThickness = style.borderThickness ? style.borderThickness
-                    : 0;
-    
-            var backgroundColor = getColor(style.backgroundColor, style.backgroundOpacity, 
-                     {
-                        r : 0,
-                        g : 0,
-                        b : 0,
-                        a : 1.0
-                    });
-                    
-            var borderColor = getColor(style.borderColor, style.borderOpacity, backgroundColor);
-
-                    
-            var position = style.position ? style.position
-                    : {
-                        x : -10,
-                        y : 1,
-                        z : 1
-                    };
-                    
-            // Should labels always be in front of model?
-            var inFront = (style.inFront !== undefined) ? style.inFront    : true;
-            if(inFront === 'false' || inFront === '0') inFront = false;
-
-            // clear canvas
-
-            var spriteAlignment = style.alignment || $3Dmol.SpriteAlignment.topLeft;
-
-            var bold = "";
-            if(style.bold)
-                bold = "bold ";
-            this.context.font = bold+fontSize + "px  " + font;
-
-            var metrics = this.context.measureText(this.text);
-            var textWidth = metrics.width;
-            
-            if(!showBackground) borderThickness = 0;
-        
-            var width = textWidth+2.5*borderThickness +2*padding;
-            var height = fontSize*1.25+2*borderThickness+2*padding;            // 1.25 is extra height factor for text below baseline: g,j,p,q.
-
-            
-            if(style.backgroundImage) {
-                var img = style.backgroundImage;
-                var w = style.backgroundWidth ? style.backgroundWidth : img.width;
-                var h = style.backgroundHeight ? style.backgroundHeight : img.height;
-                if(w > width) width = w;
-                if(h > height) height = h;
-            }
-
-            this.canvas.width = width;
-            this.canvas.height = height;
-            this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
-
-            var bold = "";
-            if(style.bold)
-                bold = "bold ";
-            this.context.font = bold+fontSize + "px  " + font;
-
-            // background color
-            this.context.fillStyle = "rgba(" + backgroundColor.r + ","
-                    + backgroundColor.g + "," + backgroundColor.b
-                    + "," + backgroundColor.a + ")";
-            // border color
-            this.context.strokeStyle = "rgba(" + borderColor.r + ","
-                    + borderColor.g + "," + borderColor.b + ","
-                    + borderColor.a + ")";
-
-            this.context.lineWidth = borderThickness;
-            if(showBackground) {
-                roundRect(this.context, borderThickness,borderThickness , width-2*borderThickness,height-2*borderThickness, 6, borderThickness > 0);
-            }
-            
-            if(style.backgroundImage) {
-                var img = style.backgroundImage;
-                var w = style.backgroundWidth ? style.backgroundWidth : img.width;
-                var h = style.backgroundHeight ? style.backgroundHeight : img.height;
-                this.context.drawImage(img,0,0, w, h);
-            }
-            
-
-            // text color
-            this.context.fillStyle = "rgba(" + fontColor.r + ","
-                    + fontColor.g + "," + fontColor.b + ","
-                    + fontColor.a + ")";
-            
-            this.context.fillText(this.text, borderThickness+padding,
-                    fontSize + borderThickness+padding, textWidth);
-
-            // canvas contents will be used for a texture
-            var texture = new $3Dmol.Texture(this.canvas);
-            texture.needsUpdate = true;
-            this.sprite.material = new $3Dmol.SpriteMaterial({
-                map : texture,
-                useScreenCoordinates : useScreen,
-                alignment : spriteAlignment,
-                depthTest : !inFront
-            });
-
-            this.sprite.scale.set(1,1,1);
-
-            this.sprite.position.set(position.x, position.y, position.z);
-        };
-
-    }(),
-
-    // clean up material and texture
-    dispose : function() {
-
-        if (this.sprite.material.map !== undefined)
-            this.sprite.material.map.dispose();
-        if (this.sprite.material !== undefined)
-            this.sprite.material.dispose();
-    }
-
-};
-
-$3Dmol = $3Dmol || {};
-//Encapsulate marching cube algorithm for isosurface generation
-// (currently used by protein surface rendering and generic volumetric data reading)
-$3Dmol.MarchingCubeInitializer = function() {
-    
-    //Marching cube algorithm - assume data has been pre-treated so isovalue is 0 
-    // (i.e. select points greater than 0)
-    //origin -  vector of origin of volumetric data (default is (0,0,0))
-    // nX, nY, nZ - specifies number of voxels in each dimension
-    // scale - cube diagonal unit vector scale (3Dmol vector) (specifying distance between data points); diagonal of cube
-    // - default is 1 - assumes unit cube (1,1,1) diag)
-    // fulltable - if true, use full marching cubes and tritables - else use trimmed table (e.g. surf render)
-    // voxel - if true, draws with a blocky voxel style (default false)
-    // verts, faces - vertex and face arrays to fill up
-    
-    //to match with protein surface...
-    var ISDONE = 2;
-    var my = {};
-    
-    my.march = function(data, verts, faces, spec) {
-
-        var fulltable = !!(spec.fulltable);
-        var origin = (spec.hasOwnProperty('origin') && spec.origin.hasOwnProperty('x')) ? spec.origin : {x:0, y:0, z:0};
-        var voxel = !!(spec.voxel);
-        
-        var nX = spec.nX || 0;
-        var nY = spec.nY || 0;
-        var nZ = spec.nZ || 0;
-        
-        var scale = spec.scale || 1.0;
-        var unitCube = null;
-        if(spec.unitCube) {
-            unitCube = spec.unitCube;
-        } else {
-            unitCube = {x:scale,y:scale,z:scale};
-        }
-        
-        //keep track of calculated vertices to avoid repeats
-        var vertnums = new Int32Array(nX*nY*nZ);
-        
-        var i, il;
-        
-        for (i = 0, il = vertnums.length; i < il; ++i)
-            vertnums[i] = -1;
-
-        // create (or retrieve) a vertex at the appropriate point for
-        // the edge (p1,p2)
-        
-        var getVertex = function(i, j, k, code, p1, p2) {
-            var pt = {x: origin.x, y: origin.y, z: origin.z};
-            var val1 = !!(code & (1 << p1));
-            var val2 = !!(code & (1 << p2));
-             
-            // p1 if they are the same or if !val1
-            var p = p1;
-            if (!val1 && val2)
-                p = p2;
-            
-            // adjust i,j,k by p
-            if (p & 1)
-                k++;
-            if (p & 2)
-                j++;
-            if (p & 4)
-                i++;
-    
-            pt.x += unitCube.x*i;
-            pt.y += unitCube.y*j;
-            pt.z += unitCube.z*k;
-    
-            var index = ((nY * i) + j) * nZ + k;
-            
-            //Have to add option to do voxels
-            if (!voxel) {
-            
-                if (vertnums[index] < 0) // not created yet
-                {
-                    vertnums[index] = verts.length;
-                    verts.push( pt );
-                }
-                return vertnums[index];
-            
-            }
-            
-            else {
-                verts.push(pt);
-                return verts.length - 1;
-            }
-            
-        };
-            
-        var intersects = new Int32Array(12);
-        
-        var etable = (fulltable) ? edgeTable2 : edgeTable;
-        var tritable = (fulltable) ? triTable2 : triTable;
-                
-        //Run marching cubes algorithm
-        for (i = 0; i < nX-1; ++i) {
-            
-            for (var j = 0; j < nY-1; ++j){
-                
-                for (var k = 0; k < nZ-1; ++k){
-                    
-                    var code = 0;
-                    
-                    for (var p = 0; p < 8; ++p) {
-                        var index = ((nY * (i + ((p & 4) >> 2))) + j + ((p & 2) >> 1)) *
-                                        nZ + k + (p & 1);
-
-                        //TODO: Need to fix vpBits in protein surface for this to work
-                        var val = !!(data[index] & ISDONE);
-                        //var val = !!(data[index] > 0);   
-                        
-                        code |= val << p;                        
-                    }
-                    
-                    if (code === 0 || code === 255)
-                        continue;
-                    
-                    var ecode = etable[code];
-                    
-                    if (ecode === 0)
-                        continue;
-                        
-                    var ttable = tritable[code];                        
-                    
-                    if (ecode & 1)
-                        intersects[0] = getVertex(i, j, k, code, 0, 1);
-                    if (ecode & 2)
-                        intersects[1] = getVertex(i, j, k, code, 1, 3);
-                    if (ecode & 4)
-                        intersects[2] = getVertex(i, j, k, code, 3, 2);
-                    if (ecode & 8)
-                        intersects[3] = getVertex(i, j, k, code, 2, 0);
-                    if (ecode & 16)
-                        intersects[4] = getVertex(i, j, k, code, 4, 5);
-                    if (ecode & 32)
-                        intersects[5] = getVertex(i, j, k, code, 5, 7);
-                    if (ecode & 64)
-                        intersects[6] = getVertex(i, j, k, code, 7, 6);
-                    if (ecode & 128)
-                        intersects[7] = getVertex(i, j, k, code, 6, 4);
-                    if (ecode & 256)
-                        intersects[8] = getVertex(i, j, k, code, 0, 4);
-                    if (ecode & 512)
-                        intersects[9] = getVertex(i, j, k, code, 1, 5);
-                    if (ecode & 1024)
-                        intersects[10] = getVertex(i, j, k, code, 3, 7);
-                    if (ecode & 2048)
-                        intersects[11] = getVertex(i, j, k, code, 2, 6);       
-
-                    for (var t = 0; t < ttable.length; t += 3) {
-                        
-                        var a = intersects[ttable[t]],
-                            b = intersects[ttable[t+1]],
-                            c = intersects[ttable[t+2]];         
-                                           
-                        if (voxel && t >= 3) {
-                            verts.push(verts[a]); a = verts.length - 1;
-                            verts.push(verts[b]); b = verts.length - 1;
-                            verts.push(verts[c]); c = verts.length - 1;
-                        }
-
-                        
-                        faces.push(a); faces.push(b); faces.push(c);                               
-                    }              
-                    
-                }
-                
-            }
-            
-        }
-             
-        
-    };
-
-    my.laplacianSmooth = function(numiter, verts, faces) {
-            var tps = new Array(verts.length);
-            var i, il, j, jl, k, kl;
-            for (i = 0, il = verts.length; i < il; i++)
-                    tps[i] = {
-                        x : 0,
-                        y : 0,
-                        z : 0
-                    };
-            var vertdeg = new Array(20);
-            var flagvert;
-            for (i = 0; i < 20; i++)
-                    vertdeg[i] = new Array(verts.length);
-            for (i = 0, il = verts.length; i < il; i++)
-                    vertdeg[0][i] = 0;
-            for (i = 0, il = faces.length / 3; i < il; i++) {
-                var aoffset = i*3, boffset = i*3 + 1, coffset = i*3 + 2;
-                flagvert = true;
-                for (j = 0, jl = vertdeg[0][faces[aoffset]]; j < jl; j++) {
-                    if (faces[boffset] == vertdeg[j + 1][faces[aoffset]]) {
-                        flagvert = false;
-                        break;
-                    }
-                }
-                if (flagvert) {
-                    vertdeg[0][faces[aoffset]]++;
-                    vertdeg[vertdeg[0][faces[aoffset]]][faces[aoffset]] = faces[boffset];
-                }
-                flagvert = true;
-                for (j = 0, jl = vertdeg[0][faces[aoffset]]; j < jl; j++) {
-                    if (faces[coffset] == vertdeg[j + 1][faces[aoffset]]) {
-                        flagvert = false;
-                        break;
-                    }
-                }
-                if (flagvert) {
-                    vertdeg[0][faces[aoffset]]++;
-                    vertdeg[vertdeg[0][faces[aoffset]]][faces[aoffset]] = faces[coffset];
-                }
-                // b
-                flagvert = true;
-                for (j = 0, jl = vertdeg[0][faces[boffset]]; j < jl; j++) {
-                    if (faces[aoffset] == vertdeg[j + 1][faces[boffset]]) {
-                        flagvert = false;
-                        break;
-                    }
-                }
-                if (flagvert) {
-                    vertdeg[0][faces[boffset]]++;
-                    vertdeg[vertdeg[0][faces[boffset]]][faces[boffset]] = faces[aoffset];
-                }
-                flagvert = true;
-                for (j = 0, jl = vertdeg[0][faces[boffset]]; j < jl; j++) {
-                    if (faces[coffset] == vertdeg[j + 1][faces[boffset]]) {
-                        flagvert = false;
-                        break;
-                    }
-                }
-                if (flagvert) {
-                    vertdeg[0][faces[boffset]]++;
-                    vertdeg[vertdeg[0][faces[boffset]]][faces[boffset]] = faces[coffset];
-                }
-                // c
-                flagvert = true;
-                for (j = 0; j < vertdeg[0][faces[coffset]]; j++) {
-                    if (faces[aoffset] == vertdeg[j + 1][faces[coffset]]) {
-                        flagvert = false;
-                        break;
-                    }
-                }
-                if (flagvert) {
-                    vertdeg[0][faces[coffset]]++;
-                    vertdeg[vertdeg[0][faces[coffset]]][faces[coffset]] = faces[aoffset];
-                }
-                flagvert = true;
-                for (j = 0, jl = vertdeg[0][faces[coffset]]; j < jl; j++) {
-                    if (faces[boffset] == vertdeg[j + 1][faces[coffset]]) {
-                        flagvert = false;
-                        break;
-                    }
-                }
-                if (flagvert) {
-                    vertdeg[0][faces[coffset]]++;
-                    vertdeg[vertdeg[0][faces[coffset]]][faces[coffset]] = faces[boffset];
-                }
-            }
-
-            var wt = 1.00;
-            var wt2 = 0.50;
-            var ssign;
-            var scaleFactor = 1;
-            var outwt = 0.75 / (scaleFactor + 3.5); // area-preserving
-            for (k = 0; k < numiter; k++) {
-                    for (i = 0, il = verts.length; i < il; i++) {
-                            if (vertdeg[0][i] < 3) {
-                                    tps[i].x = verts[i].x;
-                                    tps[i].y = verts[i].y;
-                                    tps[i].z = verts[i].z;
-                            } else if (vertdeg[0][i] == 3 || vertdeg[0][i] == 4) {
-                                    tps[i].x = 0;
-                                    tps[i].y = 0;
-                                    tps[i].z = 0;
-                                    for (j = 0, jl = vertdeg[0][i]; j < jl; j++) {
-                                            tps[i].x += verts[vertdeg[j + 1][i]].x;
-                                            tps[i].y += verts[vertdeg[j + 1][i]].y;
-                                            tps[i].z += verts[vertdeg[j + 1][i]].z;
-                                    }
-                                    tps[i].x += wt2 * verts[i].x;
-                                    tps[i].y += wt2 * verts[i].y;
-                                    tps[i].z += wt2 * verts[i].z;
-                                    tps[i].x /= wt2 + vertdeg[0][i];
-                                    tps[i].y /= wt2 + vertdeg[0][i];
-                                    tps[i].z /= wt2 + vertdeg[0][i];
-                            } else {
-                                    tps[i].x = 0;
-                                    tps[i].y = 0;
-                                    tps[i].z = 0;
-                                    for (j = 0, jl = vertdeg[0][i]; j < jl; j++) {
-                                            tps[i].x += verts[vertdeg[j + 1][i]].x;
-                                            tps[i].y += verts[vertdeg[j + 1][i]].y;
-                                            tps[i].z += verts[vertdeg[j + 1][i]].z;
-                                    }
-                                    tps[i].x += wt * verts[i].x;
-                                    tps[i].y += wt * verts[i].y;
-                                    tps[i].z += wt * verts[i].z;
-                                    tps[i].x /= wt + vertdeg[0][i];
-                                    tps[i].y /= wt + vertdeg[0][i];
-                                    tps[i].z /= wt + vertdeg[0][i];
-                            }
-                    }
-                    for (i = 0, il = verts.length; i < il; i++) {
-                            verts[i].x = tps[i].x;
-                            verts[i].y = tps[i].y;
-                            verts[i].z = tps[i].z;
-                    }
-                    /*
-                     * computenorm(); for (var i = 0; i < vertnumber; i++) { if
-                     * (verts[i].inout) ssign = 1; else ssign = -1; verts[i].x += ssign *
-                     * outwt * verts[i].pn.x; verts[i].y += ssign * outwt *
-                     * verts[i].pn.y; verts[i].z += ssign * outwt * verts[i].pn.z; }
-                     */
-            }
-    };
-
-
-    /*
-     * These tables are based off those by Paul Bourke and Geoffrey Heller:
-     * http://paulbourke.net/geometry/polygonise/
-     * http://paulbourke.net/geometry/polygonise/table2.txt
-     * 
-     * However, they have been substantially modified to reflect a more 
-     * sensible corner numbering scheme and the discrete nature of our voxel data
-     * (resulting in fewer faces).
-     */
-    my.edgeTable = [ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
-            0xb00, 0x0, 0x0, 0x0, 0x700, 0x0, 0xd00, 0xe00, 0xf00, 0x0, 0x0, 0x0,
-            0x8a, 0x0, 0x15, 0x0, 0x86, 0x0, 0x0, 0x0, 0x28c, 0x0, 0x813, 0xf19,
-            0xe10, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x0, 0x0, 0x126, 0x0, 0x0, 0x15, 0x1c,
-            0x0, 0xf23, 0x419, 0xd20, 0x0, 0xa8, 0xa2, 0xaa, 0x0, 0x285, 0x9ab,
-            0x8a2, 0x0, 0x2af, 0x125, 0xac, 0xfaa, 0xea3, 0xda9, 0xca0, 0x0, 0x0,
-            0x0, 0x0, 0x0, 0x45, 0x0, 0x384, 0x0, 0x0, 0x0, 0x700, 0x8a, 0x83,
-            0x648, 0x780, 0x0, 0x51, 0x0, 0x81a, 0x54, 0x55, 0x54, 0x56, 0x0, 0x51,
-            0x0, 0xe5c, 0x14a, 0x451, 0x759, 0x650, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x45,
-            0x0, 0x1f6, 0x0, 0x0, 0x15, 0xdfc, 0x8a, 0x7f3, 0x4f9, 0x5f0, 0xb00,
-            0x68, 0x921, 0x6a, 0x348, 0x245, 0x16f, 0x66, 0xb00, 0xe6f, 0xd65,
-            0xc6c, 0x76a, 0x663, 0x569, 0x460, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
-            0xf46, 0x0, 0x0, 0x45, 0x24c, 0x2a, 0x823, 0x29, 0xb40, 0x0, 0x0, 0x0,
-            0x6ba, 0x0, 0x8f5, 0xfff, 0xef6, 0x0, 0xff, 0x2f5, 0x2fc, 0x9ea, 0x8f3,
-            0xbf9, 0xaf0, 0x0, 0x0, 0x51, 0x152, 0x0, 0xf55, 0x45f, 0xd56, 0x54,
-            0x357, 0x55, 0x154, 0x852, 0xb53, 0x59, 0x950, 0x700, 0x2c8, 0xc2,
-            0x48a, 0xfc4, 0xec5, 0xdcf, 0xcc6, 0x2c4, 0x2cf, 0xc5, 0xcc, 0xbca,
-            0xac3, 0x9c9, 0x8c0, 0x0, 0x0, 0x0, 0x0, 0xa8, 0x1a4, 0xa8, 0x7a6,
-            0xa2, 0xa2, 0x2a4, 0xbac, 0xaa, 0xa3, 0x2a8, 0x3a0, 0xd00, 0xc18,
-            0xd00, 0xe3a, 0x34, 0x35, 0x73f, 0x636, 0x924, 0x83f, 0xb35, 0xa3c,
-            0x12a, 0x33, 0x339, 0x230, 0xe00, 0xe00, 0xc12, 0xd9a, 0x684, 0x795,
-            0x49f, 0x596, 0x92, 0xb9f, 0x815, 0x99c, 0x9a, 0x393, 0x99, 0x190,
-            0xf00, 0xe08, 0xd01, 0xc0a, 0x704, 0x605, 0x50f, 0x406, 0xb02, 0xa0f,
-            0x905, 0x80c, 0x30a, 0x203, 0x109, 0x0 ];
-    
-    var edgeTable = new Uint32Array(my.edgeTable);
-    
-    var triTable = my.triTable = [ [], [], [], [], [], [], [], [ 11, 9, 8 ], [], [], [],
-            [ 8, 10, 9 ], [], [ 10, 8, 11 ], [ 9, 11, 10 ],
-            [ 8, 10, 9, 8, 11, 10 ], [], [], [], [ 1, 7, 3 ], [], [ 4, 2, 0 ], [],
-            [ 2, 1, 7 ], [], [], [], [ 2, 7, 3, 2, 9, 7 ], [],
-            [ 1, 4, 11, 1, 0, 4 ], [ 3, 8, 0, 11, 9, 4, 11, 10, 9 ],
-            [ 4, 11, 9, 11, 10, 9 ], [], [], [], [ 5, 3, 1 ], [], [], [],
-            [ 2, 5, 8, 2, 1, 5 ], [], [], [ 2, 4, 0 ], [ 3, 2, 4 ], [],
-            [ 0, 9, 1, 8, 10, 5, 8, 11, 10 ], [ 3, 4, 0, 3, 10, 4 ],
-            [ 5, 8, 10, 8, 11, 10 ], [], [ 3, 5, 7 ], [ 7, 1, 5 ],
-            [ 1, 7, 3, 1, 5, 7 ], [], [ 9, 2, 0, 9, 7, 2 ],
-            [ 0, 3, 8, 1, 7, 11, 1, 5, 7 ], [ 11, 1, 7, 1, 5, 7 ], [],
-            [ 9, 1, 0, 5, 3, 2, 5, 7, 3 ], [ 8, 2, 5, 8, 0, 2 ],
-            [ 2, 5, 3, 5, 7, 3 ], [ 3, 9, 1, 3, 8, 9, 7, 11, 10, 7, 10, 5 ],
-            [ 9, 1, 0, 10, 7, 11, 10, 5, 7 ], [ 3, 8, 0, 7, 10, 5, 7, 11, 10 ],
-            [ 11, 5, 7, 11, 10, 5 ], [], [], [], [], [], [ 0, 6, 2 ], [],
-            [ 7, 2, 9, 7, 9, 8 ], [], [], [], [ 8, 10, 9 ], [ 7, 1, 3 ],
-            [ 7, 1, 0 ], [ 6, 9, 3, 6, 10, 9 ], [ 7, 10, 8, 10, 9, 8 ], [],
-            [ 6, 0, 4 ], [], [ 11, 1, 4, 11, 3, 1 ], [ 2, 4, 6 ],
-            [ 2, 0, 4, 2, 4, 6 ], [ 2, 4, 6 ], [ 1, 4, 2, 4, 6, 2 ], [],
-            [ 6, 0, 4 ], [], [ 2, 11, 3, 6, 9, 4, 6, 10, 9 ], [ 8, 6, 1, 8, 1, 3 ],
-            [ 10, 0, 6, 0, 4, 6 ], [ 8, 0, 3, 9, 6, 10, 9, 4, 6 ],
-            [ 10, 4, 6, 10, 9, 4 ], [], [], [], [ 5, 3, 1 ], [], [ 0, 6, 2 ], [],
-            [ 7, 4, 8, 5, 2, 1, 5, 6, 2 ], [], [], [ 2, 4, 0 ],
-            [ 7, 4, 8, 2, 11, 3, 10, 5, 6 ], [ 7, 1, 3 ],
-            [ 5, 6, 10, 0, 9, 1, 8, 7, 4 ], [ 5, 6, 10, 7, 0, 3, 7, 4, 0 ],
-            [ 10, 5, 6, 4, 8, 7 ], [ 9, 11, 8 ], [ 3, 5, 6 ],
-            [ 0, 5, 11, 0, 11, 8 ], [ 6, 3, 5, 3, 1, 5 ], [ 3, 9, 6, 3, 8, 9 ],
-            [ 9, 6, 0, 6, 2, 0 ], [ 0, 3, 8, 2, 5, 6, 2, 1, 5 ],
-            [ 1, 6, 2, 1, 5, 6 ], [ 9, 11, 8 ], [ 1, 0, 9, 6, 10, 5, 11, 3, 2 ],
-            [ 6, 10, 5, 2, 8, 0, 2, 11, 8 ], [ 3, 2, 11, 10, 5, 6 ],
-            [ 10, 5, 6, 9, 3, 8, 9, 1, 3 ], [ 0, 9, 1, 5, 6, 10 ],
-            [ 8, 0, 3, 10, 5, 6 ], [ 10, 5, 6 ], [], [], [], [], [], [], [],
-            [ 1, 10, 2, 9, 11, 6, 9, 8, 11 ], [], [], [ 6, 0, 2 ],
-            [ 3, 6, 9, 3, 2, 6 ], [ 3, 5, 1 ], [ 0, 5, 1, 0, 11, 5 ], [ 0, 3, 5 ],
-            [ 6, 9, 11, 9, 8, 11 ], [], [], [], [ 4, 5, 9, 7, 1, 10, 7, 3, 1 ], [],
-            [ 11, 6, 7, 2, 4, 5, 2, 0, 4 ],
-            [ 11, 6, 7, 8, 0, 3, 1, 10, 2, 9, 4, 5 ],
-            [ 6, 7, 11, 1, 10, 2, 9, 4, 5 ], [],
-            [ 4, 1, 0, 4, 5, 1, 6, 7, 3, 6, 3, 2 ], [ 9, 4, 5, 0, 6, 7, 0, 2, 6 ],
-            [ 4, 5, 9, 6, 3, 2, 6, 7, 3 ], [ 6, 7, 11, 5, 3, 8, 5, 1, 3 ],
-            [ 6, 7, 11, 4, 1, 0, 4, 5, 1 ], [ 4, 5, 9, 3, 8, 0, 11, 6, 7 ],
-            [ 9, 4, 5, 7, 11, 6 ], [], [], [ 0, 6, 4 ], [ 8, 6, 4, 8, 1, 6 ], [],
-            [ 0, 10, 2, 0, 9, 10, 4, 8, 11, 4, 11, 6 ],
-            [ 10, 2, 1, 6, 0, 3, 6, 4, 0 ], [ 10, 2, 1, 11, 4, 8, 11, 6, 4 ],
-            [ 4, 2, 6 ], [ 1, 0, 9, 2, 4, 8, 2, 6, 4 ], [ 2, 4, 0, 2, 6, 4 ],
-            [ 8, 2, 4, 2, 6, 4 ], [ 11, 4, 1, 11, 6, 4 ],
-            [ 0, 9, 1, 4, 11, 6, 4, 8, 11 ], [ 3, 6, 0, 6, 4, 0 ],
-            [ 8, 6, 4, 8, 11, 6 ], [ 10, 8, 9 ], [ 6, 3, 9, 6, 7, 3 ], [ 6, 7, 1 ],
-            [ 10, 7, 1, 7, 3, 1 ], [ 7, 11, 6, 8, 10, 2, 8, 9, 10 ],
-            [ 11, 6, 7, 10, 0, 9, 10, 2, 0 ], [ 2, 1, 10, 7, 11, 6, 8, 0, 3 ],
-            [ 1, 10, 2, 6, 7, 11 ], [ 7, 2, 6, 7, 9, 2 ],
-            [ 1, 0, 9, 3, 6, 7, 3, 2, 6 ], [ 7, 0, 6, 0, 2, 6 ],
-            [ 2, 7, 3, 2, 6, 7 ], [ 7, 11, 6, 3, 9, 1, 3, 8, 9 ],
-            [ 9, 1, 0, 11, 6, 7 ], [ 0, 3, 8, 11, 6, 7 ], [ 11, 6, 7 ], [], [], [],
-            [], [ 5, 3, 7 ], [ 8, 5, 2, 8, 7, 5 ], [ 5, 3, 7 ],
-            [ 1, 10, 2, 5, 8, 7, 5, 9, 8 ], [ 1, 7, 5 ], [ 1, 7, 5 ],
-            [ 9, 2, 7, 9, 7, 5 ], [ 11, 3, 2, 8, 5, 9, 8, 7, 5 ],
-            [ 1, 3, 7, 1, 7, 5 ], [ 0, 7, 1, 7, 5, 1 ], [ 9, 3, 5, 3, 7, 5 ],
-            [ 9, 7, 5, 9, 8, 7 ], [ 8, 10, 11 ], [ 3, 4, 10, 3, 10, 11 ],
-            [ 8, 10, 11 ], [ 5, 9, 4, 1, 11, 3, 1, 10, 11 ], [ 2, 4, 5 ],
-            [ 5, 2, 4, 2, 0, 4 ], [ 0, 3, 8, 5, 9, 4, 10, 2, 1 ],
-            [ 2, 1, 10, 9, 4, 5 ], [ 2, 8, 5, 2, 11, 8 ],
-            [ 3, 2, 11, 1, 4, 5, 1, 0, 4 ], [ 9, 4, 5, 8, 2, 11, 8, 0, 2 ],
-            [ 11, 3, 2, 9, 4, 5 ], [ 8, 5, 3, 5, 1, 3 ], [ 5, 0, 4, 5, 1, 0 ],
-            [ 3, 8, 0, 4, 5, 9 ], [ 9, 4, 5 ], [ 11, 9, 10 ], [ 11, 9, 10 ],
-            [ 1, 11, 4, 1, 10, 11 ], [ 8, 7, 4, 11, 1, 10, 11, 3, 1 ],
-            [ 2, 7, 9, 2, 9, 10 ], [ 4, 8, 7, 0, 10, 2, 0, 9, 10 ],
-            [ 2, 1, 10, 0, 7, 4, 0, 3, 7 ], [ 10, 2, 1, 8, 7, 4 ], [ 1, 7, 4 ],
-            [ 3, 2, 11, 4, 8, 7, 9, 1, 0 ], [ 11, 4, 2, 4, 0, 2 ],
-            [ 2, 11, 3, 7, 4, 8 ], [ 4, 1, 7, 1, 3, 7 ], [ 1, 0, 9, 8, 7, 4 ],
-            [ 3, 4, 0, 3, 7, 4 ], [ 8, 7, 4 ], [ 8, 9, 10, 8, 10, 11 ],
-            [ 3, 9, 11, 9, 10, 11 ], [ 0, 10, 8, 10, 11, 8 ],
-            [ 10, 3, 1, 10, 11, 3 ], [ 2, 8, 10, 8, 9, 10 ], [ 9, 2, 0, 9, 10, 2 ],
-            [ 8, 0, 3, 1, 10, 2 ], [ 10, 2, 1 ], [ 1, 11, 9, 11, 8, 9 ],
-            [ 11, 3, 2, 0, 9, 1 ], [ 11, 0, 2, 11, 8, 0 ], [ 11, 3, 2 ],
-            [ 8, 1, 3, 8, 9, 1 ], [ 9, 1, 0 ], [ 8, 0, 3 ], [] ];
-     
-    var edgeTable2 = [ 0x0, 0x109, 0x203, 0x30a, 0x80c, 0x905, 0xa0f,
-            0xb06, 0x406, 0x50f, 0x605, 0x70c, 0xc0a, 0xd03, 0xe09, 0xf00, 0x190,
-            0x99, 0x393, 0x29a, 0x99c, 0x895, 0xb9f, 0xa96, 0x596, 0x49f, 0x795,
-            0x69c, 0xd9a, 0xc93, 0xf99, 0xe90, 0x230, 0x339, 0x33, 0x13a, 0xa3c,
-            0xb35, 0x83f, 0x936, 0x636, 0x73f, 0x435, 0x53c, 0xe3a, 0xf33, 0xc39,
-            0xd30, 0x3a0, 0x2a9, 0x1a3, 0xaa, 0xbac, 0xaa5, 0x9af, 0x8a6, 0x7a6,
-            0x6af, 0x5a5, 0x4ac, 0xfaa, 0xea3, 0xda9, 0xca0, 0x8c0, 0x9c9, 0xac3,
-            0xbca, 0xcc, 0x1c5, 0x2cf, 0x3c6, 0xcc6, 0xdcf, 0xec5, 0xfcc, 0x4ca,
-            0x5c3, 0x6c9, 0x7c0, 0x950, 0x859, 0xb53, 0xa5a, 0x15c, 0x55, 0x35f,
-            0x256, 0xd56, 0xc5f, 0xf55, 0xe5c, 0x55a, 0x453, 0x759, 0x650, 0xaf0,
-            0xbf9, 0x8f3, 0x9fa, 0x2fc, 0x3f5, 0xff, 0x1f6, 0xef6, 0xfff, 0xcf5,
-            0xdfc, 0x6fa, 0x7f3, 0x4f9, 0x5f0, 0xb60, 0xa69, 0x963, 0x86a, 0x36c,
-            0x265, 0x16f, 0x66, 0xf66, 0xe6f, 0xd65, 0xc6c, 0x76a, 0x663, 0x569,
-            0x460, 0x460, 0x569, 0x663, 0x76a, 0xc6c, 0xd65, 0xe6f, 0xf66, 0x66,
-            0x16f, 0x265, 0x36c, 0x86a, 0x963, 0xa69, 0xb60, 0x5f0, 0x4f9, 0x7f3,
-            0x6fa, 0xdfc, 0xcf5, 0xfff, 0xef6, 0x1f6, 0xff, 0x3f5, 0x2fc, 0x9fa,
-            0x8f3, 0xbf9, 0xaf0, 0x650, 0x759, 0x453, 0x55a, 0xe5c, 0xf55, 0xc5f,
-            0xd56, 0x256, 0x35f, 0x55, 0x15c, 0xa5a, 0xb53, 0x859, 0x950, 0x7c0,
-            0x6c9, 0x5c3, 0x4ca, 0xfcc, 0xec5, 0xdcf, 0xcc6, 0x3c6, 0x2cf, 0x1c5,
-            0xcc, 0xbca, 0xac3, 0x9c9, 0x8c0, 0xca0, 0xda9, 0xea3, 0xfaa, 0x4ac,
-            0x5a5, 0x6af, 0x7a6, 0x8a6, 0x9af, 0xaa5, 0xbac, 0xaa, 0x1a3, 0x2a9,
-            0x3a0, 0xd30, 0xc39, 0xf33, 0xe3a, 0x53c, 0x435, 0x73f, 0x636, 0x936,
-            0x83f, 0xb35, 0xa3c, 0x13a, 0x33, 0x339, 0x230, 0xe90, 0xf99, 0xc93,
-            0xd9a, 0x69c, 0x795, 0x49f, 0x596, 0xa96, 0xb9f, 0x895, 0x99c, 0x29a,
-            0x393, 0x99, 0x190, 0xf00, 0xe09, 0xd03, 0xc0a, 0x70c, 0x605, 0x50f,
-            0x406, 0xb06, 0xa0f, 0x905, 0x80c, 0x30a, 0x203, 0x109, 0x0 ];
-     
-    var triTable2 = [ [], [ 8, 3, 0 ], [ 9, 0, 1 ], [ 8, 3, 1, 8, 1, 9 ],
-            [ 11, 2, 3 ], [ 11, 2, 0, 11, 0, 8 ], [ 11, 2, 3, 0, 1, 9 ],
-            [ 2, 1, 11, 1, 9, 11, 11, 9, 8 ], [ 10, 1, 2 ], [ 8, 3, 0, 1, 2, 10 ],
-            [ 9, 0, 2, 9, 2, 10 ], [ 3, 2, 8, 2, 10, 8, 8, 10, 9 ],
-            [ 10, 1, 3, 10, 3, 11 ], [ 1, 0, 10, 0, 8, 10, 10, 8, 11 ],
-            [ 0, 3, 9, 3, 11, 9, 9, 11, 10 ], [ 8, 10, 9, 8, 11, 10 ], [ 8, 4, 7 ],
-            [ 3, 0, 4, 3, 4, 7 ], [ 1, 9, 0, 8, 4, 7 ],
-            [ 9, 4, 1, 4, 7, 1, 1, 7, 3 ], [ 2, 3, 11, 7, 8, 4 ],
-            [ 7, 11, 4, 11, 2, 4, 4, 2, 0 ], [ 3, 11, 2, 4, 7, 8, 9, 0, 1 ],
-            [ 2, 7, 11, 2, 1, 7, 1, 4, 7, 1, 9, 4 ], [ 10, 1, 2, 8, 4, 7 ],
-            [ 2, 10, 1, 0, 4, 7, 0, 7, 3 ], [ 4, 7, 8, 0, 2, 10, 0, 10, 9 ],
-            [ 2, 7, 3, 2, 9, 7, 7, 9, 4, 2, 10, 9 ],
-            [ 8, 4, 7, 11, 10, 1, 11, 1, 3 ],
-            [ 11, 4, 7, 1, 4, 11, 1, 11, 10, 1, 0, 4 ],
-            [ 3, 8, 0, 7, 11, 4, 11, 9, 4, 11, 10, 9 ],
-            [ 7, 11, 4, 4, 11, 9, 11, 10, 9 ], [ 9, 5, 4 ], [ 3, 0, 8, 4, 9, 5 ],
-            [ 5, 4, 0, 5, 0, 1 ], [ 4, 8, 5, 8, 3, 5, 5, 3, 1 ],
-            [ 11, 2, 3, 9, 5, 4 ], [ 9, 5, 4, 8, 11, 2, 8, 2, 0 ],
-            [ 3, 11, 2, 1, 5, 4, 1, 4, 0 ],
-            [ 8, 5, 4, 2, 5, 8, 2, 8, 11, 2, 1, 5 ], [ 2, 10, 1, 9, 5, 4 ],
-            [ 0, 8, 3, 5, 4, 9, 10, 1, 2 ], [ 10, 5, 2, 5, 4, 2, 2, 4, 0 ],
-            [ 3, 4, 8, 3, 2, 4, 2, 5, 4, 2, 10, 5 ],
-            [ 5, 4, 9, 1, 3, 11, 1, 11, 10 ],
-            [ 0, 9, 1, 4, 8, 5, 8, 10, 5, 8, 11, 10 ],
-            [ 3, 4, 0, 3, 10, 4, 4, 10, 5, 3, 11, 10 ],
-            [ 4, 8, 5, 5, 8, 10, 8, 11, 10 ], [ 9, 5, 7, 9, 7, 8 ],
-            [ 0, 9, 3, 9, 5, 3, 3, 5, 7 ], [ 8, 0, 7, 0, 1, 7, 7, 1, 5 ],
-            [ 1, 7, 3, 1, 5, 7 ], [ 11, 2, 3, 8, 9, 5, 8, 5, 7 ],
-            [ 9, 2, 0, 9, 7, 2, 2, 7, 11, 9, 5, 7 ],
-            [ 0, 3, 8, 2, 1, 11, 1, 7, 11, 1, 5, 7 ],
-            [ 2, 1, 11, 11, 1, 7, 1, 5, 7 ], [ 1, 2, 10, 5, 7, 8, 5, 8, 9 ],
-            [ 9, 1, 0, 10, 5, 2, 5, 3, 2, 5, 7, 3 ],
-            [ 5, 2, 10, 8, 2, 5, 8, 5, 7, 8, 0, 2 ],
-            [ 10, 5, 2, 2, 5, 3, 5, 7, 3 ],
-            [ 3, 9, 1, 3, 8, 9, 7, 11, 10, 7, 10, 5 ],
-            [ 9, 1, 0, 10, 7, 11, 10, 5, 7 ], [ 3, 8, 0, 7, 10, 5, 7, 11, 10 ],
-            [ 11, 5, 7, 11, 10, 5 ], [ 11, 7, 6 ], [ 0, 8, 3, 11, 7, 6 ],
-            [ 9, 0, 1, 11, 7, 6 ], [ 7, 6, 11, 3, 1, 9, 3, 9, 8 ],
-            [ 2, 3, 7, 2, 7, 6 ], [ 8, 7, 0, 7, 6, 0, 0, 6, 2 ],
-            [ 1, 9, 0, 3, 7, 6, 3, 6, 2 ], [ 7, 6, 2, 7, 2, 9, 2, 1, 9, 7, 9, 8 ],
-            [ 1, 2, 10, 6, 11, 7 ], [ 2, 10, 1, 7, 6, 11, 8, 3, 0 ],
-            [ 11, 7, 6, 10, 9, 0, 10, 0, 2 ],
-            [ 7, 6, 11, 3, 2, 8, 8, 2, 10, 8, 10, 9 ],
-            [ 6, 10, 7, 10, 1, 7, 7, 1, 3 ],
-            [ 6, 10, 1, 6, 1, 7, 7, 1, 0, 7, 0, 8 ],
-            [ 9, 0, 3, 6, 9, 3, 6, 10, 9, 6, 3, 7 ],
-            [ 6, 10, 7, 7, 10, 8, 10, 9, 8 ], [ 8, 4, 6, 8, 6, 11 ],
-            [ 11, 3, 6, 3, 0, 6, 6, 0, 4 ], [ 0, 1, 9, 4, 6, 11, 4, 11, 8 ],
-            [ 1, 9, 4, 11, 1, 4, 11, 3, 1, 11, 4, 6 ],
-            [ 3, 8, 2, 8, 4, 2, 2, 4, 6 ], [ 2, 0, 4, 2, 4, 6 ],
-            [ 1, 9, 0, 3, 8, 2, 2, 8, 4, 2, 4, 6 ], [ 9, 4, 1, 1, 4, 2, 4, 6, 2 ],
-            [ 10, 1, 2, 11, 8, 4, 11, 4, 6 ],
-            [ 10, 1, 2, 11, 3, 6, 6, 3, 0, 6, 0, 4 ],
-            [ 0, 2, 10, 0, 10, 9, 4, 11, 8, 4, 6, 11 ],
-            [ 2, 11, 3, 6, 9, 4, 6, 10, 9 ],
-            [ 8, 4, 6, 8, 6, 1, 6, 10, 1, 8, 1, 3 ],
-            [ 1, 0, 10, 10, 0, 6, 0, 4, 6 ], [ 8, 0, 3, 9, 6, 10, 9, 4, 6 ],
-            [ 10, 4, 6, 10, 9, 4 ], [ 9, 5, 4, 7, 6, 11 ],
-            [ 4, 9, 5, 3, 0, 8, 11, 7, 6 ], [ 6, 11, 7, 4, 0, 1, 4, 1, 5 ],
-            [ 6, 11, 7, 4, 8, 5, 5, 8, 3, 5, 3, 1 ], [ 4, 9, 5, 6, 2, 3, 6, 3, 7 ],
-            [ 9, 5, 4, 8, 7, 0, 0, 7, 6, 0, 6, 2 ],
-            [ 4, 0, 1, 4, 1, 5, 6, 3, 7, 6, 2, 3 ], [ 7, 4, 8, 5, 2, 1, 5, 6, 2 ],
-            [ 6, 11, 7, 1, 2, 10, 9, 5, 4 ],
-            [ 11, 7, 6, 8, 3, 0, 1, 2, 10, 9, 5, 4 ],
-            [ 11, 7, 6, 10, 5, 2, 2, 5, 4, 2, 4, 0 ],
-            [ 7, 4, 8, 2, 11, 3, 10, 5, 6 ],
-            [ 4, 9, 5, 6, 10, 7, 7, 10, 1, 7, 1, 3 ],
-            [ 5, 6, 10, 0, 9, 1, 8, 7, 4 ], [ 5, 6, 10, 7, 0, 3, 7, 4, 0 ],
-            [ 10, 5, 6, 4, 8, 7 ], [ 5, 6, 9, 6, 11, 9, 9, 11, 8 ],
-            [ 0, 9, 5, 0, 5, 3, 3, 5, 6, 3, 6, 11 ],
-            [ 0, 1, 5, 0, 5, 11, 5, 6, 11, 0, 11, 8 ],
-            [ 11, 3, 6, 6, 3, 5, 3, 1, 5 ], [ 9, 5, 6, 3, 9, 6, 3, 8, 9, 3, 6, 2 ],
-            [ 5, 6, 9, 9, 6, 0, 6, 2, 0 ], [ 0, 3, 8, 2, 5, 6, 2, 1, 5 ],
-            [ 1, 6, 2, 1, 5, 6 ], [ 1, 2, 10, 5, 6, 9, 9, 6, 11, 9, 11, 8 ],
-            [ 1, 0, 9, 6, 10, 5, 11, 3, 2 ], [ 6, 10, 5, 2, 8, 0, 2, 11, 8 ],
-            [ 3, 2, 11, 10, 5, 6 ], [ 10, 5, 6, 9, 3, 8, 9, 1, 3 ],
-            [ 0, 9, 1, 5, 6, 10 ], [ 8, 0, 3, 10, 5, 6 ], [ 10, 5, 6 ],
-            [ 10, 6, 5 ], [ 8, 3, 0, 10, 6, 5 ], [ 0, 1, 9, 5, 10, 6 ],
-            [ 10, 6, 5, 9, 8, 3, 9, 3, 1 ], [ 3, 11, 2, 10, 6, 5 ],
-            [ 6, 5, 10, 2, 0, 8, 2, 8, 11 ], [ 1, 9, 0, 6, 5, 10, 11, 2, 3 ],
-            [ 1, 10, 2, 5, 9, 6, 9, 11, 6, 9, 8, 11 ], [ 1, 2, 6, 1, 6, 5 ],
-            [ 0, 8, 3, 2, 6, 5, 2, 5, 1 ], [ 5, 9, 6, 9, 0, 6, 6, 0, 2 ],
-            [ 9, 6, 5, 3, 6, 9, 3, 9, 8, 3, 2, 6 ], [ 11, 6, 3, 6, 5, 3, 3, 5, 1 ],
-            [ 0, 5, 1, 0, 11, 5, 5, 11, 6, 0, 8, 11 ],
-            [ 0, 5, 9, 0, 3, 5, 3, 6, 5, 3, 11, 6 ],
-            [ 5, 9, 6, 6, 9, 11, 9, 8, 11 ], [ 10, 6, 5, 4, 7, 8 ],
-            [ 5, 10, 6, 7, 3, 0, 7, 0, 4 ], [ 5, 10, 6, 0, 1, 9, 8, 4, 7 ],
-            [ 4, 5, 9, 6, 7, 10, 7, 1, 10, 7, 3, 1 ],
-            [ 7, 8, 4, 2, 3, 11, 10, 6, 5 ],
-            [ 11, 6, 7, 10, 2, 5, 2, 4, 5, 2, 0, 4 ],
-            [ 11, 6, 7, 8, 0, 3, 1, 10, 2, 9, 4, 5 ],
-            [ 6, 7, 11, 1, 10, 2, 9, 4, 5 ], [ 7, 8, 4, 5, 1, 2, 5, 2, 6 ],
-            [ 4, 1, 0, 4, 5, 1, 6, 7, 3, 6, 3, 2 ],
-            [ 9, 4, 5, 8, 0, 7, 0, 6, 7, 0, 2, 6 ], [ 4, 5, 9, 6, 3, 2, 6, 7, 3 ],
-            [ 6, 7, 11, 4, 5, 8, 5, 3, 8, 5, 1, 3 ],
-            [ 6, 7, 11, 4, 1, 0, 4, 5, 1 ], [ 4, 5, 9, 3, 8, 0, 11, 6, 7 ],
-            [ 9, 4, 5, 7, 11, 6 ], [ 10, 6, 4, 10, 4, 9 ],
-            [ 8, 3, 0, 9, 10, 6, 9, 6, 4 ], [ 1, 10, 0, 10, 6, 0, 0, 6, 4 ],
-            [ 8, 6, 4, 8, 1, 6, 6, 1, 10, 8, 3, 1 ],
-            [ 2, 3, 11, 6, 4, 9, 6, 9, 10 ],
-            [ 0, 10, 2, 0, 9, 10, 4, 8, 11, 4, 11, 6 ],
-            [ 10, 2, 1, 11, 6, 3, 6, 0, 3, 6, 4, 0 ],
-            [ 10, 2, 1, 11, 4, 8, 11, 6, 4 ], [ 9, 1, 4, 1, 2, 4, 4, 2, 6 ],
-            [ 1, 0, 9, 3, 2, 8, 2, 4, 8, 2, 6, 4 ], [ 2, 4, 0, 2, 6, 4 ],
-            [ 3, 2, 8, 8, 2, 4, 2, 6, 4 ],
-            [ 1, 4, 9, 11, 4, 1, 11, 1, 3, 11, 6, 4 ],
-            [ 0, 9, 1, 4, 11, 6, 4, 8, 11 ], [ 11, 6, 3, 3, 6, 0, 6, 4, 0 ],
-            [ 8, 6, 4, 8, 11, 6 ], [ 6, 7, 10, 7, 8, 10, 10, 8, 9 ],
-            [ 9, 3, 0, 6, 3, 9, 6, 9, 10, 6, 7, 3 ],
-            [ 6, 1, 10, 6, 7, 1, 7, 0, 1, 7, 8, 0 ],
-            [ 6, 7, 10, 10, 7, 1, 7, 3, 1 ],
-            [ 7, 11, 6, 3, 8, 2, 8, 10, 2, 8, 9, 10 ],
-            [ 11, 6, 7, 10, 0, 9, 10, 2, 0 ], [ 2, 1, 10, 7, 11, 6, 8, 0, 3 ],
-            [ 1, 10, 2, 6, 7, 11 ], [ 7, 2, 6, 7, 9, 2, 2, 9, 1, 7, 8, 9 ],
-            [ 1, 0, 9, 3, 6, 7, 3, 2, 6 ], [ 8, 0, 7, 7, 0, 6, 0, 2, 6 ],
-            [ 2, 7, 3, 2, 6, 7 ], [ 7, 11, 6, 3, 9, 1, 3, 8, 9 ],
-            [ 9, 1, 0, 11, 6, 7 ], [ 0, 3, 8, 11, 6, 7 ], [ 11, 6, 7 ],
-            [ 11, 7, 5, 11, 5, 10 ], [ 3, 0, 8, 7, 5, 10, 7, 10, 11 ],
-            [ 9, 0, 1, 10, 11, 7, 10, 7, 5 ],
-            [ 3, 1, 9, 3, 9, 8, 7, 10, 11, 7, 5, 10 ],
-            [ 10, 2, 5, 2, 3, 5, 5, 3, 7 ],
-            [ 5, 10, 2, 8, 5, 2, 8, 7, 5, 8, 2, 0 ],
-            [ 9, 0, 1, 10, 2, 5, 5, 2, 3, 5, 3, 7 ],
-            [ 1, 10, 2, 5, 8, 7, 5, 9, 8 ], [ 2, 11, 1, 11, 7, 1, 1, 7, 5 ],
-            [ 0, 8, 3, 2, 11, 1, 1, 11, 7, 1, 7, 5 ],
-            [ 9, 0, 2, 9, 2, 7, 2, 11, 7, 9, 7, 5 ],
-            [ 11, 3, 2, 8, 5, 9, 8, 7, 5 ], [ 1, 3, 7, 1, 7, 5 ],
-            [ 8, 7, 0, 0, 7, 1, 7, 5, 1 ], [ 0, 3, 9, 9, 3, 5, 3, 7, 5 ],
-            [ 9, 7, 5, 9, 8, 7 ], [ 4, 5, 8, 5, 10, 8, 8, 10, 11 ],
-            [ 3, 0, 4, 3, 4, 10, 4, 5, 10, 3, 10, 11 ],
-            [ 0, 1, 9, 4, 5, 8, 8, 5, 10, 8, 10, 11 ],
-            [ 5, 9, 4, 1, 11, 3, 1, 10, 11 ],
-            [ 3, 8, 4, 3, 4, 2, 2, 4, 5, 2, 5, 10 ],
-            [ 10, 2, 5, 5, 2, 4, 2, 0, 4 ], [ 0, 3, 8, 5, 9, 4, 10, 2, 1 ],
-            [ 2, 1, 10, 9, 4, 5 ], [ 8, 4, 5, 2, 8, 5, 2, 11, 8, 2, 5, 1 ],
-            [ 3, 2, 11, 1, 4, 5, 1, 0, 4 ], [ 9, 4, 5, 8, 2, 11, 8, 0, 2 ],
-            [ 11, 3, 2, 9, 4, 5 ], [ 4, 5, 8, 8, 5, 3, 5, 1, 3 ],
-            [ 5, 0, 4, 5, 1, 0 ], [ 3, 8, 0, 4, 5, 9 ], [ 9, 4, 5 ],
-            [ 7, 4, 11, 4, 9, 11, 11, 9, 10 ],
-            [ 3, 0, 8, 7, 4, 11, 11, 4, 9, 11, 9, 10 ],
-            [ 11, 7, 4, 1, 11, 4, 1, 10, 11, 1, 4, 0 ],
-            [ 8, 7, 4, 11, 1, 10, 11, 3, 1 ],
-            [ 2, 3, 7, 2, 7, 9, 7, 4, 9, 2, 9, 10 ],
-            [ 4, 8, 7, 0, 10, 2, 0, 9, 10 ], [ 2, 1, 10, 0, 7, 4, 0, 3, 7 ],
-            [ 10, 2, 1, 8, 7, 4 ], [ 2, 11, 7, 2, 7, 1, 1, 7, 4, 1, 4, 9 ],
-            [ 3, 2, 11, 4, 8, 7, 9, 1, 0 ], [ 7, 4, 11, 11, 4, 2, 4, 0, 2 ],
-            [ 2, 11, 3, 7, 4, 8 ], [ 9, 1, 4, 4, 1, 7, 1, 3, 7 ],
-            [ 1, 0, 9, 8, 7, 4 ], [ 3, 4, 0, 3, 7, 4 ], [ 8, 7, 4 ],
-            [ 8, 9, 10, 8, 10, 11 ], [ 0, 9, 3, 3, 9, 11, 9, 10, 11 ],
-            [ 1, 10, 0, 0, 10, 8, 10, 11, 8 ], [ 10, 3, 1, 10, 11, 3 ],
-            [ 3, 8, 2, 2, 8, 10, 8, 9, 10 ], [ 9, 2, 0, 9, 10, 2 ],
-            [ 8, 0, 3, 1, 10, 2 ], [ 10, 2, 1 ], [ 2, 11, 1, 1, 11, 9, 11, 8, 9 ],
-            [ 11, 3, 2, 0, 9, 1 ], [ 11, 0, 2, 11, 8, 0 ], [ 11, 3, 2 ],
-            [ 8, 1, 3, 8, 9, 1 ], [ 9, 1, 0 ], [ 8, 0, 3 ], [] ];
-            
-            return my;
-};
-
-//each webworker needs its own marching cube object
-$3Dmol.MarchingCube  = $3Dmol.MarchingCubeInitializer();    
-
-
-
-/**
- * $3Dmol.Parsers stores functions for parsing molecular data. They all take a string of molecular data
- * and options. The default behavior is to only read the first model in the case of multimodel files, and
- * all parsers return a list of atom list(s)
- * 
- * $3Dmol.Parsers.<ext> corresponds to the parsers for files with extension ext
- */
-$3Dmol.Parsers = (function() {
-    var parsers = {};
-
-    /**
-     * @param {AtomSpec[]}
-     *            atomsarray
-     */
-    var assignBonds = function(atomsarray) {
-        // assign bonds - yuck, can't count on connect records
-        var atoms = atomsarray.slice(0);
-        var i, j, n;
-        for (i = 0, n = atomsarray.length; i < n; i++) {
-            // Don't reindex if atoms are already indexed
-            if (!atomsarray[i].index)
-                atomsarray[i].index = i;
-        }
-
-        atoms.sort(function(a, b) {
-            return a.z - b.z;
-        });
-        for (i = 0, n = atoms.length; i < n; i++) {
-            var ai = atoms[i];
-
-            for (j = i + 1; j < n; j++) {
-                var aj = atoms[j];
-                if (aj.z - ai.z > 4.725) // can't be connected
-                    break;
-                else if (Math.abs(aj.x - ai.x) > 4.725 || Math.abs(aj.y - ai.y) > 4.725) { // can't be connected either
-                    continue;
-                }
-                else if (areConnected(ai, aj)) {
-                    if (ai.bonds.indexOf(aj.index) == -1) {
-                        // only add if not already there
-                        ai.bonds.push(aj.index);
-                        ai.bondOrder.push(1);
-                        aj.bonds.push(ai.index);
-                        aj.bondOrder.push(1);
-                    }
-                }
-            }
-        }
-    };
-
-    // this is optimized for proteins where it is assumed connected
-    // atoms are on the same or next residue
-    /**
-     * @param {AtomSpec[]}
-     *            atomsarray
-     */
-    var assignPDBBonds = function(atomsarray) {
-        // assign bonds - yuck, can't count on connect records
-        var protatoms = [];
-        var hetatoms = [];
-        var i, n;
-        for (i = 0, n = atomsarray.length; i < n; i++) {
-            var atom = atomsarray[i];
-            atom.index = i;
-            if (atom.hetflag)
-                hetatoms.push(atom);
-            else
-                protatoms.push(atom);
-        }
-
-        assignBonds(hetatoms);
-
-        // sort by resid
-        protatoms.sort(function(a, b) {
-            if (a.chain != b.chain)
-                return a.chain < b.chain ? -1 : 1;
-            return a.resi - b.resi;
-        });
-
-        // for identifying connected residues
-        var currentResi = -1;
-        var reschain = -1;
-        var lastResConnected;
-
-        for (i = 0, n = protatoms.length; i < n; i++) {
-            var ai = protatoms[i];
-
-            if (ai.resi !== currentResi) {
-                currentResi = ai.resi;
-                if (!lastResConnected)
-                    reschain++;
-
-                lastResConnected = false;
-            }
-
-            ai.reschain = reschain;
-
-            for (var j = i + 1; j < protatoms.length; j++) {
-                var aj = protatoms[j];
-                if (aj.chain != ai.chain)
-                    break;
-                if (aj.resi - ai.resi > 1) // can't be connected
-                    break;
-                if (areConnected(ai, aj)) {
-                    if (ai.bonds.indexOf(aj.index) === -1) {
-                        // only add if not already there
-                        ai.bonds.push(aj.index);
-                        ai.bondOrder.push(1);
-                        aj.bonds.push(ai.index);
-                        aj.bondOrder.push(1);
-                    }
-
-                    if (ai.resi !== aj.resi)
-                        lastResConnected = true;
-
-                }
-            }
-        }
-
-    };
-
-    // this will identify all hydrogen bonds between backbone
-    // atoms; assume atom names are correct, only identifies
-    // single closest hbond
-    var assignBackboneHBonds = function(atomsarray) {
-        var maxlength = 3.2;
-        var maxlengthSq = 10.24;
-        var atoms = [];
-        var i, j, n;
-        for (i = 0, n = atomsarray.length; i < n; i++) {
-            atomsarray[i].index = i;
-            // only consider 'N' and 'O'
-            var atom = atomsarray[i];
-            if (!atom.hetflag && (atom.atom === "N" || atom.atom === "O")) {
-                atoms.push(atom);
-                atom.hbondOther = null;
-                atom.hbondDistanceSq = Number.POSITIVE_INFINITY;
-            }
-        }
-
-        atoms.sort(function(a, b) {
-            return a.z - b.z;
-        });
-        for (i = 0, n = atoms.length; i < n; i++) {
-            var ai = atoms[i];
-
-            for (j = i + 1; j < n; j++) {
-                var aj = atoms[j];
-                var zdiff = aj.z - ai.z;
-                if (zdiff > maxlength) // can't be connected
-                    break;
-                if (aj.atom == ai.atom)
-                    continue; // can't be connected, but later might be
-                var ydiff = Math.abs(aj.y - ai.y);
-                if (ydiff > maxlength)
-                    continue;
-                var xdiff = Math.abs(aj.x - ai.x);
-                if (xdiff > maxlength)
-                    continue;
-                var dist = xdiff * xdiff + ydiff * ydiff + zdiff * zdiff;
-                if (dist > maxlengthSq)
-                    continue;
-
-                if (aj.chain == ai.chain && Math.abs(aj.resi - ai.resi) < 4)
-                    continue; // ignore bonds between too close residues
-                // select closest hbond
-                if (dist < ai.hbondDistanceSq) {
-                    ai.hbondOther = aj;
-                    ai.hbondDistanceSq = dist;
-                }
-                if (dist < aj.hbondDistanceSq) {
-                    aj.hbondOther = ai;
-                    aj.hbondDistanceSq = dist;
-                }
-            }
-        }
-    };
-
-    var computeSecondaryStructure = function(atomsarray) {
-        assignBackboneHBonds(atomsarray);
-
-        // compute, per residue, what the secondary structure is
-        var chres = {}; // lookup by chain and resid
-        var i, il, c, r;
-        var atom, val;
-
-        for (i = 0, il = atomsarray.length; i < il; i++) {
-            atom = atomsarray[i];
-
-            if (typeof (chres[atom.chain]) === "undefined")
-                chres[atom.chain] = [];
-
-            if (isFinite(atom.hbondDistanceSq)) {
-                var other = atom.hbondOther;
-                if (Math.abs(other.resi - atom.resi) === 4) {
-                    // helix
-                    chres[atom.chain][atom.resi] = 'h';
-                } else { // otherwise assume sheet
-                    chres[atom.chain][atom.resi] = 's';
-                }
-            }
-        }
-
-        // plug gaps and remove singletons
-        for (c in chres) {
-            for (r = 1; r < chres[c].length - 1; r++) {
-                var valbefore = chres[c][r - 1];
-                var valafter = chres[c][r + 1];
-                val = chres[c][r];
-                if (valbefore == valafter && val != valbefore) {
-                    chres[c][r] = valbefore;
-                }
-            }
-            for (r = 0; r < chres[c].length; r++) {
-                val = chres[c][r];
-                if (val == 'h' || val == 's') {
-                    if (chres[c][r - 1] != val && chres[c][r + 1] != val)
-                        delete chres[c][r];
-                }
-            }
-        }
-
-        // assign to all atoms in residue, keep track of start
-        var curres = null;
-        for (i = 0, il = atomsarray.length; i < il; i++) {
-            atom = atomsarray[i];
-            val = chres[atom.chain][atom.resi];
-            if (typeof (val) == "undefined")
-                continue;
-            atom.ss = val;
-            if (chres[atom.chain][atom.resi - 1] != val)
-                atom.ssbegin = true;
-            if (chres[atom.chain][atom.resi + 1] != val)
-                atom.ssend = true;
-        }
-    };
-
-    /**
-     * @param {string}
-     *            str
-     * @param {Object}
-     *            options
-     */
-    parsers.cube = parsers.CUBE = function(str, options) {
-        var atoms = [[]];
-        var lines = str.replace(/^\s+/, "").split(/\n\r|\r+/);
-
-        if (lines.length < 6)
-            return atoms;
-
-        var lineArr = lines[2].replace(/^\s+/, "").replace(/\s+/g, " ").split(
-                " ");
-
-        var natoms = Math.abs(parseFloat(lineArr[0]));
-
-        lineArr = lines[3].replace(/^\s+/, "").replace(/\s+/g, " ").split(" ");
-
-        // might have to convert from bohr units to angstroms
-        var convFactor = (parseFloat(lineArr[0]) > 0) ? 0.529177 : 1;
-
-        // Extract atom portion; send to new GLModel...
-        lines = lines.splice(6, natoms);
-
-        var start = atoms[atoms.length-1].length;
-        var end = start + lines.length;
-
-        for (var i = start; i < end; ++i) {
-            var atom = {};
-            atom.serial = i;
-            var line = lines[i - start];
-            var tokens = line.replace(/^\s+/, "").replace(/\s+/g, " ").split(
-                    " ");
-
-            if (tokens[0] == 6)
-                atom.elem = "C";
-
-            else if (tokens[0] == 1)
-                atom.elem = "H";
-
-            else if (tokens[0] == 8)
-                atom.elem = "O";
-
-            else if (tokens[0] == 17)
-                atom.elem = "Cl";
-
-            atom.x = parseFloat(tokens[2]) * convFactor;
-            atom.y = parseFloat(tokens[3]) * convFactor;
-            atom.z = parseFloat(tokens[4]) * convFactor;
-
-            atom.hetflag = true;
-            atom.bonds = [];
-            atom.bondOrder = [];
-            atom.properties = {};
-            atoms[atoms.length-1].push(atom);
-
-        }
-        for (var i = 0; i < atoms.length; i++)
-            assignBonds(atoms[i]);
-
-        return atoms;
-    };
-
-    // read an XYZ file from str and return result
-    /**
-     * @param {string}
-     *            str
-     * @param {Object}
-     *            options
-     */
-    parsers.xyz = parsers.XYZ = function(str, options) {
-        
-        var atoms = [[]];
-        var lines = str.split(/\r?\n|\r/);
-        while (lines.length > 0) {
-            if (lines.length < 3)
-                break;
-            var atomCount = parseInt(lines[0].substr(0, 3));
-            if (isNaN(atomCount) || atomCount <= 0)
-                break;
-            if (lines.length < atomCount + 2)
-                break;
-            var offset = 2;
-            var start = atoms[atoms.length-1].length;
-            var end = start + atomCount;
-            for (var i = start; i < end; i++) {
-                var line = lines[offset++];
-                var tokens = line.replace(/^\s+/, "").replace(/\s+/g, " ").split(
-                        " ");
-                var atom = {};
-                atom.serial = i;
-                var elem = tokens[0];
-                atom.atom = atom.elem = elem[0].toUpperCase() + elem.substr(1).toLowerCase();
-                atom.x = parseFloat(tokens[1]);
-                atom.y = parseFloat(tokens[2]);
-                atom.z = parseFloat(tokens[3]);
-                atom.hetflag = true;
-                atom.bonds = [];
-                atom.bondOrder = [];
-                atom.properties = {};
-                atoms[atoms.length-1][i] = atom;
-                if (tokens.length >= 7) {
-                    atom.dx = parseFloat(tokens[4]);
-                    atom.dy = parseFloat(tokens[5]);
-                    atom.dz = parseFloat(tokens[6]);
-                }
-            }
-
-            if (options.multimodel) {
-                atoms.push([]);
-                lines.splice(0, offset);
-            }
-            else {
-                break;
-            }
-        }
-        
-        for (var i = 0; i < atoms.length; i++) {
-            assignBonds(atoms[i]);
-        }
-        
-        if (options.onemol) {
-            var temp = atoms;
-            atoms = [];
-            atoms.push(temp[0]);
-            for (var i = 1; i < temp.length; i++) {
-                var offset = atoms[0].length;
-                for (var j = 0; j < temp[i].length; j++) {
-                    var a = temp[i][j];
-                    for (var k = 0; k < a.bonds.length; k++) {
-                        a.bonds[k] = a.bonds[k] + offset;
-                    }
-                    a.index = atoms[0].length;
-                    a.serial = atoms[0].length;
-                    atoms[0].push(a);
-                }
-            }
-        }
-
-         return atoms;
-    };
-
-    // put atoms specified in sdf fromat in str into atoms
-    // adds to atoms, does not replace
-    /**
-     * @param {string}
-     *            str
-     * @param {Object}
-     *            options
-     */
-    parsers.sdf = parsers.SDF = function(str, options) {
-
-        var atoms = [[]];
-        var noH = false;
-        if (typeof options.keepH !== "undefined")
-            noH = !options.keepH;
-        var lines = str.split(/\r?\n|\r/);
-        
-        while(lines.length > 0) { 
-            if (lines.length < 4)
-                break;
-            var atomCount = parseInt(lines[3].substr(0, 3));
-            if (isNaN(atomCount) || atomCount <= 0)
-                break;
-            var bondCount = parseInt(lines[3].substr(3, 3));
-            var offset = 4;
-            if (lines.length < 4 + atomCount + bondCount)
-                break;
-
-            // serial is atom's index in file; index is atoms index in 'atoms'
-            var serialToIndex = [];
-            var start = atoms[atoms.length-1].length;
-            var end = start + atomCount;
-            var i, line;
-            for (i = start; i < end; i++,offset++) {
-                line = lines[offset];
-                var atom = {};
-                var elem = line.substr(31, 3).replace(/ /g, "");
-                atom.atom = atom.elem = elem[0].toUpperCase() + elem.substr(1).toLowerCase();
-
-                if (atom.elem != 'H' || !noH) {
-                    atom.serial = i;
-                    serialToIndex[i] = atoms[atoms.length-1].length;
-                    atom.x = parseFloat(line.substr(0, 10));
-                    atom.y = parseFloat(line.substr(10, 10));
-                    atom.z = parseFloat(line.substr(20, 10));
-                    atom.hetflag = true;
-                    atom.bonds = [];
-                    atom.bondOrder = [];
-                    atom.properties = {};
-                    atom.index = atoms[atoms.length-1].length;
-                    atoms[atoms.length-1].push(atom);
-                }
-            }
-
-            for (i = 0; i < bondCount; i++,offset++) {
-                line = lines[offset];
-                var from = serialToIndex[parseInt(line.substr(0, 3)) - 1 + start];
-                var to = serialToIndex[parseInt(line.substr(3, 3)) - 1 + start];
-                var order = parseInt(line.substr(6, 3));
-                if (typeof (from) != 'undefined' && typeof (to) != 'undefined') {
-                    atoms[atoms.length-1][from].bonds.push(to);
-                    atoms[atoms.length-1][from].bondOrder.push(order);
-                    atoms[atoms.length-1][to].bonds.push(from);
-                    atoms[atoms.length-1][to].bondOrder.push(order);
-                }
-            }
-            if (options.multimodel) {
-                if (!options.onemol)
-                    atoms.push([]);
-                while (lines[offset] != "$$$$")
-                    offset++
-                lines.splice(0, ++offset);
-            }
-            else {
-                break;
-            }
-        }
-
-        return atoms;
-    };
-
-    // This parses the ChemDoodle json file format. Although this is registered
-    // for the json file extension, other chemical json file formats exist that
-    // this can not parse. Check which one you have and do not assume that
-    // .json can be parsed
-    parsers.cdjson = parsers.json = function(str, options, modelData) {
-        var atoms = [[]];
-        if (typeof str === "string") { // Str is usually automatically parsed by JQuery
-            str = JSON.parse(str);
-        }
-        var molecules = str.m;
-        var atomsInFile = molecules[0].a; // Assumes there is at least one
-        var bondsInFile = molecules[0].b; // molecule and ignores any more
-                                          // Ignores any shapes
-        var styles = molecules[0].s;
-        var parseStyle = options !== undefined && options.parseStyle !== undefined ? options.parseStyle : styles !== undefined;
-        
-        var offset = atoms[atoms.length-1].length; // When adding atoms their index will be
-                                   // Offset by the number of existing atoms
-        
-        for (var i = 0; i < atomsInFile.length; i++) {
-            var currentAtom = atomsInFile[i];
-            var atom = {};
-            atom.id = currentAtom.i; // Probably won't exist. Doesn't seem to
-                                     // break anything.
-            atom.x = currentAtom.x;
-            atom.y = currentAtom.y;
-            atom.z = currentAtom.z || 0; // Default value if file is 2D
-
-            atom.bonds = [];
-            atom.bondOrder = [];
-            
-            var elem = currentAtom.l || 'C';
-            atom.elem = elem[0].toUpperCase() + elem.substr(1).toLowerCase();
-
-            atom.serial = atoms[atoms.length-1].length;
-            if (parseStyle) {
-                atom.style = styles[currentAtom.s || 0];
-            }
-            atoms[atoms.length-1].push(atom);
-        }
-        for (var i = 0; i < bondsInFile.length; i++) {
-            var currentBond = bondsInFile[i];
-            var beginIndex = currentBond.b + offset;
-            var endIndex = currentBond.e + offset;
-            var bondOrder = currentBond.o || 1;
-            
-            var firstAtom = atoms[atoms.length-1][beginIndex];
-            var secondAtom = atoms[atoms.length-1][endIndex];
-
-            firstAtom.bonds.push(endIndex);
-            firstAtom.bondOrder.push(bondOrder);
-            secondAtom.bonds.push(beginIndex);
-            secondAtom.bondOrder.push(bondOrder);
-        }
-        return atoms;
-    }
-
-    // puts atoms specified in mmCIF fromat in str into atoms
-    /**
-     * @param {string}
-     *            str
-     * @param {Object}
-     *            options
-     */
-    parsers.mcif = parsers.cif = function(str, options, modelData) {
-        
-        var atoms = [[]];
-        var noAssembly = !options.doAssembly; // don't assemble by default
-        var copyMatrix = !options.duplicateAssemblyAtoms;
-        modelData.symmetries = [];
-
-        // Used to handle quotes correctly
-        function splitRespectingQuotes(string, separator) {
-            var sections = [];
-            var sectionStart = 0;
-            var sectionEnd = 0;
-            while (sectionEnd < string.length) {
-                while (string.substr(sectionEnd, separator.length) !== separator
-                        && sectionEnd < string.length) {
-                    // currently does not support escaping quotes
-                    if (string[sectionEnd] === "'") {
-                        sectionEnd++;
-                        while (sectionEnd < string.length
-                                && string[sectionEnd] !== "'") {
-                            sectionEnd++;
-                        }
-                    } else if (string[sectionEnd] === '"') {
-                        sectionEnd++;
-                        while (sectionEnd < string.length
-                                && string[sectionEnd] !== '"') {
-                            sectionEnd++;
-                        }
-                    }
-                    sectionEnd++;
-
-                }
-                sections.push(string.substr(sectionStart, sectionEnd
-                        - sectionStart));
-                sectionStart = sectionEnd = sectionEnd + separator.length;
-            }
-            return sections;
-        }
-
-
-        var lines = str.split(/\r?\n|\r/);
-        // Filter text to remove comments, trailing spaces, and empty lines
-        var linesFiltered = [];
-        var trimDisabled = false;
-        for (var lineNum = 0; lineNum < lines.length; lineNum++) {
-            // first remove comments
-            // incorrect if #'s are allowed in strings
-            // comments might only be allowed at beginning of line, not sure
-            var line = lines[lineNum].split('#')[0];
-
-            // inside data blocks, the string must be left verbatim
-            // datablocks are started with a ';' at the beginning of a line
-            // and ended with a ';' on its own line.
-            if (trimDisabled) {
-                if (line[0] === ';') {
-                    trimDisabled = false;
-                }
-            } else {
-                if (line[0] === ';') {
-                    trimDisabled = true;
-                }
-            }
-
-            if (trimDisabled || line !== "") {
-                if (!trimDisabled) {
-                    line = line.trim();
-                    if (line[0] === '_') {
-                        // Replace dot separating category from data item with underscore. Dots aren't guarenteed, to makes
-                        // files consistent.
-                        var dot = line.split(/\s/)[0].indexOf('.');
-                        if (dot > -1) {
-                            line[dot] = '_';
-                            line = line.substr(0,dot) + '_' + line.substr(dot + 1)
-                        }
-                    }
-                }
-                linesFiltered.push(line);
-            }
-        }
-
-        // Process the lines and puts all of the data into an object.
-        var mmCIF = {};
-        var lineNum = 0;
-        while (lineNum < linesFiltered.length) {
-            if (linesFiltered[lineNum][0] === undefined) {
-                lineNum++;
-            } else if (linesFiltered[lineNum][0] === '_') {
-                var dataItemName = (linesFiltered[lineNum].split(/\s/)[0]).toLowerCase();
-                var dataItem = (mmCIF[dataItemName] = mmCIF[dataItemName] || []);
-
-                // if nothing left on the line go to the next one
-                var restOfLine = linesFiltered[lineNum]
-                        .substr(linesFiltered[lineNum].indexOf(dataItemName)
-                                + dataItemName.length);
-                if (restOfLine === "") {
-                    lineNum++;
-                    if (linesFiltered[lineNum][0] === ';') {
-                        var dataBlock = linesFiltered[lineNum].substr(1);
-                        lineNum++;
-                        while (linesFiltered[lineNum] !== ';') {
-                            dataBlock = dataBlock + '\n'
-                                    + linesFiltered[lineNum];
-                            lineNum++;
-                        }
-                        dataItem.push(dataBlock);
-                    } else {
-                        dataItem.push(linesFiltered[lineNum]);
-                    }
-                } else {
-                    dataItem.push(restOfLine.trim());
-                }
-                lineNum++;
-            } else if (linesFiltered[lineNum].substr(0, 5) === "loop_") {
-                lineNum++;
-                var dataItems = [];
-                while (linesFiltered[lineNum] === ""
-                        || linesFiltered[lineNum][0] === '_') {
-                    if (linesFiltered[lineNum] !== "") {
-                        var dataItemName = (linesFiltered[lineNum].split(/\s/)[0]).toLowerCase();
-                        var dataItem = (mmCIF[dataItemName] = mmCIF[dataItemName] || []);
-                        dataItems.push(dataItem);
-                    }
-                    lineNum++;
-                }
-
-                var currentDataItem = 0;
-                while (lineNum < linesFiltered.length
-                        && linesFiltered[lineNum][0] !== '_'
-                        && linesFiltered[lineNum].substr(0, 5) !== "loop_") {
-                    var line = splitRespectingQuotes(linesFiltered[lineNum],
-                            " ");
-                    for (var field = 0; field < line.length; field++) {
-                        if (line[field] !== "") {
-                            dataItems[currentDataItem].push(line[field]);
-                            currentDataItem = (currentDataItem + 1)
-                                    % dataItems.length;
-                        }
-                    }
-                    lineNum++;
-                }
-            } else {
-                lineNum++;
-            }
-        }
-
-        // Pulls atom information out of the data
-        var atomsPreBonds = [];
-        var currentIndex = 0;
-        var atomCount = mmCIF._atom_site_id !== undefined ? mmCIF._atom_site_id.length
-                        : mmCIF._atom_site_label.length;
-        function sqr(n) {
-            return n*n;
-        }
-        var cell_a, cell_b, cell_c, cell_alpha, cell_beta, cell_gamma, conversionMatrix;
-        if (mmCIF._cell_length_a !== undefined) {
-            var a = cell_a = parseFloat(mmCIF._cell_length_a);
-            var b = cell_b = parseFloat(mmCIF._cell_length_b);
-            var c = cell_c = parseFloat(mmCIF._cell_length_c);
-            var alpha_deg = parseFloat(mmCIF._cell_angle_alpha) || 90;
-            var beta_deg = parseFloat(mmCIF._cell_angle_beta) || 90;
-            var gamma_deg = parseFloat(mmCIF._cell_angle_gamma) || 90;
-            var alpha = cell_alpha = alpha_deg * Math.PI / 180;
-            var beta = cell_beta = beta_deg * Math.PI / 180;
-            var gamma = cell_gamma = gamma_deg * Math.PI / 180;
-            var cos_alpha = Math.cos(alpha);
-            var cos_beta = Math.cos(beta);
-            var cos_gamma = Math.cos(gamma);
-            var sin_gamma = Math.sin(gamma);
-            conversionMatrix = [
-                [a, b*cos_gamma, c*cos_beta],
-                [0, b*sin_gamma, c*(cos_alpha-cos_beta*cos_gamma)/sin_gamma],
-                [0, 0, c*Math.sqrt(1-sqr(cos_alpha)-sqr(cos_beta)-sqr(cos_gamma)+2*cos_alpha*cos_beta*cos_gamma)/sin_gamma]
-            ];
-            modelData.cryst = {'a' : a, 'b' : b, 'c' : c, 'alpha' : alpha_deg, 'beta' : beta_deg, 'gamma' : gamma_deg};
-        }
-        function fractionalToCartesian(a, b, c) {
-            var x = conversionMatrix[0][0]*a + conversionMatrix[0][1]*b + conversionMatrix[0][2]*c;
-            var y = conversionMatrix[1][0]*a + conversionMatrix[1][1]*b + conversionMatrix[1][2]*c;
-            var z = conversionMatrix[2][0]*a + conversionMatrix[2][1]*b + conversionMatrix[2][2]*c;
-            return {x:x, y:y, z:z};
-        }
-        for (var i = 0; i < atomCount; i++) {
-            if (mmCIF._atom_site_group_pdb !== undefined && mmCIF._atom_site_group_pdb[i] === "TER")
-                continue;
-            var atom = {};
-            if (mmCIF._atom_site_cartn_x !== undefined) {
-                atom.x = parseFloat(mmCIF._atom_site_cartn_x[i]);
-                atom.y = parseFloat(mmCIF._atom_site_cartn_y[i]);
-                atom.z = parseFloat(mmCIF._atom_site_cartn_z[i]);
-            }
-            else {
-                var coords = fractionalToCartesian(
-                        parseFloat(mmCIF._atom_site_fract_x[i]),
-                        parseFloat(mmCIF._atom_site_fract_y[i]),
-                        parseFloat(mmCIF._atom_site_fract_z[i]));
-                atom.x = coords.x;
-                atom.y = coords.y;
-                atom.z = coords.z;
-            }
-            atom.chain = mmCIF._atom_site_auth_asym_id ? mmCIF._atom_site_auth_asym_id[i] : undefined;
-            atom.resi = mmCIF._atom_site_auth_seq_id ? parseInt(mmCIF._atom_site_auth_seq_id[i]) : undefined;
-            atom.resn = mmCIF._atom_site_auth_comp_id ? mmCIF._atom_site_auth_comp_id[i].trim() : undefined;
-            atom.atom = mmCIF._atom_site_auth_atom_id ? mmCIF._atom_site_auth_atom_id[i].replace(/"/gm,'')  : undefined; //"primed" names are in quotes
-            atom.hetflag = !mmCIF._atom_site_group_pdb || mmCIF._atom_site_group_pdb[i] === "HETA" || mmCIF._atom_site_group_pdb[i] === "HETATM";
-            var elem = mmCIF._atom_site_type_symbol[i];
-            atom.elem = elem[0].toUpperCase() + elem.substr(1).toLowerCase();
-            atom.bonds = [];
-            atom.ss = 'c';
-            atom.serial = i;
-            atom.bondOrder = [];
-            atom.properties = {};
-            atom.index = currentIndex++;
-            atomsPreBonds[atom.index] = atom;
-        }
-
-        // create a hash table of the atoms using label and sequence as keys
-        var atomHashTable = {};
-        for (var i = 0; i < atomCount; i++) {
-            var label_alt = (mmCIF._atom_site_label_alt_id || [])[i];
-            if (label_alt === undefined) {
-                label_alt = '.';
-            }
-            var label_asym = (mmCIF._atom_site_label_asym_id || [])[i];
-            if (label_asym === undefined) {
-                label_asym = '.';
-            }
-            var label_atom = (mmCIF._atom_site_label_atom_id || [])[i];
-            if (label_atom === undefined) {
-                label_atom = '.';
-            }
-            var label_seq = (mmCIF._atom_site_label_seq_id || [])[i];
-            if (label_seq === undefined) {
-                label_seq = '.';
-            }
-
-            if (atomHashTable[label_alt] === undefined) {
-                atomHashTable[label_alt] = {};
-            }
-            if (atomHashTable[label_alt][label_asym] === undefined) {
-                atomHashTable[label_alt][label_asym] = {};
-            }
-            if (atomHashTable[label_alt][label_asym][label_atom] === undefined) {
-                atomHashTable[label_alt][label_asym][label_atom] = {};
-            }
-
-            atomHashTable[label_alt][label_asym][label_atom][label_seq] = i;
-        }
-
-        if (false && mmCIF._struct_conn && mmCIF._struct_conn_id) {
-            for (var i = 0; i < mmCIF._struct_conn_id.length; i++) {
-                var offset = atoms[atoms.length-1].length;
-
-                var alt = (mmCIF._struct_conn_ptnr1_label_alt_id || [])[i];
-                if (alt === undefined) {
-                    alt = ".";
-                }
-                var asym = (mmCIF._struct_conn_ptnr1_label_asym_id || [])[i];
-                if (asym === undefined) {
-                    asym = ".";
-                }
-                var atom = (mmCIF._struct_conn_ptnr1_label_atom_id || [])[i];
-                if (atom === undefined) {
-                    atom = ".";
-                }
-                var seq = (mmCIF._struct_conn_ptnr1_label_seq_id || [])[i];
-                if (seq === undefined) {
-                    seq = ".";
-                }
-
-                var id1 = atomHashTable[alt][asym][atom][seq];
-                // if (atomsPreBonds[id1] === undefined) continue;
-                var index1 = atomsPreBonds[id1].index;
-
-                var alt = (mmCIF._struct_conn_ptnr2_label_alt_id || [])[i];
-                if (alt === undefined) {
-                    alt = ".";
-                }
-                var asym = (mmCIF._struct_conn_ptnr2_label_asym_id || [])[i];
-                if (asym === undefined) {
-                    asym = ".";
-                }
-                var atom = (mmCIF._struct_conn_ptnr2_label_atom_id || [])[i];
-                if (atom === undefined) {
-                    atom = ".";
-                }
-                var seq = (mmCIF._struct_conn_ptnr2_label_seq_id || [])[i];
-                if (seq === undefined) {
-                    seq = ".";
-                }
-
-                var id2 = atomHashTable[alt][asym][atom][seq];
-                if (atomsPreBonds[id2] === undefined)
-                    continue;
-                var index2 = atomsPreBonds[id2].index;
-
-                atomsPreBonds[id1].bonds.push(index2 + offset);
-                atomsPreBonds[id1].bondOrder.push(1);
-                atomsPreBonds[id2].bonds.push(index1 + offset);
-                atomsPreBonds[id2].bondOrder.push(1);
-                console.log("connected " + index1 + " and " + index2);
-            }
-        }
-
-        // atoms = atoms.concat(atomsPreBonds);
-        for (var i = 0; i < atomsPreBonds.length; i++) {
-            delete atomsPreBonds[i].index;
-            atoms[atoms.length-1].push(atomsPreBonds[i]);
-        }
-        
-        for (var i = 0; i < atoms.length; i++) {
-            assignBonds(atoms[i]);
-            computeSecondaryStructure(atoms[i]);
-        }
-        
-        if (mmCIF._pdbx_struct_oper_list_id !== undefined && !noAssembly) {
-            for (var i = 0; i < mmCIF._pdbx_struct_oper_list_id.length; i++) {
-                var matrix11 = parseFloat(mmCIF['_pdbx_struct_oper_list_matrix[1][1]'][i]);
-                var matrix12 = parseFloat(mmCIF['_pdbx_struct_oper_list_matrix[1][2]'][i]);
-                var matrix13 = parseFloat(mmCIF['_pdbx_struct_oper_list_matrix[1][3]'][i]);
-                var vector1 = parseFloat(mmCIF['_pdbx_struct_oper_list_vector[1]'][i]);
-                var matrix21 = parseFloat(mmCIF['_pdbx_struct_oper_list_matrix[2][1]'][i]);
-                var matrix22 = parseFloat(mmCIF['_pdbx_struct_oper_list_matrix[2][2]'][i]);
-                var matrix23 = parseFloat(mmCIF['_pdbx_struct_oper_list_matrix[2][3]'][i]);
-                var vector2 = parseFloat(mmCIF['_pdbx_struct_oper_list_vector[2]'][i]);
-                var matrix31 = parseFloat(mmCIF['_pdbx_struct_oper_list_matrix[3][1]'][i]);
-                var matrix32 = parseFloat(mmCIF['_pdbx_struct_oper_list_matrix[3][2]'][i]);
-                var matrix33 = parseFloat(mmCIF['_pdbx_struct_oper_list_matrix[3][3]'][i]);
-                var vector3 = parseFloat(mmCIF['_pdbx_struct_oper_list_vector[3]'][i]);
-
-                var matrix = new $3Dmol.Matrix4(matrix11, matrix12, matrix13,
-                        vector1, matrix21, matrix22, matrix23, vector2,
-                        matrix31, matrix32, matrix33, vector3);
-                modelData.symmetries.push(matrix);
-            }
-            for (var i = 0; i < atoms.length; i++) {
-                processSymmetries(modelData.symmetries, copyMatrix, atoms[i]);
-            }
-        }
-        function parseTerm(term){
-            var negative = term.match('-');
-            term = term.replace(/[-xyz]/g, "");
-            var fractionParts = term.split('/');
-
-            var numerator, denominator;
-            if (fractionParts[1] === undefined) {
-                denominator = 1;
-            }
-            else {
-                denominator = parseInt(fractionParts[1]);
-            }
-            if (fractionParts[0] === "") {
-                numerator = 1;
-            }
-            else {
-                numerator = parseInt(fractionParts[0]);
-            }
-            return numerator / denominator * (negative ? -1 : 1);
-        }
-        if (mmCIF._symmetry_equiv_pos_as_xyz !== undefined) {
-            for (var sym = 0; sym < mmCIF._symmetry_equiv_pos_as_xyz.length; sym++) {
-                var transform = mmCIF._symmetry_equiv_pos_as_xyz[sym].replace(/["' ]/g,"");
-                var componentStrings = transform.split(',').map(
-                    function(val){
-                        return val.replace(/-/g,"+-");
-                    });
-                var matrix = new $3Dmol.Matrix4(0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1);
-                for (var coord = 0; coord < 3; coord++) {
-                    var terms = componentStrings[coord].split('+');
-                    var constant = 0, xTerm = 0, yTerm = 0, zTerm = 0;
-                    for (var t = 0; t < terms.length; t++) {
-                        var term = terms[t];
-                        if (term === "")
-                            continue;
-                        var coefficient = parseTerm(term);
-                        if (term.match('x')) {
-                            matrix.elements[coord + 0] = coefficient;
-                        }
-                        else if (term.match('y')) {
-                            matrix.elements[coord + 4] = coefficient;
-                        }
-                        else if (term.match('z')) {
-                            matrix.elements[coord + 8] = coefficient;
-                        }
-                        else {
-                            matrix.elements[coord + 12] = coefficient;
-                        }
-                    }
-                }
-                var conversionMatrix4 = new $3Dmol.Matrix4(
-                    conversionMatrix[0][0], conversionMatrix[0][1], conversionMatrix[0][2], 0,
-                    conversionMatrix[1][0], conversionMatrix[1][1], conversionMatrix[1][2], 0,
-                    conversionMatrix[2][0], conversionMatrix[2][1], conversionMatrix[2][2], 0);
-                var conversionInverse = (new $3Dmol.Matrix4()).getInverse(conversionMatrix4, true);
-                matrix = (new $3Dmol.Matrix4()).multiplyMatrices(matrix, conversionInverse);
-                matrix = (new $3Dmol.Matrix4()).multiplyMatrices(conversionMatrix4, matrix);
-                modelData.symmetries.push(matrix);
-            }
-            for (var i = 0; i < atoms.length; i++) {
-                processSymmetries(modelData.symmetries, copyMatrix, atoms[i]);
-            }
-        }
-        return atoms;
-    }
-
-    // parse SYBYL mol2 file from string - assumed to only contain one molecule
-    // tag
-    /**
-     * @param {string}
-     *            str
-     * @param {Object}
-     *            options
-     */
-    parsers.mol2 = parsers.MOL2 = function(str, options) {
-
-        var atoms = [[]];
-        var noH = false;
-        if (typeof options.keepH !== "undefined")
-            noH = !options.keepH;
-
-        // assert (mol_pos < atom_pos), "Unexpected formatting of mol2 file
-        // (expected 'molecule' section before 'atom' section)";
-
-        var lines = str.substr(mol_pos, str.length).split(/\r?\n|\r/);
-        
-        while(lines.length > 0) { 
-        
-            // Note: these regex's work, though they don't match '<TRIPOS>'
-            // correctly - something to do with angle brackets
-            var mol_pos = str.search(/@<TRIPOS>MOLECULE/);
-            var atom_pos = str.search(/@<TRIPOS>ATOM/);
-
-            // Assuming both Molecule and Atom sections exist
-            if (mol_pos == -1 || atom_pos == -1)
-                break;
-        
-            // serial is atom's index in file; index is atoms index in 'atoms'
-            var serialToIndex = []; 
-            var tokens = lines[2].replace(/^\s+/, "").replace(/\s+/g, " ").split(
-                    " ");
-            var natoms = parseInt(tokens[0]);
-            var nbonds = 0;
-
-            if (tokens.length > 1)
-                nbonds = parseInt(tokens[1]);
-
-            var offset = 4;
-            var i;
-            // Continue until 'Atom' section
-            for (i = 3; i < lines.length; i++) {
-                if (lines[i] == "@<TRIPOS>ATOM") {
-                    offset = i + 1;
-                    break;
-                }
-            }
-        
-            var start = atoms[atoms.length-1].length;
-            var end = start + natoms;
-            var line;
-            // Process ATOMS
-            for (i = start; i < end; i++) {
-                line = lines[offset++];
-                tokens = line.replace(/^\s+/, "").replace(/\s+/g, " ").split(" ");
-                var atom = {};
-                // get element
-                var elem = tokens[5].split('.')[0];
-                atom.atom = atom.elem = elem[0].toUpperCase() + elem.substr(1).toLowerCase();
-                if (atom.elem == 'H' && noH) {
-                    // ignore
-                } else {
-                    // 'index' is this atom's index in 'atoms'; 'serial' is this
-                    // atom's
-                    // serial id in mol2 file
-                    var index = atoms[atoms.length-1].length;
-                    var serial = parseInt(tokens[0]);
-                    atom.serial = serial;
-                    // atom.serial = i;
-
-                    atom.x = parseFloat(tokens[2]);
-                    atom.y = parseFloat(tokens[3]);
-                    atom.z = parseFloat(tokens[4]);
-                    atom.atom = tokens[5];
-                    var charge = parseFloat(tokens[8]);
-                    
-                    atom.index = index;
-                    atom.bonds = [];
-                    atom.bondOrder = [];
-                    atom.properties = {
-                        'charge' : charge,
-                        'partialCharge' : charge
-                    };
-                    serialToIndex[serial] = index;
-
-                    atoms[atoms.length-1].push(atom);
-                }
-            }
-
-            // Process BONDS
-            var bonds_found = false;
-            while (offset < lines.length) {
-                if (lines[offset++] == "@<TRIPOS>BOND") {
-                    bonds_found = true;
-                    break;
-                }
-            }
-
-            if (bonds_found && nbonds) {
-                for (i = 0; i < nbonds; i++) {
-                    line = lines[offset++];
-
-                    tokens = line.replace(/^\s+/, "").replace(/\s+/g, " ").split(
-                            " ");
-                    var from = parseInt(tokens[1]);
-                    fromAtom = atoms[atoms.length-1][serialToIndex[from]];
-                    var to = parseInt(tokens[2]);
-                    toAtom = atoms[atoms.length-1][serialToIndex[to]];
-
-                    // Won't be able to read aromatic bonds correctly...
-                    var order = parseInt(tokens[3]);
-                    if (isNaN(order))
-                        order = 1;
-
-                    if (fromAtom !== undefined && toAtom !== undefined) {
-                        fromAtom.bonds.push(serialToIndex[to]);
-                        fromAtom.bondOrder.push(order);
-                        toAtom.bonds.push(serialToIndex[from]);
-                        toAtom.bondOrder.push(order);
-                    }
-
-                }
-            }
-            if (options.multimodel) {
-                if (!options.onemol)
-                    atoms.push([])
-                lines.splice(0, offset);
-                str = lines.join("\n"); //update for str.search
-                continue;
-            }
-            else {
-                break;
-            }
-        }
-        return atoms;
-
-    };
-
-	var bondTable = {
-		H :0.37,                                                                                                                                He:0.32,
-		Li:1.34,Be:0.90,                                                                                B :0.82,C :0.77,N :0.75,O :0.73,F :0.71,Ne:0.69,
-		Na:1.54,Mg:1.30,                                                                                Al:1.18,Si:1.11,P :1.06,S :1.02,Cl:0.99,Ar:0.97,
-		K :1.96,Ca:1.74,Sc:1.44,Ti:1.56,V :1.25,/* Cr */Mn:1.39,Fe:1.25,Co:1.26,Ni:1.21,Cu:1.38,Zn:1.31,Ga:1.26,Ge:1.22,/* As */Se:1.16,Br:1.14,Kr:1.10,
-		Rb:2.11,Sr:1.92,Y :1.62,Zr:1.48,Nb:1.37,Mo:1.45,Tc:1.56,Ru:1.26,Rh:1.35,Pd:1.31,Ag:1.53,Cd:1.48,In:1.44,Sn:1.41,Sb:1.38,Te:1.35,I :1.33,Xe:1.30,
-		Cs:2.25,Ba:1.98,Lu:1.60,Hf:1.50,Ta:1.38,W :1.46,Re:1.59,Os:1.44,Ir:1.37,Pt:1.28,Au:1.44,Hg:1.49,Tl:1.48,Pb:1.47,Bi:1.46,/* Po *//* At */Rn:1.45,
-
-		// None of the boottom row or any of the Lanthanides have bond lengths
-	}
-    var bondLength = function(elem) {
-        return bondTable[elem] || 1.6;
-    }
-    // return true if atom1 and atom2 are probably bonded to each other
-    // based on distance alone
-    var areConnected = function(atom1, atom2) {
-        var maxsq = bondLength(atom1.elem) + bondLength(atom2.elem);
-        maxsq *= maxsq;
-        maxsq *= 1.1; // fudge factor, especially important for md frames
-
-        var xdiff = atom1.x - atom2.x;
-        xdiff *= xdiff;
-        if (xdiff > maxsq)
-            return false;
-        var ydiff = atom1.y - atom2.y;
-        ydiff *= ydiff;
-        if (ydiff > maxsq)
-            return false;
-        var zdiff = atom1.z - atom2.z;
-        zdiff *= zdiff;
-        if (zdiff > maxsq)
-            return false;
-
-        var distSquared = xdiff + ydiff + zdiff;
-
-        if (isNaN(distSquared))
-            return false;
-        else if (distSquared < 0.5)
-            return false; // maybe duplicate position.
-        else if (distSquared > maxsq)
-            return false;
-        else
-            return true;
-    };
-
-    //adds symmetry info to either duplicate and rotate/translate biological unit later or add extra atoms now
-    var processSymmetries = function(copyMatrices, copyMatrix, atoms) {
-        var end = atoms.length;
-        var offset = end;
-        var t, l, n;
-        if (!copyMatrix) { // do full assembly
-            for (t = 0; t < copyMatrices.length; t++) {
-                if (!copyMatrices[t].isIdentity()) {
-                    var xyz = new $3Dmol.Vector3();
-                    for (n = 0; n < end; n++) {
-                        var bondsArr = [];
-                        for (l = 0; l < atoms[n].bonds.length; l++) {
-                            bondsArr.push(atoms[n].bonds[l] + offset);
-                        }
-                        xyz.set(atoms[n].x, atoms[n].y, atoms[n].z);
-                        xyz.applyMatrix4(copyMatrices[t]);
-                        var newAtom = {};
-                        for (var i in atoms[n]) {
-                            newAtom[i] = atoms[n][i];
-                        }
-                        newAtom.x = xyz.x;
-                        newAtom.y = xyz.y;
-                        newAtom.z = xyz.z;
-                        newAtom.bonds = bondsArr;
-                        atoms.push(newAtom);
-                    }
-                    offset = atoms.length;
-                }
-            }
-        }
-        else if(copyMatrices.length > 1) {
-            for (t = 0; t < atoms.length; t++) {
-                var symmetries = [];
-                for (l = 0; l < copyMatrices.length; l++) {
-                    if (!copyMatrices[l].isIdentity()) {
-                        var newXYZ = new $3Dmol.Vector3();
-                        newXYZ.set(atoms[t].x, atoms[t].y, atoms[t].z);
-                        newXYZ.applyMatrix4(copyMatrices[l]);
-                        symmetries.push(newXYZ);
-                    }
-                }
-                atoms[t].symmetries = symmetries;
-            }
-        }
-    }
-
-
-    // parse pdb file from str and create atoms
-    // if computeStruct is true will always perform secondary structure
-    // analysis,
-    // otherwise only do analysis of SHEET/HELIX comments are missing
-    /**
-     * @param {string}
-     *            str
-     * @param {Object}
-     *            options - keepH (do not strip hydrogens), noSecondaryStructure
-     *            (do not compute ss)
-     */
-    parsers.pdb = parsers.PDB = parsers.pdbqt = parsers.PDBQT = function(str, options, modelData) {
-
-        var atoms = [[]];
-        var atoms_cnt = 0;
-        var noH = !options.keepH; // suppress hydrogens by default
-        var computeStruct = !options.noSecondaryStructure;
-        var noAssembly = !options.doAssembly; // don't assemble by default
-        var copyMatrix = !options.duplicateAssemblyAtoms; //default true
-        modelData.symmetries = [];
-        
-        var start = atoms[atoms.length-1].length;
-        var atom;
-        var protein = {
-            sheet : [],
-            helix : []
-        }; // get secondary structure straight from pdb
-
-        var hasStruct = false;
-        var serialToIndex = []; // map from pdb serial to index in atoms
-        var lines = str.split(/\r?\n|\r/);
-        var i, j, k, line;
-        for (i = 0; i < lines.length; i++) {
-            line = lines[i].replace(/^\s*/, ''); // remove indent
-            var recordName = line.substr(0, 6);
-            var startChain, startResi, endChain, endResi;
-            
-            if(recordName.indexOf("END") == 0) {
-                if (options.multimodel) {
-                    if (!options.onemol)
-                        atoms.push([]);
-                    continue;
-                }
-                else {
-                    break;
-                }
-            }
-
-            else if (recordName == 'ATOM  ' || recordName == 'HETATM') {
-                var resn, chain, resi, icode, x, y, z, hetflag, elem, serial, altLoc, b;
-                altLoc = line.substr(16, 1);
-                if (altLoc != ' ' && altLoc != 'A')
-                    continue; // FIXME: ad hoc
-                serial = parseInt(line.substr(6, 5));
-                atom = line.substr(12, 4).replace(/ /g, "");
-                resn = line.substr(17, 3);
-                chain = line.substr(21, 1);
-                resi = parseInt(line.substr(22, 4));
-                icode = line.substr(26, 1);
-                x = parseFloat(line.substr(30, 8));
-                y = parseFloat(line.substr(38, 8));
-                z = parseFloat(line.substr(46, 8));
-                b = parseFloat(line.substr(60, 8));
-                elem = line.substr(76, 2).replace(/ /g, "");
-                if (elem === '') { // for some incorrect PDB files
-                    elem = line.substr(12, 2).replace(/ /g, "");
-                    if(elem.length > 0 && elem[0] == 'H' && elem != 'Hg') {
-                        elem = 'H'; //workaround weird hydrogen names from MD, note mercury must use lowercase
-                    }
-                    if(elem.length > 1) {
-                        elem = elem[0].toUpperCase() + elem.substr(1).toLowerCase();   
-						if(typeof(bondTable[elem]) === 'undefined') {
-							//not a known element, probably should just use first letter
-							elem = elem[0];
-						} else if(line[0] == 'A' && elem == 'Ca') { //alpha carbon, not calcium
-							elem = "C";
-						}
-                    }
-                } else {
-                    elem = elem[0].toUpperCase() + elem.substr(1).toLowerCase();                    
-                }
-
-                if(elem == 'H' && noH)
-                    continue;
-                if (line[0] == 'H')
-                    hetflag = true;
-                else
-                    hetflag = false;
-                serialToIndex[serial] = atoms[atoms.length-1].length;
-                atoms[atoms.length-1].push({
-                    'resn' : resn,
-                    'x' : x,
-                    'y' : y,
-                    'z' : z,
-                    'elem' : elem,
-                    'hetflag' : hetflag,
-                    'chain' : chain,
-                    'resi' : resi,
-                    'icode' : icode,
-                    'rescode' : resi + (icode != ' ' ? "^" + icode : ""), // combo
-                    // resi
-                    // and
-                    // icode
-                    'serial' : serial,
-                    'atom' : atom,
-                    'bonds' : [],
-                    'ss' : 'c',
-                    'bondOrder' : [],
-                    'properties' : {},
-                    'b' : b,
-                    'pdbline' : line
-                });
-            } else if (recordName == 'SHEET ') {
-                hasStruct = true;
-                startChain = line.substr(21, 1);
-                startResi = parseInt(line.substr(22, 4));
-                endChain = line.substr(32, 1);
-                endResi = parseInt(line.substr(33, 4));
-                protein.sheet
-                        .push([ startChain, startResi, endChain, endResi ]);
-            } else if (recordName == 'CONECT') {
-                // MEMO: We don't have to parse SSBOND, LINK because both are
-                // also
-                // described in CONECT. But what about 2JYT???
-                var from = parseInt(line.substr(6, 5));
-                var fromAtom = atoms[atoms.length-1][serialToIndex[from]];
-                for (j = 0; j < 4; j++) {
-                    var to = parseInt(line.substr([ 11, 16, 21, 26 ][j], 5));
-                    var toAtom = atoms[atoms.length-1][serialToIndex[to]];
-                    if (fromAtom !== undefined && toAtom !== undefined) {
-                        // minimal cleanup here - pymol likes to output
-                        // duplicated conect records
-                        var toindex = serialToIndex[to];
-                        if (fromAtom.bonds[fromAtom.bonds.length - 1] != toindex) {
-                            fromAtom.bonds.push(toindex);
-                            fromAtom.bondOrder.push(1);
-                        }
-                    }
-                }
-            } else if (recordName == 'HELIX ') {
-                hasStruct = true;
-                startChain = line.substr(19, 1);
-                startResi = parseInt(line.substr(21, 4));
-                endChain = line.substr(31, 1);
-                endResi = parseInt(line.substr(33, 4));
-                protein.helix
-                        .push([ startChain, startResi, endChain, endResi ]);
-            } else if ((!noAssembly) && (recordName == 'REMARK')
-                    && (line.substr(13, 5) == 'BIOMT')) {
-                var n;
-                var matrix = new $3Dmol.Matrix4(); 
-                for (n = 1; n <= 3; n++) {
-                    line = lines[i].replace(/^\s*/, '');
-                    if (parseInt(line.substr(18, 1)) == n) { // check for all
-                                                                // three lines
-                                                                // by matching #
-                                                                // @ end of
-                                                                // "BIOMT" to n
-                        matrix.elements[(n - 1)] = parseFloat(line.substr(23,
-                                10));
-                        matrix.elements[(n - 1) + 4] = parseFloat(line.substr(
-                                33, 10));
-                        matrix.elements[(n - 1) + 8] = parseFloat(line.substr(
-                                43, 10));
-                        matrix.elements[(n - 1) + 12] = parseFloat(line
-                                .substr(53));
-                        i++;
-                    } else {
-                        while (line.substr(13, 5) == 'BIOMT') {
-                            i++;
-                            line = lines[i].replace(/^\s*/, '');
-                        }
-                    }
-                }
-                matrix.elements[3] = 0;
-                matrix.elements[7] = 0;
-                matrix.elements[11] = 0;
-                matrix.elements[15] = 1;
-                modelData.symmetries.push(matrix);
-                i--; // set i back
-            } else if (recordName == 'CRYST1') {
-                var a, b, c, alpha, beta, gamma;
-                a = parseFloat(line.substr(7, 8));
-                b = parseFloat(line.substr(16, 8));
-                c = parseFloat(line.substr(25, 8));
-                alpha = parseFloat(line.substr(34, 6));
-                beta = parseFloat(line.substr(41, 6));
-                gamma = parseFloat(line.substr(48, 6));
-                modelData.cryst = {'a' : a, 'b' : b, 'c' : c, 'alpha' : alpha, 'beta' : beta, 'gamma' : gamma};
-            }
-        }
-
-        var starttime = (new Date()).getTime();
-        
-        for (var n = 0; n < atoms.length; n++) {
-            // assign bonds - yuck, can't count on connect records
-            assignPDBBonds(atoms[n]);
-            // console.log("bond connecting " + ((new Date()).getTime() -
-            // starttime));
-        
-            if (!noAssembly)
-                processSymmetries(modelData.symmetries, copyMatrix, atoms[n]);
-
-            if (computeStruct || !hasStruct) {
-                starttime = (new Date()).getTime();
-                computeSecondaryStructure(atoms[n]);
-                // console.log("secondary structure " + ((new Date()).getTime() -
-                // starttime));
-            }
-
-            // Assign secondary structures from pdb file
-            for (i = start; i < atoms[n].length; i++) {
-                atom = atoms[n][i];
-                if (atom === undefined)
-                    continue;
-
-                var found = false;
-                // MEMO: Can start chain and end chain differ?
-                for (j = 0; j < protein.sheet.length; j++) {
-                    if (atom.chain != protein.sheet[j][0])
-                        continue;
-                    if (atom.resi < protein.sheet[j][1])
-                        continue;
-                    if (atom.resi > protein.sheet[j][3])
-                        continue;
-                    atom.ss = 's';
-                    if (atom.resi == protein.sheet[j][1])
-                        atom.ssbegin = true;
-                    if (atom.resi == protein.sheet[j][3])
-                        atom.ssend = true;
-                }
-                for (j = 0; j < protein.helix.length; j++) {
-                    if (atom.chain != protein.helix[j][0])
-                        continue;
-                    if (atom.resi < protein.helix[j][1])
-                        continue;
-                    if (atom.resi > protein.helix[j][3])
-                        continue;
-                    atom.ss = 'h';
-                    if (atom.resi == protein.helix[j][1])
-                        atom.ssbegin = true;
-                    else if (atom.resi == protein.helix[j][3])
-                        atom.ssend = true;
-                }
-            }
-        }
-        
-        return atoms;
-    };
-
-    /**
-     * Parse a pqr file from str and create atoms. A pqr file is assumed to be a
-     * whitespace delimited PDB with charge and radius fields.
-     *
-     * @param {string}
-     *            str
-     * @param {Object}
-     *            options - noSecondaryStructure (do not compute ss)
-     */
-    parsers.pqr = parsers.PQR = function(str, options) {
-
-        var atoms = [[]];
-        var atoms_cnt = 0;
-        var start = atoms[atoms.length-1].length;
-        var atom;
-        var computeStruct = !options.noSecondaryStructure;
-
-        var serialToIndex = []; // map from pdb serial to index in atoms
-        var lines = str.split(/\r?\n|\r/);
-        var i, j, k, line;
-        for (i = 0; i < lines.length; i++) {
-            line = lines[i].replace(/^\s*/, ''); // remove indent
-            var recordName = line.substr(0, 6);
-            var startChain, startResi, endChain, endResi;
-            
-            if (recordName.indexOf("END") == 0) {
-                if (options.multimodel) {
-                    if (!options.onemol)
-                        atoms.push([]);
-                    continue;
-                }
-                else {
-                    break;
-                }
-            }
-            else if (recordName == 'ATOM  ' || recordName == 'HETATM') {
-                // I would have liked to split based solely on whitespace, but
-                // it seems that there is no guarantee that all the fields will
-                // be filled out (e.g. the chain) so this doesn't work
-                var serial = parseInt(line.substr(6, 5));
-                var atom = line.substr(12, 4).replace(/ /g, "");
-                var resn = line.substr(17, 3);
-                var chain = line.substr(21, 1);
-                var resi = parseInt(line.substr(22, 4));
-                // however let's split the coordinates, charge and radius by
-                // whitespace
-                // to support extra precision
-                var vals = line.substr(30).trim().split(/\s+/);
-                var x = parseFloat(vals[0]);
-                var y = parseFloat(vals[1]);
-                var z = parseFloat(vals[2]);
-                var charge = parseFloat(vals[3]);
-                var radius = parseFloat(vals[4]);
-
-                var elem = atom[0];
-                if (atom.length > 1 && atom[1].toUpperCase() != atom[1]) {
-                    // slight hack - identify two character elements by the
-                    // second character in the atom name being lowercase
-                    elem = atom.substr(0, 2);
-                }
-
-                if (line[0] == 'H')
-                    hetflag = true;
-                else
-                    hetflag = false;
-                serialToIndex[serial] = atoms[atoms.length-1].length;
-                atoms[atoms.length-1].push({
-                    'resn' : resn,
-                    'x' : x,
-                    'y' : y,
-                    'z' : z,
-                    'elem' : elem,
-                    'hetflag' : hetflag,
-                    'chain' : chain,
-                    'resi' : resi,
-                    'serial' : serial,
-                    'atom' : atom,
-                    'bonds' : [],
-                    'ss' : 'c',
-                    'bondOrder' : [],
-                    'properties' : {
-                        'charge' : charge,
-                        'partialCharge' : charge,
-                        'radius' : radius
-                    },
-                    'pdbline' : line
-                });
-            } else if (recordName == 'CONECT') {
-                // MEMO: We don't have to parse SSBOND, LINK because both are
-                // also
-                // described in CONECT. But what about 2JYT???
-                var from = parseInt(line.substr(6, 5));
-                var fromAtom = atoms[atoms.length-1][serialToIndex[from]];
-                for (j = 0; j < 4; j++) {
-                    var to = parseInt(line.substr([ 11, 16, 21, 26 ][j], 5));
-                    var toAtom = atoms[atoms.length-1][serialToIndex[to]];
-                    if (fromAtom !== undefined && toAtom !== undefined) {
-                        fromAtom.bonds.push(serialToIndex[to]);
-                        fromAtom.bondOrder.push(1);
-                    }
-                }
-            }
-        }
-
-        // assign bonds - yuck, can't count on connect records
-        for (var i = 0; i < atoms.length; i++) {
-            assignPDBBonds(atoms[i]);
-            if (computeStruct)
-                computeSecondaryStructure(atoms[i]);
-        }
-        
-        return atoms;
-    };
-
-    return parsers;
-})();
-var $3Dmol = $3Dmol || {};
-
-//properties for mapping
-
-/* partial charges for proteins */
-$3Dmol.partialCharges = {
-"ALA:N": -0.15,
-"ALA:CA": 0.10,
-"ALA:CB": 0.00,
-"ALA:C": 0.60,
-"ALA:O": -0.55,
-"ARG:N": -0.15,
-"ARG:CA": 0.10,
-"ARG:CB": 0.00,
-"ARG:CG": 0.00,
-"ARG:CD": 0.10,
-"ARG:NE": -0.10,
-"ARG:CZ": 0.50,
-"ARG:NH1": 0.25,
-"ARG:NH2": 0.25,
-"ARG:C": 0.60,
-"ARG:O": -0.55,
-"ASN:N": -0.15,
-"ASN:CA": 0.10,
-"ASN:CB": 0.00,
-"ASN:CG": 0.55,
-"ASN:OD1": -0.55,
-"ASN:ND2": 0.00,
-"ASN:C": 0.60,
-"ASN:O": -0.55,
-"ASP:N": -0.15,
-"ASP:CA": 0.10,
-"ASP:CB": 0.00,
-"ASP:CG": 0.14,
-"ASP:OD1": -0.57,
-"ASP:OD2": -0.57,
-"ASP:C": 0.60,
-"ASP:O": -0.55,
-"CYS:N": -0.15,
-"CYS:CA": 0.10,
-"CYS:CB": 0.19,
-"CYS:SG": -0.19,
-"CYS:C": 0.60,
-"CYS:O": -0.55,
-"GLN:N": -0.15,
-"GLN:CA": 0.10,
-"GLN:CB": 0.00,
-"GLN:CG": 0.00,
-"GLN:CD": 0.55,
-"GLN:OE1": -0.55,
-"GLN:NE2": 0.00,
-"GLN:C": 0.60,
-"GLN:O": -0.55,
-"GLU:N": -0.15,
-"GLU:CA": 0.10,
-"GLU:CB": 0.00,
-"GLU:CG": 0.00,
-"GLU:CD": 0.14,
-"GLU:OE1": -0.57,
-"GLU:OE2": -0.57,
-"GLU:C": 0.60,
-"GLU:O": -0.55,
-"GLY:N": -0.15,
-"GLY:CA": 0.10,
-"GLY:C": 0.60,
-"GLY:O": -0.55,
-"HIS:N": -0.15,
-"HIS:CA": 0.10,
-"HIS:CB": 0.00,
-"HIS:CG": 0.10,
-"HIS:ND1": -0.10,
-"HIS:CD2": 0.10,
-"HIS:NE2": -0.40,
-"HIS:CE1": 0.30,
-"HIS:C": 0.60,
-"HIS:O": -0.55,
-"ILE:N": -0.15,
-"ILE:CA": 0.10,
-"ILE:CB": 0.00,
-"ILE:CG2": 0.00,
-"ILE:CG1": 0.00,
-"ILE:CD": 0.00,
-"ILE:C": 0.60,
-"ILE:O": -0.55,
-"LEU:N": -0.15,
-"LEU:CA": 0.10,
-"LEU:CB": 0.00,
-"LEU:CG": 0.00,
-"LEU:CD1": 0.00,
-"LEU:CD2": 0.00,
-"LEU:C": 0.60,
-"LEU:O": -0.55,
-"LYS:N": -0.15,
-"LYS:CA": 0.10,
-"LYS:CB": 0.00,
-"LYS:CG": 0.00,
-"LYS:CD": 0.00,
-"LYS:CE": 0.25,
-"LYS:NZ": 0.75,
-"LYS:C": 0.60,
-"LYS:O": -0.55,
-"MET:N": -0.15,
-"MET:CA": 0.10,
-"MET:CB": 0.00,
-"MET:CG": 0.06,
-"MET:SD": -0.12,
-"MET:CE": 0.06,
-"MET:C": 0.60,
-"MET:O": -0.55,
-"PHE:N": -0.15,
-"PHE:CA": 0.10,
-"PHE:CB": 0.00,
-"PHE:CG": 0.00,
-"PHE:CD1": 0.00,
-"PHE:CD2": 0.00,
-"PHE:CE1": 0.00,
-"PHE:CE2": 0.00,
-"PHE:CZ": 0.00,
-"PHE:C": 0.60,
-"PHE:O": -0.55,
-"PRO:N": -0.25,
-"PRO:CD": 0.10,
-"PRO:CA": 0.10,
-"PRO:CB": 0.00,
-"PRO:CG": 0.00,
-"PRO:C": 0.60,
-"PRO:O": -0.55,
-"SER:N": -0.15,
-"SER:CA": 0.10,
-"SER:CB": 0.25,
-"SER:OG": -0.25,
-"SER:C": 0.60,
-"SER:O": -0.55,
-"THR:N": -0.15,
-"THR:CA": 0.10,
-"THR:CB": 0.25,
-"THR:OG1": -0.25,
-"THR:CG2": 0.00,
-"THR:C": 0.60,
-"THR:O": -0.55,
-"TRP:N": -0.15,
-"TRP:CA": 0.10,
-"TRP:CB": 0.00,
-"TRP:CG": -0.03,
-"TRP:CD2": 0.10,
-"TRP:CE2": -0.04,
-"TRP:CE3": -0.03,
-"TRP:CD1": 0.06,
-"TRP:NE1": -0.06,
-"TRP:CZ2": 0.00,
-"TRP:CZ3": 0.00,
-"TRP:CH2": 0.00,
-"TRP:C": 0.60,
-"TRP:O": -0.55,
-"TYR:N": -0.15,
-"TYR:CA": 0.10,
-"TYR:CB": 0.00,
-"TYR:CG": 0.00,
-"TYR:CD1": 0.00,
-"TYR:CE1": 0.00,
-"TYR:CD2": 0.00,
-"TYR:CE2": 0.00,
-"TYR:CZ": 0.25,
-"TYR:OH": -0.25,
-"TYR:C": 0.60,
-"TYR:O": -0.55,
-"VAL:N": -0.15,
-"VAL:CA": 0.10,
-"VAL:CB": 0.00,
-"VAL:CG1": 0.00,
-"VAL:CG2": 0.00,
-"VAL:C": 0.60,
-"VAL:O": -0.55
-};
-    
-//this can be supplied to mapAtomProperties
-$3Dmol.applyPartialCharges = function(atom, keepexisting) {
-    if(!keepexisting || typeof(atom.partialCharge) === "undefined") {
-        if(atom.resn && atom.atom) {
-            var key = atom.resn+":"+atom.atom;
-            atom.properties['partialCharge'] = $3Dmol.partialCharges[key];
-        }
-    }
-};// Specifications for various object types used in 3Dmol.js
-// This is primarily for documentation 
-(function() {
-/**
- * GLViewer input specification
- * @typedef ViewerSpec
- * @prop {Object} defaultcolors - map of elements to colors
- * @prop {boolean} nomouse - if true, disable handling of mouse events
- * @prop {ColorSpec} backgroundColor - color of background
- */
-
-/**
- * Atom representation. Depending on the input file format, not all fields may be defined.
- * @typedef AtomSpec
- * @prop {string} resn - Parent residue name
- * @prop {number} x - Atom's x coordinate
- * @prop {number} y - Atom's y coordinate
- * @prop {number} z - Atom's z coordinate
- * @prop {ColorSpec} color - Atom's color, as hex code or built-in color string
- * @prop {ColorSpec} surfaceColor - Hex code for color to be used for surface patch over this atom
- * @prop {string} elem - Element abbreviation (e.g. 'H', 'Ca', etc)
- * @prop {boolean} hetflag - Set to true if atom is a heteroatom
- * @prop {string} chain - Chain this atom belongs to, if specified in input file (e.g 'A' for chain A)
- * @prop {number} resi - Residue number 
- * @prop {number} icode
- * @prop {number} rescode
- * @prop {number} serial - Atom's serial id number
- * @prop {string} atom - Atom name; may be more specific than 'elem' (e.g 'CA' for alpha carbon)
- * @prop {Array.<number>} bonds - Array of atom ids this atom is bonded to
- * @prop {string} ss - Secondary structure identifier (for cartoon render; e.g. 'h' for helix)
- * @prop {boolean} singleBonds - true if this atom forms only single bonds or no bonds at all
- * @prop {Array.<number>} bondOrder - Array of this atom's bond orders, corresponding to bonds identfied by 'bonds'
- * @prop {Object} properties - Optional mapping of additional properties
- * @prop {number} b - Atom b factor data
- * @prop {string} pdbline - If applicable, this atom's record entry from the input PDB file (used to output new PDB from models)
- * @prop {boolean} clickable - Set this flag to true to enable click selection handling for this atom
- * @prop {function(this, $3Dmol.GLViewer)} callback - Callback click handler function to be executed on this atom and its parent viewer
- * @prop {boolean} invert - for selection, inverts the meaning of the selection
- */
-
-
-/**
- * Atom selection object. Used to specify what atoms should be selected.  Can include
- * any field from {@link AtomSpec} in which case atoms must equal the specified value.  
- * All fields must match for the selection to hold. If values
- * are provided as a list, then only one value of the list must match.
- * 
- * @example
- * viewer.addResLabels({resi: [1,2,3,4,5], atom: 'CA'}); // will label alpha carbons (CA) of residues 1-5
- * 
- * @typedef AtomSelectionSpec
- * @prop {AtomSpec} ... - any field from {@link AtomSpec}, values may be singletons or lists
- * @prop {GLModel} model - a single model or list of models from which atoms should be selected
- * @prop {number} bonds - overloaded to select number of bonds, e.g. {bonds: 0} will select all nonbonded atoms
- * @prop {function} predicate - user supplied function that gets passed an {AtomSpec} and should return true if the atom should be selected
- * @prop {boolean} invert - if set, inverts the meaning of the selection
- * @prop {boolean} byres - if set, expands the selection to include all atoms of any residue that has any atom selected
- * @prop {number} expand - expands the selection to include all atoms within a given distance from the selection
- * @prop {WithinSelectionSpec} within - intersects the selection with the set of atoms within a given distance from another selection
- */
-
-/**
- * Within selection object. Used to find the subset of an atom selection that is within
- * some distance from another atom selection. When added as a field of an {@link AtomSelectionSpec},
- * intersects the set of atoms in that selection with the set of atoms within a given
- * distance from the given {@link AtomSelectionSpec}.
- *
- * @example
- * viewer.setStyle({chain: 'A', within:{distance: 10, sel:{chain: 'B'}}}, {sphere:{}}); // stylizes atoms in chain A that are within 10 angstroms of an atom in chain B
- *
- * @typedef WithinSelectionSpec
- * @prop {number} distance - the distance in angstroms away from the atom selection to include atoms in the parent selection
- * @prop {AtomSelectionSpec} sel - the selection of atoms against which to measure the distance from the parent atom selection
- */
-
-
-
-/** 
- * @typedef AtomStyleSpec
- * @prop {LineStyleSpec} line - draw bonds as lines
- * @prop {CrossStyleSpec} cross - draw atoms as crossed lines (aka stars)
- * @prop {StickStyleSpec} stick  - draw bonds as capped cylinders
- * @prop {SphereStyleSpec} sphere - draw atoms as spheres
- * @prop {CartoonStyleSpec} cartoon - draw cartoon representation of secondary structure
- */
-
-/** 
- * @typedef SurfaceStyleSpec
- * @prop {number} opacity - sets the transparency: 0 to hide, 1 for fully opaque
- * @prop {ColorschemeSpec} colorscheme - element based coloring
- * @prop {ColorSpec} color - fixed coloring, overrides colorscheme
- * @prop {$3Dmol.VolumeData} voldata - volumetric data for vertex coloring
- * @prop {$3Dmol.Gradient} volscheme - coloring scheme for mapping volumetric data to vertex color
- * @prop {Object} map - specifies a numeric atom property (prop) and color mapping (scheme) such as {@link $3Dmol.Gradient.RWB}.  Deprecated, use colorscheme instead.
- * 
- * @example
- * viewer.addSurface($3Dmol.SurfaceType.MS, {map:{prop:'partialCharge',scheme:new $3Dmol.Gradient.RWB(-.6,.6)}, opacity:0.85});
-
- */
-
-/** 
- * Isosurface style specification
- * @typedef IsoSurfaceSpec
- * @prop {number} isoval - specifies the isovalue to draw surface at
- * @propr {boolean} voxel - if true uses voxel style rendering
- * @prop {ColorSpec} color - solid color
- * @prop {number} opacity - transparency, between 0 and 1
- * @prop {boolean} wireframe - draw as wireframe, not surface
- * @prop {number} linewidth - width of line for wireframe rendering
- * @prop {number} smoothness - amount to smooth surface (default 1)
- * @prop {boolean} clickable - if true, user can click on object to trigger callback
- * @prop {function} callback - function to call on click 
- */
-
-/** 
- * GLShape style specification
- * @typedef ShapeSpec
- * @prop {ColorSpec} color - solid color
- * @prop {number} alpha - transparency
- * @prop {boolean} wireframe - draw as wireframe, not surface
- * @prop {number} linewidth - width of line for wireframe rendering
- * @prop {boolean} clickable - if true, user can click on object to trigger callback
- * @prop {function} callback - function to call on click 
- */
-
-
-/**
- * Specification for adding custom shape. Extends {@link ShapeSpec}.
- * @typedef CustomShapeSpec
- * @augments ShapeSpec
- * @prop {Array.<$3Dmol.Vector3>} vertexArr - List of vertex positions
- * @prop {Array.<$3Dmol.Vector3>} normalArr - List of normal vectors for each vertex
- * @prop {Array.<number>} faceArr - List of triangles to build the custom shape. Each triangle is defined by the indices of 3 vertices in vertexArr, so the array length should be 3 times the number of faces.
- * @prop {ColorSpec | Array.<ColorSpec>} color - Either a single color for the whole object or an array specifying the color at each vertex.
- */
-
-/**
- * Sphere shape specification. Extends {@link ShapeSpec}  
- * 
- * @typedef SphereSpec   
- * @prop {$3Dmol.Vector3} center
- * @prop {number} radius
- * 
- */
-
-
-/**
- * Arrow shape specification.  Extends {@link ShapeSpec}  
- * @typedef ArrowSpec
- * @prop {$3Dmol.Vector3} start
- * @prop {$3Dmol.Vector3} end
- * @prop {number} radius
- * @prop {number} radiusRatio - ratio of arrow base to cylinder (1.618034 default)
- * @prop {number} mid - relative position of arrow base (0.618034 default)
- */
-
-
-/**
- * Cylinder shape specification.  Extends {@link ShapeSpec}  
- * @typedef CylinderSpec
- * @prop {$3Dmol.Vector3} start
- * @prop {$3Dmol.Vector3} end
- * @prop {number} radius
- * @prop {boolean} fromCap
- * @prop {boolean} toCap
- */
-
-/**
- * Line shape specification.  Extends {@link ShapeSpec}  (but defaults to wireframe)
- * @typedef LineSpec
- * @prop {$3Dmol.Vector3} start
- * @prop {$3Dmol.Vector3} end
- */
-
-});
-/**
- * $3Dmol.VolumeData stores volumetric data. This includes file parsing
- * functionality.
- * 
- * @class
- * @param {string} str - volumetric data
- * @param {string} format - format of supplied data (cube)
- */
-$3Dmol.VolumeData = function(str, format) {
-
-    this.unit = {
-        x : 1,
-        y : 1,
-        z : 1
-    }; // scale of each voxel
-    this.origin = {
-        x : 0,
-        y : 0,
-        z : 0
-    }; // origin (bottom "left", not center)
-    this.size = {
-        x : 0,
-        y : 0,
-        z : 0
-    }; // number of voxels in each direction
-    this.data = new Float32Array([]); // actual floating point data, arranged
-                                        // x->y->z
-
-    format = format.toLowerCase();
-    if (this[format]) {
-        this[format](str);
-    }
-};
-
-/**
- * @function $3Dmol.VolumeData.getVal
- * @param {number} x,y,z - the coordinates
- * @returns - value closest to provided coordinate; zero if coordinate invalid
- */
-$3Dmol.VolumeData.prototype.getVal = function(x,y,z) {
-    x -= this.origin.x;
-    y -= this.origin.y;
-    z -= this.origin.z;
-    
-    x /= this.unit.x;
-    y /= this.unit.y;
-    z /= this.unit.z;
-    
-    x = Math.round(x);
-    y = Math.round(y);
-    z = Math.round(z);
-    
-    if(x < 0 || x >= this.size.x) return 0;
-    if(y < 0 || y >= this.size.y) return 0;
-    if(z < 0 || z >= this.size.z) return 0;
-    
-    return this.data[x*this.size.y*this.size.z + y*this.size.z + z];
-};
-
-// parse cube data
-$3Dmol.VolumeData.prototype.cube = function(str) {
-    var lines = str.replace(/^\s+/, "").split(/[\n\r]+/);
-
-    if (lines.length < 6)
-        return;
-
-    var lineArr = lines[2].replace(/^\s+/, "").replace(/\s+/g, " ").split(" ");
-
-    var atomsnum = parseFloat(lineArr[0]); //includes sign, which indicates presence of oribital line in header
-    var natoms = Math.abs(atomsnum);
-
-    var origin = this.origin = new $3Dmol.Vector3(parseFloat(lineArr[1]),
-            parseFloat(lineArr[2]), parseFloat(lineArr[3]));
-
-    lineArr = lines[3].replace(/^\s+/, "").replace(/\s+/g, " ").split(" ");
-
-    // might have to convert from bohr units to angstroms
-    // there is a great deal of confusion here:
-    // n>0 means angstroms: http://www.gaussian.com/g_tech/g_ur/u_cubegen.htm
-    // n<0 means angstroms: http://paulbourke.net/dataformats/cube/
-    // always assume bohr: openbabel source code
-    // always assume angstrom: http://www.ks.uiuc.edu/Research/vmd/plugins/molfile/cubeplugin.html
-    // we are going to go with n<0 means angstrom - note this is just the first n
-    var convFactor = (lineArr[0] > 0) ? 0.529177 : 1;
-    origin.multiplyScalar(convFactor);
-
-    var nX = Math.abs(lineArr[0]);
-    var xVec = new $3Dmol.Vector3(parseFloat(lineArr[1]),
-            parseFloat(lineArr[2]), parseFloat(lineArr[3]))
-            .multiplyScalar(convFactor);
-
-    lineArr = lines[4].replace(/^\s+/, "").replace(/\s+/g, " ").split(" ");
-    var nY = Math.abs(lineArr[0]);
-    var yVec = new $3Dmol.Vector3(parseFloat(lineArr[1]),
-            parseFloat(lineArr[2]), parseFloat(lineArr[3]))
-            .multiplyScalar(convFactor);
-
-    lineArr = lines[5].replace(/^\s+/, "").replace(/\s+/g, " ").split(" ");
-    var nZ = Math.abs(lineArr[0]);
-    var zVec = new $3Dmol.Vector3(parseFloat(lineArr[1]),
-            parseFloat(lineArr[2]), parseFloat(lineArr[3]))
-            .multiplyScalar(convFactor);
-
-    this.size = {x:nX, y:nY, z:nZ};
-    this.unit = new $3Dmol.Vector3(xVec.x, yVec.y, zVec.z);
-    
-    if (xVec.y != 0 || xVec.z != 0 || yVec.x != 0 || yVec.z != 0 || zVec.x != 0
-            || zVec.y != 0)
-        console
-                .log("Warning: Cube file is not axis aligned.  This isn't going to look right.");   
-    var headerlines = 6;
-    if(atomsnum < 0) headerlines++; //see: http://www.ks.uiuc.edu/Research/vmd/plugins/molfile/cubeplugin.html
-    var raw = lines.splice(natoms + headerlines).join(" ");
-    raw = raw.replace(/^\s+/,'');
-    raw = raw.split(/[\s\r]+/);
-    this.data = new Float32Array(raw);
-
-};
-//Hackish way to create webworker (independent of $3Dmol namespace) within minified file
-$3Dmol.workerString = function(){
-
-    self.onmessage = function(oEvent) {
-        var obj = oEvent.data;
-        var type = obj.type;
-        if (type < 0) // sending atom data, initialize
-        {
-            self.atomData = obj.atoms;
-            self.volume = obj.volume;
-            self.ps = new ProteinSurface();
-        } else {
-            var ps = self.ps;
-            ps.initparm(obj.expandedExtent, (type == 1) ? false : true, self.volume);
-            ps.fillvoxels(self.atomData, obj.extendedAtoms);
-            ps.buildboundary();
-            if (type === 4 || type === 2) {
-                ps.fastdistancemap();
-                ps.boundingatom(false);
-                ps.fillvoxelswaals(self.atomData, obj.extendedAtoms);    
-            }        
-            ps.marchingcube(type);
-            var VandF = ps.getFacesAndVertices(obj.atomsToShow);
-            self.postMessage(VandF);
-        }
-    };
-    
-}.toString().replace(/(^.*?\{|\}$)/g, "");
-
-$3Dmol.workerString += "; var ProteinSurface=" + $3Dmol.ProteinSurface.toString().replace(/\$3Dmol.MarchingCube./g, "MarchingCube.");
-$3Dmol.workerString += ",MarchingCube=("+$3Dmol.MarchingCubeInitializer.toString() +")();";
-
-$3Dmol.SurfaceWorker = window.URL.createObjectURL(new Blob([$3Dmol.workerString],{type: 'text/javascript'}));
-
-$3Dmol['workerString'] = $3Dmol.workerString;
-$3Dmol['SurfaceWorker'] = $3Dmol.SurfaceWorker;
diff --git a/3dmol/js/vendor/fastclick.js b/3dmol/js/vendor/fastclick.js
deleted file mode 100644
index 93f82da..0000000
--- a/3dmol/js/vendor/fastclick.js
+++ /dev/null
@@ -1,8 +0,0 @@
-!function(){"use strict";/**
-	 * @preserve FastClick: polyfill to remove click delays on browsers with touch UIs.
-	 *
-	 * @codingstandard ftlabs-jsv2
-	 * @copyright The Financial Times Limited [All Rights Reserved]
-	 * @license MIT License (see LICENSE.txt)
-	 */
-function a(b,d){function e(a,b){return function(){return a.apply(b,arguments)}}var f;if(d=d||{},this.trackingClick=!1,this.trackingClickStart=0,this.targetElement=null,this.touchStartX=0,this.touchStartY=0,this.lastTouchIdentifier=0,this.touchBoundary=d.touchBoundary||10,this.layer=b,this.tapDelay=d.tapDelay||200,this.tapTimeout=d.tapTimeout||700,!a.notNeeded(b)){for(var g=["onMouse","onClick","onTouchStart","onTouchMove","onTouchEnd","onTouchCancel"],h=this,i=0,j=g.length;j>i;i++)h[g[i] [...]
diff --git a/3dmol/js/vendor/jquery.cookie.js b/3dmol/js/vendor/jquery.cookie.js
deleted file mode 100644
index 9abcdec..0000000
--- a/3dmol/js/vendor/jquery.cookie.js
+++ /dev/null
@@ -1,8 +0,0 @@
-/*!
- * jQuery Cookie Plugin v1.4.1
- * https://github.com/carhartl/jquery-cookie
- *
- * Copyright 2013 Klaus Hartl
- * Released under the MIT license
- */
-!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):a("object"==typeof exports?require("jquery"):jQuery)}(function(a){function b(a){return h.raw?a:encodeURIComponent(a)}function c(a){return h.raw?a:decodeURIComponent(a)}function d(a){return b(h.json?JSON.stringify(a):String(a))}function e(a){0===a.indexOf('"')&&(a=a.slice(1,-1).replace(/\\"/g,'"').replace(/\\\\/g,"\\"));try{return a=decodeURIComponent(a.replace(g," ")),h.json?JSON.parse(a):a}catch(b){}}function f(b,c) [...]
diff --git a/3dmol/js/vendor/jquery.js b/3dmol/js/vendor/jquery.js
deleted file mode 100644
index 09b5173..0000000
--- a/3dmol/js/vendor/jquery.js
+++ /dev/null
@@ -1,26 +0,0 @@
-/*!
- * jQuery JavaScript Library v2.1.3
- * http://jquery.com/
- *
- * Includes Sizzle.js
- * http://sizzlejs.com/
- *
- * Copyright 2005, 2014 jQuery Foundation, Inc. and other contributors
- * Released under the MIT license
- * http://jquery.org/license
- *
- * Date: 2014-12-18T15:11Z
- */
-!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){function c(a){var b=a.length,c=_.type(a);return"function"===c||_.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}function d(a,b,c){if(_.isFunction(b))return _.grep(a,function(a,d){retur [...]
- * Sizzle CSS Selector Engine v2.2.0-pre
- * http://sizzlejs.com/
- *
- * Copyright 2008, 2014 jQuery Foundation, Inc. and other contributors
- * Released under the MIT license
- * http://jquery.org/license
- *
- * Date: 2014-12-16
- */
-function(a){function b(a,b,c,d){var e,f,g,h,i,j,l,n,o,p;if((b?b.ownerDocument||b:O)!==G&&F(b),b=b||G,c=c||[],h=b.nodeType,"string"!=typeof a||!a||1!==h&&9!==h&&11!==h)return c;if(!d&&I){if(11!==h&&(e=sb.exec(a)))if(g=e[1]){if(9===h){if(f=b.getElementById(g),!f||!f.parentNode)return c;if(f.id===g)return c.push(f),c}else if(b.ownerDocument&&(f=b.ownerDocument.getElementById(g))&&M(b,f)&&f.id===g)return c.push(f),c}else{if(e[2])return $.apply(c,b.getElementsByTagName(a)),c;if((g=e[3])&&v.ge [...]
-h===(d.ownerDocument||Z)&&m.push(h.defaultView||h.parentWindow||a)}for(f=0;(g=m[f++])&&!b.isPropagationStopped();)b.type=f>1?i:l.bindType||n,k=(rb.get(g,"events")||{})[b.type]&&rb.get(g,"handle"),k&&k.apply(g,c),k=j&&g[j],k&&k.apply&&_.acceptData(g)&&(b.result=k.apply(g,c),b.result===!1&&b.preventDefault());return b.type=n,e||b.isDefaultPrevented()||l._default&&l._default.apply(m.pop(),c)!==!1||!_.acceptData(d)||j&&_.isFunction(d[n])&&!_.isWindow(d)&&(h=d[j],h&&(d[j]=null),_.event.trigge [...]
-return d.join("&").replace(xc,"+")},_.fn.extend({serialize:function(){return _.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=_.prop(this,"elements");return a?_.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!_(this).is(":disabled")&&Bc.test(this.nodeName)&&!Ac.test(a)&&(this.checked||!yb.test(a))}).map(function(a,b){var c=_(this).val();return null==c?null:_.isArray(c)?_.map(c,function(a){return{name:b.name,value:a.replac [...]
diff --git a/3dmol/js/vendor/modernizr.js b/3dmol/js/vendor/modernizr.js
deleted file mode 100644
index 54dbd54..0000000
--- a/3dmol/js/vendor/modernizr.js
+++ /dev/null
@@ -1,8 +0,0 @@
-/*!
- * Modernizr v2.8.3
- * www.modernizr.com
- *
- * Copyright (c) Faruk Ates, Paul Irish, Alex Sexton
- * Available under the BSD and MIT licenses: www.modernizr.com/license/
- */
-window.Modernizr=function(a,b,c){function d(a){t.cssText=a}function e(a,b){return d(x.join(a+";")+(b||""))}function f(a,b){return typeof a===b}function g(a,b){return!!~(""+a).indexOf(b)}function h(a,b){for(var d in a){var e=a[d];if(!g(e,"-")&&t[e]!==c)return"pfx"==b?e:!0}return!1}function i(a,b,d){for(var e in a){var g=b[a[e]];if(g!==c)return d===!1?a[e]:f(g,"function")?g.bind(d||b):g}return!1}function j(a,b,c){var d=a.charAt(0).toUpperCase()+a.slice(1),e=(a+" "+z.join(d+" ")+d).split("  [...]
diff --git a/3dmol/js/vendor/placeholder.js b/3dmol/js/vendor/placeholder.js
deleted file mode 100644
index 9525381..0000000
--- a/3dmol/js/vendor/placeholder.js
+++ /dev/null
@@ -1,2 +0,0 @@
-/*! http://mths.be/placeholder v2.0.9 by @mathias */
-!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):a(jQuery)}(function(a){function b(b){var c={},d=/^jQuery\d+$/;return a.each(b.attributes,function(a,b){b.specified&&!d.test(b.name)&&(c[b.name]=b.value)}),c}function c(b,c){var d=this,f=a(d);if(d.value==f.attr("placeholder")&&f.hasClass("placeholder"))if(f.data("placeholder-password")){if(f=f.hide().nextAll('input[type="password"]:first').show().attr("id",f.removeAttr("id").data("placeholder-id")),b===!0)return f[0]. [...]
diff --git a/3dmol/js/visualize_html.js b/3dmol/js/visualize_html.js
deleted file mode 100644
index 61ec85e..0000000
--- a/3dmol/js/visualize_html.js
+++ /dev/null
@@ -1,111 +0,0 @@
-function build_page(jobid){
-	//jobid = 1234;
-	document.title = "3Dmol Visualization " + jobid
-
-	var a = 
-	"<div id='gldiv'></div>" +
-	"<!--<hr style='margin: 0;'>-->" +
-	"<br>" +
-    "<div id='outer'>" +
-    "<div id='buttons'>" 
-	
-	//change surface
-    var b = 
-    "<!--<br><table border='1' width='100%' cellspacing='0' cellpadding='0'><tr><td valign='top'>--> " +
-    "<font style='color:white; font-size:12pt'>Surface:</font>" + 
-    "<br>" +
-"    <select class='styled-select' id='selected_surface' onchange='update_surface(1)' style='max-width:50%;'>" +
-"       <option style='color: black;' value='SAS'>Solvent Accessible</option>" +
-"<option style='color: black;' value='SES'>Solvent Excluded</option>" +
-"<option style='color: black;' value='VDW'>Van Der Waals</option>" +
-"</select><br>" +
-
-"<!--<div class='inner'><ul class='button-group round' class='leftbutton'><input type='button' button class='button-backbone pure-button' style='width: 85px; height: 30px; color: black' value='On' onclick='on_surface()'></button></input>" +
-
-"    <input type='button' button class='button-backbone pure-button' style='width: 85px; height: 30px; color: black' input type='button' value='Off' onclick='glviewer.removeSurface(surf)'></button></input></ul></div><br>-->" +
-
-"<table border='0' cellspacing='0' cellpadding='0'><tr><td>" +
-"<label class='switch1'> " +
-	"<input class='switch1-input' type='checkbox' onclick='surface_vis(this)' checked/>" +
-	"<span class='switch1-label' data-on='Show' data-off='Hide'></span> " +
-	"<span class='switch1-handle'></span>" +
-"</label>" +
-
-"</td>" +
-
-"<td>" +
-"<!--<div class='inner'><ul class='button-group round' class='rightbutton'><input type='button' button class='button-labela pure-button' style='width: 85px; height: 30px; color: black; font-size: 10pt' value='Translucent' onclick='update_surface(2)'></button></input>" +
-
-"    <input type='button' button class='button-unlabela pure-button' style='width: 85px; height: 30px; color: black' input type='button' value='Opaque' onclick='update_surface(3)'></button></input></ul></div>-->" +
-
-"<label class='switch2'>" +
-	"<input class='switch2-input' type='checkbox' onclick='surface_opacity(this)' checked/>" +
-	"<span class='switch2-label'   data-on='Opaque' data-off='Translucent'></span> " +
-	"<span class='switch2-handle'></span> " +
-"</label>" +
-
-"</td></tr></table>"+
-
-"<br><font style='color:white; font-size:12pt'>Surface Potential:</font>" +
-
-//change min isoval
-" <p style='color:white; font-size: 16px'> Min<input type=range min=-50 max=50 value=-5 id='min_isoval2' step=1 oninput='set_min_isoval2(value)'>    <span id='min_isoval'>-5 </span> kT/e </p>  " +
-
-//change max isoval
-" <p style='color:white; font-size: 16px'> Max<input type=range min=-50 max=50 value=5 id='max_isoval2' step=1 oninput='set_max_isoval2(value)'>    <span id='max_isoval'> 5 </span> kT/e </p>  " +
-
-//reset button
-"<div class='inner'><ul class='button-group round'></input>" +
-"    <input type='button' button class='button-backbone pure-button' style='width: 85px; height: 30px; color: black' input type='button' value='Reset' onclick='reset_vals()'></button></input></ul></div>" +
-
-"<br><font style='color:white; font-size:12pt'>Model:</font>  " +
-
-"<select class='styled-select' id='selected_vis' onchange='set_vis()' style='max-width:50%;'>"+
-"        <option style='color: black;' value='line'>Line </option> "+
-"        <option style='color: black;' value='stick'>Stick </option>" +
-"        <option style='color: black;' value='cross'>Cross </option>"+
-"        <option style='color: black;' value='sphere'>Sphere </option>"+
-"        <option style='color: black;' value='cartoon'>Cartoon </option>"+
-"    </select>" 
-+  "<br><br>"  +
-
- //change color scheme 
-"<font style='color:white; font-size:12pt'>Scheme: </font>" +
-
-
- 
-"<select class='styled-select' id='selected_scheme' onchange='update_surface(0);show_colorbar();' style='max-width:50%;'>" +
-
-"        <option style='color: black;' value='RWB'>Red-White-Blue </option>" +
-"        <option style='color: black;' value='RGB'>Red-Green-Blue </option>" +
-"        <!--<option style='color: black;' value='BWR'>Blue-White-Red </option>-->" +
-"    </select><br>" +
-"<!--<table border='0' cellspacing='0'><tr><td valign='top'>RWB<br>RGB</td><td valign='top'><img src='3dmol/images/rwb.png' width='250'><br><img src='3dmol/images/rgb.png' width='250'></td></tr></table>-->" +
- "<span id='colorbar'><img id='rwb' src='3dmol/images/rwb.png' width='250'></span>" +
-
-"    <!--<div class='inner'><ul class='button-group round'><input type='button' button class='button-labela pure-button' style='width: 90px; height: 30px; color: black' value='Add labels'" +
-"        onclick='addLabels(glviewer); glviewer.render();'></button></input>" +
-"    <input type='button' button class='button-unlabela pure-button' style='width: 95px; height: 30px; color: black' value='Remove labels'" +
-"        onclick='removetheLabels(glviewer); glviewer.render();'></button></input></ul></div>-->"  +
-
-"<table><tr><td>" +
-"<font style='color:white; font-size:12pt'>Labels: </font></td><td><label class='switch3'>" +
-	"<input class='switch3-input' onclick='surface_labels(this)' type='checkbox' checked/>" +
-	"<span class='switch3-label' data-on='Remove' data-off='Add'></span>" +
-	"<span class='switch3-handle'></span> " +
-"</label>" +
-
-"</td></tr></table>"+
-
-
-"<div class='inner'><ul class='button-group round'><input type='button' button class='button-backbone pure-button' style='width: 85px; height: 30px; color: black' input type='button' value='Recenter' onclick='glviewer.zoomTo();'></button></input></ul></div>" +
-"<br></div></div>" +
-"<!--</td></tr></table>-->" 
-
-
-
-
-
-document.write(a)
-document.write(b)
-}

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/pdb2pqr.git



More information about the debian-med-commit mailing list